From 228b90ea943d9bd74dceb211e4555e43d086fb07 Mon Sep 17 00:00:00 2001 From: Miguel Martinez Trivino Date: Thu, 9 Nov 2023 11:02:45 +0100 Subject: [PATCH 1/2] feat(referrer): add referrer persistance Signed-off-by: Miguel Martinez Trivino --- app/controlplane/cmd/wire_gen.go | 3 + app/controlplane/internal/biz/biz.go | 1 + app/controlplane/internal/biz/referrer.go | 270 ++++++ .../internal/biz/referrer_integration_test.go | 230 +++++ .../internal/biz/referrer_test.go | 171 ++++ .../same-digest-than-git-subject.json | 10 + .../attestations/with-duplicated-sha.json | 10 + .../attestations/with-git-subject.json | 10 + .../testdata/attestations/with-string.json | 10 + .../internal/biz/testhelpers/database.go | 1 + .../internal/biz/testhelpers/wire_gen.go | 3 + app/controlplane/internal/data/data.go | 1 + app/controlplane/internal/data/ent/client.go | 208 ++++- app/controlplane/internal/data/ent/ent.go | 2 + .../internal/data/ent/hook/hook.go | 12 + .../ent/migrate/migrations/20231107121730.sql | 8 + .../data/ent/migrate/migrations/atlas.sum | 5 +- .../internal/data/ent/migrate/schema.go | 78 ++ .../internal/data/ent/mutation.go | 844 ++++++++++++++++- .../internal/data/ent/organization.go | 18 +- .../data/ent/organization/organization.go | 34 + .../internal/data/ent/organization/where.go | 23 + .../internal/data/ent/organization_create.go | 32 + .../internal/data/ent/organization_query.go | 107 ++- .../internal/data/ent/organization_update.go | 163 ++++ .../internal/data/ent/predicate/predicate.go | 3 + .../internal/data/ent/referrer.go | 200 +++++ .../internal/data/ent/referrer/referrer.go | 172 ++++ .../internal/data/ent/referrer/where.go | 358 ++++++++ .../internal/data/ent/referrer_create.go | 355 ++++++++ .../internal/data/ent/referrer_delete.go | 88 ++ .../internal/data/ent/referrer_query.go | 845 ++++++++++++++++++ .../internal/data/ent/referrer_update.go | 501 +++++++++++ app/controlplane/internal/data/ent/runtime.go | 11 + .../internal/data/ent/schema-viz.html | 2 +- .../internal/data/ent/schema/organization.go | 1 + .../internal/data/ent/schema/referrer.go | 62 ++ app/controlplane/internal/data/ent/tx.go | 3 + app/controlplane/internal/data/referrer.go | 171 ++++ .../internal/service/attestation.go | 8 + .../renderer/chainloop/chainloop.go | 8 +- .../attestation/renderer/chainloop/v02.go | 14 +- 42 files changed, 5029 insertions(+), 27 deletions(-) create mode 100644 app/controlplane/internal/biz/referrer.go create mode 100644 app/controlplane/internal/biz/referrer_integration_test.go create mode 100644 app/controlplane/internal/biz/referrer_test.go create mode 100644 app/controlplane/internal/biz/testdata/attestations/same-digest-than-git-subject.json create mode 100644 app/controlplane/internal/biz/testdata/attestations/with-duplicated-sha.json create mode 100644 app/controlplane/internal/biz/testdata/attestations/with-git-subject.json create mode 100644 app/controlplane/internal/biz/testdata/attestations/with-string.json create mode 100644 app/controlplane/internal/data/ent/migrate/migrations/20231107121730.sql create mode 100644 app/controlplane/internal/data/ent/referrer.go create mode 100644 app/controlplane/internal/data/ent/referrer/referrer.go create mode 100644 app/controlplane/internal/data/ent/referrer/where.go create mode 100644 app/controlplane/internal/data/ent/referrer_create.go create mode 100644 app/controlplane/internal/data/ent/referrer_delete.go create mode 100644 app/controlplane/internal/data/ent/referrer_query.go create mode 100644 app/controlplane/internal/data/ent/referrer_update.go create mode 100644 app/controlplane/internal/data/ent/schema/referrer.go create mode 100644 app/controlplane/internal/data/referrer.go diff --git a/app/controlplane/cmd/wire_gen.go b/app/controlplane/cmd/wire_gen.go index 96950c377..daf8322fe 100644 --- a/app/controlplane/cmd/wire_gen.go +++ b/app/controlplane/cmd/wire_gen.go @@ -100,6 +100,8 @@ func wireApp(bootstrap *conf.Bootstrap, readerWriter credentials.ReaderWriter, l fanOutDispatcher := dispatcher.New(integrationUseCase, workflowUseCase, workflowRunUseCase, readerWriter, casClientUseCase, availablePlugins, logger) casMappingRepo := data.NewCASMappingRepo(dataData, casBackendRepo, logger) casMappingUseCase := biz.NewCASMappingUseCase(casMappingRepo, membershipRepo, logger) + referrerRepo := data.NewReferrerRepo(dataData, logger) + referrerUseCase := biz.NewReferrerUseCase(referrerRepo, organizationRepo, logger) newAttestationServiceOpts := &service.NewAttestationServiceOpts{ WorkflowRunUC: workflowRunUseCase, WorkflowUC: workflowUseCase, @@ -111,6 +113,7 @@ func wireApp(bootstrap *conf.Bootstrap, readerWriter credentials.ReaderWriter, l AttestationUC: attestationUseCase, FanoutDispatcher: fanOutDispatcher, CASMappingUseCase: casMappingUseCase, + ReferrerUC: referrerUseCase, Opts: v2, } attestationService := service.NewAttestationService(newAttestationServiceOpts) diff --git a/app/controlplane/internal/biz/biz.go b/app/controlplane/internal/biz/biz.go index 4269aa853..e309fd1a1 100644 --- a/app/controlplane/internal/biz/biz.go +++ b/app/controlplane/internal/biz/biz.go @@ -35,6 +35,7 @@ var ProviderSet = wire.NewSet( NewAttestationUseCase, NewWorkflowRunExpirerUseCase, NewCASMappingUseCase, + NewReferrerUseCase, wire.Struct(new(NewIntegrationUseCaseOpts), "*"), wire.Struct(new(NewUserUseCaseParams), "*"), ) diff --git a/app/controlplane/internal/biz/referrer.go b/app/controlplane/internal/biz/referrer.go new file mode 100644 index 000000000..63748b6c3 --- /dev/null +++ b/app/controlplane/internal/biz/referrer.go @@ -0,0 +1,270 @@ +// +// Copyright 2023 The Chainloop Authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package biz + +import ( + "bytes" + "context" + "encoding/json" + "fmt" + "io" + "time" + + "github.com/chainloop-dev/chainloop/internal/attestation/renderer/chainloop" + "github.com/chainloop-dev/chainloop/internal/servicelogger" + "github.com/go-kratos/kratos/v2/log" + cr_v1 "github.com/google/go-containerregistry/pkg/v1" + "github.com/google/uuid" + v1 "github.com/in-toto/attestation/go/v1" + "github.com/secure-systems-lab/go-securesystemslib/dsse" +) + +type Referrer struct { + Digest string + ArtifactType string + // Wether the item is downloadable from CAS or not + Downloadable bool + // points to other digests + References []string +} + +// Actual referrer stored in the DB which includes a nested list of storedReferences +type StoredReferrer struct { + ID uuid.UUID + Digest string + ArtifactType string + // Wether the item is downloadable from CAS or not + Downloadable bool + CreatedAt *time.Time + // Fully expanded list of 1-level off references + References []*StoredReferrer + OrgIDs []uuid.UUID +} + +type ReferrerMap map[string]*Referrer + +type ReferrerRepo interface { + Save(ctx context.Context, input ReferrerMap, orgID uuid.UUID) error + GetFromRoot(ctx context.Context, digest string) (*StoredReferrer, error) +} + +type ReferrerUseCase struct { + repo ReferrerRepo + orgRepo OrganizationRepo + logger *log.Helper +} + +func NewReferrerUseCase(repo ReferrerRepo, orgRepo OrganizationRepo, l log.Logger) *ReferrerUseCase { + if l == nil { + l = log.NewStdLogger(io.Discard) + } + + return &ReferrerUseCase{repo, orgRepo, servicelogger.ScopedHelper(l, "biz/Referrer")} +} + +// ExtractAndPersist extracts the referrers (subject + materials) from the given attestation +// and store it as part of the referrers index table +func (s *ReferrerUseCase) ExtractAndPersist(ctx context.Context, att *dsse.Envelope, orgID string) error { + orgUUID, err := uuid.Parse(orgID) + if err != nil { + return NewErrInvalidUUID(err) + } + + if org, err := s.orgRepo.FindByID(ctx, orgUUID); err != nil { + return fmt.Errorf("finding organization: %w", err) + } else if org == nil { + return NewErrNotFound("organization") + } + + m, err := extractReferrers(att) + if err != nil { + return fmt.Errorf("extracting referrers: %w", err) + } + + if err := s.repo.Save(ctx, m, orgUUID); err != nil { + return fmt.Errorf("saving referrers: %w", err) + } + + return nil +} + +// GetFromRoot returns the referrer identified by the provided content digest, including its first-level references +// For example if sha:deadbeef represents an attestation, the result will contain the attestation + materials associated to it +// TODO:(miguel) authz by user similar to what we do with CASmapping +func (s *ReferrerUseCase) GetFromRoot(ctx context.Context, digest string) (*StoredReferrer, error) { + ref, err := s.repo.GetFromRoot(ctx, digest) + if err != nil { + return nil, fmt.Errorf("getting referrer from root: %w", err) + } else if ref == nil { + return nil, NewErrNotFound("referrer") + } + + return ref, nil +} + +const ( + referrerAttestationType = "ATTESTATION" + referrerGitHeadType = "GIT_HEAD_COMMIT" +) + +// ExtractReferrers extracts the referrers from the given attestation +// this means +// 1 - write an entry for the attestation itself +// 2 - then to all the materials contained in the predicate +// 3 - and the subjects (some of them) +// 4 - creating link between the attestation and the materials/subjects as needed +// see tests for examples +func extractReferrers(att *dsse.Envelope) (ReferrerMap, error) { + // Calculate the attestation hash + jsonAtt, err := json.Marshal(att) + if err != nil { + return nil, fmt.Errorf("marshaling attestation: %w", err) + } + + // Calculate the attestation hash + h, _, err := cr_v1.SHA256(bytes.NewBuffer(jsonAtt)) + if err != nil { + return nil, fmt.Errorf("calculating attestation hash: %w", err) + } + + referrers := make(ReferrerMap) + // 1 - Attestation referrer + // Add the attestation itself as a referrer to the map without references yet + attestationHash := h.String() + referrers[attestationHash] = &Referrer{ + Digest: attestationHash, + ArtifactType: referrerAttestationType, + Downloadable: true, + } + + // 2 - Predicate that's referenced from the attestation + predicate, err := chainloop.ExtractPredicate(att) + if err != nil { + return nil, fmt.Errorf("extracting predicate: %w", err) + } + + // Create new referrers for each material + // and link them to the attestation + for _, material := range predicate.GetMaterials() { + // Skip materials that don't have a digest + if material.Hash == nil { + continue + } + + // Create its referrer entry if it doesn't exist yet + // the reason it might exist is because you might be attaching the same material twice + // i.e the same SBOM twice, in that case we don't want to create a new referrer + // If we are providing different types for the same digest, we should error out + if r, ok := referrers[material.Hash.String()]; ok { + if r.ArtifactType != material.Type { + return nil, fmt.Errorf("material %s has different types: %s and %s", material.Hash.String(), r.ArtifactType, material.Type) + } + + continue + } + + referrers[material.Hash.String()] = &Referrer{ + Digest: material.Hash.String(), + ArtifactType: material.Type, + Downloadable: material.UploadedToCAS, + } + + // Add the reference to the attestation + referrers[attestationHash].References = append(referrers[attestationHash].References, material.Hash.String()) + } + + // 3 - Subject that points to the attestation + statement, err := chainloop.ExtractStatement(att) + if err != nil { + return nil, fmt.Errorf("extracting predicate: %w", err) + } + + for _, subject := range statement.Subject { + subjectRef, err := intotoSubjectToReferrer(subject) + if err != nil { + return nil, fmt.Errorf("transforming subject to referrer: %w", err) + } + + if subjectRef == nil { + continue + } + + // check if we already have a referrer for this digest and set it otherwise + // this is the case for example for git.Head ones + if _, ok := referrers[subjectRef.Digest]; !ok { + referrers[subjectRef.Digest] = subjectRef + // add it to the list of of attestation-referenced digests + referrers[attestationHash].References = append(referrers[attestationHash].References, subjectRef.Digest) + } + + // Update referrer to point to the attestation + referrers[subjectRef.Digest].References = []string{attestationHash} + } + + return referrers, nil +} + +// transforms the in-toto subject to a referrer by deterministically picking +// the subject types we care about (and return nil otherwise), for now we just care about the subjects +// - git.Head and +// - material types +func intotoSubjectToReferrer(r *v1.ResourceDescriptor) (*Referrer, error) { + var digestStr string + for alg, val := range r.Digest { + digestStr = fmt.Sprintf("%s:%s", alg, val) + break + } + + // it's a.git head type + if r.Name == chainloop.SubjectGitHead { + if digestStr == "" { + return nil, fmt.Errorf("no digest found for subject %s", r.Name) + } + + return &Referrer{ + Digest: digestStr, + ArtifactType: referrerGitHeadType, + }, nil + } + + // Iterate on material types + var materialType string + var uploadedToCAS bool + // it's a material type + for k, v := range r.Annotations.AsMap() { + // It's a material type + if k == chainloop.AnnotationMaterialType { + materialType = v.(string) + } else if k == chainloop.AnnotationMaterialCAS { + uploadedToCAS = v.(bool) + } + } + + // it's not a material type + if materialType == "" { + return nil, nil + } + + if digestStr == "" { + return nil, fmt.Errorf("no digest found for subject %s", r.Name) + } + + return &Referrer{ + Digest: digestStr, + ArtifactType: materialType, + Downloadable: uploadedToCAS, + }, nil +} diff --git a/app/controlplane/internal/biz/referrer_integration_test.go b/app/controlplane/internal/biz/referrer_integration_test.go new file mode 100644 index 000000000..004cbbe29 --- /dev/null +++ b/app/controlplane/internal/biz/referrer_integration_test.go @@ -0,0 +1,230 @@ +// +// Copyright 2023 The Chainloop Authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package biz_test + +import ( + "context" + "encoding/json" + "os" + "testing" + + "github.com/chainloop-dev/chainloop/app/controlplane/internal/biz" + "github.com/chainloop-dev/chainloop/app/controlplane/internal/biz/testhelpers" + "github.com/google/uuid" + "github.com/secure-systems-lab/go-securesystemslib/dsse" + "github.com/stretchr/testify/require" + "github.com/stretchr/testify/suite" +) + +func (s *referrerIntegrationTestSuite) TestExtractAndPersists() { + // Load attestation + attJSON, err := os.ReadFile("testdata/attestations/with-git-subject.json") + require.NoError(s.T(), err) + var envelope *dsse.Envelope + require.NoError(s.T(), json.Unmarshal(attJSON, &envelope)) + + wantReferrerAtt := &biz.StoredReferrer{ + Digest: "sha256:ad704d286bcad6e155e71c33d48247931231338396acbcd9769087530085b2a2", + ArtifactType: "ATTESTATION", + Downloadable: true, + } + + wantReferrerCommit := &biz.StoredReferrer{ + Digest: "sha1:78ac366c9e8a300d51808d581422ca61f7b5b721", + ArtifactType: "GIT_HEAD_COMMIT", + } + + wantReferrerSBOM := &biz.StoredReferrer{ + Digest: "sha256:16159bb881eb4ab7eb5d8afc5350b0feeed1e31c0a268e355e74f9ccbe885e0c", + ArtifactType: "SBOM_CYCLONEDX_JSON", + Downloadable: true, + } + + wantReferrerArtifact := &biz.StoredReferrer{ + Digest: "sha256:385c4188b9c080499413f2e0fa0b3951ed107b5f0cb35c2f2b1f07a7be9a7512", + ArtifactType: "ARTIFACT", + Downloadable: true, + } + + wantReferrerOpenVEX := &biz.StoredReferrer{ + Digest: "sha256:b4bd86d5855f94bcac0a92d3100ae7b85d050bd2e5fb9037a200e5f5f0b073a2", + ArtifactType: "OPENVEX", + Downloadable: true, + } + + wantReferrerSarif := &biz.StoredReferrer{ + Digest: "sha256:c4a63494f9289dd9fd44f841efb4f5b52765c2de6332f2d86e5f6c0340b40a95", + ArtifactType: "SARIF", + Downloadable: true, + } + + wantReferrerContainerImage := &biz.StoredReferrer{ + Digest: "sha256:fbd9335f55d83d8aaf9ab1a539b0f2a87b444e8c54f34c9a1ca9d7df15605db4", + ArtifactType: "CONTAINER_IMAGE", + } + + s.T().Run("creation fails if the org doesn't exist", func(t *testing.T) { + err := s.Referrer.ExtractAndPersist(context.Background(), envelope, uuid.NewString()) + s.True(biz.IsNotFound(err)) + }) + + var prevStoredRef *biz.StoredReferrer + s.T().Run("it can store properly the first time", func(t *testing.T) { + err := s.Referrer.ExtractAndPersist(context.Background(), envelope, s.org.ID) + s.NoError(err) + prevStoredRef, err = s.Referrer.GetFromRoot(context.Background(), wantReferrerAtt.Digest) + s.NoError(err) + }) + + s.T().Run("and it's idempotent", func(t *testing.T) { + err := s.Referrer.ExtractAndPersist(context.Background(), envelope, s.org.ID) + s.NoError(err) + ref, err := s.Referrer.GetFromRoot(context.Background(), wantReferrerAtt.Digest) + s.NoError(err) + // Check it's the same referrer than previously retrieved, including timestamps + s.Equal(prevStoredRef, ref) + }) + + s.T().Run("contains all the info", func(t *testing.T) { + got, err := s.Referrer.GetFromRoot(context.Background(), wantReferrerAtt.Digest) + s.NoError(err) + // parent i.e attestation + s.Equal(wantReferrerAtt.Digest, got.Digest) + s.Equal(wantReferrerAtt.Downloadable, got.Downloadable) + s.Equal(wantReferrerAtt.ArtifactType, got.ArtifactType) + // it has all the references + require.Len(t, got.References, 6) + + for i, want := range []*biz.StoredReferrer{ + wantReferrerCommit, wantReferrerSBOM, wantReferrerArtifact, wantReferrerOpenVEX, wantReferrerSarif, wantReferrerContainerImage} { + gotR := got.References[i] + s.Equal(want.Digest, gotR.Digest) + s.Equal(want.ArtifactType, gotR.ArtifactType) + s.Equal(want.Downloadable, gotR.Downloadable) + } + s.Equal([]uuid.UUID{s.orgUUID}, got.OrgIDs) + }) + + s.T().Run("but another org can be attached", func(t *testing.T) { + org2, err := s.Organization.Create(context.Background(), "testing org 2") + require.NoError(s.T(), err) + org2UUID, err := uuid.Parse(org2.ID) + require.NoError(s.T(), err) + + err = s.Referrer.ExtractAndPersist(context.Background(), envelope, org2.ID) + s.NoError(err) + got, err := s.Referrer.GetFromRoot(context.Background(), wantReferrerAtt.Digest) + s.NoError(err) + require.Len(t, got.OrgIDs, 2) + s.Contains(got.OrgIDs, s.orgUUID) + s.Contains(got.OrgIDs, org2UUID) + + // and it's idempotent (no new orgs added) + err = s.Referrer.ExtractAndPersist(context.Background(), envelope, org2.ID) + s.NoError(err) + got, err = s.Referrer.GetFromRoot(context.Background(), wantReferrerAtt.Digest) + s.NoError(err) + require.Len(t, got.OrgIDs, 2) + }) + + s.T().Run("you can ask for info about materials that are subjects", func(t *testing.T) { + got, err := s.Referrer.GetFromRoot(context.Background(), wantReferrerContainerImage.Digest) + s.NoError(err) + // parent i.e attestation + s.Equal(wantReferrerContainerImage.Digest, got.Digest) + s.Equal(wantReferrerContainerImage.Downloadable, got.Downloadable) + s.Equal(wantReferrerContainerImage.ArtifactType, got.ArtifactType) + // it's connected to the attestation + require.Len(t, got.References, 1) + s.Equal(wantReferrerAtt.Digest, got.References[0].Digest) + s.Equal(wantReferrerAtt.ArtifactType, got.References[0].ArtifactType) + s.Equal(wantReferrerAtt.Downloadable, got.References[0].Downloadable) + }) + + s.T().Run("it might not have references", func(t *testing.T) { + got, err := s.Referrer.GetFromRoot(context.Background(), wantReferrerSarif.Digest) + s.NoError(err) + // parent i.e attestation + s.Equal(wantReferrerSarif.Digest, got.Digest) + s.Equal(wantReferrerSarif.Downloadable, got.Downloadable) + s.Equal(wantReferrerSarif.ArtifactType, got.ArtifactType) + require.Len(t, got.References, 0) + }) + + s.T().Run("or not to exist", func(t *testing.T) { + got, err := s.Referrer.GetFromRoot(context.Background(), "sha256:deadbeef") + s.True(biz.IsNotFound(err)) + s.Nil(got) + }) + + s.T().Run("it should fail if the attestation has the same material twice with different types", func(t *testing.T) { + attJSON, err = os.ReadFile("testdata/attestations/with-duplicated-sha.json") + require.NoError(s.T(), err) + require.NoError(s.T(), json.Unmarshal(attJSON, &envelope)) + + err := s.Referrer.ExtractAndPersist(context.Background(), envelope, s.org.ID) + s.ErrorContains(err, "has different types") + }) + + s.T().Run("it should fail on retrieval if we have stored two referrers with same digest (for two different types)", func(t *testing.T) { + // this attestation contains a material with same digest than the container image from git-subject.json + attJSON, err = os.ReadFile("testdata/attestations/same-digest-than-git-subject.json") + require.NoError(s.T(), err) + require.NoError(s.T(), json.Unmarshal(attJSON, &envelope)) + + // storing will not fail since it's the a different artifact type + err := s.Referrer.ExtractAndPersist(context.Background(), envelope, s.org.ID) + s.NoError(err) + + // but retrieval should fail. In the future we will ask the user to provide the artifact type in these cases of ambiguity + got, err := s.Referrer.GetFromRoot(context.Background(), wantReferrerSarif.Digest) + s.Nil(got) + s.ErrorContains(err, "found more than one referrer with digest") + }) + + s.T().Run("now there should a container image pointing to two attestations", func(t *testing.T) { + // but retrieval should fail. In the future we will ask the user to provide the artifact type in these cases of ambiguity + got, err := s.Referrer.GetFromRoot(context.Background(), wantReferrerContainerImage.Digest) + s.NoError(err) + // it should be referenced by two attestations since it's subject of both + require.Len(t, got.References, 2) + s.Equal("ATTESTATION", got.References[0].ArtifactType) + s.Equal(wantReferrerAtt.Digest, got.References[0].Digest) + s.Equal("ATTESTATION", got.References[1].ArtifactType) + s.Equal("sha256:c90ccaab0b2cfda9980836aef407f62d747680ea9793ddc6ad2e2d7ab615933d", got.References[1].Digest) + }) +} + +type referrerIntegrationTestSuite struct { + testhelpers.UseCasesEachTestSuite + org *biz.Organization + orgUUID uuid.UUID +} + +func (s *referrerIntegrationTestSuite) SetupTest() { + s.TestingUseCases = testhelpers.NewTestingUseCases(s.T()) + + var err error + s.org, err = s.Organization.Create(context.Background(), "testing org") + require.NoError(s.T(), err) + + s.orgUUID, err = uuid.Parse(s.org.ID) + require.NoError(s.T(), err) +} + +func TestReferrerIntegration(t *testing.T) { + suite.Run(t, new(referrerIntegrationTestSuite)) +} diff --git a/app/controlplane/internal/biz/referrer_test.go b/app/controlplane/internal/biz/referrer_test.go new file mode 100644 index 000000000..f42e20cf6 --- /dev/null +++ b/app/controlplane/internal/biz/referrer_test.go @@ -0,0 +1,171 @@ +// +// Copyright 2023 The Chainloop Authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package biz + +import ( + "encoding/json" + "os" + "testing" + + "github.com/secure-systems-lab/go-securesystemslib/dsse" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + "github.com/stretchr/testify/suite" +) + +func (s *referrerTestSuite) TestExtractReferrers() { + testCases := []struct { + name string + inputPath string + expectErr bool + want ReferrerMap + }{ + { + name: "basic", + inputPath: "testdata/attestations/full.json", + want: ReferrerMap{ + "sha256:1a077137aef7ca208b80c339769d0d7eecacc2850368e56e834cda1750ce413a": &Referrer{ + Digest: "sha256:1a077137aef7ca208b80c339769d0d7eecacc2850368e56e834cda1750ce413a", + ArtifactType: "ATTESTATION", + Downloadable: true, + References: []string{ + "sha256:264f55a6ff9cec2f4742a9faacc033b29f65c04dd4480e71e23579d484288d61", + "sha256:16159bb881eb4ab7eb5d8afc5350b0feeed1e31c0a268e355e74f9ccbe885e0c", + }, + }, + "sha256:16159bb881eb4ab7eb5d8afc5350b0feeed1e31c0a268e355e74f9ccbe885e0c": &Referrer{ + Digest: "sha256:16159bb881eb4ab7eb5d8afc5350b0feeed1e31c0a268e355e74f9ccbe885e0c", + ArtifactType: "SBOM_CYCLONEDX_JSON", + Downloadable: true, + }, + "sha256:264f55a6ff9cec2f4742a9faacc033b29f65c04dd4480e71e23579d484288d61": &Referrer{ + Digest: "sha256:264f55a6ff9cec2f4742a9faacc033b29f65c04dd4480e71e23579d484288d61", + ArtifactType: "CONTAINER_IMAGE", + }, + }, + }, + { + name: "basic", + inputPath: "testdata/attestations/with-string.json", + want: ReferrerMap{ + // the git commit a subject in the attestation + "sha1:58442b61a6564df94857ff69ad7c340c55703e20": &Referrer{ + Digest: "sha1:58442b61a6564df94857ff69ad7c340c55703e20", + ArtifactType: "GIT_HEAD_COMMIT", + References: []string{ + "sha256:507dddb505ceb53fb32cde31f9935c9a3ebc7b7d82f36101de638b1ab9367344", + }, + }, + "sha256:507dddb505ceb53fb32cde31f9935c9a3ebc7b7d82f36101de638b1ab9367344": &Referrer{ + Digest: "sha256:507dddb505ceb53fb32cde31f9935c9a3ebc7b7d82f36101de638b1ab9367344", + ArtifactType: "ATTESTATION", + References: []string{ + "sha1:58442b61a6564df94857ff69ad7c340c55703e20", + }, + Downloadable: true, + }, + }, + }, + { + name: "with git subject", + inputPath: "testdata/attestations/with-git-subject.json", + want: ReferrerMap{ + "sha256:fbd9335f55d83d8aaf9ab1a539b0f2a87b444e8c54f34c9a1ca9d7df15605db4": &Referrer{ + Digest: "sha256:fbd9335f55d83d8aaf9ab1a539b0f2a87b444e8c54f34c9a1ca9d7df15605db4", + ArtifactType: "CONTAINER_IMAGE", + // the container image is a subject in the attestation + References: []string{ + "sha256:ad704d286bcad6e155e71c33d48247931231338396acbcd9769087530085b2a2", + }, + }, + "sha1:78ac366c9e8a300d51808d581422ca61f7b5b721": &Referrer{ + Digest: "sha1:78ac366c9e8a300d51808d581422ca61f7b5b721", + ArtifactType: "GIT_HEAD_COMMIT", + // the git commit a subject in the attestation + References: []string{ + "sha256:ad704d286bcad6e155e71c33d48247931231338396acbcd9769087530085b2a2", + }, + }, + "sha256:385c4188b9c080499413f2e0fa0b3951ed107b5f0cb35c2f2b1f07a7be9a7512": &Referrer{ + Digest: "sha256:385c4188b9c080499413f2e0fa0b3951ed107b5f0cb35c2f2b1f07a7be9a7512", + ArtifactType: "ARTIFACT", + Downloadable: true, + }, + "sha256:c4a63494f9289dd9fd44f841efb4f5b52765c2de6332f2d86e5f6c0340b40a95": &Referrer{ + Digest: "sha256:c4a63494f9289dd9fd44f841efb4f5b52765c2de6332f2d86e5f6c0340b40a95", + ArtifactType: "SARIF", + Downloadable: true, + }, + "sha256:16159bb881eb4ab7eb5d8afc5350b0feeed1e31c0a268e355e74f9ccbe885e0c": &Referrer{ + Digest: "sha256:16159bb881eb4ab7eb5d8afc5350b0feeed1e31c0a268e355e74f9ccbe885e0c", + ArtifactType: "SBOM_CYCLONEDX_JSON", + Downloadable: true, + }, + "sha256:b4bd86d5855f94bcac0a92d3100ae7b85d050bd2e5fb9037a200e5f5f0b073a2": &Referrer{ + Digest: "sha256:b4bd86d5855f94bcac0a92d3100ae7b85d050bd2e5fb9037a200e5f5f0b073a2", + ArtifactType: "OPENVEX", + Downloadable: true, + }, + "sha256:ad704d286bcad6e155e71c33d48247931231338396acbcd9769087530085b2a2": &Referrer{ + Digest: "sha256:ad704d286bcad6e155e71c33d48247931231338396acbcd9769087530085b2a2", + ArtifactType: "ATTESTATION", + Downloadable: true, + References: []string{ + // container image + "sha256:fbd9335f55d83d8aaf9ab1a539b0f2a87b444e8c54f34c9a1ca9d7df15605db4", + // artifact + "sha256:385c4188b9c080499413f2e0fa0b3951ed107b5f0cb35c2f2b1f07a7be9a7512", + // sarif + "sha256:c4a63494f9289dd9fd44f841efb4f5b52765c2de6332f2d86e5f6c0340b40a95", + // sbom + "sha256:16159bb881eb4ab7eb5d8afc5350b0feeed1e31c0a268e355e74f9ccbe885e0c", + // openvex + "sha256:b4bd86d5855f94bcac0a92d3100ae7b85d050bd2e5fb9037a200e5f5f0b073a2", + // git head commit + "sha1:78ac366c9e8a300d51808d581422ca61f7b5b721", + }, + }, + }, + }, + } + + for _, tc := range testCases { + s.T().Run(tc.name, func(t *testing.T) { + // Load attestation + attJSON, err := os.ReadFile(tc.inputPath) + require.NoError(s.T(), err) + var envelope *dsse.Envelope + require.NoError(s.T(), json.Unmarshal(attJSON, &envelope)) + + got, err := extractReferrers(envelope) + if tc.expectErr { + s.Error(err) + return + } + + require.NoError(s.T(), err) + assert.Equal(s.T(), tc.want, got) + }) + } +} + +type referrerTestSuite struct { + suite.Suite +} + +func TestReferrer(t *testing.T) { + suite.Run(t, new(referrerTestSuite)) +} diff --git a/app/controlplane/internal/biz/testdata/attestations/same-digest-than-git-subject.json b/app/controlplane/internal/biz/testdata/attestations/same-digest-than-git-subject.json new file mode 100644 index 000000000..2a366eaa1 --- /dev/null +++ b/app/controlplane/internal/biz/testdata/attestations/same-digest-than-git-subject.json @@ -0,0 +1,10 @@ +{ + "payloadType": "application/vnd.in-toto+json", + "payload": "ewogICJ0eXBlIjogImh0dHBzOi8vaW4tdG90by5pby9TdGF0ZW1lbnQvdjEiLAogICJzdWJqZWN0IjogWwogICAgewogICAgICAibmFtZSI6ICJjaGFpbmxvb3Aud29ya2Zsb3cudGVzdC1uZXctdHlwZXMiLAogICAgICAiZGlnZXN0IjogewogICAgICAgICJzaGEyNTYiOiAiYmRjOGQzMTAzMzM5YTMyZTQ4MDM1ODQ4NzA4ODAxM2YxZWIzOTc0YTg2NmFlZGY1ZTQyNDAyZGUwMjcxYzk3YiIKICAgICAgfQogICAgfSwKICAgIHsKICAgICAgIm5hbWUiOiAiaW5kZXguZG9ja2VyLmlvL2JpdG5hbWkvbmdpbngiLAogICAgICAiZGlnZXN0IjogewogICAgICAgICJzaGEyNTYiOiAiZmJkOTMzNWY1NWQ4M2Q4YWFmOWFiMWE1MzliMGYyYTg3YjQ0NGU4YzU0ZjM0YzlhMWNhOWQ3ZGYxNTYwNWRiNCIKICAgICAgfSwKICAgICAgImFubm90YXRpb25zIjogewogICAgICAgICJjaGFpbmxvb3AubWF0ZXJpYWwubmFtZSI6ICJpbWFnZSIsCiAgICAgICAgImNoYWlubG9vcC5tYXRlcmlhbC50eXBlIjogIkNPTlRBSU5FUl9JTUFHRSIKICAgICAgfQogICAgfQogIF0sCiAgInByZWRpY2F0ZV90eXBlIjogImNoYWlubG9vcC5kZXYvYXR0ZXN0YXRpb24vdjAuMiIsCiAgInByZWRpY2F0ZSI6IHsKICAgICJidWlsZFR5cGUiOiAiY2hhaW5sb29wLmRldi93b3JrZmxvd3J1bi92MC4xIiwKICAgICJidWlsZGVyIjogewogICAgICAiaWQiOiAiY2hhaW5sb29wLmRldi9jbGkvZGV2QHNoYTI1Njo1OWUxNGYxYTlkZTcwOWNkZDBlOTFjMzZiMzNlNTRmY2NhOTVmN2RiYTFkYzcxNjlhN2Y4MTk4NmUwMjEwOGU1IgogICAgfSwKICAgICJtYXRlcmlhbHMiOiBbCiAgICAgIHsKICAgICAgICAiYW5ub3RhdGlvbnMiOiB7CiAgICAgICAgICAiY2hhaW5sb29wLm1hdGVyaWFsLm5hbWUiOiAiaW1hZ2UiLAogICAgICAgICAgImNoYWlubG9vcC5tYXRlcmlhbC50eXBlIjogIkNPTlRBSU5FUl9JTUFHRSIKICAgICAgICB9LAogICAgICAgICJkaWdlc3QiOiB7CiAgICAgICAgICAic2hhMjU2IjogImZiZDkzMzVmNTVkODNkOGFhZjlhYjFhNTM5YjBmMmE4N2I0NDRlOGM1NGYzNGM5YTFjYTlkN2RmMTU2MDVkYjQiCiAgICAgICAgfSwKICAgICAgICAibmFtZSI6ICJpbmRleC5kb2NrZXIuaW8vYml0bmFtaS9uZ2lueCIKICAgICAgfSwKICAgICAgewogICAgICAgICJhbm5vdGF0aW9ucyI6IHsKICAgICAgICAgICJjaGFpbmxvb3AubWF0ZXJpYWwuY2FzIjogdHJ1ZSwKICAgICAgICAgICJjaGFpbmxvb3AubWF0ZXJpYWwubmFtZSI6ICJzYXJpZiIsCiAgICAgICAgICAiY2hhaW5sb29wLm1hdGVyaWFsLnR5cGUiOiAiQVJUSUZBQ1QiCiAgICAgICAgfSwKICAgICAgICAiZGlnZXN0IjogewogICAgICAgICAgInNoYTI1NiI6ICJjNGE2MzQ5NGY5Mjg5ZGQ5ZmQ0NGY4NDFlZmI0ZjViNTI3NjVjMmRlNjMzMmYyZDg2ZTVmNmMwMzQwYjQwYTk1IgogICAgICAgIH0sCiAgICAgICAgIm5hbWUiOiAiZHVwbGljYXRlZCwgaXQgZXhpc3RzIHNvbWV3aGVyZSBlbHNlIgogICAgICB9CiAgICBdLAogICAgIm1ldGFkYXRhIjogewogICAgICAiZmluaXNoZWRBdCI6ICIyMDIzLTEwLTIwVDEyOjU4OjEwLjU2MjA5Mzk2MloiLAogICAgICAiaW5pdGlhbGl6ZWRBdCI6ICIyMDIzLTEwLTIwVDEyOjU3OjEwLjU2MjA5Mzk2MloiLAogICAgICAibmFtZSI6ICJ0ZXN0LW5ldy10eXBlcyIsCiAgICAgICJwcm9qZWN0IjogInRlc3QiLAogICAgICAidGVhbSI6ICIiLAogICAgICAid29ya2Zsb3dJRCI6ICJhYjk4YmU3MC0wNDFiLTRhODItOTdmMy1kYWE1NmY2MTI2MmIiLAogICAgICAid29ya2Zsb3dSdW5JRCI6ICJmOTdhMDY4MC1lNjRiLTQ3OGEtOWVlYS1kZjg4NjRmYTI3ZjgiCiAgICB9LAogICAgInJ1bm5lclR5cGUiOiAiUlVOTkVSX1RZUEVfVU5TUEVDSUZJRUQiCiAgfQp9", + "signatures": [ + { + "keyid": "", + "sig": "MEQCIAFytdWto+Bi5Tht+7haXnjiHBfwLwgf6ks0mxeeSadEAiBoLKhy+UKsdDH3ukHJPuiHvWOGhEP19kZ9aipmaT4TlQ==" + } + ] +} \ No newline at end of file diff --git a/app/controlplane/internal/biz/testdata/attestations/with-duplicated-sha.json b/app/controlplane/internal/biz/testdata/attestations/with-duplicated-sha.json new file mode 100644 index 000000000..9b9cc2bfb --- /dev/null +++ b/app/controlplane/internal/biz/testdata/attestations/with-duplicated-sha.json @@ -0,0 +1,10 @@ +{ + "payloadType": "application/vnd.in-toto+json", + "payload": "ewogICJfdHlwZSI6ICJodHRwczovL2luLXRvdG8uaW8vU3RhdGVtZW50L3YwLjEiLAogICJwcmVkaWNhdGVUeXBlIjogImNoYWlubG9vcC5kZXYvYXR0ZXN0YXRpb24vdjAuMiIsCiAgInN1YmplY3QiOiBbCiAgICB7CiAgICAgICJuYW1lIjogImNoYWlubG9vcC5kZXYvd29ya2Zsb3cvb25seS1zYm9tIiwKICAgICAgImRpZ2VzdCI6IHsKICAgICAgICAic2hhMjU2IjogIjMwMzZmMmU1ZDcwOWEyMzgwOGVhYTA1NTcwZjE3MThhZmZmOTJkNGFkMzVlMjMyYzEzODczODM3MmFjOTNkZmIiCiAgICAgIH0KICAgIH0KICBdLAogICJwcmVkaWNhdGUiOiB7CiAgICAibWV0YWRhdGEiOiB7CiAgICAgICJuYW1lIjogIm9ubHktc2JvbSIsCiAgICAgICJwcm9qZWN0IjogImZvbyIsCiAgICAgICJ0ZWFtIjogIiIsCiAgICAgICJpbml0aWFsaXplZEF0IjogIjIwMjMtMDYtMjNUMTM6MDA6MjkuMTgzNjE4OTY2WiIsCiAgICAgICJmaW5pc2hlZEF0IjogIjIwMjMtMDYtMjNUMTU6MDA6NDMuNzA5OTcwNjcrMDI6MDAiLAogICAgICAid29ya2Zsb3dSdW5JRCI6ICJhMTdjYTIxNi0yOWI2LTQxMDctYjNmYy05MjU2ZjdhZjJmZTYiLAogICAgICAid29ya2Zsb3dJRCI6ICI1ZTM0YjM0Zi04ODJjLTQ4YjgtODRjMC00ZjIzOGUxNWE1ZmQiCiAgICB9LAogICAgImVudiI6IHsgIm93bmVyIjogImpvaG4tY0BjaGFpbmxvb3AuZGV2IiwgInByb2plY3QiOiAiY2hhdGdwdCIgfSwKICAgICJidWlsZGVyIjogewogICAgICAiaWQiOiAiY2hhaW5sb29wLmRldi9jbGkvZGV2QHNoYTI1NjphOGRkZDczODEzMDAyZjYyZDJkZTExYTQyMDU0YzA2ZmFlMGQwYTg4YjZjODkwNGU3ZGU0NDc1MTk4YzBiMGQzIgogICAgfSwKICAgICJidWlsZFR5cGUiOiAiY2hhaW5sb29wLmRldi93b3JrZmxvd3J1bi92MC4xIiwKICAgICJydW5uZXJUeXBlIjogIlJVTk5FUl9UWVBFX1VOU1BFQ0lGSUVEIiwKICAgICJhbm5vdGF0aW9ucyI6IHsKICAgICAgInRvcGxldmVsIjogInRydWUiLAogICAgICAiYnJhbmNoIjogInN0YWJsZSIKICAgIH0sCiAgICAibWF0ZXJpYWxzIjogWwogICAgICB7CiAgICAgICAgImRpZ2VzdCI6IHsKICAgICAgICAgICJzaGEyNTYiOiAiMjY0ZjU1YTZmZjljZWMyZjQ3NDJhOWZhYWNjMDMzYjI5ZjY1YzA0ZGQ0NDgwZTcxZTIzNTc5ZDQ4NDI4OGQ2MSIKICAgICAgICB9LAogICAgICAgICJuYW1lIjogImluZGV4LmRvY2tlci5pby9iaXRuYW1pL25naW54IiwKICAgICAgICAiYW5ub3RhdGlvbnMiOiB7CiAgICAgICAgICAiY2hhaW5sb29wLm1hdGVyaWFsLm5hbWUiOiAiaW1hZ2UiLAogICAgICAgICAgImNoYWlubG9vcC5tYXRlcmlhbC50eXBlIjogIkNPTlRBSU5FUl9JTUFHRSIKICAgICAgICB9CiAgICAgIH0sCiAgICAgIHsKICAgICAgICAiZGlnZXN0IjogewogICAgICAgICAgInNoYTI1NiI6ICIyNjRmNTVhNmZmOWNlYzJmNDc0MmE5ZmFhY2MwMzNiMjlmNjVjMDRkZDQ0ODBlNzFlMjM1NzlkNDg0Mjg4ZDYxIgogICAgICAgIH0sCiAgICAgICAgIm5hbWUiOiAic2JvbS13aXRoLXNhbWUtZGlnZXN0IiwKICAgICAgICAiYW5ub3RhdGlvbnMiOiB7CiAgICAgICAgICAiY2hhaW5sb29wLm1hdGVyaWFsLmNhcyI6IHRydWUsCiAgICAgICAgICAiY2hhaW5sb29wLm1hdGVyaWFsLm5hbWUiOiAic2t5bmV0LXNib20iLAogICAgICAgICAgImNoYWlubG9vcC5tYXRlcmlhbC50eXBlIjogIlNCT01fQ1lDTE9ORURYX0pTT04iLAogICAgICAgICAgImNvbXBvbmVudCI6ICJuZ2lueCIKICAgICAgICB9CiAgICAgIH0sCiAgICAgIHsKICAgICAgICAiZGlnZXN0IjogewogICAgICAgICAgInNoYTI1NiI6ICIxNjE1OWJiODgxZWI0YWI3ZWI1ZDhhZmM1MzUwYjBmZWVlZDFlMzFjMGEyNjhlMzU1ZTc0ZjljY2JlODg1ZTBjIgogICAgICAgIH0sCiAgICAgICAgIm5hbWUiOiAic2JvbS5jeWNsb25lZHguanNvbiIsCiAgICAgICAgImFubm90YXRpb25zIjogewogICAgICAgICAgImNoYWlubG9vcC5tYXRlcmlhbC5jYXMiOiB0cnVlLAogICAgICAgICAgImNoYWlubG9vcC5tYXRlcmlhbC5uYW1lIjogInNreW5ldDItc2JvbSIsCiAgICAgICAgICAiY2hhaW5sb29wLm1hdGVyaWFsLnR5cGUiOiAiU0JPTV9DWUNMT05FRFhfSlNPTiIKICAgICAgICB9CiAgICAgIH0KICAgIF0KICB9Cn0K", + "signatures": [ + { + "keyid": "", + "sig": "MEQCIAFytdWto+Bi5Tht+7haXnjiHBfwLwgf6ks0mxeeSadEAiBoLKhy+UKsdDH3ukHJPuiHvWOGhEP19kZ9aipmaT4TlQ==" + } + ] +} \ No newline at end of file diff --git a/app/controlplane/internal/biz/testdata/attestations/with-git-subject.json b/app/controlplane/internal/biz/testdata/attestations/with-git-subject.json new file mode 100644 index 000000000..ba7cc4c1f --- /dev/null +++ b/app/controlplane/internal/biz/testdata/attestations/with-git-subject.json @@ -0,0 +1,10 @@ +{ + "payloadType": "application/vnd.in-toto+json", + "payload": "ewogICJ0eXBlIjogImh0dHBzOi8vaW4tdG90by5pby9TdGF0ZW1lbnQvdjEiLAogICJzdWJqZWN0IjogWwogICAgewogICAgICAibmFtZSI6ICJjaGFpbmxvb3Aud29ya2Zsb3cudGVzdC1uZXctdHlwZXMiLAogICAgICAiZGlnZXN0IjogewogICAgICAgICJzaGEyNTYiOiAiYmRjOGQzMTAzMzM5YTMyZTQ4MDM1ODQ4NzA4ODAxM2YxZWIzOTc0YTg2NmFlZGY1ZTQyNDAyZGUwMjcxYzk3YiIKICAgICAgfQogICAgfSwKICAgIHsKICAgICAgIm5hbWUiOiAiZ2l0LmhlYWQiLAogICAgICAiZGlnZXN0IjogewogICAgICAgICJzaGExIjogIjc4YWMzNjZjOWU4YTMwMGQ1MTgwOGQ1ODE0MjJjYTYxZjdiNWI3MjEiCiAgICAgIH0sCiAgICAgICJhbm5vdGF0aW9ucyI6IHsKICAgICAgICAiYXV0aG9yLmVtYWlsIjogImpvaG5AY3liZXJkeW5lLmNvbSIsCiAgICAgICAgImF1dGhvci5uYW1lIjogIkpvaG4gQ29ubm9yIiwKICAgICAgICAiZGF0ZSI6ICIyMDIzLTEwLTI2VDE3OjA5OjIwWiIsCiAgICAgICAgIm1lc3NhZ2UiOiAiY2hhdEdQVCB3cmFwcGVyIiwKICAgICAgICAicmVtb3RlcyI6IFsKICAgICAgICAgICB7CiAgICAgICAgICAgICAibmFtZSI6ICJ1cHN0cmVhbSIsCiAgICAgICAgICAgICAidXJsIjogImdpdEBjeWJlcmR5bmUuaW50ZXJuYWw6c2t5bmV0LmdpdCIKICAgICAgICAgICB9CiAgICAgICAgIF0KICAgICAgfQogICAgfSwKICAgIHsKICAgICAgIm5hbWUiOiAiaW5kZXguZG9ja2VyLmlvL2JpdG5hbWkvbmdpbngiLAogICAgICAiZGlnZXN0IjogewogICAgICAgICJzaGEyNTYiOiAiZmJkOTMzNWY1NWQ4M2Q4YWFmOWFiMWE1MzliMGYyYTg3YjQ0NGU4YzU0ZjM0YzlhMWNhOWQ3ZGYxNTYwNWRiNCIKICAgICAgfSwKICAgICAgImFubm90YXRpb25zIjogewogICAgICAgICJjaGFpbmxvb3AubWF0ZXJpYWwubmFtZSI6ICJpbWFnZSIsCiAgICAgICAgImNoYWlubG9vcC5tYXRlcmlhbC50eXBlIjogIkNPTlRBSU5FUl9JTUFHRSIKICAgICAgfQogICAgfQogIF0sCiAgInByZWRpY2F0ZV90eXBlIjogImNoYWlubG9vcC5kZXYvYXR0ZXN0YXRpb24vdjAuMiIsCiAgInByZWRpY2F0ZSI6IHsKICAgICJidWlsZFR5cGUiOiAiY2hhaW5sb29wLmRldi93b3JrZmxvd3J1bi92MC4xIiwKICAgICJidWlsZGVyIjogewogICAgICAiaWQiOiAiY2hhaW5sb29wLmRldi9jbGkvZGV2QHNoYTI1Njo1OWUxNGYxYTlkZTcwOWNkZDBlOTFjMzZiMzNlNTRmY2NhOTVmN2RiYTFkYzcxNjlhN2Y4MTk4NmUwMjEwOGU1IgogICAgfSwKICAgICJtYXRlcmlhbHMiOiBbCiAgICAgIHsKICAgICAgICAiYW5ub3RhdGlvbnMiOiB7CiAgICAgICAgICAiY2hhaW5sb29wLm1hdGVyaWFsLm5hbWUiOiAiaW1hZ2UiLAogICAgICAgICAgImNoYWlubG9vcC5tYXRlcmlhbC50eXBlIjogIkNPTlRBSU5FUl9JTUFHRSIKICAgICAgICB9LAogICAgICAgICJkaWdlc3QiOiB7CiAgICAgICAgICAic2hhMjU2IjogImZiZDkzMzVmNTVkODNkOGFhZjlhYjFhNTM5YjBmMmE4N2I0NDRlOGM1NGYzNGM5YTFjYTlkN2RmMTU2MDVkYjQiCiAgICAgICAgfSwKICAgICAgICAibmFtZSI6ICJpbmRleC5kb2NrZXIuaW8vYml0bmFtaS9uZ2lueCIKICAgICAgfSwKICAgICAgewogICAgICAgICJhbm5vdGF0aW9ucyI6IHsKICAgICAgICAgICJjaGFpbmxvb3AubWF0ZXJpYWwuY2FzIjogdHJ1ZSwKICAgICAgICAgICJjaGFpbmxvb3AubWF0ZXJpYWwubmFtZSI6ICJyb290ZnMiLAogICAgICAgICAgImNoYWlubG9vcC5tYXRlcmlhbC50eXBlIjogIkFSVElGQUNUIgogICAgICAgIH0sCiAgICAgICAgImRpZ2VzdCI6IHsKICAgICAgICAgICJzaGEyNTYiOiAiMzg1YzQxODhiOWMwODA0OTk0MTNmMmUwZmEwYjM5NTFlZDEwN2I1ZjBjYjM1YzJmMmIxZjA3YTdiZTlhNzUxMiIKICAgICAgICB9LAogICAgICAgICJuYW1lIjogImRvd25sb2FkLmdpZiIKICAgICAgfSwKICAgICAgewogICAgICAgICJhbm5vdGF0aW9ucyI6IHsKICAgICAgICAgICJjaGFpbmxvb3AubWF0ZXJpYWwuY2FzIjogdHJ1ZSwKICAgICAgICAgICJjaGFpbmxvb3AubWF0ZXJpYWwubmFtZSI6ICJzYXJpZiIsCiAgICAgICAgICAiY2hhaW5sb29wLm1hdGVyaWFsLnR5cGUiOiAiU0FSSUYiCiAgICAgICAgfSwKICAgICAgICAiZGlnZXN0IjogewogICAgICAgICAgInNoYTI1NiI6ICJjNGE2MzQ5NGY5Mjg5ZGQ5ZmQ0NGY4NDFlZmI0ZjViNTI3NjVjMmRlNjMzMmYyZDg2ZTVmNmMwMzQwYjQwYTk1IgogICAgICAgIH0sCiAgICAgICAgIm5hbWUiOiAicmVwb3J0LnNhcmlmIgogICAgICB9LAogICAgICB7CiAgICAgICAgImFubm90YXRpb25zIjogewogICAgICAgICAgImNoYWlubG9vcC5tYXRlcmlhbC5jYXMiOiB0cnVlLAogICAgICAgICAgImNoYWlubG9vcC5tYXRlcmlhbC5uYW1lIjogInNib20iLAogICAgICAgICAgImNoYWlubG9vcC5tYXRlcmlhbC50eXBlIjogIlNCT01fQ1lDTE9ORURYX0pTT04iCiAgICAgICAgfSwKICAgICAgICAiZGlnZXN0IjogewogICAgICAgICAgInNoYTI1NiI6ICIxNjE1OWJiODgxZWI0YWI3ZWI1ZDhhZmM1MzUwYjBmZWVlZDFlMzFjMGEyNjhlMzU1ZTc0ZjljY2JlODg1ZTBjIgogICAgICAgIH0sCiAgICAgICAgIm5hbWUiOiAic2JvbS5jeWNsb25lZHguanNvbiIKICAgICAgfSwKICAgICAgewogICAgICAgICJhbm5vdGF0aW9ucyI6IHsKICAgICAgICAgICJjaGFpbmxvb3AubWF0ZXJpYWwuY2FzIjogdHJ1ZSwKICAgICAgICAgICJjaGFpbmxvb3AubWF0ZXJpYWwubmFtZSI6ICJ2ZXgiLAogICAgICAgICAgImNoYWlubG9vcC5tYXRlcmlhbC50eXBlIjogIk9QRU5WRVgiCiAgICAgICAgfSwKICAgICAgICAiZGlnZXN0IjogewogICAgICAgICAgInNoYTI1NiI6ICJiNGJkODZkNTg1NWY5NGJjYWMwYTkyZDMxMDBhZTdiODVkMDUwYmQyZTVmYjkwMzdhMjAwZTVmNWYwYjA3M2EyIgogICAgICAgIH0sCiAgICAgICAgIm5hbWUiOiAib3BlbnZleF92MC4yLjAuanNvbiIKICAgICAgfQogICAgXSwKICAgICJtZXRhZGF0YSI6IHsKICAgICAgImZpbmlzaGVkQXQiOiAiMjAyMy0xMC0yMFQxMjo1ODoxMC41NjIwOTM5NjJaIiwKICAgICAgImluaXRpYWxpemVkQXQiOiAiMjAyMy0xMC0yMFQxMjo1NzoxMC41NjIwOTM5NjJaIiwKICAgICAgIm5hbWUiOiAidGVzdC1uZXctdHlwZXMiLAogICAgICAicHJvamVjdCI6ICJ0ZXN0IiwKICAgICAgInRlYW0iOiAiIiwKICAgICAgIndvcmtmbG93SUQiOiAiYWI5OGJlNzAtMDQxYi00YTgyLTk3ZjMtZGFhNTZmNjEyNjJiIiwKICAgICAgIndvcmtmbG93UnVuSUQiOiAiZjk3YTA2ODAtZTY0Yi00NzhhLTllZWEtZGY4ODY0ZmEyN2Y4IgogICAgfSwKICAgICJydW5uZXJUeXBlIjogIlJVTk5FUl9UWVBFX1VOU1BFQ0lGSUVEIgogIH0KfQ==", + "signatures": [ + { + "keyid": "", + "sig": "MEQCIAFytdWto+Bi5Tht+7haXnjiHBfwLwgf6ks0mxeeSadEAiBoLKhy+UKsdDH3ukHJPuiHvWOGhEP19kZ9aipmaT4TlQ==" + } + ] +} \ No newline at end of file diff --git a/app/controlplane/internal/biz/testdata/attestations/with-string.json b/app/controlplane/internal/biz/testdata/attestations/with-string.json new file mode 100644 index 000000000..31548b9ce --- /dev/null +++ b/app/controlplane/internal/biz/testdata/attestations/with-string.json @@ -0,0 +1,10 @@ +{ + "payloadType": "application/vnd.in-toto+json", + "payload": "eyJfdHlwZSI6Imh0dHBzOi8vaW4tdG90by5pby9TdGF0ZW1lbnQvdjEiLCJzdWJqZWN0IjpbeyJuYW1lIjoiY2hhaW5sb29wLndvcmtmbG93LnRlc3QiLCJkaWdlc3QiOnsic2hhMjU2IjoiNDEzODg5NDA4MjgwOTQ0ZTQxMDNjYzMwZGQ2OGI5NmMwNGQ2MzE4MTIwNDIwMDNkMjM1Njg1MzNmN2RmZDA3YyJ9fSx7Im5hbWUiOiJnaXQuaGVhZCIsImRpZ2VzdCI6eyJzaGExIjoiNTg0NDJiNjFhNjU2NGRmOTQ4NTdmZjY5YWQ3YzM0MGM1NTcwM2UyMCJ9LCJhbm5vdGF0aW9ucyI6eyJhdXRob3IuZW1haWwiOiJtaWd1ZWxAY2hhaW5sb29wLmRldiIsImF1dGhvci5uYW1lIjoiTWlndWVsIE1hcnRpbmV6IFRyaXZpbm8iLCJkYXRlIjoiMjAyMy0xMS0wNlQxMjoyMzo1OVoiLCJtZXNzYWdlIjoiYWRkIHJlZmVycmVyXG5cblNpZ25lZC1vZmYtYnk6IE1pZ3VlbCBNYXJ0aW5leiBUcml2aW5vIDxtaWd1ZWxAY2hhaW5sb29wLmRldj5cbiIsInJlbW90ZXMiOlt7Im5hbWUiOiJvcmlnaW4iLCJ1cmwiOiJnaXRAZ2l0aHViLmNvbTptaWdtYXJ0cmkvY2hhaW5sb29wLmdpdCJ9LHsibmFtZSI6InVwc3RyZWFtIiwidXJsIjoiZ2l0QGdpdGh1Yi5jb206Y2hhaW5sb29wLWRldi9jaGFpbmxvb3AuZ2l0In1dfX1dLCJwcmVkaWNhdGVUeXBlIjoiY2hhaW5sb29wLmRldi9hdHRlc3RhdGlvbi92MC4yIiwicHJlZGljYXRlIjp7ImFubm90YXRpb25zIjp7InZlcnNpb24iOiJvc3MifSwiYnVpbGRUeXBlIjoiY2hhaW5sb29wLmRldi93b3JrZmxvd3J1bi92MC4xIiwiYnVpbGRlciI6eyJpZCI6ImNoYWlubG9vcC5kZXYvY2xpL2RldkBzaGEyNTY6MjM3OWRlNDMwNzQ2YmQ3YjhlODliNDA2YzU2Y2M5YzY3Yzg1ODM0MmVmOWZjOGZhNWIwOWZhMjgxY2M3OWM5NyJ9LCJtYXRlcmlhbHMiOlt7ImFubm90YXRpb25zIjp7ImNoYWlubG9vcC5tYXRlcmlhbC5uYW1lIjoiYnVpbGQtcmVmIiwiY2hhaW5sb29wLm1hdGVyaWFsLnR5cGUiOiJTVFJJTkcifSwiY29udGVudCI6IlptOXZiMjlpWVhJPSJ9XSwibWV0YWRhdGEiOnsiZmluaXNoZWRBdCI6IjIwMjMtMTEtMDZUMTQ6NTk6MTkuOTU5NzE1NDQ0WiIsImluaXRpYWxpemVkQXQiOiIyMDIzLTExLTA2VDE0OjU4OjQ3Ljc2MDA5NzkwMVoiLCJuYW1lIjoidGVzdCIsInByb2plY3QiOiJiYXIiLCJ0ZWFtIjoiIiwid29ya2Zsb3dJRCI6IjI4ZmM1MDZmLWVjOGQtNDE5My1hNzlmLWViY2JmZWM0Njg3MSIsIndvcmtmbG93UnVuSUQiOiJiYzVhYTVkYS05ODQxLTQxZjQtYTM4Yi04NzViMzE4M2JkMDMifSwicnVubmVyVHlwZSI6IlJVTk5FUl9UWVBFX1VOU1BFQ0lGSUVEIn19", + "signatures": [ + { + "keyid": "", + "sig": "MEYCIQClopFpuZZvVwpzXP12ZE0NpSpD0DPdTbdwnEjqOJAd8AIhAOYL0D8PHjsckn4RzjwWfvMrvOhKlsMNaAfAWldvPNHK" + } + ] +} diff --git a/app/controlplane/internal/biz/testhelpers/database.go b/app/controlplane/internal/biz/testhelpers/database.go index 0ea1e40a9..f649677bd 100644 --- a/app/controlplane/internal/biz/testhelpers/database.go +++ b/app/controlplane/internal/biz/testhelpers/database.go @@ -65,6 +65,7 @@ type TestingUseCases struct { RegisteredIntegrations sdk.AvailablePlugins CASMapping *biz.CASMappingUseCase OrgInvitation *biz.OrgInvitationUseCase + Referrer *biz.ReferrerUseCase } type newTestingOpts struct { diff --git a/app/controlplane/internal/biz/testhelpers/wire_gen.go b/app/controlplane/internal/biz/testhelpers/wire_gen.go index f18fbb176..1a07f979b 100644 --- a/app/controlplane/internal/biz/testhelpers/wire_gen.go +++ b/app/controlplane/internal/biz/testhelpers/wire_gen.go @@ -75,6 +75,8 @@ func WireTestData(testDatabase *TestDatabase, t *testing.T, logger log.Logger, r cleanup() return nil, nil, err } + referrerRepo := data.NewReferrerRepo(dataData, logger) + referrerUseCase := biz.NewReferrerUseCase(referrerRepo, organizationRepo, logger) testingUseCases := &TestingUseCases{ DB: testDatabase, Data: dataData, @@ -91,6 +93,7 @@ func WireTestData(testDatabase *TestDatabase, t *testing.T, logger log.Logger, r RegisteredIntegrations: availablePlugins, CASMapping: casMappingUseCase, OrgInvitation: orgInvitationUseCase, + Referrer: referrerUseCase, } return testingUseCases, func() { cleanup() diff --git a/app/controlplane/internal/data/data.go b/app/controlplane/internal/data/data.go index ab3bd3d50..e76cd6eb5 100644 --- a/app/controlplane/internal/data/data.go +++ b/app/controlplane/internal/data/data.go @@ -53,6 +53,7 @@ var ProviderSet = wire.NewSet( NewCASMappingRepo, NewMembershipRepo, NewOrgInvitation, + NewReferrerRepo, ) // Data . diff --git a/app/controlplane/internal/data/ent/client.go b/app/controlplane/internal/data/ent/client.go index dcf307000..18a140c60 100644 --- a/app/controlplane/internal/data/ent/client.go +++ b/app/controlplane/internal/data/ent/client.go @@ -22,6 +22,7 @@ import ( "github.com/chainloop-dev/chainloop/app/controlplane/internal/data/ent/membership" "github.com/chainloop-dev/chainloop/app/controlplane/internal/data/ent/organization" "github.com/chainloop-dev/chainloop/app/controlplane/internal/data/ent/orginvitation" + "github.com/chainloop-dev/chainloop/app/controlplane/internal/data/ent/referrer" "github.com/chainloop-dev/chainloop/app/controlplane/internal/data/ent/robotaccount" "github.com/chainloop-dev/chainloop/app/controlplane/internal/data/ent/user" "github.com/chainloop-dev/chainloop/app/controlplane/internal/data/ent/workflow" @@ -49,6 +50,8 @@ type Client struct { OrgInvitation *OrgInvitationClient // Organization is the client for interacting with the Organization builders. Organization *OrganizationClient + // Referrer is the client for interacting with the Referrer builders. + Referrer *ReferrerClient // RobotAccount is the client for interacting with the RobotAccount builders. RobotAccount *RobotAccountClient // User is the client for interacting with the User builders. @@ -81,6 +84,7 @@ func (c *Client) init() { c.Membership = NewMembershipClient(c.config) c.OrgInvitation = NewOrgInvitationClient(c.config) c.Organization = NewOrganizationClient(c.config) + c.Referrer = NewReferrerClient(c.config) c.RobotAccount = NewRobotAccountClient(c.config) c.User = NewUserClient(c.config) c.Workflow = NewWorkflowClient(c.config) @@ -176,6 +180,7 @@ func (c *Client) Tx(ctx context.Context) (*Tx, error) { Membership: NewMembershipClient(cfg), OrgInvitation: NewOrgInvitationClient(cfg), Organization: NewOrganizationClient(cfg), + Referrer: NewReferrerClient(cfg), RobotAccount: NewRobotAccountClient(cfg), User: NewUserClient(cfg), Workflow: NewWorkflowClient(cfg), @@ -208,6 +213,7 @@ func (c *Client) BeginTx(ctx context.Context, opts *sql.TxOptions) (*Tx, error) Membership: NewMembershipClient(cfg), OrgInvitation: NewOrgInvitationClient(cfg), Organization: NewOrganizationClient(cfg), + Referrer: NewReferrerClient(cfg), RobotAccount: NewRobotAccountClient(cfg), User: NewUserClient(cfg), Workflow: NewWorkflowClient(cfg), @@ -244,8 +250,9 @@ func (c *Client) Close() error { func (c *Client) Use(hooks ...Hook) { for _, n := range []interface{ Use(...Hook) }{ c.CASBackend, c.CASMapping, c.Integration, c.IntegrationAttachment, - c.Membership, c.OrgInvitation, c.Organization, c.RobotAccount, c.User, - c.Workflow, c.WorkflowContract, c.WorkflowContractVersion, c.WorkflowRun, + c.Membership, c.OrgInvitation, c.Organization, c.Referrer, c.RobotAccount, + c.User, c.Workflow, c.WorkflowContract, c.WorkflowContractVersion, + c.WorkflowRun, } { n.Use(hooks...) } @@ -256,8 +263,9 @@ func (c *Client) Use(hooks ...Hook) { func (c *Client) Intercept(interceptors ...Interceptor) { for _, n := range []interface{ Intercept(...Interceptor) }{ c.CASBackend, c.CASMapping, c.Integration, c.IntegrationAttachment, - c.Membership, c.OrgInvitation, c.Organization, c.RobotAccount, c.User, - c.Workflow, c.WorkflowContract, c.WorkflowContractVersion, c.WorkflowRun, + c.Membership, c.OrgInvitation, c.Organization, c.Referrer, c.RobotAccount, + c.User, c.Workflow, c.WorkflowContract, c.WorkflowContractVersion, + c.WorkflowRun, } { n.Intercept(interceptors...) } @@ -280,6 +288,8 @@ func (c *Client) Mutate(ctx context.Context, m Mutation) (Value, error) { return c.OrgInvitation.mutate(ctx, m) case *OrganizationMutation: return c.Organization.mutate(ctx, m) + case *ReferrerMutation: + return c.Referrer.mutate(ctx, m) case *RobotAccountMutation: return c.RobotAccount.mutate(ctx, m) case *UserMutation: @@ -1386,6 +1396,22 @@ func (c *OrganizationClient) QueryIntegrations(o *Organization) *IntegrationQuer return query } +// QueryReferrers queries the referrers edge of a Organization. +func (c *OrganizationClient) QueryReferrers(o *Organization) *ReferrerQuery { + query := (&ReferrerClient{config: c.config}).Query() + query.path = func(context.Context) (fromV *sql.Selector, _ error) { + id := o.ID + step := sqlgraph.NewStep( + sqlgraph.From(organization.Table, organization.FieldID, id), + sqlgraph.To(referrer.Table, referrer.FieldID), + sqlgraph.Edge(sqlgraph.M2M, true, organization.ReferrersTable, organization.ReferrersPrimaryKey...), + ) + fromV = sqlgraph.Neighbors(o.driver.Dialect(), step) + return fromV, nil + } + return query +} + // Hooks returns the client hooks. func (c *OrganizationClient) Hooks() []Hook { return c.hooks.Organization @@ -1411,6 +1437,172 @@ func (c *OrganizationClient) mutate(ctx context.Context, m *OrganizationMutation } } +// ReferrerClient is a client for the Referrer schema. +type ReferrerClient struct { + config +} + +// NewReferrerClient returns a client for the Referrer from the given config. +func NewReferrerClient(c config) *ReferrerClient { + return &ReferrerClient{config: c} +} + +// Use adds a list of mutation hooks to the hooks stack. +// A call to `Use(f, g, h)` equals to `referrer.Hooks(f(g(h())))`. +func (c *ReferrerClient) Use(hooks ...Hook) { + c.hooks.Referrer = append(c.hooks.Referrer, hooks...) +} + +// Intercept adds a list of query interceptors to the interceptors stack. +// A call to `Intercept(f, g, h)` equals to `referrer.Intercept(f(g(h())))`. +func (c *ReferrerClient) Intercept(interceptors ...Interceptor) { + c.inters.Referrer = append(c.inters.Referrer, interceptors...) +} + +// Create returns a builder for creating a Referrer entity. +func (c *ReferrerClient) Create() *ReferrerCreate { + mutation := newReferrerMutation(c.config, OpCreate) + return &ReferrerCreate{config: c.config, hooks: c.Hooks(), mutation: mutation} +} + +// CreateBulk returns a builder for creating a bulk of Referrer entities. +func (c *ReferrerClient) CreateBulk(builders ...*ReferrerCreate) *ReferrerCreateBulk { + return &ReferrerCreateBulk{config: c.config, builders: builders} +} + +// Update returns an update builder for Referrer. +func (c *ReferrerClient) Update() *ReferrerUpdate { + mutation := newReferrerMutation(c.config, OpUpdate) + return &ReferrerUpdate{config: c.config, hooks: c.Hooks(), mutation: mutation} +} + +// UpdateOne returns an update builder for the given entity. +func (c *ReferrerClient) UpdateOne(r *Referrer) *ReferrerUpdateOne { + mutation := newReferrerMutation(c.config, OpUpdateOne, withReferrer(r)) + return &ReferrerUpdateOne{config: c.config, hooks: c.Hooks(), mutation: mutation} +} + +// UpdateOneID returns an update builder for the given id. +func (c *ReferrerClient) UpdateOneID(id uuid.UUID) *ReferrerUpdateOne { + mutation := newReferrerMutation(c.config, OpUpdateOne, withReferrerID(id)) + return &ReferrerUpdateOne{config: c.config, hooks: c.Hooks(), mutation: mutation} +} + +// Delete returns a delete builder for Referrer. +func (c *ReferrerClient) Delete() *ReferrerDelete { + mutation := newReferrerMutation(c.config, OpDelete) + return &ReferrerDelete{config: c.config, hooks: c.Hooks(), mutation: mutation} +} + +// DeleteOne returns a builder for deleting the given entity. +func (c *ReferrerClient) DeleteOne(r *Referrer) *ReferrerDeleteOne { + return c.DeleteOneID(r.ID) +} + +// DeleteOneID returns a builder for deleting the given entity by its id. +func (c *ReferrerClient) DeleteOneID(id uuid.UUID) *ReferrerDeleteOne { + builder := c.Delete().Where(referrer.ID(id)) + builder.mutation.id = &id + builder.mutation.op = OpDeleteOne + return &ReferrerDeleteOne{builder} +} + +// Query returns a query builder for Referrer. +func (c *ReferrerClient) Query() *ReferrerQuery { + return &ReferrerQuery{ + config: c.config, + ctx: &QueryContext{Type: TypeReferrer}, + inters: c.Interceptors(), + } +} + +// Get returns a Referrer entity by its id. +func (c *ReferrerClient) Get(ctx context.Context, id uuid.UUID) (*Referrer, error) { + return c.Query().Where(referrer.ID(id)).Only(ctx) +} + +// GetX is like Get, but panics if an error occurs. +func (c *ReferrerClient) GetX(ctx context.Context, id uuid.UUID) *Referrer { + obj, err := c.Get(ctx, id) + if err != nil { + panic(err) + } + return obj +} + +// QueryReferredBy queries the referred_by edge of a Referrer. +func (c *ReferrerClient) QueryReferredBy(r *Referrer) *ReferrerQuery { + query := (&ReferrerClient{config: c.config}).Query() + query.path = func(context.Context) (fromV *sql.Selector, _ error) { + id := r.ID + step := sqlgraph.NewStep( + sqlgraph.From(referrer.Table, referrer.FieldID, id), + sqlgraph.To(referrer.Table, referrer.FieldID), + sqlgraph.Edge(sqlgraph.M2M, true, referrer.ReferredByTable, referrer.ReferredByPrimaryKey...), + ) + fromV = sqlgraph.Neighbors(r.driver.Dialect(), step) + return fromV, nil + } + return query +} + +// QueryReferences queries the references edge of a Referrer. +func (c *ReferrerClient) QueryReferences(r *Referrer) *ReferrerQuery { + query := (&ReferrerClient{config: c.config}).Query() + query.path = func(context.Context) (fromV *sql.Selector, _ error) { + id := r.ID + step := sqlgraph.NewStep( + sqlgraph.From(referrer.Table, referrer.FieldID, id), + sqlgraph.To(referrer.Table, referrer.FieldID), + sqlgraph.Edge(sqlgraph.M2M, false, referrer.ReferencesTable, referrer.ReferencesPrimaryKey...), + ) + fromV = sqlgraph.Neighbors(r.driver.Dialect(), step) + return fromV, nil + } + return query +} + +// QueryOrganizations queries the organizations edge of a Referrer. +func (c *ReferrerClient) QueryOrganizations(r *Referrer) *OrganizationQuery { + query := (&OrganizationClient{config: c.config}).Query() + query.path = func(context.Context) (fromV *sql.Selector, _ error) { + id := r.ID + step := sqlgraph.NewStep( + sqlgraph.From(referrer.Table, referrer.FieldID, id), + sqlgraph.To(organization.Table, organization.FieldID), + sqlgraph.Edge(sqlgraph.M2M, false, referrer.OrganizationsTable, referrer.OrganizationsPrimaryKey...), + ) + fromV = sqlgraph.Neighbors(r.driver.Dialect(), step) + return fromV, nil + } + return query +} + +// Hooks returns the client hooks. +func (c *ReferrerClient) Hooks() []Hook { + return c.hooks.Referrer +} + +// Interceptors returns the client interceptors. +func (c *ReferrerClient) Interceptors() []Interceptor { + return c.inters.Referrer +} + +func (c *ReferrerClient) mutate(ctx context.Context, m *ReferrerMutation) (Value, error) { + switch m.Op() { + case OpCreate: + return (&ReferrerCreate{config: c.config, hooks: c.Hooks(), mutation: m}).Save(ctx) + case OpUpdate: + return (&ReferrerUpdate{config: c.config, hooks: c.Hooks(), mutation: m}).Save(ctx) + case OpUpdateOne: + return (&ReferrerUpdateOne{config: c.config, hooks: c.Hooks(), mutation: m}).Save(ctx) + case OpDelete, OpDeleteOne: + return (&ReferrerDelete{config: c.config, hooks: c.Hooks(), mutation: m}).Exec(ctx) + default: + return nil, fmt.Errorf("ent: unknown Referrer mutation op: %q", m.Op()) + } +} + // RobotAccountClient is a client for the RobotAccount schema. type RobotAccountClient struct { config @@ -2379,12 +2571,12 @@ func (c *WorkflowRunClient) mutate(ctx context.Context, m *WorkflowRunMutation) type ( hooks struct { CASBackend, CASMapping, Integration, IntegrationAttachment, Membership, - OrgInvitation, Organization, RobotAccount, User, Workflow, WorkflowContract, - WorkflowContractVersion, WorkflowRun []ent.Hook + OrgInvitation, Organization, Referrer, RobotAccount, User, Workflow, + WorkflowContract, WorkflowContractVersion, WorkflowRun []ent.Hook } inters struct { CASBackend, CASMapping, Integration, IntegrationAttachment, Membership, - OrgInvitation, Organization, RobotAccount, User, Workflow, WorkflowContract, - WorkflowContractVersion, WorkflowRun []ent.Interceptor + OrgInvitation, Organization, Referrer, RobotAccount, User, Workflow, + WorkflowContract, WorkflowContractVersion, WorkflowRun []ent.Interceptor } ) diff --git a/app/controlplane/internal/data/ent/ent.go b/app/controlplane/internal/data/ent/ent.go index a7f277101..d49723451 100644 --- a/app/controlplane/internal/data/ent/ent.go +++ b/app/controlplane/internal/data/ent/ent.go @@ -19,6 +19,7 @@ import ( "github.com/chainloop-dev/chainloop/app/controlplane/internal/data/ent/membership" "github.com/chainloop-dev/chainloop/app/controlplane/internal/data/ent/organization" "github.com/chainloop-dev/chainloop/app/controlplane/internal/data/ent/orginvitation" + "github.com/chainloop-dev/chainloop/app/controlplane/internal/data/ent/referrer" "github.com/chainloop-dev/chainloop/app/controlplane/internal/data/ent/robotaccount" "github.com/chainloop-dev/chainloop/app/controlplane/internal/data/ent/user" "github.com/chainloop-dev/chainloop/app/controlplane/internal/data/ent/workflow" @@ -92,6 +93,7 @@ func checkColumn(table, column string) error { membership.Table: membership.ValidColumn, orginvitation.Table: orginvitation.ValidColumn, organization.Table: organization.ValidColumn, + referrer.Table: referrer.ValidColumn, robotaccount.Table: robotaccount.ValidColumn, user.Table: user.ValidColumn, workflow.Table: workflow.ValidColumn, diff --git a/app/controlplane/internal/data/ent/hook/hook.go b/app/controlplane/internal/data/ent/hook/hook.go index 1fd7fcc45..82e6f996a 100644 --- a/app/controlplane/internal/data/ent/hook/hook.go +++ b/app/controlplane/internal/data/ent/hook/hook.go @@ -93,6 +93,18 @@ func (f OrganizationFunc) Mutate(ctx context.Context, m ent.Mutation) (ent.Value return nil, fmt.Errorf("unexpected mutation type %T. expect *ent.OrganizationMutation", m) } +// The ReferrerFunc type is an adapter to allow the use of ordinary +// function as Referrer mutator. +type ReferrerFunc func(context.Context, *ent.ReferrerMutation) (ent.Value, error) + +// Mutate calls f(ctx, m). +func (f ReferrerFunc) Mutate(ctx context.Context, m ent.Mutation) (ent.Value, error) { + if mv, ok := m.(*ent.ReferrerMutation); ok { + return f(ctx, mv) + } + return nil, fmt.Errorf("unexpected mutation type %T. expect *ent.ReferrerMutation", m) +} + // The RobotAccountFunc type is an adapter to allow the use of ordinary // function as RobotAccount mutator. type RobotAccountFunc func(context.Context, *ent.RobotAccountMutation) (ent.Value, error) diff --git a/app/controlplane/internal/data/ent/migrate/migrations/20231107121730.sql b/app/controlplane/internal/data/ent/migrate/migrations/20231107121730.sql new file mode 100644 index 000000000..e0ce4fd13 --- /dev/null +++ b/app/controlplane/internal/data/ent/migrate/migrations/20231107121730.sql @@ -0,0 +1,8 @@ +-- Create "referrers" table +CREATE TABLE "referrers" ("id" uuid NOT NULL, "digest" character varying NOT NULL, "artifact_type" character varying NOT NULL, "downloadable" boolean NOT NULL, "created_at" timestamptz NOT NULL DEFAULT CURRENT_TIMESTAMP, PRIMARY KEY ("id")); +-- Create index "referrer_digest_artifact_type" to table: "referrers" +CREATE UNIQUE INDEX "referrer_digest_artifact_type" ON "referrers" ("digest", "artifact_type"); +-- Create "referrer_organizations" table +CREATE TABLE "referrer_organizations" ("referrer_id" uuid NOT NULL, "organization_id" uuid NOT NULL, PRIMARY KEY ("referrer_id", "organization_id"), CONSTRAINT "referrer_organizations_organization_id" FOREIGN KEY ("organization_id") REFERENCES "organizations" ("id") ON UPDATE NO ACTION ON DELETE CASCADE, CONSTRAINT "referrer_organizations_referrer_id" FOREIGN KEY ("referrer_id") REFERENCES "referrers" ("id") ON UPDATE NO ACTION ON DELETE CASCADE); +-- Create "referrer_references" table +CREATE TABLE "referrer_references" ("referrer_id" uuid NOT NULL, "referred_by_id" uuid NOT NULL, PRIMARY KEY ("referrer_id", "referred_by_id"), CONSTRAINT "referrer_references_referred_by_id" FOREIGN KEY ("referred_by_id") REFERENCES "referrers" ("id") ON UPDATE NO ACTION ON DELETE CASCADE, CONSTRAINT "referrer_references_referrer_id" FOREIGN KEY ("referrer_id") REFERENCES "referrers" ("id") ON UPDATE NO ACTION ON DELETE CASCADE); diff --git a/app/controlplane/internal/data/ent/migrate/migrations/atlas.sum b/app/controlplane/internal/data/ent/migrate/migrations/atlas.sum index c76947dbc..8d6799c47 100644 --- a/app/controlplane/internal/data/ent/migrate/migrations/atlas.sum +++ b/app/controlplane/internal/data/ent/migrate/migrations/atlas.sum @@ -1,4 +1,4 @@ -h1:5aUIvgmC3yamqOydvpGyxXZrwkoXCMOmXpk3tKjU0wc= +h1:DvxRnSCi73Xw9PQ7HXzFspdGMEwChKjsLicwtMq07Xc= 20230706165452_init-schema.sql h1:VvqbNFEQnCvUVyj2iDYVQQxDM0+sSXqocpt/5H64k8M= 20230710111950-cas-backend.sql h1:A8iBuSzZIEbdsv9ipBtscZQuaBp3V5/VMw7eZH6GX+g= 20230712094107-cas-backends-workflow-runs.sql h1:a5rzxpVGyd56nLRSsKrmCFc9sebg65RWzLghKHh5xvI= @@ -10,4 +10,5 @@ h1:5aUIvgmC3yamqOydvpGyxXZrwkoXCMOmXpk3tKjU0wc= 20230905084357.sql h1:VNLRcY8vRDq5WoHv6JqBMr8HCkxLxr0LFWQzvqVRTYk= 20230914221336.sql h1:MCsBIxQ6Ow1BEhCNEF0QfAkUJxPSPxJ4l5DyAOuKLlc= 20231031124431.sql h1:HxWLbK4otq1uvGWKqngAHcOL+8G+qHbFnhRfLZCboBQ= -20231108214833.sql h1:FWxUM9xcTv2Lt6YoR9zWhhcEIQl9dHswfd/P9dAKhWk= +20231107121730.sql h1:3Rd522oNuNVnDwbScdWKo7yxISYlN/JoDi6y6QF417c= +20231108214833.sql h1:qojrntcFArOidQQLDo2qns+zr3Y5BoJpfr2BvjCpRD0= diff --git a/app/controlplane/internal/data/ent/migrate/schema.go b/app/controlplane/internal/data/ent/migrate/schema.go index 5af04d46e..abe519ce6 100644 --- a/app/controlplane/internal/data/ent/migrate/schema.go +++ b/app/controlplane/internal/data/ent/migrate/schema.go @@ -211,6 +211,27 @@ var ( Columns: OrganizationsColumns, PrimaryKey: []*schema.Column{OrganizationsColumns[0]}, } + // ReferrersColumns holds the columns for the "referrers" table. + ReferrersColumns = []*schema.Column{ + {Name: "id", Type: field.TypeUUID, Unique: true}, + {Name: "digest", Type: field.TypeString}, + {Name: "artifact_type", Type: field.TypeString}, + {Name: "downloadable", Type: field.TypeBool}, + {Name: "created_at", Type: field.TypeTime, Default: "CURRENT_TIMESTAMP"}, + } + // ReferrersTable holds the schema information for the "referrers" table. + ReferrersTable = &schema.Table{ + Name: "referrers", + Columns: ReferrersColumns, + PrimaryKey: []*schema.Column{ReferrersColumns[0]}, + Indexes: []*schema.Index{ + { + Name: "referrer_digest_artifact_type", + Unique: true, + Columns: []*schema.Column{ReferrersColumns[1], ReferrersColumns[2]}, + }, + }, + } // RobotAccountsColumns holds the columns for the "robot_accounts" table. RobotAccountsColumns = []*schema.Column{ {Name: "id", Type: field.TypeUUID, Unique: true}, @@ -380,6 +401,56 @@ var ( }, }, } + // ReferrerReferencesColumns holds the columns for the "referrer_references" table. + ReferrerReferencesColumns = []*schema.Column{ + {Name: "referrer_id", Type: field.TypeUUID}, + {Name: "referred_by_id", Type: field.TypeUUID}, + } + // ReferrerReferencesTable holds the schema information for the "referrer_references" table. + ReferrerReferencesTable = &schema.Table{ + Name: "referrer_references", + Columns: ReferrerReferencesColumns, + PrimaryKey: []*schema.Column{ReferrerReferencesColumns[0], ReferrerReferencesColumns[1]}, + ForeignKeys: []*schema.ForeignKey{ + { + Symbol: "referrer_references_referrer_id", + Columns: []*schema.Column{ReferrerReferencesColumns[0]}, + RefColumns: []*schema.Column{ReferrersColumns[0]}, + OnDelete: schema.Cascade, + }, + { + Symbol: "referrer_references_referred_by_id", + Columns: []*schema.Column{ReferrerReferencesColumns[1]}, + RefColumns: []*schema.Column{ReferrersColumns[0]}, + OnDelete: schema.Cascade, + }, + }, + } + // ReferrerOrganizationsColumns holds the columns for the "referrer_organizations" table. + ReferrerOrganizationsColumns = []*schema.Column{ + {Name: "referrer_id", Type: field.TypeUUID}, + {Name: "organization_id", Type: field.TypeUUID}, + } + // ReferrerOrganizationsTable holds the schema information for the "referrer_organizations" table. + ReferrerOrganizationsTable = &schema.Table{ + Name: "referrer_organizations", + Columns: ReferrerOrganizationsColumns, + PrimaryKey: []*schema.Column{ReferrerOrganizationsColumns[0], ReferrerOrganizationsColumns[1]}, + ForeignKeys: []*schema.ForeignKey{ + { + Symbol: "referrer_organizations_referrer_id", + Columns: []*schema.Column{ReferrerOrganizationsColumns[0]}, + RefColumns: []*schema.Column{ReferrersColumns[0]}, + OnDelete: schema.Cascade, + }, + { + Symbol: "referrer_organizations_organization_id", + Columns: []*schema.Column{ReferrerOrganizationsColumns[1]}, + RefColumns: []*schema.Column{OrganizationsColumns[0]}, + OnDelete: schema.Cascade, + }, + }, + } // WorkflowRunCasBackendsColumns holds the columns for the "workflow_run_cas_backends" table. WorkflowRunCasBackendsColumns = []*schema.Column{ {Name: "workflow_run_id", Type: field.TypeUUID}, @@ -414,12 +485,15 @@ var ( MembershipsTable, OrgInvitationsTable, OrganizationsTable, + ReferrersTable, RobotAccountsTable, UsersTable, WorkflowsTable, WorkflowContractsTable, WorkflowContractVersionsTable, WorkflowRunsTable, + ReferrerReferencesTable, + ReferrerOrganizationsTable, WorkflowRunCasBackendsTable, } ) @@ -444,6 +518,10 @@ func init() { WorkflowRunsTable.ForeignKeys[0].RefTable = RobotAccountsTable WorkflowRunsTable.ForeignKeys[1].RefTable = WorkflowsTable WorkflowRunsTable.ForeignKeys[2].RefTable = WorkflowContractVersionsTable + ReferrerReferencesTable.ForeignKeys[0].RefTable = ReferrersTable + ReferrerReferencesTable.ForeignKeys[1].RefTable = ReferrersTable + ReferrerOrganizationsTable.ForeignKeys[0].RefTable = ReferrersTable + ReferrerOrganizationsTable.ForeignKeys[1].RefTable = OrganizationsTable WorkflowRunCasBackendsTable.ForeignKeys[0].RefTable = WorkflowRunsTable WorkflowRunCasBackendsTable.ForeignKeys[1].RefTable = CasBackendsTable } diff --git a/app/controlplane/internal/data/ent/mutation.go b/app/controlplane/internal/data/ent/mutation.go index b0930fbe2..02d7a6f7d 100644 --- a/app/controlplane/internal/data/ent/mutation.go +++ b/app/controlplane/internal/data/ent/mutation.go @@ -20,6 +20,7 @@ import ( "github.com/chainloop-dev/chainloop/app/controlplane/internal/data/ent/organization" "github.com/chainloop-dev/chainloop/app/controlplane/internal/data/ent/orginvitation" "github.com/chainloop-dev/chainloop/app/controlplane/internal/data/ent/predicate" + "github.com/chainloop-dev/chainloop/app/controlplane/internal/data/ent/referrer" "github.com/chainloop-dev/chainloop/app/controlplane/internal/data/ent/robotaccount" "github.com/chainloop-dev/chainloop/app/controlplane/internal/data/ent/user" "github.com/chainloop-dev/chainloop/app/controlplane/internal/data/ent/workflow" @@ -46,6 +47,7 @@ const ( TypeMembership = "Membership" TypeOrgInvitation = "OrgInvitation" TypeOrganization = "Organization" + TypeReferrer = "Referrer" TypeRobotAccount = "RobotAccount" TypeUser = "User" TypeWorkflow = "Workflow" @@ -4369,6 +4371,9 @@ type OrganizationMutation struct { integrations map[uuid.UUID]struct{} removedintegrations map[uuid.UUID]struct{} clearedintegrations bool + referrers map[uuid.UUID]struct{} + removedreferrers map[uuid.UUID]struct{} + clearedreferrers bool done bool oldValue func(context.Context) (*Organization, error) predicates []predicate.Organization @@ -4820,6 +4825,60 @@ func (m *OrganizationMutation) ResetIntegrations() { m.removedintegrations = nil } +// AddReferrerIDs adds the "referrers" edge to the Referrer entity by ids. +func (m *OrganizationMutation) AddReferrerIDs(ids ...uuid.UUID) { + if m.referrers == nil { + m.referrers = make(map[uuid.UUID]struct{}) + } + for i := range ids { + m.referrers[ids[i]] = struct{}{} + } +} + +// ClearReferrers clears the "referrers" edge to the Referrer entity. +func (m *OrganizationMutation) ClearReferrers() { + m.clearedreferrers = true +} + +// ReferrersCleared reports if the "referrers" edge to the Referrer entity was cleared. +func (m *OrganizationMutation) ReferrersCleared() bool { + return m.clearedreferrers +} + +// RemoveReferrerIDs removes the "referrers" edge to the Referrer entity by IDs. +func (m *OrganizationMutation) RemoveReferrerIDs(ids ...uuid.UUID) { + if m.removedreferrers == nil { + m.removedreferrers = make(map[uuid.UUID]struct{}) + } + for i := range ids { + delete(m.referrers, ids[i]) + m.removedreferrers[ids[i]] = struct{}{} + } +} + +// RemovedReferrers returns the removed IDs of the "referrers" edge to the Referrer entity. +func (m *OrganizationMutation) RemovedReferrersIDs() (ids []uuid.UUID) { + for id := range m.removedreferrers { + ids = append(ids, id) + } + return +} + +// ReferrersIDs returns the "referrers" edge IDs in the mutation. +func (m *OrganizationMutation) ReferrersIDs() (ids []uuid.UUID) { + for id := range m.referrers { + ids = append(ids, id) + } + return +} + +// ResetReferrers resets all changes to the "referrers" edge. +func (m *OrganizationMutation) ResetReferrers() { + m.referrers = nil + m.clearedreferrers = false + m.removedreferrers = nil +} + // Where appends a list predicates to the OrganizationMutation builder. func (m *OrganizationMutation) Where(ps ...predicate.Organization) { m.predicates = append(m.predicates, ps...) @@ -4970,7 +5029,7 @@ func (m *OrganizationMutation) ResetField(name string) error { // AddedEdges returns all edge names that were set/added in this mutation. func (m *OrganizationMutation) AddedEdges() []string { - edges := make([]string, 0, 5) + edges := make([]string, 0, 6) if m.memberships != nil { edges = append(edges, organization.EdgeMemberships) } @@ -4986,6 +5045,9 @@ func (m *OrganizationMutation) AddedEdges() []string { if m.integrations != nil { edges = append(edges, organization.EdgeIntegrations) } + if m.referrers != nil { + edges = append(edges, organization.EdgeReferrers) + } return edges } @@ -5023,13 +5085,19 @@ func (m *OrganizationMutation) AddedIDs(name string) []ent.Value { ids = append(ids, id) } return ids + case organization.EdgeReferrers: + ids := make([]ent.Value, 0, len(m.referrers)) + for id := range m.referrers { + ids = append(ids, id) + } + return ids } return nil } // RemovedEdges returns all edge names that were removed in this mutation. func (m *OrganizationMutation) RemovedEdges() []string { - edges := make([]string, 0, 5) + edges := make([]string, 0, 6) if m.removedmemberships != nil { edges = append(edges, organization.EdgeMemberships) } @@ -5045,6 +5113,9 @@ func (m *OrganizationMutation) RemovedEdges() []string { if m.removedintegrations != nil { edges = append(edges, organization.EdgeIntegrations) } + if m.removedreferrers != nil { + edges = append(edges, organization.EdgeReferrers) + } return edges } @@ -5082,13 +5153,19 @@ func (m *OrganizationMutation) RemovedIDs(name string) []ent.Value { ids = append(ids, id) } return ids + case organization.EdgeReferrers: + ids := make([]ent.Value, 0, len(m.removedreferrers)) + for id := range m.removedreferrers { + ids = append(ids, id) + } + return ids } return nil } // ClearedEdges returns all edge names that were cleared in this mutation. func (m *OrganizationMutation) ClearedEdges() []string { - edges := make([]string, 0, 5) + edges := make([]string, 0, 6) if m.clearedmemberships { edges = append(edges, organization.EdgeMemberships) } @@ -5104,6 +5181,9 @@ func (m *OrganizationMutation) ClearedEdges() []string { if m.clearedintegrations { edges = append(edges, organization.EdgeIntegrations) } + if m.clearedreferrers { + edges = append(edges, organization.EdgeReferrers) + } return edges } @@ -5121,6 +5201,8 @@ func (m *OrganizationMutation) EdgeCleared(name string) bool { return m.clearedcas_backends case organization.EdgeIntegrations: return m.clearedintegrations + case organization.EdgeReferrers: + return m.clearedreferrers } return false } @@ -5152,10 +5234,766 @@ func (m *OrganizationMutation) ResetEdge(name string) error { case organization.EdgeIntegrations: m.ResetIntegrations() return nil + case organization.EdgeReferrers: + m.ResetReferrers() + return nil } return fmt.Errorf("unknown Organization edge %s", name) } +// ReferrerMutation represents an operation that mutates the Referrer nodes in the graph. +type ReferrerMutation struct { + config + op Op + typ string + id *uuid.UUID + digest *string + artifact_type *string + downloadable *bool + created_at *time.Time + clearedFields map[string]struct{} + referred_by map[uuid.UUID]struct{} + removedreferred_by map[uuid.UUID]struct{} + clearedreferred_by bool + references map[uuid.UUID]struct{} + removedreferences map[uuid.UUID]struct{} + clearedreferences bool + organizations map[uuid.UUID]struct{} + removedorganizations map[uuid.UUID]struct{} + clearedorganizations bool + done bool + oldValue func(context.Context) (*Referrer, error) + predicates []predicate.Referrer +} + +var _ ent.Mutation = (*ReferrerMutation)(nil) + +// referrerOption allows management of the mutation configuration using functional options. +type referrerOption func(*ReferrerMutation) + +// newReferrerMutation creates new mutation for the Referrer entity. +func newReferrerMutation(c config, op Op, opts ...referrerOption) *ReferrerMutation { + m := &ReferrerMutation{ + config: c, + op: op, + typ: TypeReferrer, + clearedFields: make(map[string]struct{}), + } + for _, opt := range opts { + opt(m) + } + return m +} + +// withReferrerID sets the ID field of the mutation. +func withReferrerID(id uuid.UUID) referrerOption { + return func(m *ReferrerMutation) { + var ( + err error + once sync.Once + value *Referrer + ) + m.oldValue = func(ctx context.Context) (*Referrer, error) { + once.Do(func() { + if m.done { + err = errors.New("querying old values post mutation is not allowed") + } else { + value, err = m.Client().Referrer.Get(ctx, id) + } + }) + return value, err + } + m.id = &id + } +} + +// withReferrer sets the old Referrer of the mutation. +func withReferrer(node *Referrer) referrerOption { + return func(m *ReferrerMutation) { + m.oldValue = func(context.Context) (*Referrer, error) { + return node, nil + } + m.id = &node.ID + } +} + +// Client returns a new `ent.Client` from the mutation. If the mutation was +// executed in a transaction (ent.Tx), a transactional client is returned. +func (m ReferrerMutation) Client() *Client { + client := &Client{config: m.config} + client.init() + return client +} + +// Tx returns an `ent.Tx` for mutations that were executed in transactions; +// it returns an error otherwise. +func (m ReferrerMutation) Tx() (*Tx, error) { + if _, ok := m.driver.(*txDriver); !ok { + return nil, errors.New("ent: mutation is not running in a transaction") + } + tx := &Tx{config: m.config} + tx.init() + return tx, nil +} + +// SetID sets the value of the id field. Note that this +// operation is only accepted on creation of Referrer entities. +func (m *ReferrerMutation) SetID(id uuid.UUID) { + m.id = &id +} + +// ID returns the ID value in the mutation. Note that the ID is only available +// if it was provided to the builder or after it was returned from the database. +func (m *ReferrerMutation) ID() (id uuid.UUID, exists bool) { + if m.id == nil { + return + } + return *m.id, true +} + +// IDs queries the database and returns the entity ids that match the mutation's predicate. +// That means, if the mutation is applied within a transaction with an isolation level such +// as sql.LevelSerializable, the returned ids match the ids of the rows that will be updated +// or updated by the mutation. +func (m *ReferrerMutation) IDs(ctx context.Context) ([]uuid.UUID, error) { + switch { + case m.op.Is(OpUpdateOne | OpDeleteOne): + id, exists := m.ID() + if exists { + return []uuid.UUID{id}, nil + } + fallthrough + case m.op.Is(OpUpdate | OpDelete): + return m.Client().Referrer.Query().Where(m.predicates...).IDs(ctx) + default: + return nil, fmt.Errorf("IDs is not allowed on %s operations", m.op) + } +} + +// SetDigest sets the "digest" field. +func (m *ReferrerMutation) SetDigest(s string) { + m.digest = &s +} + +// Digest returns the value of the "digest" field in the mutation. +func (m *ReferrerMutation) Digest() (r string, exists bool) { + v := m.digest + if v == nil { + return + } + return *v, true +} + +// OldDigest returns the old "digest" field's value of the Referrer entity. +// If the Referrer object wasn't provided to the builder, the object is fetched from the database. +// An error is returned if the mutation operation is not UpdateOne, or the database query fails. +func (m *ReferrerMutation) OldDigest(ctx context.Context) (v string, err error) { + if !m.op.Is(OpUpdateOne) { + return v, errors.New("OldDigest is only allowed on UpdateOne operations") + } + if m.id == nil || m.oldValue == nil { + return v, errors.New("OldDigest requires an ID field in the mutation") + } + oldValue, err := m.oldValue(ctx) + if err != nil { + return v, fmt.Errorf("querying old value for OldDigest: %w", err) + } + return oldValue.Digest, nil +} + +// ResetDigest resets all changes to the "digest" field. +func (m *ReferrerMutation) ResetDigest() { + m.digest = nil +} + +// SetArtifactType sets the "artifact_type" field. +func (m *ReferrerMutation) SetArtifactType(s string) { + m.artifact_type = &s +} + +// ArtifactType returns the value of the "artifact_type" field in the mutation. +func (m *ReferrerMutation) ArtifactType() (r string, exists bool) { + v := m.artifact_type + if v == nil { + return + } + return *v, true +} + +// OldArtifactType returns the old "artifact_type" field's value of the Referrer entity. +// If the Referrer object wasn't provided to the builder, the object is fetched from the database. +// An error is returned if the mutation operation is not UpdateOne, or the database query fails. +func (m *ReferrerMutation) OldArtifactType(ctx context.Context) (v string, err error) { + if !m.op.Is(OpUpdateOne) { + return v, errors.New("OldArtifactType is only allowed on UpdateOne operations") + } + if m.id == nil || m.oldValue == nil { + return v, errors.New("OldArtifactType requires an ID field in the mutation") + } + oldValue, err := m.oldValue(ctx) + if err != nil { + return v, fmt.Errorf("querying old value for OldArtifactType: %w", err) + } + return oldValue.ArtifactType, nil +} + +// ResetArtifactType resets all changes to the "artifact_type" field. +func (m *ReferrerMutation) ResetArtifactType() { + m.artifact_type = nil +} + +// SetDownloadable sets the "downloadable" field. +func (m *ReferrerMutation) SetDownloadable(b bool) { + m.downloadable = &b +} + +// Downloadable returns the value of the "downloadable" field in the mutation. +func (m *ReferrerMutation) Downloadable() (r bool, exists bool) { + v := m.downloadable + if v == nil { + return + } + return *v, true +} + +// OldDownloadable returns the old "downloadable" field's value of the Referrer entity. +// If the Referrer object wasn't provided to the builder, the object is fetched from the database. +// An error is returned if the mutation operation is not UpdateOne, or the database query fails. +func (m *ReferrerMutation) OldDownloadable(ctx context.Context) (v bool, err error) { + if !m.op.Is(OpUpdateOne) { + return v, errors.New("OldDownloadable is only allowed on UpdateOne operations") + } + if m.id == nil || m.oldValue == nil { + return v, errors.New("OldDownloadable requires an ID field in the mutation") + } + oldValue, err := m.oldValue(ctx) + if err != nil { + return v, fmt.Errorf("querying old value for OldDownloadable: %w", err) + } + return oldValue.Downloadable, nil +} + +// ResetDownloadable resets all changes to the "downloadable" field. +func (m *ReferrerMutation) ResetDownloadable() { + m.downloadable = nil +} + +// SetCreatedAt sets the "created_at" field. +func (m *ReferrerMutation) SetCreatedAt(t time.Time) { + m.created_at = &t +} + +// CreatedAt returns the value of the "created_at" field in the mutation. +func (m *ReferrerMutation) CreatedAt() (r time.Time, exists bool) { + v := m.created_at + if v == nil { + return + } + return *v, true +} + +// OldCreatedAt returns the old "created_at" field's value of the Referrer entity. +// If the Referrer object wasn't provided to the builder, the object is fetched from the database. +// An error is returned if the mutation operation is not UpdateOne, or the database query fails. +func (m *ReferrerMutation) OldCreatedAt(ctx context.Context) (v time.Time, err error) { + if !m.op.Is(OpUpdateOne) { + return v, errors.New("OldCreatedAt is only allowed on UpdateOne operations") + } + if m.id == nil || m.oldValue == nil { + return v, errors.New("OldCreatedAt requires an ID field in the mutation") + } + oldValue, err := m.oldValue(ctx) + if err != nil { + return v, fmt.Errorf("querying old value for OldCreatedAt: %w", err) + } + return oldValue.CreatedAt, nil +} + +// ResetCreatedAt resets all changes to the "created_at" field. +func (m *ReferrerMutation) ResetCreatedAt() { + m.created_at = nil +} + +// AddReferredByIDs adds the "referred_by" edge to the Referrer entity by ids. +func (m *ReferrerMutation) AddReferredByIDs(ids ...uuid.UUID) { + if m.referred_by == nil { + m.referred_by = make(map[uuid.UUID]struct{}) + } + for i := range ids { + m.referred_by[ids[i]] = struct{}{} + } +} + +// ClearReferredBy clears the "referred_by" edge to the Referrer entity. +func (m *ReferrerMutation) ClearReferredBy() { + m.clearedreferred_by = true +} + +// ReferredByCleared reports if the "referred_by" edge to the Referrer entity was cleared. +func (m *ReferrerMutation) ReferredByCleared() bool { + return m.clearedreferred_by +} + +// RemoveReferredByIDs removes the "referred_by" edge to the Referrer entity by IDs. +func (m *ReferrerMutation) RemoveReferredByIDs(ids ...uuid.UUID) { + if m.removedreferred_by == nil { + m.removedreferred_by = make(map[uuid.UUID]struct{}) + } + for i := range ids { + delete(m.referred_by, ids[i]) + m.removedreferred_by[ids[i]] = struct{}{} + } +} + +// RemovedReferredBy returns the removed IDs of the "referred_by" edge to the Referrer entity. +func (m *ReferrerMutation) RemovedReferredByIDs() (ids []uuid.UUID) { + for id := range m.removedreferred_by { + ids = append(ids, id) + } + return +} + +// ReferredByIDs returns the "referred_by" edge IDs in the mutation. +func (m *ReferrerMutation) ReferredByIDs() (ids []uuid.UUID) { + for id := range m.referred_by { + ids = append(ids, id) + } + return +} + +// ResetReferredBy resets all changes to the "referred_by" edge. +func (m *ReferrerMutation) ResetReferredBy() { + m.referred_by = nil + m.clearedreferred_by = false + m.removedreferred_by = nil +} + +// AddReferenceIDs adds the "references" edge to the Referrer entity by ids. +func (m *ReferrerMutation) AddReferenceIDs(ids ...uuid.UUID) { + if m.references == nil { + m.references = make(map[uuid.UUID]struct{}) + } + for i := range ids { + m.references[ids[i]] = struct{}{} + } +} + +// ClearReferences clears the "references" edge to the Referrer entity. +func (m *ReferrerMutation) ClearReferences() { + m.clearedreferences = true +} + +// ReferencesCleared reports if the "references" edge to the Referrer entity was cleared. +func (m *ReferrerMutation) ReferencesCleared() bool { + return m.clearedreferences +} + +// RemoveReferenceIDs removes the "references" edge to the Referrer entity by IDs. +func (m *ReferrerMutation) RemoveReferenceIDs(ids ...uuid.UUID) { + if m.removedreferences == nil { + m.removedreferences = make(map[uuid.UUID]struct{}) + } + for i := range ids { + delete(m.references, ids[i]) + m.removedreferences[ids[i]] = struct{}{} + } +} + +// RemovedReferences returns the removed IDs of the "references" edge to the Referrer entity. +func (m *ReferrerMutation) RemovedReferencesIDs() (ids []uuid.UUID) { + for id := range m.removedreferences { + ids = append(ids, id) + } + return +} + +// ReferencesIDs returns the "references" edge IDs in the mutation. +func (m *ReferrerMutation) ReferencesIDs() (ids []uuid.UUID) { + for id := range m.references { + ids = append(ids, id) + } + return +} + +// ResetReferences resets all changes to the "references" edge. +func (m *ReferrerMutation) ResetReferences() { + m.references = nil + m.clearedreferences = false + m.removedreferences = nil +} + +// AddOrganizationIDs adds the "organizations" edge to the Organization entity by ids. +func (m *ReferrerMutation) AddOrganizationIDs(ids ...uuid.UUID) { + if m.organizations == nil { + m.organizations = make(map[uuid.UUID]struct{}) + } + for i := range ids { + m.organizations[ids[i]] = struct{}{} + } +} + +// ClearOrganizations clears the "organizations" edge to the Organization entity. +func (m *ReferrerMutation) ClearOrganizations() { + m.clearedorganizations = true +} + +// OrganizationsCleared reports if the "organizations" edge to the Organization entity was cleared. +func (m *ReferrerMutation) OrganizationsCleared() bool { + return m.clearedorganizations +} + +// RemoveOrganizationIDs removes the "organizations" edge to the Organization entity by IDs. +func (m *ReferrerMutation) RemoveOrganizationIDs(ids ...uuid.UUID) { + if m.removedorganizations == nil { + m.removedorganizations = make(map[uuid.UUID]struct{}) + } + for i := range ids { + delete(m.organizations, ids[i]) + m.removedorganizations[ids[i]] = struct{}{} + } +} + +// RemovedOrganizations returns the removed IDs of the "organizations" edge to the Organization entity. +func (m *ReferrerMutation) RemovedOrganizationsIDs() (ids []uuid.UUID) { + for id := range m.removedorganizations { + ids = append(ids, id) + } + return +} + +// OrganizationsIDs returns the "organizations" edge IDs in the mutation. +func (m *ReferrerMutation) OrganizationsIDs() (ids []uuid.UUID) { + for id := range m.organizations { + ids = append(ids, id) + } + return +} + +// ResetOrganizations resets all changes to the "organizations" edge. +func (m *ReferrerMutation) ResetOrganizations() { + m.organizations = nil + m.clearedorganizations = false + m.removedorganizations = nil +} + +// Where appends a list predicates to the ReferrerMutation builder. +func (m *ReferrerMutation) Where(ps ...predicate.Referrer) { + m.predicates = append(m.predicates, ps...) +} + +// WhereP appends storage-level predicates to the ReferrerMutation builder. Using this method, +// users can use type-assertion to append predicates that do not depend on any generated package. +func (m *ReferrerMutation) WhereP(ps ...func(*sql.Selector)) { + p := make([]predicate.Referrer, len(ps)) + for i := range ps { + p[i] = ps[i] + } + m.Where(p...) +} + +// Op returns the operation name. +func (m *ReferrerMutation) Op() Op { + return m.op +} + +// SetOp allows setting the mutation operation. +func (m *ReferrerMutation) SetOp(op Op) { + m.op = op +} + +// Type returns the node type of this mutation (Referrer). +func (m *ReferrerMutation) Type() string { + return m.typ +} + +// Fields returns all fields that were changed during this mutation. Note that in +// order to get all numeric fields that were incremented/decremented, call +// AddedFields(). +func (m *ReferrerMutation) Fields() []string { + fields := make([]string, 0, 4) + if m.digest != nil { + fields = append(fields, referrer.FieldDigest) + } + if m.artifact_type != nil { + fields = append(fields, referrer.FieldArtifactType) + } + if m.downloadable != nil { + fields = append(fields, referrer.FieldDownloadable) + } + if m.created_at != nil { + fields = append(fields, referrer.FieldCreatedAt) + } + return fields +} + +// Field returns the value of a field with the given name. The second boolean +// return value indicates that this field was not set, or was not defined in the +// schema. +func (m *ReferrerMutation) Field(name string) (ent.Value, bool) { + switch name { + case referrer.FieldDigest: + return m.Digest() + case referrer.FieldArtifactType: + return m.ArtifactType() + case referrer.FieldDownloadable: + return m.Downloadable() + case referrer.FieldCreatedAt: + return m.CreatedAt() + } + return nil, false +} + +// OldField returns the old value of the field from the database. An error is +// returned if the mutation operation is not UpdateOne, or the query to the +// database failed. +func (m *ReferrerMutation) OldField(ctx context.Context, name string) (ent.Value, error) { + switch name { + case referrer.FieldDigest: + return m.OldDigest(ctx) + case referrer.FieldArtifactType: + return m.OldArtifactType(ctx) + case referrer.FieldDownloadable: + return m.OldDownloadable(ctx) + case referrer.FieldCreatedAt: + return m.OldCreatedAt(ctx) + } + return nil, fmt.Errorf("unknown Referrer field %s", name) +} + +// SetField sets the value of a field with the given name. It returns an error if +// the field is not defined in the schema, or if the type mismatched the field +// type. +func (m *ReferrerMutation) SetField(name string, value ent.Value) error { + switch name { + case referrer.FieldDigest: + v, ok := value.(string) + if !ok { + return fmt.Errorf("unexpected type %T for field %s", value, name) + } + m.SetDigest(v) + return nil + case referrer.FieldArtifactType: + v, ok := value.(string) + if !ok { + return fmt.Errorf("unexpected type %T for field %s", value, name) + } + m.SetArtifactType(v) + return nil + case referrer.FieldDownloadable: + v, ok := value.(bool) + if !ok { + return fmt.Errorf("unexpected type %T for field %s", value, name) + } + m.SetDownloadable(v) + return nil + case referrer.FieldCreatedAt: + v, ok := value.(time.Time) + if !ok { + return fmt.Errorf("unexpected type %T for field %s", value, name) + } + m.SetCreatedAt(v) + return nil + } + return fmt.Errorf("unknown Referrer field %s", name) +} + +// AddedFields returns all numeric fields that were incremented/decremented during +// this mutation. +func (m *ReferrerMutation) AddedFields() []string { + return nil +} + +// AddedField returns the numeric value that was incremented/decremented on a field +// with the given name. The second boolean return value indicates that this field +// was not set, or was not defined in the schema. +func (m *ReferrerMutation) AddedField(name string) (ent.Value, bool) { + return nil, false +} + +// AddField adds the value to the field with the given name. It returns an error if +// the field is not defined in the schema, or if the type mismatched the field +// type. +func (m *ReferrerMutation) AddField(name string, value ent.Value) error { + switch name { + } + return fmt.Errorf("unknown Referrer numeric field %s", name) +} + +// ClearedFields returns all nullable fields that were cleared during this +// mutation. +func (m *ReferrerMutation) ClearedFields() []string { + return nil +} + +// FieldCleared returns a boolean indicating if a field with the given name was +// cleared in this mutation. +func (m *ReferrerMutation) FieldCleared(name string) bool { + _, ok := m.clearedFields[name] + return ok +} + +// ClearField clears the value of the field with the given name. It returns an +// error if the field is not defined in the schema. +func (m *ReferrerMutation) ClearField(name string) error { + return fmt.Errorf("unknown Referrer nullable field %s", name) +} + +// ResetField resets all changes in the mutation for the field with the given name. +// It returns an error if the field is not defined in the schema. +func (m *ReferrerMutation) ResetField(name string) error { + switch name { + case referrer.FieldDigest: + m.ResetDigest() + return nil + case referrer.FieldArtifactType: + m.ResetArtifactType() + return nil + case referrer.FieldDownloadable: + m.ResetDownloadable() + return nil + case referrer.FieldCreatedAt: + m.ResetCreatedAt() + return nil + } + return fmt.Errorf("unknown Referrer field %s", name) +} + +// AddedEdges returns all edge names that were set/added in this mutation. +func (m *ReferrerMutation) AddedEdges() []string { + edges := make([]string, 0, 3) + if m.referred_by != nil { + edges = append(edges, referrer.EdgeReferredBy) + } + if m.references != nil { + edges = append(edges, referrer.EdgeReferences) + } + if m.organizations != nil { + edges = append(edges, referrer.EdgeOrganizations) + } + return edges +} + +// AddedIDs returns all IDs (to other nodes) that were added for the given edge +// name in this mutation. +func (m *ReferrerMutation) AddedIDs(name string) []ent.Value { + switch name { + case referrer.EdgeReferredBy: + ids := make([]ent.Value, 0, len(m.referred_by)) + for id := range m.referred_by { + ids = append(ids, id) + } + return ids + case referrer.EdgeReferences: + ids := make([]ent.Value, 0, len(m.references)) + for id := range m.references { + ids = append(ids, id) + } + return ids + case referrer.EdgeOrganizations: + ids := make([]ent.Value, 0, len(m.organizations)) + for id := range m.organizations { + ids = append(ids, id) + } + return ids + } + return nil +} + +// RemovedEdges returns all edge names that were removed in this mutation. +func (m *ReferrerMutation) RemovedEdges() []string { + edges := make([]string, 0, 3) + if m.removedreferred_by != nil { + edges = append(edges, referrer.EdgeReferredBy) + } + if m.removedreferences != nil { + edges = append(edges, referrer.EdgeReferences) + } + if m.removedorganizations != nil { + edges = append(edges, referrer.EdgeOrganizations) + } + return edges +} + +// RemovedIDs returns all IDs (to other nodes) that were removed for the edge with +// the given name in this mutation. +func (m *ReferrerMutation) RemovedIDs(name string) []ent.Value { + switch name { + case referrer.EdgeReferredBy: + ids := make([]ent.Value, 0, len(m.removedreferred_by)) + for id := range m.removedreferred_by { + ids = append(ids, id) + } + return ids + case referrer.EdgeReferences: + ids := make([]ent.Value, 0, len(m.removedreferences)) + for id := range m.removedreferences { + ids = append(ids, id) + } + return ids + case referrer.EdgeOrganizations: + ids := make([]ent.Value, 0, len(m.removedorganizations)) + for id := range m.removedorganizations { + ids = append(ids, id) + } + return ids + } + return nil +} + +// ClearedEdges returns all edge names that were cleared in this mutation. +func (m *ReferrerMutation) ClearedEdges() []string { + edges := make([]string, 0, 3) + if m.clearedreferred_by { + edges = append(edges, referrer.EdgeReferredBy) + } + if m.clearedreferences { + edges = append(edges, referrer.EdgeReferences) + } + if m.clearedorganizations { + edges = append(edges, referrer.EdgeOrganizations) + } + return edges +} + +// EdgeCleared returns a boolean which indicates if the edge with the given name +// was cleared in this mutation. +func (m *ReferrerMutation) EdgeCleared(name string) bool { + switch name { + case referrer.EdgeReferredBy: + return m.clearedreferred_by + case referrer.EdgeReferences: + return m.clearedreferences + case referrer.EdgeOrganizations: + return m.clearedorganizations + } + return false +} + +// ClearEdge clears the value of the edge with the given name. It returns an error +// if that edge is not defined in the schema. +func (m *ReferrerMutation) ClearEdge(name string) error { + switch name { + } + return fmt.Errorf("unknown Referrer unique edge %s", name) +} + +// ResetEdge resets all changes to the edge with the given name in this mutation. +// It returns an error if the edge is not defined in the schema. +func (m *ReferrerMutation) ResetEdge(name string) error { + switch name { + case referrer.EdgeReferredBy: + m.ResetReferredBy() + return nil + case referrer.EdgeReferences: + m.ResetReferences() + return nil + case referrer.EdgeOrganizations: + m.ResetOrganizations() + return nil + } + return fmt.Errorf("unknown Referrer edge %s", name) +} + // RobotAccountMutation represents an operation that mutates the RobotAccount nodes in the graph. type RobotAccountMutation struct { config diff --git a/app/controlplane/internal/data/ent/organization.go b/app/controlplane/internal/data/ent/organization.go index 7930ab825..3c2442b19 100644 --- a/app/controlplane/internal/data/ent/organization.go +++ b/app/controlplane/internal/data/ent/organization.go @@ -40,9 +40,11 @@ type OrganizationEdges struct { CasBackends []*CASBackend `json:"cas_backends,omitempty"` // Integrations holds the value of the integrations edge. Integrations []*Integration `json:"integrations,omitempty"` + // Referrers holds the value of the referrers edge. + Referrers []*Referrer `json:"referrers,omitempty"` // loadedTypes holds the information for reporting if a // type was loaded (or requested) in eager-loading or not. - loadedTypes [5]bool + loadedTypes [6]bool } // MembershipsOrErr returns the Memberships value or an error if the edge @@ -90,6 +92,15 @@ func (e OrganizationEdges) IntegrationsOrErr() ([]*Integration, error) { return nil, &NotLoadedError{edge: "integrations"} } +// ReferrersOrErr returns the Referrers value or an error if the edge +// was not loaded in eager-loading. +func (e OrganizationEdges) ReferrersOrErr() ([]*Referrer, error) { + if e.loadedTypes[5] { + return e.Referrers, nil + } + return nil, &NotLoadedError{edge: "referrers"} +} + // scanValues returns the types for scanning values from sql.Rows. func (*Organization) scanValues(columns []string) ([]any, error) { values := make([]any, len(columns)) @@ -172,6 +183,11 @@ func (o *Organization) QueryIntegrations() *IntegrationQuery { return NewOrganizationClient(o.config).QueryIntegrations(o) } +// QueryReferrers queries the "referrers" edge of the Organization entity. +func (o *Organization) QueryReferrers() *ReferrerQuery { + return NewOrganizationClient(o.config).QueryReferrers(o) +} + // Update returns a builder for updating this Organization. // Note that you need to call Organization.Unwrap() before calling this method if this Organization // was returned from a transaction, and the transaction was committed or rolled back. diff --git a/app/controlplane/internal/data/ent/organization/organization.go b/app/controlplane/internal/data/ent/organization/organization.go index 5c1ccaf97..08b8c1955 100644 --- a/app/controlplane/internal/data/ent/organization/organization.go +++ b/app/controlplane/internal/data/ent/organization/organization.go @@ -29,6 +29,8 @@ const ( EdgeCasBackends = "cas_backends" // EdgeIntegrations holds the string denoting the integrations edge name in mutations. EdgeIntegrations = "integrations" + // EdgeReferrers holds the string denoting the referrers edge name in mutations. + EdgeReferrers = "referrers" // Table holds the table name of the organization in the database. Table = "organizations" // MembershipsTable is the table that holds the memberships relation/edge. @@ -66,6 +68,11 @@ const ( IntegrationsInverseTable = "integrations" // IntegrationsColumn is the table column denoting the integrations relation/edge. IntegrationsColumn = "organization_integrations" + // ReferrersTable is the table that holds the referrers relation/edge. The primary key declared below. + ReferrersTable = "referrer_organizations" + // ReferrersInverseTable is the table name for the Referrer entity. + // It exists in this package in order to avoid circular dependency with the "referrer" package. + ReferrersInverseTable = "referrers" ) // Columns holds all SQL columns for organization fields. @@ -75,6 +82,12 @@ var Columns = []string{ FieldCreatedAt, } +var ( + // ReferrersPrimaryKey and ReferrersColumn2 are the table columns denoting the + // primary key for the referrers relation (M2M). + ReferrersPrimaryKey = []string{"referrer_id", "organization_id"} +) + // ValidColumn reports if the column name is valid (part of the table columns). func ValidColumn(column string) bool { for i := range Columns { @@ -181,6 +194,20 @@ func ByIntegrations(term sql.OrderTerm, terms ...sql.OrderTerm) OrderOption { sqlgraph.OrderByNeighborTerms(s, newIntegrationsStep(), append([]sql.OrderTerm{term}, terms...)...) } } + +// ByReferrersCount orders the results by referrers count. +func ByReferrersCount(opts ...sql.OrderTermOption) OrderOption { + return func(s *sql.Selector) { + sqlgraph.OrderByNeighborsCount(s, newReferrersStep(), opts...) + } +} + +// ByReferrers orders the results by referrers terms. +func ByReferrers(term sql.OrderTerm, terms ...sql.OrderTerm) OrderOption { + return func(s *sql.Selector) { + sqlgraph.OrderByNeighborTerms(s, newReferrersStep(), append([]sql.OrderTerm{term}, terms...)...) + } +} func newMembershipsStep() *sqlgraph.Step { return sqlgraph.NewStep( sqlgraph.From(Table, FieldID), @@ -216,3 +243,10 @@ func newIntegrationsStep() *sqlgraph.Step { sqlgraph.Edge(sqlgraph.O2M, false, IntegrationsTable, IntegrationsColumn), ) } +func newReferrersStep() *sqlgraph.Step { + return sqlgraph.NewStep( + sqlgraph.From(Table, FieldID), + sqlgraph.To(ReferrersInverseTable, FieldID), + sqlgraph.Edge(sqlgraph.M2M, true, ReferrersTable, ReferrersPrimaryKey...), + ) +} diff --git a/app/controlplane/internal/data/ent/organization/where.go b/app/controlplane/internal/data/ent/organization/where.go index 292eb826b..c3e3060a4 100644 --- a/app/controlplane/internal/data/ent/organization/where.go +++ b/app/controlplane/internal/data/ent/organization/where.go @@ -286,6 +286,29 @@ func HasIntegrationsWith(preds ...predicate.Integration) predicate.Organization }) } +// HasReferrers applies the HasEdge predicate on the "referrers" edge. +func HasReferrers() predicate.Organization { + return predicate.Organization(func(s *sql.Selector) { + step := sqlgraph.NewStep( + sqlgraph.From(Table, FieldID), + sqlgraph.Edge(sqlgraph.M2M, true, ReferrersTable, ReferrersPrimaryKey...), + ) + sqlgraph.HasNeighbors(s, step) + }) +} + +// HasReferrersWith applies the HasEdge predicate on the "referrers" edge with a given conditions (other predicates). +func HasReferrersWith(preds ...predicate.Referrer) predicate.Organization { + return predicate.Organization(func(s *sql.Selector) { + step := newReferrersStep() + sqlgraph.HasNeighborsWith(s, step, func(s *sql.Selector) { + for _, p := range preds { + p(s) + } + }) + }) +} + // And groups predicates with the AND operator between them. func And(predicates ...predicate.Organization) predicate.Organization { return predicate.Organization(func(s *sql.Selector) { diff --git a/app/controlplane/internal/data/ent/organization_create.go b/app/controlplane/internal/data/ent/organization_create.go index 74c3634c3..2970a8208 100644 --- a/app/controlplane/internal/data/ent/organization_create.go +++ b/app/controlplane/internal/data/ent/organization_create.go @@ -14,6 +14,7 @@ import ( "github.com/chainloop-dev/chainloop/app/controlplane/internal/data/ent/integration" "github.com/chainloop-dev/chainloop/app/controlplane/internal/data/ent/membership" "github.com/chainloop-dev/chainloop/app/controlplane/internal/data/ent/organization" + "github.com/chainloop-dev/chainloop/app/controlplane/internal/data/ent/referrer" "github.com/chainloop-dev/chainloop/app/controlplane/internal/data/ent/workflow" "github.com/chainloop-dev/chainloop/app/controlplane/internal/data/ent/workflowcontract" "github.com/google/uuid" @@ -143,6 +144,21 @@ func (oc *OrganizationCreate) AddIntegrations(i ...*Integration) *OrganizationCr return oc.AddIntegrationIDs(ids...) } +// AddReferrerIDs adds the "referrers" edge to the Referrer entity by IDs. +func (oc *OrganizationCreate) AddReferrerIDs(ids ...uuid.UUID) *OrganizationCreate { + oc.mutation.AddReferrerIDs(ids...) + return oc +} + +// AddReferrers adds the "referrers" edges to the Referrer entity. +func (oc *OrganizationCreate) AddReferrers(r ...*Referrer) *OrganizationCreate { + ids := make([]uuid.UUID, len(r)) + for i := range r { + ids[i] = r[i].ID + } + return oc.AddReferrerIDs(ids...) +} + // Mutation returns the OrganizationMutation object of the builder. func (oc *OrganizationCreate) Mutation() *OrganizationMutation { return oc.mutation @@ -323,6 +339,22 @@ func (oc *OrganizationCreate) createSpec() (*Organization, *sqlgraph.CreateSpec) } _spec.Edges = append(_spec.Edges, edge) } + if nodes := oc.mutation.ReferrersIDs(); len(nodes) > 0 { + edge := &sqlgraph.EdgeSpec{ + Rel: sqlgraph.M2M, + Inverse: true, + Table: organization.ReferrersTable, + Columns: organization.ReferrersPrimaryKey, + Bidi: false, + Target: &sqlgraph.EdgeTarget{ + IDSpec: sqlgraph.NewFieldSpec(referrer.FieldID, field.TypeUUID), + }, + } + for _, k := range nodes { + edge.Target.Nodes = append(edge.Target.Nodes, k) + } + _spec.Edges = append(_spec.Edges, edge) + } return _node, _spec } diff --git a/app/controlplane/internal/data/ent/organization_query.go b/app/controlplane/internal/data/ent/organization_query.go index d6c1b6e89..af1f515c2 100644 --- a/app/controlplane/internal/data/ent/organization_query.go +++ b/app/controlplane/internal/data/ent/organization_query.go @@ -16,6 +16,7 @@ import ( "github.com/chainloop-dev/chainloop/app/controlplane/internal/data/ent/membership" "github.com/chainloop-dev/chainloop/app/controlplane/internal/data/ent/organization" "github.com/chainloop-dev/chainloop/app/controlplane/internal/data/ent/predicate" + "github.com/chainloop-dev/chainloop/app/controlplane/internal/data/ent/referrer" "github.com/chainloop-dev/chainloop/app/controlplane/internal/data/ent/workflow" "github.com/chainloop-dev/chainloop/app/controlplane/internal/data/ent/workflowcontract" "github.com/google/uuid" @@ -33,6 +34,7 @@ type OrganizationQuery struct { withWorkflows *WorkflowQuery withCasBackends *CASBackendQuery withIntegrations *IntegrationQuery + withReferrers *ReferrerQuery // intermediate query (i.e. traversal path). sql *sql.Selector path func(context.Context) (*sql.Selector, error) @@ -179,6 +181,28 @@ func (oq *OrganizationQuery) QueryIntegrations() *IntegrationQuery { return query } +// QueryReferrers chains the current query on the "referrers" edge. +func (oq *OrganizationQuery) QueryReferrers() *ReferrerQuery { + query := (&ReferrerClient{config: oq.config}).Query() + query.path = func(ctx context.Context) (fromU *sql.Selector, err error) { + if err := oq.prepareQuery(ctx); err != nil { + return nil, err + } + selector := oq.sqlQuery(ctx) + if err := selector.Err(); err != nil { + return nil, err + } + step := sqlgraph.NewStep( + sqlgraph.From(organization.Table, organization.FieldID, selector), + sqlgraph.To(referrer.Table, referrer.FieldID), + sqlgraph.Edge(sqlgraph.M2M, true, organization.ReferrersTable, organization.ReferrersPrimaryKey...), + ) + fromU = sqlgraph.SetNeighbors(oq.driver.Dialect(), step) + return fromU, nil + } + return query +} + // First returns the first Organization entity from the query. // Returns a *NotFoundError when no Organization was found. func (oq *OrganizationQuery) First(ctx context.Context) (*Organization, error) { @@ -376,6 +400,7 @@ func (oq *OrganizationQuery) Clone() *OrganizationQuery { withWorkflows: oq.withWorkflows.Clone(), withCasBackends: oq.withCasBackends.Clone(), withIntegrations: oq.withIntegrations.Clone(), + withReferrers: oq.withReferrers.Clone(), // clone intermediate query. sql: oq.sql.Clone(), path: oq.path, @@ -437,6 +462,17 @@ func (oq *OrganizationQuery) WithIntegrations(opts ...func(*IntegrationQuery)) * return oq } +// WithReferrers tells the query-builder to eager-load the nodes that are connected to +// the "referrers" edge. The optional arguments are used to configure the query builder of the edge. +func (oq *OrganizationQuery) WithReferrers(opts ...func(*ReferrerQuery)) *OrganizationQuery { + query := (&ReferrerClient{config: oq.config}).Query() + for _, opt := range opts { + opt(query) + } + oq.withReferrers = query + return oq +} + // GroupBy is used to group vertices by one or more fields/columns. // It is often used with aggregate functions, like: count, max, mean, min, sum. // @@ -515,12 +551,13 @@ func (oq *OrganizationQuery) sqlAll(ctx context.Context, hooks ...queryHook) ([] var ( nodes = []*Organization{} _spec = oq.querySpec() - loadedTypes = [5]bool{ + loadedTypes = [6]bool{ oq.withMemberships != nil, oq.withWorkflowContracts != nil, oq.withWorkflows != nil, oq.withCasBackends != nil, oq.withIntegrations != nil, + oq.withReferrers != nil, } ) _spec.ScanValues = func(columns []string) ([]any, error) { @@ -578,6 +615,13 @@ func (oq *OrganizationQuery) sqlAll(ctx context.Context, hooks ...queryHook) ([] return nil, err } } + if query := oq.withReferrers; query != nil { + if err := oq.loadReferrers(ctx, query, nodes, + func(n *Organization) { n.Edges.Referrers = []*Referrer{} }, + func(n *Organization, e *Referrer) { n.Edges.Referrers = append(n.Edges.Referrers, e) }); err != nil { + return nil, err + } + } return nodes, nil } @@ -736,6 +780,67 @@ func (oq *OrganizationQuery) loadIntegrations(ctx context.Context, query *Integr } return nil } +func (oq *OrganizationQuery) loadReferrers(ctx context.Context, query *ReferrerQuery, nodes []*Organization, init func(*Organization), assign func(*Organization, *Referrer)) error { + edgeIDs := make([]driver.Value, len(nodes)) + byID := make(map[uuid.UUID]*Organization) + nids := make(map[uuid.UUID]map[*Organization]struct{}) + for i, node := range nodes { + edgeIDs[i] = node.ID + byID[node.ID] = node + if init != nil { + init(node) + } + } + query.Where(func(s *sql.Selector) { + joinT := sql.Table(organization.ReferrersTable) + s.Join(joinT).On(s.C(referrer.FieldID), joinT.C(organization.ReferrersPrimaryKey[0])) + s.Where(sql.InValues(joinT.C(organization.ReferrersPrimaryKey[1]), edgeIDs...)) + columns := s.SelectedColumns() + s.Select(joinT.C(organization.ReferrersPrimaryKey[1])) + s.AppendSelect(columns...) + s.SetDistinct(false) + }) + if err := query.prepareQuery(ctx); err != nil { + return err + } + qr := QuerierFunc(func(ctx context.Context, q Query) (Value, error) { + return query.sqlAll(ctx, func(_ context.Context, spec *sqlgraph.QuerySpec) { + assign := spec.Assign + values := spec.ScanValues + spec.ScanValues = func(columns []string) ([]any, error) { + values, err := values(columns[1:]) + if err != nil { + return nil, err + } + return append([]any{new(uuid.UUID)}, values...), nil + } + spec.Assign = func(columns []string, values []any) error { + outValue := *values[0].(*uuid.UUID) + inValue := *values[1].(*uuid.UUID) + if nids[inValue] == nil { + nids[inValue] = map[*Organization]struct{}{byID[outValue]: {}} + return assign(columns[1:], values[1:]) + } + nids[inValue][byID[outValue]] = struct{}{} + return nil + } + }) + }) + neighbors, err := withInterceptors[[]*Referrer](ctx, query, qr, query.inters) + if err != nil { + return err + } + for _, n := range neighbors { + nodes, ok := nids[n.ID] + if !ok { + return fmt.Errorf(`unexpected "referrers" node returned %v`, n.ID) + } + for kn := range nodes { + assign(kn, n) + } + } + return nil +} func (oq *OrganizationQuery) sqlCount(ctx context.Context) (int, error) { _spec := oq.querySpec() diff --git a/app/controlplane/internal/data/ent/organization_update.go b/app/controlplane/internal/data/ent/organization_update.go index a2ab8e2e3..750242de6 100644 --- a/app/controlplane/internal/data/ent/organization_update.go +++ b/app/controlplane/internal/data/ent/organization_update.go @@ -15,6 +15,7 @@ import ( "github.com/chainloop-dev/chainloop/app/controlplane/internal/data/ent/membership" "github.com/chainloop-dev/chainloop/app/controlplane/internal/data/ent/organization" "github.com/chainloop-dev/chainloop/app/controlplane/internal/data/ent/predicate" + "github.com/chainloop-dev/chainloop/app/controlplane/internal/data/ent/referrer" "github.com/chainloop-dev/chainloop/app/controlplane/internal/data/ent/workflow" "github.com/chainloop-dev/chainloop/app/controlplane/internal/data/ent/workflowcontract" "github.com/google/uuid" @@ -122,6 +123,21 @@ func (ou *OrganizationUpdate) AddIntegrations(i ...*Integration) *OrganizationUp return ou.AddIntegrationIDs(ids...) } +// AddReferrerIDs adds the "referrers" edge to the Referrer entity by IDs. +func (ou *OrganizationUpdate) AddReferrerIDs(ids ...uuid.UUID) *OrganizationUpdate { + ou.mutation.AddReferrerIDs(ids...) + return ou +} + +// AddReferrers adds the "referrers" edges to the Referrer entity. +func (ou *OrganizationUpdate) AddReferrers(r ...*Referrer) *OrganizationUpdate { + ids := make([]uuid.UUID, len(r)) + for i := range r { + ids[i] = r[i].ID + } + return ou.AddReferrerIDs(ids...) +} + // Mutation returns the OrganizationMutation object of the builder. func (ou *OrganizationUpdate) Mutation() *OrganizationMutation { return ou.mutation @@ -232,6 +248,27 @@ func (ou *OrganizationUpdate) RemoveIntegrations(i ...*Integration) *Organizatio return ou.RemoveIntegrationIDs(ids...) } +// ClearReferrers clears all "referrers" edges to the Referrer entity. +func (ou *OrganizationUpdate) ClearReferrers() *OrganizationUpdate { + ou.mutation.ClearReferrers() + return ou +} + +// RemoveReferrerIDs removes the "referrers" edge to Referrer entities by IDs. +func (ou *OrganizationUpdate) RemoveReferrerIDs(ids ...uuid.UUID) *OrganizationUpdate { + ou.mutation.RemoveReferrerIDs(ids...) + return ou +} + +// RemoveReferrers removes "referrers" edges to Referrer entities. +func (ou *OrganizationUpdate) RemoveReferrers(r ...*Referrer) *OrganizationUpdate { + ids := make([]uuid.UUID, len(r)) + for i := range r { + ids[i] = r[i].ID + } + return ou.RemoveReferrerIDs(ids...) +} + // Save executes the query and returns the number of nodes affected by the update operation. func (ou *OrganizationUpdate) Save(ctx context.Context) (int, error) { return withHooks(ctx, ou.sqlSave, ou.mutation, ou.hooks) @@ -496,6 +533,51 @@ func (ou *OrganizationUpdate) sqlSave(ctx context.Context) (n int, err error) { } _spec.Edges.Add = append(_spec.Edges.Add, edge) } + if ou.mutation.ReferrersCleared() { + edge := &sqlgraph.EdgeSpec{ + Rel: sqlgraph.M2M, + Inverse: true, + Table: organization.ReferrersTable, + Columns: organization.ReferrersPrimaryKey, + Bidi: false, + Target: &sqlgraph.EdgeTarget{ + IDSpec: sqlgraph.NewFieldSpec(referrer.FieldID, field.TypeUUID), + }, + } + _spec.Edges.Clear = append(_spec.Edges.Clear, edge) + } + if nodes := ou.mutation.RemovedReferrersIDs(); len(nodes) > 0 && !ou.mutation.ReferrersCleared() { + edge := &sqlgraph.EdgeSpec{ + Rel: sqlgraph.M2M, + Inverse: true, + Table: organization.ReferrersTable, + Columns: organization.ReferrersPrimaryKey, + Bidi: false, + Target: &sqlgraph.EdgeTarget{ + IDSpec: sqlgraph.NewFieldSpec(referrer.FieldID, field.TypeUUID), + }, + } + for _, k := range nodes { + edge.Target.Nodes = append(edge.Target.Nodes, k) + } + _spec.Edges.Clear = append(_spec.Edges.Clear, edge) + } + if nodes := ou.mutation.ReferrersIDs(); len(nodes) > 0 { + edge := &sqlgraph.EdgeSpec{ + Rel: sqlgraph.M2M, + Inverse: true, + Table: organization.ReferrersTable, + Columns: organization.ReferrersPrimaryKey, + Bidi: false, + Target: &sqlgraph.EdgeTarget{ + IDSpec: sqlgraph.NewFieldSpec(referrer.FieldID, field.TypeUUID), + }, + } + for _, k := range nodes { + edge.Target.Nodes = append(edge.Target.Nodes, k) + } + _spec.Edges.Add = append(_spec.Edges.Add, edge) + } if n, err = sqlgraph.UpdateNodes(ctx, ou.driver, _spec); err != nil { if _, ok := err.(*sqlgraph.NotFoundError); ok { err = &NotFoundError{organization.Label} @@ -605,6 +687,21 @@ func (ouo *OrganizationUpdateOne) AddIntegrations(i ...*Integration) *Organizati return ouo.AddIntegrationIDs(ids...) } +// AddReferrerIDs adds the "referrers" edge to the Referrer entity by IDs. +func (ouo *OrganizationUpdateOne) AddReferrerIDs(ids ...uuid.UUID) *OrganizationUpdateOne { + ouo.mutation.AddReferrerIDs(ids...) + return ouo +} + +// AddReferrers adds the "referrers" edges to the Referrer entity. +func (ouo *OrganizationUpdateOne) AddReferrers(r ...*Referrer) *OrganizationUpdateOne { + ids := make([]uuid.UUID, len(r)) + for i := range r { + ids[i] = r[i].ID + } + return ouo.AddReferrerIDs(ids...) +} + // Mutation returns the OrganizationMutation object of the builder. func (ouo *OrganizationUpdateOne) Mutation() *OrganizationMutation { return ouo.mutation @@ -715,6 +812,27 @@ func (ouo *OrganizationUpdateOne) RemoveIntegrations(i ...*Integration) *Organiz return ouo.RemoveIntegrationIDs(ids...) } +// ClearReferrers clears all "referrers" edges to the Referrer entity. +func (ouo *OrganizationUpdateOne) ClearReferrers() *OrganizationUpdateOne { + ouo.mutation.ClearReferrers() + return ouo +} + +// RemoveReferrerIDs removes the "referrers" edge to Referrer entities by IDs. +func (ouo *OrganizationUpdateOne) RemoveReferrerIDs(ids ...uuid.UUID) *OrganizationUpdateOne { + ouo.mutation.RemoveReferrerIDs(ids...) + return ouo +} + +// RemoveReferrers removes "referrers" edges to Referrer entities. +func (ouo *OrganizationUpdateOne) RemoveReferrers(r ...*Referrer) *OrganizationUpdateOne { + ids := make([]uuid.UUID, len(r)) + for i := range r { + ids[i] = r[i].ID + } + return ouo.RemoveReferrerIDs(ids...) +} + // Where appends a list predicates to the OrganizationUpdate builder. func (ouo *OrganizationUpdateOne) Where(ps ...predicate.Organization) *OrganizationUpdateOne { ouo.mutation.Where(ps...) @@ -1009,6 +1127,51 @@ func (ouo *OrganizationUpdateOne) sqlSave(ctx context.Context) (_node *Organizat } _spec.Edges.Add = append(_spec.Edges.Add, edge) } + if ouo.mutation.ReferrersCleared() { + edge := &sqlgraph.EdgeSpec{ + Rel: sqlgraph.M2M, + Inverse: true, + Table: organization.ReferrersTable, + Columns: organization.ReferrersPrimaryKey, + Bidi: false, + Target: &sqlgraph.EdgeTarget{ + IDSpec: sqlgraph.NewFieldSpec(referrer.FieldID, field.TypeUUID), + }, + } + _spec.Edges.Clear = append(_spec.Edges.Clear, edge) + } + if nodes := ouo.mutation.RemovedReferrersIDs(); len(nodes) > 0 && !ouo.mutation.ReferrersCleared() { + edge := &sqlgraph.EdgeSpec{ + Rel: sqlgraph.M2M, + Inverse: true, + Table: organization.ReferrersTable, + Columns: organization.ReferrersPrimaryKey, + Bidi: false, + Target: &sqlgraph.EdgeTarget{ + IDSpec: sqlgraph.NewFieldSpec(referrer.FieldID, field.TypeUUID), + }, + } + for _, k := range nodes { + edge.Target.Nodes = append(edge.Target.Nodes, k) + } + _spec.Edges.Clear = append(_spec.Edges.Clear, edge) + } + if nodes := ouo.mutation.ReferrersIDs(); len(nodes) > 0 { + edge := &sqlgraph.EdgeSpec{ + Rel: sqlgraph.M2M, + Inverse: true, + Table: organization.ReferrersTable, + Columns: organization.ReferrersPrimaryKey, + Bidi: false, + Target: &sqlgraph.EdgeTarget{ + IDSpec: sqlgraph.NewFieldSpec(referrer.FieldID, field.TypeUUID), + }, + } + for _, k := range nodes { + edge.Target.Nodes = append(edge.Target.Nodes, k) + } + _spec.Edges.Add = append(_spec.Edges.Add, edge) + } _node = &Organization{config: ouo.config} _spec.Assign = _node.assignValues _spec.ScanValues = _node.scanValues diff --git a/app/controlplane/internal/data/ent/predicate/predicate.go b/app/controlplane/internal/data/ent/predicate/predicate.go index a563b3b32..029e7a6af 100644 --- a/app/controlplane/internal/data/ent/predicate/predicate.go +++ b/app/controlplane/internal/data/ent/predicate/predicate.go @@ -27,6 +27,9 @@ type OrgInvitation func(*sql.Selector) // Organization is the predicate function for organization builders. type Organization func(*sql.Selector) +// Referrer is the predicate function for referrer builders. +type Referrer func(*sql.Selector) + // RobotAccount is the predicate function for robotaccount builders. type RobotAccount func(*sql.Selector) diff --git a/app/controlplane/internal/data/ent/referrer.go b/app/controlplane/internal/data/ent/referrer.go new file mode 100644 index 000000000..96d0cef08 --- /dev/null +++ b/app/controlplane/internal/data/ent/referrer.go @@ -0,0 +1,200 @@ +// Code generated by ent, DO NOT EDIT. + +package ent + +import ( + "fmt" + "strings" + "time" + + "entgo.io/ent" + "entgo.io/ent/dialect/sql" + "github.com/chainloop-dev/chainloop/app/controlplane/internal/data/ent/referrer" + "github.com/google/uuid" +) + +// Referrer is the model entity for the Referrer schema. +type Referrer struct { + config `json:"-"` + // ID of the ent. + ID uuid.UUID `json:"id,omitempty"` + // Digest holds the value of the "digest" field. + Digest string `json:"digest,omitempty"` + // ArtifactType holds the value of the "artifact_type" field. + ArtifactType string `json:"artifact_type,omitempty"` + // Downloadable holds the value of the "downloadable" field. + Downloadable bool `json:"downloadable,omitempty"` + // CreatedAt holds the value of the "created_at" field. + CreatedAt time.Time `json:"created_at,omitempty"` + // Edges holds the relations/edges for other nodes in the graph. + // The values are being populated by the ReferrerQuery when eager-loading is set. + Edges ReferrerEdges `json:"edges"` + selectValues sql.SelectValues +} + +// ReferrerEdges holds the relations/edges for other nodes in the graph. +type ReferrerEdges struct { + // ReferredBy holds the value of the referred_by edge. + ReferredBy []*Referrer `json:"referred_by,omitempty"` + // References holds the value of the references edge. + References []*Referrer `json:"references,omitempty"` + // Organizations holds the value of the organizations edge. + Organizations []*Organization `json:"organizations,omitempty"` + // loadedTypes holds the information for reporting if a + // type was loaded (or requested) in eager-loading or not. + loadedTypes [3]bool +} + +// ReferredByOrErr returns the ReferredBy value or an error if the edge +// was not loaded in eager-loading. +func (e ReferrerEdges) ReferredByOrErr() ([]*Referrer, error) { + if e.loadedTypes[0] { + return e.ReferredBy, nil + } + return nil, &NotLoadedError{edge: "referred_by"} +} + +// ReferencesOrErr returns the References value or an error if the edge +// was not loaded in eager-loading. +func (e ReferrerEdges) ReferencesOrErr() ([]*Referrer, error) { + if e.loadedTypes[1] { + return e.References, nil + } + return nil, &NotLoadedError{edge: "references"} +} + +// OrganizationsOrErr returns the Organizations value or an error if the edge +// was not loaded in eager-loading. +func (e ReferrerEdges) OrganizationsOrErr() ([]*Organization, error) { + if e.loadedTypes[2] { + return e.Organizations, nil + } + return nil, &NotLoadedError{edge: "organizations"} +} + +// scanValues returns the types for scanning values from sql.Rows. +func (*Referrer) scanValues(columns []string) ([]any, error) { + values := make([]any, len(columns)) + for i := range columns { + switch columns[i] { + case referrer.FieldDownloadable: + values[i] = new(sql.NullBool) + case referrer.FieldDigest, referrer.FieldArtifactType: + values[i] = new(sql.NullString) + case referrer.FieldCreatedAt: + values[i] = new(sql.NullTime) + case referrer.FieldID: + values[i] = new(uuid.UUID) + default: + values[i] = new(sql.UnknownType) + } + } + return values, nil +} + +// assignValues assigns the values that were returned from sql.Rows (after scanning) +// to the Referrer fields. +func (r *Referrer) assignValues(columns []string, values []any) error { + if m, n := len(values), len(columns); m < n { + return fmt.Errorf("mismatch number of scan values: %d != %d", m, n) + } + for i := range columns { + switch columns[i] { + case referrer.FieldID: + if value, ok := values[i].(*uuid.UUID); !ok { + return fmt.Errorf("unexpected type %T for field id", values[i]) + } else if value != nil { + r.ID = *value + } + case referrer.FieldDigest: + if value, ok := values[i].(*sql.NullString); !ok { + return fmt.Errorf("unexpected type %T for field digest", values[i]) + } else if value.Valid { + r.Digest = value.String + } + case referrer.FieldArtifactType: + if value, ok := values[i].(*sql.NullString); !ok { + return fmt.Errorf("unexpected type %T for field artifact_type", values[i]) + } else if value.Valid { + r.ArtifactType = value.String + } + case referrer.FieldDownloadable: + if value, ok := values[i].(*sql.NullBool); !ok { + return fmt.Errorf("unexpected type %T for field downloadable", values[i]) + } else if value.Valid { + r.Downloadable = value.Bool + } + case referrer.FieldCreatedAt: + if value, ok := values[i].(*sql.NullTime); !ok { + return fmt.Errorf("unexpected type %T for field created_at", values[i]) + } else if value.Valid { + r.CreatedAt = value.Time + } + default: + r.selectValues.Set(columns[i], values[i]) + } + } + return nil +} + +// Value returns the ent.Value that was dynamically selected and assigned to the Referrer. +// This includes values selected through modifiers, order, etc. +func (r *Referrer) Value(name string) (ent.Value, error) { + return r.selectValues.Get(name) +} + +// QueryReferredBy queries the "referred_by" edge of the Referrer entity. +func (r *Referrer) QueryReferredBy() *ReferrerQuery { + return NewReferrerClient(r.config).QueryReferredBy(r) +} + +// QueryReferences queries the "references" edge of the Referrer entity. +func (r *Referrer) QueryReferences() *ReferrerQuery { + return NewReferrerClient(r.config).QueryReferences(r) +} + +// QueryOrganizations queries the "organizations" edge of the Referrer entity. +func (r *Referrer) QueryOrganizations() *OrganizationQuery { + return NewReferrerClient(r.config).QueryOrganizations(r) +} + +// Update returns a builder for updating this Referrer. +// Note that you need to call Referrer.Unwrap() before calling this method if this Referrer +// was returned from a transaction, and the transaction was committed or rolled back. +func (r *Referrer) Update() *ReferrerUpdateOne { + return NewReferrerClient(r.config).UpdateOne(r) +} + +// Unwrap unwraps the Referrer entity that was returned from a transaction after it was closed, +// so that all future queries will be executed through the driver which created the transaction. +func (r *Referrer) Unwrap() *Referrer { + _tx, ok := r.config.driver.(*txDriver) + if !ok { + panic("ent: Referrer is not a transactional entity") + } + r.config.driver = _tx.drv + return r +} + +// String implements the fmt.Stringer. +func (r *Referrer) String() string { + var builder strings.Builder + builder.WriteString("Referrer(") + builder.WriteString(fmt.Sprintf("id=%v, ", r.ID)) + builder.WriteString("digest=") + builder.WriteString(r.Digest) + builder.WriteString(", ") + builder.WriteString("artifact_type=") + builder.WriteString(r.ArtifactType) + builder.WriteString(", ") + builder.WriteString("downloadable=") + builder.WriteString(fmt.Sprintf("%v", r.Downloadable)) + builder.WriteString(", ") + builder.WriteString("created_at=") + builder.WriteString(r.CreatedAt.Format(time.ANSIC)) + builder.WriteByte(')') + return builder.String() +} + +// Referrers is a parsable slice of Referrer. +type Referrers []*Referrer diff --git a/app/controlplane/internal/data/ent/referrer/referrer.go b/app/controlplane/internal/data/ent/referrer/referrer.go new file mode 100644 index 000000000..6ec600473 --- /dev/null +++ b/app/controlplane/internal/data/ent/referrer/referrer.go @@ -0,0 +1,172 @@ +// Code generated by ent, DO NOT EDIT. + +package referrer + +import ( + "time" + + "entgo.io/ent/dialect/sql" + "entgo.io/ent/dialect/sql/sqlgraph" + "github.com/google/uuid" +) + +const ( + // Label holds the string label denoting the referrer type in the database. + Label = "referrer" + // FieldID holds the string denoting the id field in the database. + FieldID = "id" + // FieldDigest holds the string denoting the digest field in the database. + FieldDigest = "digest" + // FieldArtifactType holds the string denoting the artifact_type field in the database. + FieldArtifactType = "artifact_type" + // FieldDownloadable holds the string denoting the downloadable field in the database. + FieldDownloadable = "downloadable" + // FieldCreatedAt holds the string denoting the created_at field in the database. + FieldCreatedAt = "created_at" + // EdgeReferredBy holds the string denoting the referred_by edge name in mutations. + EdgeReferredBy = "referred_by" + // EdgeReferences holds the string denoting the references edge name in mutations. + EdgeReferences = "references" + // EdgeOrganizations holds the string denoting the organizations edge name in mutations. + EdgeOrganizations = "organizations" + // Table holds the table name of the referrer in the database. + Table = "referrers" + // ReferredByTable is the table that holds the referred_by relation/edge. The primary key declared below. + ReferredByTable = "referrer_references" + // ReferencesTable is the table that holds the references relation/edge. The primary key declared below. + ReferencesTable = "referrer_references" + // OrganizationsTable is the table that holds the organizations relation/edge. The primary key declared below. + OrganizationsTable = "referrer_organizations" + // OrganizationsInverseTable is the table name for the Organization entity. + // It exists in this package in order to avoid circular dependency with the "organization" package. + OrganizationsInverseTable = "organizations" +) + +// Columns holds all SQL columns for referrer fields. +var Columns = []string{ + FieldID, + FieldDigest, + FieldArtifactType, + FieldDownloadable, + FieldCreatedAt, +} + +var ( + // ReferredByPrimaryKey and ReferredByColumn2 are the table columns denoting the + // primary key for the referred_by relation (M2M). + ReferredByPrimaryKey = []string{"referrer_id", "referred_by_id"} + // ReferencesPrimaryKey and ReferencesColumn2 are the table columns denoting the + // primary key for the references relation (M2M). + ReferencesPrimaryKey = []string{"referrer_id", "referred_by_id"} + // OrganizationsPrimaryKey and OrganizationsColumn2 are the table columns denoting the + // primary key for the organizations relation (M2M). + OrganizationsPrimaryKey = []string{"referrer_id", "organization_id"} +) + +// ValidColumn reports if the column name is valid (part of the table columns). +func ValidColumn(column string) bool { + for i := range Columns { + if column == Columns[i] { + return true + } + } + return false +} + +var ( + // DefaultCreatedAt holds the default value on creation for the "created_at" field. + DefaultCreatedAt func() time.Time + // DefaultID holds the default value on creation for the "id" field. + DefaultID func() uuid.UUID +) + +// OrderOption defines the ordering options for the Referrer queries. +type OrderOption func(*sql.Selector) + +// ByID orders the results by the id field. +func ByID(opts ...sql.OrderTermOption) OrderOption { + return sql.OrderByField(FieldID, opts...).ToFunc() +} + +// ByDigest orders the results by the digest field. +func ByDigest(opts ...sql.OrderTermOption) OrderOption { + return sql.OrderByField(FieldDigest, opts...).ToFunc() +} + +// ByArtifactType orders the results by the artifact_type field. +func ByArtifactType(opts ...sql.OrderTermOption) OrderOption { + return sql.OrderByField(FieldArtifactType, opts...).ToFunc() +} + +// ByDownloadable orders the results by the downloadable field. +func ByDownloadable(opts ...sql.OrderTermOption) OrderOption { + return sql.OrderByField(FieldDownloadable, opts...).ToFunc() +} + +// ByCreatedAt orders the results by the created_at field. +func ByCreatedAt(opts ...sql.OrderTermOption) OrderOption { + return sql.OrderByField(FieldCreatedAt, opts...).ToFunc() +} + +// ByReferredByCount orders the results by referred_by count. +func ByReferredByCount(opts ...sql.OrderTermOption) OrderOption { + return func(s *sql.Selector) { + sqlgraph.OrderByNeighborsCount(s, newReferredByStep(), opts...) + } +} + +// ByReferredBy orders the results by referred_by terms. +func ByReferredBy(term sql.OrderTerm, terms ...sql.OrderTerm) OrderOption { + return func(s *sql.Selector) { + sqlgraph.OrderByNeighborTerms(s, newReferredByStep(), append([]sql.OrderTerm{term}, terms...)...) + } +} + +// ByReferencesCount orders the results by references count. +func ByReferencesCount(opts ...sql.OrderTermOption) OrderOption { + return func(s *sql.Selector) { + sqlgraph.OrderByNeighborsCount(s, newReferencesStep(), opts...) + } +} + +// ByReferences orders the results by references terms. +func ByReferences(term sql.OrderTerm, terms ...sql.OrderTerm) OrderOption { + return func(s *sql.Selector) { + sqlgraph.OrderByNeighborTerms(s, newReferencesStep(), append([]sql.OrderTerm{term}, terms...)...) + } +} + +// ByOrganizationsCount orders the results by organizations count. +func ByOrganizationsCount(opts ...sql.OrderTermOption) OrderOption { + return func(s *sql.Selector) { + sqlgraph.OrderByNeighborsCount(s, newOrganizationsStep(), opts...) + } +} + +// ByOrganizations orders the results by organizations terms. +func ByOrganizations(term sql.OrderTerm, terms ...sql.OrderTerm) OrderOption { + return func(s *sql.Selector) { + sqlgraph.OrderByNeighborTerms(s, newOrganizationsStep(), append([]sql.OrderTerm{term}, terms...)...) + } +} +func newReferredByStep() *sqlgraph.Step { + return sqlgraph.NewStep( + sqlgraph.From(Table, FieldID), + sqlgraph.To(Table, FieldID), + sqlgraph.Edge(sqlgraph.M2M, true, ReferredByTable, ReferredByPrimaryKey...), + ) +} +func newReferencesStep() *sqlgraph.Step { + return sqlgraph.NewStep( + sqlgraph.From(Table, FieldID), + sqlgraph.To(Table, FieldID), + sqlgraph.Edge(sqlgraph.M2M, false, ReferencesTable, ReferencesPrimaryKey...), + ) +} +func newOrganizationsStep() *sqlgraph.Step { + return sqlgraph.NewStep( + sqlgraph.From(Table, FieldID), + sqlgraph.To(OrganizationsInverseTable, FieldID), + sqlgraph.Edge(sqlgraph.M2M, false, OrganizationsTable, OrganizationsPrimaryKey...), + ) +} diff --git a/app/controlplane/internal/data/ent/referrer/where.go b/app/controlplane/internal/data/ent/referrer/where.go new file mode 100644 index 000000000..509e93d99 --- /dev/null +++ b/app/controlplane/internal/data/ent/referrer/where.go @@ -0,0 +1,358 @@ +// Code generated by ent, DO NOT EDIT. + +package referrer + +import ( + "time" + + "entgo.io/ent/dialect/sql" + "entgo.io/ent/dialect/sql/sqlgraph" + "github.com/chainloop-dev/chainloop/app/controlplane/internal/data/ent/predicate" + "github.com/google/uuid" +) + +// ID filters vertices based on their ID field. +func ID(id uuid.UUID) predicate.Referrer { + return predicate.Referrer(sql.FieldEQ(FieldID, id)) +} + +// IDEQ applies the EQ predicate on the ID field. +func IDEQ(id uuid.UUID) predicate.Referrer { + return predicate.Referrer(sql.FieldEQ(FieldID, id)) +} + +// IDNEQ applies the NEQ predicate on the ID field. +func IDNEQ(id uuid.UUID) predicate.Referrer { + return predicate.Referrer(sql.FieldNEQ(FieldID, id)) +} + +// IDIn applies the In predicate on the ID field. +func IDIn(ids ...uuid.UUID) predicate.Referrer { + return predicate.Referrer(sql.FieldIn(FieldID, ids...)) +} + +// IDNotIn applies the NotIn predicate on the ID field. +func IDNotIn(ids ...uuid.UUID) predicate.Referrer { + return predicate.Referrer(sql.FieldNotIn(FieldID, ids...)) +} + +// IDGT applies the GT predicate on the ID field. +func IDGT(id uuid.UUID) predicate.Referrer { + return predicate.Referrer(sql.FieldGT(FieldID, id)) +} + +// IDGTE applies the GTE predicate on the ID field. +func IDGTE(id uuid.UUID) predicate.Referrer { + return predicate.Referrer(sql.FieldGTE(FieldID, id)) +} + +// IDLT applies the LT predicate on the ID field. +func IDLT(id uuid.UUID) predicate.Referrer { + return predicate.Referrer(sql.FieldLT(FieldID, id)) +} + +// IDLTE applies the LTE predicate on the ID field. +func IDLTE(id uuid.UUID) predicate.Referrer { + return predicate.Referrer(sql.FieldLTE(FieldID, id)) +} + +// Digest applies equality check predicate on the "digest" field. It's identical to DigestEQ. +func Digest(v string) predicate.Referrer { + return predicate.Referrer(sql.FieldEQ(FieldDigest, v)) +} + +// ArtifactType applies equality check predicate on the "artifact_type" field. It's identical to ArtifactTypeEQ. +func ArtifactType(v string) predicate.Referrer { + return predicate.Referrer(sql.FieldEQ(FieldArtifactType, v)) +} + +// Downloadable applies equality check predicate on the "downloadable" field. It's identical to DownloadableEQ. +func Downloadable(v bool) predicate.Referrer { + return predicate.Referrer(sql.FieldEQ(FieldDownloadable, v)) +} + +// CreatedAt applies equality check predicate on the "created_at" field. It's identical to CreatedAtEQ. +func CreatedAt(v time.Time) predicate.Referrer { + return predicate.Referrer(sql.FieldEQ(FieldCreatedAt, v)) +} + +// DigestEQ applies the EQ predicate on the "digest" field. +func DigestEQ(v string) predicate.Referrer { + return predicate.Referrer(sql.FieldEQ(FieldDigest, v)) +} + +// DigestNEQ applies the NEQ predicate on the "digest" field. +func DigestNEQ(v string) predicate.Referrer { + return predicate.Referrer(sql.FieldNEQ(FieldDigest, v)) +} + +// DigestIn applies the In predicate on the "digest" field. +func DigestIn(vs ...string) predicate.Referrer { + return predicate.Referrer(sql.FieldIn(FieldDigest, vs...)) +} + +// DigestNotIn applies the NotIn predicate on the "digest" field. +func DigestNotIn(vs ...string) predicate.Referrer { + return predicate.Referrer(sql.FieldNotIn(FieldDigest, vs...)) +} + +// DigestGT applies the GT predicate on the "digest" field. +func DigestGT(v string) predicate.Referrer { + return predicate.Referrer(sql.FieldGT(FieldDigest, v)) +} + +// DigestGTE applies the GTE predicate on the "digest" field. +func DigestGTE(v string) predicate.Referrer { + return predicate.Referrer(sql.FieldGTE(FieldDigest, v)) +} + +// DigestLT applies the LT predicate on the "digest" field. +func DigestLT(v string) predicate.Referrer { + return predicate.Referrer(sql.FieldLT(FieldDigest, v)) +} + +// DigestLTE applies the LTE predicate on the "digest" field. +func DigestLTE(v string) predicate.Referrer { + return predicate.Referrer(sql.FieldLTE(FieldDigest, v)) +} + +// DigestContains applies the Contains predicate on the "digest" field. +func DigestContains(v string) predicate.Referrer { + return predicate.Referrer(sql.FieldContains(FieldDigest, v)) +} + +// DigestHasPrefix applies the HasPrefix predicate on the "digest" field. +func DigestHasPrefix(v string) predicate.Referrer { + return predicate.Referrer(sql.FieldHasPrefix(FieldDigest, v)) +} + +// DigestHasSuffix applies the HasSuffix predicate on the "digest" field. +func DigestHasSuffix(v string) predicate.Referrer { + return predicate.Referrer(sql.FieldHasSuffix(FieldDigest, v)) +} + +// DigestEqualFold applies the EqualFold predicate on the "digest" field. +func DigestEqualFold(v string) predicate.Referrer { + return predicate.Referrer(sql.FieldEqualFold(FieldDigest, v)) +} + +// DigestContainsFold applies the ContainsFold predicate on the "digest" field. +func DigestContainsFold(v string) predicate.Referrer { + return predicate.Referrer(sql.FieldContainsFold(FieldDigest, v)) +} + +// ArtifactTypeEQ applies the EQ predicate on the "artifact_type" field. +func ArtifactTypeEQ(v string) predicate.Referrer { + return predicate.Referrer(sql.FieldEQ(FieldArtifactType, v)) +} + +// ArtifactTypeNEQ applies the NEQ predicate on the "artifact_type" field. +func ArtifactTypeNEQ(v string) predicate.Referrer { + return predicate.Referrer(sql.FieldNEQ(FieldArtifactType, v)) +} + +// ArtifactTypeIn applies the In predicate on the "artifact_type" field. +func ArtifactTypeIn(vs ...string) predicate.Referrer { + return predicate.Referrer(sql.FieldIn(FieldArtifactType, vs...)) +} + +// ArtifactTypeNotIn applies the NotIn predicate on the "artifact_type" field. +func ArtifactTypeNotIn(vs ...string) predicate.Referrer { + return predicate.Referrer(sql.FieldNotIn(FieldArtifactType, vs...)) +} + +// ArtifactTypeGT applies the GT predicate on the "artifact_type" field. +func ArtifactTypeGT(v string) predicate.Referrer { + return predicate.Referrer(sql.FieldGT(FieldArtifactType, v)) +} + +// ArtifactTypeGTE applies the GTE predicate on the "artifact_type" field. +func ArtifactTypeGTE(v string) predicate.Referrer { + return predicate.Referrer(sql.FieldGTE(FieldArtifactType, v)) +} + +// ArtifactTypeLT applies the LT predicate on the "artifact_type" field. +func ArtifactTypeLT(v string) predicate.Referrer { + return predicate.Referrer(sql.FieldLT(FieldArtifactType, v)) +} + +// ArtifactTypeLTE applies the LTE predicate on the "artifact_type" field. +func ArtifactTypeLTE(v string) predicate.Referrer { + return predicate.Referrer(sql.FieldLTE(FieldArtifactType, v)) +} + +// ArtifactTypeContains applies the Contains predicate on the "artifact_type" field. +func ArtifactTypeContains(v string) predicate.Referrer { + return predicate.Referrer(sql.FieldContains(FieldArtifactType, v)) +} + +// ArtifactTypeHasPrefix applies the HasPrefix predicate on the "artifact_type" field. +func ArtifactTypeHasPrefix(v string) predicate.Referrer { + return predicate.Referrer(sql.FieldHasPrefix(FieldArtifactType, v)) +} + +// ArtifactTypeHasSuffix applies the HasSuffix predicate on the "artifact_type" field. +func ArtifactTypeHasSuffix(v string) predicate.Referrer { + return predicate.Referrer(sql.FieldHasSuffix(FieldArtifactType, v)) +} + +// ArtifactTypeEqualFold applies the EqualFold predicate on the "artifact_type" field. +func ArtifactTypeEqualFold(v string) predicate.Referrer { + return predicate.Referrer(sql.FieldEqualFold(FieldArtifactType, v)) +} + +// ArtifactTypeContainsFold applies the ContainsFold predicate on the "artifact_type" field. +func ArtifactTypeContainsFold(v string) predicate.Referrer { + return predicate.Referrer(sql.FieldContainsFold(FieldArtifactType, v)) +} + +// DownloadableEQ applies the EQ predicate on the "downloadable" field. +func DownloadableEQ(v bool) predicate.Referrer { + return predicate.Referrer(sql.FieldEQ(FieldDownloadable, v)) +} + +// DownloadableNEQ applies the NEQ predicate on the "downloadable" field. +func DownloadableNEQ(v bool) predicate.Referrer { + return predicate.Referrer(sql.FieldNEQ(FieldDownloadable, v)) +} + +// CreatedAtEQ applies the EQ predicate on the "created_at" field. +func CreatedAtEQ(v time.Time) predicate.Referrer { + return predicate.Referrer(sql.FieldEQ(FieldCreatedAt, v)) +} + +// CreatedAtNEQ applies the NEQ predicate on the "created_at" field. +func CreatedAtNEQ(v time.Time) predicate.Referrer { + return predicate.Referrer(sql.FieldNEQ(FieldCreatedAt, v)) +} + +// CreatedAtIn applies the In predicate on the "created_at" field. +func CreatedAtIn(vs ...time.Time) predicate.Referrer { + return predicate.Referrer(sql.FieldIn(FieldCreatedAt, vs...)) +} + +// CreatedAtNotIn applies the NotIn predicate on the "created_at" field. +func CreatedAtNotIn(vs ...time.Time) predicate.Referrer { + return predicate.Referrer(sql.FieldNotIn(FieldCreatedAt, vs...)) +} + +// CreatedAtGT applies the GT predicate on the "created_at" field. +func CreatedAtGT(v time.Time) predicate.Referrer { + return predicate.Referrer(sql.FieldGT(FieldCreatedAt, v)) +} + +// CreatedAtGTE applies the GTE predicate on the "created_at" field. +func CreatedAtGTE(v time.Time) predicate.Referrer { + return predicate.Referrer(sql.FieldGTE(FieldCreatedAt, v)) +} + +// CreatedAtLT applies the LT predicate on the "created_at" field. +func CreatedAtLT(v time.Time) predicate.Referrer { + return predicate.Referrer(sql.FieldLT(FieldCreatedAt, v)) +} + +// CreatedAtLTE applies the LTE predicate on the "created_at" field. +func CreatedAtLTE(v time.Time) predicate.Referrer { + return predicate.Referrer(sql.FieldLTE(FieldCreatedAt, v)) +} + +// HasReferredBy applies the HasEdge predicate on the "referred_by" edge. +func HasReferredBy() predicate.Referrer { + return predicate.Referrer(func(s *sql.Selector) { + step := sqlgraph.NewStep( + sqlgraph.From(Table, FieldID), + sqlgraph.Edge(sqlgraph.M2M, true, ReferredByTable, ReferredByPrimaryKey...), + ) + sqlgraph.HasNeighbors(s, step) + }) +} + +// HasReferredByWith applies the HasEdge predicate on the "referred_by" edge with a given conditions (other predicates). +func HasReferredByWith(preds ...predicate.Referrer) predicate.Referrer { + return predicate.Referrer(func(s *sql.Selector) { + step := newReferredByStep() + sqlgraph.HasNeighborsWith(s, step, func(s *sql.Selector) { + for _, p := range preds { + p(s) + } + }) + }) +} + +// HasReferences applies the HasEdge predicate on the "references" edge. +func HasReferences() predicate.Referrer { + return predicate.Referrer(func(s *sql.Selector) { + step := sqlgraph.NewStep( + sqlgraph.From(Table, FieldID), + sqlgraph.Edge(sqlgraph.M2M, false, ReferencesTable, ReferencesPrimaryKey...), + ) + sqlgraph.HasNeighbors(s, step) + }) +} + +// HasReferencesWith applies the HasEdge predicate on the "references" edge with a given conditions (other predicates). +func HasReferencesWith(preds ...predicate.Referrer) predicate.Referrer { + return predicate.Referrer(func(s *sql.Selector) { + step := newReferencesStep() + sqlgraph.HasNeighborsWith(s, step, func(s *sql.Selector) { + for _, p := range preds { + p(s) + } + }) + }) +} + +// HasOrganizations applies the HasEdge predicate on the "organizations" edge. +func HasOrganizations() predicate.Referrer { + return predicate.Referrer(func(s *sql.Selector) { + step := sqlgraph.NewStep( + sqlgraph.From(Table, FieldID), + sqlgraph.Edge(sqlgraph.M2M, false, OrganizationsTable, OrganizationsPrimaryKey...), + ) + sqlgraph.HasNeighbors(s, step) + }) +} + +// HasOrganizationsWith applies the HasEdge predicate on the "organizations" edge with a given conditions (other predicates). +func HasOrganizationsWith(preds ...predicate.Organization) predicate.Referrer { + return predicate.Referrer(func(s *sql.Selector) { + step := newOrganizationsStep() + sqlgraph.HasNeighborsWith(s, step, func(s *sql.Selector) { + for _, p := range preds { + p(s) + } + }) + }) +} + +// And groups predicates with the AND operator between them. +func And(predicates ...predicate.Referrer) predicate.Referrer { + return predicate.Referrer(func(s *sql.Selector) { + s1 := s.Clone().SetP(nil) + for _, p := range predicates { + p(s1) + } + s.Where(s1.P()) + }) +} + +// Or groups predicates with the OR operator between them. +func Or(predicates ...predicate.Referrer) predicate.Referrer { + return predicate.Referrer(func(s *sql.Selector) { + s1 := s.Clone().SetP(nil) + for i, p := range predicates { + if i > 0 { + s1.Or() + } + p(s1) + } + s.Where(s1.P()) + }) +} + +// Not applies the not operator on the given predicate. +func Not(p predicate.Referrer) predicate.Referrer { + return predicate.Referrer(func(s *sql.Selector) { + p(s.Not()) + }) +} diff --git a/app/controlplane/internal/data/ent/referrer_create.go b/app/controlplane/internal/data/ent/referrer_create.go new file mode 100644 index 000000000..a686ae2f1 --- /dev/null +++ b/app/controlplane/internal/data/ent/referrer_create.go @@ -0,0 +1,355 @@ +// Code generated by ent, DO NOT EDIT. + +package ent + +import ( + "context" + "errors" + "fmt" + "time" + + "entgo.io/ent/dialect/sql/sqlgraph" + "entgo.io/ent/schema/field" + "github.com/chainloop-dev/chainloop/app/controlplane/internal/data/ent/organization" + "github.com/chainloop-dev/chainloop/app/controlplane/internal/data/ent/referrer" + "github.com/google/uuid" +) + +// ReferrerCreate is the builder for creating a Referrer entity. +type ReferrerCreate struct { + config + mutation *ReferrerMutation + hooks []Hook +} + +// SetDigest sets the "digest" field. +func (rc *ReferrerCreate) SetDigest(s string) *ReferrerCreate { + rc.mutation.SetDigest(s) + return rc +} + +// SetArtifactType sets the "artifact_type" field. +func (rc *ReferrerCreate) SetArtifactType(s string) *ReferrerCreate { + rc.mutation.SetArtifactType(s) + return rc +} + +// SetDownloadable sets the "downloadable" field. +func (rc *ReferrerCreate) SetDownloadable(b bool) *ReferrerCreate { + rc.mutation.SetDownloadable(b) + return rc +} + +// SetCreatedAt sets the "created_at" field. +func (rc *ReferrerCreate) SetCreatedAt(t time.Time) *ReferrerCreate { + rc.mutation.SetCreatedAt(t) + return rc +} + +// SetNillableCreatedAt sets the "created_at" field if the given value is not nil. +func (rc *ReferrerCreate) SetNillableCreatedAt(t *time.Time) *ReferrerCreate { + if t != nil { + rc.SetCreatedAt(*t) + } + return rc +} + +// SetID sets the "id" field. +func (rc *ReferrerCreate) SetID(u uuid.UUID) *ReferrerCreate { + rc.mutation.SetID(u) + return rc +} + +// SetNillableID sets the "id" field if the given value is not nil. +func (rc *ReferrerCreate) SetNillableID(u *uuid.UUID) *ReferrerCreate { + if u != nil { + rc.SetID(*u) + } + return rc +} + +// AddReferredByIDs adds the "referred_by" edge to the Referrer entity by IDs. +func (rc *ReferrerCreate) AddReferredByIDs(ids ...uuid.UUID) *ReferrerCreate { + rc.mutation.AddReferredByIDs(ids...) + return rc +} + +// AddReferredBy adds the "referred_by" edges to the Referrer entity. +func (rc *ReferrerCreate) AddReferredBy(r ...*Referrer) *ReferrerCreate { + ids := make([]uuid.UUID, len(r)) + for i := range r { + ids[i] = r[i].ID + } + return rc.AddReferredByIDs(ids...) +} + +// AddReferenceIDs adds the "references" edge to the Referrer entity by IDs. +func (rc *ReferrerCreate) AddReferenceIDs(ids ...uuid.UUID) *ReferrerCreate { + rc.mutation.AddReferenceIDs(ids...) + return rc +} + +// AddReferences adds the "references" edges to the Referrer entity. +func (rc *ReferrerCreate) AddReferences(r ...*Referrer) *ReferrerCreate { + ids := make([]uuid.UUID, len(r)) + for i := range r { + ids[i] = r[i].ID + } + return rc.AddReferenceIDs(ids...) +} + +// AddOrganizationIDs adds the "organizations" edge to the Organization entity by IDs. +func (rc *ReferrerCreate) AddOrganizationIDs(ids ...uuid.UUID) *ReferrerCreate { + rc.mutation.AddOrganizationIDs(ids...) + return rc +} + +// AddOrganizations adds the "organizations" edges to the Organization entity. +func (rc *ReferrerCreate) AddOrganizations(o ...*Organization) *ReferrerCreate { + ids := make([]uuid.UUID, len(o)) + for i := range o { + ids[i] = o[i].ID + } + return rc.AddOrganizationIDs(ids...) +} + +// Mutation returns the ReferrerMutation object of the builder. +func (rc *ReferrerCreate) Mutation() *ReferrerMutation { + return rc.mutation +} + +// Save creates the Referrer in the database. +func (rc *ReferrerCreate) Save(ctx context.Context) (*Referrer, error) { + rc.defaults() + return withHooks(ctx, rc.sqlSave, rc.mutation, rc.hooks) +} + +// SaveX calls Save and panics if Save returns an error. +func (rc *ReferrerCreate) SaveX(ctx context.Context) *Referrer { + v, err := rc.Save(ctx) + if err != nil { + panic(err) + } + return v +} + +// Exec executes the query. +func (rc *ReferrerCreate) Exec(ctx context.Context) error { + _, err := rc.Save(ctx) + return err +} + +// ExecX is like Exec, but panics if an error occurs. +func (rc *ReferrerCreate) ExecX(ctx context.Context) { + if err := rc.Exec(ctx); err != nil { + panic(err) + } +} + +// defaults sets the default values of the builder before save. +func (rc *ReferrerCreate) defaults() { + if _, ok := rc.mutation.CreatedAt(); !ok { + v := referrer.DefaultCreatedAt() + rc.mutation.SetCreatedAt(v) + } + if _, ok := rc.mutation.ID(); !ok { + v := referrer.DefaultID() + rc.mutation.SetID(v) + } +} + +// check runs all checks and user-defined validators on the builder. +func (rc *ReferrerCreate) check() error { + if _, ok := rc.mutation.Digest(); !ok { + return &ValidationError{Name: "digest", err: errors.New(`ent: missing required field "Referrer.digest"`)} + } + if _, ok := rc.mutation.ArtifactType(); !ok { + return &ValidationError{Name: "artifact_type", err: errors.New(`ent: missing required field "Referrer.artifact_type"`)} + } + if _, ok := rc.mutation.Downloadable(); !ok { + return &ValidationError{Name: "downloadable", err: errors.New(`ent: missing required field "Referrer.downloadable"`)} + } + if _, ok := rc.mutation.CreatedAt(); !ok { + return &ValidationError{Name: "created_at", err: errors.New(`ent: missing required field "Referrer.created_at"`)} + } + return nil +} + +func (rc *ReferrerCreate) sqlSave(ctx context.Context) (*Referrer, error) { + if err := rc.check(); err != nil { + return nil, err + } + _node, _spec := rc.createSpec() + if err := sqlgraph.CreateNode(ctx, rc.driver, _spec); err != nil { + if sqlgraph.IsConstraintError(err) { + err = &ConstraintError{msg: err.Error(), wrap: err} + } + return nil, err + } + if _spec.ID.Value != nil { + if id, ok := _spec.ID.Value.(*uuid.UUID); ok { + _node.ID = *id + } else if err := _node.ID.Scan(_spec.ID.Value); err != nil { + return nil, err + } + } + rc.mutation.id = &_node.ID + rc.mutation.done = true + return _node, nil +} + +func (rc *ReferrerCreate) createSpec() (*Referrer, *sqlgraph.CreateSpec) { + var ( + _node = &Referrer{config: rc.config} + _spec = sqlgraph.NewCreateSpec(referrer.Table, sqlgraph.NewFieldSpec(referrer.FieldID, field.TypeUUID)) + ) + if id, ok := rc.mutation.ID(); ok { + _node.ID = id + _spec.ID.Value = &id + } + if value, ok := rc.mutation.Digest(); ok { + _spec.SetField(referrer.FieldDigest, field.TypeString, value) + _node.Digest = value + } + if value, ok := rc.mutation.ArtifactType(); ok { + _spec.SetField(referrer.FieldArtifactType, field.TypeString, value) + _node.ArtifactType = value + } + if value, ok := rc.mutation.Downloadable(); ok { + _spec.SetField(referrer.FieldDownloadable, field.TypeBool, value) + _node.Downloadable = value + } + if value, ok := rc.mutation.CreatedAt(); ok { + _spec.SetField(referrer.FieldCreatedAt, field.TypeTime, value) + _node.CreatedAt = value + } + if nodes := rc.mutation.ReferredByIDs(); len(nodes) > 0 { + edge := &sqlgraph.EdgeSpec{ + Rel: sqlgraph.M2M, + Inverse: true, + Table: referrer.ReferredByTable, + Columns: referrer.ReferredByPrimaryKey, + Bidi: false, + Target: &sqlgraph.EdgeTarget{ + IDSpec: sqlgraph.NewFieldSpec(referrer.FieldID, field.TypeUUID), + }, + } + for _, k := range nodes { + edge.Target.Nodes = append(edge.Target.Nodes, k) + } + _spec.Edges = append(_spec.Edges, edge) + } + if nodes := rc.mutation.ReferencesIDs(); len(nodes) > 0 { + edge := &sqlgraph.EdgeSpec{ + Rel: sqlgraph.M2M, + Inverse: false, + Table: referrer.ReferencesTable, + Columns: referrer.ReferencesPrimaryKey, + Bidi: false, + Target: &sqlgraph.EdgeTarget{ + IDSpec: sqlgraph.NewFieldSpec(referrer.FieldID, field.TypeUUID), + }, + } + for _, k := range nodes { + edge.Target.Nodes = append(edge.Target.Nodes, k) + } + _spec.Edges = append(_spec.Edges, edge) + } + if nodes := rc.mutation.OrganizationsIDs(); len(nodes) > 0 { + edge := &sqlgraph.EdgeSpec{ + Rel: sqlgraph.M2M, + Inverse: false, + Table: referrer.OrganizationsTable, + Columns: referrer.OrganizationsPrimaryKey, + Bidi: false, + Target: &sqlgraph.EdgeTarget{ + IDSpec: sqlgraph.NewFieldSpec(organization.FieldID, field.TypeUUID), + }, + } + for _, k := range nodes { + edge.Target.Nodes = append(edge.Target.Nodes, k) + } + _spec.Edges = append(_spec.Edges, edge) + } + return _node, _spec +} + +// ReferrerCreateBulk is the builder for creating many Referrer entities in bulk. +type ReferrerCreateBulk struct { + config + builders []*ReferrerCreate +} + +// Save creates the Referrer entities in the database. +func (rcb *ReferrerCreateBulk) Save(ctx context.Context) ([]*Referrer, error) { + specs := make([]*sqlgraph.CreateSpec, len(rcb.builders)) + nodes := make([]*Referrer, len(rcb.builders)) + mutators := make([]Mutator, len(rcb.builders)) + for i := range rcb.builders { + func(i int, root context.Context) { + builder := rcb.builders[i] + builder.defaults() + var mut Mutator = MutateFunc(func(ctx context.Context, m Mutation) (Value, error) { + mutation, ok := m.(*ReferrerMutation) + if !ok { + return nil, fmt.Errorf("unexpected mutation type %T", m) + } + if err := builder.check(); err != nil { + return nil, err + } + builder.mutation = mutation + var err error + nodes[i], specs[i] = builder.createSpec() + if i < len(mutators)-1 { + _, err = mutators[i+1].Mutate(root, rcb.builders[i+1].mutation) + } else { + spec := &sqlgraph.BatchCreateSpec{Nodes: specs} + // Invoke the actual operation on the latest mutation in the chain. + if err = sqlgraph.BatchCreate(ctx, rcb.driver, spec); err != nil { + if sqlgraph.IsConstraintError(err) { + err = &ConstraintError{msg: err.Error(), wrap: err} + } + } + } + if err != nil { + return nil, err + } + mutation.id = &nodes[i].ID + mutation.done = true + return nodes[i], nil + }) + for i := len(builder.hooks) - 1; i >= 0; i-- { + mut = builder.hooks[i](mut) + } + mutators[i] = mut + }(i, ctx) + } + if len(mutators) > 0 { + if _, err := mutators[0].Mutate(ctx, rcb.builders[0].mutation); err != nil { + return nil, err + } + } + return nodes, nil +} + +// SaveX is like Save, but panics if an error occurs. +func (rcb *ReferrerCreateBulk) SaveX(ctx context.Context) []*Referrer { + v, err := rcb.Save(ctx) + if err != nil { + panic(err) + } + return v +} + +// Exec executes the query. +func (rcb *ReferrerCreateBulk) Exec(ctx context.Context) error { + _, err := rcb.Save(ctx) + return err +} + +// ExecX is like Exec, but panics if an error occurs. +func (rcb *ReferrerCreateBulk) ExecX(ctx context.Context) { + if err := rcb.Exec(ctx); err != nil { + panic(err) + } +} diff --git a/app/controlplane/internal/data/ent/referrer_delete.go b/app/controlplane/internal/data/ent/referrer_delete.go new file mode 100644 index 000000000..8830fab82 --- /dev/null +++ b/app/controlplane/internal/data/ent/referrer_delete.go @@ -0,0 +1,88 @@ +// Code generated by ent, DO NOT EDIT. + +package ent + +import ( + "context" + + "entgo.io/ent/dialect/sql" + "entgo.io/ent/dialect/sql/sqlgraph" + "entgo.io/ent/schema/field" + "github.com/chainloop-dev/chainloop/app/controlplane/internal/data/ent/predicate" + "github.com/chainloop-dev/chainloop/app/controlplane/internal/data/ent/referrer" +) + +// ReferrerDelete is the builder for deleting a Referrer entity. +type ReferrerDelete struct { + config + hooks []Hook + mutation *ReferrerMutation +} + +// Where appends a list predicates to the ReferrerDelete builder. +func (rd *ReferrerDelete) Where(ps ...predicate.Referrer) *ReferrerDelete { + rd.mutation.Where(ps...) + return rd +} + +// Exec executes the deletion query and returns how many vertices were deleted. +func (rd *ReferrerDelete) Exec(ctx context.Context) (int, error) { + return withHooks(ctx, rd.sqlExec, rd.mutation, rd.hooks) +} + +// ExecX is like Exec, but panics if an error occurs. +func (rd *ReferrerDelete) ExecX(ctx context.Context) int { + n, err := rd.Exec(ctx) + if err != nil { + panic(err) + } + return n +} + +func (rd *ReferrerDelete) sqlExec(ctx context.Context) (int, error) { + _spec := sqlgraph.NewDeleteSpec(referrer.Table, sqlgraph.NewFieldSpec(referrer.FieldID, field.TypeUUID)) + if ps := rd.mutation.predicates; len(ps) > 0 { + _spec.Predicate = func(selector *sql.Selector) { + for i := range ps { + ps[i](selector) + } + } + } + affected, err := sqlgraph.DeleteNodes(ctx, rd.driver, _spec) + if err != nil && sqlgraph.IsConstraintError(err) { + err = &ConstraintError{msg: err.Error(), wrap: err} + } + rd.mutation.done = true + return affected, err +} + +// ReferrerDeleteOne is the builder for deleting a single Referrer entity. +type ReferrerDeleteOne struct { + rd *ReferrerDelete +} + +// Where appends a list predicates to the ReferrerDelete builder. +func (rdo *ReferrerDeleteOne) Where(ps ...predicate.Referrer) *ReferrerDeleteOne { + rdo.rd.mutation.Where(ps...) + return rdo +} + +// Exec executes the deletion query. +func (rdo *ReferrerDeleteOne) Exec(ctx context.Context) error { + n, err := rdo.rd.Exec(ctx) + switch { + case err != nil: + return err + case n == 0: + return &NotFoundError{referrer.Label} + default: + return nil + } +} + +// ExecX is like Exec, but panics if an error occurs. +func (rdo *ReferrerDeleteOne) ExecX(ctx context.Context) { + if err := rdo.Exec(ctx); err != nil { + panic(err) + } +} diff --git a/app/controlplane/internal/data/ent/referrer_query.go b/app/controlplane/internal/data/ent/referrer_query.go new file mode 100644 index 000000000..11a2d49d4 --- /dev/null +++ b/app/controlplane/internal/data/ent/referrer_query.go @@ -0,0 +1,845 @@ +// Code generated by ent, DO NOT EDIT. + +package ent + +import ( + "context" + "database/sql/driver" + "fmt" + "math" + + "entgo.io/ent/dialect/sql" + "entgo.io/ent/dialect/sql/sqlgraph" + "entgo.io/ent/schema/field" + "github.com/chainloop-dev/chainloop/app/controlplane/internal/data/ent/organization" + "github.com/chainloop-dev/chainloop/app/controlplane/internal/data/ent/predicate" + "github.com/chainloop-dev/chainloop/app/controlplane/internal/data/ent/referrer" + "github.com/google/uuid" +) + +// ReferrerQuery is the builder for querying Referrer entities. +type ReferrerQuery struct { + config + ctx *QueryContext + order []referrer.OrderOption + inters []Interceptor + predicates []predicate.Referrer + withReferredBy *ReferrerQuery + withReferences *ReferrerQuery + withOrganizations *OrganizationQuery + // intermediate query (i.e. traversal path). + sql *sql.Selector + path func(context.Context) (*sql.Selector, error) +} + +// Where adds a new predicate for the ReferrerQuery builder. +func (rq *ReferrerQuery) Where(ps ...predicate.Referrer) *ReferrerQuery { + rq.predicates = append(rq.predicates, ps...) + return rq +} + +// Limit the number of records to be returned by this query. +func (rq *ReferrerQuery) Limit(limit int) *ReferrerQuery { + rq.ctx.Limit = &limit + return rq +} + +// Offset to start from. +func (rq *ReferrerQuery) Offset(offset int) *ReferrerQuery { + rq.ctx.Offset = &offset + return rq +} + +// Unique configures the query builder to filter duplicate records on query. +// By default, unique is set to true, and can be disabled using this method. +func (rq *ReferrerQuery) Unique(unique bool) *ReferrerQuery { + rq.ctx.Unique = &unique + return rq +} + +// Order specifies how the records should be ordered. +func (rq *ReferrerQuery) Order(o ...referrer.OrderOption) *ReferrerQuery { + rq.order = append(rq.order, o...) + return rq +} + +// QueryReferredBy chains the current query on the "referred_by" edge. +func (rq *ReferrerQuery) QueryReferredBy() *ReferrerQuery { + query := (&ReferrerClient{config: rq.config}).Query() + query.path = func(ctx context.Context) (fromU *sql.Selector, err error) { + if err := rq.prepareQuery(ctx); err != nil { + return nil, err + } + selector := rq.sqlQuery(ctx) + if err := selector.Err(); err != nil { + return nil, err + } + step := sqlgraph.NewStep( + sqlgraph.From(referrer.Table, referrer.FieldID, selector), + sqlgraph.To(referrer.Table, referrer.FieldID), + sqlgraph.Edge(sqlgraph.M2M, true, referrer.ReferredByTable, referrer.ReferredByPrimaryKey...), + ) + fromU = sqlgraph.SetNeighbors(rq.driver.Dialect(), step) + return fromU, nil + } + return query +} + +// QueryReferences chains the current query on the "references" edge. +func (rq *ReferrerQuery) QueryReferences() *ReferrerQuery { + query := (&ReferrerClient{config: rq.config}).Query() + query.path = func(ctx context.Context) (fromU *sql.Selector, err error) { + if err := rq.prepareQuery(ctx); err != nil { + return nil, err + } + selector := rq.sqlQuery(ctx) + if err := selector.Err(); err != nil { + return nil, err + } + step := sqlgraph.NewStep( + sqlgraph.From(referrer.Table, referrer.FieldID, selector), + sqlgraph.To(referrer.Table, referrer.FieldID), + sqlgraph.Edge(sqlgraph.M2M, false, referrer.ReferencesTable, referrer.ReferencesPrimaryKey...), + ) + fromU = sqlgraph.SetNeighbors(rq.driver.Dialect(), step) + return fromU, nil + } + return query +} + +// QueryOrganizations chains the current query on the "organizations" edge. +func (rq *ReferrerQuery) QueryOrganizations() *OrganizationQuery { + query := (&OrganizationClient{config: rq.config}).Query() + query.path = func(ctx context.Context) (fromU *sql.Selector, err error) { + if err := rq.prepareQuery(ctx); err != nil { + return nil, err + } + selector := rq.sqlQuery(ctx) + if err := selector.Err(); err != nil { + return nil, err + } + step := sqlgraph.NewStep( + sqlgraph.From(referrer.Table, referrer.FieldID, selector), + sqlgraph.To(organization.Table, organization.FieldID), + sqlgraph.Edge(sqlgraph.M2M, false, referrer.OrganizationsTable, referrer.OrganizationsPrimaryKey...), + ) + fromU = sqlgraph.SetNeighbors(rq.driver.Dialect(), step) + return fromU, nil + } + return query +} + +// First returns the first Referrer entity from the query. +// Returns a *NotFoundError when no Referrer was found. +func (rq *ReferrerQuery) First(ctx context.Context) (*Referrer, error) { + nodes, err := rq.Limit(1).All(setContextOp(ctx, rq.ctx, "First")) + if err != nil { + return nil, err + } + if len(nodes) == 0 { + return nil, &NotFoundError{referrer.Label} + } + return nodes[0], nil +} + +// FirstX is like First, but panics if an error occurs. +func (rq *ReferrerQuery) FirstX(ctx context.Context) *Referrer { + node, err := rq.First(ctx) + if err != nil && !IsNotFound(err) { + panic(err) + } + return node +} + +// FirstID returns the first Referrer ID from the query. +// Returns a *NotFoundError when no Referrer ID was found. +func (rq *ReferrerQuery) FirstID(ctx context.Context) (id uuid.UUID, err error) { + var ids []uuid.UUID + if ids, err = rq.Limit(1).IDs(setContextOp(ctx, rq.ctx, "FirstID")); err != nil { + return + } + if len(ids) == 0 { + err = &NotFoundError{referrer.Label} + return + } + return ids[0], nil +} + +// FirstIDX is like FirstID, but panics if an error occurs. +func (rq *ReferrerQuery) FirstIDX(ctx context.Context) uuid.UUID { + id, err := rq.FirstID(ctx) + if err != nil && !IsNotFound(err) { + panic(err) + } + return id +} + +// Only returns a single Referrer entity found by the query, ensuring it only returns one. +// Returns a *NotSingularError when more than one Referrer entity is found. +// Returns a *NotFoundError when no Referrer entities are found. +func (rq *ReferrerQuery) Only(ctx context.Context) (*Referrer, error) { + nodes, err := rq.Limit(2).All(setContextOp(ctx, rq.ctx, "Only")) + if err != nil { + return nil, err + } + switch len(nodes) { + case 1: + return nodes[0], nil + case 0: + return nil, &NotFoundError{referrer.Label} + default: + return nil, &NotSingularError{referrer.Label} + } +} + +// OnlyX is like Only, but panics if an error occurs. +func (rq *ReferrerQuery) OnlyX(ctx context.Context) *Referrer { + node, err := rq.Only(ctx) + if err != nil { + panic(err) + } + return node +} + +// OnlyID is like Only, but returns the only Referrer ID in the query. +// Returns a *NotSingularError when more than one Referrer ID is found. +// Returns a *NotFoundError when no entities are found. +func (rq *ReferrerQuery) OnlyID(ctx context.Context) (id uuid.UUID, err error) { + var ids []uuid.UUID + if ids, err = rq.Limit(2).IDs(setContextOp(ctx, rq.ctx, "OnlyID")); err != nil { + return + } + switch len(ids) { + case 1: + id = ids[0] + case 0: + err = &NotFoundError{referrer.Label} + default: + err = &NotSingularError{referrer.Label} + } + return +} + +// OnlyIDX is like OnlyID, but panics if an error occurs. +func (rq *ReferrerQuery) OnlyIDX(ctx context.Context) uuid.UUID { + id, err := rq.OnlyID(ctx) + if err != nil { + panic(err) + } + return id +} + +// All executes the query and returns a list of Referrers. +func (rq *ReferrerQuery) All(ctx context.Context) ([]*Referrer, error) { + ctx = setContextOp(ctx, rq.ctx, "All") + if err := rq.prepareQuery(ctx); err != nil { + return nil, err + } + qr := querierAll[[]*Referrer, *ReferrerQuery]() + return withInterceptors[[]*Referrer](ctx, rq, qr, rq.inters) +} + +// AllX is like All, but panics if an error occurs. +func (rq *ReferrerQuery) AllX(ctx context.Context) []*Referrer { + nodes, err := rq.All(ctx) + if err != nil { + panic(err) + } + return nodes +} + +// IDs executes the query and returns a list of Referrer IDs. +func (rq *ReferrerQuery) IDs(ctx context.Context) (ids []uuid.UUID, err error) { + if rq.ctx.Unique == nil && rq.path != nil { + rq.Unique(true) + } + ctx = setContextOp(ctx, rq.ctx, "IDs") + if err = rq.Select(referrer.FieldID).Scan(ctx, &ids); err != nil { + return nil, err + } + return ids, nil +} + +// IDsX is like IDs, but panics if an error occurs. +func (rq *ReferrerQuery) IDsX(ctx context.Context) []uuid.UUID { + ids, err := rq.IDs(ctx) + if err != nil { + panic(err) + } + return ids +} + +// Count returns the count of the given query. +func (rq *ReferrerQuery) Count(ctx context.Context) (int, error) { + ctx = setContextOp(ctx, rq.ctx, "Count") + if err := rq.prepareQuery(ctx); err != nil { + return 0, err + } + return withInterceptors[int](ctx, rq, querierCount[*ReferrerQuery](), rq.inters) +} + +// CountX is like Count, but panics if an error occurs. +func (rq *ReferrerQuery) CountX(ctx context.Context) int { + count, err := rq.Count(ctx) + if err != nil { + panic(err) + } + return count +} + +// Exist returns true if the query has elements in the graph. +func (rq *ReferrerQuery) Exist(ctx context.Context) (bool, error) { + ctx = setContextOp(ctx, rq.ctx, "Exist") + switch _, err := rq.FirstID(ctx); { + case IsNotFound(err): + return false, nil + case err != nil: + return false, fmt.Errorf("ent: check existence: %w", err) + default: + return true, nil + } +} + +// ExistX is like Exist, but panics if an error occurs. +func (rq *ReferrerQuery) ExistX(ctx context.Context) bool { + exist, err := rq.Exist(ctx) + if err != nil { + panic(err) + } + return exist +} + +// Clone returns a duplicate of the ReferrerQuery builder, including all associated steps. It can be +// used to prepare common query builders and use them differently after the clone is made. +func (rq *ReferrerQuery) Clone() *ReferrerQuery { + if rq == nil { + return nil + } + return &ReferrerQuery{ + config: rq.config, + ctx: rq.ctx.Clone(), + order: append([]referrer.OrderOption{}, rq.order...), + inters: append([]Interceptor{}, rq.inters...), + predicates: append([]predicate.Referrer{}, rq.predicates...), + withReferredBy: rq.withReferredBy.Clone(), + withReferences: rq.withReferences.Clone(), + withOrganizations: rq.withOrganizations.Clone(), + // clone intermediate query. + sql: rq.sql.Clone(), + path: rq.path, + } +} + +// WithReferredBy tells the query-builder to eager-load the nodes that are connected to +// the "referred_by" edge. The optional arguments are used to configure the query builder of the edge. +func (rq *ReferrerQuery) WithReferredBy(opts ...func(*ReferrerQuery)) *ReferrerQuery { + query := (&ReferrerClient{config: rq.config}).Query() + for _, opt := range opts { + opt(query) + } + rq.withReferredBy = query + return rq +} + +// WithReferences tells the query-builder to eager-load the nodes that are connected to +// the "references" edge. The optional arguments are used to configure the query builder of the edge. +func (rq *ReferrerQuery) WithReferences(opts ...func(*ReferrerQuery)) *ReferrerQuery { + query := (&ReferrerClient{config: rq.config}).Query() + for _, opt := range opts { + opt(query) + } + rq.withReferences = query + return rq +} + +// WithOrganizations tells the query-builder to eager-load the nodes that are connected to +// the "organizations" edge. The optional arguments are used to configure the query builder of the edge. +func (rq *ReferrerQuery) WithOrganizations(opts ...func(*OrganizationQuery)) *ReferrerQuery { + query := (&OrganizationClient{config: rq.config}).Query() + for _, opt := range opts { + opt(query) + } + rq.withOrganizations = query + return rq +} + +// GroupBy is used to group vertices by one or more fields/columns. +// It is often used with aggregate functions, like: count, max, mean, min, sum. +// +// Example: +// +// var v []struct { +// Digest string `json:"digest,omitempty"` +// Count int `json:"count,omitempty"` +// } +// +// client.Referrer.Query(). +// GroupBy(referrer.FieldDigest). +// Aggregate(ent.Count()). +// Scan(ctx, &v) +func (rq *ReferrerQuery) GroupBy(field string, fields ...string) *ReferrerGroupBy { + rq.ctx.Fields = append([]string{field}, fields...) + grbuild := &ReferrerGroupBy{build: rq} + grbuild.flds = &rq.ctx.Fields + grbuild.label = referrer.Label + grbuild.scan = grbuild.Scan + return grbuild +} + +// Select allows the selection one or more fields/columns for the given query, +// instead of selecting all fields in the entity. +// +// Example: +// +// var v []struct { +// Digest string `json:"digest,omitempty"` +// } +// +// client.Referrer.Query(). +// Select(referrer.FieldDigest). +// Scan(ctx, &v) +func (rq *ReferrerQuery) Select(fields ...string) *ReferrerSelect { + rq.ctx.Fields = append(rq.ctx.Fields, fields...) + sbuild := &ReferrerSelect{ReferrerQuery: rq} + sbuild.label = referrer.Label + sbuild.flds, sbuild.scan = &rq.ctx.Fields, sbuild.Scan + return sbuild +} + +// Aggregate returns a ReferrerSelect configured with the given aggregations. +func (rq *ReferrerQuery) Aggregate(fns ...AggregateFunc) *ReferrerSelect { + return rq.Select().Aggregate(fns...) +} + +func (rq *ReferrerQuery) prepareQuery(ctx context.Context) error { + for _, inter := range rq.inters { + if inter == nil { + return fmt.Errorf("ent: uninitialized interceptor (forgotten import ent/runtime?)") + } + if trv, ok := inter.(Traverser); ok { + if err := trv.Traverse(ctx, rq); err != nil { + return err + } + } + } + for _, f := range rq.ctx.Fields { + if !referrer.ValidColumn(f) { + return &ValidationError{Name: f, err: fmt.Errorf("ent: invalid field %q for query", f)} + } + } + if rq.path != nil { + prev, err := rq.path(ctx) + if err != nil { + return err + } + rq.sql = prev + } + return nil +} + +func (rq *ReferrerQuery) sqlAll(ctx context.Context, hooks ...queryHook) ([]*Referrer, error) { + var ( + nodes = []*Referrer{} + _spec = rq.querySpec() + loadedTypes = [3]bool{ + rq.withReferredBy != nil, + rq.withReferences != nil, + rq.withOrganizations != nil, + } + ) + _spec.ScanValues = func(columns []string) ([]any, error) { + return (*Referrer).scanValues(nil, columns) + } + _spec.Assign = func(columns []string, values []any) error { + node := &Referrer{config: rq.config} + nodes = append(nodes, node) + node.Edges.loadedTypes = loadedTypes + return node.assignValues(columns, values) + } + for i := range hooks { + hooks[i](ctx, _spec) + } + if err := sqlgraph.QueryNodes(ctx, rq.driver, _spec); err != nil { + return nil, err + } + if len(nodes) == 0 { + return nodes, nil + } + if query := rq.withReferredBy; query != nil { + if err := rq.loadReferredBy(ctx, query, nodes, + func(n *Referrer) { n.Edges.ReferredBy = []*Referrer{} }, + func(n *Referrer, e *Referrer) { n.Edges.ReferredBy = append(n.Edges.ReferredBy, e) }); err != nil { + return nil, err + } + } + if query := rq.withReferences; query != nil { + if err := rq.loadReferences(ctx, query, nodes, + func(n *Referrer) { n.Edges.References = []*Referrer{} }, + func(n *Referrer, e *Referrer) { n.Edges.References = append(n.Edges.References, e) }); err != nil { + return nil, err + } + } + if query := rq.withOrganizations; query != nil { + if err := rq.loadOrganizations(ctx, query, nodes, + func(n *Referrer) { n.Edges.Organizations = []*Organization{} }, + func(n *Referrer, e *Organization) { n.Edges.Organizations = append(n.Edges.Organizations, e) }); err != nil { + return nil, err + } + } + return nodes, nil +} + +func (rq *ReferrerQuery) loadReferredBy(ctx context.Context, query *ReferrerQuery, nodes []*Referrer, init func(*Referrer), assign func(*Referrer, *Referrer)) error { + edgeIDs := make([]driver.Value, len(nodes)) + byID := make(map[uuid.UUID]*Referrer) + nids := make(map[uuid.UUID]map[*Referrer]struct{}) + for i, node := range nodes { + edgeIDs[i] = node.ID + byID[node.ID] = node + if init != nil { + init(node) + } + } + query.Where(func(s *sql.Selector) { + joinT := sql.Table(referrer.ReferredByTable) + s.Join(joinT).On(s.C(referrer.FieldID), joinT.C(referrer.ReferredByPrimaryKey[0])) + s.Where(sql.InValues(joinT.C(referrer.ReferredByPrimaryKey[1]), edgeIDs...)) + columns := s.SelectedColumns() + s.Select(joinT.C(referrer.ReferredByPrimaryKey[1])) + s.AppendSelect(columns...) + s.SetDistinct(false) + }) + if err := query.prepareQuery(ctx); err != nil { + return err + } + qr := QuerierFunc(func(ctx context.Context, q Query) (Value, error) { + return query.sqlAll(ctx, func(_ context.Context, spec *sqlgraph.QuerySpec) { + assign := spec.Assign + values := spec.ScanValues + spec.ScanValues = func(columns []string) ([]any, error) { + values, err := values(columns[1:]) + if err != nil { + return nil, err + } + return append([]any{new(uuid.UUID)}, values...), nil + } + spec.Assign = func(columns []string, values []any) error { + outValue := *values[0].(*uuid.UUID) + inValue := *values[1].(*uuid.UUID) + if nids[inValue] == nil { + nids[inValue] = map[*Referrer]struct{}{byID[outValue]: {}} + return assign(columns[1:], values[1:]) + } + nids[inValue][byID[outValue]] = struct{}{} + return nil + } + }) + }) + neighbors, err := withInterceptors[[]*Referrer](ctx, query, qr, query.inters) + if err != nil { + return err + } + for _, n := range neighbors { + nodes, ok := nids[n.ID] + if !ok { + return fmt.Errorf(`unexpected "referred_by" node returned %v`, n.ID) + } + for kn := range nodes { + assign(kn, n) + } + } + return nil +} +func (rq *ReferrerQuery) loadReferences(ctx context.Context, query *ReferrerQuery, nodes []*Referrer, init func(*Referrer), assign func(*Referrer, *Referrer)) error { + edgeIDs := make([]driver.Value, len(nodes)) + byID := make(map[uuid.UUID]*Referrer) + nids := make(map[uuid.UUID]map[*Referrer]struct{}) + for i, node := range nodes { + edgeIDs[i] = node.ID + byID[node.ID] = node + if init != nil { + init(node) + } + } + query.Where(func(s *sql.Selector) { + joinT := sql.Table(referrer.ReferencesTable) + s.Join(joinT).On(s.C(referrer.FieldID), joinT.C(referrer.ReferencesPrimaryKey[1])) + s.Where(sql.InValues(joinT.C(referrer.ReferencesPrimaryKey[0]), edgeIDs...)) + columns := s.SelectedColumns() + s.Select(joinT.C(referrer.ReferencesPrimaryKey[0])) + s.AppendSelect(columns...) + s.SetDistinct(false) + }) + if err := query.prepareQuery(ctx); err != nil { + return err + } + qr := QuerierFunc(func(ctx context.Context, q Query) (Value, error) { + return query.sqlAll(ctx, func(_ context.Context, spec *sqlgraph.QuerySpec) { + assign := spec.Assign + values := spec.ScanValues + spec.ScanValues = func(columns []string) ([]any, error) { + values, err := values(columns[1:]) + if err != nil { + return nil, err + } + return append([]any{new(uuid.UUID)}, values...), nil + } + spec.Assign = func(columns []string, values []any) error { + outValue := *values[0].(*uuid.UUID) + inValue := *values[1].(*uuid.UUID) + if nids[inValue] == nil { + nids[inValue] = map[*Referrer]struct{}{byID[outValue]: {}} + return assign(columns[1:], values[1:]) + } + nids[inValue][byID[outValue]] = struct{}{} + return nil + } + }) + }) + neighbors, err := withInterceptors[[]*Referrer](ctx, query, qr, query.inters) + if err != nil { + return err + } + for _, n := range neighbors { + nodes, ok := nids[n.ID] + if !ok { + return fmt.Errorf(`unexpected "references" node returned %v`, n.ID) + } + for kn := range nodes { + assign(kn, n) + } + } + return nil +} +func (rq *ReferrerQuery) loadOrganizations(ctx context.Context, query *OrganizationQuery, nodes []*Referrer, init func(*Referrer), assign func(*Referrer, *Organization)) error { + edgeIDs := make([]driver.Value, len(nodes)) + byID := make(map[uuid.UUID]*Referrer) + nids := make(map[uuid.UUID]map[*Referrer]struct{}) + for i, node := range nodes { + edgeIDs[i] = node.ID + byID[node.ID] = node + if init != nil { + init(node) + } + } + query.Where(func(s *sql.Selector) { + joinT := sql.Table(referrer.OrganizationsTable) + s.Join(joinT).On(s.C(organization.FieldID), joinT.C(referrer.OrganizationsPrimaryKey[1])) + s.Where(sql.InValues(joinT.C(referrer.OrganizationsPrimaryKey[0]), edgeIDs...)) + columns := s.SelectedColumns() + s.Select(joinT.C(referrer.OrganizationsPrimaryKey[0])) + s.AppendSelect(columns...) + s.SetDistinct(false) + }) + if err := query.prepareQuery(ctx); err != nil { + return err + } + qr := QuerierFunc(func(ctx context.Context, q Query) (Value, error) { + return query.sqlAll(ctx, func(_ context.Context, spec *sqlgraph.QuerySpec) { + assign := spec.Assign + values := spec.ScanValues + spec.ScanValues = func(columns []string) ([]any, error) { + values, err := values(columns[1:]) + if err != nil { + return nil, err + } + return append([]any{new(uuid.UUID)}, values...), nil + } + spec.Assign = func(columns []string, values []any) error { + outValue := *values[0].(*uuid.UUID) + inValue := *values[1].(*uuid.UUID) + if nids[inValue] == nil { + nids[inValue] = map[*Referrer]struct{}{byID[outValue]: {}} + return assign(columns[1:], values[1:]) + } + nids[inValue][byID[outValue]] = struct{}{} + return nil + } + }) + }) + neighbors, err := withInterceptors[[]*Organization](ctx, query, qr, query.inters) + if err != nil { + return err + } + for _, n := range neighbors { + nodes, ok := nids[n.ID] + if !ok { + return fmt.Errorf(`unexpected "organizations" node returned %v`, n.ID) + } + for kn := range nodes { + assign(kn, n) + } + } + return nil +} + +func (rq *ReferrerQuery) sqlCount(ctx context.Context) (int, error) { + _spec := rq.querySpec() + _spec.Node.Columns = rq.ctx.Fields + if len(rq.ctx.Fields) > 0 { + _spec.Unique = rq.ctx.Unique != nil && *rq.ctx.Unique + } + return sqlgraph.CountNodes(ctx, rq.driver, _spec) +} + +func (rq *ReferrerQuery) querySpec() *sqlgraph.QuerySpec { + _spec := sqlgraph.NewQuerySpec(referrer.Table, referrer.Columns, sqlgraph.NewFieldSpec(referrer.FieldID, field.TypeUUID)) + _spec.From = rq.sql + if unique := rq.ctx.Unique; unique != nil { + _spec.Unique = *unique + } else if rq.path != nil { + _spec.Unique = true + } + if fields := rq.ctx.Fields; len(fields) > 0 { + _spec.Node.Columns = make([]string, 0, len(fields)) + _spec.Node.Columns = append(_spec.Node.Columns, referrer.FieldID) + for i := range fields { + if fields[i] != referrer.FieldID { + _spec.Node.Columns = append(_spec.Node.Columns, fields[i]) + } + } + } + if ps := rq.predicates; len(ps) > 0 { + _spec.Predicate = func(selector *sql.Selector) { + for i := range ps { + ps[i](selector) + } + } + } + if limit := rq.ctx.Limit; limit != nil { + _spec.Limit = *limit + } + if offset := rq.ctx.Offset; offset != nil { + _spec.Offset = *offset + } + if ps := rq.order; len(ps) > 0 { + _spec.Order = func(selector *sql.Selector) { + for i := range ps { + ps[i](selector) + } + } + } + return _spec +} + +func (rq *ReferrerQuery) sqlQuery(ctx context.Context) *sql.Selector { + builder := sql.Dialect(rq.driver.Dialect()) + t1 := builder.Table(referrer.Table) + columns := rq.ctx.Fields + if len(columns) == 0 { + columns = referrer.Columns + } + selector := builder.Select(t1.Columns(columns...)...).From(t1) + if rq.sql != nil { + selector = rq.sql + selector.Select(selector.Columns(columns...)...) + } + if rq.ctx.Unique != nil && *rq.ctx.Unique { + selector.Distinct() + } + for _, p := range rq.predicates { + p(selector) + } + for _, p := range rq.order { + p(selector) + } + if offset := rq.ctx.Offset; offset != nil { + // limit is mandatory for offset clause. We start + // with default value, and override it below if needed. + selector.Offset(*offset).Limit(math.MaxInt32) + } + if limit := rq.ctx.Limit; limit != nil { + selector.Limit(*limit) + } + return selector +} + +// ReferrerGroupBy is the group-by builder for Referrer entities. +type ReferrerGroupBy struct { + selector + build *ReferrerQuery +} + +// Aggregate adds the given aggregation functions to the group-by query. +func (rgb *ReferrerGroupBy) Aggregate(fns ...AggregateFunc) *ReferrerGroupBy { + rgb.fns = append(rgb.fns, fns...) + return rgb +} + +// Scan applies the selector query and scans the result into the given value. +func (rgb *ReferrerGroupBy) Scan(ctx context.Context, v any) error { + ctx = setContextOp(ctx, rgb.build.ctx, "GroupBy") + if err := rgb.build.prepareQuery(ctx); err != nil { + return err + } + return scanWithInterceptors[*ReferrerQuery, *ReferrerGroupBy](ctx, rgb.build, rgb, rgb.build.inters, v) +} + +func (rgb *ReferrerGroupBy) sqlScan(ctx context.Context, root *ReferrerQuery, v any) error { + selector := root.sqlQuery(ctx).Select() + aggregation := make([]string, 0, len(rgb.fns)) + for _, fn := range rgb.fns { + aggregation = append(aggregation, fn(selector)) + } + if len(selector.SelectedColumns()) == 0 { + columns := make([]string, 0, len(*rgb.flds)+len(rgb.fns)) + for _, f := range *rgb.flds { + columns = append(columns, selector.C(f)) + } + columns = append(columns, aggregation...) + selector.Select(columns...) + } + selector.GroupBy(selector.Columns(*rgb.flds...)...) + if err := selector.Err(); err != nil { + return err + } + rows := &sql.Rows{} + query, args := selector.Query() + if err := rgb.build.driver.Query(ctx, query, args, rows); err != nil { + return err + } + defer rows.Close() + return sql.ScanSlice(rows, v) +} + +// ReferrerSelect is the builder for selecting fields of Referrer entities. +type ReferrerSelect struct { + *ReferrerQuery + selector +} + +// Aggregate adds the given aggregation functions to the selector query. +func (rs *ReferrerSelect) Aggregate(fns ...AggregateFunc) *ReferrerSelect { + rs.fns = append(rs.fns, fns...) + return rs +} + +// Scan applies the selector query and scans the result into the given value. +func (rs *ReferrerSelect) Scan(ctx context.Context, v any) error { + ctx = setContextOp(ctx, rs.ctx, "Select") + if err := rs.prepareQuery(ctx); err != nil { + return err + } + return scanWithInterceptors[*ReferrerQuery, *ReferrerSelect](ctx, rs.ReferrerQuery, rs, rs.inters, v) +} + +func (rs *ReferrerSelect) sqlScan(ctx context.Context, root *ReferrerQuery, v any) error { + selector := root.sqlQuery(ctx) + aggregation := make([]string, 0, len(rs.fns)) + for _, fn := range rs.fns { + aggregation = append(aggregation, fn(selector)) + } + switch n := len(*rs.selector.flds); { + case n == 0 && len(aggregation) > 0: + selector.Select(aggregation...) + case n != 0 && len(aggregation) > 0: + selector.AppendSelect(aggregation...) + } + rows := &sql.Rows{} + query, args := selector.Query() + if err := rs.driver.Query(ctx, query, args, rows); err != nil { + return err + } + defer rows.Close() + return sql.ScanSlice(rows, v) +} diff --git a/app/controlplane/internal/data/ent/referrer_update.go b/app/controlplane/internal/data/ent/referrer_update.go new file mode 100644 index 000000000..4cce6273b --- /dev/null +++ b/app/controlplane/internal/data/ent/referrer_update.go @@ -0,0 +1,501 @@ +// Code generated by ent, DO NOT EDIT. + +package ent + +import ( + "context" + "errors" + "fmt" + + "entgo.io/ent/dialect/sql" + "entgo.io/ent/dialect/sql/sqlgraph" + "entgo.io/ent/schema/field" + "github.com/chainloop-dev/chainloop/app/controlplane/internal/data/ent/organization" + "github.com/chainloop-dev/chainloop/app/controlplane/internal/data/ent/predicate" + "github.com/chainloop-dev/chainloop/app/controlplane/internal/data/ent/referrer" + "github.com/google/uuid" +) + +// ReferrerUpdate is the builder for updating Referrer entities. +type ReferrerUpdate struct { + config + hooks []Hook + mutation *ReferrerMutation +} + +// Where appends a list predicates to the ReferrerUpdate builder. +func (ru *ReferrerUpdate) Where(ps ...predicate.Referrer) *ReferrerUpdate { + ru.mutation.Where(ps...) + return ru +} + +// AddReferenceIDs adds the "references" edge to the Referrer entity by IDs. +func (ru *ReferrerUpdate) AddReferenceIDs(ids ...uuid.UUID) *ReferrerUpdate { + ru.mutation.AddReferenceIDs(ids...) + return ru +} + +// AddReferences adds the "references" edges to the Referrer entity. +func (ru *ReferrerUpdate) AddReferences(r ...*Referrer) *ReferrerUpdate { + ids := make([]uuid.UUID, len(r)) + for i := range r { + ids[i] = r[i].ID + } + return ru.AddReferenceIDs(ids...) +} + +// AddOrganizationIDs adds the "organizations" edge to the Organization entity by IDs. +func (ru *ReferrerUpdate) AddOrganizationIDs(ids ...uuid.UUID) *ReferrerUpdate { + ru.mutation.AddOrganizationIDs(ids...) + return ru +} + +// AddOrganizations adds the "organizations" edges to the Organization entity. +func (ru *ReferrerUpdate) AddOrganizations(o ...*Organization) *ReferrerUpdate { + ids := make([]uuid.UUID, len(o)) + for i := range o { + ids[i] = o[i].ID + } + return ru.AddOrganizationIDs(ids...) +} + +// Mutation returns the ReferrerMutation object of the builder. +func (ru *ReferrerUpdate) Mutation() *ReferrerMutation { + return ru.mutation +} + +// ClearReferences clears all "references" edges to the Referrer entity. +func (ru *ReferrerUpdate) ClearReferences() *ReferrerUpdate { + ru.mutation.ClearReferences() + return ru +} + +// RemoveReferenceIDs removes the "references" edge to Referrer entities by IDs. +func (ru *ReferrerUpdate) RemoveReferenceIDs(ids ...uuid.UUID) *ReferrerUpdate { + ru.mutation.RemoveReferenceIDs(ids...) + return ru +} + +// RemoveReferences removes "references" edges to Referrer entities. +func (ru *ReferrerUpdate) RemoveReferences(r ...*Referrer) *ReferrerUpdate { + ids := make([]uuid.UUID, len(r)) + for i := range r { + ids[i] = r[i].ID + } + return ru.RemoveReferenceIDs(ids...) +} + +// ClearOrganizations clears all "organizations" edges to the Organization entity. +func (ru *ReferrerUpdate) ClearOrganizations() *ReferrerUpdate { + ru.mutation.ClearOrganizations() + return ru +} + +// RemoveOrganizationIDs removes the "organizations" edge to Organization entities by IDs. +func (ru *ReferrerUpdate) RemoveOrganizationIDs(ids ...uuid.UUID) *ReferrerUpdate { + ru.mutation.RemoveOrganizationIDs(ids...) + return ru +} + +// RemoveOrganizations removes "organizations" edges to Organization entities. +func (ru *ReferrerUpdate) RemoveOrganizations(o ...*Organization) *ReferrerUpdate { + ids := make([]uuid.UUID, len(o)) + for i := range o { + ids[i] = o[i].ID + } + return ru.RemoveOrganizationIDs(ids...) +} + +// Save executes the query and returns the number of nodes affected by the update operation. +func (ru *ReferrerUpdate) Save(ctx context.Context) (int, error) { + return withHooks(ctx, ru.sqlSave, ru.mutation, ru.hooks) +} + +// SaveX is like Save, but panics if an error occurs. +func (ru *ReferrerUpdate) SaveX(ctx context.Context) int { + affected, err := ru.Save(ctx) + if err != nil { + panic(err) + } + return affected +} + +// Exec executes the query. +func (ru *ReferrerUpdate) Exec(ctx context.Context) error { + _, err := ru.Save(ctx) + return err +} + +// ExecX is like Exec, but panics if an error occurs. +func (ru *ReferrerUpdate) ExecX(ctx context.Context) { + if err := ru.Exec(ctx); err != nil { + panic(err) + } +} + +func (ru *ReferrerUpdate) sqlSave(ctx context.Context) (n int, err error) { + _spec := sqlgraph.NewUpdateSpec(referrer.Table, referrer.Columns, sqlgraph.NewFieldSpec(referrer.FieldID, field.TypeUUID)) + if ps := ru.mutation.predicates; len(ps) > 0 { + _spec.Predicate = func(selector *sql.Selector) { + for i := range ps { + ps[i](selector) + } + } + } + if ru.mutation.ReferencesCleared() { + edge := &sqlgraph.EdgeSpec{ + Rel: sqlgraph.M2M, + Inverse: false, + Table: referrer.ReferencesTable, + Columns: referrer.ReferencesPrimaryKey, + Bidi: false, + Target: &sqlgraph.EdgeTarget{ + IDSpec: sqlgraph.NewFieldSpec(referrer.FieldID, field.TypeUUID), + }, + } + _spec.Edges.Clear = append(_spec.Edges.Clear, edge) + } + if nodes := ru.mutation.RemovedReferencesIDs(); len(nodes) > 0 && !ru.mutation.ReferencesCleared() { + edge := &sqlgraph.EdgeSpec{ + Rel: sqlgraph.M2M, + Inverse: false, + Table: referrer.ReferencesTable, + Columns: referrer.ReferencesPrimaryKey, + Bidi: false, + Target: &sqlgraph.EdgeTarget{ + IDSpec: sqlgraph.NewFieldSpec(referrer.FieldID, field.TypeUUID), + }, + } + for _, k := range nodes { + edge.Target.Nodes = append(edge.Target.Nodes, k) + } + _spec.Edges.Clear = append(_spec.Edges.Clear, edge) + } + if nodes := ru.mutation.ReferencesIDs(); len(nodes) > 0 { + edge := &sqlgraph.EdgeSpec{ + Rel: sqlgraph.M2M, + Inverse: false, + Table: referrer.ReferencesTable, + Columns: referrer.ReferencesPrimaryKey, + Bidi: false, + Target: &sqlgraph.EdgeTarget{ + IDSpec: sqlgraph.NewFieldSpec(referrer.FieldID, field.TypeUUID), + }, + } + for _, k := range nodes { + edge.Target.Nodes = append(edge.Target.Nodes, k) + } + _spec.Edges.Add = append(_spec.Edges.Add, edge) + } + if ru.mutation.OrganizationsCleared() { + edge := &sqlgraph.EdgeSpec{ + Rel: sqlgraph.M2M, + Inverse: false, + Table: referrer.OrganizationsTable, + Columns: referrer.OrganizationsPrimaryKey, + Bidi: false, + Target: &sqlgraph.EdgeTarget{ + IDSpec: sqlgraph.NewFieldSpec(organization.FieldID, field.TypeUUID), + }, + } + _spec.Edges.Clear = append(_spec.Edges.Clear, edge) + } + if nodes := ru.mutation.RemovedOrganizationsIDs(); len(nodes) > 0 && !ru.mutation.OrganizationsCleared() { + edge := &sqlgraph.EdgeSpec{ + Rel: sqlgraph.M2M, + Inverse: false, + Table: referrer.OrganizationsTable, + Columns: referrer.OrganizationsPrimaryKey, + Bidi: false, + Target: &sqlgraph.EdgeTarget{ + IDSpec: sqlgraph.NewFieldSpec(organization.FieldID, field.TypeUUID), + }, + } + for _, k := range nodes { + edge.Target.Nodes = append(edge.Target.Nodes, k) + } + _spec.Edges.Clear = append(_spec.Edges.Clear, edge) + } + if nodes := ru.mutation.OrganizationsIDs(); len(nodes) > 0 { + edge := &sqlgraph.EdgeSpec{ + Rel: sqlgraph.M2M, + Inverse: false, + Table: referrer.OrganizationsTable, + Columns: referrer.OrganizationsPrimaryKey, + Bidi: false, + Target: &sqlgraph.EdgeTarget{ + IDSpec: sqlgraph.NewFieldSpec(organization.FieldID, field.TypeUUID), + }, + } + for _, k := range nodes { + edge.Target.Nodes = append(edge.Target.Nodes, k) + } + _spec.Edges.Add = append(_spec.Edges.Add, edge) + } + if n, err = sqlgraph.UpdateNodes(ctx, ru.driver, _spec); err != nil { + if _, ok := err.(*sqlgraph.NotFoundError); ok { + err = &NotFoundError{referrer.Label} + } else if sqlgraph.IsConstraintError(err) { + err = &ConstraintError{msg: err.Error(), wrap: err} + } + return 0, err + } + ru.mutation.done = true + return n, nil +} + +// ReferrerUpdateOne is the builder for updating a single Referrer entity. +type ReferrerUpdateOne struct { + config + fields []string + hooks []Hook + mutation *ReferrerMutation +} + +// AddReferenceIDs adds the "references" edge to the Referrer entity by IDs. +func (ruo *ReferrerUpdateOne) AddReferenceIDs(ids ...uuid.UUID) *ReferrerUpdateOne { + ruo.mutation.AddReferenceIDs(ids...) + return ruo +} + +// AddReferences adds the "references" edges to the Referrer entity. +func (ruo *ReferrerUpdateOne) AddReferences(r ...*Referrer) *ReferrerUpdateOne { + ids := make([]uuid.UUID, len(r)) + for i := range r { + ids[i] = r[i].ID + } + return ruo.AddReferenceIDs(ids...) +} + +// AddOrganizationIDs adds the "organizations" edge to the Organization entity by IDs. +func (ruo *ReferrerUpdateOne) AddOrganizationIDs(ids ...uuid.UUID) *ReferrerUpdateOne { + ruo.mutation.AddOrganizationIDs(ids...) + return ruo +} + +// AddOrganizations adds the "organizations" edges to the Organization entity. +func (ruo *ReferrerUpdateOne) AddOrganizations(o ...*Organization) *ReferrerUpdateOne { + ids := make([]uuid.UUID, len(o)) + for i := range o { + ids[i] = o[i].ID + } + return ruo.AddOrganizationIDs(ids...) +} + +// Mutation returns the ReferrerMutation object of the builder. +func (ruo *ReferrerUpdateOne) Mutation() *ReferrerMutation { + return ruo.mutation +} + +// ClearReferences clears all "references" edges to the Referrer entity. +func (ruo *ReferrerUpdateOne) ClearReferences() *ReferrerUpdateOne { + ruo.mutation.ClearReferences() + return ruo +} + +// RemoveReferenceIDs removes the "references" edge to Referrer entities by IDs. +func (ruo *ReferrerUpdateOne) RemoveReferenceIDs(ids ...uuid.UUID) *ReferrerUpdateOne { + ruo.mutation.RemoveReferenceIDs(ids...) + return ruo +} + +// RemoveReferences removes "references" edges to Referrer entities. +func (ruo *ReferrerUpdateOne) RemoveReferences(r ...*Referrer) *ReferrerUpdateOne { + ids := make([]uuid.UUID, len(r)) + for i := range r { + ids[i] = r[i].ID + } + return ruo.RemoveReferenceIDs(ids...) +} + +// ClearOrganizations clears all "organizations" edges to the Organization entity. +func (ruo *ReferrerUpdateOne) ClearOrganizations() *ReferrerUpdateOne { + ruo.mutation.ClearOrganizations() + return ruo +} + +// RemoveOrganizationIDs removes the "organizations" edge to Organization entities by IDs. +func (ruo *ReferrerUpdateOne) RemoveOrganizationIDs(ids ...uuid.UUID) *ReferrerUpdateOne { + ruo.mutation.RemoveOrganizationIDs(ids...) + return ruo +} + +// RemoveOrganizations removes "organizations" edges to Organization entities. +func (ruo *ReferrerUpdateOne) RemoveOrganizations(o ...*Organization) *ReferrerUpdateOne { + ids := make([]uuid.UUID, len(o)) + for i := range o { + ids[i] = o[i].ID + } + return ruo.RemoveOrganizationIDs(ids...) +} + +// Where appends a list predicates to the ReferrerUpdate builder. +func (ruo *ReferrerUpdateOne) Where(ps ...predicate.Referrer) *ReferrerUpdateOne { + ruo.mutation.Where(ps...) + return ruo +} + +// Select allows selecting one or more fields (columns) of the returned entity. +// The default is selecting all fields defined in the entity schema. +func (ruo *ReferrerUpdateOne) Select(field string, fields ...string) *ReferrerUpdateOne { + ruo.fields = append([]string{field}, fields...) + return ruo +} + +// Save executes the query and returns the updated Referrer entity. +func (ruo *ReferrerUpdateOne) Save(ctx context.Context) (*Referrer, error) { + return withHooks(ctx, ruo.sqlSave, ruo.mutation, ruo.hooks) +} + +// SaveX is like Save, but panics if an error occurs. +func (ruo *ReferrerUpdateOne) SaveX(ctx context.Context) *Referrer { + node, err := ruo.Save(ctx) + if err != nil { + panic(err) + } + return node +} + +// Exec executes the query on the entity. +func (ruo *ReferrerUpdateOne) Exec(ctx context.Context) error { + _, err := ruo.Save(ctx) + return err +} + +// ExecX is like Exec, but panics if an error occurs. +func (ruo *ReferrerUpdateOne) ExecX(ctx context.Context) { + if err := ruo.Exec(ctx); err != nil { + panic(err) + } +} + +func (ruo *ReferrerUpdateOne) sqlSave(ctx context.Context) (_node *Referrer, err error) { + _spec := sqlgraph.NewUpdateSpec(referrer.Table, referrer.Columns, sqlgraph.NewFieldSpec(referrer.FieldID, field.TypeUUID)) + id, ok := ruo.mutation.ID() + if !ok { + return nil, &ValidationError{Name: "id", err: errors.New(`ent: missing "Referrer.id" for update`)} + } + _spec.Node.ID.Value = id + if fields := ruo.fields; len(fields) > 0 { + _spec.Node.Columns = make([]string, 0, len(fields)) + _spec.Node.Columns = append(_spec.Node.Columns, referrer.FieldID) + for _, f := range fields { + if !referrer.ValidColumn(f) { + return nil, &ValidationError{Name: f, err: fmt.Errorf("ent: invalid field %q for query", f)} + } + if f != referrer.FieldID { + _spec.Node.Columns = append(_spec.Node.Columns, f) + } + } + } + if ps := ruo.mutation.predicates; len(ps) > 0 { + _spec.Predicate = func(selector *sql.Selector) { + for i := range ps { + ps[i](selector) + } + } + } + if ruo.mutation.ReferencesCleared() { + edge := &sqlgraph.EdgeSpec{ + Rel: sqlgraph.M2M, + Inverse: false, + Table: referrer.ReferencesTable, + Columns: referrer.ReferencesPrimaryKey, + Bidi: false, + Target: &sqlgraph.EdgeTarget{ + IDSpec: sqlgraph.NewFieldSpec(referrer.FieldID, field.TypeUUID), + }, + } + _spec.Edges.Clear = append(_spec.Edges.Clear, edge) + } + if nodes := ruo.mutation.RemovedReferencesIDs(); len(nodes) > 0 && !ruo.mutation.ReferencesCleared() { + edge := &sqlgraph.EdgeSpec{ + Rel: sqlgraph.M2M, + Inverse: false, + Table: referrer.ReferencesTable, + Columns: referrer.ReferencesPrimaryKey, + Bidi: false, + Target: &sqlgraph.EdgeTarget{ + IDSpec: sqlgraph.NewFieldSpec(referrer.FieldID, field.TypeUUID), + }, + } + for _, k := range nodes { + edge.Target.Nodes = append(edge.Target.Nodes, k) + } + _spec.Edges.Clear = append(_spec.Edges.Clear, edge) + } + if nodes := ruo.mutation.ReferencesIDs(); len(nodes) > 0 { + edge := &sqlgraph.EdgeSpec{ + Rel: sqlgraph.M2M, + Inverse: false, + Table: referrer.ReferencesTable, + Columns: referrer.ReferencesPrimaryKey, + Bidi: false, + Target: &sqlgraph.EdgeTarget{ + IDSpec: sqlgraph.NewFieldSpec(referrer.FieldID, field.TypeUUID), + }, + } + for _, k := range nodes { + edge.Target.Nodes = append(edge.Target.Nodes, k) + } + _spec.Edges.Add = append(_spec.Edges.Add, edge) + } + if ruo.mutation.OrganizationsCleared() { + edge := &sqlgraph.EdgeSpec{ + Rel: sqlgraph.M2M, + Inverse: false, + Table: referrer.OrganizationsTable, + Columns: referrer.OrganizationsPrimaryKey, + Bidi: false, + Target: &sqlgraph.EdgeTarget{ + IDSpec: sqlgraph.NewFieldSpec(organization.FieldID, field.TypeUUID), + }, + } + _spec.Edges.Clear = append(_spec.Edges.Clear, edge) + } + if nodes := ruo.mutation.RemovedOrganizationsIDs(); len(nodes) > 0 && !ruo.mutation.OrganizationsCleared() { + edge := &sqlgraph.EdgeSpec{ + Rel: sqlgraph.M2M, + Inverse: false, + Table: referrer.OrganizationsTable, + Columns: referrer.OrganizationsPrimaryKey, + Bidi: false, + Target: &sqlgraph.EdgeTarget{ + IDSpec: sqlgraph.NewFieldSpec(organization.FieldID, field.TypeUUID), + }, + } + for _, k := range nodes { + edge.Target.Nodes = append(edge.Target.Nodes, k) + } + _spec.Edges.Clear = append(_spec.Edges.Clear, edge) + } + if nodes := ruo.mutation.OrganizationsIDs(); len(nodes) > 0 { + edge := &sqlgraph.EdgeSpec{ + Rel: sqlgraph.M2M, + Inverse: false, + Table: referrer.OrganizationsTable, + Columns: referrer.OrganizationsPrimaryKey, + Bidi: false, + Target: &sqlgraph.EdgeTarget{ + IDSpec: sqlgraph.NewFieldSpec(organization.FieldID, field.TypeUUID), + }, + } + for _, k := range nodes { + edge.Target.Nodes = append(edge.Target.Nodes, k) + } + _spec.Edges.Add = append(_spec.Edges.Add, edge) + } + _node = &Referrer{config: ruo.config} + _spec.Assign = _node.assignValues + _spec.ScanValues = _node.scanValues + if err = sqlgraph.UpdateNode(ctx, ruo.driver, _spec); err != nil { + if _, ok := err.(*sqlgraph.NotFoundError); ok { + err = &NotFoundError{referrer.Label} + } else if sqlgraph.IsConstraintError(err) { + err = &ConstraintError{msg: err.Error(), wrap: err} + } + return nil, err + } + ruo.mutation.done = true + return _node, nil +} diff --git a/app/controlplane/internal/data/ent/runtime.go b/app/controlplane/internal/data/ent/runtime.go index 9b43534b6..3aa9d4d43 100644 --- a/app/controlplane/internal/data/ent/runtime.go +++ b/app/controlplane/internal/data/ent/runtime.go @@ -12,6 +12,7 @@ import ( "github.com/chainloop-dev/chainloop/app/controlplane/internal/data/ent/membership" "github.com/chainloop-dev/chainloop/app/controlplane/internal/data/ent/organization" "github.com/chainloop-dev/chainloop/app/controlplane/internal/data/ent/orginvitation" + "github.com/chainloop-dev/chainloop/app/controlplane/internal/data/ent/referrer" "github.com/chainloop-dev/chainloop/app/controlplane/internal/data/ent/robotaccount" "github.com/chainloop-dev/chainloop/app/controlplane/internal/data/ent/schema" "github.com/chainloop-dev/chainloop/app/controlplane/internal/data/ent/user" @@ -120,6 +121,16 @@ func init() { organizationDescID := organizationFields[0].Descriptor() // organization.DefaultID holds the default value on creation for the id field. organization.DefaultID = organizationDescID.Default.(func() uuid.UUID) + referrerFields := schema.Referrer{}.Fields() + _ = referrerFields + // referrerDescCreatedAt is the schema descriptor for created_at field. + referrerDescCreatedAt := referrerFields[4].Descriptor() + // referrer.DefaultCreatedAt holds the default value on creation for the created_at field. + referrer.DefaultCreatedAt = referrerDescCreatedAt.Default.(func() time.Time) + // referrerDescID is the schema descriptor for id field. + referrerDescID := referrerFields[0].Descriptor() + // referrer.DefaultID holds the default value on creation for the id field. + referrer.DefaultID = referrerDescID.Default.(func() uuid.UUID) robotaccountFields := schema.RobotAccount{}.Fields() _ = robotaccountFields // robotaccountDescCreatedAt is the schema descriptor for created_at field. diff --git a/app/controlplane/internal/data/ent/schema-viz.html b/app/controlplane/internal/data/ent/schema-viz.html index 67d03616a..fd14879fa 100644 --- a/app/controlplane/internal/data/ent/schema-viz.html +++ b/app/controlplane/internal/data/ent/schema-viz.html @@ -70,7 +70,7 @@ } - const entGraph = JSON.parse("{\"nodes\":[{\"id\":\"CASBackend\",\"fields\":[{\"name\":\"location\",\"type\":\"string\"},{\"name\":\"provider\",\"type\":\"biz.CASBackendProvider\"},{\"name\":\"description\",\"type\":\"string\"},{\"name\":\"secret_name\",\"type\":\"string\"},{\"name\":\"created_at\",\"type\":\"time.Time\"},{\"name\":\"validation_status\",\"type\":\"biz.CASBackendValidationStatus\"},{\"name\":\"validated_at\",\"type\":\"time.Time\"},{\"name\":\"default\",\"type\":\"bool\"},{\"name\":\"deleted_at\",\"type\":\"time.Time\"},{\"name\":\"fallback\",\"type\":\"bool\"}]},{\"id\":\"CASMapping\",\"fields\":[{\"name\":\"digest\",\"type\":\"string\"},{\"name\":\"created_at\",\"type\":\"time.Time\"}]},{\"id\":\"Integration\",\"fields\":[{\"name\":\"kind\",\"type\":\"string\"},{\"name\":\"description\",\"type\":\"string\"},{\"name\":\"secret_name\",\"type\":\"string\"},{\"name\":\"created_at\",\"type\":\"time.Time\"},{\"name\":\"configuration\",\"type\":\"[]byte\"},{\"name\":\"deleted_at\",\"type\":\"time.Time\"}]},{\"id\":\"IntegrationAttachment\",\"fields\":[{\"name\":\"created_at\",\"type\":\"time.Time\"},{\"name\":\"configuration\",\"type\":\"[]byte\"},{\"name\":\"deleted_at\",\"type\":\"time.Time\"}]},{\"id\":\"Membership\",\"fields\":[{\"name\":\"current\",\"type\":\"bool\"},{\"name\":\"created_at\",\"type\":\"time.Time\"},{\"name\":\"updated_at\",\"type\":\"time.Time\"}]},{\"id\":\"OrgInvitation\",\"fields\":[{\"name\":\"receiver_email\",\"type\":\"string\"},{\"name\":\"status\",\"type\":\"biz.OrgInvitationStatus\"},{\"name\":\"created_at\",\"type\":\"time.Time\"},{\"name\":\"deleted_at\",\"type\":\"time.Time\"},{\"name\":\"organization_id\",\"type\":\"uuid.UUID\"},{\"name\":\"sender_id\",\"type\":\"uuid.UUID\"}]},{\"id\":\"Organization\",\"fields\":[{\"name\":\"name\",\"type\":\"string\"},{\"name\":\"created_at\",\"type\":\"time.Time\"}]},{\"id\":\"RobotAccount\",\"fields\":[{\"name\":\"name\",\"type\":\"string\"},{\"name\":\"created_at\",\"type\":\"time.Time\"},{\"name\":\"revoked_at\",\"type\":\"time.Time\"}]},{\"id\":\"User\",\"fields\":[{\"name\":\"email\",\"type\":\"string\"},{\"name\":\"created_at\",\"type\":\"time.Time\"}]},{\"id\":\"Workflow\",\"fields\":[{\"name\":\"name\",\"type\":\"string\"},{\"name\":\"project\",\"type\":\"string\"},{\"name\":\"team\",\"type\":\"string\"},{\"name\":\"runs_count\",\"type\":\"int\"},{\"name\":\"created_at\",\"type\":\"time.Time\"},{\"name\":\"deleted_at\",\"type\":\"time.Time\"},{\"name\":\"public\",\"type\":\"bool\"}]},{\"id\":\"WorkflowContract\",\"fields\":[{\"name\":\"name\",\"type\":\"string\"},{\"name\":\"created_at\",\"type\":\"time.Time\"},{\"name\":\"deleted_at\",\"type\":\"time.Time\"}]},{\"id\":\"WorkflowContractVersion\",\"fields\":[{\"name\":\"body\",\"type\":\"[]byte\"},{\"name\":\"revision\",\"type\":\"int\"},{\"name\":\"created_at\",\"type\":\"time.Time\"}]},{\"id\":\"WorkflowRun\",\"fields\":[{\"name\":\"created_at\",\"type\":\"time.Time\"},{\"name\":\"finished_at\",\"type\":\"time.Time\"},{\"name\":\"state\",\"type\":\"biz.WorkflowRunStatus\"},{\"name\":\"reason\",\"type\":\"string\"},{\"name\":\"run_url\",\"type\":\"string\"},{\"name\":\"runner_type\",\"type\":\"string\"},{\"name\":\"attestation\",\"type\":\"*dsse.Envelope\"},{\"name\":\"attestation_digest\",\"type\":\"string\"}]}],\"edges\":[{\"from\":\"CASMapping\",\"to\":\"CASBackend\",\"label\":\"cas_backend\"},{\"from\":\"CASMapping\",\"to\":\"WorkflowRun\",\"label\":\"workflow_run\"},{\"from\":\"CASMapping\",\"to\":\"Organization\",\"label\":\"organization\"},{\"from\":\"IntegrationAttachment\",\"to\":\"Integration\",\"label\":\"integration\"},{\"from\":\"IntegrationAttachment\",\"to\":\"Workflow\",\"label\":\"workflow\"},{\"from\":\"OrgInvitation\",\"to\":\"Organization\",\"label\":\"organization\"},{\"from\":\"OrgInvitation\",\"to\":\"User\",\"label\":\"sender\"},{\"from\":\"Organization\",\"to\":\"Membership\",\"label\":\"memberships\"},{\"from\":\"Organization\",\"to\":\"WorkflowContract\",\"label\":\"workflow_contracts\"},{\"from\":\"Organization\",\"to\":\"Workflow\",\"label\":\"workflows\"},{\"from\":\"Organization\",\"to\":\"CASBackend\",\"label\":\"cas_backends\"},{\"from\":\"Organization\",\"to\":\"Integration\",\"label\":\"integrations\"},{\"from\":\"RobotAccount\",\"to\":\"WorkflowRun\",\"label\":\"workflowruns\"},{\"from\":\"User\",\"to\":\"Membership\",\"label\":\"memberships\"},{\"from\":\"Workflow\",\"to\":\"RobotAccount\",\"label\":\"robotaccounts\"},{\"from\":\"Workflow\",\"to\":\"WorkflowRun\",\"label\":\"workflowruns\"},{\"from\":\"Workflow\",\"to\":\"WorkflowContract\",\"label\":\"contract\"},{\"from\":\"WorkflowContract\",\"to\":\"WorkflowContractVersion\",\"label\":\"versions\"},{\"from\":\"WorkflowRun\",\"to\":\"WorkflowContractVersion\",\"label\":\"contract_version\"},{\"from\":\"WorkflowRun\",\"to\":\"CASBackend\",\"label\":\"cas_backends\"}]}"); + const entGraph = JSON.parse("{\"nodes\":[{\"id\":\"CASBackend\",\"fields\":[{\"name\":\"location\",\"type\":\"string\"},{\"name\":\"provider\",\"type\":\"biz.CASBackendProvider\"},{\"name\":\"description\",\"type\":\"string\"},{\"name\":\"secret_name\",\"type\":\"string\"},{\"name\":\"created_at\",\"type\":\"time.Time\"},{\"name\":\"validation_status\",\"type\":\"biz.CASBackendValidationStatus\"},{\"name\":\"validated_at\",\"type\":\"time.Time\"},{\"name\":\"default\",\"type\":\"bool\"},{\"name\":\"deleted_at\",\"type\":\"time.Time\"},{\"name\":\"fallback\",\"type\":\"bool\"}]},{\"id\":\"CASMapping\",\"fields\":[{\"name\":\"digest\",\"type\":\"string\"},{\"name\":\"created_at\",\"type\":\"time.Time\"}]},{\"id\":\"Integration\",\"fields\":[{\"name\":\"kind\",\"type\":\"string\"},{\"name\":\"description\",\"type\":\"string\"},{\"name\":\"secret_name\",\"type\":\"string\"},{\"name\":\"created_at\",\"type\":\"time.Time\"},{\"name\":\"configuration\",\"type\":\"[]byte\"},{\"name\":\"deleted_at\",\"type\":\"time.Time\"}]},{\"id\":\"IntegrationAttachment\",\"fields\":[{\"name\":\"created_at\",\"type\":\"time.Time\"},{\"name\":\"configuration\",\"type\":\"[]byte\"},{\"name\":\"deleted_at\",\"type\":\"time.Time\"}]},{\"id\":\"Membership\",\"fields\":[{\"name\":\"current\",\"type\":\"bool\"},{\"name\":\"created_at\",\"type\":\"time.Time\"},{\"name\":\"updated_at\",\"type\":\"time.Time\"}]},{\"id\":\"OrgInvitation\",\"fields\":[{\"name\":\"receiver_email\",\"type\":\"string\"},{\"name\":\"status\",\"type\":\"biz.OrgInvitationStatus\"},{\"name\":\"created_at\",\"type\":\"time.Time\"},{\"name\":\"deleted_at\",\"type\":\"time.Time\"},{\"name\":\"organization_id\",\"type\":\"uuid.UUID\"},{\"name\":\"sender_id\",\"type\":\"uuid.UUID\"}]},{\"id\":\"Organization\",\"fields\":[{\"name\":\"name\",\"type\":\"string\"},{\"name\":\"created_at\",\"type\":\"time.Time\"}]},{\"id\":\"Referrer\",\"fields\":[{\"name\":\"digest\",\"type\":\"string\"},{\"name\":\"artifact_type\",\"type\":\"string\"},{\"name\":\"downloadable\",\"type\":\"bool\"},{\"name\":\"created_at\",\"type\":\"time.Time\"}]},{\"id\":\"RobotAccount\",\"fields\":[{\"name\":\"name\",\"type\":\"string\"},{\"name\":\"created_at\",\"type\":\"time.Time\"},{\"name\":\"revoked_at\",\"type\":\"time.Time\"}]},{\"id\":\"User\",\"fields\":[{\"name\":\"email\",\"type\":\"string\"},{\"name\":\"created_at\",\"type\":\"time.Time\"}]},{\"id\":\"Workflow\",\"fields\":[{\"name\":\"name\",\"type\":\"string\"},{\"name\":\"project\",\"type\":\"string\"},{\"name\":\"team\",\"type\":\"string\"},{\"name\":\"runs_count\",\"type\":\"int\"},{\"name\":\"created_at\",\"type\":\"time.Time\"},{\"name\":\"deleted_at\",\"type\":\"time.Time\"},{\"name\":\"public\",\"type\":\"bool\"}]},{\"id\":\"WorkflowContract\",\"fields\":[{\"name\":\"name\",\"type\":\"string\"},{\"name\":\"created_at\",\"type\":\"time.Time\"},{\"name\":\"deleted_at\",\"type\":\"time.Time\"}]},{\"id\":\"WorkflowContractVersion\",\"fields\":[{\"name\":\"body\",\"type\":\"[]byte\"},{\"name\":\"revision\",\"type\":\"int\"},{\"name\":\"created_at\",\"type\":\"time.Time\"}]},{\"id\":\"WorkflowRun\",\"fields\":[{\"name\":\"created_at\",\"type\":\"time.Time\"},{\"name\":\"finished_at\",\"type\":\"time.Time\"},{\"name\":\"state\",\"type\":\"biz.WorkflowRunStatus\"},{\"name\":\"reason\",\"type\":\"string\"},{\"name\":\"run_url\",\"type\":\"string\"},{\"name\":\"runner_type\",\"type\":\"string\"},{\"name\":\"attestation\",\"type\":\"*dsse.Envelope\"},{\"name\":\"attestation_digest\",\"type\":\"string\"}]}],\"edges\":[{\"from\":\"CASMapping\",\"to\":\"CASBackend\",\"label\":\"cas_backend\"},{\"from\":\"CASMapping\",\"to\":\"WorkflowRun\",\"label\":\"workflow_run\"},{\"from\":\"CASMapping\",\"to\":\"Organization\",\"label\":\"organization\"},{\"from\":\"IntegrationAttachment\",\"to\":\"Integration\",\"label\":\"integration\"},{\"from\":\"IntegrationAttachment\",\"to\":\"Workflow\",\"label\":\"workflow\"},{\"from\":\"OrgInvitation\",\"to\":\"Organization\",\"label\":\"organization\"},{\"from\":\"OrgInvitation\",\"to\":\"User\",\"label\":\"sender\"},{\"from\":\"Organization\",\"to\":\"Membership\",\"label\":\"memberships\"},{\"from\":\"Organization\",\"to\":\"WorkflowContract\",\"label\":\"workflow_contracts\"},{\"from\":\"Organization\",\"to\":\"Workflow\",\"label\":\"workflows\"},{\"from\":\"Organization\",\"to\":\"CASBackend\",\"label\":\"cas_backends\"},{\"from\":\"Organization\",\"to\":\"Integration\",\"label\":\"integrations\"},{\"from\":\"Referrer\",\"to\":\"Referrer\",\"label\":\"references\"},{\"from\":\"Referrer\",\"to\":\"Organization\",\"label\":\"organizations\"},{\"from\":\"RobotAccount\",\"to\":\"WorkflowRun\",\"label\":\"workflowruns\"},{\"from\":\"User\",\"to\":\"Membership\",\"label\":\"memberships\"},{\"from\":\"Workflow\",\"to\":\"RobotAccount\",\"label\":\"robotaccounts\"},{\"from\":\"Workflow\",\"to\":\"WorkflowRun\",\"label\":\"workflowruns\"},{\"from\":\"Workflow\",\"to\":\"WorkflowContract\",\"label\":\"contract\"},{\"from\":\"WorkflowContract\",\"to\":\"WorkflowContractVersion\",\"label\":\"versions\"},{\"from\":\"WorkflowRun\",\"to\":\"WorkflowContractVersion\",\"label\":\"contract_version\"},{\"from\":\"WorkflowRun\",\"to\":\"CASBackend\",\"label\":\"cas_backends\"}]}"); const nodes = new vis.DataSet((entGraph.nodes || []).map(n => ({ id: n.id, diff --git a/app/controlplane/internal/data/ent/schema/organization.go b/app/controlplane/internal/data/ent/schema/organization.go index ce6dad5fc..b76c0f22c 100644 --- a/app/controlplane/internal/data/ent/schema/organization.go +++ b/app/controlplane/internal/data/ent/schema/organization.go @@ -53,5 +53,6 @@ func (Organization) Edges() []ent.Edge { edge.To("workflows", Workflow.Type).StorageKey(edge.Column("organization_id")).Annotations(entsql.Annotation{OnDelete: entsql.Cascade}), edge.To("cas_backends", CASBackend.Type).Annotations(entsql.Annotation{OnDelete: entsql.Cascade}), edge.To("integrations", Integration.Type).Annotations(entsql.Annotation{OnDelete: entsql.Cascade}), + edge.From("referrers", Referrer.Type).Ref("organizations"), } } diff --git a/app/controlplane/internal/data/ent/schema/referrer.go b/app/controlplane/internal/data/ent/schema/referrer.go new file mode 100644 index 000000000..218abb0ed --- /dev/null +++ b/app/controlplane/internal/data/ent/schema/referrer.go @@ -0,0 +1,62 @@ +// +// Copyright 2023 The Chainloop Authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package schema + +import ( + "time" + + "entgo.io/ent" + "entgo.io/ent/dialect/entsql" + "entgo.io/ent/schema/edge" + "entgo.io/ent/schema/field" + "entgo.io/ent/schema/index" + "github.com/google/uuid" +) + +type Referrer struct { + ent.Schema +} + +func (Referrer) Fields() []ent.Field { + return []ent.Field{ + field.UUID("id", uuid.UUID{}).Default(uuid.New).Unique(), + field.String("digest").Immutable(), + // referrer type i.e CONTAINER + field.String("artifact_type").Immutable(), + // wether it can be downloaded from CAS or not + field.Bool("downloadable").Immutable(), + field.Time("created_at"). + Default(time.Now). + Immutable(). + Annotations(&entsql.Annotation{Default: "CURRENT_TIMESTAMP"}), + } +} + +func (Referrer) Edges() []ent.Edge { + return []ent.Edge{ + // M2M referrer can refer to itself via references + edge.To("references", Referrer.Type).From("referred_by").Immutable(), + // M2M. referrer can be part of multiple organizations + edge.To("organizations", Organization.Type), + } +} + +func (Referrer) Indexes() []ent.Index { + return []ent.Index{ + // For now we only guarantee that the digest is unique in the scope of the artifact type + index.Fields("digest", "artifact_type").Unique(), + } +} diff --git a/app/controlplane/internal/data/ent/tx.go b/app/controlplane/internal/data/ent/tx.go index 6c0845813..c840bf829 100644 --- a/app/controlplane/internal/data/ent/tx.go +++ b/app/controlplane/internal/data/ent/tx.go @@ -26,6 +26,8 @@ type Tx struct { OrgInvitation *OrgInvitationClient // Organization is the client for interacting with the Organization builders. Organization *OrganizationClient + // Referrer is the client for interacting with the Referrer builders. + Referrer *ReferrerClient // RobotAccount is the client for interacting with the RobotAccount builders. RobotAccount *RobotAccountClient // User is the client for interacting with the User builders. @@ -176,6 +178,7 @@ func (tx *Tx) init() { tx.Membership = NewMembershipClient(tx.config) tx.OrgInvitation = NewOrgInvitationClient(tx.config) tx.Organization = NewOrganizationClient(tx.config) + tx.Referrer = NewReferrerClient(tx.config) tx.RobotAccount = NewRobotAccountClient(tx.config) tx.User = NewUserClient(tx.config) tx.Workflow = NewWorkflowClient(tx.config) diff --git a/app/controlplane/internal/data/referrer.go b/app/controlplane/internal/data/referrer.go new file mode 100644 index 000000000..4d31ff5d1 --- /dev/null +++ b/app/controlplane/internal/data/referrer.go @@ -0,0 +1,171 @@ +// +// Copyright 2023 The Chainloop Authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package data + +import ( + "context" + "fmt" + + "github.com/chainloop-dev/chainloop/app/controlplane/internal/biz" + "github.com/chainloop-dev/chainloop/app/controlplane/internal/data/ent" + "github.com/chainloop-dev/chainloop/app/controlplane/internal/data/ent/referrer" + "github.com/go-kratos/kratos/v2/log" + "github.com/google/uuid" +) + +type ReferrerRepo struct { + data *Data + log *log.Helper +} + +func NewReferrerRepo(data *Data, logger log.Logger) biz.ReferrerRepo { + return &ReferrerRepo{ + data: data, + log: log.NewHelper(logger), + } +} + +type storedReferrerMap map[string]*ent.Referrer + +func (r *ReferrerRepo) Save(ctx context.Context, input biz.ReferrerMap, orgID uuid.UUID) error { + // Start transaction + tx, err := r.data.db.Tx(ctx) + if err != nil { + return fmt.Errorf("failed to create transaction: %w", err) + } + + storedMap := make(storedReferrerMap) + // 1 - Find or create each referrer + for digest, r := range input { + // Check if it exists already, if not create it + storedRef, err := tx.Referrer.Query().Where(referrer.Digest(digest), referrer.ArtifactType(r.ArtifactType)).Only(ctx) + if err != nil { + if !ent.IsNotFound(err) { + return fmt.Errorf("failed to query referrer: %w", err) + } + + storedRef, err = tx.Referrer.Create(). + SetDigest(digest).SetArtifactType(r.ArtifactType).SetDownloadable(r.Downloadable).AddOrganizationIDs(orgID).Save(ctx) + if err != nil { + return fmt.Errorf("failed to create referrer: %w", err) + } + } + + // associate it with the organization + storedRef, err = storedRef.Update().AddOrganizationIDs(orgID).Save(ctx) + if err != nil { + return fmt.Errorf("failed to add organization to referrer: %w", err) + } + + // Store it in the map + storedMap[digest] = storedRef + } + + // 2 - define the relationship between referrers + for digest, inputRef := range input { + // This is the current item stored in DB + storedReferrer := storedMap[digest] + // Iterate on the items it refer to (references) + for _, ref := range inputRef.References { + // amd find it in the DB + storedReference, ok := storedMap[ref] + if !ok { + return fmt.Errorf("referrer %s not found", ref) + } + + // Create the relationship + _, err := storedReferrer.Update().AddReferenceIDs(storedReference.ID).Save(ctx) + if err != nil { + return fmt.Errorf("failed to create referrer relationship: %w", err) + } + } + } + + if err := tx.Commit(); err != nil { + return fmt.Errorf("failed to commit transaction: %w", err) + } + + return nil +} + +func (r *ReferrerRepo) GetFromRoot(ctx context.Context, digest string) (*biz.StoredReferrer, error) { + // Find the referrer recursively starting from the root + res, err := r.doGet(ctx, digest, 0) + if err != nil { + return nil, fmt.Errorf("failed to get referrer: %w", err) + } + + return res, nil +} + +// max number of recursive levels to traverse +// we just care about 1 level, i.e att -> commit, or commit -> attestation +// we also need to limit this because there might be cycles +const maxTraverseLevels = 1 + +func (r *ReferrerRepo) doGet(ctx context.Context, digest string, level int) (*biz.StoredReferrer, error) { + // Find the referrer + // if there is more than 1 item with the same digest+artifactType it will fail + ref, err := r.data.db.Referrer.Query().Where(referrer.Digest(digest)).Only(ctx) + if err != nil { + if ent.IsNotFound(err) { + return nil, nil + } else if ent.IsNotSingular(err) { + return nil, fmt.Errorf("found more than one referrer with digest %s, please provide artifact type", digest) + } + + return nil, fmt.Errorf("failed to query referrer: %w", err) + } + + // Assemble the referrer to return + res := &biz.StoredReferrer{ + ID: ref.ID, + CreatedAt: toTimePtr(ref.CreatedAt), + Digest: ref.Digest, + ArtifactType: ref.ArtifactType, + Downloadable: ref.Downloadable, + } + + // with all the organizationIDs attached + res.OrgIDs, err = ref.QueryOrganizations().IDs(ctx) + if err != nil { + return nil, fmt.Errorf("failed to query organizations: %w", err) + } + + // Next: We'll find the references recursively up to a max of maxTraverseLevels levels + if level >= maxTraverseLevels { + return res, nil + } + + // Find the references and call recursively + refs, err := ref.QueryReferences().Order(referrer.ByDigest()).All(ctx) + if err != nil { + return nil, fmt.Errorf("failed to query references: %w", err) + } + + // Add the references to the result + for _, reference := range refs { + // Call recursively the function + ref, err := r.doGet(ctx, reference.Digest, level+1) + if err != nil { + return nil, fmt.Errorf("failed to get referrer: %w", err) + } + + res.References = append(res.References, ref) + } + + return res, nil +} diff --git a/app/controlplane/internal/service/attestation.go b/app/controlplane/internal/service/attestation.go index 5841c05e5..c262be41f 100644 --- a/app/controlplane/internal/service/attestation.go +++ b/app/controlplane/internal/service/attestation.go @@ -50,6 +50,7 @@ type AttestationService struct { casCredsUseCase *biz.CASCredentialsUseCase attestationUseCase *biz.AttestationUseCase casMappingUseCase *biz.CASMappingUseCase + referrerUseCase *biz.ReferrerUseCase } type NewAttestationServiceOpts struct { @@ -63,6 +64,7 @@ type NewAttestationServiceOpts struct { AttestationUC *biz.AttestationUseCase FanoutDispatcher *dispatcher.FanOutDispatcher CASMappingUseCase *biz.CASMappingUseCase + ReferrerUC *biz.ReferrerUseCase Opts []NewOpt } @@ -79,6 +81,7 @@ func NewAttestationService(opts *NewAttestationServiceOpts) *AttestationService integrationDispatcher: opts.FanoutDispatcher, attestationUseCase: opts.AttestationUC, casMappingUseCase: opts.CASMappingUseCase, + referrerUseCase: opts.ReferrerUC, } } @@ -217,6 +220,11 @@ func (s *AttestationService) Store(ctx context.Context, req *cpAPI.AttestationSe return nil, sl.LogAndMaskErr(err, s.log) } + // Store the exploded attestation referrer information in the DB + if err := s.referrerUseCase.ExtractAndPersist(ctx, envelope, robotAccount.OrgID); err != nil { + return nil, sl.LogAndMaskErr(err, s.log) + } + if !casBackend.Inline { // Store the mappings in the DB references, err := s.casMappingUseCase.LookupDigestsInAttestation(envelope) diff --git a/internal/attestation/renderer/chainloop/chainloop.go b/internal/attestation/renderer/chainloop/chainloop.go index bfdb47b10..de38ccd56 100644 --- a/internal/attestation/renderer/chainloop/chainloop.go +++ b/internal/attestation/renderer/chainloop/chainloop.go @@ -199,7 +199,7 @@ func (p *ProvenancePredicateCommon) GetAnnotations() map[string]string { const ( rendererPrefix = "chainloop." // Subject names - subjectGitHead = "git.head" + SubjectGitHead = "git.head" subjectGitAnnotationAuthorEmail = "author.email" subjectGitAnnotationAuthorName = "author.name" subjectGitAnnotationWhen = "date" @@ -208,9 +208,9 @@ const ( ) var ( - annotationMaterialType = prefixed("material.type") - annotationMaterialName = prefixed("material.name") - annotationMaterialCAS = prefixed("material.cas") + AnnotationMaterialType = prefixed("material.type") + AnnotationMaterialName = prefixed("material.name") + AnnotationMaterialCAS = prefixed("material.cas") annotationMaterialInlineCAS = prefixed("material.cas.inline") ) diff --git a/internal/attestation/renderer/chainloop/v02.go b/internal/attestation/renderer/chainloop/v02.go index d2a2de79f..0f297e34f 100644 --- a/internal/attestation/renderer/chainloop/v02.go +++ b/internal/attestation/renderer/chainloop/v02.go @@ -117,7 +117,7 @@ func (r *RendererV02) subject() ([]*intoto.ResourceDescriptor, error) { } subject = append(subject, &intoto.ResourceDescriptor{ - Name: subjectGitHead, + Name: SubjectGitHead, Digest: map[string]string{"sha1": head.GetHash()}, Annotations: annotations, }) @@ -209,8 +209,8 @@ func outputMaterials(att *v1.Attestation, onlyOutput bool) ([]*intoto.ResourceDe // Required, built-in annotations annotationsM := map[string]interface{}{ - annotationMaterialType: artifactType.String(), - annotationMaterialName: mdefName, + AnnotationMaterialType: artifactType.String(), + AnnotationMaterialName: mdefName, } // Custom annotations, it does not override the built-in ones @@ -222,7 +222,7 @@ func outputMaterials(att *v1.Attestation, onlyOutput bool) ([]*intoto.ResourceDe } if mdef.UploadedToCas { - annotationsM[annotationMaterialCAS] = true + annotationsM[AnnotationMaterialCAS] = true } else if mdef.InlineCas { annotationsM[annotationMaterialInlineCAS] = true } @@ -270,7 +270,7 @@ func normalizeMaterial(material *intoto.ResourceDescriptor) (*NormalizedMaterial m.Annotations[k] = v.GetStringValue() } - mType, ok := mAnnotationsMap[annotationMaterialType] + mType, ok := mAnnotationsMap[AnnotationMaterialType] if !ok { return nil, fmt.Errorf("material type not found") } @@ -278,7 +278,7 @@ func normalizeMaterial(material *intoto.ResourceDescriptor) (*NormalizedMaterial // Set the type m.Type = mType.GetStringValue() - mName, ok := mAnnotationsMap[annotationMaterialName] + mName, ok := mAnnotationsMap[AnnotationMaterialName] if !ok { return nil, fmt.Errorf("material name not found") } @@ -314,7 +314,7 @@ func normalizeMaterial(material *intoto.ResourceDescriptor) (*NormalizedMaterial // In the case of container images for example the value is in the name field m.Value = material.Name - if v, ok := mAnnotationsMap[annotationMaterialCAS]; ok && v.GetBoolValue() { + if v, ok := mAnnotationsMap[AnnotationMaterialCAS]; ok && v.GetBoolValue() { m.UploadedToCAS = true } From 5167e752df7066422f74c2ddfb4791f2e9762b7d Mon Sep 17 00:00:00 2001 From: Miguel Martinez Trivino Date: Thu, 9 Nov 2023 11:22:00 +0100 Subject: [PATCH 2/2] fix(DB): rename kind Signed-off-by: Miguel Martinez Trivino --- app/controlplane/internal/biz/referrer.go | 24 +++--- .../internal/biz/referrer_integration_test.go | 32 +++---- .../internal/biz/referrer_test.go | 34 ++++---- .../ent/migrate/migrations/20231109101843.sql | 4 + .../data/ent/migrate/migrations/atlas.sum | 3 +- .../internal/data/ent/migrate/schema.go | 4 +- .../internal/data/ent/mutation.go | 52 ++++++------ .../internal/data/ent/referrer.go | 16 ++-- .../internal/data/ent/referrer/referrer.go | 12 +-- .../internal/data/ent/referrer/where.go | 84 +++++++++---------- .../internal/data/ent/referrer_create.go | 16 ++-- .../internal/data/ent/schema-viz.html | 2 +- .../internal/data/ent/schema/referrer.go | 9 +- app/controlplane/internal/data/referrer.go | 6 +- 14 files changed, 152 insertions(+), 146 deletions(-) create mode 100644 app/controlplane/internal/data/ent/migrate/migrations/20231109101843.sql diff --git a/app/controlplane/internal/biz/referrer.go b/app/controlplane/internal/biz/referrer.go index 63748b6c3..4f722c4ec 100644 --- a/app/controlplane/internal/biz/referrer.go +++ b/app/controlplane/internal/biz/referrer.go @@ -33,8 +33,8 @@ import ( ) type Referrer struct { - Digest string - ArtifactType string + Digest string + Kind string // Wether the item is downloadable from CAS or not Downloadable bool // points to other digests @@ -43,9 +43,9 @@ type Referrer struct { // Actual referrer stored in the DB which includes a nested list of storedReferences type StoredReferrer struct { - ID uuid.UUID - Digest string - ArtifactType string + ID uuid.UUID + Digest string + Kind string // Wether the item is downloadable from CAS or not Downloadable bool CreatedAt *time.Time @@ -146,7 +146,7 @@ func extractReferrers(att *dsse.Envelope) (ReferrerMap, error) { attestationHash := h.String() referrers[attestationHash] = &Referrer{ Digest: attestationHash, - ArtifactType: referrerAttestationType, + Kind: referrerAttestationType, Downloadable: true, } @@ -169,8 +169,8 @@ func extractReferrers(att *dsse.Envelope) (ReferrerMap, error) { // i.e the same SBOM twice, in that case we don't want to create a new referrer // If we are providing different types for the same digest, we should error out if r, ok := referrers[material.Hash.String()]; ok { - if r.ArtifactType != material.Type { - return nil, fmt.Errorf("material %s has different types: %s and %s", material.Hash.String(), r.ArtifactType, material.Type) + if r.Kind != material.Type { + return nil, fmt.Errorf("material %s has different types: %s and %s", material.Hash.String(), r.Kind, material.Type) } continue @@ -178,7 +178,7 @@ func extractReferrers(att *dsse.Envelope) (ReferrerMap, error) { referrers[material.Hash.String()] = &Referrer{ Digest: material.Hash.String(), - ArtifactType: material.Type, + Kind: material.Type, Downloadable: material.UploadedToCAS, } @@ -235,8 +235,8 @@ func intotoSubjectToReferrer(r *v1.ResourceDescriptor) (*Referrer, error) { } return &Referrer{ - Digest: digestStr, - ArtifactType: referrerGitHeadType, + Digest: digestStr, + Kind: referrerGitHeadType, }, nil } @@ -264,7 +264,7 @@ func intotoSubjectToReferrer(r *v1.ResourceDescriptor) (*Referrer, error) { return &Referrer{ Digest: digestStr, - ArtifactType: materialType, + Kind: materialType, Downloadable: uploadedToCAS, }, nil } diff --git a/app/controlplane/internal/biz/referrer_integration_test.go b/app/controlplane/internal/biz/referrer_integration_test.go index 004cbbe29..c287d2ca0 100644 --- a/app/controlplane/internal/biz/referrer_integration_test.go +++ b/app/controlplane/internal/biz/referrer_integration_test.go @@ -38,42 +38,42 @@ func (s *referrerIntegrationTestSuite) TestExtractAndPersists() { wantReferrerAtt := &biz.StoredReferrer{ Digest: "sha256:ad704d286bcad6e155e71c33d48247931231338396acbcd9769087530085b2a2", - ArtifactType: "ATTESTATION", + Kind: "ATTESTATION", Downloadable: true, } wantReferrerCommit := &biz.StoredReferrer{ - Digest: "sha1:78ac366c9e8a300d51808d581422ca61f7b5b721", - ArtifactType: "GIT_HEAD_COMMIT", + Digest: "sha1:78ac366c9e8a300d51808d581422ca61f7b5b721", + Kind: "GIT_HEAD_COMMIT", } wantReferrerSBOM := &biz.StoredReferrer{ Digest: "sha256:16159bb881eb4ab7eb5d8afc5350b0feeed1e31c0a268e355e74f9ccbe885e0c", - ArtifactType: "SBOM_CYCLONEDX_JSON", + Kind: "SBOM_CYCLONEDX_JSON", Downloadable: true, } wantReferrerArtifact := &biz.StoredReferrer{ Digest: "sha256:385c4188b9c080499413f2e0fa0b3951ed107b5f0cb35c2f2b1f07a7be9a7512", - ArtifactType: "ARTIFACT", + Kind: "ARTIFACT", Downloadable: true, } wantReferrerOpenVEX := &biz.StoredReferrer{ Digest: "sha256:b4bd86d5855f94bcac0a92d3100ae7b85d050bd2e5fb9037a200e5f5f0b073a2", - ArtifactType: "OPENVEX", + Kind: "OPENVEX", Downloadable: true, } wantReferrerSarif := &biz.StoredReferrer{ Digest: "sha256:c4a63494f9289dd9fd44f841efb4f5b52765c2de6332f2d86e5f6c0340b40a95", - ArtifactType: "SARIF", + Kind: "SARIF", Downloadable: true, } wantReferrerContainerImage := &biz.StoredReferrer{ - Digest: "sha256:fbd9335f55d83d8aaf9ab1a539b0f2a87b444e8c54f34c9a1ca9d7df15605db4", - ArtifactType: "CONTAINER_IMAGE", + Digest: "sha256:fbd9335f55d83d8aaf9ab1a539b0f2a87b444e8c54f34c9a1ca9d7df15605db4", + Kind: "CONTAINER_IMAGE", } s.T().Run("creation fails if the org doesn't exist", func(t *testing.T) { @@ -104,7 +104,7 @@ func (s *referrerIntegrationTestSuite) TestExtractAndPersists() { // parent i.e attestation s.Equal(wantReferrerAtt.Digest, got.Digest) s.Equal(wantReferrerAtt.Downloadable, got.Downloadable) - s.Equal(wantReferrerAtt.ArtifactType, got.ArtifactType) + s.Equal(wantReferrerAtt.Kind, got.Kind) // it has all the references require.Len(t, got.References, 6) @@ -112,7 +112,7 @@ func (s *referrerIntegrationTestSuite) TestExtractAndPersists() { wantReferrerCommit, wantReferrerSBOM, wantReferrerArtifact, wantReferrerOpenVEX, wantReferrerSarif, wantReferrerContainerImage} { gotR := got.References[i] s.Equal(want.Digest, gotR.Digest) - s.Equal(want.ArtifactType, gotR.ArtifactType) + s.Equal(want.Kind, gotR.Kind) s.Equal(want.Downloadable, gotR.Downloadable) } s.Equal([]uuid.UUID{s.orgUUID}, got.OrgIDs) @@ -146,11 +146,11 @@ func (s *referrerIntegrationTestSuite) TestExtractAndPersists() { // parent i.e attestation s.Equal(wantReferrerContainerImage.Digest, got.Digest) s.Equal(wantReferrerContainerImage.Downloadable, got.Downloadable) - s.Equal(wantReferrerContainerImage.ArtifactType, got.ArtifactType) + s.Equal(wantReferrerContainerImage.Kind, got.Kind) // it's connected to the attestation require.Len(t, got.References, 1) s.Equal(wantReferrerAtt.Digest, got.References[0].Digest) - s.Equal(wantReferrerAtt.ArtifactType, got.References[0].ArtifactType) + s.Equal(wantReferrerAtt.Kind, got.References[0].Kind) s.Equal(wantReferrerAtt.Downloadable, got.References[0].Downloadable) }) @@ -160,7 +160,7 @@ func (s *referrerIntegrationTestSuite) TestExtractAndPersists() { // parent i.e attestation s.Equal(wantReferrerSarif.Digest, got.Digest) s.Equal(wantReferrerSarif.Downloadable, got.Downloadable) - s.Equal(wantReferrerSarif.ArtifactType, got.ArtifactType) + s.Equal(wantReferrerSarif.Kind, got.Kind) require.Len(t, got.References, 0) }) @@ -201,9 +201,9 @@ func (s *referrerIntegrationTestSuite) TestExtractAndPersists() { s.NoError(err) // it should be referenced by two attestations since it's subject of both require.Len(t, got.References, 2) - s.Equal("ATTESTATION", got.References[0].ArtifactType) + s.Equal("ATTESTATION", got.References[0].Kind) s.Equal(wantReferrerAtt.Digest, got.References[0].Digest) - s.Equal("ATTESTATION", got.References[1].ArtifactType) + s.Equal("ATTESTATION", got.References[1].Kind) s.Equal("sha256:c90ccaab0b2cfda9980836aef407f62d747680ea9793ddc6ad2e2d7ab615933d", got.References[1].Digest) }) } diff --git a/app/controlplane/internal/biz/referrer_test.go b/app/controlplane/internal/biz/referrer_test.go index f42e20cf6..bc76c5d43 100644 --- a/app/controlplane/internal/biz/referrer_test.go +++ b/app/controlplane/internal/biz/referrer_test.go @@ -39,7 +39,7 @@ func (s *referrerTestSuite) TestExtractReferrers() { want: ReferrerMap{ "sha256:1a077137aef7ca208b80c339769d0d7eecacc2850368e56e834cda1750ce413a": &Referrer{ Digest: "sha256:1a077137aef7ca208b80c339769d0d7eecacc2850368e56e834cda1750ce413a", - ArtifactType: "ATTESTATION", + Kind: "ATTESTATION", Downloadable: true, References: []string{ "sha256:264f55a6ff9cec2f4742a9faacc033b29f65c04dd4480e71e23579d484288d61", @@ -48,12 +48,12 @@ func (s *referrerTestSuite) TestExtractReferrers() { }, "sha256:16159bb881eb4ab7eb5d8afc5350b0feeed1e31c0a268e355e74f9ccbe885e0c": &Referrer{ Digest: "sha256:16159bb881eb4ab7eb5d8afc5350b0feeed1e31c0a268e355e74f9ccbe885e0c", - ArtifactType: "SBOM_CYCLONEDX_JSON", + Kind: "SBOM_CYCLONEDX_JSON", Downloadable: true, }, "sha256:264f55a6ff9cec2f4742a9faacc033b29f65c04dd4480e71e23579d484288d61": &Referrer{ - Digest: "sha256:264f55a6ff9cec2f4742a9faacc033b29f65c04dd4480e71e23579d484288d61", - ArtifactType: "CONTAINER_IMAGE", + Digest: "sha256:264f55a6ff9cec2f4742a9faacc033b29f65c04dd4480e71e23579d484288d61", + Kind: "CONTAINER_IMAGE", }, }, }, @@ -63,15 +63,15 @@ func (s *referrerTestSuite) TestExtractReferrers() { want: ReferrerMap{ // the git commit a subject in the attestation "sha1:58442b61a6564df94857ff69ad7c340c55703e20": &Referrer{ - Digest: "sha1:58442b61a6564df94857ff69ad7c340c55703e20", - ArtifactType: "GIT_HEAD_COMMIT", + Digest: "sha1:58442b61a6564df94857ff69ad7c340c55703e20", + Kind: "GIT_HEAD_COMMIT", References: []string{ "sha256:507dddb505ceb53fb32cde31f9935c9a3ebc7b7d82f36101de638b1ab9367344", }, }, "sha256:507dddb505ceb53fb32cde31f9935c9a3ebc7b7d82f36101de638b1ab9367344": &Referrer{ - Digest: "sha256:507dddb505ceb53fb32cde31f9935c9a3ebc7b7d82f36101de638b1ab9367344", - ArtifactType: "ATTESTATION", + Digest: "sha256:507dddb505ceb53fb32cde31f9935c9a3ebc7b7d82f36101de638b1ab9367344", + Kind: "ATTESTATION", References: []string{ "sha1:58442b61a6564df94857ff69ad7c340c55703e20", }, @@ -84,16 +84,16 @@ func (s *referrerTestSuite) TestExtractReferrers() { inputPath: "testdata/attestations/with-git-subject.json", want: ReferrerMap{ "sha256:fbd9335f55d83d8aaf9ab1a539b0f2a87b444e8c54f34c9a1ca9d7df15605db4": &Referrer{ - Digest: "sha256:fbd9335f55d83d8aaf9ab1a539b0f2a87b444e8c54f34c9a1ca9d7df15605db4", - ArtifactType: "CONTAINER_IMAGE", + Digest: "sha256:fbd9335f55d83d8aaf9ab1a539b0f2a87b444e8c54f34c9a1ca9d7df15605db4", + Kind: "CONTAINER_IMAGE", // the container image is a subject in the attestation References: []string{ "sha256:ad704d286bcad6e155e71c33d48247931231338396acbcd9769087530085b2a2", }, }, "sha1:78ac366c9e8a300d51808d581422ca61f7b5b721": &Referrer{ - Digest: "sha1:78ac366c9e8a300d51808d581422ca61f7b5b721", - ArtifactType: "GIT_HEAD_COMMIT", + Digest: "sha1:78ac366c9e8a300d51808d581422ca61f7b5b721", + Kind: "GIT_HEAD_COMMIT", // the git commit a subject in the attestation References: []string{ "sha256:ad704d286bcad6e155e71c33d48247931231338396acbcd9769087530085b2a2", @@ -101,27 +101,27 @@ func (s *referrerTestSuite) TestExtractReferrers() { }, "sha256:385c4188b9c080499413f2e0fa0b3951ed107b5f0cb35c2f2b1f07a7be9a7512": &Referrer{ Digest: "sha256:385c4188b9c080499413f2e0fa0b3951ed107b5f0cb35c2f2b1f07a7be9a7512", - ArtifactType: "ARTIFACT", + Kind: "ARTIFACT", Downloadable: true, }, "sha256:c4a63494f9289dd9fd44f841efb4f5b52765c2de6332f2d86e5f6c0340b40a95": &Referrer{ Digest: "sha256:c4a63494f9289dd9fd44f841efb4f5b52765c2de6332f2d86e5f6c0340b40a95", - ArtifactType: "SARIF", + Kind: "SARIF", Downloadable: true, }, "sha256:16159bb881eb4ab7eb5d8afc5350b0feeed1e31c0a268e355e74f9ccbe885e0c": &Referrer{ Digest: "sha256:16159bb881eb4ab7eb5d8afc5350b0feeed1e31c0a268e355e74f9ccbe885e0c", - ArtifactType: "SBOM_CYCLONEDX_JSON", + Kind: "SBOM_CYCLONEDX_JSON", Downloadable: true, }, "sha256:b4bd86d5855f94bcac0a92d3100ae7b85d050bd2e5fb9037a200e5f5f0b073a2": &Referrer{ Digest: "sha256:b4bd86d5855f94bcac0a92d3100ae7b85d050bd2e5fb9037a200e5f5f0b073a2", - ArtifactType: "OPENVEX", + Kind: "OPENVEX", Downloadable: true, }, "sha256:ad704d286bcad6e155e71c33d48247931231338396acbcd9769087530085b2a2": &Referrer{ Digest: "sha256:ad704d286bcad6e155e71c33d48247931231338396acbcd9769087530085b2a2", - ArtifactType: "ATTESTATION", + Kind: "ATTESTATION", Downloadable: true, References: []string{ // container image diff --git a/app/controlplane/internal/data/ent/migrate/migrations/20231109101843.sql b/app/controlplane/internal/data/ent/migrate/migrations/20231109101843.sql new file mode 100644 index 000000000..94d3581f7 --- /dev/null +++ b/app/controlplane/internal/data/ent/migrate/migrations/20231109101843.sql @@ -0,0 +1,4 @@ +-- Modify "referrers" table +ALTER TABLE "referrers" DROP COLUMN "artifact_type", ADD COLUMN "kind" character varying NOT NULL; +-- Create index "referrer_digest_kind" to table: "referrers" +CREATE UNIQUE INDEX "referrer_digest_kind" ON "referrers" ("digest", "kind"); diff --git a/app/controlplane/internal/data/ent/migrate/migrations/atlas.sum b/app/controlplane/internal/data/ent/migrate/migrations/atlas.sum index 8d6799c47..2382893af 100644 --- a/app/controlplane/internal/data/ent/migrate/migrations/atlas.sum +++ b/app/controlplane/internal/data/ent/migrate/migrations/atlas.sum @@ -1,4 +1,4 @@ -h1:DvxRnSCi73Xw9PQ7HXzFspdGMEwChKjsLicwtMq07Xc= +h1:pQXA70URwiXjD6q1YKdew9Xg21EGCWOzRVoQgztCdjA= 20230706165452_init-schema.sql h1:VvqbNFEQnCvUVyj2iDYVQQxDM0+sSXqocpt/5H64k8M= 20230710111950-cas-backend.sql h1:A8iBuSzZIEbdsv9ipBtscZQuaBp3V5/VMw7eZH6GX+g= 20230712094107-cas-backends-workflow-runs.sql h1:a5rzxpVGyd56nLRSsKrmCFc9sebg65RWzLghKHh5xvI= @@ -12,3 +12,4 @@ h1:DvxRnSCi73Xw9PQ7HXzFspdGMEwChKjsLicwtMq07Xc= 20231031124431.sql h1:HxWLbK4otq1uvGWKqngAHcOL+8G+qHbFnhRfLZCboBQ= 20231107121730.sql h1:3Rd522oNuNVnDwbScdWKo7yxISYlN/JoDi6y6QF417c= 20231108214833.sql h1:qojrntcFArOidQQLDo2qns+zr3Y5BoJpfr2BvjCpRD0= +20231109101843.sql h1:lSRWGmd08/RoQSBWVUNb01f9tLDDS8YwFLcXq8WjDHs= diff --git a/app/controlplane/internal/data/ent/migrate/schema.go b/app/controlplane/internal/data/ent/migrate/schema.go index abe519ce6..e01dcbfde 100644 --- a/app/controlplane/internal/data/ent/migrate/schema.go +++ b/app/controlplane/internal/data/ent/migrate/schema.go @@ -215,7 +215,7 @@ var ( ReferrersColumns = []*schema.Column{ {Name: "id", Type: field.TypeUUID, Unique: true}, {Name: "digest", Type: field.TypeString}, - {Name: "artifact_type", Type: field.TypeString}, + {Name: "kind", Type: field.TypeString}, {Name: "downloadable", Type: field.TypeBool}, {Name: "created_at", Type: field.TypeTime, Default: "CURRENT_TIMESTAMP"}, } @@ -226,7 +226,7 @@ var ( PrimaryKey: []*schema.Column{ReferrersColumns[0]}, Indexes: []*schema.Index{ { - Name: "referrer_digest_artifact_type", + Name: "referrer_digest_kind", Unique: true, Columns: []*schema.Column{ReferrersColumns[1], ReferrersColumns[2]}, }, diff --git a/app/controlplane/internal/data/ent/mutation.go b/app/controlplane/internal/data/ent/mutation.go index 02d7a6f7d..e9e9a2312 100644 --- a/app/controlplane/internal/data/ent/mutation.go +++ b/app/controlplane/internal/data/ent/mutation.go @@ -5248,7 +5248,7 @@ type ReferrerMutation struct { typ string id *uuid.UUID digest *string - artifact_type *string + kind *string downloadable *bool created_at *time.Time clearedFields map[string]struct{} @@ -5406,40 +5406,40 @@ func (m *ReferrerMutation) ResetDigest() { m.digest = nil } -// SetArtifactType sets the "artifact_type" field. -func (m *ReferrerMutation) SetArtifactType(s string) { - m.artifact_type = &s +// SetKind sets the "kind" field. +func (m *ReferrerMutation) SetKind(s string) { + m.kind = &s } -// ArtifactType returns the value of the "artifact_type" field in the mutation. -func (m *ReferrerMutation) ArtifactType() (r string, exists bool) { - v := m.artifact_type +// Kind returns the value of the "kind" field in the mutation. +func (m *ReferrerMutation) Kind() (r string, exists bool) { + v := m.kind if v == nil { return } return *v, true } -// OldArtifactType returns the old "artifact_type" field's value of the Referrer entity. +// OldKind returns the old "kind" field's value of the Referrer entity. // If the Referrer object wasn't provided to the builder, the object is fetched from the database. // An error is returned if the mutation operation is not UpdateOne, or the database query fails. -func (m *ReferrerMutation) OldArtifactType(ctx context.Context) (v string, err error) { +func (m *ReferrerMutation) OldKind(ctx context.Context) (v string, err error) { if !m.op.Is(OpUpdateOne) { - return v, errors.New("OldArtifactType is only allowed on UpdateOne operations") + return v, errors.New("OldKind is only allowed on UpdateOne operations") } if m.id == nil || m.oldValue == nil { - return v, errors.New("OldArtifactType requires an ID field in the mutation") + return v, errors.New("OldKind requires an ID field in the mutation") } oldValue, err := m.oldValue(ctx) if err != nil { - return v, fmt.Errorf("querying old value for OldArtifactType: %w", err) + return v, fmt.Errorf("querying old value for OldKind: %w", err) } - return oldValue.ArtifactType, nil + return oldValue.Kind, nil } -// ResetArtifactType resets all changes to the "artifact_type" field. -func (m *ReferrerMutation) ResetArtifactType() { - m.artifact_type = nil +// ResetKind resets all changes to the "kind" field. +func (m *ReferrerMutation) ResetKind() { + m.kind = nil } // SetDownloadable sets the "downloadable" field. @@ -5714,8 +5714,8 @@ func (m *ReferrerMutation) Fields() []string { if m.digest != nil { fields = append(fields, referrer.FieldDigest) } - if m.artifact_type != nil { - fields = append(fields, referrer.FieldArtifactType) + if m.kind != nil { + fields = append(fields, referrer.FieldKind) } if m.downloadable != nil { fields = append(fields, referrer.FieldDownloadable) @@ -5733,8 +5733,8 @@ func (m *ReferrerMutation) Field(name string) (ent.Value, bool) { switch name { case referrer.FieldDigest: return m.Digest() - case referrer.FieldArtifactType: - return m.ArtifactType() + case referrer.FieldKind: + return m.Kind() case referrer.FieldDownloadable: return m.Downloadable() case referrer.FieldCreatedAt: @@ -5750,8 +5750,8 @@ func (m *ReferrerMutation) OldField(ctx context.Context, name string) (ent.Value switch name { case referrer.FieldDigest: return m.OldDigest(ctx) - case referrer.FieldArtifactType: - return m.OldArtifactType(ctx) + case referrer.FieldKind: + return m.OldKind(ctx) case referrer.FieldDownloadable: return m.OldDownloadable(ctx) case referrer.FieldCreatedAt: @@ -5772,12 +5772,12 @@ func (m *ReferrerMutation) SetField(name string, value ent.Value) error { } m.SetDigest(v) return nil - case referrer.FieldArtifactType: + case referrer.FieldKind: v, ok := value.(string) if !ok { return fmt.Errorf("unexpected type %T for field %s", value, name) } - m.SetArtifactType(v) + m.SetKind(v) return nil case referrer.FieldDownloadable: v, ok := value.(bool) @@ -5845,8 +5845,8 @@ func (m *ReferrerMutation) ResetField(name string) error { case referrer.FieldDigest: m.ResetDigest() return nil - case referrer.FieldArtifactType: - m.ResetArtifactType() + case referrer.FieldKind: + m.ResetKind() return nil case referrer.FieldDownloadable: m.ResetDownloadable() diff --git a/app/controlplane/internal/data/ent/referrer.go b/app/controlplane/internal/data/ent/referrer.go index 96d0cef08..f03efb8d6 100644 --- a/app/controlplane/internal/data/ent/referrer.go +++ b/app/controlplane/internal/data/ent/referrer.go @@ -20,8 +20,8 @@ type Referrer struct { ID uuid.UUID `json:"id,omitempty"` // Digest holds the value of the "digest" field. Digest string `json:"digest,omitempty"` - // ArtifactType holds the value of the "artifact_type" field. - ArtifactType string `json:"artifact_type,omitempty"` + // Kind holds the value of the "kind" field. + Kind string `json:"kind,omitempty"` // Downloadable holds the value of the "downloadable" field. Downloadable bool `json:"downloadable,omitempty"` // CreatedAt holds the value of the "created_at" field. @@ -79,7 +79,7 @@ func (*Referrer) scanValues(columns []string) ([]any, error) { switch columns[i] { case referrer.FieldDownloadable: values[i] = new(sql.NullBool) - case referrer.FieldDigest, referrer.FieldArtifactType: + case referrer.FieldDigest, referrer.FieldKind: values[i] = new(sql.NullString) case referrer.FieldCreatedAt: values[i] = new(sql.NullTime) @@ -112,11 +112,11 @@ func (r *Referrer) assignValues(columns []string, values []any) error { } else if value.Valid { r.Digest = value.String } - case referrer.FieldArtifactType: + case referrer.FieldKind: if value, ok := values[i].(*sql.NullString); !ok { - return fmt.Errorf("unexpected type %T for field artifact_type", values[i]) + return fmt.Errorf("unexpected type %T for field kind", values[i]) } else if value.Valid { - r.ArtifactType = value.String + r.Kind = value.String } case referrer.FieldDownloadable: if value, ok := values[i].(*sql.NullBool); !ok { @@ -184,8 +184,8 @@ func (r *Referrer) String() string { builder.WriteString("digest=") builder.WriteString(r.Digest) builder.WriteString(", ") - builder.WriteString("artifact_type=") - builder.WriteString(r.ArtifactType) + builder.WriteString("kind=") + builder.WriteString(r.Kind) builder.WriteString(", ") builder.WriteString("downloadable=") builder.WriteString(fmt.Sprintf("%v", r.Downloadable)) diff --git a/app/controlplane/internal/data/ent/referrer/referrer.go b/app/controlplane/internal/data/ent/referrer/referrer.go index 6ec600473..4ebbd9e2a 100644 --- a/app/controlplane/internal/data/ent/referrer/referrer.go +++ b/app/controlplane/internal/data/ent/referrer/referrer.go @@ -17,8 +17,8 @@ const ( FieldID = "id" // FieldDigest holds the string denoting the digest field in the database. FieldDigest = "digest" - // FieldArtifactType holds the string denoting the artifact_type field in the database. - FieldArtifactType = "artifact_type" + // FieldKind holds the string denoting the kind field in the database. + FieldKind = "kind" // FieldDownloadable holds the string denoting the downloadable field in the database. FieldDownloadable = "downloadable" // FieldCreatedAt holds the string denoting the created_at field in the database. @@ -46,7 +46,7 @@ const ( var Columns = []string{ FieldID, FieldDigest, - FieldArtifactType, + FieldKind, FieldDownloadable, FieldCreatedAt, } @@ -93,9 +93,9 @@ func ByDigest(opts ...sql.OrderTermOption) OrderOption { return sql.OrderByField(FieldDigest, opts...).ToFunc() } -// ByArtifactType orders the results by the artifact_type field. -func ByArtifactType(opts ...sql.OrderTermOption) OrderOption { - return sql.OrderByField(FieldArtifactType, opts...).ToFunc() +// ByKind orders the results by the kind field. +func ByKind(opts ...sql.OrderTermOption) OrderOption { + return sql.OrderByField(FieldKind, opts...).ToFunc() } // ByDownloadable orders the results by the downloadable field. diff --git a/app/controlplane/internal/data/ent/referrer/where.go b/app/controlplane/internal/data/ent/referrer/where.go index 509e93d99..1ef4ea9f2 100644 --- a/app/controlplane/internal/data/ent/referrer/where.go +++ b/app/controlplane/internal/data/ent/referrer/where.go @@ -61,9 +61,9 @@ func Digest(v string) predicate.Referrer { return predicate.Referrer(sql.FieldEQ(FieldDigest, v)) } -// ArtifactType applies equality check predicate on the "artifact_type" field. It's identical to ArtifactTypeEQ. -func ArtifactType(v string) predicate.Referrer { - return predicate.Referrer(sql.FieldEQ(FieldArtifactType, v)) +// Kind applies equality check predicate on the "kind" field. It's identical to KindEQ. +func Kind(v string) predicate.Referrer { + return predicate.Referrer(sql.FieldEQ(FieldKind, v)) } // Downloadable applies equality check predicate on the "downloadable" field. It's identical to DownloadableEQ. @@ -141,69 +141,69 @@ func DigestContainsFold(v string) predicate.Referrer { return predicate.Referrer(sql.FieldContainsFold(FieldDigest, v)) } -// ArtifactTypeEQ applies the EQ predicate on the "artifact_type" field. -func ArtifactTypeEQ(v string) predicate.Referrer { - return predicate.Referrer(sql.FieldEQ(FieldArtifactType, v)) +// KindEQ applies the EQ predicate on the "kind" field. +func KindEQ(v string) predicate.Referrer { + return predicate.Referrer(sql.FieldEQ(FieldKind, v)) } -// ArtifactTypeNEQ applies the NEQ predicate on the "artifact_type" field. -func ArtifactTypeNEQ(v string) predicate.Referrer { - return predicate.Referrer(sql.FieldNEQ(FieldArtifactType, v)) +// KindNEQ applies the NEQ predicate on the "kind" field. +func KindNEQ(v string) predicate.Referrer { + return predicate.Referrer(sql.FieldNEQ(FieldKind, v)) } -// ArtifactTypeIn applies the In predicate on the "artifact_type" field. -func ArtifactTypeIn(vs ...string) predicate.Referrer { - return predicate.Referrer(sql.FieldIn(FieldArtifactType, vs...)) +// KindIn applies the In predicate on the "kind" field. +func KindIn(vs ...string) predicate.Referrer { + return predicate.Referrer(sql.FieldIn(FieldKind, vs...)) } -// ArtifactTypeNotIn applies the NotIn predicate on the "artifact_type" field. -func ArtifactTypeNotIn(vs ...string) predicate.Referrer { - return predicate.Referrer(sql.FieldNotIn(FieldArtifactType, vs...)) +// KindNotIn applies the NotIn predicate on the "kind" field. +func KindNotIn(vs ...string) predicate.Referrer { + return predicate.Referrer(sql.FieldNotIn(FieldKind, vs...)) } -// ArtifactTypeGT applies the GT predicate on the "artifact_type" field. -func ArtifactTypeGT(v string) predicate.Referrer { - return predicate.Referrer(sql.FieldGT(FieldArtifactType, v)) +// KindGT applies the GT predicate on the "kind" field. +func KindGT(v string) predicate.Referrer { + return predicate.Referrer(sql.FieldGT(FieldKind, v)) } -// ArtifactTypeGTE applies the GTE predicate on the "artifact_type" field. -func ArtifactTypeGTE(v string) predicate.Referrer { - return predicate.Referrer(sql.FieldGTE(FieldArtifactType, v)) +// KindGTE applies the GTE predicate on the "kind" field. +func KindGTE(v string) predicate.Referrer { + return predicate.Referrer(sql.FieldGTE(FieldKind, v)) } -// ArtifactTypeLT applies the LT predicate on the "artifact_type" field. -func ArtifactTypeLT(v string) predicate.Referrer { - return predicate.Referrer(sql.FieldLT(FieldArtifactType, v)) +// KindLT applies the LT predicate on the "kind" field. +func KindLT(v string) predicate.Referrer { + return predicate.Referrer(sql.FieldLT(FieldKind, v)) } -// ArtifactTypeLTE applies the LTE predicate on the "artifact_type" field. -func ArtifactTypeLTE(v string) predicate.Referrer { - return predicate.Referrer(sql.FieldLTE(FieldArtifactType, v)) +// KindLTE applies the LTE predicate on the "kind" field. +func KindLTE(v string) predicate.Referrer { + return predicate.Referrer(sql.FieldLTE(FieldKind, v)) } -// ArtifactTypeContains applies the Contains predicate on the "artifact_type" field. -func ArtifactTypeContains(v string) predicate.Referrer { - return predicate.Referrer(sql.FieldContains(FieldArtifactType, v)) +// KindContains applies the Contains predicate on the "kind" field. +func KindContains(v string) predicate.Referrer { + return predicate.Referrer(sql.FieldContains(FieldKind, v)) } -// ArtifactTypeHasPrefix applies the HasPrefix predicate on the "artifact_type" field. -func ArtifactTypeHasPrefix(v string) predicate.Referrer { - return predicate.Referrer(sql.FieldHasPrefix(FieldArtifactType, v)) +// KindHasPrefix applies the HasPrefix predicate on the "kind" field. +func KindHasPrefix(v string) predicate.Referrer { + return predicate.Referrer(sql.FieldHasPrefix(FieldKind, v)) } -// ArtifactTypeHasSuffix applies the HasSuffix predicate on the "artifact_type" field. -func ArtifactTypeHasSuffix(v string) predicate.Referrer { - return predicate.Referrer(sql.FieldHasSuffix(FieldArtifactType, v)) +// KindHasSuffix applies the HasSuffix predicate on the "kind" field. +func KindHasSuffix(v string) predicate.Referrer { + return predicate.Referrer(sql.FieldHasSuffix(FieldKind, v)) } -// ArtifactTypeEqualFold applies the EqualFold predicate on the "artifact_type" field. -func ArtifactTypeEqualFold(v string) predicate.Referrer { - return predicate.Referrer(sql.FieldEqualFold(FieldArtifactType, v)) +// KindEqualFold applies the EqualFold predicate on the "kind" field. +func KindEqualFold(v string) predicate.Referrer { + return predicate.Referrer(sql.FieldEqualFold(FieldKind, v)) } -// ArtifactTypeContainsFold applies the ContainsFold predicate on the "artifact_type" field. -func ArtifactTypeContainsFold(v string) predicate.Referrer { - return predicate.Referrer(sql.FieldContainsFold(FieldArtifactType, v)) +// KindContainsFold applies the ContainsFold predicate on the "kind" field. +func KindContainsFold(v string) predicate.Referrer { + return predicate.Referrer(sql.FieldContainsFold(FieldKind, v)) } // DownloadableEQ applies the EQ predicate on the "downloadable" field. diff --git a/app/controlplane/internal/data/ent/referrer_create.go b/app/controlplane/internal/data/ent/referrer_create.go index a686ae2f1..374419166 100644 --- a/app/controlplane/internal/data/ent/referrer_create.go +++ b/app/controlplane/internal/data/ent/referrer_create.go @@ -28,9 +28,9 @@ func (rc *ReferrerCreate) SetDigest(s string) *ReferrerCreate { return rc } -// SetArtifactType sets the "artifact_type" field. -func (rc *ReferrerCreate) SetArtifactType(s string) *ReferrerCreate { - rc.mutation.SetArtifactType(s) +// SetKind sets the "kind" field. +func (rc *ReferrerCreate) SetKind(s string) *ReferrerCreate { + rc.mutation.SetKind(s) return rc } @@ -163,8 +163,8 @@ func (rc *ReferrerCreate) check() error { if _, ok := rc.mutation.Digest(); !ok { return &ValidationError{Name: "digest", err: errors.New(`ent: missing required field "Referrer.digest"`)} } - if _, ok := rc.mutation.ArtifactType(); !ok { - return &ValidationError{Name: "artifact_type", err: errors.New(`ent: missing required field "Referrer.artifact_type"`)} + if _, ok := rc.mutation.Kind(); !ok { + return &ValidationError{Name: "kind", err: errors.New(`ent: missing required field "Referrer.kind"`)} } if _, ok := rc.mutation.Downloadable(); !ok { return &ValidationError{Name: "downloadable", err: errors.New(`ent: missing required field "Referrer.downloadable"`)} @@ -211,9 +211,9 @@ func (rc *ReferrerCreate) createSpec() (*Referrer, *sqlgraph.CreateSpec) { _spec.SetField(referrer.FieldDigest, field.TypeString, value) _node.Digest = value } - if value, ok := rc.mutation.ArtifactType(); ok { - _spec.SetField(referrer.FieldArtifactType, field.TypeString, value) - _node.ArtifactType = value + if value, ok := rc.mutation.Kind(); ok { + _spec.SetField(referrer.FieldKind, field.TypeString, value) + _node.Kind = value } if value, ok := rc.mutation.Downloadable(); ok { _spec.SetField(referrer.FieldDownloadable, field.TypeBool, value) diff --git a/app/controlplane/internal/data/ent/schema-viz.html b/app/controlplane/internal/data/ent/schema-viz.html index fd14879fa..3f27706a2 100644 --- a/app/controlplane/internal/data/ent/schema-viz.html +++ b/app/controlplane/internal/data/ent/schema-viz.html @@ -70,7 +70,7 @@ } - const entGraph = JSON.parse("{\"nodes\":[{\"id\":\"CASBackend\",\"fields\":[{\"name\":\"location\",\"type\":\"string\"},{\"name\":\"provider\",\"type\":\"biz.CASBackendProvider\"},{\"name\":\"description\",\"type\":\"string\"},{\"name\":\"secret_name\",\"type\":\"string\"},{\"name\":\"created_at\",\"type\":\"time.Time\"},{\"name\":\"validation_status\",\"type\":\"biz.CASBackendValidationStatus\"},{\"name\":\"validated_at\",\"type\":\"time.Time\"},{\"name\":\"default\",\"type\":\"bool\"},{\"name\":\"deleted_at\",\"type\":\"time.Time\"},{\"name\":\"fallback\",\"type\":\"bool\"}]},{\"id\":\"CASMapping\",\"fields\":[{\"name\":\"digest\",\"type\":\"string\"},{\"name\":\"created_at\",\"type\":\"time.Time\"}]},{\"id\":\"Integration\",\"fields\":[{\"name\":\"kind\",\"type\":\"string\"},{\"name\":\"description\",\"type\":\"string\"},{\"name\":\"secret_name\",\"type\":\"string\"},{\"name\":\"created_at\",\"type\":\"time.Time\"},{\"name\":\"configuration\",\"type\":\"[]byte\"},{\"name\":\"deleted_at\",\"type\":\"time.Time\"}]},{\"id\":\"IntegrationAttachment\",\"fields\":[{\"name\":\"created_at\",\"type\":\"time.Time\"},{\"name\":\"configuration\",\"type\":\"[]byte\"},{\"name\":\"deleted_at\",\"type\":\"time.Time\"}]},{\"id\":\"Membership\",\"fields\":[{\"name\":\"current\",\"type\":\"bool\"},{\"name\":\"created_at\",\"type\":\"time.Time\"},{\"name\":\"updated_at\",\"type\":\"time.Time\"}]},{\"id\":\"OrgInvitation\",\"fields\":[{\"name\":\"receiver_email\",\"type\":\"string\"},{\"name\":\"status\",\"type\":\"biz.OrgInvitationStatus\"},{\"name\":\"created_at\",\"type\":\"time.Time\"},{\"name\":\"deleted_at\",\"type\":\"time.Time\"},{\"name\":\"organization_id\",\"type\":\"uuid.UUID\"},{\"name\":\"sender_id\",\"type\":\"uuid.UUID\"}]},{\"id\":\"Organization\",\"fields\":[{\"name\":\"name\",\"type\":\"string\"},{\"name\":\"created_at\",\"type\":\"time.Time\"}]},{\"id\":\"Referrer\",\"fields\":[{\"name\":\"digest\",\"type\":\"string\"},{\"name\":\"artifact_type\",\"type\":\"string\"},{\"name\":\"downloadable\",\"type\":\"bool\"},{\"name\":\"created_at\",\"type\":\"time.Time\"}]},{\"id\":\"RobotAccount\",\"fields\":[{\"name\":\"name\",\"type\":\"string\"},{\"name\":\"created_at\",\"type\":\"time.Time\"},{\"name\":\"revoked_at\",\"type\":\"time.Time\"}]},{\"id\":\"User\",\"fields\":[{\"name\":\"email\",\"type\":\"string\"},{\"name\":\"created_at\",\"type\":\"time.Time\"}]},{\"id\":\"Workflow\",\"fields\":[{\"name\":\"name\",\"type\":\"string\"},{\"name\":\"project\",\"type\":\"string\"},{\"name\":\"team\",\"type\":\"string\"},{\"name\":\"runs_count\",\"type\":\"int\"},{\"name\":\"created_at\",\"type\":\"time.Time\"},{\"name\":\"deleted_at\",\"type\":\"time.Time\"},{\"name\":\"public\",\"type\":\"bool\"}]},{\"id\":\"WorkflowContract\",\"fields\":[{\"name\":\"name\",\"type\":\"string\"},{\"name\":\"created_at\",\"type\":\"time.Time\"},{\"name\":\"deleted_at\",\"type\":\"time.Time\"}]},{\"id\":\"WorkflowContractVersion\",\"fields\":[{\"name\":\"body\",\"type\":\"[]byte\"},{\"name\":\"revision\",\"type\":\"int\"},{\"name\":\"created_at\",\"type\":\"time.Time\"}]},{\"id\":\"WorkflowRun\",\"fields\":[{\"name\":\"created_at\",\"type\":\"time.Time\"},{\"name\":\"finished_at\",\"type\":\"time.Time\"},{\"name\":\"state\",\"type\":\"biz.WorkflowRunStatus\"},{\"name\":\"reason\",\"type\":\"string\"},{\"name\":\"run_url\",\"type\":\"string\"},{\"name\":\"runner_type\",\"type\":\"string\"},{\"name\":\"attestation\",\"type\":\"*dsse.Envelope\"},{\"name\":\"attestation_digest\",\"type\":\"string\"}]}],\"edges\":[{\"from\":\"CASMapping\",\"to\":\"CASBackend\",\"label\":\"cas_backend\"},{\"from\":\"CASMapping\",\"to\":\"WorkflowRun\",\"label\":\"workflow_run\"},{\"from\":\"CASMapping\",\"to\":\"Organization\",\"label\":\"organization\"},{\"from\":\"IntegrationAttachment\",\"to\":\"Integration\",\"label\":\"integration\"},{\"from\":\"IntegrationAttachment\",\"to\":\"Workflow\",\"label\":\"workflow\"},{\"from\":\"OrgInvitation\",\"to\":\"Organization\",\"label\":\"organization\"},{\"from\":\"OrgInvitation\",\"to\":\"User\",\"label\":\"sender\"},{\"from\":\"Organization\",\"to\":\"Membership\",\"label\":\"memberships\"},{\"from\":\"Organization\",\"to\":\"WorkflowContract\",\"label\":\"workflow_contracts\"},{\"from\":\"Organization\",\"to\":\"Workflow\",\"label\":\"workflows\"},{\"from\":\"Organization\",\"to\":\"CASBackend\",\"label\":\"cas_backends\"},{\"from\":\"Organization\",\"to\":\"Integration\",\"label\":\"integrations\"},{\"from\":\"Referrer\",\"to\":\"Referrer\",\"label\":\"references\"},{\"from\":\"Referrer\",\"to\":\"Organization\",\"label\":\"organizations\"},{\"from\":\"RobotAccount\",\"to\":\"WorkflowRun\",\"label\":\"workflowruns\"},{\"from\":\"User\",\"to\":\"Membership\",\"label\":\"memberships\"},{\"from\":\"Workflow\",\"to\":\"RobotAccount\",\"label\":\"robotaccounts\"},{\"from\":\"Workflow\",\"to\":\"WorkflowRun\",\"label\":\"workflowruns\"},{\"from\":\"Workflow\",\"to\":\"WorkflowContract\",\"label\":\"contract\"},{\"from\":\"WorkflowContract\",\"to\":\"WorkflowContractVersion\",\"label\":\"versions\"},{\"from\":\"WorkflowRun\",\"to\":\"WorkflowContractVersion\",\"label\":\"contract_version\"},{\"from\":\"WorkflowRun\",\"to\":\"CASBackend\",\"label\":\"cas_backends\"}]}"); + const entGraph = JSON.parse("{\"nodes\":[{\"id\":\"CASBackend\",\"fields\":[{\"name\":\"location\",\"type\":\"string\"},{\"name\":\"provider\",\"type\":\"biz.CASBackendProvider\"},{\"name\":\"description\",\"type\":\"string\"},{\"name\":\"secret_name\",\"type\":\"string\"},{\"name\":\"created_at\",\"type\":\"time.Time\"},{\"name\":\"validation_status\",\"type\":\"biz.CASBackendValidationStatus\"},{\"name\":\"validated_at\",\"type\":\"time.Time\"},{\"name\":\"default\",\"type\":\"bool\"},{\"name\":\"deleted_at\",\"type\":\"time.Time\"},{\"name\":\"fallback\",\"type\":\"bool\"}]},{\"id\":\"CASMapping\",\"fields\":[{\"name\":\"digest\",\"type\":\"string\"},{\"name\":\"created_at\",\"type\":\"time.Time\"}]},{\"id\":\"Integration\",\"fields\":[{\"name\":\"kind\",\"type\":\"string\"},{\"name\":\"description\",\"type\":\"string\"},{\"name\":\"secret_name\",\"type\":\"string\"},{\"name\":\"created_at\",\"type\":\"time.Time\"},{\"name\":\"configuration\",\"type\":\"[]byte\"},{\"name\":\"deleted_at\",\"type\":\"time.Time\"}]},{\"id\":\"IntegrationAttachment\",\"fields\":[{\"name\":\"created_at\",\"type\":\"time.Time\"},{\"name\":\"configuration\",\"type\":\"[]byte\"},{\"name\":\"deleted_at\",\"type\":\"time.Time\"}]},{\"id\":\"Membership\",\"fields\":[{\"name\":\"current\",\"type\":\"bool\"},{\"name\":\"created_at\",\"type\":\"time.Time\"},{\"name\":\"updated_at\",\"type\":\"time.Time\"}]},{\"id\":\"OrgInvitation\",\"fields\":[{\"name\":\"receiver_email\",\"type\":\"string\"},{\"name\":\"status\",\"type\":\"biz.OrgInvitationStatus\"},{\"name\":\"created_at\",\"type\":\"time.Time\"},{\"name\":\"deleted_at\",\"type\":\"time.Time\"},{\"name\":\"organization_id\",\"type\":\"uuid.UUID\"},{\"name\":\"sender_id\",\"type\":\"uuid.UUID\"}]},{\"id\":\"Organization\",\"fields\":[{\"name\":\"name\",\"type\":\"string\"},{\"name\":\"created_at\",\"type\":\"time.Time\"}]},{\"id\":\"Referrer\",\"fields\":[{\"name\":\"digest\",\"type\":\"string\"},{\"name\":\"kind\",\"type\":\"string\"},{\"name\":\"downloadable\",\"type\":\"bool\"},{\"name\":\"created_at\",\"type\":\"time.Time\"}]},{\"id\":\"RobotAccount\",\"fields\":[{\"name\":\"name\",\"type\":\"string\"},{\"name\":\"created_at\",\"type\":\"time.Time\"},{\"name\":\"revoked_at\",\"type\":\"time.Time\"}]},{\"id\":\"User\",\"fields\":[{\"name\":\"email\",\"type\":\"string\"},{\"name\":\"created_at\",\"type\":\"time.Time\"}]},{\"id\":\"Workflow\",\"fields\":[{\"name\":\"name\",\"type\":\"string\"},{\"name\":\"project\",\"type\":\"string\"},{\"name\":\"team\",\"type\":\"string\"},{\"name\":\"runs_count\",\"type\":\"int\"},{\"name\":\"created_at\",\"type\":\"time.Time\"},{\"name\":\"deleted_at\",\"type\":\"time.Time\"},{\"name\":\"public\",\"type\":\"bool\"}]},{\"id\":\"WorkflowContract\",\"fields\":[{\"name\":\"name\",\"type\":\"string\"},{\"name\":\"created_at\",\"type\":\"time.Time\"},{\"name\":\"deleted_at\",\"type\":\"time.Time\"}]},{\"id\":\"WorkflowContractVersion\",\"fields\":[{\"name\":\"body\",\"type\":\"[]byte\"},{\"name\":\"revision\",\"type\":\"int\"},{\"name\":\"created_at\",\"type\":\"time.Time\"}]},{\"id\":\"WorkflowRun\",\"fields\":[{\"name\":\"created_at\",\"type\":\"time.Time\"},{\"name\":\"finished_at\",\"type\":\"time.Time\"},{\"name\":\"state\",\"type\":\"biz.WorkflowRunStatus\"},{\"name\":\"reason\",\"type\":\"string\"},{\"name\":\"run_url\",\"type\":\"string\"},{\"name\":\"runner_type\",\"type\":\"string\"},{\"name\":\"attestation\",\"type\":\"*dsse.Envelope\"},{\"name\":\"attestation_digest\",\"type\":\"string\"}]}],\"edges\":[{\"from\":\"CASMapping\",\"to\":\"CASBackend\",\"label\":\"cas_backend\"},{\"from\":\"CASMapping\",\"to\":\"WorkflowRun\",\"label\":\"workflow_run\"},{\"from\":\"CASMapping\",\"to\":\"Organization\",\"label\":\"organization\"},{\"from\":\"IntegrationAttachment\",\"to\":\"Integration\",\"label\":\"integration\"},{\"from\":\"IntegrationAttachment\",\"to\":\"Workflow\",\"label\":\"workflow\"},{\"from\":\"OrgInvitation\",\"to\":\"Organization\",\"label\":\"organization\"},{\"from\":\"OrgInvitation\",\"to\":\"User\",\"label\":\"sender\"},{\"from\":\"Organization\",\"to\":\"Membership\",\"label\":\"memberships\"},{\"from\":\"Organization\",\"to\":\"WorkflowContract\",\"label\":\"workflow_contracts\"},{\"from\":\"Organization\",\"to\":\"Workflow\",\"label\":\"workflows\"},{\"from\":\"Organization\",\"to\":\"CASBackend\",\"label\":\"cas_backends\"},{\"from\":\"Organization\",\"to\":\"Integration\",\"label\":\"integrations\"},{\"from\":\"Referrer\",\"to\":\"Referrer\",\"label\":\"references\"},{\"from\":\"Referrer\",\"to\":\"Organization\",\"label\":\"organizations\"},{\"from\":\"RobotAccount\",\"to\":\"WorkflowRun\",\"label\":\"workflowruns\"},{\"from\":\"User\",\"to\":\"Membership\",\"label\":\"memberships\"},{\"from\":\"Workflow\",\"to\":\"RobotAccount\",\"label\":\"robotaccounts\"},{\"from\":\"Workflow\",\"to\":\"WorkflowRun\",\"label\":\"workflowruns\"},{\"from\":\"Workflow\",\"to\":\"WorkflowContract\",\"label\":\"contract\"},{\"from\":\"WorkflowContract\",\"to\":\"WorkflowContractVersion\",\"label\":\"versions\"},{\"from\":\"WorkflowRun\",\"to\":\"WorkflowContractVersion\",\"label\":\"contract_version\"},{\"from\":\"WorkflowRun\",\"to\":\"CASBackend\",\"label\":\"cas_backends\"}]}"); const nodes = new vis.DataSet((entGraph.nodes || []).map(n => ({ id: n.id, diff --git a/app/controlplane/internal/data/ent/schema/referrer.go b/app/controlplane/internal/data/ent/schema/referrer.go index 218abb0ed..b5e9c142f 100644 --- a/app/controlplane/internal/data/ent/schema/referrer.go +++ b/app/controlplane/internal/data/ent/schema/referrer.go @@ -34,8 +34,8 @@ func (Referrer) Fields() []ent.Field { return []ent.Field{ field.UUID("id", uuid.UUID{}).Default(uuid.New).Unique(), field.String("digest").Immutable(), - // referrer type i.e CONTAINER - field.String("artifact_type").Immutable(), + // referrer kind i.e CONTAINER + field.String("kind").Immutable(), // wether it can be downloaded from CAS or not field.Bool("downloadable").Immutable(), field.Time("created_at"). @@ -56,7 +56,8 @@ func (Referrer) Edges() []ent.Edge { func (Referrer) Indexes() []ent.Index { return []ent.Index{ - // For now we only guarantee that the digest is unique in the scope of the artifact type - index.Fields("digest", "artifact_type").Unique(), + // Two referrers of different kinds can have the same digest + // For now we only guarantee that the digest is unique in the scope of the referrer kind + index.Fields("digest", "kind").Unique(), } } diff --git a/app/controlplane/internal/data/referrer.go b/app/controlplane/internal/data/referrer.go index 4d31ff5d1..2df20aa1f 100644 --- a/app/controlplane/internal/data/referrer.go +++ b/app/controlplane/internal/data/referrer.go @@ -51,14 +51,14 @@ func (r *ReferrerRepo) Save(ctx context.Context, input biz.ReferrerMap, orgID uu // 1 - Find or create each referrer for digest, r := range input { // Check if it exists already, if not create it - storedRef, err := tx.Referrer.Query().Where(referrer.Digest(digest), referrer.ArtifactType(r.ArtifactType)).Only(ctx) + storedRef, err := tx.Referrer.Query().Where(referrer.Digest(digest), referrer.Kind(r.Kind)).Only(ctx) if err != nil { if !ent.IsNotFound(err) { return fmt.Errorf("failed to query referrer: %w", err) } storedRef, err = tx.Referrer.Create(). - SetDigest(digest).SetArtifactType(r.ArtifactType).SetDownloadable(r.Downloadable).AddOrganizationIDs(orgID).Save(ctx) + SetDigest(digest).SetKind(r.Kind).SetDownloadable(r.Downloadable).AddOrganizationIDs(orgID).Save(ctx) if err != nil { return fmt.Errorf("failed to create referrer: %w", err) } @@ -135,7 +135,7 @@ func (r *ReferrerRepo) doGet(ctx context.Context, digest string, level int) (*bi ID: ref.ID, CreatedAt: toTimePtr(ref.CreatedAt), Digest: ref.Digest, - ArtifactType: ref.ArtifactType, + Kind: ref.Kind, Downloadable: ref.Downloadable, }