Skip to content

Commit

Permalink
Add gh #66 : Added function in KVP to retreive data based on type
Browse files Browse the repository at this point in the history
KVP now has a function which emits data in either json or yaml based
on users demand.
Also added tests to check the function
Added curl clients to test GET Requests.
  • Loading branch information
kanjoe24 committed Jan 16, 2025
1 parent ae7c188 commit 70626d5
Show file tree
Hide file tree
Showing 15 changed files with 225 additions and 24 deletions.
9 changes: 8 additions & 1 deletion .vscode/settings.json
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,14 @@
"ut_control_plane.h": "c",
"ut.h": "c",
"ut_log.h": "c",
"tuple": "c"
"tuple": "c",
"array": "c",
"compare": "c",
"functional": "c",
"type_traits": "c",
"utility": "c",
"istream": "c",
"ostream": "c"
},
"cmake.configureOnOpen": true
}
10 changes: 10 additions & 0 deletions include/ut_kvp.h
Original file line number Diff line number Diff line change
Expand Up @@ -254,6 +254,16 @@ uint32_t ut_kvp_getListCount( ut_kvp_instance_t *pInstance, const char *pszKey);
*/
unsigned char* ut_kvp_getDataBytes(ut_kvp_instance_t *pInstance, const char *pszKey, int *size);

/**
* @brief Get the data block from the instance based on the type requested by user.
* User to free the instance where the data is invalid, no output will be provided
* Also caller needs to ensure, that they free the pointer to the data block
*
* @param pInstance - pointer to the KVP instance
* @param pszType - type of data to be retrieved. Currently supported types are "json" and "yaml"
*/
char* ut_kvp_getDataOfType( ut_kvp_instance_t *pInstance, const char *pszType );

/* TODO:
* - Implement functions for getting signed integer values (`ut_kvp_getInt8Field`, `ut_kvp_getInt16Field`, `ut_kvp_getInt32Field`,
*`ut_kvp_getInt64Field`
Expand Down
135 changes: 113 additions & 22 deletions src/ut_control_plane.c
Original file line number Diff line number Diff line change
Expand Up @@ -289,6 +289,69 @@ static int callback_echo(struct lws *wsi, enum lws_callback_reasons reason, void
}
#else

static char* create_response(ut_cp_instance_internal_t *pInternal, const char* key, const char* type)
{
ut_kvp_instance_t *pkvpInstance = NULL;
ut_kvp_status_t status;
char result_kvp[UT_KVP_MAX_ELEMENT_SIZE] = {0xff};
char* response = NULL;

for (uint32_t i = 0; i < pInternal->callback_entry_index; i++)
{
CallbackEntry_t entry = pInternal->callbackEntryList[i];
void *userData = malloc(strlen(entry.userData) + 1); // +1 for null terminator
if (userData == NULL)
{
UT_CONTROL_PLANE_ERROR("Memory allocation failed\n");
return NULL; // Handle memory allocation failure
}
memcpy(userData, entry.userData, strlen(entry.userData) + 1);

pkvpInstance = ut_kvp_createInstance();

/* Note: userData data will be freed by the destoryInstance() function */
status = ut_kvp_openMemory(pkvpInstance, userData, strlen(entry.userData));
if (status != UT_KVP_STATUS_SUCCESS)
{
UT_CONTROL_PLANE_ERROR("ut_kvp_open() - Read Failure\n");
ut_kvp_destroyInstance(pkvpInstance);
return NULL;
}
if (UT_KVP_STATUS_SUCCESS == ut_kvp_getStringField(pkvpInstance, key, result_kvp, UT_KVP_MAX_ELEMENT_SIZE))
{
response = ut_kvp_getDataOfType(pkvpInstance, type);
}
ut_kvp_destroyInstance(pkvpInstance);
}

return response;
}

