mirror of
https://github.com/juspay/hyperswitch.git
synced 2025-10-28 04:04:55 +08:00
feat(router): add retrieve poll status api (#4358)
Co-authored-by: hyperswitch-bot[bot] <148525504+hyperswitch-bot[bot]@users.noreply.github.com>
This commit is contained in:
committed by
GitHub
parent
5a0a8cee76
commit
ca47ea9b13
@ -25,6 +25,7 @@ pub mod payments;
|
||||
#[cfg(feature = "payouts")]
|
||||
pub mod payouts;
|
||||
pub mod pm_auth;
|
||||
pub mod poll;
|
||||
#[cfg(feature = "recon")]
|
||||
pub mod recon;
|
||||
pub mod refunds;
|
||||
|
||||
28
crates/api_models/src/poll.rs
Normal file
28
crates/api_models/src/poll.rs
Normal file
@ -0,0 +1,28 @@
|
||||
use common_utils::events::{ApiEventMetric, ApiEventsType};
|
||||
use serde::Serialize;
|
||||
use utoipa::ToSchema;
|
||||
|
||||
#[derive(Debug, ToSchema, Clone, Serialize)]
|
||||
pub struct PollResponse {
|
||||
/// The poll id
|
||||
pub poll_id: String,
|
||||
/// Status of the poll
|
||||
pub status: PollStatus,
|
||||
}
|
||||
|
||||
#[derive(Debug, strum::Display, strum::EnumString, Clone, serde::Serialize, ToSchema)]
|
||||
#[strum(serialize_all = "snake_case")]
|
||||
#[serde(rename_all = "snake_case")]
|
||||
pub enum PollStatus {
|
||||
Pending,
|
||||
Completed,
|
||||
NotFound,
|
||||
}
|
||||
|
||||
impl ApiEventMetric for PollResponse {
|
||||
fn get_api_event_type(&self) -> Option<ApiEventsType> {
|
||||
Some(ApiEventsType::Poll {
|
||||
poll_id: self.poll_id.clone(),
|
||||
})
|
||||
}
|
||||
}
|
||||
@ -56,6 +56,9 @@ pub enum ApiEventsType {
|
||||
Events {
|
||||
merchant_id_or_profile_id: String,
|
||||
},
|
||||
Poll {
|
||||
poll_id: String,
|
||||
},
|
||||
}
|
||||
|
||||
impl ApiEventMetric for serde_json::Value {}
|
||||
|
||||
@ -178,6 +178,9 @@ Never share your secret api keys. Keep them guarded and secure.
|
||||
routes::webhook_events::list_initial_webhook_delivery_attempts,
|
||||
routes::webhook_events::list_webhook_delivery_attempts,
|
||||
routes::webhook_events::retry_webhook_delivery_attempt,
|
||||
|
||||
// Routes for poll apis
|
||||
routes::poll::retrieve_poll_status,
|
||||
),
|
||||
components(schemas(
|
||||
api_models::refunds::RefundRequest,
|
||||
@ -206,6 +209,8 @@ Never share your secret api keys. Keep them guarded and secure.
|
||||
api_models::payment_methods::CardDetail,
|
||||
api_models::payment_methods::CardDetailUpdate,
|
||||
api_models::payment_methods::RequestPaymentMethodTypes,
|
||||
api_models::poll::PollResponse,
|
||||
api_models::poll::PollStatus,
|
||||
api_models::customers::CustomerResponse,
|
||||
api_models::admin::AcceptedCountries,
|
||||
api_models::admin::AcceptedCurrencies,
|
||||
|
||||
@ -13,11 +13,12 @@ pub mod payment_link;
|
||||
pub mod payment_method;
|
||||
pub mod payments;
|
||||
pub mod payouts;
|
||||
pub mod poll;
|
||||
pub mod refunds;
|
||||
pub mod routing;
|
||||
pub mod webhook_events;
|
||||
|
||||
pub use self::{
|
||||
customers::*, mandates::*, merchant_account::*, merchant_connector_account::*,
|
||||
payment_method::*, payments::*, refunds::*, routing::*, webhook_events::*,
|
||||
payment_method::*, payments::*, poll::*, refunds::*, routing::*, webhook_events::*,
|
||||
};
|
||||
|
||||
15
crates/openapi/src/routes/poll.rs
Normal file
15
crates/openapi/src/routes/poll.rs
Normal file
@ -0,0 +1,15 @@
|
||||
/// Poll - Retrieve Poll Status
|
||||
#[utoipa::path(
|
||||
get,
|
||||
path = "/poll/status/{poll_id}",
|
||||
params(
|
||||
("poll_id" = String, Path, description = "The identifier for poll")
|
||||
),
|
||||
responses(
|
||||
(status = 200, description = "The poll status was retrieved successfully", body = PollResponse)
|
||||
),
|
||||
tag = "Poll",
|
||||
operation_id = "Retrieve Poll Status",
|
||||
security(("publishable_key" = []))
|
||||
)]
|
||||
pub async fn retrieve_poll_status() {}
|
||||
@ -599,6 +599,10 @@ impl From<errors::ApiErrorResponse> for StripeErrorCode {
|
||||
object: "business_profile".to_owned(),
|
||||
id,
|
||||
},
|
||||
errors::ApiErrorResponse::PollNotFound { id } => Self::ResourceMissing {
|
||||
object: "poll".to_owned(),
|
||||
id,
|
||||
},
|
||||
errors::ApiErrorResponse::DisputeStatusValidationFailed { reason } => {
|
||||
Self::InternalServerError
|
||||
}
|
||||
|
||||
@ -28,6 +28,7 @@ pub mod payments;
|
||||
#[cfg(feature = "payouts")]
|
||||
pub mod payouts;
|
||||
pub mod pm_auth;
|
||||
pub mod poll;
|
||||
pub mod refunds;
|
||||
pub mod routing;
|
||||
pub mod surcharge_decision_config;
|
||||
|
||||
@ -166,6 +166,8 @@ pub enum ApiErrorResponse {
|
||||
MerchantConnectorAccountNotFound { id: String },
|
||||
#[error(error_type = ErrorType::ObjectNotFound, code = "HE_02", message = "Business profile with the given id '{id}' does not exist in our records")]
|
||||
BusinessProfileNotFound { id: String },
|
||||
#[error(error_type = ErrorType::ObjectNotFound, code = "HE_02", message = "Poll with the given id '{id}' does not exist in our records")]
|
||||
PollNotFound { id: String },
|
||||
#[error(error_type = ErrorType::ObjectNotFound, code = "HE_02", message = "Resource ID does not exist in our records")]
|
||||
ResourceIdNotFound,
|
||||
#[error(error_type = ErrorType::ObjectNotFound, code = "HE_02", message = "Mandate does not exist in our records")]
|
||||
|
||||
@ -238,6 +238,9 @@ impl ErrorSwitch<api_models::errors::types::ApiErrorResponse> for ApiErrorRespon
|
||||
Self::FileNotFound => {
|
||||
AER::NotFound(ApiError::new("HE", 2, "File does not exist in our records", None))
|
||||
}
|
||||
Self::PollNotFound { .. } => {
|
||||
AER::NotFound(ApiError::new("HE", 2, "Poll does not exist in our records", None))
|
||||
},
|
||||
Self::FileNotAvailable => {
|
||||
AER::NotFound(ApiError::new("HE", 2, "File not available", None))
|
||||
}
|
||||
|
||||
45
crates/router/src/core/poll.rs
Normal file
45
crates/router/src/core/poll.rs
Normal file
@ -0,0 +1,45 @@
|
||||
use api_models::poll::PollResponse;
|
||||
use common_utils::ext_traits::StringExt;
|
||||
use error_stack::ResultExt;
|
||||
use router_env::{instrument, tracing};
|
||||
|
||||
use super::errors;
|
||||
use crate::{core::errors::RouterResponse, services::ApplicationResponse, types::domain, AppState};
|
||||
|
||||
#[instrument(skip_all)]
|
||||
pub async fn retrieve_poll_status(
|
||||
state: AppState,
|
||||
req: crate::types::api::PollId,
|
||||
merchant_account: domain::MerchantAccount,
|
||||
) -> RouterResponse<PollResponse> {
|
||||
let redis_conn = state
|
||||
.store
|
||||
.get_redis_conn()
|
||||
.change_context(errors::ApiErrorResponse::InternalServerError)
|
||||
.attach_printable("Failed to get redis connection")?;
|
||||
let request_poll_id = req.poll_id;
|
||||
// prepend 'poll_{merchant_id}_' to restrict access to only fetching Poll IDs, as this is a freely passed string in the request
|
||||
let poll_id = format!("poll_{}_{}", merchant_account.merchant_id, request_poll_id);
|
||||
let redis_value = redis_conn
|
||||
.get_key::<Option<String>>(poll_id.as_str())
|
||||
.await
|
||||
.change_context(errors::ApiErrorResponse::InternalServerError)
|
||||
.attach_printable_lazy(|| {
|
||||
format!(
|
||||
"Error while fetching the value for {} from redis",
|
||||
poll_id.clone()
|
||||
)
|
||||
})?
|
||||
.ok_or(errors::ApiErrorResponse::PollNotFound {
|
||||
id: request_poll_id.clone(),
|
||||
})?;
|
||||
let status = redis_value
|
||||
.parse_enum("PollStatus")
|
||||
.change_context(errors::ApiErrorResponse::InternalServerError)
|
||||
.attach_printable("Error while parsing PollStatus")?;
|
||||
let poll_response = PollResponse {
|
||||
poll_id: request_poll_id,
|
||||
status,
|
||||
};
|
||||
Ok(ApplicationResponse::Json(poll_response))
|
||||
}
|
||||
@ -20,7 +20,7 @@ use crate::{
|
||||
PaymentLinkFormData,
|
||||
},
|
||||
types::api::{
|
||||
AttachEvidenceRequest, Config, ConfigUpdate, CreateFileRequest, DisputeId, FileId,
|
||||
AttachEvidenceRequest, Config, ConfigUpdate, CreateFileRequest, DisputeId, FileId, PollId,
|
||||
},
|
||||
};
|
||||
|
||||
@ -150,3 +150,11 @@ impl ApiEventMetric for DisputeId {
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl ApiEventMetric for PollId {
|
||||
fn get_api_event_type(&self) -> Option<ApiEventsType> {
|
||||
Some(ApiEventsType::Poll {
|
||||
poll_id: self.poll_id.clone(),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@ -125,6 +125,7 @@ pub fn mk_app(
|
||||
.service(routes::EphemeralKey::server(state.clone()))
|
||||
.service(routes::Webhooks::server(state.clone()))
|
||||
.service(routes::PaymentMethods::server(state.clone()))
|
||||
.service(routes::Poll::server(state.clone()))
|
||||
}
|
||||
|
||||
#[cfg(feature = "olap")]
|
||||
|
||||
@ -31,6 +31,7 @@ pub mod payments;
|
||||
pub mod payouts;
|
||||
#[cfg(any(feature = "olap", feature = "oltp"))]
|
||||
pub mod pm_auth;
|
||||
pub mod poll;
|
||||
#[cfg(feature = "recon")]
|
||||
pub mod recon;
|
||||
pub mod refunds;
|
||||
@ -59,7 +60,7 @@ pub use self::app::Recon;
|
||||
pub use self::app::{
|
||||
ApiKeys, AppState, BusinessProfile, Cache, Cards, Configs, ConnectorOnboarding, Customers,
|
||||
Disputes, EphemeralKey, Files, Gsm, Health, Mandates, MerchantAccount,
|
||||
MerchantConnectorAccount, PaymentLink, PaymentMethods, Payments, Refunds, User, Webhooks,
|
||||
MerchantConnectorAccount, PaymentLink, PaymentMethods, Payments, Poll, Refunds, User, Webhooks,
|
||||
};
|
||||
#[cfg(feature = "olap")]
|
||||
pub use self::app::{Blocklist, Routing, Verify, WebhookEvents};
|
||||
|
||||
@ -23,8 +23,6 @@ use super::blocklist;
|
||||
use super::dummy_connector::*;
|
||||
#[cfg(feature = "payouts")]
|
||||
use super::payouts::*;
|
||||
#[cfg(feature = "oltp")]
|
||||
use super::pm_auth;
|
||||
#[cfg(feature = "olap")]
|
||||
use super::routing as cloud_routing;
|
||||
#[cfg(feature = "olap")]
|
||||
@ -41,6 +39,8 @@ use super::{configs::*, customers::*, mandates::*, payments::*, refunds::*};
|
||||
use super::{currency, payment_methods::*};
|
||||
#[cfg(feature = "oltp")]
|
||||
use super::{ephemeral_key::*, webhooks::*};
|
||||
#[cfg(feature = "oltp")]
|
||||
use super::{pm_auth, poll::retrieve_poll_status};
|
||||
use crate::configs::secrets_transformers;
|
||||
#[cfg(all(feature = "frm", feature = "oltp"))]
|
||||
use crate::routes::fraud_check as frm_routes;
|
||||
@ -978,6 +978,17 @@ impl Configs {
|
||||
}
|
||||
}
|
||||
|
||||
pub struct Poll;
|
||||
|
||||
#[cfg(feature = "oltp")]
|
||||
impl Poll {
|
||||
pub fn server(config: AppState) -> Scope {
|
||||
web::scope("/poll")
|
||||
.app_data(web::Data::new(config))
|
||||
.service(web::resource("/status/{poll_id}").route(web::get().to(retrieve_poll_status)))
|
||||
}
|
||||
}
|
||||
|
||||
pub struct ApiKeys;
|
||||
|
||||
#[cfg(feature = "olap")]
|
||||
|
||||
@ -34,6 +34,7 @@ pub enum ApiIdentifier {
|
||||
UserRole,
|
||||
ConnectorOnboarding,
|
||||
Recon,
|
||||
Poll,
|
||||
}
|
||||
|
||||
impl From<Flow> for ApiIdentifier {
|
||||
@ -228,6 +229,8 @@ impl From<Flow> for ApiIdentifier {
|
||||
| Flow::ReconServiceRequest
|
||||
| Flow::ReconVerifyToken => Self::Recon,
|
||||
Flow::CreateConnectorAgnosticMandateConfig => Self::Routing,
|
||||
|
||||
Flow::RetrievePollStatus => Self::Poll,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
45
crates/router/src/routes/poll.rs
Normal file
45
crates/router/src/routes/poll.rs
Normal file
@ -0,0 +1,45 @@
|
||||
use actix_web::{web, HttpRequest, HttpResponse};
|
||||
use router_env::{instrument, tracing, Flow};
|
||||
|
||||
use super::app::AppState;
|
||||
use crate::{
|
||||
core::{api_locking, poll},
|
||||
services::{api, authentication as auth},
|
||||
types::api::PollId,
|
||||
};
|
||||
|
||||
/// Poll - Retrieve Poll Status
|
||||
#[utoipa::path(
|
||||
get,
|
||||
path = "/poll/status/{poll_id}",
|
||||
params(
|
||||
("poll_id" = String, Path, description = "The identifier for poll")
|
||||
),
|
||||
responses(
|
||||
(status = 200, description = "The poll status was retrieved successfully", body = PollResponse)
|
||||
),
|
||||
tag = "Poll",
|
||||
operation_id = "Retrieve Poll Status",
|
||||
security(("publishable_key" = []))
|
||||
)]
|
||||
#[instrument(skip_all, fields(flow = ?Flow::RetrievePollStatus))]
|
||||
pub async fn retrieve_poll_status(
|
||||
state: web::Data<AppState>,
|
||||
req: HttpRequest,
|
||||
path: web::Path<String>,
|
||||
) -> HttpResponse {
|
||||
let flow = Flow::RetrievePollStatus;
|
||||
let poll_id = PollId {
|
||||
poll_id: path.into_inner(),
|
||||
};
|
||||
api::server_wrap(
|
||||
flow,
|
||||
state,
|
||||
&req,
|
||||
poll_id,
|
||||
|state, auth, req, _| poll::retrieve_poll_status(state, req, auth.merchant_account),
|
||||
&auth::PublishableKeyAuth,
|
||||
api_locking::LockAction::NotApplicable,
|
||||
)
|
||||
.await
|
||||
}
|
||||
@ -17,6 +17,7 @@ pub mod payment_methods;
|
||||
pub mod payments;
|
||||
#[cfg(feature = "payouts")]
|
||||
pub mod payouts;
|
||||
pub mod poll;
|
||||
pub mod refunds;
|
||||
pub mod routing;
|
||||
#[cfg(feature = "olap")]
|
||||
@ -35,7 +36,7 @@ pub use self::fraud_check::*;
|
||||
pub use self::payouts::*;
|
||||
pub use self::{
|
||||
admin::*, api_keys::*, authentication::*, configs::*, customers::*, disputes::*, files::*,
|
||||
payment_link::*, payment_methods::*, payments::*, refunds::*, webhooks::*,
|
||||
payment_link::*, payment_methods::*, payments::*, poll::*, refunds::*, webhooks::*,
|
||||
};
|
||||
use super::ErrorResponse;
|
||||
use crate::{
|
||||
|
||||
6
crates/router/src/types/api/poll.rs
Normal file
6
crates/router/src/types/api/poll.rs
Normal file
@ -0,0 +1,6 @@
|
||||
use serde;
|
||||
|
||||
#[derive(Default, Debug, serde::Deserialize, serde::Serialize)]
|
||||
pub struct PollId {
|
||||
pub poll_id: String,
|
||||
}
|
||||
@ -402,6 +402,8 @@ pub enum Flow {
|
||||
WebhookEventDeliveryAttemptList,
|
||||
/// Manually retry the delivery for a webhook event
|
||||
WebhookEventDeliveryRetry,
|
||||
/// Retrieve status of the Poll
|
||||
RetrievePollStatus,
|
||||
}
|
||||
|
||||
///
|
||||
|
||||
@ -4516,6 +4516,44 @@
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
"/poll/status/{poll_id}": {
|
||||
"get": {
|
||||
"tags": [
|
||||
"Poll"
|
||||
],
|
||||
"summary": "Poll - Retrieve Poll Status",
|
||||
"description": "Poll - Retrieve Poll Status",
|
||||
"operationId": "Retrieve Poll Status",
|
||||
"parameters": [
|
||||
{
|
||||
"name": "poll_id",
|
||||
"in": "path",
|
||||
"description": "The identifier for poll",
|
||||
"required": true,
|
||||
"schema": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "The poll status was retrieved successfully",
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"$ref": "#/components/schemas/PollResponse"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"security": [
|
||||
{
|
||||
"publishable_key": []
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
},
|
||||
"components": {
|
||||
@ -16229,6 +16267,30 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"PollResponse": {
|
||||
"type": "object",
|
||||
"required": [
|
||||
"poll_id",
|
||||
"status"
|
||||
],
|
||||
"properties": {
|
||||
"poll_id": {
|
||||
"type": "string",
|
||||
"description": "The poll id"
|
||||
},
|
||||
"status": {
|
||||
"$ref": "#/components/schemas/PollStatus"
|
||||
}
|
||||
}
|
||||
},
|
||||
"PollStatus": {
|
||||
"type": "string",
|
||||
"enum": [
|
||||
"pending",
|
||||
"completed",
|
||||
"not_found"
|
||||
]
|
||||
},
|
||||
"PrimaryBusinessDetails": {
|
||||
"type": "object",
|
||||
"required": [
|
||||
|
||||
Reference in New Issue
Block a user