blob: 819080538cbd7a89fa9af122f3a7aacfbbd1e8d2 [file] [log] [blame]
// Copyright 2022 The ChromiumOS Authors.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
package rdb_lib
import (
"encoding/json"
"fmt"
"strings"
rdb_pb "go.chromium.org/luci/resultdb/proto/v1"
"google.golang.org/protobuf/encoding/protojson"
)
const (
InvocationIdPrefix = "invocations/"
)
type Invocation struct {
invProto rdb_pb.Invocation
testResults []*rdb_pb.TestResult
testExonerations []*rdb_pb.TestExoneration
}
// Deserialize deserializes data to Invocation data
func Deserialize(data string) (map[string]*Invocation, error) {
invMap := make(map[string]*Invocation)
for lineNum, line := range strings.Split(data, "\n") {
if line == "" {
continue
}
var entry map[string]interface{}
err := json.Unmarshal([]byte(line), &entry)
if err != nil {
return invMap, fmt.Errorf("error while unmarshalling line %d: %s", lineNum+1, err.Error())
}
if len(entry) == 0 {
return invMap, fmt.Errorf("no data found on line %d", lineNum+1)
}
invId, ok := entry["invocationId"].(string)
if !ok {
return invMap, fmt.Errorf("invocation id not found on line %d", lineNum+1)
}
inv, ok := invMap[invId]
if !ok {
inv = &Invocation{testResults: []*rdb_pb.TestResult{}, testExonerations: []*rdb_pb.TestExoneration{}}
invMap[invId] = inv
}
// retrieve invocation
invocationInterface, ok := entry["invocation"]
if ok {
data, err := json.Marshal(invocationInterface)
if err != nil {
return invMap, fmt.Errorf("error during marshaling invocation at line %d: %s", lineNum+1, err.Error())
}
// Obey proto compatibility rules.
unmarshalOpts := protojson.UnmarshalOptions{AllowPartial: true, DiscardUnknown: true}
err = unmarshalOpts.Unmarshal(data, &inv.invProto)
if err != nil {
return invMap, fmt.Errorf("error during unmarshalling invocation at line %d: %s", lineNum+1, err.Error())
}
continue
}
//retrieve test_result
testResultInterface, ok := entry["testResult"]
if ok {
data, err := json.Marshal(testResultInterface)
if err != nil {
return invMap, fmt.Errorf("error during marshaling testResult at line %d: %s", lineNum+1, err.Error())
}
var testResult rdb_pb.TestResult
// Obey proto compatibility rules.
unmarshalOpts := protojson.UnmarshalOptions{AllowPartial: true, DiscardUnknown: true}
err = unmarshalOpts.Unmarshal(data, &testResult)
if err != nil {
return invMap, fmt.Errorf("error during unmarshalling testResult at line %d: %s", lineNum+1, err.Error())
}
inv.testResults = append(inv.testResults, &testResult)
continue
}
//retrieve test_exoneration
testExonerationInterface, ok := entry["testExoneration"]
if ok {
data, err := json.Marshal(testExonerationInterface)
if err != nil {
return invMap, fmt.Errorf("error during marshaling testExoneration at line %d: %s", lineNum+1, err.Error())
}
var testExoneration rdb_pb.TestExoneration
err = protojson.Unmarshal(data, &testExoneration)
if err != nil {
return invMap, fmt.Errorf("error during unmarshalling testExoneration at line %d: %s", lineNum+1, err.Error())
}
inv.testExonerations = append(inv.testExonerations, &testExoneration)
} else {
return invMap, fmt.Errorf("no valid key (invocation, testResult, testExoneration) found at line %d", lineNum+1)
}
}
return invMap, nil
}
// ParseInvocationIds returns the invocation ids without InvocationIdPrefix
func ParseInvocationIds(invIds []string) ([]string, error) {
parsedIds := []string{}
for _, invId := range invIds {
if !strings.HasPrefix(invId, InvocationIdPrefix) {
return parsedIds, fmt.Errorf("%q invocation id does not start with invocation id prefix %q", invId, InvocationIdPrefix)
}
parsedIds = append(parsedIds, invId[len(InvocationIdPrefix):])
}
return parsedIds, nil
}