// Copyright 2023 Huawei Cloud Computing Technology Co., Ltd.
//
// 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 justbuild.just_serve;

import "build/bazel/remote/execution/v2/remote_execution.proto";

// A request message for
// [SourceTree.ServeCommitTree][justbuild.just_serve.SourceTree.ServeCommitTree].
message ServeCommitTreeRequest {
  // The Git commit identifier to be searched on the server.
  string commit = 1;

  // Relative path of requested tree with respect to the commit root.
  string subdir = 2;

  // If set to true and the tree is found, it will be uploaded to the
  // remote-execution endpoint.
  bool sync_tree = 3;
}


// A response message for
// [SourceTree.ServeCommitTree][justbuild.just_serve.SourceTree.ServeCommitTree].
message ServeCommitTreeResponse {
  // The requested Git tree identifier.
  string tree = 1;

  enum ServeCommitTreeStatus {
    // All good.
    OK = 0;

    // Failed to upload tree remotely.
    SYNC_ERROR = 1;

    // Commit not found.
    NOT_FOUND = 2;

    // An internal error occurred during the resolution of the request.
    INTERNAL_ERROR = 3;
  }

  // If the status has a code `OK` or `SYNC_ERROR`, the tree is correct.
  // For any other value, the `tree` field is not set.
  ServeCommitTreeStatus status = 2;

  // The digest of the requested tree, which can be used to retrieve it from
  // the associated remote-execution endpoint CAS, if tree was uploaded to the
  // remote-execution endpoint.
  build.bazel.remote.execution.v2.Digest digest = 3;
}

// A request message for
// [SourceTree.ServeArchiveTree][justbuild.just_serve.SourceTree.ServeArchiveTree].
message ServeArchiveTreeRequest {
  // The Git blob identifier of the archive.
  string content = 1;

  enum ArchiveType {
    TAR = 0;
    ZIP = 1;
  }

  // The type of archive this blob should be treated as.
  ArchiveType archive_type = 2;

  // Relative path of requested tree with respect to the archive root.
  string subdir = 3;

  enum SymlinksResolve {
    // Leave all entries as they are.
    NONE = 0;

    // Ignore all symlinks.
    IGNORE = 1;

    // Resolve only upwards symlinks.
    PARTIAL = 2;

    // Resolve all symlinks.
    COMPLETE = 3;
  }

  // How symlinks inside the archive directory tree should be handled.
  SymlinksResolve resolve_symlinks = 4;

  // If set to true and the tree is found, it will be uploaded to the
  // remote-execution endpoint.
  bool sync_tree = 5;
}

// A response message for
// [SourceTree.ServeArchiveTree][justbuild.just_serve.SourceTree.ServeArchiveTree].
message ServeArchiveTreeResponse {
  // The requested Git tree identifier.
  string tree = 1;

  enum ServeArchiveTreeStatus{
    // All good.
    OK = 0;

    // Failed to upload tree remotely.
    SYNC_ERROR = 1;

    // Failed to unpack as archive of the specified type.
    UNPACK_ERROR = 2;

    // Failed to resolve symlinks as requested.
    RESOLVE_ERROR = 3;

    // Content blob not found.
    NOT_FOUND = 4;

    // An internal error occurred during the resolution of the request.
    INTERNAL_ERROR = 5;
  }

  // If the status has a code `OK` or `SYNC_ERROR`, the tree is correct.
  // For any other value, the `tree` field is not set.
  ServeArchiveTreeStatus status = 2;

  // The digest of the requested tree, which can be used to retrieve it from
  // the associated remote-execution endpoint CAS, if tree was uploaded to the
  // remote-execution endpoint.
  build.bazel.remote.execution.v2.Digest digest = 3;
}

