├── .eslintrc.json
├── .github
├── ISSUE_TEMPLATE
│ ├── bug_report.md
│ └── feature_request.md
└── workflows
│ ├── npmpublish.yml
│ └── pull_request.yml
├── .gitignore
├── .nvmrc
├── .prettierrc
├── CODE_OF_CONDUCT.md
├── LICENSE
├── README.md
├── dist
└── main.js
├── package-lock.json
├── package.json
├── server-sdk.js
├── src
├── client-test.js
├── client.js
├── constants-test.js
├── constants.js
├── helpers
│ ├── index.js
│ ├── monday-api-helpers.js
│ └── ui-helpers.js
├── index.js
├── monday-api-client
│ ├── fetch.js
│ ├── index.js
│ ├── monday-api-client-test.js
│ └── monday-api-client.js
├── server-test.js
├── server.js
├── services
│ ├── background-tracking-service.js
│ └── oauth-service.js
└── tests
│ └── helpers.js
├── ts-tests
└── monday-sdk-js-module.test.ts
├── tsconfig.json
├── types
├── client-api.interface.ts
├── client-context.type.ts
├── client-data.interface.ts
├── client-execute.interface.ts
├── client-sdk.interface.ts
├── index.d.ts
├── server-sdk.interface.ts
└── theme-config.type.ts
└── webpack.config.js
/.eslintrc.json:
--------------------------------------------------------------------------------
1 | {
2 | "env": {
3 | "browser": true,
4 | "commonjs": true,
5 | "es6": true,
6 | "node": true,
7 | "amd": true,
8 | "mocha": true
9 | },
10 | "extends": "eslint:recommended",
11 | "globals": {
12 | "Atomics": "readonly",
13 | "SharedArrayBuffer": "readonly",
14 | "__BUNDLE__": true
15 | },
16 | "parserOptions": {
17 | "ecmaVersion": 2018
18 | },
19 | "rules": {}
20 | }
21 |
--------------------------------------------------------------------------------
/.github/ISSUE_TEMPLATE/bug_report.md:
--------------------------------------------------------------------------------
1 | ---
2 | name: Bug report
3 | about: Create a report to help us improve
4 | title: "[Bug]"
5 | labels: ''
6 | assignees: ''
7 |
8 | ---
9 |
10 | **Describe the bug**
11 | A clear and concise description of what the bug is.
12 |
13 | **Steps to reproduce**
14 | 1. Embed the SDK in an app
15 | 2. Call `monday.api(...)`
16 | 3. See the request fails
17 | 4. ...
18 |
19 | **Expected behavior**
20 | A clear and concise description of what you expected to happen.
21 |
22 | **Context and environment**
23 | What capabilities were you trying to use, on which browser/mobile device, what app was involved...
24 |
25 | **Additional information**
26 | Screenshots and any other pieces of information that may be relevant for us to try and reproduce the issue
27 |
--------------------------------------------------------------------------------
/.github/ISSUE_TEMPLATE/feature_request.md:
--------------------------------------------------------------------------------
1 | ---
2 | name: Feature request
3 | about: Suggest an idea for this project
4 | title: "[Feature]"
5 | labels: ''
6 | assignees: ''
7 |
8 | ---
9 |
10 | **What problem is this request going to solve?**
11 | If relevant, explain what issue this feature request is aimed at solving, or how will the SDK become better once this is implemented
12 |
13 | **What is the solution you'd like to see?**
14 | A clear and concise description of what you want to happen
15 |
16 | **Describe alternatives you've considered**
17 | Have you tried alternative methods to solve this issue? Did they fail completely, or were you able to achieve your goal in a suboptimal way?
18 |
19 | **Additional information**
20 | Add any other context or screenshots about the feature request here
21 |
--------------------------------------------------------------------------------
/.github/workflows/npmpublish.yml:
--------------------------------------------------------------------------------
1 | name: Node.js Package
2 |
3 | on:
4 | workflow_dispatch:
5 | release:
6 | types: [created]
7 |
8 | jobs:
9 | build:
10 | runs-on: ubuntu-latest
11 | steps:
12 | - uses: actions/checkout@v2
13 | - uses: actions/setup-node@v1
14 | with:
15 | node-version: 16
16 | #- run: npm ci
17 | #- run: npm tests
18 |
19 | publish-npm:
20 | needs: build
21 | runs-on: ubuntu-latest
22 | steps:
23 | - uses: actions/checkout@v2
24 | - uses: actions/setup-node@v1
25 | with:
26 | node-version: 16
27 | registry-url: https://registry.npmjs.org/
28 | - run: npm ci
29 | - run: npm publish
30 | env:
31 | NODE_AUTH_TOKEN: ${{secrets.npm_token}}
32 |
--------------------------------------------------------------------------------
/.github/workflows/pull_request.yml:
--------------------------------------------------------------------------------
1 | on: pull_request
2 | name: Pull Request
3 | jobs:
4 | test:
5 | name: Run tests
6 | runs-on: ubuntu-latest
7 | steps:
8 | - uses: actions/checkout@master
9 | - name: Use Node.js
10 | uses: actions/setup-node@v1
11 | with:
12 | node-version: "16.x"
13 | - run: npm install
14 | - run: npm run compile-types
15 | - run: npm run test
16 | - run: npm run lint
17 | - run: npm run style-check
18 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | ### IntelliJ ###
2 | .idea
3 | out/
4 | logs
5 | *.log
6 | npm-debug.log*
7 | yarn-debug.log*
8 | yarn-error.log*
9 | coverage
10 | node_modules/
11 | .npm
12 | .eslintcache
13 | *.tgz
14 | .env
15 | .env.test
16 | .cache
17 | tmp/
18 | temp/
19 | .vscode/*
20 | .history
21 | .DS_Store
22 |
--------------------------------------------------------------------------------
/.nvmrc:
--------------------------------------------------------------------------------
1 | 16.13
2 |
--------------------------------------------------------------------------------
/.prettierrc:
--------------------------------------------------------------------------------
1 | {
2 | "printWidth": 120
3 | }
4 |
--------------------------------------------------------------------------------
/CODE_OF_CONDUCT.md:
--------------------------------------------------------------------------------
1 | ### Write me
2 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2020 monday.com LTD
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | # monday.com Apps framework SDK for JavaScript
5 | [](https://github.com/mondaycom/monday-sdk-js/blob/master/LICENSE) [](https://www.npmjs.com/package/monday-sdk-js) [](https://www.npmjs.com/package/monday-sdk-js) [](https://www.jsdelivr.com/package/npm/monday-sdk-js)
6 |
7 |
8 | The monday.com SDK provides a toolset for application developers to build features and solutions on top of the monday.com Work OS platform. You'll find this SDK useful if you want to:
9 |
10 | - Access monday.com account data from your application, by utilizing the GraphQL client
11 | - Build Board Views & Dashboard Widgets that extend the monday.com UI
12 | - Build Integrations & Automations using your own external services and business logic
13 |
14 | The SDK contains methods for server-side and client-side application development. Client-side capabilities assume a valid user session is present (and can seamlessly act on behalf of that user), while server-side methods can be used to access monday.com features using explicit credentials but without any client-side code.
15 |
16 | ## Usage
17 |
18 | ### Using as an `npm` module
19 | Install the SDK as a module:
20 |
21 | `npm install monday-sdk-js --save`
22 |
23 | Then import into your project:
24 | ```js
25 | import mondaySdk from "monday-sdk-js";
26 | const monday = mondaySdk();
27 | ```
28 |
29 |
30 | ### As a `
35 |
36 | ```
37 | and then initialize the SDK anywhere in the page by declaring:
38 |
39 | ```js
40 | const monday = window.mondaySdk()
41 | ```
42 |
43 | ## Docs
44 |
45 | To get started, check out the [SDK Documentation](https://developer.monday.com/apps/docs/introduction-to-the-sdk)
46 |
--------------------------------------------------------------------------------
/dist/main.js:
--------------------------------------------------------------------------------
1 | !function(e){var t={};function n(i){if(t[i])return t[i].exports;var r=t[i]={i:i,l:!1,exports:{}};return e[i].call(r.exports,r,r.exports,n),r.l=!0,r.exports}n.m=e,n.c=t,n.d=function(e,t,i){n.o(e,t)||Object.defineProperty(e,t,{enumerable:!0,get:i})},n.r=function(e){"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0})},n.t=function(e,t){if(1&t&&(e=n(e)),8&t)return e;if(4&t&&"object"==typeof e&&e&&e.__esModule)return e;var i=Object.create(null);if(n.r(i),Object.defineProperty(i,"default",{enumerable:!0,value:e}),2&t&&"string"!=typeof e)for(var r in e)n.d(i,r,function(t){return e[t]}.bind(null,r));return i},n.n=function(e){var t=e&&e.__esModule?function(){return e.default}:function(){return e};return n.d(t,"a",t),t},n.o=function(e,t){return Object.prototype.hasOwnProperty.call(e,t)},n.p="",n(n.s=4)}([function(e,t,n){e.exports=n(6)},function(e,t,n){(function(t){function n(){return void 0!==t&&!1}const i=(e,i)=>n()&&"undefined"!==t.env[e]&&void 0!==t.env[e]?t.env[e]:i,r=()=>i("MONDAY_COM_PROTOCOL","https"),s=()=>i("MONDAY_COM_DOMAIN","monday.com"),o=()=>i("MONDAY_OAUTH_SUBDOMAIN","auth."),a=()=>`${r()}://${i("MONDAY_SUBDOMAIN_API","api.")}${s()}/v2`;e.exports={get MONDAY_DOMAIN(){return s()},get MONDAY_PROTOCOL(){return r()},get MONDAY_API_URL(){return a()},get MONDAY_OAUTH_URL(){return`${r()}://${o()}${s()}/oauth2/authorize`},get MONDAY_OAUTH_TOKEN_URL(){return`${r()}://${o()}${s()}/oauth2/token`}}}).call(this,n(7))},function(e,t){const n="undefined"!=typeof window&&void 0!==window.document;e.exports={convertToArrayIfNeeded:e=>Array.isArray(e)?e:[e],isBrowser:n}},function(e,t){e.exports={logWarnings:e=>{const t=e&&e.extensions&&e.extensions.warnings;return t&&Array.isArray(t)?(t.forEach(e=>{if(e&&e.message)try{const t=e.locations&&e.locations.map(e=>`line ${e.line}, column ${e.column}`).join("; "),n=e.path&&e.path.join(" → ");let i=e.message;i=i.replace(/\.$/,""),i=i.charAt(0).toLowerCase()+i.slice(1);const r=["[monday API]",`${n}:`,i,t&&`@ ${t}`,e.extensions?["\n\nAdditional details:",e.extensions]:void 0].flat().filter(Boolean);console.warn(...r)}catch(t){e&&console.warn("[monday API] Warning:",e)}}),e):e}}},function(e,t,n){var i,r;const{isBrowser:s}=n(2),o=n(s?5:13);"undefined"!=typeof self&&self,void 0===(r="function"==typeof(i=function(){return window.mondaySdk=o,o})?i.call(t,n,t,e):i)||(e.exports=r)},function(e,t,n){const i=n(0),{MONDAY_OAUTH_URL:r}=n(1),{convertToArrayIfNeeded:s}=n(2),{initScrollHelperIfNeeded:o}=n(10),{initBackgroundTracking:a}=n(11),{logWarnings:c}=n(3),l=[],u="v2",d="instance";class h{constructor(e={}){this._clientId=e.clientId,this._apiToken=e.apiToken,this._apiVersion=e.apiVersion,this.listeners={},this.setClientId=this.setClientId.bind(this),this.setToken=this.setToken.bind(this),this.setApiVersion=this.setApiVersion.bind(this),this.api=this.api.bind(this),this.listen=this.listen.bind(this),this.get=this.get.bind(this),this.set=this.set.bind(this),this.execute=this.execute.bind(this),this.oauth=this.oauth.bind(this),this._receiveMessage=this._receiveMessage.bind(this),this.storage={setItem:this.setStorageItem.bind(this),getItem:this.getStorageItem.bind(this),deleteItem:this.deleteStorageItem.bind(this),instance:{setItem:this.setStorageInstanceItem.bind(this),getItem:this.getStorageInstanceItem.bind(this),deleteItem:this.deleteStorageInstanceItem.bind(this)}},window.addEventListener("message",this._receiveMessage,!1),e.withoutScrollHelper||o(),a(this)}setClientId(e){this._clientId=e}setToken(e){this._apiToken=e}setApiVersion(e){this._apiVersion=e}api(e,t={}){const n={query:e,variables:t.variables},r=t.token||this._apiToken,s=t.apiVersion||this._apiVersion;let o;return o=r?i.execute(n,r,{apiVersion:s}):this._localApi("api",{params:n,apiVersion:s}).then(e=>e.data),o.then(c)}listen(e,t,n){const i=s(e),r=[];return i.forEach(e=>{r.push(this._addListener(e,t)),this._localApi("listen",{type:e,params:n})}),()=>{r.forEach(e=>e())}}get(e,t){return this._localApi("get",{type:e,params:t})}set(e,t){return this._localApi("set",{type:e,params:t})}execute(e,t){return this._localApi("execute",{type:e,params:t})}track(e,t){return this.execute("track",{name:e,data:t})}oauth(e={}){const t=e.clientId||this._clientId;if(!t)throw new Error("clientId is required");const n=`${e.mondayOauthUrl||r}?client_id=${t}`;window.location=n}setStorageItem(e,t,n={}){return this._localApi("storage",{method:"set",key:e,value:t,options:n,segment:u})}getStorageItem(e,t={}){return this._localApi("storage",{method:"get",key:e,options:t,segment:u})}deleteStorageItem(e,t={}){return this._localApi("storage",{method:"delete",key:e,options:t,segment:u})}setStorageInstanceItem(e,t,n={}){return this._localApi("storage",{method:"set",key:e,value:t,options:n,segment:d})}getStorageInstanceItem(e,t={}){return this._localApi("storage",{method:"get",key:e,options:t,segment:d})}deleteStorageInstanceItem(e,t={}){return this._localApi("storage",{method:"delete",key:e,options:t,segment:d})}_localApi(e,t){return new Promise((i,r)=>{const s=this._generateRequestId(),o=this._clientId,a=n(12).version;window.parent.postMessage({method:e,args:t,requestId:s,clientId:o,version:a},"*");const c=this._addListener(s,e=>{if(c(),e.errorMessage){const t=new Error(e.errorMessage);t.data=e.data,r(t)}else i(e)})})}_receiveMessage(e){const{method:t,type:n,requestId:i}=e.data,r=this.listeners[t]||l,s=this.listeners[n]||l,o=this.listeners[i]||l;let a=new Set([...r,...s,...o]);a&&a.forEach(t=>{try{t(e.data)}catch(e){console.error("Message callback error: ",e)}})}_addListener(e,t){return this.listeners[e]=this.listeners[e]||new Set,this.listeners[e].add(t),()=>{this.listeners[e].delete(t),0===this.listeners[e].size&&delete this.listeners[e]}}_generateRequestId(){return Math.random().toString(36).substring(2,9)}_removeEventListener(){window.removeEventListener("message",this._receiveMessage,!1)}_clearListeners(){this.listeners=[]}}e.exports=function(e={}){return new h(e)}},function(e,t,n){const{MONDAY_API_URL:i,MONDAY_OAUTH_TOKEN_URL:r}=n(1),s=n(8);e.exports={execute:async function(e,t,n={}){if(!t&&n.url!==r)throw new Error("Token is required");const o=`${n.url||i}${n.path||""}`;let a=await function(e,t,n,i={}){return s.nodeFetch(e,{method:i.method||"POST",body:JSON.stringify(t||{}),headers:{Authorization:n,"Content-Type":"application/json",...i.apiVersion?{"API-Version":i.apiVersion}:{}}})}(o,e,t,n);const c=a.status,l=a.headers.get("content-type");if(!l||!l.includes("application/json")){if(504===c)throw new Error("Received timeout from monday.com's GraphQL API");const e=await a.text();throw new Error(e)}try{return await a.json()}catch(e){throw new Error("Could not parse JSON from monday.com's GraphQL API response")}},COULD_NOT_PARSE_JSON_RESPONSE_ERROR:"Could not parse JSON from monday.com's GraphQL API response",TOKEN_IS_REQUIRED_ERROR:"Token is required",API_TIMEOUT_ERROR:"Received timeout from monday.com's GraphQL API"}},function(e,t){var n,i,r=e.exports={};function s(){throw new Error("setTimeout has not been defined")}function o(){throw new Error("clearTimeout has not been defined")}function a(e){if(n===setTimeout)return setTimeout(e,0);if((n===s||!n)&&setTimeout)return n=setTimeout,setTimeout(e,0);try{return n(e,0)}catch(t){try{return n.call(null,e,0)}catch(t){return n.call(this,e,0)}}}!function(){try{n="function"==typeof setTimeout?setTimeout:s}catch(e){n=s}try{i="function"==typeof clearTimeout?clearTimeout:o}catch(e){i=o}}();var c,l=[],u=!1,d=-1;function h(){u&&c&&(u=!1,c.length?l=c.concat(l):d=-1,l.length&&p())}function p(){if(!u){var e=a(h);u=!0;for(var t=l.length;t;){for(c=l,l=[];++d1)for(var n=1;n{if(n)return;n=!0;const t=()=>{e.track("ping")};t(),setInterval(t,3e5)}}},function(e){e.exports=JSON.parse('{"name":"monday-sdk-js","version":"0.4.12","private":false,"repository":"https://github.com/mondaycom/monday-sdk-js","main":"src/index.js","types":"types/index.d.ts","author":"talharamati ","license":"MIT","files":["LICENSE","README.md","dist/","src/","types/","server-sdk.js"],"dependencies":{"node-fetch":"^2.6.0"},"devDependencies":{"@babel/cli":"^7.6.0","@babel/core":"^7.6.0","@babel/node":"^7.6.1","@babel/preset-env":"^7.6.0","@babel/preset-react":"^7.0.0","@babel/register":"^7.6.0","@types/source-map":"^0.5.2","babel-loader":"^8.0.6","chai":"^4.2.0","eslint":"^6.8.0","jsdom":"^16.2.0","mocha":"^7.1.0","prettier":"^1.19.1","sinon":"^9.0.0","sinon-chai":"^3.5.0","typescript":"^4.9.5","webpack":"^4.38.0","webpack-cli":"^3.3.6","webpack-dev-server":"^3.7.2"},"scripts":{"start":"webpack-dev-server","build":"webpack --mode=production --env.WEBPACK_BUILD=true","test":"mocha \'./src/**/*-test.js\'","test:watch":"mocha \'./src/**/*-test.js\' --watch","precommit":"yarn lint && yarn style-check","lint":"eslint \'./src/**/*.*\'","style-check":"prettier --check \'./src/**/*.js\'","style-fix":"prettier --write \'./src/**/*.js\'","compile-types":"tsc --noEmit"}}')},function(e,t,n){const{logWarnings:i}=n(3),r=n(0),{oauthToken:s}=n(14);class o{constructor(e={}){this._token=e.token,this._apiVersion=e.apiVersion,this.setToken=this.setToken.bind(this),this.setApiVersion=this.setApiVersion.bind(this),this.api=this.api.bind(this)}setToken(e){this._token=e}setApiVersion(e){this._apiVersion=e}api(e,t={}){const n={query:e,variables:t.variables},s=t.token||this._token,o=t.apiVersion||this._apiVersion;if(!s)throw new Error("Should send 'token' as an option or call mondaySdk.setToken(TOKEN)");return r.execute(n,s,{apiVersion:o}).then(i)}oauthToken(e,t,n){return s(e,t,n)}}e.exports=function(e={}){return new o(e)}},function(e,t,n){const{execute:i}=n(0),{MONDAY_OAUTH_TOKEN_URL:r}=n(1);e.exports={oauthToken:(e,t,n)=>i({code:e,client_id:t,client_secret:n},null,{url:r})}}]);
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "monday-sdk-js",
3 | "version": "0.5.6",
4 | "private": false,
5 | "repository": "https://github.com/mondaycom/monday-sdk-js",
6 | "main": "src/index.js",
7 | "types": "types/index.d.ts",
8 | "author": "talharamati ",
9 | "license": "MIT",
10 | "files": [
11 | "LICENSE",
12 | "README.md",
13 | "dist/",
14 | "src/",
15 | "types/",
16 | "server-sdk.js"
17 | ],
18 | "dependencies": {
19 | "node-fetch": "^2.6.0"
20 | },
21 | "devDependencies": {
22 | "@babel/cli": "^7.6.0",
23 | "@babel/core": "^7.6.0",
24 | "@babel/node": "^7.6.1",
25 | "@babel/preset-env": "^7.6.0",
26 | "@babel/preset-react": "^7.0.0",
27 | "@babel/register": "^7.6.0",
28 | "@types/source-map": "^0.5.2",
29 | "babel-loader": "^8.0.6",
30 | "chai": "^4.2.0",
31 | "eslint": "^6.8.0",
32 | "jsdom": "^16.2.0",
33 | "mocha": "^7.1.0",
34 | "prettier": "^1.19.1",
35 | "sinon": "^9.0.0",
36 | "sinon-chai": "^3.5.0",
37 | "typescript": "^4.9.5",
38 | "webpack": "^4.38.0",
39 | "webpack-cli": "^3.3.6",
40 | "webpack-dev-server": "^3.7.2"
41 | },
42 | "scripts": {
43 | "start": "webpack-dev-server",
44 | "build": "webpack --mode=production --env.WEBPACK_BUILD=true",
45 | "test": "mocha './src/**/*-test.js'",
46 | "test:watch": "mocha './src/**/*-test.js' --watch",
47 | "precommit": "yarn lint && yarn style-check",
48 | "lint": "eslint './src/**/*.*'",
49 | "style-check": "prettier --check './src/**/*.js'",
50 | "style-fix": "prettier --write './src/**/*.js'",
51 | "compile-types": "tsc --noEmit"
52 | }
53 | }
54 |
--------------------------------------------------------------------------------
/server-sdk.js:
--------------------------------------------------------------------------------
1 | module.exports = require("./src/server.js");
2 |
--------------------------------------------------------------------------------
/src/client-test.js:
--------------------------------------------------------------------------------
1 | const { sinon, expect } = require("./tests/helpers");
2 | const initMondaySdk = require("./client");
3 |
4 | describe("Monday Client Test", () => {
5 | const clientId = "clientId";
6 | const apiToken = "123456789";
7 |
8 | let mondayClient;
9 | let options = {
10 | clientId,
11 | apiToken
12 | };
13 | let clock;
14 | beforeEach(() => {
15 | clock = sinon.useFakeTimers();
16 | mondayClient = initMondaySdk(options);
17 | });
18 |
19 | afterEach(() => {
20 | clock.restore();
21 | mondayClient._removeEventListener();
22 | mondayClient._clearListeners();
23 | });
24 | describe("init", () => {
25 | it("should set client id", () => {
26 | expect(mondayClient._clientId).to.equal(clientId);
27 | });
28 |
29 | it("should set the token id", () => {
30 | expect(mondayClient._apiToken).to.equal(apiToken);
31 | });
32 | });
33 |
34 | describe("setters", () => {
35 | it("should set client id correctly", () => {
36 | const newId = "newId";
37 | const { setClientId } = mondayClient;
38 |
39 | setClientId(newId);
40 |
41 | expect(mondayClient._clientId).to.equal(newId);
42 | });
43 |
44 | it("should set the api token correctly", () => {
45 | const newToken = "new toekn";
46 | const { setToken } = mondayClient;
47 |
48 | setToken(newToken);
49 |
50 | expect(mondayClient._apiToken).to.equal(newToken);
51 | });
52 |
53 | it("should set the version correctly", () => {
54 | const newVersion = "2023-01";
55 | const { setApiVersion } = mondayClient;
56 |
57 | setApiVersion(newVersion);
58 |
59 | expect(mondayClient._apiVersion).to.eq(newVersion);
60 | });
61 | });
62 |
63 | describe("post message", () => {
64 | let listenCallback;
65 | let methodCallback;
66 |
67 | const type = "fakeType";
68 |
69 | beforeEach(() => {
70 | listenCallback = sinon.stub();
71 | methodCallback = sinon.stub();
72 | });
73 |
74 | afterEach(() => {
75 | listenCallback.reset();
76 | methodCallback.reset();
77 | });
78 |
79 | it("callback should be call - type", () => {
80 | const data = {
81 | method: "method",
82 | type,
83 | requestId: "requestId"
84 | };
85 | mondayClient.listen(type, listenCallback);
86 | window.postMessage(data, "*");
87 | clock.tick(5);
88 | expect(listenCallback).to.be.calledWithExactly(data);
89 | });
90 |
91 | it("unsubscribe should prevent callback being called", () => {
92 | const data = {
93 | method: "method",
94 | type,
95 | requestId: "requestId"
96 | };
97 | const unsubscribe = mondayClient.listen(type, listenCallback);
98 | window.postMessage(data, "*");
99 | window.postMessage(data, "*");
100 | window.postMessage(data, "*");
101 | clock.tick(5);
102 | unsubscribe();
103 | window.postMessage(data, "*");
104 | expect(listenCallback).to.be.calledWithExactly(data).and.calledThrice;
105 | });
106 | });
107 | describe("api methods", () => {
108 | let postMessageStub;
109 | beforeEach(() => {
110 | mondayClient.setToken(null);
111 | postMessageStub = sinon.stub();
112 | });
113 |
114 | afterEach(() => {
115 | postMessageStub.reset();
116 | });
117 | it("should post message to window with requestId ", () => {
118 | window.addEventListener("message", postMessageStub, false);
119 | mondayClient.api("query");
120 | clock.tick(5);
121 | expect(postMessageStub).to.be.called;
122 | window.removeEventListener("message", postMessageStub, false);
123 | });
124 |
125 | it("get api post message", () => {
126 | window.addEventListener("message", postMessageStub, false);
127 | mondayClient.get({ type: "type", method: "method" });
128 | clock.tick(5);
129 | expect(postMessageStub).to.be.called;
130 | window.removeEventListener("message", postMessageStub, false);
131 | });
132 |
133 | it("get api post message", () => {
134 | window.addEventListener("message", postMessageStub, false);
135 | mondayClient.execute({ type: "type", method: "method" });
136 | clock.tick(5);
137 | expect(postMessageStub).to.be.called;
138 | window.removeEventListener("message", postMessageStub, false);
139 | });
140 | });
141 | });
142 |
143 | describe("Monday Client Test API - Returning data", () => {
144 | let clock;
145 | beforeEach(() => {
146 | //we are not initializing mondaySdk in beforeEach to have it's event listener being registered
147 | //AFTER the test's event listener to prevent SDK to react on it's messages before the test listener
148 | clock = sinon.useFakeTimers();
149 | });
150 |
151 | it("API should resolve the promise coming from the host app ", done => {
152 | const clientId = "clientId";
153 | const responseData = { accountId: 123, boards: [] };
154 | function onPostMessage(event) {
155 | const { requestId, method, type } = event.data;
156 | if (method === "api" && !event.data.data) {
157 | window.postMessage({ requestId, data: responseData, method, type }, "*");
158 | //because in tests we don't have 2 different window object (parent and iframe) they are exchanging in the same
159 | //events space, so here we need to stop the initial SDK event propogation to not allow SDK to react to it's own event
160 | event.stopImmediatePropagation();
161 | }
162 | }
163 | window.addEventListener("message", onPostMessage, false);
164 |
165 | const mondayClient = initMondaySdk({ clientId });
166 | mondayClient.api("query").then(res => {
167 | expect(res).to.be.ok;
168 | expect(res).to.be.equal(responseData);
169 | done();
170 | });
171 | clock.tick(5);
172 | window.removeEventListener("message", onPostMessage, false);
173 | });
174 |
175 | it("API should reject the promise, when host raises an event with errorMessage", done => {
176 | const clientId = "clientId";
177 | const errorMessage = "My custom error";
178 | const errorData = { errors: ["1", "2", "3"] };
179 | function onPostMessage(event) {
180 | const { requestId, method, type } = event.data;
181 | if (method === "api" && !event.data.errorMessage) {
182 | window.postMessage({ requestId, data: errorData, method, type, errorMessage: errorMessage }, "*");
183 | //because in tests we don't have 2 different window object (parent and iframe) they are exchanging in the same
184 | //events space, so here we need to stop the initial SDK event propogation to not allow SDK to react to it's own event
185 | event.stopImmediatePropagation();
186 | }
187 | }
188 | window.addEventListener("message", onPostMessage, false);
189 |
190 | const mondayClient = initMondaySdk({ clientId });
191 | mondayClient.api("query").catch(err => {
192 | expect(err).to.be.ok;
193 | expect(err.message).to.be.equal(errorMessage);
194 | expect(err.data).to.be.equal(errorData);
195 | done();
196 | });
197 | clock.tick(5);
198 | window.removeEventListener("message", onPostMessage, false);
199 | });
200 | });
201 |
--------------------------------------------------------------------------------
/src/client.js:
--------------------------------------------------------------------------------
1 | const mondayApiClient = require("./monday-api-client");
2 | const { MONDAY_OAUTH_URL } = require("./constants.js");
3 | const { convertToArrayIfNeeded } = require("./helpers");
4 | const { initScrollHelperIfNeeded } = require("./helpers/ui-helpers");
5 | const { initBackgroundTracking } = require("./services/background-tracking-service");
6 | const { logWarnings } = require("./helpers/monday-api-helpers");
7 |
8 | const EMPTY_ARRAY = [];
9 |
10 | const STORAGE_SEGMENT_KINDS = {
11 | GLOBAL: "v2",
12 | INSTANCE: "instance"
13 | };
14 |
15 | class MondayClientSdk {
16 | constructor(options = {}) {
17 | this._clientId = options.clientId;
18 | this._apiToken = options.apiToken;
19 | this._apiVersion = options.apiVersion;
20 |
21 | this.listeners = {};
22 |
23 | this.setClientId = this.setClientId.bind(this);
24 | this.setToken = this.setToken.bind(this);
25 | this.setApiVersion = this.setApiVersion.bind(this);
26 | this.api = this.api.bind(this);
27 | this.listen = this.listen.bind(this);
28 | this.get = this.get.bind(this);
29 | this.set = this.set.bind(this);
30 | this.execute = this.execute.bind(this);
31 | this.oauth = this.oauth.bind(this);
32 | this._receiveMessage = this._receiveMessage.bind(this);
33 |
34 | this.storage = {
35 | setItem: this.setStorageItem.bind(this),
36 | getItem: this.getStorageItem.bind(this),
37 | deleteItem: this.deleteStorageItem.bind(this),
38 | instance: {
39 | setItem: this.setStorageInstanceItem.bind(this),
40 | getItem: this.getStorageInstanceItem.bind(this),
41 | deleteItem: this.deleteStorageInstanceItem.bind(this)
42 | }
43 | };
44 |
45 | window.addEventListener("message", this._receiveMessage, false);
46 |
47 | if (!options.withoutScrollHelper) initScrollHelperIfNeeded();
48 |
49 | initBackgroundTracking(this);
50 | }
51 |
52 | setClientId(clientId) {
53 | this._clientId = clientId;
54 | }
55 |
56 | setToken(token) {
57 | this._apiToken = token;
58 | }
59 |
60 | setApiVersion(apiVersion) {
61 | this._apiVersion = apiVersion;
62 | }
63 |
64 | api(query, options = {}) {
65 | const params = { query, variables: options.variables };
66 | const token = options.token || this._apiToken;
67 | const apiVersion = options.apiVersion || this._apiVersion;
68 |
69 | let responsePromise;
70 | if (token) {
71 | responsePromise = mondayApiClient.execute(params, token, { apiVersion });
72 | } else {
73 | responsePromise = this._localApi("api", { params, apiVersion }).then(result => result.data);
74 | }
75 |
76 | return responsePromise.then(logWarnings);
77 | }
78 |
79 | listen(typeOrTypes, callback, params) {
80 | const types = convertToArrayIfNeeded(typeOrTypes);
81 | const unsubscribes = [];
82 |
83 | types.forEach(type => {
84 | unsubscribes.push(this._addListener(type, callback));
85 | this._localApi("listen", { type, params });
86 | });
87 |
88 | return () => {
89 | unsubscribes.forEach(unsubscribe => unsubscribe());
90 | };
91 | }
92 |
93 | get(type, params) {
94 | return this._localApi("get", { type, params });
95 | }
96 |
97 | set(type, params) {
98 | return this._localApi("set", { type, params });
99 | }
100 |
101 | execute(type, params) {
102 | return this._localApi("execute", { type, params });
103 | }
104 |
105 | track(name, data) {
106 | return this.execute("track", { name, data });
107 | }
108 |
109 | oauth(options = {}) {
110 | const clientId = options.clientId || this._clientId;
111 | if (!clientId) throw new Error("clientId is required");
112 |
113 | const mondayOauthUrl = options.mondayOauthUrl || MONDAY_OAUTH_URL;
114 |
115 | const url = `${mondayOauthUrl}?client_id=${clientId}`;
116 | window.location = url;
117 | }
118 |
119 | setStorageItem(key, value, options = {}) {
120 | return this._localApi("storage", { method: "set", key, value, options, segment: STORAGE_SEGMENT_KINDS.GLOBAL });
121 | }
122 |
123 | getStorageItem(key, options = {}) {
124 | return this._localApi("storage", { method: "get", key, options, segment: STORAGE_SEGMENT_KINDS.GLOBAL });
125 | }
126 |
127 | deleteStorageItem(key, options = {}) {
128 | return this._localApi("storage", { method: "delete", key, options, segment: STORAGE_SEGMENT_KINDS.GLOBAL });
129 | }
130 |
131 | setStorageInstanceItem(key, value, options = {}) {
132 | return this._localApi("storage", { method: "set", key, value, options, segment: STORAGE_SEGMENT_KINDS.INSTANCE });
133 | }
134 |
135 | getStorageInstanceItem(key, options = {}) {
136 | return this._localApi("storage", { method: "get", key, options, segment: STORAGE_SEGMENT_KINDS.INSTANCE });
137 | }
138 |
139 | deleteStorageInstanceItem(key, options = {}) {
140 | return this._localApi("storage", { method: "delete", key, options, segment: STORAGE_SEGMENT_KINDS.INSTANCE });
141 | }
142 |
143 | _localApi(method, args) {
144 | return new Promise((resolve, reject) => {
145 | const requestId = this._generateRequestId();
146 | const clientId = this._clientId;
147 | const pjson = require("../package.json");
148 | const version = pjson.version;
149 |
150 | window.parent.postMessage({ method, args, requestId, clientId, version }, "*");
151 | const removeListener = this._addListener(requestId, data => {
152 | removeListener();
153 | if (data.errorMessage) {
154 | const error = new Error(data.errorMessage);
155 | error.data = data.data;
156 | reject(error);
157 | } else {
158 | resolve(data);
159 | }
160 | });
161 | });
162 | }
163 |
164 | _receiveMessage(event) {
165 | const { method, type, requestId } = event.data;
166 | const methodListeners = this.listeners[method] || EMPTY_ARRAY;
167 | const typeListeners = this.listeners[type] || EMPTY_ARRAY;
168 | const requestIdListeners = this.listeners[requestId] || EMPTY_ARRAY;
169 | let listeners = new Set([...methodListeners, ...typeListeners, ...requestIdListeners]);
170 |
171 | if (listeners) {
172 | listeners.forEach(listener => {
173 | try {
174 | listener(event.data);
175 | } catch (err) {
176 | console.error("Message callback error: ", err);
177 | }
178 | });
179 | }
180 | }
181 |
182 | _addListener(key, callback) {
183 | this.listeners[key] = this.listeners[key] || new Set();
184 | this.listeners[key].add(callback);
185 |
186 | return () => {
187 | this.listeners[key].delete(callback);
188 | if (this.listeners[key].size === 0) {
189 | delete this.listeners[key];
190 | }
191 | };
192 | }
193 |
194 | _generateRequestId() {
195 | return Math.random()
196 | .toString(36)
197 | .substring(2, 9);
198 | }
199 |
200 | _removeEventListener() {
201 | window.removeEventListener("message", this._receiveMessage, false);
202 | }
203 | _clearListeners() {
204 | this.listeners = [];
205 | }
206 | }
207 |
208 | function init(options = {}) {
209 | return new MondayClientSdk(options);
210 | }
211 |
212 | module.exports = init;
213 |
--------------------------------------------------------------------------------
/src/constants-test.js:
--------------------------------------------------------------------------------
1 | const { expect } = require("./tests/helpers");
2 | const constants = require("./constants");
3 |
4 | describe("constants", () => {
5 | //setup
6 | beforeEach(() => {
7 | process.env.NODE_ENV = "development";
8 | process.env.MONDAY_DOMAIN = undefined;
9 | process.env.MONDAY_COM_PROTOCOL = undefined;
10 | process.env.MONDAY_COM_DOMAIN = undefined;
11 | process.env.MONDAY_SUBDOMAIN_API = undefined;
12 | process.env.MONDAY_OAUTH_SUBDOMAIN = undefined;
13 | });
14 |
15 | it("should have at least 3 constants", () => {
16 | expect(Object.keys(constants).length).to.be.at.least(3);
17 | });
18 |
19 | it("check that constants changed after setting env variables", () => {
20 | const MONDAY_COM_PROTOCOL = "http";
21 | const MONDAY_COM_DOMAIN = "mondaystaging.com";
22 | const MONDAY_SUBDOMAIN_API = "";
23 | const MONDAY_OAUTH_SUBDOMAIN = "authZ.";
24 | const MONDAY_API_URL = `${MONDAY_COM_PROTOCOL}://${MONDAY_SUBDOMAIN_API}${MONDAY_COM_DOMAIN}/v2`;
25 | const MONDAY_OAUTH_URL = `${MONDAY_COM_PROTOCOL}://${MONDAY_OAUTH_SUBDOMAIN}${MONDAY_COM_DOMAIN}/oauth2/authorize`;
26 | const MONDAY_OAUTH_TOKEN_URL = `${MONDAY_COM_PROTOCOL}://${MONDAY_OAUTH_SUBDOMAIN}${MONDAY_COM_DOMAIN}/oauth2/token`;
27 | process.env.MONDAY_COM_PROTOCOL = MONDAY_COM_PROTOCOL;
28 | process.env.MONDAY_COM_DOMAIN = MONDAY_COM_DOMAIN;
29 | process.env.MONDAY_SUBDOMAIN_API = MONDAY_SUBDOMAIN_API;
30 | process.env.MONDAY_OAUTH_SUBDOMAIN = MONDAY_OAUTH_SUBDOMAIN;
31 |
32 | expect(constants.MONDAY_DOMAIN).to.eq(MONDAY_COM_DOMAIN);
33 | expect(constants.MONDAY_PROTOCOL).to.eq(MONDAY_COM_PROTOCOL);
34 | expect(constants.MONDAY_API_URL).to.eq(MONDAY_API_URL);
35 | expect(constants.MONDAY_OAUTH_URL).to.eq(MONDAY_OAUTH_URL);
36 | expect(constants.MONDAY_OAUTH_TOKEN_URL).to.eq(MONDAY_OAUTH_TOKEN_URL);
37 | });
38 |
39 | describe("check that constants are correct when NODE_ENV is development", () => {
40 | it("MONDAY_DOMAIN should be correct", () => {
41 | expect(constants.MONDAY_DOMAIN).to.eq("monday.com");
42 | });
43 |
44 | it("MONDAY_PROTOCOL should be correct", () => {
45 | expect(constants.MONDAY_PROTOCOL).to.eq("https");
46 | });
47 |
48 | it("MONDAY_API_URL should be correct", () => {
49 | expect(constants.MONDAY_API_URL).to.eq("https://api.monday.com/v2");
50 | });
51 |
52 | it("MONDAY_OAUTH_URL should be correct", () => {
53 | expect(constants.MONDAY_OAUTH_URL).to.eq("https://auth.monday.com/oauth2/authorize");
54 | });
55 |
56 | it("MONDAY_OAUTH_TOKEN_URL should be correct", () => {
57 | expect(constants.MONDAY_OAUTH_TOKEN_URL).to.eq("https://auth.monday.com/oauth2/token");
58 | });
59 | });
60 |
61 | describe("check that constants are correct when NODE_ENV is undefined", () => {
62 | beforeEach(() => {
63 | process.env.NODE_ENV = undefined;
64 | process.env.MONDAY_COM_DOMAIN = "should not be used";
65 | });
66 |
67 | it("MONDAY_DOMAIN should be correct", () => {
68 | expect(constants.MONDAY_DOMAIN).to.eq("monday.com");
69 | });
70 |
71 | it("MONDAY_PROTOCOL should be correct", () => {
72 | expect(constants.MONDAY_PROTOCOL).to.eq("https");
73 | });
74 |
75 | it("MONDAY_API_URL should be correct", () => {
76 | expect(constants.MONDAY_API_URL).to.eq("https://api.monday.com/v2");
77 | });
78 |
79 | it("MONDAY_OAUTH_URL should be correct", () => {
80 | expect(constants.MONDAY_OAUTH_URL).to.eq("https://auth.monday.com/oauth2/authorize");
81 | });
82 |
83 | it("MONDAY_OAUTH_TOKEN_URL should be correct", () => {
84 | expect(constants.MONDAY_OAUTH_TOKEN_URL).to.eq("https://auth.monday.com/oauth2/token");
85 | });
86 | });
87 |
88 | describe("check that constants are correct when NODE_ENV is undefined", () => {
89 | beforeEach(() => {
90 | process.env.NODE_ENV = "production";
91 | process.env.MONDAY_COM_DOMAIN = "should not be used";
92 | });
93 |
94 | it("MONDAY_DOMAIN should be correct", () => {
95 | expect(constants.MONDAY_DOMAIN).to.eq("monday.com");
96 | });
97 |
98 | it("MONDAY_PROTOCOL should be correct", () => {
99 | expect(constants.MONDAY_PROTOCOL).to.eq("https");
100 | });
101 |
102 | it("MONDAY_API_URL should be correct", () => {
103 | expect(constants.MONDAY_API_URL).to.eq("https://api.monday.com/v2");
104 | });
105 |
106 | it("MONDAY_OAUTH_URL should be correct", () => {
107 | expect(constants.MONDAY_OAUTH_URL).to.eq("https://auth.monday.com/oauth2/authorize");
108 | });
109 |
110 | it("MONDAY_OAUTH_TOKEN_URL should be correct", () => {
111 | expect(constants.MONDAY_OAUTH_TOKEN_URL).to.eq("https://auth.monday.com/oauth2/token");
112 | });
113 | });
114 | });
115 |
--------------------------------------------------------------------------------
/src/constants.js:
--------------------------------------------------------------------------------
1 | function isNodeEnv() {
2 | return typeof process !== "undefined";
3 | }
4 |
5 | function isNodeDevStageEnv() {
6 | return isNodeEnv() && (process.env.NODE_ENV === "development" || process.env.NODE_ENV === "staging");
7 | }
8 |
9 | const getEnvOrDefault = (key, defaultVal) => {
10 | return isNodeDevStageEnv() && process.env[key] !== "undefined" && process.env[key] !== undefined
11 | ? process.env[key]
12 | : defaultVal;
13 | };
14 |
15 | const MONDAY_PROTOCOL = () => getEnvOrDefault("MONDAY_COM_PROTOCOL", "https");
16 | const MONDAY_DOMAIN = () => getEnvOrDefault("MONDAY_COM_DOMAIN", "monday.com");
17 | const MONDAY_SUBDOMAIN_API = () => getEnvOrDefault("MONDAY_SUBDOMAIN_API", "api.");
18 | const MONDAY_OAUTH_SUBDOMAIN = () => getEnvOrDefault("MONDAY_OAUTH_SUBDOMAIN", "auth.");
19 |
20 | const MONDAY_API_URL = () => `${MONDAY_PROTOCOL()}://${MONDAY_SUBDOMAIN_API()}${MONDAY_DOMAIN()}/v2`;
21 | const MONDAY_OAUTH_URL = () => `${MONDAY_PROTOCOL()}://${MONDAY_OAUTH_SUBDOMAIN()}${MONDAY_DOMAIN()}/oauth2/authorize`;
22 | const MONDAY_OAUTH_TOKEN_URL = () =>
23 | `${MONDAY_PROTOCOL()}://${MONDAY_OAUTH_SUBDOMAIN()}${MONDAY_DOMAIN()}/oauth2/token`;
24 |
25 | module.exports = {
26 | get MONDAY_DOMAIN() {
27 | return MONDAY_DOMAIN();
28 | },
29 | get MONDAY_PROTOCOL() {
30 | return MONDAY_PROTOCOL();
31 | },
32 | get MONDAY_API_URL() {
33 | return MONDAY_API_URL();
34 | },
35 | get MONDAY_OAUTH_URL() {
36 | return MONDAY_OAUTH_URL();
37 | },
38 | get MONDAY_OAUTH_TOKEN_URL() {
39 | return MONDAY_OAUTH_TOKEN_URL();
40 | }
41 | };
42 |
--------------------------------------------------------------------------------
/src/helpers/index.js:
--------------------------------------------------------------------------------
1 | const convertToArrayIfNeeded = x => {
2 | return Array.isArray(x) ? x : [x];
3 | };
4 |
5 | const isBrowser = typeof window !== "undefined" && typeof window.document !== "undefined";
6 |
7 | module.exports = {
8 | convertToArrayIfNeeded,
9 | isBrowser
10 | };
11 |
--------------------------------------------------------------------------------
/src/helpers/monday-api-helpers.js:
--------------------------------------------------------------------------------
1 | const logWarnings = res => {
2 | const warnings = res && res.extensions && res.extensions.warnings;
3 | if (!warnings || !Array.isArray(warnings)) return res;
4 |
5 | warnings.forEach(warning => {
6 | if (!warning || !warning.message) return;
7 |
8 | try {
9 | const locations =
10 | warning.locations && warning.locations.map(loc => `line ${loc.line}, column ${loc.column}`).join("; ");
11 | const path = warning.path && warning.path.join(" → ");
12 |
13 | let message = warning.message;
14 |
15 | // remove the dot at the end of the message
16 | message = message.replace(/\.$/, "");
17 | // start the message with lower case letter
18 | message = message.charAt(0).toLowerCase() + message.slice(1);
19 |
20 | const messageParts = [
21 | "[monday API]",
22 | `${path}:`,
23 | message,
24 | locations && `@ ${locations}`,
25 | warning.extensions ? ["\n\nAdditional details:", warning.extensions] : undefined
26 | ]
27 | .flat()
28 | .filter(Boolean);
29 |
30 | console.warn(...messageParts);
31 | } catch (e) {
32 | if (warning) {
33 | console.warn("[monday API] Warning:", warning);
34 | }
35 | }
36 | });
37 |
38 | return res;
39 | };
40 |
41 | module.exports = {
42 | logWarnings
43 | };
44 |
--------------------------------------------------------------------------------
/src/helpers/ui-helpers.js:
--------------------------------------------------------------------------------
1 | let scrollHelperInitialized = false;
2 |
3 | function initScrollHelperIfNeeded() {
4 | if (scrollHelperInitialized) return;
5 | // will prevent white flashes when scrolling using wheel events on laptops
6 | // with dual gpu when using the power saving gpu
7 | // it's reproduceable on macbook when not connected to an external display
8 | scrollHelperInitialized = true;
9 |
10 | // when an elemnt cover the scollable element, wheel events are synchronous on chromium based browsers
11 | const css =
12 | 'body::before { content: ""; position: fixed; top: 0; right: 0; bottom: 0; left: 0; pointer-events: none; z-index: 2147483647; /* mondaySdk css - can be disabled with: mondaySdk({withoutScrollHelper: true }) */ }';
13 | const style = document.createElement("style");
14 | style.appendChild(document.createTextNode(css));
15 |
16 | const head = document.head || document.getElementsByTagName("head")[0];
17 | head.appendChild(style);
18 | }
19 |
20 | module.exports = {
21 | initScrollHelperIfNeeded
22 | };
23 |
--------------------------------------------------------------------------------
/src/index.js:
--------------------------------------------------------------------------------
1 | const { isBrowser } = require("./helpers");
2 | const init = isBrowser ? require("./client") : require("./server");
3 |
4 | (function(root, factory) {
5 | if (typeof define === "function" && define.amd) {
6 | define(factory);
7 | } else if (typeof exports === "object") {
8 | module.exports = factory();
9 | } else if (root) {
10 | root = factory();
11 | }
12 | })(typeof self !== "undefined" ? self : this, function() {
13 | if (typeof __BUNDLE__ !== "undefined" && __BUNDLE__) {
14 | window.mondaySdk = init;
15 | }
16 | return init;
17 | });
18 |
--------------------------------------------------------------------------------
/src/monday-api-client/fetch.js:
--------------------------------------------------------------------------------
1 | const fetch = require("node-fetch");
2 |
3 | // for tests - to allow stubbing node-fetch with sinon
4 | function nodeFetch(url, options = {}) {
5 | return fetch(url, options);
6 | }
7 |
8 | module.exports = {
9 | nodeFetch
10 | };
11 |
--------------------------------------------------------------------------------
/src/monday-api-client/index.js:
--------------------------------------------------------------------------------
1 | module.exports = require("./monday-api-client");
2 |
--------------------------------------------------------------------------------
/src/monday-api-client/monday-api-client-test.js:
--------------------------------------------------------------------------------
1 | const { expect, assert, sinon } = require("./../tests/helpers");
2 | const fetch = require("./fetch");
3 | const mondayApiClient = require("./monday-api-client");
4 |
5 | describe("mondayApiClient", () => {
6 | let nodeFetchStub;
7 | let fetchMock;
8 |
9 | beforeEach(() => {
10 | fetchMock = sinon.stub(async () => {
11 | return { data: "some_data" };
12 | });
13 | nodeFetchStub = sinon
14 | .stub(fetch, "nodeFetch")
15 | .resolves({ json: fetchMock, headers: { get: () => "application/json" } });
16 | });
17 |
18 | afterEach(() => {
19 | nodeFetchStub.restore();
20 | });
21 |
22 | it("should be able to execute an api request", async () => {
23 | const result = await mondayApiClient.execute("query { boards { id, name }}", "api_token");
24 | expect(result).to.deep.equal({ data: "some_data" });
25 | });
26 |
27 | it("should call node fetch with the correct args", async () => {
28 | await mondayApiClient.execute("query { boards { id, name }}", "api_token");
29 | assert.calledOnce(nodeFetchStub);
30 | assert.calledWithExactly(nodeFetchStub, "https://api.monday.com/v2", {
31 | body: '"query { boards { id, name }}"',
32 | headers: { Authorization: "api_token", "Content-Type": "application/json" },
33 | method: "POST"
34 | });
35 | });
36 |
37 | it("should call node fetch with the version header", async () => {
38 | const apiVersion = "2023-01";
39 | await mondayApiClient.execute("query { boards { id, name }}", "api_token", { apiVersion });
40 | assert.calledOnce(nodeFetchStub);
41 | assert.calledWithExactly(nodeFetchStub, "https://api.monday.com/v2", {
42 | body: '"query { boards { id, name }}"',
43 | headers: { Authorization: "api_token", "Content-Type": "application/json", "API-Version": apiVersion },
44 | method: "POST"
45 | });
46 | });
47 |
48 | it(`should throw ${mondayApiClient.TOKEN_IS_REQUIRED_ERROR}`, async () => {
49 | let errorMessage;
50 | try {
51 | await mondayApiClient.execute("query { boards { id, name }}");
52 | } catch (err) {
53 | errorMessage = err.message;
54 | }
55 | expect(errorMessage).to.eq(mondayApiClient.TOKEN_IS_REQUIRED_ERROR);
56 | });
57 |
58 | it(`should throw ${mondayApiClient.COULD_NOT_PARSE_JSON_RESPONSE_ERROR}`, async () => {
59 | nodeFetchStub.returns({ json: "not json", headers: { get: () => "application/json" } });
60 | let errorMessage;
61 | try {
62 | await mondayApiClient.execute("query { boards { id, name }}", "api_token");
63 | } catch (err) {
64 | errorMessage = err.message;
65 | }
66 | expect(errorMessage).to.eq(mondayApiClient.COULD_NOT_PARSE_JSON_RESPONSE_ERROR);
67 | });
68 |
69 | it(`should throw ${mondayApiClient.API_TIMEOUT_ERROR}`, async () => {
70 | nodeFetchStub.returns({ headers: { get: () => "text/plain" }, status: 504 });
71 | let errorMessage;
72 | try {
73 | await mondayApiClient.execute("query { boards { id, name }}", "api_token");
74 | } catch (err) {
75 | errorMessage = err.message;
76 | }
77 | expect(errorMessage).to.eq(mondayApiClient.API_TIMEOUT_ERROR);
78 | });
79 | });
80 |
--------------------------------------------------------------------------------
/src/monday-api-client/monday-api-client.js:
--------------------------------------------------------------------------------
1 | const { MONDAY_API_URL, MONDAY_OAUTH_TOKEN_URL } = require("./../constants.js");
2 | const fetch = require("./fetch");
3 |
4 | const COULD_NOT_PARSE_JSON_RESPONSE_ERROR = "Could not parse JSON from monday.com's GraphQL API response";
5 | const TOKEN_IS_REQUIRED_ERROR = "Token is required";
6 | const API_TIMEOUT_ERROR = "Received timeout from monday.com's GraphQL API";
7 |
8 | function apiRequest(url, data, token, options = {}) {
9 | return fetch.nodeFetch(url, {
10 | method: options.method || "POST",
11 | body: JSON.stringify(data || {}),
12 | headers: {
13 | Authorization: token,
14 | "Content-Type": "application/json",
15 | ...(options.apiVersion ? { "API-Version": options.apiVersion } : {})
16 | }
17 | });
18 | }
19 |
20 | async function execute(data, token, options = {}) {
21 | if (!token && options.url !== MONDAY_OAUTH_TOKEN_URL) throw new Error(TOKEN_IS_REQUIRED_ERROR);
22 |
23 | const url = options.url || MONDAY_API_URL;
24 | const path = options.path || "";
25 | const fullUrl = `${url}${path}`;
26 | let response = await apiRequest(fullUrl, data, token, options);
27 |
28 | const responseStatusCode = response.status;
29 | const responseContentType = response.headers.get("content-type");
30 | if (!responseContentType || !responseContentType.includes("application/json")) {
31 | if (responseStatusCode === 504) {
32 | throw new Error(API_TIMEOUT_ERROR);
33 | }
34 |
35 | const responseText = await response.text();
36 | throw new Error(responseText);
37 | }
38 |
39 | try {
40 | return await response.json();
41 | } catch (err) {
42 | throw new Error(COULD_NOT_PARSE_JSON_RESPONSE_ERROR);
43 | }
44 | }
45 |
46 | module.exports = { execute, COULD_NOT_PARSE_JSON_RESPONSE_ERROR, TOKEN_IS_REQUIRED_ERROR, API_TIMEOUT_ERROR };
47 |
--------------------------------------------------------------------------------
/src/server-test.js:
--------------------------------------------------------------------------------
1 | const { sinon, assert, expect } = require("./tests/helpers");
2 | let initServerSdk = require("./server");
3 | const mondayApiClient = require("./monday-api-client");
4 |
5 | describe("server sdk", () => {
6 | describe("init", () => {
7 | it("should be able to init serverSdk", () => {
8 | initServerSdk();
9 | });
10 |
11 | it("should be able to init serverSdk without token", () => {
12 | const serverSdk = initServerSdk();
13 | expect(serverSdk._token).to.eq(undefined);
14 | });
15 |
16 | it("should be able to init serverSdk with token", () => {
17 | const serverSdk = initServerSdk({ token: "sometoken123" });
18 | expect(serverSdk._token).to.eq("sometoken123");
19 | });
20 |
21 | it("should be able to init serverSdk with API version", () => {
22 | const serverSdk = initServerSdk({ apiVersion: "2023-01" });
23 | expect(serverSdk._apiVersion).to.eq("2023-01");
24 | });
25 | });
26 |
27 | describe("setToken", () => {
28 | it("should be able to set token", () => {
29 | const serverSdk = initServerSdk();
30 | expect(serverSdk._token).to.eq(undefined);
31 | serverSdk.setToken("sometoken123");
32 | expect(serverSdk._token).to.eq("sometoken123");
33 | });
34 |
35 | it("should be binded", () => {
36 | const serverSdk = initServerSdk();
37 | expect(serverSdk._token).to.eq(undefined);
38 | const { setToken } = serverSdk;
39 | setToken("sometoken123");
40 | expect(serverSdk._token).to.eq("sometoken123");
41 | });
42 | });
43 |
44 | describe("setApiVersion", () => {
45 | it("should be able to set the version", () => {
46 | const serverSdk = initServerSdk();
47 | expect(serverSdk._apiVersion).to.eq(undefined);
48 |
49 | const { setApiVersion } = serverSdk;
50 | setApiVersion("2023-01");
51 |
52 | expect(serverSdk._apiVersion).to.eq("2023-01");
53 | });
54 | });
55 |
56 | describe("api", () => {
57 | let mondayApiClientExecuteStub;
58 |
59 | beforeEach(() => {
60 | mondayApiClientExecuteStub = sinon.stub(mondayApiClient, "execute").resolves("api-response");
61 | });
62 |
63 | afterEach(() => {
64 | mondayApiClientExecuteStub.restore();
65 | });
66 |
67 | it("should call to mondayApi if sdk was initialized with token", async () => {
68 | const serverSdk = initServerSdk({ token: "api_token" });
69 | const result = await serverSdk.api("query { boards { id, name }}");
70 | expect(result).to.eq("api-response");
71 | });
72 |
73 | it("should call mondayApiClient with the current args", async () => {
74 | const serverSdk = initServerSdk({ token: "api_token" });
75 | await serverSdk.api("query { boards { id, name }}");
76 | assert.calledOnce(mondayApiClientExecuteStub);
77 | assert.calledWithExactly(
78 | mondayApiClientExecuteStub,
79 | { query: "query { boards { id, name }}", variables: undefined },
80 | "api_token",
81 | { apiVersion: undefined }
82 | );
83 | });
84 |
85 | it("should be able to pass variables in options", async () => {
86 | const serverSdk = initServerSdk({ token: "api_token" });
87 |
88 | const query = `
89 | mutation create_pulse($pulseName: String!, $columnValues: JSON) {
90 | create_pulse(
91 | pulse_name: $pulseName,
92 | board_id: 123,
93 | group_id: my_group",
94 | column_values: $columnValues
95 | ) {
96 | id
97 | }
98 | }
99 | `;
100 |
101 | const variables = { pulseName: "new pulse", columnValues: { numbers: 3 } };
102 |
103 | await serverSdk.api(query, { variables });
104 | assert.calledOnce(mondayApiClientExecuteStub);
105 | assert.calledWithExactly(
106 | mondayApiClientExecuteStub,
107 | {
108 | query: `
109 | mutation create_pulse($pulseName: String!, $columnValues: JSON) {
110 | create_pulse(
111 | pulse_name: $pulseName,
112 | board_id: 123,
113 | group_id: my_group",
114 | column_values: $columnValues
115 | ) {
116 | id
117 | }
118 | }
119 | `,
120 | variables: { columnValues: { numbers: 3 }, pulseName: "new pulse" }
121 | },
122 | "api_token",
123 | { apiVersion: undefined }
124 | );
125 | });
126 |
127 | it("should call to mondayApi when token is passed from options", async () => {
128 | const serverSdk = initServerSdk();
129 | serverSdk.setToken("api_token");
130 | const result = await serverSdk.api("query { boards { id, name }}");
131 | expect(result).to.eq("api-response");
132 | });
133 |
134 | it("should call to mondayApi prefer token from options", async () => {
135 | const serverSdk = initServerSdk({ token: "api_token" });
136 | serverSdk.setToken("api_token_2");
137 | await serverSdk.api("query { boards { id, name }}");
138 | assert.calledOnce(mondayApiClientExecuteStub);
139 | assert.calledWithExactly(
140 | mondayApiClientExecuteStub,
141 | { query: "query { boards { id, name }}", variables: undefined },
142 | "api_token_2",
143 | { apiVersion: undefined }
144 | );
145 | });
146 |
147 | it("should provide version to mondayApi", async () => {
148 | const serverSdk = initServerSdk({ token: "api_token", apiVersion: "2023-01" });
149 | serverSdk.setApiVersion("2023-04");
150 | await serverSdk.api("query { boards { id, name }}");
151 | assert.calledOnce(mondayApiClientExecuteStub);
152 | assert.calledWithExactly(
153 | mondayApiClientExecuteStub,
154 | { query: "query { boards { id, name }}", variables: undefined },
155 | "api_token",
156 | { apiVersion: "2023-04" }
157 | );
158 | });
159 |
160 | it("should prefer version from method options when calling mondayApi", async () => {
161 | const serverSdk = initServerSdk({ token: "api_token", apiVersion: "2023-01" });
162 | serverSdk.setApiVersion("2023-04");
163 | await serverSdk.api("query { boards { id, name }}", { apiVersion: "2023-07" });
164 | assert.calledOnce(mondayApiClientExecuteStub);
165 | assert.calledWithExactly(
166 | mondayApiClientExecuteStub,
167 | { query: "query { boards { id, name }}", variables: undefined },
168 | "api_token",
169 | { apiVersion: "2023-07" }
170 | );
171 | });
172 |
173 | it("should call to mondayApi prefer version from options", async () => {
174 | const serverSdk = initServerSdk({ token: "api_token", apiVersion: "2023-01" });
175 | serverSdk.setApiVersion("2023-04");
176 | await serverSdk.api("query { boards { id, name }}", { apiVersion: "2023-07" });
177 | assert.calledOnce(mondayApiClientExecuteStub);
178 | assert.calledWithExactly(
179 | mondayApiClientExecuteStub,
180 | { query: "query { boards { id, name }}", variables: undefined },
181 | "api_token",
182 | { apiVersion: "2023-07" }
183 | );
184 | });
185 |
186 | it("should throw error if token is missing", async () => {
187 | let errorMessage;
188 | const serverSdk = initServerSdk();
189 | try {
190 | await serverSdk.api("query { boards { id, name }}");
191 | } catch (err) {
192 | errorMessage = err.message;
193 | }
194 |
195 | expect(errorMessage).to.equal("Should send 'token' as an option or call mondaySdk.setToken(TOKEN)");
196 | });
197 | });
198 | });
199 |
--------------------------------------------------------------------------------
/src/server.js:
--------------------------------------------------------------------------------
1 | const { logWarnings } = require("./helpers/monday-api-helpers");
2 | const mondayApiClient = require("./monday-api-client");
3 | const { oauthToken } = require("./services/oauth-service.js");
4 |
5 | const TOKEN_MISSING_ERROR = "Should send 'token' as an option or call mondaySdk.setToken(TOKEN)";
6 |
7 | class MondayServerSdk {
8 | constructor(options = {}) {
9 | this._token = options.token;
10 | this._apiVersion = options.apiVersion;
11 |
12 | this.setToken = this.setToken.bind(this);
13 | this.setApiVersion = this.setApiVersion.bind(this);
14 | this.api = this.api.bind(this);
15 | }
16 |
17 | setToken(token) {
18 | this._token = token;
19 | }
20 |
21 | setApiVersion(apiVersion) {
22 | this._apiVersion = apiVersion;
23 | }
24 |
25 | api(query, options = {}) {
26 | const params = { query, variables: options.variables };
27 | const token = options.token || this._token;
28 | const apiVersion = options.apiVersion || this._apiVersion;
29 |
30 | if (!token) throw new Error(TOKEN_MISSING_ERROR);
31 |
32 | return mondayApiClient.execute(params, token, { apiVersion }).then(logWarnings);
33 | }
34 |
35 | oauthToken(code, clientId, clientSecret) {
36 | return oauthToken(code, clientId, clientSecret);
37 | }
38 | }
39 |
40 | function init(options = {}) {
41 | return new MondayServerSdk(options);
42 | }
43 |
44 | module.exports = init;
45 |
--------------------------------------------------------------------------------
/src/services/background-tracking-service.js:
--------------------------------------------------------------------------------
1 | const _5_MINUTES_MS = 5 * 60 * 1000;
2 |
3 | let initialized = false;
4 | const initBackgroundTracking = sdk => {
5 | if (initialized) return;
6 | initialized = true;
7 |
8 | const ping = () => {
9 | sdk.track("ping");
10 | };
11 | ping();
12 | setInterval(ping, _5_MINUTES_MS);
13 | };
14 |
15 | module.exports = {
16 | initBackgroundTracking
17 | };
18 |
--------------------------------------------------------------------------------
/src/services/oauth-service.js:
--------------------------------------------------------------------------------
1 | const { execute } = require("../monday-api-client");
2 | const { MONDAY_OAUTH_TOKEN_URL } = require("../constants.js");
3 |
4 | const oauthToken = (code, clientId, clientSecret) => {
5 | const data = { code, client_id: clientId, client_secret: clientSecret };
6 | return execute(data, null, { url: MONDAY_OAUTH_TOKEN_URL });
7 | };
8 |
9 | module.exports = {
10 | oauthToken
11 | };
12 |
--------------------------------------------------------------------------------
/src/tests/helpers.js:
--------------------------------------------------------------------------------
1 | const sinon = require("sinon");
2 | const chai = require("chai");
3 | const sinonChai = require("sinon-chai");
4 | const jsdom = require("jsdom");
5 | const { JSDOM } = jsdom;
6 |
7 | chai.use(sinonChai);
8 |
9 | const dom = new JSDOM("", {
10 | url: "http://localhost"
11 | });
12 | global.window = dom.window;
13 | global.window.parent = dom.window;
14 | global.document = dom.window.document;
15 |
16 | module.exports = {
17 | chai,
18 | expect: chai.expect,
19 | assert: sinon.assert,
20 | sinon
21 | };
22 |
--------------------------------------------------------------------------------
/ts-tests/monday-sdk-js-module.test.ts:
--------------------------------------------------------------------------------
1 | import mondaySdk from "../types";
2 | const monday = mondaySdk();
3 |
4 | monday.api("test");
5 |
6 | monday.setApiVersion("2023-10");
7 | mondaySdk({ apiVersion: "2023-10" });
8 | monday.api("test", { apiVersion: "2023-07" });
9 |
10 | monday.setToken("test");
11 |
12 | monday.get("context", { appFeatureType: "AppFeatureBoardView" }).then(res => {
13 | const { data }: { data: { app: { id: number }; theme: string; boardId: number; viewMode: string } } = res;
14 | });
15 |
16 | monday.get("context").then(res => {
17 | const {
18 | data
19 | }: {
20 | data: {
21 | subscription?: {
22 | billing_period: string;
23 | days_left: number;
24 | is_trial: boolean;
25 | max_units: number | null;
26 | plan_id: string;
27 | pricing_version: number;
28 | renewal_date: string;
29 | };
30 | };
31 | } = res;
32 | });
33 |
34 | monday.get<{ id: number; name: string }>("testString").then(res => {
35 | const { data }: { data: { id: number; name: string } } = res;
36 | });
37 |
38 | monday.get<{ text: string; level: number }>("settings").then(res => {
39 | const { data }: { data: { text: string; level: number } } = res;
40 | });
41 |
42 | monday.get("itemIds").then(res => {
43 | const { data }: { data: number[] } = res;
44 | });
45 |
46 | monday.get("sessionToken").then(res => {
47 | const { data }: { data: string } = res;
48 | });
49 |
50 | monday.get("location").then(res => {
51 | const {
52 | data
53 | }: {
54 | data: {
55 | href: string;
56 | search: string;
57 | };
58 | } = res;
59 | });
60 |
61 | monday.get("filter").then(res => {
62 | const {
63 | data
64 | }: {
65 | data: {
66 | term: string;
67 | rules: (Record & {
68 | column_id?: string;
69 | compare_value?: string[];
70 | compare_attribute?: string;
71 | operator?: string;
72 | })[];
73 | operator: string | null;
74 | };
75 | } = res;
76 | });
77 |
78 | monday.set("settings", { text: "this is a test", number: 23 });
79 |
80 | monday.listen(
81 | "context",
82 | res => {
83 | const { data }: { data: { app: { id: number }; theme: string; itemId: number } } = res;
84 | },
85 | { appFeatureType: "AppFeatureItemView" }
86 | );
87 |
88 | monday.execute("openItemCard", { itemId: 123 });
89 | monday.execute("confirm", { message: "Hello" });
90 | monday.execute("notice", { message: "Hello" });
91 |
92 | monday.execute("openFilesDialog", {
93 | boardId: 12345,
94 | itemId: 23456,
95 | columnId: "files",
96 | assetId: 34567
97 | });
98 |
99 | monday.execute("triggerFilesUpload", {
100 | boardId: 12345,
101 | itemId: 23456,
102 | columnId: "files"
103 | });
104 | monday.execute("openAppFeatureModal", { urlPath: "/path", urlParams: {}, width: "100px", height: "100px" });
105 | monday.execute("closeAppFeatureModal");
106 | monday.execute("valueCreatedForUser");
107 |
108 | monday.execute("addDocBlock", {
109 | type: "normal text",
110 | content: { deltaFormat: [{ insert: "test" }] }
111 | });
112 |
113 | monday.execute("updateDocBlock", {
114 | id: "1234-1234-23434dsf",
115 | content: { deltaFormat: [{ insert: "test" }] }
116 | });
117 |
118 | monday.execute("addMultiBlocks", {
119 | afterBlockId: "1234-1234-23434dsf",
120 | blocks: [
121 | {
122 | type: "normal text",
123 | content: { deltaFormat: [{ insert: "test" }] }
124 | }
125 | ]
126 | });
127 | monday.execute("closeDocModal");
128 |
129 | monday.oauth({ clientId: "clientId" });
130 |
131 | monday.storage.instance.getItem("test").then(res => {
132 | const { data }: { data: { error?: string; success: boolean }; errorMessage?: string } = res;
133 | });
134 |
135 | monday.storage.instance.setItem("test", "123").then(res => {
136 | const { data }: { data: { error?: string; success: boolean }; errorMessage?: string } = res;
137 | });
138 |
139 | monday.storage.instance.deleteItem("test").then(res => {
140 | const { data }: { data: { error?: string; success: boolean }; errorMessage?: string } = res;
141 | });
142 |
143 | monday.storage.getItem("test").then(res => {
144 | const { data }: { data: { error?: string; success: boolean }; errorMessage?: string } = res;
145 | });
146 |
147 | monday.storage.setItem("test", "123").then(res => {
148 | const { data }: { data: { error?: string; success: boolean }; errorMessage?: string } = res;
149 | });
150 |
151 | monday.storage.deleteItem("test").then(res => {
152 | const { data }: { data: { error?: string; success: boolean }; errorMessage?: string } = res;
153 | });
154 |
155 | const mondayServer = mondaySdk({ token: "123", apiVersion: "2023-10" });
156 |
157 | mondayServer.setToken("123");
158 | mondayServer.setApiVersion("2023-10");
159 | mondayServer.api("test");
160 | mondayServer.api("test", { token: "test" });
161 | mondayServer.api("test", { variables: { variable1: "test" } });
162 | mondayServer.api("test", { token: "test", variables: { variable1: "test" } });
163 | mondayServer.api("test", { token: "test", apiVersion: "2023-07" });
164 | mondayServer.oauthToken("test", "test", "test");
165 |
--------------------------------------------------------------------------------
/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "module": "commonjs",
4 | "lib": [
5 | "es6"
6 | ],
7 | "noImplicitAny": true,
8 | "noImplicitThis": true,
9 | "strictFunctionTypes": true,
10 | "strictNullChecks": true,
11 | "noEmit": true,
12 | "forceConsistentCasingInFileNames": true,
13 | "esModuleInterop": true
14 | },
15 | "files": [
16 | "types/index.d.ts",
17 | "ts-tests/monday-sdk-js-module.test.ts"
18 | ]
19 | }
--------------------------------------------------------------------------------
/types/client-api.interface.ts:
--------------------------------------------------------------------------------
1 | export interface APIOptions {
2 | /**
3 | * Access token for the API
4 | * If not set, will use the credentials of the current user (client only)
5 | */
6 | token?: string | undefined;
7 |
8 | /**
9 | * An object containing GraphQL query variables
10 | */
11 | variables?: object | undefined;
12 |
13 | /**
14 | * A string specifying which version of the API should be used
15 | * If not set, will use the current API version
16 | */
17 | apiVersion?: string | undefined;
18 | }
19 |
20 | interface OAuthOptions {
21 | /**
22 | * The OAuth client ID of the requesting application
23 | * Defaults to your client ID
24 | */
25 | clientId?: string | undefined;
26 |
27 | /**
28 | * The URL of the monday OAuth endpoint
29 | */
30 | mondayOauthUrl?: string | undefined;
31 | }
32 |
33 | export interface ClientApi {
34 | /**
35 | * Used for querying the monday.com GraphQL API seamlessly on behalf of the connected user, or using a provided API token.
36 | * For more information about the GraphQL API and all queries and mutations possible, read the [API Documentation](https://monday.com/developers/v2)
37 | * @param query A [GraphQL](https://graphql.org/) query, can be either a query (retrieval operation) or a mutation (creation/update/deletion operation).
38 | * Placeholders may be used, which will be substituted by the variables object passed within the options.
39 | * @param options
40 | */
41 | api(query: string, options?: APIOptions): Promise<{ data: T, account_id: number }>;
42 |
43 | /**
44 | * Instead of passing the API token to the `api()` method on each request, you can set the API token once using:
45 | * @param token Access token for the API
46 | */
47 | setToken(token: string): void;
48 |
49 | /**
50 | * Allows to set the API version for future requests.
51 | * @param version A string specifying which version of the API should be used
52 | */
53 | setApiVersion(version: string): void;
54 |
55 | /**
56 | * Performs a client-side redirection of the user to the monday OAuth screen with your client ID embedded in the URL,
57 | * sin order to get their approval to generate a temporary OAuth token based on your requested permission scopes.
58 | * @param object An object with options
59 | */
60 | oauth(object?: OAuthOptions): void;
61 | }
62 |
--------------------------------------------------------------------------------
/types/client-context.type.ts:
--------------------------------------------------------------------------------
1 | import { Theme } from "./theme-config.type";
2 | type User = {
3 | id: string;
4 | isAdmin: boolean;
5 | isGuest: boolean;
6 | isViewOnly: boolean;
7 | countryCode: string;
8 | currentLanguage: string;
9 | timeFormat: string;
10 | timeZoneOffset: number;
11 | };
12 |
13 | type Account = {
14 | id: string;
15 | };
16 |
17 | type App = {
18 | id: number;
19 | clientId: string;
20 | };
21 |
22 | type AppVersion = {
23 | id: number;
24 | name: string;
25 | status: string;
26 | type: string;
27 | versionData: {
28 | major: number;
29 | minor: number;
30 | patch: number;
31 | type: string;
32 | };
33 | };
34 |
35 | export type Permissions = {
36 | approvedScopes: string[];
37 | requiredScopes: string[];
38 | };
39 |
40 | export type AppSubscription = {
41 | /**
42 | * The billing period frequency: monthly or yearly
43 | */
44 | billing_period: "yearly" | "monthly";
45 | /**
46 | * The number of days left until the subscription ends
47 | */
48 | days_left: number;
49 | /**
50 | * Returns true if it is still a trial subscription
51 | */
52 | is_trial: boolean;
53 | /**
54 | * The maximum number of seats allowed for seat-based plans. Returns null for feature-based plans
55 | */
56 | max_units: number | null;
57 | /**
58 | * The subscription plan ID from the app's side
59 | */
60 | plan_id: string;
61 | /**
62 | * The subscription's pricing version
63 | */
64 | pricing_version: number;
65 | /**
66 | * The date when the subscription renews, in ISO 8601 format
67 | */
68 | renewal_date: string;
69 | };
70 |
71 | export type BaseContext = {
72 | themeConfig?: Theme;
73 | theme: string;
74 | account: Account;
75 | user: User;
76 | region: string;
77 | app: App;
78 | appVersion: AppVersion;
79 | permissions: Permissions;
80 | subscription?: AppSubscription;
81 | };
82 |
83 | export type AppFeatureBoardViewContext = BaseContext & {
84 | boardId: number;
85 | boardIds: number[];
86 | boardViewId: number;
87 | viewMode: string;
88 | instanceId: number;
89 | instanceType: string;
90 | workspaceId: number;
91 | };
92 |
93 | export type AppFeatureAiBoardMainMenuHeaderContext = BaseContext & {
94 | location: string;
95 | locationContext: {
96 | boardId: number;
97 | workspaceId: number;
98 | };
99 | appFeatureId: number;
100 | withExternalWidth: boolean;
101 | withHeaderPadding: boolean;
102 | boardId: number;
103 | workspaceId: number;
104 | };
105 |
106 | export type AppFeatureDashboardWidgetContext = BaseContext & {
107 | boardIds: number[];
108 | widgetId: number;
109 | viewMode: string;
110 | editMode: boolean;
111 | instanceId: number;
112 | instanceType: string;
113 | };
114 |
115 | export type AppFeatureItemMenuActionContext = BaseContext & {
116 | boardId: number;
117 | pulseId: number;
118 | itemId: number;
119 | };
120 |
121 | export type AppFeatureItemBatchActionContext = BaseContext & {
122 | boardId: number;
123 | itemId: number;
124 | selectedPulsesIds: number[];
125 | };
126 |
127 | export type AppFeatureGroupMenuActionContext = BaseContext & {
128 | groupId: string;
129 | boardId: number;
130 | groupColor: string;
131 | };
132 |
133 | export type AppFeatureObjectContext = BaseContext & {
134 | boardId: number;
135 | boardIds: [number];
136 | workspaceId: number;
137 | appFeatureId: number;
138 | instanceId: number;
139 | instanceType: string;
140 | isFullScreen: boolean;
141 | isPresentingMode: boolean;
142 | objectPermissions: string;
143 | isFirstLevelControlPinned: boolean;
144 | isSlidePanelOpen: boolean;
145 | boardLoadingState: number;
146 | };
147 |
148 | export type AppFeatureWorkspaceViewContext = BaseContext & {
149 | workspaceId: number;
150 | };
151 |
152 | export type AppFeatureItemViewContext = BaseContext & {
153 | workspaceId: number;
154 | boardId: number;
155 | boardIds: [number];
156 | itemId: number;
157 | instanceId: number;
158 | instanceType: string;
159 | };
160 |
161 | export type AppFeatureAiDocQuickStartType = BaseContext & {
162 | location: string;
163 | locationContext: {
164 | docId: number;
165 | objectId: number;
166 | workspaceId: number;
167 | additionalSdkMethodsList: string[];
168 | };
169 | appFeatureId: number;
170 | withExternalWidth: boolean;
171 | withHeaderPadding: boolean;
172 | docId: number;
173 | objectId: number;
174 | workspaceId: number;
175 | additionalSdkMethodsList: string[];
176 | };
177 |
178 | export type AppFeatureAiDocTopBarContext = BaseContext & {
179 | location: string;
180 | locationContext: {
181 | input: string;
182 | docId: number;
183 | objectId: number;
184 | workspaceId: number;
185 | additionalSdkMethodsList: string[];
186 | };
187 | appFeatureId: number;
188 | withExternalWidth: boolean;
189 | withHeaderPadding: boolean;
190 | input: string;
191 | docId: number;
192 | objectId: number;
193 | workspaceId: number;
194 | additionalSdkMethodsList: string[];
195 | };
196 | export type FocusedBlock = {
197 | id: string;
198 | createdUserId: number;
199 | accountId: number;
200 | docId: number;
201 | type: string;
202 | content: {
203 | alignment: string;
204 | direction: string;
205 | deltaFormat: Array<{
206 | insert: string;
207 | }>;
208 | base64Encoded: string;
209 | };
210 | position: number;
211 | parentBlockId: string | null;
212 | createdAt: string;
213 | updatedAt: string;
214 | };
215 |
216 | export type AppFeatureAiDocSlashCommandContext = BaseContext & {
217 | location: string;
218 | locationContext: {
219 | focusedBlocks: FocusedBlock[];
220 | canMoveToNextSelectedTextualBlock: boolean;
221 | canMoveToPrevSelectedTextualBlock: boolean;
222 | input: string;
223 | isTextSelectedInBlock: boolean;
224 | docId: number;
225 | objectId: number;
226 | workspaceId: number;
227 | additionalSdkMethodsList: string[];
228 | };
229 | appFeatureId: number;
230 | withExternalWidth: boolean;
231 | withHeaderPadding: boolean;
232 | focusedBlocks: FocusedBlock[];
233 | canMoveToNextSelectedTextualBlock: boolean;
234 | canMoveToPrevSelectedTextualBlock: boolean;
235 | input: string;
236 | isTextSelectedInBlock: boolean;
237 | docId: number;
238 | objectId: number;
239 | workspaceId: number;
240 | additionalSdkMethodsList: string[];
241 | };
242 |
243 | export type AppFeatureDocActionsContext = BaseContext & {
244 | themeConfig: Theme;
245 | docId: number;
246 | objectId: number;
247 | workspaceId: number;
248 | focusedBlocks: FocusedBlock[];
249 | placement: string;
250 | highlightedText: string;
251 | range: {
252 | index: number;
253 | length: number;
254 | };
255 | blockId: string;
256 | };
257 |
258 | export type AppFeatureContextMap = {
259 | Base: BaseContext;
260 | AppFeatureBoardView: AppFeatureBoardViewContext;
261 | AppFeatureAiBoardMainMenuHeader: AppFeatureAiBoardMainMenuHeaderContext;
262 | AppFeatureDashboardWidget: AppFeatureDashboardWidgetContext;
263 | AppFeatureItemMenuAction: AppFeatureItemMenuActionContext;
264 | AppFeatureItemBatchAction: AppFeatureItemBatchActionContext;
265 | AppFeatureGroupMenuAction: AppFeatureGroupMenuActionContext;
266 | AppFeatureObject: AppFeatureObjectContext;
267 | AppFeatureWorkspaceView: AppFeatureWorkspaceViewContext;
268 | AppFeatureItemView: AppFeatureItemViewContext;
269 | AppFeatureAiDocQuickStart: AppFeatureAiDocQuickStartType;
270 | AppFeatureAiDocTopBar: AppFeatureAiDocTopBarContext;
271 | AppFeatureAiDocSlashCommand: AppFeatureAiDocSlashCommandContext;
272 | AppFeatureDocActions: AppFeatureDocActionsContext;
273 | };
274 |
275 | export type AppFeatureTypes = keyof AppFeatureContextMap;
276 |
--------------------------------------------------------------------------------
/types/client-data.interface.ts:
--------------------------------------------------------------------------------
1 | import { AppFeatureContextMap, AppFeatureTypes } from "./client-context.type";
2 |
3 | export type LocationResponse = Record & {
4 | href: string;
5 | search: string;
6 | };
7 |
8 | export type FilterResponse = Record & {
9 | term: string;
10 | rules: (Record & {
11 | column_id?: string;
12 | compare_value?: string[];
13 | compare_attribute?: string;
14 | operator?: string;
15 | })[];
16 | operator: string | null;
17 | };
18 |
19 | type SubscribableEventsResponse = {
20 | context: AppFeatureContextMap[AppFeatureType];
21 | settings: Record;
22 | itemIds: number[];
23 | events: Record;
24 | location: LocationResponse;
25 | filter: FilterResponse;
26 | };
27 |
28 | type SubscribableEvents = keyof SubscribableEventsResponse;
29 |
30 | type SettableTypes = "settings";
31 |
32 | type StorageResponse = {
33 | success: boolean;
34 | value: any;
35 | version?: any;
36 | };
37 |
38 | type Response = {
39 | data: T;
40 | errorMessage?: string | undefined;
41 | method: string;
42 | requestId: string;
43 | type?: string | undefined;
44 | };
45 |
46 | type DeleteResponse = {
47 | data: {
48 | success: boolean;
49 | value: any;
50 | };
51 | errorMessage?: string | undefined;
52 | method: string;
53 | requestId: string;
54 | type?: string | undefined;
55 | };
56 |
57 | interface SetResponse {
58 | data: {
59 | success: boolean;
60 | version: string;
61 | reason?: string | undefined;
62 | error?: string | undefined;
63 | };
64 | errorMessage?: string | undefined;
65 | requestId: string;
66 | method: string;
67 | type?: string | undefined;
68 | }
69 |
70 | export type GetterResponse = {
71 | context: AppFeatureContextMap[AppFeatureType];
72 | settings: Record;
73 | itemIds: number[];
74 | sessionToken: string;
75 | location: LocationResponse;
76 | filter: FilterResponse;
77 | };
78 | export interface ClientData {
79 | /**
80 | * Used for retrieving data from the parent monday.com application where your app is currently running.
81 | * This object can only be used when your app is running inside an `iframe`. This can only be used in client-side apps.
82 | * @param type The type of requested information (available values below)
83 | * - `'context'` Information about where this app is currently displayed, depending on the type of feature
84 | * - `'settings'` The application settings as configured by the user that installed the app
85 | * - `'itemIds'` The list of item IDs that are filtered in the current board (or all items if no filters are applied)
86 | * - `'sessionToken'` A JWT token which is decoded with your app's secret and can be used as a session token between your app's frontend & backend
87 | * @param params Reserved for future use
88 | */
89 | get<
90 | CustomResponse,
91 | T extends keyof GetterResponse = keyof GetterResponse,
92 | AppFeatureType extends AppFeatureTypes = AppFeatureTypes
93 | >(
94 | type: T | string,
95 | params?: Record & { appFeatureType?: AppFeatureType }
96 | ): Promise[T] & CustomResponse>>;
97 |
98 | /**
99 | * Creates a listener which allows subscribing to certain types of client-side events.
100 | * @param typeOrTypes The type, or array of types, of events to subscribe to
101 | * @param callback A callback function that is fired when the listener is triggered by a client-side event
102 | * @param params Reserved for future use
103 | * @return Unsubscribe/unlisten from all added during this method call
104 | */
105 | listen<
106 | CustomResponse,
107 | T extends SubscribableEvents = SubscribableEvents,
108 | AppFeatureType extends AppFeatureTypes = AppFeatureTypes
109 | >(
110 | typeOrTypes: (T | string) | ReadonlyArray,
111 | callback: (res: { data: SubscribableEventsResponse[T] & CustomResponse }) => void,
112 | params?: Record & { appFeatureType?: AppFeatureType }
113 | ): () => void;
114 |
115 | /**
116 | * Set data in your application, such as updating settings
117 | * @param type The type of data that can be set
118 | * @param params object containing the data you want to update
119 | */
120 | set(type: SettableTypes, params: object): Promise;
121 |
122 | /**
123 | * The Storage API is in early beta stages, its API is likely to change
124 | *
125 | * The monday apps infrastructure includes a persistent, key-value database storage that developers
126 | * can leverage to store data without having to create their own backend and maintain their own database.
127 | *
128 | * The database currently offers instance-level storage only, meaning that each application instance (i.e. a single board view or a dashboard widget) maintains its own storage.
129 | * Apps cannot share storage across accounts or even across apps installed in the same location.
130 | */
131 | storage: {
132 | /**
133 | * Returns a stored value from the database under `key` for the app (**without any reference to the instance**)
134 | * @param {string} key - Used to access to stored data
135 | */
136 | getItem(key: string): Promise;
137 |
138 | /**
139 | * Deletes a stored value from the database under `key` for the app (**without any reference to the instance**)
140 | * @param {string} key - Used to delete the stored data
141 | */
142 | deleteItem(key: string): Promise;
143 |
144 | /**
145 | * Stores `value` under `key` in the database for the app (**without any reference to the instance**)
146 | * @param {string} key - Used to delete the stored data
147 | * @param {any} value - The value to store
148 | * @param {object=} options
149 | * @param {string=} options.previous_version - Use the new version of the storage (instance-less)
150 | * @param {number=} options.ttl - The time to live of the item in seconds
151 | */
152 | setItem(key: string, value: any, options?: { previous_version?: string, ttl?: number }): Promise;
153 | /***
154 | * The instance storage is a key-value database that is scoped to a specific app instance.
155 | * **Does not work** for instance-less apps.
156 | */
157 | instance: {
158 | /**
159 | * Returns a stored value from the database under `key` for a specific app instance
160 | * @param key
161 | */
162 | getItem(key: string): Promise;
163 |
164 | /**
165 | * Deletes a stored value from the database under `key` for a specific app instance
166 | * @param key
167 | */
168 | deleteItem(key: string): Promise;
169 |
170 | /**
171 | * Stores `value` under `key` in the database for a specific app instance
172 | * @param key
173 | * @param value
174 | * @param options
175 | */
176 | setItem(key: string, value: any, options?: { previous_version?: string }): Promise;
177 | };
178 | };
179 | }
180 |
--------------------------------------------------------------------------------
/types/client-execute.interface.ts:
--------------------------------------------------------------------------------
1 | /**
2 | * Blocks that are supported by our SDK
3 | */
4 | type BlockTypes = 'normal text' | 'large title' | 'medium title' | 'small title' | 'bulleted list' | 'numbered list' | 'quote' | 'check list' | 'code';
5 |
6 | /**
7 | * Block content in delta format
8 | */
9 | interface BlockContent { deltaFormat: Array