/*
* Copyright (C) 2019 The Android Open Source Project
*
* 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.
*/

syntax = "proto3";

package luci.resultdb.v1;

import "google/api/field_behavior.proto";
import "google/protobuf/empty.proto";
import "google/protobuf/field_mask.proto";
import public "tools/tradefederation/core/proto/resultdb/artifact.proto";
import public "tools/tradefederation/core/proto/resultdb/invocation.proto";
import public "tools/tradefederation/core/proto/resultdb/root_invocation.proto";
import public "tools/tradefederation/core/proto/resultdb/test_result.proto";
import public "tools/tradefederation/core/proto/resultdb/work_unit.proto";

option go_package = "go.chromium.org/luci/resultdb/proto/v1;resultpb";
option java_package = "com.android.resultdb.proto";
option java_multiple_files = true;

// Service to record test results.
//
// RPCs that mutate an root invocation, work unit or invocation return
// FAILED_PRECONDITION error code if the resource is finalized (or finalizing).
//
// # Authorisation
//
// The responses to CreateRootInvocation, CreateWorkUnit and CreateInvocation
// include a metadata key "update-token".
// It MUST be passed to all other mutation RPCs, such as CreateTestResult.
// Otherwise the request will fail with UNAUTHENTICATED error code.
//
// # Retry policy
//
// To ensure reliable recording of test results, please retry any error of type
// INTERNAL, UNKNOWN or UNAVAILABLE with the following parameters:
// - Initial backoff of 1 second (minimum).
// - For each consecutive error, increase the minimum back-off period
//   by a factor 2, exponentially up to 32 seconds.
// For a minimum of 6 retries over 63 (=1+2+4+8+16+32) seconds.
//
//
// Use of LUCI is subject to the Google [Terms of Service](https://policies.google.com/terms)
// and [Privacy Policy](https://policies.google.com/privacy).
service Recorder {
  // == Root Invocations =======================================================

  // Creates a new root invocation and its root work unit.
  //
  // The response header metadata contains "update-token" required for future
  // updates to the root invocation and the root work unit, including finalization.
  // The same update token is used for both resources.
  //
  // If RootInvocation with the given ID already exists, returns ALREADY_EXISTS
  // error code.
  //
  // N.B. this method is an AIP-136 custom method as it creates two resources
  // at once. As such, it does not follow the pattern of AIP-133.
  rpc CreateRootInvocation(CreateRootInvocationRequest) returns (CreateRootInvocationResponse) {};

  // Updates an existing non-final RootInvocation.
  rpc UpdateRootInvocation(UpdateRootInvocationRequest) returns (RootInvocation) {};

  // Marks the given RootInvocation as final and begins the process of
  // transitioning it to the state FINALIZED.
  rpc FinalizeRootInvocation(FinalizeRootInvocationRequest) returns (RootInvocation) {};

  // == Work Units =============================================================

  // Creates a new work unit inside a root invocation.
  //
  // The response header metadata contains "update-token" required for future
  // updates, including finalization.
  //
  // If a work unit with the given ID already exists, returns ALREADY_EXISTS
  // error code.
  //
  // See CreateWorkUnitRequest for more details.
  rpc CreateWorkUnit(CreateWorkUnitRequest) returns (WorkUnit) {};

  // Batch creates new WorkUnits inside a root invocation.
  //
  // Unlike CreateWorkUnit, update tokens are included directly in the response
  // message to avoid limits on HTTP response headers. Do not log these values.
  //
  // If a work unit with the given ID already exists, returns ALREADY_EXISTS
  // error code.
  rpc BatchCreateWorkUnits(BatchCreateWorkUnitsRequest)
      returns (BatchCreateWorkUnitsResponse) {};

  // Updates an existing non-final WorkUnit.
  rpc UpdateWorkUnit(UpdateWorkUnitRequest) returns (WorkUnit) {};

  // Batch updates existing non-final WorkUnits.
  rpc BatchUpdateWorkUnits(BatchUpdateWorkUnitsRequest)
      returns (BatchUpdateWorkUnitsResponse) {};

  // Marks the given work unit as final and begins the process of
  // transitioning it to the state FINALIZED.
  rpc FinalizeWorkUnit(FinalizeWorkUnitRequest) returns (WorkUnit) {};

  // Batch marks a set of WorkUnits as final and begins the process offer
  // transitioning them to the state FINALIZED.
  rpc BatchFinalizeWorkUnits(BatchFinalizeWorkUnitsRequest)
      returns (BatchFinalizeWorkUnitsResponse) {};

  // Mints an 'inclusion token' that proves the current caller has permission
  // to include work units from a given realm. The minted inclusion token
  // can be passed to another caller/system and used in CreateWorkUnit to
  // create child work units into the parent work unit.
  //
  // Systems which produce test results should require such a token (instead
  // of an update token) if they are distrustful of their callers and believe
  // they may not be authorised to see results from a given realm.
  rpc DelegateWorkUnitInclusion(DelegateWorkUnitInclusionRequest) returns (DelegateWorkUnitInclusionResponse) {};

  // == Invocations (Legacy) ===================================================

  // Creates a new invocation.
  // The request specifies the invocation id and its contents.
  //
  // The response header metadata contains "update-token" required for future
  // updates, including finalization.
  //
  // If invocation with the given ID already exists, returns ALREADY_EXISTS
  // error code.
  rpc CreateInvocation(CreateInvocationRequest) returns (Invocation) {};

  // Creates multiple invocations in a single rpc.
  //
  // Unlike CreateInvocation, update tokens are included in the response message
  // to avoid limits on HTTP response headers. Do not log these values.
  rpc BatchCreateInvocations(BatchCreateInvocationsRequest)
      returns (BatchCreateInvocationsResponse) {};

  // Updates an existing non-finalized invocation.
  rpc UpdateInvocation(UpdateInvocationRequest) returns (Invocation) {};

  // Transitions the given invocation to the state FINALIZED.
  rpc FinalizeInvocation(FinalizeInvocationRequest) returns (Invocation) {};

  // Updates inclusions for a non-finalized invocation.
  rpc UpdateIncludedInvocations(UpdateIncludedInvocationsRequest)
      returns (google.protobuf.Empty) {};

  // == Baselines ==============================================================

  // Recursively marks all test variants associated with the invocation as
  // submitted, merging them into the invocation's associated baseline.
  rpc MarkInvocationSubmitted(MarkInvocationSubmittedRequest)
      returns (google.protobuf.Empty) {};

  // == Test results ===========================================================

  // Appends a test result to a non-finalized invocation.
  rpc CreateTestResult(CreateTestResultRequest) returns (TestResult) {};
  // Atomically appends a batch of test results to a non-finalized invocation.
  rpc BatchCreateTestResults(BatchCreateTestResultsRequest)
      returns (BatchCreateTestResultsResponse) {};

  // Appends a test exoneration to a non-finalized invocation.
  rpc CreateTestExoneration(CreateTestExonerationRequest)
      returns (TestExoneration) {};
  // Atomically appends a batch of test exonerations to a non-finalized
  // invocation.
  rpc BatchCreateTestExonerations(BatchCreateTestExonerationsRequest)
      returns (BatchCreateTestExonerationsResponse) {};

  // == Artifacts ==============================================================

  // Create multiple artifacts.
  //
  // An artifact can be either invocation-level or test-result-level.
  // See Artifact.name for more info.
  rpc BatchCreateArtifacts(BatchCreateArtifactsRequest)
      returns (BatchCreateArtifactsResponse) {};
}

