Skip to content

Commit

Permalink
Optional status details (#105)
Browse files Browse the repository at this point in the history
* 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.
  • Loading branch information
kstribrnAmzn committed Jul 8, 2024
1 parent 9379f55 commit b85b306
Show file tree
Hide file tree
Showing 4 changed files with 243 additions and 71 deletions.
8 changes: 4 additions & 4 deletions docs/doxygen/include/size_table.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,8 @@
</tr>
<tr>
<td>jobs.c</td>
<td><center>2.8K</center></td>
<td><center>2.5K</center></td>
<td><center>2.9K</center></td>
<td><center>2.6K</center></td>
</tr>
<tr>
<td>job_parser.c</td>
Expand All @@ -29,7 +29,7 @@
</tr>
<tr>
<td><b>Total estimates</b></td>
<td><b><center>6.8K</center></b></td>
<td><b><center>6.0K</center></b></td>
<td><b><center>6.9K</center></b></td>
<td><b><center>6.1K</center></b></td>
</tr>
</table>
61 changes: 45 additions & 16 deletions source/include/jobs.h
Original file line number Diff line number Diff line change
Expand Up @@ -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 )

Expand Down Expand Up @@ -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;

/*-----------------------------------------------------------*/

/**
Expand Down Expand Up @@ -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.
*
* <b>Example</b>
* @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 );
*
Expand All @@ -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] */
Expand Down
107 changes: 77 additions & 30 deletions source/jobs.c
Original file line number Diff line number Diff line change
Expand Up @@ -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.
*
Expand Down Expand Up @@ -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 )
Expand Down
Loading

0 comments on commit b85b306

Please sign in to comment.