diff --git a/iot/README.md b/iot/README.md index 16fa87d59b..05bb7f1356 100644 --- a/iot/README.md +++ b/iot/README.md @@ -7,7 +7,7 @@ Cloud IoT Core platform. ## Quickstart -1. Install the Google Cloud SDK as described in [the device manager guide](/iot/docs/device_manager_guide#install_the_gcloud_cli). +1. Install the Google Cloud SDK as described in [the device manager guide](https://cloud.google.com/iot/docs/device_manager_guide#install_the_gcloud_cli). 1. Create a Cloud Pub/Sub topic: gcloud beta pubsub topics create projects/my-iot-project/topics/device-events diff --git a/iot/manager/cloudiot_device_manager_example.js b/iot/manager/cloudiot_device_manager_example.js deleted file mode 100644 index ad0cd2bbdf..0000000000 --- a/iot/manager/cloudiot_device_manager_example.js +++ /dev/null @@ -1,316 +0,0 @@ -/** - * Copyright 2017, Google, Inc. - * Licensed under the Apache License, Version 2.0 (the `License`); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an `AS IS` BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -/** - * Example of using the Google Cloud IoT Core device manager to administer - * devices. - * - * This example uses the Device Manager API to create, retrieve, disable, list - * and delete Cloud IoT Core devices and registries, using both RSA and - * elliptic curve keys for authentication. - * - * To start, follow the instructions on the Developer Guide at - * cloud.google.com/iot to create a service_account.json file and API key. - * - * Register your device as described in the parent README. - * - * Usage example: - * - * $ npm install - * $ nodejs cloudiot-device-manager-example.js \ - * --project_id=my-project-id \ - * --pubsub_topic=projects/my-project-id/topics/my-topic-id \ - * --api_key=YOUR_API_KEY \ - * --ec_public_key_file=ec_public.pem \ - * --rsa_certificate_file=rsa_cert.pem \ - * --service_account_json=service_account.json - * - * Troubleshooting: - * - * If you get a 400 error when running the example, with the message "The API - * Key and the authentication credential are from different projects" it means - * that you are using the wrong API Key. Ensure that you are using the API key - * from Google Cloud Platform's API Manager's Credentials page. - */ - -'use strict'; - -const async = require('async'); -const fs = require('fs'); -const google = require('googleapis'); -const program = require('commander'); - -program.description('Example Google Cloud IoT device manager integration') - .option('--project_id ', 'GCP cloud project name.') - .option('--pubsub_topic ', 'Cloud Pub/Sub topic to use.') - .option('--api_key ', 'Your API key.') - .option( - '--ec_public_key_file ', 'Path to EC public key.', - 'ec_public.pem') - .option( - '--rsa_certificate_file ', - 'Path to RSA certificate file.', 'rsa_cert.pem') - .option('--cloud_region ', 'GCP cloud region.', 'us-central1') - .option( - '--service_account_json ', - 'Path to service account JSON file.', 'service_account.json') - .option( - '--registry_id ', - 'Custom registry id. ' + - 'If not provided, a unique registry id will be generated.', - '') - .parse(process.argv); - -const API_VERSION = 'v1alpha1'; -const DISCOVERY_API = 'https://cloudiot.googleapis.com/$discovery/rest'; - -// Lookup the registry, assuming that it exists. -function lookupRegistry (client, registryName, cb) { - const request = { - name: registryName - }; - - client.projects.locations.registries.get(request, (err, data) => { - if (err) { - console.log('Could not look up registry'); - console.log(err); - cb(err); - } else { - console.log('Looked up existing registry'); - console.log(data); - cb(null); - } - }); -} - -// Create a new registry, or look up an existing one if it doesn't exist. -function lookupOrCreateRegistry (client, registryId, parentName, pubsubTopic, cb) { - const request = { - parent: parentName, - id: registryId, - resource: { - eventNotificationConfig: { - pubsubTopicName: pubsubTopic - } - } - }; - - client.projects.locations.registries.create(request, (err, data) => { - if (err) { - if (err.code === 409) { - // The registry already exists - look it up instead. - lookupRegistry(client, cb); - } else { - console.log('Could not create registry'); - console.log(err); - cb(err); - } - } else { - console.log('Successfully created registry'); - console.log(data); - cb(null); - } - }); -} - -// Create a new device with the given id. The body defines the parameters for -// the device, such as authentication. -function createDevice (client, deviceId, registryName, body, cb) { - console.log('Creating device:', deviceId); - - const request = { - parent: registryName, - id: deviceId, - resource: body - }; - - client.projects.locations.registries.devices.create(request, (err, data) => { - if (err) { - console.log('Could not create device'); - console.log(err); - cb(err); - } else { - console.log('Created device'); - console.log(data); - cb(null); - } - }); -} - -// Create a device using RS256 for authentication. -function createDeviceWithRs256 (client, deviceId, registryName, rsaCertificateFile, cb) { - const body = { - credentials: [ - { - publicKey: { - format: 'RSA_X509_PEM', - key: fs.readFileSync(rsaCertificateFile).toString() - } - } - ] - }; - - createDevice(client, deviceId, registryName, body, cb); -} - -// Add ES256 authentication to the given device. -function patchEs256ForAuth (client, deviceId, registryName, ecPublicKeyFile, cb) { - const request = { - name: `${registryName}/devices/${deviceId}`, - updateMask: 'credentials', - resource: { - credentials: [ - { - publicKey: { - format: 'ES256_PEM', - key: fs.readFileSync(ecPublicKeyFile).toString() - } - } - ] - } - }; - - client.projects.locations.registries.devices.patch(request, (err, data) => { - if (err) { - console.log('Error patching device:', deviceId); - console.log(err); - cb(err); - } else { - console.log('Patched device:', deviceId); - console.log(data); - cb(null); - } - }); -} - -// List all of the devices in the given registry. -function listDevices (client, registryName, cb) { - const request = { - parent: registryName - }; - - client.projects.locations.registries.devices.list(request, (err, data) => { - if (err) { - console.log('Could not list devices'); - console.log(err); - cb(err); - } else { - console.log('Current devices in registry:', data['devices']); - cb(null); - } - }); -} - -// Delete the given device from the registry. -function deleteDevice (client, deviceId, registryName, cb) { - const request = { - name: `${registryName}/devices/${deviceId}` - }; - - client.projects.locations.registries.devices.delete(request, (err, data) => { - if (err) { - console.log('Could not delete device:', deviceId); - console.log(err); - cb(err); - } else { - console.log('Successfully deleted device:', deviceId); - console.log(data); - cb(null); - } - }); -} - -// Delete the given registry. Note that this will only succeed if the registry -// is empty. -function deleteRegistry (client, registryName, cb) { - const request = { - name: registryName - }; - - client.projects.locations.registries.delete(request, (err, data) => { - if (err) { - console.log('Could not delete registry'); - console.log(err); - } else { - console.log('Successfully deleted registry'); - console.log(data); - } - cb(err); - }); -} - -// Set up authentiation using the downloaded service_account.json file. -function setUpAuth () { - const serviceAccount = JSON.parse(fs.readFileSync(program.service_account_json)); - const jwtAccess = new google.auth.JWT(); - jwtAccess.fromJSON(serviceAccount); - // Note that if you require additional scopes, they should be specified as a - // string, separated by spaces. - jwtAccess.scopes = 'https://www.googleapis.com/auth/cloud-platform'; - // Set the default authentication to the above JWT access. - google.options({ auth: jwtAccess }); -} - -setUpAuth(); - -const discoveryUrl = `${DISCOVERY_API}?version=${API_VERSION}&key=${program.api_key}`; - -google.discoverAPI(discoveryUrl, {}, (err, client) => { - if (err) { - console.log('Error during API discovery', err); - return; - } - - let registryId = program.registry_id; - // If no registryId is specified, create a unique one. - if (registryId.length === 0) { - registryId = `nodejs-example-registry-${(new Date()).getTime()}`; - } - // The project/location's URI - const parentName = `projects/${program.project_id}/locations/${program.cloud_region}`; - // The registry's URI - const registryName = `${parentName}/registries/${registryId}`; - const pubsubTopic = program.pubsub_topic; - - const rs256deviceId = 'rs256-device'; - const es256deviceId = 'es256-device'; - - // Set up a series of async functions for the demo. The chain terminates on - // the first error encountered. - async.series([ - // Lookup our registry, or create it if it does not exist. - (cb) => lookupOrCreateRegistry(client, registryId, parentName, pubsubTopic, cb), - // Create a device that uses an RSA key for authentication. - (cb) => createDeviceWithRs256(client, rs256deviceId, registryName, program.rsa_certificate_file, cb), - // List all of the devices in the registry. - (cb) => listDevices(client, registryName, cb), - // Create device without authentication. - (cb) => createDevice(client, es256deviceId, registryName, {}, cb), - // List all of the devices in the registry. - (cb) => listDevices(client, registryName, cb), - // Patch the device that we created without authentication to use an EC - // key for authentication. - (cb) => patchEs256ForAuth(client, es256deviceId, registryName, program.ec_public_key_file, cb), - // List all of the devices in the registry. - (cb) => listDevices(client, registryName, cb), - // Delete the RSA authenticated device from the registry. - (cb) => deleteDevice(client, rs256deviceId, registryName, cb), - // Delete the EC authenticated device from the registry. - (cb) => deleteDevice(client, es256deviceId, registryName, cb), - // Finally delete the registry. This call will only succeed if the - // registry has no devices in it. - (cb) => deleteRegistry(client, registryName, cb) - ]); -}); diff --git a/iot/manager/manager.js b/iot/manager/manager.js new file mode 100644 index 0000000000..3d7d19e69b --- /dev/null +++ b/iot/manager/manager.js @@ -0,0 +1,721 @@ +/** + * Copyright 2017, Google, Inc. + * Licensed under the Apache License, Version 2.0 (the `License`); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an `AS IS` BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +'use strict'; + +const fs = require('fs'); +const google = require('googleapis'); + +const API_VERSION = 'v1alpha1'; +const DISCOVERY_API = 'https://cloudiot.googleapis.com/$discovery/rest'; + +// Configures the topic for Cloud IoT Core. +function setupIotTopic (topicName) { + const PubSub = require('@google-cloud/pubsub'); + + const pubsub = PubSub(); + const topic = pubsub.topic(topicName); + const serviceAccount = `serviceAccount:cloud-iot@system.gserviceaccount.com`; + + topic.iam.getPolicy() + .then((results) => { + const policy = results[0] || {}; + policy.bindings || (policy.bindings = []); + console.log(JSON.stringify(policy, null, 2)); + + let hasRole = false; + let binding = { + role: 'roles/pubsub.publisher', + members: [serviceAccount] + }; + + policy.bindings.forEach((_binding) => { + if (_binding.role === binding.role) { + binding = _binding; + hasRole = true; + return false; + } + }); + + if (hasRole) { + binding.members || (binding.members = []); + if (binding.members.indexOf(serviceAccount) === -1) { + binding.members.push(serviceAccount); + } + } else { + policy.bindings.push(binding); + } + + // Updates the IAM policy for the topic + return topic.iam.setPolicy(policy); + }) + .then((results) => { + const updatedPolicy = results[0]; + + console.log(JSON.stringify(updatedPolicy, null, 2)); + }) + .catch((err) => { + console.error('ERROR:', err); + }); +} + +function createIotTopic (topicName) { + // Imports the Google Cloud client library + const PubSub = require('@google-cloud/pubsub'); + + // Instantiates a client + const pubsub = PubSub(); + + pubsub.createTopic(topicName) + .then((results) => { + setupIotTopic(topicName); + }); +} + +// Lookup the registry, assuming that it exists. +function lookupRegistry (client, registryId, projectId, cloudRegion, cb) { + // [BEGIN iot_lookup_registry] + // Client retrieved in callback + // getClient(apiKey, serviceAccountJson, function(client) {...}); + // const cloudRegion = 'us-central1'; + // const projectId = 'adjective-noun-123'; + // const registryId = 'my-registry'; + const parentName = `projects/${projectId}/locations/${cloudRegion}`; + const registryName = `${parentName}/registries/${registryId}`; + const request = { + name: registryName + }; + + client.projects.locations.registries.get(request, (err, data) => { + if (err) { + console.log('Could not look up registry'); + console.log(err); + } else { + console.log('Looked up existing registry'); + console.log(data); + } + }); + // [END iot_lookup_registry] +} + +// Create a new registry, or look up an existing one if it doesn't exist. +function lookupOrCreateRegistry (client, registryId, projectId, cloudRegion, + pubsubTopicId) { + // [BEGIN iot_lookup_or_create_registry] + // Client retrieved in callback + // getClient(apiKey, serviceAccountJson, function(client) {...}); + // const cloudRegion = 'us-central1'; + // const projectId = 'adjective-noun-123'; + // const registryId = 'my-registry'; + // const pubsubTopicId = 'my-iot-topic'; + const parentName = `projects/${projectId}/locations/${cloudRegion}`; + const pubsubTopic = `projects/${projectId}/topics/${pubsubTopicId}`; + + const request = { + parent: parentName, + id: registryId, + resource: { + eventNotificationConfig: { + pubsubTopicName: pubsubTopic + } + } + }; + + client.projects.locations.registries.create(request, (err, data) => { + if (err) { + if (err.code === 409) { + // The registry already exists - look it up instead. + lookupRegistry(client, registryId, projectId, cloudRegion); + } else { + console.log('Could not create registry'); + console.log(err); + } + } else { + console.log('Successfully created registry'); + console.log(data); + } + }); + // [END iot_lookup_or_create_registry] +} + +// Create a new device with the given id. The body defines the parameters for +// the device, such as authentication. +function createUnauthDevice (client, deviceId, registryId, projectId, + cloudRegion, body) { + // [BEGIN iot_create_unauth_device] + // Client retrieved in callback + // getClient(apiKey, serviceAccountJson, function(client) {...}); + // const cloudRegion = 'us-central1'; + // const deviceId = 'my-unauth-device'; + // const projectId = 'adjective-noun-123'; + // const registryId = 'my-registry'; + console.log('Creating device:', deviceId); + const parentName = `projects/${projectId}/locations/${cloudRegion}`; + const registryName = `${parentName}/registries/${registryId}`; + + const request = { + parent: registryName, + id: deviceId, + resource: body + }; + + client.projects.locations.registries.devices.create(request, (err, data) => { + if (err) { + console.log('Could not create device'); + console.log(err); + } else { + console.log('Created device'); + console.log(data); + } + }); + // [END iot_create_unauth_device] +} + +// Create a device using RSA256 for authentication. +function createRsaDevice (client, deviceId, registryId, projectId, cloudRegion, + rsaCertificateFile) { + // [BEGIN iot_create_rsa_device] + // Client retrieved in callback + // getClient(apiKey, serviceAccountJson, function(client) {...}); + // const cloudRegion = 'us-central1'; + // const deviceId = 'my-unauth-device'; + // const projectId = 'adjective-noun-123'; + // const registryId = 'my-registry'; + const parentName = `projects/${projectId}/locations/${cloudRegion}`; + const registryName = `${parentName}/registries/${registryId}`; + const body = { + credentials: [ + { + publicKey: { + format: 'RSA_X509_PEM', + key: fs.readFileSync(rsaCertificateFile).toString() + } + } + ] + }; + + const request = { + parent: registryName, + id: deviceId, + resource: body + }; + + console.log(JSON.stringify(request)); + + client.projects.locations.registries.devices.create(request, (err, data) => { + if (err) { + console.log('Could not create device'); + console.log(err); + } else { + console.log('Created device'); + console.log(data); + } + }); + // [END iot_create_rsa_device] +} + +// Create a device using ES256 for authentication. +function createEsDevice (client, deviceId, registryId, projectId, cloudRegion, + esCertificateFile) { + // [BEGIN iot_create_es_device] + // Client retrieved in callback + // getClient(apiKey, serviceAccountJson, function(client) {...}); + // const cloudRegion = 'us-central1'; + // const deviceId = 'my-es-device'; + // const projectId = 'adjective-noun-123'; + // const registryId = 'my-registry'; + const parentName = `projects/${projectId}/locations/${cloudRegion}`; + const registryName = `${parentName}/registries/${registryId}`; + const body = { + credentials: [ + { + publicKey: { + format: 'ES256_PEM', + key: fs.readFileSync(esCertificateFile).toString() + } + } + ] + }; + + const request = { + parent: registryName, + id: deviceId, + resource: body + }; + + client.projects.locations.registries.devices.create(request, (err, data) => { + if (err) { + console.log('Could not create device'); + console.log(err); + } else { + console.log('Created device'); + console.log(data); + } + }); + // [END iot_create_es_device] +} + +// Add RSA256 authentication to the given device. +function patchRsa256ForAuth (client, deviceId, registryId, rsaPublicKeyFile, + projectId, cloudRegion) { + // [BEGIN iot_patch_rsa] + // Client retrieved in callback + // getClient(apiKey, serviceAccountJson, function(client) {...}); + // const cloudRegion = 'us-central1'; + // const deviceId = 'my-rsa-device'; + // const projectId = 'adjective-noun-123'; + // const registryId = 'my-registry'; + const parentName = + `projects/${projectId}/locations/${cloudRegion}`; + const registryName = `${parentName}/registries/${registryId}`; + const request = { + name: `${registryName}/devices/${deviceId}`, + updateMask: 'credentials', + resource: { + credentials: [ + { + publicKey: { + format: 'RSA_X509_PEM', + key: fs.readFileSync(rsaPublicKeyFile).toString() + } + } + ] + } + }; + + client.projects.locations.registries.devices.patch(request, (err, data) => { + if (err) { + console.log('Error patching device:', deviceId); + console.log(err); + } else { + console.log('Patched device:', deviceId); + console.log(data); + } + }); + // [END iot_patch_rsa] +} + +// Add ES256 authentication to the given device. +function patchEs256ForAuth (client, deviceId, registryId, esPublicKeyFile, + projectId, cloudRegion) { + // [BEGIN iot_patch_es] + // Client retrieved in callback + // getClient(apiKey, serviceAccountJson, function(client) {...}); + // const cloudRegion = 'us-central1'; + // const deviceId = 'my-rsa-device'; + // const projectId = 'adjective-noun-123'; + // const registryId = 'my-registry'; + const parentName = + `projects/${projectId}/locations/${cloudRegion}`; + const registryName = `${parentName}/registries/${registryId}`; + const request = { + name: `${registryName}/devices/${deviceId}`, + updateMask: 'credentials', + resource: { + credentials: [ + { + publicKey: { + format: 'ES256_PEM', + key: fs.readFileSync(esPublicKeyFile).toString() + } + } + ] + } + }; + + client.projects.locations.registries.devices.patch(request, (err, data) => { + if (err) { + console.log('Error patching device:', deviceId); + console.log(err); + } else { + console.log('Patched device:', deviceId); + console.log(data); + } + }); + // [END iot_patch_es] +} + +// List all of the devices in the given registry. +function listDevices (client, registryId, projectId, cloudRegion) { + // [START iot_list_devices] + // Client retrieved in callback + // getClient(apiKey, serviceAccountJson, function(client) {...}); + // const cloudRegion = 'us-central1'; + // const projectId = 'adjective-noun-123'; + // const registryId = 'my-registry'; + const parentName = `projects/${projectId}/locations/${cloudRegion}`; + const registryName = `${parentName}/registries/${registryId}`; + + const request = { + parent: registryName + }; + + client.projects.locations.registries.devices.list(request, (err, data) => { + if (err) { + console.log('Could not list devices'); + console.log(err); + } else { + console.log('Current devices in registry:', data['devices']); + } + }); + // [END iot_list_devices] +} + +// Delete the given device from the registry. +function deleteDevice (client, deviceId, registryId, projectId, cloudRegion, + cb) { + // [BEGIN iot_delete_device] + // Client retrieved in callback + // getClient(apiKey, serviceAccountJson, function(client) {...}); + // const cloudRegion = 'us-central1'; + // const projectId = 'adjective-noun-123'; + // const registryId = 'my-registry'; + const parentName = `projects/${projectId}/locations/${cloudRegion}`; + const registryName = `${parentName}/registries/${registryId}`; + const request = { + name: `${registryName}/devices/${deviceId}` + }; + + client.projects.locations.registries.devices.delete(request, (err, data) => { + if (err) { + console.log('Could not delete device:', deviceId); + console.log(err); + } else { + console.log('Successfully deleted device:', deviceId); + console.log(data); + if (cb) { + cb(); + } + } + }); + // [END iot_delete_device] +} + +// Clear the given registry by removing all devices and deleting the registry. +function clearRegistry (client, registryId, projectId, cloudRegion) { + const parentName = `projects/${projectId}/locations/${cloudRegion}`; + const registryName = `${parentName}/registries/${registryId}`; + const requestDelete = { + name: registryName + }; + + const after = function () { + client.projects.locations.registries.delete(requestDelete, (err, data) => { + if (err) { + console.log('Could not delete registry'); + console.log(err); + } else { + console.log(`Successfully deleted registry ${registryName}`); + console.log(data); + } + }); + }; + + const request = { + parent: registryName + }; + + client.projects.locations.registries.devices.list(request, (err, data) => { + if (err) { + console.log('Could not list devices'); + console.log(err); + } else { + console.log('Current devices in registry:', data['devices']); + let devices = data['devices']; + if (devices) { + devices.forEach((device, index) => { + console.log(`${device.id} [${index}/${devices.length}] removed`); + if (index === devices.length - 1) { + deleteDevice(client, device.id, registryId, projectId, cloudRegion, + after); + } else { + deleteDevice(client, device.id, registryId, projectId, cloudRegion + ); + } + }); + } else { + after(); + } + } + }); +} + +// Delete the given registry. Note that this will only succeed if the registry +// is empty. +function deleteRegistry (client, registryId, projectId, cloudRegion) { + // [BEGIN iot_delete_registry] + // Client retrieved in callback + // getClient(apiKey, serviceAccountJson, function(client) {...}); + // const cloudRegion = 'us-central1'; + // const projectId = 'adjective-noun-123'; + // const registryId = 'my-registry'; + const parentName = `projects/${projectId}/locations/${cloudRegion}`; + const registryName = `${parentName}/registries/${registryId}`; + const request = { + name: registryName + }; + + client.projects.locations.registries.delete(request, (err, data) => { + if (err) { + console.log('Could not delete registry'); + console.log(err); + } else { + console.log('Successfully deleted registry'); + console.log(data); + } + }); + // [END iot_delete_registry] +} + +// Retrieve the given device from the registry. +function getDevice (client, deviceId, registryId, projectId, cloudRegion) { + // [BEGIN iot_get_device] + // Client retrieved in callback + // getClient(apiKey, serviceAccountJson, function(client) {...}); + // const cloudRegion = 'us-central1'; + // const projectId = 'adjective-noun-123'; + // const registryId = 'my-registry'; + const parentName = `projects/${projectId}/locations/${cloudRegion}`; + const registryName = `${parentName}/registries/${registryId}`; + const request = { + name: `${registryName}/devices/${deviceId}` + }; + + client.projects.locations.registries.devices.get(request, (err, data) => { + if (err) { + console.log('Could not delete device:', deviceId); + console.log(err); + } else { + console.log('Found device:', deviceId); + console.log(data); + } + }); + // [END iot_get_device] +} + +// Returns an authorized API client by discovering the Cloud IoT Core API with +// the provided API key. +function getClient (apiKey, serviceAccountJson, cb) { + const serviceAccount = JSON.parse(fs.readFileSync(serviceAccountJson)); + const jwtAccess = new google.auth.JWT(); + jwtAccess.fromJSON(serviceAccount); + // Note that if you require additional scopes, they should be specified as a + // string, separated by spaces. + jwtAccess.scopes = 'https://www.googleapis.com/auth/cloud-platform'; + // Set the default authentication to the above JWT access. + google.options({ auth: jwtAccess }); + + const discoveryUrl = `${DISCOVERY_API}?version=${API_VERSION}&key=${apiKey}`; + + google.discoverAPI(discoveryUrl, {}, (err, client) => { + if (err) { + console.log('Error during API discovery', err); + return undefined; + } + cb(client); + }); +} + +require(`yargs`) // eslint-disable-line + .demand(4) + .options({ + apiKey: { + alias: 'a', + default: process.env.API_KEY, + description: 'The API key used for discoverying the API.', + requiresArg: true, + type: 'string' + }, + cloudRegion: { + alias: 'c', + default: 'us-central1', + requiresArg: true, + type: 'string' + }, + projectId: { + alias: 'p', + default: process.env.GCLOUD_PROJECT || process.env.GOOGLE_CLOUD_PROJECT, + description: 'The Project ID to use. Defaults to the value of the GCLOUD_PROJECT or GOOGLE_CLOUD_PROJECT environment variables.', + requiresArg: true, + type: 'string' + }, + serviceAccount: { + alias: 's', + default: process.env.GOOGLE_APPLICATION_CREDENTIALS, + description: 'The path to your service credentials JSON.', + requiresArg: true, + type: 'string' + } + }) + .command( + `createRsa256Device `, + `Creates an RSA256 device.`, + {}, + (opts) => { + const cb = function (client) { + createRsaDevice(client, opts.deviceId, opts.registryId, opts.projectId, + opts.cloudRegion, opts.rsaPath); + }; + getClient(opts.apiKey, opts.serviceAccount, cb); + } + ) + .command( + `createEs256Device `, + `Creates an ES256 device.`, + {}, + (opts) => { + const cb = function (client) { + createEsDevice(client, opts.deviceId, opts.registryId, opts.projectId, + opts.cloudRegion, opts.esPath); + }; + getClient(opts.apiKey, opts.serviceAccount, cb); + } + ) + .command( + `createUnauthDevice `, + `Creates a device without authorization.`, + {}, + (opts) => { + const cb = function (client) { + createUnauthDevice(client, opts.deviceId, opts.registryId, + opts.projectId, opts.cloudRegion, {}); + }; + getClient(opts.apiKey, opts.serviceAccount, cb); + } + ) + .command( + `createRegistry `, + `Creates a device registry.`, + {}, + (opts) => { + const cb = function (client) { + lookupOrCreateRegistry(client, opts.registryId, opts.projectId, + opts.cloudRegion, opts.pubsubTopic); + }; + getClient(opts.apiKey, opts.serviceAccount, cb); + } + ) + .command( + `createIotTopic `, + `Creates and configures a PubSub topic for Cloud IoT Core.`, + {}, + (opts) => createIotTopic(opts.pubsubTopic) + ) + .command( + `setupIotTopic `, + `Configures the PubSub topic for Cloud IoT Core.`, + {}, + (opts) => setupIotTopic(opts.pubsubTopic) + ) + .command( + `deleteDevice `, + `Deletes a device from the device registry.`, + {}, + (opts) => { + const cb = function (client) { + deleteDevice(client, opts.deviceId, opts.registryId, opts.projectId, + opts.cloudRegion); + }; + getClient(opts.apiKey, opts.serviceAccount, cb); + } + ) + .command( + `clearRegistry `, + `!!Be careful! Removes all devices and then deletes a device registry!!`, + {}, + (opts) => { + const cb = function (client) { + clearRegistry(client, opts.registryId, opts.projectId, + opts.cloudRegion); + }; + getClient(opts.apiKey, opts.serviceAccount, cb); + } + ) + .command( + `deleteRegistry `, + `Deletes a device registry.`, + {}, + (opts) => { + const cb = function (client) { + deleteRegistry(client, opts.registryId, opts.projectId, + opts.cloudRegion); + }; + getClient(opts.apiKey, opts.serviceAccount, cb); + } + ) + .command( + `getDevice `, + `Retrieves device info given a device ID.`, + {}, + (opts) => { + const cb = function (client) { + getDevice(client, opts.deviceId, opts.registryId, opts.projectId, + opts.cloudRegion); + }; + getClient(opts.apiKey, opts.serviceAccount, cb); + } + ) + .command( + `listDevices `, + `Lists the devices in a given registry.`, + {}, + (opts) => { + const cb = function (client) { + listDevices(client, opts.registryId, opts.projectId, opts.cloudRegion); + }; + getClient(opts.apiKey, opts.serviceAccount, cb); + } + ) + .command( + `patchEs256 `, + `Patches a device with ES256 authorization credentials.`, + {}, + (opts) => { + const cb = function (client) { + patchEs256ForAuth(client, opts.deviceId, opts.registryId, + opts.es256Path, opts.projectId, opts.cloudRegion); + }; + getClient(opts.apiKey, opts.serviceAccount, cb); + } + ) + .command( + `patchRsa256 `, + `Patches a device with RSA256 authentication credentials.`, + {}, + (opts) => { + const cb = function (client) { + patchRsa256ForAuth(client, opts.deviceId, opts.registryId, + opts.rsa256Path, opts.projectId, opts.cloudRegion); + }; + getClient(opts.apiKey, opts.serviceAccount, cb); + } + ) + .example(`node $0 createEs256Device my-es-device my-registry ../ec_public.pem --apiKey=abc123zz`) + .example(`node $0 createRegistry my-registry my-iot-topic --service_account_json=$HOME/creds_iot.json --api_key=abc123zz --project_id=my-project-id`) + .example(`node $0 createRsa256Device my-rsa-device my-registry ../rsa_cert.pem --apiKey=abc123zz`) + .example(`node $0 createUnauthDevice my-device my-registry`) + .example(`node $0 deleteDevice my-device my-registry`) + .example(`node $0 deleteRegistry my-device my-registry`) + .example(`node $0 getDevice my-device my-registry`) + .example(`node $0 listDevices my-node-registry`) + .example(`node $0 patchRsa256 my-device my-registry ../rsa_cert.pem`) + .example(`node $0 patchEs256 my-device my-registry ../ec_public.pem`) + .example(`node $0 setupTopic my-iot-topic --service_account_json=$HOME/creds_iot.json --api_key=abc123zz --project_id=my-project-id`) + .wrap(120) + .recommendCommands() + .epilogue(`For more information, see https://cloud.google.com/iot-core/docs`) + .help() + .strict() + .argv; diff --git a/iot/manager/package.json b/iot/manager/package.json index 526336880b..90200ce762 100644 --- a/iot/manager/package.json +++ b/iot/manager/package.json @@ -4,8 +4,10 @@ "description": "Example of Cloud IoT device administration", "main": "cloudiot-device-manager-example.js", "dependencies": { - "async": "2.4.1", - "commander": "2.9.0", - "googleapis": "19.0.0" + "@google-cloud/nodejs-repo-tools": "^1.4.15", + "@google-cloud/pubsub": "0.12.0", + "ava": "^0.20.0", + "googleapis": "19.0.0", + "yargs": "^8.0.2" } } diff --git a/iot/manager/resources/ec_public.pem b/iot/manager/resources/ec_public.pem new file mode 100644 index 0000000000..9c7e5b2e98 --- /dev/null +++ b/iot/manager/resources/ec_public.pem @@ -0,0 +1,4 @@ +-----BEGIN PUBLIC KEY----- +MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEvcIrwr9Zb17mOu1kLHvlZ2LOYnVx +ceKgUWX93xab9j7OGKnEeXzafG+G0D8+d0Kw26/hV7OSvO5t1sB6FHJKkQ== +-----END PUBLIC KEY----- diff --git a/iot/manager/resources/rsa_cert.pem b/iot/manager/resources/rsa_cert.pem new file mode 100644 index 0000000000..b41020505d --- /dev/null +++ b/iot/manager/resources/rsa_cert.pem @@ -0,0 +1,19 @@ +-----BEGIN CERTIFICATE----- +MIIDFzCCAf+gAwIBAgIJAP2XoKtudrz5MA0GCSqGSIb3DQEBBQUAMBExDzANBgNV +BAMTBnVudXNlZDAeFw0xNzA3MTExOTE2MzBaFw0xNzA4MTAxOTE2MzBaMBExDzAN +BgNVBAMTBnVudXNlZDCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBANfn +stQebiazvSs0t81t7YTBNk3+Aqal0qFKY+QisbTs+BpnDcQIg6Ik974v8OtZ+t/f +c+AdCnyWpOsVzpIVpWfE6p71LsZknGgzL8/lMfgy6PFzzzU7kWzPw+MZ90VbHed3 +k1fJv5E09Er684tAkyCsnr5ihol5jWq1UlnqgnHugYlJu3uOGXjdXCBtG1EzNkDx +ilfVkpgitKCGHYqXrpCCSTZ2Yhp4GrbYh0Hpxg2lF3bE5XMW8p8jl8Ykmjvq+hqt +DFOSS76FXOVG7oQU6Fc8owIRLknAK2IXPI/ciGlubivA0oQwD4E+7hLo4c2M+Jq7 +faitjqPPiuemUcHrZV8CAwEAAaNyMHAwHQYDVR0OBBYEFGJSZ6h5VH/g74dez/WJ +Ftl+C3NVMEEGA1UdIwQ6MDiAFGJSZ6h5VH/g74dez/WJFtl+C3NVoRWkEzARMQ8w +DQYDVQQDEwZ1bnVzZWSCCQD9l6Crbna8+TAMBgNVHRMEBTADAQH/MA0GCSqGSIb3 +DQEBBQUAA4IBAQBvygToNj8Y4hYcNwLsZwJeqhLwiSu2oQ12vpB8FnfCUsrnn9sZ +rqVheNEbr8UjPfTtgUMABxu+iabQHXT2FJqVf0J97Ted/CKUFfvXL3pMs/N5nAuV +TJzvM+Foi3NBjiDxptp3YNS+htiV/z4hX7wvFVhPf+g+Wp8RqmbZwLdcyB4ftBx1 +AZ50HFv+6xH9pF6QrqS8HtfV4Jy9ZHWTnkspdP3U4sEawHxGdcjNx4zwb6oaD6hz ++F2FxlekG2gBsk4tDq+ReYqGa/G9NT4HPIIMwVyIv72ru/9HsL8yU1me3NugXWnf +IatIVphZrfrp+6yJuvmIT5vUn8tMivQp/6rI +-----END CERTIFICATE----- diff --git a/iot/manager/system-test/manager.test.js b/iot/manager/system-test/manager.test.js new file mode 100644 index 0000000000..555e3bc292 --- /dev/null +++ b/iot/manager/system-test/manager.test.js @@ -0,0 +1,173 @@ +/** + * Copyright 2017, Google, Inc. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +'use strict'; + +const path = require(`path`); +const PubSub = require(`@google-cloud/pubsub`); +const test = require(`ava`); +const tools = require(`@google-cloud/nodejs-repo-tools`); +const uuid = require(`uuid`); + +const topicName = `nodejs-docs-samples-test-iot-${uuid.v4()}`; +const registryName = `nodejs-test-registry-iot-${uuid.v4()}`; +const cmd = `node manager.js`; +const cwd = path.join(__dirname, `..`); + +test.before(tools.checkCredentials); +test.before(async () => { + const pubsub = PubSub(); + return pubsub.createTopic(topicName) + .then((results) => { + const topic = results[0]; + console.log(`Topic ${topic.name} created.`); + return topic; + }); +}); + +test.after.always(async () => { + const pubsub = PubSub(); + const topic = pubsub.topic(topicName); + return topic.delete() + .then(() => { + console.log(`Topic ${topic.name} deleted.`); + }); +}); + +test(`should create and delete an unauthorized device`, async (t) => { + const localDevice = `test-device`; + const localRegName = `${registryName}-unauth`; + let output = await tools.runAsync(`${cmd} setupIotTopic ${topicName}`, cwd); + output = await tools.runAsync( + `${cmd} createRegistry ${localRegName} ${topicName}`, cwd); + output = await tools.runAsync( + `${cmd} createUnauthDevice ${localDevice} ${localRegName}`, cwd); + t.true(output.includes(`Created device`)); + output = await tools.runAsync( + `${cmd} deleteDevice ${localDevice} ${localRegName}`, cwd); + t.true(output.includes(`Successfully deleted device`)); + output = await tools.runAsync(`${cmd} deleteRegistry ${localRegName}`, cwd); +}); + +test(`should create and delete an RSA256 device`, async (t) => { + const localDevice = `test-rsa-device`; + const localRegName = `${registryName}-rsa256`; + let output = await tools.runAsync(`${cmd} setupIotTopic ${topicName}`, cwd); + output = await tools.runAsync( + `${cmd} createRegistry ${localRegName} ${topicName}`, cwd); + output = await tools.runAsync( + `${cmd} createRsa256Device ${localDevice} ${localRegName} resources/rsa_cert.pem`, cwd); + t.true(output.includes(`Created device`)); + output = await tools.runAsync( + `${cmd} deleteDevice ${localDevice} ${localRegName}`, cwd); + t.true(output.includes(`Successfully deleted device`)); + output = await tools.runAsync(`${cmd} deleteRegistry ${localRegName}`, cwd); +}); + +test(`should create and delete an EC256 device`, async (t) => { + const localDevice = `test-es-device`; + const localRegName = `${registryName}-es256`; + let output = await tools.runAsync(`${cmd} setupIotTopic ${topicName}`, cwd); + output = await tools.runAsync( + `${cmd} createRegistry ${localRegName} ${topicName}`, cwd); + output = await tools.runAsync( + `${cmd} createEs256Device ${localDevice} ${localRegName} resources/ec_public.pem`, cwd); + t.true(output.includes(`Created device`)); + output = await tools.runAsync( + `${cmd} deleteDevice ${localDevice} ${localRegName}`, cwd); + t.true(output.includes(`Successfully deleted device`)); + output = await tools.runAsync(`${cmd} deleteRegistry ${localRegName}`, cwd); +}); + +test(`should patch an unauthorized device with RSA256`, async (t) => { + const localDevice = `patchme`; + const localRegName = `${registryName}-patchRSA`; + let output = await tools.runAsync(`${cmd} setupIotTopic ${topicName}`, cwd); + output = await tools.runAsync( + `${cmd} createRegistry ${localRegName} ${topicName}`, cwd); + output = await tools.runAsync( + `${cmd} createUnauthDevice ${localDevice} ${localRegName}`, cwd); + t.true(output.includes(`Created device`)); + output = await tools.runAsync( + `${cmd} patchRsa256 ${localDevice} ${localRegName} resources/rsa_cert.pem`, cwd); + t.true(output.includes(`Patched device:`)); + output = await tools.runAsync( + `${cmd} deleteDevice ${localDevice} ${localRegName}`, cwd); + t.true(output.includes(`Successfully deleted device`)); + output = await tools.runAsync(`${cmd} deleteRegistry ${localRegName}`, cwd); +}); + +test(`should patch an unauthorized device with RSA256`, async (t) => { + const localDevice = `patchme`; + const localRegName = `${registryName}-patchES`; + let output = await tools.runAsync(`${cmd} setupIotTopic ${topicName}`, cwd); + output = await tools.runAsync( + `${cmd} createRegistry ${localRegName} ${topicName}`, cwd); + output = await tools.runAsync( + `${cmd} createUnauthDevice ${localDevice} ${localRegName}`, cwd); + t.true(output.includes(`Created device`)); + output = await tools.runAsync( + `${cmd} patchEs256 ${localDevice} ${localRegName} resources/ec_public.pem`, cwd); + t.true(output.includes(`Patched device:`)); + output = await tools.runAsync( + `${cmd} deleteDevice ${localDevice} ${localRegName}`, cwd); + t.true(output.includes(`Successfully deleted device`)); + output = await tools.runAsync(`${cmd} deleteRegistry ${localRegName}`, cwd); +}); + +test(`should create and list devices`, async (t) => { + const localDevice = `test-device`; + const localRegName = `${registryName}-list`; + let output = await tools.runAsync(`${cmd} setupIotTopic ${topicName}`, cwd); + output = await tools.runAsync( + `${cmd} createRegistry ${localRegName} ${topicName}`, cwd); + output = await tools.runAsync( + `${cmd} createUnauthDevice ${localDevice} ${localRegName}`, cwd); + t.true(output.includes(`Created device`)); + output = await tools.runAsync( + `${cmd} listDevices ${localRegName}`, cwd); + t.true(output.includes(`Current devices in registry: [ { id: '${localDevice}'`)); + output = await tools.runAsync( + `${cmd} deleteDevice ${localDevice} ${localRegName}`, cwd); + t.true(output.includes(`Successfully deleted device`)); + output = await tools.runAsync(`${cmd} deleteRegistry ${localRegName}`, cwd); +}); + +test(`should create and get a device`, async (t) => { + const localDevice = `test-device`; + const localRegName = `${registryName}-get`; + let output = await tools.runAsync(`${cmd} setupIotTopic ${topicName}`, cwd); + output = await tools.runAsync( + `${cmd} createRegistry ${localRegName} ${topicName}`, cwd); + output = await tools.runAsync( + `${cmd} createUnauthDevice ${localDevice} ${localRegName}`, cwd); + t.true(output.includes(`Created device`)); + output = await tools.runAsync( + `${cmd} getDevice ${localDevice} ${localRegName}`, cwd); + t.true(output.includes(`Found device: ${localDevice}`)); + output = await tools.runAsync( + `${cmd} deleteDevice ${localDevice} ${localRegName}`, cwd); + t.true(output.includes(`Successfully deleted device`)); + output = await tools.runAsync(`${cmd} deleteRegistry ${localRegName}`, cwd); +}); + +test(`should create and delete a registry`, async (t) => { + let output = await tools.runAsync(`${cmd} setupIotTopic ${topicName}`, cwd); + output = await tools.runAsync( + `${cmd} createRegistry ${registryName} ${topicName}`, cwd); + t.true(output.includes(`Successfully created registry`)); + output = await tools.runAsync(`${cmd} deleteRegistry ${registryName}`, cwd); + t.true(output.includes(`Successfully deleted registry`)); +}); diff --git a/speech/package.json b/speech/package.json index 0443ab89e0..5c10301965 100644 --- a/speech/package.json +++ b/speech/package.json @@ -18,14 +18,14 @@ "test": "npm run system-test" }, "dependencies": { - "@google-cloud/speech": "0.9.3", - "@google-cloud/storage": "1.1.1", + "@google-cloud/speech": "^0.10.1", + "@google-cloud/storage": "^1.2.0", "node-record-lpcm16": "0.3.0", - "yargs": "8.0.2" + "yargs": "^8.0.2" }, "devDependencies": { - "@google-cloud/nodejs-repo-tools": "1.4.15", - "ava": "0.19.1", + "@google-cloud/nodejs-repo-tools": "^1.4.15", + "ava": "^0.19.1", "proxyquire": "1.8.0", "sinon": "2.3.4" }, diff --git a/speech/quickstart.js b/speech/quickstart.js index d983de9067..f088297a44 100644 --- a/speech/quickstart.js +++ b/speech/quickstart.js @@ -18,9 +18,10 @@ // [START speech_quickstart] // Imports the Google Cloud client library const Speech = require('@google-cloud/speech'); +const fs = require('fs'); // Your Google Cloud Platform project ID -const projectId = 'YOUR_PROJECT_ID'; +const projectId = 'your-project-id'; // Instantiates a client const speechClient = Speech({ @@ -30,17 +31,28 @@ const speechClient = Speech({ // The name of the audio file to transcribe const fileName = './resources/audio.raw'; +// Reads a local audio file and converts it to base64 +const file = fs.readFileSync(fileName); +const audioBytes = file.toString('base64'); + // The audio file's encoding, sample rate in hertz, and BCP-47 language code -const options = { +const audio = { + content: audioBytes +}; +const config = { encoding: 'LINEAR16', sampleRateHertz: 16000, languageCode: 'en-US' }; +const request = { + audio: audio, + config: config +}; // Detects speech in the audio file -speechClient.recognize(fileName, options) +speechClient.recognize(request) .then((results) => { - const transcription = results[0]; + const transcription = results[0].results[0].alternatives[0].transcript; console.log(`Transcription: ${transcription}`); }) .catch((err) => { diff --git a/speech/recognize.js b/speech/recognize.js index 3b6fef5fa2..eca5a11ce0 100644 --- a/speech/recognize.js +++ b/speech/recognize.js @@ -26,6 +26,7 @@ function syncRecognize (filename, encoding, sampleRateHertz, languageCode) { // [START speech_sync_recognize] // Imports the Google Cloud client library + const fs = require('fs'); const Speech = require('@google-cloud/speech'); // Instantiates a client @@ -43,18 +44,25 @@ function syncRecognize (filename, encoding, sampleRateHertz, languageCode) { // The BCP-47 language code to use, e.g. 'en-US' // const languageCode = 'en-US'; - const request = { + const config = { encoding: encoding, sampleRateHertz: sampleRateHertz, languageCode: languageCode }; + const audio = { + content: fs.readFileSync(filename).toString('base64') + }; + + const request = { + config: config, + audio: audio + }; // Detects speech in the audio file - speech.recognize(filename, request) + speech.recognize(request) .then((results) => { - const transcription = results[0]; - - console.log(`Transcription: ${transcription}`); + const transcription = results[0].results[0].alternatives[0].transcript; + console.log(`Transcription: `, transcription); }) .catch((err) => { console.error('ERROR:', err); @@ -82,18 +90,25 @@ function syncRecognizeGCS (gcsUri, encoding, sampleRateHertz, languageCode) { // The BCP-47 language code to use, e.g. 'en-US' // const languageCode = 'en-US'; - const request = { + const config = { encoding: encoding, sampleRateHertz: sampleRateHertz, languageCode: languageCode }; + const audio = { + uri: gcsUri + }; + + const request = { + config: config, + audio: audio + }; // Detects speech in the audio file - speech.recognize(gcsUri, request) + speech.recognize(request) .then((results) => { - const transcription = results[0]; - - console.log(`Transcription: ${transcription}`); + const transcription = results[0].results[0].alternatives[0].transcript; + console.log(`Transcription: `, transcription); }) .catch((err) => { console.error('ERROR:', err); @@ -105,6 +120,7 @@ function asyncRecognize (filename, encoding, sampleRateHertz, languageCode) { // [START speech_async_recognize] // Imports the Google Cloud client library const Speech = require('@google-cloud/speech'); + const fs = require('fs'); // Instantiates a client const speech = Speech(); @@ -121,22 +137,30 @@ function asyncRecognize (filename, encoding, sampleRateHertz, languageCode) { // The BCP-47 language code to use, e.g. 'en-US' // const languageCode = 'en-US'; - const request = { + const config = { encoding: encoding, sampleRateHertz: sampleRateHertz, languageCode: languageCode }; + const audio = { + content: fs.readFileSync(filename).toString('base64') + }; + + const request = { + config: config, + audio: audio + }; // Detects speech in the audio file. This creates a recognition job that you // can wait for now, or get its result later. - speech.startRecognition(filename, request) + speech.longRunningRecognize(request) .then((results) => { const operation = results[0]; // Get a Promise representation of the final result of the job return operation.promise(); }) .then((results) => { - const transcription = results[0]; + const transcription = results[0].results[0].alternatives[0].transcript; console.log(`Transcription: ${transcription}`); }) .catch((err) => { @@ -165,22 +189,31 @@ function asyncRecognizeGCS (gcsUri, encoding, sampleRateHertz, languageCode) { // The BCP-47 language code to use, e.g. 'en-US' // const languageCode = 'en-US'; - const request = { + const config = { encoding: encoding, sampleRateHertz: sampleRateHertz, languageCode: languageCode }; + const audio = { + uri: gcsUri + }; + + const request = { + config: config, + audio: audio + }; + // Detects speech in the audio file. This creates a recognition job that you // can wait for now, or get its result later. - speech.startRecognition(gcsUri, request) + speech.longRunningRecognize(request) .then((results) => { const operation = results[0]; // Get a Promise representation of the final result of the job return operation.promise(); }) .then((results) => { - const transcription = results[0]; + const transcription = results[0].results[0].alternatives[0].transcript; console.log(`Transcription: ${transcription}`); }) .catch((err) => { @@ -221,10 +254,11 @@ function streamingRecognize (filename, encoding, sampleRateHertz, languageCode) }; // Stream the audio to the Google Cloud Speech API - const recognizeStream = speech.createRecognizeStream(request) + const recognizeStream = speech.streamingRecognize(request) .on('error', console.error) .on('data', (data) => { - console.log(`Transcription: ${data.results}`); + console.log( + `Transcription: ${data.results[0].alternatives[0].transcript}`); }); // Stream an audio file from disk to the Speech API, e.g. "./resources/audio.raw" @@ -261,9 +295,13 @@ function streamingMicRecognize (encoding, sampleRateHertz, languageCode) { }; // Create a recognize stream - const recognizeStream = speech.createRecognizeStream(request) + const recognizeStream = speech.streamingRecognize(request) .on('error', console.error) - .on('data', (data) => process.stdout.write(data.results)); + .on('data', (data) => + process.stdout.write( + (data.results[0] && data.results[0].alternatives[0]) + ? `Transcription: ${data.results[0].alternatives[0].transcript}\n` + : `\n\nReached transcription time limit, press Ctrl+C\n`)); // Start recording and send the microphone input to the Speech API record diff --git a/speech/system-test/quickstart.test.js b/speech/system-test/quickstart.test.js index b58d1abf42..6a86782a10 100644 --- a/speech/system-test/quickstart.test.js +++ b/speech/system-test/quickstart.test.js @@ -16,57 +16,20 @@ 'use strict'; const path = require(`path`); -const proxyquire = require(`proxyquire`).noPreserveCache(); -const sinon = require(`sinon`); -const speech = proxyquire(`@google-cloud/speech`, {})(); const test = require(`ava`); +const cmd = `node quickstart.js`; +const cwd = path.join(__dirname, `..`); +const text = `how old is the Brooklyn Bridge`; + const { - checkCredentials, - stubConsole, - restoreConsole + runAsync } = require(`@google-cloud/nodejs-repo-tools`); -const fileName = path.join(__dirname, `../resources/audio.raw`); -const config = { - encoding: `LINEAR16`, - sampleRateHertz: 16000, - languageCode: `en-US` -}; - -test.before(checkCredentials); -test.before(stubConsole); -test.after.always(restoreConsole); - -test.cb(`should detect speech`, (t) => { - const expectedFileName = `./resources/audio.raw`; - const expectedText = `how old is the Brooklyn Bridge`; - - const speechMock = { - recognize: (_fileName, _config) => { - t.is(_fileName, expectedFileName); - t.deepEqual(_config, config); - - return speech.recognize(fileName, config) - .then(([transcription]) => { - t.is(transcription, expectedText); - - setTimeout(() => { - try { - t.is(console.log.callCount, 1); - t.deepEqual(console.log.getCall(0).args, [`Transcription: ${expectedText}`]); - t.end(); - } catch (err) { - t.end(err); - } - }, 200); - - return [transcription]; - }); - } - }; +test.before(async () => { +}); - proxyquire(`../quickstart`, { - '@google-cloud/speech': sinon.stub().returns(speechMock) - }); +test(`should run quickstart`, async (t) => { + const output = await runAsync(`${cmd}`, cwd); + t.true(output.includes(`Transcription: ${text}`)); }); diff --git a/speech/system-test/recognize.test.js b/speech/system-test/recognize.test.js index 73606a2a03..bf56a5fb1e 100644 --- a/speech/system-test/recognize.test.js +++ b/speech/system-test/recognize.test.js @@ -45,12 +45,12 @@ test.after.always(async () => { test(`should run sync recognize`, async (t) => { const output = await runAsync(`${cmd} sync ${filepath}`, cwd); - t.true(output.includes(`Transcription: ${text}`)); + t.true(output.includes(`Transcription: ${text}`)); }); test(`should run sync recognize on a GCS file`, async (t) => { const output = await runAsync(`${cmd} sync-gcs gs://${bucketName}/${filename}`, cwd); - t.true(output.includes(`Transcription: ${text}`)); + t.true(output.includes(`Transcription: ${text}`)); }); test(`should run async recognize on a local file`, async (t) => {