// == Root Invocations =========================================================

// A request message for the CreateRootInvocation RPC.
//
// To use this RPC, the caller will need resultdb.rootInvocations.create and
// resultdb.workUnits.create permission in the realm `root_invocation.realm`.
//
// The following permissions may also be required depending on the request:
// - if an ID not starting with "u-" is specified,
//   resultdb.rootInvocations.createWithReservedID permission is required
// - if the producer resource is set,
//   resultdb.rootInvocations.setProducerResource permission is required
// - if a baseline is set,
//   resultdb.baselines.put permission is required in the ":@project" realm
//   of the LUCI project the root invocation is being created in.
message CreateRootInvocationRequest {
  // RootInvocation identifier, becomes a part of the root_invocation.name.
  // Only trusted systems with an agreed format negotiated with ResultDB
  // may create nicely formatted IDs (e.g. "build-1234567890"), other
  // clients must use IDs of the form "u-{GUID}".
  //
  // Regex: ^[a-z][a-z0-9_\-]*$.
  string root_invocation_id = 1 [ (google.api.field_behavior) = REQUIRED ];

  // The root invocation to create.
  RootInvocation root_invocation = 2;

  // The root work unit to create inside the above root invocation.
  //
  // The name of this work unit will always be
  // "rootInvocations/{root_invocation_id}/workUnits/root".
  //
  // Do not set the realm or producer_resource fields, these will be inherited
  // from the root invocation.
  //
  // Required.
  WorkUnit root_work_unit = 3;

  // A unique identifier for this request. Restricted to 36 ASCII characters.
  // A random UUID is recommended.
  //
  // If the same request is replayed (see "Retry policy" at the top of this doc),
  // this request_id guarantees idempotence.
  //
  // Required.
  string request_id = 4;
}

// A response message for the CreateRootInvocation RPC.
message CreateRootInvocationResponse {
  // The root invocation that was created.
  RootInvocation root_invocation = 1;

  // The root work unit that was created inside the above root invocation.
  // Test results, artifacts, exonerations and further work units can be
  // created inside this resource.
  WorkUnit root_work_unit = 2;
}

// A request message for the UpdateRootInvocation RPC.
message UpdateRootInvocationRequest {
  // The root invocation to update.
  // If updating tags, the existing tags will be replaced with the new ones.
  RootInvocation root_invocation = 1 [ (google.api.field_behavior) = REQUIRED ];

  // The list of fields to be updated.
  google.protobuf.FieldMask update_mask = 2;
}

// A request message for the FinalizeRootInvocation RPC.
//
// This RPC is idempotent: attempting to finalize an already finalized
// (or finalizing) root invocation will have no effect.
message FinalizeRootInvocationRequest {
  // Name of the root invocation to finalize.
  string name = 1 [ (google.api.field_behavior) = REQUIRED ];

  // Describes what to do with the root work unit when finalizing the root
  // invocation.
  enum FinalizationScope {
    // The scope is not specified. Do not use.
    FINALIZATION_SCOPE_UNSPECIFIED = 0;

    // Do not finalize the root work unit.
    // FinalizeWorkUnit must be called on on the root work unit separately.
    EXCLUDE_ROOT_WORK_UNIT = 1;

    // Finalize the root work unit in addition to the root invocation.
    // If the root work unit is already finalized (or finalizing),
    // no further action is taken on it.
    INCLUDE_ROOT_WORK_UNIT = 2;
  }

  // Specifies whether to finalize the root work unit along with the root
  // invocation. Required.
  FinalizationScope finalization_scope = 2 [ (google.api.field_behavior) = REQUIRED ];
}

// == Work Units ===============================================================

// A request message for the CreateWorkUnit RPC.
//
// To use this RPC, you must have:
// 1. the update token from the parent work unit in the "update-token"
//   request header, or alternatively, an inclusion token from
//   DelegateWorkUnitInclusion in an "inclusion-token" request header.
//   This authorises the modification of the parent work unit.
// 2. If the realm of the work unit to be created differs from the parent
//    work unit:
//  - the resultdb.workunits.create permission on the realm the work unit
//     is being created in, to verify the caller is trusted to maintain
//     integrity of data in the realm to which it is writing.
//  - resultdb.workunits.include permission for the realm the work unit is
//    being created in. This ensures the caller is authorised to make the
//    results of the child work unit visible within the parent work unit,
//    and by extension the root invocation. This is because including the child
//    work unit implicitly declassifies the results in the the realm of the
//    root invocation. This is not necessary if an inclusion token was provided
//    at point (1.) as this permission was checked when the token was minted.
// 3. if an ID not starting with "u-" is specified,
//   the resultdb.workUnits.createWithReservedID permission
// 4. if the producer resource is set,
//   the resultdb.workUnits.setProducerResource permission
//
// This RPC returns an update token in a response header named "update-token".
// This token is required to make future updates. Treat this token as you might
// an OAuth token, do not log it.
message CreateWorkUnitRequest {
  // The parent work unit in which this work unit should be created.
  // Format: rootInvocations/{ROOT_INVOCATION_ID}/workUnits/{PARENT_WORK_UNIT_ID}.
  string parent = 1;

  // WorkUnit identifier, becomes a part of the work_unit.name.
  // Only trusted systems with an agreed format negotiated with ResultDB
  // may create nicely formatted IDs (e.g. "build-1234567890"), other
  // clients must use IDs of the form "u-{GUID}".
  //
  // # Prefixed Work Unit IDs (:)
  //
  // Sometimes the a process wishes to allocate multiple work units
  // for its own use that share the same update token. A common reason is to
  // enable batch uploading test results to those multiple work units in
  // the same BatchCreateTestResults request, for efficiency.
  //
  // For this purpose, we offer prefixed work unit IDs. Create a
  // work unit with an ID of form `<base_id>:<child_id>` and specify its
  // parent as `<base_id>` or `<base_id>:<other_child_id>`. The newly
  // created work unit will use the same update token as the work unit
  // `<base_id>` and other work units prefixed with `<base_id>:` in
  // the same root invocation.
  //
  // Note that only single prefixing is possible; the ID construction
  // `<base_id>:<child_1>:<grandchild_1>` is not allowed. You may
  // however create `<base_id>:<grandchild_1>` under `<base_id>:<child_1>`
  // to have the grandchild share the same update token as the child,
  // the base work unit and all other `<base_id>:`-prefixed descendents.
  //
  // Prefixed work units can be created by any caller with access to the
  // base work unit's update token, regardless of if the caller also has
  // resultdb.workUnits.createWithReservedID. I.E. the owner of a nicely
  // named work unit is entitled to all its prefixed children.
  //
  // Prefixed work units do not offer the same isolation and integrity
  // guarantees as non-prefixed work units because of the shared update
  // token, so are only suitable for uploads within the same security context
  // (i.e. same machine, task and account). They must not be delegated.
  //
  // Regex: ^[a-z][a-z0-9_\-]*(:[a-z][a-z0-9_\-])?$. Limited to
  // 100 characters in length.
  string work_unit_id = 2 [ (google.api.field_behavior) = REQUIRED ];

  // WorkUnit data to insert.
  WorkUnit work_unit = 3;

  // A unique identifier for this request. Restricted to 36 ASCII characters.
  // A random UUID is recommended.
  //
  // If the same request is replayed (see "Retry policy" at the top of this doc),
  // this request_id guarantees idempotence.
  //
  // Required (unless this request is nested in a BatchCreateWorkUnitsRequest).
  string request_id = 4;
}

