Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
syntax = "proto3";

package flipcash.chat.v1;

option go_package = "github.com/code-payments/flipcash2-protobuf-api/generated/go/chat/v1;chatpb";
option java_package = "com.codeinc.flipcash.gen.chat.v1";
option objc_class_prefix = "FPBChatV1";

import "chat/v1/model.proto";
import "common/v1/common.proto";
import "validate/validate.proto";

service Chat {
// GetChat returns the metadata for a specific chat
rpc GetChat(GetChatRequest) returns (GetChatResponse);

// GetDmChatFeed gets the set of DM chats for an owner account using
// a paged API, ordered by last activity with the most recent first.
//
// Chats are ordered by a mutable key (last_activity), so pagination alone
// cannot guarantee a complete read: a chat can receive new activity and
// move into a region the client has already paged past. To get the full
// list, the client MUST combine this RPC with the event stream:
//
// 1. Open the event stream to receive ChatUpdate and begin buffering updates
// BEFORE the first GetDmChatFeed call. This ordering is the contract that
// closes the gap; subscribing after pagination starts can drop chats.
// 2. Page through GetDmChatFeed to exhaustion (until has_more is false),
// always echoing back the paging token returned by the prior response.
// All pages are served against a single snapshot pinned by that token,
// so the set is read consistently.
// 3. Merge the buffered and ongoing stream updates onto the paginated
// set. Any chat whose activity changed after the snapshot watermark
// is delivered via the stream rather than via pagination.
//
// Read together, pagination guarantees the set (every chat exactly once)
// and the stream guarantees freshness and ordering. The local last_activity
// sort is maintained by the client from the stream after the initial read.
rpc GetDmChatFeed(GetDmChatFeedRequest) returns (GetDmChatFeedResponse);
}

message GetChatRequest {
common.v1.ChatId chat_id = 1;

common.v1.Auth auth = 10;
}

message GetChatResponse {
Result result = 1;
enum Result {
OK = 0;
DENIED = 1;
NOT_FOUND = 2;
}

Metadata metadata = 2;
}

message GetDmChatFeedRequest {
// QueryOptions controls page_size. Ordering is fixed to most-recent
// activity first and is not client-selectable.
//
// Leave query_options.paging_token unset on the first request: the server
// mints a token that pins a new snapshot and returns it in the response. On
// every subsequent request, set query_options.paging_token to the
// paging_token from the most recent response to advance within the same
// snapshot. The token is opaque and server-generated; do not construct it.
common.v1.QueryOptions query_options = 1;

common.v1.Auth auth = 10 [(validate.rules).message.required = true];
}

message GetDmChatFeedResponse {
Result result = 1;
enum Result {
OK = 0;
DENIED = 1;
NOT_FOUND = 2;
}

repeated Metadata chats = 2 [(validate.rules).repeated = {
min_items: 0
max_items: 100
}];

// PagingToken is the server-generated token for this paginated read. On the
// first response it pins a new snapshot; on later responses it carries the
// advanced cursor over (last_activity, chat_id). The client MUST send the
// most recent value back in query_options.paging_token on the next
// GetDmChatFeedRequest. Set when result is OK.
common.v1.PagingToken paging_token = 3;

// HasMore indicates whether further pages remain in this snapshot. When
// false, the paginated set has been fully read; the complete chat list is
// this set reconciled with the event stream (see GetDmChatFeed). When true, the
// client should issue another GetDmChatFeedRequest with the returned
// paging_token.
bool has_more = 4;
}
71 changes: 71 additions & 0 deletions definitions/flipcash/protos/src/main/proto/chat/v1/model.proto
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
syntax = "proto3";

package flipcash.chat.v1;

option go_package = "github.com/code-payments/flipcash2-protobuf-api/generated/go/chat/v1;chatpb";
option java_package = "com.codeinc.flipcash.gen.chat.v1";
option objc_class_prefix = "FPBChatV1";

import "common/v1/common.proto";
import "profile/v1/model.proto";
import "messaging/v1/model.proto";
import "validate/validate.proto";
import "google/protobuf/timestamp.proto";

