mirror of
https://github.com/AppFlowy-IO/AppFlowy.git
synced 2024-08-30 18:12:39 +00:00
chore: move responsibility of url valdiation to front-frontend (#5129)
* chore: move responsibility of url valdiation to frontend * chore: fix test * chore: fix tauri build
This commit is contained in:
@ -4,9 +4,6 @@ use flowy_derive::ProtoBuf;
|
||||
#[derive(Clone, Debug, Default, ProtoBuf)]
|
||||
pub struct URLCellDataPB {
|
||||
#[pb(index = 1)]
|
||||
pub url: String,
|
||||
|
||||
#[pb(index = 2)]
|
||||
pub content: String,
|
||||
}
|
||||
|
||||
|
@ -1,6 +1,5 @@
|
||||
use std::cmp::Ordering;
|
||||
|
||||
use bytes::Bytes;
|
||||
use collab::core::any_map::AnyMapExtension;
|
||||
use collab_database::fields::{Field, TypeOptionData, TypeOptionDataBuilder};
|
||||
use collab_database::rows::{new_cell_builder, Cell};
|
||||
@ -9,9 +8,7 @@ use serde::{Deserialize, Serialize};
|
||||
use flowy_error::{FlowyError, FlowyResult};
|
||||
|
||||
use crate::entities::{FieldType, TextFilterPB};
|
||||
use crate::services::cell::{
|
||||
stringify_cell, CellDataChangeset, CellDataDecoder, CellProtobufBlobParser,
|
||||
};
|
||||
use crate::services::cell::{stringify_cell, CellDataChangeset, CellDataDecoder};
|
||||
use crate::services::field::type_options::util::ProtobufStr;
|
||||
use crate::services::field::{
|
||||
TypeOption, TypeOptionCellData, TypeOptionCellDataCompare, TypeOptionCellDataFilter,
|
||||
@ -146,39 +143,6 @@ impl TypeOptionCellDataCompare for RichTextTypeOption {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct TextCellData(pub String);
|
||||
impl AsRef<str> for TextCellData {
|
||||
fn as_ref(&self) -> &str {
|
||||
&self.0
|
||||
}
|
||||
}
|
||||
|
||||
impl std::ops::Deref for TextCellData {
|
||||
type Target = String;
|
||||
|
||||
fn deref(&self) -> &Self::Target {
|
||||
&self.0
|
||||
}
|
||||
}
|
||||
|
||||
impl ToString for TextCellData {
|
||||
fn to_string(&self) -> String {
|
||||
self.0.clone()
|
||||
}
|
||||
}
|
||||
|
||||
pub struct TextCellDataParser();
|
||||
impl CellProtobufBlobParser for TextCellDataParser {
|
||||
type Object = TextCellData;
|
||||
fn parser(bytes: &Bytes) -> FlowyResult<Self::Object> {
|
||||
match String::from_utf8(bytes.to_vec()) {
|
||||
Ok(s) => Ok(TextCellData(s)),
|
||||
Err(_) => Ok(TextCellData("".to_owned())),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Default, Debug, Clone)]
|
||||
pub struct StrCellData(pub String);
|
||||
impl std::ops::Deref for StrCellData {
|
||||
|
@ -7,161 +7,26 @@ mod tests {
|
||||
use crate::services::field::FieldBuilder;
|
||||
use crate::services::field::URLTypeOption;
|
||||
|
||||
/// The expected_str will equal to the input string, but the expected_url will be empty if there's no
|
||||
/// http url in the input string.
|
||||
#[test]
|
||||
fn url_type_option_does_not_contain_url_test() {
|
||||
let type_option = URLTypeOption::default();
|
||||
let field_type = FieldType::URL;
|
||||
let field = FieldBuilder::from_field_type(field_type).build();
|
||||
assert_url(&type_option, "123", "123", "", &field);
|
||||
assert_url(&type_option, "", "", "", &field);
|
||||
}
|
||||
|
||||
/// The expected_str will equal to the input string, but the expected_url will not be empty
|
||||
/// if there's a http url in the input string.
|
||||
#[test]
|
||||
fn url_type_option_contains_url_test() {
|
||||
fn url_test() {
|
||||
let type_option = URLTypeOption::default();
|
||||
let field_type = FieldType::URL;
|
||||
let field = FieldBuilder::from_field_type(field_type).build();
|
||||
assert_url(
|
||||
&type_option,
|
||||
"AppFlowy website - https://www.appflowy.io",
|
||||
"AppFlowy website - https://www.appflowy.io",
|
||||
"https://www.appflowy.io/",
|
||||
&field,
|
||||
);
|
||||
|
||||
assert_url(
|
||||
&type_option,
|
||||
"AppFlowy website appflowy.io",
|
||||
"AppFlowy website appflowy.io",
|
||||
"https://appflowy.io",
|
||||
"https://www.appflowy.io",
|
||||
"https://www.appflowy.io",
|
||||
&field,
|
||||
);
|
||||
assert_url(&type_option, "123", "123", &field);
|
||||
assert_url(&type_option, "", "", &field);
|
||||
}
|
||||
|
||||
/// if there's a http url and some words following it in the input string.
|
||||
#[test]
|
||||
fn url_type_option_contains_url_with_string_after_test() {
|
||||
let type_option = URLTypeOption::default();
|
||||
let field_type = FieldType::URL;
|
||||
let field = FieldBuilder::from_field_type(field_type).build();
|
||||
assert_url(
|
||||
&type_option,
|
||||
"AppFlowy website - https://www.appflowy.io welcome!",
|
||||
"AppFlowy website - https://www.appflowy.io welcome!",
|
||||
"https://www.appflowy.io/",
|
||||
&field,
|
||||
);
|
||||
|
||||
assert_url(
|
||||
&type_option,
|
||||
"AppFlowy website appflowy.io welcome!",
|
||||
"AppFlowy website appflowy.io welcome!",
|
||||
"https://appflowy.io",
|
||||
&field,
|
||||
);
|
||||
}
|
||||
|
||||
/// if there's a http url and special words following it in the input string.
|
||||
#[test]
|
||||
fn url_type_option_contains_url_with_special_string_after_test() {
|
||||
let type_option = URLTypeOption::default();
|
||||
let field_type = FieldType::URL;
|
||||
let field = FieldBuilder::from_field_type(field_type).build();
|
||||
assert_url(
|
||||
&type_option,
|
||||
"AppFlowy website - https://www.appflowy.io!",
|
||||
"AppFlowy website - https://www.appflowy.io!",
|
||||
"https://www.appflowy.io/",
|
||||
&field,
|
||||
);
|
||||
|
||||
assert_url(
|
||||
&type_option,
|
||||
"AppFlowy website appflowy.io!",
|
||||
"AppFlowy website appflowy.io!",
|
||||
"https://appflowy.io",
|
||||
&field,
|
||||
);
|
||||
}
|
||||
|
||||
/// if there's a level4 url in the input string.
|
||||
#[test]
|
||||
fn level4_url_type_test() {
|
||||
let type_option = URLTypeOption::default();
|
||||
let field_type = FieldType::URL;
|
||||
let field = FieldBuilder::from_field_type(field_type).build();
|
||||
assert_url(
|
||||
&type_option,
|
||||
"test - https://tester.testgroup.appflowy.io",
|
||||
"test - https://tester.testgroup.appflowy.io",
|
||||
"https://tester.testgroup.appflowy.io/",
|
||||
&field,
|
||||
);
|
||||
|
||||
assert_url(
|
||||
&type_option,
|
||||
"test tester.testgroup.appflowy.io",
|
||||
"test tester.testgroup.appflowy.io",
|
||||
"https://tester.testgroup.appflowy.io",
|
||||
&field,
|
||||
);
|
||||
}
|
||||
|
||||
/// urls with different top level domains.
|
||||
#[test]
|
||||
fn different_top_level_domains_test() {
|
||||
let type_option = URLTypeOption::default();
|
||||
let field_type = FieldType::URL;
|
||||
let field = FieldBuilder::from_field_type(field_type).build();
|
||||
assert_url(
|
||||
&type_option,
|
||||
"appflowy - https://appflowy.com",
|
||||
"appflowy - https://appflowy.com",
|
||||
"https://appflowy.com/",
|
||||
&field,
|
||||
);
|
||||
|
||||
assert_url(
|
||||
&type_option,
|
||||
"appflowy - https://appflowy.top",
|
||||
"appflowy - https://appflowy.top",
|
||||
"https://appflowy.top/",
|
||||
&field,
|
||||
);
|
||||
|
||||
assert_url(
|
||||
&type_option,
|
||||
"appflowy - https://appflowy.net",
|
||||
"appflowy - https://appflowy.net",
|
||||
"https://appflowy.net/",
|
||||
&field,
|
||||
);
|
||||
|
||||
assert_url(
|
||||
&type_option,
|
||||
"appflowy - https://appflowy.edu",
|
||||
"appflowy - https://appflowy.edu",
|
||||
"https://appflowy.edu/",
|
||||
&field,
|
||||
);
|
||||
}
|
||||
|
||||
fn assert_url(
|
||||
type_option: &URLTypeOption,
|
||||
input_str: &str,
|
||||
expected_str: &str,
|
||||
expected_url: &str,
|
||||
_field: &Field,
|
||||
) {
|
||||
fn assert_url(type_option: &URLTypeOption, input_str: &str, expected_url: &str, _field: &Field) {
|
||||
let decode_cell_data = type_option
|
||||
.apply_changeset(input_str.to_owned(), None)
|
||||
.unwrap()
|
||||
.1;
|
||||
assert_eq!(expected_str.to_owned(), decode_cell_data.data);
|
||||
assert_eq!(expected_url.to_owned(), decode_cell_data.url);
|
||||
assert_eq!(expected_url.to_owned(), decode_cell_data.data);
|
||||
}
|
||||
}
|
||||
|
@ -1,3 +1,11 @@
|
||||
use std::cmp::Ordering;
|
||||
|
||||
use collab::core::any_map::AnyMapExtension;
|
||||
use collab_database::fields::{TypeOptionData, TypeOptionDataBuilder};
|
||||
use collab_database::rows::Cell;
|
||||
use flowy_error::FlowyResult;
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
use crate::entities::{TextFilterPB, URLCellDataPB};
|
||||
use crate::services::cell::{CellDataChangeset, CellDataDecoder};
|
||||
use crate::services::field::{
|
||||
@ -6,15 +14,6 @@ use crate::services::field::{
|
||||
};
|
||||
use crate::services::sort::SortCondition;
|
||||
|
||||
use collab::core::any_map::AnyMapExtension;
|
||||
use collab_database::fields::{TypeOptionData, TypeOptionDataBuilder};
|
||||
use collab_database::rows::Cell;
|
||||
use fancy_regex::Regex;
|
||||
use flowy_error::FlowyResult;
|
||||
use lazy_static::lazy_static;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use std::cmp::Ordering;
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, Default)]
|
||||
pub struct URLTypeOption {
|
||||
pub url: String,
|
||||
@ -82,14 +81,7 @@ impl CellDataChangeset for URLTypeOption {
|
||||
changeset: <Self as TypeOption>::CellChangeset,
|
||||
_cell: Option<Cell>,
|
||||
) -> FlowyResult<(Cell, <Self as TypeOption>::CellData)> {
|
||||
let mut url = "".to_string();
|
||||
if let Ok(Some(m)) = URL_REGEX.find(&changeset) {
|
||||
url = auto_append_scheme(m.as_str());
|
||||
}
|
||||
let url_cell_data = URLCellData {
|
||||
url,
|
||||
data: changeset,
|
||||
};
|
||||
let url_cell_data = URLCellData { data: changeset };
|
||||
Ok((url_cell_data.clone().into(), url_cell_data))
|
||||
}
|
||||
}
|
||||
@ -124,26 +116,3 @@ impl TypeOptionCellDataCompare for URLTypeOption {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn auto_append_scheme(s: &str) -> String {
|
||||
// Only support https scheme by now
|
||||
match url::Url::parse(s) {
|
||||
Ok(url) => {
|
||||
if url.scheme() == "https" {
|
||||
url.into()
|
||||
} else {
|
||||
format!("https://{}", s)
|
||||
}
|
||||
},
|
||||
Err(_) => {
|
||||
format!("https://{}", s)
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
lazy_static! {
|
||||
static ref URL_REGEX: Regex = Regex::new(
|
||||
"[(http(s)?):\\/\\/(www\\.)?a-zA-Z0-9@:%._\\+~#=]{2,256}\\.[a-z]{2,6}\\b([-a-zA-Z0-9@:%_\\+.~#?&//=]*)"
|
||||
)
|
||||
.unwrap();
|
||||
}
|
||||
|
@ -11,14 +11,12 @@ use crate::services::field::{TypeOptionCellData, CELL_DATA};
|
||||
|
||||
#[derive(Clone, Debug, Default, Serialize, Deserialize)]
|
||||
pub struct URLCellData {
|
||||
pub url: String,
|
||||
pub data: String,
|
||||
}
|
||||
|
||||
impl URLCellData {
|
||||
pub fn new(s: &str) -> Self {
|
||||
Self {
|
||||
url: "".to_string(),
|
||||
data: s.to_string(),
|
||||
}
|
||||
}
|
||||
@ -36,16 +34,14 @@ impl TypeOptionCellData for URLCellData {
|
||||
|
||||
impl From<&Cell> for URLCellData {
|
||||
fn from(cell: &Cell) -> Self {
|
||||
let url = cell.get_str_value("url").unwrap_or_default();
|
||||
let content = cell.get_str_value(CELL_DATA).unwrap_or_default();
|
||||
Self { url, data: content }
|
||||
let data = cell.get_str_value(CELL_DATA).unwrap_or_default();
|
||||
Self { data }
|
||||
}
|
||||
}
|
||||
|
||||
impl From<URLCellData> for Cell {
|
||||
fn from(data: URLCellData) -> Self {
|
||||
new_cell_builder(FieldType::URL)
|
||||
.insert_str_value("url", data.url)
|
||||
.insert_str_value(CELL_DATA, data.data)
|
||||
.build()
|
||||
}
|
||||
@ -53,19 +49,13 @@ impl From<URLCellData> for Cell {
|
||||
|
||||
impl From<URLCellData> for URLCellDataPB {
|
||||
fn from(data: URLCellData) -> Self {
|
||||
Self {
|
||||
url: data.url,
|
||||
content: data.data,
|
||||
}
|
||||
Self { content: data.data }
|
||||
}
|
||||
}
|
||||
|
||||
impl From<URLCellDataPB> for URLCellData {
|
||||
fn from(data: URLCellDataPB) -> Self {
|
||||
Self {
|
||||
url: data.url,
|
||||
data: data.content,
|
||||
}
|
||||
Self { data: data.content }
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -54,7 +54,7 @@ impl GroupCustomize for URLGroupController {
|
||||
) -> FlowyResult<(Option<InsertedGroupPB>, Option<GroupPB>)> {
|
||||
// Just return if the group with this url already exists
|
||||
let mut inserted_group = None;
|
||||
if self.context.get_group(&_cell_data.url).is_none() {
|
||||
if self.context.get_group(&_cell_data.content).is_none() {
|
||||
let cell_data: URLCellData = _cell_data.clone().into();
|
||||
let group = Group::new(cell_data.data);
|
||||
let mut new_group = self.context.add_new_group(group)?;
|
||||
|
Reference in New Issue
Block a user