// A request message for
// [SourceTree.ServeDistdirTree][justbuild.just_serve.SourceTree.ServeDistdirTree].
message ServeDistdirTreeRequest {
  // A distfile item.
  message Distfile {
    // The name of the distfile.
    string name = 1;

    // The Git blob identifier of the distfile content.
    string content = 2;

    // Whether the blob should occur executable in the resulting
    // directory.
    bool executable = 3;
  }

  // The list of distfiles.
  repeated Distfile distfiles = 1;

  // If set to true and all distfile blobs are found, the resulting tree
  // and all the content blobs will be uploaded to the remote-execution
  // endpoint.
  bool sync_tree = 2;
}

// A response message for
// [SourceTree.ServeDistdirTree][justbuild.just_serve.SourceTree.ServeDistdirTree].
message ServeDistdirTreeResponse {
  // The requested Git tree identifier.
  string tree = 1;

  enum ServeDistdirTreeStatus{
    // All good
    OK = 0;

    // Failed to upload content blobs remotely
    SYNC_ERROR = 1;

    // At least one content blob is missing
    NOT_FOUND = 2;

    // An internal error occurred during the resolution of the request.
    INTERNAL_ERROR = 3;
  }

  // If the status has a code `OK` or `SYNC_ERROR`, the tree is correct.
  // For any other value, the `tree` field is not set.
  ServeDistdirTreeStatus status = 2;

  // The digest of the requested tree, which can be used to retrieve it from
  // the associated remote-execution endpoint CAS, if tree was uploaded to the
  // remote-execution endpoint.
  build.bazel.remote.execution.v2.Digest digest = 3;
}

// A request message for
// [SourceTree.ServeContent][justbuild.just_serve.SourceTree.ServeContent].
message ServeContentRequest {
  // The content Git blob identifier.
  string content = 1;
}

// A response message for
// [SourceTree.ServeContent][justbuild.just_serve.SourceTree.ServeContent].
message ServeContentResponse {
  enum ServeContentStatus{
    // All good.
    OK = 0;

    // Content blob not known.
    NOT_FOUND = 1;

    // Failed to upload content blob to remote CAS.
    SYNC_ERROR = 2;

    // An internal error occurred during the resolution of the request.
    INTERNAL_ERROR = 3;
  }

  // If the status has a code `OK`, the content blob is in the remote CAS.
  ServeContentStatus status = 1;

  // The digest of the requested blob, which can be used to retrieve it from
  // the associated remote-execution endpoint CAS.
  build.bazel.remote.execution.v2.Digest digest = 2;
}

// A request message for
// [SourceTree.ServeTree][justbuild.just_serve.SourceTree.ServeTree].
message ServeTreeRequest {
  // The Git tree identifier.
  string tree = 1;
}

// A response message for
// [SourceTree.ServeTree][justbuild.just_serve.SourceTree.ServeTree].
message ServeTreeResponse {
  enum ServeTreeStatus{
    // All good.
    OK = 0;

    // Tree not known.
    NOT_FOUND = 1;

    // Failed to upload tree to remote CAS.
    SYNC_ERROR = 2;

    // An internal error occurred during the resolution of the request.
    INTERNAL_ERROR = 3;
  }

  // If the status has a code `OK`, the tree is in the remote CAS.
  ServeTreeStatus status = 1;

  // The digest of the requested tree, which can be used to retrieve it from
  // the associated remote-execution endpoint CAS.
  build.bazel.remote.execution.v2.Digest digest = 2;
}

// A request message for
// [SourceTree.CheckRootTree][justbuild.just_serve.SourceTree.CheckRootTree].
message CheckRootTreeRequest {
  // The Git tree identifier.
  string tree = 1;
}

// A response message for
// [SourceTree.CheckRootTree][justbuild.just_serve.SourceTree.CheckRootTree].
message CheckRootTreeResponse {
  enum CheckRootTreeStatus{
    // All good.
    OK = 0;

    // Tree not known.
    NOT_FOUND = 1;

    // An internal error occurred during the resolution of the request.
    INTERNAL_ERROR = 2;
  }

  // If the status has a code `OK`, the tree is known locally and available
  // in a location where this serve instance can build against.
  CheckRootTreeStatus status = 1;
}

