Skip to content

Commit

Permalink
♻️ Refactor before adding more getter
Browse files Browse the repository at this point in the history
Prepare the code for adding more device info getters.
  • Loading branch information
AndreMiras committed Dec 5, 2024
1 parent ba80825 commit adcc978
Show file tree
Hide file tree
Showing 3 changed files with 85 additions and 38 deletions.
64 changes: 48 additions & 16 deletions src/cli.ts
Original file line number Diff line number Diff line change
Expand Up @@ -32,41 +32,73 @@ const promptPassword = (): Promise<string> => {
* @param command The command to which options should be added.
* @returns The command with options added.
*/
const addCommonOptions = (command: Command): Command =>
const addAuthOptions = (command: Command): Command =>
command
.requiredOption("-u, --username <username>", "Username")
.option("-p, --password <password>", "Password");

/**
* Adds MAC address option to a command.
* @param command The command to which the MAC address option should be added.
* @returns The command with the MAC address option added.
*/
const addMacOption = (command: Command): Command =>
command.requiredOption("-m, --mac <macAddress>", "MAC address of the device");

/**
* Executes a getter command by handling common steps (authentication, API initialization).
* @param options The options passed from the CLI command.
* @param getter A function to call on the configured API object.
*/
const executeGetter = async (
options: { username: string; password?: string; mac: string },
getter: (
api: ReturnType<typeof configure>,
jwtToken: string,
mac: string
) => Promise<unknown>
): Promise<void> => {
const { username, password, mac } = options;
const normalizedMac = mac.replace(/:/g, "");
const pwd = password || (await promptPassword());
const jwtToken = await signIn(username, pwd);
const api = configure();
const result = await getter(api, jwtToken, normalizedMac);
console.log(result);
};

const createProgram = (): Command => {
const program = new Command();
program
.name("edilkamin-cli")
.description("CLI tool for interacting with the Edilkamin API")
.version(version);
// Command: signIn
addCommonOptions(
addAuthOptions(
program.command("signIn").description("Sign in and retrieve a JWT token")
).action(async (options) => {
const { username, password } = options;
const pwd = password || (await promptPassword());
const jwtToken = await signIn(username, pwd);
console.log("JWT Token:", jwtToken);
});
// Command: deviceInfo
addCommonOptions(
program
.command("deviceInfo")
.description("Retrieve device info for a specific MAC address")
.requiredOption("-m, --mac <macAddress>", "MAC address of the device")
).action(async (options) => {
const { username, password, mac } = options;
const normalizedMac = mac.replace(/:/g, "");
const pwd = password || (await promptPassword());
const jwtToken = await signIn(username, pwd);
const api = configure(); // Use the default API configuration
const deviceInfo = await api.deviceInfo(jwtToken, normalizedMac);
console.log("Device Info:", deviceInfo.data);
// Generic getter commands
[
{
commandName: "deviceInfo",
description: "Retrieve device info for a specific MAC address",
getter: (
api: ReturnType<typeof configure>,
jwtToken: string,
mac: string
) => api.deviceInfo(jwtToken, mac),
},
].forEach(({ commandName, description, getter }) => {
addMacOption(
addAuthOptions(program.command(commandName).description(description))
).action((options) => executeGetter(options, getter));
});

return program;
};

Expand Down
40 changes: 22 additions & 18 deletions src/library.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import { API_URL } from "./constants";

describe("library", () => {
let axiosStub: sinon.SinonStub;
const expectedToken = "mockJwtToken";

beforeEach(() => {
axiosStub = sinon.stub(axios, "create").returns({
Expand All @@ -24,7 +25,6 @@ describe("library", () => {
it("should sign in and return the JWT token", async () => {
const expectedUsername = "testuser";
const expectedPassword = "testpassword";
const expectedToken = "mockJwtToken";
const signIn = sinon.stub().resolves({ isSignedIn: true });
const signOut = sinon.stub();
const fetchAuthSession = sinon.stub().resolves({
Expand Down Expand Up @@ -53,7 +53,6 @@ describe("library", () => {
it("should throw an error if sign-in fails", async () => {
const expectedUsername = "testuser";
const expectedPassword = "testpassword";
const expectedToken = "mockJwtToken";
const signIn = sinon.stub().resolves({ isSignedIn: false });
const signOut = sinon.stub();
const fetchAuthSession = sinon.stub().resolves({
Expand All @@ -79,6 +78,7 @@ describe("library", () => {
});

describe("configure", () => {
const expectedApi = ["deviceInfo", "setPower", "setPowerOff", "setPowerOn"];
it("should create API methods with the correct baseURL", () => {
const baseURL = "https://example.com/api";
const api = configure(baseURL);
Expand All @@ -89,12 +89,7 @@ describe("library", () => {
},
],
]);
assert.deepEqual(Object.keys(api), [
"deviceInfo",
"setPower",
"setPowerOff",
"setPowerOn",
]);
assert.deepEqual(Object.keys(api), expectedApi);
});
it("should create API methods with the default baseURL", () => {
const api = configure();
Expand All @@ -105,21 +100,30 @@ describe("library", () => {
},
],
]);
assert.deepEqual(Object.keys(api), [
"deviceInfo",
"setPower",
"setPowerOff",
"setPowerOn",
]);
assert.deepEqual(Object.keys(api), expectedApi);
});
});

describe("API Methods", () => {
const mockDeviceInfo = {
status: {
commands: {
power: true,
},
temperatures: {
enviroment: 19,
},
},
nvm: {
user_parameters: {
enviroment_1_temperature: 22,
},
},
};

it("should call axios for deviceInfo", async () => {
const expectedDevice = { id: "123", name: "Mock Device" };
const expectedToken = "mockToken";
const mockAxios = {
get: sinon.stub().resolves({ data: expectedDevice }),
get: sinon.stub().resolves({ data: mockDeviceInfo }),
};
axiosStub.returns(mockAxios);
const api = configure("https://example.com/api");
Expand All @@ -130,7 +134,7 @@ describe("library", () => {
{ headers: { Authorization: `Bearer ${expectedToken}` } },
],
]);
assert.deepEqual(result.data, expectedDevice);
assert.deepEqual(result, mockDeviceInfo);
});

// Tests for setPowerOn and setPowerOff
Expand Down
19 changes: 15 additions & 4 deletions src/library.ts
Original file line number Diff line number Diff line change
Expand Up @@ -61,10 +61,16 @@ const createAuthService = (auth: typeof amplifyAuth) => {
const { signIn } = createAuthService(amplifyAuth);

const deviceInfo =
(axiosInstance: AxiosInstance) => (jwtToken: string, macAddress: string) =>
axiosInstance.get<DeviceInfoType>(`device/${macAddress}/info`, {
headers: headers(jwtToken),
});
(axiosInstance: AxiosInstance) =>
async (jwtToken: string, macAddress: string) => {
const response = await axiosInstance.get<DeviceInfoType>(
`device/${macAddress}/info`,
{
headers: headers(jwtToken),
}
);
return response.data;
};

const mqttCommand =
(axiosInstance: AxiosInstance) =>
Expand All @@ -76,6 +82,10 @@ const mqttCommand =
{ headers: headers(jwtToken) }
);

/**
* Set device power.
* Return response string e.g. "Command 0123456789abcdef executed successfully".
*/
const setPower =
(axiosInstance: AxiosInstance) =>
(jwtToken: string, macAddress: string, value: number) =>
Expand All @@ -84,6 +94,7 @@ const setPower =
const setPowerOn =
(axiosInstance: AxiosInstance) => (jwtToken: string, macAddress: string) =>
setPower(axiosInstance)(jwtToken, macAddress, 1);

const setPowerOff =
(axiosInstance: AxiosInstance) => (jwtToken: string, macAddress: string) =>
setPower(axiosInstance)(jwtToken, macAddress, 0);
Expand Down

0 comments on commit adcc978

Please sign in to comment.