// A request message for BatchCreateWorkUnits RPC.
//
// The request will be handled atomically (i.e. all work units are
// created or none are).
//
// See CreateWorkUnitRequest for details about required permissions.
message BatchCreateWorkUnitsRequest {
  // The work units to create.
  //
  // Nested structures of work units may be created in the same batch, by
  // having later request entries refer to work units created by earlier
  // request entries. For example, requests[1] may assume requests[0] has
  // already been created and refer to it as its parent.
  //
  // There is one important caveat: only one "update-token" request metadata
  // key (needed to modify the parent work unit) or "inclusion-token" metadata
  // key may be provided per batch request. This means that all work units being
  // created must be children of the same work unit parent OR children of work
  // units sharing the same update token (see CreateWorkUnitRequest.work_unit_id
  // for details about prefixed work unit IDs that share an update token).
  //
  // # Allowed usages
  //
  // Create multiple children in one request, sharing the same work unit parent
  // "wu0":
  //
  // Root invocation
  // ...
  //   wu0 <-- Pre-existing
  //     |- wu1
  //     |- wu2
  //
  // Create multiple nested structures, all descending from work units sharing
  // update token from "wu0":
  //
  // Root invocation
  // ...
  //   wu0:shard1  <-- Pre-existing
  //     |- wu0:shard1-attempt1
  //     |   |- wu0:shard1-attempt1-module1
  //     |
  //     |- wu0:shard1-attempt2
  //         |- wu0:shard1-attempt1-module2
  //
  //   wu0:shard2  <-- Pre-existing
  //     |- wu0:shard2-attempt1
  //
  // # Disallowed usages
  //
  // Create a nested structure, descending from work units with
  // different update tokens:
  //
  // Root invocation
  // ...
  //   wu0 <-- Pre-existing
  //     |- wu1 <--- Requires update token for "wu0".
  //     |- wu2
  //        |- wu21  <--- Requires update token for "wu2".
  //
  // Create multiple nested structures, descending from work units with
  // different update tokens:
  //
  // Root invocation
  // ...
  //   wu0  <-- Pre-existing
  //    |- wu1
  //    |- wu2
  //
  //   wu20  <-- Pre-existing
  //    |- wu21
  //
  // This will not succeed because the server needs the update token of both
  // "wu0" and "wu20" to succeed, and only one update token can be supplied
  // per request.
  //
  // requests[i].request_id MUST be either empty or equal to request_id in
  // this message.
  //
  // Up to 500 requests. Total size of all requests (as measured by proto.Size())
  // MUST be <= 10 MiB.
  repeated CreateWorkUnitRequest requests = 1;

  // A unique identifier for this request. Restricted to 36 ASCII characters.
  // A random UUID is recommended.
  // If the same request is replayed, the request_id guarantees idempotence.
  // Required.
  string request_id = 2;
}

// A response message for BatchCreateWorkUnits RPC.
message BatchCreateWorkUnitsResponse {
  // The created work units.
  repeated WorkUnit work_units = 1;

  // One token per each created work unit.
  // These are passed in the response instead of as metadata, because large
  // batches increase the size of the response headers beyond allowed limits.
  // update_tokens[i] corresponds to work_units[i].
  // *Do not log these values (they are secrets that should be treated
  // similar to OAuth tokens)*.
  repeated string update_tokens = 2;
}

