Delete empty labels when they are updated
In order to delete a label, client sends in the request a null value as label value. Co-Authored-by: Antoine Poinsot <darosior@protonmail.com>
This commit is contained in:
parent
a4183dde69
commit
01ca960370
@ -312,8 +312,9 @@ cover the requested feerate.
|
|||||||
|
|
||||||
### `updatelabels`
|
### `updatelabels`
|
||||||
|
|
||||||
Update the labels from a given map of key/value, with the labelled bitcoin addresses, txids and outpoints as keys
|
Update the labels from a given map of key/value, with the labelled bitcoin addresses, txids and
|
||||||
and the label as value. If a label already exist for the given item, the new label overrides the previous one.
|
outpoints as keys and the label as value. If a label already exist for the given item, the new label
|
||||||
|
overrides the previous one. If a `null` value is passed, the label is deleted.
|
||||||
|
|
||||||
#### Request
|
#### Request
|
||||||
|
|
||||||
|
|||||||
@ -592,7 +592,7 @@ impl DaemonControl {
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn update_labels(&self, items: &HashMap<LabelItem, String>) {
|
pub fn update_labels(&self, items: &HashMap<LabelItem, Option<String>>) {
|
||||||
let mut db_conn = self.db.connection();
|
let mut db_conn = self.db.connection();
|
||||||
db_conn.update_labels(items);
|
db_conn.update_labels(items);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -129,7 +129,9 @@ pub trait DatabaseConnection {
|
|||||||
/// Delete a Spend transaction from database.
|
/// Delete a Spend transaction from database.
|
||||||
fn delete_spend(&mut self, txid: &bitcoin::Txid);
|
fn delete_spend(&mut self, txid: &bitcoin::Txid);
|
||||||
|
|
||||||
fn update_labels(&mut self, items: &HashMap<LabelItem, String>);
|
/// Update, for a set of items (as key), their label (as value). A `None` value deletes the
|
||||||
|
/// label.
|
||||||
|
fn update_labels(&mut self, items: &HashMap<LabelItem, Option<String>>);
|
||||||
|
|
||||||
fn labels(&mut self, labels: &HashSet<LabelItem>) -> HashMap<String, String>;
|
fn labels(&mut self, labels: &HashSet<LabelItem>) -> HashMap<String, String>;
|
||||||
|
|
||||||
@ -275,7 +277,7 @@ impl DatabaseConnection for SqliteConn {
|
|||||||
self.delete_spend(txid)
|
self.delete_spend(txid)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn update_labels(&mut self, items: &HashMap<LabelItem, String>) {
|
fn update_labels(&mut self, items: &HashMap<LabelItem, Option<String>>) {
|
||||||
self.update_labels(items)
|
self.update_labels(items)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -585,7 +585,7 @@ impl SqliteConn {
|
|||||||
.expect("Db must not fail")
|
.expect("Db must not fail")
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn update_labels(&mut self, items: &HashMap<LabelItem, String>) {
|
pub fn update_labels(&mut self, items: &HashMap<LabelItem, Option<String>>) {
|
||||||
db_exec(&mut self.conn, |db_tx| {
|
db_exec(&mut self.conn, |db_tx| {
|
||||||
for (labelled, kind, value) in items
|
for (labelled, kind, value) in items
|
||||||
.iter()
|
.iter()
|
||||||
@ -596,11 +596,18 @@ impl SqliteConn {
|
|||||||
LabelItem::OutPoint(a) =>(a.to_string(), DbLabelledKind::OutPoint, v),
|
LabelItem::OutPoint(a) =>(a.to_string(), DbLabelledKind::OutPoint, v),
|
||||||
}
|
}
|
||||||
}) {
|
}) {
|
||||||
|
if let Some(value) = value {
|
||||||
db_tx.execute(
|
db_tx.execute(
|
||||||
"INSERT INTO labels (wallet_id, item, item_kind, value) VALUES (?1, ?2, ?3, ?4) \
|
"INSERT INTO labels (wallet_id, item, item_kind, value) VALUES (?1, ?2, ?3, ?4) \
|
||||||
ON CONFLICT DO UPDATE SET value=excluded.value",
|
ON CONFLICT DO UPDATE SET value=excluded.value",
|
||||||
rusqlite::params![WALLET_ID, labelled, kind as i64, value],
|
rusqlite::params![WALLET_ID, labelled, kind as i64, value],
|
||||||
)?;
|
)?;
|
||||||
|
} else {
|
||||||
|
db_tx.execute(
|
||||||
|
"DELETE FROM labels WHERE wallet_id = ?1 AND item = ?2",
|
||||||
|
rusqlite::params![WALLET_ID, labelled],
|
||||||
|
)?;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
})
|
})
|
||||||
@ -895,18 +902,24 @@ CREATE TABLE spend_transactions (
|
|||||||
assert!(db_labels.is_empty());
|
assert!(db_labels.is_empty());
|
||||||
|
|
||||||
let mut txids_labels = HashMap::new();
|
let mut txids_labels = HashMap::new();
|
||||||
txids_labels.insert(txid.clone(), "hello".to_string());
|
txids_labels.insert(txid.clone(), Some("hello".to_string()));
|
||||||
|
|
||||||
conn.update_labels(&txids_labels);
|
conn.update_labels(&txids_labels);
|
||||||
|
|
||||||
let db_labels = conn.db_labels(&items);
|
let db_labels = conn.db_labels(&items);
|
||||||
assert_eq!(db_labels[0].value, "hello");
|
assert_eq!(db_labels[0].value, "hello");
|
||||||
|
|
||||||
txids_labels.insert(txid, "hello again".to_string());
|
txids_labels.insert(txid.clone(), Some("hello again".to_string()));
|
||||||
conn.update_labels(&txids_labels);
|
conn.update_labels(&txids_labels);
|
||||||
|
|
||||||
let db_labels = conn.db_labels(&items);
|
let db_labels = conn.db_labels(&items);
|
||||||
assert_eq!(db_labels[0].value, "hello again");
|
assert_eq!(db_labels[0].value, "hello again");
|
||||||
|
|
||||||
|
// Now delete the label by passing a None value.
|
||||||
|
*txids_labels.get_mut(&txid).unwrap() = None;
|
||||||
|
conn.update_labels(&txids_labels);
|
||||||
|
let db_labels = conn.db_labels(&items);
|
||||||
|
assert!(db_labels.is_empty());
|
||||||
}
|
}
|
||||||
|
|
||||||
fs::remove_dir_all(tmp_dir).unwrap();
|
fs::remove_dir_all(tmp_dir).unwrap();
|
||||||
@ -2102,7 +2115,7 @@ CREATE TABLE spend_transactions (
|
|||||||
let txid_str = "0c62a990d20d54429e70859292e82374ba6b1b951a3ab60f26bb65fee5724ff7";
|
let txid_str = "0c62a990d20d54429e70859292e82374ba6b1b951a3ab60f26bb65fee5724ff7";
|
||||||
let txid = LabelItem::from_str(txid_str, bitcoin::Network::Bitcoin).unwrap();
|
let txid = LabelItem::from_str(txid_str, bitcoin::Network::Bitcoin).unwrap();
|
||||||
let mut txids_labels = HashMap::new();
|
let mut txids_labels = HashMap::new();
|
||||||
txids_labels.insert(txid.clone(), "hello".to_string());
|
txids_labels.insert(txid.clone(), Some("hello".to_string()));
|
||||||
conn.update_labels(&txids_labels);
|
conn.update_labels(&txids_labels);
|
||||||
|
|
||||||
let mut items = HashSet::new();
|
let mut items = HashSet::new();
|
||||||
|
|||||||
@ -223,16 +223,15 @@ fn update_labels(control: &DaemonControl, params: Params) -> Result<serde_json::
|
|||||||
.ok_or_else(|| Error::invalid_params("Invalid 'labels' parameter."))?
|
.ok_or_else(|| Error::invalid_params("Invalid 'labels' parameter."))?
|
||||||
.iter()
|
.iter()
|
||||||
{
|
{
|
||||||
let value = value
|
let value = value.as_str().map(|s| s.to_string());
|
||||||
.as_str()
|
if let Some(value) = &value {
|
||||||
.map(|s| s.to_string())
|
|
||||||
.ok_or_else(|| Error::invalid_params(format!("Invalid 'labels.{}' value.", item)))?;
|
|
||||||
if value.len() > 100 {
|
if value.len() > 100 {
|
||||||
return Err(Error::invalid_params(format!(
|
return Err(Error::invalid_params(format!(
|
||||||
"Invalid 'labels.{}' value length: must be less or equal than 100 characters",
|
"Invalid 'labels.{}' value length: must be less or equal than 100 characters",
|
||||||
item
|
item
|
||||||
)));
|
)));
|
||||||
}
|
}
|
||||||
|
}
|
||||||
let item =
|
let item =
|
||||||
LabelItem::from_str(item, control.config.bitcoin_config.network).ok_or_else(|| {
|
LabelItem::from_str(item, control.config.bitcoin_config.network).ok_or_else(|| {
|
||||||
Error::invalid_params(format!(
|
Error::invalid_params(format!(
|
||||||
|
|||||||
@ -363,7 +363,7 @@ impl DatabaseConnection for DummyDatabase {
|
|||||||
todo!()
|
todo!()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn update_labels(&mut self, _items: &HashMap<LabelItem, String>) {
|
fn update_labels(&mut self, _items: &HashMap<LabelItem, Option<String>>) {
|
||||||
todo!()
|
todo!()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -952,3 +952,17 @@ def test_labels(lianad, bitcoind):
|
|||||||
assert res[inexistent_txid] == "inex_txid"
|
assert res[inexistent_txid] == "inex_txid"
|
||||||
assert res[inexistent_outpoint] == "inex_outpoint"
|
assert res[inexistent_outpoint] == "inex_outpoint"
|
||||||
assert res[random_address] == "bitcoind-addr"
|
assert res[random_address] == "bitcoind-addr"
|
||||||
|
|
||||||
|
# Delete 2 of the labels set above. They shouldn't be returned anymore.
|
||||||
|
lianad.rpc.updatelabels(
|
||||||
|
{
|
||||||
|
addr: None,
|
||||||
|
sec_addr: None,
|
||||||
|
random_address: "this address is random",
|
||||||
|
}
|
||||||
|
)
|
||||||
|
res = lianad.rpc.getlabels([addr, sec_addr, random_address])["labels"]
|
||||||
|
assert len(res) == 1
|
||||||
|
assert addr not in res
|
||||||
|
assert sec_addr not in res
|
||||||
|
assert res[random_address] == "this address is random"
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user