1 package services
2
3 import (
4 "context"
5 "database/sql"
6 "errors"
7 "fmt"
8 "slices"
9 "strconv"
10 "strings"
11
12 sqlerr "edge-infra.dev/pkg/edge/api/apierror/sql"
13 "edge-infra.dev/pkg/edge/api/graph/model"
14 sqlquery "edge-infra.dev/pkg/edge/api/sql"
15 )
16
17 var (
18 defaultBWC = 1
19 )
20
21
22 type CompatibilityService interface {
23 GetArtifactVersionCompatibility(ctx context.Context, artifact model.ArtifactVersion, compatibleArtifactName *string) (*model.ArtifactCompatibility, error)
24 IsCompatible(ctx context.Context, primaryArtifact, secondaryArtifact model.ArtifactVersion) (bool, error)
25 AddArtifactCompatibility(ctx context.Context, artifactCompatibility model.ArtifactCompatibilityPayload) (added bool, err error)
26 DeleteArtifactCompatibility(ctx context.Context, artifactCompatibility model.ArtifactCompatibilityPayload) (deleted bool, err error)
27 }
28
29 type compatibilityService struct {
30 SQLDB *sql.DB
31 }
32
33 func NewCompatibilityService(sqlDB *sql.DB) *compatibilityService {
34 return &compatibilityService{
35 SQLDB: sqlDB,
36 }
37 }
38
39
40 func (s *compatibilityService) GetArtifactVersionCompatibility(ctx context.Context, artifact model.ArtifactVersion, compatibleArtifactName *string) (*model.ArtifactCompatibility, error) {
41 var rows *sql.Rows
42 var err error
43 artifactCompatibility := model.ArtifactCompatibility{Artifact: &artifact}
44 if compatibleArtifactName != nil {
45 rows, err = s.SQLDB.QueryContext(ctx, sqlquery.GetArtifactVersionsAndCompatibilityForArtifactName, artifact.Name, artifact.Version, compatibleArtifactName)
46 if err != nil {
47 return nil, err
48 }
49 } else {
50 rows, err = s.SQLDB.QueryContext(ctx, sqlquery.GetArtifactVersionsAndCompatibility, artifact.Name, artifact.Version)
51 if err != nil {
52 return nil, err
53 }
54 }
55 for rows.Next() {
56 var aName, aVersion, compatibleArtifactName, compatibleArtifactVersion string
57 var artifactNthIndex int
58 if err := rows.Scan(&aName, &aVersion, &artifactNthIndex, &compatibleArtifactName, &compatibleArtifactVersion); err != nil {
59 return nil, err
60 }
61 artifactCompatibility.CompatibleArtifacts = append(artifactCompatibility.CompatibleArtifacts, &model.ArtifactVersion{Name: compatibleArtifactName, Version: compatibleArtifactVersion})
62 if artifactCompatibility.NthIndex == 0 {
63 artifactCompatibility.NthIndex = artifactNthIndex
64 }
65 }
66 if err := rows.Err(); err != nil {
67 return nil, sqlerr.Wrap(err)
68 }
69 return &artifactCompatibility, nil
70 }
71
72
73 func (s *compatibilityService) IsCompatible(ctx context.Context, primaryArtifact, secondaryArtifact model.ArtifactVersion) (bool, error) {
74 compatibleArtifacts, err := s.GetArtifactVersionCompatibility(ctx, primaryArtifact, nil)
75 if err != nil {
76 return false, err
77 }
78 if compatibleArtifacts == nil {
79 return false, nil
80 }
81 if primaryArtifact.Name == secondaryArtifact.Name {
82 primaryArtifactParts := strings.Split(primaryArtifact.Version, ".")
83 primaryArtifactMinorVersion, err := strconv.Atoi(primaryArtifactParts[1])
84 if err != nil {
85 return false, err
86 }
87 secondaryArtifactParts := strings.Split(secondaryArtifact.Version, ".")
88 secondaryArtifactMinorVersion, err := strconv.Atoi(secondaryArtifactParts[1])
89 if err != nil {
90 return false, err
91 }
92 return primaryArtifactMinorVersion-secondaryArtifactMinorVersion <= compatibleArtifacts.NthIndex, nil
93 }
94 compatible := slices.ContainsFunc(compatibleArtifacts.CompatibleArtifacts, func(artifact *model.ArtifactVersion) bool {
95 return artifact.Name == secondaryArtifact.Name && artifact.Version == secondaryArtifact.Version
96 })
97 return compatible, nil
98 }
99
100
101 func (s *compatibilityService) AddArtifactCompatibility(ctx context.Context, artifactCompatibility model.ArtifactCompatibilityPayload) (added bool, err error) {
102
103 exists, err := s.validateArtifactExists(ctx, artifactCompatibility.Artifact.Name, artifactCompatibility.Artifact.Version)
104 if err != nil {
105 return false, err
106 }
107 if !exists {
108 return false, fmt.Errorf("artifact name or version does not exist")
109 }
110
111
112 if artifactCompatibility.NthIndex == nil {
113 artifactCompatibility.NthIndex = &defaultBWC
114 }
115 tx, err := s.SQLDB.BeginTx(ctx, nil)
116 if err != nil {
117 return false, err
118 }
119
120 defer func() {
121 if err != nil {
122 err = errors.Join(err, tx.Rollback())
123 }
124 }()
125
126 for i := range artifactCompatibility.CompatibleArtifacts {
127 if _, err := tx.ExecContext(ctx, sqlquery.AddArtifactCompatibility, artifactCompatibility.Artifact.Name, artifactCompatibility.Artifact.Version, artifactCompatibility.NthIndex, artifactCompatibility.CompatibleArtifacts[i].Name, artifactCompatibility.CompatibleArtifacts[i].Version); err != nil {
128 return false, err
129 }
130 }
131 if err := tx.Commit(); err != nil {
132 return false, fmt.Errorf("failed to commit transaction. err: %v", err)
133 }
134 return true, nil
135 }
136
137
138 func (s *compatibilityService) DeleteArtifactCompatibility(ctx context.Context, artifactCompatibility model.ArtifactCompatibilityPayload) (deleted bool, err error) {
139
140 exists, err := s.validateArtifactExists(ctx, artifactCompatibility.Artifact.Name, artifactCompatibility.Artifact.Version)
141 if err != nil {
142 return false, err
143 }
144 if !exists {
145 return false, fmt.Errorf("artifact name or version does not exist")
146 }
147
148 tx, err := s.SQLDB.BeginTx(ctx, nil)
149 if err != nil {
150 return false, err
151 }
152
153 defer func() {
154 if err != nil {
155 err = errors.Join(err, tx.Rollback())
156 }
157 }()
158
159 for i := range artifactCompatibility.CompatibleArtifacts {
160 if _, err := tx.ExecContext(ctx, sqlquery.DeleteArtifactCompatibility, artifactCompatibility.Artifact.Name, artifactCompatibility.Artifact.Version, artifactCompatibility.CompatibleArtifacts[i].Name, artifactCompatibility.CompatibleArtifacts[i].Version); err != nil {
161 return false, err
162 }
163 }
164 if err := tx.Commit(); err != nil {
165 return false, fmt.Errorf("failed to commit transaction. err: %v", err)
166 }
167 return true, nil
168 }
169
170 func (s *compatibilityService) validateArtifactExists(ctx context.Context, artifactName, artifactVersion string) (bool, error) {
171
172 row := s.SQLDB.QueryRowContext(ctx, sqlquery.CheckArtifactVersionExists, artifactName, artifactVersion)
173 var exists bool
174 err := row.Scan(&exists)
175 if err != nil {
176 return false, err
177 }
178 if !exists {
179 return false, nil
180 }
181 return true, nil
182 }
183
View as plain text