// A request message for
// [SourceTree.GetRemoteTree][justbuild.just_serve.SourceTree.GetRemoteTree].
message GetRemoteTreeRequest {
  reserved 1; // The Git tree identifier in an earlier version of the API.

  // The tree digest, which can be used to retrieve it from the associated
  // remote-execution endpoint.
  build.bazel.remote.execution.v2.Digest digest = 2;
}

// A response message for
// [SourceTree.GetRemoteTree][justbuild.just_serve.SourceTree.GetRemoteTree].
message GetRemoteTreeResponse {
  enum GetRemoteTreeStatus{
    // All good.
    OK = 0;

    // Tree is not found in remote CAS or it failed to be retrieved.
    FAILED_PRECONDITION = 1;

    // An internal error occurred during the resolution of the request.
    INTERNAL_ERROR = 2;
  }

  // If the status has a code `OK`, the tree is available locally in a
  // location where this serve instance can build against.
  GetRemoteTreeStatus status = 1;
}

// A request message for
// [SourceTree.ComputeTreeStructure][justbuild.just_serve.SourceTree.ComputeTreeStructure].
message ComputeTreeStructureRequest {
  // The Git tree identifier of the tree to compute the tree structure for.
  string tree = 1;
}

// A response message for
// [SourceTree.ComputeTreeStructure][justbuild.just_serve.SourceTree.ComputeTreeStructure].
message ComputeTreeStructureResponse {
  enum ComputeTreeStructureStatus{
    // All good.
    OK = 0;
    
    // Tree not known.
    NOT_FOUND = 1;

    // An internal error occurred during the resolution of the request.
    INTERNAL_ERROR = 2;
  }

  // If the status has a code `OK`, the tree structure is available locally in
  // a location where this serve instance can build against.
  ComputeTreeStructureStatus status = 1;

  // The Git tree identifier of the tree structure.
  string tree_structure_hash = 2;
}

// Service for improved interaction with the target-level cache.
service SourceTree {
  // Retrieve the Git-subtree identifier from a given Git commit.
  //
  // There are no method-specific errors.
  rpc ServeCommitTree(ServeCommitTreeRequest) returns (ServeCommitTreeResponse) {}

  // Retrieve the Git-subtree identifier for the tree obtained
  // by unpacking an archive with a given blob identifier.
  //
  // There are no method-specific errors.
  rpc ServeArchiveTree(ServeArchiveTreeRequest) returns (ServeArchiveTreeResponse) {}

  // Compute the Git-tree identifier for the tree containing the content
  // blobs of a list of distfiles. The implementation must only return the
  // tree identifier if ALL content blobs are known.
  //
  // There are no method-specific errors.
  rpc ServeDistdirTree(ServeDistdirTreeRequest) returns (ServeDistdirTreeResponse) {}

  // Make a given content blob available in remote CAS, if blob is known.
  //
  // There are no method-specific errors.
  rpc ServeContent(ServeContentRequest) returns (ServeContentResponse) {}

  // Make a given tree available in remote CAS, if tree is known.
  //
  // There are no method-specific errors.
  rpc ServeTree(ServeTreeRequest) returns (ServeTreeResponse) {}

  // Check if a Git-tree is locally known and, if found, make it available
  // in a location where this serve instance can build against.
  // The implementation should not interrogate the associated remote-execution
  // endpoint at any point during the completion of this request.
  //
  // There are no method-specific errors.
  rpc CheckRootTree(CheckRootTreeRequest) returns (CheckRootTreeResponse) {}

  // Retrieve a given tree from the CAS of the associated remote-execution
  // endpoint and make it available in a location where this serve instance
  // can build against.
  //
  // There are no method-specific errors.
  rpc GetRemoteTree(GetRemoteTreeRequest) returns (GetRemoteTreeResponse) {}

  // Compute the tree structure of the given tree and return the Git tree
  // identifier of the resulting structure.
  //
  // There are no method-specific errors.
  rpc ComputeTreeStructure(ComputeTreeStructureRequest) returns (ComputeTreeStructureResponse) {}
}

