Skip to content

Commit

Permalink
Implement Backup client
Browse files Browse the repository at this point in the history
  • Loading branch information
danielgranhao committed Nov 9, 2023
1 parent 2a0885d commit 0e1f6ef
Show file tree
Hide file tree
Showing 13 changed files with 568 additions and 11 deletions.
1 change: 1 addition & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -5,4 +5,5 @@ members = [
"graphql",
"honey-badger",
"mole",
"squirrel",
]
4 changes: 4 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -22,3 +22,7 @@ and always returns the latest state of them when requested.
## Crow
Crows are known for their love to collect stuff.
The library allows to register for and list withdraw collect offers (e.g. Lightning Address).

## Squirrel
Squirrels love to store nuts for the winter.
The library allows backups of local data to be created.
2 changes: 1 addition & 1 deletion graphql/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ edition = "2021"

[dependencies]
chrono = { version = "0.4.24", default-features = false, features = ["std"] }
graphql_client = { version = "0.13.0", features = ["reqwest-blocking"]}
graphql_client = { version = "0.13.0", features = ["reqwest-blocking", "reqwest"]}
log = "0.4.17"
reqwest = { version = "0.11", default-features = false, features = ["json", "blocking", "rustls-tls"]}
serde = { version = "1.0", features = ["derive"]}
Expand Down
14 changes: 14 additions & 0 deletions graphql/schemas/operations.graphql
Original file line number Diff line number Diff line change
Expand Up @@ -172,3 +172,17 @@ query MigrationBalance($nodePubKey: String) {
mutation MigrateFunds($invoice: String, $base16InvoiceSignature: String, $ldkNodePubKey: String) {
migrate_funds(invoice: $invoice, base16InvoiceSignature: $base16InvoiceSignature, ldkNodePubKey: $ldkNodePubKey)
}

mutation CreateBackup($encryptedBackup: String!, $schemaName: String!, $schemaVersion: String!) {
create_backup(encryptedBackup: $encryptedBackup, schemaName: $schemaName, schemaVersion: $schemaVersion) {
updatedAt
}
}

mutation RecoverBackup($schemaName: String!) {
recover_backup(schemaName: $schemaName) {
encryptedBackup
schemaVersion
updatedAt
}
}
17 changes: 17 additions & 0 deletions graphql/schemas/schema_wallet_read.graphql
Original file line number Diff line number Diff line change
Expand Up @@ -802,6 +802,8 @@ type mutation_root {
): accepted_terms_conditions
accept_wallet_acl_by_pk(pk_columns: AcceptWalletPkRequestInput!): WalletAcl

create_backup(encryptedBackup: String!, schemaName: String!, schemaVersion: String!): CreateBackupResponse

"""
delete data from the table: "channel_manager"
"""
Expand Down Expand Up @@ -895,6 +897,7 @@ type mutation_root {
on_conflict: channel_monitor_on_conflict
): channel_monitor
migrate_funds(base16InvoiceSignature: String, invoice: String, ldkNodePubKey: String): Boolean!
recover_backup(schemaName: String!): RecoverBackupResponse
refresh_session(refreshToken: String!): TokenContainer
refresh_session_v2(refreshToken: String!): SessionPermit
register_email(email: String): WalletEmail
Expand Down Expand Up @@ -1787,3 +1790,17 @@ input wallet_acl_stream_cursor_value_input {
ownerWalletPubKeyId: uuid
role: String
}

type CreateBackupResponse {
walletId: String
schemaName: String
schemaVersion: String
updatedAt: DateTime
}

type RecoverBackupResponse {
walletId: String
encryptedBackup: String
schemaVersion: String
updatedAt: DateTime
}
45 changes: 44 additions & 1 deletion graphql/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ pub use crate::errors::*;
pub use perro;
pub use reqwest;

use graphql_client::reqwest::post_graphql_blocking;
use graphql_client::reqwest::{post_graphql, post_graphql_blocking};
use graphql_client::Response;
use perro::{permanent_failure, runtime_error, MapToError, OptionToError};
use reqwest::blocking::Client;
Expand Down Expand Up @@ -37,6 +37,25 @@ pub fn build_client(access_token: Option<&str>) -> Result<Client> {
Ok(client)
}

pub fn build_async_client(access_token: Option<&str>) -> Result<reqwest::Client> {
let user_agent = "graphql-rust/0.12.0";
let timeout = Duration::from_secs(20);

let mut builder = reqwest::Client::builder()
.user_agent(user_agent)
.timeout(timeout);
if let Some(access_token) = access_token {
let value = HeaderValue::from_str(&format!("Bearer {access_token}"))
.map_to_permanent_failure("Failed to build header value from str")?;
builder = builder.default_headers(std::iter::once((AUTHORIZATION, value)).collect());
}

let client = builder
.build()
.map_to_permanent_failure("Failed to build a async reqwest client")?;
Ok(client)
}

pub fn post_blocking<Query: graphql_client::GraphQLQuery>(
client: &Client,
backend_url: &String,
Expand All @@ -61,6 +80,30 @@ pub fn post_blocking<Query: graphql_client::GraphQLQuery>(
get_response_data(response, backend_url)
}

pub async fn post<Query: graphql_client::GraphQLQuery>(
client: &reqwest::Client,
backend_url: &String,
variables: Query::Variables,
) -> Result<Query::ResponseData> {
let response = match post_graphql::<Query, _>(client, backend_url, variables).await {
Ok(r) => r,
Err(e) => {
if is_502_status(e.status()) || e.to_string().contains("502") {
// checking for the error containing 502 because reqwest is unexpectedly returning a decode error instead of status error
return Err(runtime_error(
GraphQlRuntimeErrorCode::RemoteServiceUnavailable,
"The remote server returned status 502",
));
}
return Err(runtime_error(
GraphQlRuntimeErrorCode::NetworkError,
"Failed to execute the query",
));
}
};
get_response_data(response, backend_url)
}

pub fn parse_from_rfc3339(rfc3339: &str) -> Result<SystemTime> {
let datetime = chrono::DateTime::parse_from_rfc3339(rfc3339).map_to_runtime_error(
GraphQlRuntimeErrorCode::CorruptData,
Expand Down
17 changes: 17 additions & 0 deletions graphql/src/schema.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ use graphql_client::GraphQLQuery;

#[allow(non_camel_case_types)]
type bytea = String;
type DateTime = String;
#[allow(non_camel_case_types)]
type numeric = u32;
#[allow(non_camel_case_types)]
Expand Down Expand Up @@ -190,3 +191,19 @@ pub struct MigrationBalance;
response_derives = "Debug"
)]
pub struct MigrateFunds;

#[derive(GraphQLQuery)]
#[graphql(
schema_path = "schemas/schema_wallet_read.graphql",
query_path = "schemas/operations.graphql",
response_derives = "Debug"
)]
pub struct CreateBackup;

