Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions crates/api_models/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
28 changes: 28 additions & 0 deletions crates/api_models/src/poll.rs
Original file line number Diff line number Diff line change
@@ -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(),
})
}
}
3 changes: 3 additions & 0 deletions crates/common_utils/src/events.rs
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,9 @@ pub enum ApiEventsType {
Events {
merchant_id_or_profile_id: String,
},
Poll {
poll_id: String,
},
}

impl ApiEventMetric for serde_json::Value {}
Expand Down
5 changes: 5 additions & 0 deletions crates/openapi/src/openapi.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down Expand Up @@ -205,6 +208,8 @@ Never share your secret api keys. Keep them guarded and secure.
api_models::payment_methods::CardDetailFromLocker,
api_models::payment_methods::CardDetail,
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,
Expand Down
3 changes: 2 additions & 1 deletion crates/openapi/src/routes.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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 changes: 15 additions & 0 deletions crates/openapi/src/routes/poll.rs
Original file line number Diff line number Diff line change
@@ -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() {}
4 changes: 4 additions & 0 deletions crates/router/src/compatibility/stripe/errors.rs
Original file line number Diff line number Diff line change
Expand Up @@ -595,6 +595,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
}
Expand Down
1 change: 1 addition & 0 deletions crates/router/src/core.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
2 changes: 2 additions & 0 deletions crates/router/src/core/errors/api_error_response.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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")]
Expand Down
3 changes: 3 additions & 0 deletions crates/router/src/core/errors/transformers.rs
Original file line number Diff line number Diff line change
Expand Up @@ -235,6 +235,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))
}
Expand Down
45 changes: 45 additions & 0 deletions crates/router/src/core/poll.rs
Original file line number Diff line number Diff line change
@@ -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))
}
10 changes: 9 additions & 1 deletion crates/router/src/events/api_logs.rs
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ use crate::{
PaymentLinkFormData,
},
types::api::{
AttachEvidenceRequest, Config, ConfigUpdate, CreateFileRequest, DisputeId, FileId,
AttachEvidenceRequest, Config, ConfigUpdate, CreateFileRequest, DisputeId, FileId, PollId,
},
};

Expand Down Expand Up @@ -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(),
})
}
}
1 change: 1 addition & 0 deletions crates/router/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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")]
Expand Down
3 changes: 2 additions & 1 deletion crates/router/src/routes.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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};
Expand Down
15 changes: 13 additions & 2 deletions crates/router/src/routes/app.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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")]
Expand All @@ -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;
Expand Down Expand Up @@ -974,6 +974,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")]
Expand Down
3 changes: 3 additions & 0 deletions crates/router/src/routes/lock_utils.rs
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ pub enum ApiIdentifier {
UserRole,
ConnectorOnboarding,
Recon,
Poll,
}

impl From<Flow> for ApiIdentifier {
Expand Down Expand Up @@ -227,6 +228,8 @@ impl From<Flow> for ApiIdentifier {
| Flow::ReconServiceRequest
| Flow::ReconVerifyToken => Self::Recon,
Flow::CreateConnectorAgnosticMandateConfig => Self::Routing,

Flow::RetrievePollStatus => Self::Poll,
}
}
}
45 changes: 45 additions & 0 deletions crates/router/src/routes/poll.rs
Original file line number Diff line number Diff line change
@@ -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
}
3 changes: 2 additions & 1 deletion crates/router/src/types/api.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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")]
Expand All @@ -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::{
Expand Down
6 changes: 6 additions & 0 deletions crates/router/src/types/api/poll.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
use serde;

#[derive(Default, Debug, serde::Deserialize, serde::Serialize)]
pub struct PollId {
pub poll_id: String,
}
2 changes: 2 additions & 0 deletions crates/router_env/src/logger/types.rs
Original file line number Diff line number Diff line change
Expand Up @@ -400,6 +400,8 @@ pub enum Flow {
WebhookEventDeliveryAttemptList,
/// Manually retry the delivery for a webhook event
WebhookEventDeliveryRetry,
/// Retrieve status of the Poll
RetrievePollStatus,
}

///
Expand Down
Loading