// A request message for
// [Target.ServeTarget][justbuild.just_serve.Target.ServeTarget].
message ServeTargetRequest {
  // Digest of the blob containing the target description.
  //
  // The client has to guarantee that the blob has been uploaded to the
  // remote CAS.
  build.bazel.remote.execution.v2.Digest target_cache_key_id = 1;

  // Request that serve keep the artifact stage as an additional root,
  // so that susequent requests to serve a target can use this root.
  bool keep_artifact_root = 4;

  // A single property of the remote-execution environment.
  message Property {
    // Property name.
    string name = 1;

    // Property value.
    string value = 2;
  }

  // The execution properties. The key-value pairs accumulate, with latest
  // value for each key being taken.
  repeated Property execution_properties  = 2;

  // Digest of the blob containing the endpoint configuration.
  //
  // The client has to guarantee that the blob has been uploaded to the
  // remote CAS.
  build.bazel.remote.execution.v2.Digest dispatch_info  = 3;

  // Digests that the serve SHOULD make available before handling the request.
  // Serve MUST attempt to perform execution even if it fails to receive some
  // digests.
  repeated build.bazel.remote.execution.v2.Digest required_digests = 5;
}

// A response message for
// [Target.ServeTarget][justbuild.just_serve.Target.ServeTarget].
message ServeTargetResponse {
  // Digest of the blob with the JSON object containing the target-cache
  // value on success. The implementation must guarantee that all the
  // referenced objects are present in the remote CAS.
  build.bazel.remote.execution.v2.Digest target_value = 1;

  // Digest of the blob containing a log report relevant to the user, e.g.,
  // the error report for a failed analysis or build of a known target.
  // The implementation must guarantee that this blob is present in the
  // remote CAS.
  build.bazel.remote.execution.v2.Digest log = 2;
}

// A request message for
// [Target.ServeTargetVariables][justbuild.just_serve.Target.ServeTargetVariables].
message ServeTargetVariablesRequest {
  // Git identifier of the target-level root tree.
  string root_tree = 1;

  // Relative path of the targets file inside the root tree.
  string target_file = 2;

  // Name of the export target to look up.
  string target = 3;
}

// A response message for
// [Target.ServeTargetVariables][justbuild.just_serve.Target.ServeTargetVariables].
message ServeTargetVariablesResponse {
  // List of flexible configuration variables.
  repeated string flexible_config = 1;
}

// A request message for
// [Target.ServeTargetDescription][justbuild.just_serve.Target.ServeTargetDescription].
message ServeTargetDescriptionRequest {
  // Git identifier of the target-level root tree.
  string root_tree = 1;

  // Relative path of the targets file inside the root tree.
  string target_file = 2;

  // Name of the export target to look up.
  string target = 3;
}

// A response message for
// [Target.ServeTargetDescription][justbuild.just_serve.Target.ServeTargetDescription].
message ServeTargetDescriptionResponse {
  // Digest of the blob containing the desired target description fields.
  // The implementation must guarantee the reference object is available in
  // the remote CAS.
  build.bazel.remote.execution.v2.Digest description_id = 1;
}

