diff --git a/.env b/.env
index 310ae9c..1b23c72 100644
--- a/.env
+++ b/.env
@@ -1,10 +1,10 @@
## COMMON VARIABLES FOR ALL APPS
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
-APP_API_BASE_URL="http://10.100.110.29:3000"
-APP_IFRAME_BASE_URL="http://10.100.110.29:3000"
+APP_API_BASE_URL="http://10.100.110.60:3000"
+APP_IFRAME_BASE_URL="http://10.100.110.60:3000"
APL="File"
-APL_URL="./temp/apl.json"
+APL_URL="apl.json"
LOG_LEVEL="DEBUG"
## THESE VARIABLES ARE FOR SITEMAP-GENERATOR APP
diff --git a/apl.json b/apl.json
new file mode 100644
index 0000000..287dfd2
--- /dev/null
+++ b/apl.json
@@ -0,0 +1,9 @@
+{
+ "http://localhost:8000/graphql/": {
+ "domain": "http://10.100.110.60:3000",
+ "token": "NhJa5QG3J4UGbXQSiYF9L7HySMCKqA",
+ "saleorApiUrl": "http://localhost:8000/graphql/",
+ "appId": "saleor-app-template-ui",
+ "jwks": null
+ }
+}
\ No newline at end of file
diff --git a/app-template-ui/Cargo.toml b/app-template-ui/Cargo.toml
index 0556caa..30dbcde 100644
--- a/app-template-ui/Cargo.toml
+++ b/app-template-ui/Cargo.toml
@@ -41,17 +41,7 @@ envy = { workspace = true }
cynic = { workspace = true, features = ["http-surf"], optional = true }
surf = { workspace = true, optional = true }
-[target.'cfg(target_family = "unix")'.dependencies]
-saleor-app-sdk = { workspace = true, optional = true, features = [
- "file_apl",
- "redis_apl",
- "tracing",
- "middleware",
- "webhook_utils",
-] }
-
-[target.'cfg(target_family = "wasm")'.dependencies]
-saleor-app-sdk = { workspace = true, optional = true, features = ["bridge"] }
+saleor-app-sdk = { workspace = true, optional = true }
[build-dependencies]
cynic-codegen = { workspace = true, optional = true }
@@ -79,6 +69,7 @@ ssr = [
"leptos/ssr",
"leptos_meta/ssr",
"leptos_router/ssr",
+ "saleor-app-sdk/bridge",
]
hydrate = [
"leptos/hydrate",
diff --git a/app-template-ui/src/main.rs b/app-template-ui/src/main.rs
index 1fc85fa..a668561 100644
--- a/app-template-ui/src/main.rs
+++ b/app-template-ui/src/main.rs
@@ -55,7 +55,11 @@ async fn main() -> Result<(), std::io::Error> {
let saleor_app = SaleorApp::new(&config).unwrap();
let app_manifest = AppManifestBuilder::new(&config, cargo_info!())
- .add_permission(AppPermission::ManageProducts)
+ .add_permissions(vec![
+ AppPermission::ManageProducts,
+ AppPermission::ManageOrders,
+ AppPermission::ManageProductTypesAndAttributes,
+ ])
.add_extension(
AppExtensionBuilder::new()
.set_url("/extensions/order_to_pdf")
@@ -69,7 +73,8 @@ async fn main() -> Result<(), std::io::Error> {
.set_target(AppExtensionTarget::Popup)
.build(),
)
- .build();
+ .build()
+ .expect("Manifest has invalid parameters");
let app_state = AppState {
manifest: app_manifest,
@@ -89,11 +94,11 @@ async fn main() -> Result<(), std::io::Error> {
.fallback(file_and_error_handler)
.route(
"/api/webhooks",
- post(webhooks).route_layer(middleware::from_fn(webhook_signature_verifier)),
+ post(webhooks)//.route_layer(middleware::from_fn(webhook_signature_verifier)),
)
.route(
"/api/register",
- post(register).route_layer(middleware::from_fn(webhook_signature_verifier)),
+ post(register)//.route_layer(middleware::from_fn(webhook_signature_verifier)),
)
.route("/api/manifest", get(manifest))
.with_state(app_state.clone());
diff --git a/app-template-ui/src/routes/api/register.rs b/app-template-ui/src/routes/api/register.rs
index 9e214ba..0d38c2e 100644
--- a/app-template-ui/src/routes/api/register.rs
+++ b/app-template-ui/src/routes/api/register.rs
@@ -5,9 +5,9 @@ use axum::{
http::{HeaderMap, StatusCode},
};
use saleor_app_sdk::{AuthData, AuthToken};
-use tracing::{debug, info};
+use tracing::{debug, error, info};
-use crate::{app::AppState, error_template::AxumError};
+use crate::{app::AppState, error_template::{self, AxumError}};
pub async fn register(
@@ -34,7 +34,10 @@ pub async fn register(
app_id: state.manifest.id,
saleor_api_url: saleor_api_url.clone(),
};
- app.apl.set(auth_data).await?;
+ if let Err(e) = app.apl.set(auth_data).await{
+ error!("{:?}",e);
+ return Err(error_template::AxumError::Anyhow(e))
+ };
info!("registered app for{:?}", &saleor_api_url);
Ok(StatusCode::OK)
diff --git a/app-template-ui/src/routes/extensions/order_to_pdf.rs b/app-template-ui/src/routes/extensions/order_to_pdf.rs
index d207c13..f5403c3 100644
--- a/app-template-ui/src/routes/extensions/order_to_pdf.rs
+++ b/app-template-ui/src/routes/extensions/order_to_pdf.rs
@@ -1,7 +1,46 @@
use leptos::*;
+use leptos_dom::logging::{console_error, console_log};
#[component]
pub fn Pdf() -> impl IntoView {
+ let bridge = create_effect(|_| {
+ use saleor_app_sdk::bridge::AppBridge;
+ use saleor_app_sdk::{
+ bridge::action::{Action, PayloadRequestPermissions},
+ manifest::AppPermission,
+ };
+ match AppBridge::new(true) {
+ Ok(mut app_bridge) => {
+ console_log("App Bridge connected");
+ let cb_handle = app_bridge
+ .listen_to_events(|event| match event {
+ Ok(event) => console_log(&format!("order_to_pdf: {:?}", event)),
+ Err(e) => console_error(&format!("order_to_pdf: {:?}", e)),
+ })
+ .unwrap();
+ //TODO: imagine leaking memory on purpose xd
+ cb_handle.forget();
+ _ = app_bridge.dispatch_event(Action::RequestPermissions(
+ PayloadRequestPermissions {
+ permissions: vec![AppPermission::ManageOrders],
+ redirect_path: "".to_owned(),
+ },
+ ));
+ }
+ Err(e) => console_error(&format!("{:?}", e)),
+ }; // let mut bridge = bridge.unwrap();
+
+ // match bridge.dispatch_event(Event::Handshake(PayloadHanshake::default())) {
+ // Ok(ev) => {
+ // console_log(&format! {"{:?}",ev});
+ // }
+ // Err(e) => {
+ // console_log(&format! {"{:?}",e});
+ // }
+ // };
+ // async fn temp(){}
+ // temp()
+ });
view! {
Yello!
r#"Loading AppBridge, please wait..."#
diff --git a/app-template/src/main.rs b/app-template/src/main.rs
index d7b7c90..adab0ab 100644
--- a/app-template/src/main.rs
+++ b/app-template/src/main.rs
@@ -76,7 +76,9 @@ async fn main() -> anyhow::Result<()> {
.build(),
)
.add_permission(AppPermission::ManageProducts)
- .build();
+ .build()
+ .expect("Manifest has invalid parameters");
+
let app_state = AppState {
manifest: app_manifest,
config: config.clone(),
diff --git a/sdk/src/bridge/action.rs b/sdk/src/bridge/action.rs
new file mode 100644
index 0000000..18fa067
--- /dev/null
+++ b/sdk/src/bridge/action.rs
@@ -0,0 +1,45 @@
+use serde::{Deserialize, Serialize};
+
+use crate::manifest::AppPermission;
+
+#[derive(Deserialize, Serialize, Debug)]
+#[serde(tag = "type", content = "payload")]
+#[serde(rename_all = "camelCase")]
+pub enum Action {
+ Redirect(PayloadRedirect),
+ RequestPermissions(PayloadRequestPermissions),
+ NotifyReady(String),
+ Notification(PayloadNotification),
+}
+
+#[derive(Deserialize, Serialize, Debug, Default)]
+#[serde(rename_all = "camelCase")]
+pub struct PayloadRequestPermissions {
+ pub permissions: Vec,
+ pub redirect_path: String,
+}
+
+#[derive(Deserialize, Serialize, Debug, Default)]
+#[serde(rename_all = "camelCase")]
+pub struct PayloadNotification {
+ pub status: Option,
+ pub title: Option,
+ pub text: Option,
+ pub api_message: Option,
+}
+
+#[derive(Deserialize, Serialize, Debug)]
+#[serde(rename_all = "lowercase")]
+pub enum NotificationStatus {
+ Info,
+ Success,
+ Warning,
+ Error,
+}
+
+#[derive(Deserialize, Serialize, Debug)]
+#[serde(rename_all = "camelCase")]
+pub struct PayloadRedirect {
+ pub to: String,
+ pub new_context: Option,
+}
diff --git a/sdk/src/bridge/event.rs b/sdk/src/bridge/event.rs
index 62ab73b..9e37887 100644
--- a/sdk/src/bridge/event.rs
+++ b/sdk/src/bridge/event.rs
@@ -1,4 +1,4 @@
-use crate::locales::LocaleCode;
+use crate::{locales::LocaleCode, manifest::AppPermission};
use super::ThemeType;
// use bus::{Bus, BusReader};
@@ -72,14 +72,38 @@ pub enum Event {
Theme(PayloadTheme),
LocaleChanged(PayloadLocaleChanged),
TokenRefreshed(PayloadTokenRefreshed),
- NotifyReady(String),
+}
+
+#[derive(Deserialize, Serialize, Debug, Default)]
+#[serde(rename_all = "camelCase")]
+pub struct PayloadRequestPermissions {
+ pub permissions: Vec,
+ pub redirect_path: String,
+}
+
+#[derive(Deserialize, Serialize, Debug, Default)]
+#[serde(rename_all = "camelCase")]
+pub struct PayloadNotification {
+ pub status: Option,
+ pub title: Option,
+ pub text: Option,
+ pub api_message: Option,
}
#[derive(Deserialize, Serialize, Debug)]
+#[serde(rename_all = "lowercase")]
+pub enum NotificationStatus {
+ Info,
+ Success,
+ Warning,
+ Error,
+}
+
+#[derive(Deserialize, Serialize, Debug, Default)]
#[serde(rename_all = "camelCase")]
pub struct PayloadHanshake {
pub token: String,
- pub version: String,
+ pub version: f32,
pub saleor_version: Option,
pub dashboard_version: Option,
}
@@ -87,23 +111,24 @@ pub struct PayloadHanshake {
#[derive(Deserialize, Serialize, Debug)]
#[serde(rename_all = "camelCase")]
pub struct PayloadResponse {
- pub action_id: String,
+ pub action_id: Option,
pub ok: bool,
}
#[derive(Deserialize, Serialize, Debug)]
#[serde(rename_all = "camelCase")]
pub struct PayloadRedirect {
- pub path: String,
+ pub to: String,
+ pub new_context: Option,
}
-#[derive(Deserialize, Serialize, Debug)]
+#[derive(Deserialize, Serialize, Debug, Default)]
#[serde(rename_all = "camelCase")]
pub struct PayloadTheme {
pub theme: ThemeType,
}
-#[derive(Deserialize, Serialize, Debug)]
+#[derive(Deserialize, Serialize, Debug, Default)]
#[serde(rename_all = "camelCase")]
pub struct PayloadLocaleChanged {
pub locale: LocaleCode,
diff --git a/sdk/src/bridge/mod.rs b/sdk/src/bridge/mod.rs
index fda314e..82b17b7 100644
--- a/sdk/src/bridge/mod.rs
+++ b/sdk/src/bridge/mod.rs
@@ -1,7 +1,9 @@
+pub mod action;
pub mod event;
use std::str::FromStr;
+use action::Action;
use serde::{Deserialize, Serialize};
use strum_macros::{EnumString, IntoStaticStr};
use wasm_bindgen::{closure::Closure, JsCast, JsValue};
@@ -9,7 +11,7 @@ use wasm_bindgen::{closure::Closure, JsCast, JsValue};
use crate::{locales::LocaleCode, manifest::AppPermission};
use self::event::Event;
-use web_sys::console;
+use web_sys::{console, js_sys::JSON};
pub struct AppBridge {
pub state: AppBridgeState,
@@ -19,7 +21,7 @@ pub struct AppBridge {
* Should automatically emit Actions.NotifyReady.
* If app loading time is longer, this can be disabled and sent manually.
*/
- auto_notify_ready: Option,
+ auto_notify_ready: bool,
}
#[derive(Default, Debug)]
@@ -120,7 +122,7 @@ impl Default for ThemeType {
}
impl AppBridge {
- pub fn new(auto_notify_ready: Option) -> Result {
+ pub fn new(auto_notify_ready: bool) -> Result {
// debug!("creating app bridge");
console::log_1(&"creating app bridge".into());
if web_sys::Window::is_type_of(&JsValue::from_str("undefined")) {
@@ -148,39 +150,61 @@ impl AppBridge {
},
referer_origin: referrer,
};
- if bridge.auto_notify_ready.unwrap_or(false) {
+ if bridge.auto_notify_ready {
bridge.notify_ready()?;
}
Ok(bridge)
}
- pub fn listen_to_events(&mut self) -> Result<&mut Self, AppBridgeError> {
+ /*
+ * make sure to keep the returned closure handle safe, once it deallocs events will no longer
+ * trigger
+ */
+ pub fn listen_to_events(
+ &mut self,
+ mut on_event: impl FnMut(Result) + 'static,
+ ) -> Result, AppBridgeError> {
let window = web_sys::window().ok_or(AppBridgeError::WindowIsUndefined)?;
- let cb = Closure::wrap(Box::new(|e: JsValue| {
- let event_data: Result = serde_wasm_bindgen::from_value(e);
- web_sys::console::log_1(&format!("{:?}", &event_data).into());
- // debug!("{:?}", &event_data);
- }) as Box);
+ let cb = Closure::wrap(Box::new(move |e: JsValue| {
+ web_sys::console::log_1(
+ &format!(
+ "sdk::bridge::listen_to_events: {:?}",
+ &JSON::stringify(&web_sys::js_sys::Reflect::get(&e, &"data".into()).unwrap())
+ .unwrap()
+ )
+ .into(),
+ );
+ let event_data: Result = serde_wasm_bindgen::from_value(
+ web_sys::js_sys::Reflect::get(&e, &"data".into())
+ .expect("Closure should've received object with .data property, but didn't, saleor plz fix?"),
+ );
+ // web_sys::console::log_1(&format!("{:?}", &event_data).into());
+ on_event(event_data);
+ }) as Box);
+
window
.add_event_listener_with_callback("message", &cb.as_ref().unchecked_ref())
.map_err(|e| AppBridgeError::JsValue(e))?;
- Ok(self)
+ Ok(cb)
}
- pub fn dispatch_event(&mut self, event: Event) -> Result {
+ pub fn dispatch_event(&mut self, action: Action) -> Result<(), AppBridgeError> {
let window = web_sys::window().ok_or(AppBridgeError::WindowIsUndefined)?;
let parent = match window.parent() {
Ok(p) => p.ok_or(AppBridgeError::WindowParentIsUndefined)?,
Err(e) => return Err(AppBridgeError::JsValue(e)),
};
- let message = JsValue::from_str(&serde_json::to_string(&event)?);
+ // let message = JsValue::from(&event);
+ let message = serde_wasm_bindgen::to_value(&action)?;
+ web_sys::console::log_1(&message);
parent
.post_message(&message, "*")
.map_err(|e| AppBridgeError::JsValue(e))?;
- todo!()
+ Ok(())
}
+
pub fn notify_ready(&mut self) -> Result<&mut Self, AppBridgeError> {
- self.dispatch_event(Event::NotifyReady("{}".to_owned()))?;
+ self.dispatch_event(Action::NotifyReady("{}".to_owned()))?;
Ok(self)
}
}
@@ -201,6 +225,6 @@ pub enum AppBridgeError {
#[derive(Serialize, Deserialize, Debug)]
pub struct SaleorIframeEvent {
- origin: String,
- data: Event,
+ pub origin: String,
+ pub data: Event,
}
diff --git a/sdk/src/manifest/mod.rs b/sdk/src/manifest/mod.rs
index b9c7205..401c75b 100644
--- a/sdk/src/manifest/mod.rs
+++ b/sdk/src/manifest/mod.rs
@@ -1,9 +1,10 @@
use serde::{Deserialize, Serialize};
pub mod extension;
+use thiserror::Error;
use crate::{config::Config, webhooks::WebhookManifest};
-#[derive(Debug, Clone, Serialize, Deserialize)]
+#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
#[serde(rename_all = "SCREAMING_SNAKE_CASE")]
pub enum AppPermission {
ManageUsers,
@@ -213,10 +214,27 @@ impl AppManifestBuilder {
}
self
}
- pub fn build(self) -> AppManifest {
- self.manifest
+ pub fn build(self) -> Result {
+ if let Some(ext) = self
+ .manifest
+ .extensions
+ .as_ref()
+ .map(|exts| exts.iter().flat_map(|e| &e.permissions).collect::>())
+ {
+ match ext.iter().all(|e| self.manifest.permissions.contains(e)) {
+ true => Ok(self.manifest),
+ false => Err(AppManifestBuilderError::MismatchedPermissions),
+ }
+ } else {
+ Ok(self.manifest)
+ }
}
}
+#[derive(Error, Debug)]
+pub enum AppManifestBuilderError {
+ #[error("manifest.permissions doesn't list all permissions needed in manifest.extensions.permissions")]
+ MismatchedPermissions,
+}
pub struct CargoInfo {
pub name: String,
pub description: String,
diff --git a/simple-payment-gateway/src/main.rs b/simple-payment-gateway/src/main.rs
index e51db00..edb9f9f 100644
--- a/simple-payment-gateway/src/main.rs
+++ b/simple-payment-gateway/src/main.rs
@@ -86,14 +86,15 @@ async fn main() -> anyhow::Result<()> {
AppPermission::ManageCheckouts,
AppPermission::HandleCheckouts,
])
- .build();
+ .build()
+ .expect("Manifest has invalid parameters");
let app_state = AppState {
active_payment_methods: get_active_payment_methods_from_env()?,
manifest: app_manifest,
config: config.clone(),
saleor_app: Arc::new(Mutex::new(saleor_app)),
- cod_extra_price_as_product_slug: std::env::var("COD_EXTRA_PRICE_AS_PRODUCT_SLUG").ok()
+ cod_extra_price_as_product_slug: std::env::var("COD_EXTRA_PRICE_AS_PRODUCT_SLUG").ok(),
};
let app = create_routes(app_state);
diff --git a/sitemap-generator/src/main.rs b/sitemap-generator/src/main.rs
index 1cc1107..a9455a8 100644
--- a/sitemap-generator/src/main.rs
+++ b/sitemap-generator/src/main.rs
@@ -84,7 +84,9 @@ async fn create_app(config: &Config, sitemap_config: SitemapConfig) -> Router {
])
.build(),
)
- .build();
+ .build()
+ .expect("Manifest has invalid parameters");
+
debug!("Created AppManifest...");
let (sender, receiver) = tokio::sync::mpsc::channel(100);