Skip to content

Commit 96ef8cc

Browse files
authored
fix(mqtt): fixed how mqtt library is imported (#514)
fix(mqtt): fixed how mqtt library is imported - Because of some issues with some bundlers (namely, Parcel 2) we have updated the way mqtt.connect is imported in Connection.ts - Added a changelog section in readme file. - Improved a check on empty messages before sending them.
1 parent 2c43fcd commit 96ef8cc

12 files changed

+128
-64
lines changed

.gitignore

+2-1
Original file line numberDiff line numberDiff line change
@@ -2,4 +2,5 @@ node_modules
22
lib
33
dist
44
es
5-
.tmp
5+
.tmp
6+
misc

.nvmrc

+1
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
v14.17.0

README.md

+28
Original file line numberDiff line numberDiff line change
@@ -162,3 +162,31 @@ ArduinoIoTCloud.connect(options)
162162
})
163163
.catch(error => console.error(error));
164164
```
165+
166+
## Development
167+
168+
### Testing
169+
In order to test the library you have to export a couple of environment variables and then
170+
launch a specific `npm` script as follows:
171+
172+
```sh
173+
$ export CLIENT_ID=<YOUR_CLIENT_ID>
174+
$ export CLIENT_SECRET=<YOUR_CLIENT_SECRET>
175+
$ npm run test
176+
```
177+
178+
## Changelog
179+
### [0.9.0] - 2023-01-16
180+
181+
#### Changed
182+
A few development settings have been updated, this should not affect how the library works.
183+
- 'mqtt' is imported differently if the library is used in the browser or in node.
184+
In browser we're using 'mqtt/dist/mqtt' because of some issues with React with some bundlers (namely, Parcel 2)
185+
186+
See:
187+
188+
[https://github.com/mqttjs/MQTT.js/issues/1412#issuecomment-1193363330](https://github.com/mqttjs/MQTT.js/issues/1412#issuecomment-1193363330)
189+
190+
[https://github.com/mqttjs/MQTT.js/issues/1233](https://github.com/mqttjs/MQTT.js/issues/1233)
191+
192+
- updated README file with this changelog and some instructions about testing

package-lock.json

+3-2
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

+3-2
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "arduino-iot-js",
3-
"version": "0.7.1",
3+
"version": "0.9.0",
44
"license": "GPLv3",
55
"description": "JS module providing Arduino Create IoT Cloud Connection",
66
"main": "./lib/index.js",
@@ -58,7 +58,8 @@
5858
"clean": "rimraf lib es",
5959
"build:es": "rollup -c",
6060
"build:lib": "rollup -c ./rollup.config.lib.js",
61-
"build": "npm run clean && npm run build:es && npm run build:lib"
61+
"build": "npm run clean && npm run build:es && npm run build:lib",
62+
"dev": "rollup -c -w"
6263
},
6364
"repository": {
6465
"type": "git",

src/builder/APIConnectionBuilder.ts

+8-7
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
import { MqttClient } from 'mqtt';
12
import { IHttpClient } from '../http/IHttpClient';
23
import { Connection } from '../connection/Connection';
34
import { IConnection } from '../connection/IConnection';
@@ -10,8 +11,12 @@ type AccessResponse = {
1011
token_type: string;
1112
};
1213

14+
function isApiOptions(options: CloudOptions): options is APIOptions {
15+
return !!(options as APIOptions).clientId;
16+
}
17+
1318
export class APIConnectionBuilder implements IConnectionBuilder {
14-
constructor(private client: IHttpClient) {}
19+
constructor(private client: IHttpClient, private mqttConnect: (string, IClientOptions) => MqttClient) {}
1520

1621
public canBuild(options: CloudOptions): boolean {
1722
return isApiOptions(options);
@@ -27,11 +32,7 @@ export class APIConnectionBuilder implements IConnectionBuilder {
2732
body.append('client_secret', options.clientSecret);
2833
body.append('audience', options.audience || 'https://api2.arduino.cc/iot');
2934

30-
const { access_token } = await this.client.post<AccessResponse>(apiUrl, body, headers);
31-
return Connection.From(options.host, options.port, access_token);
35+
const { access_token: accessToken } = await this.client.post<AccessResponse>(apiUrl, body, headers);
36+
return Connection.From(options.host, options.port, accessToken, this.mqttConnect);
3237
}
3338
}
34-
35-
function isApiOptions(options: CloudOptions): options is APIOptions {
36-
return !!(options as APIOptions).clientId;
37-
}

src/builder/TokenConnectionBuilder.ts

+4-1
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,17 @@
1+
import { MqttClient } from 'mqtt';
12
import { Connection } from '../connection/Connection';
23
import { IConnection } from '../connection/IConnection';
34
import { IConnectionBuilder } from './IConnectionBuilder';
45
import { BrowserOptions, CloudOptions, BaseCloudOptions } from '../client/ICloudClient';
56

67
export class TokenConnectionBuilder implements IConnectionBuilder {
8+
constructor(private mqttConnect: (string, IClientOptions) => MqttClient) {}
9+
710
canBuild(options: CloudOptions): boolean {
811
return !!(options as BrowserOptions).token;
912
}
1013

1114
build({ host, port, token }: BrowserOptions & BaseCloudOptions): Promise<IConnection> {
12-
return Connection.From(host, port, token);
15+
return Connection.From(host, port, token, this.mqttConnect);
1316
}
1417
}

src/connection/Connection.ts

+11-3
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,12 @@ export class Connection implements IConnection {
3434
});
3535
}
3636

37-
public static async From(host: string, port: string | number, token: string): Promise<IConnection> {
37+
public static async From(
38+
host: string,
39+
port: string | number,
40+
token: string,
41+
mqttConnect: (string, IClientOptions) => mqtt.MqttClient
42+
): Promise<IConnection> {
3843
if (!token) throw new Error('connection failed: you need to provide a valid token');
3944
if (!host) throw new Error('connection failed: you need to provide a valid host (broker)');
4045

@@ -46,7 +51,7 @@ export class Connection implements IConnection {
4651
};
4752

4853
const connection = new Connection();
49-
connection.client = mqtt.connect(`wss://${host}:${port}/mqtt`, {
54+
connection.client = mqttConnect(`wss://${host}:${port}/mqtt`, {
5055
...BaseConnectionOptions,
5156
...options,
5257
});
@@ -106,7 +111,10 @@ export class Connection implements IConnection {
106111
else valueToSend = value;
107112
});
108113

