From 9ebf417a0a5b04dba03e60320079d83b1753fe98 Mon Sep 17 00:00:00 2001 From: Konstantin Ivanov <54908981+konstantiniiv@users.noreply.github.com> Date: Tue, 2 Apr 2024 13:16:13 +0200 Subject: [PATCH] DROID-2362 Protocol | MW 0.33.0-rc6 (#1047) --- gradle/libs.versions.toml | 2 +- protocol/src/main/proto/commands.proto | 471 +++++++++++++++++++++++++ protocol/src/main/proto/events.proto | 8 + protocol/src/main/proto/models.proto | 115 ++++++ 4 files changed, 595 insertions(+), 1 deletion(-) diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 2ec9c47d8d..3e7c608f3f 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -1,5 +1,5 @@ [versions] -middlewareVersion = "v0.33.0-rc2" +middlewareVersion = "v0.33.0-rc6" kotlinVersion = '1.9.22' androidxCoreVersion = "1.12.0" diff --git a/protocol/src/main/proto/commands.proto b/protocol/src/main/proto/commands.proto index 34b97df9fc..ab9684db06 100644 --- a/protocol/src/main/proto/commands.proto +++ b/protocol/src/main/proto/commands.proto @@ -93,6 +93,33 @@ message Rpc { } message Space { + message LeaveApprove { + message Request { + string spaceId = 1; + repeated string identities = 2; + } + + message Response { + Error error = 1; + + message Error { + Code code = 1; + string description = 2; + + enum Code { + NULL = 0; + UNKNOWN_ERROR = 1; + BAD_INPUT = 2; + + NO_SUCH_SPACE = 101; + SPACE_IS_DELETED = 102; + REQUEST_FAILED = 103; + NO_APPROVE_REQUESTS = 104; + } + } + } + } + message InviteGenerate { message Request { string spaceId = 1; @@ -557,6 +584,7 @@ message Rpc { UNKNOWN_ERROR = 1; BAD_INPUT = 2; + APP_TOKEN_NOT_FOUND_IN_THE_CURRENT_ACCOUNT = 101; // means the client logged into another account or the account directory has been cleaned } } } @@ -633,6 +661,7 @@ message Rpc { FAILED_TO_STOP_RUNNING_NODE = 104; FAILED_TO_WRITE_CONFIG = 105; FAILED_TO_CREATE_LOCAL_REPO = 106; + ACCOUNT_CREATION_IS_CANCELED = 107; CONFIG_FILE_NOT_FOUND = 200; CONFIG_FILE_INVALID = 201; @@ -758,6 +787,7 @@ message Rpc { ANOTHER_ANYTYPE_PROCESS_IS_RUNNING = 108; FAILED_TO_FETCH_REMOTE_NODE_HAS_INCOMPATIBLE_PROTO_VERSION = 110; ACCOUNT_IS_DELETED = 111; + ACCOUNT_LOAD_IS_CANCELED = 112; CONFIG_FILE_NOT_FOUND = 200; CONFIG_FILE_INVALID = 201; @@ -6355,6 +6385,445 @@ message Rpc { } } } + + /** + * A Membership is a bundle of several "Features" + * every user should have one and only one tier + * users can not have N tiers (no combining) + */ + message Membership { + /** + * Get the current status of the membership + * including the tier, status, dates, etc + * WARNING: this can be cached by Anytype heart + */ + message GetStatus { + message Request { + // pass true to force the cache update + // by default this is false + bool noCache = 1; + } + + message Response { + Error error = 1; + + anytype.model.Membership data = 2; + + message Error { + Code code = 1; + string description = 2; + + enum Code { + NULL = 0; + UNKNOWN_ERROR = 1; + BAD_INPUT = 2; + + NOT_LOGGED_IN = 3; + PAYMENT_NODE_ERROR = 4; + CACHE_ERROR = 5; + + MEMBERSHIP_NOT_FOUND = 6; + MEMBERSHIP_WRONG_STATE = 7; + } + } + } + } + + /** + * Check if the requested name is valid for the requested tier + * before requesting a payment link and paying + */ + message IsNameValid { + message Request { + uint32 requestedTier = 1; + + // full name including .any suffix + string requestedAnyName = 2; + } + + message Response { + Error error = 1; + + message Error { + Code code = 1; + string description = 2; + + enum Code { + NULL = 0; + UNKNOWN_ERROR = 1; + BAD_INPUT = 2; + + TOO_SHORT = 3; + TOO_LONG = 4; + HAS_INVALID_CHARS = 5; + TIER_FEATURES_NO_NAME = 6; + // if everything is fine - "name is already taken" check should be done in the NS + // see IsNameAvailable() + TIER_NOT_FOUND = 7; + + NOT_LOGGED_IN = 8; + PAYMENT_NODE_ERROR = 9; + CACHE_ERROR = 10; + } + } + } + } + + /** + * Generate a link to the payment provider + * where user can pay for the membership + */ + message GetPaymentUrl { + message Request { + uint32 requestedTier = 1; + + anytype.model.Membership.PaymentMethod paymentMethod = 2; + + // if empty - then no name requested + // if non-empty - PP node will register that name on behalf of the user + string requestedAnyName = 3; + } + + message Response { + Error error = 1; + + // will feature current billing ID + // stripe.com/?client_reference_id=1234 + string paymentUrl = 2; + + message Error { + Code code = 1; + string description = 2; + + enum Code { + NULL = 0; + UNKNOWN_ERROR = 1; + BAD_INPUT = 2; + + NOT_LOGGED_IN = 3; + PAYMENT_NODE_ERROR = 4; + CACHE_ERROR = 5; + + TIER_NOT_FOUND = 6; + TIER_INVALID = 7; + PAYMENT_METHOD_INVALID = 8; + BAD_ANYNAME = 9; + MEMBERSHIP_ALREADY_EXISTS = 10; + } + } + } + } + + /** + * Generate a link to the portal where user can: + * a) change his billing details + * b) see payment info, invoices, etc + * c) cancel membership + */ + message GetPortalLinkUrl { + message Request { + + } + + message Response { + Error error = 1; + + string portalUrl = 2; + + message Error { + Code code = 1; + string description = 2; + + enum Code { + NULL = 0; + UNKNOWN_ERROR = 1; + BAD_INPUT = 2; + + NOT_LOGGED_IN = 3; + PAYMENT_NODE_ERROR = 4; + CACHE_ERROR = 5; + } + } + } + } + + message Finalize { + message Request { + // if empty - then no name requested + // if non-empty - PP node will register that name on behalf of the user + string requestedAnyName = 1; + } + + message Response { + Error error = 1; + + message Error { + Code code = 1; + string description = 2; + + enum Code { + NULL = 0; + UNKNOWN_ERROR = 1; + BAD_INPUT = 2; + + NOT_LOGGED_IN = 3; + PAYMENT_NODE_ERROR = 4; + CACHE_ERROR = 5; + + MEMBERSHIP_NOT_FOUND = 6; + MEMBERSHIP_WRONG_STATE = 7; + + BAD_ANYNAME = 8; + } + } + } + } + + /** + * Send an e-mail with verification code to the user + * can be called multiple times but with some timeout (N seconds) between calls + */ + message GetVerificationEmail { + message Request { + string email = 1; + + bool subscribeToNewsletter = 2; + } + + message Response { + Error error = 1; + + message Error { + Code code = 1; + string description = 2; + + enum Code { + NULL = 0; + UNKNOWN_ERROR = 1; + BAD_INPUT = 2; + + NOT_LOGGED_IN = 3; + PAYMENT_NODE_ERROR = 4; + CACHE_ERROR = 5; + + EMAIL_WRONG_FORMAT = 6; + EMAIL_ALREADY_VERIFIED = 7; + EMAIL_ALREDY_SENT = 8; + EMAIL_FAILED_TO_SEND = 9; + + MEMBERSHIP_ALREADY_EXISTS = 10; + } + } + } + } + + /** + * Verify the e-mail address of the user + * need a correct code that was sent to the user when calling GetVerificationEmail + */ + message VerifyEmailCode { + message Request { + string code = 1; + } + + message Response { + Error error = 1; + + message Error { + Code code = 1; + string description = 2; + + enum Code { + NULL = 0; + UNKNOWN_ERROR = 1; + BAD_INPUT = 2; + + NOT_LOGGED_IN = 3; + PAYMENT_NODE_ERROR = 4; + CACHE_ERROR = 5; + + EMAIL_ALREADY_VERIFIED = 6; + CODE_EXPIRED = 7; + CODE_WRONG = 8; + + MEMBERSHIP_NOT_FOUND = 9; + MEMBERSHIP_ALREADY_ACTIVE = 10; + } + } + } + } + + /** + * Tiers can change on the backend so if you want to show users the latest data + * you can call this method to get the latest tiers + */ + message Tiers { + message Get { + message Request { + // pass true to force the cache update + // by default this is false + bool noCache = 1; + + string locale = 2; + } + + message Response { + Error error = 1; + repeated anytype.model.MembershipTierData tiers = 2; + + message Error { + Code code = 1; + string description = 2; + + enum Code { + NULL = 0; + UNKNOWN_ERROR = 1; + BAD_INPUT = 2; + + NOT_LOGGED_IN = 3; + PAYMENT_NODE_ERROR = 4; + CACHE_ERROR = 5; + } + } + } + } + } + } + + message NameService { + message ResolveName { + message Request { + // including ".any" suffix + string fullName = 1; + } + + message Response { + Error error = 1; + + bool available = 2; + + // EOA -> SCW -> name + // This field is non-empty only if name is "already registered" + string ownerScwEthAddress = 3; + + // This field is non-empty only if name is "already registered" + string ownerEthAddress = 4; + + // A content hash attached to this name + // This field is non-empty only if name is "already registered" + string ownerAnyAddress = 5; + + // A SpaceId attached to this name + // This field is non-empty only if name is "already registered" + string spaceId = 6; + + // A timestamp when this name expires + int64 nameExpires = 7; + + message Error { + Code code = 1; + string description = 2; + + enum Code { + NULL = 0; + UNKNOWN_ERROR = 1; + BAD_INPUT = 2; + } + } + } + } + + message ResolveAnyId { + message Request { + string anyId = 1; + } + + message Response { + Error error = 1; + + bool found = 2; + + // including ".any" suffix + string fullName = 3; + + message Error { + Code code = 1; + string description = 2; + + enum Code { + NULL = 0; + UNKNOWN_ERROR = 1; + BAD_INPUT = 2; + } + } + } + } + + message ResolveSpaceId { + message Request { + string spaceId = 1; + } + + message Response { + Error error = 1; + + bool found = 2; + + // including ".any" suffix + string fullName = 3; + + message Error { + Code code = 1; + string description = 2; + + enum Code { + NULL = 0; + UNKNOWN_ERROR = 1; + BAD_INPUT = 2; + } + } + } + } + + message UserAccount { + message Get { + message Request { + + } + + message Response { + Error error = 1; + + // this will use ReverseResolve to get current name + // user can buy many names, but + // only 1 name can be set as "current": ETH address <-> name + string anyNameAttached = 2; + + // Number of names that the user can reserve + uint64 namesCountLeft = 3; + + // Number of operations: update name, add new data, etc + uint64 operationsCountLeft = 4; + + // TODO: all operations list + + message Error { + Code code = 1; + string description = 2; + + enum Code { + NULL = 0; + UNKNOWN_ERROR = 1; + BAD_INPUT = 2; + NOT_LOGGED_IN = 3; + BAD_NAME_RESOLVE = 4; + } + } + } + } + } + } + message Broadcast { message PayloadEvent { message Request { @@ -6380,6 +6849,8 @@ message Rpc { } } + + message Empty { } diff --git a/protocol/src/main/proto/events.proto b/protocol/src/main/proto/events.proto index 3ecddf1d1a..087fa9a3f6 100644 --- a/protocol/src/main/proto/events.proto +++ b/protocol/src/main/proto/events.proto @@ -100,6 +100,8 @@ message Event { Notification.Update notificationUpdate = 115; Payload.Broadcast payloadBroadcast = 116; + + Membership.Update membershipUpdate = 117; } } @@ -1034,6 +1036,12 @@ message Event { } } + message Membership { + message Update { + anytype.model.Membership data = 1; + } + } + message Notification { message Send { anytype.model.Notification notification = 1; diff --git a/protocol/src/main/proto/models.proto b/protocol/src/main/proto/models.proto index 73af1ae5a9..42f472589f 100644 --- a/protocol/src/main/proto/models.proto +++ b/protocol/src/main/proto/models.proto @@ -985,6 +985,7 @@ message Notification { model.Import.Type importType = 3; string spaceId = 4; string name = 5; + string spaceName = 6; } message Export { @@ -1002,6 +1003,7 @@ message Notification { model.Import.ErrorCode errorCode = 2; string spaceId = 3; string name = 4; + string spaceName = 5; } message RequestToJoin { @@ -1115,6 +1117,7 @@ message IdentityProfile { string iconCid = 3; repeated FileEncryptionKey iconEncryptionKeys = 4; string description = 5; + string globalName = 6; } message FileInfo { @@ -1153,3 +1156,115 @@ message ManifestInfo { repeated string categories = 11; string language = 12; } + +message Membership { + enum Tier { + TierNewUser = 0; + // "free" tier + TierExplorer = 1; + // this tier can be used just for testing in debug mode + // it will still create an active subscription, but with NO features + TierBuilder1WeekTEST = 2; + // this tier can be used just for testing in debug mode + // it will still create an active subscription, but with NO features + TierCoCreator1WeekTEST = 3; + TierBuilder = 4; + TierCoCreator = 5; + } + + enum Status { + StatusUnknown = 0; + // please wait a bit more + StatusPending = 1; + StatusActive = 2; + // in some cases we need to finalize the process: + // - if user has bought membership directly without first calling + // the BuySubscription method + // + // in this case please call Finalize to finish the process + StatusPendingRequiresFinalization = 3; + } + + enum PaymentMethod { + MethodCard = 0; // Stripe + MethodCrypto = 1; + MethodApplePay = 2; + MethodGooglePay = 3; + MethodAppleInapp = 4; + MethodGoogleInapp = 5; + } + + // it was Tier before, changed to int32 to allow dynamic values + uint32 tier = 1; + Status status = 2; + uint64 dateStarted = 3; + uint64 dateEnds = 4; + bool isAutoRenew = 5; + PaymentMethod paymentMethod = 6; + // can be empty if user did not ask for any name + string requestedAnyName = 7; + // if the email was verified by the user or set during the checkout - it will be here + string userEmail = 8; + bool subscribeToNewsletter = 9; +} + +message MembershipTierData { + enum PeriodType { + PeriodTypeUnknown = 0; + PeriodTypeUnlimited = 1; + PeriodTypeDays = 2; + PeriodTypeWeeks = 3; + PeriodTypeMonths = 4; + PeriodTypeYears = 5; + } + + enum FeatureId { + Unknown = 0; + StorageGBs = 1; + Invites = 2; + SpaceWriters = 3; + SpaceReaders = 4; + SharedSpaces = 5; + } + + message Feature { + FeatureId featureId = 1; + + // usually feature has uint value + // like "storage" - 120 + uint32 valueUint = 2; + + // in case feature will have string value + string valueStr = 3; + } + + // this is a unique ID of the tier + // you should hardcode this in your app and provide icon, graphics, etc for each tier + // (even for old/historical/inactive/hidden tiers) + uint32 id = 1; + // localazied name of the tier + string name = 2; + // just a short technical description + // you don't have to use it, you can use your own UI-friendly texts + string description = 3; + // can you buy it (ON ALL PLATFORMS, without clarification)? + bool isActive = 4; + // is this tier for debugging only? + bool isTest = 5; + // hidden tiers are only visible once user got them + bool isHiddenTier = 6; + // how long is the period of the subscription + PeriodType periodType = 7; + // i.e. "5 days" or "3 years" + uint32 periodValue = 8; + // this one is a price we use ONLY on Stripe platform + uint32 priceStripeUsdCents = 9; + // number of ANY NS names that this tier includes + // (not counted as a "feature" and not in the features list) + uint32 anyNamesCountIncluded = 10; + // somename.any - len of 8 + uint32 anyNameMinLength = 11; + // each tier has a set of features + // the key is FeatureId + repeated Feature features = 12; +} \ No newline at end of file