From 08912dce98160d083a1f6559ecb8c305363ba820 Mon Sep 17 00:00:00 2001 From: Serhii Mamontov Date: Thu, 8 Aug 2024 18:37:55 +0300 Subject: [PATCH] feat(history): add delete message pubnub_api_types Add `delete message` API support to advanced history module. --- core/pbcc_advanced_history.c | 129 ++++++++++++++++++ core/pbcc_advanced_history.h | 37 +++++ core/pubnub_advanced_history.c | 88 ++++++++++-- core/pubnub_advanced_history.h | 48 +++++++ core/pubnub_api_types.h | 7 + core/pubnub_netcore.c | 1 + core/samples/pubnub_advanced_history_sample.c | 67 ++++++++- qt/pubnub_qt.cpp | 3 + 8 files changed, 366 insertions(+), 14 deletions(-) diff --git a/core/pbcc_advanced_history.c b/core/pbcc_advanced_history.c index 8f62e72a..7fb1648f 100644 --- a/core/pbcc_advanced_history.c +++ b/core/pbcc_advanced_history.c @@ -10,6 +10,7 @@ #include "pubnub_url_encode.h" #include "pubnub_assert.h" +#include "pubnub_helper.h" #include "pubnub_log.h" #else #error this module can only be used if PUBNUB_USE_ADVANCED_HISTORY is defined and set to 1 @@ -543,3 +544,131 @@ enum pubnub_res pbcc_message_counts_prep( PUBNUB_LOG_DEBUG("pbcc_message_counts_prep. REQUEST =%s\n", p->http_buf); return (rslt != PNR_OK) ? rslt : PNR_STARTED; } + +enum pubnub_res pbcc_delete_messages_prep(struct pbcc_context* pb, + char const* channel, + char const* start, + char const* end) +{ + char const* const uname = pubnub_uname(); + char const* user_id = pbcc_user_id_get(pb); +#if PUBNUB_CRYPTO_API + enum pubnub_res rslt = PNR_OK; +#endif + + PUBNUB_ASSERT_OPT(NULL != user_id); + + pb->msg_ofs = pb->msg_end = 0; + pb->http_content_len = 0; + + pb->http_buf_len = + snprintf(pb->http_buf, + sizeof pb->http_buf, + "/v3/history/sub-key/%s/channel/", + pb->subscribe_key); + APPEND_URL_ENCODED_M(pb, channel); + + URL_PARAMS_INIT(qparam, PUBNUB_MAX_URL_PARAMS); + if (uname) { ADD_URL_PARAM(qparam, pnsdk, uname); } + ADD_URL_PARAM(qparam, uuid, user_id); +#if PUBNUB_CRYPTO_API + if (pb->secret_key == NULL) { ADD_URL_AUTH_PARAM(pb, qparam, auth); } + ADD_TS_TO_URL_PARAM(); +#else + ADD_URL_AUTH_PARAM(pb, qparam, auth); +#endif + if (start) { ADD_URL_PARAM(qparam, start, start); } + if (end) { ADD_URL_PARAM(qparam, end, end); } + +#if PUBNUB_CRYPTO_API + SORT_URL_PARAMETERS(qparam); +#endif + ENCODE_URL_PARAMETERS(pb, qparam); +#if PUBNUB_CRYPTO_API + if (pb->secret_key != NULL) { + rslt = pbcc_sign_url(pb, "", pubnubSendViaGET, true); + } +#endif + + PUBNUB_LOG_DEBUG("pbcc_delete_messages_prep. REQUEST =%s\n", pb->http_buf); +#if PUBNUB_CRYPTO_API + return (rslt != PNR_OK) ? rslt : PNR_STARTED; +#else + return PNR_STARTED; +#endif +} + +pubnub_chamebl_t pbcc_get_delete_messages_response(struct pbcc_context* pb) +{ + pubnub_chamebl_t resp; + char const* reply = pb->http_reply; + int reply_len = pb->http_buf_len; + + if (PNR_OK != pb->last_result) { + PUBNUB_LOG_ERROR("pbcc_get_delete_messages_response(pb=%p) can be " + "called only if previous transactin " + "PBTT_DELETE_MESSAGES(%d) is finished successfully. " + "Transaction result was: %d('%s')\n", + pb, + PBTT_DELETE_MESSAGES, + pb->last_result, + pubnub_res_2_string(pb->last_result)); + resp.ptr = NULL; + resp.size = 0; + return resp; + } + + resp.ptr = (char*)reply; + resp.size = reply_len; + return resp; +} + +enum pubnub_res pbcc_parse_delete_messages_response(struct pbcc_context* pb) +{ + enum pbjson_object_name_parse_result jpresult; + struct pbjson_elem el; + struct pbjson_elem found; + char* reply = pb->http_reply; + int reply_len = pb->http_buf_len; + + if ((reply[0] != '{') || (reply[reply_len - 1] != '}')) { + PUBNUB_LOG_ERROR( + "Error: pbcc_parse_delete_messages_response(pbcc=%p) - " + "Response is not json object: response='%.*s'\n", + pb, + reply_len, + reply); + return PNR_FORMAT_ERROR; + } + el.start = reply; + el.end = reply + reply_len; + if (pbjson_value_for_field_found(&el, "status", "403")) { + PUBNUB_LOG_ERROR( + "Error: pbcc_parse_delete_messages_response(pbcc=%p) - " + "Access Denied: response='%.*s'\n", + pb, + reply_len, + reply); + return PNR_ACCESS_DENIED; + } + jpresult = pbjson_get_object_value(&el, "error", &found); + if (jonmpOK == jpresult) { + if (pbjson_elem_equals_string(&found, "true")) { + return PNR_ERROR_ON_SERVER; + } + } + else { + PUBNUB_LOG_ERROR( + "Error: pbcc_parse_delete_messages_response(pbcc=%p) - " + "'error' atribute not found in the response. error=%d\n" + "response='%.*s'\n", + pb, + jpresult, + reply_len, + reply); + return PNR_FORMAT_ERROR; + } + pb->chan_ofs = pb->chan_end = 0; + + return PNR_OK; +} \ No newline at end of file diff --git a/core/pbcc_advanced_history.h b/core/pbcc_advanced_history.h index 958bb6a5..37340a09 100644 --- a/core/pbcc_advanced_history.h +++ b/core/pbcc_advanced_history.h @@ -70,6 +70,43 @@ enum pubnub_res pbcc_message_counts_prep( char const* channel, char const* timetoken, char const* channel_timetokens); + +/** + * @brief Prepare `delete messages` operation (transaction), mostly by + * formatting the URI of the HTTP request. + * + * @param pb PubNub context which provides resources to send request. + * @param channel Channel from which messages should be deleted. + * @param start Timetoken delimiting the start of time slice (exclusive) to + * delete messages from. + * @param end Timetoken delimiting the end of time slice (inclusive) to + * delete messages to. + * @return Results of `delete messages` transaction call. + */ +enum pubnub_res pbcc_delete_messages_prep(struct pbcc_context* pb, + char const* channel, + char const* start, + char const* end); + +/** + * @brief Get `delete messages` service response. + * + * @param pb PubNub context which has been used to delete channel messages. + * @return `pubnub_chamebl_t` with pointer to string with response. + */ +pubnub_chamebl_t pbcc_get_delete_messages_response(struct pbcc_context* pb); + + + +/** + * @brief Parses the string received as a response for a history v3 + * operation (transaction). + * + * @param pb PubNub context to parse delete channel messages response. + * @return PNR_OK: OK, !PNR_OK: error (invalid response) +*/ +enum pubnub_res pbcc_parse_delete_messages_response(struct pbcc_context* pb); + #endif /* INC_PBCC_ADVANCED_HISTORY */ #endif /* PUBNUB_USE_ADVANCED_HISTORY */ diff --git a/core/pubnub_advanced_history.c b/core/pubnub_advanced_history.c index 8f6101ef..bb5b3122 100644 --- a/core/pubnub_advanced_history.c +++ b/core/pubnub_advanced_history.c @@ -14,7 +14,7 @@ int pubnub_get_error_message(pubnub_t* pb, pubnub_chamebl_t* o_msg) { int rslt; - + PUBNUB_ASSERT(pb_valid_ctx_ptr(pb)); pubnub_mutex_lock(pb->monitor); @@ -30,7 +30,7 @@ int pubnub_get_error_message(pubnub_t* pb, pubnub_chamebl_t* o_msg) int pubnub_get_chan_msg_counts_size(pubnub_t* pb) -{ +{ /* Number of '"channel":msg_count' pairs */ int rslt; @@ -42,14 +42,14 @@ int pubnub_get_chan_msg_counts_size(pubnub_t* pb) return -1; } rslt = pbcc_get_chan_msg_counts_size(&(pb->core)); - + pubnub_mutex_unlock(pb->monitor); return rslt; } enum pubnub_res pubnub_message_counts(pubnub_t* pb, - char const* channel, + char const* channel, char const* timetoken) { enum pubnub_res rslt; @@ -65,10 +65,12 @@ enum pubnub_res pubnub_message_counts(pubnub_t* pb, } if (strchr(timetoken, ',') == NULL) { - rslt = pbcc_message_counts_prep(PBTT_MESSAGE_COUNTS , &(pb->core), channel, timetoken, NULL); + rslt = pbcc_message_counts_prep( + PBTT_MESSAGE_COUNTS, &(pb->core), channel, timetoken, NULL); } else { - rslt = pbcc_message_counts_prep(PBTT_MESSAGE_COUNTS , &(pb->core), channel, NULL, timetoken); + rslt = pbcc_message_counts_prep( + PBTT_MESSAGE_COUNTS, &(pb->core), channel, NULL, timetoken); } if (PNR_STARTED == rslt) { pb->trans = PBTT_MESSAGE_COUNTS; @@ -82,8 +84,8 @@ enum pubnub_res pubnub_message_counts(pubnub_t* pb, } -int pubnub_get_chan_msg_counts(pubnub_t* pb, - size_t* io_count, +int pubnub_get_chan_msg_counts(pubnub_t* pb, + size_t* io_count, struct pubnub_chan_msg_count* chan_msg_counters) { int rslt; @@ -98,7 +100,7 @@ int pubnub_get_chan_msg_counts(pubnub_t* pb, return -1; } rslt = pbcc_get_chan_msg_counts(&(pb->core), io_count, chan_msg_counters); - + pubnub_mutex_unlock(pb->monitor); return rslt; } @@ -107,7 +109,7 @@ int pubnub_get_chan_msg_counts(pubnub_t* pb, int pubnub_get_message_counts(pubnub_t* pb, char const* channel, int* o_count) { int rslt; - + PUBNUB_ASSERT(pb_valid_ctx_ptr(pb)); PUBNUB_ASSERT_OPT(channel != NULL); PUBNUB_ASSERT_OPT(o_count != NULL); @@ -118,9 +120,73 @@ int pubnub_get_message_counts(pubnub_t* pb, char const* channel, int* o_count) return -1; } rslt = pbcc_get_message_counts(&(pb->core), channel, o_count); - + + pubnub_mutex_unlock(pb->monitor); + return rslt; +} + +struct pubnub_delete_messages_options pubnub_delete_messages_defopts(void) +{ + struct pubnub_delete_messages_options options; + + options.start = NULL; + options.end = NULL; + + return options; +} + +enum pubnub_res pubnub_delete_messages(pubnub_t* pb, + char const* channel, + struct pubnub_delete_messages_options options) +{ + PUBNUB_ASSERT(pb_valid_ctx_ptr(pb)); + PUBNUB_ASSERT_OPT(NULL != channel); + + pubnub_mutex_lock(pb->monitor); + if (!pbnc_can_start_transaction(pb)) { + pubnub_mutex_unlock(pb->monitor); + return PNR_IN_PROGRESS; + } + + enum pubnub_res rslt = + pbcc_delete_messages_prep(&pb->core, channel, options.start, options.end); + + if (PNR_STARTED == rslt) { + pb->trans = PBTT_DELETE_MESSAGES; + pb->method = pubnubUseDELETE; + pb->core.last_result = PNR_STARTED; + pbnc_fsm(pb); + rslt = pb->core.last_result; + } pubnub_mutex_unlock(pb->monitor); + return rslt; } +pubnub_chamebl_t pubnub_get_delete_messages_response(pubnub_t* pb) +{ + pubnub_chamebl_t resp; + PUBNUB_ASSERT(pb_valid_ctx_ptr(pb)); + + pubnub_mutex_lock(pb->monitor); + if (PBTT_DELETE_MESSAGES != pb->trans) { + PUBNUB_LOG_ERROR( + "pubnub_get_delete_messages_response(pb=%p) can be " + "called only if previous transaction is " + "PBTT_DELETE_MESSAGES(%d). Previous transaction was: %d\n", + pb, + PBTT_DELETE_MESSAGES, + pb->trans); + pubnub_mutex_unlock(pb->monitor); + resp.ptr = NULL; + resp.size = 0; + return resp; + } + + resp = pbcc_get_delete_messages_response(&pb->core); + pubnub_mutex_unlock(pb->monitor); + + return resp; +} + #endif /* PUBNUB_USE_ADVANCED_HISTORY */ diff --git a/core/pubnub_advanced_history.h b/core/pubnub_advanced_history.h index 234614b1..050f132e 100644 --- a/core/pubnub_advanced_history.h +++ b/core/pubnub_advanced_history.h @@ -20,6 +20,20 @@ struct pubnub_chan_msg_count { size_t message_count; }; +// Delete messages endpoint configuration definition. +struct pubnub_delete_messages_options { + /** + * @brief Timetoken delimiting the start of time slice (exclusive) to delete + * messages from. + */ + char const* start; + /** + * @brief Timetoken delimiting the end of time slice (inclusive) to delete + * messages to. + */ + char const* end; +}; + /** If successful returns number of members(key:value pairs) of JSON object 'channels', or -1 on error(transaction still in progress, or so) @@ -63,6 +77,40 @@ PUBNUB_EXTERN int pubnub_get_chan_msg_counts(pubnub_t* pb, */ PUBNUB_EXTERN int pubnub_get_message_counts(pubnub_t* pb, char const*channel, int* o_count); +/** + * @brief Delete message transaction default options. + * + * @note It's best to always call `pubnub_delete_messages_defopts()` to + * initialize `pubnub_delete_messages_options`. since it has serveral + * parameters which maybe extended in the future. + * + * @return Default options for delete message transaction. + */ +PUBNUB_EXTERN struct pubnub_delete_messages_options pubnub_delete_messages_defopts(void); + +/** + * @brief Remove messages from the `channel`. + * + * Start `delete_messages` transaction to permanently remove messages from the `channel` storage. + * + * @param pb PubNub context which should be used to perform + * `delete messages` transaction. + * @param channel Channel from which messages should be deleted. + * @param options Additional `delete messages` transaction configuration object. + * @return Results of `delete messages` transaction call. + */ +PUBNUB_EXTERN enum pubnub_res +pubnub_delete_messages(pubnub_t* pb, + char const* channel, + struct pubnub_delete_messages_options options); + +/** + * @brief Get `delete messages` service response. + * + * @param pb PubNub context which has been used to delete channel messages. + * @return `pubnub_chamebl_t` with pointer to string with response. + */ +PUBNUB_EXTERN pubnub_chamebl_t pubnub_get_delete_messages_response(pubnub_t* pb); #endif /* !defined INC_PUBNUB_ADVANCED_HISTORY */ diff --git a/core/pubnub_api_types.h b/core/pubnub_api_types.h index 816ec3bd..92f6e5c0 100644 --- a/core/pubnub_api_types.h +++ b/core/pubnub_api_types.h @@ -200,6 +200,13 @@ enum pubnub_trans { history counts for channels listed. */ PBTT_MESSAGE_COUNTS, + /** + * @brief History V3 (delete messages) operation/transaction. + * + * @note All `channel` messages will be removed If neither `start`, nor + * `end` has been passed. + */ + PBTT_DELETE_MESSAGES, #endif #if PUBNUB_USE_OBJECTS_API /** Objects API transaction Returns a paginated list of users associated with the diff --git a/core/pubnub_netcore.c b/core/pubnub_netcore.c index 697ed425..29369faa 100644 --- a/core/pubnub_netcore.c +++ b/core/pubnub_netcore.c @@ -295,6 +295,7 @@ static PFpbcc_parse_response_T m_aParseResponse[] = { dont_parse, #endif #if PUBNUB_USE_ADVANCED_HISTORY , pbcc_parse_message_counts_response /* PBTT_MESSAGE_COUNTS */ + , pbcc_parse_delete_messages_response /* PBTT_DELETE_MESSAGES */ #endif #if PUBNUB_USE_OBJECTS_API , pbcc_parse_objects_api_response /* PBTT_GET_USERS */ diff --git a/core/samples/pubnub_advanced_history_sample.c b/core/samples/pubnub_advanced_history_sample.c index b92b1f1b..61d238f9 100644 --- a/core/samples/pubnub_advanced_history_sample.c +++ b/core/samples/pubnub_advanced_history_sample.c @@ -328,6 +328,35 @@ static void get_message_counts_for_the_list_of_timetokens(pubnub_t* pbp, internal_msg_counts); } +static int get_message_counts_for_the_channel(pubnub_t* pbp, char* channel, char* timetoken) +{ + enum pubnub_res count_res = pubnub_message_counts(pbp, channel, timetoken); + if (count_res == PNR_STARTED) { + count_res = pubnub_await(pbp); + } + if (PNR_OK != count_res) { + return -1; + } + + int count = 0; + pubnub_get_message_counts(pbp, channel, &count); + + return count; +} + +static enum pubnub_res delete_channel_messages(pubnub_t* pbp, char* channel, char* start, char* end) +{ + struct pubnub_delete_messages_options options = pubnub_delete_messages_defopts(); + if (NULL != start) { options.start = start; } + if (NULL != end) { options.end = end; } + + enum pubnub_res delete_res = pubnub_delete_messages(pbp, channel, options); + if (delete_res == PNR_STARTED) { + delete_res = pubnub_await(pbp); + } + + return delete_res; +} int main(int argc, char* argv[]) { @@ -368,11 +397,43 @@ int main(int argc, char* argv[]) get_message_counts_for_a_single_timetoken(pbp_2, string_channels, sizeof m_timetokens/sizeof m_timetokens[0]); - - sync_sample_free(pbp_2); - sync_sample_free(pbp); + puts("Pubnub message_counts demo over."); + puts("Pubnub delete_message demo start."); + enum pubnub_res delete_res = delete_channel_messages(pbp, m_channel[4], m_timetokens[1], m_timetokens[3]); + if (PNR_OK != delete_res) { + printf("Delete messages failed with code: %d('%s')\n", delete_res, pubnub_res_2_string(delete_res)); + sync_sample_free(pbp_2); + sync_sample_free(pbp); + return -1; + } + + int count = get_message_counts_for_the_channel(pbp, m_channel[4], m_timetokens[0]); + if (count != 3) { + printf("It is expected that 2 of 5 messages to be removed. There is %d messages in %s\n", count, m_channel[4]); + return -1; + } + + delete_res = delete_channel_messages(pbp, m_channel[3], NULL, NULL); + if (PNR_OK != delete_res) { + printf("Delete messages failed with code: %d('%s')\n", delete_res, pubnub_res_2_string(delete_res)); + sync_sample_free(pbp_2); + sync_sample_free(pbp); + return -1; + } + + count = get_message_counts_for_the_channel(pbp, m_channel[3], m_timetokens[0]); + if (count != 0) { + printf("It is expected that 4 of 4 messages to be removed. There is %d messages in %s\n", count, m_channel[3]); + return -1; + } + + puts("Pubnub delete_message demo is over."); + + sync_sample_free(pbp_2); + sync_sample_free(pbp); + return 0; } diff --git a/qt/pubnub_qt.cpp b/qt/pubnub_qt.cpp index e17e0967..f989046e 100644 --- a/qt/pubnub_qt.cpp +++ b/qt/pubnub_qt.cpp @@ -1381,6 +1381,9 @@ pubnub_res pubnub_qt::finish(QByteArray const& data, int http_code) case PBTT_MESSAGE_COUNTS: pbres = pbcc_parse_message_counts_response(d_context.data()); break; + case PBTT_DELETE_MESSAGES: + pbres = pbcc_parse_delete_messages_response(d_context.data()); + break; #endif #if PUBNUB_USE_OBJECTS_API case PBTT_GETALL_UUIDMETADATA: