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

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
68 changes: 50 additions & 18 deletions generated/graphql.ts

Large diffs are not rendered by default.

3 changes: 2 additions & 1 deletion next-env.d.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
/// <reference types="next" />
/// <reference types="next/image-types/global" />
/// <reference types="next/navigation-types/compat/navigation" />

// NOTE: This file should not be edited
// see https://nextjs.org/docs/basic-features/typescript for more information.
// see https://nextjs.org/docs/app/api-reference/config/typescript for more information.
18 changes: 9 additions & 9 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -22,16 +22,16 @@
"pnpm": ">=9.0.0 <10.0.0"
},
"dependencies": {
"@saleor/app-sdk": "0.51.0",
"@saleor/app-sdk": "0.0.0-pr-20250127113254",
"@saleor/macaw-ui": "1.1.10",
"@urql/exchange-auth": "^1.0.0",
"@vitejs/plugin-react": "4.2.1",
"graphql": "^16.8.1",
"graphql-tag": "^2.12.6",
"jsdom": "^20.0.3",
"next": "14.1.2",
"react": "18.2.0",
"react-dom": "18.2.0",
"next": "15.1.3",
"react": "18.3.1",
"react-dom": "18.3.1",
"urql": "^4.0.2",
"vite": "5.2.10",
"vitest": "1.5.2"
Expand All @@ -48,11 +48,11 @@
"@graphql-codegen/urql-introspection": "2.2.1",
"@graphql-typed-document-node/core": "^3.2.0",
"@saleor/eslint-plugin-saleor-app": "^0.1.2",
"@types/node": "^18.11.18",
"@types/react": "^18.2.6",
"@types/react-dom": "^18.2.4",
"eslint": "8.31.0",
"eslint-config-next": "13.1.2",
"@types/node": "^22.10.5",
"@types/react": "18.3.1",
"@types/react-dom": "18.3.1",
"eslint": "9.17.0",
"eslint-config-next": "15.1.3",
"eslint-config-prettier": "^8.6.0",
"prettier": "^2.8.2",
"typescript": "5.0.4"
Expand Down
3,146 changes: 1,929 additions & 1,217 deletions pnpm-lock.yaml

Large diffs are not rendered by default.

59 changes: 59 additions & 0 deletions src/app/api-v2/manifest/route.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
import { createManifestHandler } from "@saleor/app-sdk/handlers/next-app-router";
import { AppManifest } from "@saleor/app-sdk/types";
import packageJson from "../../../../package.json";
import { orderCreatedWebhook } from "../webhooks/order-created/webhook";
import { orderFilterShippingMethodsWebhook } from "../webhooks/order-filter-shipping-methods/webhook";

export const GET = createManifestHandler({
manifestFactory({ appBaseUrl, request }) {
/**
* Allow to overwrite default app base url, to enable Docker support.
*
* See docs: https://docs.saleor.io/docs/3.x/developer/extending/apps/local-app-development
*/
const iframeBaseUrl = process.env.APP_IFRAME_BASE_URL ?? appBaseUrl;
const apiBaseURL = process.env.APP_API_BASE_URL ?? appBaseUrl;

const manifest: AppManifest = {
name: "Saleor App Template",
tokenTargetUrl: `${apiBaseURL}/api-v2/register`,
appUrl: iframeBaseUrl,
/**
* Set permissions for app if needed
* https://docs.saleor.io/docs/3.x/developer/permissions
*/
permissions: [
/**
* Add permission to allow "ORDER_CREATED" webhook registration.
*
* This can be removed
*/
"MANAGE_ORDERS",
],
id: "saleor.app",
version: packageJson.version,
/**
* Configure webhooks here. They will be created in Saleor during installation
* Read more
* https://docs.saleor.io/docs/3.x/developer/api-reference/webhooks/objects/webhook
*
* Easiest way to create webhook is to use app-sdk
* https://github.com/saleor/saleor-app-sdk/blob/main/docs/saleor-webhook.md
*/
webhooks: [orderCreatedWebhook.getWebhookManifest(apiBaseURL), orderFilterShippingMethodsWebhook.getWebhookManifest(apiBaseURL)],
/**
* Optionally, extend Dashboard with custom UIs
* https://docs.saleor.io/docs/3.x/developer/extending/apps/extending-dashboard-with-apps
*/
extensions: [],
author: "Saleor Commerce",
brand: {
logo: {
default: `${apiBaseURL}/logo.png`,
},
},
};

return manifest;
},
});
26 changes: 26 additions & 0 deletions src/app/api-v2/register/route.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
import { createAppRegisterHandler } from "@saleor/app-sdk/handlers/next-app-router";
import { saleorApp } from "../../../saleor-app";

export const runtime = "nodejs"; // FileAPL requires node environment to save files on disk using fs

/**
* Required endpoint, called by Saleor to install app.
* It will exchange tokens with app, so saleorApp.apl will contain token
*/
export const POST = createAppRegisterHandler({
apl: saleorApp.apl,
allowedSaleorUrls: [
/**
* You may want your app to work only for certain Saleor instances.
*
* Your app can work for every Saleor that installs it, but you can
* limit it here
*
* By default, every url is allowed.
*
* URL should be a full graphQL address, usually starting with https:// and ending with /graphql/
*
* Alternatively pass a function
*/
],
});
49 changes: 49 additions & 0 deletions src/app/api-v2/webhooks/order-created/route.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
import { createClient } from "../../../../lib/create-graphq-client";
import { orderCreatedWebhook } from "./webhook";

/**
* Export decorated Request handler, which adds extra context
*/
export const POST = orderCreatedWebhook.createHandler((request, ctx) => {
const {
/**
* Access payload from Saleor - defined above
*/
payload,
/**
* Saleor event that triggers the webhook (here - ORDER_CREATED)
*/
event,
/**
* App's URL
*/
baseUrl,
/**
* Auth data (from APL) - contains token and saleorApiUrl that can be used to construct graphQL client
*/
authData,
} = ctx;

/**
* Perform logic based on Saleor Event payload
*/
console.log(`Order was created for customer: ${payload.order?.userEmail}`);

/**
* Create GraphQL client to interact with Saleor API.
*/
const client = createClient(authData.saleorApiUrl, async () => ({ token: authData.token }));

/**
* Now you can fetch additional data using urql.
* https://formidable.com/open-source/urql/docs/api/core/#clientquery
*/

// const data = await client.query().toPromise()

/**
* Inform Saleor that webhook was delivered properly.
*/
return new Response("Accepted", { status: 200 });
});

49 changes: 49 additions & 0 deletions src/app/api-v2/webhooks/order-created/webhook.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
import { gql } from "urql";
import { OrderCreatedWebhookPayloadFragment } from "../../../../../generated/graphql";
import { saleorApp } from "../../../../saleor-app";
import { SaleorAsyncWebhook } from "@saleor/app-sdk/handlers/next-app-router";

/**
* Example payload of the webhook. It will be transformed with graphql-codegen to Typescript type: OrderCreatedWebhookPayloadFragment
*/
const OrderCreatedWebhookPayload = gql`
fragment OrderCreatedWebhookPayload on OrderCreated {
order {
userEmail
id
number
user {
email
firstName
lastName
}
}
}
`;

/**
* Top-level webhook subscription query, that will be attached to the Manifest.
* Saleor will use it to register webhook.
*/
const OrderCreatedGraphqlSubscription = gql`
# Payload fragment must be included in the root query
${OrderCreatedWebhookPayload}
subscription OrderCreated {
event {
...OrderCreatedWebhookPayload
}
}
`;

/**
* Create abstract Webhook. It decorates handler and performs security checks under the hood.
*
* orderCreatedWebhook.getWebhookManifest() must be called in api/manifest too!
*/
export const orderCreatedWebhook = new SaleorAsyncWebhook<OrderCreatedWebhookPayloadFragment>({
name: "Order Created in Saleor",
webhookPath: "api-v2/webhooks/order-created",
event: "ORDER_CREATED",
apl: saleorApp.apl,
query: OrderCreatedGraphqlSubscription,
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
import { orderFilterShippingMethodsWebhook } from "./webhook";

export const POST = orderFilterShippingMethodsWebhook.createHandler((request, ctx) => {
const { payload } = ctx;
console.log("Order Filter Shipping Methods Webhook received with: ", payload);
return new Response("{}", { status: 200 })
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
import { gql } from "urql";
import { SaleorSyncWebhook } from "@saleor/app-sdk/handlers/next-app-router";
import { OrderFilterShippingMethodsPayloadFragment } from "../../../../../generated/graphql";
import { saleorApp } from "../../../../saleor-app";

const OrderFilterShippingMethodsPayload = gql`
fragment OrderFilterShippingMethodsPayload on OrderFilterShippingMethods {
order {
deliveryMethod {
... on ShippingMethod {
id
name
}
}
}
}
`;

const OrderFilterShippingMethodsSubscription = gql`
${OrderFilterShippingMethodsPayload}
subscription OrderFilterShippingMethods {
event {
...OrderFilterShippingMethodsPayload
}
}
`;

export const orderFilterShippingMethodsWebhook =
new SaleorSyncWebhook<OrderFilterShippingMethodsPayloadFragment>({
name: "Order Filter Shipping Methods",
webhookPath: "api-v2/webhooks/order-filter-shipping-methods",
event: "ORDER_FILTER_SHIPPING_METHODS",
apl: saleorApp.apl,
query: OrderFilterShippingMethodsSubscription,
});
24 changes: 20 additions & 4 deletions tsconfig.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,11 @@
{
"compilerOptions": {
"target": "es5",
"lib": ["dom", "dom.iterable", "esnext"],
"lib": [
"dom",
"dom.iterable",
"esnext"
],
"allowJs": true,
"skipLibCheck": true,
"strict": true,
Expand All @@ -13,8 +17,20 @@
"resolveJsonModule": true,
"isolatedModules": true,
"jsx": "preserve",
"incremental": true
"incremental": true,
"plugins": [
{
"name": "next"
}
]
},
"include": ["next-env.d.ts", "**/*.ts", "**/*.tsx"],
"exclude": ["node_modules"]
"include": [
"**/*.ts",
"**/*.tsx",
"next-env.d.ts",
".next/types/**/*.ts"
],
"exclude": [
"node_modules"
]
}
Loading