Skip to content

fix(mqtt): fixed how mqtt library is imported #514

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 8 commits into from
Jan 27, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -2,4 +2,5 @@ node_modules
lib
dist
es
.tmp
.tmp
misc
1 change: 1 addition & 0 deletions .nvmrc
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
v14.17.0
28 changes: 28 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -162,3 +162,31 @@ ArduinoIoTCloud.connect(options)
})
.catch(error => console.error(error));
```

## Development

### Testing
In order to test the library you have to export a couple of environment variables and then
launch a specific `npm` script as follows:

```sh
$ export CLIENT_ID=<YOUR_CLIENT_ID>
$ export CLIENT_SECRET=<YOUR_CLIENT_SECRET>
$ npm run test
```

## Changelog
### [0.9.0] - 2023-01-16

#### Changed
A few development settings have been updated, this should not affect how the library works.
- 'mqtt' is imported differently if the library is used in the browser or in node.
In browser we're using 'mqtt/dist/mqtt' because of some issues with React with some bundlers (namely, Parcel 2)

See:

[https://github.com/mqttjs/MQTT.js/issues/1412#issuecomment-1193363330](https://github.com/mqttjs/MQTT.js/issues/1412#issuecomment-1193363330)

[https://github.com/mqttjs/MQTT.js/issues/1233](https://github.com/mqttjs/MQTT.js/issues/1233)

- updated README file with this changelog and some instructions about testing
5 changes: 3 additions & 2 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

5 changes: 3 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "arduino-iot-js",
"version": "0.7.1",
"version": "0.9.0",
"license": "GPLv3",
"description": "JS module providing Arduino Create IoT Cloud Connection",
"main": "./lib/index.js",
Expand Down Expand Up @@ -58,7 +58,8 @@
"clean": "rimraf lib es",
"build:es": "rollup -c",
"build:lib": "rollup -c ./rollup.config.lib.js",
"build": "npm run clean && npm run build:es && npm run build:lib"
"build": "npm run clean && npm run build:es && npm run build:lib",
"dev": "rollup -c -w"
},
"repository": {
"type": "git",
Expand Down
15 changes: 8 additions & 7 deletions src/builder/APIConnectionBuilder.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import { MqttClient } from 'mqtt';
import { IHttpClient } from '../http/IHttpClient';
import { Connection } from '../connection/Connection';
import { IConnection } from '../connection/IConnection';
Expand All @@ -10,8 +11,12 @@ type AccessResponse = {
token_type: string;
};

function isApiOptions(options: CloudOptions): options is APIOptions {
return !!(options as APIOptions).clientId;
}

export class APIConnectionBuilder implements IConnectionBuilder {
constructor(private client: IHttpClient) {}
constructor(private client: IHttpClient, private mqttConnect: (string, IClientOptions) => MqttClient) {}

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

const { access_token } = await this.client.post<AccessResponse>(apiUrl, body, headers);
return Connection.From(options.host, options.port, access_token);
const { access_token: accessToken } = await this.client.post<AccessResponse>(apiUrl, body, headers);
return Connection.From(options.host, options.port, accessToken, this.mqttConnect);
}
}

function isApiOptions(options: CloudOptions): options is APIOptions {
return !!(options as APIOptions).clientId;
}
5 changes: 4 additions & 1 deletion src/builder/TokenConnectionBuilder.ts
Original file line number Diff line number Diff line change
@@ -1,14 +1,17 @@
import { MqttClient } from 'mqtt';
import { Connection } from '../connection/Connection';
import { IConnection } from '../connection/IConnection';
import { IConnectionBuilder } from './IConnectionBuilder';
import { BrowserOptions, CloudOptions, BaseCloudOptions } from '../client/ICloudClient';

export class TokenConnectionBuilder implements IConnectionBuilder {
constructor(private mqttConnect: (string, IClientOptions) => MqttClient) {}

canBuild(options: CloudOptions): boolean {
return !!(options as BrowserOptions).token;
}

build({ host, port, token }: BrowserOptions & BaseCloudOptions): Promise<IConnection> {
return Connection.From(host, port, token);
return Connection.From(host, port, token, this.mqttConnect);
}
}
14 changes: 11 additions & 3 deletions src/connection/Connection.ts
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,12 @@ export class Connection implements IConnection {
});
}

public static async From(host: string, port: string | number, token: string): Promise<IConnection> {
public static async From(
host: string,
port: string | number,
token: string,
mqttConnect: (string, IClientOptions) => mqtt.MqttClient
): Promise<IConnection> {
if (!token) throw new Error('connection failed: you need to provide a valid token');
if (!host) throw new Error('connection failed: you need to provide a valid host (broker)');

Expand All @@ -46,7 +51,7 @@ export class Connection implements IConnection {
};

const connection = new Connection();
connection.client = mqtt.connect(`wss://${host}:${port}/mqtt`, {
connection.client = mqttConnect(`wss://${host}:${port}/mqtt`, {
...BaseConnectionOptions,
...options,
});
Expand Down Expand Up @@ -106,7 +111,10 @@ export class Connection implements IConnection {
else valueToSend = value;
});

