github.com/kubeshop/testkube@v1.17.23/pkg/cloud/data/result/result.go (about) 1 package result 2 3 import ( 4 "context" 5 "encoding/json" 6 "time" 7 8 "go.mongodb.org/mongo-driver/mongo" 9 "google.golang.org/grpc" 10 11 "github.com/kubeshop/testkube/pkg/cloud/data/executor" 12 13 "github.com/pkg/errors" 14 15 "github.com/kubeshop/testkube/pkg/repository/result" 16 17 "github.com/kubeshop/testkube/pkg/cloud" 18 19 "github.com/kubeshop/testkube/pkg/api/v1/testkube" 20 ) 21 22 var _ result.Repository = (*CloudRepository)(nil) 23 24 type CloudRepository struct { 25 executor executor.Executor 26 } 27 28 func NewCloudResultRepository(cloudClient cloud.TestKubeCloudAPIClient, grpcConn *grpc.ClientConn, apiKey string) *CloudRepository { 29 return &CloudRepository{executor: executor.NewCloudGRPCExecutor(cloudClient, grpcConn, apiKey)} 30 } 31 32 func (r *CloudRepository) GetNextExecutionNumber(ctx context.Context, testName string) (int32, error) { 33 req := NextExecutionNumberRequest{TestName: testName} 34 response, err := r.executor.Execute(ctx, CmdResultGetNextExecutionNumber, req) 35 if err != nil { 36 return 0, err 37 } 38 var commandResponse NextExecutionNumberResponse 39 if err := json.Unmarshal(response, &commandResponse); err != nil { 40 return 0, err 41 } 42 return commandResponse.TestNumber, nil 43 } 44 45 func (r *CloudRepository) GetExecution(ctx context.Context, id string) (testkube.Execution, error) { 46 req := GetRequest{ID: id} 47 response, err := r.executor.Execute(ctx, CmdResultGet, req) 48 if err != nil { 49 return testkube.Execution{}, err 50 } 51 var commandResponse GetResponse 52 if err := json.Unmarshal(response, &commandResponse); err != nil { 53 return testkube.Execution{}, err 54 } 55 return commandResponse.Execution, nil 56 } 57 58 func (r *CloudRepository) Get(ctx context.Context, id string) (testkube.Execution, error) { 59 req := GetRequest{ID: id} 60 response, err := r.executor.Execute(ctx, CmdResultGet, req) 61 if err != nil { 62 return testkube.Execution{}, err 63 } 64 var commandResponse GetResponse 65 if err := json.Unmarshal(response, &commandResponse); err != nil { 66 return testkube.Execution{}, err 67 } 68 return commandResponse.Execution, nil 69 } 70 71 func (r *CloudRepository) GetByNameAndTest(ctx context.Context, name, testName string) (testkube.Execution, error) { 72 req := GetByNameAndTestRequest{Name: name, TestName: testName} 73 response, err := r.executor.Execute(ctx, CmdResultGetByNameAndTest, req) 74 if err != nil { 75 return testkube.Execution{}, err 76 } 77 var commandResponse GetByNameAndTestResponse 78 if err := json.Unmarshal(response, &commandResponse); err != nil { 79 return testkube.Execution{}, err 80 } 81 return commandResponse.Execution, nil 82 } 83 84 func (r *CloudRepository) getLatestByTest(ctx context.Context, testName, sortField string) (testkube.Execution, error) { 85 req := GetLatestByTestRequest{TestName: testName, SortField: sortField} 86 response, err := r.executor.Execute(ctx, CmdResultGetLatestByTest, req) 87 if err != nil { 88 return testkube.Execution{}, err 89 } 90 var commandResponse GetLatestByTestResponse 91 if err := json.Unmarshal(response, &commandResponse); err != nil { 92 return testkube.Execution{}, err 93 } 94 return commandResponse.Execution, nil 95 } 96 97 // TODO: When it will be implemented, replace with a new Cloud command, to avoid 2 calls with 2 sort fields 98 func (r *CloudRepository) GetLatestByTest(ctx context.Context, testName string) (*testkube.Execution, error) { 99 startExecution, startErr := r.getLatestByTest(ctx, testName, "starttime") 100 if startErr != nil && startErr != mongo.ErrNoDocuments { 101 return nil, startErr 102 } 103 endExecution, endErr := r.getLatestByTest(ctx, testName, "endtime") 104 if endErr != nil && endErr != mongo.ErrNoDocuments { 105 return nil, endErr 106 } 107 108 if startErr == nil && endErr == nil { 109 if startExecution.StartTime.After(endExecution.EndTime) { 110 return &startExecution, nil 111 } else { 112 return &endExecution, nil 113 } 114 } else if startErr == nil { 115 return &startExecution, nil 116 } else if endErr == nil { 117 return &endExecution, nil 118 } 119 return nil, startErr 120 } 121 122 // TODO: When it will be implemented, replace with a new Cloud command, to avoid 2 calls with 2 sort fields 123 func (r *CloudRepository) getLatestByTests(ctx context.Context, testNames []string, sortField string) ([]testkube.Execution, error) { 124 req := GetLatestByTestsRequest{TestNames: testNames, SortField: sortField} 125 response, err := r.executor.Execute(ctx, CmdResultGetLatestByTests, req) 126 if err != nil { 127 return nil, err 128 } 129 var commandResponse GetLatestByTestsResponse 130 if err := json.Unmarshal(response, &commandResponse); err != nil { 131 return nil, err 132 } 133 return commandResponse.Executions, nil 134 } 135 136 // TODO: When it will be implemented, replace with a new Cloud command, to avoid 2 calls with 2 sort fields 137 func (r *CloudRepository) GetLatestByTests(ctx context.Context, testNames []string) ([]testkube.Execution, error) { 138 startExecutions, err := r.getLatestByTests(ctx, testNames, "starttime") 139 if err != nil { 140 return nil, err 141 } 142 endExecutions, err := r.getLatestByTests(ctx, testNames, "endtime") 143 if err != nil { 144 return nil, err 145 } 146 executionsCount := len(startExecutions) 147 if len(endExecutions) > executionsCount { 148 executionsCount = len(endExecutions) 149 } 150 executionsMap := make(map[string]*testkube.Execution, executionsCount) 151 for i := range startExecutions { 152 executionsMap[startExecutions[i].TestName] = &startExecutions[i] 153 } 154 for i := range endExecutions { 155 startExecution, ok := executionsMap[endExecutions[i].TestName] 156 if ok { 157 if endExecutions[i].EndTime.After(startExecution.StartTime) { 158 executionsMap[endExecutions[i].TestName] = &endExecutions[i] 159 } 160 } else { 161 executionsMap[endExecutions[i].TestName] = &endExecutions[i] 162 } 163 } 164 executions := make([]testkube.Execution, 0, executionsCount) 165 for _, value := range executionsMap { 166 executions = append(executions, *value) 167 } 168 return executions, nil 169 } 170 171 func (r *CloudRepository) GetExecutions(ctx context.Context, filter result.Filter) ([]testkube.Execution, error) { 172 filterImpl, ok := filter.(*result.FilterImpl) 173 if !ok { 174 return nil, errors.New("invalid filter") 175 } 176 req := GetExecutionsRequest{Filter: filterImpl} 177 response, err := r.executor.Execute(ctx, CmdResultGetExecutions, req) 178 if err != nil { 179 return nil, err 180 } 181 var commandResponse GetLatestByTestsResponse 182 if err := json.Unmarshal(response, &commandResponse); err != nil { 183 return nil, err 184 } 185 return commandResponse.Executions, nil 186 } 187 188 func (r *CloudRepository) GetExecutionTotals(ctx context.Context, paging bool, filters ...result.Filter) (testkube.ExecutionsTotals, error) { 189 var filterImpls []*result.FilterImpl 190 for _, f := range filters { 191 filterImpl, ok := f.(*result.FilterImpl) 192 if !ok { 193 return testkube.ExecutionsTotals{}, errors.New("invalid filter") 194 } 195 filterImpls = append(filterImpls, filterImpl) 196 } 197 req := GetExecutionTotalsRequest{Paging: paging, Filter: filterImpls} 198 response, err := r.executor.Execute(ctx, CmdResultGetExecutionTotals, req) 199 if err != nil { 200 return testkube.ExecutionsTotals{}, err 201 } 202 var commandResponse GetExecutionTotalsResponse 203 if err := json.Unmarshal(response, &commandResponse); err != nil { 204 return testkube.ExecutionsTotals{}, err 205 } 206 return commandResponse.Result, nil 207 } 208 209 func (r *CloudRepository) Insert(ctx context.Context, result testkube.Execution) error { 210 req := InsertRequest{Result: result} 211 _, err := r.executor.Execute(ctx, CmdResultInsert, req) 212 if err != nil { 213 return err 214 } 215 return nil 216 } 217 218 func (r *CloudRepository) Update(ctx context.Context, result testkube.Execution) error { 219 req := UpdateRequest{Result: result} 220 _, err := r.executor.Execute(ctx, CmdResultUpdate, req) 221 if err != nil { 222 return err 223 } 224 return nil 225 } 226 227 func (r *CloudRepository) UpdateResult(ctx context.Context, id string, execution testkube.Execution) error { 228 req := UpdateResultInExecutionRequest{ID: id, Execution: execution} 229 _, err := r.executor.Execute(ctx, CmdResultUpdateResult, req) 230 if err != nil { 231 return err 232 } 233 return nil 234 } 235 236 func (r *CloudRepository) StartExecution(ctx context.Context, id string, startTime time.Time) error { 237 req := StartExecutionRequest{ID: id, StartTime: startTime} 238 _, err := r.executor.Execute(ctx, CmdResultStartExecution, req) 239 if err != nil { 240 return err 241 } 242 return nil 243 } 244 245 func (r *CloudRepository) EndExecution(ctx context.Context, execution testkube.Execution) error { 246 req := EndExecutionRequest{Execution: execution} 247 _, err := r.executor.Execute(ctx, CmdResultEndExecution, req) 248 if err != nil { 249 return err 250 } 251 return nil 252 } 253 254 func (r *CloudRepository) GetLabels(ctx context.Context) (map[string][]string, error) { 255 response, err := r.executor.Execute(ctx, CmdResultGetLabels, nil) 256 if err != nil { 257 return nil, err 258 } 259 var commandResponse GetLabelsResponse 260 if err := json.Unmarshal(response, &commandResponse); err != nil { 261 return nil, err 262 } 263 return nil, nil 264 } 265 266 func (r *CloudRepository) DeleteByTest(ctx context.Context, testName string) error { 267 req := DeleteByTestRequest{TestName: testName} 268 _, err := r.executor.Execute(ctx, CmdResultDeleteByTest, req) 269 if err != nil { 270 return err 271 } 272 return nil 273 } 274 275 func (r *CloudRepository) DeleteByTestSuite(ctx context.Context, testSuiteName string) error { 276 req := DeleteByTestSuiteRequest{TestSuiteName: testSuiteName} 277 _, err := r.executor.Execute(ctx, CmdResultDeleteByTestSuite, req) 278 if err != nil { 279 return err 280 } 281 return nil 282 } 283 284 func (r *CloudRepository) DeleteAll(ctx context.Context) error { 285 req := DeleteAllRequest{} 286 _, err := r.executor.Execute(ctx, CmdResultDeleteAll, req) 287 if err != nil { 288 return err 289 } 290 return nil 291 } 292 293 func (r *CloudRepository) DeleteByTests(ctx context.Context, testNames []string) error { 294 req := DeleteByTestsRequest{TestNames: testNames} 295 _, err := r.executor.Execute(ctx, CmdResultDeleteByTests, req) 296 if err != nil { 297 return err 298 } 299 return nil 300 } 301 302 func (r *CloudRepository) DeleteByTestSuites(ctx context.Context, testSuiteNames []string) error { 303 req := DeleteByTestSuitesRequest{TestSuiteNames: testSuiteNames} 304 _, err := r.executor.Execute(ctx, CmdResultDeleteByTestSuites, req) 305 if err != nil { 306 return err 307 } 308 return nil 309 } 310 311 func (r *CloudRepository) DeleteForAllTestSuites(ctx context.Context) error { 312 req := DeleteForAllTestSuitesResponse{} 313 _, err := r.executor.Execute(ctx, CmdResultDeleteForAllTestSuites, req) 314 if err != nil { 315 return err 316 } 317 return nil 318 } 319 320 func (r *CloudRepository) GetTestMetrics(ctx context.Context, name string, limit, last int) (testkube.ExecutionsMetrics, error) { 321 req := GetTestMetricsRequest{Name: name, Limit: limit, Last: last} 322 response, err := r.executor.Execute(ctx, CmdResultGetTestMetrics, req) 323 if err != nil { 324 return testkube.ExecutionsMetrics{}, err 325 } 326 var commandResponse GetTestMetricsResponse 327 if err := json.Unmarshal(response, &commandResponse); err != nil { 328 return testkube.ExecutionsMetrics{}, err 329 } 330 return commandResponse.Metrics, nil 331 } 332 333 func (r *CloudRepository) Count(ctx context.Context, filter result.Filter) (int64, error) { 334 return 0, nil 335 }