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:
Sai Harsha Vardhan
2024-04-17 15:50:53 +05:30
committed by GitHub
parent 5a0a8cee76
commit ca47ea9b13
21 changed files with 254 additions and 6 deletions

View File

@ -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;

View 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(),
})
}
}

View File

@ -56,6 +56,9 @@ pub enum ApiEventsType {
Events {
merchant_id_or_profile_id: String,
},
Poll {
poll_id: String,
},
}
impl ApiEventMetric for serde_json::Value {}

View File

@ -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,

View File

@ -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::*,
};

View 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() {}

View File

@ -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
}

View File

@ -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;

View File

@ -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")]

View File

@ -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))
}

View 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))
}

View File

@ -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(),
})
}
}

View File

@ -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")]

View File

@ -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};

View File

@ -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")]

View File

@ -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,
}
}
}

View 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
}

View File

@ -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::{

View File

@ -0,0 +1,6 @@
use serde;
#[derive(Default, Debug, serde::Deserialize, serde::Serialize)]
pub struct PollId {
pub poll_id: String,
}

View File

@ -402,6 +402,8 @@ pub enum Flow {
WebhookEventDeliveryAttemptList,
/// Manually retry the delivery for a webhook event
WebhookEventDeliveryRetry,
/// Retrieve status of the Poll
RetrievePollStatus,
}
///

View File

@ -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": [