blob: 45a13a83576bce18177dda857afbe20992a4022e [file] [log] [blame]
// Copyright 2018 The Fuchsia Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
package service
import (
"context"
"log"
"github.com/google/uuid"
api "google.golang.org/genproto/googleapis/devtools/resultstore/v2"
"google.golang.org/genproto/protobuf/field_mask"
)
// Service executes ResultStore RPCs.
//
// ResultStore resources are hierarchical, so Service methods return keys that must be
// used to establish this hierarchy when creating new resources.
//
// A typical series of RPCs might look like the following.
//
// 1. Create an Invocation.
// 2. Create a Target, specifying the parent Invocation Key.
// 3. Create a Configuration, specifying the parent Invocation Key.
// 3. Create a ConfiguredTarget, specifying the configuration ID and parent Target Key.
// 4. Create an Action, specifying the parent ConfiguredTarget Key.
// 5. Update / Complete the Action.
// 6. Update / Complete the Configured Target.
// 7. Update / Complete the Target.
// 8. Complete the Invocation.
//
// For more information about the ResultStore APIs, see the proto library at
// https://github.com/googleapis/googleapis/tree/master/google/devtools/resultstore/v2
//
// Service methods generate UUIDs for certain proto.Message fields. If the provided
// Context contains a non-empty string value for TestUUIDContextKey, that value will be
// used instead.
type Service struct {
authToken string
client api.ResultStoreUploadClient
}
// New creates a new Service. Set useFakeUUIDs to true to use fake UUIDs for
// testing. authToken is the authorization token to use for the invocation. The same
// token must be used in all requests to modify a single invocation.
func New(client api.ResultStoreUploadClient, authToken string) *Service {
return &Service{client: client, authToken: authToken}
}
// uuid generates an RFC-4122 compliant Version 4 UUID. If the provided Context contains
// a non-empty string value for TestUUIDContextKey, that value will be used instead. If
// there was an error when reading the UUID from the context, a message is logged
// describing the error and a new UUID is generated.
func (s *Service) uuid(ctx context.Context) string {
value, err := GetTestUUID(ctx)
if err != nil {
log.Println(err)
return uuid.New().String()
}
if value == "" {
return uuid.New().String()
}
return value
}
// CreateInvocation must be called before any other "Create" methods, since all resources
// belong to an Invocation.
func (s *Service) CreateInvocation(
ctx context.Context, invocation *Invocation) (*InvocationKey, error) {
output, err := s.client.CreateInvocation(ctx, &api.CreateInvocationRequest{
RequestId: s.uuid(ctx),
AuthorizationToken: s.authToken,
InvocationId: s.uuid(ctx),
Invocation: invocation.ToResultStoreInvocation(),
})
if err != nil {
return nil, err
}
return &InvocationKey{EntityID: output.GetId(), EntityName: output.GetName()}, nil
}
// CreateConfiguration creates a new Configuration representing a build or test
// environment.
func (s *Service) CreateConfiguration(ctx context.Context, config *Configuration, invocationName string) (*ConfigurationKey, error) {
output, err := s.client.CreateConfiguration(ctx, &api.CreateConfigurationRequest{
RequestId: s.uuid(ctx),
AuthorizationToken: s.authToken,
Parent: invocationName,
ConfigId: config.ID,
Configuration: config.ToResultStoreConfiguration(),
})
if err != nil {
return nil, err
}
return &ConfigurationKey{EntityID: output.GetId(), EntityName: output.GetName()}, nil
}
// CreateTarget creates a new build or test target.
func (s *Service) CreateTarget(ctx context.Context, target *Target, invocationName string) (*TargetKey, error) {
output, err := s.client.CreateTarget(ctx, &api.CreateTargetRequest{
RequestId: s.uuid(ctx),
AuthorizationToken: s.authToken,
Parent: invocationName,
TargetId: target.ID,
Target: target.ToResultStoreTarget(),
})
if err != nil {
return nil, err
}
return &TargetKey{EntityID: output.GetId(), EntityName: output.GetName()}, nil
}
// CreateConfiguredTarget creates a new ConfiguredTarget representing a target being built
// or tested with a specific configuration.
func (s *Service) CreateConfiguredTarget(ctx context.Context, configTarget *ConfiguredTarget, targetName string) (*ConfiguredTargetKey, error) {
output, err := s.client.CreateConfiguredTarget(ctx, &api.CreateConfiguredTargetRequest{
AuthorizationToken: s.authToken,
RequestId: s.uuid(ctx),
Parent: targetName,
ConfigId: configTarget.ConfigID,
ConfiguredTarget: configTarget.ToResultStoreConfiguredTarget(),
})
if err != nil {
return nil, err
}
return &ConfiguredTargetKey{EntityID: output.GetId(), EntityName: output.GetName()}, nil
}
// CreateTestAction creates a new Action representing a Target test.
func (s *Service) CreateTestAction(ctx context.Context, action *TestAction, configTargetName string) (*ActionKey, error) {
output, err := s.client.CreateAction(ctx, &api.CreateActionRequest{
AuthorizationToken: s.authToken,
RequestId: s.uuid(ctx),
Parent: configTargetName,
ActionId: action.ID,
Action: action.ToResultStoreAction(),
})
if err != nil {
return nil, err
}
return &ActionKey{EntityID: output.GetId(), EntityName: output.GetName()}, nil
}
// UpdateTestAction updates the duration and status of a previously started TestAction.
func (s *Service) UpdateTestAction(ctx context.Context, action *TestAction) (*ActionKey, error) {
output, err := s.client.UpdateAction(ctx, &api.UpdateActionRequest{
AuthorizationToken: s.authToken,
Action: action.ToResultStoreAction(),
UpdateMask: &field_mask.FieldMask{
Paths: []string{"timing.duration", "status_attributes"},
},
})
if err != nil {
return nil, err
}
return &ActionKey{EntityID: output.GetId(), EntityName: output.GetName()}, nil
}
// UpdateConfiguredTarget updates the duration and status of a previously started
// UpdateConfigured.
func (s *Service) UpdateConfiguredTarget(ctx context.Context, configTarget *ConfiguredTarget) (*ConfiguredTargetKey, error) {
output, err := s.client.UpdateConfiguredTarget(ctx, &api.UpdateConfiguredTargetRequest{
AuthorizationToken: s.authToken,
ConfiguredTarget: configTarget.ToResultStoreConfiguredTarget(),
UpdateMask: &field_mask.FieldMask{
Paths: []string{"timing.duration", "status_attributes"},
},
})
if err != nil {
return nil, err
}
return &ConfiguredTargetKey{EntityID: output.GetId(), EntityName: output.GetName()}, nil
}
// UpdateTarget updates the duration and status of a previously started Target.
func (s *Service) UpdateTarget(ctx context.Context, target *Target) (*TargetKey, error) {
output, err := s.client.UpdateTarget(ctx, &api.UpdateTargetRequest{
AuthorizationToken: s.authToken,
UpdateMask: &field_mask.FieldMask{
Paths: []string{"timing.duration", "status_attributes"},
},
Target: target.ToResultStoreTarget(),
})
if err != nil {
return nil, err
}
return &TargetKey{EntityID: output.GetId(), EntityName: output.GetName()}, nil
}
// UpdateInvocation updates the duration and status of a previously started Invocation.
func (s *Service) UpdateInvocation(ctx context.Context, invocation *Invocation) (*InvocationKey, error) {
output, err := s.client.UpdateInvocation(ctx, &api.UpdateInvocationRequest{
AuthorizationToken: s.authToken,
UpdateMask: &field_mask.FieldMask{
Paths: []string{"timing.duration", "status_attributes"},
},
Invocation: invocation.ToResultStoreInvocation(),
})
if err != nil {
return nil, err
}
return &InvocationKey{EntityID: output.GetId(), EntityName: output.GetName()}, nil
}
// FinishConfiguredTarget closes a ConfiguredTarget for editing.
func (s *Service) FinishConfiguredTarget(ctx context.Context, name string) (*ConfiguredTargetKey, error) {
output, err := s.client.FinishConfiguredTarget(ctx, &api.FinishConfiguredTargetRequest{
AuthorizationToken: s.authToken,
Name: name,
})
if err != nil {
return nil, err
}
return &ConfiguredTargetKey{EntityID: output.GetId(), EntityName: output.GetName()}, nil
}
// FinishTarget closes a Target for editing.
func (s *Service) FinishTarget(ctx context.Context, name string) (*TargetKey, error) {
output, err := s.client.FinishTarget(ctx, &api.FinishTargetRequest{
AuthorizationToken: s.authToken,
Name: name,
})
if err != nil {
return nil, err
}
return &TargetKey{EntityID: output.GetId(), EntityName: output.GetName()}, nil
}
// FinishInvocation closes an Invocation for editing.
func (s *Service) FinishInvocation(ctx context.Context, name string) (*InvocationKey, error) {
output, err := s.client.FinishInvocation(ctx, &api.FinishInvocationRequest{
AuthorizationToken: s.authToken,
Name: name,
})
if err != nil {
return nil, err
}
return &InvocationKey{EntityID: output.GetId(), EntityName: output.GetName()}, nil
}