Unverified Commit 306e8d3d authored by Vasili Novikov's avatar Vasili Novikov
Browse files

Implement insert_tree API

parent 081cb87f
Showing with 106 additions and 11 deletions
+106 -11
......@@ -196,6 +196,47 @@ Mark an item as deleted:
* Update `dateModified` (server's time is taken)
### POST /v2/$owner_key/insert_tree
```json5
{
"databaseKey": "2DD29CA851E7B56E4697B0E1F08507293D761A05CE4D1B628663F411A8086D99",
"payload": { /* item definition (see below) */ }
}
```
Insert a tree with edges (of arbitrary depth) in one batch.
Each item should either be an object with only `uid` and `_edges` fields:
```json5
{
"uid": 123456789 /* uid of the item to create edge with */,
"_edges": [ /* see below edges definition*/ ]
}
```
Or the full item to be created, in which case `uid` is optional,
but all standard mandatory item fields need to be present:
```json5
{
"_type": "SomeItemType",
"_edges": [ /* see below edges definition*/ ],
/* other item properties here */
}
```
Each edge in the array above is required to have the following form:
```json5
{
"_type": "SomeEdgeType",
"_target": { /* item of identical structure to the above */ }
/* optional edge properties here */
}
```
As always, inserting edges will result in updating timestamps for `_source` items
(even if they are referenced by `uid` only).
The method will return the `uid` of the created root item, e.g. `123456789`.
### POST /v2/$owner_key/search_by_fields/
```json
{
......@@ -204,8 +245,10 @@ Mark an item as deleted:
}
```
Search items by their fields.
Field `_dateServerModifiedAfter` is not treated in the standard way, and instead, it filters
items by their `_dateServerModified` field using the `>` operator.
Ephemeral underscore field `_dateServerModifiedAfter`, if specified,
is treated specially. It will filter out those items that have
`_dateServerModified` higher (`>`) than the specified value.
The endpoint will return an array of all items with exactly the same properties.
......
......@@ -49,6 +49,22 @@ pub struct BulkAction {
pub delete_edges: Vec<DeleteEdge>,
}
#[derive(Serialize, Deserialize, Debug)]
pub struct InsertTreeItem {
#[serde(default)]
pub _edges: Vec<InsertTreeEdge>,
#[serde(flatten)]
pub fields: HashMap<String, Value>,
}
#[derive(Serialize, Deserialize, Debug)]
pub struct InsertTreeEdge {
pub _type: String,
pub _target: InsertTreeItem,
#[serde(flatten)]
pub fields: HashMap<String, Value>,
}
#[derive(Serialize, Deserialize, Debug)]
#[serde(rename_all = "camelCase")]
pub struct SearchByFields {
......
use crate::api_model::BulkAction;
use crate::api_model::CreateItem;
use crate::api_model::DeleteEdge;
use crate::api_model::InsertTreeItem;
use crate::api_model::SearchByFields;
use crate::error::Error;
use crate::error::Result;
......@@ -250,12 +250,25 @@ pub fn bulk_action_tx(tx: &Transaction, bulk_action: BulkAction) -> Result<()> {
Ok(())
}
pub fn create_item(conn: &mut Connection, create_action: CreateItem) -> Result<i64> {
debug!("Creating item {:?}", create_action);
let tx = conn.transaction()?;
let result = create_item_tx(&tx, create_action.fields)?;
tx.commit()?;
Ok(result)
pub fn insert_tree(tx: &Transaction, item: InsertTreeItem) -> Result<i64> {
let source_uid: i64 = if item.fields.len() > 1 {
create_item_tx(tx, item.fields)?
} else if let Some(uid) = item.fields.get("uid").map(|v| v.as_i64()).flatten() {
if !item._edges.is_empty() {
update_item_tx(tx, uid, HashMap::new())?;
}
uid
} else {
return Err(Error {
code: StatusCode::BAD_REQUEST,
msg: format!("Cannot create item: {:?}", item),
});
};
for edge in item._edges {
let target_item = insert_tree(tx, edge._target)?;
create_edge(tx, &edge._type, source_uid, target_item, edge.fields)?;
}
Ok(source_uid)
}
pub fn search_by_fields(tx: &Transaction, query: SearchByFields) -> Result<Vec<Value>> {
......
use crate::api_model::BulkAction;
use crate::api_model::CreateItem;
use crate::api_model::GetFile;
use crate::api_model::InsertTreeItem;
use crate::api_model::PayloadWrapper;
use crate::api_model::RunDownloader;
use crate::api_model::RunImporter;
......@@ -126,7 +127,18 @@ pub async fn run_server(cli_options: &CLIOptions) {
});
let init_db = initialized_databases_arc.clone();
let search = items_api
let insert_tree = items_api
.and(warp::path!(String / "insert_tree"))
.and(warp::path::end())
.and(warp::body::json())
.map(move |owner: String, body: PayloadWrapper<InsertTreeItem>| {
let result = warp_endpoints::insert_tree(owner, init_db.deref(), body);
let result = result.map(|result| warp::reply::json(&result));
respond_with_result(result)
});
let init_db = initialized_databases_arc.clone();
let search_by_fields = items_api
.and(warp::path!(String / "search_by_fields"))
.and(warp::path::end())
.and(warp::body::json())
......@@ -250,7 +262,8 @@ pub async fn run_server(cli_options: &CLIOptions) {
.or(bulk_action.with(&headers))
.or(update_item.with(&headers))
.or(delete_item.with(&headers))
.or(search.with(&headers))
.or(insert_tree.with(&headers))
.or(search_by_fields.with(&headers))
.or(get_items_with_edges.with(&headers))
.or(run_downloader.with(&headers))
.or(run_importer.with(&headers))
......
use crate::api_model::BulkAction;
use crate::api_model::CreateItem;
use crate::api_model::GetFile;
use crate::api_model::InsertTreeItem;
use crate::api_model::PayloadWrapper;
use crate::api_model::RunDownloader;
use crate::api_model::RunImporter;
......@@ -94,6 +95,15 @@ pub fn delete_item(
})
}
pub fn insert_tree(
owner: String,
init_db: &RwLock<HashSet<String>>,
body: PayloadWrapper<InsertTreeItem>,
) -> Result<i64> {
let mut conn: Connection = check_owner_and_initialize_db(&owner, &init_db, &body.database_key)?;
in_transaction(&mut conn, |tx| internal_api::insert_tree(&tx, body.payload))
}
pub fn search_by_fields(
owner: String,
init_db: &RwLock<HashSet<String>>,
......
Supports Markdown
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment