syntax = "proto3"; package memos.api.v1; import "google/api/annotations.proto"; import "google/api/client.proto"; import "google/api/field_behavior.proto"; import "google/api/resource.proto"; import "google/protobuf/empty.proto"; import "google/protobuf/field_mask.proto"; import "google/protobuf/timestamp.proto"; option go_package = "gen/api/v1"; service AttachmentService { // CreateAttachment creates a new attachment. rpc CreateAttachment(CreateAttachmentRequest) returns (Attachment) { option (google.api.http) = { post: "/api/v1/attachments" body: "attachment" }; option (google.api.method_signature) = "attachment"; } // UploadAttachment uploads a file in bounded chunks. The first call carries // the spec and returns an upload_id; later calls carry that upload_id. // Uploads are bound to the authenticated user, expire after 30 minutes of // inactivity, and do not survive a server restart. rpc UploadAttachment(UploadAttachmentRequest) returns (UploadAttachmentResponse) { option (google.api.http) = { post: "/api/v1/attachments:upload" body: "*" }; } // ListAttachments lists all attachments. rpc ListAttachments(ListAttachmentsRequest) returns (ListAttachmentsResponse) { option (google.api.http) = {get: "/api/v1/attachments"}; } // GetAttachment returns an attachment by name. rpc GetAttachment(GetAttachmentRequest) returns (Attachment) { option (google.api.http) = {get: "/api/v1/{name=attachments/*}"}; option (google.api.method_signature) = "name"; } // UpdateAttachment updates an attachment. rpc UpdateAttachment(UpdateAttachmentRequest) returns (Attachment) { option (google.api.http) = { patch: "/api/v1/{attachment.name=attachments/*}" body: "attachment" }; option (google.api.method_signature) = "attachment,update_mask"; } // DeleteAttachment deletes an attachment by name. rpc DeleteAttachment(DeleteAttachmentRequest) returns (google.protobuf.Empty) { option (google.api.http) = {delete: "/api/v1/{name=attachments/*}"}; option (google.api.method_signature) = "name"; } // BatchDeleteAttachments deletes multiple attachments in one request. rpc BatchDeleteAttachments(BatchDeleteAttachmentsRequest) returns (google.protobuf.Empty) { option (google.api.http) = { post: "/api/v1/attachments:batchDelete" body: "*" }; } } enum MotionMediaFamily { MOTION_MEDIA_FAMILY_UNSPECIFIED = 0; APPLE_LIVE_PHOTO = 1; ANDROID_MOTION_PHOTO = 2; } enum MotionMediaRole { MOTION_MEDIA_ROLE_UNSPECIFIED = 0; STILL = 1; VIDEO = 2; CONTAINER = 3; } message MotionMedia { MotionMediaFamily family = 1; MotionMediaRole role = 2; string group_id = 3; int64 presentation_timestamp_us = 4; bool has_embedded_video = 5; } // MediaMetadata contains normalized metadata explicitly supplied by a client. // The server validates and stores this data but does not extract it from the media file. message MediaMetadata { // Optional. Display-oriented width in pixels. optional int32 width = 1; // Optional. Display-oriented height in pixels. optional int32 height = 2; oneof details { PhotoMetadata photo = 3; VideoMetadata video = 4; } } message PhotoMetadata { // Optional. Capture time as recorded by the source media. MediaCaptureTime capture_time = 1; // Optional. Geographic location recorded by the source media. MediaLocation location = 2; // Optional. EXIF orientation value from 1 through 8 as recorded by the source file. // This value is informational and must not be reapplied to the stored attachment; // width and height already describe its display-oriented dimensions. optional int32 source_exif_orientation = 3; string camera_make = 4; string camera_model = 5; string lens_model = 6; optional double f_number = 7; optional double exposure_time_seconds = 8; optional int32 iso = 9; optional double focal_length_mm = 10; } message MediaCaptureTime { // Local date and time without a time zone, formatted as YYYY-MM-DDTHH:mm:ss[.fraction]. string local_date_time = 1; // Optional. UTC offset formatted as Z or +/-HH:MM. optional string utc_offset = 2; } message MediaLocation { // Optional. WGS84 latitude in decimal degrees. Must be provided with longitude. optional double latitude = 1; // Optional. WGS84 longitude in decimal degrees. Must be provided with latitude. optional double longitude = 2; // Optional. Signed altitude in meters relative to sea level. optional double altitude_meters = 3; } message VideoMetadata { optional double duration_seconds = 1; } message Attachment { option (google.api.resource) = { type: "memos.api.v1/Attachment" pattern: "attachments/{attachment}" singular: "attachment" plural: "attachments" }; // The name of the attachment. // Format: attachments/{attachment} string name = 1 [(google.api.field_behavior) = IDENTIFIER]; // Output only. The creation timestamp. google.protobuf.Timestamp create_time = 2 [(google.api.field_behavior) = OUTPUT_ONLY]; // The filename of the attachment. string filename = 3 [(google.api.field_behavior) = REQUIRED]; // Input only. The content of the attachment. bytes content = 4 [(google.api.field_behavior) = INPUT_ONLY]; // Optional. The external link of the attachment. string external_link = 5 [(google.api.field_behavior) = OPTIONAL]; // The MIME type of the attachment. string type = 6 [(google.api.field_behavior) = REQUIRED]; // Output only. The size of the attachment in bytes. int64 size = 7 [(google.api.field_behavior) = OUTPUT_ONLY]; // Optional. The related memo. Refer to `Memo.name`. // Format: memos/{memo} optional string memo = 8 [(google.api.field_behavior) = OPTIONAL]; // Optional. Motion media metadata. MotionMedia motion_media = 9 [(google.api.field_behavior) = OPTIONAL]; // Optional. Immutable normalized media metadata explicitly supplied by the client at creation time. MediaMetadata media_metadata = 10 [ (google.api.field_behavior) = OPTIONAL, (google.api.field_behavior) = IMMUTABLE ]; } message CreateAttachmentRequest { // Required. The attachment to create. Attachment attachment = 1 [(google.api.field_behavior) = REQUIRED]; // Optional. The attachment ID to use for this attachment. // If empty, a unique ID will be generated. // Format: ^[a-zA-Z0-9]([a-zA-Z0-9-]{0,34}[a-zA-Z0-9])?$ string attachment_id = 2 [(google.api.field_behavior) = OPTIONAL]; } message UploadAttachmentRequest { // Required. Start a new upload or continue an existing one. oneof upload { // Starts a new upload. The same call may also carry data and finish_write. UploadAttachmentSpec spec = 1; // Continues the upload identified by a previous response. string upload_id = 2; } // Required. Zero-based byte offset at which data is written. Must equal the // committed size, except when retrying the most recently accepted chunk // with identical bytes and offset, which is accepted without writing again. int64 write_offset = 3 [(google.api.field_behavior) = REQUIRED]; // Optional. File bytes, at most max_chunk_size long. With no data and // finish_write false, the call reports progress without writing, and // write_offset is ignored. bytes data = 4 [(google.api.field_behavior) = OPTIONAL]; // Optional. Finalize the upload after writing data. The committed size must // then equal total_size. Any later call for the same upload_id returns the // created attachment. bool finish_write = 5 [(google.api.field_behavior) = OPTIONAL]; } message UploadAttachmentSpec { // Required. Metadata for the attachment to create. content must be empty; // file bytes are sent in data. Attachment attachment = 1 [(google.api.field_behavior) = REQUIRED]; // Optional. The attachment ID to use for this attachment. // If empty, a unique ID will be generated. // Format: ^[a-zA-Z0-9]([a-zA-Z0-9-]{0,34}[a-zA-Z0-9])?$ string attachment_id = 2 [(google.api.field_behavior) = OPTIONAL]; // Optional. Total size of the file in bytes before media processing. // Zero represents an empty file. int64 total_size = 3 [(google.api.field_behavior) = OPTIONAL]; } message UploadAttachmentResponse { // Opaque ID for subsequent calls. This is not a resource name. string upload_id = 1; // Number of file bytes committed so far. int64 committed_size = 2; // Set once the upload has been finalized. Attachment attachment = 3; // Maximum number of data bytes accepted in one call. int32 max_chunk_size = 4; } message ListAttachmentsRequest { // Optional. The maximum number of attachments to return. // The service may return fewer than this value. // If unspecified, at most 50 attachments will be returned. // The maximum value is 1000; values above 1000 will be coerced to 1000. int32 page_size = 1 [(google.api.field_behavior) = OPTIONAL]; // Optional. A page token, received from a previous `ListAttachments` call. // Provide this to retrieve the subsequent page. string page_token = 2 [(google.api.field_behavior) = OPTIONAL]; // Optional. Filter to apply to the list results. // Example: "mime_type==\"image/png\"" or "filename.contains(\"test\")" // Supported operators: =, !=, <, <=, >, >=, : (contains), in // Supported fields: filename, mime_type, create_time, memo_id, space. // `space` only supports a non-negated `==` comparison with a Space resource // name or null. // `space` is the linked memo's space resource name, or null when the // attachment is unlinked or its linked memo has no space. string filter = 3 [(google.api.field_behavior) = OPTIONAL]; // Optional. The order to sort results by. // Example: "create_time desc" or "filename asc" string order_by = 4 [(google.api.field_behavior) = OPTIONAL]; reserved 5, 6; reserved "space", "unassigned"; } message ListAttachmentsResponse { // The list of attachments. repeated Attachment attachments = 1; // A token that can be sent as `page_token` to retrieve the next page. // If this field is omitted, there are no subsequent pages. string next_page_token = 2; } message GetAttachmentRequest { // Required. The attachment name of the attachment to retrieve. // Format: attachments/{attachment} string name = 1 [ (google.api.field_behavior) = REQUIRED, (google.api.resource_reference) = {type: "memos.api.v1/Attachment"} ]; } message UpdateAttachmentRequest { // Required. The attachment which replaces the attachment on the server. Attachment attachment = 1 [(google.api.field_behavior) = REQUIRED]; // Required. The list of fields to update. google.protobuf.FieldMask update_mask = 2 [(google.api.field_behavior) = REQUIRED]; } message DeleteAttachmentRequest { // Required. The attachment name of the attachment to delete. // Format: attachments/{attachment} string name = 1 [ (google.api.field_behavior) = REQUIRED, (google.api.resource_reference) = {type: "memos.api.v1/Attachment"} ]; } message BatchDeleteAttachmentsRequest { repeated string names = 1 [(google.api.field_behavior) = REQUIRED]; }