Skip to content

Commit 9796eb4

Browse files
committed
feat(network): structured JSON responses for sync functions
The three public sync functions was leaking internal server JSON or returning raw integers. We want them to return coherent, user-facing JSON with a computed status field and relevant version/row info. New responses: - cloudsync_network_send_changes — formats JSON: {"status":"...", "localVersion":N, "serverVersion":N} - cloudsync_network_check_changes — formats JSON: {"rowsReceived":N} - cloudsync_network_sync — combines send + check results into: {"status":"...", "localVersion":N, "serverVersion":N, "rowsReceived":N} The status helper computes "synced", "syncing", "retry", or "error" from the apply/status response fields.
1 parent 46ab54b commit 9796eb4

File tree

1 file changed

+83
-20
lines changed

1 file changed

+83
-20
lines changed

src/network.c

Lines changed: 83 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -517,6 +517,23 @@ static int64_t json_extract_int(const char *json, size_t json_len, const char *k
517517
return strtoll(json + val->start, NULL, 10);
518518
}
519519

520+
static int json_extract_array_size(const char *json, size_t json_len, const char *key) {
521+
if (!json || json_len == 0 || !key) return -1;
522+
523+
jsmn_parser parser;
524+
jsmntok_t tokens[JSMN_MAX_TOKENS];
525+
jsmn_init(&parser);
526+
int ntokens = jsmn_parse(&parser, json, json_len, tokens, JSMN_MAX_TOKENS);
527+
if (ntokens < 1 || tokens[0].type != JSMN_OBJECT) return -1;
528+
529+
int i = jsmn_find_key(json, tokens, ntokens, key);
530+
if (i < 0 || i + 1 >= ntokens) return -1;
531+
532+
jsmntok_t *val = &tokens[i + 1];
533+
if (val->type != JSMN_ARRAY) return -1;
534+
535+
return val->size;
536+
}
520537

