clippy refactor, payment gateway payment flow revertion
This commit is contained in:
parent
795793ca84
commit
df27d18043
24 changed files with 413 additions and 444 deletions
7
.env
7
.env
|
@ -2,6 +2,7 @@
|
||||||
REQUIRED_SALEOR_VERSION="^3.13"
|
REQUIRED_SALEOR_VERSION="^3.13"
|
||||||
# only sets port, the host is always 0.0.0.0 (listens to everything). Set this to docker-compose service name
|
# only sets port, the host is always 0.0.0.0 (listens to everything). Set this to docker-compose service name
|
||||||
APP_API_BASE_URL="http://10.100.110.234:3000"
|
APP_API_BASE_URL="http://10.100.110.234:3000"
|
||||||
|
APP_IFRAME_BASE_URL="http://app-name.site.com"
|
||||||
APL="Redis"
|
APL="Redis"
|
||||||
APL_URL="redis://localhost:6380/2"
|
APL_URL="redis://localhost:6380/2"
|
||||||
LOG_LEVEL="DEBUG"
|
LOG_LEVEL="DEBUG"
|
||||||
|
@ -20,9 +21,9 @@ SITEMAP_PAGES_TEMPLATE="https://example.com/{page.slug}"
|
||||||
SITEMAP_INDEX_HOSTNAME="https://example.com"
|
SITEMAP_INDEX_HOSTNAME="https://example.com"
|
||||||
|
|
||||||
## THESE VARIABLES ARE FOR SIMPLE-PAYMENT-GATEWAY APP
|
## THESE VARIABLES ARE FOR SIMPLE-PAYMENT-GATEWAY APP
|
||||||
#To see all possible options, check simple-payment-gateway/src/app:GatewayTypes
|
#To see all possible options, check simple-payment-gateway/src/app:PaymentMethods
|
||||||
ACTIVE_GATEWAYS="cod,cash,transfer"
|
ACTIVE_PAYMENT_METHODS="cod,cash,transfer"
|
||||||
# only SK,EN available :). Determines what language the gateway names will be in storefront
|
# only SK,EN available :). Determines what language the gateway names will be in storefront
|
||||||
LOCALE="SK"
|
LOCALE="Sk"
|
||||||
# uses https://crates.io/crates/iso_currency
|
# uses https://crates.io/crates/iso_currency
|
||||||
CURRENCIES="EUR"
|
CURRENCIES="EUR"
|
||||||
|
|
|
@ -2,6 +2,7 @@
|
||||||
REQUIRED_SALEOR_VERSION="^3.13"
|
REQUIRED_SALEOR_VERSION="^3.13"
|
||||||
# only sets port, the host is always 0.0.0.0 (listens to everything). Set this to docker-compose service name
|
# only sets port, the host is always 0.0.0.0 (listens to everything). Set this to docker-compose service name
|
||||||
APP_API_BASE_URL="http://0.0.0.0:3000"
|
APP_API_BASE_URL="http://0.0.0.0:3000"
|
||||||
|
APP_IFRAME_BASE_URL="http://app-name.site.com"
|
||||||
APL="Redis"
|
APL="Redis"
|
||||||
APL_URL="redis://localhost:6379/1"
|
APL_URL="redis://localhost:6379/1"
|
||||||
LOG_LEVEL="DEBUG"
|
LOG_LEVEL="DEBUG"
|
||||||
|
@ -22,7 +23,7 @@ SITEMAP_INDEX_HOSTNAME="https://example.com"
|
||||||
## THESE VARIABLES ARE FOR SIMPLE-PAYMENT-GATEWAY APP
|
## THESE VARIABLES ARE FOR SIMPLE-PAYMENT-GATEWAY APP
|
||||||
#To see all possible options, check simple-payment-gateway/src/app:GatewayTypes
|
#To see all possible options, check simple-payment-gateway/src/app:GatewayTypes
|
||||||
ACTIVE_GATEWAYS="cod,cash,transfer"
|
ACTIVE_GATEWAYS="cod,cash,transfer"
|
||||||
# only SK,EN available :). Determines what language the gateway names will be in storefront
|
# only Sk,En available :). Determines what language the gateway names will be in storefront
|
||||||
LOCALE="SK"
|
LOCALE="Sk"
|
||||||
# uses https://crates.io/crates/iso_currency
|
# uses https://crates.io/crates/iso_currency
|
||||||
CURRENCIES="EUR"
|
CURRENCIES="EUR"
|
||||||
|
|
10
Dockerfile
10
Dockerfile
|
@ -17,8 +17,11 @@ RUN cargo chef cook --release --recipe-path=recipe.json
|
||||||
COPY . .
|
COPY . .
|
||||||
RUN cargo build --release
|
RUN cargo build --release
|
||||||
|
|
||||||
|
|
||||||
FROM debian:bookworm-slim as chef-sitemap-generator
|
FROM debian:bookworm-slim as chef-sitemap-generator
|
||||||
COPY --from=builder /apps/target/release/sitemap-generator /sitemap-generator
|
WORKDIR /app
|
||||||
|
COPY --from=builder /apps/target/release/sitemap-generator .
|
||||||
|
COPY ./sitemap-generator/public ./public
|
||||||
RUN apt-get update -y && \
|
RUN apt-get update -y && \
|
||||||
apt-get install -y pkg-config libssl-dev curl
|
apt-get install -y pkg-config libssl-dev curl
|
||||||
RUN mkdir /sitemaps
|
RUN mkdir /sitemaps
|
||||||
|
@ -31,8 +34,11 @@ LABEL org.opencontainers.image.title="djkato/saleor-sitemap-generator"\
|
||||||
org.opencontainers.image.authors="Djkáťo <djkatovfx@gmail.com>"\
|
org.opencontainers.image.authors="Djkáťo <djkatovfx@gmail.com>"\
|
||||||
org.opencontainers.image.licenses="PolyForm-Noncommercial-1.0.0"
|
org.opencontainers.image.licenses="PolyForm-Noncommercial-1.0.0"
|
||||||
|
|
||||||
|
|
||||||
FROM debian:bookworm-slim as chef-simple-payment-gateway
|
FROM debian:bookworm-slim as chef-simple-payment-gateway
|
||||||
COPY --from=builder /apps/target/release/simple-payment-gateway /simple-payment-gateway
|
WORKDIR /app
|
||||||
|
COPY --from=builder /apps/target/release/simple-payment-gateway .
|
||||||
|
COPY ./simple-payment-gateway/public ./public
|
||||||
RUN apt-get update -y && \
|
RUN apt-get update -y && \
|
||||||
apt-get install -y pkg-config libssl-dev curl
|
apt-get install -y pkg-config libssl-dev curl
|
||||||
CMD [ "./simple-payment-gateway" ]
|
CMD [ "./simple-payment-gateway" ]
|
||||||
|
|
|
@ -79,6 +79,7 @@ and set all necessary env variables in `app-simple-gateway.env` according to the
|
||||||
|
|
||||||
To use, you need to have [Rust environment prepared](https://rustup.rs/).
|
To use, you need to have [Rust environment prepared](https://rustup.rs/).
|
||||||
Every folder represents a different workspace. To add a new lib, do `cargo new <project-name> --lib` or `cargo new <project-name>` for binary apps. It should appear as a new member under root `Cargo.toml`
|
Every folder represents a different workspace. To add a new lib, do `cargo new <project-name> --lib` or `cargo new <project-name>` for binary apps. It should appear as a new member under root `Cargo.toml`
|
||||||
|
To run apps propery, use `cargo run -c <crate name>`
|
||||||
|
|
||||||
# Unofficial Saleor App SDK
|
# Unofficial Saleor App SDK
|
||||||
|
|
||||||
|
|
|
@ -33,22 +33,18 @@ where
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn trace_to_std(config: &Config) {
|
pub fn trace_to_std(config: &Config) -> anyhow::Result<()> {
|
||||||
let filter = EnvFilter::builder()
|
let filter = EnvFilter::builder()
|
||||||
.with_default_directive(LevelFilter::DEBUG.into())
|
.with_default_directive(LevelFilter::DEBUG.into())
|
||||||
.from_env()
|
.from_env()?
|
||||||
.unwrap()
|
.add_directive(format!("{}={}", env!("CARGO_PKG_NAME"), config.log_level).parse()?);
|
||||||
.add_directive(
|
|
||||||
format!("{}={}", env!("CARGO_PKG_NAME"), config.log_level)
|
|
||||||
.parse()
|
|
||||||
.unwrap(),
|
|
||||||
);
|
|
||||||
tracing_subscriber::fmt()
|
tracing_subscriber::fmt()
|
||||||
.with_max_level(config.log_level)
|
.with_max_level(config.log_level)
|
||||||
.with_env_filter(filter)
|
.with_env_filter(filter)
|
||||||
.with_target(true)
|
.with_target(true)
|
||||||
.compact()
|
.compact()
|
||||||
.init();
|
.init();
|
||||||
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
|
|
|
@ -1,3 +1,11 @@
|
||||||
|
#![allow(
|
||||||
|
non_upper_case_globals,
|
||||||
|
clippy::large_enum_variant,
|
||||||
|
clippy::upper_case_acronyms
|
||||||
|
)]
|
||||||
|
#![feature(let_chains)]
|
||||||
|
#![deny(clippy::unwrap_used, clippy::expect_used)]
|
||||||
|
|
||||||
mod app;
|
mod app;
|
||||||
mod queries;
|
mod queries;
|
||||||
mod routes;
|
mod routes;
|
||||||
|
@ -5,8 +13,8 @@ mod routes;
|
||||||
use saleor_app_sdk::{
|
use saleor_app_sdk::{
|
||||||
cargo_info,
|
cargo_info,
|
||||||
config::Config,
|
config::Config,
|
||||||
manifest::{AppManifest, AppPermission},
|
manifest::{AppManifestBuilder, AppPermission},
|
||||||
webhooks::{AsyncWebhookEventType, WebhookManifest},
|
webhooks::{AsyncWebhookEventType, WebhookManifestBuilder},
|
||||||
SaleorApp,
|
SaleorApp,
|
||||||
};
|
};
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
|
@ -20,13 +28,13 @@ use crate::{
|
||||||
#[tokio::main]
|
#[tokio::main]
|
||||||
async fn main() -> anyhow::Result<()> {
|
async fn main() -> anyhow::Result<()> {
|
||||||
let config = Config::load()?;
|
let config = Config::load()?;
|
||||||
trace_to_std(&config);
|
trace_to_std(&config)?;
|
||||||
|
|
||||||
let saleor_app = SaleorApp::new(&config)?;
|
let saleor_app = SaleorApp::new(&config)?;
|
||||||
|
|
||||||
let app_manifest = AppManifest::new(&config, cargo_info!())
|
let app_manifest = AppManifestBuilder::new(&config, cargo_info!())
|
||||||
.add_webhook(
|
.add_webhook(
|
||||||
WebhookManifest::new(&config)
|
WebhookManifestBuilder::new(&config)
|
||||||
.set_query(
|
.set_query(
|
||||||
r#"
|
r#"
|
||||||
subscription QueryProductsChanged {
|
subscription QueryProductsChanged {
|
||||||
|
@ -85,7 +93,7 @@ async fn main() -> anyhow::Result<()> {
|
||||||
.unwrap_or(&"3000"),
|
.unwrap_or(&"3000"),
|
||||||
)
|
)
|
||||||
.await?;
|
.await?;
|
||||||
tracing::debug!("listening on {}", listener.local_addr().unwrap());
|
tracing::debug!("listening on {}", listener.local_addr()?);
|
||||||
match axum::serve(listener, app).await {
|
match axum::serve(listener, app).await {
|
||||||
Ok(o) => Ok(o),
|
Ok(o) => Ok(o),
|
||||||
Err(e) => anyhow::bail!(e),
|
Err(e) => anyhow::bail!(e),
|
||||||
|
|
|
@ -22,7 +22,7 @@ pub fn create_routes(state: AppState) -> Router {
|
||||||
(StatusCode::NOT_FOUND, "Not found")
|
(StatusCode::NOT_FOUND, "Not found")
|
||||||
}
|
}
|
||||||
let service = handle_404.into_service();
|
let service = handle_404.into_service();
|
||||||
let serve_dir = ServeDir::new("saleor-app-template/public").not_found_service(service);
|
let serve_dir = ServeDir::new("./public").not_found_service(service);
|
||||||
|
|
||||||
Router::new()
|
Router::new()
|
||||||
.layer(middleware::from_fn(webhook_signature_verifier))
|
.layer(middleware::from_fn(webhook_signature_verifier))
|
||||||
|
|
|
@ -37,16 +37,15 @@ pub async fn webhooks(
|
||||||
.get(SALEOR_API_URL_HEADER)
|
.get(SALEOR_API_URL_HEADER)
|
||||||
.context("missing saleor api url header")?;
|
.context("missing saleor api url header")?;
|
||||||
let event_type = get_webhook_event_type(&headers)?;
|
let event_type = get_webhook_event_type(&headers)?;
|
||||||
match event_type {
|
if let EitherWebhookType::Async(a) = event_type {
|
||||||
EitherWebhookType::Async(a) => match a {
|
match a {
|
||||||
AsyncWebhookEventType::ProductUpdated
|
AsyncWebhookEventType::ProductUpdated
|
||||||
| AsyncWebhookEventType::ProductCreated
|
| AsyncWebhookEventType::ProductCreated
|
||||||
| AsyncWebhookEventType::ProductDeleted => {
|
| AsyncWebhookEventType::ProductDeleted => {
|
||||||
update_product(product, url.to_str()?, state).await?
|
update_product(product, url.to_str()?, state).await?
|
||||||
}
|
}
|
||||||
_ => (),
|
_ => (),
|
||||||
},
|
}
|
||||||
_ => (),
|
|
||||||
}
|
}
|
||||||
|
|
||||||
info!("got webhooks!");
|
info!("got webhooks!");
|
||||||
|
|
|
@ -1,8 +1,7 @@
|
||||||
## COMMON VARIABLES FOR ALL APPS
|
## COMMON VARIABLES FOR ALL APPS
|
||||||
REQUIRED_SALEOR_VERSION="^3.13"
|
REQUIRED_SALEOR_VERSION="^3.13"
|
||||||
APP_API_BASE_URL="http://0.0.0.0:3001"
|
APP_API_BASE_URL="http://app-payment-gateway:3000"
|
||||||
MANIFEST_URL="http://0.0.0.0:3001"
|
APP_IFRAME_BASE_URL="http://app-payment-gateway.site.com"
|
||||||
CONFIG_URL="http://0.0.0.0:3001"
|
|
||||||
APL="Redis"
|
APL="Redis"
|
||||||
APL_URL="redis://redis-apl:6379/1"
|
APL_URL="redis://redis-apl:6379/1"
|
||||||
LOG_LEVEL="DEBUG"
|
LOG_LEVEL="DEBUG"
|
||||||
|
@ -23,5 +22,7 @@ SITEMAP_INDEX_HOSTNAME="https://example.com"
|
||||||
## THESE VARIABLES ARE FOR SIMPLE-PAYMENT-GATEWAY APP
|
## THESE VARIABLES ARE FOR SIMPLE-PAYMENT-GATEWAY APP
|
||||||
#To see all possible options, check simple-payment-gateway/src/app:GatewayTypes
|
#To see all possible options, check simple-payment-gateway/src/app:GatewayTypes
|
||||||
ACTIVE_GATEWAYS="cod,cash,transfer"
|
ACTIVE_GATEWAYS="cod,cash,transfer"
|
||||||
# only SK,EN available :). Determines what language the gateway names will be in storefront
|
# only Sk,En available :). Determines what language the gateway names will be in storefront
|
||||||
LOCALE="SK"
|
LOCALE="Sk"
|
||||||
|
# uses https://crates.io/crates/iso_currency
|
||||||
|
CURRENCIES="EUR"
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
## COMMON VARIABLES FOR ALL APPS
|
## COMMON VARIABLES FOR ALL APPS
|
||||||
REQUIRED_SALEOR_VERSION="^3.13"
|
REQUIRED_SALEOR_VERSION="^3.13"
|
||||||
APP_API_BASE_URL="http://0.0.0.0:3002"
|
APP_API_BASE_URL="http://0.0.0.0:3000"
|
||||||
|
APP_IFRAME_BASE_URL="http://app-payment-gateway.site.com"
|
||||||
APL="Redis"
|
APL="Redis"
|
||||||
APL_URL="redis://redis-apl:6379/1"
|
APL_URL="redis://redis-apl:6379/1"
|
||||||
LOG_LEVEL="DEBUG"
|
LOG_LEVEL="DEBUG"
|
||||||
|
|
|
@ -24,6 +24,7 @@ pub struct Config {
|
||||||
#[serde(default = "version_default")]
|
#[serde(default = "version_default")]
|
||||||
pub required_saleor_version: String,
|
pub required_saleor_version: String,
|
||||||
pub app_api_base_url: String,
|
pub app_api_base_url: String,
|
||||||
|
pub app_iframe_base_url: String,
|
||||||
pub apl: AplType,
|
pub apl: AplType,
|
||||||
pub apl_url: String,
|
pub apl_url: String,
|
||||||
#[serde(with = "LocalTracingLevel")]
|
#[serde(with = "LocalTracingLevel")]
|
||||||
|
|
|
@ -214,7 +214,7 @@ macro_rules! cargo_info {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub use cargo_info;
|
pub use cargo_info;
|
||||||
impl AppManifest {
|
impl AppManifestBuilder {
|
||||||
/**
|
/**
|
||||||
* Builder for AppManifest
|
* Builder for AppManifest
|
||||||
*
|
*
|
||||||
|
@ -227,7 +227,7 @@ impl AppManifest {
|
||||||
* To set webhooks and permissions use the add_webhook() and add_permissions()
|
* To set webhooks and permissions use the add_webhook() and add_permissions()
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
pub fn new(config: &Config, info: CargoInfo) -> AppManifestBuilder {
|
pub fn new(config: &Config, info: CargoInfo) -> Self {
|
||||||
AppManifestBuilder {
|
AppManifestBuilder {
|
||||||
manifest: AppManifest {
|
manifest: AppManifest {
|
||||||
id: info.name.clone(),
|
id: info.name.clone(),
|
||||||
|
|
|
@ -269,15 +269,15 @@ impl WebhookManifestBuilder {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl WebhookManifest {
|
impl WebhookManifestBuilder {
|
||||||
/**
|
/**
|
||||||
* Creates defaults of name(<cargo_app_name> webhook) and target url(/api/webhooks) from config and env.
|
* Creates defaults of name(<cargo_app_name> webhook) and target url(/api/webhooks) from config and env.
|
||||||
*/
|
*/
|
||||||
pub fn new(config: &Config) -> WebhookManifestBuilder {
|
pub fn new(config: &Config) -> Self {
|
||||||
WebhookManifestBuilder {
|
WebhookManifestBuilder {
|
||||||
webhook_manifest: WebhookManifest {
|
webhook_manifest: WebhookManifest {
|
||||||
target_url: format!("{}/api/webhooks", config.app_api_base_url),
|
target_url: format!("{}/api/webhooks", config.app_api_base_url),
|
||||||
name: env!("CARGO_PKG_NAME").to_owned() + " webhook",
|
name: "webhook".to_owned(),
|
||||||
is_active: Some(true),
|
is_active: Some(true),
|
||||||
..Default::default()
|
..Default::default()
|
||||||
},
|
},
|
||||||
|
|
|
@ -4,6 +4,7 @@ use crate::headers::SALEOR_EVENT_HEADER;
|
||||||
|
|
||||||
use super::{AsyncWebhookEventType, SyncWebhookEventType};
|
use super::{AsyncWebhookEventType, SyncWebhookEventType};
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
pub enum EitherWebhookType {
|
pub enum EitherWebhookType {
|
||||||
Sync(SyncWebhookEventType),
|
Sync(SyncWebhookEventType),
|
||||||
Async(AsyncWebhookEventType),
|
Async(AsyncWebhookEventType),
|
||||||
|
|
|
@ -5,14 +5,11 @@ use axum::{
|
||||||
use enum_iterator::{all, Sequence};
|
use enum_iterator::{all, Sequence};
|
||||||
use iso_currency::Currency;
|
use iso_currency::Currency;
|
||||||
use std::{str::FromStr, sync::Arc};
|
use std::{str::FromStr, sync::Arc};
|
||||||
use tracing::level_filters::LevelFilter;
|
use tracing::{debug, level_filters::LevelFilter};
|
||||||
use tracing_subscriber::EnvFilter;
|
use tracing_subscriber::EnvFilter;
|
||||||
|
|
||||||
use saleor_app_sdk::{
|
use saleor_app_sdk::{config::Config, locales::LocaleCode, manifest::AppManifest, SaleorApp};
|
||||||
config::Config, locales::LocaleCode, manifest::AppManifest,
|
use serde::{Deserialize, Serialize};
|
||||||
webhooks::sync_response::PaymentGateway, SaleorApp,
|
|
||||||
};
|
|
||||||
use serde::Serialize;
|
|
||||||
// Make our own error that wraps `anyhow::Error`.
|
// Make our own error that wraps `anyhow::Error`.
|
||||||
pub struct AppError(anyhow::Error);
|
pub struct AppError(anyhow::Error);
|
||||||
|
|
||||||
|
@ -38,27 +35,23 @@ where
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn trace_to_std(config: &Config) {
|
pub fn trace_to_std(config: &Config) -> anyhow::Result<()> {
|
||||||
let filter = EnvFilter::builder()
|
let filter = EnvFilter::builder()
|
||||||
.with_default_directive(LevelFilter::DEBUG.into())
|
.with_default_directive(LevelFilter::DEBUG.into())
|
||||||
.from_env()
|
.from_env()?
|
||||||
.unwrap()
|
.add_directive(format!("{}={}", env!("CARGO_PKG_NAME"), config.log_level).parse()?);
|
||||||
.add_directive(
|
|
||||||
format!("{}={}", env!("CARGO_PKG_NAME"), config.log_level)
|
|
||||||
.parse()
|
|
||||||
.unwrap(),
|
|
||||||
);
|
|
||||||
tracing_subscriber::fmt()
|
tracing_subscriber::fmt()
|
||||||
.with_max_level(config.log_level)
|
.with_max_level(config.log_level)
|
||||||
.with_env_filter(filter)
|
.with_env_filter(filter)
|
||||||
.with_target(true)
|
.with_target(true)
|
||||||
.compact()
|
.compact()
|
||||||
.init();
|
.init();
|
||||||
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, Sequence, Serialize)]
|
#[derive(Debug, Clone, Sequence, Serialize, Deserialize)]
|
||||||
#[serde(rename_all = "lowercase")]
|
#[serde(rename_all = "lowercase")]
|
||||||
pub enum GatewayType {
|
pub enum PaymentMethodType {
|
||||||
Accreditation,
|
Accreditation,
|
||||||
Cash,
|
Cash,
|
||||||
/**
|
/**
|
||||||
|
@ -75,16 +68,16 @@ pub struct AppState {
|
||||||
pub saleor_app: Arc<tokio::sync::Mutex<SaleorApp>>,
|
pub saleor_app: Arc<tokio::sync::Mutex<SaleorApp>>,
|
||||||
pub config: Config,
|
pub config: Config,
|
||||||
pub manifest: AppManifest,
|
pub manifest: AppManifest,
|
||||||
pub active_gateways: Vec<ActiveGateway>,
|
pub active_payment_methods: Vec<ActivePaymentMethod>,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_active_gateways_from_env() -> anyhow::Result<Vec<ActiveGateway>> {
|
pub fn get_active_payment_methods_from_env() -> anyhow::Result<Vec<ActivePaymentMethod>> {
|
||||||
_ = dotenvy::dotenv();
|
_ = dotenvy::dotenv();
|
||||||
//eg: "accreditation,cod,other,transfer"
|
//eg: "accreditation,cod,other,transfer"
|
||||||
let env_types = std::env::var("ACTIVE_GATEWAYS")?;
|
let env_methods = std::env::var("ACTIVE_PAYMENT_METHODS")?;
|
||||||
let locale = std::env::var("LOCALE")?;
|
let locale = std::env::var("LOCALE")?;
|
||||||
let locale = LocaleCode::from_str(&locale)?;
|
|
||||||
let currencies = std::env::var("CURRENCIES")?;
|
let currencies = std::env::var("CURRENCIES")?;
|
||||||
|
let locale = LocaleCode::from_str(&locale)?;
|
||||||
let currencies = currencies.split(',').collect::<Vec<_>>();
|
let currencies = currencies.split(',').collect::<Vec<_>>();
|
||||||
let currencies = currencies
|
let currencies = currencies
|
||||||
.iter()
|
.iter()
|
||||||
|
@ -92,44 +85,52 @@ pub fn get_active_gateways_from_env() -> anyhow::Result<Vec<ActiveGateway>> {
|
||||||
.collect::<Result<Vec<_>, _>>()
|
.collect::<Result<Vec<_>, _>>()
|
||||||
.map_err(|e| anyhow::anyhow!(format!("{:?}", e)))?;
|
.map_err(|e| anyhow::anyhow!(format!("{:?}", e)))?;
|
||||||
|
|
||||||
let str_types: Vec<_> = env_types.split(',').collect();
|
let str_types: Vec<_> = env_methods.split(',').collect();
|
||||||
let gateway_types = str_types
|
let payment_methods = str_types
|
||||||
.iter()
|
.iter()
|
||||||
.zip(all::<GatewayType>())
|
.flat_map(|s| all::<PaymentMethodType>().map(move |g| (s, g)))
|
||||||
.filter_map(|(s, g)| match format!("{:?}", g).to_lowercase() == *s {
|
.filter_map(|(s, g)| match format!("{:?}", g).to_lowercase() == *s {
|
||||||
true => Some(g),
|
true => Some(g),
|
||||||
false => None,
|
false => None,
|
||||||
})
|
})
|
||||||
.map(|g| ActiveGateway {
|
.map(|g| ActivePaymentMethod {
|
||||||
gateway_type: g.clone(),
|
typ: g.clone(),
|
||||||
gateway: PaymentGateway {
|
name: match (g, &locale) {
|
||||||
currencies: currencies.clone(),
|
(PaymentMethodType::COD, LocaleCode::Sk) => "Dobierka".to_owned(),
|
||||||
id: format!("{:?}", &g).to_lowercase(),
|
(PaymentMethodType::Cash, LocaleCode::Sk) => "Hotovosť".to_owned(),
|
||||||
config: vec![],
|
(PaymentMethodType::Transfer, LocaleCode::Sk) => "Bankový prevod".to_owned(),
|
||||||
name: match (g, &locale) {
|
(PaymentMethodType::Inkaso, LocaleCode::Sk) => "Inkaso".to_owned(),
|
||||||
(GatewayType::COD, LocaleCode::Sk) => "Dobierka".to_owned(),
|
(PaymentMethodType::Accreditation, LocaleCode::Sk) => "Vzajomný zápočet".to_owned(),
|
||||||
(GatewayType::Cash, LocaleCode::Sk) => "Hotovosť".to_owned(),
|
(PaymentMethodType::Other, LocaleCode::Sk) => "Iné".to_owned(),
|
||||||
(GatewayType::Transfer, LocaleCode::Sk) => "Bankový prevod".to_owned(),
|
(PaymentMethodType::COD, LocaleCode::En) => "Cash on delivery".to_owned(),
|
||||||
(GatewayType::Inkaso, LocaleCode::Sk) => "Inkaso".to_owned(),
|
(PaymentMethodType::Cash, LocaleCode::En) => "Cash".to_owned(),
|
||||||
(GatewayType::Accreditation, LocaleCode::Sk) => "Vzajomný zápočet".to_owned(),
|
(PaymentMethodType::Transfer, LocaleCode::En) => "Bank transfer".to_owned(),
|
||||||
(GatewayType::Other, LocaleCode::Sk) => "Iné".to_owned(),
|
(PaymentMethodType::Inkaso, LocaleCode::En) => "Encashment".to_owned(),
|
||||||
(GatewayType::COD, LocaleCode::En) => "Cash on delivery".to_owned(),
|
(PaymentMethodType::Accreditation, LocaleCode::En) => "Mutual credit".to_owned(),
|
||||||
(GatewayType::Cash, LocaleCode::En) => "Cash".to_owned(),
|
(PaymentMethodType::Other, LocaleCode::En) => "Other".to_owned(),
|
||||||
(GatewayType::Transfer, LocaleCode::En) => "Bank transfer".to_owned(),
|
(g, l) => unimplemented!("Gateway {:?} in locale {:?} not implemented", g, l),
|
||||||
(GatewayType::Inkaso, LocaleCode::En) => "Encashment".to_owned(),
|
|
||||||
(GatewayType::Accreditation, LocaleCode::En) => "Mutual credit".to_owned(),
|
|
||||||
(GatewayType::Other, LocaleCode::En) => "Other".to_owned(),
|
|
||||||
(g, l) => unimplemented!("Gateway {:?} in locale {:?} not implemented", g, l),
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
.collect::<Vec<_>>();
|
.collect::<Vec<_>>();
|
||||||
|
debug!(
|
||||||
Ok(gateway_types)
|
"active gateway types:{:?}\ncurrencies:{:?}\nlocale:{:?}",
|
||||||
|
&payment_methods, ¤cies, &locale
|
||||||
|
);
|
||||||
|
Ok(payment_methods)
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, Serialize)]
|
#[derive(Debug, Clone, Serialize)]
|
||||||
pub struct ActiveGateway {
|
pub struct ActivePaymentMethod {
|
||||||
pub gateway_type: GatewayType,
|
pub typ: PaymentMethodType,
|
||||||
pub gateway: PaymentGateway,
|
pub name: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Serialize)]
|
||||||
|
pub struct PaymentGatewayInitializeSessionData {
|
||||||
|
pub payment_methods: Vec<ActivePaymentMethod>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Deserialize, Serialize)]
|
||||||
|
pub struct TransactionInitializeSessionData {
|
||||||
|
pub payment_method: PaymentMethodType,
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,4 +1,8 @@
|
||||||
#![allow(non_upper_case_globals)]
|
#![allow(
|
||||||
|
non_upper_case_globals,
|
||||||
|
clippy::large_enum_variant,
|
||||||
|
clippy::upper_case_acronyms
|
||||||
|
)]
|
||||||
#![feature(let_chains)]
|
#![feature(let_chains)]
|
||||||
#![deny(clippy::unwrap_used, clippy::expect_used)]
|
#![deny(clippy::unwrap_used, clippy::expect_used)]
|
||||||
mod app;
|
mod app;
|
||||||
|
@ -8,20 +12,19 @@ mod routes;
|
||||||
use saleor_app_sdk::{
|
use saleor_app_sdk::{
|
||||||
cargo_info,
|
cargo_info,
|
||||||
config::Config,
|
config::Config,
|
||||||
manifest::{AppManifest, AppPermission},
|
manifest::{AppManifestBuilder, AppPermission},
|
||||||
webhooks::{SyncWebhookEventType, WebhookManifest},
|
webhooks::{SyncWebhookEventType, WebhookManifestBuilder},
|
||||||
SaleorApp,
|
SaleorApp,
|
||||||
};
|
};
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
use tokio::sync::Mutex;
|
use tokio::sync::Mutex;
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
app::{get_active_gateways_from_env, trace_to_std, AppState},
|
app::{get_active_payment_methods_from_env, trace_to_std, AppState},
|
||||||
queries::event_transactions::{
|
queries::event_transactions::{
|
||||||
sub_list_payment_gateways, sub_payment_gateway_initialize_session,
|
sub_payment_gateway_initialize_session, sub_transaction_cancelation_requested,
|
||||||
sub_transaction_cancelation_requested, sub_transaction_charge_requested,
|
sub_transaction_charge_requested, sub_transaction_initialize_session,
|
||||||
sub_transaction_initialize_session, sub_transaction_process_session,
|
sub_transaction_process_session, sub_transaction_refund_requested,
|
||||||
sub_transaction_refund_requested,
|
|
||||||
},
|
},
|
||||||
routes::create_routes,
|
routes::create_routes,
|
||||||
};
|
};
|
||||||
|
@ -29,53 +32,53 @@ use crate::{
|
||||||
#[tokio::main]
|
#[tokio::main]
|
||||||
async fn main() -> anyhow::Result<()> {
|
async fn main() -> anyhow::Result<()> {
|
||||||
let config = Config::load()?;
|
let config = Config::load()?;
|
||||||
trace_to_std(&config);
|
trace_to_std(&config)?;
|
||||||
|
|
||||||
let saleor_app = SaleorApp::new(&config)?;
|
let saleor_app = SaleorApp::new(&config)?;
|
||||||
|
|
||||||
let app_manifest = AppManifest::new(&config, cargo_info!())
|
let app_manifest = AppManifestBuilder::new(&config, cargo_info!())
|
||||||
.add_webhook(
|
.add_webhook(
|
||||||
WebhookManifest::new(&config)
|
WebhookManifestBuilder::new(&config)
|
||||||
.set_query(sub_transaction_process_session)
|
.set_query(sub_transaction_process_session)
|
||||||
.add_sync_event(SyncWebhookEventType::TransactionProcessSession)
|
.add_sync_event(SyncWebhookEventType::TransactionProcessSession)
|
||||||
.build(),
|
.build(),
|
||||||
)
|
)
|
||||||
.add_webhook(
|
.add_webhook(
|
||||||
WebhookManifest::new(&config)
|
WebhookManifestBuilder::new(&config)
|
||||||
.set_query(sub_transaction_charge_requested)
|
.set_query(sub_transaction_charge_requested)
|
||||||
.add_sync_event(SyncWebhookEventType::TransactionChargeRequested)
|
.add_sync_event(SyncWebhookEventType::TransactionChargeRequested)
|
||||||
.build(),
|
.build(),
|
||||||
)
|
)
|
||||||
.add_webhook(
|
.add_webhook(
|
||||||
WebhookManifest::new(&config)
|
WebhookManifestBuilder::new(&config)
|
||||||
.set_query(sub_transaction_refund_requested)
|
.set_query(sub_transaction_refund_requested)
|
||||||
.add_sync_event(SyncWebhookEventType::TransactionRefundRequested)
|
.add_sync_event(SyncWebhookEventType::TransactionRefundRequested)
|
||||||
.build(),
|
.build(),
|
||||||
)
|
)
|
||||||
.add_webhook(
|
.add_webhook(
|
||||||
WebhookManifest::new(&config)
|
WebhookManifestBuilder::new(&config)
|
||||||
.set_query(sub_transaction_initialize_session)
|
.set_query(sub_transaction_initialize_session)
|
||||||
.add_sync_event(SyncWebhookEventType::TransactionInitializeSession)
|
.add_sync_event(SyncWebhookEventType::TransactionInitializeSession)
|
||||||
.build(),
|
.build(),
|
||||||
)
|
)
|
||||||
.add_webhook(
|
.add_webhook(
|
||||||
WebhookManifest::new(&config)
|
WebhookManifestBuilder::new(&config)
|
||||||
.set_query(sub_payment_gateway_initialize_session)
|
.set_query(sub_payment_gateway_initialize_session)
|
||||||
.add_sync_event(SyncWebhookEventType::PaymentGatewayInitializeSession)
|
.add_sync_event(SyncWebhookEventType::PaymentGatewayInitializeSession)
|
||||||
.build(),
|
.build(),
|
||||||
)
|
)
|
||||||
.add_webhook(
|
.add_webhook(
|
||||||
WebhookManifest::new(&config)
|
WebhookManifestBuilder::new(&config)
|
||||||
.set_query(sub_transaction_cancelation_requested)
|
.set_query(sub_transaction_cancelation_requested)
|
||||||
.add_sync_event(SyncWebhookEventType::TransactionCancelationRequested)
|
.add_sync_event(SyncWebhookEventType::TransactionCancelationRequested)
|
||||||
.build(),
|
.build(),
|
||||||
)
|
)
|
||||||
.add_webhook(
|
// .add_webhook(
|
||||||
WebhookManifest::new(&config)
|
// WebhookManifestBuilder::new(&config)
|
||||||
.set_query(sub_list_payment_gateways)
|
// .set_query(sub_list_payment_gateways)
|
||||||
.add_sync_event(SyncWebhookEventType::PaymentListGateways)
|
// .add_sync_event(SyncWebhookEventType::PaymentListGateways)
|
||||||
.build(),
|
// .build(),
|
||||||
)
|
// )
|
||||||
.add_permissions(vec![
|
.add_permissions(vec![
|
||||||
AppPermission::HandlePayments,
|
AppPermission::HandlePayments,
|
||||||
AppPermission::ManageOrders,
|
AppPermission::ManageOrders,
|
||||||
|
@ -85,7 +88,7 @@ async fn main() -> anyhow::Result<()> {
|
||||||
.build();
|
.build();
|
||||||
|
|
||||||
let app_state = AppState {
|
let app_state = AppState {
|
||||||
active_gateways: get_active_gateways_from_env()?,
|
active_payment_methods: get_active_payment_methods_from_env()?,
|
||||||
manifest: app_manifest,
|
manifest: app_manifest,
|
||||||
config: config.clone(),
|
config: config.clone(),
|
||||||
saleor_app: Arc::new(Mutex::new(saleor_app)),
|
saleor_app: Arc::new(Mutex::new(saleor_app)),
|
||||||
|
@ -102,7 +105,7 @@ async fn main() -> anyhow::Result<()> {
|
||||||
.unwrap_or(&"3000"),
|
.unwrap_or(&"3000"),
|
||||||
)
|
)
|
||||||
.await?;
|
.await?;
|
||||||
tracing::debug!("listening on {}", listener.local_addr().unwrap());
|
tracing::debug!("listening on {}", listener.local_addr()?);
|
||||||
match axum::serve(listener, app).await {
|
match axum::serve(listener, app).await {
|
||||||
Ok(o) => Ok(o),
|
Ok(o) => Ok(o),
|
||||||
Err(e) => anyhow::bail!(e),
|
Err(e) => anyhow::bail!(e),
|
||||||
|
|
|
@ -55,17 +55,17 @@ fragment OrderDetails on Order {
|
||||||
}
|
}
|
||||||
"#;
|
"#;
|
||||||
|
|
||||||
pub const sub_list_payment_gateways: &str = r#"
|
// pub const sub_list_payment_gateways: &str = r#"
|
||||||
subscription ListPaymentGateways {
|
// subscription ListPaymentGateways {
|
||||||
event {
|
// event {
|
||||||
... on PaymentListGateways {
|
// ... on PaymentListGateways {
|
||||||
checkout {
|
// checkout {
|
||||||
id
|
// id
|
||||||
}
|
// }
|
||||||
}
|
// }
|
||||||
}
|
// }
|
||||||
}
|
// }
|
||||||
"#;
|
// "#;
|
||||||
|
|
||||||
pub const sub_payment_gateway_initialize_session: &str = concatcp!(
|
pub const sub_payment_gateway_initialize_session: &str = concatcp!(
|
||||||
r#"
|
r#"
|
||||||
|
|
|
@ -22,7 +22,7 @@ pub fn create_routes(state: AppState) -> Router {
|
||||||
(StatusCode::NOT_FOUND, "Not found")
|
(StatusCode::NOT_FOUND, "Not found")
|
||||||
}
|
}
|
||||||
let service = handle_404.into_service();
|
let service = handle_404.into_service();
|
||||||
let serve_dir = ServeDir::new("simple-payment-gateway/public").not_found_service(service);
|
let serve_dir = ServeDir::new("./public").not_found_service(service);
|
||||||
|
|
||||||
Router::new()
|
Router::new()
|
||||||
.layer(middleware::from_fn(webhook_signature_verifier))
|
.layer(middleware::from_fn(webhook_signature_verifier))
|
||||||
|
|
|
@ -7,23 +7,23 @@ use saleor_app_sdk::{
|
||||||
webhooks::{
|
webhooks::{
|
||||||
sync_response::{
|
sync_response::{
|
||||||
CancelationRequestedResult, ChargeRequestedResult,
|
CancelationRequestedResult, ChargeRequestedResult,
|
||||||
PaymentGatewayInitializeSessionResponse, PaymentListGatewaysResponse,
|
PaymentGatewayInitializeSessionResponse, RefundRequestedResult,
|
||||||
RefundRequestedResult, TransactionCancelationRequestedResponse,
|
TransactionCancelationRequestedResponse, TransactionChargeRequestedResponse,
|
||||||
TransactionChargeRequestedResponse, TransactionInitializeSessionResponse,
|
TransactionInitializeSessionResponse, TransactionProcessSessionResponse,
|
||||||
TransactionProcessSessionResponse, TransactionRefundRequestedResponse,
|
TransactionRefundRequestedResponse, TransactionSessionResult,
|
||||||
TransactionSessionResult,
|
|
||||||
},
|
},
|
||||||
utils::{get_webhook_event_type, EitherWebhookType},
|
utils::{get_webhook_event_type, EitherWebhookType},
|
||||||
SyncWebhookEventType,
|
SyncWebhookEventType,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
use serde::Serialize;
|
|
||||||
use serde_json::Value;
|
use serde_json::Value;
|
||||||
use std::str::FromStr;
|
use std::str::FromStr;
|
||||||
use tracing::{debug, error, info};
|
use tracing::{debug, error, info};
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
app::{ActiveGateway, AppError, AppState, GatewayType},
|
app::{
|
||||||
|
AppError, AppState, PaymentGatewayInitializeSessionData, TransactionInitializeSessionData,
|
||||||
|
},
|
||||||
queries::{
|
queries::{
|
||||||
event_transactions::{
|
event_transactions::{
|
||||||
TransactionCancelationRequested2, TransactionChargeRequested2,
|
TransactionCancelationRequested2, TransactionChargeRequested2,
|
||||||
|
@ -43,7 +43,6 @@ pub async fn webhooks(
|
||||||
) -> Result<Json<Value>, AppError> {
|
) -> Result<Json<Value>, AppError> {
|
||||||
debug!("/api/webhooks");
|
debug!("/api/webhooks");
|
||||||
debug!("req: {:?}", body);
|
debug!("req: {:?}", body);
|
||||||
debug!("headers: {:?}", headers);
|
|
||||||
|
|
||||||
let saleor_api_url = headers
|
let saleor_api_url = headers
|
||||||
.get(SALEOR_API_URL_HEADER)
|
.get(SALEOR_API_URL_HEADER)
|
||||||
|
@ -51,57 +50,96 @@ pub async fn webhooks(
|
||||||
.to_str()?
|
.to_str()?
|
||||||
.to_owned();
|
.to_owned();
|
||||||
let event_type = get_webhook_event_type(&headers)?;
|
let event_type = get_webhook_event_type(&headers)?;
|
||||||
|
debug!("event: {:?}", event_type);
|
||||||
let res: Json<Value> = match event_type {
|
let res: Json<Value> = match event_type {
|
||||||
EitherWebhookType::Sync(a) => match a {
|
EitherWebhookType::Sync(a) => match a {
|
||||||
SyncWebhookEventType::PaymentListGateways => {
|
// SyncWebhookEventType::PaymentListGateways => {
|
||||||
let gateways = state
|
// let gateways = state
|
||||||
.active_gateways
|
// .active_gateways
|
||||||
.iter()
|
// .iter()
|
||||||
.cloned()
|
// .cloned()
|
||||||
.map(|g| g.gateway)
|
// .map(|g| g.gateway)
|
||||||
.collect::<Vec<_>>();
|
// .collect::<Vec<_>>();
|
||||||
Json::from(serde_json::to_value(PaymentListGatewaysResponse(gateways))?)
|
// Json::from(serde_json::to_value(PaymentListGatewaysResponse(gateways))?)
|
||||||
}
|
// }
|
||||||
|
SyncWebhookEventType::PaymentGatewayInitializeSession => {
|
||||||
SyncWebhookEventType::TransactionCancelationRequested => {
|
let data = serde_json::to_value(PaymentGatewayInitializeSessionData {
|
||||||
let data = serde_json::from_str::<TransactionCancelationRequested2>(&body)?;
|
payment_methods: state.active_payment_methods,
|
||||||
|
})?;
|
||||||
Json::from(serde_json::to_value(
|
Json::from(serde_json::to_value(
|
||||||
TransactionCancelationRequestedResponse {
|
PaymentGatewayInitializeSessionResponse::<Value> { data: Some(data) },
|
||||||
time: None,
|
|
||||||
psp_reference: "".to_owned(),
|
|
||||||
external_url: None,
|
|
||||||
message: None,
|
|
||||||
amount: data
|
|
||||||
.action
|
|
||||||
.amount
|
|
||||||
.and_then(|a| Decimal::from_str(&a.0).ok()),
|
|
||||||
result: Some(CancelationRequestedResult::CancelSuccess),
|
|
||||||
},
|
|
||||||
)?)
|
)?)
|
||||||
}
|
}
|
||||||
SyncWebhookEventType::PaymentGatewayInitializeSession => Json::from(
|
SyncWebhookEventType::TransactionInitializeSession => {
|
||||||
serde_json::to_value(PaymentGatewayInitializeSessionResponse::<u8> { data: None })?,
|
let session_data = serde_json::from_str::<TransactionInitializeSession2>(&body)?;
|
||||||
),
|
let payment_method: TransactionInitializeSessionData = serde_json::from_str(
|
||||||
SyncWebhookEventType::TransactionProcessSession => {
|
&session_data
|
||||||
let data = serde_json::from_str::<TransactionProcessSession2>(&body)?;
|
.data
|
||||||
Json::from(serde_json::to_value(TransactionProcessSessionResponse::<
|
.context("Missing data field on TransactionInitializeSession2 body")?
|
||||||
u8,
|
.0,
|
||||||
> {
|
)?;
|
||||||
data: None,
|
debug!(
|
||||||
time: None,
|
"Transaction session initialised with '{:?}' payment method.",
|
||||||
psp_reference: None,
|
&payment_method.payment_method
|
||||||
external_url: None,
|
);
|
||||||
message: None,
|
let str_payment_method = serde_json::to_string(&payment_method)?;
|
||||||
amount: Decimal::from_str(&data.action.amount.0)?,
|
|
||||||
result: match data.action.action_type {
|
let app = state.saleor_app.lock().await;
|
||||||
TransactionFlowStrategyEnum::Charge => {
|
let auth_data = app.apl.get(&saleor_api_url).await?;
|
||||||
TransactionSessionResult::ChargeSuccess
|
|
||||||
}
|
let operation = TransactionUpdate::build(TransactionUpdateVariables {
|
||||||
TransactionFlowStrategyEnum::Authorization => {
|
id: &session_data.transaction.id,
|
||||||
TransactionSessionResult::AuthorizationSuccess
|
transaction: Some(TransactionUpdateInput {
|
||||||
}
|
message: Some(&str_payment_method),
|
||||||
|
..Default::default()
|
||||||
|
}),
|
||||||
|
});
|
||||||
|
let mut res = surf::post(&saleor_api_url)
|
||||||
|
.header("authorization-bearer", auth_data.token)
|
||||||
|
.run_graphql(operation)
|
||||||
|
.await;
|
||||||
|
|
||||||
|
let mut webhook_result = WebhookResult::Failiure;
|
||||||
|
if let Ok(r) = &mut res
|
||||||
|
&& let Some(data) = &mut r.data
|
||||||
|
&& let Some(q_res) = &mut data.transaction_update
|
||||||
|
{
|
||||||
|
if !q_res.errors.is_empty() {
|
||||||
|
q_res
|
||||||
|
.errors
|
||||||
|
.iter()
|
||||||
|
.for_each(|e| error!("failed update transaction, {:?}", e));
|
||||||
|
} else if q_res.transaction.is_some() {
|
||||||
|
webhook_result = WebhookResult::Success;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Json::from(serde_json::to_value(
|
||||||
|
TransactionInitializeSessionResponse::<u8> {
|
||||||
|
data: None,
|
||||||
|
time: None,
|
||||||
|
psp_reference: None,
|
||||||
|
external_url: None,
|
||||||
|
message: None,
|
||||||
|
amount: Decimal::from_str(&session_data.action.amount.0)?,
|
||||||
|
result: match (session_data.action.action_type, webhook_result) {
|
||||||
|
(TransactionFlowStrategyEnum::Charge, WebhookResult::Success) => {
|
||||||
|
TransactionSessionResult::ChargeSuccess
|
||||||
|
}
|
||||||
|
(
|
||||||
|
TransactionFlowStrategyEnum::Authorization,
|
||||||
|
WebhookResult::Success,
|
||||||
|
) => TransactionSessionResult::AuthorizationSuccess,
|
||||||
|
(TransactionFlowStrategyEnum::Charge, WebhookResult::Failiure) => {
|
||||||
|
TransactionSessionResult::ChargeFailiure
|
||||||
|
}
|
||||||
|
(
|
||||||
|
TransactionFlowStrategyEnum::Authorization,
|
||||||
|
WebhookResult::Failiure,
|
||||||
|
) => TransactionSessionResult::AuthorizationFailure,
|
||||||
|
},
|
||||||
},
|
},
|
||||||
})?)
|
)?)
|
||||||
}
|
}
|
||||||
SyncWebhookEventType::TransactionChargeRequested => {
|
SyncWebhookEventType::TransactionChargeRequested => {
|
||||||
let data = serde_json::from_str::<TransactionChargeRequested2>(&body)?;
|
let data = serde_json::from_str::<TransactionChargeRequested2>(&body)?;
|
||||||
|
@ -131,91 +169,54 @@ pub async fn webhooks(
|
||||||
result: Some(RefundRequestedResult::RefundSuccess),
|
result: Some(RefundRequestedResult::RefundSuccess),
|
||||||
})?)
|
})?)
|
||||||
}
|
}
|
||||||
SyncWebhookEventType::TransactionInitializeSession => {
|
|
||||||
let data = serde_json::from_str::<TransactionInitializeSession2>(&body)?;
|
|
||||||
let app = state.saleor_app.lock().await;
|
|
||||||
let auth_data = app.apl.get(&saleor_api_url).await?;
|
|
||||||
let operation = TransactionUpdate::build(TransactionUpdateVariables {
|
|
||||||
id: &data.transaction.id,
|
|
||||||
transaction: Some(TransactionUpdateInput {
|
|
||||||
message: Some(""),
|
|
||||||
..Default::default()
|
|
||||||
}),
|
|
||||||
});
|
|
||||||
let mut res = surf::post(&saleor_api_url)
|
|
||||||
.header("authorization-bearer", auth_data.token)
|
|
||||||
.run_graphql(operation)
|
|
||||||
.await;
|
|
||||||
|
|
||||||
let mut webhook_result = WebhookResult::Failiure;
|
|
||||||
if let Ok(r) = &mut res
|
|
||||||
&& let Some(data) = &mut r.data
|
|
||||||
&& let Some(q_res) = &mut data.transaction_update
|
|
||||||
{
|
|
||||||
if !q_res.errors.is_empty() {
|
|
||||||
q_res
|
|
||||||
.errors
|
|
||||||
.iter()
|
|
||||||
.for_each(|e| error!("failed update transaction, {:?}", e));
|
|
||||||
} else if let Some(tr) = &mut q_res.transaction {
|
|
||||||
tr.message = serde_json::to_string(&PaymentMethod {
|
|
||||||
payment_method: GatewayType::COD,
|
|
||||||
})?;
|
|
||||||
webhook_result = WebhookResult::Success;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
SyncWebhookEventType::TransactionCancelationRequested => {
|
||||||
|
let data = serde_json::from_str::<TransactionCancelationRequested2>(&body)?;
|
||||||
Json::from(serde_json::to_value(
|
Json::from(serde_json::to_value(
|
||||||
TransactionInitializeSessionResponse::<u8> {
|
TransactionCancelationRequestedResponse {
|
||||||
data: None,
|
|
||||||
time: None,
|
time: None,
|
||||||
psp_reference: None,
|
psp_reference: "".to_owned(),
|
||||||
external_url: None,
|
external_url: None,
|
||||||
message: None,
|
message: None,
|
||||||
amount: Decimal::from_str(&data.action.amount.0)?,
|
amount: data
|
||||||
result: match (data.action.action_type, webhook_result) {
|
.action
|
||||||
(TransactionFlowStrategyEnum::Charge, WebhookResult::Success) => {
|
.amount
|
||||||
TransactionSessionResult::ChargeSuccess
|
.and_then(|a| Decimal::from_str(&a.0).ok()),
|
||||||
}
|
result: Some(CancelationRequestedResult::CancelSuccess),
|
||||||
(
|
|
||||||
TransactionFlowStrategyEnum::Authorization,
|
|
||||||
WebhookResult::Success,
|
|
||||||
) => TransactionSessionResult::AuthorizationSuccess,
|
|
||||||
(TransactionFlowStrategyEnum::Charge, WebhookResult::Failiure) => {
|
|
||||||
TransactionSessionResult::ChargeFailiure
|
|
||||||
}
|
|
||||||
(
|
|
||||||
TransactionFlowStrategyEnum::Authorization,
|
|
||||||
WebhookResult::Failiure,
|
|
||||||
) => TransactionSessionResult::AuthorizationFailure,
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
)?)
|
)?)
|
||||||
}
|
}
|
||||||
|
SyncWebhookEventType::TransactionProcessSession => {
|
||||||
|
let data = serde_json::from_str::<TransactionProcessSession2>(&body)?;
|
||||||
|
Json::from(serde_json::to_value(TransactionProcessSessionResponse::<
|
||||||
|
u8,
|
||||||
|
> {
|
||||||
|
data: None,
|
||||||
|
time: None,
|
||||||
|
psp_reference: None,
|
||||||
|
external_url: None,
|
||||||
|
message: None,
|
||||||
|
amount: Decimal::from_str(&data.action.amount.0)?,
|
||||||
|
result: match data.action.action_type {
|
||||||
|
TransactionFlowStrategyEnum::Charge => {
|
||||||
|
TransactionSessionResult::ChargeSuccess
|
||||||
|
}
|
||||||
|
TransactionFlowStrategyEnum::Authorization => {
|
||||||
|
TransactionSessionResult::AuthorizationSuccess
|
||||||
|
}
|
||||||
|
},
|
||||||
|
})?)
|
||||||
|
}
|
||||||
_ => Json::from(Value::from_str("")?),
|
_ => Json::from(Value::from_str("")?),
|
||||||
},
|
},
|
||||||
_ => Json::from(Value::from_str("")?),
|
_ => Json::from(Value::from_str("")?),
|
||||||
};
|
};
|
||||||
|
debug!("{:?}", res.to_string());
|
||||||
info!("got webhooks!");
|
info!("got webhooks!");
|
||||||
Ok(res)
|
Ok(res)
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Serialize, Clone, Debug)]
|
|
||||||
pub struct JsonResponse {
|
|
||||||
data: JsonResponseData,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Serialize, Clone, Debug)]
|
|
||||||
pub struct JsonResponseData {
|
|
||||||
current_gateway: ActiveGateway,
|
|
||||||
}
|
|
||||||
enum WebhookResult {
|
enum WebhookResult {
|
||||||
Success,
|
Success,
|
||||||
Failiure,
|
Failiure,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Serialize, Clone, Debug)]
|
|
||||||
struct PaymentMethod {
|
|
||||||
payment_method: GatewayType,
|
|
||||||
}
|
|
||||||
|
|
|
@ -36,22 +36,18 @@ where
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn trace_to_std(config: &Config) {
|
pub fn trace_to_std(config: &Config) -> anyhow::Result<()> {
|
||||||
let filter = EnvFilter::builder()
|
let filter = EnvFilter::builder()
|
||||||
.with_default_directive(LevelFilter::DEBUG.into())
|
.with_default_directive(LevelFilter::DEBUG.into())
|
||||||
.from_env()
|
.from_env()?
|
||||||
.unwrap()
|
.add_directive(format!("{}={}", env!("CARGO_PKG_NAME"), config.log_level).parse()?);
|
||||||
.add_directive(
|
|
||||||
format!("{}={}", env!("CARGO_PKG_NAME"), config.log_level)
|
|
||||||
.parse()
|
|
||||||
.unwrap(),
|
|
||||||
);
|
|
||||||
tracing_subscriber::fmt()
|
tracing_subscriber::fmt()
|
||||||
.with_max_level(config.log_level)
|
.with_max_level(config.log_level)
|
||||||
.with_env_filter(filter)
|
.with_env_filter(filter)
|
||||||
.with_target(true)
|
.with_target(true)
|
||||||
.compact()
|
.compact()
|
||||||
.init();
|
.init();
|
||||||
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -1,3 +1,8 @@
|
||||||
|
#![allow(
|
||||||
|
non_upper_case_globals,
|
||||||
|
clippy::large_enum_variant,
|
||||||
|
clippy::upper_case_acronyms
|
||||||
|
)]
|
||||||
#![feature(let_chains)]
|
#![feature(let_chains)]
|
||||||
#![deny(clippy::unwrap_used, clippy::expect_used)]
|
#![deny(clippy::unwrap_used, clippy::expect_used)]
|
||||||
mod app;
|
mod app;
|
||||||
|
@ -6,8 +11,8 @@ mod routes;
|
||||||
|
|
||||||
use saleor_app_sdk::{
|
use saleor_app_sdk::{
|
||||||
config::Config,
|
config::Config,
|
||||||
manifest::{cargo_info, AppManifest, AppPermission},
|
manifest::{cargo_info, AppManifestBuilder, AppPermission},
|
||||||
webhooks::{AsyncWebhookEventType, WebhookManifest},
|
webhooks::{AsyncWebhookEventType, WebhookManifestBuilder},
|
||||||
SaleorApp,
|
SaleorApp,
|
||||||
};
|
};
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
|
@ -23,7 +28,7 @@ use crate::{
|
||||||
#[tokio::main]
|
#[tokio::main]
|
||||||
async fn main() -> anyhow::Result<()> {
|
async fn main() -> anyhow::Result<()> {
|
||||||
let config = Config::load()?;
|
let config = Config::load()?;
|
||||||
trace_to_std(&config);
|
trace_to_std(&config)?;
|
||||||
let sitemap_config = SitemapConfig::load()?;
|
let sitemap_config = SitemapConfig::load()?;
|
||||||
debug!("Creating configs...");
|
debug!("Creating configs...");
|
||||||
|
|
||||||
|
@ -31,13 +36,13 @@ async fn main() -> anyhow::Result<()> {
|
||||||
|
|
||||||
debug!("Creating saleor App...");
|
debug!("Creating saleor App...");
|
||||||
|
|
||||||
let app_manifest = AppManifest::new(&config, cargo_info!())
|
let app_manifest = AppManifestBuilder::new(&config, cargo_info!())
|
||||||
.add_permissions(vec![
|
.add_permissions(vec![
|
||||||
AppPermission::ManageProducts,
|
AppPermission::ManageProducts,
|
||||||
AppPermission::ManagePages,
|
AppPermission::ManagePages,
|
||||||
])
|
])
|
||||||
.add_webhook(
|
.add_webhook(
|
||||||
WebhookManifest::new(&config)
|
WebhookManifestBuilder::new(&config)
|
||||||
.set_query(EVENTS_QUERY)
|
.set_query(EVENTS_QUERY)
|
||||||
.add_async_events(vec![
|
.add_async_events(vec![
|
||||||
AsyncWebhookEventType::ProductCreated,
|
AsyncWebhookEventType::ProductCreated,
|
||||||
|
@ -98,7 +103,7 @@ async fn main() -> anyhow::Result<()> {
|
||||||
.unwrap_or(&"3000"),
|
.unwrap_or(&"3000"),
|
||||||
)
|
)
|
||||||
.await?;
|
.await?;
|
||||||
info!("listening on {}", listener.local_addr().unwrap());
|
info!("listening on {}", listener.local_addr()?);
|
||||||
match axum::serve(listener, app).await {
|
match axum::serve(listener, app).await {
|
||||||
Ok(o) => Ok(o),
|
Ok(o) => Ok(o),
|
||||||
Err(e) => anyhow::bail!(e),
|
Err(e) => anyhow::bail!(e),
|
||||||
|
|
|
@ -23,7 +23,7 @@ pub fn create_routes(state: AppState) -> Router {
|
||||||
}
|
}
|
||||||
let service = handle_404.into_service();
|
let service = handle_404.into_service();
|
||||||
//TODO : Fix this relative path issue in workspaces
|
//TODO : Fix this relative path issue in workspaces
|
||||||
let serve_dir = ServeDir::new("./sitemap-generator/public").not_found_service(service);
|
let serve_dir = ServeDir::new("./public").not_found_service(service);
|
||||||
|
|
||||||
//TODO Query for everything using the app auth token
|
//TODO Query for everything using the app auth token
|
||||||
//TODO "Failed fetching initial products: More than one channel exists, please spocify which
|
//TODO "Failed fetching initial products: More than one channel exists, please spocify which
|
||||||
|
|
|
@ -198,10 +198,7 @@ pub async fn regenerate(state: AppState, saleor_api_url: String) -> anyhow::Resu
|
||||||
category: match xml_data.iter().find(|all| {
|
category: match xml_data.iter().find(|all| {
|
||||||
x.relations
|
x.relations
|
||||||
.iter()
|
.iter()
|
||||||
.find(|rel| {
|
.any(|rel| all.id == *rel && all.data_type == XmlDataType::Category)
|
||||||
all.id == **rel && all.data_type == XmlDataType::Category
|
|
||||||
})
|
|
||||||
.is_some()
|
|
||||||
}) {
|
}) {
|
||||||
Some(c) => Some(event_subjects_updated::Category {
|
Some(c) => Some(event_subjects_updated::Category {
|
||||||
slug: c.slug.clone(),
|
slug: c.slug.clone(),
|
||||||
|
@ -286,34 +283,27 @@ async fn get_all_pages(
|
||||||
);
|
);
|
||||||
//Keep fetching next page
|
//Keep fetching next page
|
||||||
let mut next_cursor = pages.page_info.end_cursor.clone();
|
let mut next_cursor = pages.page_info.end_cursor.clone();
|
||||||
loop {
|
while let Some(cursor) = &mut next_cursor {
|
||||||
if let Some(cursor) = &mut next_cursor {
|
let res = surf::post(saleor_api_url)
|
||||||
let res = surf::post(saleor_api_url)
|
.header("authorization-bearer", token)
|
||||||
.header("authorization-bearer", token)
|
.run_graphql(GetPagesNext::build(GetPagesNextVariables { after: cursor }))
|
||||||
.run_graphql(GetPagesNext::build(GetPagesNextVariables { after: cursor }))
|
.await;
|
||||||
.await;
|
if let Ok(query) = &res
|
||||||
if let Ok(query) = &res
|
&& let Some(data) = &query.data
|
||||||
&& let Some(data) = &query.data
|
&& let Some(pages) = &data.pages
|
||||||
&& let Some(pages) = &data.pages
|
{
|
||||||
{
|
all_pages.append(
|
||||||
all_pages.append(
|
&mut pages
|
||||||
&mut pages
|
.edges
|
||||||
.edges
|
.iter()
|
||||||
.iter()
|
.map(|p| p.node.clone())
|
||||||
.map(|p| p.node.clone())
|
.collect::<Vec<_>>(),
|
||||||
.collect::<Vec<_>>(),
|
);
|
||||||
);
|
debug!("fetched next pages, eg.:{:?}", &pages.edges.first());
|
||||||
debug!("fetched next pages, eg.:{:?}", &pages.edges.first());
|
next_cursor.clone_from(&pages.page_info.end_cursor);
|
||||||
if !pages.page_info.has_next_page {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
next_cursor.clone_from(&pages.page_info.end_cursor);
|
|
||||||
} else {
|
|
||||||
error!("Failed fetching initial pages! {:?}", &res);
|
|
||||||
anyhow::bail!("Failed fetching initial pages! {:?}", res);
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
break;
|
error!("Failed fetching next pages! {:?}", &res);
|
||||||
|
anyhow::bail!("Failed fetching next pages! {:?}", res);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
@ -349,44 +339,37 @@ async fn get_all_categories(saleor_api_url: &str, token: &str) -> anyhow::Result
|
||||||
);
|
);
|
||||||
//Keep fetching next page
|
//Keep fetching next page
|
||||||
let mut next_cursor = categories.page_info.end_cursor.clone();
|
let mut next_cursor = categories.page_info.end_cursor.clone();
|
||||||
loop {
|
while let Some(cursor) = &mut next_cursor {
|
||||||
if let Some(cursor) = &mut next_cursor {
|
let res = surf::post(saleor_api_url)
|
||||||
let res = surf::post(saleor_api_url)
|
.header("authorization-bearer", token)
|
||||||
.header("authorization-bearer", token)
|
.run_graphql(GetCategoriesNext::build(GetCategoriesNextVariables {
|
||||||
.run_graphql(GetCategoriesNext::build(GetCategoriesNextVariables {
|
after: Some(cursor),
|
||||||
after: Some(cursor),
|
}))
|
||||||
}))
|
.await;
|
||||||
.await;
|
if let Ok(query) = &res
|
||||||
if let Ok(query) = &res
|
&& let Some(data) = &query.data
|
||||||
&& let Some(data) = &query.data
|
&& let Some(categories) = &data.categories
|
||||||
&& let Some(categories) = &data.categories
|
{
|
||||||
{
|
all_categories.append(
|
||||||
all_categories.append(
|
&mut categories
|
||||||
&mut categories
|
.edges
|
||||||
.edges
|
.iter()
|
||||||
.iter()
|
.map(|p| p.node.clone())
|
||||||
.map(|p| p.node.clone())
|
.collect::<Vec<_>>(),
|
||||||
.collect::<Vec<_>>(),
|
);
|
||||||
);
|
debug!(
|
||||||
debug!(
|
"fetched first categories, eg.:{:?}",
|
||||||
"fetched first categories, eg.:{:?}",
|
&categories.edges.first()
|
||||||
&categories.edges.first()
|
);
|
||||||
);
|
next_cursor.clone_from(&categories.page_info.end_cursor);
|
||||||
if !categories.page_info.has_next_page {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
next_cursor.clone_from(&categories.page_info.end_cursor);
|
|
||||||
} else {
|
|
||||||
error!("Failed fetching initial pages! {:?}", &res);
|
|
||||||
anyhow::bail!("Failed fetching initial pages! {:?}", res);
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
break;
|
error!("Failed fetching next categories! {:?}", &res);
|
||||||
|
anyhow::bail!("Failed fetching next categories! {:?}", res);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
error!("Failed fetching initial pages! {:?}", &res);
|
error!("Failed fetching initial Categories! {:?}", &res);
|
||||||
anyhow::bail!("Failed fetching initial pages! {:?}", res);
|
anyhow::bail!("Failed fetching initial Categories! {:?}", res);
|
||||||
};
|
};
|
||||||
info!("All categories collected");
|
info!("All categories collected");
|
||||||
Ok(all_categories)
|
Ok(all_categories)
|
||||||
|
@ -418,39 +401,32 @@ async fn get_all_collections(saleor_api_url: &str, token: &str) -> anyhow::Resul
|
||||||
|
|
||||||
//Keep fetching next page
|
//Keep fetching next page
|
||||||
let mut next_cursor = collections.page_info.end_cursor.clone();
|
let mut next_cursor = collections.page_info.end_cursor.clone();
|
||||||
loop {
|
while let Some(cursor) = &mut next_cursor {
|
||||||
if let Some(cursor) = &mut next_cursor {
|
let res = surf::post(saleor_api_url)
|
||||||
let res = surf::post(saleor_api_url)
|
.header("authorization-bearer", token)
|
||||||
.header("authorization-bearer", token)
|
.run_graphql(GetCollectionsNext::build(GetCollectionsNextVariables {
|
||||||
.run_graphql(GetCollectionsNext::build(GetCollectionsNextVariables {
|
after: Some(cursor),
|
||||||
after: Some(cursor),
|
}))
|
||||||
}))
|
.await;
|
||||||
.await;
|
if let Ok(query) = &res
|
||||||
if let Ok(query) = &res
|
&& let Some(data) = &query.data
|
||||||
&& let Some(data) = &query.data
|
&& let Some(collections) = &data.collections
|
||||||
&& let Some(collections) = &data.collections
|
{
|
||||||
{
|
all_collections.append(
|
||||||
all_collections.append(
|
&mut collections
|
||||||
&mut collections
|
.edges
|
||||||
.edges
|
.iter()
|
||||||
.iter()
|
.map(|p| p.node.clone())
|
||||||
.map(|p| p.node.clone())
|
.collect::<Vec<_>>(),
|
||||||
.collect::<Vec<_>>(),
|
);
|
||||||
);
|
debug!(
|
||||||
debug!(
|
"fetched next collections, eg.:{:?}",
|
||||||
"fetched next collections, eg.:{:?}",
|
&collections.edges.first()
|
||||||
&collections.edges.first()
|
);
|
||||||
);
|
next_cursor.clone_from(&collections.page_info.end_cursor);
|
||||||
if !collections.page_info.has_next_page {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
next_cursor.clone_from(&collections.page_info.end_cursor);
|
|
||||||
} else {
|
|
||||||
error!("Failed fetching initial collecnios! {:?}", &res);
|
|
||||||
anyhow::bail!("Failed fetching initial collections! {:?}", res);
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
break;
|
error!("Failed fetching next collecnios! {:?}", &res);
|
||||||
|
anyhow::bail!("Failed fetching next collections! {:?}", res);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
@ -498,7 +474,7 @@ async fn get_all_products(
|
||||||
debug!("fetched first products, eg: {:?}", products.edges.first());
|
debug!("fetched first products, eg: {:?}", products.edges.first());
|
||||||
let mut next_cursor = products.page_info.end_cursor.clone();
|
let mut next_cursor = products.page_info.end_cursor.clone();
|
||||||
loop {
|
loop {
|
||||||
if let Some(cursor) = &mut next_cursor {
|
while let Some(cursor) = &mut next_cursor {
|
||||||
let res = surf::post(saleor_api_url)
|
let res = surf::post(saleor_api_url)
|
||||||
.header("authorization-bearer", token)
|
.header("authorization-bearer", token)
|
||||||
.run_graphql(GetCategoryProductsNext::build(
|
.run_graphql(GetCategoryProductsNext::build(
|
||||||
|
@ -526,22 +502,14 @@ async fn get_all_products(
|
||||||
.collect::<Vec<_>>(),
|
.collect::<Vec<_>>(),
|
||||||
);
|
);
|
||||||
debug!("fetched next products, eg: {:?}", products.edges.first());
|
debug!("fetched next products, eg: {:?}", products.edges.first());
|
||||||
if !products.page_info.has_next_page {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
next_cursor.clone_from(&products.page_info.end_cursor);
|
next_cursor.clone_from(&products.page_info.end_cursor);
|
||||||
} else {
|
} else {
|
||||||
error!("Failed fetching initial products! {:?}", &res);
|
error!("Failed fetching initial products! {:?}", &res);
|
||||||
anyhow::bail!("Failed fetching initial products! {:?}", res);
|
anyhow::bail!("Failed fetching initial products! {:?}", res);
|
||||||
}
|
}
|
||||||
} else {
|
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
}
|
||||||
error!("Failed fetching initial products! {:?}", &res);
|
|
||||||
anyhow::bail!("Failed fetching initial products! {:?}", res);
|
|
||||||
};
|
|
||||||
info!("All products collected...");
|
info!("All products collected...");
|
||||||
Ok(all_categorised_products)
|
Ok(all_categorised_products)
|
||||||
}
|
}
|
||||||
|
|
|
@ -13,12 +13,7 @@ use saleor_app_sdk::{
|
||||||
AsyncWebhookEventType,
|
AsyncWebhookEventType,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
use sitemap_rs::{
|
use sitemap_rs::{sitemap::Sitemap, sitemap_index::SitemapIndex, url::Url, url_set::UrlSet};
|
||||||
sitemap::Sitemap,
|
|
||||||
sitemap_index::SitemapIndex,
|
|
||||||
url::{Url},
|
|
||||||
url_set::UrlSet,
|
|
||||||
};
|
|
||||||
use tinytemplate::TinyTemplate;
|
use tinytemplate::TinyTemplate;
|
||||||
use tokio::spawn;
|
use tokio::spawn;
|
||||||
use tracing::{debug, error, info};
|
use tracing::{debug, error, info};
|
||||||
|
@ -26,8 +21,8 @@ use tracing::{debug, error, info};
|
||||||
use crate::{
|
use crate::{
|
||||||
app::{AppError, AppState, XmlData, XmlDataType},
|
app::{AppError, AppState, XmlData, XmlDataType},
|
||||||
queries::event_subjects_updated::{
|
queries::event_subjects_updated::{
|
||||||
Category, Category2, CategoryUpdated, Collection, CollectionUpdated, Page,
|
Category, Category2, CategoryUpdated, Collection, CollectionUpdated, Page, PageUpdated,
|
||||||
PageUpdated, Product, ProductUpdated,
|
Product, ProductUpdated,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -46,8 +41,8 @@ pub async fn webhooks(
|
||||||
.to_str()?
|
.to_str()?
|
||||||
.to_owned();
|
.to_owned();
|
||||||
let event_type = get_webhook_event_type(&headers)?;
|
let event_type = get_webhook_event_type(&headers)?;
|
||||||
match event_type {
|
if let EitherWebhookType::Async(a) = event_type {
|
||||||
EitherWebhookType::Async(a) => match a {
|
match a {
|
||||||
AsyncWebhookEventType::ProductUpdated
|
AsyncWebhookEventType::ProductUpdated
|
||||||
| AsyncWebhookEventType::ProductCreated
|
| AsyncWebhookEventType::ProductCreated
|
||||||
| AsyncWebhookEventType::ProductDeleted => {
|
| AsyncWebhookEventType::ProductDeleted => {
|
||||||
|
@ -90,8 +85,7 @@ pub async fn webhooks(
|
||||||
}
|
}
|
||||||
|
|
||||||
_ => (),
|
_ => (),
|
||||||
},
|
}
|
||||||
_ => (),
|
|
||||||
}
|
}
|
||||||
|
|
||||||
info!("webhook proccessed");
|
info!("webhook proccessed");
|
||||||
|
@ -185,10 +179,7 @@ async fn update_sitemap_product(
|
||||||
xml_cat.last_modified = chrono::offset::Utc::now().fixed_offset();
|
xml_cat.last_modified = chrono::offset::Utc::now().fixed_offset();
|
||||||
// If the category exists but product isn't in relation to it yet,
|
// If the category exists but product isn't in relation to it yet,
|
||||||
// add it
|
// add it
|
||||||
if !xml_cat
|
if !xml_cat.relations.iter().any(|c| *c == product.id) {
|
||||||
.relations
|
|
||||||
.iter().any(|c| *c == product.id)
|
|
||||||
{
|
|
||||||
xml_cat.relations.push(product.id.clone());
|
xml_cat.relations.push(product.id.clone());
|
||||||
}
|
}
|
||||||
//if cat isn't in xml data, add it
|
//if cat isn't in xml data, add it
|
||||||
|
@ -216,10 +207,7 @@ async fn update_sitemap_product(
|
||||||
category: match xml_data.iter().find(|all| {
|
category: match xml_data.iter().find(|all| {
|
||||||
x.relations
|
x.relations
|
||||||
.iter()
|
.iter()
|
||||||
.find(|rel| {
|
.any(|rel| all.id == *rel && all.data_type == XmlDataType::Category)
|
||||||
all.id == **rel && all.data_type == XmlDataType::Category
|
|
||||||
})
|
|
||||||
.is_some()
|
|
||||||
}) {
|
}) {
|
||||||
Some(c) => Some(Category {
|
Some(c) => Some(Category {
|
||||||
slug: c.slug.clone(),
|
slug: c.slug.clone(),
|
||||||
|
@ -322,49 +310,38 @@ async fn update_sitemap_category(
|
||||||
let mut tt = TinyTemplate::new();
|
let mut tt = TinyTemplate::new();
|
||||||
if is_category_in_product_url && x.data_type == XmlDataType::Product {
|
if is_category_in_product_url && x.data_type == XmlDataType::Product {
|
||||||
tt.add_template("product_url", &state.sitemap_config.product_template)?;
|
tt.add_template("product_url", &state.sitemap_config.product_template)?;
|
||||||
let context;
|
|
||||||
//If current xml products category is this changed category, just use that instead
|
//If current xml products category is this changed category, just use that instead
|
||||||
//of searching for it again
|
//of searching for it again
|
||||||
match x.relations.iter().find(|c| *c == &category.id) {
|
let context = ProductUpdated {
|
||||||
Some(_) => {
|
product: match x.relations.iter().find(|c| *c == &category.id) {
|
||||||
context = ProductUpdated {
|
Some(_) => Some(Product {
|
||||||
product: Some(Product {
|
id: x.id.clone(),
|
||||||
id: x.id.clone(),
|
slug: x.slug.clone(),
|
||||||
slug: x.slug.clone(),
|
category: Some(Category {
|
||||||
category: Some(Category {
|
slug: category.slug.clone(),
|
||||||
slug: category.slug.clone(),
|
id: category.id.clone(),
|
||||||
id: category.id.clone(),
|
}),
|
||||||
|
}),
|
||||||
|
None => Some(Product {
|
||||||
|
id: x.id.clone(),
|
||||||
|
slug: x.slug.clone(),
|
||||||
|
category: match xml_data.iter().find(|all| {
|
||||||
|
x.relations.iter().any(|rel| {
|
||||||
|
all.id == *rel && all.data_type == XmlDataType::Category
|
||||||
|
})
|
||||||
|
}) {
|
||||||
|
Some(c) => Some(Category {
|
||||||
|
slug: c.slug.clone(),
|
||||||
|
id: c.id.clone(),
|
||||||
}),
|
}),
|
||||||
}),
|
None => Some(Category {
|
||||||
};
|
slug: "unknown".to_owned(),
|
||||||
}
|
id: cynic::Id::new("unknown".to_owned()),
|
||||||
None => {
|
}),
|
||||||
context = ProductUpdated {
|
},
|
||||||
product: Some(Product {
|
}),
|
||||||
id: x.id.clone(),
|
},
|
||||||
slug: x.slug.clone(),
|
};
|
||||||
category: match xml_data.iter().find(|all| {
|
|
||||||
x.relations
|
|
||||||
.iter()
|
|
||||||
.find(|rel| {
|
|
||||||
all.id == **rel
|
|
||||||
&& all.data_type == XmlDataType::Category
|
|
||||||
})
|
|
||||||
.is_some()
|
|
||||||
}) {
|
|
||||||
Some(c) => Some(Category {
|
|
||||||
slug: c.slug.clone(),
|
|
||||||
id: c.id.clone(),
|
|
||||||
}),
|
|
||||||
None => Some(Category {
|
|
||||||
slug: "unknown".to_owned(),
|
|
||||||
id: cynic::Id::new("unknown".to_owned()),
|
|
||||||
}),
|
|
||||||
},
|
|
||||||
}),
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
product_urls.push(
|
product_urls.push(
|
||||||
Url::builder(tt.render("product_url", &context)?)
|
Url::builder(tt.render("product_url", &context)?)
|
||||||
.last_modified(x.last_modified)
|
.last_modified(x.last_modified)
|
||||||
|
@ -564,8 +541,7 @@ pub async fn write_xml(
|
||||||
let mut sitemaps: Vec<UrlSet> = vec![];
|
let mut sitemaps: Vec<UrlSet> = vec![];
|
||||||
for urls in sliced_urls {
|
for urls in sliced_urls {
|
||||||
for url in urls.iter().cloned() {
|
for url in urls.iter().cloned() {
|
||||||
let mut sitemap_urls: Vec<Url> = vec![];
|
let sitemap_urls = vec![url];
|
||||||
sitemap_urls.push(url);
|
|
||||||
sitemaps.push(UrlSet::new(sitemap_urls)?);
|
sitemaps.push(UrlSet::new(sitemap_urls)?);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -616,22 +592,25 @@ async fn update_sitemap_index(state: &AppState) -> anyhow::Result<()> {
|
||||||
|
|
||||||
let sitemaps: Vec<Sitemap> = paths
|
let sitemaps: Vec<Sitemap> = paths
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.map(|p| {
|
.filter_map(|p| {
|
||||||
Sitemap::new(
|
if let Some(file_name) = p.file_name() {
|
||||||
format!(
|
Some(Sitemap::new(
|
||||||
"{}/{}",
|
format!(
|
||||||
state.sitemap_config.index_hostname,
|
"{}/{}",
|
||||||
p.file_name()
|
state.sitemap_config.index_hostname,
|
||||||
.expect("file dissapeared or broke during sitemap-index construction")
|
file_name.to_string_lossy()
|
||||||
.to_string_lossy()
|
),
|
||||||
),
|
p.metadata().map_or(None, |meta| {
|
||||||
p.metadata().map_or(None, |meta| {
|
meta.modified().map_or(None, |modified| {
|
||||||
meta.modified().map_or(None, |modified| {
|
let dt_utc: DateTime<Utc> = modified.into();
|
||||||
let dt_utc: DateTime<Utc> = modified.into();
|
Some(dt_utc.fixed_offset())
|
||||||
Some(dt_utc.fixed_offset())
|
})
|
||||||
})
|
}),
|
||||||
}),
|
))
|
||||||
)
|
} else {
|
||||||
|
error!("file dissapeared or broke during sitemap-index construction");
|
||||||
|
None
|
||||||
|
}
|
||||||
})
|
})
|
||||||
.collect::<Vec<_>>();
|
.collect::<Vec<_>>();
|
||||||
let sitemap_index = SitemapIndex::new(sitemaps)?;
|
let sitemap_index = SitemapIndex::new(sitemaps)?;
|
||||||
|
@ -646,7 +625,7 @@ async fn update_sitemap_index(state: &AppState) -> anyhow::Result<()> {
|
||||||
|
|
||||||
let mut buf = Vec::<u8>::new();
|
let mut buf = Vec::<u8>::new();
|
||||||
sitemap_index.write(&mut buf)?;
|
sitemap_index.write(&mut buf)?;
|
||||||
file.write_all(&mut buf).await?;
|
file.write_all(&buf).await?;
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue