-
Notifications
You must be signed in to change notification settings - Fork 59
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
TerminateAgreement endpoint #832
Changes from 14 commits
1aa89e7
476e5d0
8682a4d
26c3cdf
b613f0b
f88d581
2dc6545
22d2549
d0c043d
4db7abc
6bdd90e
72c1b4f
84d5e54
6394a2f
ba18112
28b9a0f
d0c4881
daccccb
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Large diffs are not rendered by default.
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -5,14 +5,17 @@ use std::sync::Arc; | |
use std::time::{Duration, Instant}; | ||
|
||
use ya_client::model::market::proposal::Proposal as ClientProposal; | ||
use ya_client::model::market::reason::Reason; | ||
use ya_client::model::market::NewProposal; | ||
use ya_client::model::NodeId; | ||
use ya_market_resolver::{match_demand_offer, Match}; | ||
use ya_persistence::executor::DbExecutor; | ||
use ya_service_api_web::middleware::Identity; | ||
|
||
use crate::config::Config; | ||
use crate::db::dao::{AgreementEventsDao, NegotiationEventsDao, ProposalDao, SaveProposalError}; | ||
use crate::db::dao::{ | ||
AgreementDao, AgreementEventsDao, NegotiationEventsDao, ProposalDao, SaveProposalError, | ||
}; | ||
use crate::db::model::{ | ||
Agreement, AgreementEvent, AgreementId, AgreementState, AppSessionId, IssuerType, MarketEvent, | ||
OwnerType, Proposal, | ||
|
@@ -22,14 +25,19 @@ use crate::matcher::{ | |
error::{DemandError, QueryOfferError}, | ||
store::SubscriptionStore, | ||
}; | ||
use crate::negotiation::error::{AgreementEventsError, AgreementStateError, GetProposalError}; | ||
use crate::negotiation::notifier::NotifierError; | ||
use crate::negotiation::{ | ||
error::{MatchValidationError, ProposalError, QueryEventsError}, | ||
error::{ | ||
AgreementError, AgreementEventsError, AgreementStateError, GetProposalError, | ||
MatchValidationError, ProposalError, QueryEventsError, ReasonError, | ||
}, | ||
notifier::NotifierError, | ||
EventNotifier, | ||
}; | ||
use crate::protocol::negotiation::error::{CounterProposalError, RemoteProposalError}; | ||
use crate::protocol::negotiation::messages::ProposalReceived; | ||
use crate::protocol::negotiation::common as protocol_common; | ||
use crate::protocol::negotiation::error::{ | ||
CounterProposalError, RemoteAgreementError, RemoteProposalError, TerminateAgreementError, | ||
}; | ||
use crate::protocol::negotiation::messages::{AgreementTerminated, ProposalReceived}; | ||
|
||
type IsFirst = bool; | ||
|
||
|
@@ -279,6 +287,134 @@ impl CommonBroker { | |
}) | ||
} | ||
|
||
// Called locally via REST | ||
pub async fn terminate_agreement( | ||
&self, | ||
id: Identity, | ||
agreement_id: AgreementId, | ||
reason: Option<String>, | ||
jiivan marked this conversation as resolved.
Show resolved
Hide resolved
|
||
) -> Result<(), AgreementError> { | ||
verify_reason(reason.as_ref())?; | ||
let dao = self.db.as_dao::<AgreementDao>(); | ||
log::debug!( | ||
"Getting agreement. id: {:?}, agrid: {}, reason: {:?}", | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Please don't display using debug if it is possible |
||
id, | ||
agreement_id, | ||
reason | ||
); // XXX | ||
let mut agreement = match dao | ||
.select_by_node( | ||
agreement_id.clone(), | ||
id.identity.clone(), | ||
Utc::now().naive_utc(), | ||
) | ||
.await | ||
.map_err(|e| AgreementError::Get(agreement_id.clone(), e))? | ||
{ | ||
None => return Err(AgreementError::NotFound(agreement_id)), | ||
Some(agreement) => agreement, | ||
}; | ||
// from now on agreement_id is invalid. Use only agreement.id | ||
// (which has valid owner) | ||
expect_state(&agreement, AgreementState::Approved)?; | ||
agreement.state = AgreementState::Terminated; | ||
let owner_type = agreement.id.owner(); | ||
protocol_common::propagate_terminate_agreement( | ||
&agreement, | ||
id.identity.clone(), | ||
match owner_type { | ||
OwnerType::Requestor => agreement.provider_id, | ||
OwnerType::Provider => agreement.requestor_id, | ||
}, | ||
reason.clone(), | ||
) | ||
.await?; | ||
dao.terminate(&agreement.id, reason, owner_type) | ||
.await | ||
.map_err(|e| AgreementError::Get(agreement.id.clone(), e))?; | ||
|
||
match owner_type { | ||
OwnerType::Provider => counter!("market.agreements.provider.terminated", 1), | ||
OwnerType::Requestor => counter!("market.agreements.requestor.terminated", 1), | ||
}; | ||
log::info!( | ||
"Requestor {} terminated Agreement [{}] and sent to Provider.", | ||
&id.identity, | ||
&agreement.id, | ||
); | ||
Ok(()) | ||
} | ||
|
||
// Called remotely via GSB | ||
pub async fn on_agreement_terminated( | ||
self, | ||
caller: String, | ||
msg: AgreementTerminated, | ||
owner_type: OwnerType, | ||
) -> Result<(), TerminateAgreementError> { | ||
let caller: NodeId = | ||
caller | ||
.parse() | ||
jiivan marked this conversation as resolved.
Show resolved
Hide resolved
|
||
.map_err(|e: ya_client::model::node_id::ParseError| { | ||
TerminateAgreementError::CallerParseError { | ||
e: e.to_string(), | ||
caller, | ||
id: msg.agreement_id.clone(), | ||
} | ||
})?; | ||
Ok(self | ||
.on_agreement_terminated_inner(caller, msg, owner_type) | ||
.await?) | ||
} | ||
|
||
async fn on_agreement_terminated_inner( | ||
self, | ||
caller: NodeId, | ||
msg: AgreementTerminated, | ||
owner_type: OwnerType, | ||
) -> Result<(), RemoteAgreementError> { | ||
let dao = self.db.as_dao::<AgreementDao>(); | ||
let agreement_id = msg.agreement_id.translate(owner_type); | ||
let agreement = dao | ||
.select(&agreement_id, None, Utc::now().naive_utc()) | ||
.await | ||
.map_err(|_e| RemoteAgreementError::NotFound(agreement_id.clone()))? | ||
.ok_or(RemoteAgreementError::NotFound(agreement_id.clone()))?; | ||
|
||
match owner_type { | ||
OwnerType::Requestor => { | ||
if agreement.provider_id != caller { | ||
// Don't reveal, that we know this Agreement id. | ||
Err(RemoteAgreementError::NotFound(agreement_id.clone()))? | ||
} | ||
} | ||
OwnerType::Provider => { | ||
if agreement.requestor_id != caller { | ||
// Don't reveal, that we know this Agreement id. | ||
Err(RemoteAgreementError::NotFound(agreement_id.clone()))? | ||
} | ||
} | ||
} | ||
|
||
// Opposite side terminated. | ||
let terminator = match owner_type { | ||
OwnerType::Provider => OwnerType::Requestor, | ||
OwnerType::Requestor => OwnerType::Provider, | ||
}; | ||
|
||
dao.terminate(&agreement_id, msg.reason, terminator) | ||
.await | ||
.map_err(|e| { | ||
log::info!( | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I would say it is even |
||
"Couldn't terminate agreement. id: {}, e: {}", | ||
agreement_id, | ||
e | ||
); | ||
RemoteAgreementError::InternalError(agreement_id.clone()) | ||
})?; | ||
Ok(()) | ||
} | ||
|
||
// TODO: We need more elegant solution than this. This function still returns | ||
// CounterProposalError, which should be hidden in negotiation API and implementations | ||
// of handlers should return RemoteProposalError. | ||
|
@@ -445,3 +581,9 @@ pub fn expect_state( | |
AgreementState::Terminated => AgreementStateError::Terminated(agreement.id.clone()), | ||
})? | ||
} | ||
|
||
fn verify_reason(reason: Option<&String>) -> Result<(), ReasonError> { | ||
Ok(if let Some(s) = reason { | ||
serde_json::from_str::<Reason>(s)?; | ||
}) | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This will be ugly log:
Some("Reason")
Maybe you should implement something like
.display()