// The target-level cache service.
service Target {
  // Given a target-level caching key, returns the computed value. In doing
  // so, it can build on the associated endpoint passing the
  // RemoteExecutionProperties contained in the ServeTargetRequest.
  // The execution backend description, the resulting target cache value,
  // and all other artifacts referenced therein MUST be made available in
  // the CAS of the associated remote-execution endpoint.
  //
  // A failure to analyse or build a known target (i.e., a target for which
  // we have all the needed information available) should NOT be reported as
  // an error. Instead, the failure log should be uploaded as a blob to the
  // CAS of the associated remote-execution endpoint and its digest provided
  // to the client in the response field `log`. In this case, the field
  // `target_value` MUST not be set.
  //
  // If the status has a code different from `OK` or `NOT_FOUND`, the
  // response MUST not be used.
  //
  // Errors:
  // * `NOT_FOUND`: Unknown target or missing needed local information.
  //   This should only be used for non-fatal failures.
  // * `FAILED_PRECONDITION`: Required entries missing in the remote
  //   execution endpoint.
  // * `UNAVAILABLE`: Could not communicate with the remote-execution
  //   endpoint.
  // * `INVALID_ARGUMENT`: The client provided invalid arguments in request.
  // * `INTERNAL`: Internally, something is very broken.
  rpc ServeTarget(ServeTargetRequest) returns (ServeTargetResponse) {}

  // Given the target-level root tree and the name of an export target,
  // returns the list of flexible variables from that target's description.
  //
  // If the status has a code different from `OK`, the response MUST not be
  // used.
  //
  // Errors:
  // * `FAILED_PRECONDITION`: An error occurred in retrieving the
  //   configuration of the requested target, such as missing entries
  //   (target-root, target file, target name), unparsable target file, or
  //   requested target not being of "type" : "export".
  // * `INTERNAL`: Internally, something is very broken.
  rpc ServeTargetVariables(ServeTargetVariablesRequest) returns (ServeTargetVariablesResponse) {}

  // Given the target-level root tree and the name of an export target,
  // returns the digest of the blob containing the flexible variables field,
  // as well as the documentation fields pertaining tho the target and
  // its configuration variables, as taken from the target's description.
  // This information should be enough for a client to produce locally a
  // full description of said target.
  //
  // The server MUST make the returned blob available in the remote CAS.
  //
  // If the status has a code different from `OK`, the response MUST not be
  // used.
  //
  // Errors:
  // * `FAILED_PRECONDITION`: An error occurred in retrieving the
  //   configuration of the requested target, such as missing entries
  //   (target-root, target file, target name), unparsable target file, or
  //   requested target not being of "type" : "export".
  // * `UNAVAILABLE`: Could not communicate with the remote CAS.
  // * `INTERNAL`: Internally, something is very broken.
  rpc ServeTargetDescription(ServeTargetDescriptionRequest) returns (ServeTargetDescriptionResponse) {}
}

// A request message for
// [Configuration.RemoteExecutionEndpoint][justbuild.just_serve.Configuration.RemoteExecutionEndpoint].
message RemoteExecutionEndpointRequest {}

// A response message for
// [Configuration.RemoteExecutionEndpoint][justbuild.just_serve.Configuration.RemoteExecutionEndpoint].
message RemoteExecutionEndpointResponse {
  // The remote endpoint address in HOST:PORT format, if one is set.
  string address = 1;
}

// A request message for
// [Configuration.Compatibility][justbuild.just_serve.Configuration.Compatibility].
message CompatibilityRequest {}

// A response message for
// [Configuration.Compatibility][justbuild.just_serve.Configuration.Compatibility].
message CompatibilityResponse {
  // Standard remote-execution protocol compatibility flag.
  bool compatible = 1;
}

// This service can be used by the client to double-check the server
// configuration.
service Configuration {
  // Returns the address of the associated remote endpoint, if set,
  // or an empty string signaling that the serve endpoint acts also
  // as a remote-execution endpoint.
  //
  // There are no method-specific errors.
  rpc RemoteExecutionEndpoint(RemoteExecutionEndpointRequest) returns (RemoteExecutionEndpointResponse) {}

  // Returns a flag signaling whether the associated remote-execution
  // endpoint uses the standard remote-execution protocol.
  //
  // There are no method-specific errors.
  rpc Compatibility(CompatibilityRequest) returns (CompatibilityResponse) {}
}
