├── .editorconfig ├── .eslintignore ├── .eslintrc.js ├── .github ├── pull_request_template.md └── workflows │ ├── publish.yml │ └── tests.yml ├── .gitignore ├── .npmignore ├── .npmrc ├── .prettierrc ├── DOCS.md ├── LICENSE.md ├── README.md ├── __tests__ ├── Authorization │ └── info.ts ├── Connector │ └── resolveToken.ts ├── Device │ └── sendData.ts ├── Dictionary │ ├── __mocks__ │ │ └── dictionaries.ts │ ├── applyToString.ts │ ├── getExpressionsFromString.ts │ ├── getLanguagesData.ts │ ├── getValueFromKey.ts │ ├── parseExpression.ts │ └── resolveExpression.ts ├── Migration │ ├── Migration.ts │ ├── __mocks__ │ │ ├── mapOldStructure.json │ │ └── widgetsOldStructure.json │ ├── angular.ts │ ├── areachart.ts │ ├── card.ts │ ├── clock.ts │ ├── compose.ts │ ├── custom.ts │ ├── cylinder.ts │ ├── dial.ts │ ├── display.ts │ ├── dynamic_table.ts │ ├── grainbin.ts │ ├── heatmap.ts │ ├── horizontalbarchart.ts │ ├── icon.ts │ ├── image.ts │ ├── imagemarker.ts │ ├── inputcontrol.ts │ ├── inputform.ts │ ├── keypad.ts │ ├── linechart.ts │ ├── map.ts │ ├── multipleaxischart.ts │ ├── note.ts │ ├── pie.ts │ ├── pushbutton.ts │ ├── removeHttpFromUrl.ts │ ├── semidonut.ts │ ├── solid.ts │ ├── statictable.ts │ ├── stepbutton.ts │ ├── tile.ts │ ├── verticalbarchart.ts │ ├── video.ts │ └── vumeter.ts └── Utils │ ├── analysisRouter.ts │ ├── dateParser.ts │ ├── envToJson.ts │ ├── getAPIVersion.ts │ └── parseLorawanQRcode.ts ├── assets └── favicon.png ├── jest.config.js ├── package-lock.json ├── package.json ├── src ├── common │ ├── Cache.ts │ ├── HashGenerator.ts │ ├── JSONParseSafe.ts │ ├── RequestInProgress.ts │ ├── TagoIOModule.ts │ ├── chunk.test.ts │ ├── chunk.ts │ ├── common.types.ts │ └── sleep.ts ├── config.ts ├── infrastructure │ ├── apiRequest.ts │ ├── apiSSE.ts │ ├── apiSocket.ts │ ├── envParams.json │ └── isBrowser.ts ├── modules.ts ├── modules │ ├── Analysis │ │ ├── Analysis.ts │ │ └── analysis.types.ts │ ├── Authorization │ │ ├── Authorization.ts │ │ └── authorization.types.ts │ ├── Device │ │ ├── Device.ts │ │ └── device.types.ts │ ├── Dictionary │ │ ├── Dictionary.ts │ │ └── dictionary.types.ts │ ├── Migration │ │ ├── Migration.ts │ │ ├── angular.ts │ │ ├── areachart.ts │ │ ├── card.ts │ │ ├── clock.ts │ │ ├── common │ │ │ ├── chartColors.ts │ │ │ ├── convertDownsample.ts │ │ │ ├── convertFormula.ts │ │ │ ├── convertInterval.ts │ │ │ ├── convertRange.ts │ │ │ ├── generateWidgetItemId.ts │ │ │ ├── index.ts │ │ │ └── removeHttpFromURL.ts │ │ ├── compose.ts │ │ ├── convertFields.ts │ │ ├── custom.ts │ │ ├── cylinder.ts │ │ ├── dial.ts │ │ ├── display.ts │ │ ├── dynamic_table.ts │ │ ├── grainbin.ts │ │ ├── heatmap.ts │ │ ├── horizontalbarchart.ts │ │ ├── icon.ts │ │ ├── image.ts │ │ ├── imagemarker.ts │ │ ├── inputcontrol.ts │ │ ├── inputform.ts │ │ ├── keypad.ts │ │ ├── linechart.ts │ │ ├── map.ts │ │ ├── multipleaxischart.ts │ │ ├── note.ts │ │ ├── pie.ts │ │ ├── pushbutton.ts │ │ ├── semidonut.ts │ │ ├── solid.ts │ │ ├── statictable.ts │ │ ├── stepbutton.ts │ │ ├── tile.ts │ │ ├── verticalbarchart.ts │ │ ├── video.ts │ │ └── vumeter.ts │ ├── Network │ │ ├── Network.ts │ │ └── network.types.ts │ ├── Resources │ │ ├── Access.ts │ │ ├── Account.ts │ │ ├── AccountDeprecated.ts │ │ ├── Actions.ts │ │ ├── Analyses.ts │ │ ├── Billing.ts │ │ ├── Buckets.ts │ │ ├── Dashboard.Widgets.ts │ │ ├── Dashboards.ts │ │ ├── Devices.ts │ │ ├── Dictionaries.ts │ │ ├── Entities.ts │ │ ├── Files.test.ts │ │ ├── Files.ts │ │ ├── Integration.Connectors.ts │ │ ├── Integration.Networks.ts │ │ ├── Integration.ts │ │ ├── Notifications.ts │ │ ├── PaymentHistory.ts │ │ ├── PaymentMethods.ts │ │ ├── Plan.ts │ │ ├── Profile.ts │ │ ├── Resources.ts │ │ ├── Run.ts │ │ ├── Secrets.ts │ │ ├── ServiceAuthorization.ts │ │ ├── ServiceAuthorization.types.ts │ │ ├── TagoCores.ts │ │ ├── Tags.ts │ │ ├── Template.ts │ │ ├── access.types.ts │ │ ├── account.types.ts │ │ ├── actions.types.ts │ │ ├── analysis.types.ts │ │ ├── billing.types.ts │ │ ├── buckets.types.ts │ │ ├── dashboards.types.ts │ │ ├── devices.types.ts │ │ ├── dictionaries.types.ts │ │ ├── entities.types.ts │ │ ├── files.types.ts │ │ ├── integration.connectors.types.ts │ │ ├── integration.networks.types.ts │ │ ├── notifications.types.ts │ │ ├── paymentHistory.types.ts │ │ ├── plan.types.ts │ │ ├── profile.types.ts │ │ ├── run.types.ts │ │ ├── secrets.type.ts │ │ ├── tagocore.types.ts │ │ └── template.types.ts │ ├── RunUser │ │ ├── RunUser.ts │ │ ├── SDB.ts │ │ └── runUser.types.ts │ ├── Services │ │ ├── AWS-SQS.ts │ │ ├── Attachment.ts │ │ ├── Console.ts │ │ ├── Email.ts │ │ ├── MQTT.ts │ │ ├── Notification.ts │ │ ├── PDF.ts │ │ ├── SMS.ts │ │ ├── SMTP.ts │ │ ├── Sendgrid.ts │ │ ├── Services.ts │ │ ├── Twilio-Whatsapp.ts │ │ └── Twillio.ts │ └── Utils │ │ ├── Utils.ts │ │ ├── dateParser.ts │ │ ├── envToJson.ts │ │ ├── getAPIVersion.ts │ │ ├── getDevice.ts │ │ ├── getTokenByName.ts │ │ ├── parseLorawanQRCode.ts │ │ ├── router │ │ ├── router.ts │ │ ├── router.types.ts │ │ ├── service.ts │ │ └── types.ts │ │ ├── sendDownlink.test.ts │ │ ├── sendDownlink.ts │ │ ├── types.ts │ │ ├── updateMultipleDropdown.ts │ │ ├── uploadFile.ts │ │ └── utils.types.ts ├── regions.ts └── types.ts ├── tsconfig.json ├── typedoc.json └── updateEnv.js /.editorconfig: -------------------------------------------------------------------------------- 1 | # http://editorconfig.org 2 | root = true 3 | 4 | [*] 5 | indent_style = space 6 | indent_size = 2 7 | charset = utf-8 8 | trim_trailing_whitespace = true 9 | insert_final_newline = true 10 | 11 | [*.md] 12 | trim_trailing_whitespace = false 13 | -------------------------------------------------------------------------------- /.eslintignore: -------------------------------------------------------------------------------- 1 | examples/ 2 | lib/ 3 | docs/ 4 | -------------------------------------------------------------------------------- /.eslintrc.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | root: true, 3 | parserOptions: { 4 | ecmaVersion: 6, 5 | sourceType: "module", 6 | ecmaFeatures: { 7 | jsx: false, 8 | }, 9 | }, 10 | parser: "@typescript-eslint/parser", 11 | plugins: ["@typescript-eslint"], 12 | extends: ["plugin:prettier/recommended", "plugin:@typescript-eslint/eslint-recommended"], 13 | }; 14 | -------------------------------------------------------------------------------- /.github/pull_request_template.md: -------------------------------------------------------------------------------- 1 | ## What does PR do? 2 | 3 | ... 4 | 5 | ## Type of alteration 6 | 7 | - [ ] Bug fix 8 | - [ ] New feature 9 | - [ ] Breaking change 10 | - [ ] Documentation update 11 | -------------------------------------------------------------------------------- /.github/workflows/publish.yml: -------------------------------------------------------------------------------- 1 | name: Publish to NPM 2 | 3 | on: 4 | release: 5 | types: [published] 6 | 7 | jobs: 8 | build: 9 | name: "Build and Deploy" 10 | runs-on: ubuntu-latest 11 | permissions: 12 | contents: read 13 | id-token: write 14 | steps: 15 | - uses: actions/checkout@v4 16 | - uses: actions/setup-node@v4 17 | with: 18 | node-version: '20.x' 19 | registry-url: 'https://registry.npmjs.org' 20 | - run: npm ci 21 | - run: npm publish --provenance --access public 22 | env: 23 | NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }} 24 | docs: 25 | name: "Build Docs/Types and Deploy" 26 | runs-on: ubuntu-latest 27 | needs: [build] 28 | steps: 29 | - uses: actions/checkout@v4 30 | with: 31 | persist-credentials: false # otherwise, the token used is the GITHUB_TOKEN, instead of your personal token 32 | fetch-depth: 0 # otherwise, you will failed to push refs to dest repo 33 | - name: Create local changes 34 | run: | 35 | npm ci 36 | npm run docs 37 | npm run monacoTypes 38 | rm -rf __tests__ src .github *.js *.json *.md *.MD 39 | rm -rf .editorconfig .eslintignore .eslintrc.js .npmignore .npmrc .prettierrc 40 | rm -rf assets 41 | mv docs/* . 42 | rm -rf docs/ 43 | touch .nojekyll 44 | echo "js.sdk.tago.io" > CNAME 45 | - name: Commit files 46 | run: | 47 | git config --local user.email "action@github.com" 48 | git config --local user.name "GitHub Action" 49 | git add -A 50 | git commit -m "generated documentation" 51 | - name: Push changes 52 | uses: ad-m/github-push-action@master 53 | with: 54 | github_token: ${{ secrets.GITHUB_TOKEN }} 55 | branch: gh-pages 56 | force: true 57 | -------------------------------------------------------------------------------- /.github/workflows/tests.yml: -------------------------------------------------------------------------------- 1 | name: Units Tests 2 | 3 | on: push 4 | 5 | jobs: 6 | build: 7 | runs-on: ubuntu-latest 8 | 9 | strategy: 10 | matrix: 11 | node-version: [16.x, 18.x, 20.x] 12 | 13 | steps: 14 | - uses: actions/checkout@v4 15 | - name: Use Node.js ${{ matrix.node-version }} 16 | uses: actions/setup-node@v4 17 | with: 18 | node-version: ${{ matrix.node-version }} 19 | - run: npm ci 20 | - run: npm run linter 21 | - run: npm test 22 | - run: npm run build --if-present 23 | env: 24 | CI: true 25 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | logs 2 | *.log 3 | pids 4 | *.pid 5 | *.seed 6 | .DS_Store 7 | lib-cov 8 | coverage 9 | .grunt 10 | build/Release 11 | node_modules 12 | *.sublime-* 13 | build/ 14 | out/ 15 | /lib/ 16 | .vscode 17 | yarn.lock 18 | .cache 19 | !.github/** 20 | *.local.* 21 | *.tgz 22 | examples/**/package-lock.json 23 | /docs/ 24 | src/infrastructure/envParams.json 25 | -------------------------------------------------------------------------------- /.npmignore: -------------------------------------------------------------------------------- 1 | nodemon.json 2 | examples/ 3 | __tests__/ 4 | docs/ 5 | .github/ 6 | .editorconfig 7 | .eslintignore 8 | .prettierrc 9 | .eslintrc.js 10 | *.tgz 11 | *.local.* 12 | .DS_Store 13 | jest.config.js 14 | typedoc.json 15 | assets/ 16 | .vscode/ 17 | DOCS.md 18 | updateEnv.js 19 | -------------------------------------------------------------------------------- /.npmrc: -------------------------------------------------------------------------------- 1 | save-prefix="" 2 | engine-strict=true 3 | -------------------------------------------------------------------------------- /.prettierrc: -------------------------------------------------------------------------------- 1 | { 2 | "trailingComma": "es5", 3 | "printWidth": 120, 4 | "useTabs": false, 5 | "tabWidth": 2, 6 | "semi": true, 7 | "singleQuote": false, 8 | "bracketSpacing": true, 9 | "arrowParens": "always", 10 | "endOfLine": "lf" 11 | } 12 | -------------------------------------------------------------------------------- /DOCS.md: -------------------------------------------------------------------------------- 1 |
2 |

3 | TagoIO 4 |

5 | 6 | ## Installation 7 | 8 | ```bash 9 | $ npm install @tago-io/sdk --save 10 | ``` 11 | 12 | ___ 13 | 14 | ## Quick Example 15 | #### Insert Device Data 16 | ``` javascript 17 | const { Device } = require("@tago-io/sdk"); 18 | 19 | const myDevice = new Device({ token: "00000000-2ec4-11e6-a77d-991b8f63b767" }); 20 | 21 | const myData = { 22 | variable: "temperature", 23 | location: { lat: 42.2974279, lng: -85.628292 }, 24 | time: new Date(), 25 | unit: "C", 26 | value: 63, 27 | }; 28 | 29 | async function sendMyData() { 30 | const result = await myDevice.sendData(myData); 31 | 32 | console.log(result); 33 | // 1 Data Added 34 | } 35 | 36 | async function getMyData() { 37 | const result = await myDevice.getData({ variables: ["temperature"], query: "last_item" }); 38 | 39 | console.info("Current Temperature is:", result[0] ? result[0].value : "N/A"); 40 | // Current Temperature is: 63 41 | } 42 | 43 | // -> See full documentation at: http://sdk.js.tago.io/ 44 | ``` 45 | 46 | ___ 47 | 48 | ## License 49 | 50 | TagoIO SDK for JavaScript in the browser and Node.js is released under the [Apache-2.0 License](https://github.com/tago-io/tago-sdk-js/blob/master/LICENSE.md). 51 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 |
2 |

3 | TagoIO 4 |

