syntax = "proto3";

package nominal.registry.v1;

import "buf/validate/validate.proto";
import "google/api/annotations.proto";
import "google/protobuf/timestamp.proto";
import "nominal/gen/v1/alias.proto";

option java_multiple_files = true;
option java_package = "io.nominal.registry.v1";

enum ContainerImageStatus {
  CONTAINER_IMAGE_STATUS_UNSPECIFIED = 0;
  CONTAINER_IMAGE_STATUS_PENDING = 1;
  CONTAINER_IMAGE_STATUS_READY = 2;
  CONTAINER_IMAGE_STATUS_FAILED = 3;
}

message ContainerImage {
  string rid = 1 [
    (buf.validate.field).required = true,
    (nominal.gen.v1.alias) = {java_name: "io.nominal.api.rids.ContainerImageRid"}
  ];
  string name = 2;
  string tag = 3;
  optional int64 size_bytes = 4;
  ContainerImageStatus status = 5;
  google.protobuf.Timestamp created_at = 6;
}

message CreateImageRequest {
  string workspace_rid = 1 [
    (buf.validate.field).required = true,
    (nominal.gen.v1.alias) = {java_name: "io.nominal.api.rids.WorkspaceRid"}
  ];
  string name = 2 [(buf.validate.field).required = true];
  string tag = 3 [(buf.validate.field).required = true];
  // Object storage key returned by the existing multipart upload flow (UploadService).
  string object_key = 4 [(buf.validate.field).required = true];
}

message CreateImageResponse {
  ContainerImage image = 1;
}

message GetImageRequest {
  string rid = 1 [
    (buf.validate.field).required = true,
    (nominal.gen.v1.alias) = {java_name: "io.nominal.api.rids.ContainerImageRid"}
  ];
  string workspace_rid = 2 [
    (buf.validate.field).required = true,
    (nominal.gen.v1.alias) = {java_name: "io.nominal.api.rids.WorkspaceRid"}
  ];
}

message GetImageResponse {
  ContainerImage image = 1;
}

message DeleteImageRequest {
  string rid = 1 [
    (buf.validate.field).required = true,
    (nominal.gen.v1.alias) = {java_name: "io.nominal.api.rids.ContainerImageRid"}
  ];
  string workspace_rid = 2 [
    (buf.validate.field).required = true,
    (nominal.gen.v1.alias) = {java_name: "io.nominal.api.rids.WorkspaceRid"}
  ];
}

message DeleteImageResponse {}

message NameFilter {
  string name = 1 [(buf.validate.field).required = true];
}

message TagFilter {
  string tag = 1 [(buf.validate.field).required = true];
}

message StatusFilter {
  ContainerImageStatus status = 1 [(buf.validate.field).required = true];
}

message AndFilter {
  repeated SearchFilter clauses = 1;
}

message SearchFilter {
  oneof filter {
    option (buf.validate.oneof) = {required: true};

    NameFilter name = 1;
    TagFilter tag = 2;
    StatusFilter status = 3;
    AndFilter and = 4;
  }
}

message SearchImagesRequest {
  string workspace_rid = 1 [
    (buf.validate.field).required = true,
    (nominal.gen.v1.alias) = {java_name: "io.nominal.api.rids.WorkspaceRid"}
  ];
  // Optional composable filter. Omit to list all images in the workspace.
  optional SearchFilter filter = 2;
  optional int32 page_size = 3 [
    (buf.validate.field).int32.gte = 1,
    (buf.validate.field).int32.lte = 1000
  ];
  optional string next_page_token = 4 [(nominal.gen.v1.alias) = {java_name: "io.nominal.api.Token"}];
}

message SearchImagesResponse {
  repeated ContainerImage images = 1;
  optional string next_page_token = 2 [(nominal.gen.v1.alias) = {java_name: "io.nominal.api.Token"}];
}

// Manages container images in the internal registry.
service RegistryService {
  // Create a container image from a previously uploaded tarball, triggering the push to the
  // internal registry. The client should first upload the tarball via the existing UploadService
  // multipart flow, then call this endpoint with the object key to create the image record and
  // start the push.
  rpc CreateImage(CreateImageRequest) returns (CreateImageResponse) {
    option (google.api.http) = {
      post: "/registry/v1/images"
      body: "*"
    };
  }

  // Get the current status of a container image.
  rpc GetImage(GetImageRequest) returns (GetImageResponse) {
    option (google.api.http) = {get: "/registry/v1/images/{rid}"};
  }

  // Delete a container image.
  rpc DeleteImage(DeleteImageRequest) returns (DeleteImageResponse) {
    option (google.api.http) = {delete: "/registry/v1/images/{rid}"};
  }

  // Search for container images within a workspace, with optional filters and cursor-based pagination.
  rpc SearchImages(SearchImagesRequest) returns (SearchImagesResponse) {
    option (google.api.http) = {
      post: "/registry/v1/images/search"
      body: "*"
    };
  }
}