message Metadata {
common.v1.ChatId chat_id = 1 [(validate.rules).message.required = true];

// The type of chat
ChatType type = 2 [(validate.rules).enum = {
not_in: [0] // UNKNOWN
}];
enum ChatType {
UNKNOWN = 0;
DM = 1;
}

// Members of this chat
repeated Member members = 3;

// The last message in this chat
messaging.v1.Message last_message = 4;

// The timestamp of the last activity in this chat
google.protobuf.Timestamp last_activity = 5 [(validate.rules).timestamp.required = true];
}

message Member {
common.v1.UserId user_id = 1 [(validate.rules).message.required = true];

// The user profile for this member. It contains a subset of identifiers
// that can be publicly viewed within the chat.
profile.v1.UserProfile user_profile = 2 [(validate.rules).message.required = true];

// Chat message state for this member.
//
// If set, the list may contain DELIVERED and READ pointers. SENT pointers
// are only shared between the sender and server, to indicate persistence.
repeated messaging.v1.Pointer pointers = 3 [(validate.rules).repeated = {
min_items: 0
max_items: 2
}];
}

message MetadataUpdate {
oneof kind {
option (validate.required) = true;

FullRefresh full_refresh = 1;
LastActivityChanged last_activity_changed = 2;
}

// Refreshes the entire chat metadata
message FullRefresh {
Metadata metadata = 1 [(validate.rules).message.required = true];
}

// The last activity timestamp has changed to a newer value
message LastActivityChanged {
google.protobuf.Timestamp new_last_activity = 1 [(validate.rules).timestamp.required = true];
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,13 @@ message UserId {
}];
}

message ChatId {
bytes value = 1 [(validate.rules).bytes = {
min_len: 32
max_len: 32
}];
}

// AppInstallId is a unque ID tied to a client app installation. It does not
// identify a device. Value should remain private and not be shared across
// installs.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import "validate/validate.proto";

option go_package = "github.com/code-payments/flipcash2-protobuf-api/generated/go/contact/v1;contactpb";
option java_package = "com.codeinc.flipcash.gen.contact.v1";
option objc_class_prefix = "FPBContactV1";

// ContactList manages a user's contact list and surfaces which contacts are
// Flipcash users.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import "validate/validate.proto";

option go_package = "github.com/code-payments/flipcash2-protobuf-api/generated/go/contact/v1;contactpb";
option java_package = "com.codeinc.flipcash.gen.contact.v1";
option objc_class_prefix = "FPBContactV1";

