From b85b3062a5049ad6fb18f648a7abede8a0e27432 Mon Sep 17 00:00:00 2001
From: Kody Stribrny <89810515+kstribrnAmzn@users.noreply.github.com>
Date: Mon, 8 Jul 2024 07:24:13 -0700
Subject: [PATCH] Optional status details (#105)
* Feat: Support StatusDetails messaging in Update Message
Allows users to send a valid JSON key-value set to the
jobs service. This API does lightweight
validation of the status details payload.
---
docs/doxygen/include/size_table.md | 8 +-
source/include/jobs.h | 61 +++++++++----
source/jobs.c | 107 +++++++++++++++-------
test/unit-test/jobs_utest.c | 138 ++++++++++++++++++++++++-----
4 files changed, 243 insertions(+), 71 deletions(-)
diff --git a/docs/doxygen/include/size_table.md b/docs/doxygen/include/size_table.md
index 67eb14c..6296ec7 100644
--- a/docs/doxygen/include/size_table.md
+++ b/docs/doxygen/include/size_table.md
@@ -9,8 +9,8 @@
jobs.c |
- 2.8K |
- 2.5K |
+ 2.9K |
+ 2.6K |
job_parser.c |
@@ -29,7 +29,7 @@
Total estimates |
- 6.8K |
- 6.0K |
+ 6.9K |
+ 6.1K |
diff --git a/source/include/jobs.h b/source/include/jobs.h
index adeb223..0939e54 100644
--- a/source/include/jobs.h
+++ b/source/include/jobs.h
@@ -150,6 +150,9 @@
#define JOBS_API_EXPECTED_VERSION "\",\"expectedVersion\":\""
#define JOBS_API_EXPECTED_VERSION_LENGTH ( sizeof( JOBS_API_EXPECTED_VERSION ) - 1U )
+#define JOBS_API_STATUS_DETAILS "\",\"statusDetails\":\""
+#define JOBS_API_STATUS_DETAILS_LENGTH ( sizeof( JOBS_API_STATUS_DETAILS ) - 1U )
+
#define JOBS_API_COMMON_LENGTH( thingNameLength ) \
( JOBS_API_PREFIX_LENGTH + ( thingNameLength ) + JOBS_API_BRIDGE_LENGTH )
@@ -314,6 +317,32 @@ typedef enum
JobsMaxTopic
} JobsTopic_t;
+/**
+ * @ingroup jobs_struct_types
+ * @brief Structure for Jobs_UpdateMsg request parameters.
+ *
+ * @note For optional fields setting a pointer to NULL or
+ * the length to 0U will disable this field from being used.
+ *
+ *
+ * @note Optional fields include:
+ * * expectedVersion
+ * * expectedVersionLength
+ * * statusDetails
+ * * statusDetailsLength
+ *
+ * @note The status details must be a JSON formatted key-value
+ * pair.
+ */
+typedef struct
+{
+ JobCurrentStatus_t status; /**< Status to update the job to. */
+ const char * expectedVersion; /**< Expected version, optional. */
+ size_t expectedVersionLength; /**< Expected version length, optional. */
+ const char * statusDetails; /**< JSON key-value pair, optional. */
+ size_t statusDetailsLength; /**< JSON key-value pair length, optional. */
+} JobsUpdateRequest_t;
+
/*-----------------------------------------------------------*/
/**
@@ -838,31 +867,33 @@ JobsStatus_t Jobs_Update( char * buffer,
/**
* @brief Populate a message string for an UpdateJobExecution request.
*
- * @param status Current status of the job
- * @param expectedVersion The version that is expected, NULL if no version is expected
- * @param expectedVersionLength The length of the expectedVersion string, 0U if no version is expected
- * @param buffer The buffer to be written to
- * @param bufferSize the size of the buffer
+ * @param request A jobs update request structure.
+ * @param buffer The buffer to be written to.
+ * @param bufferSize the size of the buffer.
*
- * @return 0 if write to buffer fails
- * @return messageLength if the write is successful
+ * @return 0 if write to buffer fails.
+ * @return messageLength if the write is successful.
*
* Example
* @code{c}
*
* // The Following Example shows usage of the Jobs_UpdateMsg API to
* // generate a message string for the UpdateJobExecution API
- * // of the AWS IoT Jobs Service
+ * // of the AWS IoT Jobs Service.
*
* const char * expectedVersion = "2";
- * size_t expectedVersionLength = ( sizeof(expectedVersion ) - 1U );
- * JobCurrentStatus_t status = Succeeded;
+ * const chat * statusDetails = "{\"key\":\"value\"}";
* char messageBuffer[ UPDATE_JOB_MSG_LENGTH ] = {0};
* size_t messageLength = 0U;
*
- * messageLength = Jobs_UpdateMsg( status,
- * expectedVersion,
- * expectedVersionLength,
+ * JobsUpdateRequest_t request;
+ * request.status = Succeeded;
+ * request.expectedVersion = expectedVersion;
+ * request.expectedVersionLength = ( sizeof( expectedVersion ) - 1U );
+ * request.statusDetails = statusDetails;
+ * request.statusDetailsLength = ( sizeof( statusDetails ) - 1U );
+ *
+ * messageLength = Jobs_UpdateMsg( request
* messageBuffer,
* UPDATE_JOB_MSG_LENGTH );
*
@@ -876,9 +907,7 @@ JobsStatus_t Jobs_Update( char * buffer,
* @endcode
*/
/* @[declare_jobs_updatemsg] */
-size_t Jobs_UpdateMsg( JobCurrentStatus_t status,
- const char * expectedVersion,
- size_t expectedVersionLength,
+size_t Jobs_UpdateMsg( JobsUpdateRequest_t request,
char * buffer,
size_t bufferSize );
/* @[declare_jobs_updatemsg] */
diff --git a/source/jobs.c b/source/jobs.c
index e66755d..bd15740 100644
--- a/source/jobs.c
+++ b/source/jobs.c
@@ -82,6 +82,15 @@ static const size_t apiTopicLength[] =
JOBS_API_UPDATE_LENGTH + JOBS_API_FAILURE_LENGTH,
};
+static const char * const jobStatusString[] =
+{
+ "QUEUED",
+ "IN_PROGRESS",
+ "FAILED",
+ "SUCCEEDED",
+ "REJECTED"
+};
+
/**
* @brief Predicate returns true for a valid thing name or job ID character.
*
@@ -813,53 +822,91 @@ JobsStatus_t Jobs_Update( char * buffer,
return ret;
}
-size_t Jobs_UpdateMsg( JobCurrentStatus_t status,
- const char * expectedVersion,
- size_t expectedVersionLength,
- char * buffer,
- size_t bufferSize )
+/**
+ * @brief Get the total length of optional fields provided for
+ * the Jobs_UpdateMsg. These optional fields, if provided, require
+ * additional buffer space.
+ *
+ * @param request A JobsUpdateRequest_t containing the optional fields.
+ * @return size_t The buffer space required for the optional fields.
+ */
+static size_t getOptionalFieldsLength( JobsUpdateRequest_t request )
{
- static const char * const jobStatusString[] =
+ size_t minimumOptionalFieldsBufferSize = 0U;
+
+ if( ( request.expectedVersion != NULL ) && ( request.expectedVersionLength > 0U ) )
{
- "QUEUED",
- "IN_PROGRESS",
- "FAILED",
- "SUCCEEDED",
- "REJECTED"
- };
+ minimumOptionalFieldsBufferSize += JOBS_API_EXPECTED_VERSION_LENGTH + request.expectedVersionLength;
+ }
- static const size_t jobStatusStringLengths[] =
+ if( ( request.statusDetails != NULL ) && ( request.statusDetailsLength ) )
{
- CONST_STRLEN( "QUEUED" ),
- CONST_STRLEN( "IN_PROGRESS" ),
- CONST_STRLEN( "FAILED" ),
- CONST_STRLEN( "SUCCEEDED" ),
- CONST_STRLEN( "REJECTED" )
- };
+ minimumOptionalFieldsBufferSize += JOBS_API_STATUS_DETAILS_LENGTH + request.statusDetailsLength;
+ }
- assert( ( ( size_t ) status ) < ARRAY_LENGTH( jobStatusString ) );
+ return minimumOptionalFieldsBufferSize;
+}
- size_t start = 0U;
- size_t minimumBufferSize = JOBS_API_STATUS_LENGTH + jobStatusStringLengths[ status ] + CONST_STRLEN( "\"}" );
+/**
+ * @brief Get the total length of the required fields in the
+ * Jobs_UpdateMsg request.
+ *
+ * @param request A JobsUpdateRequest_t containing the optional fields.
+ * @return size_t The buffer space required for the optional fields.
+ */
+static size_t getRequiredFieldsLength( JobsUpdateRequest_t request )
+{
+ return JOBS_API_STATUS_LENGTH + strlen( jobStatusString[ request.status ] ) + CONST_STRLEN( "\"}" );
+}
- if( ( expectedVersion != NULL ) && ( expectedVersionLength > 0U ) )
+/**
+ * @brief Check non-null optional fields in the Jobs_UpdateMsg request
+ * for validity.
+ *
+ * @param request A JobsUpdateRequest_t containing the optional fields.
+ * @return true Optional fields appear valid.
+ * @return false Optional fields are invalid.
+ */
+static bool areOptionalFieldsValid( JobsUpdateRequest_t request )
+{
+ bool optionalFieldsValid = true;
+
+ if( ( request.statusDetails != NULL ) && ( request.statusDetailsLength ) )
{
- minimumBufferSize += JOBS_API_EXPECTED_VERSION_LENGTH + expectedVersionLength;
+ optionalFieldsValid &= ( JSONSuccess == JSON_Validate( request.statusDetails, request.statusDetailsLength ) );
}
- bool writeFailed = bufferSize < minimumBufferSize;
+ return optionalFieldsValid;
+}
+
+size_t Jobs_UpdateMsg( JobsUpdateRequest_t request,
+ char * buffer,
+ size_t bufferSize )
+{
+ assert( ( ( size_t ) request.status ) < ARRAY_LENGTH( jobStatusString ) );
+
+ size_t start = 0U;
+ size_t minimumBufferSize = getRequiredFieldsLength( request ) + getOptionalFieldsLength( request );
+ bool writeFailed = bufferSize < minimumBufferSize || !areOptionalFieldsValid( request );
- if( !writeFailed && ( jobStatusString[ status ] != NULL ) )
+ if( !writeFailed )
{
( void ) strnAppend( buffer, &start, bufferSize, JOBS_API_STATUS, JOBS_API_STATUS_LENGTH );
- ( void ) strnAppend( buffer, &start, bufferSize, jobStatusString[ status ], jobStatusStringLengths[ status ] );
+ ( void ) strnAppend( buffer, &start, bufferSize, jobStatusString[ request.status ], strlen( jobStatusString[ request.status ] ) );
}
- /* This is an optional field so do not fail if expected version is missing */
- if( !writeFailed && ( expectedVersion != NULL ) && ( expectedVersionLength > 0U ) )
+ /* This is an optional field so do not fail if expected version is missing.*/
+ if( !writeFailed && ( request.expectedVersion != NULL ) && ( request.expectedVersionLength > 0U ) )
{
( void ) strnAppend( buffer, &start, bufferSize, JOBS_API_EXPECTED_VERSION, JOBS_API_EXPECTED_VERSION_LENGTH );
- ( void ) strnAppend( buffer, &start, bufferSize, expectedVersion, expectedVersionLength );
+ ( void ) strnAppend( buffer, &start, bufferSize, request.expectedVersion, request.expectedVersionLength );
+ }
+
+ /* This is an optional field so do not fail if status details is missing.*/
+ if( !writeFailed && ( request.statusDetails != NULL ) && ( request.statusDetailsLength > 0U ) )
+ {
+ ( void ) strnAppend( buffer, &start, bufferSize, JOBS_API_STATUS_DETAILS, JOBS_API_STATUS_DETAILS_LENGTH );
+ ( void ) strnAppend( buffer, &start, bufferSize, request.statusDetails, request.statusDetailsLength );
}
if( !writeFailed )
diff --git a/test/unit-test/jobs_utest.c b/test/unit-test/jobs_utest.c
index cf11ae7..bbfd006 100644
--- a/test/unit-test/jobs_utest.c
+++ b/test/unit-test/jobs_utest.c
@@ -878,65 +878,161 @@ void test_getStartNextPendingJobExecutionMsg_hasValidParameters( void )
/*Tests for getUpdateJobExecutionMsg */
void test_getUpdateJobExecutionMsg_hasNullExpectedVersion( void )
{
- JobCurrentStatus_t status = Queued;
char buffer[ TOPIC_BUFFER_SIZE + 1 ] = { 0 };
- size_t result = Jobs_UpdateMsg( status, NULL, 1U, buffer, TOPIC_BUFFER_SIZE );
+ JobsUpdateRequest_t request =
+ {
+ Queued,
+ NULL,
+ 1,
+ "{\"key\": \"value\"}",
+ strlen( "{\"key\": \"value\"}" )
+ };
- TEST_ASSERT_EQUAL( 19U, result );
+ size_t result = Jobs_UpdateMsg( request, buffer, TOPIC_BUFFER_SIZE );
+
+ TEST_ASSERT_EQUAL( 54U, result );
+ TEST_ASSERT_EQUAL_STRING( "{\"status\":\"QUEUED\",\"statusDetails\":\"{\"key\": \"value\"}\"}", buffer );
}
void test_getUpdateJobExecutionMsg_hasZeroLengthExpectedVersion( void )
{
- char * version = "1.0.1";
- JobCurrentStatus_t status = Queued;
char buffer[ TOPIC_BUFFER_SIZE + 1 ] = { 0 };
+ JobsUpdateRequest_t request =
+ {
+ Queued,
+ "1.0.1",
+ 0,
+ "{\"key\": \"value\"}",
+ strlen( "{\"key\": \"value\"}" )
+ };
- size_t result = Jobs_UpdateMsg( status, version, 0U, buffer, TOPIC_BUFFER_SIZE );
+ size_t result = Jobs_UpdateMsg( request, buffer, TOPIC_BUFFER_SIZE );
- TEST_ASSERT_EQUAL( 19U, result );
+ TEST_ASSERT_EQUAL( 54U, result );
+ TEST_ASSERT_EQUAL_STRING( "{\"status\":\"QUEUED\",\"statusDetails\":\"{\"key\": \"value\"}\"}", buffer );
+}
+
+void test_getUpdateJobExecutionMsg_hasNullStatusDetails( void )
+{
+ char buffer[ TOPIC_BUFFER_SIZE + 1 ] = { 0 };
+ JobsUpdateRequest_t request =
+ {
+ Queued,
+ "1.0.1",
+ strlen( "1.0.1" ),
+ NULL,
+ strlen( "{\"key\": \"value\"}" )
+ };
+
+ size_t result = Jobs_UpdateMsg( request, buffer, TOPIC_BUFFER_SIZE );
+
+ TEST_ASSERT_EQUAL( 45U, result );
+ TEST_ASSERT_EQUAL_STRING( "{\"status\":\"QUEUED\",\"expectedVersion\":\"1.0.1\"}", buffer );
+}
+
+void test_getUpdateJobExecutionMsg_hasZeroLengthStatusDetails( void )
+{
+ char buffer[ TOPIC_BUFFER_SIZE + 1 ] = { 0 };
+ JobsUpdateRequest_t request =
+ {
+ Queued,
+ "1.0.1",
+ strlen( "1.0.1" ),
+ "{\"key\": \"value\"}",
+ 0
+ };
+
+ size_t result = Jobs_UpdateMsg( request, buffer, TOPIC_BUFFER_SIZE );
+
+ TEST_ASSERT_EQUAL( 45U, result );
+ TEST_ASSERT_EQUAL_STRING( "{\"status\":\"QUEUED\",\"expectedVersion\":\"1.0.1\"}", buffer );
+}
+
+void test_getUpdateJobExecutionMsg_hasMalformedStatusDetails( void )
+{
+ char buffer[ TOPIC_BUFFER_SIZE + 1 ] = { 0 };
+ JobsUpdateRequest_t request =
+ {
+ Queued,
+ "1.0.1",
+ strlen( "1.0.1" ),
+ "malformed-details",
+ strlen( "malformed-details" )
+ };
+
+ size_t result = Jobs_UpdateMsg( request, buffer, TOPIC_BUFFER_SIZE );
+
+ TEST_ASSERT_EQUAL( 0U, result );
}
void test_getUpdateJobExecutionMsg_hasTooSmallBufferSizeForRequiredParameters( void )
{
- JobCurrentStatus_t status = Queued;
char buffer[ 2 ] = { 0 };
+ JobsUpdateRequest_t request =
+ {
+ Queued,
+ NULL,
+ 0,
+ NULL,
+ 0
+ };
- size_t result = Jobs_UpdateMsg( status, NULL, 0U, buffer, 1 );
+ size_t result = Jobs_UpdateMsg( request, buffer, 1 );
TEST_ASSERT_EQUAL( 0U, result );
}
void test_getUpdateJobExecutionMsg_hasTooSmallBufferSizeForAllParameters( void )
{
- char * version = "1.0.1";
- size_t versionLength = strlen( version );
- JobCurrentStatus_t status = Queued;
- char buffer[ 25 ] = { 0 };
+ char buffer[ 38 ] = { 0 };
+
+ JobsUpdateRequest_t request =
+ {
+ Queued,
+ "1.0.1",
+ strlen( "1.0.1" ),
+ "{\"key\": \"value\"}",
+ strlen( "{\"key\": \"value\"}" )
+ };
- size_t result = Jobs_UpdateMsg( status, version, versionLength, buffer, 1 );
+ size_t result = Jobs_UpdateMsg( request, buffer, 38 );
TEST_ASSERT_EQUAL( 0U, result );
}
void test_getUpdateJobExecutionMsg_hasAllValidParameters( void )
{
- char * version = "1.0.1";
- size_t versionLength = strlen( version );
- JobCurrentStatus_t status = Queued;
char buffer[ TOPIC_BUFFER_SIZE + 1 ] = { 0 };
+ JobsUpdateRequest_t request =
+ {
+ Queued,
+ "1.0.1",
+ strlen( "1.0.1" ),
+ "{\"key\": \"value\"}",
+ strlen( "{\"key\": \"value\"}" )
+ };
- size_t result = Jobs_UpdateMsg( status, version, versionLength, buffer, TOPIC_BUFFER_SIZE );
+ size_t result = Jobs_UpdateMsg( request, buffer, TOPIC_BUFFER_SIZE );
- TEST_ASSERT_EQUAL( 45U, result );
+ TEST_ASSERT_EQUAL( 80U, result );
+ TEST_ASSERT_EQUAL_STRING( "{\"status\":\"QUEUED\",\"expectedVersion\":\"1.0.1\",\"statusDetails\":\"{\"key\": \"value\"}\"}", buffer );
}
void test_getUpdateJobExecutionMsg_hasRequiredValidParameters( void )
{
- JobCurrentStatus_t status = Queued;
char buffer[ TOPIC_BUFFER_SIZE + 1 ] = { 0 };
+ JobsUpdateRequest_t request =
+ {
+ Queued,
+ NULL,
+ 0,
+ NULL,
+ 0
+ };
- size_t result = Jobs_UpdateMsg( status, NULL, 0U, buffer, TOPIC_BUFFER_SIZE );
+ size_t result = Jobs_UpdateMsg( request, buffer, TOPIC_BUFFER_SIZE );
TEST_ASSERT_EQUAL( 19U, result );
+ TEST_ASSERT_EQUAL_STRING( "{\"status\":\"QUEUED\"}", buffer );
}