// A request message for UpdateWorkUnit RPC.
//
// The request will be handled atomically (i.e. all work units are
// updated or none are).
message UpdateWorkUnitRequest {
  // The work unit to update.
  //
  // If updating tags, the existing tags will be replaced with the new ones.
  WorkUnit work_unit = 1 [ (google.api.field_behavior) = REQUIRED ];

  // The list of fields to be updated.
  google.protobuf.FieldMask update_mask = 2;
}

// A request message for BatchUpdateWorkUnits RPC.
//
// Note: As each request may only have one update token, batching is only
// possible for work units sharing the same prefix (and thus update token).
// See CreateWorkUnitRequest.work_unit_id.
message BatchUpdateWorkUnitsRequest {
  // The request messages specifying the work unit updates.
  //
  // A maximum of 500 work units can be modified in one request.
  repeated UpdateWorkUnitRequest requests = 1;
}

// A response message for BatchUpdateWorkUnits RPC.
message BatchUpdateWorkUnitsResponse {
  // The updated work units.
  // To limit response size, the `extended_properties` field is elided
  // from work units.
  repeated WorkUnit work_units = 1;
}

// A request message for FinalizeWorkUnit RPC.
//
// This RPC is idempotent: attempting to finalize an already finalized
// (or finalizing) work unit will have no effect.
//
// To limit response size, the `extended_properties` field is elided
// from the work unit returned by the RPC.
message FinalizeWorkUnitRequest {
  // Resource name of the work unit to finalize.
  string name = 1 [ (google.api.field_behavior) = REQUIRED ];
}

// A request message for BatchFinalizeWorkUnits RPC.
//
// The Batch will be handled atomically (i.e. all requests will
// be processed or none will). In practice, the only case where
// requests will not be processed is if a request is invalid or permission
// is lacking; it is not an error if one of the work units is already
// finalizing as the semantics of each request is defined idempotently.
//
// Note: As each request may only have one update token, batching is only
// possible for work units sharing the same prefix (and thus update token).
// See CreateWorkUnitRequest.work_unit_id.
message BatchFinalizeWorkUnitsRequest {
  // The request messages specifying the work units to finalize.
  //
  // A maximum of 500 work units can be finalized in one request.
  repeated FinalizeWorkUnitRequest requests = 1;
}

// A response message for BatchFinalizeWorkUnits RPC.
message BatchFinalizeWorkUnitsResponse {
  // The finalized work units.
  // To limit response size, the `extended_properties` field is elided
  // from work units.
  repeated WorkUnit work_units = 1;
}

// Mints an inclusion token that proves the current caller has permission to
// include work units from a given realm. The minted inclusion token can be
// passed to another caller/system and used in CreateWorkUnit to include
// child work units into the parent work unit.
//
// The token returned by this RPC is in a response header metadata key
// named "inclusion-token" and is a replacement to the update token that
// is otherwise required by CreateWorkUnit.
//
// Purpose:
//
// This RPC allows the permissions required by CreateWorkUnit to be
// split over two callers/systems:
//
//  1. Caller #1, which owns the parent work unit and wishes to include
//     a child work unit from a (possibly) different realm, but may not
//     have one of the permissions required by CreateWorkUnit, e.g.:
//     - permission to create work units in that realm
//     (resultdb.workunits.create).
//     - permission to create work units with reserved names
//     (resultdb.workunits.createWithReservedID).
//     - permission to set the producer resource field
//     (resultdb.workunits.setProducerResource).
//  2. Caller #2, which is happy to create work unit in a given realm
//     and has permission to do so, but wants caller #1 to prove it has
//     read (include) access to the created work unit before it includes
//     the work unit in caller #1's work unit (which implicitly declassifies
//     the results into that work unit, and by extension, the root invocation).
//
// Use of this RPC is usually not necessary and is designed for systems
// where multiple systems need to cooperate to create work units in a
// secure manner. If the first caller has all required permissions, it is
// common to call CreateWorkUnit directly with the update token to create a new
// work unit. The ID of the created work unit and its update token can then be
// passed to a second system to populate.
//
// Security: Inclusion tokens should be treated with the same level of security
// as update tokens, as they effectively grant permission to modify the parent work unit.
// Treat them as secrets and do not log them.
message DelegateWorkUnitInclusionRequest {
  // The resource name of the parent work unit in which further work units
  // shall be included (created).
  //
  // The update token of this work unit must be passed in the
  // "update-token" request metadata key, or a PERMISSION_DENIED error will
  // be returned.
  //
  // The minted inclusion token will be scoped to this work unit; the token
  // will not allow inclusion into any other work unit.
  string work_unit = 1;

  // The realm of the prospective child work unit(s).
  // The caller must have resultdb.workunits.include permission on this realm.
  string realm = 2;
}