#[derive(GraphQLQuery)]
#[graphql(
schema_path = "schemas/schema_wallet_read.graphql",
query_path = "schemas/operations.graphql",
response_derives = "Debug"
)]
pub struct RecoverBackup;
1 change: 1 addition & 0 deletions honey-badger/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ log = "0.4.17"
rand = "0.8.5"
secp256k1 = { version = "0.24.3", features = ["global-context"] }
serde_json = "1.0"
tokio = "1.32.0"

graphql = { path = "../graphql" }

Expand Down
23 changes: 22 additions & 1 deletion honey-badger/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ struct AdjustedToken {

pub struct Auth {
provider: Mutex<AuthProvider>,
async_provider: tokio::sync::Mutex<AuthProvider>,
token: Mutex<AdjustedToken>,
}

Expand All @@ -35,13 +36,16 @@ impl Auth {
wallet_keypair: KeyPair,
auth_keypair: KeyPair,
) -> Result<Self> {
let provider = AuthProvider::new(backend_url, auth_level, wallet_keypair, auth_keypair)?;
let provider = AuthProvider::new(&backend_url, auth_level, &wallet_keypair, &auth_keypair)?;
let async_provider =
AuthProvider::new(&backend_url, auth_level, &wallet_keypair, &auth_keypair)?;
let expired_token = AdjustedToken {
raw: String::new(),
expires_at: SystemTime::UNIX_EPOCH,
};
Ok(Auth {
provider: Mutex::new(provider),
async_provider: tokio::sync::Mutex::new(async_provider),
token: Mutex::new(expired_token),
})
}
Expand All @@ -63,6 +67,23 @@ impl Auth {
.ok_or_permanent_failure("Newly refreshed token is not valid long enough")
}

pub async fn async_query_token(&self) -> Result<String> {
if let Some(token) = self.get_token_if_valid() {
return Ok(token);
}

let mut provider = self.async_provider.lock().await;
// Anyone else refreshed the token by chance?...
if let Some(token) = self.get_token_if_valid() {
return Ok(token);
}

let token = adjust_token(provider.async_query_token().await?)?;
*self.token.lock().unwrap() = token;
self.get_token_if_valid()
.ok_or_permanent_failure("Newly refreshed token is not valid long enough")
}

pub fn get_wallet_pubkey_id(&self) -> Option<String> {
self.provider.lock().unwrap().get_wallet_pubkey_id()
}
Expand Down
Loading

0 comments on commit 0e1f6ef

Please sign in to comment.