diff --git a/README.md b/README.md index ddf98c9c5..141020785 100644 --- a/README.md +++ b/README.md @@ -76,7 +76,6 @@ npx create-miro-app@latest | [github-appcards](examples/github-appcards) | This full-stack example shows how to build an integration with GitHub that syncs data between GitHub issues and Miro app cards. | | [plant-uml](https://github.com/miroapp/miro-plantuml) | This full-stack example shows how to import [Plant UML](https://plantuml.com/) diagrams into Miro as editable board items. | | [nextjs](examples/nextjs-full-stack) | This full-stack example shows a Next.js application that uploads a camera image to the Miro board using Web SDK and REST API integration. | -| [webhooks-manager](examples/webhooks-manager/) | This full-stack example demonstrates how to interact with the webhooks API, and how to handle the webhooks challenge. |

 

diff --git a/examples/node-webhooks/.gitignore b/examples/node-webhooks/.gitignore deleted file mode 100644 index 22367671f..000000000 --- a/examples/node-webhooks/.gitignore +++ /dev/null @@ -1,24 +0,0 @@ -# See https://help.github.com/articles/ignoring-files/ for more about ignoring files. - -# dependencies -/node_modules -/.pnp -.pnp.js -.next - -# testing -/coverage - -# misc -.DS_Store -*.pem -.idea - -# debug -npm-debug.log* -yarn-debug.log* -yarn-error.log* - -# local env files -.env -dist \ No newline at end of file diff --git a/examples/node-webhooks/.sample.env b/examples/node-webhooks/.sample.env deleted file mode 100644 index d099aa952..000000000 --- a/examples/node-webhooks/.sample.env +++ /dev/null @@ -1,3 +0,0 @@ -MIRO_CLIENT_ID="" -MIRO_CLIENT_SECRET="" -MIRO_REDIRECT_URL="" \ No newline at end of file diff --git a/examples/node-webhooks/README.md b/examples/node-webhooks/README.md deleted file mode 100644 index 1eb75f28a..000000000 --- a/examples/node-webhooks/README.md +++ /dev/null @@ -1,103 +0,0 @@ -# Node Webhooks - -This app demonstrates how to receive webhook events from your Miro board using Node.js and Express.js. By following this guide, you will set up a local environment, create a webhook subscription, and test receiving events when changes are made on your Miro board. - -# 👨🏻‍💻 App Demo - -https://github.com/user-attachments/assets/1448b658-9e6f-4652-8300-6cebbf081f7a - -# 📒 Table of Contents - -- [Included Features](#features) -- [Tools and Technologies](#tools) -- [Prerequisites](#prerequisites) -- [Associated Developer Tutorial](#tutorial) -- [Run the app locally](#run) -- [Folder Structure](#folder) -- [Contributing](#contributing) -- [License](#license) - -# ⚙️ Included Features - -- [Miro Node Client Library with Express SDK](https://miroapp.github.io/api-clients/node/index.html) - - [miro.exchangeCodeForAccessToken()](https://miroapp.github.io/api-clients/node/classes/index.Miro.html#exchangeCodeForAccessToken) - - [miro.isAuthorized()](https://miroapp.github.io/api-clients/node/classes/index.Miro.html#isAuthorized) - - [miro.getAuthUrl()](https://miroapp.github.io/api-clients/node/classes/index.Miro.html#getAuthUrl) - - [miro.as()](https://miroapp.github.io/api-clients/node/classes/index.Miro.html#as) - - [api.getAllBoards()](https://miroapp.github.io/api-clients/node/classes/index.MiroApi.html#getAllBoards) - -# 🛠️ Tools and Technologies - -- [Node.js](https://nodejs.org/en) -- [Express.js](https://expressjs.com/) - -# ✅ Prerequisites - -- You have a [Miro account](https://miro.com/signup/). -- You're [signed in to Miro](https://miro.com/login/). -- Your Miro account has a [Developer team](https://developers.miro.com/docs/create-a-developer-team). -- Your development environment includes [Node.js 14.13](https://nodejs.org/en/download) or a later version, and npm. -- Your development environment includes [ngrok](https://ngrok.com/) or something similar. - -# 📖 Associated developer tutorial - -> To view a more in depth developer tutorial of this app including code explanations, see [Getting started with webhooks](https://developers.miro.com/docs/getting-started-with-webhooks) on Miro's developer portal. - -# 🏃🏽‍♂️ Run the app locally - -1. **Create a Miro app** on [developers.miro.com](https://developers.miro.com/). This will take you to the app settings page, where you will find the `MIRO_CLIENT_ID` and `MIRO_CLIENT_SECRET`. These need to be added to your `.env` file. - - - Ensure the `boards:read` scope is checked. - - Install the app on your developer team. You will get an **access token**, which is required later to authenticate your webhook subscription. - -2. In a new terminal window, run: - -``` -ngrok http 3000 -``` - -This will output something like this: - -``` -Forwarding https: -> http://localhost:3000 -``` - -The `https:` is your `MIRO_REDIRECT_URL` to be used in the `.env` file and then later when calling the API to create a webhook subscription. - -3. Rename the `.sample.env` file to `.env` and then add in your `MIRO_CLIENT_ID` and `MIRO_CLIENT_SECRET` from your [developers.miro.com](https://developers.miro.com/) app settings page. Use the `forwarding URL` from the previous step for the `MIRO_REDIRECT_URL` in the .env file. Save the file as `.env` with your new variables. - -4. Run `npm i` to install dependencies. - -5. Run `npm start` to start the dev server. - -6. Go to your developer team, and open the board you want to receive webhook events for. - -7. In a separate browser tab, open up the API Exporer for the [Create Webhook Subscription endpoint](https://developers.miro.com/reference/create-board-subscription). - -8. Provide the following information in the API Explorer: - -> **Access Token**: Once you get the access token after installing your app on a developer team (from step 4 above), you can add the access token to the Authorization section of the API reference page. -> -> **boardId:** Get the board ID of the board you want to receive notifications for. This board should be in the same team where you installed the app. You can find board ID in the URL when you go to your board: https://miro.com/app/board/. -> -> **callbackUrl:** This is the URL where you will receive events. It should be the same as `MIRO_REDIRECT_URL` in `.env`. 9. Select Try It! to run the API request right from the browser. If you get a 201 response, you are ready to receive events! - -10. Next, go to to the same board which you referenced in the request above, and create a sticky. You should now receive a webhook event! Great job! You've just learned how to get started with Miro's webhooks with Python 🎉. - -# 🗂️ Folder structure - -``` -. -├── src -│ └── app.js - main logic to receive webhooks and start the server -│ └── miroMiddleware.css <-- Middleware file to setup OAuth -├── .sample.env <-- File with sample env variables. Need to rename to .env and then add in your variables. -``` - -# 🫱🏻‍🫲🏽 Contributing - -If you want to contribute to this example, or any other Miro Open Source project, please review [Miro's contributing guide](https://github.com/miroapp/app-examples/blob/main/CONTRIBUTING.md). - -# 🪪 License - -[MIT License](https://github.com/miroapp/app-examples/blob/main/LICENSE). diff --git a/examples/node-webhooks/app-manifest.yaml b/examples/node-webhooks/app-manifest.yaml deleted file mode 100644 index c7b0c121a..000000000 --- a/examples/node-webhooks/app-manifest.yaml +++ /dev/null @@ -1,5 +0,0 @@ -# See https://developers.miro.com/docs/app-manifest on how to use this -appName: Node Webhooks -sdkUri: "http://localhost:3000" -scopes: - - boards:read diff --git a/examples/node-webhooks/jsconfig.json b/examples/node-webhooks/jsconfig.json deleted file mode 100644 index d23878618..000000000 --- a/examples/node-webhooks/jsconfig.json +++ /dev/null @@ -1,7 +0,0 @@ -{ - "compilerOptions": { - "typeRoots": ["./node_modules/@types", "./node_modules/@mirohq"] - }, - "include": ["src"], - "exclude": ["node_modules"] -} diff --git a/examples/node-webhooks/package.json b/examples/node-webhooks/package.json deleted file mode 100644 index 0201106d1..000000000 --- a/examples/node-webhooks/package.json +++ /dev/null @@ -1,21 +0,0 @@ -{ - "name": "node-webhooks", - "version": "0.1.0", - "license": "MIT", - "scripts": { - "start": "vite", - "build": "vite build", - "serve": "vite preview" - }, - "dependencies": { - "express": "^4.18.1", - "@mirohq/miro-api": "^2.0.0", - "cookie-parser": "^1.4.6", - "dotenv": "^16.0.3" - }, - "devDependencies": { - "vite": "3.0.3", - "vite-plugin-node": "^2.0.0" - }, - "type": "module" -} \ No newline at end of file diff --git a/examples/node-webhooks/src/app.js b/examples/node-webhooks/src/app.js deleted file mode 100644 index 8d40cfa68..000000000 --- a/examples/node-webhooks/src/app.js +++ /dev/null @@ -1,64 +0,0 @@ -import { config } from "dotenv"; - -import express from "express"; -import cookieParser from "cookie-parser"; - -import miroMiddleware from "./miroMiddleware"; - -config(); - -const app = express(); - -app.use(cookieParser("")); -app.use(miroMiddleware); -app.use(express.json()); - -app.get("/auth/miro/callback", async (req, res) => { - if (typeof req.query.code !== "string") { - res.status(400); - res.send("Missing code query parameter!"); - return; - } - await req.miro.exchangeCodeForAccessToken(req.cookies.id, req.query.code); - res.redirect("/"); -}); - -app.get("/", async (req, res) => { - if (!(await req.miro.isAuthorized(req.cookies.id))) { - res.redirect(req.miro.getAuthUrl()); - return; - } - - const api = req.miro.as(req.cookies.id); - - res.header("content-type", "text/html"); - res.write("These are the boards that you have access to:
"); - - const allBoards = api.getAllBoards(); - for await (const board of allBoards) { - res.write(`${board.name}
`); - } - res.send(); -}); - -app.post("/", async (req, res) => { - if (req.body.event) { - console.log("Webhook event:"); - console.log(req.body.event); - } - - if (req.body.challenge) { - console.log("Challenge:", req.body.challenge); - res.send(req.body); - return; - } - res.send("OK"); -}); - -if (import.meta.env.PROD) { - app.listen(3000, () => - console.log("Started server on http://127.0.0.1:3000"), - ); -} - -export const viteNodeApp = app; diff --git a/examples/node-webhooks/src/miroMiddleware.js b/examples/node-webhooks/src/miroMiddleware.js deleted file mode 100644 index 958f90246..000000000 --- a/examples/node-webhooks/src/miroMiddleware.js +++ /dev/null @@ -1,22 +0,0 @@ -import { Miro } from "@mirohq/miro-api"; - -export default function middlware(req, res, next) { - req.miro = new Miro({ - storage: { - // eslint-disable-next-line no-unused-vars - async get(_userId) { - try { - return JSON.parse(req.cookies.state); - } catch (err) { - return undefined; - } - }, - - set(userId, state) { - res.cookie("id", userId, { path: "/", secure: true }); - res.cookie("state", JSON.stringify(state), { path: "/", secure: true }); - }, - }, - }); - next(); -} diff --git a/examples/node-webhooks/tsconfig.json b/examples/node-webhooks/tsconfig.json deleted file mode 100644 index afecbf88b..000000000 --- a/examples/node-webhooks/tsconfig.json +++ /dev/null @@ -1,21 +0,0 @@ -{ - "compilerOptions": { - "target": "ESNext", - "useDefineForClassFields": true, - "lib": ["DOM", "DOM.Iterable", "ESNext"], - "allowJs": false, - "skipLibCheck": false, - "esModuleInterop": false, - "allowSyntheticDefaultImports": true, - "strict": true, - "forceConsistentCasingInFileNames": true, - "module": "ESNext", - "moduleResolution": "Node", - "resolveJsonModule": true, - "isolatedModules": true, - "noEmit": true, - "jsx": "react-jsx", - "typeRoots": ["./node_modules/@types", "./node_modules/@mirohq"] - }, - "include": ["./src", "node_modules"] -} diff --git a/examples/node-webhooks/vite.config.js b/examples/node-webhooks/vite.config.js deleted file mode 100644 index eb11f4b1b..000000000 --- a/examples/node-webhooks/vite.config.js +++ /dev/null @@ -1,18 +0,0 @@ -import { defineConfig } from "vite"; -import { VitePluginNode } from "vite-plugin-node"; - -export default defineConfig({ - server: { - port: 3000, - }, - plugins: [ - ...VitePluginNode({ - adapter: "express", - - appPath: "./src/app.js", - - exportName: "viteNodeApp", - }), - ], - optimizeDeps: {}, -}); diff --git a/examples/python-webhooks/.gitignore b/examples/python-webhooks/.gitignore deleted file mode 100644 index 22367671f..000000000 --- a/examples/python-webhooks/.gitignore +++ /dev/null @@ -1,24 +0,0 @@ -# See https://help.github.com/articles/ignoring-files/ for more about ignoring files. - -# dependencies -/node_modules -/.pnp -.pnp.js -.next - -# testing -/coverage - -# misc -.DS_Store -*.pem -.idea - -# debug -npm-debug.log* -yarn-debug.log* -yarn-error.log* - -# local env files -.env -dist \ No newline at end of file diff --git a/examples/python-webhooks/.sample.env b/examples/python-webhooks/.sample.env deleted file mode 100644 index d099aa952..000000000 --- a/examples/python-webhooks/.sample.env +++ /dev/null @@ -1,3 +0,0 @@ -MIRO_CLIENT_ID="" -MIRO_CLIENT_SECRET="" -MIRO_REDIRECT_URL="" \ No newline at end of file diff --git a/examples/python-webhooks/README.md b/examples/python-webhooks/README.md deleted file mode 100644 index 48f8b3385..000000000 --- a/examples/python-webhooks/README.md +++ /dev/null @@ -1,105 +0,0 @@ -# Python Webhooks - -This app demonstrates how to receive webhook events from your Miro board using Python and Flask. By following this guide, you will set up a local environment, create a webhook subscription, and test receiving events when changes are made on your Miro board. - -# 👨🏻‍💻 App Demo - -https://github.com/user-attachments/assets/0ccffb46-daab-4fc9-8ff8-f5720237f75a - -# 📒 Table of Contents - -- [Included Features](#features) -- [Tools and Technologies](#tools) -- [Prerequisites](#prerequisites) -- [Associated Developer Tutorial](#tutorial) -- [Run the app locally](#run) -- [Folder Structure](#folder) -- [Contributing](#contributing) -- [License](#license) - -# ⚙️ Included Features - -- [Miro Node Client Library with Python](https://miroapp.github.io/api-clients/python/miro_api.html) - - [miro.exchangeCodeForAccessToken()](https://miroapp.github.io/api-clients/python/miro_api.html#Miro.exchange_code_for_access_token) - - [miro.isAuthorized()](https://miroapp.github.io/api-clients/python/miro_api.html#Miro.is_authorized) - - [miro.getAuthUrl()](https://miroapp.github.io/api-clients/python/miro_api.html#Miro.get_auth_url) - - [api.get_all_boards()](https://miroapp.github.io/api-clients/python/miro_api/api_extended.html#MiroApiExtended.get_all_boards) - -# 🛠️ Tools and Technologies - -- [Python](https://www.python.org/) -- [Flask](https://flask.palletsprojects.com/en/3.0.x/) - -# ✅ Prerequisites - -- You have a [Miro account](https://miro.com/signup/). -- You're [signed in to Miro](https://miro.com/login/). -- Your Miro account has a [Developer team](https://developers.miro.com/docs/create-a-developer-team). -- Your development environment includes [Python](https://www.python.org/) 3.9 or a later version. -- Your development environment includes [pip](https://www.python.org/) 24.0 or a later version. -- Your development environment includes [ngrok](https://ngrok.com/) or something similar. - -# 📖 Associated developer tutorial - -> To view a more in depth developer tutorial of this app including code explanations, see [Getting started with Miro webhooks using Python](https://developers.miro.com/docs/getting-started-with-webhooks-python) on Miro's Developer documentation. - -# 🏃🏽‍♂️ Run the app locally - -> It is recommended to use a virtual environment to run this app example. Go to where you `.venv` is located and then run `source ./bin/activate` command. Read more about venvs [here](https://docs.python.org/3/library/venv.html). - -1. Create a new Miro app on [developers.miro.com](https://developers.miro.com/). This will take you to the app settings page. There you will find the `MIRO_CLIENT_ID` and `MIRO_CLIENT_SECRET` to be added to your `.env` file. Ensure that your app URL is `http://localhost:5000` since that is what port Flask will be running on. - - - Ensure the `boards:read` scope is selected. - - Install the app on your developer team. You will get an **access token** which you will need later to authenticate the creation of your webhook subscription. - -2. In a new terminal window, run: - -``` -ngrok http 5000 -``` - -This will output something like this: - -``` -Forwarding https: -> http://localhost:5000 -``` - -The `https:` is your `MIRO_REDIRECT_URL` to be used in the `.env` file and then later when calling the API to create a webhook subscription. - -3. Rename the `.sample.env` file to `.env` and then add in your `MIRO_CLIENT_ID` and `MIRO_CLIENT_SECRET` from your [developers.miro.com](https://developers.miro.com/) app settings page. Use the `forwarding URL` from the previous step for the `MIRO_REDIRECT_URL` in the .env file. Save the file as `.env` with your new variables. - -4. Run `pip install -r requirements.txt` to install dependencies. - -5. In a separate terminal from the ngrok terminal (leave ngrok running) go to `app-examples/examples/python-webhooks` directiory. Run `flask --app app run` to start the server. Your server should be running on port 5000. - -6. Go to your developer team, and open the board you want to receive webhook events for. - -7. In a separate browser tab, open up the API Exporer for the [Create Webhook Subscription endpoint](https://developers.miro.com/reference/create-board-subscription). - -8. Provide the following information in the API Explorer: - -> **Access Token**: Once you get the access token after installing your app on a developer team (from step 4 above), you can add the access token to the Authorization section of the API reference page. -> -> **boardId:** Get the board ID of the board you want to receive notifications for. This board should be in the same team where you installed the app. You can find board ID in the URL when you go to your board: https://miro.com/app/board/. -> -> **callbackUrl:** This is the URL where you will receive events. It should be the same as `MIRO_REDIRECT_URL` in `.env`. 9. Select Try It! to run the API request right from the browser. If you get a 201 response, you are ready to receive events! - -9. Next, go to to the same board which you referenced in the request above, and create a sticky. You should now receive a webhook event! Great job! You've just learned how to get started with Miro's webhooks with Python 🎉. - -# 🗂️ Folder structure - -``` -. -├── app.py - main logic to receive webhooks and start the server -├── .sample.env <-- File with sample env variables. Need to rename to .env and then add in your variables. -├── app-manifest.yaml <-- File with sample manifest file for easy copy paste into your developer app settings manifest. -├── requirements.txt <-- File with libraries which the project depends on, including versions. -``` - -# 🫱🏻‍🫲🏽 Contributing - -If you want to contribute to this example, or any other Miro Open Source project, please review [Miro's contributing guide](https://github.com/miroapp/app-examples/blob/main/CONTRIBUTING.md). - -# 🪪 License - -[MIT License](https://github.com/miroapp/app-examples/blob/main/LICENSE). diff --git a/examples/python-webhooks/app-manifest.yaml b/examples/python-webhooks/app-manifest.yaml deleted file mode 100644 index 949a22cef..000000000 --- a/examples/python-webhooks/app-manifest.yaml +++ /dev/null @@ -1,5 +0,0 @@ -# See https://developers.miro.com/docs/app-manifest on how to use this -appName: Python Webhooks -sdkUri: "http://localhost:5000" -scopes: - - boards:read diff --git a/examples/python-webhooks/app.py b/examples/python-webhooks/app.py deleted file mode 100644 index 922450898..000000000 --- a/examples/python-webhooks/app.py +++ /dev/null @@ -1,53 +0,0 @@ -from flask import Flask, session, request, json, Response -from miro_api import Miro -from miro_api.storage import Storage - -from dotenv import load_dotenv - -load_dotenv() - -app = Flask(__name__) - -app.secret_key = b"very_random_secret" - - -class SessionStorage(Storage): - session_key = "miro_state" - - def get(self): - return session[self.session_key] - - def set(self, state): - if not state: - session.pop(self.session_key, None) - return - session[self.session_key] = state - - -miro = Miro(storage=SessionStorage()) - - -def render_boards(): - boards = miro.api.get_all_boards() - names = "
".join([board.name for board in boards]) - return f"

List of boards in the team: {names}

" - - -@app.route("/", methods=["GET", "POST"]) -def hello_world(): - if request.method == "GET": - if miro.is_authorized: - return render_boards() - - if code := request.args.get("code", ""): - miro.exchange_code_for_access_token(code) - return render_boards() - - return f"Login to Miro" - if request.method == "POST": - webhook_data = request.json - formatted_webhook_data = json.dumps(webhook_data, indent=4) - print(f"webhook event: {formatted_webhook_data}") - return Response( - json.dumps(webhook_data), status=200, mimetype="application/json" - ) diff --git a/examples/python-webhooks/requirements.txt b/examples/python-webhooks/requirements.txt deleted file mode 100644 index f1239ada1..000000000 --- a/examples/python-webhooks/requirements.txt +++ /dev/null @@ -1,3 +0,0 @@ -Flask==3.0.3 -miro_api==2.2.0 -python-dotenv==1.0.1 \ No newline at end of file