edilkamin 1.3.2 → 1.4.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.github/workflows/cli-tests.yml +4 -1
- package/.github/workflows/documentation.yml +8 -5
- package/.github/workflows/publish.yml +5 -3
- package/.github/workflows/tests.yml +14 -3
- package/README.md +1 -0
- package/dist/{cli.js → esm/cli.js} +15 -20
- package/dist/esm/constants.js +2 -0
- package/dist/{index.d.ts → esm/index.d.ts} +1 -1
- package/dist/esm/index.js +4 -0
- package/dist/esm/library.d.ts +26 -0
- package/dist/esm/library.js +84 -0
- package/dist/esm/library.test.js +168 -0
- package/dist/esm/types.js +1 -0
- package/eslint.config.mjs +35 -0
- package/package.json +32 -9
- package/src/cli.ts +4 -2
- package/src/index.ts +1 -3
- package/src/library.test.ts +136 -36
- package/src/library.ts +46 -18
- package/tsconfig.cjs.json +7 -0
- package/tsconfig.esm.json +7 -0
- package/tsconfig.json +8 -7
- package/dist/constants.js +0 -5
- package/dist/index.js +0 -11
- package/dist/library.d.ts +0 -12
- package/dist/library.js +0 -99
- package/dist/library.test.js +0 -81
- package/dist/types.js +0 -2
- /package/dist/{cli.d.ts → esm/cli.d.ts} +0 -0
- /package/dist/{constants.d.ts → esm/constants.d.ts} +0 -0
- /package/dist/{library.test.d.ts → esm/library.test.d.ts} +0 -0
- /package/dist/{types.d.ts → esm/types.d.ts} +0 -0
|
@@ -8,10 +8,13 @@ jobs:
|
|
|
8
8
|
tests:
|
|
9
9
|
runs-on: ubuntu-latest
|
|
10
10
|
timeout-minutes: 5
|
|
11
|
+
strategy:
|
|
12
|
+
matrix:
|
|
13
|
+
node-version: [20.x, 22.x]
|
|
11
14
|
steps:
|
|
12
15
|
- uses: actions/checkout@v4
|
|
13
16
|
- uses: actions/setup-node@v4
|
|
14
17
|
with:
|
|
15
|
-
node-version:
|
|
18
|
+
node-version: ${{ matrix.node-version }}
|
|
16
19
|
- run: yarn install --no-ignore-optional
|
|
17
20
|
- run: yarn cli --help
|
|
@@ -2,17 +2,17 @@ name: Documentation
|
|
|
2
2
|
|
|
3
3
|
on:
|
|
4
4
|
push:
|
|
5
|
-
|
|
6
|
-
- main
|
|
5
|
+
pull_request:
|
|
7
6
|
|
|
8
7
|
jobs:
|
|
9
8
|
deploy:
|
|
10
9
|
runs-on: ubuntu-latest
|
|
10
|
+
timeout-minutes: 5
|
|
11
11
|
steps:
|
|
12
12
|
- uses: actions/checkout@v4
|
|
13
13
|
- uses: actions/setup-node@v4
|
|
14
14
|
with:
|
|
15
|
-
node-version: "
|
|
15
|
+
node-version: "22.x"
|
|
16
16
|
- name: git config
|
|
17
17
|
run: |
|
|
18
18
|
git config user.name documentation-deploy-action
|
|
@@ -20,7 +20,7 @@ jobs:
|
|
|
20
20
|
git remote set-url origin https://${{github.actor}}:${{github.token}}@github.com/${{github.repository}}.git
|
|
21
21
|
- run: yarn install
|
|
22
22
|
- run: yarn typedoc src/index.ts --out /tmp/docs
|
|
23
|
-
- name:
|
|
23
|
+
- name: commit documentation
|
|
24
24
|
run: |
|
|
25
25
|
git ls-remote --exit-code . origin/gh-pages \
|
|
26
26
|
&& git checkout -b gh-pages \
|
|
@@ -30,5 +30,8 @@ jobs:
|
|
|
30
30
|
cp --recursive /tmp/docs/. .
|
|
31
31
|
echo /node_modules > .gitignore
|
|
32
32
|
git add --all
|
|
33
|
-
git commit --all --message ":memo: docs: Update generated documentation"
|
|
33
|
+
git commit --all --message ":memo: docs: Update generated documentation" || echo "No changes to commit"
|
|
34
|
+
- name: push documentation
|
|
35
|
+
if: github.ref_name == 'main'
|
|
36
|
+
run: |
|
|
34
37
|
git push origin gh-pages
|
|
@@ -2,20 +2,22 @@ name: Publish
|
|
|
2
2
|
|
|
3
3
|
on:
|
|
4
4
|
push:
|
|
5
|
-
|
|
6
|
-
- "*"
|
|
5
|
+
pull_request:
|
|
7
6
|
|
|
8
7
|
jobs:
|
|
9
8
|
build:
|
|
10
9
|
runs-on: ubuntu-latest
|
|
10
|
+
timeout-minutes: 5
|
|
11
11
|
steps:
|
|
12
12
|
- uses: actions/checkout@v4
|
|
13
13
|
- uses: actions/setup-node@v4
|
|
14
14
|
with:
|
|
15
|
-
node-version: "
|
|
15
|
+
node-version: "22.x"
|
|
16
16
|
registry-url: "https://registry.npmjs.org"
|
|
17
17
|
- run: yarn install
|
|
18
18
|
- run: yarn build
|
|
19
|
+
- run: npm publish --dry-run
|
|
19
20
|
- run: npm publish
|
|
21
|
+
if: startsWith(github.ref, 'refs/tags/')
|
|
20
22
|
env:
|
|
21
23
|
NODE_AUTH_TOKEN: ${{ secrets.NODE_AUTH_TOKEN }}
|
|
@@ -5,14 +5,25 @@ on:
|
|
|
5
5
|
pull_request:
|
|
6
6
|
|
|
7
7
|
jobs:
|
|
8
|
-
|
|
8
|
+
tests:
|
|
9
9
|
runs-on: ubuntu-latest
|
|
10
|
+
timeout-minutes: 5
|
|
11
|
+
strategy:
|
|
12
|
+
matrix:
|
|
13
|
+
node-version: [20.x, 22.x]
|
|
10
14
|
steps:
|
|
11
15
|
- uses: actions/checkout@v4
|
|
12
16
|
- uses: actions/setup-node@v4
|
|
13
17
|
with:
|
|
14
|
-
node-version:
|
|
18
|
+
node-version: ${{ matrix.node-version }}
|
|
15
19
|
- run: yarn install
|
|
16
20
|
- run: yarn lint
|
|
17
21
|
- run: yarn build
|
|
18
|
-
- run:
|
|
22
|
+
- run: yarn test
|
|
23
|
+
- uses: codecov/codecov-action@v5
|
|
24
|
+
with:
|
|
25
|
+
files: ./coverage/lcov.info
|
|
26
|
+
token: ${{ secrets.CODECOV_TOKEN }}
|
|
27
|
+
fail_ci_if_error: true
|
|
28
|
+
env:
|
|
29
|
+
CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }}
|
package/README.md
CHANGED
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
[](https://github.com/AndreMiras/edilkamin.js/actions/workflows/tests.yml)
|
|
4
4
|
[](https://github.com/AndreMiras/edilkamin.js/actions/workflows/cli-tests.yml)
|
|
5
|
+
[](https://app.codecov.io/gh/AndreMiras/edilkamin.js/tree/main)
|
|
5
6
|
[](https://github.com/AndreMiras/edilkamin.js/actions/workflows/documentation.yml)
|
|
6
7
|
[](https://badge.fury.io/js/edilkamin)
|
|
7
8
|
|
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
"use strict";
|
|
3
2
|
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
|
4
3
|
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
|
5
4
|
return new (P || (P = Promise))(function (resolve, reject) {
|
|
@@ -9,17 +8,12 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
|
|
|
9
8
|
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
10
9
|
});
|
|
11
10
|
};
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
};
|
|
15
|
-
|
|
16
|
-
exports.main = void 0;
|
|
17
|
-
const library_1 = require("./library");
|
|
18
|
-
const commander_1 = require("commander");
|
|
19
|
-
const readline_1 = __importDefault(require("readline"));
|
|
20
|
-
const package_json_1 = require("../package.json");
|
|
11
|
+
import { Command } from "commander";
|
|
12
|
+
import readline from "readline";
|
|
13
|
+
import { version } from "../package.json";
|
|
14
|
+
import { configure, signIn } from "./library";
|
|
21
15
|
const promptPassword = () => {
|
|
22
|
-
const rl =
|
|
16
|
+
const rl = readline.createInterface({
|
|
23
17
|
input: process.stdin,
|
|
24
18
|
output: process.stdout,
|
|
25
19
|
terminal: true,
|
|
@@ -27,8 +21,8 @@ const promptPassword = () => {
|
|
|
27
21
|
return new Promise((resolve) => {
|
|
28
22
|
rl.question("Enter password: ", (password) => {
|
|
29
23
|
// Hide the password input
|
|
30
|
-
|
|
31
|
-
|
|
24
|
+
readline.moveCursor(process.stdout, 0, -1);
|
|
25
|
+
readline.clearLine(process.stdout, 0);
|
|
32
26
|
rl.close();
|
|
33
27
|
resolve(password);
|
|
34
28
|
});
|
|
@@ -49,16 +43,16 @@ const addCommonOptions = (command) => command
|
|
|
49
43
|
.requiredOption("-u, --username <username>", "Username")
|
|
50
44
|
.option("-p, --password <password>", "Password");
|
|
51
45
|
const createProgram = () => {
|
|
52
|
-
const program = new
|
|
46
|
+
const program = new Command();
|
|
53
47
|
program
|
|
54
48
|
.name("edilkamin-cli")
|
|
55
49
|
.description("CLI tool for interacting with the Edilkamin API")
|
|
56
|
-
.version(
|
|
50
|
+
.version(version);
|
|
57
51
|
// Command: signIn
|
|
58
52
|
addCommonOptions(program.command("signIn").description("Sign in and retrieve a JWT token")).action((options) => __awaiter(void 0, void 0, void 0, function* () {
|
|
59
53
|
const { username, password } = options;
|
|
60
54
|
const pwd = password || (yield promptPassword());
|
|
61
|
-
const jwtToken = yield
|
|
55
|
+
const jwtToken = yield signIn(username, pwd);
|
|
62
56
|
console.log("JWT Token:", jwtToken);
|
|
63
57
|
}));
|
|
64
58
|
// Command: deviceInfo
|
|
@@ -67,10 +61,11 @@ const createProgram = () => {
|
|
|
67
61
|
.description("Retrieve device info for a specific MAC address")
|
|
68
62
|
.requiredOption("-m, --mac <macAddress>", "MAC address of the device")).action((options) => __awaiter(void 0, void 0, void 0, function* () {
|
|
69
63
|
const { username, password, mac } = options;
|
|
64
|
+
const normalizedMac = mac.replace(/:/g, "");
|
|
70
65
|
const pwd = password || (yield promptPassword());
|
|
71
|
-
const jwtToken = yield
|
|
72
|
-
const api =
|
|
73
|
-
const deviceInfo = yield api.deviceInfo(jwtToken,
|
|
66
|
+
const jwtToken = yield signIn(username, pwd);
|
|
67
|
+
const api = configure(); // Use the default API configuration
|
|
68
|
+
const deviceInfo = yield api.deviceInfo(jwtToken, normalizedMac);
|
|
74
69
|
console.log("Device Info:", deviceInfo.data);
|
|
75
70
|
}));
|
|
76
71
|
return program;
|
|
@@ -79,7 +74,7 @@ const main = () => {
|
|
|
79
74
|
const program = createProgram();
|
|
80
75
|
program.parse(process.argv);
|
|
81
76
|
};
|
|
82
|
-
exports.main = main;
|
|
83
77
|
if (require.main === module) {
|
|
84
78
|
main();
|
|
85
79
|
}
|
|
80
|
+
export { main };
|
|
@@ -1,4 +1,4 @@
|
|
|
1
1
|
export { API_URL } from "./constants";
|
|
2
|
+
export { configure, signIn } from "./library";
|
|
2
3
|
export { CommandsType, DeviceInfoType, StatusType, TemperaturesType, UserParametersType, } from "./types";
|
|
3
|
-
export { signIn, configure } from "./library";
|
|
4
4
|
export declare const deviceInfo: (jwtToken: string, macAddress: string) => Promise<import("axios").AxiosResponse<import("./types").DeviceInfoType, any>>, setPower: (jwtToken: string, macAddress: string, value: number) => Promise<import("axios").AxiosResponse<any, any>>, setPowerOff: (jwtToken: string, macAddress: string) => Promise<import("axios").AxiosResponse<any, any>>, setPowerOn: (jwtToken: string, macAddress: string) => Promise<import("axios").AxiosResponse<any, any>>;
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import * as amplifyAuth from "aws-amplify/auth";
|
|
2
|
+
import { DeviceInfoType } from "./types";
|
|
3
|
+
/**
|
|
4
|
+
* Generates headers with a JWT token for authenticated requests.
|
|
5
|
+
* @param {string} jwtToken - The JWT token for authorization.
|
|
6
|
+
* @returns {object} - The headers object with the Authorization field.
|
|
7
|
+
*/
|
|
8
|
+
declare const headers: (jwtToken: string) => {
|
|
9
|
+
Authorization: string;
|
|
10
|
+
};
|
|
11
|
+
/**
|
|
12
|
+
* Creates an authentication service with sign-in functionality.
|
|
13
|
+
* @param {typeof amplifyAuth} auth - The authentication module to use.
|
|
14
|
+
* @returns {object} - An object containing authentication-related methods.
|
|
15
|
+
*/
|
|
16
|
+
declare const createAuthService: (auth: typeof amplifyAuth) => {
|
|
17
|
+
signIn: (username: string, password: string) => Promise<string>;
|
|
18
|
+
};
|
|
19
|
+
declare const signIn: (username: string, password: string) => Promise<string>;
|
|
20
|
+
declare const configure: (baseURL?: string) => {
|
|
21
|
+
deviceInfo: (jwtToken: string, macAddress: string) => Promise<import("axios").AxiosResponse<DeviceInfoType, any>>;
|
|
22
|
+
setPower: (jwtToken: string, macAddress: string, value: number) => Promise<import("axios").AxiosResponse<any, any>>;
|
|
23
|
+
setPowerOff: (jwtToken: string, macAddress: string) => Promise<import("axios").AxiosResponse<any, any>>;
|
|
24
|
+
setPowerOn: (jwtToken: string, macAddress: string) => Promise<import("axios").AxiosResponse<any, any>>;
|
|
25
|
+
};
|
|
26
|
+
export { configure, createAuthService, headers, signIn };
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
|
2
|
+
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
|
3
|
+
return new (P || (P = Promise))(function (resolve, reject) {
|
|
4
|
+
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
|
5
|
+
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
|
6
|
+
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
|
7
|
+
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
8
|
+
});
|
|
9
|
+
};
|
|
10
|
+
import { strict as assert } from "assert";
|
|
11
|
+
import { Amplify } from "aws-amplify";
|
|
12
|
+
import * as amplifyAuth from "aws-amplify/auth";
|
|
13
|
+
import axios from "axios";
|
|
14
|
+
import { API_URL } from "./constants";
|
|
15
|
+
const amplifyconfiguration = {
|
|
16
|
+
aws_project_region: "eu-central-1",
|
|
17
|
+
aws_user_pools_id: "eu-central-1_BYmQ2VBlo",
|
|
18
|
+
aws_user_pools_web_client_id: "7sc1qltkqobo3ddqsk4542dg2h",
|
|
19
|
+
};
|
|
20
|
+
/**
|
|
21
|
+
* Generates headers with a JWT token for authenticated requests.
|
|
22
|
+
* @param {string} jwtToken - The JWT token for authorization.
|
|
23
|
+
* @returns {object} - The headers object with the Authorization field.
|
|
24
|
+
*/
|
|
25
|
+
const headers = (jwtToken) => ({ Authorization: `Bearer ${jwtToken}` });
|
|
26
|
+
/**
|
|
27
|
+
* Configures Amplify if not already configured.
|
|
28
|
+
* Ensures the configuration is only applied once.
|
|
29
|
+
*/
|
|
30
|
+
const configureAmplify = () => {
|
|
31
|
+
const currentConfig = Amplify.getConfig();
|
|
32
|
+
if (Object.keys(currentConfig).length !== 0)
|
|
33
|
+
return;
|
|
34
|
+
Amplify.configure(amplifyconfiguration);
|
|
35
|
+
};
|
|
36
|
+
/**
|
|
37
|
+
* Creates an authentication service with sign-in functionality.
|
|
38
|
+
* @param {typeof amplifyAuth} auth - The authentication module to use.
|
|
39
|
+
* @returns {object} - An object containing authentication-related methods.
|
|
40
|
+
*/
|
|
41
|
+
const createAuthService = (auth) => {
|
|
42
|
+
/**
|
|
43
|
+
* Signs in a user with the provided credentials.
|
|
44
|
+
* @param {string} username - The username of the user.
|
|
45
|
+
* @param {string} password - The password of the user.
|
|
46
|
+
* @returns {Promise<string>} - The JWT token of the signed-in user.
|
|
47
|
+
* @throws {Error} - If sign-in fails or no tokens are retrieved.
|
|
48
|
+
*/
|
|
49
|
+
const signIn = (username, password) => __awaiter(void 0, void 0, void 0, function* () {
|
|
50
|
+
configureAmplify();
|
|
51
|
+
yield auth.signOut(); // Ensure the user is signed out first
|
|
52
|
+
const { isSignedIn } = yield auth.signIn({ username, password });
|
|
53
|
+
assert.ok(isSignedIn, "Sign-in failed");
|
|
54
|
+
const { tokens } = yield auth.fetchAuthSession();
|
|
55
|
+
assert.ok(tokens, "No tokens found");
|
|
56
|
+
return tokens.accessToken.toString();
|
|
57
|
+
});
|
|
58
|
+
return { signIn };
|
|
59
|
+
};
|
|
60
|
+
// Create the default auth service using amplifyAuth
|
|
61
|
+
const { signIn } = createAuthService(amplifyAuth);
|
|
62
|
+
const deviceInfo = (axiosInstance) => (jwtToken, macAddress) => axiosInstance.get(`device/${macAddress}/info`, {
|
|
63
|
+
headers: headers(jwtToken),
|
|
64
|
+
});
|
|
65
|
+
const mqttCommand = (axiosInstance) =>
|
|
66
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
67
|
+
(jwtToken, macAddress, payload) => axiosInstance.put("mqtt/command", Object.assign({ mac_address: macAddress }, payload), { headers: headers(jwtToken) });
|
|
68
|
+
const setPower = (axiosInstance) => (jwtToken, macAddress, value) => mqttCommand(axiosInstance)(jwtToken, macAddress, { name: "power", value });
|
|
69
|
+
const setPowerOn = (axiosInstance) => (jwtToken, macAddress) => setPower(axiosInstance)(jwtToken, macAddress, 1);
|
|
70
|
+
const setPowerOff = (axiosInstance) => (jwtToken, macAddress) => setPower(axiosInstance)(jwtToken, macAddress, 0);
|
|
71
|
+
const configure = (baseURL = API_URL) => {
|
|
72
|
+
const axiosInstance = axios.create({ baseURL });
|
|
73
|
+
const deviceInfoInstance = deviceInfo(axiosInstance);
|
|
74
|
+
const setPowerInstance = setPower(axiosInstance);
|
|
75
|
+
const setPowerOffInstance = setPowerOff(axiosInstance);
|
|
76
|
+
const setPowerOnInstance = setPowerOn(axiosInstance);
|
|
77
|
+
return {
|
|
78
|
+
deviceInfo: deviceInfoInstance,
|
|
79
|
+
setPower: setPowerInstance,
|
|
80
|
+
setPowerOff: setPowerOffInstance,
|
|
81
|
+
setPowerOn: setPowerOnInstance,
|
|
82
|
+
};
|
|
83
|
+
};
|
|
84
|
+
export { configure, createAuthService, headers, signIn };
|
|
@@ -0,0 +1,168 @@
|
|
|
1
|
+
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
|
2
|
+
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
|
3
|
+
return new (P || (P = Promise))(function (resolve, reject) {
|
|
4
|
+
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
|
5
|
+
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
|
6
|
+
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
|
7
|
+
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
8
|
+
});
|
|
9
|
+
};
|
|
10
|
+
import { strict as assert } from "assert";
|
|
11
|
+
import axios from "axios";
|
|
12
|
+
import sinon from "sinon";
|
|
13
|
+
import { configure, createAuthService } from "../src/library";
|
|
14
|
+
import { API_URL } from "./constants";
|
|
15
|
+
describe("library", () => {
|
|
16
|
+
let axiosStub;
|
|
17
|
+
beforeEach(() => {
|
|
18
|
+
axiosStub = sinon.stub(axios, "create").returns({
|
|
19
|
+
get: sinon.stub(),
|
|
20
|
+
put: sinon.stub(),
|
|
21
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
22
|
+
});
|
|
23
|
+
});
|
|
24
|
+
afterEach(() => {
|
|
25
|
+
sinon.restore();
|
|
26
|
+
});
|
|
27
|
+
describe("signIn", () => {
|
|
28
|
+
it("should sign in and return the JWT token", () => __awaiter(void 0, void 0, void 0, function* () {
|
|
29
|
+
const expectedUsername = "testuser";
|
|
30
|
+
const expectedPassword = "testpassword";
|
|
31
|
+
const expectedToken = "mockJwtToken";
|
|
32
|
+
const signIn = sinon.stub().resolves({ isSignedIn: true });
|
|
33
|
+
const signOut = sinon.stub();
|
|
34
|
+
const fetchAuthSession = sinon.stub().resolves({
|
|
35
|
+
tokens: {
|
|
36
|
+
accessToken: { toString: () => expectedToken },
|
|
37
|
+
},
|
|
38
|
+
});
|
|
39
|
+
const authStub = {
|
|
40
|
+
signIn,
|
|
41
|
+
signOut,
|
|
42
|
+
fetchAuthSession,
|
|
43
|
+
};
|
|
44
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
45
|
+
const authService = createAuthService(authStub);
|
|
46
|
+
const token = yield authService.signIn(expectedUsername, expectedPassword);
|
|
47
|
+
assert.deepEqual(authStub.signOut.args, [[]]);
|
|
48
|
+
assert.deepEqual(signIn.args, [
|
|
49
|
+
[{ username: expectedUsername, password: expectedPassword }],
|
|
50
|
+
]);
|
|
51
|
+
assert.equal(token, expectedToken);
|
|
52
|
+
}));
|
|
53
|
+
it("should throw an error if sign-in fails", () => __awaiter(void 0, void 0, void 0, function* () {
|
|
54
|
+
const expectedUsername = "testuser";
|
|
55
|
+
const expectedPassword = "testpassword";
|
|
56
|
+
const expectedToken = "mockJwtToken";
|
|
57
|
+
const signIn = sinon.stub().resolves({ isSignedIn: false });
|
|
58
|
+
const signOut = sinon.stub();
|
|
59
|
+
const fetchAuthSession = sinon.stub().resolves({
|
|
60
|
+
tokens: {
|
|
61
|
+
accessToken: { toString: () => expectedToken },
|
|
62
|
+
},
|
|
63
|
+
});
|
|
64
|
+
const authStub = {
|
|
65
|
+
signIn,
|
|
66
|
+
signOut,
|
|
67
|
+
fetchAuthSession,
|
|
68
|
+
};
|
|
69
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
70
|
+
const authService = createAuthService(authStub);
|
|
71
|
+
yield assert.rejects(() => __awaiter(void 0, void 0, void 0, function* () { return authService.signIn(expectedUsername, expectedPassword); }), {
|
|
72
|
+
name: "AssertionError",
|
|
73
|
+
message: "Sign-in failed",
|
|
74
|
+
});
|
|
75
|
+
}));
|
|
76
|
+
});
|
|
77
|
+
describe("configure", () => {
|
|
78
|
+
it("should create API methods with the correct baseURL", () => {
|
|
79
|
+
const baseURL = "https://example.com/api";
|
|
80
|
+
const api = configure(baseURL);
|
|
81
|
+
assert.deepEqual(axiosStub.args, [
|
|
82
|
+
[
|
|
83
|
+
{
|
|
84
|
+
baseURL,
|
|
85
|
+
},
|
|
86
|
+
],
|
|
87
|
+
]);
|
|
88
|
+
assert.deepEqual(Object.keys(api), [
|
|
89
|
+
"deviceInfo",
|
|
90
|
+
"setPower",
|
|
91
|
+
"setPowerOff",
|
|
92
|
+
"setPowerOn",
|
|
93
|
+
]);
|
|
94
|
+
});
|
|
95
|
+
it("should create API methods with the default baseURL", () => {
|
|
96
|
+
const api = configure();
|
|
97
|
+
assert.deepEqual(axiosStub.args, [
|
|
98
|
+
[
|
|
99
|
+
{
|
|
100
|
+
baseURL: API_URL,
|
|
101
|
+
},
|
|
102
|
+
],
|
|
103
|
+
]);
|
|
104
|
+
assert.deepEqual(Object.keys(api), [
|
|
105
|
+
"deviceInfo",
|
|
106
|
+
"setPower",
|
|
107
|
+
"setPowerOff",
|
|
108
|
+
"setPowerOn",
|
|
109
|
+
]);
|
|
110
|
+
});
|
|
111
|
+
});
|
|
112
|
+
describe("API Methods", () => {
|
|
113
|
+
it("should call axios for deviceInfo", () => __awaiter(void 0, void 0, void 0, function* () {
|
|
114
|
+
const expectedDevice = { id: "123", name: "Mock Device" };
|
|
115
|
+
const expectedToken = "mockToken";
|
|
116
|
+
const mockAxios = {
|
|
117
|
+
get: sinon.stub().resolves({ data: expectedDevice }),
|
|
118
|
+
};
|
|
119
|
+
axiosStub.returns(mockAxios);
|
|
120
|
+
const api = configure("https://example.com/api");
|
|
121
|
+
const result = yield api.deviceInfo(expectedToken, "mockMacAddress");
|
|
122
|
+
assert.deepEqual(mockAxios.get.args, [
|
|
123
|
+
[
|
|
124
|
+
"device/mockMacAddress/info",
|
|
125
|
+
{ headers: { Authorization: `Bearer ${expectedToken}` } },
|
|
126
|
+
],
|
|
127
|
+
]);
|
|
128
|
+
assert.deepEqual(result.data, expectedDevice);
|
|
129
|
+
}));
|
|
130
|
+
// Tests for setPowerOn and setPowerOff
|
|
131
|
+
[
|
|
132
|
+
{
|
|
133
|
+
method: "setPowerOn",
|
|
134
|
+
call: (api) => api.setPowerOn("mockToken", "mockMacAddress"),
|
|
135
|
+
expectedValue: 1,
|
|
136
|
+
},
|
|
137
|
+
{
|
|
138
|
+
method: "setPowerOff",
|
|
139
|
+
call: (api) => api.setPowerOff("mockToken", "mockMacAddress"),
|
|
140
|
+
expectedValue: 0,
|
|
141
|
+
},
|
|
142
|
+
].forEach(({ method, call, expectedValue }) => {
|
|
143
|
+
it(`should call axios for ${method}`, () => __awaiter(void 0, void 0, void 0, function* () {
|
|
144
|
+
const mockAxios = {
|
|
145
|
+
put: sinon.stub().resolves({ status: 200 }),
|
|
146
|
+
};
|
|
147
|
+
axiosStub.returns(mockAxios);
|
|
148
|
+
const api = configure("https://example.com/api");
|
|
149
|
+
// Invoke the method using the mapped call function
|
|
150
|
+
const result = yield call(api);
|
|
151
|
+
assert.deepEqual(mockAxios.put.args, [
|
|
152
|
+
[
|
|
153
|
+
"mqtt/command",
|
|
154
|
+
{
|
|
155
|
+
mac_address: "mockMacAddress",
|
|
156
|
+
name: "power",
|
|
157
|
+
value: expectedValue,
|
|
158
|
+
},
|
|
159
|
+
{
|
|
160
|
+
headers: { Authorization: "Bearer mockToken" },
|
|
161
|
+
},
|
|
162
|
+
],
|
|
163
|
+
]);
|
|
164
|
+
assert.equal(result.status, 200);
|
|
165
|
+
}));
|
|
166
|
+
});
|
|
167
|
+
});
|
|
168
|
+
});
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import typescriptEslint from "@typescript-eslint/eslint-plugin";
|
|
2
|
+
import simpleImportSort from "eslint-plugin-simple-import-sort";
|
|
3
|
+
import globals from "globals";
|
|
4
|
+
import tsParser from "@typescript-eslint/parser";
|
|
5
|
+
import path from "node:path";
|
|
6
|
+
import { fileURLToPath } from "node:url";
|
|
7
|
+
import js from "@eslint/js";
|
|
8
|
+
import { FlatCompat } from "@eslint/eslintrc";
|
|
9
|
+
|
|
10
|
+
const __filename = fileURLToPath(import.meta.url);
|
|
11
|
+
const __dirname = path.dirname(__filename);
|
|
12
|
+
|
|
13
|
+
const compat = new FlatCompat({
|
|
14
|
+
baseDirectory: __dirname,
|
|
15
|
+
recommendedConfig: js.configs.recommended,
|
|
16
|
+
allConfig: js.configs.all,
|
|
17
|
+
});
|
|
18
|
+
|
|
19
|
+
export default [
|
|
20
|
+
...compat.extends(
|
|
21
|
+
"eslint:recommended",
|
|
22
|
+
"plugin:@typescript-eslint/recommended"
|
|
23
|
+
),
|
|
24
|
+
{
|
|
25
|
+
plugins: {
|
|
26
|
+
"@typescript-eslint": typescriptEslint,
|
|
27
|
+
"simple-import-sort": simpleImportSort,
|
|
28
|
+
},
|
|
29
|
+
rules: {
|
|
30
|
+
// Sorting imports and exports
|
|
31
|
+
"simple-import-sort/imports": "error",
|
|
32
|
+
"simple-import-sort/exports": "error",
|
|
33
|
+
},
|
|
34
|
+
},
|
|
35
|
+
];
|
package/package.json
CHANGED
|
@@ -1,17 +1,24 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "edilkamin",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.4.1",
|
|
4
4
|
"description": "",
|
|
5
|
-
"main": "dist/index.js",
|
|
6
|
-
"
|
|
5
|
+
"main": "dist/cjs/index.js",
|
|
6
|
+
"module": "dist/esm/index.js",
|
|
7
|
+
"types": "dist/esm/index.d.ts",
|
|
7
8
|
"scripts": {
|
|
8
9
|
"cli": "ts-node src/cli.ts",
|
|
9
10
|
"cli:debug": "node --inspect --require ts-node/register/transpile-only src/cli.ts",
|
|
10
|
-
"test": "mocha --require ts-node/register src/*.test.ts",
|
|
11
|
-
"test:debug": "mocha --require ts-node/register/transpile-only --inspect src/*.test.ts",
|
|
12
|
-
"lint": "prettier --check src docs .github *.md",
|
|
13
|
-
"format": "prettier --write src docs .github *.md",
|
|
14
|
-
"
|
|
11
|
+
"test": "nyc mocha --require ts-node/register src/*.test.ts",
|
|
12
|
+
"test:debug": "nyc mocha --require ts-node/register/transpile-only --inspect src/*.test.ts",
|
|
13
|
+
"lint:prettier": "prettier --check src docs .github *.json *.md *.mjs",
|
|
14
|
+
"format:prettier": "prettier --write src docs .github *.json *.md *.mjs",
|
|
15
|
+
"lint:eslint": "eslint src",
|
|
16
|
+
"format:eslint": "eslint --fix src",
|
|
17
|
+
"lint": "yarn lint:prettier && yarn lint:eslint",
|
|
18
|
+
"format": "yarn format:prettier && yarn format:eslint",
|
|
19
|
+
"build:cjs": "tsc -p tsconfig.cjs.json",
|
|
20
|
+
"build:esm": "tsc -p tsconfig.esm.json",
|
|
21
|
+
"build": "npm run build:cjs && npm run build:esm"
|
|
15
22
|
},
|
|
16
23
|
"repository": {
|
|
17
24
|
"type": "git",
|
|
@@ -24,7 +31,14 @@
|
|
|
24
31
|
},
|
|
25
32
|
"homepage": "https://github.com/AndreMiras/edilkamin.js#readme",
|
|
26
33
|
"bin": {
|
|
27
|
-
"edilkamin": "dist/cli.js"
|
|
34
|
+
"edilkamin": "dist/cjs/cli.js"
|
|
35
|
+
},
|
|
36
|
+
"nyc": {
|
|
37
|
+
"reporter": [
|
|
38
|
+
"html",
|
|
39
|
+
"lcov",
|
|
40
|
+
"text"
|
|
41
|
+
]
|
|
28
42
|
},
|
|
29
43
|
"dependencies": {
|
|
30
44
|
"aws-amplify": "^6.10.0",
|
|
@@ -32,9 +46,18 @@
|
|
|
32
46
|
},
|
|
33
47
|
"devDependencies": {
|
|
34
48
|
"@aws-amplify/cli": "^7.6.21",
|
|
49
|
+
"@eslint/eslintrc": "^3.2.0",
|
|
50
|
+
"@eslint/js": "^9.16.0",
|
|
35
51
|
"@types/mocha": "^10.0.10",
|
|
36
52
|
"@types/sinon": "^17.0.3",
|
|
53
|
+
"@typescript-eslint/eslint-plugin": "^8.17.0",
|
|
54
|
+
"@typescript-eslint/parser": "^8.17.0",
|
|
55
|
+
"eslint": "^9.16.0",
|
|
56
|
+
"eslint-config-prettier": "^9.1.0",
|
|
57
|
+
"eslint-plugin-prettier": "^5.2.1",
|
|
58
|
+
"eslint-plugin-simple-import-sort": "^12.1.1",
|
|
37
59
|
"mocha": "^10.8.2",
|
|
60
|
+
"nyc": "^17.1.0",
|
|
38
61
|
"prettier": "^2.5.1",
|
|
39
62
|
"sinon": "^19.0.2",
|
|
40
63
|
"ts-node": "^10.9.1",
|
package/src/cli.ts
CHANGED
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import { signIn, configure } from "./library";
|
|
3
2
|
import { Command } from "commander";
|
|
4
3
|
import readline from "readline";
|
|
4
|
+
|
|
5
5
|
import { version } from "../package.json";
|
|
6
|
+
import { configure, signIn } from "./library";
|
|
6
7
|
|
|
7
8
|
const promptPassword = (): Promise<string> => {
|
|
8
9
|
const rl = readline.createInterface({
|
|
@@ -59,10 +60,11 @@ const createProgram = (): Command => {
|
|
|
59
60
|
.requiredOption("-m, --mac <macAddress>", "MAC address of the device")
|
|
60
61
|
).action(async (options) => {
|
|
61
62
|
const { username, password, mac } = options;
|
|
63
|
+
const normalizedMac = mac.replace(/:/g, "");
|
|
62
64
|
const pwd = password || (await promptPassword());
|
|
63
65
|
const jwtToken = await signIn(username, pwd);
|
|
64
66
|
const api = configure(); // Use the default API configuration
|
|
65
|
-
const deviceInfo = await api.deviceInfo(jwtToken,
|
|
67
|
+
const deviceInfo = await api.deviceInfo(jwtToken, normalizedMac);
|
|
66
68
|
console.log("Device Info:", deviceInfo.data);
|
|
67
69
|
});
|
|
68
70
|
return program;
|
package/src/index.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { configure } from "./library";
|
|
2
2
|
|
|
3
3
|
export { API_URL } from "./constants";
|
|
4
|
-
|
|
4
|
+
export { configure, signIn } from "./library";
|
|
5
5
|
export {
|
|
6
6
|
CommandsType,
|
|
7
7
|
DeviceInfoType,
|
|
@@ -10,6 +10,4 @@ export {
|
|
|
10
10
|
UserParametersType,
|
|
11
11
|
} from "./types";
|
|
12
12
|
|
|
13
|
-
export { signIn, configure } from "./library";
|
|
14
|
-
|
|
15
13
|
export const { deviceInfo, setPower, setPowerOff, setPowerOn } = configure();
|
package/src/library.test.ts
CHANGED
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
import { strict as assert } from "assert";
|
|
2
|
-
import sinon from "sinon";
|
|
3
|
-
import { Amplify } from "aws-amplify";
|
|
4
2
|
import axios from "axios";
|
|
5
|
-
import
|
|
3
|
+
import sinon from "sinon";
|
|
4
|
+
|
|
5
|
+
import { configure, createAuthService } from "../src/library";
|
|
6
|
+
import { API_URL } from "./constants";
|
|
6
7
|
|
|
7
8
|
describe("library", () => {
|
|
8
9
|
let axiosStub: sinon.SinonStub;
|
|
@@ -11,6 +12,7 @@ describe("library", () => {
|
|
|
11
12
|
axiosStub = sinon.stub(axios, "create").returns({
|
|
12
13
|
get: sinon.stub(),
|
|
13
14
|
put: sinon.stub(),
|
|
15
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
14
16
|
} as any);
|
|
15
17
|
});
|
|
16
18
|
|
|
@@ -18,12 +20,91 @@ describe("library", () => {
|
|
|
18
20
|
sinon.restore();
|
|
19
21
|
});
|
|
20
22
|
|
|
23
|
+
describe("signIn", () => {
|
|
24
|
+
it("should sign in and return the JWT token", async () => {
|
|
25
|
+
const expectedUsername = "testuser";
|
|
26
|
+
const expectedPassword = "testpassword";
|
|
27
|
+
const expectedToken = "mockJwtToken";
|
|
28
|
+
const signIn = sinon.stub().resolves({ isSignedIn: true });
|
|
29
|
+
const signOut = sinon.stub();
|
|
30
|
+
const fetchAuthSession = sinon.stub().resolves({
|
|
31
|
+
tokens: {
|
|
32
|
+
accessToken: { toString: () => expectedToken },
|
|
33
|
+
},
|
|
34
|
+
});
|
|
35
|
+
const authStub = {
|
|
36
|
+
signIn,
|
|
37
|
+
signOut,
|
|
38
|
+
fetchAuthSession,
|
|
39
|
+
};
|
|
40
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
41
|
+
const authService = createAuthService(authStub as any);
|
|
42
|
+
const token = await authService.signIn(
|
|
43
|
+
expectedUsername,
|
|
44
|
+
expectedPassword
|
|
45
|
+
);
|
|
46
|
+
assert.deepEqual(authStub.signOut.args, [[]]);
|
|
47
|
+
assert.deepEqual(signIn.args, [
|
|
48
|
+
[{ username: expectedUsername, password: expectedPassword }],
|
|
49
|
+
]);
|
|
50
|
+
assert.equal(token, expectedToken);
|
|
51
|
+
});
|
|
52
|
+
|
|
53
|
+
it("should throw an error if sign-in fails", async () => {
|
|
54
|
+
const expectedUsername = "testuser";
|
|
55
|
+
const expectedPassword = "testpassword";
|
|
56
|
+
const expectedToken = "mockJwtToken";
|
|
57
|
+
const signIn = sinon.stub().resolves({ isSignedIn: false });
|
|
58
|
+
const signOut = sinon.stub();
|
|
59
|
+
const fetchAuthSession = sinon.stub().resolves({
|
|
60
|
+
tokens: {
|
|
61
|
+
accessToken: { toString: () => expectedToken },
|
|
62
|
+
},
|
|
63
|
+
});
|
|
64
|
+
const authStub = {
|
|
65
|
+
signIn,
|
|
66
|
+
signOut,
|
|
67
|
+
fetchAuthSession,
|
|
68
|
+
};
|
|
69
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
70
|
+
const authService = createAuthService(authStub as any);
|
|
71
|
+
await assert.rejects(
|
|
72
|
+
async () => authService.signIn(expectedUsername, expectedPassword),
|
|
73
|
+
{
|
|
74
|
+
name: "AssertionError",
|
|
75
|
+
message: "Sign-in failed",
|
|
76
|
+
}
|
|
77
|
+
);
|
|
78
|
+
});
|
|
79
|
+
});
|
|
80
|
+
|
|
21
81
|
describe("configure", () => {
|
|
22
82
|
it("should create API methods with the correct baseURL", () => {
|
|
23
83
|
const baseURL = "https://example.com/api";
|
|
24
84
|
const api = configure(baseURL);
|
|
25
|
-
assert.
|
|
26
|
-
|
|
85
|
+
assert.deepEqual(axiosStub.args, [
|
|
86
|
+
[
|
|
87
|
+
{
|
|
88
|
+
baseURL,
|
|
89
|
+
},
|
|
90
|
+
],
|
|
91
|
+
]);
|
|
92
|
+
assert.deepEqual(Object.keys(api), [
|
|
93
|
+
"deviceInfo",
|
|
94
|
+
"setPower",
|
|
95
|
+
"setPowerOff",
|
|
96
|
+
"setPowerOn",
|
|
97
|
+
]);
|
|
98
|
+
});
|
|
99
|
+
it("should create API methods with the default baseURL", () => {
|
|
100
|
+
const api = configure();
|
|
101
|
+
assert.deepEqual(axiosStub.args, [
|
|
102
|
+
[
|
|
103
|
+
{
|
|
104
|
+
baseURL: API_URL,
|
|
105
|
+
},
|
|
106
|
+
],
|
|
107
|
+
]);
|
|
27
108
|
assert.deepEqual(Object.keys(api), [
|
|
28
109
|
"deviceInfo",
|
|
29
110
|
"setPower",
|
|
@@ -35,43 +116,62 @@ describe("library", () => {
|
|
|
35
116
|
|
|
36
117
|
describe("API Methods", () => {
|
|
37
118
|
it("should call axios for deviceInfo", async () => {
|
|
119
|
+
const expectedDevice = { id: "123", name: "Mock Device" };
|
|
120
|
+
const expectedToken = "mockToken";
|
|
38
121
|
const mockAxios = {
|
|
39
|
-
get: sinon
|
|
40
|
-
.stub()
|
|
41
|
-
.resolves({ data: { id: "123", name: "Mock Device" } }),
|
|
122
|
+
get: sinon.stub().resolves({ data: expectedDevice }),
|
|
42
123
|
};
|
|
43
|
-
axiosStub.returns(mockAxios
|
|
124
|
+
axiosStub.returns(mockAxios);
|
|
44
125
|
const api = configure("https://example.com/api");
|
|
45
|
-
const result = await api.deviceInfo(
|
|
46
|
-
assert.
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
});
|
|
54
|
-
assert.deepEqual(result.data, { id: "123", name: "Mock Device" });
|
|
126
|
+
const result = await api.deviceInfo(expectedToken, "mockMacAddress");
|
|
127
|
+
assert.deepEqual(mockAxios.get.args, [
|
|
128
|
+
[
|
|
129
|
+
"device/mockMacAddress/info",
|
|
130
|
+
{ headers: { Authorization: `Bearer ${expectedToken}` } },
|
|
131
|
+
],
|
|
132
|
+
]);
|
|
133
|
+
assert.deepEqual(result.data, expectedDevice);
|
|
55
134
|
});
|
|
56
135
|
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
}
|
|
71
|
-
|
|
72
|
-
|
|
136
|
+
// Tests for setPowerOn and setPowerOff
|
|
137
|
+
[
|
|
138
|
+
{
|
|
139
|
+
method: "setPowerOn",
|
|
140
|
+
call: (api: ReturnType<typeof configure>) =>
|
|
141
|
+
api.setPowerOn("mockToken", "mockMacAddress"),
|
|
142
|
+
expectedValue: 1,
|
|
143
|
+
},
|
|
144
|
+
{
|
|
145
|
+
method: "setPowerOff",
|
|
146
|
+
call: (api: ReturnType<typeof configure>) =>
|
|
147
|
+
api.setPowerOff("mockToken", "mockMacAddress"),
|
|
148
|
+
expectedValue: 0,
|
|
149
|
+
},
|
|
150
|
+
].forEach(({ method, call, expectedValue }) => {
|
|
151
|
+
it(`should call axios for ${method}`, async () => {
|
|
152
|
+
const mockAxios = {
|
|
153
|
+
put: sinon.stub().resolves({ status: 200 }),
|
|
154
|
+
};
|
|
155
|
+
axiosStub.returns(mockAxios);
|
|
156
|
+
const api = configure("https://example.com/api");
|
|
157
|
+
|
|
158
|
+
// Invoke the method using the mapped call function
|
|
159
|
+
const result = await call(api);
|
|
160
|
+
assert.deepEqual(mockAxios.put.args, [
|
|
161
|
+
[
|
|
162
|
+
"mqtt/command",
|
|
163
|
+
{
|
|
164
|
+
mac_address: "mockMacAddress",
|
|
165
|
+
name: "power",
|
|
166
|
+
value: expectedValue,
|
|
167
|
+
},
|
|
168
|
+
{
|
|
169
|
+
headers: { Authorization: "Bearer mockToken" },
|
|
170
|
+
},
|
|
171
|
+
],
|
|
172
|
+
]);
|
|
173
|
+
assert.equal(result.status, 200);
|
|
73
174
|
});
|
|
74
|
-
assert.equal(result.status, 200);
|
|
75
175
|
});
|
|
76
176
|
});
|
|
77
177
|
});
|
package/src/library.ts
CHANGED
|
@@ -2,35 +2,64 @@ import { strict as assert } from "assert";
|
|
|
2
2
|
import { Amplify } from "aws-amplify";
|
|
3
3
|
import * as amplifyAuth from "aws-amplify/auth";
|
|
4
4
|
import axios, { AxiosInstance } from "axios";
|
|
5
|
-
|
|
5
|
+
|
|
6
6
|
import { API_URL } from "./constants";
|
|
7
|
+
import { DeviceInfoType } from "./types";
|
|
7
8
|
|
|
8
9
|
const amplifyconfiguration = {
|
|
9
10
|
aws_project_region: "eu-central-1",
|
|
10
11
|
aws_user_pools_id: "eu-central-1_BYmQ2VBlo",
|
|
11
12
|
aws_user_pools_web_client_id: "7sc1qltkqobo3ddqsk4542dg2h",
|
|
12
13
|
};
|
|
13
|
-
Amplify.configure(amplifyconfiguration);
|
|
14
14
|
|
|
15
|
+
/**
|
|
16
|
+
* Generates headers with a JWT token for authenticated requests.
|
|
17
|
+
* @param {string} jwtToken - The JWT token for authorization.
|
|
18
|
+
* @returns {object} - The headers object with the Authorization field.
|
|
19
|
+
*/
|
|
15
20
|
const headers = (jwtToken: string) => ({ Authorization: `Bearer ${jwtToken}` });
|
|
16
21
|
|
|
17
22
|
/**
|
|
18
|
-
*
|
|
23
|
+
* Configures Amplify if not already configured.
|
|
24
|
+
* Ensures the configuration is only applied once.
|
|
25
|
+
*/
|
|
26
|
+
const configureAmplify = () => {
|
|
27
|
+
const currentConfig = Amplify.getConfig();
|
|
28
|
+
if (Object.keys(currentConfig).length !== 0) return;
|
|
29
|
+
Amplify.configure(amplifyconfiguration);
|
|
30
|
+
};
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* Creates an authentication service with sign-in functionality.
|
|
34
|
+
* @param {typeof amplifyAuth} auth - The authentication module to use.
|
|
35
|
+
* @returns {object} - An object containing authentication-related methods.
|
|
19
36
|
*/
|
|
20
|
-
const
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
37
|
+
const createAuthService = (auth: typeof amplifyAuth) => {
|
|
38
|
+
/**
|
|
39
|
+
* Signs in a user with the provided credentials.
|
|
40
|
+
* @param {string} username - The username of the user.
|
|
41
|
+
* @param {string} password - The password of the user.
|
|
42
|
+
* @returns {Promise<string>} - The JWT token of the signed-in user.
|
|
43
|
+
* @throws {Error} - If sign-in fails or no tokens are retrieved.
|
|
44
|
+
*/
|
|
45
|
+
const signIn = async (
|
|
46
|
+
username: string,
|
|
47
|
+
password: string
|
|
48
|
+
): Promise<string> => {
|
|
49
|
+
configureAmplify();
|
|
50
|
+
await auth.signOut(); // Ensure the user is signed out first
|
|
51
|
+
const { isSignedIn } = await auth.signIn({ username, password });
|
|
52
|
+
assert.ok(isSignedIn, "Sign-in failed");
|
|
53
|
+
const { tokens } = await auth.fetchAuthSession();
|
|
54
|
+
assert.ok(tokens, "No tokens found");
|
|
55
|
+
return tokens.accessToken.toString();
|
|
56
|
+
};
|
|
57
|
+
return { signIn };
|
|
32
58
|
};
|
|
33
59
|
|
|
60
|
+
// Create the default auth service using amplifyAuth
|
|
61
|
+
const { signIn } = createAuthService(amplifyAuth);
|
|
62
|
+
|
|
34
63
|
const deviceInfo =
|
|
35
64
|
(axiosInstance: AxiosInstance) => (jwtToken: string, macAddress: string) =>
|
|
36
65
|
axiosInstance.get<DeviceInfoType>(`device/${macAddress}/info`, {
|
|
@@ -39,6 +68,7 @@ const deviceInfo =
|
|
|
39
68
|
|
|
40
69
|
const mqttCommand =
|
|
41
70
|
(axiosInstance: AxiosInstance) =>
|
|
71
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
42
72
|
(jwtToken: string, macAddress: string, payload: any) =>
|
|
43
73
|
axiosInstance.put(
|
|
44
74
|
"mqtt/command",
|
|
@@ -72,6 +102,4 @@ const configure = (baseURL: string = API_URL) => {
|
|
|
72
102
|
};
|
|
73
103
|
};
|
|
74
104
|
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
export { signIn, configure };
|
|
105
|
+
export { configure, createAuthService, headers, signIn };
|
package/tsconfig.json
CHANGED
|
@@ -1,16 +1,17 @@
|
|
|
1
1
|
{
|
|
2
2
|
"compilerOptions": {
|
|
3
|
-
"
|
|
4
|
-
"target": "
|
|
5
|
-
"
|
|
6
|
-
"outDir": "
|
|
3
|
+
"target": "es6",
|
|
4
|
+
"target": "es6",
|
|
5
|
+
"moduleResolution": "node",
|
|
6
|
+
"outDir": "dist",
|
|
7
7
|
"rootDir": "src",
|
|
8
8
|
"strict": true,
|
|
9
|
+
"declaration": true,
|
|
9
10
|
"esModuleInterop": true,
|
|
10
11
|
"resolveJsonModule": true,
|
|
12
|
+
"noUnusedLocals": true,
|
|
13
|
+
"noUnusedParameters": true,
|
|
11
14
|
"preserveConstEnums": true
|
|
12
15
|
},
|
|
13
|
-
"include": [
|
|
14
|
-
"src/**/*.ts"
|
|
15
|
-
]
|
|
16
|
+
"include": ["src/**/*.ts"]
|
|
16
17
|
}
|
package/dist/constants.js
DELETED
package/dist/index.js
DELETED
|
@@ -1,11 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
var _a;
|
|
3
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
4
|
-
exports.setPowerOn = exports.setPowerOff = exports.setPower = exports.deviceInfo = exports.configure = exports.signIn = exports.API_URL = void 0;
|
|
5
|
-
const library_1 = require("./library");
|
|
6
|
-
var constants_1 = require("./constants");
|
|
7
|
-
Object.defineProperty(exports, "API_URL", { enumerable: true, get: function () { return constants_1.API_URL; } });
|
|
8
|
-
var library_2 = require("./library");
|
|
9
|
-
Object.defineProperty(exports, "signIn", { enumerable: true, get: function () { return library_2.signIn; } });
|
|
10
|
-
Object.defineProperty(exports, "configure", { enumerable: true, get: function () { return library_2.configure; } });
|
|
11
|
-
_a = (0, library_1.configure)(), exports.deviceInfo = _a.deviceInfo, exports.setPower = _a.setPower, exports.setPowerOff = _a.setPowerOff, exports.setPowerOn = _a.setPowerOn;
|
package/dist/library.d.ts
DELETED
|
@@ -1,12 +0,0 @@
|
|
|
1
|
-
import { DeviceInfoType } from "./types";
|
|
2
|
-
/**
|
|
3
|
-
* Sign in to return the JWT token.
|
|
4
|
-
*/
|
|
5
|
-
declare const signIn: (username: string, password: string) => Promise<string>;
|
|
6
|
-
declare const configure: (baseURL?: string) => {
|
|
7
|
-
deviceInfo: (jwtToken: string, macAddress: string) => Promise<import("axios").AxiosResponse<DeviceInfoType, any>>;
|
|
8
|
-
setPower: (jwtToken: string, macAddress: string, value: number) => Promise<import("axios").AxiosResponse<any, any>>;
|
|
9
|
-
setPowerOff: (jwtToken: string, macAddress: string) => Promise<import("axios").AxiosResponse<any, any>>;
|
|
10
|
-
setPowerOn: (jwtToken: string, macAddress: string) => Promise<import("axios").AxiosResponse<any, any>>;
|
|
11
|
-
};
|
|
12
|
-
export { signIn, configure };
|
package/dist/library.js
DELETED
|
@@ -1,99 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
-
if (k2 === undefined) k2 = k;
|
|
4
|
-
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
-
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
-
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
-
}
|
|
8
|
-
Object.defineProperty(o, k2, desc);
|
|
9
|
-
}) : (function(o, m, k, k2) {
|
|
10
|
-
if (k2 === undefined) k2 = k;
|
|
11
|
-
o[k2] = m[k];
|
|
12
|
-
}));
|
|
13
|
-
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
-
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
-
}) : function(o, v) {
|
|
16
|
-
o["default"] = v;
|
|
17
|
-
});
|
|
18
|
-
var __importStar = (this && this.__importStar) || (function () {
|
|
19
|
-
var ownKeys = function(o) {
|
|
20
|
-
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
21
|
-
var ar = [];
|
|
22
|
-
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
23
|
-
return ar;
|
|
24
|
-
};
|
|
25
|
-
return ownKeys(o);
|
|
26
|
-
};
|
|
27
|
-
return function (mod) {
|
|
28
|
-
if (mod && mod.__esModule) return mod;
|
|
29
|
-
var result = {};
|
|
30
|
-
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
31
|
-
__setModuleDefault(result, mod);
|
|
32
|
-
return result;
|
|
33
|
-
};
|
|
34
|
-
})();
|
|
35
|
-
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
|
36
|
-
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
|
37
|
-
return new (P || (P = Promise))(function (resolve, reject) {
|
|
38
|
-
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
|
39
|
-
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
|
40
|
-
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
|
41
|
-
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
42
|
-
});
|
|
43
|
-
};
|
|
44
|
-
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
45
|
-
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
46
|
-
};
|
|
47
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
48
|
-
exports.configure = exports.signIn = void 0;
|
|
49
|
-
const assert_1 = require("assert");
|
|
50
|
-
const aws_amplify_1 = require("aws-amplify");
|
|
51
|
-
const amplifyAuth = __importStar(require("aws-amplify/auth"));
|
|
52
|
-
const axios_1 = __importDefault(require("axios"));
|
|
53
|
-
const constants_1 = require("./constants");
|
|
54
|
-
const amplifyconfiguration = {
|
|
55
|
-
aws_project_region: "eu-central-1",
|
|
56
|
-
aws_user_pools_id: "eu-central-1_BYmQ2VBlo",
|
|
57
|
-
aws_user_pools_web_client_id: "7sc1qltkqobo3ddqsk4542dg2h",
|
|
58
|
-
};
|
|
59
|
-
aws_amplify_1.Amplify.configure(amplifyconfiguration);
|
|
60
|
-
const headers = (jwtToken) => ({ Authorization: `Bearer ${jwtToken}` });
|
|
61
|
-
/**
|
|
62
|
-
* Sign in to return the JWT token.
|
|
63
|
-
*/
|
|
64
|
-
const signIn = (username, password) => __awaiter(void 0, void 0, void 0, function* () {
|
|
65
|
-
// in case the user is already signed in, refs:
|
|
66
|
-
// https://github.com/aws-amplify/amplify-js/issues/13813
|
|
67
|
-
yield amplifyAuth.signOut();
|
|
68
|
-
const { isSignedIn, nextStep } = yield amplifyAuth.signIn({
|
|
69
|
-
username,
|
|
70
|
-
password,
|
|
71
|
-
});
|
|
72
|
-
assert_1.strict.ok(isSignedIn);
|
|
73
|
-
const { tokens } = yield amplifyAuth.fetchAuthSession();
|
|
74
|
-
assert_1.strict.ok(tokens);
|
|
75
|
-
return tokens.accessToken.toString();
|
|
76
|
-
});
|
|
77
|
-
exports.signIn = signIn;
|
|
78
|
-
const deviceInfo = (axiosInstance) => (jwtToken, macAddress) => axiosInstance.get(`device/${macAddress}/info`, {
|
|
79
|
-
headers: headers(jwtToken),
|
|
80
|
-
});
|
|
81
|
-
const mqttCommand = (axiosInstance) => (jwtToken, macAddress, payload) => axiosInstance.put("mqtt/command", Object.assign({ mac_address: macAddress }, payload), { headers: headers(jwtToken) });
|
|
82
|
-
const setPower = (axiosInstance) => (jwtToken, macAddress, value) => mqttCommand(axiosInstance)(jwtToken, macAddress, { name: "power", value });
|
|
83
|
-
const setPowerOn = (axiosInstance) => (jwtToken, macAddress) => setPower(axiosInstance)(jwtToken, macAddress, 1);
|
|
84
|
-
const setPowerOff = (axiosInstance) => (jwtToken, macAddress) => setPower(axiosInstance)(jwtToken, macAddress, 0);
|
|
85
|
-
const configure = (baseURL = constants_1.API_URL) => {
|
|
86
|
-
const axiosInstance = axios_1.default.create({ baseURL });
|
|
87
|
-
const deviceInfoInstance = deviceInfo(axiosInstance);
|
|
88
|
-
const setPowerInstance = setPower(axiosInstance);
|
|
89
|
-
const setPowerOffInstance = setPowerOff(axiosInstance);
|
|
90
|
-
const setPowerOnInstance = setPowerOn(axiosInstance);
|
|
91
|
-
return {
|
|
92
|
-
deviceInfo: deviceInfoInstance,
|
|
93
|
-
setPower: setPowerInstance,
|
|
94
|
-
setPowerOff: setPowerOffInstance,
|
|
95
|
-
setPowerOn: setPowerOnInstance,
|
|
96
|
-
};
|
|
97
|
-
};
|
|
98
|
-
exports.configure = configure;
|
|
99
|
-
const defaultApi = configure();
|
package/dist/library.test.js
DELETED
|
@@ -1,81 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
|
3
|
-
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
|
4
|
-
return new (P || (P = Promise))(function (resolve, reject) {
|
|
5
|
-
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
|
6
|
-
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
|
7
|
-
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
|
8
|
-
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
9
|
-
});
|
|
10
|
-
};
|
|
11
|
-
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
12
|
-
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
13
|
-
};
|
|
14
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
15
|
-
const assert_1 = require("assert");
|
|
16
|
-
const sinon_1 = __importDefault(require("sinon"));
|
|
17
|
-
const axios_1 = __importDefault(require("axios"));
|
|
18
|
-
const library_1 = require("../src/library");
|
|
19
|
-
describe("library", () => {
|
|
20
|
-
let axiosStub;
|
|
21
|
-
beforeEach(() => {
|
|
22
|
-
axiosStub = sinon_1.default.stub(axios_1.default, "create").returns({
|
|
23
|
-
get: sinon_1.default.stub(),
|
|
24
|
-
put: sinon_1.default.stub(),
|
|
25
|
-
});
|
|
26
|
-
});
|
|
27
|
-
afterEach(() => {
|
|
28
|
-
sinon_1.default.restore();
|
|
29
|
-
});
|
|
30
|
-
describe("configure", () => {
|
|
31
|
-
it("should create API methods with the correct baseURL", () => {
|
|
32
|
-
const baseURL = "https://example.com/api";
|
|
33
|
-
const api = (0, library_1.configure)(baseURL);
|
|
34
|
-
assert_1.strict.ok(axiosStub.calledOnce);
|
|
35
|
-
assert_1.strict.deepEqual(axiosStub.firstCall.args[0], { baseURL });
|
|
36
|
-
assert_1.strict.deepEqual(Object.keys(api), [
|
|
37
|
-
"deviceInfo",
|
|
38
|
-
"setPower",
|
|
39
|
-
"setPowerOff",
|
|
40
|
-
"setPowerOn",
|
|
41
|
-
]);
|
|
42
|
-
});
|
|
43
|
-
});
|
|
44
|
-
describe("API Methods", () => {
|
|
45
|
-
it("should call axios for deviceInfo", () => __awaiter(void 0, void 0, void 0, function* () {
|
|
46
|
-
const mockAxios = {
|
|
47
|
-
get: sinon_1.default
|
|
48
|
-
.stub()
|
|
49
|
-
.resolves({ data: { id: "123", name: "Mock Device" } }),
|
|
50
|
-
};
|
|
51
|
-
axiosStub.returns(mockAxios);
|
|
52
|
-
const api = (0, library_1.configure)("https://example.com/api");
|
|
53
|
-
const result = yield api.deviceInfo("mockToken", "mockMacAddress");
|
|
54
|
-
assert_1.strict.ok(mockAxios.get.calledOnce);
|
|
55
|
-
assert_1.strict.equal(mockAxios.get.firstCall.args[0], "device/mockMacAddress/info");
|
|
56
|
-
assert_1.strict.deepEqual(mockAxios.get.firstCall.args[1], {
|
|
57
|
-
headers: { Authorization: "Bearer mockToken" },
|
|
58
|
-
});
|
|
59
|
-
assert_1.strict.deepEqual(result.data, { id: "123", name: "Mock Device" });
|
|
60
|
-
}));
|
|
61
|
-
it("should call axios for setPowerOn", () => __awaiter(void 0, void 0, void 0, function* () {
|
|
62
|
-
const mockAxios = {
|
|
63
|
-
put: sinon_1.default.stub().resolves({ status: 200 }),
|
|
64
|
-
};
|
|
65
|
-
axiosStub.returns(mockAxios);
|
|
66
|
-
const api = (0, library_1.configure)("https://example.com/api");
|
|
67
|
-
const result = yield api.setPowerOn("mockToken", "mockMacAddress");
|
|
68
|
-
assert_1.strict.ok(mockAxios.put.calledOnce);
|
|
69
|
-
assert_1.strict.equal(mockAxios.put.firstCall.args[0], "mqtt/command");
|
|
70
|
-
assert_1.strict.deepEqual(mockAxios.put.firstCall.args[1], {
|
|
71
|
-
mac_address: "mockMacAddress",
|
|
72
|
-
name: "power",
|
|
73
|
-
value: 1,
|
|
74
|
-
});
|
|
75
|
-
assert_1.strict.deepEqual(mockAxios.put.firstCall.args[2], {
|
|
76
|
-
headers: { Authorization: "Bearer mockToken" },
|
|
77
|
-
});
|
|
78
|
-
assert_1.strict.equal(result.status, 200);
|
|
79
|
-
}));
|
|
80
|
-
});
|
|
81
|
-
});
|
package/dist/types.js
DELETED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|