Reputation: 13171
The JavaScript module "@google-cloud/iot" doesn't seem to much resemble its documentation here (https://googleapis.dev/nodejs/iot/latest/index.html).
For example, the function client.sendCommandToDevice()
is documented to return type SendCommandToDeviceResponse
, while it actually returns a complex intersection of interfaces and undefineds. I can find the right type buried in a nest of namespaces inside the module, but even then I can't manage to get the data I need from the result. For example, the ...Response type has a static function "toJSON()" that takes a Response and returns JSON--but it won't take the actual response object, and I can't find any way to convert the actual response object into the expected type.
This isn't the only such error. Is this perhaps an artifact of over-hastily converting an old JavaScript API to TypeScript without checking to see that it's actually usable in TypeScript? Is there perhaps some documentation on the older JavaScript API that I might look at to compare?
Here's a code snippet (before transpilation):
// node 14.17.3, TypeScript 4.4.4, iot 2.5.1
import iot from "@google-cloud/iot";
// Yes, I did the credentials thing...
const client = new iot.v1.DeviceManagerClient();
function generateRequest(id: string, data: any) {
const formattedName = client.devicePath("myproject", "us-central1", "myregistry", id);
const binData = Buffer.from(JSON.stringify(data)).toString("base64");
return {
ok: true,
name: formattedName,
binData: binData,
};
}
async function run() {
const project = await client.getProjectId();
const parent = client.locationPath(project, 'us-central1');
const deviceId = "d99999995";
const data = { get: [ "program" ] };
const request = generateRequest(deviceId, data);
let r = await client.sendCommandToDevice(request);
// what is r here???
/* Same issue with, e.g.:
client.updateDevice();
client.modifyCloudToDeviceConfig()
*/
return {
ok: true,
result: r,
};
}
run().catch((err) => { console.error(err); });
Upvotes: 0
Views: 356
Reputation: 40091
Immediate feedback: your responses to the commenters show your evident frustration. I think it's reasonable to assume that people don't take time to respond to your question to frustrate you. I was reluctant to reply to, not wishing to also be chastised, but like them I want to try to help you...
The documentation for the library appears correct but I'm neither a TypeScript nor JavaScript guru. I am very familiar with GCP and its services and writing client code.
Google has been evolving its services to reflect its internal widespread use of gRPC. It appears to me that some of this move towards more gRPC-native services is bleeding through into its SDKs (viz the preponderance of protobuf messages) and I find this confusing especially because I've not seen it fully explained.
In the case of @google-cloud/iot
the methods do reference protobuf messages. So, your sendCommandToDevice
method has a signature which takes a SendCommandToDeviceRequest
and returns a Promised SendCommandToDeviceResponse
.
As I say, I'm unfamiliar with TypeScript but, in JavaScript (sic.), I wonder whether you must use the SendCommandToDeviceRequest
constructor to create your request:
function generateRequest(id, data) {
const formattedName = client.devicePath("myproject", "us-central1", "myregistry", id);
const binaryData = Buffer.from(JSON.stringify(data)).toString("base64");
return {
name: formattedName,
binaryData: binaryData,
};
}
const rqst = new iot.protos.google.cloud.iot.v1.SendCommandToDeviceRequest(
generateRequest(deviceId, data)
);
NOTE The property is
binaryData
notbinData
Then, since it's await
'ed, I think your response type will be SendCommandToDeviceResponse
:
const resp = await client.sendCommandToDevice(rqst);
@Ash_max referenced this type in the comments. Although the constructor is listed at the top of the page, lower down the page is toJSON()
. So you should be able to:
console.log(`response: ${resp.toJSON()}`);
maybe
JSON.stringify(resp.toJSON())
I've not time to try this today but I'll try to repro your experience tomorrow and update this thread.
I created a GCP project, registry and device.
I ran the Node.JS code outlined above and the return type is confusing; I'm getting an array of 3 objects, the first of which looks to be a proto but doesn't support toJSON
and I can't get it toString()
either.
So, I looked at APIs Explorer and according to it sendCommandToDevice (which I take as definitive), the response body will be empty on success.
Confused, I wrote the equivalent Golang program:
package main
import (
"context"
b64 "encoding/base64"
"fmt"
"log"
"os"
"google.golang.org/api/cloudiot/v1"
)
func main() {
ctx := context.Background()
client, err := cloudiot.NewService(ctx)
if err != nil {
log.Fatal(err)
}
rqst := &cloudiot.SendCommandToDeviceRequest{
BinaryData: b64.StdEncoding.EncodeToString([]byte("Hello Freddie")),
}
name := fmt.Sprintf("projects/%s/locations/%s/registries/%s/devices/%s",
os.Getenv("PROJECT"),
os.Getenv("REGION"),
os.Getenv("REGISTRY"),
os.Getenv("DEVICE"),
)
devices := client.Projects.Locations.Registries.Devices
resp, err := devices.SendCommandToDevice(name, rqst).Do()
if err != nil {
log.Fatal(err)
}
log.Printf("%+v", resp)
}
And its resp
is definitely of type SendCommandToDeviceResponse
and, contains ServerResponse
which includes the HTTPStatusCode
:
&{
ServerResponse:{
HTTPStatusCode:200
Header:map[
Cache-Control:[private]
Content-Type:[application/json; charset=UTF-8]
Date:[Wed, 20 Oct 2021 00:20:00 GMT]
Server:[ESF] Vary:[Origin X-Origin Referer]
X-Content-Type-Options:[nosniff]
X-Frame-Options:[SAMEORIGIN]
X-Xss-Protection:[0]
]
}
}
So, the JavaScript (Node.JS|TypeScript) code should somehow (!) be able to grab that ServerResponse
too from the SendCommandToDeviceResponse
.
The documentation appears to be correct.
Upvotes: 1