chore: billing error

This commit is contained in:
nathan
2024-07-19 12:26:05 +08:00
parent 5bbf174ffd
commit 42a5b57aa5
16 changed files with 133 additions and 50 deletions

View File

@ -73,7 +73,9 @@ Future<FlowyResult<Uint8List, Uint8List>> _extractPayload(
case FFIStatusCode.Ok:
return FlowySuccess(Uint8List.fromList(response.payload));
case FFIStatusCode.Err:
return FlowyFailure(Uint8List.fromList(response.payload));
final errorBytes = Uint8List.fromList(response.payload);
ErrorCodeNotifier.receiveErrorBytes(errorBytes);
return FlowyFailure(errorBytes);
case FFIStatusCode.Internal:
final error = utf8.decode(response.payload);
Log.error("Dispatch internal error: $error");

View File

@ -1,4 +1,8 @@
import 'package:appflowy_backend/log.dart';
import 'package:appflowy_backend/protobuf/dart-ffi/protobuf.dart';
import 'package:appflowy_backend/protobuf/flowy-error/code.pbenum.dart';
import 'package:appflowy_backend/protobuf/flowy-error/errors.pbserver.dart';
import 'package:flutter/foundation.dart';
class FlowyInternalError {
late FFIStatusCode _statusCode;
@ -20,15 +24,13 @@ class FlowyInternalError {
return "$_statusCode: $_error";
}
FlowyInternalError(
{required FFIStatusCode statusCode, required String error}) {
FlowyInternalError({
required FFIStatusCode statusCode,
required String error,
}) {
_statusCode = statusCode;
_error = error;
}
factory FlowyInternalError.from(FFIResponse resp) {
return FlowyInternalError(statusCode: resp.code, error: "");
}
}
class StackTraceError {
@ -48,3 +50,48 @@ class StackTraceError {
return '${error.runtimeType}. Stack trace: $trace';
}
}
class ErrorCodeNotifier extends ChangeNotifier {
// Static instance
static final ErrorCodeNotifier _instance = ErrorCodeNotifier._();
// Factory constructor to return the same instance
factory ErrorCodeNotifier() {
return _instance;
}
FlowyError? _error;
static void receiveError(FlowyError error) {
if (_instance._error?.code != error.code) {
_instance._error = error;
_instance.notifyListeners();
}
}
static void receiveErrorBytes(Uint8List bytes) {
try {
final error = FlowyError.fromBuffer(bytes);
if (_instance._error?.code != error.code) {
_instance._error = error;
_instance.notifyListeners();
}
} catch (e) {
Log.error("Can not parse error bytes: $e");
}
}
static void onError(
void Function(FlowyError error) onError,
bool Function(ErrorCode code)? onErrorIf,
) {
_instance.addListener(() {
final error = _instance._error;
if (error != null) {
if (onErrorIf == null || onErrorIf(error.code)) {
onError(error);
}
}
});
}
}

View File

@ -2103,6 +2103,7 @@ dependencies = [
"client-api",
"collab",
"collab-entity",
"flowy-error",
"lib-infra",
]

View File

@ -1918,6 +1918,7 @@ dependencies = [
"client-api",
"collab",
"collab-entity",
"flowy-error",
"lib-infra",
]

View File

@ -414,11 +414,11 @@ impl DatabaseCloudService for ServerProvider {
}
fn summary_database_row(
&self,
workspace_id: &str,
object_id: &str,
summary_row: SummaryRowContent,
) -> FutureResult<String, Error> {
&self,
workspace_id: &str,
object_id: &str,
summary_row: SummaryRowContent,
) -> FutureResult<String, FlowyError> {
let workspace_id = workspace_id.to_string();
let server = self.get_server();
let object_id = object_id.to_string();
@ -435,7 +435,7 @@ impl DatabaseCloudService for ServerProvider {
workspace_id: &str,
translate_row: TranslateRowContent,
language: &str,
) -> FutureResult<TranslateRowResponse, Error> {
) -> FutureResult<TranslateRowResponse, FlowyError> {
let workspace_id = workspace_id.to_string();
let server = self.get_server();
let language = language.to_string();

View File

@ -11,3 +11,4 @@ collab-entity = { workspace = true }
collab = { workspace = true }
anyhow.workspace = true
client-api = { workspace = true }
flowy-error = { workspace = true }

View File

@ -4,6 +4,7 @@ use collab::core::collab::DataSource;
use collab_entity::CollabType;
use lib_infra::future::FutureResult;
use std::collections::HashMap;
use flowy_error::FlowyError;
pub type CollabDocStateByOid = HashMap<String, DataSource>;
pub type SummaryRowContent = HashMap<String, String>;
@ -40,14 +41,14 @@ pub trait DatabaseCloudService: Send + Sync {
workspace_id: &str,
object_id: &str,
summary_row: SummaryRowContent,
) -> FutureResult<String, Error>;
) -> FutureResult<String, FlowyError>;
fn translate_database_row(
&self,
workspace_id: &str,
translate_row: TranslateRowContent,
language: &str,
) -> FutureResult<TranslateRowResponse, Error>;
) -> FutureResult<TranslateRowResponse, FlowyError>;
}
pub struct DatabaseSnapshot {

View File

@ -17,10 +17,11 @@ flowy-derive.workspace = true
flowy-notification = { workspace = true }
parking_lot.workspace = true
protobuf.workspace = true
flowy-error = { workspace = true, features = [
flowy-error = { path = "../flowy-error", features = [
"impl_from_dispatch_error",
"impl_from_collab_database",
] }
]}
lib-dispatch = { workspace = true }
tokio = { workspace = true, features = ["sync"] }
bytes.workspace = true

View File

@ -286,6 +286,12 @@ pub enum ErrorCode {
#[error("Local AI unavailable")]
LocalAIUnavailable = 99,
#[error("File storage limit exceeded")]
FileStorageLimitExceeded = 100,
#[error("AI Response limit exceeded")]
AIResponseLimitExceeded = 101,
}
impl ErrorCode {

View File

@ -72,6 +72,14 @@ impl FlowyError {
self.code == ErrorCode::LocalVersionNotSupport
}
pub fn is_file_limit_exceeded(&self) -> bool {
self.code == ErrorCode::FileStorageLimitExceeded
}
pub fn is_ai_response_limit_exceeded(&self) -> bool {
self.code == ErrorCode::AIResponseLimitExceeded
}
static_flowy_error!(internal, ErrorCode::Internal);
static_flowy_error!(record_not_found, ErrorCode::RecordNotFound);
static_flowy_error!(workspace_initialize, ErrorCode::WorkspaceInitializeError);

View File

@ -24,6 +24,8 @@ impl From<AppResponseError> for FlowyError {
AppErrorCode::UserUnAuthorized => ErrorCode::UserUnauthorized,
AppErrorCode::WorkspaceLimitExceeded => ErrorCode::WorkspaceLimitExceeded,
AppErrorCode::WorkspaceMemberLimitExceeded => ErrorCode::WorkspaceMemberLimitExceeded,
AppErrorCode::AIResponseLimitExceeded => ErrorCode::AIResponseLimitExceeded,
AppErrorCode::FileStorageLimitExceeded => ErrorCode::FileStorageLimitExceeded,
_ => ErrorCode::Internal,
};

View File

@ -13,7 +13,7 @@ pub mod reqwest;
#[cfg(feature = "impl_from_sqlite")]
pub mod database;
#[cfg(feature = "impl_from_collab_document")]
#[cfg(any(feature = "impl_from_collab_document", feature = "impl_from_collab_folder", feature = "impl_from_collab_database"))]
pub mod collab;
#[cfg(feature = "impl_from_collab_persistence")]

View File

@ -16,6 +16,7 @@ use flowy_database_pub::cloud::{
CollabDocStateByOid, DatabaseCloudService, DatabaseSnapshot, SummaryRowContent,
TranslateRowContent, TranslateRowResponse,
};
use flowy_error::FlowyError;
use lib_infra::future::FutureResult;
use crate::af_cloud::define::ServerUser;
@ -124,9 +125,9 @@ where
fn summary_database_row(
&self,
workspace_id: &str,
_object_id: &str,
object_id: &str,
summary_row: SummaryRowContent,
) -> FutureResult<String, Error> {
) -> FutureResult<String, FlowyError> {
let workspace_id = workspace_id.to_string();
let try_get_client = self.inner.try_get_client();
FutureResult::new(async move {
@ -148,7 +149,7 @@ where
workspace_id: &str,
translate_row: TranslateRowContent,
language: &str,
) -> FutureResult<TranslateRowResponse, Error> {
) -> FutureResult<TranslateRowResponse, FlowyError> {
let language = language.to_string();
let workspace_id = workspace_id.to_string();
let try_get_client = self.inner.try_get_client();

View File

@ -8,16 +8,17 @@ use flowy_database_pub::cloud::{
CollabDocStateByOid, DatabaseCloudService, DatabaseSnapshot, SummaryRowContent,
TranslateRowContent, TranslateRowResponse,
};
use flowy_error::FlowyError;
use lib_infra::future::FutureResult;
pub(crate) struct LocalServerDatabaseCloudServiceImpl();
impl DatabaseCloudService for LocalServerDatabaseCloudServiceImpl {
fn get_database_object_doc_state(
&self,
object_id: &str,
collab_type: CollabType,
_workspace_id: &str,
&self,
object_id: &str,
collab_type: CollabType,
workspace_id: &str,
) -> FutureResult<Option<Vec<u8>>, Error> {
let object_id = object_id.to_string();
// create the minimal required data for the given collab type
@ -62,9 +63,9 @@ impl DatabaseCloudService for LocalServerDatabaseCloudServiceImpl {
fn batch_get_database_object_doc_state(
&self,
_object_ids: Vec<String>,
_object_ty: CollabType,
_workspace_id: &str,
object_ids: Vec<String>,
object_ty: CollabType,
workspace_id: &str,
) -> FutureResult<CollabDocStateByOid, Error> {
FutureResult::new(async move { Ok(CollabDocStateByOid::default()) })
}
@ -79,20 +80,20 @@ impl DatabaseCloudService for LocalServerDatabaseCloudServiceImpl {
fn summary_database_row(
&self,
_workspace_id: &str,
_object_id: &str,
_summary_row: SummaryRowContent,
) -> FutureResult<String, Error> {
workspace_id: &str,
object_id: &str,
summary_row: SummaryRowContent,
) -> FutureResult<String, FlowyError> {
// TODO(lucas): local ai
FutureResult::new(async move { Ok("".to_string()) })
}
fn translate_database_row(
&self,
_workspace_id: &str,
_translate_row: TranslateRowContent,
_language: &str,
) -> FutureResult<TranslateRowResponse, Error> {
workspace_id: &str,
translate_row: TranslateRowContent,
language: &str,
) -> FutureResult<TranslateRowResponse, FlowyError> {
// TODO(lucas): local ai
FutureResult::new(async move { Ok(TranslateRowResponse::default()) })
}

View File

@ -6,6 +6,7 @@ use flowy_database_pub::cloud::{
CollabDocStateByOid, DatabaseCloudService, DatabaseSnapshot, SummaryRowContent,
TranslateRowContent, TranslateRowResponse,
};
use flowy_error::FlowyError;
use lib_dispatch::prelude::af_spawn;
use lib_infra::future::FutureResult;
@ -29,10 +30,10 @@ where
T: SupabaseServerService,
{
fn get_database_object_doc_state(
&self,
object_id: &str,
collab_type: CollabType,
_workspace_id: &str,
&self,
object_id: &str,
collab_type: CollabType,
workspace_id: &str,
) -> FutureResult<Option<Vec<u8>>, Error> {
let try_get_postgrest = self.server.try_get_weak_postgrest();
let object_id = object_id.to_string();
@ -56,7 +57,7 @@ where
&self,
object_ids: Vec<String>,
object_ty: CollabType,
_workspace_id: &str,
workspace_id: &str,
) -> FutureResult<CollabDocStateByOid, Error> {
let try_get_postgrest = self.server.try_get_weak_postgrest();
let (tx, rx) = channel();
@ -100,19 +101,19 @@ where
fn summary_database_row(
&self,
_workspace_id: &str,
_object_id: &str,
_summary_row: SummaryRowContent,
) -> FutureResult<String, Error> {
workspace_id: &str,
object_id: &str,
summary_row: SummaryRowContent,
) -> FutureResult<String, FlowyError> {
FutureResult::new(async move { Ok("".to_string()) })
}
fn translate_database_row(
&self,
_workspace_id: &str,
_translate_row: TranslateRowContent,
_language: &str,
) -> FutureResult<TranslateRowResponse, Error> {
workspace_id: &str,
translate_row: TranslateRowContent,
language: &str,
) -> FutureResult<TranslateRowResponse, FlowyError> {
FutureResult::new(async move { Ok(TranslateRowResponse::default()) })
}
}

View File

@ -10,7 +10,7 @@ use std::sync::atomic::{AtomicBool, AtomicU8};
use std::sync::{Arc, Weak};
use std::time::Duration;
use tokio::sync::{watch, RwLock};
use tracing::{info, trace};
use tracing::{error, info, trace};
#[derive(Clone)]
pub enum Signal {
@ -128,6 +128,11 @@ impl FileUploader {
} => {
let record = BoxAny::new(record);
if let Err(err) = self.storage_service.start_upload(&chunks, &record).await {
if (err.is_file_limit_exceeded()) {
error!("Failed to upload file: {}", err);
self.pause();
}
info!(
"Failed to upload file: {}, retry_count:{}",
err, retry_count
@ -154,6 +159,11 @@ impl FileUploader {
.resume_upload(&workspace_id, &parent_dir, &file_id)
.await
{
if (err.is_file_limit_exceeded()) {
error!("Failed to upload file: {}", err);
self.pause();
}
info!(
"Failed to resume upload file: {}, retry_count:{}",
err, retry_count