/* Copyright 2019 Google LLC

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

    https://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 = "proto2";

package ml_metadata;

import "google/protobuf/field_mask.proto";
import "ml_metadata/proto/metadata_store.proto";

// An artifact and type pair. Part of an artifact struct.
message ArtifactAndType {
  optional Artifact artifact = 1;
  optional ArtifactType type = 2;
}

// A dictionary of artifact structs. Can represent a dictionary.
message ArtifactStructMap {
  // An artifact struct that is a dictionary.
  // Can be represented as a JSON dictionary of artifact structs.
  map<string, ArtifactStruct> properties = 1;
}

// An artifact struct that is a list.
message ArtifactStructList {
  // Can be represented as a JSON list of artifact structs.
  repeated ArtifactStruct elements = 1;
}

// An artifact struct represents the input or output of an Execution.
// See the more specific types referenced in the message for more details.
message ArtifactStruct {
  // Note: an artifact struct may be empty to indicate "None" or null.
  oneof value {
    ArtifactAndType artifact = 1;
    ArtifactStructMap map = 2;
    ArtifactStructList list = 3;
  }
}

message PutArtifactsRequest {
  repeated Artifact artifacts = 1;

  message Options {
    // When there are multiple writers to update an existing node to
    // different states, there may be a race and the end result of the
    // concurrent update is nondeterministic. If the field is set, then an
    // optimistic concurrency control (OCC) scheme is used during update:
    // it compares the `artifact`.`last_update_time_since_epoch` in the request
    // with the stored `last_update_time_since_epoch` having the same
    // `artifact`.`id`. If they are different, the request fails, and the user
    // can read the stored node and retry node update.
    // When the option is set, the timestamp after update is guaranteed to be
    // increased and different from the input artifact.
    // When set the option, the caller should set it for all concurrent writers.
    optional bool abort_if_latest_updated_time_changed = 1;
  }

  // Additional options to change the behavior of the method.
  optional Options options = 2;

  // Options regarding transactions.
  optional TransactionOptions transaction_options = 3;

  // FieldMask for artifacts in the PUT update
  // If `artifact.id` is not specified, it means a new artifact will be created
  // and `update_mask` will not be applied to the creation.
  // If `update_mask` is empty, update the artifacts as a whole.
  // If `update_mask` is not empty, only update fields or properties specified
  // in `update_mask`.
  // Example request protos:
  // 1. Examples that update `properties` / `custom_properties`:
  //   1.1 Add a <'key', 'val'> pair into `custom_properties`:
  //      {
  //        artifacts {
  //          id: 1234
  //          type_id: 5678
  //          custom_properties {
  //            key: "key"
  //            value: {
  //              string_value: "val"
  //            }
  //          }
  //        }
  //        update_mask {
  //          paths: "custom_properties.key"
  //        }
  //      }
  //   1.2 Set `custom_properties['key'].bool_value` to true:
  //      {
  //        artifacts {
  //          id: 1234
  //          type_id: 5678
  //          custom_properties {
  //            key: "key"
  //            value: {
  //              bool_value: true
  //            }
  //          }
  //        }
  //        update_mask {
  //          paths: "custom_properties.key"
  //        }
  //      }
  //   1.3 Delete the complete <'key', 'val'> pair from `custom_properties`:
  //      {
  //        artifacts {
  //          id: 1234
  //          type_id: 5678
  //          custom_properties {}
  //        }
  //        update_mask {
  //          paths: "custom_properties.key"
  //        }
  //      }
  // 2. Examples that update fields such as `uri`, `external_id`, etc:
  //   2.1 Update `external_id` field:
  //      {
  //        artifacts {
  //          id: 1234
  //          type_id: 5678
  //          external_id: "new_value"
  //        }
  //        update_mask {
  //          paths: "external_id"
  //        }
  //      }
  //   2.2 Set `uri` field:
  //      {
  //        artifacts {
  //          id: 1234
  //          type_id: 5678
  //          uri: "set_value"
  //        }
  //        update_mask {
  //          paths: "uri"
  //        }
  //      }
  // If `paths: "properties"` or `paths: "custom_properties"` are added to
  // `update_mask`, the key-level updates will be ignored and we only perform
  // field-level updates on the all `properties`/`custom_properties`.
  // For example:
  //   If the mask is: {"properties", "properties.key1"}, the field path
  //   "properties.key1" will be ignored and all `properties` will be updated.
  //   (Do not suggest)
  //   If the mask is {"properties", "external_id"}, all
  //   `properties` and field `external_id` will be updated. (Do not suggest)
  optional google.protobuf.FieldMask update_mask = 4;
}

message PutArtifactsResponse {
  // A list of artifact ids index-aligned with PutArtifactsRequest.
  repeated int64 artifact_ids = 1;
}

message PutArtifactTypeRequest {
  // The field is required in any request. Stored types in MLMD can be updated
  // by introducing new properties and remain backward compatible. If a type
  // with the same name exists in the database, it updates the existing type,
  // otherwise it creates a new type.
  optional ArtifactType artifact_type = 1;

  // If true then allows adding properties to an existing stored type.
  // If false, then type update is not allowed and it raises AlreadyExists
  // error if the given type has any new property that is not defined in the
  // stored type.
  optional bool can_add_fields = 2;

  // If true then allows omitting properties of an existing stored type.
  // If false, then no properties of the stored type can be omitted in the
  // given type, otherwise it raises AlreadyExists error.
  optional bool can_omit_fields = 5;

  // Deprecated fields.
  optional bool can_delete_fields = 3 [deprecated = true];
  optional bool all_fields_match = 4 [default = true, deprecated = true];

  // Options regarding transactions.
  optional TransactionOptions transaction_options = 6;
}

message PutArtifactTypeResponse {
  // The type ID of the artifact type.
  optional int64 type_id = 1;
}

message PutExecutionsRequest {
  repeated Execution executions = 1;

  // Options regarding transactions.
  optional TransactionOptions transaction_options = 2;

  // FieldMask for executions in the PUT update
  // If `execution.id` is not specified, it means a new execution will be
  // created and `update_mask` will not be applied to the creation.
  // If `update_mask` is empty, update the executions as a whole.
  // If `update_mask` is not empty, only update fields or properties specified
  // in `update_mask`.
  // Example request protos:
  //   1. Add a <'key', 'val'> pair into `custom_properties`:
  //      {
  //        executions {
  //          id: 1234
  //          type_id: 5678
  //          custom_properties {
  //            key: "key"
  //            value: {
  //              string_value: "val"
  //            }
  //          }
  //        }
  //        update_mask {
  //          paths: "custom_properties.key"
  //        }
  //      }
  //   2. Set `last_known_state` field:
  //      {
  //        executions {
  //          id: 1234
  //          type_id: 5678
  //          last_known_state: CACHED
  //        }
  //        update_mask {
  //          paths: "last_known_state"
  //        }
  //      }
  // Please refer to `PutArtifactsRequest` for more details.
  optional google.protobuf.FieldMask update_mask = 3;
}

message PutExecutionsResponse {
  // A list of execution ids index-aligned with PutExecutionsRequest.
  repeated int64 execution_ids = 1;
}

message PutExecutionTypeRequest {
  // The field is required in any request. Stored types in MLMD can be updated
  // by introducing new properties and remain backward compatible. If a type
  // with the same name exists in the database, it updates the existing type,
  // otherwise it creates a new type.
  optional ExecutionType execution_type = 1;

  // If true then allows adding properties to an existing stored type.
  // If false, then type update is not allowed and it raises AlreadyExists
  // error if the given type has any new property that is not defined in the
  // stored type.
  optional bool can_add_fields = 2;

  // If true then allows omitting properties of an existing stored type.
  // If false, then no properties of the stored type can be omitted in the
  // given type, otherwise it raises AlreadyExists error.
  optional bool can_omit_fields = 5;

  // Deprecated fields.
  optional bool can_delete_fields = 3 [deprecated = true];
  optional bool all_fields_match = 4 [default = true, deprecated = true];

  // Options regarding transactions.
  optional TransactionOptions transaction_options = 6;
}

message PutExecutionTypeResponse {
  // The type ID of the execution type.
  optional int64 type_id = 1;
}

message PutEventsRequest {
  repeated Event events = 1;

  // Options regarding transactions.
  optional TransactionOptions transaction_options = 2;
}

message PutEventsResponse {}

message PutExecutionRequest {
  // A pair of an artifact and an event used or generated by an execution, e.g.,
  // during the execution run, it uses none or many artifacts as input, and
  // generate none or many artifacts as output.
  message ArtifactAndEvent {
    // The pair may have an artifact. If present and no artifact.id is given,
    // then it inserts the artifact, otherwise it updates the artifact.
    optional Artifact artifact = 1;
    // The pair may have an event. Providing event.artifact_id or
    // event.execution_id is optional. If the ids are given, it must align with
    // the `artifact`.id / `execution`.id respectively. If artifact is not
    // given and event.artifact_id is set, it must exist in the backend.
    optional Event event = 2;
  }

  message Options {
    // When there's a race to publish executions with a new context with the
    // same context.name, by default there'll be one writer succeeds and
    // the rest of the writers returning AlreadyExists errors. If set to true,
    // the API will reuse the stored context in the transaction and perform
    // an update.
    optional bool reuse_context_if_already_exist = 1;

    // When there's a race to publish executions with a new artifact with the
    // same artifact.external_id, by default there'll be one writer succeeds and
    // the rest of the writers returning AlreadyExists errors.
    // If set to true and an Artifact has non-empty external_id,
    // the API will reuse the stored artifact in the transaction and
    // perform an update. Otherwise, it will fall back to relying on `id` field
    // to decide if it's update (if `id` exists) or insert (if `id` is empty).
    optional bool reuse_artifact_if_already_exist_by_external_id = 2;
  }
  // The execution that produces many artifact and event pairs.
  optional Execution execution = 1;
  // The list of artifact and event pairs.
  repeated ArtifactAndEvent artifact_event_pairs = 2;
  // A list of contexts associated with the execution and artifacts. For each
  // given context without a context.id, it inserts the context, otherwise it
  // updates the stored context with the same id.
  // Associations between each pair of contexts and the execution, and
  // attributions between each pair of contexts and artifacts are created if
  // they do not already exist.
  repeated Context contexts = 3;
  // Additional options to change the behavior of the method.
  optional Options options = 4;

  // Options regarding transactions.
  optional TransactionOptions transaction_options = 5;
}

message PutExecutionResponse {
  // An execution id of the `execution` in PutExecutionRequest.
  optional int64 execution_id = 1;
  // A list of artifact ids index-aligned with `artifact_event_pairs` in the
  // PutExecutionRequest.
  repeated int64 artifact_ids = 2;
  // A list of context ids index-aligned with `contexts` in the
  // PutExecutionRequest.
  repeated int64 context_ids = 3;
}

message PutLineageSubgraphRequest {
  repeated Execution executions = 1;
  repeated Artifact artifacts = 2;
  repeated Context contexts = 3;

  message EventEdge {
    // Index in the array of executions.
    optional int32 execution_index = 1;
    // Index in the array of artifacts.
    optional int32 artifact_index = 2;
    optional Event event = 3;
  }
  repeated EventEdge event_edges = 4;

  message Options {
    // When there's a race to publish executions with a new context with the
    // same context.name, by default there'll be one writer succeeds and
    // the rest of the writers returning AlreadyExists errors. If set to true,
    // the API will reuse the stored context in the transaction and perform
    // an update.
    optional bool reuse_context_if_already_exist = 1;

    // When there's a race to publish executions with a new artifact with the
    // same artifact.external_id, by default there'll be one writer succeeds and
    // the rest of the writers returning AlreadyExists errors.
    // If set to true and an Artifact has non-empty external_id,
    // the API will reuse the stored artifact in the transaction and
    // perform an update. Otherwise, it will fall back to relying on `id` field
    // to decide if it's update (if `id` exists) or insert (if `id` is empty).
    optional bool reuse_artifact_if_already_exist_by_external_id = 2;
  }
  optional Options options = 5;

  optional TransactionOptions transaction_options = 6;
}

message PutLineageSubgraphResponse {
  // A list of execution ids index-aligned with `executions` in the request
  repeated int64 execution_ids = 1 [packed = true];
  // A list of artifact ids index-aligned with `artifacts` in the request
  repeated int64 artifact_ids = 2 [packed = true];
  // A list of context ids index-aligned with `contexts` in the request
  repeated int64 context_ids = 3 [packed = true];
}

message PutTypesRequest {
  repeated ArtifactType artifact_types = 1;
  repeated ExecutionType execution_types = 2;
  repeated ContextType context_types = 3;

  // If true then allows adding properties to an existing stored type.
  // If false, then type update is not allowed and it raises AlreadyExists
  // error if the given type has any new property that is not defined in the
  // stored type.
  optional bool can_add_fields = 4;

  // If true then allows omitting properties of an existing stored type.
  // If false, then no properties of the stored type can be omitted in the
  // given type, otherwise it raises AlreadyExists error.
  optional bool can_omit_fields = 7;

  // Deprecated fields.
  optional bool can_delete_fields = 5 [deprecated = true];
  optional bool all_fields_match = 6 [default = true, deprecated = true];

  // Options regarding transactions.
  optional TransactionOptions transaction_options = 8;
}

message PutTypesResponse {
  // The type ids of the artifact type.
  repeated int64 artifact_type_ids = 1;
  // The type ids of the execution type.
  repeated int64 execution_type_ids = 2;
  // The type ids of the context type.
  repeated int64 context_type_ids = 3;
}

message PutContextTypeRequest {
  // The field is required in any request. Stored types in MLMD can be updated
  // by introducing new properties and remain backward compatible. If a type
  // with the same name exists in the database, it updates the existing type,
  // otherwise it creates a new type.
  optional ContextType context_type = 1;

  // If true then allows adding properties to an existing stored type.
  // If false, then type update is not allowed and it raises AlreadyExists
  // error if the given type has any new property that is not defined in the
  // stored type.
  optional bool can_add_fields = 2;

  // If true then allows omitting properties of an existing stored type.
  // If false, then no properties of the stored type can be omitted in the
  // given type, otherwise it raises AlreadyExists error.
  optional bool can_omit_fields = 5;

  // Deprecated fields.
  optional bool can_delete_fields = 3 [deprecated = true];
  optional bool all_fields_match = 4 [default = true, deprecated = true];

  // Options regarding transactions.
  optional TransactionOptions transaction_options = 6;
}

message PutContextTypeResponse {
  // The type ID of the context type.
  optional int64 type_id = 1;
}

message PutContextsRequest {
  repeated Context contexts = 1;

  // Options regarding transactions.
  optional TransactionOptions transaction_options = 2;

  // FieldMask for contexts in the PUT update
  // If `context.id` is not specified, it means a new context will be
  // created and `update_mask` will not be applied to the creation.
  // If `update_mask` is empty, update the contexts as a whole.
  // If `update_mask` is not empty, only update fields or properties specified
  // in `update_mask`.
  // Example request protos:
  //   1. Add a <'key', 'val'> pair into `custom_properties`:
  //      {
  //        contexts {
  //          id: 1234
  //          type_id: 5678
  //          custom_properties {
  //            key: "key"
  //            value: {
  //              string_value: "val"
  //            }
  //          }
  //        }
  //        update_mask {
  //          paths: "custom_properties.key"
  //        }
  //      }
  //   2. Set `name` field:
  //      {
  //        contexts {
  //          id: 1234
  //          type_id: 5678
  //          name: "set_name"
  //        }
  //        update_mask {
  //          paths: "name"
  //        }
  //      }
  // Please refer to `PutArtifactsRequest` for more details.
  optional google.protobuf.FieldMask update_mask = 3;
}

message PutContextsResponse {
  // A list of context ids index-aligned with PutContextsRequest.
  repeated int64 context_ids = 1;
}

message PutAttributionsAndAssociationsRequest {
  repeated Attribution attributions = 1;
  repeated Association associations = 2;

  // Options regarding transactions.
  optional TransactionOptions transaction_options = 3;
}

message PutAttributionsAndAssociationsResponse {}

message PutParentContextsRequest {
  repeated ParentContext parent_contexts = 1;

  // Options regarding transactions.
  optional TransactionOptions transaction_options = 2;
}

message PutParentContextsResponse {}

message GetArtifactsByTypeRequest {
  optional string type_name = 1;
  // If not set, it looks for the type with type_name with default type_version.
  optional string type_version = 2;
  // Specify List options.
  // Currently supports:
  //   1. Field to order the results.
  //   2. Page size.
  // If set, the request will
  //   first fetch all artifacts with specified `type_name` and `type_version`,
  //   then order by a specifield field
  //   finally find the correct page and return #Artifacts of the page size.
  // Higher-level APIs may only use the functionalies partially.
  // Please reference the API documentation for the API behaviors.
  optional ListOperationOptions options = 3;
  // Options regarding transactions.
  optional TransactionOptions transaction_options = 4;
}

message GetArtifactsByTypeResponse {
  repeated Artifact artifacts = 1;
  // Token to use to retrieve next page of results if list options are used in
  // the request.
  optional string next_page_token = 2;
}

message GetArtifactByTypeAndNameRequest {
  optional string type_name = 1;
  // If not set, it looks for the type with type_name and artifact_name with
  // default type_version.
  optional string type_version = 3;
  optional string artifact_name = 2;
  // Options regarding transactions.
  optional TransactionOptions transaction_options = 4;
}

message GetArtifactByTypeAndNameResponse {
  optional Artifact artifact = 1;
}

message GetArtifactsByIDRequest {
  // A list of artifact ids to retrieve.
  repeated int64 artifact_ids = 1;
  // An option to populate all the ArtifactTypes in the response.
  // If true, returns retrieved Artifacts and their artifact types, which can be
  // matched by type_ids.
  // If false, returns only the retrieved Artifacts.
  // Example request proto:
  //   {
  //     artifact_ids: 101,
  //     populate_artifact_types: true,
  //   }
  //   The response will contain an artifact with id = 101 and an artifact type
  //   with id = artifact.type_id().
  optional bool populate_artifact_types = 3 [default = false];
  // Options regarding transactions.
  optional TransactionOptions transaction_options = 2;
}

message GetArtifactsByIDResponse {
  // Artifacts with matching ids.
  // This is not index-aligned: if an id is not found, it is not returned.
  repeated Artifact artifacts = 1;
  // ArtifactTypes populated with matching type_ids owned by `artifacts`.
  // This is not index-aligned: if a type_id is not found, it is not returned.
  repeated ArtifactType artifact_types = 2;
}

// Request to retrieve Artifacts using List options.
// If option is not specified then all Artifacts are returned.
message GetArtifactsRequest {
  // Specify options.
  // Please refer to the documentation of ListOperationOptions for the supported
  // functionalities.
  optional ListOperationOptions options = 1;
  // Options regarding transactions.
  optional TransactionOptions transaction_options = 2;
}

message GetArtifactsResponse {
  // Returned artifacts.
  repeated Artifact artifacts = 1;

  // Token to use to retrieve next page of results if list options are used in
  // the request.
  optional string next_page_token = 2;
}

message GetArtifactsByURIRequest {
  // A list of artifact uris to retrieve.
  repeated string uris = 2;
  // Options regarding transactions.
  optional TransactionOptions transaction_options = 3;

  reserved 1;
}

message GetArtifactsByURIResponse {
  repeated Artifact artifacts = 1;
}

// Request to retrieve Executions using List options.
// If option is not specified then all Executions are returned.
message GetExecutionsRequest {
  // Specify options.
  // Please refer to the documentation of ListOperationOptions for the supported
  // functionalities.
  optional ListOperationOptions options = 1;
  // Options regarding transactions.
  optional TransactionOptions transaction_options = 2;
}

message GetExecutionsResponse {
  // Returned executions.
  repeated Execution executions = 1;

  // Token to use to retrieve next page of results if list options are used in
  // the request.
  optional string next_page_token = 2;
}

message GetArtifactTypeRequest {
  optional string type_name = 1;
  // If not set, it looks for the type with type_name with default type_version.
  optional string type_version = 2;
  // Options regarding transactions.
  optional TransactionOptions transaction_options = 3;
}

message GetArtifactTypeResponse {
  // Gets an artifact type, or clear if it does not exist.
  optional ArtifactType artifact_type = 1;
}

message GetArtifactTypesRequest {
  // Options regarding transactions.
  optional TransactionOptions transaction_options = 1;
}

message GetArtifactTypesResponse {
  repeated ArtifactType artifact_types = 1;
}

message GetExecutionTypesRequest {
  // Options regarding transactions.
  optional TransactionOptions transaction_options = 1;
}

message GetExecutionTypesResponse {
  repeated ExecutionType execution_types = 1;
}

message GetContextTypesRequest {
  // Options regarding transactions.
  optional TransactionOptions transaction_options = 1;
}

message GetContextTypesResponse {
  repeated ContextType context_types = 1;
}

message GetArtifactsByExternalIdsRequest {
  repeated string external_ids = 1;
  // Options regarding transactions.
  optional TransactionOptions transaction_options = 2;
}

message GetArtifactsByExternalIdsResponse {
  repeated Artifact artifacts = 1;
}

message GetExecutionsByExternalIdsRequest {
  repeated string external_ids = 1;
  // Options regarding transactions.
  optional TransactionOptions transaction_options = 2;
}

message GetExecutionsByExternalIdsResponse {
  repeated Execution executions = 1;
}

message GetContextsByExternalIdsRequest {
  repeated string external_ids = 1;
  // Options regarding transactions.
  optional TransactionOptions transaction_options = 2;
}

message GetContextsByExternalIdsResponse {
  repeated Context contexts = 1;
}

message GetArtifactTypesByExternalIdsRequest {
  repeated string external_ids = 1;
  // Options regarding transactions.
  optional TransactionOptions transaction_options = 2;
}

message GetArtifactTypesByExternalIdsResponse {
  repeated ArtifactType artifact_types = 1;
}

message GetExecutionTypesByExternalIdsRequest {
  repeated string external_ids = 1;
  // Options regarding transactions.
  optional TransactionOptions transaction_options = 2;
}

message GetExecutionTypesByExternalIdsResponse {
  repeated ExecutionType execution_types = 1;
}

message GetContextTypesByExternalIdsRequest {
  repeated string external_ids = 1;
  // Options regarding transactions.
  optional TransactionOptions transaction_options = 2;
}

message GetContextTypesByExternalIdsResponse {
  repeated ContextType context_types = 1;
}

message GetExecutionsByTypeRequest {
  optional string type_name = 1;
  // If not set, it looks for the type with type_name with default type_version.
  optional string type_version = 2;
  // Specify List options.
  // Currently supports:
  //   1. Field to order the results.
  //   2. Page size.
  // If set, the request will
  //   first fetch all executions with specified `type_name` and `type_version`,
  //   then order by a specifield field
  //   finally find the correct page and return #Executions of the page size.
  // Higher-level APIs may only use the functionalies partially.
  // Please reference the API documentation for the API behaviors.
  optional ListOperationOptions options = 3;
  // Options regarding transactions.
  optional TransactionOptions transaction_options = 4;
}

message GetExecutionsByTypeResponse {
  repeated Execution executions = 1;
  // Token to use to retrieve next page of results if list options are used in
  // the request.
  optional string next_page_token = 2;
}

message GetExecutionByTypeAndNameRequest {
  optional string type_name = 1;
  // If not set, it looks for the type with type_name and execution_name with
  // default type_version.
  optional string type_version = 3;
  optional string execution_name = 2;
  // Options regarding transactions.
  optional TransactionOptions transaction_options = 4;
}

message GetExecutionByTypeAndNameResponse {
  optional Execution execution = 1;
}

message GetExecutionsByIDRequest {
  // A list of execution ids to retrieve.
  repeated int64 execution_ids = 1;
  // Options regarding transactions.
  optional TransactionOptions transaction_options = 2;
}

message GetExecutionsByIDResponse {
  // The result is not index-aligned: if an id is not found, it is not
  // returned.
  repeated Execution executions = 1;
}

message GetExecutionTypeRequest {
  optional string type_name = 1;
  // If not set, it looks for the type with type_name with default type_version.
  optional string type_version = 2;
  // Options regarding transactions.
  optional TransactionOptions transaction_options = 3;
}

message GetExecutionTypeResponse {
  // Gets an execution type, or clear if it does not exist.
  optional ExecutionType execution_type = 1;
}


// Gets all events with matching execution ids.
message GetEventsByExecutionIDsRequest {
  repeated int64 execution_ids = 1;
  // Options regarding transactions.
  optional TransactionOptions transaction_options = 2;
}

message GetEventsByExecutionIDsResponse {
  repeated Event events = 1;
}

message GetEventsByArtifactIDsRequest {
  repeated int64 artifact_ids = 1;
  // Options regarding transactions.
  optional TransactionOptions transaction_options = 2;
}

message GetEventsByArtifactIDsResponse {
  repeated Event events = 1;
}

message GetArtifactTypesByIDRequest {
  repeated int64 type_ids = 1;
  // Options regarding transactions.
  optional TransactionOptions transaction_options = 2;
}

message GetArtifactTypesByIDResponse {
  // The result is not index-aligned: if an id is not found, it is not
  // returned.
  repeated ArtifactType artifact_types = 1;
}

message GetExecutionTypesByIDRequest {
  repeated int64 type_ids = 1;
  // Options regarding transactions.
  optional TransactionOptions transaction_options = 2;
}

message GetExecutionTypesByIDResponse {
  // The result is not index-aligned: if an id is not found, it is not
  // returned.
  repeated ExecutionType execution_types = 1;
}

message GetContextTypeRequest {
  optional string type_name = 1;
  // If not set, it looks for the type with type_name with default type_version.
  optional string type_version = 2;
  // Options regarding transactions.
  optional TransactionOptions transaction_options = 3;
}

message GetContextTypeResponse {
  // Gets a context type, or clear if it does not exist.
  optional ContextType context_type = 1;
}

message GetContextTypesByIDRequest {
  repeated int64 type_ids = 1;
  // Options regarding transactions.
  optional TransactionOptions transaction_options = 2;
}

message GetContextTypesByIDResponse {
  // The result is not index-aligned: if an id is not found, it is not
  // returned.
  repeated ContextType context_types = 1;
}

// Request to retrieve Contexts using List options.
// If option is not specified then all Contexts are returned.
message GetContextsRequest {
  // Specify options.
  // Please refer to the documentation of ListOperationOptions for the supported
  // functionalities.
  optional ListOperationOptions options = 1;
  // Options regarding transactions.
  optional TransactionOptions transaction_options = 2;
}

message GetContextsResponse {
  // Returned contexts.
  repeated Context contexts = 1;

  // Token to use to retrieve next page of results if list options are used in
  // the request.
  optional string next_page_token = 2;
}

message GetContextsByTypeRequest {
  optional string type_name = 1;
  // Specify options.
  // Currently supports:
  //   1. Field to order the results.
  //   2. Page size.
  // If set, the request will
  //   first fetch all contexts with specified `type_name` and `type_version`,
  //   then order by a specifield field
  //   finally find the correct page and return #Contexts of the page size.
  // Higher-level APIs may only use the functionalies partially.
  // Please reference the API documentation for the API behaviors.
  optional ListOperationOptions options = 2;
  // If not set, it looks for the type with type_name and options with default
  // type_version.
  optional string type_version = 3;
  // Options regarding transactions.
  optional TransactionOptions transaction_options = 4;
}

message GetContextsByTypeResponse {
  repeated Context contexts = 1;
  // Token to use to retrieve next page of results if list options are used in
  // the request.
  optional string next_page_token = 2;
}

message GetContextByTypeAndNameRequest {
  optional string type_name = 1;
  // If not set, it looks for the type with type_name and context_name with
  // default type_version.
  optional string type_version = 3;
  optional string context_name = 2;
  // Options regarding transactions.
  optional TransactionOptions transaction_options = 4;
}

message GetContextByTypeAndNameResponse {
  optional Context context = 1;
}

message GetContextsByIDRequest {
  // A list of context ids to retrieve.
  repeated int64 context_ids = 1;
  // Options regarding transactions.
  optional TransactionOptions transaction_options = 2;
}

message GetContextsByIDResponse {
  // The result is not index-aligned: if an id is not found, it is not
  // returned.
  repeated Context contexts = 1;
}

message GetContextsByArtifactRequest {
  optional int64 artifact_id = 1;
  // Options regarding transactions.
  optional TransactionOptions transaction_options = 2;
}

message GetContextsByArtifactResponse {
  repeated Context contexts = 1;
}

message GetContextsByExecutionRequest {
  optional int64 execution_id = 1;
  // Options regarding transactions.
  optional TransactionOptions transaction_options = 2;
}

message GetContextsByExecutionResponse {
  repeated Context contexts = 1;
}

message GetParentContextsByContextRequest {
  optional int64 context_id = 1;
  // Options regarding transactions.
  optional TransactionOptions transaction_options = 2;
}

message GetParentContextsByContextResponse {
  repeated Context contexts = 1;
}

message GetChildrenContextsByContextRequest {
  optional int64 context_id = 1;
  // Options regarding transactions.
  optional TransactionOptions transaction_options = 2;
}

message GetChildrenContextsByContextResponse {
  repeated Context contexts = 1;
}

message GetParentContextsByContextsRequest {
  repeated int64 context_ids = 1 [packed = true];
  // Options regarding transactions.
  optional TransactionOptions transaction_options = 2;
}

message GetParentContextsByContextsResponse {
  message ParentContextsPerChild {
    repeated Context parent_contexts = 1;
  }
  map<int64, ParentContextsPerChild> contexts = 2;
}

message GetChildrenContextsByContextsRequest {
  repeated int64 context_ids = 1 [packed = true];
  // Options regarding transactions.
  optional TransactionOptions transaction_options = 2;
}

message GetChildrenContextsByContextsResponse {
  message ChildrenContextsPerParent {
    repeated Context children_contexts = 1;
  }
  map<int64, ChildrenContextsPerParent> contexts = 2;
}

message GetArtifactsByContextRequest {
  optional int64 context_id = 1;

  // Specify List options.
  // Currently supports:
  //   1. Field to order the results.
  //   2. Page size.
  optional ListOperationOptions options = 2;
  // Options regarding transactions.
  optional TransactionOptions transaction_options = 3;
}

message GetArtifactsByContextResponse {
  repeated Artifact artifacts = 1;

  // Token to use to retrieve next page of results if list options are used in
  // the request.
  optional string next_page_token = 2;
}

message GetExecutionsByContextRequest {
  optional int64 context_id = 1;

  // Specify List options.
  // Currently supports:
  //   1. Field to order the results.
  //   2. Page size.
  optional ListOperationOptions options = 2;
  // Options regarding transactions.
  optional TransactionOptions transaction_options = 3;
}

message GetExecutionsByContextResponse {
  repeated Execution executions = 1;

  // Token to use to retrieve next page of results if list options are used in
  // the request.
  optional string next_page_token = 2;
  // Options regarding transactions.
  optional TransactionOptions transaction_options = 3;
}


// TODO(b/283852485): Deprecate GetLineageGraph API after migration to
// GetLineageSubgraph API.
// A lineage query request to specify the query nodes of interest and the
// boundary conditions for pruning the returned graph.
message GetLineageGraphRequest {
  optional LineageGraphQueryOptions options = 1;
  // Options regarding transactions.
  optional TransactionOptions transaction_options = 2;
}

// A connected lineage `subgraph` about the MLMD nodes derived from
// LineageGraphRequest.query_conditions.
message GetLineageGraphResponse {
  optional LineageGraph subgraph = 1;
}

message GetLineageSubgraphRequest {
  // Query options for lineage graph tracing from a list of interested
  // nodes.
  // A lineage subgraph without node details (e.g., external_id, properties)
  // will be returned. Please refer to `LineageSubgraphQueryOptions` for more
  // details.
  optional LineageSubgraphQueryOptions lineage_subgraph_query_options = 1;
  // `read_mask` contains user specified paths of fields that should be included
  // in the returned lineage subgraph.
  //   Supported field paths are: 'artifacts', 'executions', 'contexts',
  //   'artifact_types', 'execution_types', 'context_types', and 'events'.
  //   TODO(b/283852485): Include 'associations' or 'attributions' in the
  //     returned graph.
  //   If 'artifacts', 'executions', or 'contexts' is specified in `read_mask`,
  //     the dehydrated nodes will be included.
  //     Note: A dehydrated node means a node containing only its id and no
  //     other information. User should call GetNodesByID or other APIs to get
  //     node details later on.
  //   If 'artifact_types', 'execution_types', or 'context_types' is specified
  //     in `read_mask`, all the node types will be included.
  //   If 'events' is specified in `read_mask`, the events will be included.
  //   If `read_mask` is not set, the API will return all the fields in
  //     the returned graph.
  //   Note: Only paths of fields in LineageGraph message are supported. Paths
  //   of fields in the submessage, such as "artifacts.id", "contexts.name" are
  //   not acknowledged.
  optional google.protobuf.FieldMask read_mask = 3;
  optional TransactionOptions transaction_options = 2;
}

message GetLineageSubgraphResponse {
  // A lineage subgraph of MLMD nodes and relations retrieved from lineage
  // graph tracing.
  optional LineageGraph lineage_subgraph = 1;
}



// LINT.IfChange
service MetadataStoreService {
  // Inserts or updates an ArtifactType.
  //
  // A type has a set of strong typed properties describing the schema of any
  // stored instance associated with that type. A type is identified by a name
  // and an optional version.
  //
  // Type Creation:
  // If no type exists in the database with the given identifier
  // (name, version), it creates a new type and returns the type_id.
  //
  // Type Evolution:
  // If the request type with the same (name, version) already exists
  // (let's call it stored_type), the method enforces the stored_type can be
  // updated only when the request type is backward compatible for the already
  // stored instances.
  //
  // Backwards compatibility is violated iff:
  //
  //   a) there is a property where the request type and stored_type have
  //      different value type (e.g., int vs. string)
  //   b) `can_add_fields = false` and the request type has a new property that
  //      is not stored.
  //   c) `can_omit_fields = false` and stored_type has an existing property
  //      that is not provided in the request type.
  //
  // If non-backward type change is required in the application, e.g.,
  // deprecate properties, re-purpose property name, change value types,
  // a new type can be created with a different (name, version) identifier.
  // Note the type version is optional, and a version value with empty string
  // is treated as unset.
  //
  // Args:
  //   artifact_type: the type to be inserted or updated.
  //   can_add_fields:
  //     when set to true, new properties can be added;
  //     when set to false, returns ALREADY_EXISTS if the request type has
  //     properties that are not in stored_type.
  //   can_omit_fields:
  //     when set to true, stored properties can be omitted in the request type;
  //     when set to false, returns ALREADY_EXISTS if the stored_type has
  //     properties not in the request type.
  //
  // Returns:
  //   The type_id of the stored type.
  //
  // Raises:
  //   ALREADY_EXISTS error in the case listed above.
  //   INVALID_ARGUMENT error, if the given type has no name, or any
  //     property value type is unknown.
  rpc PutArtifactType(PutArtifactTypeRequest)
      returns (PutArtifactTypeResponse) {}

  // Inserts or updates an ExecutionType. Please refer to PutArtifactType for
  // type upsert API description.
  rpc PutExecutionType(PutExecutionTypeRequest)
      returns (PutExecutionTypeResponse) {}

  // Inserts or updates an ContextType. Please refer to PutArtifactType for
  // type upsert API description.
  rpc PutContextType(PutContextTypeRequest) returns (PutContextTypeResponse) {}

  // Bulk inserts types atomically.
  rpc PutTypes(PutTypesRequest) returns (PutTypesResponse) {}

  // Inserts or updates artifacts in the database.
  //
  // If an artifact_id is specified for an artifact, it is an update.
  // If an artifact_id is unspecified, it will insert a new artifact.
  // For new artifacts, type must be specified.
  // For old artifacts, type must be unchanged or unspecified.
  //
  // It is not guaranteed that the created or updated artifacts will share the
  // same `create_time_since_epoch` or `last_update_time_since_epoch`
  // timestamps.
  //
  // Args:
  //   artifacts: A list of artifacts to insert or update.
  //
  // Returns:
  //   A list of artifact ids index-aligned with the input.
  rpc PutArtifacts(PutArtifactsRequest) returns (PutArtifactsResponse) {}

  // Inserts or updates executions in the database.
  //
  // If an execution_id is specified for an execution, it is an update.
  // If an execution_id is unspecified, it will insert a new execution.
  // For new executions, type must be specified.
  // For old executions, type must be unchanged or unspecified.
  //
  // It is not guaranteed that the created or updated executions will share the
  // same `create_time_since_epoch` or `last_update_time_since_epoch`
  // timestamps.
  //
  // Args:
  //   executions: A list of executions to insert or update.
  //
  // Returns:
  //   A list of execution ids index-aligned with the input.
  //
  rpc PutExecutions(PutExecutionsRequest) returns (PutExecutionsResponse) {}

  // Inserts events in the database.
  //
  // The execution_id and artifact_id must already exist.
  // Once created, events cannot be modified.
  // AlreadyExists error will be raised if duplicated events are found.
  //
  // It is not guaranteed that the created or updated events will share the
  // same `milliseconds_since_epoch` timestamps.
  //
  // Args:
  //   events: A list of events to insert or update.
  rpc PutEvents(PutEventsRequest) returns (PutEventsResponse) {}

  // Inserts or updates an Execution and its input and output artifacts and
  // related contexts atomically. The `artifact_event_pairs` include the state
  // changes of the Artifacts used or generated by the Execution, as well as the
  // input/output Event. The `contexts` describe the associations of the
  // execution and the attributions of the artifacts.
  //
  // If an execution_id is specified, it is an update on the corresponding
  // execution, otherwise it does an insertion.
  // For insertion, type must be specified. Same rule applies to artifacts
  // and contexts in the request. Corresponding errors may raised. For example:
  // AlreadyExists error will be raised if duplicated executions, artifacts
  // or events are found.
  //
  // It is not guaranteed that the created or updated executions, artifacts,
  // contexts and events will share the same `create_time_since_epoch`,
  // `last_update_time_since_epoch`, or `milliseconds_since_epoch` timestamps.
  //
  // Args:
  //   execution: An execution to insert or update.
  //   artifact_event_pairs: Artifacts to insert or update and events to insert.
  //   contexts: The contexts that the execution and the artifacts belong to.
  //
  // Returns:
  //   An execution id and a list of artifacts and contexts ids index-aligned
  //   with the input.
  rpc PutExecution(PutExecutionRequest) returns (PutExecutionResponse) {}

  // Inserts or updates a lineage subgraph (i.e. a collection of event edges
  // and its executions, artifacts, and related contexts) atomically. The
  // `event_edges` include an Event and the indices of the corresponding
  // execution and artifact from the input list of executions and artifacts. The
  // `contexts` describe the associations of the Execution and the attributions
  // of the Artifact.
  //
  // If an execution_id is specified, it is an update on the corresponding
  // Execution, otherwise it does an insertion. For insertion, type must be
  // specified. These rules apply to Artifacts and Contexts as well.
  // Corresponding errors may be raised. For example: AlreadyExists error will
  // be raised if duplicated executions, artifacts, or events are found.
  //
  // It is not guaranteed that the created or updated executions, artifacts,
  // contexts and events will share the same `create_time_since_epoch`,
  // `last_update_time_since_epoch`, or `milliseconds_since_epoch` timestamps.
  //
  // Args:
  //   executions: A list of executions to insert or update.
  //   artifacts: A list of artifacts to insert or update.
  //   contexts: A list of contexts to insert and/or create associations and
  //       attributions with.
  //   event_edges: A list of events to insert with the indices of the
  //       corresponding execution and artifact from the input lists of
  //       executions and artifacts.
  //
  // Returns:
  //   Lists of execution, artifact, and context ids index-aligned with the
  //   inputs.
  rpc PutLineageSubgraph(PutLineageSubgraphRequest)
      returns (PutLineageSubgraphResponse) {}

  // Inserts or updates contexts in database and returns a list of context ids.
  //
  // If an context_id is specified for a context, it is an update.
  // If an context_id is unspecified, it will insert a new context.
  // For new contexts, type must be specified.
  // For old contexts, type must be unchanged or unspecified.
  //
  // It is not guaranteed that the created or updated contexts will share the
  // same `create_time_since_epoch` or `last_update_time_since_epoch`
  // timestamps.
  //
  // Args:
  //   contexts: A list of contexts to insert or update.
  //
  // Returns:
  //   A list of context ids index-aligned with the input.
  rpc PutContexts(PutContextsRequest) returns (PutContextsResponse) {}

  // Inserts attribution and association relationships in the database.
  // The context_id, artifact_id, and execution_id must already exist.
  // If the relationship exists, this call does nothing. Once added, the
  // relationships cannot be modified.
  //
  // Args:
  //   attributions: A list of attributions to insert.
  //   associations: A list of associations to insert.
  rpc PutAttributionsAndAssociations(PutAttributionsAndAssociationsRequest)
      returns (PutAttributionsAndAssociationsResponse) {}

  // Inserts parental context relationships in the database.
  // The ParentContext relationship has direction. The call fails if cycles are
  // detected.
  //
  // Args:
  //   parent_contexts: A list of parent contexts to insert.
  rpc PutParentContexts(PutParentContextsRequest)
      returns (PutParentContextsResponse) {}

  // Gets an artifact type. Returns a NOT_FOUND error if the type does not
  // exist.
  rpc GetArtifactType(GetArtifactTypeRequest)
      returns (GetArtifactTypeResponse) {}

  // Gets a list of artifact types by ID.
  // If no artifact types with an ID exists, the artifact type is skipped.
  rpc GetArtifactTypesByID(GetArtifactTypesByIDRequest)
      returns (GetArtifactTypesByIDResponse) {}

  // Gets a list of all artifact types.
  rpc GetArtifactTypes(GetArtifactTypesRequest)
      returns (GetArtifactTypesResponse) {}

  // Gets an execution type, or None if it does not exist.
  rpc GetExecutionType(GetExecutionTypeRequest)
      returns (GetExecutionTypeResponse) {}

  // Gets a list of execution types by ID.
  // If no execution types with an ID exists, the execution type is skipped.
  rpc GetExecutionTypesByID(GetExecutionTypesByIDRequest)
      returns (GetExecutionTypesByIDResponse) {}

  // Gets a list of all execution types.
  rpc GetExecutionTypes(GetExecutionTypesRequest)
      returns (GetExecutionTypesResponse) {}

  // Gets a context type. Returns a NOT_FOUND error if the type does not exist.
  rpc GetContextType(GetContextTypeRequest) returns (GetContextTypeResponse) {}

  // Gets a list of context types by ID.
  // If no context types with an ID exists, the context type is skipped.
  rpc GetContextTypesByID(GetContextTypesByIDRequest)
      returns (GetContextTypesByIDResponse) {}

  // Gets a list of all context types.
  rpc GetContextTypes(GetContextTypesRequest)
      returns (GetContextTypesResponse) {}

  // Gets all the artifacts.
  rpc GetArtifacts(GetArtifactsRequest) returns (GetArtifactsResponse) {}

  // Gets all the executions.
  rpc GetExecutions(GetExecutionsRequest) returns (GetExecutionsResponse) {}

  // Gets all the contexts.
  rpc GetContexts(GetContextsRequest) returns (GetContextsResponse) {}

  // Gets all artifacts with matching ids.
  //
  // The result is not index-aligned: if an id is not found, it is not returned.
  //
  // Args:
  //   artifact_ids: A list of artifact ids to retrieve.
  //
  // Returns:
  //   Artifacts with matching ids.
  rpc GetArtifactsByID(GetArtifactsByIDRequest)
      returns (GetArtifactsByIDResponse) {}

  // Gets all executions with matching ids.
  //
  // The result is not index-aligned: if an id is not found, it is not returned.
  //
  // Args:
  //   execution_ids: A list of execution ids to retrieve.
  rpc GetExecutionsByID(GetExecutionsByIDRequest)
      returns (GetExecutionsByIDResponse) {}

  // Gets all contexts with matching ids.
  //
  // The result is not index-aligned: if an id is not found, it is not returned.
  //
  // Args:
  //   context_ids: A list of context ids to retrieve.
  rpc GetContextsByID(GetContextsByIDRequest)
      returns (GetContextsByIDResponse) {}

  // Gets all the artifacts of a given type.
  rpc GetArtifactsByType(GetArtifactsByTypeRequest)
      returns (GetArtifactsByTypeResponse) {}

  // Gets all the executions of a given type.
  rpc GetExecutionsByType(GetExecutionsByTypeRequest)
      returns (GetExecutionsByTypeResponse) {}

  // Gets all the contexts of a given type.
  rpc GetContextsByType(GetContextsByTypeRequest)
      returns (GetContextsByTypeResponse) {}

  // Gets the artifact of the given type and artifact name.
  rpc GetArtifactByTypeAndName(GetArtifactByTypeAndNameRequest)
      returns (GetArtifactByTypeAndNameResponse) {}

  // Gets the execution of the given type and execution name.
  rpc GetExecutionByTypeAndName(GetExecutionByTypeAndNameRequest)
      returns (GetExecutionByTypeAndNameResponse) {}

  // Gets the context of the given type and context name.
  rpc GetContextByTypeAndName(GetContextByTypeAndNameRequest)
      returns (GetContextByTypeAndNameResponse) {}

  // Gets all the artifacts with matching uris.
  rpc GetArtifactsByURI(GetArtifactsByURIRequest)
      returns (GetArtifactsByURIResponse) {}

  // Gets all events with matching execution ids.
  rpc GetEventsByExecutionIDs(GetEventsByExecutionIDsRequest)
      returns (GetEventsByExecutionIDsResponse) {}

  // Gets all events with matching artifact ids.
  rpc GetEventsByArtifactIDs(GetEventsByArtifactIDsRequest)
      returns (GetEventsByArtifactIDsResponse) {}

  // Gets all the artifacts with matching external ids.
  rpc GetArtifactsByExternalIds(GetArtifactsByExternalIdsRequest)
      returns (GetArtifactsByExternalIdsResponse) {}

  // Gets all the artifacts with matching external ids.
  rpc GetExecutionsByExternalIds(GetExecutionsByExternalIdsRequest)
      returns (GetExecutionsByExternalIdsResponse) {}

  // Gets all the artifacts with matching external ids.
  rpc GetContextsByExternalIds(GetContextsByExternalIdsRequest)
      returns (GetContextsByExternalIdsResponse) {}

  // Gets all the artifacts with matching external ids.
  rpc GetArtifactTypesByExternalIds(GetArtifactTypesByExternalIdsRequest)
      returns (GetArtifactTypesByExternalIdsResponse) {}

  // Gets all the artifacts with matching external ids.
  rpc GetExecutionTypesByExternalIds(GetExecutionTypesByExternalIdsRequest)
      returns (GetExecutionTypesByExternalIdsResponse) {}

  // Gets all the artifacts with matching external ids.
  rpc GetContextTypesByExternalIds(GetContextTypesByExternalIdsRequest)
      returns (GetContextTypesByExternalIdsResponse) {}


  // Gets all context that an artifact is attributed to.
  rpc GetContextsByArtifact(GetContextsByArtifactRequest)
      returns (GetContextsByArtifactResponse) {}

  // Gets all context that an execution is associated with.
  rpc GetContextsByExecution(GetContextsByExecutionRequest)
      returns (GetContextsByExecutionResponse) {}

  // Gets all parent contexts that a context is related.
  rpc GetParentContextsByContext(GetParentContextsByContextRequest)
      returns (GetParentContextsByContextResponse) {}

  // Gets all children contexts that a context is related.
  rpc GetChildrenContextsByContext(GetChildrenContextsByContextRequest)
      returns (GetChildrenContextsByContextResponse) {}

  // Batch getting all the parent contexts that a list of contexts are related.
  rpc GetParentContextsByContexts(GetParentContextsByContextsRequest)
      returns (GetParentContextsByContextsResponse) {}

  // Batch getting all the children contexts that a list of contexts are
  // related.
  rpc GetChildrenContextsByContexts(GetChildrenContextsByContextsRequest)
      returns (GetChildrenContextsByContextsResponse) {}

  // Gets all direct artifacts that a context attributes to.
  rpc GetArtifactsByContext(GetArtifactsByContextRequest)
      returns (GetArtifactsByContextResponse) {}

  // Gets all direct executions that a context associates with.
  rpc GetExecutionsByContext(GetExecutionsByContextRequest)
      returns (GetExecutionsByContextResponse) {}


  // TODO(b/283852485): Deprecate GetLineageGraph API after migration to
  // GetLineageSubgraph API.
  // The transaction performs a constrained transitive closure and returns a
  // lineage subgraph satisfying the conditions and constraints specified in
  // the GetLineageGraphRequest.
  rpc GetLineageGraph(GetLineageGraphRequest)
      returns (GetLineageGraphResponse) {}

  // Gets a lineage subgraph by performing graph traversal from a list of
  // interested nodes.
  // A lineage subgraph without node details (e.g., external_id, properties)
  // will be returned.
  rpc GetLineageSubgraph(GetLineageSubgraphRequest)
      returns (GetLineageSubgraphResponse) {}


}
// LINT.ThenChange(../metadata_store/metadata_store_service_interface.h)