// A response message for DelegateWorkUnitInclusion RPC.
message DelegateWorkUnitInclusionResponse {
  // The response header contains a metadata key named "inclusion-token".
  // The token is not included in the response body to minimise the risk of
  // accidentally logging this secret data.
}

// == Invocations (Legacy) =====================================================

// A request message for CreateInvocation RPC.
message CreateInvocationRequest {
  // Invocation identifier, becomes a part of the invocation.name.
  // LUCI systems MAY create invocations with nicely formatted IDs, such as
  // "build-1234567890". All other clients MUST use GUIDs.
  //
  // Regex: ^[a-z][a-z0-9_\-]*$.
  string invocation_id = 1 [ (google.api.field_behavior) = REQUIRED ];

  // Invocation data to insert.
  Invocation invocation = 2;

  // A unique identifier for this request. Restricted to 36 ASCII characters.
  // A random UUID is recommended.
  // This request is only idempotent if a `request_id` is provided.
  string request_id = 3;
}

// A request message for BatchCreateInvocations RPC.
message BatchCreateInvocationsRequest {
  // requests[i].request_id MUST be either empty or equal to request_id in
  // this message.
  //
  // Up to 500 requests.
  repeated CreateInvocationRequest requests = 1;

  // A unique identifier for this request. Restricted to 36 ASCII characters.
  // A random UUID is recommended.
  // This request is only idempotent if a `request_id` is provided, so it is
  // strongly recommended to populate this field.
  string request_id = 2;
}

// A response message for BatchCreateInvocations RPC.
message BatchCreateInvocationsResponse {
  // Invocations created.
  repeated Invocation invocations = 1;

  // One token per each created invocation.
  // These are passed in the response instead of as metadata, because large
  // batches increase the size of the response headers beyond allowed limits and
  // cause failures like crbug.com/1064496
  // update_tokens[i] corresponds to invocations[i].
  // *Do not log these values*.
  repeated string update_tokens = 2;
}


// A request message for UpdateInvocation RPC.
message UpdateInvocationRequest {
  // Invocation to update.
  // If updating tags, the existing tags will be replaced with the new ones.
  //
  // If the state of the invocation is set to FINALIZING (with correct update_mask),
  // this will trigger the finalization process of the invocation.
  //
  // This is useful for the cases when we want the update and the finalization
  // to happen in the same transaction to prevent update-update race. This will
  // guarantee eventual consistency.
  Invocation invocation = 1 [ (google.api.field_behavior) = REQUIRED ];

  // The list of fields to be updated.
  google.protobuf.FieldMask update_mask = 2;
}

// A request message for FinalizeInvocation RPC.
message FinalizeInvocationRequest {
  // Name of the invocation to finalize.
  string name = 1 [ (google.api.field_behavior) = REQUIRED ];
}

// A request message for UpdateIncludedInvocations RPC.
message UpdateIncludedInvocationsRequest {
  // Name of the invocation to add/remove inclusions to/from,
  // see Invocation.name.
  // For example, name of the buildbucket build invocation that should include
  // a swarming task invocation.
  string including_invocation = 1 [ (google.api.field_behavior) = REQUIRED ];

  // Names of the invocations to include, see Invocation.name.
  // If any of these invocations are already included, they will be silently
  // ignored for idempotency.
  repeated string add_invocations = 2;

  // Deprecated: Removing invocations is no longer supported. Do not use.
  repeated string remove_invocations = 3;
}

// == Baselines ================================================================

// A request message for MarkInvocationSubmitted RPC.
// To use this RPC, callers need:
// - resultdb.invocations.setSubmitted in the realm the <project>:@project, where
//   project is the project of the nominated invocation.
message MarkInvocationSubmittedRequest {
  // Name of the root invocation, e.g. "rootInvocations/{id}".
  string root_invocation = 2 [ (google.api.field_behavior) = REQUIRED ];

  // DEPRECATED: use `root_invocation` instead.
  // Name of the invocation, e.g. "invocations/{id}".
  string invocation = 1 [ deprecated = true ];
}

// == Test results =============================================================

