From ebc0d48d4ce53624367d366d7d4f9e56b635320d Mon Sep 17 00:00:00 2001 From: Igor Abdrakhimov Date: Tue, 21 Nov 2023 15:49:26 -0800 Subject: [PATCH] CLean code --- servicetests/tests/shadow_update/index.ts | 303 +------------- servicetests/tests/util/cli_args.js | 460 ---------------------- 2 files changed, 18 insertions(+), 745 deletions(-) delete mode 100644 servicetests/tests/util/cli_args.js diff --git a/servicetests/tests/shadow_update/index.ts b/servicetests/tests/shadow_update/index.ts index e2a08478..07c8a99f 100644 --- a/servicetests/tests/shadow_update/index.ts +++ b/servicetests/tests/shadow_update/index.ts @@ -1,5 +1,4 @@ import { mqtt, iotshadow } from 'aws-iot-device-sdk-v2'; -import { stringify } from 'querystring'; import {once} from "events"; const sleep = (ms: number) => new Promise(resolve => setTimeout(resolve, ms)); @@ -7,288 +6,35 @@ const sleep = (ms: number) => new Promise(resolve => setTimeout(resolve, ms)); type Args = { [index: string]: any }; const yargs = require('yargs'); -// The relative path is '../../util/cli_args' from here, but the compiled javascript file gets put one level +// The relative path is '../../../samples/util/cli_args' from here, but the compiled javascript file gets put one level // deeper inside the 'dist' folder -const common_args = require('../../util/cli_args'); +const common_args = require('../../../../samples/util/cli_args'); -var shadow_value: unknown; var shadow_property: string; -var shadow_update_complete = false; - yargs.command('*', false, (yargs: any) => { common_args.add_direct_connection_establishment_arguments(yargs); common_args.add_shadow_arguments(yargs); }, main).parse(); -async function sub_to_shadow_update(shadow: iotshadow.IotShadowClient, argv: Args) { - return new Promise(async (resolve, reject) => { - try { - function updateAccepted(error?: iotshadow.IotShadowError, response?: iotshadow.model.UpdateShadowResponse) { - if (response) { - - if (response.clientToken !== undefined) { - console.log("Succcessfully updated shadow for clientToken: " + response.clientToken + "."); - } - else { - console.log("Succcessfully updated shadow."); - } - if (response.state?.desired !== undefined) { - console.log("\t desired state: " + stringify(response.state.desired)); - } - if (response.state?.reported !== undefined) { - console.log("\t reported state: " + stringify(response.state.reported)); - } - } - - if (error || !response) { - console.log("Updated shadow is missing the target property."); - } - resolve(true); - } - - function updateRejected(error?: iotshadow.IotShadowError, response?: iotshadow.model.ErrorResponse) { - if (response) { - console.log("Update request was rejected."); - } - - if (error) { - console.log("Error occurred..") - } - reject(error); - } - - console.log("Subscribing to Update events for thing " + argv.thing_name + "; shadow " + argv.shadow_property); - const updateShadowSubRequest: iotshadow.model.UpdateNamedShadowSubscriptionRequest = { - shadowName: argv.shadow_property, - thingName: argv.thing_name - }; - - await shadow.subscribeToUpdateShadowAccepted( - updateShadowSubRequest, - mqtt.QoS.AtLeastOnce, - (error, response) => updateAccepted(error, response)); - - await shadow.subscribeToUpdateShadowRejected( - updateShadowSubRequest, - mqtt.QoS.AtLeastOnce, - (error, response) => updateRejected(error, response)); - - resolve(true); - } - catch (error) { - reject(error); - } - }); -} - -async function sub_to_shadow_get(shadow: iotshadow.IotShadowClient, argv: Args) { - return new Promise(async (resolve, reject) => { - try { - function getAccepted(error?: iotshadow.IotShadowError, response?: iotshadow.model.GetShadowResponse) { - - if (response?.state) { - if (response?.state.delta) { - const value = response.state.delta; - if (value) { - console.log("Shadow contains delta value '" + stringify(value) + "'."); - change_shadow_value(shadow, argv, value); - } - } - - if (response?.state.reported) { - const value_any: any = response.state.reported; - if (value_any) { - let found_property = false; - for (var prop in value_any) { - if (prop === shadow_property) { - found_property = true; - console.log("Shadow contains '" + prop + "'. Reported value: '" + String(value_any[prop]) + "'."); - break; - } - } - if (found_property === false) { - console.log("Shadow does not contain '" + shadow_property + "' property."); - } - } - } - } - - if (error || !response) { - console.log("Error occurred.."); - } - shadow_update_complete = true; - resolve(true); - } - - function getRejected(error?: iotshadow.IotShadowError, response?: iotshadow.model.ErrorResponse) { - - if (response) { - console.log("In getRejected response."); - } - - if (error) { - console.log("Error occurred.."); - } - - shadow_update_complete = true; - reject(error); - } - - console.log("Subscribing to Get events.."); - const getShadowSubRequest: iotshadow.model.GetShadowSubscriptionRequest = { - thingName: argv.thing_name - }; - - await shadow.subscribeToGetShadowAccepted( - getShadowSubRequest, - mqtt.QoS.AtLeastOnce, - (error, response) => getAccepted(error, response)); - - await shadow.subscribeToGetShadowRejected( - getShadowSubRequest, - mqtt.QoS.AtLeastOnce, - (error, response) => getRejected(error, response)); - - resolve(true); - } - catch (error) { - reject(error); - } - }); -} - -async function sub_to_shadow_delta(shadow: iotshadow.IotShadowClient, argv: Args) { - return new Promise(async (resolve, reject) => { - try { - function deltaEvent(error?: iotshadow.IotShadowError, response?: iotshadow.model.GetShadowResponse) { - console.log("\nReceived shadow delta event."); - - if (response?.clientToken != null) { - console.log(" ClientToken: " + response.clientToken); - } - - if (response?.state !== null) { - let value_any: any = response?.state; - if (value_any === null || value_any === undefined) { - console.log("Delta reports that '" + shadow_property + "' was deleted. Resetting defaults.."); - let data_to_send: any = {}; - data_to_send[shadow_property] = argv.shadow_value; - change_shadow_value(shadow, argv, data_to_send); - } - else { - if (value_any[shadow_property] !== undefined) { - if (value_any[shadow_property] !== shadow_value) { - console.log("Delta reports that desired value is '" + value_any[shadow_property] + "'. Changing local value.."); - let data_to_send: any = {}; - data_to_send[shadow_property] = value_any[shadow_property]; - change_shadow_value(shadow, argv, data_to_send); - } - else { - console.log("Delta did not report a change in '" + shadow_property + "'."); - } - } - else { - console.log("Desired value not found in delta. Skipping.."); - } - } - } - else { - console.log("Delta did not report a change in '" + shadow_property + "'."); - } - - resolve(true); - } - - console.log("Subscribing to Delta events.."); - const deltaShadowSubRequest: iotshadow.model.ShadowDeltaUpdatedSubscriptionRequest = { - thingName: argv.thing_name - }; - - await shadow.subscribeToShadowDeltaUpdatedEvents( - deltaShadowSubRequest, - mqtt.QoS.AtLeastOnce, - (error, response) => deltaEvent(error, response)); - - resolve(true); - } - catch (error) { - reject(error); - } - }); -} - -async function get_current_shadow(shadow: iotshadow.IotShadowClient, argv: Args) { - return new Promise(async (resolve, reject) => { - try { - const getShadow: iotshadow.model.GetShadowRequest = { - thingName: argv.thing_name - } - - shadow_update_complete = false; - console.log("Requesting current shadow state.."); - shadow.publishGetShadow( - getShadow, - mqtt.QoS.AtLeastOnce); - - await get_current_shadow_update_wait(); - resolve(true); - } - catch (error) { - reject(error); - } - }); -} - - -async function get_current_shadow_update_wait() { - // Wait until shadow_update_complete is true, showing the result returned - return await new Promise(resolve => { - const interval = setInterval(() => { - if (shadow_update_complete == true) { - resolve(true); - clearInterval(interval); - }; - }, 200); - }); -} function change_shadow_value(shadow: iotshadow.IotShadowClient, argv: Args, new_value?: object) { return new Promise(async (resolve, reject) => { try { if (typeof new_value !== 'undefined') { - let new_value_any: any = new_value; - let skip_send = false; - - if (new_value_any !== null) { - if (new_value_any[shadow_property] == shadow_value) { - skip_send = true; - } - } - if (skip_send == false) { - if (new_value_any === null) { - shadow_value = new_value_any; - } - else { - shadow_value = new_value_any[shadow_property]; - } - - console.log("Changed local shadow value to '" + shadow_value + "'."); - - var updateShadow: iotshadow.model.UpdateShadowRequest = { - state: { - desired: new_value, - reported: new_value - }, - thingName: argv.thing_name - }; - - await shadow.publishUpdateShadow( - updateShadow, - mqtt.QoS.AtLeastOnce) - - console.log("Update request published."); - } + var updateShadow: iotshadow.model.UpdateShadowRequest = { + state: { + desired: new_value, + reported: new_value + }, + thingName: argv.thing_name + }; + + await shadow.publishUpdateShadow( + updateShadow, + mqtt.QoS.AtLeastOnce) + + console.log("Update request published."); } } catch (error) { @@ -326,22 +72,9 @@ async function main(argv: Args) { } try { - await sub_to_shadow_update(shadow, argv); - await sub_to_shadow_get(shadow, argv); - await sub_to_shadow_delta(shadow, argv); - await get_current_shadow(shadow, argv); - - await sleep(500); // wait half a second - - var messages_sent = 0; - while (messages_sent < 5) { - let data_to_send: any = {} - data_to_send[shadow_property] = "Shadow_Value_" + messages_sent.toString() - await change_shadow_value(shadow, argv, data_to_send); - await get_current_shadow(shadow, argv); - messages_sent += 1 - } - + let data_to_send: any = {} + data_to_send[shadow_property] = "on" + await change_shadow_value(shadow, argv, data_to_send); } catch (error) { console.log(error); } diff --git a/servicetests/tests/util/cli_args.js b/servicetests/tests/util/cli_args.js deleted file mode 100644 index b3a88fe1..00000000 --- a/servicetests/tests/util/cli_args.js +++ /dev/null @@ -1,460 +0,0 @@ -/** - * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. - * SPDX-License-Identifier: Apache-2.0. - */ - -/* - * The 'aws-iot-device-sdk-v2' module exports the same set of mqtt/http/io primitives as the crt, but - * it is not importable from this file based on js module resolution rules (because this file is sitting off - * in shared la-la-land) which walk up the directory tree from the file itself. - * - * So use the aws-crt interfaces directly, but a real application that was rooted in a single place could - * naturally use something like - * - * const iotsdk = require('aws-iot-device-sdk-v2') - * const auth = iotsdk.auth; - * etc... - * - */ -const awscrt = require('aws-crt'); -const auth = awscrt.auth; -const http = awscrt.http; -const io = awscrt.io; -const iot = awscrt.iot; -const mqtt = awscrt.mqtt; -const mqtt5 = awscrt.mqtt5; - -/* - * Arguments that control how the sample should establish its mqtt connection(s). - * Adds arguments for direct MQTT connections and websocket connections - */ -function add_connection_establishment_arguments(yargs) { - add_universal_arguments(yargs); - add_common_mqtt_arguments(yargs); - add_direct_tls_connect_arguments(yargs); - add_proxy_arguments(yargs); - add_common_websocket_arguments(yargs); -} - -/* - * Adds arguments that allow for easy direct MQTT connections. - */ -function add_direct_connection_establishment_arguments(yargs) { - add_universal_arguments(yargs); - add_common_mqtt_arguments(yargs); - add_direct_tls_connect_arguments(yargs); - add_proxy_arguments(yargs); -} - -/* - * Adds universal arguments every sample should have (help and logging verbosity) - */ -function add_universal_arguments(yargs) { - yargs - .option('verbosity', { - alias: 'v', - description: 'The amount of detail in the logging output of the sample (optional).', - type: 'string', - default: 'none', - choices: ['fatal', 'error', 'warn', 'info', 'debug', 'trace', 'none'] - }) - .option('is_ci', { - description: 'Launches the sample in CI mode (optional, set as anything to enable)', - type: 'boolean', - default: false - }) - .help() - .alias('help', 'h') - .showHelpOnFail(false) -} - -/* - * Common MQTT arguments needed for making a connection - */ -function add_common_mqtt_arguments(yargs) { - yargs - .option('endpoint', { - alias: 'e', - description: ': Your AWS IoT custom endpoint, not including a port.', - type: 'string', - required: true - }) - .option('ca_file', { - alias: 'r', - description: ': File path to a Root CA certificate file in PEM format (optional, system trust store used by default).', - type: 'string', - required: false - }) - .option('client_id', { - alias: 'C', - description: 'Client ID for MQTT connection.', - type: 'string', - required: false - }) -} - -/* - * Common MQTT arguments needed for making a connection - */ -function add_direct_tls_connect_arguments(yargs, is_required=false) { - yargs - .option('cert', { - alias: 'c', - description: ': File path to a PEM encoded certificate to use with mTLS.', - type: 'string', - required: is_required - }) - .option('key', { - alias: 'k', - description: ': File path to a PEM encoded private key that matches cert.', - type: 'string', - required: is_required - }) -} - -/* - * Proxy arguments - */ -function add_proxy_arguments(yargs) { - yargs - .option('proxy_host', { - alias: 'H', - description: 'Hostname of the proxy to connect to (optional, required if --proxy_port is set).', - type: 'string', - required: false - }) - .option('proxy_port', { - alias: 'P', - default: 8080, - description: 'Port of the proxy to connect to (optional, required if --proxy_host is set).', - type: 'number', - required: false - }) -} - -/* - * Common Websocket arguments needed for making a connection - */ -function add_common_websocket_arguments(yargs, is_required=false) { - yargs - .option('signing_region', { - alias: ['s', 'region'], - description: 'If you specify --signing_region then you will use websockets to connect. This' + - 'is the region that will be used for computing the Sigv4 signature. This region must match the' + - 'AWS region in your endpoint.', - type: 'string', - required: is_required - }) -} - -/* - * Arguments specific to sending a message to a topic multiple times. We have multiple samples that use these arguments. - */ -function add_topic_message_arguments(yargs) { - yargs - .option('topic', { - alias: 't', - description: 'Topic to publish to (optional).', - type: 'string', - default: 'test/topic' - }) - .option('count', { - alias: 'n', - default: 10, - description: 'Number of messages to publish/receive before exiting. ' + - 'Specify 0 to run forever (optional).', - type: 'number', - required: false - }) - .option('message', { - alias: 'M', - description: 'Message to publish (optional).', - type: 'string', - default: 'Hello world!' - }) -} - -/* - * Arguments specific to the shadow style samples. - */ -function add_shadow_arguments(yargs) { - yargs - .option('shadow_property', { - alias: 'p', - description: 'Name of property in shadow to keep in sync', - type: 'string', - default: 'color' - }) - .option('thing_name', { - alias: 'n', - description: 'The name assigned to your IoT Thing', - type: 'string', - default: 'name' - }) - .option('mqtt5', { - description: 'Use an MQTT5 client rather than a MQTT311 client', - type: 'boolean', - default: false - }); -} - -/** - * Arguments specific to the custom authorizer style samples - */ -function add_custom_authorizer_arguments(yargs) { - yargs - .option('custom_auth_username', { - description: 'The name to send when connecting through the custom authorizer (optional)', - type: 'string', - default: '' - }) - .option('custom_auth_authorizer_name', { - description: 'The name of the custom authorizer to connect to (optional but required for everything but custom domains)', - type: 'string', - default: '' - }) - .option('custom_auth_authorizer_signature', { - description: 'The digital signature of the value of the `--custom_auth_token_value` parameter using the private key associated with the authorizer. The binary signature value must be base64 encoded and then URI encoded; the SDK will not do this for you. (optional)', - type: 'string', - default: '' - }) - .option('custom_auth_password', { - description: 'The password to send when connecting through a custom authorizer (optional)', - type: 'string', - default: '' - }) - .option('custom_auth_token_key_name', { - description: 'The query string parameter name that the token value should be bound to in the MQTT Connect packet. (optional)', - type: 'string', - default: undefined - }) - .option('custom_auth_token_value', { - description: 'An arbitrary value chosen by the user. You must also submit a digital signature of this value using the private key associated with the authorizer. (optional)', - type: 'string', - default: undefined - }) -} - -/* - * Arguments specific to the Jobs style samples. - */ -function add_jobs_arguments(yargs) { - yargs - .option('thing_name', { - alias: 'n', - description: 'The name assigned to your IoT Thing', - type: 'string', - default: 'name' - }) - .option('job_time', { - alias: 't', - description: 'Emulate working on a job by sleeping this many seconds (optional, default=5)', - type: 'number', - default: 5 - }) -} - -/* - * Arguments specific to the Cognito samples. - */ -function add_cognito_arguments(yargs) { - yargs - .option('cognito_identity', { - alias: 'i', - description: 'The Cognito identity ID to use to connect via Cognito', - type: 'string', - default: '', - required: true - }) -} - -function add_x509_arguments(yargs) { - yargs - .option('x509_endpoint', { - description: 'The credentials endpoint to fetch x509 credentials from', - type: 'string', - default: '', - required: true - }) - .option('x509_thing_name', { - description: 'Thing name to fetch x509 credentials on behalf of', - type: 'string', - default: '', - required: true - }) - .option('x509_role_alias', { - description: 'Role alias to use with the x509 credentials provider', - type: 'string', - default: '', - required: true - }) - .option('x509_cert', { - description: 'Path to the IoT thing certificate used in fetching x509 credentials', - type: 'string', - default: '', - required: true - }) - .option('x509_key', { - description: 'Path to the IoT thing private key used in fetching x509 credentials', - type: 'string', - default: '', - required: true - }) - .option('x509_ca_file', { - description: 'Path to the root certificate used in fetching x509 credentials', - type: 'string', - default: '', - required: false - }) -} - -/* - * Handles any non-specific arguments that are relevant to all samples - */ -function apply_sample_arguments(argv) { - if (argv.verbosity != 'none') { - const level = parseInt(io.LogLevel[argv.verbosity.toUpperCase()]); - io.enable_logging(level); - } -} - -/* - * A set of simple connection builder functions intended to cover a variety of scenarios/configurations. - * - * There is plenty of redundant code across these functions, but in this case we are trying to show, stand-alone and - * top-to-bottom, all the steps needed to establish an mqtt connection for each scenario. - * - * ToDo : Add a connection builder for custom auth case - * ToDo : Add a connection builder showing x509 provider usage. Pre-req: x509 provider binding. - * ToDo : Add a connection builder for websockets using cognito and the Aws SDK for JS (or implement and bind - * a cognito provider). - */ - -/* - * Build an mqtt connection using websockets, (http) proxy optional. - */ -function build_websocket_mqtt_connection_from_args(argv) { - let config_builder = iot.AwsIotMqttConnectionConfigBuilder.new_with_websockets({ - region: argv.signing_region, - credentials_provider: auth.AwsCredentialsProvider.newDefault() - }); - - if (argv.proxy_host) { - config_builder.with_http_proxy_options(new http.HttpProxyOptions(argv.proxy_host, argv.proxy_port)); - } - - if (argv.ca_file != null) { - config_builder.with_certificate_authority_from_path(undefined, argv.ca_file); - } - - config_builder.with_clean_session(false); - config_builder.with_client_id(argv.client_id || "test-" + Math.floor(Math.random() * 100000000)); - config_builder.with_endpoint(argv.endpoint); - const config = config_builder.build(); - - const client = new mqtt.MqttClient(); - return client.new_connection(config); -} - -/* - * Build a direct mqtt connection using mtls, (http) proxy optional - */ -function build_direct_mqtt_connection_from_args(argv) { - let config_builder = iot.AwsIotMqttConnectionConfigBuilder.new_mtls_builder_from_path(argv.cert, argv.key); - - if (argv.proxy_host) { - config_builder.with_http_proxy_options(new http.HttpProxyOptions(argv.proxy_host, argv.proxy_port)); - } - - if (argv.ca_file != null) { - config_builder.with_certificate_authority_from_path(undefined, argv.ca_file); - } - - config_builder.with_clean_session(false); - config_builder.with_client_id(argv.client_id || "test-" + Math.floor(Math.random() * 100000000)); - config_builder.with_endpoint(argv.endpoint); - const config = config_builder.build(); - - const client = new mqtt.MqttClient(); - return client.new_connection(config); -} - -/* - * Uses all of the connection-relevant arguments to create an mqtt connection as desired. - */ -function build_connection_from_cli_args(argv) { - /* - * Only basic websocket and direct mqtt connections for now. Later add custom authorizer and x509 support. - */ - if (argv.signing_region) { - return build_websocket_mqtt_connection_from_args(argv); - } else { - return build_direct_mqtt_connection_from_args(argv); - } -} - -function build_websocket_mqtt5_client_from_args(argv) { - let config_builder = iot.AwsIotMqtt5ClientConfigBuilder.newWebsocketMqttBuilderWithSigv4Auth(argv.endpoint, { - region: argv.signing_region, - credentials_provider: auth.AwsCredentialsProvider.newDefault() - }); - - if (argv.proxy_host) { - config_builder.withHttpProxyOptions(new http.HttpProxyOptions(argv.proxy_host, argv.proxy_port)); - } - - if (argv.ca_file != null) { - config_builder.withCertificateAuthorityFromPath(undefined, argv.ca_file); - } - - config_builder.withSessionBehavior(mqtt5.ClientSessionBehavior.RejoinPostSuccess); - - return new mqtt5.Mqtt5Client(config_builder.build()); -} - -function build_direct_mqtt5_client_from_args(argv) { - let config_builder = iot.AwsIotMqtt5ClientConfigBuilder.newDirectMqttBuilderWithMtlsFromPath(argv.endpoint, argv.cert, argv.key); - - if (argv.proxy_host) { - config_builder.withHttpProxyOptions(new http.HttpProxyOptions(argv.proxy_host, argv.proxy_port)); - } - - if (argv.ca_file != null) { - config_builder.withCertificateAuthorityFromPath(undefined, argv.ca_file); - } - - config_builder.withSessionBehavior(mqtt5.ClientSessionBehavior.RejoinPostSuccess); - config_builder.withConnectProperties({ - clientId: argv.client_id || "test-" + Math.floor(Math.random() * 100000000), - keepAliveIntervalSeconds: 120 - }) - return new mqtt5.Mqtt5Client(config_builder.build()); -} - -function build_mqtt5_client_from_cli_args(argv) { - /* - * Only basic websocket and direct mqtt connections for now. Later add custom authorizer and x509 support. - */ - if (argv.signing_region) { - return build_websocket_mqtt5_client_from_args(argv); - } else { - return build_direct_mqtt5_client_from_args(argv); - } -} - -exports.add_connection_establishment_arguments = add_connection_establishment_arguments; -exports.add_direct_connection_establishment_arguments = add_direct_connection_establishment_arguments; -exports.add_universal_arguments = add_universal_arguments; -exports.add_common_mqtt_arguments = add_common_mqtt_arguments; -exports.add_direct_tls_connect_arguments = add_direct_tls_connect_arguments; -exports.add_proxy_arguments = add_proxy_arguments; -exports.add_common_websocket_arguments = add_common_websocket_arguments; -exports.add_topic_message_arguments = add_topic_message_arguments; -exports.add_shadow_arguments = add_shadow_arguments; -exports.add_custom_authorizer_arguments = add_custom_authorizer_arguments; -exports.add_jobs_arguments = add_jobs_arguments; -exports.add_cognito_arguments = add_cognito_arguments; -exports.add_x509_arguments = add_x509_arguments -exports.apply_sample_arguments = apply_sample_arguments; -exports.build_connection_from_cli_args = build_connection_from_cli_args; -exports.build_mqtt5_client_from_cli_args = build_mqtt5_client_from_cli_args;