diff --git a/.vscode/settings.json b/.vscode/settings.json index c7554ad..bf1564f 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -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 } \ No newline at end of file diff --git a/include/ut_kvp.h b/include/ut_kvp.h index 3924368..31d4597 100644 --- a/include/ut_kvp.h +++ b/include/ut_kvp.h @@ -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` diff --git a/src/ut_control_plane.c b/src/ut_control_plane.c index b0561c1..38ab7ed 100644 --- a/src/ut_control_plane.c +++ b/src/ut_control_plane.c @@ -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; @@ -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 @@ -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) @@ -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) @@ -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) @@ -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; } } diff --git a/src/ut_kvp.c b/src/ut_kvp.c index 7fef9fa..b8324a6 100644 --- a/src/ut_kvp.c +++ b/src/ut_kvp.c @@ -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) { diff --git a/tests/src/ut_test_control_plane.c b/tests/src/ut_test_control_plane.c index 93653b6..f1607c8 100644 --- a/tests/src/ut_test_control_plane.c +++ b/tests/src/ut_test_control_plane.c @@ -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` or `test_script_for_curl_request.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 diff --git a/tests/src/ut_test_kvp.c b/tests/src/ut_test_kvp.c index 939a9d5..bc330b6 100644 --- a/tests/src/ut_test_kvp.c +++ b/tests/src/ut_test_kvp.c @@ -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 */ @@ -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); @@ -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); diff --git a/tests/websocket-clients/curl-get-client-json-incorrect.sh b/tests/websocket-clients/curl-get-client-json-incorrect.sh new file mode 100755 index 0000000..6728978 --- /dev/null +++ b/tests/websocket-clients/curl-get-client-json-incorrect.sh @@ -0,0 +1 @@ +curl -X GET "http://localhost:8080/api/getKVP?key=test" -H "Accept: application/json" diff --git a/tests/websocket-clients/curl-get-client-json.sh b/tests/websocket-clients/curl-get-client-json.sh new file mode 100755 index 0000000..58a41c9 --- /dev/null +++ b/tests/websocket-clients/curl-get-client-json.sh @@ -0,0 +1 @@ +curl -X GET "http://localhost:8080/api/getKVP?key=test/jsonData" -H "Accept: application/json" diff --git a/tests/websocket-clients/curl-get-client-no-header.sh b/tests/websocket-clients/curl-get-client-no-header.sh new file mode 100755 index 0000000..9c6d0de --- /dev/null +++ b/tests/websocket-clients/curl-get-client-no-header.sh @@ -0,0 +1 @@ +curl -X GET "http://localhost:8080/api/getKVP?key=test/yamlData" diff --git a/tests/websocket-clients/curl-get-client-yaml-incorrect.sh b/tests/websocket-clients/curl-get-client-yaml-incorrect.sh new file mode 100755 index 0000000..4cb369a --- /dev/null +++ b/tests/websocket-clients/curl-get-client-yaml-incorrect.sh @@ -0,0 +1 @@ +curl -X GET "http://localhost:8080/api/getKVP?key=test" -H "Accept: application/x-yaml" diff --git a/tests/websocket-clients/curl-get-client-yaml.sh b/tests/websocket-clients/curl-get-client-yaml.sh new file mode 100755 index 0000000..3c2bffa --- /dev/null +++ b/tests/websocket-clients/curl-get-client-yaml.sh @@ -0,0 +1 @@ +curl -X GET "http://localhost:8080/api/getKVP?key=test/yamlData" -H "Accept: application/x-yaml" diff --git a/tests/websocket-clients/curl-client-binary.sh b/tests/websocket-clients/curl-push-client-binary.sh similarity index 100% rename from tests/websocket-clients/curl-client-binary.sh rename to tests/websocket-clients/curl-push-client-binary.sh diff --git a/tests/websocket-clients/curl-client-json.sh b/tests/websocket-clients/curl-push-client-json.sh similarity index 100% rename from tests/websocket-clients/curl-client-json.sh rename to tests/websocket-clients/curl-push-client-json.sh diff --git a/tests/websocket-clients/curl-client-yaml.sh b/tests/websocket-clients/curl-push-client-yaml.sh similarity index 100% rename from tests/websocket-clients/curl-client-yaml.sh rename to tests/websocket-clients/curl-push-client-yaml.sh diff --git a/tests/websocket-clients/test_script_for_curl_request.sh b/tests/websocket-clients/test_script_for_curl_request.sh new file mode 100755 index 0000000..316bf22 --- /dev/null +++ b/tests/websocket-clients/test_script_for_curl_request.sh @@ -0,0 +1,57 @@ +#!/bin/bash + +# Define color codes +RED='\033[0;31m' +GREEN='\033[0;32m' +YELLOW='\033[0;33m' +BLUE='\033[0;34m' +NC='\033[0m' # No color + +# Define test scripts and expected results +declare -A tests=( + ["./curl-get-client-yaml-incorrect.sh"]="error: Internal Server Error" + ["./curl-get-client-yaml.sh"]="--- +test: + yamlData: somevalue + x: 1 + on: true +test2: + yamlData1: somevalue1 + y: 2 + off: false" + ["./curl-get-client-json-incorrect.sh"]='{"error": "Internal Server Error"}' + ["./curl-get-client-json.sh"]='{ + "test": { + "jsonData": "somevalue", + "x": 1, + "on": true + }, + "test2": { + "jsonData1": "somevalue1", + "y": 2, + "off": false + } +}' + ["./curl-get-client-no-header.sh"]='{"error": "Unsupported Accept header or invalid type"}' + ["./curl-push-client-binary.sh"]="" + ["./curl-push-client-json.sh"]="" + ["./curl-push-client-yaml.sh"]="" +) + +# Loop through tests and validate output +for script in "${!tests[@]}"; do + echo "Running: $script" + output=$($script) + expected="${tests[$script]}" + + if [[ "$output" == "$expected" ]]; then + echo -e "${GREEN}Test Passed: Output matches expected result.${NC}" + else + echo -e "${RED}Test Failed: Output does not match expected result.${NC}" + echo -e "${YELLOW}Expected:${NC}" + echo "$expected" + echo "${YELLOW}Got:${NC}" + echo "$output" + fi + echo "---------------------------------" +done