// Helper function to send error response
static int send_error_response(struct lws *wsi, int status, const char *content_type, const char *body)
{
unsigned char buffer[LWS_PRE + 1024];
unsigned char *p = buffer + LWS_PRE, *end = buffer + sizeof(buffer);

// Add HTTP headers
if (lws_add_http_common_headers(wsi, status, content_type, strlen(body), &p, end) < 0)
return -1;

// Finalize headers
if (lws_finalize_http_header(wsi, &p, end) < 0)
return -1;

// Write headers
if (lws_write(wsi, buffer + LWS_PRE, p - (buffer + LWS_PRE), LWS_WRITE_HTTP_HEADERS) < 0)
return -1;

// Write body
if (lws_write(wsi, (unsigned char *)body, strlen(body), LWS_WRITE_HTTP_FINAL) < 0)
return -1;

return 0;
}

static int callback_http(struct lws *wsi, enum lws_callback_reasons reason, void *user, void *in, size_t len)
{
cp_message_t msg;
Expand All @@ -305,9 +368,8 @@ static int callback_http(struct lws *wsi, enum lws_callback_reasons reason, void
char *requested_uri = (char *)in; // Use the 'in' parameter to get the URI
char query_string[256] = {0}; // Buffer for the query string
char accept_header[256] = {0}; // Buffer for the Accept header
char response[1024] = {0}; // Buffer for the response
char *response = NULL;
char *key = NULL;
char *value = NULL;
unsigned char buffer[LWS_PRE + 1024]; // Allocate buffer for headers and body
unsigned char *p = buffer + LWS_PRE; // Pointer to start of usable buffer space
unsigned char *end = buffer + sizeof(buffer); // Pointer to end of buffer
Expand All @@ -323,15 +385,24 @@ static int callback_http(struct lws *wsi, enum lws_callback_reasons reason, void
{
UT_CONTROL_PLANE_DEBUG("Query String: %s\n", query_string);

// Parse key-value pairs from the query string
key = strtok(query_string, "=");
value = strtok(NULL, "&");

if (key && value)
// Directly look for the "key=" prefix in the query string
char *key_param = strstr(query_string, "key=");
if (key_param != NULL)
{
key = key_param + 4; // Extract the value part (skip "key=")
UT_CONTROL_PLANE_DEBUG("Parsed Query Parameter: key=%s\n", key);
}
else
{
UT_CONTROL_PLANE_DEBUG("Received GET parameter: %s = %s\n", key, value);
UT_CONTROL_PLANE_ERROR("Query parameter 'key' not found\n");
key = NULL; // Ensure key is NULL if not found
}
}
else
{
UT_CONTROL_PLANE_ERROR("Failed to extract query string\n");
key = NULL; // Ensure key is NULL if query string is absent
}

// Extract the Accept header
if (lws_hdr_copy(wsi, accept_header, sizeof(accept_header), WSI_TOKEN_HTTP_ACCEPT) > 0)
Expand All @@ -341,19 +412,27 @@ static int callback_http(struct lws *wsi, enum lws_callback_reasons reason, void
else
{
UT_CONTROL_PLANE_ERROR("Missing Accept header\n");
lws_return_http_status(wsi, HTTP_STATUS_BAD_REQUEST, NULL);
return -1; // Missing Accept header
const char *error_response = "{\"error\": \"Missing Accept header\"}";
if (send_error_response(wsi, HTTP_STATUS_BAD_REQUEST, "application/json", error_response) < 0)
return -1;
return -1;
}

// Check for valid key parameter
if (key && strcmp(key, "key") == 0)
if (key)
{
if (strcmp(value, "json") == 0 && strncmp(accept_header, "application/json", 16) == 0)
if (strncmp(accept_header, "application/json", 16) == 0)
{
// Format pInternal as JSON
snprintf(response, sizeof(response),
"{\"field1\": \"%s\", \"field2\": %d, \"field3\": \"%s\"}",
"value1", 123, "value3");
response = create_response(pInternal, key, "json");

if (response == NULL)
{
UT_CONTROL_PLANE_ERROR("Failed to create response\n");
const char *error_response = "{\"error\": \"Internal Server Error\"}";
if (send_error_response(wsi, HTTP_STATUS_INTERNAL_SERVER_ERROR, "application/json", error_response) < 0)
return -1;
return -1;
}

// Add HTTP headers
if (lws_add_http_common_headers(wsi, HTTP_STATUS_OK, "application/json", strlen(response), &p, end) < 0)
Expand All @@ -379,14 +458,22 @@ static int callback_http(struct lws *wsi, enum lws_callback_reasons reason, void
return -1; // Failed to write body
}

free(response);

return 1; // HTTP request handled
}
else if (strcmp(value, "yaml") == 0 && strncmp(accept_header, "application/x-yaml", 18) == 0)
else if (strncmp(accept_header, "application/x-yaml", 18) == 0)
{
// Format pInternal as YAML
snprintf(response, sizeof(response),
"field1: %s\nfield2: %d\nfield3: %s\n",
"value1", 123, "value3");
response = create_response(pInternal, key, "yaml");

if (response == NULL)
{
UT_CONTROL_PLANE_ERROR("Failed to create response\n");
const char *error_response = "error: Internal Server Error\n";
if (send_error_response(wsi, HTTP_STATUS_INTERNAL_SERVER_ERROR, "application/x-yaml", error_response) < 0)
return -1;
return -1;
}

// Add HTTP headers
if (lws_add_http_common_headers(wsi, HTTP_STATUS_OK, "application/json", strlen(response), &p, end) < 0)
Expand All @@ -412,12 +499,16 @@ static int callback_http(struct lws *wsi, enum lws_callback_reasons reason, void
return -1; // Failed to write body
}

free(response);

return 1; // HTTP request handled
}
else
{
UT_CONTROL_PLANE_ERROR("Invalid key value or unsupported Accept header\n");
lws_return_http_status(wsi, HTTP_STATUS_BAD_REQUEST, NULL);
const char *error_response = "{\"error\": \"Unsupported Accept header or invalid type\"}";
if (send_error_response(wsi, HTTP_STATUS_BAD_REQUEST, "application/json", error_response) < 0)
return -1;
return -1;
}
}
Expand Down
43 changes: 43 additions & 0 deletions src/ut_kvp.c
Original file line number Diff line number Diff line change
Expand Up @@ -763,6 +763,49 @@ unsigned char* ut_kvp_getDataBytes(ut_kvp_instance_t *pInstance, const char *psz
return output_bytes;
}

char* ut_kvp_getDataOfType( ut_kvp_instance_t *pInstance, const char *pszType )
{
ut_kvp_instance_internal_t *pInternal = validateInstance(pInstance);
char *kvp_yaml_output = NULL;

if (pInternal == NULL)
{
return NULL;
}

if ( pInternal->fy_handle == NULL)
{
return NULL;
}

if (pszType == NULL)
{
UT_LOG_ERROR("Invalid Param - type");
return NULL;
}

if (strncmp(pszType, "json", 4) == 0)
{
kvp_yaml_output = fy_emit_document_to_string(pInternal->fy_handle, FYECF_MODE_JSON);
}
else if (strncmp(pszType, "yaml", 4) == 0)
{
kvp_yaml_output = fy_emit_document_to_string(pInternal->fy_handle, FYECF_DEFAULT);
}
else
{
UT_LOG_ERROR("Invalid type");
return NULL;
}

if (kvp_yaml_output == NULL)
{
UT_LOG_ERROR("Failed to emit YAML document\n");
return NULL;
}
return kvp_yaml_output;
}

/** Static Functions */
static ut_kvp_instance_internal_t *validateInstance(ut_kvp_instance_t *pInstance)
{
Expand Down
2 changes: 1 addition & 1 deletion tests/src/ut_test_control_plane.c
Original file line number Diff line number Diff line change
Expand Up @@ -276,7 +276,7 @@ void run_client_function()
UT_LOG("Please Run the command `./python-client-send-json.py or/& ./python-client-send-yaml.py` from another terminal and press return;'\n");
UT_LOG("In order to pass the test you need to run each of the python scripts'\n");
#else
UT_LOG("Please Run the command `./curl-client-json.sh or/& ./curl-client-yaml.sh or/& ./curl-client-binary.sh` from another terminal and press return;'\n");
UT_LOG("Please Run the command `./curl-requests-script.sh` from another terminal and press return;'\n");
UT_LOG("In order to pass the test you need to run each of the curl scripts'\n");
#endif

Expand Down
29 changes: 29 additions & 0 deletions tests/src/ut_test_kvp.c
Original file line number Diff line number Diff line change
Expand Up @@ -915,6 +915,33 @@ static void create_delete_kvp_memory_instance_for_given_file(const char* filenam
ut_kvp_destroyInstance( pInstance );
}

void test_ut_kvp_getDataOfTypeJson()
{
char* jsonData = ut_kvp_getDataOfType(gpMainTestInstance, "json");
if(jsonData != NULL)
{
// Print the emitted KVP string
printf("%s\n", jsonData);

// Free the emitted KVP string
free(jsonData);
}
}

void test_ut_kvp_getDataOfTypeYaml()
{
char* yamlData = ut_kvp_getDataOfType(gpMainTestInstance, "yaml");
if(yamlData != NULL)
{
// Print the emitted KVP string
printf("%s\n", yamlData);

// Free the emitted KVP string
free(yamlData);
}
}


/*These tests, test for availability of include file in the malloc'd buffer
**The malloc'd buffer, only contains files to be included
*/
Expand Down Expand Up @@ -1150,6 +1177,7 @@ void register_kvp_functions( void )
UT_add_test(gpKVPSuite2, "kvp double", test_ut_kvp_getDoubleField);
UT_add_test(gpKVPSuite2, "kvp node presence", test_ut_kvp_fieldPresent);
UT_add_test(gpKVPSuite2, "kvp dataByte", test_ut_kvp_dataByte);
UT_add_test(gpKVPSuite2, "kvp get JSON data", test_ut_kvp_getDataOfTypeJson);

/* Perform the same parsing tests but use a json file instead */
gpKVPSuite3 = UT_add_suite("ut-kvp - test main functions JSON Decoder ", test_ut_kvp_createGlobalJSONInstance, test_ut_kvp_freeGlobalInstance);
Expand All @@ -1166,6 +1194,7 @@ void register_kvp_functions( void )
UT_add_test(gpKVPSuite3, "kvp float", test_ut_kvp_getFloatField);
UT_add_test(gpKVPSuite3, "kvp double", test_ut_kvp_getDoubleField);
UT_add_test(gpKVPSuite3, "kvp node presence", test_ut_kvp_fieldPresent);
UT_add_test(gpKVPSuite3, "kvp get Yaml data", test_ut_kvp_getDataOfTypeYaml);


gpKVPSuite4 = UT_add_suite("ut-kvp - test main functions Test without Open ", NULL, NULL);
Expand Down
1 change: 1 addition & 0 deletions tests/websocket-clients/curl-get-client-json-incorrect.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
curl -X GET "http://localhost:8080/api/getKVP?key=test" -H "Accept: application/json"
1 change: 1 addition & 0 deletions tests/websocket-clients/curl-get-client-json.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
curl -X GET "http://localhost:8080/api/getKVP?key=test/jsonData" -H "Accept: application/json"
1 change: 1 addition & 0 deletions tests/websocket-clients/curl-get-client-no-header.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
curl -X GET "http://localhost:8080/api/getKVP?key=test/yamlData"
1 change: 1 addition & 0 deletions tests/websocket-clients/curl-get-client-yaml-incorrect.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
curl -X GET "http://localhost:8080/api/getKVP?key=test" -H "Accept: application/x-yaml"
1 change: 1 addition & 0 deletions tests/websocket-clients/curl-get-client-yaml.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
curl -X GET "http://localhost:8080/api/getKVP?key=test/yamlData" -H "Accept: application/x-yaml"
File renamed without changes.
File renamed without changes.
16 changes: 16 additions & 0 deletions tests/websocket-clients/curl-requests-script.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
./curl-get-client-yaml-incorrect.sh
sleep 1
./curl-get-client-yaml.sh
sleep 1
./curl-get-client-json-incorrect.sh
sleep 1
./curl-get-client-json.sh
sleep 1
./curl-push-client-json.sh
sleep 1
./curl-push-client-yaml.sh
sleep 1
./curl-push-client-binary.sh
sleep 1
./curl-get-client-no-header.sh
sleep 1

0 comments on commit 70626d5

Please sign in to comment.