if (valueToSend !== {}) messages.push({ topic, propertyName: current, value: valueToSend });
// Checking if valueToSend is NOT {}
if (Utils.isNotAnEmptyObject(valueToSend)) {
messages.push({ topic, propertyName: current, value: valueToSend });
}

return messages;
}
Expand Down
53 changes: 29 additions & 24 deletions src/index.lib.ts
Original file line number Diff line number Diff line change
@@ -1,33 +1,38 @@
/*
* Copyright 2020 ARDUINO SA (http://www.arduino.cc/)
* This file is part of arduino-iot-js.
* Copyright (c) 2020
* Authors: Fabrizio Mirabito, Francesco Pirrotta
*
* This software is released under:
* The GNU General Public License, which covers the main part of
* arduino-iot-js
* The terms of this license can be found at:
* https://www.gnu.org/licenses/gpl-3.0.en.html
*
* You can be released from the requirements of the above licenses by purchasing
* a commercial license. Buying such a license is mandatory if you want to modify or
* otherwise use the software for commercial activities involving the Arduino
* software without disclosing the source code of your own applications. To purchase
* a commercial license, send an email to [email protected].
*
*/
* Copyright 2020 ARDUINO SA (http://www.arduino.cc/)
* This file is part of arduino-iot-js.
* Copyright (c) 2020
* Authors: Fabrizio Mirabito, Francesco Pirrotta
*
* This software is released under:
* The GNU General Public License, which covers the main part of
* arduino-iot-js
* The terms of this license can be found at:
* https://www.gnu.org/licenses/gpl-3.0.en.html
*
* You can be released from the requirements of the above licenses by purchasing
* a commercial license. Buying such a license is mandatory if you want to modify or
* otherwise use the software for commercial activities involving the Arduino
* software without disclosing the source code of your own applications. To purchase
* a commercial license, send an email to [email protected].
*
*/

import SenML from './senML';
import fetch from "node-fetch";
import fetch from 'node-fetch';
import mqtt from 'mqtt';

import { HttpClientFactory } from './http/HttpClientFactory';
import { CloudClient } from "./client/CloudClient";
import { APIConnectionBuilder } from "./builder/APIConnectionBuilder";
import { TokenConnectionBuilder } from "./builder/TokenConnectionBuilder";
import { CloudClient } from './client/CloudClient';
import { APIConnectionBuilder } from './builder/APIConnectionBuilder';
import { TokenConnectionBuilder } from './builder/TokenConnectionBuilder';

const builders = [new TokenConnectionBuilder(), new APIConnectionBuilder(HttpClientFactory.Create(fetch))];
const builders = [
new TokenConnectionBuilder(mqtt.connect),
new APIConnectionBuilder(HttpClientFactory.Create(fetch), mqtt.connect),
];
const ArduinoIoTCloud = new CloudClient(builders);

export { SenML };
export { ArduinoIoTCloud };
export { CloudOptions, CloudMessageValue } from "./client/ICloudClient";
export { CloudOptions, CloudMessageValue } from './client/ICloudClient';
52 changes: 28 additions & 24 deletions src/index.ts
Original file line number Diff line number Diff line change
@@ -1,33 +1,37 @@
/*
* Copyright 2020 ARDUINO SA (http://www.arduino.cc/)
* This file is part of arduino-iot-js.
* Copyright (c) 2020
* Authors: Fabrizio Mirabito, Francesco Pirrotta
*
* This software is released under:
* The GNU General Public License, which covers the main part of
* arduino-iot-js
* The terms of this license can be found at:
* https://www.gnu.org/licenses/gpl-3.0.en.html
*
* You can be released from the requirements of the above licenses by purchasing
* a commercial license. Buying such a license is mandatory if you want to modify or
* otherwise use the software for commercial activities involving the Arduino
* software without disclosing the source code of your own applications. To purchase
* a commercial license, send an email to [email protected].
*
*/
* Copyright 2020 ARDUINO SA (http://www.arduino.cc/)
* This file is part of arduino-iot-js.
* Copyright (c) 2020
* Authors: Fabrizio Mirabito, Francesco Pirrotta
*
* This software is released under:
* The GNU General Public License, which covers the main part of
* arduino-iot-js
* The terms of this license can be found at:
* https://www.gnu.org/licenses/gpl-3.0.en.html
*
* You can be released from the requirements of the above licenses by purchasing
* a commercial license. Buying such a license is mandatory if you want to modify or
* otherwise use the software for commercial activities involving the Arduino
* software without disclosing the source code of your own applications. To purchase
* a commercial license, send an email to [email protected].
*
*/

import "whatwg-fetch";
import 'whatwg-fetch';
import mqtt from 'mqtt/dist/mqtt';
import SenML from './senML';
import { HttpClientFactory } from './http/HttpClientFactory';
import { CloudClient } from "./client/CloudClient";
import { APIConnectionBuilder } from "./builder/APIConnectionBuilder";
import { TokenConnectionBuilder } from "./builder/TokenConnectionBuilder";
import { CloudClient } from './client/CloudClient';
import { APIConnectionBuilder } from './builder/APIConnectionBuilder';
import { TokenConnectionBuilder } from './builder/TokenConnectionBuilder';

const builders = [new TokenConnectionBuilder(), new APIConnectionBuilder(HttpClientFactory.Create(fetch))];
const builders = [
new TokenConnectionBuilder(mqtt.connect),
new APIConnectionBuilder(HttpClientFactory.Create(fetch), mqtt.connect),
];
const ArduinoIoTCloud = new CloudClient(builders);

export { SenML };
export { ArduinoIoTCloud };
export { CloudOptions, CloudMessageValue } from "./client/ICloudClient";
export { CloudOptions, CloudMessageValue } from './client/ICloudClient';
5 changes: 5 additions & 0 deletions src/utils/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,10 @@ function isArray<T>(value: CloudMessageValue): value is T[] {
return Array.isArray(value);
}

function isNotAnEmptyObject(value): boolean {
return !(typeof value === 'object' && Object.keys(value).length == 0);
}

function toArrayBuffer(buf: { length: number }): ArrayBuffer {
const ab = new ArrayBuffer(buf.length);
const view = new Uint8Array(ab);
Expand Down Expand Up @@ -69,4 +73,5 @@ export default {
toArrayBuffer,
toBuffer,
arrayBufferToBase64,
isNotAnEmptyObject,
};
6 changes: 6 additions & 0 deletions test/arduino-cloud.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,12 @@ describe('Test the library basic functionalities', () => {
await ArduinoIoTCloud.onPropertyValue(thingId, propertyBoolName, (value) => value === propertyBoolVal ? done() : null);
sendPropertyAsDevice(deviceId, thingId, propertyBoolName, propertyBoolVal);
});

it('Simulate client read boolean as FALSE property sent by device', async (done) => {
await ArduinoIoTCloud.onPropertyValue(thingId, propertyBoolName, (value) => !value ? done() : null);
sendPropertyAsDevice(deviceId, thingId, propertyBoolName, false);
});

})
});

Expand Down