// A request message for CreateTestResult RPC.
message CreateTestResultRequest {
  // Name of the parent work unit. See WorkUnit.name.
  string parent = 4 [ (google.api.field_behavior) = REQUIRED ];

  // DEPRECATED: Use `parent` instead.
  // Name of the parent invocation, see Invocation.name.
  string invocation = 1 [ deprecated = true ];

  // The test result to create.
  // Test id and result id are used to dedupe requests, i.e.
  // if a test result with the same test id and result id already exists in
  // the invocation, then the requests succeeds as opposed to returns with
  // ALREADY_EXISTS error.
  TestResult test_result = 2 [ (google.api.field_behavior) = REQUIRED ];

  // A unique identifier for this request. Restricted to 36 ASCII characters.
  // A random UUID is recommended.
  //
  // If the same request is replayed (see "Retry policy" at the top of this doc),
  // this request_id guarantees idempotence.
  //
  // Required (unless this request is nested in a BatchCreateTestResultsRequest).
  string request_id = 3;
}

// A request message for BatchCreateTestResults RPC.
//
// The request will be handled atomically (i.e. all test results are
// created or none are).
//
// Use of batching for test result uploads is strongly encouraged for
// performance and efficiency reasons. Clients implementing buffering for the
// purposes of batching will typically cut a batch once any of the following
// criteria are met:
// - 500 test results are in the batch,
// - the size of the batch would exceed 10 MiB,
// - a given dwell time has elapsed (e.g. 1 minute), or
// - all test results have completed and the last batch needs to be flushed.
//
// Batched test results can cover more than one work unit, provided all
// work units share the same update token (i.e. in case of Prefixed work units;
// see CreateWorkUnitRequest.work_unit_id for details). In such situations,
// the batching for test results may be integrated with the batching of work
// units (e.g. creating test results in a batch will be preceded by batch-
// creating any pending work units to avoid NotFound errors).
message BatchCreateTestResultsRequest {
  // Name of the parent work unit (if all test results to be created belong to the
  // same work unit). See WorkUnit.name.
  //
  // If this set, then parent field in CreateTestResultRequest messages
  // must either be empty or match this field.
  string parent = 4;

  // DEPRECATED: Use `parent` instead.
  // Name of the parent invocation, see Invocation.name.
  string invocation = 1 [ deprecated = true ];

  // Request messages specifying the test results to be created.
  //
  // Up to 500 requests. Total size of all requests (as measured by proto.Size())
  // MUST be <= 10 MiB.
  repeated CreateTestResultRequest requests = 2;

  // A unique identifier for this request. Restricted to 36 ASCII characters.
  // A random UUID is recommended.
  //
  // If the same request is replayed (see "Retry policy" at the top of this doc),
  // this request_id guarantees idempotence.
  //
  // The request_id field in CreateTestResultRequest messages must either
  // be empty or match this field.
  //
  // Required.
  string request_id = 3;
}

// A response message for BatchCreateTestResults RPC.
message BatchCreateTestResultsResponse {
  // Test results created.
  repeated TestResult test_results = 1;
}

// A request message for CreateTestExoneration RPC.
message CreateTestExonerationRequest {
  // Name of the parent work unit. See WorkUnit.name.
  string parent = 4 [ (google.api.field_behavior) = REQUIRED ];

  // DEPRECATED: Use `parent` instead.
  // Name of the parent invocation, see Invocation.name.
  string invocation = 1 [ deprecated = true ];

  // The TestExoneration to create.
  TestExoneration test_exoneration = 2
      [ (google.api.field_behavior) = REQUIRED ];

  // A unique identifier for this request. Restricted to 36 ASCII characters.
  // A random UUID is recommended.
  //
  // If the same request is replayed (see "Retry policy" at the top of this doc),
  // this request_id guarantees idempotence.
  //
  // Required (unless this request is nested in a BatchCreateTestExonerationsRequest).
  string request_id = 3;
}

// A request message for BatchCreateTestExonerations RPC.
//
// The request will be handled atomically (i.e. all test exonerations are
// created or none are).
//
// Use of batching for exoneration uploads is strongly encouraged for
// performance and efficiency reasons.
//
// Batched test exonerations can cover more than one work unit, provided all
// work units share the same update token (i.e. in case of Prefixed work units;
// see CreateWorkUnitRequest.work_unit_id for details). This is primarily
// for consistency with the other Batch RPCs; in practice there is not
// a lot of benefit to report exonerations through one work unit over another,
// as they are a root invocation-level concept and are only reported through
// work units for traceability reasons.
message BatchCreateTestExonerationsRequest {
  // Name of the parent work unit (if all test exonerations to be created belong to the
  // same work unit). See WorkUnit.name.
  //
  // If this set, then parent field in CreateTestExonerationRequest messages
  // must either be empty or match this field.
  string parent = 4;

  // DEPRECATED: Use `parent` instead.
  // Name of the parent invocation, see Invocation.name.
  string invocation = 1 [ deprecated = true ];

  // Requests to create TestExonerations.
  //
  // Up to 500 requests. Total size of all requests (as measured by proto.Size())
  // MUST be <= 10 MiB.
  repeated CreateTestExonerationRequest requests = 2;

  // A unique identifier for this request. Restricted to 36 ASCII characters.
  // A random UUID is recommended.
  //
  // If the same request is replayed (see "Retry policy" at the top of this doc),
  // this request_id guarantees idempotence.
  //
  // The request_id field in CreateTestResultRequest messages must either
  // be empty or match this field.
  //
  // Required.
  string request_id = 3;
}