109-
if (valueToSend !== {}) messages.push({ topic, propertyName: current, value: valueToSend });
114+
// Checking if valueToSend is NOT {}
115+
if (Utils.isNotAnEmptyObject(valueToSend)) {
116+
messages.push({ topic, propertyName: current, value: valueToSend });
117+
}
110118

111119
return messages;
112120
}

src/index.lib.ts

+29-24
Original file line numberDiff line numberDiff line change
@@ -1,33 +1,38 @@
11
/*
2-
* Copyright 2020 ARDUINO SA (http://www.arduino.cc/)
3-
* This file is part of arduino-iot-js.
4-
* Copyright (c) 2020
5-
* Authors: Fabrizio Mirabito, Francesco Pirrotta
6-
*
7-
* This software is released under:
8-
* The GNU General Public License, which covers the main part of
9-
* arduino-iot-js
10-
* The terms of this license can be found at:
11-
* https://www.gnu.org/licenses/gpl-3.0.en.html
12-
*
13-
* You can be released from the requirements of the above licenses by purchasing
14-
* a commercial license. Buying such a license is mandatory if you want to modify or
15-
* otherwise use the software for commercial activities involving the Arduino
16-
* software without disclosing the source code of your own applications. To purchase
17-
* a commercial license, send an email to [email protected].
18-
*
19-
*/
2+
* Copyright 2020 ARDUINO SA (http://www.arduino.cc/)
3+
* This file is part of arduino-iot-js.
4+
* Copyright (c) 2020
5+
* Authors: Fabrizio Mirabito, Francesco Pirrotta
6+
*
7+
* This software is released under:
8+
* The GNU General Public License, which covers the main part of
9+
* arduino-iot-js
10+
* The terms of this license can be found at:
11+
* https://www.gnu.org/licenses/gpl-3.0.en.html
12+
*
13+
* You can be released from the requirements of the above licenses by purchasing
14+
* a commercial license. Buying such a license is mandatory if you want to modify or
15+
* otherwise use the software for commercial activities involving the Arduino
16+
* software without disclosing the source code of your own applications. To purchase
17+
* a commercial license, send an email to [email protected].
18+
*
19+
*/
2020

2121
import SenML from './senML';
22-
import fetch from "node-fetch";
22+
import fetch from 'node-fetch';
23+
import mqtt from 'mqtt';
24+
2325
import { HttpClientFactory } from './http/HttpClientFactory';
24-
import { CloudClient } from "./client/CloudClient";
25-
import { APIConnectionBuilder } from "./builder/APIConnectionBuilder";
26-
import { TokenConnectionBuilder } from "./builder/TokenConnectionBuilder";
26+
import { CloudClient } from './client/CloudClient';
27+
import { APIConnectionBuilder } from './builder/APIConnectionBuilder';
28+
import { TokenConnectionBuilder } from './builder/TokenConnectionBuilder';
2729

28-
const builders = [new TokenConnectionBuilder(), new APIConnectionBuilder(HttpClientFactory.Create(fetch))];
30+
const builders = [
31+
new TokenConnectionBuilder(mqtt.connect),
32+
new APIConnectionBuilder(HttpClientFactory.Create(fetch), mqtt.connect),
33+
];
2934
const ArduinoIoTCloud = new CloudClient(builders);
3035

3136
export { SenML };
3237
export { ArduinoIoTCloud };
33-
export { CloudOptions, CloudMessageValue } from "./client/ICloudClient";
38+
export { CloudOptions, CloudMessageValue } from './client/ICloudClient';

src/index.ts

+28-24
Original file line numberDiff line numberDiff line change
@@ -1,33 +1,37 @@
11
/*
2-
* Copyright 2020 ARDUINO SA (http://www.arduino.cc/)
3-
* This file is part of arduino-iot-js.
4-
* Copyright (c) 2020
5-
* Authors: Fabrizio Mirabito, Francesco Pirrotta
6-
*
7-
* This software is released under:
8-
* The GNU General Public License, which covers the main part of
9-
* arduino-iot-js
10-
* The terms of this license can be found at:
11-
* https://www.gnu.org/licenses/gpl-3.0.en.html
12-
*
13-
* You can be released from the requirements of the above licenses by purchasing
14-
* a commercial license. Buying such a license is mandatory if you want to modify or
15-
* otherwise use the software for commercial activities involving the Arduino
16-
* software without disclosing the source code of your own applications. To purchase
17-
* a commercial license, send an email to [email protected].
18-
*
19-
*/
2+
* Copyright 2020 ARDUINO SA (http://www.arduino.cc/)
3+
* This file is part of arduino-iot-js.
4+
* Copyright (c) 2020
5+
* Authors: Fabrizio Mirabito, Francesco Pirrotta
6+
*
7+
* This software is released under:
8+
* The GNU General Public License, which covers the main part of
9+
* arduino-iot-js
10+
* The terms of this license can be found at:
11+
* https://www.gnu.org/licenses/gpl-3.0.en.html
12+
*
13+
* You can be released from the requirements of the above licenses by purchasing
14+
* a commercial license. Buying such a license is mandatory if you want to modify or
15+
* otherwise use the software for commercial activities involving the Arduino
16+
* software without disclosing the source code of your own applications. To purchase
17+
* a commercial license, send an email to [email protected].
18+
*
19+
*/
2020

21-
import "whatwg-fetch";
21+
import 'whatwg-fetch';
22+
import mqtt from 'mqtt/dist/mqtt';
2223
import SenML from './senML';
2324
import { HttpClientFactory } from './http/HttpClientFactory';
24-
import { CloudClient } from "./client/CloudClient";
25-
import { APIConnectionBuilder } from "./builder/APIConnectionBuilder";
26-
import { TokenConnectionBuilder } from "./builder/TokenConnectionBuilder";
25+
import { CloudClient } from './client/CloudClient';
26+
import { APIConnectionBuilder } from './builder/APIConnectionBuilder';
27+
import { TokenConnectionBuilder } from './builder/TokenConnectionBuilder';
2728

28-
const builders = [new TokenConnectionBuilder(), new APIConnectionBuilder(HttpClientFactory.Create(fetch))];
29+
const builders = [
30+
new TokenConnectionBuilder(mqtt.connect),
31+
new APIConnectionBuilder(HttpClientFactory.Create(fetch), mqtt.connect),
32+
];
2933
const ArduinoIoTCloud = new CloudClient(builders);
3034

3135
export { SenML };
3236
export { ArduinoIoTCloud };
33-
export { CloudOptions, CloudMessageValue } from "./client/ICloudClient";
37+
export { CloudOptions, CloudMessageValue } from './client/ICloudClient';

src/utils/index.ts

+5
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,10 @@ function isArray<T>(value: CloudMessageValue): value is T[] {
3131
return Array.isArray(value);
3232
}
3333

34+
function isNotAnEmptyObject(value): boolean {
35+
return !(typeof value === 'object' && Object.keys(value).length == 0);
36+
}
37+
3438
function toArrayBuffer(buf: { length: number }): ArrayBuffer {
3539
const ab = new ArrayBuffer(buf.length);
3640
const view = new Uint8Array(ab);
@@ -69,4 +73,5 @@ export default {
6973
toArrayBuffer,
7074
toBuffer,
7175
arrayBufferToBase64,
76+
isNotAnEmptyObject,
7277
};

test/arduino-cloud.test.js

+6
Original file line numberDiff line numberDiff line change
@@ -92,6 +92,12 @@ describe('Test the library basic functionalities', () => {
9292
await ArduinoIoTCloud.onPropertyValue(thingId, propertyBoolName, (value) => value === propertyBoolVal ? done() : null);
9393
sendPropertyAsDevice(deviceId, thingId, propertyBoolName, propertyBoolVal);
9494
});
95+
96+
it('Simulate client read boolean as FALSE property sent by device', async (done) => {
97+
await ArduinoIoTCloud.onPropertyValue(thingId, propertyBoolName, (value) => !value ? done() : null);
98+
sendPropertyAsDevice(deviceId, thingId, propertyBoolName, false);
99+
});
100+
95101
})
96102
});
97103

0 commit comments

Comments
 (0)