message FlipcashContact {
phone.v1.PhoneNumber phone = 1 [(validate.rules).message.required = true];
Expand Down
24 changes: 23 additions & 1 deletion definitions/flipcash/protos/src/main/proto/event/v1/model.proto
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,9 @@ option go_package = "github.com/code-payments/flipcash2-protobuf-api/generated/g
option java_package = "com.codeinc.flipcash.gen.events.v1";
option objc_class_prefix = "FPBEventV1";

import "chat/v1/model.proto";
import "common/v1/common.proto";
import "messaging/v1/model.proto";
import "google/protobuf/duration.proto";
import "google/protobuf/timestamp.proto";
import "validate/validate.proto";
Expand All @@ -27,7 +29,8 @@ message Event {
oneof type {
option (validate.required) = true;

TestEvent test = 3;
TestEvent test = 3;
ChatUpdate chat_update = 4;
}
}

Expand Down Expand Up @@ -71,3 +74,22 @@ message ClientPong {
// of potential network latency
google.protobuf.Timestamp timestamp = 1 [(validate.rules).timestamp.required = true];
}

message ChatUpdate {
// The chat that this update is for
common.v1.ChatId chat = 1 [(validate.rules).message.required = true];

// If present, new real-time messages sent on the chat
messaging.v1.MessageBatch new_messages = 2;

// If present, message pointer updates for members in the chat
messaging.v1.PointerBatch pointer_updates = 3;

// If present, message typing notification state changes for members in the chat
messaging.v1.IsTypingNotificationBatch is_typing_notifications = 4;

// If present, updates to the chat metadata
repeated chat.v1.MetadataUpdate metadata_updates = 5 [(validate.rules).repeated = {
max_items: 1024 // Arbitrary
}];
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,140 @@
syntax = "proto3";

package flipcash.messaging.v1;

option go_package = "github.com/code-payments/flipcash2-protobuf-api/generated/go/messaging/v1;messagingpb";
option java_package = "com.codeinc.flipcash.gen.messaging.v1";
option objc_class_prefix = "FPBMessagingV1";

import "common/v1/common.proto";
import "messaging/v1/model.proto";
import "validate/validate.proto";

service Messaging {
// GetMessage gets a single message in a chat
rpc GetMessage(GetMessageRequest) returns (GetMessageResponse);

// GetMessages gets the set of messages for a chat using a paged and batched APIs
rpc GetMessages(GetMessagesRequest) returns (GetMessagesResponse);

// SendMessage sends a message to a chat.
rpc SendMessage(SendMessageRequest) returns (SendMessageResponse);

// AdvancePointer advances a pointer in message history for a chat member.
rpc AdvancePointer(AdvancePointerRequest) returns (AdvancePointerResponse);

// NotifyIsTypingRequest notifies a chat that the sending member is typing.
//
// These requests are transient, and may be dropped at any point.
rpc NotifyIsTyping(NotifyIsTypingRequest) returns (NotifyIsTypingResponse);
}

message GetMessageRequest {
common.v1.ChatId chat_id = 1 [(validate.rules).message.required = true];

MessageId message_id = 2 [(validate.rules).message.required = true];

common.v1.Auth auth = 10;
}

message GetMessageResponse {
Result result = 1;
enum Result {
OK = 0;
DENIED = 1;
NOT_FOUND = 2;
}

Message message = 2;
}

message GetMessagesRequest {
common.v1.ChatId chat_id = 1 [(validate.rules).message.required = true];

oneof query {
option (validate.required) = true;

common.v1.QueryOptions options = 2;
MessageIdBatch message_ids = 3;
}

common.v1.Auth auth = 10;
}

message GetMessagesResponse {
Result result = 1;
enum Result {
OK = 0;
DENIED = 1;
NOT_FOUND = 2;
}

MessageBatch messages = 2;
}

message SendMessageRequest {
common.v1.ChatId chat_id = 1 [(validate.rules).message.required = true];

// Allowed content types that can be sent by client:
// - TextContent
repeated Content content = 2 [(validate.rules).repeated = {
min_items: 1
max_items: 1
}];

// Client-generated idempotency token for this send. Used to dedup retried
// sends and to correlate the optimistic local echo with the server-assigned
// message returned in the response.
ClientMessageId client_message_id = 3 [(validate.rules).message.required = true];

common.v1.Auth auth = 10 [(validate.rules).message.required = true];
}

message SendMessageResponse {
Result result = 1;
enum Result {
OK = 0;
DENIED = 1;
}

// The chat message that was sent if the RPC was succesful, which includes
// server-side metadata like the generated message ID and official timestamp
Message message = 2;
}

message AdvancePointerRequest {
common.v1.ChatId chat_id = 1 [(validate.rules).message.required = true];

Pointer.Type pointer_type = 2 [(validate.rules).enum = {
in: [2, 3] // DELIVERED, READ
}];

MessageId new_value = 3 [(validate.rules).message.required = true];

common.v1.Auth auth = 10 [(validate.rules).message.required = true];
}

message AdvancePointerResponse {
Result result = 1;
enum Result {
OK = 0;
DENIED = 1;
MESSAGE_NOT_FOUND = 2;
}
}

message NotifyIsTypingRequest {
common.v1.ChatId chat_id = 1 [(validate.rules).message.required = true];

IsTypingNotification.State state = 2;

common.v1.Auth auth = 10 [(validate.rules).message.required = true];
}

message NotifyIsTypingResponse {
Result result = 1;
enum Result {
OK = 0;
DENIED = 1;
}
}
Loading
Loading