5 | 6 | # TagoIO - SDK for Node.js and Browser 7 | > TagoIO provides easy connection of electronic devices with external data to driver smarter decisions using contextual analysis. 8 | 9 | ## Help 10 | 11 | TagoIO SDK for JavaScript in the browser and Node.JS. 12 | 13 | | what | where | 14 | |-----------------------|--------------------------| 15 | | TagoIO website | https://tago.io | 16 | | SDK documentation | https://js.sdk.tago.io | 17 | | General documentation | https://docs.tago.io | 18 | 19 | ## Installation 20 | 21 | ```bash 22 | $ npm install @tago-io/sdk --save 23 | ``` 24 | 25 | ## Quick Example 26 | #### Insert Device Data 27 | ``` javascript 28 | const { Device } = require("@tago-io/sdk"); 29 | 30 | const myDevice = new Device({ token: "00000000-2ec4-11e6-a77d-991b8f63b767" }); 31 | 32 | const myData = { 33 | variable: "temperature", 34 | location: { lat: 42.2974279, lng: -85.628292 }, 35 | time: new Date(), 36 | unit: "C", 37 | value: 63, 38 | }; 39 | 40 | async function sendMyData() { 41 | const result = await myDevice.sendData(myData); 42 | 43 | console.log(result); 44 | // 1 Data Added 45 | } 46 | 47 | async function getMyData() { 48 | const result = await myDevice.getData({ variables: ["temperature"], query: "last_item" }); 49 | 50 | console.info("Current Temperature is:", result[0] ? result[0].value : "N/A"); 51 | // Current Temperature is: 63 52 | } 53 | 54 | // -> See full documentation at: https://js.sdk.tago.io/ 55 | ``` 56 | 57 | ## License 58 | 59 | TagoIO SDK for JavaScript in the browser and Node.js is released under the [Apache-2.0 License](https://github.com/tago-io/tago-sdk-js/blob/master/LICENSE.md). 60 | -------------------------------------------------------------------------------- /__tests__/Authorization/info.ts: -------------------------------------------------------------------------------- 1 | import express, { Express } from "express"; 2 | import http from "http"; 3 | import { Authorization } from "../../src/modules"; 4 | import { AddressInfo } from "net"; 5 | 6 | jest.setTimeout(3000); 7 | 8 | describe("Authorization class", () => { 9 | let app: Express; 10 | let service: http.Server; 11 | beforeEach((done) => { 12 | const startServer = () => { 13 | app = express(); 14 | app.use(express.json()); 15 | service = app.listen(0); 16 | process.env.TAGOIO_API = `http://localhost:${(service.address() as AddressInfo).port}`; 17 | done(); 18 | }; 19 | 20 | if (service) { 21 | service.close(startServer); 22 | } else { 23 | startServer(); 24 | } 25 | }); 26 | 27 | afterAll((done) => { 28 | service.close(done); 29 | }); 30 | 31 | test("info", async () => { 32 | let url: string; 33 | let body: object; 34 | let query: object; 35 | app.get("/info", (req, res) => { 36 | url = req.path; 37 | body = req.body; 38 | query = req.query; 39 | res.send({ status: true, result: { id: "test" } }); 40 | }); 41 | 42 | const authorization = new Authorization({ token: "test", region: "env" }); 43 | const result = await authorization.info(); 44 | 45 | expect(result).toMatchObject({ id: "test" }); 46 | expect(url).toBe("/info"); 47 | expect(query).toMatchObject({}); 48 | expect(body).toMatchObject({}); 49 | }); 50 | 51 | test("info with details", async () => { 52 | let url: string; 53 | let body: object; 54 | let query: object; 55 | app.get("/info", (req, res) => { 56 | url = req.url; 57 | body = req.body; 58 | query = req.query; 59 | res.send({ status: true, result: { id: "test" } }); 60 | }); 61 | 62 | const authorization = new Authorization({ token: "test", region: "env", details: true }); 63 | await authorization.info(); 64 | 65 | expect(query).toMatchObject({ details: "true" }); 66 | }); 67 | }); 68 | -------------------------------------------------------------------------------- /__tests__/Connector/resolveToken.ts: -------------------------------------------------------------------------------- 1 | import express, { Express } from "express"; 2 | import http from "http"; 3 | import { Network } from "../../src/modules"; 4 | import { AddressInfo } from "net"; 5 | 6 | jest.setTimeout(3000); 7 | 8 | describe("Network class", () => { 9 | let app: Express; 10 | let service: http.Server; 11 | beforeEach((done) => { 12 | const startServer = () => { 13 | app = express(); 14 | app.use(express.json()); 15 | service = app.listen(0); 16 | process.env.TAGOIO_API = `http://localhost:${(service.address() as AddressInfo).port}`; 17 | done(); 18 | }; 19 | 20 | if (service) { 21 | service.close(startServer); 22 | } else { 23 | startServer(); 24 | } 25 | }); 26 | 27 | afterAll((done) => { 28 | service.close(done); 29 | }); 30 | 31 | test("Resolving token without authorization", async () => { 32 | let url: string; 33 | let body: object; 34 | app.get(`/integration/network/resolve/:serieNumber`, (req, res) => { 35 | url = req.url; 36 | body = req.body; 37 | res.send({ status: true, result: "token" }); 38 | }); 39 | 40 | const connector = new Network({ token: "test", region: "env" }); 41 | const result = await connector.resolveToken("serieNumber"); 42 | 43 | expect(result).toBe("token"); 44 | expect(url).toBe("/integration/network/resolve/serieNumber"); 45 | expect(body).toMatchObject({}); 46 | }); 47 | 48 | test("Resolving token with authorization", async () => { 49 | let url: string; 50 | let body: object; 51 | let query: object; 52 | app.get(`/integration/network/resolve/:serieNumber/:authorization`, (req, res) => { 53 | url = req.url; 54 | body = req.body; 55 | query = req.query; 56 | res.send({ status: true, result: "token" }); 57 | }); 58 | 59 | const connector = new Network({ token: "test", region: "env" }); 60 | const result = await connector.resolveToken("serieNumber", "authorization"); 61 | 62 | expect(result).toBe("token"); 63 | expect(url).toBe("/integration/network/resolve/serieNumber/authorization"); 64 | expect(query).toMatchObject({}); 65 | expect(body).toMatchObject({}); 66 | }); 67 | 68 | test("Resolving token with detail true", async () => { 69 | let query: object; 70 | app.get(`/integration/network/resolve/:serieNumber`, (req, res) => { 71 | query = req.query; 72 | res.send({ status: true, result: "token" }); 73 | }); 74 | 75 | const connector = new Network({ token: "test", region: "env", details: true }); 76 | const result = await connector.resolveToken("serieNumber"); 77 | 78 | expect(result).toBe("token"); 79 | expect(query).toMatchObject({ details: "true" }); 80 | }); 81 | }); 82 | -------------------------------------------------------------------------------- /__tests__/Device/sendData.ts: -------------------------------------------------------------------------------- 1 | import express, { Express } from "express"; 2 | import http from "http"; 3 | import { Device } from "../../src/modules"; 4 | import { AddressInfo } from "net"; 5 | 6 | describe("Device class", () => { 7 | let app: Express; 8 | let service: http.Server; 9 | beforeEach((done) => { 10 | const startServer = () => { 11 | app = express(); 12 | app.use(express.json()); 13 | service = app.listen(0); 14 | process.env.TAGOIO_API = `http://localhost:${(service.address() as AddressInfo).port}`; 15 | done(); 16 | }; 17 | 18 | if (service) { 19 | service.close(startServer); 20 | } else { 21 | startServer(); 22 | } 23 | }); 24 | 25 | afterAll((done) => { 26 | service.close(done); 27 | }); 28 | 29 | test("Sending data", async () => { 30 | let url: string; 31 | let body: object; 32 | app.post("/data", (req, res) => { 33 | url = req.url; 34 | body = req.body; 35 | res.send({ status: true, result: "1 Data Added" }); 36 | }); 37 | 38 | const device = new Device({ token: "test", region: "env" }); 39 | const result = await device.sendData({ variable: "aa" }); 40 | 41 | expect(result).toBe("1 Data Added"); 42 | expect(url).toBe("/data"); 43 | expect(body).toMatchObject([{ variable: "aa" }]); 44 | }); 45 | }); 46 | -------------------------------------------------------------------------------- /__tests__/Dictionary/getLanguagesData.ts: -------------------------------------------------------------------------------- 1 | import express, { Express } from "express"; 2 | import http from "http"; 3 | import { AddressInfo } from "net"; 4 | 5 | import { Dictionary } from "../../src/modules"; 6 | 7 | describe("getLanguagesData", () => { 8 | let app: Express; 9 | let service: http.Server; 10 | let dictionary: Dictionary; 11 | let url: string; 12 | let body: object; 13 | 14 | const slug = "SLUG"; 15 | const language = "en-US"; 16 | 17 | beforeAll((done) => { 18 | dictionary = new Dictionary({ language: "en-US", token: "mockToken" }); 19 | 20 | const startServer = () => { 21 | app = express(); 22 | app.use(express.json()); 23 | service = app.listen(0); 24 | process.env.TAGOIO_API = `http://localhost:${(service.address() as AddressInfo).port}`; 25 | 26 | app.get(`/dictionary/${slug}/${language}`, (req, res) => { 27 | url = req.url; 28 | body = req.body; 29 | res.send({ 30 | status: true, 31 | result: { 32 | KEY1: "first value", 33 | KEY2: "second value", 34 | }, 35 | }); 36 | }); 37 | 38 | done(); 39 | }; 40 | 41 | if (service) { 42 | service.close(startServer); 43 | } else { 44 | startServer(); 45 | } 46 | }); 47 | 48 | afterAll((done) => { 49 | service.close(done); 50 | }); 51 | 52 | beforeEach(() => { 53 | url = undefined; 54 | body = undefined; 55 | }); 56 | 57 | it("makes a request when applying the dictionary to a string with expressions", async () => { 58 | const result = await dictionary.applyToString( 59 | "This is a string with two dictionary expressions: #SLUG.KEY1# and #SLUG.KEY2#." 60 | ); 61 | 62 | expect(result).toBe("This is a string with two dictionary expressions: first value and second value."); 63 | expect(url).toBe(`/dictionary/${slug}/${language}?fallback=true`); 64 | expect(body).toMatchObject({}); 65 | }); 66 | 67 | it("does not make a request if there's no dictionary expression in the string", async () => { 68 | const result = await dictionary.applyToString("This is a string with zero dictionary expressions."); 69 | 70 | expect(result).toBe("This is a string with zero dictionary expressions."); 71 | expect(url).toBeUndefined(); 72 | expect(body).toBeUndefined(); 73 | }); 74 | }); 75 | -------------------------------------------------------------------------------- /__tests__/Dictionary/getValueFromKey.ts: -------------------------------------------------------------------------------- 1 | import { Dictionary } from "../../src/modules"; 2 | 3 | import { enUS, ptBR } from "./__mocks__/dictionaries"; 4 | 5 | describe("getValueFromKey", () => { 6 | let dictionary: Dictionary; 7 | let fn: jest.SpyInstance; 8 | 9 | beforeAll(() => { 10 | dictionary = new Dictionary({ token: "mockToken" }); 11 | 12 | // Mock the function that gets data from the API 13 | fn = jest.spyOn(dictionary, "getLanguagesData").mockImplementation(async (slug, language) => { 14 | return language === "pt-BR" ? ptBR[slug] : enUS[slug]; 15 | }); 16 | }); 17 | 18 | it("does not get a value with one or more missing parameters", async () => { 19 | expect(async () => await dictionary.getValueFromKey(undefined, "TEST", "TEST_KEY")).rejects.toThrow(); 20 | expect(async () => await dictionary.getValueFromKey("en-US", undefined, "TEST_KEY")).rejects.toThrow(); 21 | expect(async () => await dictionary.getValueFromKey("en-US", "TEST", undefined)).rejects.toThrow(); 22 | }); 23 | 24 | it("return the expression as is if the dictionary does not exist", async () => { 25 | const value = await dictionary.getValueFromKey("en-US", "DOESNTEXIST", "TEST_KEY"); 26 | 27 | expect(value).toBe("#DOESNTEXIST.TEST_KEY#"); 28 | }); 29 | 30 | it("return the expression as is if the key is not present in a dictionary", async () => { 31 | const value = await dictionary.getValueFromKey("en-US", "TEST", "TEST_KEY_NOT_IN_DICT"); 32 | 33 | expect(value).toBe("#TEST.TEST_KEY_NOT_IN_DICT#"); 34 | }); 35 | 36 | it("gets a value if the dictionary exists and the key is present", async () => { 37 | const value = await dictionary.getValueFromKey("en-US", "TEST", "TEST_KEY"); 38 | 39 | expect(value).toBe("Some test value"); 40 | }); 41 | }); 42 | -------------------------------------------------------------------------------- /__tests__/Dictionary/parseExpression.ts: -------------------------------------------------------------------------------- 1 | import { Dictionary } from "../../src/modules"; 2 | 3 | describe("parseExpression", () => { 4 | let dictionary: Dictionary; 5 | 6 | beforeAll(() => { 7 | dictionary = new Dictionary({ token: "mockToken" }); 8 | }); 9 | 10 | it("parses an expression without parameters", () => { 11 | const parsed = dictionary.parseExpression("#TEST.TEST_KEY#"); 12 | 13 | expect(parsed).toEqual({ dictionary: "TEST", key: "TEST_KEY" }); 14 | }); 15 | 16 | it("parses an expression with one parameter", () => { 17 | const parsed = dictionary.parseExpression("#TEST.TEST_KEY,a#"); 18 | 19 | expect(parsed).toEqual({ dictionary: "TEST", key: "TEST_KEY", params: ["a"] }); 20 | }); 21 | 22 | it("parses an expression with three parameters", () => { 23 | const parsed = dictionary.parseExpression("#TEST.TEST_KEY,a,bb,ccc#"); 24 | 25 | expect(parsed).toEqual({ dictionary: "TEST", key: "TEST_KEY", params: ["a", "bb", "ccc"] }); 26 | }); 27 | 28 | it("parses an expression with three parameters normal parameters and one quoted with a comma", () => { 29 | const parsed = dictionary.parseExpression('#TEST.TEST_KEY,a,bb,ccc,"ddd,d"#'); 30 | 31 | expect(parsed).toEqual({ dictionary: "TEST", key: "TEST_KEY", params: ["a", "bb", "ccc", "ddd,d"] }); 32 | }); 33 | 34 | it("parses an expression with three parameters normal parameters and one quoted with a hashtag", () => { 35 | const parsed = dictionary.parseExpression('#TEST.TEST_KEY,a,bb,ccc,"ddd#d"#'); 36 | 37 | expect(parsed).toEqual({ dictionary: "TEST", key: "TEST_KEY", params: ["a", "bb", "ccc", "ddd#d"] }); 38 | }); 39 | 40 | it("does not parse a value enclosed in hashtags but is not an expression, returning null", () => { 41 | expect(dictionary.parseExpression("#slug#")).toEqual(null); 42 | expect(dictionary.parseExpression("#slug.#")).toEqual(null); 43 | expect(dictionary.parseExpression("#slug.lowercase#")).toEqual(null); 44 | expect(dictionary.parseExpression("#slug.VALID_KEY#")).toEqual(null); 45 | expect(dictionary.parseExpression("#SLUG.#")).toEqual(null); 46 | expect(dictionary.parseExpression("#Slug.MIXED_CASE_SLUG#")).toEqual(null); 47 | expect(dictionary.parseExpression("#SLUG.Mixed_Case_Key#")).toEqual(null); 48 | expect(dictionary.parseExpression("#SLUG.KEY WITH SPACES#")).toEqual(null); 49 | expect(dictionary.parseExpression("#S L U G.VALID_KEY#")).toEqual(null); 50 | }); 51 | }); 52 | -------------------------------------------------------------------------------- /__tests__/Dictionary/resolveExpression.ts: -------------------------------------------------------------------------------- 1 | import { Dictionary } from "../../src/modules"; 2 | 3 | import { enUS, ptBR } from "./__mocks__/dictionaries"; 4 | 5 | describe("resolveExpression", () => { 6 | let dictionary: Dictionary; 7 | let fn: jest.SpyInstance; 8 | 9 | beforeAll(() => { 10 | dictionary = new Dictionary({ token: "mockToken" }); 11 | 12 | // Mock the function that gets data from the API 13 | fn = jest.spyOn(dictionary, "getLanguagesData").mockImplementation(async (slug, language) => { 14 | return language === "pt-BR" ? ptBR[slug] : enUS[slug]; 15 | }); 16 | }); 17 | 18 | it("resolves an expression with a single parameter", async () => { 19 | const expression = dictionary.parseExpression("#TEST.TEST_RESOLVE_SINGLE_PARAM,123#"); 20 | const resolved = await dictionary.resolveExpression({ language: "en-US", expression }); 21 | 22 | expect(resolved).toEqual("Resolved single param 123"); 23 | }); 24 | 25 | it("resolves an expression with more than one parameter", async () => { 26 | const expression = dictionary.parseExpression("#TEST.TEST_RESOLVE_THREE_PARAMS,123,456,789#"); 27 | const resolved = await dictionary.resolveExpression({ language: "en-US", expression }); 28 | 29 | expect(resolved).toEqual("Resolved params: 1 = 123, 2 = 456, 3 = 789"); 30 | }); 31 | 32 | it("resolves an expression with three parameters passing only the first argument", async () => { 33 | const expression = dictionary.parseExpression("#TEST.TEST_RESOLVE_THREE_PARAMS,123#"); 34 | const resolved = await dictionary.resolveExpression({ language: "en-US", expression }); 35 | 36 | expect(resolved).toEqual("Resolved params: 1 = 123, 2 = $1, 3 = $2"); 37 | }); 38 | 39 | it("resolves an expression with three arguments, including quotes commas and hashes", async () => { 40 | const expression = dictionary.parseExpression('#TEST.TEST_RESOLVE_THREE_PARAMS,"1,23","#456","#789 & #10"#'); 41 | const resolved = await dictionary.resolveExpression({ language: "en-US", expression }); 42 | 43 | expect(resolved).toEqual("Resolved params: 1 = 1,23, 2 = #456, 3 = #789 & #10"); 44 | }); 45 | 46 | it("resolves an expression with one parameter passing more than one argument", async () => { 47 | const expression = dictionary.parseExpression("#TEST.TEST_RESOLVE_SINGLE_PARAM,789,456,123#"); 48 | const resolved = await dictionary.resolveExpression({ language: "en-US", expression }); 49 | 50 | expect(resolved).toEqual("Resolved single param 789"); 51 | }); 52 | 53 | it("resolves an expression with a dollar sign on the value string", async () => { 54 | const expression = dictionary.parseExpression('#TEST.BALANCE_LABEL,"499,99"#'); 55 | const resolved = await dictionary.resolveExpression({ language: "pt-BR", expression }); 56 | 57 | expect(resolved).toEqual("Saldo (BRL): R$ 499,99"); 58 | }); 59 | }); 60 | -------------------------------------------------------------------------------- /__tests__/Migration/Migration.ts: -------------------------------------------------------------------------------- 1 | import Migration from "../../src/modules/Migration/Migration"; 2 | import * as oldStructure from "./__mocks__/widgetsOldStructure.json"; 3 | 4 | const oldWidget = oldStructure.noteStructure; 5 | 6 | describe("Note widget - migration suite", () => { 7 | /** 8 | * Tests to see if the root properties have been transferred properly. 9 | */ 10 | it("converts base structure properly", () => { 11 | const newStructure = Migration.convertWidget(oldWidget); 12 | 13 | expect(newStructure.label).toEqual(oldWidget.label); 14 | expect(newStructure.id).toEqual(oldWidget.id); 15 | expect(newStructure.dashboard).toEqual(oldWidget.dashboard); 16 | expect(newStructure.type).toEqual("note"); 17 | }); 18 | 19 | /** 20 | * Tests to see if some root properties have NOT been transferred. 21 | */ 22 | it("should not convert data, realtime, or analysis", () => { 23 | const newStructure = Migration.convertWidget(oldWidget); 24 | 25 | expect(newStructure.data).toEqual([]); 26 | expect(newStructure.realtime).toBeFalsy(); 27 | expect(newStructure.analysis_run).toBeFalsy(); 28 | }); 29 | 30 | /** 31 | * Tests to see if display properties were transferred properly. 32 | */ 33 | it("converts display properties", () => { 34 | const newStructure = Migration.convertWidget(oldWidget); 35 | 36 | expect(newStructure.display).not.toBeFalsy(); 37 | expect(newStructure.display.value).toEqual(oldWidget.display.text); 38 | expect(newStructure.display.header_buttons).toEqual(oldWidget.display.header_buttons); 39 | expect(newStructure.display.help).toEqual(oldWidget.display.help); 40 | }); 41 | 42 | /** 43 | * Tests to see if the structure is old or new. 44 | */ 45 | it("correctly identifies if it's new structure or old", () => { 46 | const newStructure = Migration.convertWidget(oldWidget); 47 | 48 | expect(Migration.isOldStructure(newStructure)).toEqual(false); 49 | expect(Migration.isOldStructure(oldWidget)).toEqual(true); 50 | }); 51 | 52 | /** 53 | * Test if the header buttons, or help buttons are undefined 54 | */ 55 | it("correctly identifies when header_buttons or help is undefined", () => { 56 | // Testing when is just row or column deleted 57 | const copyOfOld1 = Object.assign({ ...oldWidget }, {}); 58 | delete copyOfOld1.display.header_buttons; 59 | delete copyOfOld1.display.help; 60 | 61 | const newStructure1 = Migration.convertWidget(oldWidget); 62 | expect(newStructure1.display).not.toBeFalsy(); 63 | expect(newStructure1.display.header_buttons).toEqual([]); 64 | expect(newStructure1.display.help).toEqual(""); 65 | expect(Migration.isOldStructure(newStructure1)).toEqual(false); 66 | }); 67 | }); 68 | -------------------------------------------------------------------------------- /__tests__/Migration/card.ts: -------------------------------------------------------------------------------- 1 | import { convert, isOldStructure } from "../../src/modules/Migration/card"; 2 | import * as oldStructure from "./__mocks__/widgetsOldStructure.json"; 3 | 4 | const oldWidget = oldStructure.cardStructure; 5 | 6 | describe("Card widget - migration suite", () => { 7 | /** 8 | * Tests to see if the root properties have been transferred properly. 9 | */ 10 | it("converts base structure properly", () => { 11 | const newStructure = convert(oldWidget); 12 | 13 | expect(newStructure.label).toEqual(oldWidget.label); 14 | expect(newStructure.id).toEqual(oldWidget.id); 15 | expect(newStructure.dashboard).toEqual(oldWidget.dashboard); 16 | expect(newStructure.type).toEqual("card"); 17 | }); 18 | 19 | /** 20 | * Tests to see if some root properties have NOT been transferred. 21 | */ 22 | it("should not convert data, realtime, or analysis", () => { 23 | const newStructure = convert(oldWidget); 24 | 25 | expect(newStructure.data).toEqual(oldWidget.data); 26 | expect(newStructure.realtime).toBeFalsy(); 27 | expect(newStructure.analysis_run).toBeFalsy(); 28 | }); 29 | 30 | /** 31 | * Tests to see if display properties were transferred properly. 32 | */ 33 | it("converts display properties", () => { 34 | const newStructure = convert(oldWidget); 35 | 36 | expect(newStructure.display).not.toBeFalsy(); 37 | expect(newStructure.display.header_buttons).toEqual(oldWidget.display.header_buttons); 38 | expect(newStructure.display.help).toEqual(oldWidget.display.help); 39 | }); 40 | 41 | /** 42 | * Tests to see if the structure is old or new. 43 | */ 44 | it("correctly identifies if it's new structure or old", () => { 45 | const newStructure = convert(oldWidget); 46 | 47 | expect(isOldStructure(newStructure)).toEqual(false); 48 | expect(isOldStructure(oldWidget)).toEqual(true); 49 | }); 50 | 51 | /** 52 | * Test if the header buttons, or help buttons are undefined 53 | */ 54 | it("correctly identifies when header_buttons or help is undefined", () => { 55 | // Testing when is just row or column deleted 56 | const copyOfOld1 = Object.assign({ ...oldWidget }, {}); 57 | delete copyOfOld1.display.header_buttons; 58 | delete copyOfOld1.display.help; 59 | 60 | const newStructure1 = convert(copyOfOld1); 61 | expect(newStructure1.display).not.toBeFalsy(); 62 | expect(newStructure1.display.header_buttons).toEqual([]); 63 | expect(newStructure1.display.help).toEqual(""); 64 | expect(isOldStructure(newStructure1)).toEqual(false); 65 | }); 66 | }); 67 | -------------------------------------------------------------------------------- /__tests__/Migration/clock.ts: -------------------------------------------------------------------------------- 1 | import { convert, isOldStructure } from "../../src/modules/Migration/clock"; 2 | import * as oldStructure from "./__mocks__/widgetsOldStructure.json"; 3 | 4 | const oldWidget = oldStructure.clockStructure; 5 | 6 | describe("Clock widget - migration suite", () => { 7 | /** 8 | * Tests to see if the root properties have been transferred properly. 9 | */ 10 | it("converts base structure properly", () => { 11 | const newStructure = convert(oldWidget); 12 | 13 | expect(newStructure.label).toEqual(oldWidget.label); 14 | expect(newStructure.id).toEqual(oldWidget.id); 15 | expect(newStructure.dashboard).toEqual(oldWidget.dashboard); 16 | expect(newStructure.type).toEqual("clock"); 17 | }); 18 | 19 | /** 20 | * Tests to see if some root properties have NOT been transferred. 21 | */ 22 | it("should not convert data, realtime, or analysis", () => { 23 | const newStructure = convert(oldWidget); 24 | 25 | expect(newStructure.data).toEqual(oldWidget.data); 26 | expect(newStructure.realtime).toBeFalsy(); 27 | expect(newStructure.analysis_run).toBeFalsy(); 28 | }); 29 | 30 | /** 31 | * Tests to see if display properties were transferred properly. 32 | */ 33 | it("converts display properties", () => { 34 | const newStructure = convert(oldWidget); 35 | 36 | expect(newStructure.display).not.toBeFalsy(); 37 | expect(newStructure.display.header_buttons).toEqual(oldWidget.display.header_buttons); 38 | expect(newStructure.display.help).toEqual(oldWidget.display.help); 39 | }); 40 | 41 | /** 42 | * Tests to see if the structure is old or new. 43 | */ 44 | it("correctly identifies if it's new structure or old", () => { 45 | const newStructure = convert(oldWidget); 46 | 47 | expect(isOldStructure(newStructure)).toEqual(false); 48 | expect(isOldStructure(oldWidget)).toEqual(true); 49 | }); 50 | 51 | /** 52 | * Test if the header buttons, or help buttons are undefined 53 | */ 54 | it("correctly identifies when header_buttons or help is undefined", () => { 55 | // Testing when is just row or column deleted 56 | const copyOfOld1 = Object.assign({ ...oldWidget }, {}); 57 | delete copyOfOld1.display.header_buttons; 58 | delete copyOfOld1.display.help; 59 | 60 | const newStructure1 = convert(copyOfOld1); 61 | expect(newStructure1.display).not.toBeFalsy(); 62 | expect(newStructure1.display.header_buttons).toEqual([]); 63 | expect(newStructure1.display.help).toEqual(""); 64 | expect(isOldStructure(newStructure1)).toEqual(false); 65 | }); 66 | }); 67 | -------------------------------------------------------------------------------- /__tests__/Migration/custom.ts: -------------------------------------------------------------------------------- 1 | import { convert, isOldStructure } from "../../src/modules/Migration/custom"; 2 | import * as oldStructure from "./__mocks__/widgetsOldStructure.json"; 3 | 4 | const oldWidget = oldStructure.customStructure; 5 | 6 | describe("Custom widget - migration suite", () => { 7 | /** 8 | * Tests to see if the root properties have been transferred properly. 9 | */ 10 | it("converts base structure properly", () => { 11 | const newStructure = convert(oldWidget); 12 | 13 | expect(newStructure.label).toEqual(oldWidget.label); 14 | expect(newStructure.id).toEqual(oldWidget.id); 15 | expect(newStructure.dashboard).toEqual(oldWidget.dashboard); 16 | expect(newStructure.type).toEqual("iframe"); 17 | }); 18 | 19 | /** 20 | * Tests to see if some root properties have NOT been transferred. 21 | */ 22 | it("should not convert data, realtime, or analysis", () => { 23 | const newStructure = convert(oldWidget); 24 | 25 | expect(newStructure.data).toEqual(oldWidget.data); 26 | expect(newStructure.realtime).toBeFalsy(); 27 | expect(newStructure.analysis_run).toEqual(oldWidget?.analysis_run); 28 | }); 29 | 30 | /** 31 | * Tests to see if display properties were transferred properly. 32 | */ 33 | it("converts display properties", () => { 34 | const newStructure = convert(oldWidget); 35 | 36 | expect(newStructure.display).not.toBeFalsy(); 37 | expect(newStructure.display.header_buttons).toEqual(oldWidget.display.header_buttons); 38 | expect(newStructure.display.help).toEqual(oldWidget.display.help); 39 | }); 40 | 41 | /** 42 | * Tests to see if the structure is old or new. 43 | */ 44 | it("correctly identifies if it's new structure or old", () => { 45 | const newStructure = convert(oldWidget); 46 | 47 | expect(isOldStructure(newStructure)).toEqual(false); 48 | expect(isOldStructure(oldWidget)).toEqual(true); 49 | }); 50 | 51 | /** 52 | * Test if the header buttons, or help buttons are undefined 53 | */ 54 | it("correctly identifies when header_buttons or help is undefined", () => { 55 | // Testing when is just row or column deleted 56 | const copyOfOld1 = Object.assign({ ...oldWidget }, {}); 57 | delete copyOfOld1.display.header_buttons; 58 | delete copyOfOld1.display.help; 59 | 60 | const newStructure1 = convert(copyOfOld1); 61 | expect(newStructure1.display).not.toBeFalsy(); 62 | expect(newStructure1.display.header_buttons).toEqual([]); 63 | expect(newStructure1.display.help).toEqual(""); 64 | expect(isOldStructure(newStructure1)).toEqual(false); 65 | }); 66 | }); 67 | -------------------------------------------------------------------------------- /__tests__/Migration/display.ts: -------------------------------------------------------------------------------- 1 | import { convert, isOldStructure } from "../../src/modules/Migration/display"; 2 | import * as oldStructure from "./__mocks__/widgetsOldStructure.json"; 3 | 4 | const oldWidget = oldStructure.displayStructure; 5 | 6 | describe("Display widget - migration suite", () => { 7 | /** 8 | * Tests to see if the root properties have been transferred properly. 9 | */ 10 | it("converts base structure properly", () => { 11 | const newStructure = convert(oldWidget); 12 | 13 | expect(newStructure.label).toEqual(oldWidget.label); 14 | expect(newStructure.id).toEqual(oldWidget.id); 15 | expect(newStructure.dashboard).toEqual(oldWidget.dashboard); 16 | expect(newStructure.type).toEqual("display"); 17 | }); 18 | 19 | /** 20 | * Tests to see if some root properties have NOT been transferred. 21 | */ 22 | it("should not convert data, realtime, or analysis", () => { 23 | const newStructure = convert(oldWidget); 24 | 25 | expect(newStructure.data).toEqual(oldWidget.data); 26 | expect(newStructure.realtime).toBeFalsy(); 27 | expect(newStructure.analysis_run).toBeFalsy(); 28 | }); 29 | 30 | /** 31 | * Tests to see if display properties were transferred properly. 32 | */ 33 | it("converts display properties", () => { 34 | const newStructure = convert(oldWidget); 35 | 36 | expect(newStructure.display).not.toBeFalsy(); 37 | expect(newStructure.display.header_buttons).toEqual(oldWidget.display.header_buttons); 38 | expect(newStructure.display.help).toEqual(oldWidget.display.help); 39 | }); 40 | 41 | /** 42 | * Tests to see if the structure is old or new. 43 | */ 44 | it("correctly identifies if it's new structure or old", () => { 45 | const newStructure = convert(oldWidget); 46 | 47 | expect(isOldStructure(newStructure)).toEqual(false); 48 | expect(isOldStructure(oldWidget)).toEqual(true); 49 | }); 50 | 51 | /** 52 | * Test if the header buttons, or help buttons are undefined 53 | */ 54 | it("correctly identifies when header_buttons or help is undefined", () => { 55 | // Testing when is just row or column deleted 56 | const copyOfOld1 = Object.assign({ ...oldWidget }, {}); 57 | delete copyOfOld1.display.header_buttons; 58 | delete copyOfOld1.display.help; 59 | 60 | const newStructure1 = convert(copyOfOld1); 61 | expect(newStructure1.display).not.toBeFalsy(); 62 | expect(newStructure1.display.header_buttons).toEqual([]); 63 | expect(newStructure1.display.help).toEqual(""); 64 | expect(isOldStructure(newStructure1)).toEqual(false); 65 | }); 66 | }); 67 | -------------------------------------------------------------------------------- /__tests__/Migration/grainbin.ts: -------------------------------------------------------------------------------- 1 | import { convert, isOldStructure } from "../../src/modules/Migration/grainbin"; 2 | import * as oldStructure from "./__mocks__/widgetsOldStructure.json"; 3 | 4 | const oldWidget = oldStructure.grainbin; 5 | 6 | describe("Grain bin widget - migration suite", () => { 7 | /** 8 | * Tests to see if the root properties have been transferred properly. 9 | */ 10 | it("converts base structure properly", () => { 11 | const newStructure = convert(oldWidget); 12 | 13 | expect(newStructure.label).toEqual(oldWidget.label); 14 | expect(newStructure.id).toEqual(oldWidget.id); 15 | expect(newStructure.dashboard).toEqual(oldWidget.dashboard); 16 | expect(newStructure.type).toEqual("grainbin"); 17 | }); 18 | 19 | /** 20 | * Tests to see if some root properties have NOT been transferred. 21 | */ 22 | it("should not convert data, realtime, or analysis", () => { 23 | const newStructure = convert(oldWidget); 24 | 25 | expect(newStructure.data).toEqual(oldWidget.data); 26 | expect(newStructure.realtime).toBeFalsy(); 27 | expect(newStructure.analysis_run).toBeFalsy(); 28 | }); 29 | 30 | /** 31 | * Tests to see if display properties were transferred properly. 32 | */ 33 | it("converts display properties", () => { 34 | const newStructure = convert(oldWidget); 35 | 36 | expect(newStructure.display).not.toBeFalsy(); 37 | expect(newStructure.display.header_buttons).toEqual(oldWidget.display.header_buttons); 38 | expect(newStructure.display.help).toEqual(oldWidget.display.help); 39 | }); 40 | 41 | /** 42 | * Tests to see if the structure is old or new. 43 | */ 44 | it("correctly identifies if it's new structure or old", () => { 45 | const newStructure = convert(oldWidget); 46 | 47 | expect(isOldStructure(newStructure)).toEqual(false); 48 | expect(isOldStructure(oldWidget)).toEqual(true); 49 | }); 50 | 51 | /** 52 | * Test if the header buttons, or help buttons are undefined 53 | */ 54 | it("correctly identifies when header_buttons or help is undefined", () => { 55 | // Testing when is just row or column deleted 56 | const copyOfOld1 = Object.assign({ ...oldWidget }, {}); 57 | delete copyOfOld1.display.header_buttons; 58 | delete copyOfOld1.display.help; 59 | 60 | const newStructure1 = convert(copyOfOld1); 61 | expect(newStructure1.display).not.toBeFalsy(); 62 | expect(newStructure1.display.header_buttons).toEqual([]); 63 | expect(newStructure1.display.help).toEqual(""); 64 | expect(isOldStructure(newStructure1)).toEqual(false); 65 | }); 66 | }); 67 | -------------------------------------------------------------------------------- /__tests__/Migration/heatmap.ts: -------------------------------------------------------------------------------- 1 | import { convert, isOldStructure } from "../../src/modules/Migration/heatmap"; 2 | import * as oldStructure from "./__mocks__/widgetsOldStructure.json"; 3 | 4 | const oldWidget = oldStructure.heatmapStructure; 5 | 6 | describe("Heat map widget - migration suite", () => { 7 | /** 8 | * Tests to see if the root properties have been transferred properly. 9 | */ 10 | it("converts base structure properly", () => { 11 | const newStructure = convert(oldWidget); 12 | 13 | expect(newStructure.label).toEqual(oldWidget.label); 14 | expect(newStructure.id).toEqual(oldWidget.id); 15 | expect(newStructure.dashboard).toEqual(oldWidget.dashboard); 16 | expect(newStructure.type).toEqual("heat_map"); 17 | }); 18 | 19 | /** 20 | * Tests to see if some root properties have NOT been transferred. 21 | */ 22 | it("should not convert data, realtime, or analysis", () => { 23 | const newStructure = convert(oldWidget); 24 | 25 | expect(newStructure.data).toEqual(oldWidget.data); 26 | expect(newStructure.realtime).toBeFalsy(); 27 | expect(newStructure.analysis_run).toBeFalsy(); 28 | }); 29 | 30 | /** 31 | * Tests to see if display properties were transferred properly. 32 | */ 33 | it("converts display properties", () => { 34 | const newStructure = convert(oldWidget); 35 | 36 | expect(newStructure.display).not.toBeFalsy(); 37 | expect(newStructure.display.header_buttons).toEqual(oldWidget.display.header_buttons); 38 | expect(newStructure.display.help).toEqual(oldWidget.display.help); 39 | }); 40 | 41 | /** 42 | * Tests to see if the structure is old or new. 43 | */ 44 | it("correctly identifies if it's new structure or old", () => { 45 | const newStructure = convert(oldWidget); 46 | 47 | expect(isOldStructure(newStructure)).toEqual(false); 48 | expect(isOldStructure(oldWidget)).toEqual(true); 49 | }); 50 | 51 | /** 52 | * Test if the header buttons, or help buttons are undefined 53 | */ 54 | it("correctly identifies when header_buttons or help is undefined", () => { 55 | // Testing when is just row or column deleted 56 | const copyOfOld1 = Object.assign({ ...oldWidget }, {}); 57 | delete copyOfOld1.display.header_buttons; 58 | delete copyOfOld1.display.help; 59 | 60 | const newStructure1 = convert(copyOfOld1); 61 | expect(newStructure1.display).not.toBeFalsy(); 62 | expect(newStructure1.display.header_buttons).toEqual([]); 63 | expect(newStructure1.display.help).toEqual(""); 64 | expect(isOldStructure(newStructure1)).toEqual(false); 65 | }); 66 | }); 67 | -------------------------------------------------------------------------------- /__tests__/Migration/image.ts: -------------------------------------------------------------------------------- 1 | import { convert, isOldStructure } from "../../src/modules/Migration/image"; 2 | import * as oldStructure from "./__mocks__/widgetsOldStructure.json"; 3 | 4 | const oldWidget = oldStructure.imageStructure; 5 | 6 | describe("Image widget - migration suite", () => { 7 | /** 8 | * Tests to see if the root properties have been transferred properly. 9 | */ 10 | it("converts base structure properly", () => { 11 | const newStructure = convert(oldWidget); 12 | 13 | expect(newStructure.label).toEqual(oldWidget.label); 14 | expect(newStructure.id).toEqual(oldWidget.id); 15 | expect(newStructure.dashboard).toEqual(oldWidget.dashboard); 16 | expect(newStructure.type).toEqual("image"); 17 | }); 18 | 19 | /** 20 | * Tests to see if some root properties have NOT been transferred. 21 | */ 22 | it("should not convert data, realtime, or analysis", () => { 23 | const newStructure = convert(oldWidget); 24 | 25 | expect(newStructure.data).toEqual(oldWidget.data); 26 | expect(newStructure.realtime).toBeFalsy(); 27 | expect(newStructure.analysis_run).toBeFalsy(); 28 | }); 29 | 30 | /** 31 | * Tests to see if display properties were transferred properly. 32 | */ 33 | it("converts display properties", () => { 34 | const newStructure = convert(oldWidget); 35 | 36 | expect(newStructure.display).not.toBeFalsy(); 37 | expect(newStructure.display.header_buttons).toEqual(oldWidget.display.header_buttons); 38 | expect(newStructure.display.help).toEqual(oldWidget.display.help); 39 | }); 40 | 41 | /** 42 | * Tests to see if the structure is old or new. 43 | */ 44 | it("correctly identifies if it's new structure or old", () => { 45 | const newStructure = convert(oldWidget); 46 | 47 | expect(isOldStructure(newStructure)).toEqual(false); 48 | expect(isOldStructure(oldWidget)).toEqual(true); 49 | }); 50 | 51 | /** 52 | * Test if the header buttons, or help buttons are undefined 53 | */ 54 | it("correctly identifies when header_buttons or help is undefined", () => { 55 | // Testing when is just row or column deleted 56 | const copyOfOld1 = Object.assign({ ...oldWidget }, {}); 57 | delete copyOfOld1.display.header_buttons; 58 | delete copyOfOld1.display.help; 59 | 60 | const newStructure1 = convert(copyOfOld1); 61 | expect(newStructure1.display).not.toBeFalsy(); 62 | expect(newStructure1.display.header_buttons).toEqual([]); 63 | expect(newStructure1.display.help).toEqual(""); 64 | expect(isOldStructure(newStructure1)).toEqual(false); 65 | }); 66 | }); 67 | -------------------------------------------------------------------------------- /__tests__/Migration/imagemarker.ts: -------------------------------------------------------------------------------- 1 | import { convert, isOldStructure } from "../../src/modules/Migration/imagemarker"; 2 | import * as oldStructure from "./__mocks__/widgetsOldStructure.json"; 3 | 4 | const oldWidget = oldStructure.imagemarkerStructure; 5 | 6 | describe("Image marker widget - migration suite", () => { 7 | /** 8 | * Tests to see if the root properties have been transferred properly. 9 | */ 10 | it("converts base structure properly", () => { 11 | const newStructure = convert(oldWidget); 12 | 13 | expect(newStructure.label).toEqual(oldWidget.label); 14 | expect(newStructure.id).toEqual(oldWidget.id); 15 | expect(newStructure.dashboard).toEqual(oldWidget.dashboard); 16 | expect(newStructure.type).toEqual("image_marker"); 17 | }); 18 | 19 | /** 20 | * Tests to see if some root properties have NOT been transferred. 21 | */ 22 | it("should not convert data, realtime, or analysis", () => { 23 | const newStructure = convert(oldWidget); 24 | 25 | expect(newStructure.data).toEqual(oldWidget.data); 26 | expect(newStructure.realtime).toBeFalsy(); 27 | expect(newStructure.analysis_run).toBeFalsy(); 28 | }); 29 | 30 | /** 31 | * Tests to see if display properties were transferred properly. 32 | */ 33 | it("converts display properties", () => { 34 | const newStructure = convert(oldWidget); 35 | 36 | expect(newStructure.display).not.toBeFalsy(); 37 | expect(newStructure.display.header_buttons).toEqual(oldWidget.display.header_buttons); 38 | expect(newStructure.display.help).toEqual(oldWidget.display.help); 39 | }); 40 | 41 | /** 42 | * Tests to see if the structure is old or new. 43 | */ 44 | it("correctly identifies if it's new structure or old", () => { 45 | const newStructure = convert(oldWidget); 46 | 47 | expect(isOldStructure(newStructure)).toEqual(false); 48 | expect(isOldStructure(oldWidget)).toEqual(true); 49 | }); 50 | 51 | /** 52 | * Test if the header buttons, or help buttons are undefined 53 | */ 54 | it("correctly identifies when header_buttons or help is undefined", () => { 55 | // Testing when is just row or column deleted 56 | const copyOfOld1 = Object.assign({ ...oldWidget }, {}); 57 | delete copyOfOld1.display.header_buttons; 58 | delete copyOfOld1.display.help; 59 | 60 | const newStructure1 = convert(copyOfOld1); 61 | expect(newStructure1.display).not.toBeFalsy(); 62 | expect(newStructure1.display.header_buttons).toEqual([]); 63 | expect(newStructure1.display.help).toEqual(""); 64 | expect(isOldStructure(newStructure1)).toEqual(false); 65 | }); 66 | }); 67 | -------------------------------------------------------------------------------- /__tests__/Migration/inputcontrol.ts: -------------------------------------------------------------------------------- 1 | import { convert, isOldStructure } from "../../src/modules/Migration/inputcontrol"; 2 | import * as oldStructure from "./__mocks__/widgetsOldStructure.json"; 3 | 4 | const oldWidget = oldStructure.inputcontrolStructure; 5 | 6 | describe("Input control widget - migration suite", () => { 7 | /** 8 | * Tests to see if the root properties have been transferred properly. 9 | */ 10 | it("converts base structure properly", () => { 11 | const newStructure = convert(oldWidget); 12 | 13 | expect(newStructure.label).toEqual(oldWidget.label); 14 | expect(newStructure.id).toEqual(oldWidget.id); 15 | expect(newStructure.dashboard).toEqual(oldWidget.dashboard); 16 | expect(newStructure.type).toEqual("control"); 17 | }); 18 | 19 | /** 20 | * Tests to see if some root properties have NOT been transferred. 21 | */ 22 | it("should not convert data, realtime, or analysis", () => { 23 | const newStructure = convert(oldWidget); 24 | 25 | expect(newStructure.data).toEqual(oldWidget.data); 26 | expect(newStructure.realtime).toBeFalsy(); 27 | expect(newStructure.analysis_run).toBeFalsy(); 28 | }); 29 | 30 | /** 31 | * Tests to see if display properties were transferred properly. 32 | */ 33 | it("converts display properties", () => { 34 | const newStructure = convert(oldWidget); 35 | 36 | expect(newStructure.display).not.toBeFalsy(); 37 | expect(newStructure.display.header_buttons).toEqual(oldWidget.display.header_buttons); 38 | expect(newStructure.display.help).toEqual(oldWidget.display.help); 39 | }); 40 | 41 | /** 42 | * Tests to see if the structure is old or new. 43 | */ 44 | it("correctly identifies if it's new structure or old", () => { 45 | const newStructure = convert(oldWidget); 46 | 47 | expect(isOldStructure(newStructure)).toEqual(false); 48 | expect(isOldStructure(oldWidget)).toEqual(true); 49 | }); 50 | 51 | /** 52 | * Test if the header buttons, or help buttons are undefined 53 | */ 54 | it("correctly identifies when header_buttons or help is undefined", () => { 55 | // Testing when is just row or column deleted 56 | const copyOfOld1 = Object.assign({ ...oldWidget }, {}); 57 | delete copyOfOld1.display.header_buttons; 58 | delete copyOfOld1.display.help; 59 | 60 | const newStructure1 = convert(copyOfOld1); 61 | expect(newStructure1.display).not.toBeFalsy(); 62 | expect(newStructure1.display.header_buttons).toEqual([]); 63 | expect(newStructure1.display.help).toEqual(""); 64 | expect(isOldStructure(newStructure1)).toEqual(false); 65 | }); 66 | }); 67 | -------------------------------------------------------------------------------- /__tests__/Migration/keypad.ts: -------------------------------------------------------------------------------- 1 | import { convert, isOldStructure } from "../../src/modules/Migration/keypad"; 2 | import * as oldStructure from "./__mocks__/widgetsOldStructure.json"; 3 | 4 | const oldWidget = oldStructure.keypadStructure; 5 | 6 | describe("Keypad widget - migration suite", () => { 7 | /** 8 | * Tests to see if the root properties have been transferred properly. 9 | */ 10 | it("converts base structure properly", () => { 11 | const newStructure = convert(oldWidget); 12 | 13 | expect(newStructure.label).toEqual(oldWidget.label); 14 | expect(newStructure.id).toEqual(oldWidget.id); 15 | expect(newStructure.dashboard).toEqual(oldWidget.dashboard); 16 | expect(newStructure.type).toEqual("keypad"); 17 | }); 18 | 19 | /** 20 | * Tests to see if some root properties have NOT been transferred. 21 | */ 22 | it("should not convert data, realtime, or analysis", () => { 23 | const newStructure = convert(oldWidget); 24 | 25 | expect(newStructure.data).toEqual(oldWidget.data); 26 | expect(newStructure.realtime).toBeFalsy(); 27 | expect(newStructure.analysis_run).toBeFalsy(); 28 | }); 29 | 30 | /** 31 | * Tests to see if display properties were transferred properly. 32 | */ 33 | it("converts display properties", () => { 34 | const newStructure = convert(oldWidget); 35 | 36 | expect(newStructure.display).not.toBeFalsy(); 37 | expect(newStructure.display.header_buttons).toEqual(oldWidget.display.header_buttons); 38 | expect(newStructure.display.help).toEqual(oldWidget.display.help); 39 | }); 40 | 41 | /** 42 | * Tests to see if the structure is old or new. 43 | */ 44 | it("correctly identifies if it's new structure or old", () => { 45 | const newStructure = convert(oldWidget); 46 | 47 | expect(isOldStructure(newStructure)).toEqual(false); 48 | expect(isOldStructure(oldWidget)).toEqual(true); 49 | }); 50 | 51 | /** 52 | * Test if the header buttons, or help buttons are undefined 53 | */ 54 | it("correctly identifies when header_buttons or help is undefined", () => { 55 | // Testing when is just row or column deleted 56 | const copyOfOld1 = Object.assign({ ...oldWidget }, {}); 57 | delete copyOfOld1.display.header_buttons; 58 | delete copyOfOld1.display.help; 59 | 60 | const newStructure1 = convert(copyOfOld1); 61 | expect(newStructure1.display).not.toBeFalsy(); 62 | expect(newStructure1.display.header_buttons).toEqual([]); 63 | expect(newStructure1.display.help).toEqual(""); 64 | expect(isOldStructure(newStructure1)).toEqual(false); 65 | }); 66 | }); 67 | -------------------------------------------------------------------------------- /__tests__/Migration/note.ts: -------------------------------------------------------------------------------- 1 | import { convert, isOldStructure } from "../../src/modules/Migration/note"; 2 | import * as oldStructure from "./__mocks__/widgetsOldStructure.json"; 3 | 4 | const oldWidget = oldStructure.noteStructure; 5 | 6 | describe("Note widget - migration suite", () => { 7 | /** 8 | * Tests to see if the root properties have been transferred properly. 9 | */ 10 | it("converts base structure properly", () => { 11 | const newStructure = convert(oldWidget); 12 | 13 | expect(newStructure.label).toEqual(oldWidget.label); 14 | expect(newStructure.id).toEqual(oldWidget.id); 15 | expect(newStructure.dashboard).toEqual(oldWidget.dashboard); 16 | expect(newStructure.type).toEqual("note"); 17 | }); 18 | 19 | /** 20 | * Tests to see if some root properties have NOT been transferred. 21 | */ 22 | it("should not convert data, realtime, or analysis", () => { 23 | const newStructure = convert(oldWidget); 24 | 25 | expect(newStructure.data).toEqual([]); 26 | expect(newStructure.realtime).toBeFalsy(); 27 | expect(newStructure.analysis_run).toBeFalsy(); 28 | }); 29 | 30 | /** 31 | * Tests to see if display properties were transferred properly. 32 | */ 33 | it("converts display properties", () => { 34 | const newStructure = convert(oldWidget); 35 | 36 | expect(newStructure.display).not.toBeFalsy(); 37 | expect(newStructure.display.value).toEqual(oldWidget.display.text); 38 | expect(newStructure.display.header_buttons).toEqual(oldWidget.display.header_buttons); 39 | expect(newStructure.display.help).toEqual(oldWidget.display.help); 40 | }); 41 | 42 | /** 43 | * Tests to see if the structure is old or new. 44 | */ 45 | it("correctly identifies if it's new structure or old", () => { 46 | const newStructure = convert(oldWidget); 47 | 48 | expect(isOldStructure(newStructure)).toEqual(false); 49 | expect(isOldStructure(oldWidget)).toEqual(true); 50 | }); 51 | 52 | /** 53 | * Test if the header buttons, or help buttons are undefined 54 | */ 55 | it("correctly identifies when header_buttons or help is undefined", () => { 56 | // Testing when is just row or column deleted 57 | const copyOfOld1 = Object.assign({ ...oldWidget }, {}); 58 | delete copyOfOld1.display.header_buttons; 59 | delete copyOfOld1.display.help; 60 | 61 | const newStructure1 = convert(copyOfOld1); 62 | expect(newStructure1.display).not.toBeFalsy(); 63 | expect(newStructure1.display.header_buttons).toEqual([]); 64 | expect(newStructure1.display.help).toEqual(""); 65 | expect(isOldStructure(newStructure1)).toEqual(false); 66 | }); 67 | }); 68 | -------------------------------------------------------------------------------- /__tests__/Migration/pushbutton.ts: -------------------------------------------------------------------------------- 1 | import { convert, isOldStructure } from "../../src/modules/Migration/pushbutton"; 2 | import * as oldStructure from "./__mocks__/widgetsOldStructure.json"; 3 | 4 | const oldWidget = oldStructure.pushbuttonStructure; 5 | 6 | describe("Push button widget - migration suite", () => { 7 | /** 8 | * Tests to see if the root properties have been transferred properly. 9 | */ 10 | it("converts base structure properly", () => { 11 | const newStructure = convert(oldWidget); 12 | 13 | expect(newStructure.label).toEqual(oldWidget.label); 14 | expect(newStructure.id).toEqual(oldWidget.id); 15 | expect(newStructure.dashboard).toEqual(oldWidget.dashboard); 16 | expect(newStructure.type).toEqual("push_button"); 17 | }); 18 | 19 | /** 20 | * Tests to see if some root properties have NOT been transferred. 21 | */ 22 | it("should not convert data, realtime, or analysis", () => { 23 | const newStructure = convert(oldWidget); 24 | 25 | expect(newStructure.data).toEqual(oldWidget.data); 26 | expect(newStructure.realtime).toBeFalsy(); 27 | expect(newStructure.analysis_run).toEqual(oldWidget?.analysis_run); 28 | }); 29 | 30 | /** 31 | * Tests to see if display properties were transferred properly. 32 | */ 33 | it("converts display properties", () => { 34 | const newStructure = convert(oldWidget); 35 | 36 | expect(newStructure.display).not.toBeFalsy(); 37 | expect(newStructure.display.header_buttons).toEqual(oldWidget.display.header_buttons); 38 | expect(newStructure.display.help).toEqual(oldWidget.display.help); 39 | }); 40 | 41 | /** 42 | * Tests to see if the structure is old or new. 43 | */ 44 | it("correctly identifies if it's new structure or old", () => { 45 | const newStructure = convert(oldWidget); 46 | 47 | expect(isOldStructure(newStructure)).toEqual(false); 48 | expect(isOldStructure(oldWidget)).toEqual(true); 49 | }); 50 | 51 | /** 52 | * Test if the header buttons, or help buttons are undefined 53 | */ 54 | it("correctly identifies when header_buttons or help is undefined", () => { 55 | // Testing when is just row or column deleted 56 | const copyOfOld1 = Object.assign({ ...oldWidget }, {}); 57 | delete copyOfOld1.display.header_buttons; 58 | delete copyOfOld1.display.help; 59 | 60 | const newStructure1 = convert(copyOfOld1); 61 | expect(newStructure1.display).not.toBeFalsy(); 62 | expect(newStructure1.display.header_buttons).toEqual([]); 63 | expect(newStructure1.display.help).toEqual(""); 64 | expect(isOldStructure(newStructure1)).toEqual(false); 65 | }); 66 | }); 67 | -------------------------------------------------------------------------------- /__tests__/Migration/removeHttpFromUrl.ts: -------------------------------------------------------------------------------- 1 | import removeHttpFromURL from "../../src/modules/Migration/common/removeHttpFromURL"; 2 | describe("removeHttpFromURL - migration suite", () => { 3 | it("removes HTTP(s) from URLs correctly", () => { 4 | const url = "https://tago.io"; 5 | const url2 = "http://tago.io"; 6 | const url3 = "tago.io"; 7 | 8 | expect(removeHttpFromURL(url)).toEqual("tago.io"); 9 | expect(removeHttpFromURL(url2)).toEqual("tago.io"); 10 | expect(removeHttpFromURL(url3)).toEqual("tago.io"); 11 | expect(removeHttpFromURL(undefined)).toEqual(""); 12 | expect(removeHttpFromURL("/")).toEqual(""); 13 | }); 14 | }); 15 | -------------------------------------------------------------------------------- /__tests__/Migration/tile.ts: -------------------------------------------------------------------------------- 1 | import { convert, isOldStructure } from "../../src/modules/Migration/tile"; 2 | import * as oldStructure from "./__mocks__/widgetsOldStructure.json"; 3 | 4 | const oldWidget = oldStructure.tileStructure; 5 | 6 | describe("Tile widget - migration suite", () => { 7 | /** 8 | * Tests to see if the root properties have been transferred properly. 9 | */ 10 | it("converts base structure properly", () => { 11 | const newStructure = convert(oldWidget); 12 | 13 | expect(newStructure.label).toEqual(oldWidget.label); 14 | expect(newStructure.id).toEqual(oldWidget.id); 15 | expect(newStructure.dashboard).toEqual(oldWidget.dashboard); 16 | expect(newStructure.type).toEqual("tile"); 17 | }); 18 | 19 | /** 20 | * Tests to see if some root properties have NOT been transferred. 21 | */ 22 | it("should not convert data, realtime, or analysis", () => { 23 | const newStructure = convert(oldWidget); 24 | 25 | expect(newStructure.data).toEqual([]); 26 | expect(newStructure.realtime).toBeFalsy(); 27 | expect(newStructure.analysis_run).toBeFalsy(); 28 | }); 29 | 30 | /** 31 | * Tests to see if display properties were transferred properly. 32 | */ 33 | it("converts display properties", () => { 34 | const newStructure = convert(oldWidget); 35 | 36 | expect(newStructure.display).not.toBeFalsy(); 37 | expect(newStructure.display.header_buttons).toEqual(oldWidget.display.header_buttons); 38 | expect(newStructure.display.help).toEqual(oldWidget.display.help); 39 | }); 40 | 41 | /** 42 | * Tests to see if the structure is old or new. 43 | */ 44 | it("correctly identifies if it's new structure or old", () => { 45 | const newStructure = convert(oldWidget); 46 | 47 | expect(isOldStructure(newStructure)).toEqual(false); 48 | expect(isOldStructure(oldWidget)).toEqual(true); 49 | }); 50 | 51 | /** 52 | * Test if the header buttons, or help buttons are undefined 53 | */ 54 | it("correctly identifies when header_buttons or help is undefined", () => { 55 | // Testing when is just row or column deleted 56 | const copyOfOld1 = Object.assign({ ...oldWidget }, {}); 57 | delete copyOfOld1.display.header_buttons; 58 | delete copyOfOld1.display.help; 59 | 60 | const newStructure1 = convert(copyOfOld1); 61 | expect(newStructure1.display).not.toBeFalsy(); 62 | expect(newStructure1.display.header_buttons).toEqual([]); 63 | expect(newStructure1.display.help).toEqual(""); 64 | expect(isOldStructure(newStructure1)).toEqual(false); 65 | }); 66 | }); 67 | -------------------------------------------------------------------------------- /__tests__/Migration/video.ts: -------------------------------------------------------------------------------- 1 | import { convert, isOldStructure } from "../../src/modules/Migration/video"; 2 | import * as oldStructure from "./__mocks__/widgetsOldStructure.json"; 3 | 4 | const oldWidget = oldStructure.videoStructure; 5 | 6 | describe("Video widget - migration suite", () => { 7 | /** 8 | * Tests to see if the root properties have been transferred properly. 9 | */ 10 | it("converts base structure properly", () => { 11 | const newStructure = convert(oldWidget); 12 | 13 | expect(newStructure.label).toEqual(oldWidget.label); 14 | expect(newStructure.id).toEqual(oldWidget.id); 15 | expect(newStructure.dashboard).toEqual(oldWidget.dashboard); 16 | expect(newStructure.type).toEqual("video"); 17 | }); 18 | 19 | /** 20 | * Tests to see if some root properties have NOT been transferred. 21 | */ 22 | it("should not convert data, realtime, or analysis", () => { 23 | const newStructure = convert(oldWidget); 24 | 25 | expect(newStructure.data).toEqual(oldWidget.data); 26 | expect(newStructure.realtime).toBeFalsy(); 27 | expect(newStructure.analysis_run).toBeFalsy(); 28 | }); 29 | 30 | /** 31 | * Tests to see if display properties were transferred properly. 32 | */ 33 | it("converts display properties", () => { 34 | const newStructure = convert(oldWidget); 35 | 36 | expect(newStructure.display).not.toBeFalsy(); 37 | expect(newStructure.display.header_buttons).toEqual(oldWidget.display.header_buttons); 38 | expect(newStructure.display.help).toEqual(oldWidget.display.help); 39 | }); 40 | 41 | /** 42 | * Tests to see if the structure is old or new. 43 | */ 44 | it("correctly identifies if it's new structure or old", () => { 45 | const newStructure = convert(oldWidget); 46 | 47 | expect(isOldStructure(newStructure)).toEqual(false); 48 | expect(isOldStructure(oldWidget)).toEqual(true); 49 | }); 50 | 51 | /** 52 | * Test if the header buttons, or help buttons are undefined 53 | */ 54 | it("correctly identifies when header_buttons or help is undefined", () => { 55 | // Testing when is just row or column deleted 56 | const copyOfOld1 = Object.assign({ ...oldWidget }, {}); 57 | delete copyOfOld1.display.header_buttons; 58 | delete copyOfOld1.display.help; 59 | 60 | const newStructure1 = convert(copyOfOld1); 61 | expect(newStructure1.display).not.toBeFalsy(); 62 | expect(newStructure1.display.header_buttons).toEqual([]); 63 | expect(newStructure1.display.help).toEqual(""); 64 | expect(isOldStructure(newStructure1)).toEqual(false); 65 | }); 66 | }); 67 | -------------------------------------------------------------------------------- /__tests__/Utils/dateParser.ts: -------------------------------------------------------------------------------- 1 | import dateParser from "../../src/modules/Utils/dateParser"; 2 | 3 | describe("Object parameter date parser", () => { 4 | test("Should work with data string", () => { 5 | let data = { 6 | a: "2020-09-28T18:10:21.000Z", 7 | b: "test", 8 | }; 9 | 10 | data = dateParser(data, ["a"]); 11 | // @ts-ignore 12 | expect(data.a instanceof Date).toBeTruthy(); 13 | expect(data.b).toEqual("test"); 14 | }); 15 | 16 | test('Should work with "never" value', () => { 17 | let data = { 18 | a: "2020-09-28T18:10:21.000Z", 19 | b: "never", 20 | }; 21 | 22 | data = dateParser(data, ["a", "b"]); 23 | // @ts-ignore 24 | expect(data.a instanceof Date).toBeTruthy(); 25 | expect(data.b).toEqual("never"); 26 | }); 27 | 28 | test("Should not parse non Date value", () => { 29 | let data = { 30 | a: "this is not a Date", 31 | b: "test", 32 | }; 33 | 34 | data = dateParser(data, ["a"]); 35 | // @ts-ignore 36 | expect(data.a instanceof Date).toBeFalsy(); 37 | expect(data.b).toEqual("test"); 38 | }); 39 | 40 | test("Input object should be different from output", () => { 41 | const dataInput = { 42 | a: "2020-09-28T18:10:21.000Z", 43 | }; 44 | 45 | const dataResult = dateParser(dataInput, ["a"]); 46 | // @ts-ignore 47 | expect(dataResult).not.toEqual(dataInput); 48 | }); 49 | }); 50 | -------------------------------------------------------------------------------- /__tests__/Utils/envToJson.ts: -------------------------------------------------------------------------------- 1 | import envToJson from "../../src/modules/Utils/envToJson"; 2 | 3 | describe("TagoIO Env to JSON", () => { 4 | test("Transform with success", () => { 5 | const envMock = [ 6 | { key: "test", value: "valueTest" }, 7 | { key: "test2", value: "valueTest2" }, 8 | ]; 9 | const result = envToJson(envMock); 10 | expect(result.test).toEqual("valueTest"); 11 | expect(result.test2).toEqual("valueTest2"); 12 | }); 13 | 14 | test("Transform with multiples values with same key", () => { 15 | const envMock = [ 16 | { key: "test", value: "valueTest" }, 17 | { key: "test", value: "valueTest3" }, 18 | ]; 19 | const result = envToJson(envMock); 20 | expect(result.test).toEqual("valueTest3"); 21 | }); 22 | }); 23 | -------------------------------------------------------------------------------- /__tests__/Utils/getAPIVersion.ts: -------------------------------------------------------------------------------- 1 | import express, { Express } from "express"; 2 | import http from "http"; 3 | import { AddressInfo } from "net"; 4 | import { Utils } from "../../src/modules"; 5 | 6 | jest.setTimeout(3000); 7 | 8 | describe("Get Api version", () => { 9 | let app: Express; 10 | let service: http.Server; 11 | beforeEach((done) => { 12 | const startServer = () => { 13 | app = express(); 14 | app.use(express.json()); 15 | service = app.listen(0); 16 | process.env.TAGOIO_API = `http://localhost:${(service.address() as AddressInfo).port}`; 17 | done(); 18 | }; 19 | 20 | if (service) { 21 | service.close(startServer); 22 | } else { 23 | startServer(); 24 | } 25 | }); 26 | 27 | afterAll((done) => { 28 | service.close(done); 29 | }); 30 | 31 | test("Sending data", async () => { 32 | let url: string; 33 | app.get("/status", (req, res) => { 34 | url = req.url; 35 | res.send({ status: true, result: { version: "x.x.x" } }); 36 | }); 37 | 38 | const result = await Utils.getAPIVersion("env"); 39 | 40 | expect(result).toBe("x.x.x"); 41 | expect(url).toBe("/status"); 42 | }); 43 | }); 44 | -------------------------------------------------------------------------------- /__tests__/Utils/parseLorawanQRcode.ts: -------------------------------------------------------------------------------- 1 | import { Account, Device, Utils } from "../../src/modules"; 2 | 3 | const func = async () => { 4 | return true; 5 | }; 6 | 7 | describe("Parse LoRaWAN QR Code", () => { 8 | test("8 Fields Success", async () => { 9 | const scope = [ 10 | { variable: "test", value: 1, origin: "", time: new Date(), serie: "123" }, 11 | { input_form_button_id: "122" }, 12 | ]; 13 | const qr_code = Utils.parseLorawanQRCode( 14 | "LW:D0:1122334455667788:AABBCCDDEEFF0011:AABB1122:OAABBCCDDEEFF:SYYWWNNNNNN:PFOOBAR:CAF2C" 15 | ); 16 | 17 | // @ts-ignore 18 | expect(qr_code.join_eui).toBe("1122334455667788"); 19 | expect(qr_code.dev_eui).toBe("AABBCCDDEEFF0011"); 20 | expect(qr_code.profile_id).toBe("AABB1122"); 21 | expect(qr_code.owner_token).toBe("OAABBCCDDEEFF"); 22 | expect(qr_code.sn_number).toBe("SYYWWNNNNNN"); 23 | expect(qr_code.proprietary).toBe("PFOOBAR"); 24 | expect(qr_code.checksum).toBe("CAF2C"); 25 | }); 26 | 27 | test("4 Fields Success", async () => { 28 | const scope = [ 29 | { variable: "test", value: 1, origin: "", time: new Date(), serie: "123" }, 30 | { input_form_button_id: "122" }, 31 | ]; 32 | const qr_code = Utils.parseLorawanQRCode("LW:D0:1122334455667788:AABBCCDDEEFF0011"); 33 | 34 | // @ts-ignore 35 | expect(qr_code.join_eui).toBe("1122334455667788"); 36 | expect(qr_code.dev_eui).toBe("AABBCCDDEEFF0011"); 37 | }); 38 | 39 | test("Invalid QR Code", async () => { 40 | const scope = [ 41 | { variable: "test", value: 1, origin: "", time: new Date(), serie: "123" }, 42 | { input_form_button_id: "122" }, 43 | ]; 44 | 45 | let qr_code: any; 46 | try { 47 | qr_code = Utils.parseLorawanQRCode("Test"); 48 | } catch (e) { 49 | expect(e).toBe("Invalid QR Code"); 50 | } 51 | 52 | // @ts-ignore 53 | expect(qr_code).toBeUndefined(); 54 | }); 55 | }); 56 | -------------------------------------------------------------------------------- /assets/favicon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tago-io/sdk-js/b6121e32768e23f3560a761af9e2e69d3d325b7e/assets/favicon.png -------------------------------------------------------------------------------- /jest.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | transform: { 3 | "^.+\\.(t|j)sx?$": ["@swc/jest"], 4 | }, 5 | testEnvironment: "node", 6 | modulePathIgnorePatterns: [".*/__mocks__/.*\\.ts"], 7 | }; 8 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@tago-io/sdk", 3 | "version": "11.3.10", 4 | "description": "TagoIO SDK for JavaScript in the browser and Node.js", 5 | "author": "TagoIO Inc.", 6 | "homepage": "https://tago.io", 7 | "license": "Apache-2.0", 8 | "repository": "tago-io/sdk-js.git", 9 | "main": "./lib/modules.js", 10 | "types": "./lib/modules.d.ts", 11 | "private": false, 12 | "keywords": [ 13 | "tago", 14 | "tagoio", 15 | "iot", 16 | "tago.io", 17 | "sdk", 18 | "analysis", 19 | "device", 20 | "tagoio", 21 | "tago-io" 22 | ], 23 | "engines": { 24 | "node": ">=16.0.0", 25 | "npm": ">=6.0.0" 26 | }, 27 | "scripts": { 28 | "prepublishOnly": "npm run build", 29 | "build": "rm -rf ./lib; node updateEnv.js; tsc", 30 | "linter": "eslint .", 31 | "test": "jest", 32 | "monacoTypes": "dts-bundle-generator -o docs/tago-io.sdk.d.ts src/modules.ts", 33 | "docs": "typedoc", 34 | "link": "yalc publish" 35 | }, 36 | "devDependencies": { 37 | "@swc/jest": "0.2.36", 38 | "@types/express": "4.17.21", 39 | "@types/jest": "29.5.12", 40 | "@types/node": "20.11.28", 41 | "@types/papaparse": "5.3.14", 42 | "@types/qs": "6.9.12", 43 | "@types/socket.io-client": "1.4.36", 44 | "@typescript-eslint/eslint-plugin": "7.2.0", 45 | "@typescript-eslint/parser": "7.2.0", 46 | "dts-bundle-generator": "9.3.1", 47 | "eslint": "8.57.0", 48 | "eslint-config-prettier": "9.1.0", 49 | "eslint-plugin-prettier": "5.1.3", 50 | "express": "4.18.3", 51 | "jest": "29.7.0", 52 | "prettier": "3.2.5", 53 | "typedoc": "0.25.12", 54 | "typedoc-plugin-extras": "3.0.0", 55 | "typedoc-plugin-missing-exports": "2.2.0", 56 | "typescript": "5.4.2" 57 | }, 58 | "dependencies": { 59 | "axios": "1.7.4", 60 | "form-data": "4.0.0", 61 | "nanoid": "3.3.7", 62 | "papaparse": "5.4.1", 63 | "qs": "6.12.0", 64 | "socket.io-client": "4.7.5" 65 | }, 66 | "optionalDependencies": { 67 | "eventsource": "4.0.0" 68 | } 69 | } 70 | -------------------------------------------------------------------------------- /src/common/Cache.ts: -------------------------------------------------------------------------------- 1 | import { AxiosRequestConfig } from "axios"; 2 | import { generateRequestID } from "./HashGenerator"; 3 | 4 | type requestID = number; 5 | type expireTimestamp = number; 6 | 7 | const cacheObj = new Map<[requestID, expireTimestamp], any>(); 8 | 9 | function clearCacheTTL() { 10 | for (const item of cacheObj.keys()) { 11 | if (item[1] < Date.now()) { 12 | cacheObj.delete(item); 13 | } 14 | } 15 | } 16 | 17 | function addCache(axiosObj: AxiosRequestConfig, obj: any, ttlMS = 5000) { 18 | clearCacheTTL(); 19 | cacheObj.set([generateRequestID(axiosObj), Date.now() + ttlMS], obj); 20 | } 21 | 22 | function getCache(axiosObj: AxiosRequestConfig): any { 23 | clearCacheTTL(); 24 | const key = generateRequestID(axiosObj); 25 | 26 | for (const item of cacheObj.keys()) { 27 | if (item[0] === key) { 28 | return cacheObj.get(item); 29 | } 30 | } 31 | 32 | return undefined; 33 | } 34 | 35 | function clearCache() { 36 | cacheObj.clear(); 37 | } 38 | 39 | export { addCache, getCache, clearCache }; 40 | -------------------------------------------------------------------------------- /src/common/HashGenerator.ts: -------------------------------------------------------------------------------- 1 | import { AxiosRequestConfig } from "axios"; 2 | 3 | function hashGenerator(obj: any): number { 4 | const objString = JSON.stringify(obj); 5 | 6 | let hash = 0; 7 | let chr; 8 | 9 | if (objString.length === 0) { 10 | return hash; 11 | } 12 | 13 | for (let i = 0; i < objString.length; i += 1) { 14 | chr = objString.charCodeAt(i); 15 | hash = (hash << 5) - hash + chr; 16 | hash |= 0; // Convert to 32bit integer 17 | } 18 | 19 | return hash; 20 | } 21 | 22 | function generateRequestID(axiosObj: AxiosRequestConfig): number { 23 | const objKey = { 24 | url: axiosObj.url, 25 | token: axiosObj.headers?.token, 26 | params: axiosObj.params, 27 | body: axiosObj.data, 28 | method: axiosObj.method, 29 | }; 30 | 31 | const requestID = hashGenerator(objKey); 32 | 33 | return requestID; 34 | } 35 | 36 | export { hashGenerator, generateRequestID }; 37 | -------------------------------------------------------------------------------- /src/common/JSONParseSafe.ts: -------------------------------------------------------------------------------- 1 | function JSONParseSafe(jsonString: string, defaultValue: any = {}) { 2 | if (!jsonString) { 3 | return defaultValue; 4 | } 5 | 6 | try { 7 | return JSON.parse(jsonString); 8 | } catch (error) { 9 | return defaultValue; 10 | } 11 | } 12 | 13 | export { JSONParseSafe }; 14 | -------------------------------------------------------------------------------- /src/common/RequestInProgress.ts: -------------------------------------------------------------------------------- 1 | import { AxiosRequestConfig } from "axios"; 2 | import { generateRequestID } from "./HashGenerator"; 3 | 4 | const requestsInProgress = new Set(); 5 | 6 | function addRequestInProgress(axiosObj: AxiosRequestConfig): void { 7 | requestsInProgress.add(generateRequestID(axiosObj)); 8 | } 9 | 10 | function removeRequestInProgress(axiosObj: AxiosRequestConfig): void { 11 | requestsInProgress.delete(generateRequestID(axiosObj)); 12 | } 13 | 14 | function isRequestInProgress(axiosObj: AxiosRequestConfig): boolean { 15 | return requestsInProgress.has(generateRequestID(axiosObj)); 16 | } 17 | 18 | export { addRequestInProgress, removeRequestInProgress, isRequestInProgress }; 19 | -------------------------------------------------------------------------------- /src/common/chunk.test.ts: -------------------------------------------------------------------------------- 1 | import { chunk } from "./chunk"; 2 | 3 | describe("chunk", () => { 4 | it("should divide an array into chunks of the specified size", () => { 5 | expect(chunk([1, 2, 3, 4, 5], 2)).toEqual([[1, 2], [3, 4], [5]]); 6 | expect(chunk([1, 2, 3, 4, 5, 6], 3)).toEqual([ 7 | [1, 2, 3], 8 | [4, 5, 6], 9 | ]); 10 | }); 11 | 12 | it("should return an array of empty arrays when size is less than 1", () => { 13 | expect(chunk([1, 2, 3, 4, 5], 0)).toEqual([[], [], [], [], []]); 14 | expect(chunk([1, 2, 3, 4, 5], -1)).toEqual([[], [], [], [], []]); 15 | }); 16 | 17 | it("should throw an error when input is not an array", () => { 18 | expect(() => chunk(123 as any, 2)).toThrow("Expected an array"); 19 | expect(() => chunk("string" as any, 2)).toThrow("Expected an array"); 20 | }); 21 | }); 22 | -------------------------------------------------------------------------------- /src/common/chunk.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * @internal 3 | */ 4 | function chunk(array: T[], size: number): T[][] { 5 | if (!Array.isArray(array)) { 6 | throw new TypeError("Expected an array"); 7 | } 8 | 9 | if (typeof size !== "number" || size < 1) { 10 | return array.map(() => []); 11 | } 12 | 13 | const chunked_arr: T[][] = []; 14 | for (let i = 0; i < array.length; i += size) { 15 | chunked_arr.push(array.slice(i, i + size)); 16 | } 17 | 18 | return chunked_arr; 19 | } 20 | 21 | export { chunk }; 22 | -------------------------------------------------------------------------------- /src/common/sleep.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Make the async function wait 3 | * @internal 4 | * @param timerMS Time to wait 5 | */ 6 | const sleep = (timerMS: number) => new Promise((resolve) => setTimeout(resolve, timerMS)); 7 | 8 | export default sleep; 9 | -------------------------------------------------------------------------------- /src/config.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Default configuration for SDK 3 | * @internal 4 | */ 5 | const config = { 6 | requestAttempts: Number(process.env.TAGOIO_REQUEST_ATTEMPTS) || 5, 7 | requestTimeout: 60000, 8 | socketOpts: { 9 | reconnectionDelay: 10000, 10 | reconnection: true, 11 | transports: ["websocket"], 12 | }, 13 | }; 14 | 15 | export default config; 16 | -------------------------------------------------------------------------------- /src/infrastructure/apiSSE.ts: -------------------------------------------------------------------------------- 1 | import type { GenericModuleParams } from "../common/TagoIOModule"; 2 | import regions from "../regions"; 3 | 4 | const channelsWithID = ["analysis_console", "device_inspector", "device_data", "entity_data", "ui_dashboard"] as const; 5 | const channelsWithoutID = ["analysis_trigger", "notification", "ui"] as const; 6 | const channels = [...channelsWithID, ...channelsWithoutID] as const; 7 | 8 | type ChannelWithID = (typeof channelsWithID)[number]; 9 | type ChannelWithoutID = (typeof channelsWithoutID)[number]; 10 | 11 | type OpenSSEWithID = { 12 | channel: ChannelWithID; 13 | resource_id: string; 14 | }; 15 | 16 | type OpenSSEWithoutID = { 17 | channel: ChannelWithoutID; 18 | }; 19 | 20 | type OpenSSEConfig = OpenSSEWithID | OpenSSEWithoutID; 21 | 22 | function isChannelWithID(params: OpenSSEConfig): params is OpenSSEWithID { 23 | return channelsWithID.includes(params.channel as ChannelWithID); 24 | } 25 | 26 | async function loadEventSourceLib(): Promise { 27 | if (globalThis.EventSource) { 28 | return globalThis.EventSource; 29 | } 30 | 31 | // @ts-expect-error EventSource types from DOMLib 32 | return import("eventsource").then((x) => x?.EventSource || x); 33 | } 34 | 35 | function formatChannel(configuration: OpenSSEConfig) { 36 | const { channel } = configuration; 37 | 38 | if (isChannelWithID(configuration)) { 39 | return `${channel}.${configuration.resource_id}`; 40 | } 41 | 42 | return channel; 43 | } 44 | 45 | async function openSSEListening(channels: OpenSSEConfig | OpenSSEConfig[], options: GenericModuleParams) { 46 | let channelsParam = ""; 47 | if (Array.isArray(channels)) { 48 | channelsParam = channels.map((channel) => formatChannel(channel)).join(","); 49 | } else { 50 | channelsParam = formatChannel(channels); 51 | } 52 | 53 | const url = new URL(regions(options.region).sse); 54 | url.pathname = "/events"; 55 | url.searchParams.set("channels", channelsParam); 56 | url.searchParams.set("token", options.token); 57 | 58 | const EventSource = await loadEventSourceLib(); 59 | const connection = new EventSource(url.toString()); 60 | 61 | return connection; 62 | } 63 | 64 | export { openSSEListening, channels }; 65 | export type { OpenSSEConfig }; 66 | -------------------------------------------------------------------------------- /src/infrastructure/apiSocket.ts: -------------------------------------------------------------------------------- 1 | import io from "socket.io-client"; 2 | import config from "../config"; 3 | import regions from "../regions"; 4 | import { GenericModuleParams } from "../common/TagoIOModule"; 5 | 6 | /** 7 | * TagoIO Socket Connection 8 | * In some cases you will should emit channels to subscribe/unsubscribe 9 | * example: 10 | * - socket.emit('attach', 'bucket', '5d30e5f8577736001b1a5e11'); 11 | * - socket.emit('unattach', 'bucket', '5d30e5f8577736001b1a5e11'); 12 | * @internal 13 | * @param params TagoIO Token and Region 14 | * @param socketIOExtraOptions SocketIO Extra Options 15 | */ 16 | function apiSocket(params: GenericModuleParams, socketIOExtraOptions = {}) { 17 | const socket = io.connect(regions(params.region).realtime, { 18 | ...config.socketOpts, 19 | query: { 20 | token: params.token, 21 | }, 22 | ...socketIOExtraOptions, 23 | }); 24 | 25 | return socket; 26 | } 27 | 28 | /** 29 | * @internal 30 | */ 31 | const channels = { 32 | notification: "notification::data", 33 | analysisConsole: "analysis::console", 34 | analysisTrigger: "analysis::trigger", 35 | bucketData: "bucket::data", 36 | }; 37 | 38 | export default apiSocket; 39 | export { channels }; 40 | -------------------------------------------------------------------------------- /src/infrastructure/envParams.json: -------------------------------------------------------------------------------- 1 | {"version": "11.3.2"} 2 | -------------------------------------------------------------------------------- /src/infrastructure/isBrowser.ts: -------------------------------------------------------------------------------- 1 | function checkIfItBrowser(): boolean { 2 | let isBrowser = true; 3 | 4 | if (typeof process === "object") { 5 | if (typeof process.versions === "object") { 6 | if (typeof process.versions.node !== "undefined") { 7 | isBrowser = false; 8 | } 9 | } 10 | } 11 | 12 | if (typeof window === "object") { 13 | // @ts-ignore 14 | if (typeof window.Deno === "object" && typeof window.document === "undefined") { 15 | isBrowser = false; 16 | } 17 | } 18 | 19 | return isBrowser; 20 | } 21 | 22 | export default checkIfItBrowser; 23 | -------------------------------------------------------------------------------- /src/modules.ts: -------------------------------------------------------------------------------- 1 | export { default as Device } from "./modules/Device/Device"; 2 | export { default as Analysis } from "./modules/Analysis/Analysis"; 3 | export { default as Account } from "./modules/Resources/AccountDeprecated"; 4 | export { default as Resources } from "./modules/Resources/Resources"; 5 | export { default as Services } from "./modules/Services/Services"; 6 | export { default as Network } from "./modules/Network/Network"; 7 | export { default as Authorization } from "./modules/Authorization/Authorization"; 8 | export { default as RunUser } from "./modules/RunUser/RunUser"; 9 | export { default as Dictionary } from "./modules/Dictionary/Dictionary"; 10 | export { default as Migration } from "./modules/Migration/Migration"; 11 | 12 | export * as Utils from "./modules/Utils/Utils"; 13 | export * as Cache from "./common/Cache"; 14 | export * as Types from "./types"; 15 | 16 | export * as SSE from "./infrastructure/apiSSE"; 17 | -------------------------------------------------------------------------------- /src/modules/Analysis/analysis.types.ts: -------------------------------------------------------------------------------- 1 | import { Regions, RegionsObj } from "../../regions"; 2 | 3 | type analysisFunction = (context: any, data: any) => any; 4 | 5 | interface AnalysisConstructorParams { 6 | token?: string; 7 | region?: Regions | RegionsObj; 8 | // options?: any; 9 | /** 10 | * Auto Start analysis after instance the class 11 | * If turn it off, you can start analysis calling [AnalysisInstance].start(); 12 | * [Default: true] 13 | */ 14 | autostart?: boolean; 15 | /** 16 | * Load TagoIO Analysis envs on process.env. 17 | * 18 | * Warning: It's not safe to use on external analysis 19 | * It will load all env on process, then if the external analysis receive multiples requests 20 | * simultaneous, it can mass up. 21 | * 22 | * [Default: false] 23 | */ 24 | loadEnvOnProcess?: boolean; 25 | } 26 | 27 | interface AnalysisEnvironment { 28 | [key: string]: string; 29 | } 30 | 31 | type AnalysisToken = string; 32 | type AnalysisID = string; 33 | /** 34 | * As current version of the SDK doesn't provide the TagoContext interface. 35 | */ 36 | interface TagoContext { 37 | token: AnalysisToken; 38 | analysis_id: AnalysisID; 39 | environment: AnalysisEnvironment[]; 40 | log: (...args: any[]) => void; 41 | } 42 | 43 | export { AnalysisConstructorParams, analysisFunction, AnalysisEnvironment, TagoContext }; 44 | -------------------------------------------------------------------------------- /src/modules/Authorization/Authorization.ts: -------------------------------------------------------------------------------- 1 | import TagoIOModule, { AuthorizationModuleParams } from "../../common/TagoIOModule"; 2 | import dateParser from "../Utils/dateParser"; 3 | import { AuthorizationInfo } from "./authorization.types"; 4 | 5 | class Authorization extends TagoIOModule { 6 | /** 7 | * Get information about the current Authorization 8 | */ 9 | public async info(): Promise { 10 | let result = await this.doRequest({ 11 | path: "/info", 12 | method: "GET", 13 | params: { 14 | details: this.params.details, 15 | }, 16 | }); 17 | 18 | result = dateParser(result, ["created_at", "expire_time", "last_authorization"]); 19 | return result; 20 | } 21 | } 22 | 23 | export default Authorization; 24 | -------------------------------------------------------------------------------- /src/modules/Authorization/authorization.types.ts: -------------------------------------------------------------------------------- 1 | import { PermissionOption, ExpireTimeOption, GenericID } from "../../common/common.types"; 2 | 3 | interface AuthorizationInfo { 4 | name: string; 5 | type: string; 6 | permission: PermissionOption; 7 | serie_number: string | null; 8 | last_authorization: Date | null; 9 | verification_code: string; 10 | expire_time: ExpireTimeOption; 11 | ref_id: GenericID; 12 | created_at: Date; 13 | created_by: string | null; 14 | } 15 | 16 | export { AuthorizationInfo }; 17 | -------------------------------------------------------------------------------- /src/modules/Dictionary/dictionary.types.ts: -------------------------------------------------------------------------------- 1 | import { GenericModuleParams } from "../../common/TagoIOModule"; 2 | import { Regions, RegionsObj } from "../../regions"; 3 | 4 | interface IDictionaryModuleParams extends GenericModuleParams { 5 | token: string; 6 | region?: Regions | RegionsObj; 7 | language?: string; 8 | options?: object; 9 | } 10 | 11 | interface IDictionaryModuleParamsAnonymous extends GenericModuleParams { 12 | runURL: string; 13 | region?: Regions | RegionsObj; 14 | language?: string; 15 | options?: object; 16 | } 17 | 18 | interface IParsedExpression { 19 | dictionary: string; 20 | key: string; 21 | params?: string[]; 22 | } 23 | 24 | interface IResolveExpressionParams { 25 | language: string; 26 | expression: IParsedExpression; 27 | } 28 | 29 | interface IApplyToStringOptions { 30 | language?: string; 31 | } 32 | 33 | export { 34 | IDictionaryModuleParams, 35 | IDictionaryModuleParamsAnonymous, 36 | IParsedExpression, 37 | IResolveExpressionParams, 38 | IApplyToStringOptions, 39 | }; 40 | -------------------------------------------------------------------------------- /src/modules/Migration/angular.ts: -------------------------------------------------------------------------------- 1 | // ? ==================================== (c) TagoIO ==================================== 2 | // * What is this file? 3 | // Migration of old ANGULAR to new ANGULAR. 4 | // ? ==================================================================================== 5 | 6 | import { WidgetInfo } from "../Resources/dashboards.types"; 7 | import convertFormula from "./common/convertFormula"; 8 | import convertRange from "./common/convertRange"; 9 | 10 | /** 11 | * Takes the OLD widget and returns the NEW structure. 12 | */ 13 | export function convert(oldWidget: any): WidgetInfo { 14 | const oldDisplay = oldWidget.display || {}; 15 | 16 | let decimalsString = ""; 17 | if (oldDisplay?.numberformat && typeof oldDisplay?.numberformat === "string") { 18 | decimalsString = oldDisplay?.numberformat?.split(".")?.[1] || ""; 19 | } 20 | const decimals = decimalsString.length || -1; 21 | 22 | const newStructure: any = { 23 | dashboard: oldWidget.dashboard, 24 | display: { 25 | alias: "", 26 | formula: { 27 | fixed_unit: oldDisplay?.unit, 28 | unit_type: oldDisplay?.unit ? "fixed" : "origin", 29 | }, 30 | header_buttons: oldDisplay.header_buttons || [], 31 | help: oldDisplay.help || "", 32 | number_format: { 33 | decimals, 34 | show_thousand: false, 35 | }, 36 | range: convertRange(oldDisplay), 37 | show_variables: !oldDisplay?.hide_variables, 38 | theme: { 39 | color: { 40 | background: null, 41 | fill: null, 42 | outline: null, 43 | text: null, 44 | text_border: null, 45 | }, 46 | }, 47 | }, 48 | id: oldWidget.id, 49 | label: oldWidget.label, 50 | realtime: null, 51 | type: "angular", 52 | }; 53 | if (Array.isArray(oldWidget.data)) { 54 | for (const item of oldWidget.data) { 55 | if (item?.is_hide) { 56 | continue; 57 | } 58 | for (const variable of item.variables) { 59 | const key = `${item?.origin}${variable}`; 60 | if (oldDisplay?.vars_format?.[key]) { 61 | newStructure.display.number_format = oldDisplay?.vars_format?.[key]; 62 | } 63 | // This is the formula variable 64 | if (oldDisplay.vars_formula?.[key]) { 65 | newStructure.display.formula = convertFormula(oldDisplay.vars_formula?.[key]); 66 | } 67 | if (oldDisplay?.vars_labels?.[key] && oldDisplay?.vars_labels?.[key] !== variable) { 68 | newStructure.display.alias = oldDisplay?.vars_labels?.[key]; 69 | } 70 | } 71 | } 72 | 73 | newStructure.data = oldWidget.data; 74 | } 75 | return newStructure; 76 | } 77 | 78 | /** 79 | * The note has the OLD structure if the display contains a TEXT property. 80 | */ 81 | export function isOldStructure(widget: any): boolean { 82 | const isOld = !!( 83 | widget?.display?.vars_labels || 84 | widget?.display?.vars_format || 85 | widget?.display?.vars_formula || 86 | widget?.display?.gauge_type 87 | ); 88 | 89 | return isOld; 90 | } 91 | -------------------------------------------------------------------------------- /src/modules/Migration/card.ts: -------------------------------------------------------------------------------- 1 | // ? ==================================== (c) TagoIO ==================================== 2 | // * What is this file? 3 | // Migration of old CARD to new CARD. 4 | // ? ==================================================================================== 5 | import { WidgetInfo } from "../Resources/dashboards.types"; 6 | import { removeHttpFromURL } from "./common/"; 7 | 8 | function convert(oldWidget: any): WidgetInfo { 9 | const oldDisplay = oldWidget.display || {}; 10 | 11 | let decimalsString = ""; 12 | if (oldDisplay?.numberformat && typeof oldDisplay?.numberformat === "string") { 13 | decimalsString = oldDisplay?.numberformat?.split(".")?.[1] || ""; 14 | } 15 | const decimals = decimalsString.length || -1; 16 | 17 | const newStructure: any = { 18 | dashboard: oldWidget.dashboard, 19 | display: { 20 | alias: "", 21 | formula: { 22 | enable: false, 23 | fixed_unit: oldDisplay?.unit || "", 24 | unit_type: "fixed", 25 | }, 26 | header_buttons: oldDisplay.header_buttons || [], 27 | help: oldDisplay.help || "", 28 | max_points: oldDisplay?.max_points || 5, 29 | number_format: { 30 | decimals, 31 | show_thousand: false, 32 | }, 33 | show_chart: !!oldDisplay?.show_chart, 34 | show_unit: !!oldDisplay?.show_unit, 35 | show_variables: !oldDisplay?.hide_variables, 36 | theme: { 37 | color: { 38 | background: oldDisplay?.conditions_background || [], 39 | chart: oldDisplay?.conditions_chart || [], 40 | text: oldDisplay?.conditions_value || [], 41 | }, 42 | }, 43 | url: removeHttpFromURL(oldDisplay?.url) || "", 44 | }, 45 | id: oldWidget.id, 46 | label: oldWidget.label, 47 | realtime: null, 48 | type: "card", 49 | }; 50 | 51 | if (Array.isArray(oldWidget.data)) { 52 | for (const item of oldWidget.data) { 53 | if (item?.is_hide) { 54 | continue; 55 | } 56 | for (const variable of item.variables) { 57 | const key = `${item?.origin}${variable}`; 58 | if (oldDisplay?.vars_format?.[key]) { 59 | newStructure.display.number_format = oldDisplay?.vars_format?.[key]; 60 | } 61 | 62 | if (oldDisplay?.vars_labels?.[key] && oldDisplay?.vars_labels?.[key] !== variable) { 63 | newStructure.display.alias = oldDisplay?.vars_labels?.[key]; 64 | } 65 | } 66 | } 67 | 68 | newStructure.data = oldWidget.data; 69 | } 70 | return newStructure; 71 | } 72 | 73 | function isOldStructure(widget: any): boolean { 74 | const isOld = !!(widget?.display?.vars_labels || widget?.display?.vars_format || widget?.display?.vars_formula); 75 | 76 | return isOld; 77 | } 78 | 79 | export { convert, isOldStructure }; 80 | -------------------------------------------------------------------------------- /src/modules/Migration/clock.ts: -------------------------------------------------------------------------------- 1 | // ? ==================================== (c) TagoIO ==================================== 2 | // * What is this file? 3 | // Migration of old CLOCK to new CLOCK. 4 | // ? ==================================================================================== 5 | 6 | import { WidgetInfo } from "../Resources/dashboards.types"; 7 | 8 | /** 9 | * Takes the OLD widget and returns the NEW structure. 10 | */ 11 | export function convert(oldWidget: any): WidgetInfo { 12 | const oldDisplay = oldWidget.display || {}; 13 | 14 | const newStructure: any = { 15 | dashboard: oldWidget.dashboard, 16 | display: { 17 | header_buttons: oldDisplay.header_buttons || [], 18 | help: oldDisplay.help || "", 19 | show_last_update: false, 20 | theme: { 21 | color: { 22 | background: null, 23 | fill: null, 24 | header: null, 25 | outline: null, 26 | text: null, 27 | text_border: null, 28 | }, 29 | }, 30 | }, 31 | id: oldWidget.id, 32 | label: oldWidget.label, 33 | realtime: null, 34 | type: "clock", 35 | }; 36 | 37 | newStructure.data = oldWidget.data; 38 | 39 | return newStructure; 40 | } 41 | 42 | /** 43 | * The note has the OLD structure if the display contains a TEXT property. 44 | */ 45 | export function isOldStructure(widget: any): boolean { 46 | const isOld = !!widget?.display?.gauge_type; 47 | 48 | return isOld; 49 | } 50 | -------------------------------------------------------------------------------- /src/modules/Migration/common/chartColors.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * These are the default line colors, once one is used the next one will be used 3 | * and then the next one, then the next... 4 | */ 5 | const chartColors: string[] = [ 6 | "hsl(210, 71%, 70%)", 7 | "hsl(240, 4%, 27%)", 8 | "hsl(110, 76%, 71%)", 9 | "hsl(26, 87%, 61%)", 10 | "hsl(237, 70%, 71%)", 11 | "hsl(346, 84%, 65%)", 12 | "hsl(53, 73%, 61%)", 13 | "hsl(179, 54%, 37%)", 14 | "hsl(0, 87%, 66%)", 15 | "hsl(175, 65%, 74%)", 16 | "hsl(0, 100%, 28%)", 17 | "hsl(191, 78%, 45%)", 18 | "hsl(267, 46%, 30%)", 19 | "hsl(217, 68%, 68%)", 20 | "hsl(0, 68%, 46%)", 21 | "hsl(82, 47%, 60%)", 22 | ]; 23 | 24 | export default chartColors; 25 | -------------------------------------------------------------------------------- /src/modules/Migration/common/convertDownsample.ts: -------------------------------------------------------------------------------- 1 | // ? ==================================== (c) TagoIO ==================================== 2 | // * What is this file? 3 | // Converts the OLD DOWNSAMPLE to new DOWNSAMPLE 4 | // ? ==================================================================================== 5 | 6 | function convertDownsample(oldWidgetDisplay: any): any { 7 | const enabled = oldWidgetDisplay?.downsample_enabled || false; 8 | const factor = oldWidgetDisplay?.downsample_factor || 10; 9 | const threshold = oldWidgetDisplay?.downsample_threshold || 1000; 10 | 11 | return { 12 | enabled, 13 | factor: Math.min(50, Number(factor)), // Case exceed the maximum value 14 | threshold: Math.min(10000, Number(threshold)), /// Case exceed the maximum value 15 | }; 16 | } 17 | 18 | export default convertDownsample; 19 | -------------------------------------------------------------------------------- /src/modules/Migration/common/convertFormula.ts: -------------------------------------------------------------------------------- 1 | // ? ==================================== (c) TagoIO ==================================== 2 | // * What is this file? 3 | // Converts the OLD FORMULA to BLUEPRINT FORMULA 4 | // ? ==================================================================================== 5 | 6 | function convertFormula(oldFormula: any): any { 7 | if (!oldFormula || Object.keys(oldFormula).length === 0) { 8 | return null; 9 | } 10 | 11 | const newFormula: any = { 12 | enable: oldFormula?.enable, 13 | formula_type: "fixed", 14 | unit_type: oldFormula?.unit_type, 15 | value: oldFormula?.formula, 16 | }; 17 | 18 | if (oldFormula.unit_type) { 19 | // fixed, variable, origin 20 | newFormula.unit_type = oldFormula.unit_type; 21 | } 22 | 23 | if (oldFormula.unit) { 24 | // fixed unit (F, C, ...) 25 | newFormula.fixed_unit = oldFormula.unit; 26 | } 27 | 28 | if (oldFormula?.formula_type === "dynamic") { 29 | // uses variable for formula instead of value 30 | newFormula.formula_type = "variable"; 31 | } 32 | 33 | if (oldFormula?.formula_variable) { 34 | const oldVariable = oldFormula.formula_variable; 35 | newFormula.variable = { 36 | origin: oldVariable.origin?.id || oldVariable?.origin, 37 | variable: oldVariable?.variable, 38 | }; 39 | 40 | if (typeof newFormula.variable.origin !== "string") { 41 | // origin has to be a string. If it's not, then something went wrong 42 | // during the formula conversion. 43 | newFormula.variable = null; 44 | } 45 | } 46 | 47 | return newFormula; 48 | } 49 | 50 | export default convertFormula; 51 | -------------------------------------------------------------------------------- /src/modules/Migration/common/convertInterval.ts: -------------------------------------------------------------------------------- 1 | // ? ==================================== (c) TagoIO ==================================== 2 | // * What is this file? 3 | // Converts the OLD INTERVAL to BLUEPRINT interval 4 | // ? ==================================================================================== 5 | 6 | function convertInterval(intv: string): string { 7 | return intv.replace("hra", "hour").replace("wek", "week").replace("mth", "month").replace("yer", "year"); 8 | } 9 | 10 | export default convertInterval; 11 | -------------------------------------------------------------------------------- /src/modules/Migration/common/convertRange.ts: -------------------------------------------------------------------------------- 1 | // ? ==================================== (c) TagoIO ==================================== 2 | // * What is this file? 3 | // Converts the OLD RANGE to BLUEPRINT RANGE 4 | // ? ==================================================================================== 5 | 6 | const isNumber = (s: any): boolean => { 7 | const str = ("" + s).trim(); 8 | if (str.length === 0) { 9 | return false; 10 | } 11 | return !isNaN(+str); 12 | }; 13 | 14 | function convertRange(oldWidgetDisplay: any): any { 15 | const range: any = {}; 16 | 17 | if (oldWidgetDisplay.range_limit === "metadata") { 18 | range.type = "metadata"; 19 | if (oldWidgetDisplay.range_limit_metadata === "custom") { 20 | range.metadata_origin = "variable"; 21 | const oldVariable = oldWidgetDisplay.range_limit_variable; 22 | const variable = { 23 | origin: oldVariable.origin?.id || oldVariable?.origin, 24 | variable: oldVariable?.variable, 25 | }; 26 | range.variable = variable; 27 | } else { 28 | range.metadata_origin = oldWidgetDisplay.range_limit_metadata === "origin" ? "original" : "formula"; 29 | } 30 | } else { 31 | range.type = "minmax"; 32 | const min = oldWidgetDisplay.minimum; 33 | const max = oldWidgetDisplay.maximum; 34 | range.minimum = min !== undefined && isNumber(min) ? Number(min) : 0; 35 | range.maximum = max !== undefined && isNumber(max) ? Number(max) : 100; 36 | } 37 | 38 | return range; 39 | } 40 | 41 | export default convertRange; 42 | -------------------------------------------------------------------------------- /src/modules/Migration/common/generateWidgetItemId.ts: -------------------------------------------------------------------------------- 1 | // ? ==================================== (c) TagoIO ==================================== 2 | // * What is this file? 3 | // Generates a random unique ID for a widget item in an array. 4 | // ? ==================================================================================== 5 | 6 | import { nanoid } from "nanoid"; 7 | 8 | function generateWidgetItemID(): string { 9 | const id = nanoid(); 10 | return id; 11 | } 12 | 13 | export default generateWidgetItemID; 14 | -------------------------------------------------------------------------------- /src/modules/Migration/common/index.ts: -------------------------------------------------------------------------------- 1 | import chartColors from "./chartColors"; 2 | import convertDownsample from "./convertDownsample"; 3 | import convertFormula from "./convertFormula"; 4 | import convertInterval from "./convertInterval"; 5 | import convertRange from "./convertRange"; 6 | import generateWidgetItemId from "./generateWidgetItemId"; 7 | import removeHttpFromURL from "./removeHttpFromURL"; 8 | 9 | export { 10 | chartColors, 11 | convertDownsample, 12 | convertFormula, 13 | convertInterval, 14 | convertRange, 15 | generateWidgetItemId, 16 | removeHttpFromURL, 17 | }; 18 | -------------------------------------------------------------------------------- /src/modules/Migration/common/removeHttpFromURL.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Remove HTTP and HTTPS from the URL on links to media and other assets. 3 | * Return an empty string if the value is not a string. 4 | */ 5 | export default function removeHttpFromURL(url: any): string { 6 | if (typeof url !== "string") { 7 | return ""; 8 | } 9 | 10 | let result: string = url.replace(/(^\w+:|^)\/\//, ""); 11 | 12 | if (result[result.length - 1] === "/") { 13 | result = result.substring(0, result.length - 1); 14 | } 15 | 16 | return result; 17 | } 18 | -------------------------------------------------------------------------------- /src/modules/Migration/custom.ts: -------------------------------------------------------------------------------- 1 | // ? ==================================== (c) TagoIO ==================================== 2 | // * What is this file? 3 | // Migration of old CUSTOM to new CUSTOM 4 | // ? ==================================================================================== 5 | import { WidgetInfo } from "../Resources/dashboards.types"; 6 | 7 | export function convert(oldWidget: any): WidgetInfo { 8 | const oldDisplay = oldWidget.display || {}; 9 | 10 | const newStructure: any = { 11 | analysis_run: oldWidget?.analysis_run, 12 | dashboard: oldWidget.dashboard, 13 | data: oldWidget?.data || [], 14 | display: { 15 | header_buttons: oldDisplay.header_buttons || [], 16 | header_size: oldDisplay?.header_size, 17 | help: oldDisplay.help || "", 18 | parameters: oldDisplay?.parameters || [], 19 | theme: { 20 | color: { 21 | background: null, 22 | }, 23 | }, 24 | url: oldDisplay?.url || "", 25 | user: oldDisplay?.user, 26 | variables: oldDisplay?.variables || [], 27 | }, 28 | id: oldWidget.id, 29 | label: oldWidget.label, 30 | realtime: null, 31 | type: "iframe", 32 | }; 33 | 34 | return newStructure; 35 | } 36 | 37 | export function isOldStructure(widget: any) { 38 | const isOld = !!(widget?.display?.watermark !== undefined); 39 | 40 | return isOld; 41 | } 42 | -------------------------------------------------------------------------------- /src/modules/Migration/cylinder.ts: -------------------------------------------------------------------------------- 1 | // ? ==================================== (c) TagoIO ==================================== 2 | // * What is this file? 3 | // Migration of old CYLINDER to new CYLINDER 4 | // ? ==================================================================================== 5 | import { WidgetInfo } from "../Resources/dashboards.types"; 6 | import { convertFormula, convertRange } from "./common"; 7 | 8 | export function convert(oldWidget: any): WidgetInfo { 9 | const oldDisplay = oldWidget.display || {}; 10 | 11 | let decimalsString = ""; 12 | if (oldDisplay?.numberformat && typeof oldDisplay?.numberformat === "string") { 13 | decimalsString = oldDisplay?.numberformat?.split(".")?.[1] || ""; 14 | } 15 | const decimals = decimalsString.length || -1; 16 | 17 | const conditions = 18 | Array.isArray(oldDisplay?.conditions) && oldDisplay?.conditions.length > 0 ? oldDisplay?.conditions : []; 19 | conditions.reverse(); 20 | 21 | const newStructure: any = { 22 | dashboard: oldWidget.dashboard, 23 | display: { 24 | alias: "", 25 | formula: { 26 | fixed_unit: oldDisplay?.unit, 27 | unit_type: oldDisplay?.unit ? "fixed" : "origin", 28 | }, 29 | header_buttons: oldDisplay.header_buttons || [], 30 | help: oldDisplay.help || "", 31 | number_format: { 32 | decimals, 33 | show_thousand: false, 34 | }, 35 | range: convertRange(oldDisplay), 36 | show_variables: !oldDisplay?.hide_variables, 37 | theme: { 38 | color: { 39 | background: null, 40 | fill: conditions, 41 | glass: null, 42 | header: null, 43 | text: null, 44 | tick: null, 45 | }, 46 | }, 47 | tick_amount: oldDisplay?.num_ticks || 5, 48 | }, 49 | id: oldWidget.id, 50 | label: oldWidget.label, 51 | realtime: null, 52 | type: "cylinder", 53 | }; 54 | // Tick amount should be number in all circumstance 55 | newStructure.display.tick_amount = Number(newStructure.display.tick_amount); 56 | 57 | if (Array.isArray(oldWidget.data)) { 58 | for (const item of oldWidget.data) { 59 | if (item?.is_hide) { 60 | continue; 61 | } 62 | for (const variable of item.variables) { 63 | const key = `${item?.origin}${variable}`; 64 | if (oldDisplay?.vars_format?.[key]) { 65 | newStructure.display.number_format = oldDisplay?.vars_format?.[key]; 66 | } 67 | // This is the formula variable 68 | if (oldDisplay.vars_formula?.[key]) { 69 | newStructure.display.formula = convertFormula(oldDisplay.vars_formula?.[key]); 70 | } 71 | if (oldDisplay?.vars_labels?.[key] && oldDisplay?.vars_labels?.[key] !== variable) { 72 | newStructure.display.alias = oldDisplay?.vars_labels?.[key]; 73 | } 74 | } 75 | } 76 | 77 | newStructure.data = oldWidget.data; 78 | } 79 | return newStructure; 80 | } 81 | 82 | export function isOldStructure(widget: any) { 83 | const isOld = !!(widget?.display?.vars_labels || widget?.display?.vars_format || widget?.display?.vars_formula); 84 | 85 | return isOld; 86 | } 87 | -------------------------------------------------------------------------------- /src/modules/Migration/dial.ts: -------------------------------------------------------------------------------- 1 | // ? ==================================== (c) TagoIO ==================================== 2 | // * What is this file? 3 | // Migration of old DIAL to new DIAL. 4 | // ? ==================================================================================== 5 | import { WidgetInfo } from "../Resources/dashboards.types"; 6 | import { convertFormula, convertRange } from "./common"; 7 | 8 | export function convert(oldWidget: any): WidgetInfo { 9 | const oldDisplay = oldWidget.display || {}; 10 | 11 | let decimalsString = ""; 12 | if (oldDisplay?.numberformat && typeof oldDisplay?.numberformat === "string") { 13 | decimalsString = oldDisplay?.numberformat?.split(".")?.[1] || ""; 14 | } 15 | const decimals = decimalsString.length || -1; 16 | 17 | const newStructure: any = { 18 | dashboard: oldWidget.dashboard, 19 | display: { 20 | alias: "", 21 | header_buttons: oldDisplay.header_buttons || [], 22 | help: oldDisplay.help || "", 23 | number_format: { 24 | decimals, 25 | show_thousand: false, 26 | }, 27 | range: convertRange(oldDisplay), 28 | show_variables: !oldDisplay?.hide_variables, 29 | theme: { 30 | color: { 31 | background: null, 32 | fill: null, 33 | outline: null, 34 | text: null, 35 | text_border: null, 36 | }, 37 | }, 38 | }, 39 | id: oldWidget.id, 40 | label: oldWidget.label, 41 | realtime: null, 42 | type: "dial", 43 | }; 44 | 45 | if (Array.isArray(oldWidget.data)) { 46 | for (const item of oldWidget.data) { 47 | if (item?.is_hide) { 48 | continue; 49 | } 50 | for (const variable of item.variables) { 51 | const key = `${item?.origin}${variable}`; 52 | if (oldDisplay?.vars_format?.[key]) { 53 | newStructure.display.number_format = oldDisplay?.vars_format?.[key]; 54 | } 55 | // This is the formula variable 56 | if (oldDisplay.vars_formula?.[key] && !newStructure.display?.formula) { 57 | newStructure.display.formula = convertFormula(oldDisplay.vars_formula?.[key]); 58 | } 59 | if (oldDisplay?.vars_labels?.[key] && oldDisplay?.vars_labels?.[key] !== variable) { 60 | newStructure.display.alias = oldDisplay?.vars_labels?.[key]; 61 | } 62 | } 63 | } 64 | 65 | if (!newStructure.display?.formula) { 66 | newStructure.display.formula = { 67 | fixed_unit: oldDisplay?.unit, 68 | unit_type: oldDisplay?.unit ? "fixed" : "origin", 69 | }; 70 | } 71 | 72 | newStructure.data = oldWidget.data; 73 | } 74 | return newStructure; 75 | } 76 | 77 | export function isOldStructure(widget: any) { 78 | const isOld = !!( 79 | widget?.display?.vars_labels || 80 | widget?.display?.vars_format || 81 | widget?.display?.vars_formula || 82 | widget?.display?.gauge_type 83 | ); 84 | 85 | return isOld; 86 | } 87 | -------------------------------------------------------------------------------- /src/modules/Migration/display.ts: -------------------------------------------------------------------------------- 1 | // ? ==================================== (c) TagoIO ==================================== 2 | // * What is this file? 3 | // Migration of old DISPLAY to new DISPLAY 4 | // ? ==================================================================================== 5 | import { WidgetInfo } from "../Resources/dashboards.types"; 6 | import convertFormula from "./common/convertFormula"; 7 | 8 | export function convert(oldWidget: any): WidgetInfo { 9 | const oldDisplay = oldWidget.display || {}; 10 | 11 | const newStructure: any = { 12 | dashboard: oldWidget.dashboard, 13 | display: { 14 | font_size: { 15 | type: "auto", 16 | }, 17 | header_buttons: oldDisplay.header_buttons || [], 18 | help: oldDisplay.help || "", 19 | show_units: true, 20 | show_variables: !oldDisplay.hide_variables, 21 | style: "default", 22 | variables: [], 23 | }, 24 | id: oldWidget.id, 25 | label: oldWidget.label, 26 | realtime: null, 27 | type: "display", 28 | }; 29 | 30 | if (Array.isArray(oldWidget.data)) { 31 | newStructure.data = oldWidget.data; // transfers the .data property 32 | 33 | newStructure.display.variables = []; // creates the variable array 34 | 35 | for (const item of oldWidget.data) { 36 | if (item.is_hide) { 37 | // is_hide items are not visible in the columns, so we cannot 38 | // put them in the variables array 39 | continue; 40 | } 41 | 42 | for (const variable of item.variables) { 43 | const key = `${item.origin}${variable}`; 44 | 45 | const alias = oldDisplay.vars_labels?.[key]; 46 | const numberFormat = oldDisplay.vars_format?.[key]; 47 | const formula = convertFormula(oldDisplay.vars_formula?.[key]); 48 | 49 | newStructure.display.variables.push({ 50 | origin: item.origin, 51 | variable, 52 | ...(alias ? { alias } : {}), 53 | ...(numberFormat ? { number_format: numberFormat } : {}), 54 | ...(formula ? { formula } : {}), 55 | }); 56 | } 57 | } 58 | } 59 | 60 | return newStructure; 61 | } 62 | 63 | export function isOldStructure(widget: any) { 64 | const isOld = !!( 65 | widget?.display?.vars_labels || 66 | widget?.display?.vars_format || 67 | widget?.display?.vars_formula || 68 | widget?.display?.numberformat || 69 | widget?.display?.column_alignments || 70 | widget?.display?.hide_variables !== undefined || 71 | widget?.display?.watermark !== undefined 72 | ); 73 | 74 | return isOld; 75 | } 76 | -------------------------------------------------------------------------------- /src/modules/Migration/grainbin.ts: -------------------------------------------------------------------------------- 1 | // ? ==================================== (c) TagoIO ==================================== 2 | // * What is this file? 3 | // Migration of old GRAINBIN to new GRAINBIN 4 | // ? ==================================================================================== 5 | import { WidgetInfo } from "../Resources/dashboards.types"; 6 | 7 | export function convert(oldWidget: any): WidgetInfo { 8 | const oldDisplay = oldWidget.display || {}; 9 | 10 | let decimalsString = ""; 11 | if (oldDisplay?.numberformat && typeof oldDisplay?.numberformat === "string") { 12 | decimalsString = oldDisplay?.numberformat?.split(".")?.[1] || ""; 13 | } 14 | const decimals = decimalsString.length || -1; 15 | 16 | const newStructure: any = { 17 | dashboard: oldWidget.dashboard, 18 | display: { 19 | alias: oldDisplay?.gauge_label || "", 20 | formula: { 21 | fixed_unit: oldDisplay?.unit, 22 | unit_type: oldDisplay?.unit ? "fixed" : "origin", 23 | }, 24 | header_buttons: oldDisplay.header_buttons || [], 25 | help: oldDisplay.help || "", 26 | number_format: { 27 | decimals, 28 | show_thousand: false, 29 | }, 30 | range: { 31 | maximum: oldDisplay?.maximum || 100, 32 | minimum: oldDisplay?.minimum || 0, 33 | type: "minmax", 34 | }, 35 | show_variables: !oldDisplay?.hide_variables, 36 | theme: { 37 | color: { 38 | background: null, 39 | fill: null, 40 | header: null, 41 | text: null, 42 | text_background: null, 43 | }, 44 | }, 45 | }, 46 | id: oldWidget.id, 47 | label: oldWidget.label, 48 | realtime: null, 49 | type: "grainbin", 50 | }; 51 | 52 | if (Array.isArray(oldWidget.data)) { 53 | newStructure.data = [oldWidget.data?.[0]] || []; 54 | } 55 | return newStructure; 56 | } 57 | 58 | export function isOldStructure(widget: any) { 59 | const isOld = !!( 60 | widget?.display?.gauge_label || 61 | widget?.display?.minimum || 62 | widget?.display?.maximum || 63 | widget?.display?.unit 64 | ); 65 | 66 | return isOld; 67 | } 68 | -------------------------------------------------------------------------------- /src/modules/Migration/heatmap.ts: -------------------------------------------------------------------------------- 1 | // ? ==================================== (c) TagoIO ==================================== 2 | // * What is this file? 3 | // Migration of old HEAT MAP to new HEAT MAP 4 | // ? ==================================================================================== 5 | import { WidgetInfo } from "../Resources/dashboards.types"; 6 | 7 | export function convert(oldWidget: any): WidgetInfo { 8 | const oldDisplay = oldWidget.display || {}; 9 | 10 | const newStructure: any = { 11 | dashboard: oldWidget.dashboard, 12 | display: { 13 | allow_zoom: false, 14 | header_buttons: oldDisplay.header_buttons || [], 15 | help: oldDisplay.help || "", 16 | max_points: oldDisplay?.max_points || 500, 17 | object_fit: "contain", 18 | occupy_whole_widget: false, 19 | radius: oldDisplay?.radius || 1, 20 | scale: { 21 | enable: !!oldDisplay?.scale_fixed, 22 | maximum: oldDisplay?.scale_maximum || 100, 23 | minimum: oldDisplay?.scale_minimum || 0, 24 | type: "fixed", 25 | }, 26 | show_coordinates: oldDisplay?.show_coordinates !== undefined ? oldDisplay?.show_coordinates : true, 27 | show_last_update: false, 28 | show_scale: oldDisplay?.show_scale !== undefined ? oldDisplay?.show_scale : true, 29 | show_zoom_buttons: false, 30 | source: { 31 | static_image: oldDisplay?.img_path || "", 32 | type: "static", 33 | }, 34 | theme: { 35 | color: { 36 | background: null, 37 | button_background: null, 38 | button_border: null, 39 | button_icon: null, 40 | header: null, 41 | text: null, 42 | text_background: null, 43 | text_border: null, 44 | }, 45 | timezone: { 46 | id: oldWidget?.data?.[0]?.timezone || "UTC", 47 | }, 48 | }, 49 | variables: [], 50 | }, 51 | id: oldWidget.id, 52 | label: oldWidget.label, 53 | realtime: null, 54 | type: "heat_map", 55 | }; 56 | 57 | if (Array.isArray(oldWidget.data)) { 58 | newStructure.data = oldWidget.data; // transfers the .data property 59 | 60 | newStructure.display.variables = []; // creates the variable array 61 | 62 | for (const item of oldWidget.data) { 63 | if (item.is_hide) { 64 | // is_hide items are not visible in the columns, so we cannot 65 | // put them in the variables array 66 | continue; 67 | } 68 | 69 | for (const variable of item.variables) { 70 | newStructure.display.variables.push({ 71 | origin: item.origin, 72 | overwrite_coordinates: false, 73 | variable, 74 | }); 75 | } 76 | } 77 | } 78 | 79 | return newStructure; 80 | } 81 | 82 | export function isOldStructure(widget: any) { 83 | const isOld = !!( 84 | widget?.display?.layer_type || 85 | widget?.display?.img_path || 86 | widget?.display?.scale_minimum || 87 | widget?.display?.scale_maximum || 88 | widget?.display?.scale_fixed || 89 | widget?.display?.watermark !== undefined 90 | ); 91 | 92 | return isOld; 93 | } 94 | -------------------------------------------------------------------------------- /src/modules/Migration/image.ts: -------------------------------------------------------------------------------- 1 | // ? ==================================== (c) TagoIO ==================================== 2 | // * What is this file? 3 | // Migration of old IMAGE to new IMAGE 4 | // ? ==================================================================================== 5 | import { WidgetInfo } from "../Resources/dashboards.types"; 6 | 7 | function convertSource(oldDisplay: any) { 8 | const { type_image } = oldDisplay; 9 | if (type_image === "static") { 10 | return { 11 | static_image: oldDisplay?.static_image || oldDisplay?.static_media, 12 | type: "static", 13 | }; 14 | } 15 | const oldConditions = oldDisplay?.conditions || []; 16 | return { 17 | conditions: oldConditions.map((e: any) => ({ 18 | condition: e?.condition, 19 | url: e?.image || e?.media_url, 20 | value: e?.value, 21 | })), 22 | type: "conditional", 23 | }; 24 | } 25 | /** 26 | * Takes the OLD widget and returns the NEW structure. 27 | */ 28 | export function convert(oldWidget: any): WidgetInfo { 29 | const oldDisplay = oldWidget.display || {}; 30 | 31 | const newStructure: any = { 32 | dashboard: oldWidget.dashboard, 33 | display: { 34 | allow_zoom: false, 35 | formula: { 36 | enable: false, 37 | formula_type: "fixed", 38 | unit_type: "origin", 39 | value: "", 40 | }, 41 | header_buttons: oldDisplay.header_buttons || [], 42 | help: oldDisplay.help || "", 43 | object_fit: "contain", 44 | occupy_whole_widget: false, 45 | show_zoom_buttons: false, 46 | source: convertSource(oldDisplay), 47 | theme: { 48 | color: { 49 | background: null, 50 | header: null, 51 | }, 52 | }, 53 | }, 54 | id: oldWidget.id, 55 | label: oldWidget.label, 56 | realtime: null, 57 | type: "image", 58 | }; 59 | 60 | if (Array.isArray(oldWidget.data)) { 61 | newStructure.data = oldWidget.data; 62 | } 63 | 64 | return newStructure; 65 | } 66 | 67 | export function isOldStructure(widget: any) { 68 | const isOld = !!( 69 | widget?.display?.type_media || 70 | widget?.display?.type_image || 71 | widget?.display?.static_media || 72 | widget?.display?.static_image || 73 | widget?.display?.conditions 74 | ); 75 | 76 | return isOld; 77 | } 78 | -------------------------------------------------------------------------------- /src/modules/Migration/inputcontrol.ts: -------------------------------------------------------------------------------- 1 | // ? ==================================== (c) TagoIO ==================================== 2 | // * What is this file? 3 | // Migration of old INPUT CONTROL to new INPUT CONTROL 4 | // ? ==================================================================================== 5 | import { WidgetInfo } from "../Resources/dashboards.types"; 6 | import { generateWidgetItemId } from "./common"; 7 | 8 | enum EInputControlFieldType { 9 | "Switch" = "switch", 10 | "Text" = "text", 11 | } 12 | 13 | export function convert(oldWidget: any): WidgetInfo { 14 | const oldDisplay = oldWidget.display || {}; 15 | 16 | const newStructure: any = { 17 | dashboard: oldWidget.dashboard, 18 | display: { 19 | header_buttons: oldDisplay.header_buttons || [], 20 | help: oldDisplay.help || "", 21 | sections: [], 22 | theme: { 23 | color: { 24 | background: null, 25 | field: null, 26 | footer: null, 27 | }, 28 | }, 29 | }, 30 | id: oldWidget.id, 31 | label: oldWidget.label, 32 | realtime: null, 33 | type: "control", 34 | }; 35 | 36 | const section: any = { 37 | description: "", 38 | fields: [], 39 | id: generateWidgetItemId(), 40 | show_border: false, 41 | show_caption: false, 42 | title: "", 43 | }; 44 | 45 | for (const oldField of oldDisplay?.controls || []) { 46 | const data = { 47 | ...(oldField?.bucket ? { bucket: oldField?.bucket } : {}), 48 | ...(oldField?.origin ? { origin: oldField?.origin } : {}), 49 | ...(oldField?.variable ? { variable: oldField?.variable } : {}), 50 | }; 51 | 52 | section.fields.push({ 53 | data, 54 | icon: null, 55 | id: generateWidgetItemId(), 56 | label: oldField?.name, 57 | label_selected: oldField?.label_yes || "", 58 | label_type: "text", 59 | label_unselected: oldField?.label_no || "", 60 | send_data: true, 61 | show_new_line: oldField?.new_line !== undefined ? oldField?.new_line : true, 62 | type: oldField?.type === "switch" ? EInputControlFieldType.Switch : EInputControlFieldType.Text, 63 | }); 64 | } 65 | 66 | newStructure.display.sections.push(section); 67 | if (Array.isArray(oldWidget.data)) { 68 | newStructure.data = oldWidget.data; 69 | } 70 | 71 | return newStructure; 72 | } 73 | 74 | export function isOldStructure(widget: any) { 75 | const isOld = !!(widget?.display?.watermark !== undefined || widget?.display?.input_type); 76 | 77 | return isOld; 78 | } 79 | -------------------------------------------------------------------------------- /src/modules/Migration/inputform.ts: -------------------------------------------------------------------------------- 1 | // ? ==================================== (c) TagoIO ==================================== 2 | // * What is this file? 3 | // Migration of old INPUT FORM to new INPUT FORM 4 | // ? ==================================================================================== 5 | import { WidgetInfo } from "../Resources/dashboards.types"; 6 | import { generateWidgetItemId } from "./common"; 7 | import { convertField, convertSubmitButton } from "./convertFields"; 8 | 9 | export function convert(oldWidget: any): WidgetInfo { 10 | const oldDisplay = oldWidget.display || {}; 11 | 12 | const newStructure: any = { 13 | dashboard: oldWidget.dashboard, 14 | display: { 15 | buttons: [convertSubmitButton(oldDisplay, oldWidget?.analysis_run)], 16 | header_buttons: oldDisplay.header_buttons || [], 17 | help: oldDisplay.help || "", 18 | sections: [], 19 | theme: { 20 | color: { 21 | background: null, 22 | field: null, 23 | header: null, 24 | }, 25 | }, 26 | }, 27 | id: oldWidget.id, 28 | label: oldWidget.label, 29 | realtime: null, 30 | type: "form", 31 | }; 32 | 33 | const section: any = { 34 | description: "", 35 | fields: [], 36 | id: generateWidgetItemId(), 37 | show_border: false, 38 | show_caption: false, 39 | title: "", 40 | }; 41 | 42 | const fieldsKeys = Object.keys(oldDisplay?.form?.fields || []); 43 | 44 | for (const key of fieldsKeys) { 45 | const oldField = oldDisplay?.form?.fields[key]; 46 | const visibilityConditions = 47 | oldField?.visibility_conditions && Array.isArray(oldField?.visibility_conditions) 48 | ? oldField?.visibility_conditions.map((e: any) => ({ 49 | condition: e?.condition, 50 | field: `${e?.variable}${e?.origin}`, 51 | value: e?.value, 52 | })) 53 | : []; 54 | 55 | /** 56 | * Old input form only use one field per variable, 57 | * so there is no problem to use his key as an id 58 | * when some fields use the id to reference it, it 59 | * will be easier to apply 60 | */ 61 | const field = { 62 | ...oldField, 63 | id: key, 64 | visibility_conditions: visibilityConditions, 65 | }; 66 | 67 | const convertedField = convertField(field, oldDisplay?.form?.show_map); 68 | 69 | if (convertedField) { 70 | section.fields.push(convertedField); 71 | } 72 | } 73 | 74 | newStructure.display.sections.push(section); 75 | 76 | if (Array.isArray(oldWidget.data)) { 77 | newStructure.data = oldWidget.data; 78 | } 79 | 80 | return newStructure; 81 | } 82 | 83 | export function isOldStructure(widget: any) { 84 | const isOld = !!( 85 | !widget?.display?.buttons || 86 | widget?.display?.form || 87 | widget?.display?.input_type || 88 | widget?.display?.bypass_bucket || 89 | widget?.display?.label_submit 90 | ); 91 | 92 | return isOld; 93 | } 94 | -------------------------------------------------------------------------------- /src/modules/Migration/keypad.ts: -------------------------------------------------------------------------------- 1 | // ? ==================================== (c) TagoIO ==================================== 2 | // * What is this file? 3 | // Migration of old KEYPAD to new KEYPAD 4 | // ? ==================================================================================== 5 | import { WidgetInfo } from "../Resources/dashboards.types"; 6 | 7 | export function convert(oldWidget: any): WidgetInfo { 8 | const oldDisplay = oldWidget.display || {}; 9 | 10 | const newStructure: any = { 11 | dashboard: oldWidget.dashboard, 12 | display: { 13 | buttons: oldDisplay?.buttons || [], 14 | bypass_bucket: oldDisplay?.bypass_bucket, 15 | header_buttons: oldDisplay.header_buttons || [], 16 | help: oldDisplay.help || "", 17 | message_variable: oldDisplay?.message_variable, 18 | play_audio: !!oldDisplay?.play_audio, 19 | send_run_user: !!oldDisplay?.send_run_user, 20 | show_asterisk: !!oldDisplay?.show_asterisk, 21 | show_digits_bar: !!oldDisplay?.show_digits_bar, 22 | show_last_column: !!oldDisplay?.show_last_column, 23 | show_last_row: !!oldDisplay?.show_last_row, 24 | show_number_sign: !!oldDisplay?.show_number_sign, 25 | theme: { 26 | color: { 27 | background: oldDisplay?.main_color, 28 | click: oldDisplay?.click_color, 29 | header: null, 30 | }, 31 | }, 32 | }, 33 | id: oldWidget.id, 34 | label: oldWidget.label, 35 | realtime: null, 36 | type: "keypad", 37 | }; 38 | 39 | if (Array.isArray(oldWidget.data)) { 40 | newStructure.data = oldWidget.data; 41 | } 42 | return newStructure; 43 | } 44 | 45 | export function isOldStructure(widget: any) { 46 | const isOld = !!(widget?.display?.watermark !== undefined || widget?.display?.click_color); 47 | 48 | return isOld; 49 | } 50 | -------------------------------------------------------------------------------- /src/modules/Migration/note.ts: -------------------------------------------------------------------------------- 1 | // ? ==================================== (c) TagoIO ==================================== 2 | // * What is this file? 3 | // Migration of old NOTE to new NOTE 4 | // ? ==================================================================================== 5 | import { WidgetInfo } from "../Resources/dashboards.types"; 6 | 7 | export function convert(oldWidget: any): WidgetInfo { 8 | const oldDisplay = oldWidget.display || {}; 9 | 10 | const newStructure: any = { 11 | dashboard: oldWidget.dashboard, 12 | data: [], 13 | display: { 14 | header_buttons: oldDisplay.header_buttons || [], 15 | help: oldDisplay.help || "", 16 | value: oldDisplay.text || "", 17 | }, 18 | id: oldWidget.id, 19 | label: oldWidget.label, 20 | realtime: null, 21 | type: "note", 22 | }; 23 | 24 | return newStructure; 25 | } 26 | 27 | export function isOldStructure(widget: any) { 28 | const isOld = !!widget?.display?.text; 29 | return isOld; 30 | } 31 | -------------------------------------------------------------------------------- /src/modules/Migration/pie.ts: -------------------------------------------------------------------------------- 1 | // ? ==================================== (c) TagoIO ==================================== 2 | // * What is this file? 3 | // Migration of old PIE to new PIE 4 | // ? ==================================================================================== 5 | import { WidgetInfo } from "../Resources/dashboards.types"; 6 | import { chartColors } from "./common"; 7 | 8 | export function convert(oldWidget: any): WidgetInfo { 9 | const oldDisplay = oldWidget.display || {}; 10 | 11 | const newStructure: any = { 12 | dashboard: oldWidget.dashboard, 13 | display: { 14 | header_buttons: oldDisplay.header_buttons || [], 15 | help: oldDisplay.help || "", 16 | show_legend: true, 17 | variables: [], 18 | }, 19 | id: oldWidget.id, 20 | label: oldWidget.label, 21 | realtime: null, 22 | type: "pie", 23 | }; 24 | 25 | if (Array.isArray(oldWidget.data)) { 26 | newStructure.data = oldWidget.data; // transfers the .data property 27 | 28 | newStructure.display.variables = []; // creates the variable array 29 | 30 | for (const item of oldWidget.data) { 31 | if (item.is_hide) { 32 | // is_hide items are not visible, so we cannot 33 | // put them in the variables array 34 | continue; 35 | } 36 | 37 | for (const variable of item?.variables) { 38 | const key = `${item.origin}${variable}`; 39 | 40 | const alias = oldDisplay.vars_labels?.[key]; 41 | const numberFormat = oldDisplay.vars_format?.[key]; 42 | const color = 43 | chartColors.find((e) => { 44 | return !newStructure.display.variables.find((v: any) => v?.color === e); 45 | }) || "#999"; 46 | 47 | newStructure.display.variables.push({ 48 | color, 49 | origin: item.origin, 50 | variable, 51 | ...(alias ? { alias } : {}), 52 | ...(numberFormat ? { number_format: numberFormat } : {}), 53 | }); 54 | } 55 | } 56 | } 57 | 58 | return newStructure; 59 | } 60 | 61 | export function isOldStructure(widget: any) { 62 | const isOld = !!( 63 | widget?.display?.vars_labels || 64 | widget?.display?.vars_format || 65 | widget?.display?.numberformat || 66 | widget?.display?.hide_variables !== undefined || 67 | widget?.display?.watermark !== undefined || 68 | widget?.display?.pie_type 69 | ); 70 | 71 | return isOld; 72 | } 73 | -------------------------------------------------------------------------------- /src/modules/Migration/pushbutton.ts: -------------------------------------------------------------------------------- 1 | // ? ==================================== (c) TagoIO ==================================== 2 | // * What is this file? 3 | // Migration of old PUSH BUTTON to new PUSH BUTTON 4 | // ? ==================================================================================== 5 | import { WidgetInfo } from "../Resources/dashboards.types"; 6 | 7 | function convertState(oldState: any) { 8 | const newState = { 9 | ...oldState, 10 | type: oldState?.type || "text", 11 | color: oldState?.text_color || "", 12 | }; 13 | delete newState.text_color; 14 | 15 | return newState; 16 | } 17 | 18 | export function convert(oldWidget: any): WidgetInfo { 19 | const oldDisplay = oldWidget.display || {}; 20 | 21 | const newStructure: any = { 22 | analysis_run: oldWidget?.analysis_run, 23 | dashboard: oldWidget.dashboard, 24 | display: { 25 | alias: "", 26 | button_type: oldDisplay?.button_type === "bi-stable" ? "bistable" : "monostable", 27 | header_buttons: oldDisplay.header_buttons || [], 28 | help: oldDisplay.help || "", 29 | show_variables: !oldDisplay?.hide_variables, 30 | state_one: convertState(oldDisplay?.state_one), 31 | state_two: convertState(oldDisplay?.state_two), 32 | theme: { 33 | color: { 34 | background: null, 35 | text: null, 36 | }, 37 | }, 38 | }, 39 | id: oldWidget.id, 40 | label: oldWidget.label, 41 | realtime: null, 42 | type: "push_button", 43 | }; 44 | 45 | if (Array.isArray(oldWidget.data)) { 46 | for (const item of oldWidget.data) { 47 | if (item?.is_hide) { 48 | continue; 49 | } 50 | for (const variable of item.variables) { 51 | const key = `${item?.origin}${variable}`; 52 | if (oldDisplay?.vars_labels?.[key] && oldDisplay?.vars_labels?.[key] !== variable) { 53 | newStructure.display.alias = oldDisplay?.vars_labels?.[key]; 54 | } 55 | } 56 | } 57 | 58 | newStructure.data = oldWidget.data; 59 | } 60 | return newStructure; 61 | } 62 | 63 | export function isOldStructure(widget: any) { 64 | const isOld = !!(widget?.display?.vars_labels || widget?.display?.vars_format || widget?.display?.vars_formula); 65 | 66 | return isOld; 67 | } 68 | -------------------------------------------------------------------------------- /src/modules/Migration/semidonut.ts: -------------------------------------------------------------------------------- 1 | // ? ==================================== (c) TagoIO ==================================== 2 | // * What is this file? 3 | // Migration of old SEMIDONUT to new SEMIDONUT 4 | // ? ==================================================================================== 5 | import { WidgetInfo } from "../Resources/dashboards.types"; 6 | import { chartColors } from "./common"; 7 | 8 | export function convert(oldWidget: any): WidgetInfo { 9 | const oldDisplay = oldWidget.display || {}; 10 | 11 | const newStructure: any = { 12 | dashboard: oldWidget.dashboard, 13 | display: { 14 | header_buttons: oldDisplay.header_buttons || [], 15 | help: oldDisplay.help || "", 16 | show_legend: true, 17 | variables: [], 18 | }, 19 | id: oldWidget.id, 20 | label: oldWidget.label, 21 | realtime: null, 22 | type: "semidonut", 23 | }; 24 | 25 | if (Array.isArray(oldWidget.data)) { 26 | newStructure.data = oldWidget.data; // transfers the .data property 27 | 28 | newStructure.display.variables = []; // creates the variable array 29 | 30 | for (const item of oldWidget.data) { 31 | if (item.is_hide) { 32 | // is_hide items are not visible, so we cannot 33 | // put them in the variables array 34 | continue; 35 | } 36 | 37 | for (const variable of item?.variables) { 38 | const key = `${item.origin}${variable}`; 39 | 40 | const alias = oldDisplay.vars_labels?.[key]; 41 | const numberFormat = oldDisplay.vars_format?.[key]; 42 | const color = 43 | chartColors.find((e) => { 44 | return !newStructure.display.variables.find((v: any) => v?.color === e); 45 | }) || "#999"; 46 | 47 | newStructure.display.variables.push({ 48 | color, 49 | origin: item.origin, 50 | variable, 51 | ...(alias ? { alias } : {}), 52 | ...(numberFormat ? { number_format: numberFormat } : {}), 53 | }); 54 | } 55 | } 56 | } 57 | 58 | return newStructure; 59 | } 60 | 61 | export function isOldStructure(widget: any) { 62 | const isOld = !!( 63 | widget?.display?.vars_labels || 64 | widget?.display?.vars_format || 65 | widget?.display?.numberformat || 66 | widget?.display?.hide_variables !== undefined || 67 | widget?.display?.watermark !== undefined || 68 | widget?.display?.pie_type 69 | ); 70 | 71 | return isOld; 72 | } 73 | -------------------------------------------------------------------------------- /src/modules/Migration/solid.ts: -------------------------------------------------------------------------------- 1 | // ? ==================================== (c) TagoIO ==================================== 2 | // * What is this file? 3 | // Migration of old SOLID to new SOLID 4 | // ? ==================================================================================== 5 | import { WidgetInfo } from "../Resources/dashboards.types"; 6 | import { convertFormula, convertRange } from "./common"; 7 | 8 | export function convert(oldWidget: any): WidgetInfo { 9 | const oldDisplay = oldWidget.display || {}; 10 | 11 | let decimalsString = ""; 12 | if (oldDisplay?.numberformat && typeof oldDisplay?.numberformat === "string") { 13 | decimalsString = oldDisplay?.numberformat?.split(".")?.[1] || ""; 14 | } 15 | const decimals = decimalsString.length || -1; 16 | 17 | const newStructure: any = { 18 | dashboard: oldWidget.dashboard, 19 | display: { 20 | alias: "", 21 | formula: { 22 | fixed_unit: oldDisplay?.unit, 23 | unit_type: oldDisplay?.unit ? "fixed" : "origin", 24 | }, 25 | header_buttons: oldDisplay.header_buttons || [], 26 | help: oldDisplay.help || "", 27 | number_format: { 28 | decimals, 29 | show_thousand: false, 30 | }, 31 | range: convertRange(oldDisplay), 32 | show_variables: !oldDisplay?.hide_variables, 33 | theme: { 34 | color: { 35 | background: null, 36 | fill: null, 37 | outline: null, 38 | text: null, 39 | text_border: null, 40 | }, 41 | }, 42 | }, 43 | id: oldWidget.id, 44 | label: oldWidget.label, 45 | realtime: null, 46 | type: "solid", 47 | }; 48 | if (Array.isArray(oldWidget.data)) { 49 | for (const item of oldWidget.data) { 50 | if (item?.is_hide) { 51 | continue; 52 | } 53 | for (const variable of item.variables) { 54 | const key = `${item?.origin}${variable}`; 55 | if (oldDisplay?.vars_format?.[key]) { 56 | newStructure.display.number_format = oldDisplay?.vars_format?.[key]; 57 | } 58 | // This is the formula variable 59 | if (oldDisplay.vars_formula?.[key]) { 60 | newStructure.display.formula = convertFormula(oldDisplay.vars_formula?.[key]); 61 | } 62 | if (oldDisplay?.vars_labels?.[key] && oldDisplay?.vars_labels?.[key] !== variable) { 63 | newStructure.display.alias = oldDisplay?.vars_labels?.[key]; 64 | } 65 | } 66 | } 67 | 68 | newStructure.data = oldWidget.data; 69 | } 70 | return newStructure; 71 | } 72 | 73 | export function isOldStructure(widget: any) { 74 | const isOld = !!( 75 | widget?.display?.vars_labels || 76 | widget?.display?.vars_format || 77 | widget?.display?.vars_formula || 78 | widget?.display?.gauge_type 79 | ); 80 | 81 | return isOld; 82 | } 83 | -------------------------------------------------------------------------------- /src/modules/Migration/stepbutton.ts: -------------------------------------------------------------------------------- 1 | // ? ==================================== (c) TagoIO ==================================== 2 | // * What is this file? 3 | // Migration of old STEP BUTTON to new STEP BUTTON 4 | // ? ==================================================================================== 5 | import { WidgetInfo } from "../Resources/dashboards.types"; 6 | 7 | export function convert(oldWidget: any): WidgetInfo { 8 | const oldDisplay = oldWidget.display || {}; 9 | 10 | const newStructure: any = { 11 | dashboard: oldWidget.dashboard, 12 | display: { 13 | alias: "", 14 | header_buttons: oldDisplay.header_buttons || [], 15 | help: oldDisplay.help || "", 16 | hour_visualization: oldDisplay?.clock_visualization || "", 17 | limit: { 18 | enabled: !!oldDisplay?.enable_limit, 19 | maximum: oldDisplay?.maximum || 100, 20 | minimum: oldDisplay?.minimum || 0, 21 | }, 22 | number_format: { 23 | decimals: -1, 24 | show_thousand: false, 25 | }, 26 | show_unit: !!oldDisplay?.show_unit, 27 | show_variables: !oldDisplay?.hide_variables, 28 | step_value: oldDisplay?.increment || 1, 29 | theme: { 30 | color: { 31 | background: null, 32 | button: oldDisplay?.conditions_button, 33 | text: null, 34 | value: oldDisplay?.conditions_text, 35 | }, 36 | }, 37 | type: oldDisplay?.input_type, 38 | unit: oldDisplay?.unit || "", 39 | }, 40 | id: oldWidget.id, 41 | label: oldWidget.label, 42 | realtime: null, 43 | type: "step_button", 44 | }; 45 | 46 | if (Array.isArray(oldWidget.data)) { 47 | for (const item of oldWidget.data) { 48 | if (item?.is_hide) { 49 | continue; 50 | } 51 | for (const variable of item.variables) { 52 | const key = `${item?.origin}${variable}`; 53 | if (oldDisplay?.vars_format?.[key]) { 54 | newStructure.display.number_format = oldDisplay?.vars_format?.[key]; 55 | } 56 | 57 | if (oldDisplay?.vars_labels?.[key] && oldDisplay?.vars_labels?.[key] !== variable) { 58 | newStructure.display.alias = oldDisplay?.vars_labels?.[key]; 59 | } 60 | } 61 | } 62 | 63 | newStructure.data = oldWidget.data; 64 | } 65 | return newStructure; 66 | } 67 | 68 | export function isOldStructure(widget: any) { 69 | const isOld = !!( 70 | widget?.display?.input_type || 71 | widget?.display?.vars_labels || 72 | widget?.display?.vars_format || 73 | widget?.display?.conditions_button 74 | ); 75 | 76 | return isOld; 77 | } 78 | -------------------------------------------------------------------------------- /src/modules/Migration/tile.ts: -------------------------------------------------------------------------------- 1 | // ? ==================================== (c) TagoIO ==================================== 2 | // * What is this file? 3 | // Migration of old TILE to new TILE 4 | // ? ==================================================================================== 5 | import { WidgetInfo } from "../Resources/dashboards.types"; 6 | import { removeHttpFromURL } from "./common"; 7 | 8 | export function convert(oldWidget: any): WidgetInfo { 9 | const oldDisplay = oldWidget.display || {}; 10 | 11 | const newStructure: any = { 12 | dashboard: oldWidget.dashboard, 13 | data: [], 14 | display: { 15 | header_buttons: oldDisplay.header_buttons || [], 16 | help: oldDisplay.help || "", 17 | image_type: oldDisplay?.icon_color ? "icon" : "image", 18 | image_url: oldDisplay?.icon_url || "", 19 | occupy_whole_widget: oldDisplay?.fit_image, 20 | opacity: oldDisplay?.opacity, 21 | theme: { 22 | color: { 23 | background: oldDisplay?.background_color || null, 24 | header: null, 25 | hover: null, 26 | icon: oldDisplay?.icon_color || null, 27 | title: oldDisplay?.label_color || null, 28 | }, 29 | }, 30 | title: oldDisplay?.label_button || "", 31 | url: removeHttpFromURL(oldDisplay?.link) || "", 32 | }, 33 | id: oldWidget.id, 34 | label: oldWidget.label, 35 | realtime: null, 36 | type: "tile", 37 | }; 38 | 39 | return newStructure; 40 | } 41 | 42 | export function isOldStructure(widget: any) { 43 | const isOld = !!( 44 | widget?.display?.background_color || 45 | widget?.display?.fit_image || 46 | widget?.display?.label_color || 47 | widget?.display?.label_button || 48 | widget?.display?.link 49 | ); 50 | 51 | return isOld; 52 | } 53 | -------------------------------------------------------------------------------- /src/modules/Migration/video.ts: -------------------------------------------------------------------------------- 1 | // ? ==================================== (c) TagoIO ==================================== 2 | // * What is this file? 3 | // Migration of old VIDEO to new VIDEO 4 | // ? ==================================================================================== 5 | import { WidgetInfo } from "../Resources/dashboards.types"; 6 | 7 | function convertSource(oldDisplay: any) { 8 | return { 9 | static_video: oldDisplay?.static_media || oldDisplay?.static_image, 10 | type: "static", 11 | }; 12 | } 13 | 14 | export function convert(oldWidget: any): WidgetInfo { 15 | const oldDisplay = oldWidget.display || {}; 16 | 17 | const newStructure: any = { 18 | dashboard: oldWidget.dashboard, 19 | display: { 20 | auto_play: true, 21 | header_buttons: oldDisplay.header_buttons || [], 22 | help: oldDisplay.help || "", 23 | initially_muted: true, 24 | loop: true, 25 | object_fit: "contain", 26 | occupy_whole_widget: false, 27 | show_controls: true, 28 | source: convertSource(oldDisplay), 29 | theme: { 30 | color: { 31 | background: null, 32 | header: null, 33 | }, 34 | }, 35 | }, 36 | id: oldWidget.id, 37 | label: oldWidget.label, 38 | realtime: null, 39 | type: "video", 40 | }; 41 | 42 | if (Array.isArray(oldWidget.data)) { 43 | newStructure.data = oldWidget.data; 44 | } 45 | 46 | return newStructure; 47 | } 48 | 49 | export function isOldStructure(widget: any) { 50 | const isOld = !!( 51 | widget?.display?.type_media || 52 | widget?.display?.type_image || 53 | widget?.display?.static_media || 54 | widget?.display?.static_image || 55 | widget?.display?.conditions 56 | ); 57 | 58 | return isOld; 59 | } 60 | -------------------------------------------------------------------------------- /src/modules/Migration/vumeter.ts: -------------------------------------------------------------------------------- 1 | // ? ==================================== (c) TagoIO ==================================== 2 | // * What is this file? 3 | // Migration of old VUMETER to new VUMETER 4 | // ? ==================================================================================== 5 | import { WidgetInfo } from "../Resources/dashboards.types"; 6 | import { convertFormula, convertRange } from "./common"; 7 | 8 | export function convert(oldWidget: any): WidgetInfo { 9 | const oldDisplay = oldWidget.display || {}; 10 | 11 | let decimalsString = ""; 12 | if (oldDisplay?.numberformat && typeof oldDisplay?.numberformat === "string") { 13 | decimalsString = oldDisplay?.numberformat?.split(".")?.[1] || ""; 14 | } 15 | const decimals = decimalsString.length || -1; 16 | 17 | const newStructure: any = { 18 | dashboard: oldWidget.dashboard, 19 | display: { 20 | alias: "", 21 | formula: { 22 | fixed_unit: oldDisplay?.unit, 23 | unit_type: oldDisplay?.unit ? "fixed" : "origin", 24 | }, 25 | header_buttons: oldDisplay.header_buttons || [], 26 | help: oldDisplay.help || "", 27 | number_format: { 28 | decimals, 29 | show_thousand: false, 30 | }, 31 | range: convertRange(oldDisplay), 32 | show_variables: !oldDisplay?.hide_variables, 33 | theme: { 34 | color: { 35 | background: null, 36 | fill: null, 37 | outline: null, 38 | text: null, 39 | text_border: null, 40 | }, 41 | }, 42 | }, 43 | id: oldWidget.id, 44 | label: oldWidget.label, 45 | realtime: null, 46 | type: "vu-meter", 47 | }; 48 | 49 | if (Array.isArray(oldWidget.data)) { 50 | for (const item of oldWidget.data) { 51 | if (item?.is_hide) { 52 | continue; 53 | } 54 | for (const variable of item.variables) { 55 | const key = `${item?.origin}${variable}`; 56 | if (oldDisplay?.vars_format?.[key]) { 57 | newStructure.display.number_format = oldDisplay?.vars_format?.[key]; 58 | } 59 | // This is the formula variable 60 | if (oldDisplay.vars_formula?.[key]) { 61 | newStructure.display.formula = convertFormula(oldDisplay.vars_formula?.[key]); 62 | } 63 | if (oldDisplay?.vars_labels?.[key] && oldDisplay?.vars_labels?.[key] !== variable) { 64 | newStructure.display.alias = oldDisplay?.vars_labels?.[key]; 65 | } 66 | } 67 | } 68 | 69 | newStructure.data = oldWidget.data; 70 | } 71 | return newStructure; 72 | } 73 | 74 | export function isOldStructure(widget: any) { 75 | const isOld = !!( 76 | widget?.display?.vars_labels || 77 | widget?.display?.vars_format || 78 | widget?.display?.vars_formula || 79 | widget?.display?.gauge_type 80 | ); 81 | 82 | return isOld; 83 | } 84 | -------------------------------------------------------------------------------- /src/modules/Network/network.types.ts: -------------------------------------------------------------------------------- 1 | import { Query } from "../../common/common.types"; 2 | import { DeviceItem } from "../Device/device.types"; 3 | 4 | interface IDeviceParameters { 5 | name?: string; 6 | label?: string; 7 | type?: "text" | "dropdown" | "switch" | "number"; 8 | default?: any; 9 | group?: "default" | "main" | "advanced" | "hide"; 10 | options?: any[]; // optional, only for dropdown 11 | } 12 | 13 | interface INetworkInfo { 14 | id?: string; 15 | name?: string; 16 | description?: string; 17 | logo_url?: string; 18 | icon_url?: string; 19 | banner_url?: string; 20 | device_parameters?: IDeviceParameters[]; 21 | middleware_endpoint?: string; 22 | payload_encoder?: string; 23 | payload_decoder?: string; 24 | public?: boolean; 25 | documentation_url?: string; 26 | serial_number?: { 27 | mask?: string; 28 | label?: string; 29 | image?: string; 30 | case?: string; 31 | help?: string; 32 | required?: boolean; 33 | }; 34 | require_devices_access?: boolean; 35 | } 36 | 37 | interface NetworkDeviceListQuery 38 | extends Omit< 39 | Query, 40 | "fields" 41 | > {} 42 | 43 | interface NetworkDeviceListQueryInfo extends DeviceItem { 44 | token: string; 45 | } 46 | 47 | export { INetworkInfo, NetworkDeviceListQuery, NetworkDeviceListQueryInfo }; 48 | -------------------------------------------------------------------------------- /src/modules/Resources/Buckets.ts: -------------------------------------------------------------------------------- 1 | import type { ExportOption, GenericID } from "../../common/common.types"; 2 | import TagoIOModule, { GenericModuleParams } from "../../common/TagoIOModule"; 3 | import Devices from "./Devices"; 4 | import type { ExportBucket, ExportBucketOption } from "./buckets.types"; 5 | import type { DeviceQuery } from "./devices.types"; 6 | 7 | /** 8 | * @deprecated Use `Resources.devices` instead. 9 | */ 10 | 11 | class Buckets extends TagoIOModule { 12 | public devices = new Devices(this.params); 13 | 14 | /** 15 | * @description Lists all devices from your application with pagination. 16 | * 17 | * @deprecated Use `Resources.devices.list()` instead 18 | * 19 | * @example 20 | * If receive an error "Authorization Denied", check policy **Device** / **Access** in Access Management. 21 | * ```typescript 22 | * // Use this instead of Buckets 23 | * const list = await Resources.devices.list({ 24 | * page: 1, 25 | * fields: ["id", "name"], 26 | * amount: 10, 27 | * orderBy: ["name", "asc"] 28 | * }); 29 | * console.log(list); 30 | * ``` 31 | */ 32 | public async list(queryObj?: T) { 33 | return await this.devices.list(queryObj); 34 | } 35 | 36 | /** 37 | * @description Retrieves detailed information about a specific device. 38 | * 39 | * @deprecated Use `Resources.devices.info()` instead 40 | * 41 | * @example 42 | * If receive an error "Authorization Denied", check policy **Device** / **Access** in Access Management. 43 | * ```typescript 44 | * // Use this instead of Buckets 45 | * const deviceInfo = await Resources.devices.info("device-id-123"); 46 | * console.log(deviceInfo); 47 | * ``` 48 | */ 49 | public async info(deviceID: GenericID) { 50 | return await this.devices.info(deviceID); 51 | } 52 | 53 | /** 54 | * @description Gets the amount of data stored for a device. 55 | * 56 | * @example 57 | * If receive an error "Authorization Denied", check policy **Device** / **Access** in Access Management. 58 | * ```typescript 59 | * // Use this instead of Buckets 60 | * const amount = await Resources.devices.amount("device-id-123"); 61 | * console.log(amount); 62 | * ``` 63 | */ 64 | public async amount(deviceID: GenericID) { 65 | return await this.devices.amount(deviceID); 66 | } 67 | } 68 | 69 | export default Buckets; 70 | -------------------------------------------------------------------------------- /src/modules/Resources/Integration.ts: -------------------------------------------------------------------------------- 1 | import TagoIOModule, { GenericModuleParams } from "../../common/TagoIOModule"; 2 | import Connectors from "./Integration.Connectors"; 3 | import Networks from "./Integration.Networks"; 4 | 5 | class Integration extends TagoIOModule { 6 | public connectors = new Connectors(this.params); 7 | 8 | public networks = new Networks(this.params); 9 | } 10 | 11 | export default Integration; 12 | -------------------------------------------------------------------------------- /src/modules/Resources/PaymentHistory.ts: -------------------------------------------------------------------------------- 1 | import TagoIOModule, { GenericModuleParams } from "../../common/TagoIOModule"; 2 | import { PaymentInfo } from "./paymentHistory.types"; 3 | 4 | class PaymentHistory extends TagoIOModule { 5 | /** 6 | * @description Retrieves the payment transaction history for the current account, including all past payments and their details. 7 | * 8 | * @example 9 | * If receive an error "Authorization Denied", check policy in Access Management. 10 | * ```typescript 11 | * const resources = new Resources({ token: "YOUR-PROFILE-TOKEN" }); 12 | * const result = await resources.paymentHistory.getHistory(); 13 | * console.log(result); // [ { strip_id: 'stripe-id-123', invoice_number: 'ABC-123', status: 'paid', ... } ] 14 | * ``` 15 | */ 16 | public async getHistory(): Promise { 17 | const result = await this.doRequest({ 18 | path: "/account/payment_history/", 19 | method: "GET", 20 | }); 21 | 22 | return result; 23 | } 24 | } 25 | 26 | export default PaymentHistory; 27 | -------------------------------------------------------------------------------- /src/modules/Resources/PaymentMethods.ts: -------------------------------------------------------------------------------- 1 | import { GenericID, GenericToken } from "../../common/common.types"; 2 | import TagoIOModule, { GenericModuleParams } from "../../common/TagoIOModule"; 3 | 4 | interface PaymentMethodCreateInfo { 5 | name: string; 6 | token: GenericToken; 7 | brand?: string; 8 | default_card?: boolean; 9 | } 10 | 11 | interface PaymentMethodInfo { 12 | name: string; 13 | brand: string; 14 | /** Last four digits of card */ 15 | last4: string; 16 | funding: string; 17 | exp_month: string; 18 | exp_year: string; 19 | } 20 | 21 | interface PaymentMethodListResponse { 22 | card: PaymentMethodInfo; 23 | } 24 | 25 | class PaymentMethods extends TagoIOModule { 26 | /** 27 | * @description Creates a new payment method for the current account using the provided payment information. 28 | * 29 | * @see {@link https://help.tago.io/portal/en/kb/articles/204-payment-methods} Payment Methods 30 | * @see {@link https://help.tago.io/portal/en/kb/articles/205-common-billing-issues} Common Billing Issues 31 | */ 32 | public async create(paymentMethodData: PaymentMethodCreateInfo): Promise { 33 | const result = await this.doRequest({ 34 | path: "/account/payment_method/", 35 | method: "POST", 36 | body: paymentMethodData, 37 | }); 38 | 39 | return result; 40 | } 41 | 42 | /** 43 | * @description Retrieves all payment methods associated with the current account. 44 | * 45 | * @see {@link https://help.tago.io/portal/en/kb/articles/204-payment-methods} Payment Methods 46 | * @see {@link https://help.tago.io/portal/en/kb/articles/205-common-billing-issues} Common Billing Issues 47 | * 48 | * @example 49 | * If receive an error "Authorization Denied", check policy in Access Management. 50 | * ```typescript 51 | * const resources = new Resources({ token: "YOUR-PROFILE-TOKEN" }); 52 | * const paymentMethods = await resources.paymentMethods.list(); 53 | * console.log(paymentMethods); // { card: { name: 'My Card', brand: 'Visa', last4: '1234', ... } } 54 | * ``` 55 | */ 56 | public async list(): Promise { 57 | const result = await this.doRequest({ 58 | path: "/account/payment_method/", 59 | method: "GET", 60 | }); 61 | 62 | return result; 63 | } 64 | 65 | /** 66 | * @description Removes a payment method from the account using its ID. 67 | * 68 | * @see {@link https://help.tago.io/portal/en/kb/articles/204-payment-methods} Payment Methods 69 | * @see {@link https://help.tago.io/portal/en/kb/articles/205-common-billing-issues} Common Billing Issues 70 | */ 71 | public async delete(paymentMethodID: GenericID): Promise { 72 | const result = await this.doRequest({ 73 | path: "/account/payment_method/", 74 | method: "DELETE", 75 | body: { 76 | id: paymentMethodID, 77 | }, 78 | }); 79 | 80 | return result; 81 | } 82 | } 83 | 84 | export default PaymentMethods; 85 | -------------------------------------------------------------------------------- /src/modules/Resources/Plan.ts: -------------------------------------------------------------------------------- 1 | import TagoIOModule, { GenericModuleParams } from "../../common/TagoIOModule"; 2 | import dateParser from "../Utils/dateParser"; 3 | import { CurrentPrices, PlanInfo, PlanSetInfo, Summary } from "./plan.types"; 4 | 5 | class Plan extends TagoIOModule { 6 | /** 7 | * @description Sets the active plan and configures service limits for the account, including SMS, email, data records, device requests and analysis quotas. 8 | * 9 | * @see {@link https://help.tago.io/portal/en/kb/articles/207-upgrading-plans-services} Upgrading Plans & Services 10 | * 11 | * @example 12 | * ```typescript 13 | * const resources = new Resources({ token: "YOUR-PROFILE-TOKEN" }); 14 | * const result = await resources.plan.setPlanParameters({ 15 | * plan: "plan_id", 16 | * sms: 100, 17 | * email: 1000, 18 | * data_records: 200000, 19 | * device_request: 250, 20 | * analysis: 1000 21 | * }); 22 | * console.log(result); 23 | * ``` 24 | */ 25 | public async setPlanParameters(data: PlanSetInfo): Promise { 26 | const result = await this.doRequest({ 27 | path: "/account/plan/", 28 | method: "POST", 29 | body: data, 30 | }); 31 | 32 | return result; 33 | } 34 | 35 | /** 36 | * @description Retrieves information about the currently active plan and its associated services. 37 | * 38 | * @see {@link https://help.tago.io/portal/en/kb/articles/114-account-plans} Account Plans 39 | * 40 | * @example 41 | * ```typescript 42 | * const resources = new Resources({ token: "YOUR-PROFILE-TOKEN" }); 43 | * const result = await resources.plan.getActivePlan(); 44 | * console.log(result); // { plan: 'scale' } 45 | * ``` 46 | */ 47 | public async getActivePlan(): Promise> { 48 | let result = await this.doRequest({ 49 | path: "/account/plan", 50 | method: "GET", 51 | }); 52 | 53 | result = dateParser(result, ["created_at", "end_date"]); 54 | 55 | return result; 56 | } 57 | 58 | /** 59 | * @description Retrieves the current pricing information for all available services. 60 | * 61 | * @see {@link https://help.tago.io/portal/en/kb/articles/114-account-plans} Account Plans 62 | * 63 | * @example 64 | * ```typescript 65 | * const resources = new Resources({ token: "YOUR-PROFILE-TOKEN" }); 66 | * const prices = await resources.plan.getCurrentPrices(); 67 | * console.log(prices); // { analysis: [ { price: 0, amount: 3000 } ], data_records: [...], ... } 68 | * ``` 69 | */ 70 | public async getCurrentPrices(): Promise { 71 | const result = await this.doRequest({ 72 | path: "/pricing", 73 | method: "GET", 74 | }); 75 | 76 | return result; 77 | } 78 | } 79 | 80 | export default Plan; 81 | -------------------------------------------------------------------------------- /src/modules/Resources/ServiceAuthorization.types.ts: -------------------------------------------------------------------------------- 1 | import { GenericID } from "../../types"; 2 | 3 | /** 4 | * Token used on TagoIO, string with 34 characters 5 | */ 6 | type GenericToken = string; 7 | 8 | type TokenCreateResponse = { 9 | token: GenericToken; 10 | name: string; 11 | profile: GenericID; 12 | /** [Optional] Verification code to validate middleware requests. */ 13 | additional_parameters?: string; 14 | }; 15 | 16 | export { GenericToken, TokenCreateResponse }; 17 | -------------------------------------------------------------------------------- /src/modules/Resources/Tags.ts: -------------------------------------------------------------------------------- 1 | import TagoIOModule, { GenericModuleParams } from "../../common/TagoIOModule"; 2 | 3 | type TagTypes = "bucket" | "device" | "dashboard" | "action" | "analysis" | "tcore" | "run_users" | "secrets"; 4 | 5 | class Tags extends TagoIOModule { 6 | /** 7 | * @description Retrieves all available tag keys for a specific resource type in the account. 8 | * 9 | * @see {@link https://help.tago.io/portal/en/kb/articles/tags} Tags System 10 | * 11 | * @example 12 | * ```typescript 13 | * const resources = new Resources({ token: "YOUR-PROFILE-TOKEN" }); 14 | * 15 | * const deviceTags = await resources.tags.getTagKeys("device"); 16 | * console.log(deviceTags); // [ 'tag-key1', 'tag-key2', 'tag-key3' ] 17 | * 18 | * const dashboardTags = await resources.tags.getTagKeys("dashboard"); 19 | * console.log(dashboardTags); // [ 'tag-key1', 'tag-key2', 'tag-key3' ] 20 | * ``` 21 | */ 22 | public async getTagKeys(type: TagTypes): Promise { 23 | const result = await this.doRequest({ 24 | path: `/tags/keys/${type}`, 25 | method: "GET", 26 | }); 27 | 28 | return result; 29 | } 30 | } 31 | 32 | export default Tags; 33 | -------------------------------------------------------------------------------- /src/modules/Resources/access.types.ts: -------------------------------------------------------------------------------- 1 | import { GenericID, TagsObj, Query } from "../../common/common.types"; 2 | 3 | interface Permissions { 4 | effect: "allow" | "deny"; 5 | action: string[]; 6 | resource: string[]; 7 | } 8 | 9 | interface AccessCreateInfo { 10 | name: string; 11 | permissions: Permissions[]; 12 | // TODO: target type 13 | targets: []; 14 | profile?: GenericID; 15 | tags?: TagsObj[]; 16 | active?: boolean; 17 | } 18 | 19 | interface AccessInfo extends AccessCreateInfo { 20 | id: GenericID; 21 | created_at: Date; 22 | updated_at: Date; 23 | } 24 | 25 | type AccessQuery = Query; 26 | 27 | export { AccessCreateInfo, AccessInfo, AccessQuery, Permissions }; 28 | -------------------------------------------------------------------------------- /src/modules/Resources/account.types.ts: -------------------------------------------------------------------------------- 1 | import { GenericID } from "../../common/common.types"; 2 | import { ProfileListInfo } from "./profile.types"; 3 | 4 | interface AccountCreateInfo { 5 | /** Name of the account */ 6 | name: string; 7 | /** Email of the account */ 8 | email: string; 9 | /** Password of the account */ 10 | password: string; 11 | /** Password confirmation */ 12 | cpassword: string; 13 | /** Country of the account */ 14 | country?: string; 15 | /** Timezone of the account */ 16 | timezone: string; 17 | /** Company of the account */ 18 | company?: string; 19 | /** Set true if wanna receive newsletter */ 20 | newsletter?: boolean; 21 | developer?: boolean; 22 | } 23 | 24 | interface AccountInfo extends Omit { 25 | active: boolean; 26 | blocked: boolean; 27 | id: GenericID; 28 | /** language set e.g "en-us" */ 29 | language: string; 30 | last_login: Date | null; 31 | options: { 32 | user_view_welcome: boolean; 33 | /** How decimal values are separated */ 34 | decimal_separator: string; 35 | thousand_separator: string; 36 | last_whats_new: Date | null; 37 | }; 38 | phone: string | null; 39 | send_invoice: boolean; 40 | stripe_id: string | null; 41 | /** Type of the account e.g "user" */ 42 | type: string; 43 | /** Plan of the account e.g "free" | "starter" | "scale" */ 44 | plan: string; 45 | created_at: Date; 46 | updated_at: Date; 47 | /** One-Time Password (OTP) settings */ 48 | otp?: { 49 | authenticator: boolean; 50 | sms: boolean; 51 | email: boolean; 52 | }; 53 | } 54 | 55 | interface LoginResponse { 56 | type: string; 57 | id: GenericID; 58 | email: string; 59 | company: string; 60 | name: string; 61 | profiles: Required[]; 62 | } 63 | 64 | interface LoginCredentials { 65 | email: string; 66 | password: string; 67 | otp_type: OTPType; 68 | pin_code: string; 69 | } 70 | 71 | type OTPType = "sms" | "email" | "authenticator"; 72 | interface TokenCreateInfo { 73 | /** Id of profile to create the token */ 74 | profile_id: GenericID; 75 | /** Email of the account */ 76 | email: string; 77 | /** Password of the account */ 78 | password: string; 79 | /** OTP Pin Code */ 80 | pin_code: string; 81 | /** OTP Type */ 82 | otp_type: OTPType; 83 | /** Name of the token */ 84 | name: string; 85 | } 86 | 87 | export { AccountInfo, AccountCreateInfo, LoginResponse, TokenCreateInfo, OTPType, LoginCredentials }; 88 | -------------------------------------------------------------------------------- /src/modules/Resources/analysis.types.ts: -------------------------------------------------------------------------------- 1 | import { Base64, RunTypeOptions, GenericID, TagsObj, Query, ExpireTimeOption } from "../../common/common.types"; 2 | 3 | interface ScriptFile { 4 | name: string; 5 | content: Base64; 6 | language: RunTypeOptions; 7 | } 8 | 9 | interface AnalysisCreateInfo { 10 | name: string; 11 | description?: string | null; 12 | interval?: string; 13 | run_on?: "tago" | "external"; 14 | file_name?: string; 15 | runtime?: RunTypeOptions; 16 | active?: true; 17 | profile?: GenericID; 18 | /** Environment variables */ 19 | variables?: { 20 | key: string; 21 | value: string | number | boolean; 22 | }; 23 | tags?: TagsObj[]; 24 | } 25 | 26 | interface VersionsAnalysis { 27 | [version_number_key: string]: { 28 | file_name: string; 29 | created_at: Date | string; 30 | /** E.g John Doe (john.doe@email.com) */ 31 | created_by: string; 32 | }; 33 | } 34 | 35 | interface AnalysisInfo extends AnalysisCreateInfo { 36 | id: GenericID; 37 | token: string; 38 | last_run: ExpireTimeOption; 39 | created_at: Date; 40 | updated_at: Date; 41 | locked_at: any; 42 | console?: string[]; 43 | /** Current version being used */ 44 | version?: number | string; 45 | versions?: VersionsAnalysis; 46 | } 47 | 48 | type AnalysisQuery = Query; 49 | type AnalysisListItem = Pick & Partial; 50 | 51 | export { AnalysisInfo, AnalysisCreateInfo, ScriptFile, AnalysisQuery, AnalysisListItem }; 52 | -------------------------------------------------------------------------------- /src/modules/Resources/dictionaries.types.ts: -------------------------------------------------------------------------------- 1 | import { GenericID, Query } from "../../common/common.types"; 2 | 3 | interface DictionaryCreateInfo { 4 | name: string; 5 | slug: string; 6 | /** First dictionary language E.g "en-US" */ 7 | fallback: string; 8 | } 9 | 10 | interface DictionaryLanguage { 11 | /** Language code E.g "en-US" */ 12 | code: string; 13 | active: boolean; 14 | } 15 | 16 | interface DictionaryInfo extends DictionaryCreateInfo { 17 | id: GenericID; 18 | languages: DictionaryLanguage[]; 19 | created_at: Date; 20 | updated_at: Date; 21 | } 22 | 23 | interface LanguageData { 24 | [key: string]: string; 25 | } 26 | 27 | interface LanguageEditData { 28 | dictionary: LanguageData; 29 | active: boolean; 30 | } 31 | 32 | interface LanguageInfoQuery { 33 | fallback?: boolean; 34 | } 35 | 36 | type DictionaryQuery = Query; 37 | 38 | export { DictionaryCreateInfo, DictionaryInfo, LanguageInfoQuery, DictionaryQuery, LanguageData, LanguageEditData }; 39 | -------------------------------------------------------------------------------- /src/modules/Resources/entities.types.ts: -------------------------------------------------------------------------------- 1 | import { GenericID, Query, TagsObj } from "../../common/common.types"; 2 | 3 | type EntityFieldType = "uuid" | "string" | "int" | "float" | "json" | "timestamp" | "text" | "boolean"; 4 | 5 | type EntityFieldCreate = { 6 | action: "create"; 7 | type?: EntityFieldType; 8 | required?: boolean; 9 | }; 10 | 11 | type EntityFieldRename = { 12 | action: "rename"; 13 | new_name: string; 14 | }; 15 | 16 | type EntityFieldDelete = { 17 | action: "delete"; 18 | }; 19 | 20 | type EntitySchema = Record; 21 | 22 | type EntityIndex = Record< 23 | string, 24 | { 25 | fields?: string[]; 26 | } 27 | >; 28 | 29 | type EntityCreateInfo = { 30 | name?: string; 31 | schema?: EntitySchema; 32 | index?: EntityIndex; 33 | tags?: TagsObj[]; 34 | payload_decoder?: string | null; 35 | }; 36 | 37 | type EntityInfo = Required & { 38 | id: string; 39 | profile: string; 40 | created_at: string; 41 | updated_at: string; 42 | }; 43 | 44 | interface EntityQuery 45 | extends Query { 46 | resolveBucketName?: boolean; 47 | resolveConnectorName?: boolean; 48 | } 49 | 50 | type EntityListItem = Pick & 51 | Partial; 52 | 53 | type EntityDataQuery = { 54 | /** Filters to narrow down the requests from the API. */ 55 | filter?: Record; 56 | /** Amount of items to be fetched. */ 57 | amount?: number; 58 | page?: number; 59 | /** Amount of items to be skipped. */ 60 | skip?: number; 61 | /** Ordering for the requested data. */ 62 | order?: any; 63 | /** Timestamp to pin the requested data to a specific start date. */ 64 | startDate?: string; 65 | /** Timestamp to pin the requested data up to a specific end date. */ 66 | endDate?: string; 67 | /** Index to use for the query. */ 68 | index?: string; 69 | }; 70 | 71 | type EntityData = { id: GenericID } & Record; 72 | 73 | type EntityUnknownData = { 74 | [field: string]: any; 75 | }; 76 | 77 | export { 78 | EntityIndex, 79 | EntitySchema, 80 | EntityFieldType, 81 | EntityCreateInfo, 82 | EntityInfo, 83 | EntityQuery, 84 | EntityListItem, 85 | EntityDataQuery, 86 | EntityData, 87 | EntityUnknownData, 88 | }; 89 | -------------------------------------------------------------------------------- /src/modules/Resources/files.types.ts: -------------------------------------------------------------------------------- 1 | import { GenericID } from "../../types"; 2 | 3 | interface FileQuery { 4 | path?: string; 5 | paginationToken?: string; 6 | quantity?: number; 7 | } 8 | 9 | interface FileListInfo { 10 | files: { 11 | filename: string; 12 | size: number; 13 | last_modified: Date | null; 14 | }[]; 15 | folders: string[]; 16 | } 17 | 18 | interface Base64File { 19 | /** Name of file */ 20 | filename: string; 21 | /** String of Base64 */ 22 | file: string; 23 | /** 24 | * Make file public 25 | * default: false 26 | */ 27 | public?: boolean; 28 | } 29 | 30 | interface CopyFiles { 31 | from: string; 32 | to: string; 33 | } 34 | 35 | interface MoveFiles { 36 | from: string; 37 | to: string; 38 | } 39 | 40 | interface FilesPermission { 41 | file: string; 42 | public: boolean; 43 | } 44 | 45 | type UploadOptions = { 46 | /** the maximum amount of tries to upload each chunk to TagoIO. After this many unsuccessful tries of a single chunk, the upload is aborted */ 47 | maxTriesForEachChunk?: number; 48 | /** timeout before trying to upload the same chunk if the request failed */ 49 | timeoutForEachFailedChunk?: number; 50 | /** The file's content type. This is optional */ 51 | contentType?: string; 52 | /** if the file can be accessed by anybody with a link or not */ 53 | isPublic?: boolean; 54 | /** 55 | * Dashboard ID. 56 | * 57 | * Uploading files from a widget requires `dashboard`, `widget`, and `fieldId` to be provided. 58 | */ 59 | dashboard?: string; 60 | /** 61 | * Widget ID. 62 | * 63 | * Uploading files from a widget requires `dashboard`, `widget`, and `fieldId` to be provided. 64 | */ 65 | widget?: string; 66 | /** 67 | * ID of the field from the widget where the file is selected. 68 | * 69 | * Uploading files from a widget requires `dashboard`, `widget`, and `fieldId` to be provided. 70 | */ 71 | fieldId?: string; 72 | /** will provide a cancel token for you to cancel the request */ 73 | onCancelToken?: (cancel: () => void) => any; 74 | /** the byte size of each chunk sent to TagoIO. This will influence how many requests this function will perform */ 75 | chunkSize?: number; 76 | /** will provide the upload percentage for this file */ 77 | onProgress?: (percentage: number) => any; 78 | /** 79 | * map blueprint devices to real devices, so BP Device ID path is replaced by the real device ID 80 | */ 81 | blueprint_devices?: { origin: GenericID; id: GenericID; bucket?: GenericID }[]; 82 | }; 83 | 84 | export { FileQuery, FileListInfo, Base64File, MoveFiles, FilesPermission, UploadOptions, CopyFiles }; 85 | -------------------------------------------------------------------------------- /src/modules/Resources/integration.connectors.types.ts: -------------------------------------------------------------------------------- 1 | import { GenericID, Query, TokenData } from "../../common/common.types"; 2 | 3 | interface IDeviceParameters { 4 | name?: string; 5 | label?: string; 6 | type?: "text" | "dropdown" | "switch" | "number"; 7 | default?: any; 8 | group?: "default" | "main" | "advanced" | "hide"; 9 | /** Optional only for dropdown */ 10 | options?: any[]; 11 | } 12 | 13 | interface ConnectorCreateInfo { 14 | name?: string; 15 | description?: string; 16 | logo_url?: string; 17 | device_parameters?: IDeviceParameters[]; 18 | networks?: GenericID[]; 19 | payload_encoder?: string; 20 | /** Base64 decoded string */ 21 | payload_decoder?: string; 22 | /** Refers to the **description** in the Documentation settings */ 23 | install_text?: string; 24 | /** Refers to the **completion text** in the Documentation settings */ 25 | install_end_text?: string; 26 | device_annotation?: string; 27 | } 28 | 29 | interface ConnectorInfo extends ConnectorCreateInfo { 30 | id: GenericID; 31 | public: boolean; 32 | description?: string; 33 | logo_url?: string; 34 | created_at: Date; 35 | updated_at: Date; 36 | device_parameters?: IDeviceParameters[]; 37 | networks?: GenericID[]; 38 | /** Refers to the **description** in the Documentation settings */ 39 | install_text?: string; 40 | /** Refers to the **completion text** in the Documentation settings */ 41 | install_end_text?: string; 42 | device_annotation?: string; 43 | } 44 | 45 | type ConnectorQuery = Query< 46 | ConnectorInfo, 47 | | "name" 48 | | "id" 49 | | "description" 50 | | "logo_url" 51 | | "install_text" 52 | | "install_end_text" 53 | | "device_annotation" 54 | | "payload_decoder" 55 | | "networks" 56 | >; 57 | 58 | export { ConnectorInfo, ConnectorCreateInfo, ConnectorQuery }; 59 | -------------------------------------------------------------------------------- /src/modules/Resources/integration.networks.types.ts: -------------------------------------------------------------------------------- 1 | import { GenericID, GenericToken, Query } from "../../common/common.types"; 2 | 3 | interface IDeviceParameters { 4 | name?: string; 5 | label?: string; 6 | type?: "text" | "dropdown" | "switch" | "number"; 7 | default?: any; 8 | group?: "default" | "main" | "advanced" | "hide"; 9 | options?: any[]; // optional, only for dropdown 10 | } 11 | 12 | interface NetworkCreateInfo { 13 | name?: string; 14 | description?: string; 15 | logo_url?: string; 16 | icon_url?: string; 17 | banner_url?: string; 18 | device_parameters?: IDeviceParameters[]; 19 | middleware_endpoint?: string; 20 | payload_encoder?: string; 21 | /** Base64 decoded string */ 22 | payload_decoder?: string; 23 | public?: boolean; 24 | documentation_url?: string; 25 | serial_number?: { 26 | mask?: string; 27 | label?: string; 28 | image?: string; 29 | case?: string; 30 | help?: string; 31 | required?: boolean; 32 | }; 33 | require_devices_access?: boolean; 34 | } 35 | 36 | interface NetworkInfo extends NetworkCreateInfo { 37 | id: GenericID; 38 | name?: string; 39 | description?: string; 40 | logo_url?: string; 41 | icon_url?: string; 42 | banner_url?: string; 43 | device_parameters?: IDeviceParameters[]; 44 | middleware_endpoint?: string; 45 | payload_encoder?: string; 46 | /** Base64 decoded string */ 47 | payload_decoder?: string; 48 | public?: boolean; 49 | documentation_url?: string; 50 | serial_number?: { 51 | mask?: string; 52 | label?: string; 53 | image?: string; 54 | case?: string; 55 | help?: string; 56 | required?: boolean; 57 | }; 58 | } 59 | 60 | interface NetworkTokenInfo { 61 | name: string; 62 | token: GenericToken; 63 | } 64 | 65 | interface NetworkTokenCreateResponse { 66 | token: GenericToken; 67 | name: string; 68 | network: GenericID; 69 | } 70 | 71 | type NetworkQuery = Query< 72 | NetworkInfo, 73 | | "name" 74 | | "description" 75 | | "logo_url" 76 | | "icon_url" 77 | | "banner_url" 78 | | "device_parameters" 79 | | "middleware_endpoint" 80 | | "payload_encoder" 81 | | "payload_decoder" 82 | | "serial_number" 83 | | "documentation_url" 84 | | "public" 85 | | "created_at" 86 | | "updated_at" 87 | >; 88 | 89 | export { NetworkInfo, NetworkCreateInfo, NetworkTokenInfo, NetworkQuery, NetworkTokenCreateResponse }; 90 | -------------------------------------------------------------------------------- /src/modules/Resources/notifications.types.ts: -------------------------------------------------------------------------------- 1 | import { GenericID, Query } from "../../common/common.types"; 2 | 3 | interface NotificationTriggerAnalysis { 4 | analysis_id: GenericID; 5 | } 6 | interface NotificationTriggerHTTP { 7 | url: string; 8 | method: "POST" | "GET" | "PUT" | "DELETE" | "REDIRECT"; 9 | body: { [key: string]: any }; 10 | } 11 | interface NotificationTriggerProfile { 12 | share_profile: "accept" | "refuse"; 13 | } 14 | interface NotificationButton { 15 | id: string; 16 | label: string; 17 | color?: string; 18 | triggers: (NotificationTriggerAnalysis | NotificationTriggerHTTP | NotificationTriggerProfile)[]; 19 | } 20 | 21 | interface NotificationIconImage { 22 | image_url: string; 23 | bg_color?: HexColor; 24 | fit?: "fill" | "contain" | "cover"; 25 | } 26 | 27 | type HexColor = string; 28 | interface NotificationIconSVG { 29 | svg_url: string; 30 | svg_color?: HexColor; 31 | bg_color?: HexColor; 32 | } 33 | interface NotificationCreate { 34 | title: string; 35 | message: string; 36 | read?: boolean; 37 | icon?: NotificationIconSVG | NotificationIconImage; 38 | buttons?: NotificationButton[]; 39 | buttons_enabled?: boolean; 40 | buttons_autodisable?: boolean; 41 | } 42 | // TODO: FIX: The filter condition read is not working. 43 | type NotificationQuery = Query<{ read: boolean }, "created_at">; 44 | type NotificationInfo = { id: GenericID; created_at: Date } & Required; 45 | 46 | export { 47 | NotificationCreate, 48 | NotificationQuery, 49 | NotificationInfo, 50 | NotificationButton, 51 | NotificationTriggerProfile, 52 | NotificationTriggerHTTP, 53 | NotificationTriggerAnalysis, 54 | NotificationIconImage, 55 | NotificationIconSVG, 56 | }; 57 | -------------------------------------------------------------------------------- /src/modules/Resources/paymentHistory.types.ts: -------------------------------------------------------------------------------- 1 | import { GenericID } from "../../common/common.types"; 2 | 3 | interface PaymentInfo { 4 | status: boolean; 5 | result: [ 6 | { 7 | id: GenericID; 8 | stripe_id: GenericID; 9 | account: GenericID; 10 | info: string; 11 | notes: string; 12 | invoice_number: number; 13 | invoice_code: string; 14 | total: number; 15 | due_date: string; 16 | payment_method: string; 17 | created_at: string; 18 | updated_at: string; 19 | paid: true; 20 | ref_account: { 21 | name: string; 22 | email: string; 23 | id: GenericID; 24 | send_invoice: boolean; 25 | }; 26 | ref_payment_method: { last4: string; brand: string }; 27 | account_invoice_items: { 28 | unit_value: number; 29 | description: string; 30 | qty: number; 31 | value: number; 32 | }[]; 33 | }, 34 | ]; 35 | } 36 | 37 | export { PaymentInfo }; 38 | -------------------------------------------------------------------------------- /src/modules/Resources/plan.types.ts: -------------------------------------------------------------------------------- 1 | import { ExpireTimeOption, GenericID } from "../../common/common.types"; 2 | 3 | interface PlanSetInfo { 4 | plan: string; 5 | sms?: number; 6 | email?: number; 7 | data_records?: number; 8 | device_request?: number; 9 | analysis?: number; 10 | } 11 | 12 | interface PlanInfo extends PlanSetInfo { 13 | id: GenericID; 14 | active: number; 15 | end_date: Date | null; 16 | price: number; 17 | created_at: Date; 18 | next_plan: string; 19 | } 20 | 21 | interface ProfileLimit { 22 | id: GenericID; 23 | name: string; 24 | limits: { 25 | input: number; 26 | output: number; 27 | sms: number; 28 | email: number; 29 | analysis: number; 30 | data_records: number; 31 | }; 32 | addons: object; 33 | } 34 | 35 | interface Discount { 36 | description: string; 37 | value: number; 38 | expire_at: ExpireTimeOption; 39 | } 40 | 41 | interface Summary { 42 | profiles: ProfileLimit[]; 43 | plan: string; 44 | discounts: Discount[]; 45 | } 46 | 47 | interface Price { 48 | price: number; 49 | amount: number; 50 | } 51 | 52 | interface CurrentPrices { 53 | analysis: Price[]; 54 | data_records: Price[]; 55 | sms: Price[]; 56 | output: Price[]; 57 | input: Price[]; 58 | email: Price[]; 59 | plans: { name: string; price: number }[]; 60 | addons: { name: string; price: number }[]; 61 | } 62 | 63 | export { PlanSetInfo, PlanInfo, ProfileLimit, Discount, Summary, Price, CurrentPrices }; 64 | -------------------------------------------------------------------------------- /src/modules/Resources/secrets.type.ts: -------------------------------------------------------------------------------- 1 | import { GenericID, Query, TagsObj } from "../../common/common.types"; 2 | 3 | type SecretsValue = { 4 | value: string; 5 | }; 6 | 7 | interface SecretsInfo { 8 | id: GenericID; 9 | key: string; 10 | tags?: TagsObj[]; 11 | value_length: number; 12 | created_at: Date; 13 | updated_at: Date; 14 | } 15 | 16 | type SecretsCreate = Pick & SecretsValue & Partial>; 17 | 18 | type SecretsEdit = Partial & SecretsValue>; 19 | 20 | type SecretsQuery = Query; 21 | 22 | export { SecretsInfo, SecretsCreate, SecretsEdit, SecretsQuery }; 23 | -------------------------------------------------------------------------------- /src/modules/Resources/tagocore.types.ts: -------------------------------------------------------------------------------- 1 | import { GenericID, GenericToken, TagsObj, Query } from "../../common/common.types"; 2 | 3 | interface TagoCoreComputerUsage { 4 | total: number; 5 | used: number; 6 | description: string; 7 | title: string; 8 | type: string; 9 | detail: string; 10 | } 11 | 12 | interface TagoCoreOS { 13 | name: string; 14 | arch: string; 15 | version: string; 16 | platform?: string; 17 | } 18 | 19 | interface TagoCoreSummary { 20 | device: number; 21 | action: number; 22 | analysis: number; 23 | } 24 | 25 | interface TagoCoreInfo { 26 | active: boolean; 27 | computer_usage: TagoCoreComputerUsage[]; 28 | connected: boolean; 29 | created_at: string; 30 | id: GenericID; 31 | internet_ip: string; 32 | last_connection: string; 33 | local_ips: string; 34 | name: string; 35 | os: TagoCoreOS; 36 | profile: string; 37 | summary: TagoCoreSummary; 38 | system_start_time: string; 39 | tags: TagsObj[]; 40 | tcore_start_time: string; 41 | tcore_version: string; 42 | token: GenericToken; 43 | updated_at: string; 44 | machine_id: string; 45 | } 46 | 47 | interface TagoCoreListInfo { 48 | active: boolean; 49 | connected: boolean; 50 | created_at: string; 51 | id: GenericID; 52 | internet_ip: string; 53 | last_connection: string; 54 | local_ips: string; 55 | name: string; 56 | profile: string; 57 | system_start_time: string; 58 | tags: TagsObj[]; 59 | tcore_start_time: string; 60 | tcore_version: string; 61 | updated_at: string; 62 | machine_id: string; 63 | } 64 | 65 | type TagoCoreQuery = Query< 66 | TagoCoreInfo, 67 | | "name" 68 | | "active" 69 | | "created_at" 70 | | "updated_at" 71 | | "last_connection" 72 | | "local_ips" 73 | | "internet_ip" 74 | | "system_start_time" 75 | | "tcore_start_time" 76 | | "machine_id" 77 | >; 78 | 79 | export { TagoCoreComputerUsage, TagoCoreInfo, TagoCoreListInfo, TagoCoreOS, TagoCoreQuery, TagoCoreSummary }; 80 | -------------------------------------------------------------------------------- /src/modules/Resources/template.types.ts: -------------------------------------------------------------------------------- 1 | import { GenericID } from "../../common/common.types"; 2 | 3 | interface TemplateObjDashboard { 4 | dashboard: GenericID; 5 | name: string; 6 | image_logo?: string; 7 | image_main?: string; 8 | /** Dashboard Setup Object */ 9 | setup?: any; 10 | } 11 | 12 | interface TemplateObjAnalysis { 13 | analysis: GenericID; 14 | name: string; 15 | image_logo?: string; 16 | image_main?: string; 17 | /** Analysis Setup Object */ 18 | setup?: any; 19 | } 20 | 21 | interface TemplateInstallDashboard { 22 | device?: { id: GenericID; bucket: GenericID } | void; 23 | devices?: { id: GenericID; bucket: GenericID } | void; 24 | analysis?: GenericID[] | void; 25 | replace?: { [field: string]: any } | void; 26 | } 27 | 28 | interface TemplateInstallAnalysis { 29 | device_token?: string; 30 | replace?: { [field: string]: any } | void; 31 | } 32 | 33 | interface TemplateObj { 34 | name: string; 35 | type: "dashboard" | "analysis"; 36 | image_main?: string; 37 | image_logo?: string; 38 | updated_at: Date; 39 | created_at: Date; 40 | } 41 | 42 | interface TemplateInstallReturn { 43 | dashboard?: string; 44 | analysis?: string; 45 | } 46 | 47 | export { 48 | TemplateObjDashboard, 49 | TemplateObjAnalysis, 50 | TemplateInstallDashboard, 51 | TemplateInstallAnalysis, 52 | TemplateObj, 53 | TemplateInstallReturn, 54 | }; 55 | -------------------------------------------------------------------------------- /src/modules/RunUser/SDB.ts: -------------------------------------------------------------------------------- 1 | import TagoIOModule, { GenericModuleParams } from "../../common/TagoIOModule"; 2 | 3 | class SDB extends TagoIOModule { 4 | /** 5 | * Retrieves a custom parameter of a Run user. 6 | * The Run user is identified by the token in the constructor. 7 | * @param tagoRunURL TagoIO Run url without http 8 | * @param key Identifier of the parameter 9 | */ 10 | public async getItem(tagoRunURL: string, key: string): Promise { 11 | const result = await this.doRequest({ 12 | path: `/run/${tagoRunURL}/sdb/${key}`, 13 | method: "GET", 14 | }); 15 | return result; 16 | } 17 | 18 | /** 19 | * Creates or updates a custom parameter of a Run user. 20 | * The Run user is identified by the token in the constructor. 21 | * @param tagoRunURL TagoIO Run url without http 22 | * @param key Identifier of the parameter 23 | * @param value Value of the parameter 24 | */ 25 | public async setItem(tagoRunURL: string, key: string, value: string): Promise { 26 | const result = await this.doRequest({ 27 | path: `/run/${tagoRunURL}/sdb/${key}`, 28 | method: "POST", 29 | body: value, 30 | }); 31 | return result; 32 | } 33 | 34 | /** 35 | * Delete a custom parameter of a Run user. 36 | * The Run user is identified by the token in the constructor. 37 | * @param tagoRunURL TagoIO Run url without http 38 | * @param key Identifier of the parameter 39 | */ 40 | public async removeItem(tagoRunURL: string, key: string): Promise { 41 | const result = await this.doRequest({ 42 | path: `/run/${tagoRunURL}/sdb/${key}`, 43 | method: "DELETE", 44 | }); 45 | return result; 46 | } 47 | } 48 | 49 | export default SDB; 50 | -------------------------------------------------------------------------------- /src/modules/RunUser/runUser.types.ts: -------------------------------------------------------------------------------- 1 | import { ExpireTimeOption, GenericID, GenericToken } from "../../common/common.types"; 2 | import { OTPType } from "../Resources/account.types"; 3 | 4 | interface UserOptions { 5 | decimal_separator?: string; 6 | thousand_separator?: string; 7 | date_format?: string; 8 | time_format?: string; 9 | show_last_updated_at?: string; 10 | } 11 | 12 | interface RunUserCreateInfo { 13 | name: string; 14 | email: string; 15 | password: string; 16 | timezone: string; 17 | company?: string; 18 | phone?: string | null; 19 | language?: string; 20 | active: boolean; 21 | newsletter?: boolean; 22 | options?: UserOptions; 23 | } 24 | 25 | interface RunUserCreate { 26 | user: GenericID; 27 | } 28 | 29 | interface RunUserInfo extends RunUserCreateInfo { 30 | id: GenericID; 31 | created_at: Date; 32 | otp?: { 33 | authenticator: boolean; 34 | sms: boolean; 35 | email: boolean; 36 | }; 37 | } 38 | 39 | interface RunUserLoginResponse { 40 | token: GenericToken; 41 | expire_date: ExpireTimeOption; 42 | } 43 | 44 | interface RunUserCredentials { 45 | email: string; 46 | password: string; 47 | } 48 | interface RunUserLogin extends RunUserCredentials { 49 | otp_type?: OTPType; 50 | pin_code?: string; 51 | } 52 | 53 | interface RunNotificationInfo { 54 | id: GenericID; 55 | run_user: GenericID; 56 | title: string; 57 | message: string; 58 | buttons: []; 59 | read: boolean; 60 | created_at: Date; 61 | updated_at: Date; 62 | buttons_enabled: boolean; 63 | buttons_autodisable: boolean; 64 | } 65 | 66 | export { 67 | RunUserInfo, 68 | RunUserCreateInfo, 69 | RunUserCreate, 70 | RunUserLogin, 71 | RunUserCredentials, 72 | RunUserLoginResponse, 73 | RunNotificationInfo, 74 | }; 75 | -------------------------------------------------------------------------------- /src/modules/Services/AWS-SQS.ts: -------------------------------------------------------------------------------- 1 | import TagoIOModule, { GenericModuleParams } from "../../common/TagoIOModule"; 2 | import { Data } from "../../types"; 3 | 4 | interface AWSCredentials { 5 | /** AWS region, e.g., us-east-1 */ 6 | aws_region: string; 7 | /** SQS queue URL */ 8 | queue_url: string; 9 | /** AWS Access Key ID */ 10 | access_key_id: string; 11 | /** AWS Secret Access Key */ 12 | secret_access_key: string; 13 | } 14 | 15 | interface AWSSQSData { 16 | /** SQS secret or AWS credentials */ 17 | sqs_secret: string | AWSCredentials; 18 | /** Message to be sent to SQS */ 19 | data?: Partial | Partial[]; 20 | } 21 | 22 | class AWSSQS extends TagoIOModule { 23 | /** 24 | * Send a message to Amazon SQS 25 | * 26 | * @param sqsData - The AWS SQS object containing all necessary information 27 | * @returns A promise that resolves to a success message 28 | * 29 | * @remarks 30 | * This method requires valid AWS credentials and SQS queue information. 31 | * For enhanced security, it's strongly recommended to store these credentials 32 | * using TagoIO Secrets rather than hardcoding them. 33 | * 34 | * @see {@link https://docs.aws.amazon.com/AWSSimpleQueueService/latest/SQSDeveloperGuide/welcome.html} for AWS SQS documentation 35 | * @see {@link https://help.tago.io/portal/en/kb/articles/secrets} for TagoIO Secrets usage 36 | * 37 | * @example 38 | * ```typescript 39 | * const environment = Utils.envToJson(context.environment); 40 | * const sqsService = new Services({ token: context.token }).aws_sqs; 41 | * const result = await sqsService.sendMessage({ 42 | * sqs_secret: environment.AWS_SQS_TAGOIO_SECRET, 43 | * data: { variable: "temperature", value: 1 } 44 | * }); 45 | * console.log(result); 46 | * ``` 47 | */ 48 | public async sendMessage(sqsData: AWSSQSData): Promise { 49 | try { 50 | if (typeof sqsData.sqs_secret === "string") { 51 | JSON.parse(sqsData.sqs_secret); 52 | } 53 | } catch (error) { 54 | if (error instanceof SyntaxError) { 55 | throw new Error("SQS Secret is not a valid JSON"); 56 | } 57 | } 58 | 59 | const dataList = Array.isArray(sqsData.data) ? sqsData.data : [sqsData.data]; 60 | 61 | const result = await this.doRequest({ 62 | path: "/analysis/services/queue-sqs/send", 63 | method: "POST", 64 | body: { ...sqsData, dataList, batch_enabled: true, data: undefined }, 65 | }); 66 | 67 | return result; 68 | } 69 | } 70 | 71 | export default AWSSQS; 72 | -------------------------------------------------------------------------------- /src/modules/Services/Attachment.ts: -------------------------------------------------------------------------------- 1 | import TagoIOModule, { GenericModuleParams } from "../../common/TagoIOModule"; 2 | import { Base64 } from "../../common/common.types"; 3 | 4 | interface ArchiveFile { 5 | name: string; 6 | content: Base64; 7 | type: string; 8 | } 9 | 10 | class Attachment extends TagoIOModule { 11 | /** 12 | * Send Attachment 13 | * @param archive Archive JSON Object 14 | */ 15 | public async upload(archive: ArchiveFile): Promise { 16 | const result = await this.doRequest({ 17 | path: "/analysis/services/attachment/upload", 18 | method: "POST", 19 | body: { 20 | archive: archive.content, 21 | filename: archive.name, 22 | type: archive.type, 23 | }, 24 | }); 25 | 26 | return result; 27 | } 28 | } 29 | 30 | export default Attachment; 31 | -------------------------------------------------------------------------------- /src/modules/Services/Console.ts: -------------------------------------------------------------------------------- 1 | import TagoIOModule, { GenericModuleParams } from "../../common/TagoIOModule"; 2 | 3 | class ConsoleService extends TagoIOModule { 4 | /** 5 | * Log message in analysis console 6 | * @param message Log message 7 | * @param time Date of message 8 | */ 9 | public async log(message: string, time?: Date): Promise { 10 | const timestamp = new Date(time).getTime(); 11 | 12 | const result = await this.doRequest({ 13 | path: "/analysis/services/console/send", 14 | method: "POST", 15 | body: { message, timestamp: timestamp }, 16 | }); 17 | 18 | return result; 19 | } 20 | } 21 | 22 | export default ConsoleService; 23 | -------------------------------------------------------------------------------- /src/modules/Services/MQTT.ts: -------------------------------------------------------------------------------- 1 | import TagoIOModule, { GenericModuleParams } from "../../common/TagoIOModule"; 2 | import { GenericID } from "../../common/common.types"; 3 | 4 | interface MQTTData { 5 | /** Topic of the message */ 6 | topic: string; 7 | /** Message scope */ 8 | message: string; 9 | /** Device to receive message */ 10 | device: GenericID; 11 | /** Options of the publishing message */ 12 | options?: { 13 | /** Default 0 */ 14 | qos?: number; 15 | }; 16 | } 17 | 18 | interface MQTTDataDeprecated extends Omit { 19 | /** 20 | * Bucket to receive message 21 | * @deprecated use "device" instead 22 | */ 23 | bucket: GenericID; 24 | } 25 | 26 | class MQTT extends TagoIOModule { 27 | /** 28 | * Publish MQTT 29 | * @param mqtt MQTT Object 30 | */ 31 | public async publish(mqtt: MQTTData | MQTTDataDeprecated): Promise { 32 | let device: GenericID; 33 | if ("device" in mqtt) { 34 | device = mqtt.device; 35 | } else { 36 | device = mqtt.bucket; 37 | } 38 | 39 | const result = await this.doRequest({ 40 | path: "/analysis/services/mqtt/publish", 41 | method: "POST", 42 | body: { 43 | topic: mqtt.topic, 44 | message: mqtt.message, 45 | device: device, 46 | ...mqtt.options, 47 | }, 48 | }); 49 | 50 | return result; 51 | } 52 | } 53 | 54 | export default MQTT; 55 | -------------------------------------------------------------------------------- /src/modules/Services/Notification.ts: -------------------------------------------------------------------------------- 1 | import TagoIOModule, { GenericModuleParams } from "../../common/TagoIOModule"; 2 | import { GenericID } from "../../common/common.types"; 3 | import { NotificationCreate } from "../Resources/notifications.types"; 4 | 5 | class Notification extends TagoIOModule { 6 | /** 7 | * Send Notification 8 | * You can add ref_id from a bucket or dashboard, 9 | * if it is valid it will show up a button Go To Dashboard 10 | * Any account with share of the dashboard/bucket will receive too. 11 | * @param notification Notification Object 12 | */ 13 | public async send(notification: NotificationCreate): Promise { 14 | const result = await this.doRequest({ 15 | path: "/analysis/services/notification/send", 16 | method: "POST", 17 | body: notification, 18 | }); 19 | 20 | return result; 21 | } 22 | } 23 | 24 | export default Notification; 25 | -------------------------------------------------------------------------------- /src/modules/Services/SMS.ts: -------------------------------------------------------------------------------- 1 | import TagoIOModule, { GenericModuleParams } from "../../common/TagoIOModule"; 2 | 3 | interface SMSData { 4 | /** Number to send SMS, Example: +5599999999999 */ 5 | to: string; 6 | /** Message to be send */ 7 | message: string; 8 | } 9 | 10 | class SMS extends TagoIOModule { 11 | /** 12 | * Send SMS to phone number 13 | * @param sms SMS Object 14 | */ 15 | public async send(sms: SMSData): Promise { 16 | const result = await this.doRequest({ 17 | path: "/analysis/services/sms/send", 18 | method: "POST", 19 | body: sms, 20 | }); 21 | 22 | return result; 23 | } 24 | } 25 | 26 | export default SMS; 27 | -------------------------------------------------------------------------------- /src/modules/Services/Services.ts: -------------------------------------------------------------------------------- 1 | import TagoIOModule, { GenericModuleParams } from "../../common/TagoIOModule"; 2 | import Attachment from "./Attachment"; 3 | import AWSSQS from "./AWS-SQS"; 4 | import ConsoleService from "./Console"; 5 | import Email from "./Email"; 6 | import MQTT from "./MQTT"; 7 | import Notification from "./Notification"; 8 | import PDFService from "./PDF"; 9 | import Sendgrid from "./Sendgrid"; 10 | import SMS from "./SMS"; 11 | import SMTP from "./SMTP"; 12 | import TwilioWhatsapp from "./Twilio-Whatsapp"; 13 | import Twilio from "./Twillio"; 14 | 15 | class Services extends TagoIOModule { 16 | constructor(params?: GenericModuleParams) { 17 | super({ token: process.env.T_ANALYSIS_TOKEN, ...params }); 18 | } 19 | 20 | public console = new ConsoleService(this.params); 21 | static get console() { 22 | return new this().console; 23 | } 24 | 25 | public sms = new SMS(this.params); 26 | static get sms() { 27 | return new this().sms; 28 | } 29 | 30 | public email = new Email(this.params); 31 | static get email() { 32 | return new this().email; 33 | } 34 | 35 | public twilio = new Twilio(this.params); 36 | static get twilio() { 37 | return new this().twilio; 38 | } 39 | 40 | public smtp = new SMTP(this.params); 41 | static get smtp() { 42 | return new this().smtp; 43 | } 44 | 45 | public aws_sqs = new AWSSQS(this.params); 46 | static get aws_sqs() { 47 | return new this().aws_sqs; 48 | } 49 | 50 | public sendgrid = new Sendgrid(this.params); 51 | static get sendgrid() { 52 | return new this().sendgrid; 53 | } 54 | 55 | public twilio_whatsapp = new TwilioWhatsapp(this.params); 56 | static get twilio_whatsapp() { 57 | return new this().twilio_whatsapp; 58 | } 59 | 60 | /** @internal @deprecated renamed to .mqtt (lowercase) */ 61 | public MQTT = new MQTT(this.params); 62 | public mqtt = new MQTT(this.params); 63 | static get mqtt() { 64 | return new this().mqtt; 65 | } 66 | 67 | /** @internal @deprecated renamed to .notification (lowercase) */ 68 | public Notification = new Notification(this.params); 69 | public notification = new Notification(this.params); 70 | static get notification() { 71 | return new this().notification; 72 | } 73 | 74 | /** @internal @deprecated renamed to .attachment (lowercase) */ 75 | public Attachment = new Attachment(this.params); 76 | public attachment = new Attachment(this.params); 77 | static get attachment() { 78 | return new this().attachment; 79 | } 80 | 81 | /** @internal @deprecated renamed to .pdf (lowercase) */ 82 | public PDF = new PDFService(this.params); 83 | public pdf = new PDFService(this.params); 84 | static get pdf() { 85 | return new this().pdf; 86 | } 87 | } 88 | 89 | export default Services; 90 | -------------------------------------------------------------------------------- /src/modules/Services/Twillio.ts: -------------------------------------------------------------------------------- 1 | import TagoIOModule, { type GenericModuleParams } from "../../common/TagoIOModule"; 2 | 3 | interface TwilioData { 4 | /** Number to send SMS, Example: +5599999999999 */ 5 | to: string; 6 | /** Message to be send */ 7 | message: string; 8 | /** From number registered with Twilio, Example: +5599999999999 */ 9 | from: string; 10 | /** Twilio account SID */ 11 | twilio_sid: string; 12 | /** Twilio auth token */ 13 | twilio_token: string; 14 | } 15 | 16 | class Twilio extends TagoIOModule { 17 | /** 18 | * Send SMS to a phone number using Twilio Integration 19 | * 20 | * @param sms - The SMS object containing all necessary information 21 | * @returns A promise that resolves to a success message 22 | * 23 | * @remarks 24 | * This method requires a Twilio account with valid credentials (SID and Token). 25 | * For enhanced security, it's strongly recommended to store these credentials 26 | * using TagoIO Secrets rather than hardcoding them. 27 | * 28 | * @see {@link https://www.twilio.com/console} for Twilio account management 29 | * @see {@link https://help.tago.io/portal/en/kb/articles/secrets} for TagoIO Secrets usage 30 | * 31 | * @example 32 | * ```typescript 33 | * const environment = Utils.envToJson(context.environment); 34 | * const twilioService = new Services({ token: context.token }).twilio; 35 | * const result = await twilioService.send({ 36 | * to: "+1234567890", 37 | * message: "Hello from TagoIO!", 38 | * from: "+0987654321", 39 | * twilio_sid: environment.TWILIO_SID, 40 | * twilio_token: environment.TWILIO_TOKEN, 41 | * }); 42 | * console.log(result); 43 | * ``` 44 | * @example 45 | * ```typescript 46 | * const environment = Utils.envToJson(context.environment); 47 | * const twilioService = new Services({ token: context.token }).twilio; 48 | * const result = await twilioService.send({ 49 | * to: "+1234567890", 50 | * message: "Hello from TagoIO!", 51 | * from: "MYFROMID", 52 | * twilio_sid: environment.TWILIO_SID, 53 | * twilio_token: environment.TWILIO_TOKEN, 54 | * }); 55 | * console.log(result); 56 | * ``` 57 | * @example 58 | * ```typescript 59 | * const environment = Utils.envToJson(context.environment); 60 | * const twilioService = new Services({ token: context.token }).twilio; 61 | * const result = await twilioService.send({ 62 | * to: "+1234567890", 63 | * message: "Hello from TagoIO!", 64 | * from: "MGXXXXXXXXXXXXXXXX", 65 | * twilio_sid: environment.TWILIO_SID, 66 | * twilio_token: environment.TWILIO_TOKEN, 67 | * }); 68 | * console.log(result); 69 | * ``` 70 | */ 71 | public async send(sms: TwilioData): Promise { 72 | const result = await this.doRequest({ 73 | path: "/analysis/services/sms-twilio/send", 74 | method: "POST", 75 | body: sms, 76 | }); 77 | 78 | return result; 79 | } 80 | } 81 | 82 | export default Twilio; 83 | -------------------------------------------------------------------------------- /src/modules/Utils/Utils.ts: -------------------------------------------------------------------------------- 1 | export { default as envToJson } from "./envToJson"; 2 | export { default as getAPIVersion } from "./getAPIVersion"; 3 | export { default as getTokenByName } from "./getTokenByName"; 4 | export { default as AnalysisRouter } from "./router/router"; 5 | export { default as getDevice } from "./getDevice"; 6 | export { default as uploadFile } from "./uploadFile"; 7 | export { default as sendDownlink } from "./sendDownlink"; 8 | export { default as updateMultipleDropdown } from "./updateMultipleDropdown"; 9 | export { default as parseLorawanQRCode } from "./parseLorawanQRCode"; 10 | -------------------------------------------------------------------------------- /src/modules/Utils/dateParser.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Receive an object and a list of parameters to be parsed to Date object 3 | */ 4 | export default function dateParser(target: T, parameters: (keyof T)[]): T { 5 | target = { ...target }; 6 | 7 | for (const parameter of parameters) { 8 | const value: unknown = target[parameter]; 9 | 10 | if (value && typeof value === "string" && value !== "never") { 11 | const parsedDate = new Date(value); 12 | 13 | if (!isNaN(parsedDate.getTime())) { 14 | (target[parameter] as unknown) = parsedDate; 15 | } 16 | } 17 | } 18 | 19 | return target; 20 | } 21 | -------------------------------------------------------------------------------- /src/modules/Utils/envToJson.ts: -------------------------------------------------------------------------------- 1 | import { AnalysisEnvironment } from "../Analysis/analysis.types"; 2 | 3 | /** 4 | * Convert Environment Array to Object 5 | * Note: It will replace duplicate keys for the last one 6 | * @param environment Array of environment items from TagoIO 7 | */ 8 | function envToJson(environment: AnalysisEnvironment[]): { [key: string]: string } { 9 | return environment.reduce((pv, cv) => { 10 | pv[cv.key] = cv.value; 11 | return pv; 12 | }, {}); 13 | } 14 | 15 | export default envToJson; 16 | -------------------------------------------------------------------------------- /src/modules/Utils/getAPIVersion.ts: -------------------------------------------------------------------------------- 1 | import TagoIOModule, { GenericModuleParams, doRequestParams } from "../../common/TagoIOModule"; 2 | import { Regions, RegionsObj } from "../../regions"; 3 | 4 | class GetAPIVersion extends TagoIOModule { 5 | public static async getVersion(region?: Regions | RegionsObj): Promise { 6 | const params: doRequestParams = { 7 | path: "/status", 8 | method: "GET", 9 | }; 10 | 11 | const result = await TagoIOModule.doRequestAnonymous<{ version: string }>(params, region); 12 | 13 | return result.version; 14 | } 15 | } 16 | 17 | export default GetAPIVersion.getVersion; 18 | -------------------------------------------------------------------------------- /src/modules/Utils/getDevice.ts: -------------------------------------------------------------------------------- 1 | import Device from "../Device/Device"; 2 | import Account from "../Resources/AccountDeprecated"; 3 | import getTokenByName from "./getTokenByName"; 4 | 5 | /** 6 | * Get the TagoIO Device instanced class from a device id 7 | * @deprecated Use the Resources.devices methods instead 8 | */ 9 | async function getDevice(account: Account, device_id: string): Promise { 10 | if (!(account instanceof Account)) { 11 | throw "The parameter 'account' must be an instance of a TagoIO Account."; 12 | } 13 | 14 | const token = await getTokenByName(account, device_id); 15 | if (!token) { 16 | throw `Token not found for the device id ${device_id}`; 17 | } 18 | 19 | const device = new Device({ token }); 20 | 21 | return device; 22 | } 23 | 24 | export default getDevice; 25 | -------------------------------------------------------------------------------- /src/modules/Utils/getTokenByName.ts: -------------------------------------------------------------------------------- 1 | import Account from "../Resources/AccountDeprecated"; 2 | 3 | /** 4 | * 5 | * @param account Account instance 6 | * @param deviceID Id of device 7 | * @param names Array of names of the token, if null will return the first token found 8 | * @deprecated Use the Resources.devices.tokenList method instead 9 | */ 10 | async function getTokenByName(account: Account, deviceID: string, names?: string[] | string): Promise { 11 | if (!(account instanceof Account)) { 12 | throw "Account parameter must be an instance of TagoIO Account."; 13 | } 14 | 15 | const tokens = await account.devices.tokenList(deviceID); 16 | if (!tokens || !tokens[0]) { 17 | return null; 18 | } 19 | 20 | if (!names || !names.length) { 21 | return tokens[0]?.token; 22 | } 23 | 24 | const namesArray = Array.isArray(names) ? names : [names]; 25 | 26 | const token = tokens.find((t) => namesArray.some((n) => t.name.includes(n))); 27 | 28 | if (!token) { 29 | throw `Can't find Token for ${deviceID} in ${namesArray.join(", ")}`; 30 | } 31 | 32 | return token.token; 33 | } 34 | 35 | export default getTokenByName; 36 | -------------------------------------------------------------------------------- /src/modules/Utils/parseLorawanQRCode.ts: -------------------------------------------------------------------------------- 1 | import { QRCodeFormat } from "./utils.types"; 2 | 3 | /** 4 | * Parse a LoRaWAN QR Code string to JSON format. 5 | * example: 6 | * @param qr_code QR Code string, example: LW:D0:1122334455667788:AABBCCDDEEFF0011:AABB1122:OAABBCCDDEEFF:SYYWWNNNNNN:PFOOBAR:CAF2C 7 | */ 8 | function parseQRCode(qr_code: string): QRCodeFormat { 9 | if (typeof qr_code !== "string") { 10 | throw "QR Code must be a string"; 11 | } 12 | 13 | const parsed = qr_code.split(":"); 14 | 15 | if (parsed.length < 4) { 16 | throw "Invalid QR Code"; 17 | } 18 | 19 | if (parsed.length >= 9) { 20 | return { 21 | type: parsed[0], 22 | schema_id: parsed[1], 23 | join_eui: parsed[2], 24 | dev_eui: parsed[3], 25 | profile_id: parsed[4], 26 | owner_token: parsed[5], 27 | sn_number: parsed[6], 28 | proprietary: parsed[7], 29 | checksum: parsed[8], 30 | }; 31 | } 32 | 33 | return { 34 | type: parsed[0], 35 | schema_id: parsed[1], 36 | join_eui: parsed[2], 37 | dev_eui: parsed[3], 38 | profile_id: parsed[4], 39 | }; 40 | } 41 | 42 | export default parseQRCode; 43 | -------------------------------------------------------------------------------- /src/modules/Utils/router/router.ts: -------------------------------------------------------------------------------- 1 | import Account from "../../Resources/AccountDeprecated"; 2 | import { RouterConstructor } from "./router.types"; 3 | import RouterService from "./service"; 4 | class AnalysisRouter { 5 | services: RouterService[] = []; 6 | 7 | /** 8 | * Create an Analysis Router. 9 | * Use router.register to register new routes for your analysis. 10 | * Use router.exec() to execute the router and run your functions. 11 | * Example: 12 | * router.register(myFunction).whenInputFormID('create-device-input'); 13 | * router.exec(); 14 | */ 15 | constructor(private params: RouterConstructor) { 16 | if (params.account && !(params.account instanceof Account)) { 17 | throw "The parameter 'account' must be an instance of a TagoIO Account."; 18 | } 19 | } 20 | 21 | public register(func: (parameters: RouterConstructor) => any) { 22 | const service = new RouterService(func); 23 | this.services.push(service); 24 | 25 | return service; 26 | } 27 | 28 | /** 29 | * Start the router. It will pick the service if all conditions are match 30 | * and send all parameter provided to the final function. 31 | * @returns json with status and services that run 32 | */ 33 | public async exec() { 34 | const my_list: string[] = []; 35 | for (const service of this.services) { 36 | if (!service.verifyConditionsTrue(this.params.scope, this.params.environment)) { 37 | continue; 38 | } 39 | 40 | service.runService(this.params); 41 | my_list.push(service.getServiceName()); 42 | } 43 | 44 | return { status: !!my_list.length, services: my_list }; 45 | } 46 | } 47 | 48 | export { RouterConstructor }; 49 | export default AnalysisRouter; 50 | -------------------------------------------------------------------------------- /src/modules/Utils/router/router.types.ts: -------------------------------------------------------------------------------- 1 | import { TagoContext } from "../../Analysis/analysis.types"; 2 | import Device from "../../Device/Device"; 3 | import Account from "../../Resources/AccountDeprecated"; 4 | import { UserCreateInfo } from "../../Resources/run.types"; 5 | 6 | /** 7 | * User List scope to be used in your analysis 8 | * example: 9 | * - async function editUser({ scope }: RouterConstructor & { scope: UserListScope[] }) {} 10 | */ 11 | interface UserListScope extends Partial> { 12 | user: string; 13 | /** Tag keys are formated as tags.key and parameters as param.key */ 14 | [key: string]: string | any; 15 | 16 | /** old parameter key will include a json with the old values of the parameters */ 17 | old?: { [key: string]: string } & Partial>; 18 | } 19 | 20 | /** 21 | * Custom Button scope to be used in your analysis functions 22 | * example: 23 | * - async function customBtnService({ scope }: RouterConstructor & { scope: CustomBtnScope[] }) {} 24 | */ 25 | interface CustomBtnScope { 26 | /** only when using User List */ 27 | user?: string; 28 | /** only when using Device List and Dynamic Table */ 29 | device?: string; // 30 | /** only when using Dynamic Table */ 31 | group?: string; 32 | /** only when using Dynamic Table */ 33 | variable?: string; 34 | /** only when using Dynamic Table */ 35 | value?: string; 36 | displayValue: string; 37 | property: string; 38 | } 39 | 40 | /** 41 | * Device List scope to be used in your analysis functions 42 | * example: 43 | * - async function editDevice({ scope }: RouterConstructor & { scope: DeviceListScope[] }) {} 44 | */ 45 | interface DeviceListScope { 46 | device: string; 47 | name?: string; 48 | 49 | /** Tag keys are formated as tags.key and parameters as param.key */ 50 | [key: string]: string | any; 51 | 52 | /** old parameter key will include a json with the old values of the parameters */ 53 | old?: { name?: string; [key: string]: string }; 54 | } 55 | 56 | /** 57 | * Router interface to be used inside your analysis 58 | */ 59 | interface RouterConstructor { 60 | /** scope of your analysis */ 61 | scope: any[]; 62 | 63 | /** environment variable parsed with Utils.envToJson(context.environment) */ 64 | environment: { [key: string]: string }; 65 | 66 | /** instanced account class with new Account({ token: "Your-token" }) */ 67 | account?: Account; 68 | 69 | /** instanced device class with new Device({ token: "Your-token" }) */ 70 | config_dev?: Device; 71 | 72 | /** context of your analysis */ 73 | context?: TagoContext; 74 | } 75 | 76 | export { RouterConstructor, UserListScope, DeviceListScope, CustomBtnScope }; 77 | -------------------------------------------------------------------------------- /src/modules/Utils/router/types.ts: -------------------------------------------------------------------------------- 1 | export * as Router from "./router.types"; 2 | -------------------------------------------------------------------------------- /src/modules/Utils/types.ts: -------------------------------------------------------------------------------- 1 | export * as Utils from "./utils.types"; 2 | -------------------------------------------------------------------------------- /src/modules/Utils/updateMultipleDropdown.ts: -------------------------------------------------------------------------------- 1 | import Device from "../Device/Device"; 2 | 3 | /** 4 | * Go through variables used in Multiple Dropdown variables and remove a specified value. 5 | * Then updates the variables. 6 | * @param device TagoIO Device instanced class. 7 | * @param variables variables inside the device to be verified. 8 | * @param value value to be removed. 9 | */ 10 | async function updateMultipleDropdown(device: Device, variables: string | string[], values: string | string[]) { 11 | const fixed_values = Array.isArray(values) ? values : [values]; 12 | 13 | const data_list = await device.getData({ variables, qty: 999 }); 14 | 15 | data_list.forEach((item) => { 16 | let sentValues = item.metadata?.sentValues as { value: string; label: string }[]; 17 | let new_data_value = (item.value as string).split(";"); 18 | 19 | if (sentValues.find((x) => fixed_values.includes(x.value))) { 20 | sentValues = sentValues.filter((x) => { 21 | if (fixed_values.includes(x.value)) { 22 | new_data_value = new_data_value.filter((y) => !fixed_values.includes(y) && !fixed_values.includes(x.label)); 23 | } 24 | return !fixed_values.includes(x.value); 25 | }); 26 | 27 | const new_item = { 28 | ...item, 29 | value: new_data_value.join(";"), 30 | metadata: { ...item.metadata, sentValues }, 31 | } as any; 32 | 33 | device.deleteData({ ids: item.id }).catch((e) => console.error(e)); 34 | device.sendData(new_item).catch((e) => console.error(e)); 35 | } 36 | }); 37 | } 38 | 39 | export default updateMultipleDropdown; 40 | -------------------------------------------------------------------------------- /src/modules/Utils/uploadFile.ts: -------------------------------------------------------------------------------- 1 | import path from "path"; 2 | 3 | import Account from "../Resources/AccountDeprecated"; 4 | import Resources from "../Resources/Resources"; 5 | import { UploadFileOptions } from "./utils.types"; 6 | 7 | type FileURL = string; 8 | 9 | /** 10 | * Upload a file and return it's URL. 11 | * @requires Profile Access for the Analysis 12 | * @requires Files Access for the Analysis 13 | */ 14 | async function uploadFile(resource: Account | Resources, options: UploadFileOptions): Promise { 15 | if (!(resource instanceof Account) && !(resource instanceof Resources)) { 16 | throw "The parameter 'resource' must be an instance of a TagoIO Resource."; 17 | } 18 | 19 | const { info: profileInfo } = await resource.profiles.info("current"); 20 | 21 | if (options.path) { 22 | if (!options.path.includes("/")) { 23 | throw `Invalid file path: ${options.path}`; 24 | } 25 | 26 | if (options.path.startsWith("/")) { 27 | options.path = options.path.substring(1); 28 | } 29 | } 30 | 31 | const fixed_path = path.join(options.path || "", options.filename); 32 | 33 | const body = { file: options.file_base64, filename: fixed_path }; 34 | await resource.files.uploadBase64([{ ...body, public: options.public || true }]); 35 | 36 | return `https://api.tago.io/file/${profileInfo.id}/${fixed_path}`; 37 | } 38 | 39 | export default uploadFile; 40 | -------------------------------------------------------------------------------- /src/modules/Utils/utils.types.ts: -------------------------------------------------------------------------------- 1 | import { GenericID, Query } from "../../common/common.types"; 2 | 3 | interface QRCodeFormat { 4 | type: string; 5 | schema_id: string; 6 | join_eui: string; 7 | dev_eui: string; 8 | profile_id: string; 9 | owner_token?: string; 10 | sn_number?: string; 11 | proprietary?: string; 12 | checksum?: string; 13 | } 14 | 15 | interface UploadFileOptions { 16 | filename: string; 17 | file_base64: string; 18 | public?: boolean; 19 | /** Path where the file will be stored. Such as /reports/ */ 20 | path?: string; 21 | } 22 | 23 | type HexadecimalPayload = string; 24 | interface DownlinkOptions { 25 | payload: HexadecimalPayload; 26 | port: string; 27 | confirmed?: boolean; 28 | } 29 | 30 | export { QRCodeFormat, UploadFileOptions, DownlinkOptions }; 31 | -------------------------------------------------------------------------------- /src/regions.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * @internal 3 | */ 4 | // let noRegionWarning = false; 5 | 6 | interface RegionsObj { 7 | api: string; 8 | realtime?: string; 9 | sse: string; 10 | } 11 | 12 | type Regions = "us-e1" | "eu-w1" | "env"; 13 | 14 | /** 15 | * Object of Regions Definition 16 | * @internal 17 | */ 18 | const regionsDefinition = { 19 | "us-e1": { 20 | api: "https://api.tago.io", 21 | realtime: "https://realtime.tago.io", 22 | sse: "https://sse.tago.io/events", 23 | }, 24 | "eu-w1": { 25 | api: "https://api.eu-w1.tago.io", 26 | sse: "https://sse.eu-w1.tago.io/events", 27 | }, 28 | env: undefined as void, // ? process object should be on trycatch. 29 | }; 30 | 31 | let runtimeRegion: RegionsObj | undefined; 32 | 33 | /** 34 | * Get connection URI for Realtime and API 35 | * @internal 36 | * @param region Region 37 | */ 38 | function getConnectionURI(region?: Regions | RegionsObj): RegionsObj { 39 | // @ts-expect-error Fallback 40 | if (region === "usa-1") { 41 | region = "us-e1"; 42 | } 43 | 44 | const value = 45 | typeof region === "string" ? regionsDefinition[region] : typeof region === "object" ? region : undefined; 46 | 47 | if (value) { 48 | return value; 49 | } 50 | 51 | if (runtimeRegion) { 52 | return runtimeRegion; 53 | } 54 | 55 | if (region !== undefined && !region !== null && region !== "env") { 56 | throw new ReferenceError(`> TagoIO-SDK: Invalid region ${region}.`); 57 | } 58 | 59 | try { 60 | const api = process.env.TAGOIO_API; 61 | const realtime = process.env.TAGOIO_REALTIME; 62 | const sse = process.env.TAGOIO_SSE; 63 | 64 | if (!api && region !== "env") { 65 | throw "Invalid Env"; 66 | } 67 | 68 | return { api, realtime, sse }; 69 | } catch (error) { 70 | // if (!noRegionWarning) { 71 | // console.info("> TagoIO-SDK: No region or env defined, using fallback as usa-1."); 72 | // noRegionWarning = true; 73 | // } 74 | 75 | return regionsDefinition["us-e1"]; 76 | } 77 | } 78 | 79 | /** 80 | * Set region in-memory to be inherited by other modules when set in the Analysis runtime 81 | * with `Analysis.use()`. 82 | * 83 | * @example 84 | * 85 | * ```ts 86 | * async function myAnalysis(context, scope) { 87 | * // this uses the region defined through `use` 88 | * const resources = new Resources({ token }); 89 | * 90 | * // it's still possible to override if needed 91 | * const europeResources = new Resources({ token, region: "eu-w1" }); 92 | * } 93 | * 94 | * Analysis.use(myAnalysis, { region: "us-e1" }); 95 | * ``` 96 | */ 97 | function setRuntimeRegion(region: RegionsObj) { 98 | runtimeRegion = region; 99 | } 100 | 101 | export default getConnectionURI; 102 | export type { Regions, RegionsObj }; 103 | export { regionsDefinition, setRuntimeRegion }; 104 | -------------------------------------------------------------------------------- /src/types.ts: -------------------------------------------------------------------------------- 1 | export * from "./common/common.types"; 2 | 3 | export * from "./modules/Resources/account.types"; 4 | export * from "./modules/Resources/access.types"; 5 | export * from "./modules/Resources/actions.types"; 6 | export * from "./modules/Resources/analysis.types"; 7 | export * from "./modules/Resources/buckets.types"; 8 | export * from "./modules/Resources/dashboards.types"; 9 | export * from "./modules/Resources/devices.types"; 10 | export * from "./modules/Resources/dictionaries.types"; 11 | export * from "./modules/Resources/files.types"; 12 | export * from "./modules/Resources/notifications.types"; 13 | export * from "./modules/Resources/plan.types"; 14 | export * from "./modules/Resources/profile.types"; 15 | export * from "./modules/Resources/run.types"; 16 | export * from "./modules/Resources/template.types"; 17 | export * from "./modules/Resources/paymentHistory.types"; 18 | export * from "./modules/Resources/billing.types"; 19 | export * from "./modules/Resources/integration.networks.types"; 20 | export * from "./modules/Resources/integration.connectors.types"; 21 | export * from "./modules/Resources/tagocore.types"; 22 | 23 | export * from "./modules/Analysis/analysis.types"; 24 | export * from "./modules/Device/device.types"; 25 | export * from "./modules/Authorization/authorization.types"; 26 | export * from "./modules/RunUser/runUser.types"; 27 | export * from "./modules/Network/network.types"; 28 | export * from "./modules/Dictionary/dictionary.types"; 29 | // export * from "./modules/Services/types"; 30 | // export * from "./modules/Utils/types"; 31 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "outDir": "./lib", 4 | "rootDir": "./src", 5 | "noImplicitAny": true, 6 | "module": "commonjs", 7 | "target": "ES2020", 8 | "allowJs": false, 9 | "moduleResolution": "node", 10 | "esModuleInterop": true, 11 | "declaration": true, 12 | "declarationMap": true, 13 | "sourceMap": true, 14 | "allowSyntheticDefaultImports": true, 15 | "resolveJsonModule": true, 16 | "lib": ["ES2020", "DOM"] 17 | }, 18 | "include": ["src/**/*"], 19 | "exclude": ["src/**/*.test.*", "lib"] 20 | } 21 | -------------------------------------------------------------------------------- /typedoc.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "TagoIO SDK for JavaScript and TypeScript", 3 | "readme": "./DOCS.md", 4 | "entryPoints": ["./src/modules.ts"], 5 | "entryPointStrategy": "resolve", 6 | "out": "docs", 7 | "gaID": "UA-57075518-5", 8 | "exclude": ["./src/common/Cache.ts", "./src/modules/Migration/Migration.ts", "./src/infrastructure/apiSSE.ts"], 9 | "excludePrivate": true, 10 | "excludeProtected": true, 11 | "excludeInternal": true, 12 | "excludeExternals": true, 13 | "githubPages": true, 14 | "hideGenerator": true, 15 | "cleanOutputDir": true, 16 | "plugin": ["typedoc-plugin-missing-exports", "typedoc-plugin-extras"], 17 | "favicon": "assets/favicon.png" 18 | } 19 | -------------------------------------------------------------------------------- /updateEnv.js: -------------------------------------------------------------------------------- 1 | const fs = require("fs"); 2 | const packageJson = require("./package.json"); 3 | 4 | fs.writeFileSync("./src/infrastructure/envParams.json", `{"version": "${packageJson.version}"}`, { 5 | encoding: "utf-8", 6 | }); 7 | --------------------------------------------------------------------------------