aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorSteve Harris2018-02-17 20:38:29 -0600
committerGordon Wang2018-03-26 12:44:16 -0500
commit0e58049b600e52680f99de29c9d38332e6f6d95f (patch)
treea4c75994cbfa31647b94811fa2e29d55ba9d1f3c
parent9ad74d845905343bcc1f327e5307af39cae5d458 (diff)
downloadaws-iot-device-sdk-embedded-c-0e58049b600e52680f99de29c9d38332e6f6d95f.tar.gz
aws-iot-device-sdk-embedded-c-0e58049b600e52680f99de29c9d38332e6f6d95f.tar.xz
aws-iot-device-sdk-embedded-c-0e58049b600e52680f99de29c9d38332e6f6d95f.zip
Add Jobs support
-rw-r--r--README.md3
-rw-r--r--include/aws_iot_error.h8
-rw-r--r--include/aws_iot_jobs_interface.h267
-rw-r--r--include/aws_iot_jobs_json.h108
-rw-r--r--include/aws_iot_jobs_topics.h88
-rw-r--r--include/aws_iot_jobs_types.h109
-rw-r--r--include/aws_iot_json_utils.h16
-rw-r--r--samples/linux/jobs_sample/Makefile70
-rw-r--r--samples/linux/jobs_sample/aws_iot_config.h73
-rw-r--r--samples/linux/jobs_sample/jobs_sample.c368
-rw-r--r--src/aws_iot_jobs_interface.c215
-rw-r--r--src/aws_iot_jobs_json.c198
-rw-r--r--src/aws_iot_jobs_topics.c129
-rw-r--r--src/aws_iot_jobs_types.c73
-rw-r--r--src/aws_iot_json_utils.c31
-rw-r--r--tests/integration/include/aws_iot_config.h59
-rw-r--r--tests/integration/src/aws_iot_test_integration_runner.c24
-rw-r--r--tests/integration/src/aws_iot_test_jobs_api.c279
-rw-r--r--tests/unit/include/aws_iot_config.h48
-rw-r--r--tests/unit/include/aws_iot_tests_unit_helper_functions.h5
-rw-r--r--tests/unit/src/aws_iot_tests_unit_common_tests_helper.c8
-rw-r--r--tests/unit/src/aws_iot_tests_unit_helper_functions.c25
-rw-r--r--tests/unit/src/aws_iot_tests_unit_jobs.cpp60
-rw-r--r--tests/unit/src/aws_iot_tests_unit_jobs_interface.c337
-rw-r--r--tests/unit/src/aws_iot_tests_unit_jobs_json.c244
-rw-r--r--tests/unit/src/aws_iot_tests_unit_jobs_topics.c161
-rw-r--r--tests/unit/src/aws_iot_tests_unit_jobs_types.c70
-rw-r--r--tests/unit/src/aws_iot_tests_unit_shadow_action_helper.c13
-rw-r--r--tests/unit/tls_mock/aws_iot_tests_unit_mock_tls.c71
-rw-r--r--tests/unit/tls_mock/aws_iot_tests_unit_mock_tls_params.c8
-rw-r--r--tests/unit/tls_mock/aws_iot_tests_unit_mock_tls_params.h11
31 files changed, 3118 insertions, 61 deletions
diff --git a/README.md b/README.md
index 8b762d4..acacd1a 100644
--- a/README.md
+++ b/README.md
@@ -16,6 +16,9 @@ The Device SDK provides functionality to create and maintain a mutually authenti
16### Thing Shadow 16### Thing Shadow
17The Device SDK implements the specific protocol for Thing Shadows to retrieve, update and delete Thing Shadows adhering to the protocol that is implemented to ensure correct versioning and support for client tokens. It abstracts the necessary MQTT topic subscriptions by automatically subscribing to and unsubscribing from the reserved topics as needed for each API call. Inbound state change requests are automatically signalled via a configurable callback. 17The Device SDK implements the specific protocol for Thing Shadows to retrieve, update and delete Thing Shadows adhering to the protocol that is implemented to ensure correct versioning and support for client tokens. It abstracts the necessary MQTT topic subscriptions by automatically subscribing to and unsubscribing from the reserved topics as needed for each API call. Inbound state change requests are automatically signalled via a configurable callback.
18 18
19### Jobs
20The Device SDK implements features to facilitate use of the AWS Jobs service. The Jobs service can be used for device management tasks such as updating program files, rotating device certificates, or running other maintenance tasks such are restoring device settings or restarting devices.
21
19## Design Goals of this SDK 22## Design Goals of this SDK
20The embedded C SDK was specifically designed for resource constrained devices (running on micro-controllers and RTOS). 23The embedded C SDK was specifically designed for resource constrained devices (running on micro-controllers and RTOS).
21 24
diff --git a/include/aws_iot_error.h b/include/aws_iot_error.h
index 03f297b..f0ccc39 100644
--- a/include/aws_iot_error.h
+++ b/include/aws_iot_error.h
@@ -1,5 +1,5 @@
1/* 1/*
2 * Copyright 2010-2015 Amazon.com, Inc. or its affiliates. All Rights Reserved. 2 * Copyright 2010-2018 Amazon.com, Inc. or its affiliates. All Rights Reserved.
3 * 3 *
4 * Licensed under the Apache License, Version 2.0 (the "License"). 4 * Licensed under the Apache License, Version 2.0 (the "License").
5 * You may not use this file except in compliance with the License. 5 * You may not use this file except in compliance with the License.
@@ -151,6 +151,12 @@ typedef enum {
151 MUTEX_UNLOCK_ERROR = -48, 151 MUTEX_UNLOCK_ERROR = -48,
152 /** Mutex destroy failed */ 152 /** Mutex destroy failed */
153 MUTEX_DESTROY_ERROR = -49, 153 MUTEX_DESTROY_ERROR = -49,
154 /** Input argument exceeded the allowed maximum size */
155 MAX_SIZE_ERROR = -50,
156 /** Some limit has been exceeded, e.g. the maximum number of subscriptions has been reached */
157 LIMIT_EXCEEDED_ERROR = -51,
158 /** Invalid input topic type */
159 INVALID_TOPIC_TYPE_ERROR = -52
154} IoT_Error_t; 160} IoT_Error_t;
155 161
156#ifdef __cplusplus 162#ifdef __cplusplus
diff --git a/include/aws_iot_jobs_interface.h b/include/aws_iot_jobs_interface.h
new file mode 100644
index 0000000..4f033d9
--- /dev/null
+++ b/include/aws_iot_jobs_interface.h
@@ -0,0 +1,267 @@
1/*
2 * Copyright 2015-2018 Amazon.com, Inc. or its affiliates. All Rights Reserved.
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License").
5 * You may not use this file except in compliance with the License.
6 * A copy of the License is located at
7 *
8 * http://aws.amazon.com/apache2.0
9 *
10 * or in the "license" file accompanying this file. This file is distributed
11 * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
12 * express or implied. See the License for the specific language governing
13 * permissions and limitations under the License.
14 */
15
16/**
17 * @file aws_iot_jobs_interface.h
18 * @brief An interface for interacting with the AWS IoT Jobs system.
19 *
20 * This file defines utility functions for interacting with the AWS IoT jobs
21 * APIs over MQTT. It provides functions for managing subscriptions to job
22 * related topics and for sending queries and updates requests for jobs.
23 * Callers are responsible for managing the subscriptions and associating
24 * responses with the queries and update messages.
25 */
26#ifndef AWS_IOT_JOBS_INTERFACE_H_
27#define AWS_IOT_JOBS_INTERFACE_H_
28
29#ifdef DISABLE_IOT_JOBS
30#error "Jobs API is disabled"
31#endif
32
33/**
34 * @file aws_iot_jobs_interface.h
35 * @brief Functions for interacting with the AWS IoT Jobs system.
36 */
37#include "aws_iot_mqtt_client_interface.h"
38#include "aws_iot_jobs_topics.h"
39#include "aws_iot_jobs_types.h"
40#include "aws_iot_error.h"
41#include "aws_iot_json_utils.h"
42
43#ifdef __cplusplus
44extern "C" {
45#endif
46
47/**
48 * @brief Subscribe to jobs messages for the given thing and/or jobs.
49 *
50 * The function can be used to subscribe to all job related messages. To subscribe
51 * to messages not related to a job the jobId passed should be null. The jobId
52 * can also be "+" to subscribe to messages related to any job, or "$next" to
53 * indicate the next pending job.
54 *
55 * See also #aws_iot_jobs_subscribe_to_all_job_messages to subscribe to all possible
56 * messages in one operation.
57 *
58 * \note Subscribing with the same thing, job and topic type more than
59 * once is undefined.
60 *
61 * \param pClient the client to use
62 * \param qos the qos to use
63 * \param thingName the name of the thing to subscribe to
64 * \param jobId the job id to subscribe to. To subscribe to messages not related to
65 * a job the jobId passed should be null. The jobId can also be "+" to subscribe to
66 * messages related to any job, or "$next" to indicate the next pending job.
67 * \param topicType the topic type to subscribe to
68 * \param replyType the reply topic type to subscribe to
69 * \param pApplicationHandler the callback handler
70 * \param pApplicationHandlerData the callback context data. This must remain valid at least until
71 * aws_iot_jobs_unsubscribe_from_job_messages is called.
72 * \param topicBuffer. A buffer to use to hold the topic name for the subscription. This buffer
73 * must remain valid at least until aws_iot_jobs_unsubscribe_from_job_messages is called.
74 * \param topicBufferSize the size of the topic buffer. The function will fail
75 * with LIMIT_EXCEEDED_ERROR if this is too small.
76 * \return the result of subscribing to the topic (see aws_iot_mqtt_subscribe)
77 */
78IoT_Error_t aws_iot_jobs_subscribe_to_job_messages(
79 AWS_IoT_Client *pClient, QoS qos,
80 const char *thingName,
81 const char *jobId,
82 AwsIotJobExecutionTopicType topicType,
83 AwsIotJobExecutionTopicReplyType replyType,
84 pApplicationHandler_t pApplicationHandler,
85 void *pApplicationHandlerData,
86 char *topicBuffer,
87 uint16_t topicBufferSize);
88
89/**
90 * @brief Subscribe to all job messages.
91 *
92 * Subscribe to all job messages for the given thing.
93 *
94 * \note Subscribing with the same thing more than once is undefined.
95 *
96 * \param pClient the client to use
97 * \param qos the qos to use
98 * \param thingName the name of the thing to subscribe to
99 * \param pApplicationHandler the callback handler
100 * \param pApplicationHandlerData the callback context data. This must remain valid at least until
101 * aws_iot_jobs_unsubscribe_from_job_messages is called.
102 * \param topicBuffer. A buffer to use to hold the topic name for the subscription. This buffer
103 * must remain valid at least until aws_iot_jobs_unsubscribe_from_job_messages is called.
104 * \param topicBufferSize the size of the topic buffer. The function will fail
105 * with LIMIT_EXCEEDED_ERROR if this is too small.
106 * \return the result of subscribing to the topic (see aws_iot_mqtt_subscribe)
107 */
108IoT_Error_t aws_iot_jobs_subscribe_to_all_job_messages(
109 AWS_IoT_Client *pClient, QoS qos,
110 const char *thingName,
111 pApplicationHandler_t pApplicationHandler,
112 void *pApplicationHandlerData,
113 char *topicBuffer,
114 uint16_t topicBufferSize);
115
116/**
117 * @brief Unsubscribe from a job subscription
118 *
119 * Remove the subscription created using #aws_iot_jobs_subscribe_to_job_messages or
120 * #aws_iot_jobs_subscribe_to_all_job_messages.
121 *
122 * \param pClient the client to use
123 * \param topicBuffer the topic buffer passed to #aws_iot_jobs_subscribe_to_job_messages or
124 * #aws_iot_jobs_subscribe_to_all_job_messages when the subscription was created.
125 * \return #SUCCESS or the first error encountered.
126 */
127IoT_Error_t aws_iot_jobs_unsubscribe_from_job_messages(
128 AWS_IoT_Client *pClient,
129 char *topicBuffer);
130
131/**
132 * @brief Send a query to one of the job query APIs.
133 *
134 * Send a query to one of the job query APIs. If jobId is null this
135 * requests a list of pending jobs for the thing. If jobId is
136 * not null then it sends a query for the details of that job.
137 * If jobId is $next then it sends a query for the details for
138 * the next pending job.
139 *
140 * \param pClient the client to use
141 * \param qos the qos to use
142 * \param thingName the thing name to query for
143 * \param jobId the id of the job to query for. If null a list
144 * of all pending jobs for the thing is requested.
145 * \param clientToken the client token to use for the query.
146 * If null no clientToken is sent resulting in an empty message.
147 * \param topicBuffer the topic buffer to use. This may be discarded
148 * as soon as this function returns
149 * \param topicBufferSize the size of topicBuffer
150 * \param messageBuffer the message buffer to use. May be NULL
151 * if clientToken is NULL
152 * \param messageBufferSize the size of messageBuffer
153 * \param topicType the topic type to publish query to
154 * \return LIMIT_EXCEEDED_ERROR if the topic buffer or
155 * message buffer is too small, NULL_VALUE_ERROR if the any of
156 * the required inputs are NULL, otherwise the result
157 * of the mqtt publish
158 */
159IoT_Error_t aws_iot_jobs_send_query(
160 AWS_IoT_Client *pClient, QoS qos,
161 const char *thingName,
162 const char *jobId,
163 const char *clientToken,
164 char *topicBuffer,
165 uint16_t topicBufferSize,
166 char *messageBuffer,
167 size_t messageBufferSize,
168 AwsIotJobExecutionTopicType topicType);
169
170/**
171 * @brief Send a start next command to the job start-next API.
172 *
173 * Send a start next command to the job start-next API.
174 *
175 * \param pClient the client to use
176 * \param qos the qos to use
177 * \param thingName the thing name to query for
178 * \param startNextRequest the start-next request to send
179 * \param topicBuffer the topic buffer to use. This may be discarded
180 * as soon as this function returns
181 * \param topicBufferSize the size of topicBuffer
182 * \param messageBuffer the message buffer to use. May be NULL
183 * if clientToken is NULL
184 * \param messageBufferSize the size of messageBuffer
185 * \return LIMIT_EXCEEDED_ERROR if the topic buffer or
186 * message buffer is too small, NULL_VALUE_ERROR if the any of
187 * the required inputs are NULL, otherwise the result
188 * of the mqtt publish
189 */
190IoT_Error_t aws_iot_jobs_start_next(
191 AWS_IoT_Client *pClient, QoS qos,
192 const char *thingName,
193 const AwsIotStartNextPendingJobExecutionRequest *startNextRequest,
194 char *topicBuffer,
195 uint16_t topicBufferSize,
196 char *messageBuffer,
197 size_t messageBufferSize);
198
199/**
200 * @brief Send a describe job query to the job query API.
201 *
202 * Send a describe job query to the job query API. If jobId is null this
203 * requests a list of pending jobs for the thing. If jobId is
204 * not null then it sends a query for the details of that job.
205 *
206 * \param pClient the client to use
207 * \param qos the qos to use
208 * \param thingName the thing name to query for
209 * \param jobId the id of the job to query for. If null a list
210 * of all pending jobs for the thing is requested.
211 * \param describeRequest the describe request to send
212 * \param topicBuffer the topic buffer to use. This may be discarded
213 * as soon as this function returns
214 * \param topicBufferSize the size of topicBuffer
215 * \param messageBuffer the message buffer to use. May be NULL
216 * if clientToken is NULL
217 * \param messageBufferSize the size of messageBuffer
218 * \return LIMIT_EXCEEDED_ERROR if the topic buffer or
219 * message buffer is too small, NULL_VALUE_ERROR if the any of
220 * the required inputs are NULL, otherwise the result
221 * of the mqtt publish
222 */
223IoT_Error_t aws_iot_jobs_describe(
224 AWS_IoT_Client *pClient, QoS qos,
225 const char *thingName,
226 const char *jobId,
227 const AwsIotDescribeJobExecutionRequest *describeRequest,
228 char *topicBuffer,
229 uint16_t topicBufferSize,
230 char *messageBuffer,
231 size_t messageBufferSize);
232
233/**
234 * @brief Send an update about a job execution.
235 *
236 * Send an update about a job execution.
237 *
238 * \param pClient the client to use
239 * \param qos the qos to use
240 * \param thingName the thing name to send the update for
241 * \param jobId the id of the job to send the update for
242 * \param updateRequest the update request to send
243 * \param topicBuffer the topic buffer to use. This may be discarded
244 * as soon as this function returns
245 * \param topicBufferSize the size of topicBuffer
246 * \param messageBuffer the message buffer to use.
247 * \param messageBufferSize the size of messageBuffer
248 * \return LIMIT_EXCEEDED_ERROR if the topic buffer or
249 * message buffer is too small, NULL_VALUE_ERROR if the any of
250 * the required inputs are NULL, otherwise the result
251 * of the mqtt publish
252 */
253IoT_Error_t aws_iot_jobs_send_update(
254 AWS_IoT_Client *pClient, QoS qos,
255 const char *thingName,
256 const char *jobId,
257 const AwsIotJobExecutionUpdateRequest *updateRequest,
258 char *topicBuffer,
259 uint16_t topicBufferSize,
260 char *messageBuffer,
261 size_t messageBufferSize);
262
263#ifdef __cplusplus
264}
265#endif
266
267#endif /* AWS_IOT_JOBS_INTERFACE_H_ */
diff --git a/include/aws_iot_jobs_json.h b/include/aws_iot_jobs_json.h
new file mode 100644
index 0000000..55e4514
--- /dev/null
+++ b/include/aws_iot_jobs_json.h
@@ -0,0 +1,108 @@
1/*
2 * Copyright 2015-2018 Amazon.com, Inc. or its affiliates. All Rights Reserved.
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License").
5 * You may not use this file except in compliance with the License.
6 * A copy of the License is located at
7 *
8 * http://aws.amazon.com/apache2.0
9 *
10 * or in the "license" file accompanying this file. This file is distributed
11 * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
12 * express or implied. See the License for the specific language governing
13 * permissions and limitations under the License.
14 */
15
16/**
17 * @file aws_iot_jobs_json.h
18 * @brief Functions for mapping between json and the AWS Iot Job data structures.
19 */
20
21#ifdef DISABLE_IOT_JOBS
22#error "Jobs API is disabled"
23#endif
24
25#ifndef AWS_IOT_JOBS_JSON_H_
26#define AWS_IOT_JOBS_JSON_H_
27
28#include <stdbool.h>
29#include "jsmn.h"
30#include "aws_iot_jobs_types.h"
31
32#ifdef __cplusplus
33extern "C" {
34#endif
35
36/**
37 * Serialize a job execution update request into a json string.
38 *
39 * \param requestBuffer buffer to contain the serialized request. If null
40 * this function will return the size of the buffer required
41 * \param bufferSize the size of the buffer. If this is smaller than the required
42 * length the string will be truncated to fit.
43 * \request the request to serialize.
44 * \return The size of the json string to store the serialized request or -1
45 * if the request is invalid. Note that the return value should be checked against
46 * the size of the buffer and if its larger handle the fact that the string has
47 * been truncated.
48 */
49int aws_iot_jobs_json_serialize_update_job_execution_request(
50 char *requestBuffer, size_t bufferSize,
51 const AwsIotJobExecutionUpdateRequest *request);
52
53/**
54 * Serialize a job API request that contains only a client token.
55 *
56 * \param requestBuffer buffer to contain the serialized request. If null
57 * this function will return the size of the buffer required
58 * \param bufferSize the size of the buffer. If this is smaller than the required
59 * length the string will be truncated to fit.
60 * \param clientToken the client token to use for the request.
61 * \return The size of the json string to store the serialized request or -1
62 * if the request is invalid. Note that the return value should be checked against
63 * the size of the buffer and if its larger handle the fact that the string has
64 * been truncated.
65 */
66int aws_iot_jobs_json_serialize_client_token_only_request(
67 char *requestBuffer, size_t bufferSize,
68 const char *clientToken);
69
70/**
71 * Serialize describe job execution request into json string.
72 *
73 * \param requestBuffer buffer to contain the serialized request. If null
74 * this function will return the size of the buffer required
75 * \param bufferSize the size of the buffer. If this is smaller than the required
76 * length the string will be truncated to fit.
77 * \param request the request to serialize.
78 * \return The size of the json string to store the serialized request or -1
79 * if the request is invalid. Note that the return value should be checked against
80 * the size of the buffer and if its larger handle the fact that the string has
81 * been truncated.
82 */
83int aws_iot_jobs_json_serialize_describe_job_execution_request(
84 char *requestBuffer, size_t bufferSize,
85 const AwsIotDescribeJobExecutionRequest *request);
86
87/**
88 * Serialize start next job execution request into json string.
89 *
90 * \param requestBuffer buffer to contain the serialized request. If null
91 * this function will return the size of the buffer required
92 * \param bufferSize the size of the buffer. If this is smaller than the required
93 * length the string will be truncated to fit.
94 * \param request the start-next request to serialize.
95 * \return The size of the json string to store the serialized request or -1
96 * if the request is invalid. Note that the return value should be checked against
97 * the size of the buffer and if its larger handle the fact that the string has
98 * been truncated.
99 */
100int aws_iot_jobs_json_serialize_start_next_job_execution_request(
101 char *requestBuffer, size_t bufferSize,
102 const AwsIotStartNextPendingJobExecutionRequest *request);
103
104#ifdef __cplusplus
105}
106#endif
107
108#endif /* AWS_IOT_JOBS_JSON_H_ */
diff --git a/include/aws_iot_jobs_topics.h b/include/aws_iot_jobs_topics.h
new file mode 100644
index 0000000..3e29187
--- /dev/null
+++ b/include/aws_iot_jobs_topics.h
@@ -0,0 +1,88 @@
1/*
2 * Copyright 2015-2018 Amazon.com, Inc. or its affiliates. All Rights Reserved.
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License").
5 * You may not use this file except in compliance with the License.
6 * A copy of the License is located at
7 *
8 * http://aws.amazon.com/apache2.0
9 *
10 * or in the "license" file accompanying this file. This file is distributed
11 * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
12 * express or implied. See the License for the specific language governing
13 * permissions and limitations under the License.
14 */
15
16/**
17 * @file aws_iot_job_topics.h
18 * @brief Functions for parsing and creating MQTT topics used by the AWS IoT Jobs system.
19 */
20
21#ifdef DISABLE_IOT_JOBS
22#error "Jobs API is disabled"
23#endif
24
25#ifndef AWS_IOT_JOBS_TOPICS_H_
26#define AWS_IOT_JOBS_TOPICS_H_
27
28#include <stdint.h>
29#include <stdbool.h>
30#include <stddef.h>
31
32#ifdef __cplusplus
33extern "C" {
34#endif
35
36#define JOB_ID_NEXT "$next"
37#define JOB_ID_WILDCARD "+"
38
39/**
40 * The type of job topic.
41 */
42typedef enum {
43 JOB_UNRECOGNIZED_TOPIC = 0,
44 JOB_GET_PENDING_TOPIC,
45 JOB_START_NEXT_TOPIC,
46 JOB_DESCRIBE_TOPIC,
47 JOB_UPDATE_TOPIC,
48 JOB_NOTIFY_TOPIC,
49 JOB_NOTIFY_NEXT_TOPIC,
50 JOB_WILDCARD_TOPIC
51} AwsIotJobExecutionTopicType;
52
53/**
54 * The type of reply topic, or #JOB_REQUEST_TYPE for
55 * topics that are not replies.
56 */
57typedef enum {
58 JOB_UNRECOGNIZED_TOPIC_TYPE = 0,
59 JOB_REQUEST_TYPE,
60 JOB_ACCEPTED_REPLY_TYPE,
61 JOB_REJECTED_REPLY_TYPE,
62 JOB_WILDCARD_REPLY_TYPE
63} AwsIotJobExecutionTopicReplyType;
64
65/**
66 * @brief Get the topic matching the provided details and put into the provided buffer.
67 *
68 * If the buffer is not large enough to store the full topic the topic will be truncated
69 * to fit, with the last character always being a null terminator.
70 *
71 * \param buffer the buffer to put the results into
72 * \param bufferSize the size of the buffer
73 * \param topicType the type of the topic
74 * \param replyType the reply type of the topic
75 * \param thingName the name of the thing in the topic
76 * \param jobId the name of the job id in the topic
77 * \return the number of characters in the topic excluding the null terminator. A return
78 * value of bufferSize or more means that the topic string was truncated.
79 */
80int aws_iot_jobs_get_api_topic(char *buffer, size_t bufferSize,
81 AwsIotJobExecutionTopicType topicType, AwsIotJobExecutionTopicReplyType replyType,
82 const char* thingName, const char* jobId);
83
84#ifdef __cplusplus
85}
86#endif
87
88#endif /* AWS_IOT_JOBS_TOPICS_H_ */
diff --git a/include/aws_iot_jobs_types.h b/include/aws_iot_jobs_types.h
new file mode 100644
index 0000000..22ba62f
--- /dev/null
+++ b/include/aws_iot_jobs_types.h
@@ -0,0 +1,109 @@
1/*
2 * Copyright 2015-2018 Amazon.com, Inc. or its affiliates. All Rights Reserved.
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License").
5 * You may not use this file except in compliance with the License.
6 * A copy of the License is located at
7 *
8 * http://aws.amazon.com/apache2.0
9 *
10 * or in the "license" file accompanying this file. This file is distributed
11 * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
12 * express or implied. See the License for the specific language governing
13 * permissions and limitations under the License.
14 */
15
16/**
17 * @file aws_iot_jobs_types.h
18 * @brief Structures defining the interface with the AWS IoT Jobs system
19 *
20 * This file defines the structures returned by and sent to the AWS IoT Jobs system.
21 *
22 */
23
24#ifdef DISABLE_IOT_JOBS
25#error "Jobs API is disabled"
26#endif
27
28#ifndef AWS_IOT_JOBS_TYPES_H_
29#define AWS_IOT_JOBS_TYPES_H_
30
31#include <stdbool.h>
32#include <stdint.h>
33#include "jsmn.h"
34#include "timer_interface.h"
35
36#ifdef __cplusplus
37extern "C" {
38#endif
39
40typedef enum {
41 JOB_EXECUTION_STATUS_NOT_SET = 0,
42 JOB_EXECUTION_QUEUED,
43 JOB_EXECUTION_IN_PROGRESS,
44 JOB_EXECUTION_FAILED,
45 JOB_EXECUTION_SUCCEEDED,
46 JOB_EXECUTION_CANCELED,
47 JOB_EXECUTION_REJECTED,
48 /***
49 * Used for any status not in the supported list of statuses
50 */
51 JOB_EXECUTION_UNKNOWN_STATUS = 99
52} JobExecutionStatus;
53
54extern const char *JOB_EXECUTION_QUEUED_STR;
55extern const char *JOB_EXECUTION_IN_PROGRESS_STR;
56extern const char *JOB_EXECUTION_FAILED_STR;
57extern const char *JOB_EXECUTION_SUCCESS_STR;
58extern const char *JOB_EXECUTION_CANCELED_STR;
59extern const char *JOB_EXECUTION_REJECTED_STR;
60
61/**
62 * Convert a string to its matching status.
63 *
64 * \return the matching status, or JOB_EXECUTION_UNKNOWN_STATUS if the string was not recognized.
65 */
66JobExecutionStatus aws_iot_jobs_map_string_to_job_status(const char *str);
67
68/**
69 * Convert a status to its string.
70 *
71 * \return a string representing the status, or null if the status is not recognized.
72 */
73const char *aws_iot_jobs_map_status_to_string(JobExecutionStatus status);
74
75/**
76 * A request to update the status of a job execution.
77 */
78typedef struct {
79 int64_t expectedVersion; // set to 0 to ignore
80 int64_t executionNumber; // set to 0 to ignore
81 JobExecutionStatus status;
82 const char *statusDetails;
83 bool includeJobExecutionState;
84 bool includeJobDocument;
85 const char *clientToken;
86} AwsIotJobExecutionUpdateRequest;
87
88/**
89 * A request to get the status of a job execution.
90 */
91typedef struct {
92 int64_t executionNumber; // set to 0 to ignore
93 bool includeJobDocument;
94 const char *clientToken;
95} AwsIotDescribeJobExecutionRequest;
96
97/**
98 * A request to get and start the next pending (not in a terminal state) job execution for a Thing.
99 */
100typedef struct {
101 const char *statusDetails;
102 const char *clientToken;
103} AwsIotStartNextPendingJobExecutionRequest;
104
105#ifdef __cplusplus
106}
107#endif
108
109#endif /* AWS_IOT_JOBS_TYPES_H_ */
diff --git a/include/aws_iot_json_utils.h b/include/aws_iot_json_utils.h
index ceb02b3..2c040cf 100644
--- a/include/aws_iot_json_utils.h
+++ b/include/aws_iot_json_utils.h
@@ -1,5 +1,5 @@
1/* 1/*
2 * Copyright 2010-2015 Amazon.com, Inc. or its affiliates. All Rights Reserved. 2 * Copyright 2010-2018 Amazon.com, Inc. or its affiliates. All Rights Reserved.
3 * 3 *
4 * Licensed under the Apache License, Version 2.0 (the "License"). 4 * Licensed under the Apache License, Version 2.0 (the "License").
5 * You may not use this file except in compliance with the License. 5 * You may not use this file except in compliance with the License.
@@ -190,6 +190,20 @@ IoT_Error_t parseBooleanValue(bool *b, const char *jsonString, jsmntok_t *token)
190 */ 190 */
191IoT_Error_t parseStringValue(char *buf, const char *jsonString, jsmntok_t *token); 191IoT_Error_t parseStringValue(char *buf, const char *jsonString, jsmntok_t *token);
192 192
193/**
194 * @brief Find the JSON node associated with the given key in the given object.
195 *
196 * Given a JSON node parse the string value from the value.
197 *
198 * @param key json key
199 * @param token json token - pointer to JSON node
200 * @param jsonString json string
201 *
202 * @return pointer to found property value
203 * @return NULL - not found
204 */
205jsmntok_t *findToken(const char *key, const char *jsonString, jsmntok_t *token);
206
193#ifdef __cplusplus 207#ifdef __cplusplus
194} 208}
195#endif 209#endif
diff --git a/samples/linux/jobs_sample/Makefile b/samples/linux/jobs_sample/Makefile
new file mode 100644
index 0000000..5fc68e6
--- /dev/null
+++ b/samples/linux/jobs_sample/Makefile
@@ -0,0 +1,70 @@
1#This target is to ensure accidental execution of Makefile as a bash script will not execute commands like rm in unexpected directories and exit gracefully.
2.prevent_execution:
3 exit 0
4
5CC = gcc
6
7#remove @ for no make command prints
8DEBUG = @
9
10APP_DIR = .
11APP_INCLUDE_DIRS += -I $(APP_DIR)
12APP_NAME = jobs_sample
13APP_SRC_FILES = $(APP_NAME).c
14
15#IoT client directory
16IOT_CLIENT_DIR = ../../..
17
18PLATFORM_DIR = $(IOT_CLIENT_DIR)/platform/linux/mbedtls
19PLATFORM_COMMON_DIR = $(IOT_CLIENT_DIR)/platform/linux/common
20
21IOT_INCLUDE_DIRS += -I $(IOT_CLIENT_DIR)/include
22IOT_INCLUDE_DIRS += -I $(IOT_CLIENT_DIR)/sdk_config
23IOT_INCLUDE_DIRS += -I $(IOT_CLIENT_DIR)/external_libs/jsmn
24IOT_INCLUDE_DIRS += -I $(PLATFORM_COMMON_DIR)
25IOT_INCLUDE_DIRS += -I $(PLATFORM_DIR)
26
27IOT_SRC_FILES += $(shell find $(IOT_CLIENT_DIR)/src/ -name '*.c')
28IOT_SRC_FILES += $(shell find $(IOT_CLIENT_DIR)/external_libs/jsmn -name '*.c')
29IOT_SRC_FILES += $(shell find $(PLATFORM_DIR)/ -name '*.c')
30IOT_SRC_FILES += $(shell find $(PLATFORM_COMMON_DIR)/ -name '*.c')
31
32#TLS - mbedtls
33MBEDTLS_DIR = $(IOT_CLIENT_DIR)/external_libs/mbedTLS
34TLS_LIB_DIR = $(MBEDTLS_DIR)/library
35TLS_INCLUDE_DIR = -I $(MBEDTLS_DIR)/include
36EXTERNAL_LIBS += -L$(TLS_LIB_DIR)
37LD_FLAG += -Wl,-rpath,$(TLS_LIB_DIR)
38LD_FLAG += -ldl $(TLS_LIB_DIR)/libmbedtls.a $(TLS_LIB_DIR)/libmbedcrypto.a $(TLS_LIB_DIR)/libmbedx509.a -lpthread
39
40#Aggregate all include and src directories
41INCLUDE_ALL_DIRS += $(IOT_INCLUDE_DIRS)
42INCLUDE_ALL_DIRS += $(TLS_INCLUDE_DIR)
43INCLUDE_ALL_DIRS += $(APP_INCLUDE_DIRS)
44
45SRC_FILES += $(APP_SRC_FILES)
46SRC_FILES += $(IOT_SRC_FILES)
47
48# Logging level control
49LOG_FLAGS += -DENABLE_IOT_DEBUG
50LOG_FLAGS += -DENABLE_IOT_INFO
51LOG_FLAGS += -DENABLE_IOT_WARN
52LOG_FLAGS += -DENABLE_IOT_ERROR
53
54COMPILER_FLAGS += $(LOG_FLAGS)
55#If the processor is big endian uncomment the compiler flag
56#COMPILER_FLAGS += -DREVERSED
57
58MBED_TLS_MAKE_CMD = $(MAKE) -C $(MBEDTLS_DIR)
59
60PRE_MAKE_CMD = $(MBED_TLS_MAKE_CMD)
61MAKE_CMD = $(CC) $(SRC_FILES) $(COMPILER_FLAGS) -o $(APP_NAME) $(LD_FLAG) $(EXTERNAL_LIBS) $(INCLUDE_ALL_DIRS)
62
63all:
64 $(PRE_MAKE_CMD)
65 $(DEBUG)$(MAKE_CMD)
66 $(POST_MAKE_CMD)
67
68clean:
69 rm -f $(APP_DIR)/$(APP_NAME)
70 $(MBED_TLS_MAKE_CMD) clean
diff --git a/samples/linux/jobs_sample/aws_iot_config.h b/samples/linux/jobs_sample/aws_iot_config.h
new file mode 100644
index 0000000..8bd8a7d
--- /dev/null
+++ b/samples/linux/jobs_sample/aws_iot_config.h
@@ -0,0 +1,73 @@
1/*
2 * Copyright 2010-2018 Amazon.com, Inc. or its affiliates. All Rights Reserved.
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License").
5 * You may not use this file except in compliance with the License.
6 * A copy of the License is located at
7 *
8 * http://aws.amazon.com/apache2.0
9 *
10 * or in the "license" file accompanying this file. This file is distributed
11 * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
12 * express or implied. See the License for the specific language governing
13 * permissions and limitations under the License.
14 */
15
16/**
17 * @file aws_iot_config.h
18 * @brief AWS IoT specific configuration file
19 */
20
21#ifndef SRC_JOBS_IOT_JOB_CONFIG_H_
22#define SRC_JOBS_IOT_JOB_CONFIG_H_
23
24// Get from console
25// =================================================
26#define AWS_IOT_MQTT_HOST "" ///< Customer specific MQTT HOST. The same will be used for Thing Shadow
27#define AWS_IOT_MQTT_PORT 8883 ///< default port for MQTT/S
28#define AWS_IOT_MQTT_CLIENT_ID "c-sdk-client-id" ///< MQTT client ID should be unique for every device
29#define AWS_IOT_MY_THING_NAME "AWS-IoT-C-SDK" ///< Thing Name of the Shadow this device is associated with
30#define AWS_IOT_ROOT_CA_FILENAME "rootCA.crt" ///< Root CA file name
31#define AWS_IOT_CERTIFICATE_FILENAME "cert.pem" ///< device signed certificate file name
32#define AWS_IOT_PRIVATE_KEY_FILENAME "privkey.pem" ///< Device private key filename
33
34// MQTT PubSub
35#ifndef DISABLE_IOT_JOBS
36#define AWS_IOT_MQTT_RX_BUF_LEN 512 ///< Any message that comes into the device should be less than this buffer size. If a received message is bigger than this buffer size the message will be dropped.
37#else
38#define AWS_IOT_MQTT_RX_BUF_LEN 2048
39#endif
40#define AWS_IOT_MQTT_TX_BUF_LEN 512 ///< Any time a message is sent out through the MQTT layer. The message is copied into this buffer anytime a publish is done. This will also be used in the case of Thing Shadow
41#define AWS_IOT_MQTT_NUM_SUBSCRIBE_HANDLERS 5 ///< Maximum number of topic filters the MQTT client can handle at any given time. This should be increased appropriately when using Thing Shadow
42
43// Shadow and Job common configs
44#define MAX_SIZE_OF_UNIQUE_CLIENT_ID_BYTES 80 ///< Maximum size of the Unique Client Id. For More info on the Client Id refer \ref response "Acknowledgments"
45#define MAX_SIZE_CLIENT_ID_WITH_SEQUENCE MAX_SIZE_OF_UNIQUE_CLIENT_ID_BYTES + 10 ///< This is size of the extra sequence number that will be appended to the Unique client Id
46#define MAX_SIZE_CLIENT_TOKEN_CLIENT_SEQUENCE MAX_SIZE_CLIENT_ID_WITH_SEQUENCE + 20 ///< This is size of the the total clientToken key and value pair in the JSON
47#define MAX_SIZE_OF_THING_NAME 30 ///< The Thing Name should not be bigger than this value. Modify this if the Thing Name needs to be bigger
48
49// Thing Shadow specific configs
50#define SHADOW_MAX_SIZE_OF_RX_BUFFER 512 ///< Maximum size of the SHADOW buffer to store the received Shadow message
51#define MAX_ACKS_TO_COMEIN_AT_ANY_GIVEN_TIME 10 ///< At Any given time we will wait for this many responses. This will correlate to the rate at which the shadow actions are requested
52#define MAX_THINGNAME_HANDLED_AT_ANY_GIVEN_TIME 10 ///< We could perform shadow action on any thing Name and this is maximum Thing Names we can act on at any given time
53#define MAX_JSON_TOKEN_EXPECTED 120 ///< These are the max tokens that is expected to be in the Shadow JSON document. Include the metadata that gets published
54#define MAX_SHADOW_TOPIC_LENGTH_WITHOUT_THINGNAME 60 ///< All shadow actions have to be published or subscribed to a topic which is of the format $aws/things/{thingName}/shadow/update/accepted. This refers to the size of the topic without the Thing Name
55#define MAX_SHADOW_TOPIC_LENGTH_BYTES MAX_SHADOW_TOPIC_LENGTH_WITHOUT_THINGNAME + MAX_SIZE_OF_THING_NAME ///< This size includes the length of topic with Thing Name
56
57// Job specific configs
58#ifndef DISABLE_IOT_JOBS
59#define MAX_SIZE_OF_JOB_ID 64
60#define MAX_JOB_JSON_TOKEN_EXPECTED 120
61#define MAX_SIZE_OF_JOB_REQUEST AWS_IOT_MQTT_TX_BUF_LEN
62
63#define MAX_JOB_TOPIC_LENGTH_WITHOUT_JOB_ID_OR_THING_NAME 40
64#define MAX_JOB_TOPIC_LENGTH_BYTES MAX_JOB_TOPIC_LENGTH_WITHOUT_JOB_ID_OR_THING_NAME + MAX_SIZE_OF_THING_NAME + MAX_SIZE_OF_JOB_ID + 2
65#endif
66
67// Auto Reconnect specific config
68#define AWS_IOT_MQTT_MIN_RECONNECT_WAIT_INTERVAL 1000 ///< Minimum time before the First reconnect attempt is made as part of the exponential back-off algorithm
69#define AWS_IOT_MQTT_MAX_RECONNECT_WAIT_INTERVAL 128000 ///< Maximum time interval after which exponential back-off will stop attempting to reconnect.
70
71#define DISABLE_METRICS false ///< Disable the collection of metrics by setting this to true
72
73#endif /* SRC_JOBS_IOT_JOB_CONFIG_H_ */
diff --git a/samples/linux/jobs_sample/jobs_sample.c b/samples/linux/jobs_sample/jobs_sample.c
new file mode 100644
index 0000000..2f67551
--- /dev/null
+++ b/samples/linux/jobs_sample/jobs_sample.c
@@ -0,0 +1,368 @@
1/*
2 * Copyright 2010-2018 Amazon.com, Inc. or its affiliates. All Rights Reserved.
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License").
5 * You may not use this file except in compliance with the License.
6 * A copy of the License is located at
7 *
8 * http://aws.amazon.com/apache2.0
9 *
10 * or in the "license" file accompanying this file. This file is distributed
11 * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
12 * express or implied. See the License for the specific language governing
13 * permissions and limitations under the License.
14 */
15
16/**
17 *
18 * This example takes the parameters from the aws_iot_config.h file and establishes
19 * a connection to the AWS IoT MQTT Platform. It performs several operations to
20 * demonstrate the basic capabilities of the AWS IoT Jobs platform.
21 *
22 * If all the certs are correct, you should see the list of pending Job Executions
23 * printed out by the iot_get_pending_callback_handler. If there are any existing pending
24 * job executions each will be processed one at a time in the iot_next_job_callback_handler.
25 * After all of the pending jobs have been processed the program will wait for
26 * notifications for new pending jobs and process them one at a time as they come in.
27 *
28 * In the main body you can see how each callback is registered for each corresponding
29 * Jobs topic.
30 *
31 */
32#include <stdio.h>
33#include <stdlib.h>
34#include <ctype.h>
35#include <unistd.h>
36#include <limits.h>
37#include <string.h>
38
39#include "aws_iot_config.h"
40#include "aws_iot_json_utils.h"
41#include "aws_iot_log.h"
42#include "aws_iot_version.h"
43#include "aws_iot_mqtt_client_interface.h"
44#include "aws_iot_jobs_interface.h"
45
46/**
47 * @brief Default cert location
48 */
49char certDirectory[PATH_MAX + 1] = "../../../certs";
50
51/**
52 * @brief Default MQTT HOST URL is pulled from the aws_iot_config.h
53 */
54char HostAddress[255] = AWS_IOT_MQTT_HOST;
55
56/**
57 * @brief Default MQTT port is pulled from the aws_iot_config.h
58 */
59uint32_t port = AWS_IOT_MQTT_PORT;
60
61static jsmn_parser jsonParser;
62static jsmntok_t jsonTokenStruct[MAX_JSON_TOKEN_EXPECTED];
63static int32_t tokenCount;
64
65void iot_get_pending_callback_handler(AWS_IoT_Client *pClient, char *topicName, uint16_t topicNameLen,
66 IoT_Publish_Message_Params *params, void *pData) {
67 IOT_UNUSED(pData);
68 IOT_UNUSED(pClient);
69 IOT_INFO("\nJOB_GET_PENDING_TOPIC callback");
70 IOT_INFO("topic: %.*s", topicNameLen, topicName);
71 IOT_INFO("payload: %.*s", (int) params->payloadLen, (char *)params->payload);
72
73 jsmn_init(&jsonParser);
74
75 tokenCount = jsmn_parse(&jsonParser, params->payload, (int) params->payloadLen, jsonTokenStruct, MAX_JSON_TOKEN_EXPECTED);
76
77 if(tokenCount < 0) {
78 IOT_WARN("Failed to parse JSON: %d", tokenCount);
79 return;
80 }
81
82 /* Assume the top-level element is an object */
83 if(tokenCount < 1 || jsonTokenStruct[0].type != JSMN_OBJECT) {
84 IOT_WARN("Top Level is not an object");
85 return;
86 }
87
88 jsmntok_t *jobs;
89
90 jobs = findToken("inProgressJobs", params->payload, jsonTokenStruct);
91
92 if (jobs) {
93 IOT_INFO("inProgressJobs: %.*s", jobs->end - jobs->start, (char *)params->payload + jobs->start);
94 }
95
96 jobs = findToken("queuedJobs", params->payload, jsonTokenStruct);
97
98 if (jobs) {
99 IOT_INFO("queuedJobs: %.*s", jobs->end - jobs->start, (char *)params->payload + jobs->start);
100 }
101}
102
103void iot_next_job_callback_handler(AWS_IoT_Client *pClient, char *topicName, uint16_t topicNameLen,
104 IoT_Publish_Message_Params *params, void *pData) {
105 char topicToPublishUpdate[MAX_JOB_TOPIC_LENGTH_BYTES];
106 char messageBuffer[200];
107
108 IOT_UNUSED(pData);
109 IOT_UNUSED(pClient);
110 IOT_INFO("\nJOB_NOTIFY_NEXT_TOPIC / JOB_DESCRIBE_TOPIC($next) callback");
111 IOT_INFO("topic: %.*s", topicNameLen, topicName);
112 IOT_INFO("payload: %.*s", (int) params->payloadLen, (char *)params->payload);
113
114 jsmn_init(&jsonParser);
115
116 tokenCount = jsmn_parse(&jsonParser, params->payload, (int) params->payloadLen, jsonTokenStruct, MAX_JSON_TOKEN_EXPECTED);
117
118 if(tokenCount < 0) {
119 IOT_WARN("Failed to parse JSON: %d", tokenCount);
120 return;
121 }
122
123 /* Assume the top-level element is an object */
124 if(tokenCount < 1 || jsonTokenStruct[0].type != JSMN_OBJECT) {
125 IOT_WARN("Top Level is not an object");
126 return;
127 }
128
129 jsmntok_t *tokExecution;
130
131 tokExecution = findToken("execution", params->payload, jsonTokenStruct);
132
133 if (tokExecution) {
134 IOT_INFO("execution: %.*s", tokExecution->end - tokExecution->start, (char *)params->payload + tokExecution->start);
135
136 jsmntok_t *tok;
137
138 tok = findToken("jobId", params->payload, tokExecution);
139
140 if (tok) {
141 IoT_Error_t rc;
142 char jobId[MAX_SIZE_OF_JOB_ID + 1];
143 AwsIotJobExecutionUpdateRequest updateRequest;
144
145 rc = parseStringValue(jobId, params->payload, tok);
146 if(SUCCESS != rc) {
147 IOT_ERROR("parseStringValue returned error : %d ", rc);
148 return;
149 }
150
151 IOT_INFO("jobId: %s", jobId);
152
153 tok = findToken("jobDocument", params->payload, tokExecution);
154
155 /*
156 * Do your job processing here.
157 */
158
159 if (tok) {
160 IOT_INFO("jobDocument: %.*s", tok->end - tok->start, (char *)params->payload + tok->start);
161 /* Alternatively if the job still has more steps the status can be set to JOB_EXECUTION_IN_PROGRESS instead */
162 updateRequest.status = JOB_EXECUTION_SUCCEEDED;
163 updateRequest.statusDetails = "{\"exampleDetail\":\"a value appropriate for your successful job\"}";
164 } else {
165 updateRequest.status = JOB_EXECUTION_FAILED;
166 updateRequest.statusDetails = "{\"failureDetail\":\"Unable to process job document\"}";
167 }
168
169 updateRequest.expectedVersion = 0;
170 updateRequest.executionNumber = 0;
171 updateRequest.includeJobExecutionState = false;
172 updateRequest.includeJobDocument = false;
173 updateRequest.clientToken = NULL;
174
175 rc = aws_iot_jobs_send_update(pClient, QOS0, AWS_IOT_MY_THING_NAME, jobId, &updateRequest,
176 topicToPublishUpdate, sizeof(topicToPublishUpdate), messageBuffer, sizeof(messageBuffer));
177 }
178 } else {
179 IOT_INFO("execution property not found, nothing to do");
180 }
181}
182
183void iot_update_accepted_callback_handler(AWS_IoT_Client *pClient, char *topicName, uint16_t topicNameLen,
184 IoT_Publish_Message_Params *params, void *pData) {
185 IOT_UNUSED(pData);
186 IOT_UNUSED(pClient);
187 IOT_INFO("\nJOB_UPDATE_TOPIC / accepted callback");
188 IOT_INFO("topic: %.*s", topicNameLen, topicName);
189 IOT_INFO("payload: %.*s", (int) params->payloadLen, (char *)params->payload);
190}
191
192void iot_update_rejected_callback_handler(AWS_IoT_Client *pClient, char *topicName, uint16_t topicNameLen,
193 IoT_Publish_Message_Params *params, void *pData) {
194 IOT_UNUSED(pData);
195 IOT_UNUSED(pClient);
196 IOT_INFO("\nJOB_UPDATE_TOPIC / rejected callback");
197 IOT_INFO("topic: %.*s", topicNameLen, topicName);
198 IOT_INFO("payload: %.*s", (int) params->payloadLen, (char *)params->payload);
199
200 /* Do error handling here for when the update was rejected */
201}
202
203void disconnectCallbackHandler(AWS_IoT_Client *pClient, void *data) {
204 IOT_WARN("MQTT Disconnect");
205 IoT_Error_t rc = FAILURE;
206
207 if(NULL == pClient) {
208 return;
209 }
210
211 IOT_UNUSED(data);
212
213 if(aws_iot_is_autoreconnect_enabled(pClient)) {
214 IOT_INFO("Auto Reconnect is enabled, Reconnecting attempt will start now");
215 } else {
216 IOT_WARN("Auto Reconnect not enabled. Starting manual reconnect...");
217 rc = aws_iot_mqtt_attempt_reconnect(pClient);
218 if(NETWORK_RECONNECTED == rc) {
219 IOT_WARN("Manual Reconnect Successful");
220 } else {
221 IOT_WARN("Manual Reconnect Failed - %d", rc);
222 }
223 }
224}
225
226int main(int argc, char **argv) {
227 char rootCA[PATH_MAX + 1];
228 char clientCRT[PATH_MAX + 1];
229 char clientKey[PATH_MAX + 1];
230 char CurrentWD[PATH_MAX + 1];
231 char cPayload[100];
232
233 int32_t i = 0;
234
235 IoT_Error_t rc = FAILURE;
236
237 AWS_IoT_Client client;
238 IoT_Client_Init_Params mqttInitParams = iotClientInitParamsDefault;
239 IoT_Client_Connect_Params connectParams = iotClientConnectParamsDefault;
240
241 IoT_Publish_Message_Params paramsQOS0;
242
243 getcwd(CurrentWD, sizeof(CurrentWD));
244 snprintf(rootCA, PATH_MAX + 1, "%s/%s/%s", CurrentWD, certDirectory, AWS_IOT_ROOT_CA_FILENAME);
245 snprintf(clientCRT, PATH_MAX + 1, "%s/%s/%s", CurrentWD, certDirectory, AWS_IOT_CERTIFICATE_FILENAME);
246 snprintf(clientKey, PATH_MAX + 1, "%s/%s/%s", CurrentWD, certDirectory, AWS_IOT_PRIVATE_KEY_FILENAME);
247
248 IOT_DEBUG("rootCA %s", rootCA);
249 IOT_DEBUG("clientCRT %s", clientCRT);
250 IOT_DEBUG("clientKey %s", clientKey);
251
252 mqttInitParams.enableAutoReconnect = false; // We enable this later below
253 mqttInitParams.pHostURL = HostAddress;
254 mqttInitParams.port = port;
255 mqttInitParams.pRootCALocation = rootCA;
256 mqttInitParams.pDeviceCertLocation = clientCRT;
257 mqttInitParams.pDevicePrivateKeyLocation = clientKey;
258 mqttInitParams.mqttCommandTimeout_ms = 20000;
259 mqttInitParams.tlsHandshakeTimeout_ms = 5000;
260 mqttInitParams.isSSLHostnameVerify = true;
261 mqttInitParams.disconnectHandler = disconnectCallbackHandler;
262 mqttInitParams.disconnectHandlerData = NULL;
263
264 rc = aws_iot_mqtt_init(&client, &mqttInitParams);
265 if(SUCCESS != rc) {
266 IOT_ERROR("aws_iot_mqtt_init returned error : %d ", rc);
267 return rc;
268 }
269
270 connectParams.keepAliveIntervalInSec = 600;
271 connectParams.isCleanSession = true;
272 connectParams.MQTTVersion = MQTT_3_1_1;
273 connectParams.pClientID = AWS_IOT_MQTT_CLIENT_ID;
274 connectParams.clientIDLen = (uint16_t) strlen(AWS_IOT_MQTT_CLIENT_ID);
275 connectParams.isWillMsgPresent = false;
276
277 IOT_INFO("Connecting...");
278 rc = aws_iot_mqtt_connect(&client, &connectParams);
279 if(SUCCESS != rc) {
280 IOT_ERROR("Error(%d) connecting to %s:%d", rc, mqttInitParams.pHostURL, mqttInitParams.port);
281 return rc;
282 }
283 /*
284 * Enable Auto Reconnect functionality. Minimum and Maximum time of Exponential backoff are set in aws_iot_config.h
285 * #AWS_IOT_MQTT_MIN_RECONNECT_WAIT_INTERVAL
286 * #AWS_IOT_MQTT_MAX_RECONNECT_WAIT_INTERVAL
287 */
288 rc = aws_iot_mqtt_autoreconnect_set_status(&client, true);
289 if(SUCCESS != rc) {
290 IOT_ERROR("Unable to set Auto Reconnect to true - %d", rc);
291 return rc;
292 }
293
294 char topicToSubscribeGetPending[MAX_JOB_TOPIC_LENGTH_BYTES];
295 char topicToSubscribeNotifyNext[MAX_JOB_TOPIC_LENGTH_BYTES];
296 char topicToSubscribeGetNext[MAX_JOB_TOPIC_LENGTH_BYTES];
297 char topicToSubscribeUpdateAccepted[MAX_JOB_TOPIC_LENGTH_BYTES];
298 char topicToSubscribeUpdateRejected[MAX_JOB_TOPIC_LENGTH_BYTES];
299
300 char topicToPublishGetPending[MAX_JOB_TOPIC_LENGTH_BYTES];
301 char topicToPublishGetNext[MAX_JOB_TOPIC_LENGTH_BYTES];
302
303 rc = aws_iot_jobs_subscribe_to_job_messages(
304 &client, QOS0, AWS_IOT_MY_THING_NAME, NULL, JOB_GET_PENDING_TOPIC, JOB_WILDCARD_REPLY_TYPE,
305 iot_get_pending_callback_handler, NULL, topicToSubscribeGetPending, sizeof(topicToSubscribeGetPending));
306
307 if(SUCCESS != rc) {
308 IOT_ERROR("Error subscribing JOB_GET_PENDING_TOPIC: %d ", rc);
309 return rc;
310 }
311
312 rc = aws_iot_jobs_subscribe_to_job_messages(
313 &client, QOS0, AWS_IOT_MY_THING_NAME, NULL, JOB_NOTIFY_NEXT_TOPIC, JOB_REQUEST_TYPE,
314 iot_next_job_callback_handler, NULL, topicToSubscribeNotifyNext, sizeof(topicToSubscribeNotifyNext));
315
316 if(SUCCESS != rc) {
317 IOT_ERROR("Error subscribing JOB_NOTIFY_NEXT_TOPIC: %d ", rc);
318 return rc;
319 }
320
321 rc = aws_iot_jobs_subscribe_to_job_messages(
322 &client, QOS0, AWS_IOT_MY_THING_NAME, JOB_ID_NEXT, JOB_DESCRIBE_TOPIC, JOB_WILDCARD_REPLY_TYPE,
323 iot_next_job_callback_handler, NULL, topicToSubscribeGetNext, sizeof(topicToSubscribeGetNext));
324
325 if(SUCCESS != rc) {
326 IOT_ERROR("Error subscribing JOB_DESCRIBE_TOPIC ($next): %d ", rc);
327 return rc;
328 }
329
330 rc = aws_iot_jobs_subscribe_to_job_messages(
331 &client, QOS0, AWS_IOT_MY_THING_NAME, JOB_ID_WILDCARD, JOB_UPDATE_TOPIC, JOB_ACCEPTED_REPLY_TYPE,
332 iot_update_accepted_callback_handler, NULL, topicToSubscribeUpdateAccepted, sizeof(topicToSubscribeUpdateAccepted));
333
334 if(SUCCESS != rc) {
335 IOT_ERROR("Error subscribing JOB_UPDATE_TOPIC/accepted: %d ", rc);
336 return rc;
337 }
338
339 rc = aws_iot_jobs_subscribe_to_job_messages(
340 &client, QOS0, AWS_IOT_MY_THING_NAME, JOB_ID_WILDCARD, JOB_UPDATE_TOPIC, JOB_REJECTED_REPLY_TYPE,
341 iot_update_rejected_callback_handler, NULL, topicToSubscribeUpdateRejected, sizeof(topicToSubscribeUpdateRejected));
342
343 if(SUCCESS != rc) {
344 IOT_ERROR("Error subscribing JOB_UPDATE_TOPIC/rejected: %d ", rc);
345 return rc;
346 }
347
348 paramsQOS0.qos = QOS0;
349 paramsQOS0.payload = (void *) cPayload;
350 paramsQOS0.isRetained = 0;
351 paramsQOS0.payloadLen = strlen(cPayload);
352
353 rc = aws_iot_jobs_send_query(&client, QOS0, AWS_IOT_MY_THING_NAME, NULL, NULL, topicToPublishGetPending, sizeof(topicToPublishGetPending), NULL, 0, JOB_GET_PENDING_TOPIC);
354
355 AwsIotDescribeJobExecutionRequest describeRequest;
356 describeRequest.executionNumber = 0;
357 describeRequest.includeJobDocument = true;
358 describeRequest.clientToken = NULL;
359
360 rc = aws_iot_jobs_describe(&client, QOS0, AWS_IOT_MY_THING_NAME, JOB_ID_NEXT, &describeRequest, topicToPublishGetNext, sizeof(topicToPublishGetNext), NULL, 0);
361
362 while(SUCCESS == rc) {
363 //Max time the yield function will wait for read messages
364 rc = aws_iot_mqtt_yield(&client, 50000);
365 }
366
367 return rc;
368}
diff --git a/src/aws_iot_jobs_interface.c b/src/aws_iot_jobs_interface.c
new file mode 100644
index 0000000..0452d67
--- /dev/null
+++ b/src/aws_iot_jobs_interface.c
@@ -0,0 +1,215 @@
1/*
2* Copyright 2015-2018 Amazon.com, Inc. or its affiliates. All Rights Reserved.
3*
4* Licensed under the Apache License, Version 2.0 (the "License").
5* You may not use this file except in compliance with the License.
6* A copy of the License is located at
7*
8* http://aws.amazon.com/apache2.0
9*
10* or in the "license" file accompanying this file. This file is distributed
11* on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
12* express or implied. See the License for the specific language governing
13* permissions and limitations under the License.
14*/
15
16#include "aws_iot_jobs_interface.h"
17#include "aws_iot_log.h"
18#include "aws_iot_jobs_json.h"
19#include <string.h>
20
21#ifdef __cplusplus
22extern "C" {
23#endif
24
25#define CHECK_GENERATE_STRING_RESULT(result, bufferSize) \
26 if (result < 0) { \
27 return FAILURE; \
28 } else if ((unsigned) result >= bufferSize) { \
29 return LIMIT_EXCEEDED_ERROR; \
30 }
31
32
33IoT_Error_t aws_iot_jobs_subscribe_to_job_messages(
34 AWS_IoT_Client *pClient, QoS qos,
35 const char *thingName,
36 const char *jobId,
37 AwsIotJobExecutionTopicType topicType,
38 AwsIotJobExecutionTopicReplyType replyType,
39 pApplicationHandler_t pApplicationHandler,
40 void *pApplicationHandlerData,
41 char *topicBuffer,
42 uint16_t topicBufferSize)
43{
44 int requiredSize = aws_iot_jobs_get_api_topic(topicBuffer, topicBufferSize, topicType, replyType, thingName, jobId);
45 CHECK_GENERATE_STRING_RESULT(requiredSize, topicBufferSize);
46
47 return aws_iot_mqtt_subscribe(pClient, topicBuffer, (uint16_t)strlen(topicBuffer), qos, pApplicationHandler, pApplicationHandlerData);
48}
49
50IoT_Error_t aws_iot_jobs_subscribe_to_all_job_messages(
51 AWS_IoT_Client *pClient, QoS qos,
52 const char *thingName,
53 pApplicationHandler_t pApplicationHandler,
54 void *pApplicationHandlerData,
55 char *topicBuffer,
56 uint16_t topicBufferSize)
57{
58 return aws_iot_jobs_subscribe_to_job_messages(pClient, qos, thingName, NULL, JOB_WILDCARD_TOPIC, JOB_WILDCARD_REPLY_TYPE,
59 pApplicationHandler, pApplicationHandlerData, topicBuffer, topicBufferSize);
60}
61
62IoT_Error_t aws_iot_jobs_unsubscribe_from_job_messages(
63 AWS_IoT_Client *pClient,
64 char *topicBuffer)
65{
66 return aws_iot_mqtt_unsubscribe(pClient, topicBuffer, (uint16_t)strlen(topicBuffer));
67}
68
69IoT_Error_t aws_iot_jobs_send_query(
70 AWS_IoT_Client *pClient, QoS qos,
71 const char *thingName,
72 const char *jobId,
73 const char *clientToken,
74 char *topicBuffer,
75 uint16_t topicBufferSize,
76 char *messageBuffer,
77 size_t messageBufferSize,
78 AwsIotJobExecutionTopicType topicType)
79{
80 if (thingName == NULL || topicBuffer == NULL || (clientToken != NULL && messageBuffer == NULL)) {
81 return NULL_VALUE_ERROR;
82 }
83
84 int neededSize = aws_iot_jobs_get_api_topic(topicBuffer, topicBufferSize, topicType, JOB_REQUEST_TYPE, thingName, jobId);
85 CHECK_GENERATE_STRING_RESULT(neededSize, topicBufferSize);
86 uint16_t topicSize = (uint16_t) neededSize;
87
88 char emptyBuffer[1];
89 size_t messageLength;
90 if (clientToken == NULL) {
91 messageLength = 0;
92 messageBuffer = emptyBuffer;
93 } else {
94 int serializeResult = aws_iot_jobs_json_serialize_client_token_only_request(messageBuffer, messageBufferSize, clientToken);
95 CHECK_GENERATE_STRING_RESULT(serializeResult, messageBufferSize);
96 messageLength = (size_t)serializeResult;
97 }
98
99 IoT_Publish_Message_Params publishParams;
100 publishParams.qos = qos;
101 publishParams.isRetained = 0;
102 publishParams.isDup = 0;
103 publishParams.id = 0;
104 publishParams.payload = messageBuffer;
105 publishParams.payloadLen = messageLength;
106
107 return aws_iot_mqtt_publish(pClient, topicBuffer, topicSize, &publishParams);
108}
109
110IoT_Error_t aws_iot_jobs_start_next(
111 AWS_IoT_Client *pClient, QoS qos,
112 const char *thingName,
113 const AwsIotStartNextPendingJobExecutionRequest *startNextRequest,
114 char *topicBuffer,
115 uint16_t topicBufferSize,
116 char *messageBuffer,
117 size_t messageBufferSize)
118{
119 if (thingName == NULL || topicBuffer == NULL || startNextRequest == NULL) {
120 return NULL_VALUE_ERROR;
121 }
122
123 int neededSize = aws_iot_jobs_get_api_topic(topicBuffer, topicBufferSize, JOB_START_NEXT_TOPIC, JOB_REQUEST_TYPE, thingName, NULL);
124 CHECK_GENERATE_STRING_RESULT(neededSize, topicBufferSize);
125 uint16_t topicSize = (uint16_t) neededSize;
126
127 int serializeResult = aws_iot_jobs_json_serialize_start_next_job_execution_request(messageBuffer, messageBufferSize, startNextRequest);
128 CHECK_GENERATE_STRING_RESULT(serializeResult, messageBufferSize);
129
130 IoT_Publish_Message_Params publishParams;
131 publishParams.qos = qos;
132 publishParams.isRetained = 0;
133 publishParams.isDup = 0;
134 publishParams.id = 0;
135 publishParams.payload = messageBuffer;
136 publishParams.payloadLen = (size_t) serializeResult;
137
138 return aws_iot_mqtt_publish(pClient, topicBuffer, topicSize, &publishParams);
139}
140
141IoT_Error_t aws_iot_jobs_describe(
142 AWS_IoT_Client *pClient, QoS qos,
143 const char *thingName,
144 const char *jobId,
145 const AwsIotDescribeJobExecutionRequest *describeRequest,
146 char *topicBuffer,
147 uint16_t topicBufferSize,
148 char *messageBuffer,
149 size_t messageBufferSize)
150{
151 if (thingName == NULL || topicBuffer == NULL || describeRequest == NULL) {
152 return NULL_VALUE_ERROR;
153 }
154
155 int neededSize = aws_iot_jobs_get_api_topic(topicBuffer, topicBufferSize, JOB_DESCRIBE_TOPIC, JOB_REQUEST_TYPE, thingName, jobId);
156 CHECK_GENERATE_STRING_RESULT(neededSize, topicBufferSize);
157 uint16_t topicSize = (uint16_t) neededSize;
158
159 char emptyBuffer[1];
160 size_t messageLength;
161 if (messageBuffer == NULL) {
162 messageLength = 0;
163 messageBuffer = emptyBuffer;
164 } else {
165 int serializeResult = aws_iot_jobs_json_serialize_describe_job_execution_request(messageBuffer, messageBufferSize, describeRequest);
166 CHECK_GENERATE_STRING_RESULT(serializeResult, messageBufferSize);
167 messageLength = (size_t) serializeResult;
168 }
169
170 IoT_Publish_Message_Params publishParams;
171 publishParams.qos = qos;
172 publishParams.isRetained = 0;
173 publishParams.isDup = 0;
174 publishParams.id = 0;
175 publishParams.payload = messageBuffer;
176 publishParams.payloadLen = messageLength;
177
178 return aws_iot_mqtt_publish(pClient, topicBuffer, topicSize, &publishParams);
179}
180
181IoT_Error_t aws_iot_jobs_send_update(
182 AWS_IoT_Client *pClient, QoS qos,
183 const char *thingName,
184 const char *jobId,
185 const AwsIotJobExecutionUpdateRequest *updateRequest,
186 char *topicBuffer,
187 uint16_t topicBufferSize,
188 char *messageBuffer,
189 size_t messageBufferSize)
190{
191 if (thingName == NULL || topicBuffer == NULL || jobId == NULL || updateRequest == NULL) {
192 return NULL_VALUE_ERROR;
193 }
194
195 int neededSize = aws_iot_jobs_get_api_topic(topicBuffer, topicBufferSize, JOB_UPDATE_TOPIC, JOB_REQUEST_TYPE, thingName, jobId);
196 CHECK_GENERATE_STRING_RESULT(neededSize, topicBufferSize);
197 uint16_t topicSize = (uint16_t) neededSize;
198
199 int serializeResult = aws_iot_jobs_json_serialize_update_job_execution_request(messageBuffer, messageBufferSize, updateRequest);
200 CHECK_GENERATE_STRING_RESULT(serializeResult, messageBufferSize);
201
202 IoT_Publish_Message_Params publishParams;
203 publishParams.qos = qos;
204 publishParams.isRetained = 0;
205 publishParams.isDup = 0;
206 publishParams.id = 0;
207 publishParams.payload = messageBuffer;
208 publishParams.payloadLen = (size_t) serializeResult;
209
210 return aws_iot_mqtt_publish(pClient, topicBuffer, topicSize, &publishParams);
211}
212
213#ifdef __cplusplus
214}
215#endif
diff --git a/src/aws_iot_jobs_json.c b/src/aws_iot_jobs_json.c
new file mode 100644
index 0000000..eaaed75
--- /dev/null
+++ b/src/aws_iot_jobs_json.c
@@ -0,0 +1,198 @@
1/*
2* Copyright 2015-2018 Amazon.com, Inc. or its affiliates. All Rights Reserved.
3*
4* Licensed under the Apache License, Version 2.0 (the "License").
5* You may not use this file except in compliance with the License.
6* A copy of the License is located at
7*
8* http://aws.amazon.com/apache2.0
9*
10* or in the "license" file accompanying this file. This file is distributed
11* on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
12* express or implied. See the License for the specific language governing
13* permissions and limitations under the License.
14*/
15
16#ifdef __cplusplus
17extern "C" {
18#endif
19
20#define __STDC_FORMAT_MACROS
21#include <string.h>
22#include <stdbool.h>
23#include <stdlib.h>
24#include <stddef.h>
25#include <stdio.h>
26#include <stdarg.h>
27#include <inttypes.h>
28
29#include "jsmn.h"
30#include "aws_iot_jobs_json.h"
31
32struct _SerializeState {
33 int totalSize;
34 char *nextPtr;
35 size_t remaingSize;
36};
37
38static void _printToBuffer(struct _SerializeState *state, const char *fmt, ...) {
39 if (state->totalSize == -1) return;
40
41 va_list vl;
42 va_start(vl, fmt);
43 int len = vsnprintf(state->nextPtr, state->remaingSize, fmt, vl);
44 if (len < 0) {
45 state->totalSize = -1;
46 } else {
47 state->totalSize += len;
48 if (state->nextPtr != NULL) {
49 if (state->remaingSize > (size_t) len) {
50 state->remaingSize -= (size_t) len;
51 state->nextPtr += len;
52 } else {
53 state->remaingSize = 0;
54 state->nextPtr = NULL;
55 }
56 }
57 }
58 va_end(vl);
59}
60
61static void _printKey(struct _SerializeState *state, bool first, const char *key) {
62 if (first) {
63 _printToBuffer(state, "{\"%s\":", key);
64 } else {
65 _printToBuffer(state, ",\"%s\":", key);
66 }
67}
68
69static void _printStringValue(struct _SerializeState *state, const char *value) {
70 if (value == NULL) {
71 _printToBuffer(state, "null");
72 } else {
73 _printToBuffer(state, "\"%s\"", value);
74 }
75}
76
77static void _printLongValue(struct _SerializeState *state, int64_t value) {
78 _printToBuffer(state, "%" PRId64, value);
79}
80
81static void _printBooleanValue(struct _SerializeState *state, bool value) {
82 if(value) {
83 _printToBuffer(state, "true");
84 } else {
85 _printToBuffer(state, "false");
86 }
87}
88
89int aws_iot_jobs_json_serialize_update_job_execution_request(
90 char *requestBuffer, size_t bufferSize,
91 const AwsIotJobExecutionUpdateRequest *request)
92{
93 const char *statusStr = aws_iot_jobs_map_status_to_string(request->status);
94 if (statusStr == NULL) return -1;
95 if (requestBuffer == NULL) bufferSize = 0;
96
97 struct _SerializeState state = { 0, requestBuffer, bufferSize };
98 _printKey(&state, true, "status");
99 _printStringValue(&state, statusStr);
100 if (request->statusDetails != NULL) {
101 _printKey(&state, false, "statusDetails");
102 _printToBuffer(&state, "%s", request->statusDetails);
103 }
104 if (request->executionNumber != 0) {
105 _printKey(&state, false, "executionNumber");
106 _printLongValue(&state, request->executionNumber);
107 }
108 if (request->expectedVersion != 0) {
109 _printKey(&state, false, "expectedVersion");
110 _printLongValue(&state, request->expectedVersion);
111 }
112 if (request->includeJobExecutionState) {
113 _printKey(&state, false, "includeJobExecutionState");
114 _printBooleanValue(&state, request->includeJobExecutionState);
115 }
116 if (request->includeJobDocument) {
117 _printKey(&state, false, "includeJobDocument");
118 _printBooleanValue(&state, request->includeJobDocument);
119 }
120 if (request->clientToken != NULL) {
121 _printKey(&state, false, "clientToken");
122 _printStringValue(&state, request->clientToken);
123 }
124
125 _printToBuffer(&state, "}");
126
127 return state.totalSize;
128}
129
130int aws_iot_jobs_json_serialize_client_token_only_request(
131 char *requestBuffer, size_t bufferSize,
132 const char *clientToken)
133{
134 struct _SerializeState state = { 0, requestBuffer, bufferSize };
135 _printKey(&state, true, "clientToken");
136 _printStringValue(&state, clientToken);
137 _printToBuffer(&state, "}");
138
139 return state.totalSize;
140}
141
142int aws_iot_jobs_json_serialize_describe_job_execution_request(
143 char *requestBuffer, size_t bufferSize,
144 const AwsIotDescribeJobExecutionRequest *request)
145{
146 bool first = true;
147
148 if (requestBuffer == NULL) return 0;
149
150 struct _SerializeState state = { 0, requestBuffer, bufferSize };
151 if (request->clientToken != NULL) {
152 _printKey(&state, first, "clientToken");
153 _printStringValue(&state, request->clientToken);
154 first = false;
155 }
156 if (request->executionNumber != 0) {
157 _printKey(&state, first, "executionNumber");
158 _printLongValue(&state, request->executionNumber);
159 first = false;
160 }
161 if (request->includeJobDocument) {
162 _printKey(&state, first, "includeJobDocument");
163 _printBooleanValue(&state, request->includeJobDocument);
164 }
165
166 _printToBuffer(&state, "}");
167
168 return state.totalSize;
169}
170
171int aws_iot_jobs_json_serialize_start_next_job_execution_request(
172 char *requestBuffer, size_t bufferSize,
173 const AwsIotStartNextPendingJobExecutionRequest *request)
174{
175 if (requestBuffer == NULL) bufferSize = 0;
176 struct _SerializeState state = { 0, requestBuffer, bufferSize };
177 if (request->statusDetails != NULL) {
178 _printKey(&state, true, "statusDetails");
179 _printToBuffer(&state, "%s", request->statusDetails);
180 }
181 if (request->clientToken != NULL) {
182 if(request->statusDetails != NULL) {
183 _printKey(&state, false, "clientToken");
184 } else {
185 _printKey(&state, true, "clientToken");
186 }
187 _printStringValue(&state, request->clientToken);
188 }
189 if (request->clientToken == NULL && request->statusDetails == NULL) {
190 _printToBuffer(&state, "{");
191 }
192 _printToBuffer(&state, "}");
193 return state.totalSize;
194}
195
196#ifdef __cplusplus
197}
198#endif
diff --git a/src/aws_iot_jobs_topics.c b/src/aws_iot_jobs_topics.c
new file mode 100644
index 0000000..39c5cba
--- /dev/null
+++ b/src/aws_iot_jobs_topics.c
@@ -0,0 +1,129 @@
1/*
2* Copyright 2015-2018 Amazon.com, Inc. or its affiliates. All Rights Reserved.
3*
4* Licensed under the Apache License, Version 2.0 (the "License").
5* You may not use this file except in compliance with the License.
6* A copy of the License is located at
7*
8* http://aws.amazon.com/apache2.0
9*
10* or in the "license" file accompanying this file. This file is distributed
11* on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
12* express or implied. See the License for the specific language governing
13* permissions and limitations under the License.
14*/
15
16#ifdef __cplusplus
17extern "C" {
18#endif
19
20#include "aws_iot_jobs_topics.h"
21#include <string.h>
22#include <stdio.h>
23#include <stdbool.h>
24
25#define BASE_THINGS_TOPIC "$aws/things/"
26
27#define NOTIFY_OPERATION "notify"
28#define NOTIFY_NEXT_OPERATION "notify-next"
29#define GET_OPERATION "get"
30#define START_NEXT_OPERATION "start-next"
31#define WILDCARD_OPERATION "+"
32#define UPDATE_OPERATION "update"
33#define ACCEPTED_REPLY "accepted"
34#define REJECTED_REPLY "rejected"
35#define WILDCARD_REPLY "+"
36
37static const char *_get_operation_for_base_topic(AwsIotJobExecutionTopicType topicType) {
38 switch (topicType) {
39 case JOB_UPDATE_TOPIC:
40 return UPDATE_OPERATION;
41 case JOB_NOTIFY_TOPIC:
42 return NOTIFY_OPERATION;
43 case JOB_NOTIFY_NEXT_TOPIC:
44 return NOTIFY_NEXT_OPERATION;
45 case JOB_GET_PENDING_TOPIC:
46 case JOB_DESCRIBE_TOPIC:
47 return GET_OPERATION;
48 case JOB_START_NEXT_TOPIC:
49 return START_NEXT_OPERATION;
50 case JOB_WILDCARD_TOPIC:
51 return WILDCARD_OPERATION;
52 case JOB_UNRECOGNIZED_TOPIC:
53 default:
54 return NULL;
55 }
56}
57
58static bool _base_topic_requires_job_id(AwsIotJobExecutionTopicType topicType) {
59 switch (topicType) {
60 case JOB_UPDATE_TOPIC:
61 case JOB_DESCRIBE_TOPIC:
62 return true;
63 case JOB_NOTIFY_TOPIC:
64 case JOB_NOTIFY_NEXT_TOPIC:
65 case JOB_START_NEXT_TOPIC:
66 case JOB_GET_PENDING_TOPIC:
67 case JOB_WILDCARD_TOPIC:
68 case JOB_UNRECOGNIZED_TOPIC:
69 default:
70 return false;
71 }
72}
73
74static const char *_get_suffix_for_topic_type(AwsIotJobExecutionTopicReplyType replyType) {
75 switch (replyType) {
76 case JOB_REQUEST_TYPE:
77 return "";
78 break;
79 case JOB_ACCEPTED_REPLY_TYPE:
80 return "/" ACCEPTED_REPLY;
81 break;
82 case JOB_REJECTED_REPLY_TYPE:
83 return "/" REJECTED_REPLY;
84 break;
85 case JOB_WILDCARD_REPLY_TYPE:
86 return "/" WILDCARD_REPLY;
87 break;
88 case JOB_UNRECOGNIZED_TOPIC_TYPE:
89 default:
90 return NULL;
91 }
92}
93
94int aws_iot_jobs_get_api_topic(char *buffer, size_t bufferSize,
95 AwsIotJobExecutionTopicType topicType, AwsIotJobExecutionTopicReplyType replyType,
96 const char* thingName, const char* jobId)
97{
98 if (thingName == NULL) {
99 return -1;
100 }
101
102 if ((topicType == JOB_NOTIFY_TOPIC || topicType == JOB_NOTIFY_NEXT_TOPIC) && replyType != JOB_REQUEST_TYPE) {
103 return -1;
104 }
105
106 bool requireJobId = _base_topic_requires_job_id(topicType);
107 if (jobId == NULL && requireJobId) {
108 return -1;
109 }
110
111 const char *operation = _get_operation_for_base_topic(topicType);
112 if (operation == NULL) {
113 return -1;
114 }
115
116 const char *suffix = _get_suffix_for_topic_type(replyType);
117
118 if (requireJobId || (topicType == JOB_WILDCARD_TOPIC && jobId != NULL)) {
119 return snprintf(buffer, bufferSize, BASE_THINGS_TOPIC "%s/jobs/%s/%s%s", thingName, jobId, operation, suffix);
120 } else if (topicType == JOB_WILDCARD_TOPIC) {
121 return snprintf(buffer, bufferSize, BASE_THINGS_TOPIC "%s/jobs/#", thingName);
122 } else {
123 return snprintf(buffer, bufferSize, BASE_THINGS_TOPIC "%s/jobs/%s%s", thingName, operation, suffix);
124 }
125}
126
127#ifdef __cplusplus
128}
129#endif
diff --git a/src/aws_iot_jobs_types.c b/src/aws_iot_jobs_types.c
new file mode 100644
index 0000000..9dcbf50
--- /dev/null
+++ b/src/aws_iot_jobs_types.c
@@ -0,0 +1,73 @@
1/*
2* Copyright 2015-2018 Amazon.com, Inc. or its affiliates. All Rights Reserved.
3*
4* Licensed under the Apache License, Version 2.0 (the "License").
5* You may not use this file except in compliance with the License.
6* A copy of the License is located at
7*
8* http://aws.amazon.com/apache2.0
9*
10* or in the "license" file accompanying this file. This file is distributed
11* on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
12* express or implied. See the License for the specific language governing
13* permissions and limitations under the License.
14*/
15
16#ifdef __cplusplus
17extern "C" {
18#endif
19
20#include <string.h>
21#include "aws_iot_jobs_types.h"
22
23const char *JOB_EXECUTION_QUEUED_STR = "QUEUED";
24const char *JOB_EXECUTION_IN_PROGRESS_STR = "IN_PROGRESS";
25const char *JOB_EXECUTION_FAILED_STR = "FAILED";
26const char *JOB_EXECUTION_SUCCEEDED_STR = "SUCCEEDED";
27const char *JOB_EXECUTION_CANCELED_STR = "CANCELED";
28const char *JOB_EXECUTION_REJECTED_STR = "REJECTED";
29
30JobExecutionStatus aws_iot_jobs_map_string_to_job_status(const char *str) {
31 if (str == NULL || str[0] == '\0') {
32 return JOB_EXECUTION_STATUS_NOT_SET;
33 } else if (strcmp(str, JOB_EXECUTION_QUEUED_STR) == 0) {
34 return JOB_EXECUTION_QUEUED;
35 } else if(strcmp(str, JOB_EXECUTION_IN_PROGRESS_STR) == 0) {
36 return JOB_EXECUTION_IN_PROGRESS;
37 } else if(strcmp(str, JOB_EXECUTION_FAILED_STR) == 0) {
38 return JOB_EXECUTION_FAILED;
39 } else if(strcmp(str, JOB_EXECUTION_SUCCEEDED_STR) == 0) {
40 return JOB_EXECUTION_SUCCEEDED;
41 } else if(strcmp(str, JOB_EXECUTION_CANCELED_STR) == 0) {
42 return JOB_EXECUTION_CANCELED;
43 } else if(strcmp(str, JOB_EXECUTION_REJECTED_STR) == 0) {
44 return JOB_EXECUTION_REJECTED;
45 } else {
46 return JOB_EXECUTION_UNKNOWN_STATUS;
47 }
48}
49
50const char *aws_iot_jobs_map_status_to_string(JobExecutionStatus status) {
51 switch(status) {
52 case JOB_EXECUTION_QUEUED:
53 return JOB_EXECUTION_QUEUED_STR;
54 case JOB_EXECUTION_IN_PROGRESS:
55 return JOB_EXECUTION_IN_PROGRESS_STR;
56 case JOB_EXECUTION_FAILED:
57 return JOB_EXECUTION_FAILED_STR;
58 case JOB_EXECUTION_SUCCEEDED:
59 return JOB_EXECUTION_SUCCEEDED_STR;
60 case JOB_EXECUTION_CANCELED:
61 return JOB_EXECUTION_CANCELED_STR;
62 case JOB_EXECUTION_REJECTED:
63 return JOB_EXECUTION_REJECTED_STR;
64 case JOB_EXECUTION_STATUS_NOT_SET:
65 case JOB_EXECUTION_UNKNOWN_STATUS:
66 default:
67 return NULL;
68 }
69}
70
71#ifdef __cplusplus
72}
73#endif
diff --git a/src/aws_iot_json_utils.c b/src/aws_iot_json_utils.c
index 3a058f8..16a2609 100644
--- a/src/aws_iot_json_utils.c
+++ b/src/aws_iot_json_utils.c
@@ -1,5 +1,5 @@
1/* 1/*
2 * Copyright 2010-2015 Amazon.com, Inc. or its affiliates. All Rights Reserved. 2 * Copyright 2010-2018 Amazon.com, Inc. or its affiliates. All Rights Reserved.
3 * 3 *
4 * Licensed under the Apache License, Version 2.0 (the "License"). 4 * Licensed under the Apache License, Version 2.0 (the "License").
5 * You may not use this file except in compliance with the License. 5 * You may not use this file except in compliance with the License.
@@ -196,6 +196,35 @@ IoT_Error_t parseStringValue(char *buf, const char *jsonString, jsmntok_t *token
196 return SUCCESS; 196 return SUCCESS;
197} 197}
198 198
199jsmntok_t *findToken(const char *key, const char *jsonString, jsmntok_t *token) {
200 jsmntok_t *result = token;
201 int i;
202
203 if(token->type != JSMN_OBJECT) {
204 IOT_WARN("Token was not an object.");
205 return NULL;
206 }
207
208 if(token->size == 0) {
209 return NULL;
210 }
211
212 result = token + 1;
213
214 for (i = 0; i < token->size; i++) {
215 if (0 == jsoneq(jsonString, result, key)) {
216 return result + 1;
217 }
218
219 int propertyEnd = (result + 1)->end;
220 result += 2;
221 while (result->start < propertyEnd)
222 result++;
223 }
224
225 return NULL;
226}
227
199#ifdef __cplusplus 228#ifdef __cplusplus
200} 229}
201#endif 230#endif
diff --git a/tests/integration/include/aws_iot_config.h b/tests/integration/include/aws_iot_config.h
index 5a3889c..2c80973 100644
--- a/tests/integration/include/aws_iot_config.h
+++ b/tests/integration/include/aws_iot_config.h
@@ -19,34 +19,49 @@
19// Get from console 19// Get from console
20// ================================================= 20// =================================================
21#define AWS_IOT_MQTT_HOST "" ///< Customer specific MQTT HOST. The same will be used for Thing Shadow 21#define AWS_IOT_MQTT_HOST "" ///< Customer specific MQTT HOST. The same will be used for Thing Shadow
22#define AWS_IOT_MQTT_PORT 8883 ///< default port for MQTT/S 22#define AWS_IOT_MQTT_PORT 8883 ///< default port for MQTT/S
23#define AWS_IOT_MQTT_CLIENT_ID "c-sdk-client-id" ///< MQTT client ID should be unique for every device 23#define AWS_IOT_MQTT_CLIENT_ID "c-sdk-client-id" ///< MQTT client ID should be unique for every device
24#define AWS_IOT_MY_THING_NAME "AWS-IoT-C-SDK" ///< Thing Name of the Shadow this device is associated with 24#define AWS_IOT_MY_THING_NAME "AWS-IoT-C-SDK" ///< Thing Name of the Shadow this device is associated with
25#define AWS_IOT_ROOT_CA_FILENAME "rootCA.crt" ///< Root CA file name 25#define AWS_IOT_ROOT_CA_FILENAME "rootCA.crt" ///< Root CA file name
26#define AWS_IOT_CERTIFICATE_FILENAME "cert.pem" ///< device signed certificate file name 26#define AWS_IOT_CERTIFICATE_FILENAME "cert.pem" ///< device signed certificate file name
27#define AWS_IOT_PRIVATE_KEY_FILENAME "privkey.pem" ///< Device private key filename 27#define AWS_IOT_PRIVATE_KEY_FILENAME "privkey.pem" ///< Device private key filename
28// =================================================
29 28
30// MQTT PubSub 29// MQTT PubSub
31#define AWS_IOT_MQTT_TX_BUF_LEN 512 ///< Any time a message is sent out through the MQTT layer. The message is copied into this buffer anytime a publish is done. This will also be used in the case of Thing Shadow 30#ifndef DISABLE_IOT_JOBS
32#define AWS_IOT_MQTT_RX_BUF_LEN 512 ///< Any message that comes into the device should be less than this buffer size. If a received message is bigger than this buffer size the message will be dropped. 31#define AWS_IOT_MQTT_RX_BUF_LEN 512 ///< Any message that comes into the device should be less than this buffer size. If a received message is bigger than this buffer size the message will be dropped.
33#define AWS_IOT_MQTT_NUM_SUBSCRIBE_HANDLERS 5 ///< Maximum number of topic filters the MQTT client can handle at any given time. This should be increased appropriately when using Thing Shadow 32#else
33#define AWS_IOT_MQTT_RX_BUF_LEN 2048
34#endif
35#define AWS_IOT_MQTT_TX_BUF_LEN 512 ///< Any time a message is sent out through the MQTT layer. The message is copied into this buffer anytime a publish is done. This will also be used in the case of Thing Shadow
36#define AWS_IOT_MQTT_NUM_SUBSCRIBE_HANDLERS 5 ///< Maximum number of topic filters the MQTT client can handle at any given time. This should be increased appropriately when using Thing Shadow
37
38// Shadow and Job common configs
39#define MAX_SIZE_OF_UNIQUE_CLIENT_ID_BYTES 80 ///< Maximum size of the Unique Client Id. For More info on the Client Id refer \ref response "Acknowledgments"
40#define MAX_SIZE_CLIENT_ID_WITH_SEQUENCE MAX_SIZE_OF_UNIQUE_CLIENT_ID_BYTES + 10 ///< This is size of the extra sequence number that will be appended to the Unique client Id
41#define MAX_SIZE_CLIENT_TOKEN_CLIENT_SEQUENCE MAX_SIZE_CLIENT_ID_WITH_SEQUENCE + 20 ///< This is size of the the total clientToken key and value pair in the JSON
42#define MAX_SIZE_OF_THING_NAME 30 ///< The Thing Name should not be bigger than this value. Modify this if the Thing Name needs to be bigger
34 43
35// Thing Shadow specific configs 44// Thing Shadow specific configs
36#define SHADOW_MAX_SIZE_OF_RX_BUFFER (AWS_IOT_MQTT_RX_BUF_LEN+1) ///< Maximum size of the SHADOW buffer to store the received Shadow message, including terminating NULL byte. 45#define SHADOW_MAX_SIZE_OF_RX_BUFFER 512 ///< Maximum size of the SHADOW buffer to store the received Shadow message
37#define MAX_SIZE_OF_UNIQUE_CLIENT_ID_BYTES 80 ///< Maximum size of the Unique Client Id. For More info on the Client Id refer \ref response "Acknowledgments" 46#define MAX_ACKS_TO_COMEIN_AT_ANY_GIVEN_TIME 10 ///< At Any given time we will wait for this many responses. This will correlate to the rate at which the shadow actions are requested
38#define MAX_SIZE_CLIENT_ID_WITH_SEQUENCE MAX_SIZE_OF_UNIQUE_CLIENT_ID_BYTES + 10 ///< This is size of the extra sequence number that will be appended to the Unique client Id 47#define MAX_THINGNAME_HANDLED_AT_ANY_GIVEN_TIME 10 ///< We could perform shadow action on any thing Name and this is maximum Thing Names we can act on at any given time
39#define MAX_SIZE_CLIENT_TOKEN_CLIENT_SEQUENCE MAX_SIZE_CLIENT_ID_WITH_SEQUENCE + 20 ///< This is size of the the total clientToken key and value pair in the JSON 48#define MAX_JSON_TOKEN_EXPECTED 120 ///< These are the max tokens that is expected to be in the Shadow JSON document. Include the metadata that gets published
40#define MAX_ACKS_TO_COMEIN_AT_ANY_GIVEN_TIME 10 ///< At Any given time we will wait for this many responses. This will correlate to the rate at which the shadow actions are requested 49#define MAX_SHADOW_TOPIC_LENGTH_WITHOUT_THINGNAME 60 ///< All shadow actions have to be published or subscribed to a topic which is of the format $aws/things/{thingName}/shadow/update/accepted. This refers to the size of the topic without the Thing Name
41#define MAX_THINGNAME_HANDLED_AT_ANY_GIVEN_TIME 10 ///< We could perform shadow action on any thing Name and this is maximum Thing Names we can act on at any given time 50#define MAX_SHADOW_TOPIC_LENGTH_BYTES MAX_SHADOW_TOPIC_LENGTH_WITHOUT_THINGNAME + MAX_SIZE_OF_THING_NAME ///< This size includes the length of topic with Thing Name
42#define MAX_JSON_TOKEN_EXPECTED 120 ///< These are the max tokens that is expected to be in the Shadow JSON document. Include the metadata that gets published 51
43#define MAX_SHADOW_TOPIC_LENGTH_WITHOUT_THINGNAME 60 ///< All shadow actions have to be published or subscribed to a topic which is of the format $aws/things/{thingName}/shadow/update/accepted. This refers to the size of the topic without the Thing Name 52// Job specific configs
44#define MAX_SIZE_OF_THING_NAME 20 ///< The Thing Name should not be bigger than this value. Modify this if the Thing Name needs to be bigger 53#ifndef DISABLE_IOT_JOBS
45#define MAX_SHADOW_TOPIC_LENGTH_BYTES MAX_SHADOW_TOPIC_LENGTH_WITHOUT_THINGNAME + MAX_SIZE_OF_THING_NAME ///< This size includes the length of topic with Thing Name 54#define MAX_SIZE_OF_JOB_ID 64
55#define MAX_JOB_JSON_TOKEN_EXPECTED 120
56#define MAX_SIZE_OF_JOB_REQUEST AWS_IOT_MQTT_TX_BUF_LEN
57
58#define MAX_JOB_TOPIC_LENGTH_WITHOUT_JOB_ID_OR_THING_NAME 40
59#define MAX_JOB_TOPIC_LENGTH_BYTES MAX_JOB_TOPIC_LENGTH_WITHOUT_JOB_ID_OR_THING_NAME + MAX_SIZE_OF_THING_NAME + MAX_SIZE_OF_JOB_ID + 2
60#endif
46 61
47// Auto Reconnect specific config 62// Auto Reconnect specific config
48#define AWS_IOT_MQTT_MIN_RECONNECT_WAIT_INTERVAL 1000 ///< Minimum time before the First reconnect attempt is made as part of the exponential back-off algorithm 63#define AWS_IOT_MQTT_MIN_RECONNECT_WAIT_INTERVAL 1000 ///< Minimum time before the First reconnect attempt is made as part of the exponential back-off algorithm
49#define AWS_IOT_MQTT_MAX_RECONNECT_WAIT_INTERVAL 128000 ///< Maximum time interval after which exponential back-off will stop attempting to reconnect. 64#define AWS_IOT_MQTT_MAX_RECONNECT_WAIT_INTERVAL 128000 ///< Maximum time interval after which exponential back-off will stop attempting to reconnect.
50 65
51#define DISABLE_METRICS false ///< Disable the collection of metrics by setting this to true 66#define DISABLE_METRICS false ///< Disable the collection of metrics by setting this to true
52 67
diff --git a/tests/integration/src/aws_iot_test_integration_runner.c b/tests/integration/src/aws_iot_test_integration_runner.c
index 5ce5bd0..61ec00f 100644
--- a/tests/integration/src/aws_iot_test_integration_runner.c
+++ b/tests/integration/src/aws_iot_test_integration_runner.c
@@ -1,5 +1,5 @@
1/* 1/*
2* Copyright 2015-2016 Amazon.com, Inc. or its affiliates. All Rights Reserved. 2* Copyright 2015-2018 Amazon.com, Inc. or its affiliates. All Rights Reserved.
3* 3*
4* Licensed under the Apache License, Version 2.0 (the "License"). 4* Licensed under the Apache License, Version 2.0 (the "License").
5* You may not use this file except in compliance with the License. 5* You may not use this file except in compliance with the License.
@@ -22,6 +22,7 @@
22 22
23int main() { 23int main() {
24 int rc = 0; 24 int rc = 0;
25
25 printf("\n\n"); 26 printf("\n\n");
26 printf("*************************************************************************************************\n"); 27 printf("*************************************************************************************************\n");
27 printf("* Starting TEST 1 MQTT Version 3.1.1 Basic Subscribe QoS 1 Publish QoS 1 with Single Client *\n"); 28 printf("* Starting TEST 1 MQTT Version 3.1.1 Basic Subscribe QoS 1 Publish QoS 1 with Single Client *\n");
@@ -34,7 +35,7 @@ int main() {
34 return 1; 35 return 1;
35 } 36 }
36 printf("\n*************************************************************************************************\n"); 37 printf("\n*************************************************************************************************\n");
37 printf("* Test 1 MQTT Version 3.1.1 Basic Subscribe QoS 1 Publish QoS 1 with Single Client SUCCESS!! *\n"); 38 printf("* TEST 1 MQTT Version 3.1.1 Basic Subscribe QoS 1 Publish QoS 1 with Single Client SUCCESS!! *\n");
38 printf("*************************************************************************************************\n"); 39 printf("*************************************************************************************************\n");
39 40
40 printf("\n\n"); 41 printf("\n\n");
@@ -67,5 +68,24 @@ int main() {
67 printf("* TEST 3 MQTT Version 3.1.1 Auto Reconnect SUCCESS!! *\n"); 68 printf("* TEST 3 MQTT Version 3.1.1 Auto Reconnect SUCCESS!! *\n");
68 printf("**********************************************************\n"); 69 printf("**********************************************************\n");
69 70
71#ifndef DISABLE_IOT_JOBS
72#ifndef DISABLE_IOT_JOBS_INTERFACE
73 printf("\n\n");
74 printf("*************************************************************************************************\n");
75 printf("* Starting TEST 4 Jobs API Test *\n");
76 printf("*************************************************************************************************\n");
77 rc = aws_iot_jobs_basic_test();
78 if(0 != rc) {
79 printf("\n********************************************************************************************************\n");
80 printf("* TEST 4 Jobs API Test FAILED! RC : %4d *\n", rc);
81 printf("********************************************************************************************************\n");
82 return 1;
83 }
84 printf("\n*************************************************************************************************\n");
85 printf("* TEST 4 Jobs API Test SUCCESS!! *\n");
86 printf("*************************************************************************************************\n");
87#endif
88#endif
89
70 return 0; 90 return 0;
71} 91}
diff --git a/tests/integration/src/aws_iot_test_jobs_api.c b/tests/integration/src/aws_iot_test_jobs_api.c
new file mode 100644
index 0000000..a7e4ce0
--- /dev/null
+++ b/tests/integration/src/aws_iot_test_jobs_api.c
@@ -0,0 +1,279 @@
1/*
2* Copyright 2015-2018 Amazon.com, Inc. or its affiliates. All Rights Reserved.
3*
4* Licensed under the Apache License, Version 2.0 (the "License").
5* You may not use this file except in compliance with the License.
6* A copy of the License is located at
7*
8* http://aws.amazon.com/apache2.0
9*
10* or in the "license" file accompanying this file. This file is distributed
11* on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
12* express or implied. See the License for the specific language governing
13* permissions and limitations under the License.
14*/
15
16#ifndef DISABLE_IOT_JOBS
17#ifndef DISABLE_IOT_JOBS_INTERFACE
18
19#include "aws_iot_test_integration_common.h"
20#include <aws_iot_jobs_interface.h>
21#include <aws_iot_jobs_json.h>
22#include <inttypes.h>
23#include <stdio.h>
24#include <assert.h>
25
26static AWS_IoT_Client client;
27
28static jsmn_parser jsonParser;
29static jsmntok_t jsonTokenStruct[MAX_JSON_TOKEN_EXPECTED];
30static int32_t tokenCount;
31
32static void aws_iot_mqtt_tests_disconnect_callback_handler(AWS_IoT_Client *pClient, void *param) {
33}
34
35void iot_get_pending_callback_handler(AWS_IoT_Client *pClient, char *topicName, uint16_t topicNameLen,
36 IoT_Publish_Message_Params *params, void *pData) {
37 IoT_Error_t rc = SUCCESS;
38 char topicToPublishGetNext[MAX_JOB_TOPIC_LENGTH_BYTES];
39
40 IOT_UNUSED(pData);
41 IOT_UNUSED(pClient);
42 IOT_INFO("\nJOB_GET_PENDING_TOPIC callback");
43 IOT_INFO("topic: %.*s", topicNameLen, topicName);
44 IOT_INFO("payload: %.*s", (int) params->payloadLen, (char *)params->payload);
45
46 jsmn_init(&jsonParser);
47
48 tokenCount = jsmn_parse(&jsonParser, params->payload, (int) params->payloadLen, jsonTokenStruct, MAX_JSON_TOKEN_EXPECTED);
49
50 if(tokenCount < 0) {
51 IOT_WARN("Failed to parse JSON: %d", tokenCount);
52 return;
53 }
54
55 /* Assume the top-level element is an object */
56 if(tokenCount < 1 || jsonTokenStruct[0].type != JSMN_OBJECT) {
57 IOT_WARN("Top Level is not an object");
58 return;
59 }
60
61 jsmntok_t *jobs;
62
63 jobs = findToken("inProgressJobs", params->payload, jsonTokenStruct);
64
65 if (jobs) {
66 IOT_INFO("inProgressJobs: %.*s", jobs->end - jobs->start, (char *)params->payload + jobs->start);
67 }
68
69 jobs = findToken("queuedJobs", params->payload, jsonTokenStruct);
70
71 if (jobs) {
72 IOT_INFO("queuedJobs: %.*s", jobs->end - jobs->start, (char *)params->payload + jobs->start);
73 }
74
75 AwsIotDescribeJobExecutionRequest describeRequest;
76 describeRequest.executionNumber = 0;
77 describeRequest.includeJobDocument = true;
78 describeRequest.clientToken = NULL;
79
80 rc = aws_iot_jobs_describe(&client, QOS0, AWS_IOT_MY_THING_NAME, JOB_ID_NEXT, &describeRequest, topicToPublishGetNext, sizeof(topicToPublishGetNext), NULL, 0);
81}
82
83void iot_next_job_callback_handler(AWS_IoT_Client *pClient, char *topicName, uint16_t topicNameLen,
84 IoT_Publish_Message_Params *params, void *pData) {
85 char topicToPublishUpdate[MAX_JOB_TOPIC_LENGTH_BYTES];
86 char messageBuffer[200];
87
88 IOT_UNUSED(pClient);
89 IOT_INFO("\nJOB_NOTIFY_NEXT_TOPIC / JOB_DESCRIBE_TOPIC($next) callback");
90 IOT_INFO("topic: %.*s", topicNameLen, topicName);
91 IOT_INFO("payload: %.*s", (int) params->payloadLen, (char *)params->payload);
92
93 jsmn_init(&jsonParser);
94
95 tokenCount = jsmn_parse(&jsonParser, params->payload, (int) params->payloadLen, jsonTokenStruct, MAX_JSON_TOKEN_EXPECTED);
96
97 if(tokenCount < 0) {
98 IOT_WARN("Failed to parse JSON: %d", tokenCount);
99 return;
100 }
101
102 /* Assume the top-level element is an object */
103 if(tokenCount < 1 || jsonTokenStruct[0].type != JSMN_OBJECT) {
104 IOT_WARN("Top Level is not an object");
105 return;
106 }
107
108 jsmntok_t *tokExecution;
109
110 tokExecution = findToken("execution", params->payload, jsonTokenStruct);
111
112 if (tokExecution) {
113 IOT_INFO("execution: %.*s", tokExecution->end - tokExecution->start, (char *)params->payload + tokExecution->start);
114
115 jsmntok_t *tok;
116
117 tok = findToken("jobId", params->payload, tokExecution);
118
119 if (tok) {
120 IoT_Error_t rc;
121 char jobId[MAX_SIZE_OF_JOB_ID + 1];
122 AwsIotJobExecutionUpdateRequest updateRequest;
123
124 rc = parseStringValue(jobId, params->payload, tok);
125 if(SUCCESS != rc) {
126 IOT_ERROR("parseStringValue returned error : %d ", rc);
127 return;
128 }
129
130 IOT_INFO("jobId: %s", jobId);
131
132 tok = findToken("jobDocument", params->payload, tokExecution);
133
134 /*
135 * Do your job processing here.
136 */
137
138 if (tok) {
139 IOT_INFO("jobDocument: %.*s", tok->end - tok->start, (char *)params->payload + tok->start);
140 /* Alternatively if the job still has more steps the status can be set to JOB_EXECUTION_IN_PROGRESS instead */
141 updateRequest.status = JOB_EXECUTION_SUCCEEDED;
142 updateRequest.statusDetails = "{\"exampleDetail\":\"a value appropriate for your successful job\"}";
143 } else {
144 updateRequest.status = JOB_EXECUTION_FAILED;
145 updateRequest.statusDetails = "{\"failureDetail\":\"Unable to process job document\"}";
146 }
147
148 updateRequest.expectedVersion = 0;
149 updateRequest.executionNumber = 0;
150 updateRequest.includeJobExecutionState = false;
151 updateRequest.includeJobDocument = false;
152 updateRequest.clientToken = NULL;
153
154 rc = aws_iot_jobs_send_update(pClient, QOS0, AWS_IOT_MY_THING_NAME, jobId, &updateRequest,
155 topicToPublishUpdate, sizeof(topicToPublishUpdate), messageBuffer, sizeof(messageBuffer));
156 }
157 } else {
158 IOT_INFO("execution property not found, nothing to do, jobs integration test complete");
159
160 bool *callbackDone = (bool *) pData;
161 *callbackDone = true;
162 }
163}
164
165int aws_iot_jobs_basic_test() {
166 char certDirectory[15] = "../../certs";
167 char clientCRT[PATH_MAX + 1];
168 char root_CA[PATH_MAX + 1];
169 char clientKey[PATH_MAX + 1];
170 char CurrentWD[PATH_MAX + 1];
171 char clientId[50];
172 char cPayload[100];
173 IoT_Client_Init_Params initParams;
174 IoT_Client_Connect_Params connectParams;
175 IoT_Publish_Message_Params paramsQOS0;
176 IoT_Error_t rc = SUCCESS;
177 struct timeval connectTime;
178 struct timeval start, end;
179 unsigned int connectCounter = 0;
180
181 IOT_DEBUG("\nConnecting Client ");
182 do {
183 getcwd(CurrentWD, sizeof(CurrentWD));
184 snprintf(root_CA, PATH_MAX + 1, "%s/%s/%s", CurrentWD, certDirectory, AWS_IOT_ROOT_CA_FILENAME);
185 snprintf(clientCRT, PATH_MAX + 1, "%s/%s/%s", CurrentWD, certDirectory, AWS_IOT_CERTIFICATE_FILENAME);
186 snprintf(clientKey, PATH_MAX + 1, "%s/%s/%s", CurrentWD, certDirectory, AWS_IOT_PRIVATE_KEY_FILENAME);
187 srand((unsigned int)time(NULL));
188 snprintf(clientId, 50, "%s_%d", INTEGRATION_TEST_CLIENT_ID, rand() % 10000);
189
190 printf("\n\nClient ID : %s \n", clientId);
191
192 IOT_DEBUG("Root CA Path : %s\n clientCRT : %s\n clientKey : %s\n", root_CA, clientCRT, clientKey);
193 initParams.pHostURL = AWS_IOT_MQTT_HOST;
194 initParams.port = 8883;
195 initParams.pRootCALocation = root_CA;
196 initParams.pDeviceCertLocation = clientCRT;
197 initParams.pDevicePrivateKeyLocation = clientKey;
198 initParams.mqttCommandTimeout_ms = 10000;
199 initParams.tlsHandshakeTimeout_ms = 10000;
200 initParams.disconnectHandler = aws_iot_mqtt_tests_disconnect_callback_handler;
201 initParams.enableAutoReconnect = false;
202 aws_iot_mqtt_init(&client, &initParams);
203
204 connectParams.keepAliveIntervalInSec = 10;
205 connectParams.isCleanSession = true;
206 connectParams.MQTTVersion = MQTT_3_1_1;
207 connectParams.pClientID = (char *)&clientId;
208 connectParams.clientIDLen = strlen(clientId);
209 connectParams.isWillMsgPresent = false;
210 connectParams.pUsername = NULL;
211 connectParams.usernameLen = 0;
212 connectParams.pPassword = NULL;
213 connectParams.passwordLen = 0;
214
215 gettimeofday(&connectTime, NULL);
216 rc = aws_iot_mqtt_connect(&client, &connectParams);
217 gettimeofday(&end, NULL);
218 timersub(&end, &start, &connectTime);
219
220 connectCounter++;
221 } while(rc != SUCCESS && connectCounter < CONNECT_MAX_ATTEMPT_COUNT);
222
223 if(SUCCESS == rc) {
224 IOT_DEBUG("## Connect Success. Time sec: %d, usec: %d\n", connectTime.tv_sec, connectTime.tv_usec);
225 } else {
226 IOT_ERROR("## Connect Failed. error code %d\n", rc);
227 return -1;
228 }
229
230 bool callbackDone = false;
231 char topicToSubscribeGetPending[MAX_JOB_TOPIC_LENGTH_BYTES];
232 char topicToSubscribeNotifyNext[MAX_JOB_TOPIC_LENGTH_BYTES];
233 char topicToSubscribeGetNext[MAX_JOB_TOPIC_LENGTH_BYTES];
234
235 char topicToPublishGetPending[MAX_JOB_TOPIC_LENGTH_BYTES];
236
237 rc = aws_iot_jobs_subscribe_to_job_messages(
238 &client, QOS0, AWS_IOT_MY_THING_NAME, NULL, JOB_GET_PENDING_TOPIC, JOB_WILDCARD_REPLY_TYPE,
239 iot_get_pending_callback_handler, &callbackDone, topicToSubscribeGetPending, sizeof(topicToSubscribeGetPending));
240
241 if(SUCCESS != rc) {
242 IOT_ERROR("Error subscribing JOB_GET_PENDING_TOPIC: %d ", rc);
243 return rc;
244 }
245
246 rc = aws_iot_jobs_subscribe_to_job_messages(
247 &client, QOS0, AWS_IOT_MY_THING_NAME, NULL, JOB_NOTIFY_NEXT_TOPIC, JOB_REQUEST_TYPE,
248 iot_next_job_callback_handler, &callbackDone, topicToSubscribeNotifyNext, sizeof(topicToSubscribeNotifyNext));
249
250 if(SUCCESS != rc) {
251 IOT_ERROR("Error subscribing JOB_NOTIFY_NEXT_TOPIC: %d ", rc);
252 return rc;
253 }
254
255 rc = aws_iot_jobs_subscribe_to_job_messages(
256 &client, QOS0, AWS_IOT_MY_THING_NAME, JOB_ID_NEXT, JOB_DESCRIBE_TOPIC, JOB_WILDCARD_REPLY_TYPE,
257 iot_next_job_callback_handler, &callbackDone, topicToSubscribeGetNext, sizeof(topicToSubscribeGetNext));
258
259 if(SUCCESS != rc) {
260 IOT_ERROR("Error subscribing JOB_DESCRIBE_TOPIC ($next): %d ", rc);
261 return rc;
262 }
263
264 paramsQOS0.qos = QOS0;
265 paramsQOS0.payload = (void *) cPayload;
266 paramsQOS0.isRetained = 0;
267 paramsQOS0.payloadLen = strlen(cPayload);
268
269 rc = aws_iot_jobs_send_query(&client, QOS0, AWS_IOT_MY_THING_NAME, NULL, NULL, topicToPublishGetPending, sizeof(topicToPublishGetPending), NULL, 0, JOB_GET_PENDING_TOPIC);
270
271 while (!callbackDone) {
272 aws_iot_mqtt_yield(&client, 5000);
273 }
274
275 return 0;
276}
277
278#endif
279#endif
diff --git a/tests/unit/include/aws_iot_config.h b/tests/unit/include/aws_iot_config.h
index e22db06..248baed 100644
--- a/tests/unit/include/aws_iot_config.h
+++ b/tests/unit/include/aws_iot_config.h
@@ -32,26 +32,42 @@
32#define AWS_IOT_PRIVATE_KEY_FILENAME "privkey.pem" 32#define AWS_IOT_PRIVATE_KEY_FILENAME "privkey.pem"
33// ================================================= 33// =================================================
34 34
35
35// MQTT PubSub 36// MQTT PubSub
36#define AWS_IOT_MQTT_TX_BUF_LEN 512 37#ifndef DISABLE_IOT_JOBS
37#define AWS_IOT_MQTT_RX_BUF_LEN 512 38#define AWS_IOT_MQTT_RX_BUF_LEN 512 ///< Any message that comes into the device should be less than this buffer size. If a received message is bigger than this buffer size the message will be dropped.
38#define AWS_IOT_MQTT_NUM_SUBSCRIBE_HANDLERS 5 39#else
40#define AWS_IOT_MQTT_RX_BUF_LEN 2048
41#endif
42#define AWS_IOT_MQTT_TX_BUF_LEN 512 ///< Any time a message is sent out through the MQTT layer. The message is copied into this buffer anytime a publish is done. This will also be used in the case of Thing Shadow
43#define AWS_IOT_MQTT_NUM_SUBSCRIBE_HANDLERS 5 ///< Maximum number of topic filters the MQTT client can handle at any given time. This should be increased appropriately when using Thing Shadow
44
45// Shadow and Job common configs
46#define MAX_SIZE_OF_UNIQUE_CLIENT_ID_BYTES 80 ///< Maximum size of the Unique Client Id. For More info on the Client Id refer \ref response "Acknowledgments"
47#define MAX_SIZE_CLIENT_ID_WITH_SEQUENCE MAX_SIZE_OF_UNIQUE_CLIENT_ID_BYTES + 10 ///< This is size of the extra sequence number that will be appended to the Unique client Id
48#define MAX_SIZE_CLIENT_TOKEN_CLIENT_SEQUENCE MAX_SIZE_CLIENT_ID_WITH_SEQUENCE + 20 ///< This is size of the the total clientToken key and value pair in the JSON
49#define MAX_SIZE_OF_THING_NAME 30 ///< The Thing Name should not be bigger than this value. Modify this if the Thing Name needs to be bigger
39 50
40// Thing Shadow specific configs 51// Thing Shadow specific configs
41#define SHADOW_MAX_SIZE_OF_RX_BUFFER 512 52#define SHADOW_MAX_SIZE_OF_RX_BUFFER 512 ///< Maximum size of the SHADOW buffer to store the received Shadow message
42#define MAX_SIZE_OF_UNIQUE_CLIENT_ID_BYTES 80 /** {"clientToken": ">>uniqueClientID<<+sequenceNumber"}*/ 53#define MAX_ACKS_TO_COMEIN_AT_ANY_GIVEN_TIME 10 ///< At Any given time we will wait for this many responses. This will correlate to the rate at which the shadow actions are requested
43#define MAX_SIZE_CLIENT_ID_WITH_SEQUENCE MAX_SIZE_OF_UNIQUE_CLIENT_ID_BYTES + 10 /** {"clientToken": ">>uniqueClientID+sequenceNumber<<"}*/ 54#define MAX_THINGNAME_HANDLED_AT_ANY_GIVEN_TIME 10 ///< We could perform shadow action on any thing Name and this is maximum Thing Names we can act on at any given time
44#define MAX_SIZE_CLIENT_TOKEN_CLIENT_SEQUENCE MAX_SIZE_CLIENT_ID_WITH_SEQUENCE + 20 /** >>{"clientToken": "uniqueClientID+sequenceNumber"}<<*/ 55#define MAX_JSON_TOKEN_EXPECTED 120 ///< These are the max tokens that is expected to be in the Shadow JSON document. Include the metadata that gets published
45#define MAX_SIZE_OF_THINGNAME 30 56#define MAX_SHADOW_TOPIC_LENGTH_WITHOUT_THINGNAME 60 ///< All shadow actions have to be published or subscribed to a topic which is of the format $aws/things/{thingName}/shadow/update/accepted. This refers to the size of the topic without the Thing Name
46#define MAX_ACKS_TO_COMEIN_AT_ANY_GIVEN_TIME 10 57#define MAX_SHADOW_TOPIC_LENGTH_BYTES MAX_SHADOW_TOPIC_LENGTH_WITHOUT_THINGNAME + MAX_SIZE_OF_THING_NAME ///< This size includes the length of topic with Thing Name
47#define MAX_THINGNAME_HANDLED_AT_ANY_GIVEN_TIME 10 58
48#define MAX_JSON_TOKEN_EXPECTED 120 59// Job specific configs
49#define MAX_SHADOW_TOPIC_LENGTH_WITHOUT_THINGNAME 60 60#ifndef DISABLE_IOT_JOBS
50#define MAX_SIZE_OF_THING_NAME 20 61#define MAX_SIZE_OF_JOB_ID 64
51#define MAX_SHADOW_TOPIC_LENGTH_BYTES MAX_SHADOW_TOPIC_LENGTH_WITHOUT_THINGNAME + MAX_SIZE_OF_THING_NAME 62#define MAX_JOB_JSON_TOKEN_EXPECTED 120
63#define MAX_SIZE_OF_JOB_REQUEST AWS_IOT_MQTT_TX_BUF_LEN
64
65#define MAX_JOB_TOPIC_LENGTH_WITHOUT_JOB_ID_OR_THING_NAME 40
66#define MAX_JOB_TOPIC_LENGTH_BYTES MAX_JOB_TOPIC_LENGTH_WITHOUT_JOB_ID_OR_THING_NAME + MAX_SIZE_OF_THING_NAME + MAX_SIZE_OF_JOB_ID + 2
67#endif
52 68
53// Auto Reconnect specific config 69// Auto Reconnect specific config
54#define AWS_IOT_MQTT_MIN_RECONNECT_WAIT_INTERVAL 1000 70#define AWS_IOT_MQTT_MIN_RECONNECT_WAIT_INTERVAL 1000 ///< Minimum time before the First reconnect attempt is made as part of the exponential back-off algorithm
55#define AWS_IOT_MQTT_MAX_RECONNECT_WAIT_INTERVAL 128000 71#define AWS_IOT_MQTT_MAX_RECONNECT_WAIT_INTERVAL 128000 ///< Maximum time interval after which exponential back-off will stop attempting to reconnect.
56 72
57#endif /* IOT_TESTS_UNIT_CONFIG_H_ */ 73#endif /* IOT_TESTS_UNIT_CONFIG_H_ */
diff --git a/tests/unit/include/aws_iot_tests_unit_helper_functions.h b/tests/unit/include/aws_iot_tests_unit_helper_functions.h
index d8a1806..a7d3f80 100644
--- a/tests/unit/include/aws_iot_tests_unit_helper_functions.h
+++ b/tests/unit/include/aws_iot_tests_unit_helper_functions.h
@@ -23,6 +23,7 @@
23 23
24#include <string.h> 24#include <string.h>
25#include "aws_iot_mqtt_client_interface.h" 25#include "aws_iot_mqtt_client_interface.h"
26#include "aws_iot_jobs_topics.h"
26 27
27typedef struct { 28typedef struct {
28 unsigned char PacketType; 29 unsigned char PacketType;
@@ -92,4 +93,8 @@ bool isConnectTxBufPayloadCorrect(IoT_Client_Connect_Params *settings, unsigned
92 93
93void printPrfrdParams(ConnectBufferProofread *params); 94void printPrfrdParams(ConnectBufferProofread *params);
94 95
96const char *getJobTopicTypeName(AwsIotJobExecutionTopicType topicType);
97
98const char *getJobReplyTypeName(AwsIotJobExecutionTopicReplyType replyType);
99
95#endif /* IOT_TESTS_UNIT_HELPER_FUNCTIONS_H_ */ 100#endif /* IOT_TESTS_UNIT_HELPER_FUNCTIONS_H_ */
diff --git a/tests/unit/src/aws_iot_tests_unit_common_tests_helper.c b/tests/unit/src/aws_iot_tests_unit_common_tests_helper.c
index e7a852e..9d16042 100644
--- a/tests/unit/src/aws_iot_tests_unit_common_tests_helper.c
+++ b/tests/unit/src/aws_iot_tests_unit_common_tests_helper.c
@@ -131,7 +131,7 @@ TEST_C(CommonTests, UnexpectedAckFiltering) {
131TEST_C(CommonTests, BigMQTTRxMessageIgnore) { 131TEST_C(CommonTests, BigMQTTRxMessageIgnore) {
132 uint32_t i = 0; 132 uint32_t i = 0;
133 IoT_Error_t rc = FAILURE; 133 IoT_Error_t rc = FAILURE;
134 char expectedCallbackString[AWS_IOT_MQTT_TX_BUF_LEN + 2]; 134 char expectedCallbackString[AWS_IOT_MQTT_RX_BUF_LEN + 2];
135 135
136 IOT_DEBUG("\n-->Running CommonTests - Ignore Large Incoming Message \n"); 136 IOT_DEBUG("\n-->Running CommonTests - Ignore Large Incoming Message \n");
137 137
@@ -139,7 +139,7 @@ TEST_C(CommonTests, BigMQTTRxMessageIgnore) {
139 rc = aws_iot_mqtt_subscribe(&iotClient, "limitTest/topic1", 16, QOS0, iot_tests_unit_common_subscribe_callback_handler, NULL); 139 rc = aws_iot_mqtt_subscribe(&iotClient, "limitTest/topic1", 16, QOS0, iot_tests_unit_common_subscribe_callback_handler, NULL);
140 CHECK_EQUAL_C_INT(SUCCESS, rc); 140 CHECK_EQUAL_C_INT(SUCCESS, rc);
141 141
142 for(i = 0; i < AWS_IOT_MQTT_TX_BUF_LEN; i++) { 142 for(i = 0; i < AWS_IOT_MQTT_RX_BUF_LEN; i++) {
143 expectedCallbackString[i] = 'X'; 143 expectedCallbackString[i] = 'X';
144 } 144 }
145 expectedCallbackString[i + 1] = '\0'; 145 expectedCallbackString[i + 1] = '\0';
@@ -156,7 +156,7 @@ TEST_C(CommonTests, BigMQTTRxMessageIgnore) {
156TEST_C(CommonTests, BigMQTTRxMessageReadNextMessage) { 156TEST_C(CommonTests, BigMQTTRxMessageReadNextMessage) {
157 uint32_t i = 0; 157 uint32_t i = 0;
158 IoT_Error_t rc = FAILURE; 158 IoT_Error_t rc = FAILURE;
159 char expectedCallbackString[AWS_IOT_MQTT_TX_BUF_LEN + 2]; 159 char expectedCallbackString[AWS_IOT_MQTT_RX_BUF_LEN + 2];
160 160
161 IOT_DEBUG("\n-->Running CommonTests - Clear Buffer when large message received and continue reading \n"); 161 IOT_DEBUG("\n-->Running CommonTests - Clear Buffer when large message received and continue reading \n");
162 162
@@ -164,7 +164,7 @@ TEST_C(CommonTests, BigMQTTRxMessageReadNextMessage) {
164 rc = aws_iot_mqtt_subscribe(&iotClient, "limitTest/topic1", 16, QOS0, iot_tests_unit_common_subscribe_callback_handler, NULL); 164 rc = aws_iot_mqtt_subscribe(&iotClient, "limitTest/topic1", 16, QOS0, iot_tests_unit_common_subscribe_callback_handler, NULL);
165 CHECK_EQUAL_C_INT(SUCCESS, rc); 165 CHECK_EQUAL_C_INT(SUCCESS, rc);
166 166
167 for(i = 0; i < AWS_IOT_MQTT_TX_BUF_LEN; i++) { 167 for(i = 0; i < AWS_IOT_MQTT_RX_BUF_LEN; i++) {
168 expectedCallbackString[i] = 'X'; 168 expectedCallbackString[i] = 'X';
169 } 169 }
170 expectedCallbackString[i + 1] = '\0'; 170 expectedCallbackString[i + 1] = '\0';
diff --git a/tests/unit/src/aws_iot_tests_unit_helper_functions.c b/tests/unit/src/aws_iot_tests_unit_helper_functions.c
index afa676d..428bc29 100644
--- a/tests/unit/src/aws_iot_tests_unit_helper_functions.c
+++ b/tests/unit/src/aws_iot_tests_unit_helper_functions.c
@@ -536,3 +536,28 @@ void printPrfrdParams(ConnectBufferProofread *params) {
536 printf("KeepAliveInterval: %u\n", params->KeepAlive); 536 printf("KeepAliveInterval: %u\n", params->KeepAlive);
537 printf("----------------\n"); 537 printf("----------------\n");
538} 538}
539
540const char *getJobTopicTypeName(AwsIotJobExecutionTopicType topicType) {
541 switch (topicType) {
542 case JOB_UPDATE_TOPIC: return "JOB_UPDATE_TOPIC";
543 case JOB_NOTIFY_TOPIC: return "JOB_NOTIFY_TOPIC";
544 case JOB_NOTIFY_NEXT_TOPIC: return "JOB_NOTIFY_NEXT_TOPIC";
545 case JOB_GET_PENDING_TOPIC: return "JOB_GET_PENDING_TOPIC";
546 case JOB_DESCRIBE_TOPIC: return "JOB_DESCRIBE_TOPIC";
547 case JOB_START_NEXT_TOPIC: return "JOB_START_NEXT_TOPIC";
548 case JOB_WILDCARD_TOPIC: return "JOB_WILDCARD_TOPIC";
549 case JOB_UNRECOGNIZED_TOPIC: return "JOB_UNRECOGNIZED_TOPIC";
550 default: return "invalid";
551 }
552}
553
554const char *getJobReplyTypeName(AwsIotJobExecutionTopicReplyType replyType) {
555 switch (replyType) {
556 case JOB_REQUEST_TYPE: return "JOB_REQUEST_TYPE";
557 case JOB_ACCEPTED_REPLY_TYPE: return "JOB_ACCEPTED_REPLY_TYPE";
558 case JOB_REJECTED_REPLY_TYPE: return "JOB_REJECTED_REPLY_TYPE";
559 case JOB_WILDCARD_REPLY_TYPE: return "JOB_WILDCARD_REPLY_TYPE";
560 case JOB_UNRECOGNIZED_TOPIC_TYPE: return "JOB_UNRECOGNIZED_TOPIC_TYPE";
561 default: return "invalid";
562 }
563}
diff --git a/tests/unit/src/aws_iot_tests_unit_jobs.cpp b/tests/unit/src/aws_iot_tests_unit_jobs.cpp
new file mode 100644
index 0000000..edbf8ce
--- /dev/null
+++ b/tests/unit/src/aws_iot_tests_unit_jobs.cpp
@@ -0,0 +1,60 @@
1/*
2* Copyright 2015-2018 Amazon.com, Inc. or its affiliates. All Rights Reserved.
3*
4* Licensed under the Apache License, Version 2.0 (the "License").
5* You may not use this file except in compliance with the License.
6* A copy of the License is located at
7*
8* http://aws.amazon.com/apache2.0
9*
10* or in the "license" file accompanying this file. This file is distributed
11* on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
12* express or implied. See the License for the specific language governing
13* permissions and limitations under the License.
14*/
15
16#include <CppUTest/CommandLineTestRunner.h>
17#include <CppUTest/TestHarness_c.h>
18
19
20TEST_GROUP_C(JobsTypesTests) {
21 TEST_GROUP_C_SETUP_WRAPPER(JobsTypesTests)
22 TEST_GROUP_C_TEARDOWN_WRAPPER(JobsTypesTests)
23};
24
25TEST_GROUP_C_WRAPPER(JobsTypesTests, StringToStatus)
26TEST_GROUP_C_WRAPPER(JobsTypesTests, StatusToString)
27
28TEST_GROUP_C(JobsJsonTests) {
29 TEST_GROUP_C_SETUP_WRAPPER(JobsJsonTests)
30 TEST_GROUP_C_TEARDOWN_WRAPPER(JobsJsonTests)
31};
32
33TEST_GROUP_C_WRAPPER(JobsJsonTests, SerializeUpdateRequest)
34TEST_GROUP_C_WRAPPER(JobsJsonTests, SerializeUpdateRequestWithNullBuffer)
35TEST_GROUP_C_WRAPPER(JobsJsonTests, SerializeUpdateRequestWithTooSmallBuffer)
36TEST_GROUP_C_WRAPPER(JobsJsonTests, SerializeDescribeJobExecutionRequest)
37TEST_GROUP_C_WRAPPER(JobsJsonTests, SerializeDescribeJobExecutionRequestWithNullBuffer)
38TEST_GROUP_C_WRAPPER(JobsJsonTests, SerializeDescribeJobExecutionRequestWithTooSmallBuffer)
39TEST_GROUP_C_WRAPPER(JobsJsonTests, SerializeStartNextRequest)
40TEST_GROUP_C_WRAPPER(JobsJsonTests, SerializeStartNextRequestWithNullBuffer)
41TEST_GROUP_C_WRAPPER(JobsJsonTests, SerializeStartNextRequestWithTooSmallBuffer)
42
43TEST_GROUP_C(JobsTopicsTests) {
44 TEST_GROUP_C_SETUP_WRAPPER(JobsTopicsTests)
45 TEST_GROUP_C_TEARDOWN_WRAPPER(JobsTopicsTests)
46};
47
48TEST_GROUP_C_WRAPPER(JobsTopicsTests, GenerateValidTopics)
49TEST_GROUP_C_WRAPPER(JobsTopicsTests, GenerateWithMissingJobId)
50TEST_GROUP_C_WRAPPER(JobsTopicsTests, GenerateWithInvalidTopicOrReplyType)
51TEST_GROUP_C_WRAPPER(JobsTopicsTests, GenerateWithInvalidCombinations)
52
53TEST_GROUP_C(JobsInterfaceTest) {
54 TEST_GROUP_C_SETUP_WRAPPER(JobsInterfaceTest)
55 TEST_GROUP_C_TEARDOWN_WRAPPER(JobsInterfaceTest)
56};
57
58TEST_GROUP_C_WRAPPER(JobsInterfaceTest, TestSubscribeAndUnsubscribe)
59TEST_GROUP_C_WRAPPER(JobsInterfaceTest, TestSendQuery)
60TEST_GROUP_C_WRAPPER(JobsInterfaceTest, TestSendUpdate)
diff --git a/tests/unit/src/aws_iot_tests_unit_jobs_interface.c b/tests/unit/src/aws_iot_tests_unit_jobs_interface.c
new file mode 100644
index 0000000..c19a38d
--- /dev/null
+++ b/tests/unit/src/aws_iot_tests_unit_jobs_interface.c
@@ -0,0 +1,337 @@
1/*
2* Copyright 2015-2018 Amazon.com, Inc. or its affiliates. All Rights Reserved.
3*
4* Licensed under the Apache License, Version 2.0 (the "License").
5* You may not use this file except in compliance with the License.
6* A copy of the License is located at
7*
8* http://aws.amazon.com/apache2.0
9*
10* or in the "license" file accompanying this file. This file is distributed
11* on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
12* express or implied. See the License for the specific language governing
13* permissions and limitations under the License.
14*/
15
16#include <aws_iot_jobs_interface.h>
17
18#include "aws_iot_tests_unit_mock_tls_params.h"
19#include "aws_iot_tests_unit_helper_functions.h"
20#include "aws_iot_config.h"
21#include <aws_iot_tests_unit_helper_functions.h>
22#include <CppUTest/TestHarness_c.h>
23#include <aws_iot_log.h>
24
25static AWS_IoT_Client client;
26static IoT_Client_Connect_Params connectParams;
27static IoT_Client_Init_Params mqttInitParams;
28
29static const char *THING_NAME = "T1";
30static const char *JOB_ID = "J1";
31
32static int CALLBACK_DATA = 5;
33
34static const char *expectedTopic;
35static const void *expectedData;
36static size_t expectedDataLen;
37
38static int callbackCount = 0;
39
40static bool expectError;
41static bool expectedJsonContext;
42
43
44static void testCallback(
45 AWS_IoT_Client *pClient,
46 char *topicName, uint16_t topicNameLen,
47 IoT_Publish_Message_Params *params, void *pData)
48{
49 callbackCount++;
50
51 CHECK_C(pData == &CALLBACK_DATA);
52 CHECK_C(pClient == &client);
53
54 CHECK_EQUAL_C_INT((int)strlen(expectedTopic), (int)topicNameLen);
55 CHECK_C(strncmp(expectedTopic, topicName, (size_t) topicNameLen) == 0);
56
57 CHECK_C(memcmp(expectedData, params->payload, expectedDataLen) == 0);
58}
59
60TEST_GROUP_C_SETUP(JobsInterfaceTest) {
61 IoT_Error_t ret_val = SUCCESS;
62
63 InitMQTTParamsSetup(&mqttInitParams, AWS_IOT_MQTT_HOST, AWS_IOT_MQTT_PORT, false, NULL);
64 ret_val = aws_iot_mqtt_init(&client, &mqttInitParams);
65 CHECK_EQUAL_C_INT(SUCCESS, ret_val);
66
67 ConnectMQTTParamsSetup(&connectParams, (char *) AWS_IOT_MQTT_CLIENT_ID, (uint16_t) strlen(AWS_IOT_MQTT_CLIENT_ID));
68 setTLSRxBufferForConnack(&connectParams, 0, 0);
69 ret_val = aws_iot_mqtt_connect(&client, &connectParams);
70 CHECK_EQUAL_C_INT(SUCCESS, ret_val);
71
72 ResetTLSBuffer();
73
74 /* Ensure that old data can't be used */
75 lastSubscribeMsgLen = 0;
76 lastUnsubscribeMsgLen = 0;
77 lastPublishMessageTopicLen = 0;
78 lastPublishMessagePayloadLen = 0;
79
80 callbackCount = 0;
81 expectedTopic = NULL;
82 expectedData = NULL;
83 expectedDataLen = 0;
84
85 expectError = false;
86 expectedJsonContext = false;
87}
88
89TEST_GROUP_C_TEARDOWN(JobsInterfaceTest) {
90 IoT_Error_t rc = aws_iot_mqtt_disconnect(&client);
91 IOT_UNUSED(rc);
92}
93
94static void publishTestMessage(AwsIotJobExecutionTopicType topicType, bool withJobId) {
95 char replyTopic[MAX_JOB_TOPIC_LENGTH_BYTES + 1];
96 AwsIotJobExecutionTopicReplyType replyType;
97 char * message;
98
99 if (topicType == JOB_NOTIFY_TOPIC) {
100 replyType = JOB_REQUEST_TYPE;
101 message = "{\"jobs\":{\"IN_PROGRESS\":[{}]},\"timestamp\":5}";
102 } else if (topicType == JOB_NOTIFY_NEXT_TOPIC) {
103 replyType = JOB_REQUEST_TYPE;
104 message = "{\"execution\":{\"jobId\":\"J1\",\"status\":\"IN_PROGRESS\",\"queuedAt\":50,\"versionNumber\":20},\"timestamp\":5}";
105 }
106 else {
107 replyType = JOB_REJECTED_REPLY_TYPE;
108 message = "{\"code\":\"foo\",\"message\":\"bar\", \"clientToken\":\"baz\",\"timestamp\":6}";
109 }
110
111 const char *passedJobId = NULL;
112 if (withJobId) {
113 if (topicType == JOB_WILDCARD_TOPIC) {
114 topicType = JOB_DESCRIBE_TOPIC;
115 }
116 passedJobId = JOB_ID;
117 }
118
119 int replyTopicLen = aws_iot_jobs_get_api_topic(
120 replyTopic, MAX_JOB_TOPIC_LENGTH_BYTES + 1,
121 topicType, replyType, THING_NAME, passedJobId);
122
123 expectedData = message;
124 expectedDataLen = strlen(message) + 1;
125 expectedTopic = replyTopic;
126
127 IoT_Publish_Message_Params params;
128 params.payload = message;
129 params.payloadLen = strlen(message);
130 params.qos = QOS0;
131
132 int preSendCount = callbackCount;
133
134 setTLSRxBufferWithMsgOnSubscribedTopic(replyTopic, (size_t)replyTopicLen, QOS0, params, message);
135 IoT_Error_t error = aws_iot_mqtt_yield(&client, 100);
136
137 CHECK_EQUAL_C_INT(SUCCESS, error);
138 CHECK_EQUAL_C_INT(preSendCount + 1, callbackCount);
139}
140
141static void testSubscribeAndUnsubscribe(AwsIotJobExecutionTopicType topicType, bool withJobId) {
142 char expectedTopic[MAX_JOB_TOPIC_LENGTH_BYTES + 1];
143 char topicBuffer[MAX_JOB_TOPIC_LENGTH_BYTES + 1];
144
145 IOT_DEBUG("\n-->Running Jobs Interface Tests - test subscribe/unsubscribe %s %s \n", getJobTopicTypeName(topicType), (withJobId ? "(w/ job id)" : ""));
146
147 AwsIotJobExecutionTopicReplyType replyType;
148 if (topicType != JOB_NOTIFY_TOPIC && topicType != JOB_NOTIFY_NEXT_TOPIC) {
149 replyType = JOB_WILDCARD_REPLY_TYPE;
150 } else {
151 replyType = JOB_REQUEST_TYPE;
152 }
153
154 const char *passedJobId = NULL;
155 if (withJobId) passedJobId = JOB_ID;
156
157 int expectedTopicLength = aws_iot_jobs_get_api_topic(
158 expectedTopic, MAX_JOB_TOPIC_LENGTH_BYTES + 1,
159 topicType, replyType,
160 THING_NAME, passedJobId);
161
162 CHECK_C(expectedTopicLength > 0 && expectedTopicLength < MAX_JOB_TOPIC_LENGTH_BYTES);
163
164 /* Clear to make sure old data doesn't cause the test to pass when we shouldn't */
165 lastSubscribeMsgLen = 0;
166 lastPublishMessageTopicLen = 0;
167
168 IoT_Publish_Message_Params unused;
169
170 setTLSRxBufferForSuback(expectedTopic, (size_t)expectedTopicLength, QOS0, unused);
171
172 IoT_Error_t iotError;
173 if (topicType == JOB_WILDCARD_TOPIC && !withJobId) {
174 iotError = aws_iot_jobs_subscribe_to_all_job_messages(
175 &client, QOS0, THING_NAME, testCallback, &CALLBACK_DATA, topicBuffer, sizeof(topicBuffer));
176 } else {
177 iotError = aws_iot_jobs_subscribe_to_job_messages(
178 &client, QOS0, THING_NAME, passedJobId, topicType, replyType, testCallback, &CALLBACK_DATA, topicBuffer, sizeof(topicBuffer));
179 }
180
181 CHECK_EQUAL_C_INT(SUCCESS, iotError);
182
183 CHECK_EQUAL_C_INT(expectedTopicLength, (int)strlen(topicBuffer));
184 CHECK_EQUAL_C_INT(expectedTopicLength, (int)lastSubscribeMsgLen);
185 CHECK_EQUAL_C_STRING(expectedTopic, topicBuffer);
186 CHECK_EQUAL_C_STRING(expectedTopic, LastSubscribeMessage);
187
188 publishTestMessage(topicType, withJobId);
189
190 LastUnsubscribeMessage[0] = 0;
191 lastUnsubscribeMsgLen = 0;
192
193 setTLSRxBufferForUnsuback();
194 iotError = aws_iot_jobs_unsubscribe_from_job_messages(&client, topicBuffer);
195
196 CHECK_EQUAL_C_INT(SUCCESS, iotError);
197
198 CHECK_EQUAL_C_STRING(expectedTopic, LastUnsubscribeMessage);
199
200 IOT_DEBUG("-->Success - test subscribe/unsubscribe %s %s \n", getJobTopicTypeName(topicType), (withJobId ? "(w/ job id)" : ""));
201}
202
203TEST_C(JobsInterfaceTest, TestSubscribeAndUnsubscribe) {
204 testSubscribeAndUnsubscribe(JOB_NOTIFY_TOPIC, false);
205 testSubscribeAndUnsubscribe(JOB_NOTIFY_NEXT_TOPIC, false);
206 testSubscribeAndUnsubscribe(JOB_DESCRIBE_TOPIC, true);
207 testSubscribeAndUnsubscribe(JOB_GET_PENDING_TOPIC, false);
208 testSubscribeAndUnsubscribe(JOB_UPDATE_TOPIC, true);
209 testSubscribeAndUnsubscribe(JOB_WILDCARD_TOPIC, true);
210 testSubscribeAndUnsubscribe(JOB_WILDCARD_TOPIC, false);
211 testSubscribeAndUnsubscribe(JOB_START_NEXT_TOPIC, false);
212}
213
214static void testSendQuery(bool withJobId, bool withClientToken, AwsIotJobExecutionTopicType topicType) {
215 char expectedTopic[MAX_JOB_TOPIC_LENGTH_BYTES + 1];
216 char topicBuffer[MAX_JOB_TOPIC_LENGTH_BYTES + 1];
217 char messageBuffer[MAX_SIZE_OF_JOB_REQUEST + 1];
218
219 IOT_DEBUG("\n-->Running Jobs Interface Tests - test send query %s %s \n", getJobTopicTypeName(topicType), (withClientToken ? "(w/ clientToken)" : ""));
220
221 char *clientToken = NULL;
222 char *bufferToPass = NULL;
223 size_t bufferLenToPass = 0;
224 if (withClientToken) {
225 clientToken = "FooBar";
226 bufferToPass = messageBuffer;
227 bufferLenToPass = MAX_SIZE_OF_JOB_REQUEST + 1;
228 }
229
230 const char *passedJobId = NULL;
231 if (withJobId) passedJobId = JOB_ID;
232
233 int expectedTopicLength = aws_iot_jobs_get_api_topic(
234 expectedTopic, MAX_JOB_TOPIC_LENGTH_BYTES + 1,
235 topicType, JOB_REQUEST_TYPE,
236 THING_NAME, passedJobId);
237
238 CHECK_C(expectedTopicLength > 0 && expectedTopicLength <= MAX_JOB_TOPIC_LENGTH_BYTES);
239
240 LastPublishMessageTopic[0] = 0;
241 lastPublishMessageTopicLen = 0;
242 LastPublishMessagePayload[0] = 0;
243 lastPublishMessagePayloadLen = 0;
244
245 setTLSRxBufferForPuback();
246 IoT_Error_t iotError;
247
248 if (topicType == JOB_DESCRIBE_TOPIC) {
249 AwsIotDescribeJobExecutionRequest request;
250 request.executionNumber = 0;
251 request.includeJobDocument = false;
252 request.clientToken = clientToken;
253
254 iotError = aws_iot_jobs_describe(
255 &client, QOS0, THING_NAME, passedJobId, &request, topicBuffer, sizeof(topicBuffer), bufferToPass, bufferLenToPass);
256 } else if (topicType == JOB_START_NEXT_TOPIC) {
257 AwsIotStartNextPendingJobExecutionRequest request;
258 request.statusDetails = NULL;
259 request.clientToken = clientToken;
260
261 iotError = aws_iot_jobs_start_next(
262 &client, QOS0, THING_NAME, &request, topicBuffer, sizeof(topicBuffer), messageBuffer, sizeof(messageBuffer));
263 } else {
264 iotError = aws_iot_jobs_send_query(
265 &client, QOS0, THING_NAME, passedJobId, clientToken, topicBuffer, sizeof(topicBuffer), bufferToPass, bufferLenToPass, topicType);
266 }
267
268 CHECK_EQUAL_C_INT(SUCCESS, iotError);
269 CHECK_EQUAL_C_INT(expectedTopicLength, (int)lastPublishMessageTopicLen);
270 CHECK_EQUAL_C_STRING(expectedTopic, LastPublishMessageTopic);
271
272 if (withClientToken) {
273 CHECK_EQUAL_C_INT((int)strlen(LastPublishMessagePayload), (int)lastPublishMessagePayloadLen);
274 CHECK_EQUAL_C_STRING("{\"clientToken\":\"FooBar\"}", LastPublishMessagePayload);
275 } else if (topicType == JOB_START_NEXT_TOPIC) {
276 CHECK_EQUAL_C_INT(2, (int)lastPublishMessagePayloadLen);
277 CHECK_EQUAL_C_STRING("{}", LastPublishMessagePayload);
278 } else {
279 CHECK_EQUAL_C_INT(0, (int)lastPublishMessagePayloadLen);
280 }
281
282 IOT_DEBUG("-->Success - test send query %s %s \n", getJobTopicTypeName(topicType), (withClientToken ? "(w/ clientToken)" : ""));
283}
284
285TEST_C(JobsInterfaceTest, TestSendQuery) {
286 int token;
287 for (token = 0; token < 2; ++token) {
288 testSendQuery(false, token == 0, JOB_GET_PENDING_TOPIC);
289 testSendQuery(true, token == 0, JOB_DESCRIBE_TOPIC);
290 testSendQuery(false, token == 0, JOB_START_NEXT_TOPIC);
291 }
292}
293
294TEST_C(JobsInterfaceTest, TestSendUpdate) {
295 AwsIotJobExecutionUpdateRequest updateRequest;
296 updateRequest.clientToken = "FooBar";
297 updateRequest.status = JOB_EXECUTION_SUCCEEDED;
298 updateRequest.expectedVersion = 1;
299 updateRequest.executionNumber = 0;
300 updateRequest.statusDetails = NULL;
301 updateRequest.includeJobExecutionState = false;
302 updateRequest.includeJobDocument = false;
303
304 char expectedTopic[MAX_JOB_TOPIC_LENGTH_BYTES + 1];
305 char topicBuffer[MAX_JOB_TOPIC_LENGTH_BYTES + 1];
306 char messageBuffer[AWS_IOT_MQTT_TX_BUF_LEN + 1];
307
308 IOT_DEBUG("\n-->Running Jobs Interface Tests - test send update \n");
309
310 int expectedTopicLength = aws_iot_jobs_get_api_topic(
311 expectedTopic, MAX_JOB_TOPIC_LENGTH_BYTES + 1,
312 JOB_UPDATE_TOPIC, JOB_REQUEST_TYPE,
313 THING_NAME, JOB_ID);
314
315 CHECK_C(expectedTopicLength > 0 && expectedTopicLength <= MAX_JOB_TOPIC_LENGTH_BYTES);
316
317 LastPublishMessageTopic[0] = 0;
318 lastPublishMessageTopicLen = 0;
319 LastPublishMessagePayload[0] = 0;
320 lastPublishMessagePayloadLen = 0;
321
322 setTLSRxBufferForPuback();
323 IoT_Error_t iotError;
324 iotError = aws_iot_jobs_send_update(
325 &client, QOS0, THING_NAME, JOB_ID, &updateRequest,
326 topicBuffer, sizeof(topicBuffer),
327 messageBuffer, sizeof(messageBuffer));
328
329 CHECK_EQUAL_C_INT(SUCCESS, iotError);
330
331 CHECK_EQUAL_C_INT(expectedTopicLength, (int)lastPublishMessageTopicLen);
332 CHECK_EQUAL_C_STRING(expectedTopic, LastPublishMessageTopic);
333
334 CHECK_EQUAL_C_STRING("{\"status\":\"SUCCEEDED\",\"expectedVersion\":1,\"clientToken\":\"FooBar\"}", LastPublishMessagePayload);
335
336 IOT_DEBUG("-->Success - test send update \n");
337}
diff --git a/tests/unit/src/aws_iot_tests_unit_jobs_json.c b/tests/unit/src/aws_iot_tests_unit_jobs_json.c
new file mode 100644
index 0000000..5a04554
--- /dev/null
+++ b/tests/unit/src/aws_iot_tests_unit_jobs_json.c
@@ -0,0 +1,244 @@
1/*
2* Copyright 2015-2018 Amazon.com, Inc. or its affiliates. All Rights Reserved.
3*
4* Licensed under the Apache License, Version 2.0 (the "License").
5* You may not use this file except in compliance with the License.
6* A copy of the License is located at
7*
8* http://aws.amazon.com/apache2.0
9*
10* or in the "license" file accompanying this file. This file is distributed
11* on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
12* express or implied. See the License for the specific language governing
13* permissions and limitations under the License.
14*/
15
16#include <stdio.h>
17#include <string.h>
18#include <unistd.h>
19#include <stdbool.h>
20#include <CppUTest/TestHarness_c.h>
21#include <aws_iot_jobs_json.h>
22#include <aws_iot_tests_unit_helper_functions.h>
23#include <aws_iot_log.h>
24
25#ifdef __cplusplus
26extern "C" {
27#endif
28
29#define NUM_TOKENS 50
30
31static jsmntok_t tokens[NUM_TOKENS];
32
33TEST_GROUP_C_SETUP(JobsJsonTests) {
34}
35
36TEST_GROUP_C_TEARDOWN(JobsJsonTests) {
37}
38
39static const char *defaultUpdateRequestSerialized =
40 "{\"status\":\"IN_PROGRESS\",\"statusDetails\":{\"Step\":\"1\",\"StepStatus\":\"Foo\"},"
41 "\"executionNumber\":2,\"expectedVersion\":1,\"includeJobExecutionState\":true,\"includeJobDocument\":true,\"clientToken\":\"1234\"}";
42
43static void fillDefaultUpdateRequest(AwsIotJobExecutionUpdateRequest *request)
44{
45 request->status = JOB_EXECUTION_IN_PROGRESS;
46 request->statusDetails = "{\"Step\":\"1\",\"StepStatus\":\"Foo\"}";
47 request->expectedVersion = 1;
48 request->executionNumber = 2;
49 request->includeJobExecutionState = true;
50 request->includeJobDocument = true;
51 request->clientToken = "1234";
52}
53
54TEST_C(JobsJsonTests, SerializeUpdateRequest) {
55 AwsIotJobExecutionUpdateRequest updateRequest;
56
57 IOT_DEBUG("\n-->Running Jobs Json Tests - serialize update request \n");
58
59 fillDefaultUpdateRequest(&updateRequest);
60
61 char buffer[256];
62 int charsUsed = aws_iot_jobs_json_serialize_update_job_execution_request(buffer, sizeof(buffer), &updateRequest);
63 CHECK_EQUAL_C_INT((int)strlen(defaultUpdateRequestSerialized), charsUsed);
64 CHECK_EQUAL_C_STRING(defaultUpdateRequestSerialized, buffer);
65
66 jsmn_parser parser;
67 jsmn_init(&parser);
68
69 int num_parsed_tokens = jsmn_parse(&parser, buffer, (size_t)charsUsed, tokens, NUM_TOKENS);
70 CHECK_C(num_parsed_tokens > 0);
71
72 IOT_DEBUG("-->Success - serialize update request \n");
73}
74
75TEST_C(JobsJsonTests, SerializeUpdateRequestWithNullBuffer) {
76 AwsIotJobExecutionUpdateRequest updateRequest;
77
78 IOT_DEBUG("\n-->Running Jobs Json Tests - serialize update request w/ null buffer \n");
79
80 fillDefaultUpdateRequest(&updateRequest);
81
82 int charsUsed = aws_iot_jobs_json_serialize_update_job_execution_request(NULL, 0, &updateRequest);
83 CHECK_EQUAL_C_INT((int)strlen(defaultUpdateRequestSerialized), charsUsed);
84
85 IOT_DEBUG("-->Success - serialize update request w/ null buffer \n");
86}
87
88TEST_C(JobsJsonTests, SerializeUpdateRequestWithTooSmallBuffer) {
89 AwsIotJobExecutionUpdateRequest updateRequest;
90
91 IOT_DEBUG("\n-->Running Jobs Json Tests - serialize update request w/ too small buffer \n");
92
93 fillDefaultUpdateRequest(&updateRequest);
94
95 char buffer[11];
96 // A check character which shouldn't be overwritten as we pass a length of 10
97 buffer[10] = -1;
98
99 int charsUsed = aws_iot_jobs_json_serialize_update_job_execution_request(buffer, 10, &updateRequest);
100 CHECK_EQUAL_C_INT((int)strlen(defaultUpdateRequestSerialized), charsUsed);
101
102 CHECK_EQUAL_C_CHAR(-1, buffer[10]);
103 CHECK_EQUAL_C_CHAR(0, buffer[9]);
104 // 9 because the 10th is \0
105 CHECK_C(strncmp(defaultUpdateRequestSerialized, buffer, 9) == 0);
106
107 IOT_DEBUG("-->Success - serialize update request w/ too small buffer \n");
108}
109
110static void fillDefaultDescribeJobExecutionRequest(
111 AwsIotDescribeJobExecutionRequest *request)
112{
113 request->executionNumber = 1;
114 request->includeJobDocument = true;
115 request->clientToken = "1234";
116}
117
118static const char *defaultDescribeJobExecutionRequestSerialized =
119 "{\"clientToken\":\"1234\",\"executionNumber\":1,\"includeJobDocument\":true}";
120
121TEST_C(JobsJsonTests, SerializeDescribeJobExecutionRequest) {
122 AwsIotDescribeJobExecutionRequest describeJobExecutionRequest;
123
124 IOT_DEBUG("\n-->Running Jobs Json Tests - serialize describe job execution request \n");
125
126 fillDefaultDescribeJobExecutionRequest(&describeJobExecutionRequest);
127
128 char buffer[256];
129 int charsUsed = aws_iot_jobs_json_serialize_describe_job_execution_request(buffer, 256, &describeJobExecutionRequest);
130 CHECK_EQUAL_C_INT((int)strlen(defaultDescribeJobExecutionRequestSerialized), charsUsed);
131
132 CHECK_EQUAL_C_STRING(defaultDescribeJobExecutionRequestSerialized, buffer);
133
134 jsmn_parser parser;
135 jsmn_init(&parser);
136
137 int num_parsed_tokens = jsmn_parse(&parser, buffer, (size_t)charsUsed, tokens, NUM_TOKENS);
138 CHECK_C(num_parsed_tokens > 0);
139
140 IOT_DEBUG("-->Success - serialize describe job execution request \n");
141}
142
143TEST_C(JobsJsonTests, SerializeDescribeJobExecutionRequestWithNullBuffer) {
144 AwsIotDescribeJobExecutionRequest describeJobExecutionRequest;
145
146 IOT_DEBUG("\n-->Running Jobs Json Tests - serialize describe job execution request w/ null buffer \n");
147
148 fillDefaultDescribeJobExecutionRequest(&describeJobExecutionRequest);
149
150 int charsUsed = aws_iot_jobs_json_serialize_describe_job_execution_request(NULL, 0, &describeJobExecutionRequest);
151 CHECK_EQUAL_C_INT(0, charsUsed);
152
153 IOT_DEBUG("-->Success - serialize describe job execution request w/ null buffer \n");
154}
155
156TEST_C(JobsJsonTests, SerializeDescribeJobExecutionRequestWithTooSmallBuffer) {
157 AwsIotDescribeJobExecutionRequest describeJobExecutionRequest;
158
159 IOT_DEBUG("\n-->Running Jobs Json Tests - serialize describe job execution request w/ too small buffer \n");
160
161 fillDefaultDescribeJobExecutionRequest(&describeJobExecutionRequest);
162
163 char buffer[11];
164 // A check character which shouldn't be overwritten as we pass a length of 10
165 buffer[10] = -1;
166
167 int charsUsed = aws_iot_jobs_json_serialize_describe_job_execution_request(buffer, 10, &describeJobExecutionRequest);
168 CHECK_EQUAL_C_INT((int)strlen(defaultDescribeJobExecutionRequestSerialized), charsUsed);
169
170 CHECK_EQUAL_C_CHAR(-1, buffer[10]);
171 CHECK_EQUAL_C_CHAR(0, buffer[9]);
172 // 9 because the 10th is \0
173 CHECK_C(strncmp(defaultDescribeJobExecutionRequestSerialized, buffer, 9) == 0);
174
175 IOT_DEBUG("-->Success - serialize describe job execution request w/ too small buffer \n");
176}
177
178static void fillDefaultStartNextRequest(AwsIotStartNextPendingJobExecutionRequest *request)
179{
180 request->statusDetails = "{\"Step\":\"1\",\"StepStatus\":\"Foo\"}";
181 request->clientToken = "1234";
182}
183
184static const char *defaultStartNextRequestSerialized =
185 "{\"statusDetails\":{\"Step\":\"1\",\"StepStatus\":\"Foo\"},\"clientToken\":\"1234\"}";
186
187TEST_C(JobsJsonTests, SerializeStartNextRequest) {
188 AwsIotStartNextPendingJobExecutionRequest startNextRequest;
189 fillDefaultStartNextRequest(&startNextRequest);
190
191 IOT_DEBUG("\n-->Running Jobs Json Tests - serialize start next request \n");
192
193 char buffer[256];
194 int charsUsed = aws_iot_jobs_json_serialize_start_next_job_execution_request(buffer, 256, &startNextRequest);
195 CHECK_EQUAL_C_INT((int)strlen(defaultStartNextRequestSerialized), charsUsed);
196
197 CHECK_EQUAL_C_STRING(defaultStartNextRequestSerialized, buffer);
198 jsmn_parser parser;
199 jsmn_init(&parser);
200
201 int num_parsed_tokens = jsmn_parse(&parser, buffer, (size_t)charsUsed, tokens, NUM_TOKENS);
202 CHECK_C(num_parsed_tokens > 0);
203
204 IOT_DEBUG("-->Success - serialize start next request \n");
205}
206
207TEST_C(JobsJsonTests, SerializeStartNextRequestWithNullBuffer) {
208 AwsIotStartNextPendingJobExecutionRequest startNextRequest;
209
210 IOT_DEBUG("\n-->Running Jobs Json Tests - serialize start next request w/ null buffer \n");
211
212 fillDefaultStartNextRequest(&startNextRequest);
213
214 int charsUsed = aws_iot_jobs_json_serialize_start_next_job_execution_request(NULL, 0, &startNextRequest);
215 CHECK_EQUAL_C_INT((int)strlen(defaultStartNextRequestSerialized), charsUsed);
216
217 IOT_DEBUG("-->Success - serialize start next request w/ null buffer \n");
218}
219
220TEST_C(JobsJsonTests, SerializeStartNextRequestWithTooSmallBuffer) {
221 AwsIotStartNextPendingJobExecutionRequest startNextRequest;
222
223 IOT_DEBUG("\n-->Running Jobs Json Tests - serialize start next request w/ too small buffer \n");
224
225 fillDefaultStartNextRequest(&startNextRequest);
226
227 char buffer[11];
228 // A check character which shouldn't be overwritten as we pass a length of 10
229 buffer[10] = -1;
230
231 int charsUsed = aws_iot_jobs_json_serialize_start_next_job_execution_request(buffer, 10, &startNextRequest);
232 CHECK_EQUAL_C_INT((int) strlen(defaultStartNextRequestSerialized), charsUsed);
233
234 CHECK_EQUAL_C_CHAR(-1, buffer[10]);
235 CHECK_EQUAL_C_CHAR(0, buffer[9]);
236 // 9 because the 10th is \0
237 CHECK_C(strncmp(defaultStartNextRequestSerialized, buffer, 9) == 0);
238
239 IOT_DEBUG("-->Success - serialize start next request w/ too small buffer \n");
240}
241
242#ifdef __cplusplus
243}
244#endif
diff --git a/tests/unit/src/aws_iot_tests_unit_jobs_topics.c b/tests/unit/src/aws_iot_tests_unit_jobs_topics.c
new file mode 100644
index 0000000..b6c6ace
--- /dev/null
+++ b/tests/unit/src/aws_iot_tests_unit_jobs_topics.c
@@ -0,0 +1,161 @@
1/*
2* Copyright 2015-2018 Amazon.com, Inc. or its affiliates. All Rights Reserved.
3*
4* Licensed under the Apache License, Version 2.0 (the "License").
5* You may not use this file except in compliance with the License.
6* A copy of the License is located at
7*
8* http://aws.amazon.com/apache2.0
9*
10* or in the "license" file accompanying this file. This file is distributed
11* on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
12* express or implied. See the License for the specific language governing
13* permissions and limitations under the License.
14*/
15
16#include "aws_iot_jobs_topics.h"
17#include <stdio.h>
18#include <string.h>
19#include <unistd.h>
20#include <stdbool.h>
21#include <CppUTest/TestHarness_c.h>
22#include <aws_iot_tests_unit_helper_functions.h>
23#include <aws_iot_log.h>
24
25#ifdef __cplusplus
26extern "C" {
27#endif
28
29TEST_GROUP_C_SETUP(JobsTopicsTests) {
30}
31
32TEST_GROUP_C_TEARDOWN(JobsTopicsTests) {
33}
34
35#define TEST_THING_NAME "TestThing"
36#define TEST_TOPIC_PREFIX "$aws/things/" TEST_THING_NAME "/jobs"
37#define TEST_JOB_ID "TestJob"
38#define TEST_NEXT_JOB_ID "$next"
39#define TEST_TOPIC_W_JOB_PREFIX TEST_TOPIC_PREFIX "/" TEST_JOB_ID
40
41static int generateTopicForTestThingAndJob(
42 char *buffer, size_t bufferSize,
43 AwsIotJobExecutionTopicType topicType, AwsIotJobExecutionTopicReplyType replyType)
44{
45 return aws_iot_jobs_get_api_topic(
46 buffer, bufferSize, topicType, replyType, TEST_THING_NAME, TEST_JOB_ID);
47}
48
49static int generateTopicForTestThing(
50 char *buffer, size_t bufferSize,
51 AwsIotJobExecutionTopicType topicType, AwsIotJobExecutionTopicReplyType replyType)
52{
53 return aws_iot_jobs_get_api_topic(
54 buffer, bufferSize, topicType, replyType, TEST_THING_NAME, NULL);
55}
56
57static void testGenerateValidApiTopic(
58 AwsIotJobExecutionTopicType topicType, AwsIotJobExecutionTopicReplyType replyType,
59 bool includeThingName, const char *expectedOutput)
60{
61 int requestedSize;
62 int expectedSize = (int)strlen(expectedOutput);
63
64 IOT_DEBUG("\n-->Running Jobs Topics Tests - generate valid topics %s w/ reply type \n", getJobTopicTypeName(topicType), getJobReplyTypeName(replyType));
65
66 if (includeThingName) {
67 requestedSize = generateTopicForTestThingAndJob(NULL, 0, topicType, replyType);
68 CHECK_EQUAL_C_INT(expectedSize, requestedSize);
69 } else {
70 requestedSize = generateTopicForTestThing(NULL, 0, topicType, replyType);
71 CHECK_EQUAL_C_INT(expectedSize, requestedSize);
72 }
73
74 CHECK_C(requestedSize > 3);
75 char *buffer = (char *) malloc((size_t)(requestedSize + 1) * sizeof(char));
76 buffer[3] = -1;
77
78 if (includeThingName) {
79 requestedSize = generateTopicForTestThingAndJob(buffer, 3, topicType, replyType);
80 } else {
81 requestedSize = generateTopicForTestThing(buffer, 3, topicType, replyType);
82 }
83 CHECK_EQUAL_C_INT(expectedSize, requestedSize);
84
85 CHECK_EQUAL_C_CHAR(expectedOutput[0], buffer[0]);
86 CHECK_EQUAL_C_CHAR(expectedOutput[1], buffer[1]);
87 CHECK_EQUAL_C_CHAR('\0', buffer[2]);
88 CHECK_EQUAL_C_CHAR(-1, buffer[3]);
89
90 if (includeThingName) {
91 requestedSize = generateTopicForTestThingAndJob(buffer, (size_t)requestedSize + 1, topicType, replyType);
92 } else {
93 requestedSize = generateTopicForTestThing(buffer, (size_t)requestedSize + 1, topicType, replyType);
94 }
95
96 CHECK_EQUAL_C_INT(expectedSize, requestedSize);
97 CHECK_EQUAL_C_STRING(expectedOutput, buffer);
98
99 free(buffer);
100
101 IOT_DEBUG("-->Success - generate valid topics %s w/ reply type \n", getJobTopicTypeName(topicType), getJobReplyTypeName(replyType));
102}
103
104static void testGenerateValidApiTopicWithAllReplyTypes(
105 AwsIotJobExecutionTopicType topicType, bool includeThingName,
106 const char *expectedBaseTopic)
107{
108 char buffer[1024];
109
110 testGenerateValidApiTopic(
111 topicType, JOB_REQUEST_TYPE, includeThingName, expectedBaseTopic);
112
113 snprintf(buffer, 1024, "%s/accepted", expectedBaseTopic);
114 testGenerateValidApiTopic(
115 topicType, JOB_ACCEPTED_REPLY_TYPE, includeThingName, buffer);
116
117 snprintf(buffer, 1024, "%s/rejected", expectedBaseTopic);
118 testGenerateValidApiTopic(
119 topicType, JOB_REJECTED_REPLY_TYPE, includeThingName, buffer);
120
121 snprintf(buffer, 1024, "%s/+", expectedBaseTopic);
122 testGenerateValidApiTopic(
123 topicType, JOB_WILDCARD_REPLY_TYPE, includeThingName, buffer);
124
125}
126
127TEST_C(JobsTopicsTests, GenerateValidTopics) {
128 testGenerateValidApiTopicWithAllReplyTypes(JOB_UPDATE_TOPIC, true, TEST_TOPIC_W_JOB_PREFIX "/update");
129 testGenerateValidApiTopicWithAllReplyTypes(JOB_DESCRIBE_TOPIC, true, TEST_TOPIC_W_JOB_PREFIX "/get");
130 testGenerateValidApiTopicWithAllReplyTypes(JOB_WILDCARD_TOPIC, true, TEST_TOPIC_W_JOB_PREFIX "/+");
131 testGenerateValidApiTopic(JOB_WILDCARD_TOPIC, JOB_REQUEST_TYPE, false, TEST_TOPIC_PREFIX "/#");
132 testGenerateValidApiTopic(JOB_NOTIFY_TOPIC, JOB_REQUEST_TYPE, false, TEST_TOPIC_PREFIX "/notify");
133 testGenerateValidApiTopic(JOB_NOTIFY_NEXT_TOPIC, JOB_REQUEST_TYPE, false, TEST_TOPIC_PREFIX "/notify-next");
134 testGenerateValidApiTopic(JOB_START_NEXT_TOPIC, JOB_REQUEST_TYPE, false, TEST_TOPIC_PREFIX "/start-next");
135}
136
137TEST_C(JobsTopicsTests, GenerateWithMissingJobId) {
138 CHECK_EQUAL_C_INT(-1, generateTopicForTestThing(NULL, 0, JOB_UPDATE_TOPIC, JOB_REQUEST_TYPE));
139 CHECK_EQUAL_C_INT(-1, generateTopicForTestThing(NULL, 0, JOB_DESCRIBE_TOPIC, JOB_REQUEST_TYPE));
140}
141
142TEST_C(JobsTopicsTests, GenerateWithInvalidTopicOrReplyType) {
143 CHECK_EQUAL_C_INT(-1, generateTopicForTestThing(NULL, 0, JOB_UNRECOGNIZED_TOPIC, JOB_REQUEST_TYPE));
144 CHECK_EQUAL_C_INT(-1, generateTopicForTestThing(NULL, 0, -1, JOB_REQUEST_TYPE));
145
146 CHECK_EQUAL_C_INT(-1, generateTopicForTestThing(NULL, 0, JOB_DESCRIBE_TOPIC, JOB_UNRECOGNIZED_TOPIC_TYPE));
147 CHECK_EQUAL_C_INT(-1, generateTopicForTestThing(NULL, 0, JOB_DESCRIBE_TOPIC, -1));
148}
149
150TEST_C(JobsTopicsTests, GenerateWithInvalidCombinations) {
151 CHECK_EQUAL_C_INT(-1, generateTopicForTestThing(NULL, 0, JOB_NOTIFY_TOPIC, JOB_ACCEPTED_REPLY_TYPE));
152 CHECK_EQUAL_C_INT(-1, generateTopicForTestThing(NULL, 0, JOB_NOTIFY_TOPIC, JOB_REJECTED_REPLY_TYPE));
153 CHECK_EQUAL_C_INT(-1, generateTopicForTestThing(NULL, 0, JOB_NOTIFY_TOPIC, JOB_WILDCARD_REPLY_TYPE));
154 CHECK_EQUAL_C_INT(-1, generateTopicForTestThing(NULL, 0, JOB_NOTIFY_NEXT_TOPIC, JOB_ACCEPTED_REPLY_TYPE));
155 CHECK_EQUAL_C_INT(-1, generateTopicForTestThing(NULL, 0, JOB_NOTIFY_NEXT_TOPIC, JOB_REJECTED_REPLY_TYPE));
156 CHECK_EQUAL_C_INT(-1, generateTopicForTestThing(NULL, 0, JOB_NOTIFY_NEXT_TOPIC, JOB_WILDCARD_REPLY_TYPE));
157}
158
159#ifdef __cplusplus
160}
161#endif
diff --git a/tests/unit/src/aws_iot_tests_unit_jobs_types.c b/tests/unit/src/aws_iot_tests_unit_jobs_types.c
new file mode 100644
index 0000000..60dc8c9
--- /dev/null
+++ b/tests/unit/src/aws_iot_tests_unit_jobs_types.c
@@ -0,0 +1,70 @@
1/*
2* Copyright 2015-2018 Amazon.com, Inc. or its affiliates. All Rights Reserved.
3*
4* Licensed under the Apache License, Version 2.0 (the "License").
5* You may not use this file except in compliance with the License.
6* A copy of the License is located at
7*
8* http://aws.amazon.com/apache2.0
9*
10* or in the "license" file accompanying this file. This file is distributed
11* on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
12* express or implied. See the License for the specific language governing
13* permissions and limitations under the License.
14*/
15
16#include <stdio.h>
17#include <string.h>
18#include <unistd.h>
19#include <stdbool.h>
20#include <CppUTest/TestHarness_c.h>
21#include <aws_iot_jobs_types.h>
22#include <aws_iot_tests_unit_helper_functions.h>
23#include <aws_iot_log.h>
24
25#ifdef __cplusplus
26extern "C" {
27#endif
28
29TEST_GROUP_C_SETUP(JobsTypesTests) {
30}
31
32TEST_GROUP_C_TEARDOWN(JobsTypesTests) {
33}
34
35TEST_C(JobsTypesTests, StringToStatus) {
36 char emptyString = '\0';
37
38 IOT_DEBUG("\n-->Running Jobs Types Tests - string to status \n");
39
40 CHECK_EQUAL_C_INT((int)JOB_EXECUTION_STATUS_NOT_SET, (int)aws_iot_jobs_map_string_to_job_status(NULL));
41 CHECK_EQUAL_C_INT((int)JOB_EXECUTION_STATUS_NOT_SET, (int)aws_iot_jobs_map_string_to_job_status(&emptyString));
42 CHECK_EQUAL_C_INT((int)JOB_EXECUTION_QUEUED, (int)aws_iot_jobs_map_string_to_job_status("QUEUED"));
43 CHECK_EQUAL_C_INT((int)JOB_EXECUTION_IN_PROGRESS, (int)aws_iot_jobs_map_string_to_job_status("IN_PROGRESS"));
44 CHECK_EQUAL_C_INT((int)JOB_EXECUTION_FAILED, (int)aws_iot_jobs_map_string_to_job_status("FAILED"));
45 CHECK_EQUAL_C_INT((int)JOB_EXECUTION_SUCCEEDED, (int)aws_iot_jobs_map_string_to_job_status("SUCCEEDED"));
46 CHECK_EQUAL_C_INT((int)JOB_EXECUTION_CANCELED, (int)aws_iot_jobs_map_string_to_job_status("CANCELED"));
47 CHECK_EQUAL_C_INT((int)JOB_EXECUTION_REJECTED, (int)aws_iot_jobs_map_string_to_job_status("REJECTED"));
48 CHECK_EQUAL_C_INT((int)JOB_EXECUTION_UNKNOWN_STATUS, (int)aws_iot_jobs_map_string_to_job_status("invalidStatus"));
49
50 IOT_DEBUG("-->Success - string to status \n");
51}
52
53TEST_C(JobsTypesTests, StatusToString) {
54 IOT_DEBUG("\n-->Running Jobs Types Tests - status to string \n");
55
56 CHECK_EQUAL_C_STRING("QUEUED", aws_iot_jobs_map_status_to_string(JOB_EXECUTION_QUEUED));
57 CHECK_EQUAL_C_STRING("IN_PROGRESS", aws_iot_jobs_map_status_to_string(JOB_EXECUTION_IN_PROGRESS));
58 CHECK_EQUAL_C_STRING("FAILED", aws_iot_jobs_map_status_to_string(JOB_EXECUTION_FAILED));
59 CHECK_EQUAL_C_STRING("SUCCEEDED", aws_iot_jobs_map_status_to_string(JOB_EXECUTION_SUCCEEDED));
60 CHECK_EQUAL_C_STRING("CANCELED", aws_iot_jobs_map_status_to_string(JOB_EXECUTION_CANCELED));
61 CHECK_EQUAL_C_STRING("REJECTED", aws_iot_jobs_map_status_to_string(JOB_EXECUTION_REJECTED));
62 CHECK_EQUAL_C_STRING(NULL, aws_iot_jobs_map_status_to_string(JOB_EXECUTION_STATUS_NOT_SET));
63 CHECK_EQUAL_C_STRING(NULL, aws_iot_jobs_map_status_to_string(JOB_EXECUTION_UNKNOWN_STATUS));
64
65 IOT_DEBUG("-->Success - status to string \n");
66}
67
68#ifdef __cplusplus
69}
70#endif
diff --git a/tests/unit/src/aws_iot_tests_unit_shadow_action_helper.c b/tests/unit/src/aws_iot_tests_unit_shadow_action_helper.c
index 59bb3c8..684603d 100644
--- a/tests/unit/src/aws_iot_tests_unit_shadow_action_helper.c
+++ b/tests/unit/src/aws_iot_tests_unit_shadow_action_helper.c
@@ -759,12 +759,12 @@ TEST_C(ShadowActionTests, ACKWaitingMoreThanAllowed) {
759 IOT_DEBUG("-->Success - Ack waiting more than allowed wait time \n"); 759 IOT_DEBUG("-->Success - Ack waiting more than allowed wait time \n");
760} 760}
761 761
762#define JSON_SIZE_OVERFLOW "{\"state\":{\"reported\":{\"file\":\"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\"}}, \"clientToken\":\"" AWS_IOT_MQTT_CLIENT_ID "-0\"}"
763
764TEST_C(ShadowActionTests, InboundDataTooBigForBuffer) { 762TEST_C(ShadowActionTests, InboundDataTooBigForBuffer) {
763 uint32_t i = 0;
765 IoT_Error_t ret_val = SUCCESS; 764 IoT_Error_t ret_val = SUCCESS;
766 char getRequestJson[120]; 765 char getRequestJson[120];
767 IoT_Publish_Message_Params params; 766 IoT_Publish_Message_Params params;
767 char expectedCallbackString[AWS_IOT_MQTT_RX_BUF_LEN + 2];
768 768
769 IOT_DEBUG("-->Running Shadow Action Tests - Inbound data too big for buffer \n"); 769 IOT_DEBUG("-->Running Shadow Action Tests - Inbound data too big for buffer \n");
770 770
@@ -775,8 +775,13 @@ TEST_C(ShadowActionTests, InboundDataTooBigForBuffer) {
775 false); 775 false);
776 CHECK_EQUAL_C_INT(SUCCESS, ret_val); 776 CHECK_EQUAL_C_INT(SUCCESS, ret_val);
777 777
778 params.payloadLen = strlen(JSON_SIZE_OVERFLOW); 778 for(i = 0; i < AWS_IOT_MQTT_RX_BUF_LEN; i++) {
779 params.payload = JSON_SIZE_OVERFLOW; 779 expectedCallbackString[i] = 'X';
780 }
781 expectedCallbackString[i + 1] = '\0';
782
783 params.payloadLen = strlen(expectedCallbackString);
784 params.payload = expectedCallbackString;
780 params.qos = QOS0; 785 params.qos = QOS0;
781 setTLSRxBufferWithMsgOnSubscribedTopic(GET_ACCEPTED_TOPIC, strlen(GET_ACCEPTED_TOPIC), QOS0, params, 786 setTLSRxBufferWithMsgOnSubscribedTopic(GET_ACCEPTED_TOPIC, strlen(GET_ACCEPTED_TOPIC), QOS0, params,
782 params.payload); 787 params.payload);
diff --git a/tests/unit/tls_mock/aws_iot_tests_unit_mock_tls.c b/tests/unit/tls_mock/aws_iot_tests_unit_mock_tls.c
index f9a6cc7..950b5a1 100644
--- a/tests/unit/tls_mock/aws_iot_tests_unit_mock_tls.c
+++ b/tests/unit/tls_mock/aws_iot_tests_unit_mock_tls.c
@@ -72,10 +72,45 @@ IoT_Error_t iot_tls_is_connected(Network *pNetwork) {
72 return NETWORK_PHYSICAL_LAYER_CONNECTED; 72 return NETWORK_PHYSICAL_LAYER_CONNECTED;
73} 73}
74 74
75static size_t iot_tls_mqtt_read_variable_length_int(const unsigned char *buffer, size_t startPos) {
76 size_t result = 0;
77 size_t pos = startPos;
78 size_t multiplier = 1;
79 do {
80 result = (buffer[pos] & 0x7f) * multiplier;
81 multiplier *= 0x80;
82 pos++;
83 } while ((buffer[pos - 1] & 0x80) && pos - startPos < 4);
84
85 return result;
86}
87
88static size_t iot_tls_mqtt_get_end_of_variable_length_int(const unsigned char *buffer, size_t startPos) {
89 size_t pos = startPos;
90 while ((buffer[pos] & 0x80) && pos - startPos < 3) pos++;
91 pos++;
92
93 return pos;
94}
95
96static uint16_t iot_tls_mqtt_get_fixed_uint16_from_message(const unsigned char *msgBuffer, size_t startPos) {
97 uint8_t firstByte = (uint8_t)(msgBuffer[startPos]);
98 uint8_t secondByte = (uint8_t)(msgBuffer[startPos + 1]);
99 return (uint16_t) (secondByte + (256 * firstByte));
100}
101
102static uint16_t iot_tls_mqtt_copy_string_from_message(char *dest, const unsigned char *msgBuffer, size_t startPos) {
103 uint16_t length = iot_tls_mqtt_get_fixed_uint16_from_message(msgBuffer, startPos);
104
105 snprintf(dest, length + 1u, "%s", &(msgBuffer[startPos + 2])); // Added one for null character
106 return length;
107}
108
75IoT_Error_t iot_tls_write(Network *pNetwork, unsigned char *pMsg, size_t len, Timer *timer, size_t *written_len) { 109IoT_Error_t iot_tls_write(Network *pNetwork, unsigned char *pMsg, size_t len, Timer *timer, size_t *written_len) {
76 size_t i = 0; 110 size_t i = 0;
77 uint8_t firstByte, secondByte; 111 uint8_t firstPacketByte;
78 uint16_t topicNameLen; 112 size_t mqttPacketLength;
113 size_t variableHeaderStart;
79 IOT_UNUSED(pNetwork); 114 IOT_UNUSED(pNetwork);
80 IOT_UNUSED(timer); 115 IOT_UNUSED(timer);
81 116
@@ -85,17 +120,35 @@ IoT_Error_t iot_tls_write(Network *pNetwork, unsigned char *pMsg, size_t len, Ti
85 TxBuffer.len = len; 120 TxBuffer.len = len;
86 *written_len = len; 121 *written_len = len;
87 122
123 mqttPacketLength = iot_tls_mqtt_read_variable_length_int(TxBuffer.pBuffer, 1);
124 variableHeaderStart = iot_tls_mqtt_get_end_of_variable_length_int(TxBuffer.pBuffer, 1);
125
126 firstPacketByte = TxBuffer.pBuffer[0];
88 /* Save last two subscribed topics */ 127 /* Save last two subscribed topics */
89 if((TxBuffer.pBuffer[0] == 0x82 ? true : false)) { 128 if((firstPacketByte == 0x82 ? true : false)) {
90 snprintf(SecondLastSubscribeMessage, lastSubscribeMsgLen, "%s", LastSubscribeMessage); 129 snprintf(SecondLastSubscribeMessage, lastSubscribeMsgLen + 1u, "%s", LastSubscribeMessage);
91 secondLastSubscribeMsgLen = lastSubscribeMsgLen; 130 secondLastSubscribeMsgLen = lastSubscribeMsgLen;
92 131
93 firstByte = (uint8_t)(TxBuffer.pBuffer[4]); 132 lastSubscribeMsgLen = iot_tls_mqtt_copy_string_from_message(
94 secondByte = (uint8_t)(TxBuffer.pBuffer[5]); 133 LastSubscribeMessage, TxBuffer.pBuffer, variableHeaderStart + 2);
95 topicNameLen = (uint16_t) (secondByte + (256 * firstByte)); 134 } else if (firstPacketByte == 0xA2) {
135 lastUnsubscribeMsgLen = iot_tls_mqtt_copy_string_from_message(
136 LastUnsubscribeMessage, TxBuffer.pBuffer, variableHeaderStart + 2);
137 } else if ((firstPacketByte & 0x30) == 0x30) {
138 QoS qos = (QoS) (firstPacketByte & 0x6 >> 1);
139
140 lastPublishMessageTopicLen =
141 iot_tls_mqtt_copy_string_from_message(LastPublishMessageTopic, TxBuffer.pBuffer, variableHeaderStart);
142
143 size_t payloadStart = variableHeaderStart + 2 + lastPublishMessageTopicLen;
144
145 if (qos > QOS0) {
146 payloadStart += 2;
147 }
96 148
97 snprintf(LastSubscribeMessage, topicNameLen + 1u, "%s", &(TxBuffer.pBuffer[6])); // Added one for null character 149 lastPublishMessagePayloadLen = mqttPacketLength - payloadStart + 2; /* + 2 as the first two bytes don't count towards the length */
98 lastSubscribeMsgLen = topicNameLen + 1u; 150 memcpy(LastPublishMessagePayload, TxBuffer.pBuffer + payloadStart, lastPublishMessagePayloadLen);
151 LastPublishMessagePayload[lastPublishMessagePayloadLen] = 0;
99 } 152 }
100 153
101 return SUCCESS; 154 return SUCCESS;
diff --git a/tests/unit/tls_mock/aws_iot_tests_unit_mock_tls_params.c b/tests/unit/tls_mock/aws_iot_tests_unit_mock_tls_params.c
index efe8550..7523cdc 100644
--- a/tests/unit/tls_mock/aws_iot_tests_unit_mock_tls_params.c
+++ b/tests/unit/tls_mock/aws_iot_tests_unit_mock_tls_params.c
@@ -27,6 +27,14 @@ size_t lastSubscribeMsgLen;
27char SecondLastSubscribeMessage[TLSMaxBufferSize]; 27char SecondLastSubscribeMessage[TLSMaxBufferSize];
28size_t secondLastSubscribeMsgLen; 28size_t secondLastSubscribeMsgLen;
29 29
30char LastUnsubscribeMessage[TLSMaxBufferSize];
31size_t lastUnsubscribeMsgLen;
32
33char LastPublishMessageTopic[TLSMaxBufferSize];
34size_t lastPublishMessageTopicLen;
35char LastPublishMessagePayload[TLSMaxBufferSize];
36size_t lastPublishMessagePayloadLen;
37
30TlsBuffer RxBuffer = {.pBuffer = RxBuf,.len = 512, .NoMsgFlag=1, .expiry_time = {0, 0}, .BufMaxSize = TLSMaxBufferSize}; 38TlsBuffer RxBuffer = {.pBuffer = RxBuf,.len = 512, .NoMsgFlag=1, .expiry_time = {0, 0}, .BufMaxSize = TLSMaxBufferSize};
31TlsBuffer TxBuffer = {.pBuffer = TxBuf,.len = 512, .NoMsgFlag=1, .expiry_time = {0, 0}, .BufMaxSize = TLSMaxBufferSize}; 39TlsBuffer TxBuffer = {.pBuffer = TxBuf,.len = 512, .NoMsgFlag=1, .expiry_time = {0, 0}, .BufMaxSize = TLSMaxBufferSize};
32 40
diff --git a/tests/unit/tls_mock/aws_iot_tests_unit_mock_tls_params.h b/tests/unit/tls_mock/aws_iot_tests_unit_mock_tls_params.h
index 22a9ead..cfaad23 100644
--- a/tests/unit/tls_mock/aws_iot_tests_unit_mock_tls_params.h
+++ b/tests/unit/tls_mock/aws_iot_tests_unit_mock_tls_params.h
@@ -25,8 +25,9 @@
25#include <stddef.h> 25#include <stddef.h>
26#include <stdint.h> 26#include <stdint.h>
27#include <stdbool.h> 27#include <stdbool.h>
28#include <aws_iot_mqtt_client.h>
28 29
29#define TLSMaxBufferSize 1024 30#define TLSMaxBufferSize 2560
30#define NO_MSG_LENGTH_MENTIONED -1 31#define NO_MSG_LENGTH_MENTIONED -1
31 32
32typedef struct { 33typedef struct {
@@ -49,6 +50,14 @@ extern size_t lastSubscribeMsgLen;
49extern char SecondLastSubscribeMessage[TLSMaxBufferSize]; 50extern char SecondLastSubscribeMessage[TLSMaxBufferSize];
50extern size_t secondLastSubscribeMsgLen; 51extern size_t secondLastSubscribeMsgLen;
51 52
53extern char LastUnsubscribeMessage[TLSMaxBufferSize];
54extern size_t lastUnsubscribeMsgLen;
55
56extern char LastPublishMessageTopic[TLSMaxBufferSize];
57extern size_t lastPublishMessageTopicLen;
58extern char LastPublishMessagePayload[TLSMaxBufferSize];
59extern size_t lastPublishMessagePayloadLen;
60
52extern char hostAddress[512]; 61extern char hostAddress[512];
53extern uint16_t port; 62extern uint16_t port;
54extern uint32_t handshakeTimeout_ms; 63extern uint32_t handshakeTimeout_ms;