521538
int network_extract_query_param (const char *query, const char *key, char *output, size_t output_size) {
522539
if (!query || !key || !output || output_size == 0) {
@@ -843,6 +860,23 @@ void cloudsync_network_set_apikey (sqlite3_context *context, int argc, sqlite3_v
843860
(result) ? sqlite3_result_int(context, SQLITE_OK) : sqlite3_result_error_code(context, SQLITE_NOMEM);
844861
}
845862

863+
// MARK: - Sync result
864+
865+
typedef struct {
866+
int64_t server_version; // lastOptimisticVersion
867+
int64_t local_version; // new_db_version (max local)
868+
const char *status; // computed status string
869+
int rows_received; // rows from check
870+
} sync_result;
871+
872+
static const char *network_compute_status(int64_t last_optimistic, int64_t last_confirmed,
873+
int gaps_size, int64_t local_version) {
874+
if (last_optimistic < 0 || last_confirmed < 0) return "error";
875+
if (gaps_size > 0 || last_optimistic < local_version) return "retry";
876+
if (last_optimistic == last_confirmed) return "synced";
877+
return "syncing";
878+
}
879+
846880
// MARK: -
847881

848882
void cloudsync_network_has_unsent_changes (sqlite3_context *context, int argc, sqlite3_value **argv) {
@@ -882,7 +916,7 @@ void cloudsync_network_has_unsent_changes (sqlite3_context *context, int argc, s
882916
sqlite3_result_int(context, (last_optimistic_version >= 0 && last_optimistic_version < last_local_change));
883917
}
884918

885-
int cloudsync_network_send_changes_internal (sqlite3_context *context, int argc, sqlite3_value **argv) {
919+
int cloudsync_network_send_changes_internal (sqlite3_context *context, int argc, sqlite3_value **argv, sync_result *out) {
886920
DEBUG_FUNCTION("cloudsync_network_send_changes");
887921

888922
// retrieve global context
@@ -943,43 +977,63 @@ int cloudsync_network_send_changes_internal (sqlite3_context *context, int argc,
943977
res = network_receive_buffer(netdata, netdata->apply_endpoint, netdata->authentication, true, true, json_payload, CLOUDSYNC_HEADER_SQLITECLOUD);
944978
} else {
945979
// there is no data to send, just check the status to update the db_version value in settings and to reply the status
980+
new_db_version = db_version;
946981
res = network_receive_buffer(netdata, netdata->status_endpoint, netdata->authentication, true, false, NULL, CLOUDSYNC_HEADER_SQLITECLOUD);
947982
}
948983

949984
int64_t last_optimistic_version = -1;
985+
int64_t last_confirmed_version = -1;
986+
int gaps_size = -1;
950987

951988
if (res.code == CLOUDSYNC_NETWORK_BUFFER && res.buffer) {
952989
last_optimistic_version = json_extract_int(res.buffer, res.blen, "lastOptimisticVersion", -1);
990+
last_confirmed_version = json_extract_int(res.buffer, res.blen, "lastConfirmedVersion", -1);
991+
gaps_size = json_extract_array_size(res.buffer, res.blen, "gaps");
992+
if (gaps_size < 0) gaps_size = 0;
953993
} else if (res.code != CLOUDSYNC_NETWORK_OK) {
954994
network_result_to_sqlite_error(context, res, "cloudsync_network_send_changes unable to notify BLOB upload to remote host.");
955995
network_result_cleanup(&res);
956996
return SQLITE_ERROR;
957997
}
958-
998+
959999
// update db_version in settings
9601000
char buf[256];
961-
if (last_optimistic_version > 0) {
1001+
if (last_optimistic_version >= 0) {
9621002
if (last_optimistic_version != db_version) {
9631003
snprintf(buf, sizeof(buf), "%" PRId64, last_optimistic_version);
9641004
dbutils_settings_set_key_value(data, CLOUDSYNC_KEY_SEND_DBVERSION, buf);
965-
}
1005+
}
9661006
} else if (new_db_version != db_version) {
9671007
snprintf(buf, sizeof(buf), "%" PRId64, new_db_version);
9681008
dbutils_settings_set_key_value(data, CLOUDSYNC_KEY_SEND_DBVERSION, buf);
9691009
}
970-
971-
network_set_sqlite_result(context, &res);
1010+
1011+
// populate sync result
1012+
if (out) {
1013+
out->server_version = last_optimistic_version;
1014+
out->local_version = new_db_version;
1015+
out->status = network_compute_status(last_optimistic_version, last_confirmed_version, gaps_size, new_db_version);
1016+
}
1017+
9721018
network_result_cleanup(&res);
9731019
return SQLITE_OK;
9741020
}
9751021

9761022
void cloudsync_network_send_changes (sqlite3_context *context, int argc, sqlite3_value **argv) {
9771023
DEBUG_FUNCTION("cloudsync_network_send_changes");
978-
979-
cloudsync_network_send_changes_internal(context, argc, argv);
1024+
1025+
sync_result sr = {-1, 0, NULL, 0};
1026+
int rc = cloudsync_network_send_changes_internal(context, argc, argv, &sr);
1027+
if (rc != SQLITE_OK) return;
1028+
1029+
char buf[256];
1030+
snprintf(buf, sizeof(buf),
1031+
"{\"status\":\"%s\",\"localVersion\":%" PRId64 ",\"serverVersion\":%" PRId64 "}",
1032+
sr.status ? sr.status : "error", sr.local_version, sr.server_version);
1033+
sqlite3_result_text(context, buf, -1, SQLITE_TRANSIENT);
9801034
}
9811035

982-
int cloudsync_network_check_internal(sqlite3_context *context, int *pnrows) {
1036+
int cloudsync_network_check_internal(sqlite3_context *context, int *pnrows, sync_result *out) {
9831037
cloudsync_context *data = (cloudsync_context *)sqlite3_user_data(context);
9841038
network_data *netdata = (network_data *)cloudsync_auxdata(data);
9851039
if (!netdata) {sqlite3_result_error(context, "Unable to retrieve CloudSync network context.", -1); return -1;}
@@ -1009,25 +1063,32 @@ int cloudsync_network_check_internal(sqlite3_context *context, int *pnrows) {
10091063
rc = network_set_sqlite_result(context, &result);
10101064
}
10111065

1066+
if (out && pnrows) out->rows_received = *pnrows;
1067+
10121068
network_result_cleanup(&result);
10131069
return rc;
10141070
}
10151071

10161072
void cloudsync_network_sync (sqlite3_context *context, int wait_ms, int max_retries) {
1017-
int rc = cloudsync_network_send_changes_internal(context, 0, NULL);
1073+
sync_result sr = {-1, 0, NULL, 0};
1074+
int rc = cloudsync_network_send_changes_internal(context, 0, NULL, &sr);
10181075
if (rc != SQLITE_OK) return;
1019-
1076+
10201077
int ntries = 0;
10211078
int nrows = 0;
10221079
while (ntries < max_retries) {
10231080
if (ntries > 0) sqlite3_sleep(wait_ms);
1024-
rc = cloudsync_network_check_internal(context, &nrows);
1081+
rc = cloudsync_network_check_internal(context, &nrows, &sr);
10251082
if (rc == SQLITE_OK && nrows > 0) break;
10261083
ntries++;
10271084
}
1028-
1029-
sqlite3_result_error_code(context, (nrows == -1) ? SQLITE_ERROR : SQLITE_OK);
1030-
if (nrows >= 0) sqlite3_result_int(context, nrows);
1085+
if (rc != SQLITE_OK) return;
1086+
1087+
char buf[256];
1088+
snprintf(buf, sizeof(buf),
1089+
"{\"status\":\"%s\",\"localVersion\":%" PRId64 ",\"serverVersion\":%" PRId64 ",\"rowsReceived\":%d}",
1090+
sr.status ? sr.status : "error", sr.local_version, sr.server_version, nrows);
1091+
sqlite3_result_text(context, buf, -1, SQLITE_TRANSIENT);
10311092
}
10321093

10331094
void cloudsync_network_sync0 (sqlite3_context *context, int argc, sqlite3_value **argv) {
@@ -1049,12 +1110,14 @@ void cloudsync_network_sync2 (sqlite3_context *context, int argc, sqlite3_value
10491110

10501111
void cloudsync_network_check_changes (sqlite3_context *context, int argc, sqlite3_value **argv) {
10511112
DEBUG_FUNCTION("cloudsync_network_check_changes");
1052-
1113+
10531114
int nrows = 0;
1054-
int rc = cloudsync_network_check_internal(context, &nrows);
1055-
1056-
// returns number of applied rows
1057-
if (rc == SQLITE_OK) sqlite3_result_int(context, nrows);
1115+
int rc = cloudsync_network_check_internal(context, &nrows, NULL);
1116+
if (rc != SQLITE_OK) return;
1117+
1118+
char buf[128];
1119+
snprintf(buf, sizeof(buf), "{\"rowsReceived\":%d}", nrows);
1120+
sqlite3_result_text(context, buf, -1, SQLITE_TRANSIENT);
10581121
}
10591122

10601123
void cloudsync_network_reset_sync_version (sqlite3_context *context, int argc, sqlite3_value **argv) {

0 commit comments

Comments
 (0)