Compare commits

...
Author SHA1 Message Date
Brandon Thomas d1f23bc3fe cursor behavior fixes 2026-07-12 15:28:13 -04:00
14 changed files with 131 additions and 46 deletions
@@ -70,9 +70,16 @@ pub async fn list_folders_handler(
CommonWebError::from_error(err)
})?;
let maybe_cursor = rows.last().map(|last| {
server_state.opaque_cursors.encode_last_id_cursor(CURSOR_NAME, last.id)
}).transpose()?;
// Only hand out a next-page cursor when this page was full. A short page
// means the list is exhausted, and emitting a cursor anyway would make
// clients fetch one guaranteed-empty trailing page.
let maybe_cursor = if rows.len() == limit as usize {
rows.last()
.map(|last| server_state.opaque_cursors.encode_last_id_cursor(CURSOR_NAME, last.id))
.transpose()?
} else {
None
};
let media_domain = get_media_domain(&http_request);
let server_environment = server_state.server_environment;
@@ -163,9 +163,16 @@ pub async fn list_folder_media_files_handler(
CommonWebError::from_error(err)
})?;
let maybe_cursor = rows.last().map(|last| {
server_state.opaque_cursors.encode_last_id_cursor(CURSOR_NAME, last.media_file_id)
}).transpose()?;
// Only hand out a next-page cursor when this page was full. A short page
// means the list is exhausted, and emitting a cursor anyway would make
// clients fetch one guaranteed-empty trailing page.
let maybe_cursor = if rows.len() == limit as usize {
rows.last()
.map(|last| server_state.opaque_cursors.encode_last_id_cursor(CURSOR_NAME, last.media_file_id))
.transpose()?
} else {
None
};
let media_domain = get_media_domain(&http_request);
let server_environment = server_state.server_environment;
@@ -93,9 +93,16 @@ pub async fn list_subfolders_handler(
CommonWebError::from_error(err)
})?;
let maybe_cursor = rows.last().map(|last| {
server_state.opaque_cursors.encode_last_id_cursor(CURSOR_NAME, last.id)
}).transpose()?;
// Only hand out a next-page cursor when this page was full. A short page
// means the list is exhausted, and emitting a cursor anyway would make
// clients fetch one guaranteed-empty trailing page.
let maybe_cursor = if rows.len() == limit as usize {
rows.last()
.map(|last| server_state.opaque_cursors.encode_last_id_cursor(CURSOR_NAME, last.id))
.transpose()?
} else {
None
};
let media_domain = get_media_domain(&http_request);
let server_environment = server_state.server_environment;
@@ -98,10 +98,16 @@ pub async fn moderator_list_staff_audit_logs_handler(
CommonWebError::from_error(err)
})?;
let maybe_cursor = records.last().map(|last| {
server_state.opaque_cursors
.encode_last_id_cursor(CURSOR_NAME, last.id)
}).transpose()?;
// Only hand out a next-page cursor when this page was full. A short page
// means the list is exhausted, and emitting a cursor anyway would make
// clients fetch one guaranteed-empty trailing page.
let maybe_cursor = if records.len() == limit as usize {
records.last()
.map(|last| server_state.opaque_cursors.encode_last_id_cursor(CURSOR_NAME, last.id))
.transpose()?
} else {
None
};
let audit_logs = records.into_iter().map(|r| {
StaffAuditLogResponse {
@@ -140,10 +140,16 @@ pub async fn moderator_list_email_address_changes_for_user_handler(
})?;
// 5. Encode the next-page cursor from the last id, if there was one.
let maybe_cursor = rows.last().map(|last| {
server_state.opaque_cursors
.encode_last_id_cursor(CURSOR_NAME, last.id)
}).transpose()?;
// Only hand out a next-page cursor when this page was full. A short page
// means the list is exhausted, and emitting a cursor anyway would make
// clients fetch one guaranteed-empty trailing page.
let maybe_cursor = if rows.len() == limit as usize {
rows.last()
.map(|last| server_state.opaque_cursors.encode_last_id_cursor(CURSOR_NAME, last.id))
.transpose()?
} else {
None
};
let changes = rows.into_iter().map(to_response_item).collect();
@@ -64,10 +64,16 @@ pub async fn moderator_list_global_user_referrals_handler(
CommonWebError::from_error(err)
})?;
let maybe_cursor = records.last().map(|last| {
server_state.opaque_cursors
.encode_last_id_cursor(CURSOR_NAME, last.id)
}).transpose()?;
// Only hand out a next-page cursor when this page was full. A short page
// means the list is exhausted, and emitting a cursor anyway would make
// clients fetch one guaranteed-empty trailing page.
let maybe_cursor = if records.len() == limit as usize {
records.last()
.map(|last| server_state.opaque_cursors.encode_last_id_cursor(CURSOR_NAME, last.id))
.transpose()?
} else {
None
};
let referrals = records.into_iter().map(|r| {
UserReferralResponse {
@@ -72,10 +72,16 @@ pub async fn moderator_list_user_referrals_for_user_handler(
CommonWebError::from_error(err)
})?;
let maybe_cursor = records.last().map(|last| {
server_state.opaque_cursors
.encode_last_id_cursor(CURSOR_NAME, last.id)
}).transpose()?;
// Only hand out a next-page cursor when this page was full. A short page
// means the list is exhausted, and emitting a cursor anyway would make
// clients fetch one guaranteed-empty trailing page.
let maybe_cursor = if records.len() == limit as usize {
records.last()
.map(|last| server_state.opaque_cursors.encode_last_id_cursor(CURSOR_NAME, last.id))
.transpose()?
} else {
None
};
let referrals = records.into_iter().map(|r| {
UserReferralResponse {
@@ -104,10 +104,16 @@ pub async fn moderator_list_user_session_impersonation_requests_for_user_handler
CommonWebError::from_error(err)
})?;
let maybe_cursor = records.last().map(|last| {
server_state.opaque_cursors
.encode_last_id_cursor(CURSOR_NAME, last.id)
}).transpose()?;
// Only hand out a next-page cursor when this page was full. A short page
// means the list is exhausted, and emitting a cursor anyway would make
// clients fetch one guaranteed-empty trailing page.
let maybe_cursor = if records.len() == limit as usize {
records.last()
.map(|last| server_state.opaque_cursors.encode_last_id_cursor(CURSOR_NAME, last.id))
.transpose()?
} else {
None
};
let impersonation_requests = records.into_iter().map(|r| {
UserImpersonationRequestResponse {
@@ -97,10 +97,16 @@ pub async fn moderator_list_user_session_impersonation_requests_handler(
CommonWebError::from_error(err)
})?;
let maybe_cursor = records.last().map(|last| {
server_state.opaque_cursors
.encode_last_id_cursor(CURSOR_NAME, last.id)
}).transpose()?;
// Only hand out a next-page cursor when this page was full. A short page
// means the list is exhausted, and emitting a cursor anyway would make
// clients fetch one guaranteed-empty trailing page.
let maybe_cursor = if records.len() == limit as usize {
records.last()
.map(|last| server_state.opaque_cursors.encode_last_id_cursor(CURSOR_NAME, last.id))
.transpose()?
} else {
None
};
let impersonation_requests = records.into_iter().map(|r| {
AllImpersonationRequestResponse {
@@ -101,9 +101,16 @@ pub async fn list_media_files_with_tag_handler(
CommonWebError::from_error(err)
})?;
let maybe_cursor = rows.last().map(|last| {
server_state.opaque_cursors.encode_last_id_cursor(CURSOR_NAME, last.media_file_id)
}).transpose()?;
// Only hand out a next-page cursor when this page was full. A short page
// means the list is exhausted, and emitting a cursor anyway would make
// clients fetch one guaranteed-empty trailing page.
let maybe_cursor = if rows.len() == limit as usize {
rows.last()
.map(|last| server_state.opaque_cursors.encode_last_id_cursor(CURSOR_NAME, last.media_file_id))
.transpose()?
} else {
None
};
let media_domain = get_media_domain(&http_request);
let server_environment = server_state.server_environment;
@@ -77,9 +77,16 @@ pub async fn list_tagged_media_files_handler(
CommonWebError::from_error(err)
})?;
let maybe_cursor = rows.last().map(|last| {
server_state.opaque_cursors.encode_last_id_cursor(CURSOR_NAME, last.media_file_id)
}).transpose()?;
// Only hand out a next-page cursor when this page was full. A short page
// means the list is exhausted, and emitting a cursor anyway would make
// clients fetch one guaranteed-empty trailing page.
let maybe_cursor = if rows.len() == limit as usize {
rows.last()
.map(|last| server_state.opaque_cursors.encode_last_id_cursor(CURSOR_NAME, last.media_file_id))
.transpose()?
} else {
None
};
let media_domain = get_media_domain(&http_request);
let server_environment = server_state.server_environment;
@@ -62,9 +62,16 @@ pub async fn list_tags_handler(
CommonWebError::from_error(err)
})?;
let maybe_cursor = rows.last().map(|last| {
server_state.opaque_cursors.encode_last_id_cursor(CURSOR_NAME, last.id)
}).transpose()?;
// Only hand out a next-page cursor when this page was full. A short page
// means the list is exhausted, and emitting a cursor anyway would make
// clients fetch one guaranteed-empty trailing page.
let maybe_cursor = if rows.len() == limit as usize {
rows.last()
.map(|last| server_state.opaque_cursors.encode_last_id_cursor(CURSOR_NAME, last.id))
.transpose()?
} else {
None
};
Ok(Json(ListTagsSuccessResponse {
success: true,
@@ -77,9 +77,16 @@ pub async fn list_untagged_media_files_handler(
CommonWebError::from_error(err)
})?;
let maybe_cursor = rows.last().map(|last| {
server_state.opaque_cursors.encode_last_id_cursor(CURSOR_NAME, last.media_file_id)
}).transpose()?;
// Only hand out a next-page cursor when this page was full. A short page
// means the list is exhausted, and emitting a cursor anyway would make
// clients fetch one guaranteed-empty trailing page.
let maybe_cursor = if rows.len() == limit as usize {
rows.last()
.map(|last| server_state.opaque_cursors.encode_last_id_cursor(CURSOR_NAME, last.media_file_id))
.transpose()?
} else {
None
};
let media_domain = get_media_domain(&http_request);
let server_environment = server_state.server_environment;
@@ -26,7 +26,7 @@ impl WebOpaqueCursorEncoderV2 {
self.encoder.encode_last_id_cursor(name, id)
.map_err(|err| {
warn!("Failed to encode pagination cursor for {:?}: {:?}", name, err);
CommonWebError::server_error_with_message("Failed to encode cursor")
CommonWebError::from_error(err)
})
}