// A response message for BatchCreateTestExonerations RPC.
message BatchCreateTestExonerationsResponse {
  // Test exonerations created.
  repeated TestExoneration test_exonerations = 1;
}

// == Artifacts ================================================================

// A request message for CreateArtifactRequest.
message CreateArtifactRequest {
  // Name of the parent work unit. See WorkUnit.name.
  //
  // To create test result-level artifacts, set test_id_structured and result_id on
  // the Artifact. Otherwise, to create work unit-level artifacts, leave
  // test_id_structured and result_id blank.
  //
  // Legacy uploaders can also use the name formats:
  // - invocations/{INVOCATION_ID} for create artifacts in an invocation.
  // - invocations/{INVOCATION_ID}/tests/{URL_ENCODED_TEST_ID}/results/{RESULT_ID}
  // to create test result-level artifacts (in this case, test_id_structured and
  // result_id MUST be left blank on the Artifact).
  string parent = 1 [ (google.api.field_behavior) = REQUIRED ];

  // Artifact to upload.
  // The length of the artifact contents MUST be <= 512KiB.
  // artifact.artifact_id MUST be set.
  // artifact.name will be ignored.
  Artifact artifact = 2 [ (google.api.field_behavior) = REQUIRED ];

  // A unique identifier for this request. Restricted to 36 ASCII characters.
  // A random UUID is recommended.
  //
  // If the same request is replayed (see "Retry policy" at the top of this doc),
  // this request_id guarantees idempotence.
  //
  // Required (unless this request is nested in a BatchCreateArtifactsRequest).
  string request_id = 3;
}

// A request message for BatchCreateArtifactsRequest.
//
// The request will be handled atomically (i.e. all artifacts are
// created or none are).
//
// Use of batching for artifacts uploads is strongly encouraged for
// performance and efficiency reasons. Batches would typically but cut for
// one of the following reasons:
// - 500 test artifacts are in the batch,
// - the size of the batch would exceed 10 MiB (this refers to the size of
//   the field `requests` not the artifact contents), or
// - a given dwell time has elapsed (e.g. 1 minute)
//
// Batched artifacts can cover more than one work unit, provided all
// work units share the same update token (i.e. in case of Prefixed work units;
// see CreateWorkUnitRequest.work_unit_id for details). In such situations,
// the batching for artifacts may be integrated with the batching of work
// units (e.g. creating artifacts in a batch will be preceded by batch-
// creating any pending work units to avoid NotFound errors).
//
// To simplify batching, there is no requirement to create test results before
// the corresponding test result artifacts are uploaded; the only ordering
// requirement is for the work unit to be created before any artifacts are
// uploaded to it.
//
// While this means it is technically possible to create orphaned test result
// artifacts (by uploading the artifacts and never uploading the corresponding
// test results), clients should not rely on being able to read such orphaned
// artifacts later.
message BatchCreateArtifactsRequest {
  // Name of the parent work unit (if all artifacts to be created belong to the
  // same work unit). See WorkUnit.name.
  //
  // If this set, then parent field in CreateArtifactRequest messages
  // must either be empty or match this field.
  //
  // Legacy clients may also use an invocation name, see Invocation.name.
  string parent = 3;

  // Requests to create Artifacts.
  //
  // Up to 500 requests. Total size of all requests (as measured by proto.Size())
  // MUST be <= 10 MiB.
  repeated CreateArtifactRequest requests = 2;

  // A unique identifier for this request. Restricted to 36 ASCII characters.
  // A random UUID is recommended.
  //
  // If the same request is replayed (see "Retry policy" at the top of this doc),
  // this request_id guarantees idempotence.
  //
  // The request_id field in CreateArtifactRequest messages must either
  // be empty or match this field.
  //
  // Required.
  string request_id = 4;
}

message BatchCreateArtifactsResponse {
  // Artifacts created.
  repeated Artifact artifacts = 1;
}
