├── .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 |
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 |
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 |
--------------------------------------------------------------------------------