mirror of
https://github.com/okieselbach/AutopilotMonitor.git
synced 2026-10-24 12:42:38 +00:00
chore(backend,web): bump feedback comment limit 500 -> 4096 chars
This commit is contained in:
+7
-3
@@ -59,15 +59,19 @@ public sealed class SubmitOffboardingFeedbackFunctionTests
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task Submit_TrimsAndTruncatesComment_To500Chars()
|
||||
public async Task Submit_TrimsAndTruncatesComment_To4096Chars()
|
||||
{
|
||||
// Departing tenants often have more context to share than the in-app 500-char
|
||||
// limit allows; the offboarding endpoint accepts up to 4096 chars (still well
|
||||
// under the Azure Tables 64 KB property cap). Anything beyond is silently
|
||||
// truncated rather than rejected so the user's submit doesn't fail outright.
|
||||
var h = Harness.WithInProgressMarker();
|
||||
var longComment = new string('x', 600);
|
||||
var longComment = new string('x', 5000);
|
||||
|
||||
var result = await h.Sut.ProcessSubmitAsync(TenantId, Upn, DisplayName, " " + longComment + " ");
|
||||
|
||||
Assert.Equal(SubmitOutcome.Ok, result.Outcome);
|
||||
Assert.Equal(500, h.Repo.PersistedOffboardingFeedback!.Comment!.Length);
|
||||
Assert.Equal(4096, h.Repo.PersistedOffboardingFeedback!.Comment!.Length);
|
||||
Assert.DoesNotContain(" ", h.Repo.PersistedOffboardingFeedback.Comment!.Substring(0, 1));
|
||||
}
|
||||
|
||||
|
||||
+9
-3
@@ -32,7 +32,11 @@ namespace AutopilotMonitor.Functions.Functions.Admin;
|
||||
/// </summary>
|
||||
public class SubmitOffboardingFeedbackFunction
|
||||
{
|
||||
private const int MaxCommentLength = 500;
|
||||
// Departing tenants often have more context to share than the in-app 500-char limit
|
||||
// allows. Azure Table Storage caps a single string property at 64 KB, so 4096 chars
|
||||
// (max ~16 KB UTF-8) sits well within both the table and the 16 KB request-body
|
||||
// guard above.
|
||||
private const int MaxCommentLength = 4096;
|
||||
|
||||
private readonly ILogger<SubmitOffboardingFeedbackFunction> _logger;
|
||||
private readonly IOffboardingAuditRepository _offboardingRepo;
|
||||
@@ -72,10 +76,12 @@ public class SubmitOffboardingFeedbackFunction
|
||||
}
|
||||
|
||||
// Reject oversized bodies before parsing — defense against accidental clipboard-paste
|
||||
// of huge logs into the textarea.
|
||||
// of huge logs into the textarea. 64 KB is the Azure Table Storage single-property
|
||||
// cap, which gives 4096 chars (max ~16 KB UTF-8) plus JSON envelope plenty of room
|
||||
// while still bouncing pathological multi-MB pastes before we read them.
|
||||
if (req.Headers.TryGetValues("Content-Length", out var clValues)
|
||||
&& long.TryParse(clValues.FirstOrDefault(), out var contentLength)
|
||||
&& contentLength > 16_384)
|
||||
&& contentLength > 65_536)
|
||||
{
|
||||
return await BadRequest(req, "Request body too large");
|
||||
}
|
||||
|
||||
@@ -159,10 +159,12 @@ namespace AutopilotMonitor.Functions.Functions.Feedback
|
||||
return badRequest;
|
||||
}
|
||||
|
||||
// Trim comment
|
||||
// Trim comment. Aligned with the offboarding-feedback endpoint at 4096 chars
|
||||
// so users have room for a substantive comment without hitting a tight limit;
|
||||
// Azure Tables caps a single property at 64 KB so this stays well within.
|
||||
var comment = body.Comment?.Trim();
|
||||
if (comment?.Length > 500)
|
||||
comment = comment.Substring(0, 500);
|
||||
if (comment?.Length > 4096)
|
||||
comment = comment.Substring(0, 4096);
|
||||
|
||||
// Upsert feedback record
|
||||
await _feedbackRepo.SaveInAppFeedbackAsync(new FeedbackEntry
|
||||
|
||||
@@ -39,7 +39,7 @@ interface OffboardingSectionProps {
|
||||
* (re-onboard → offboard) gets a fresh prompt each time. */
|
||||
const FEEDBACK_SUBMITTED_KEY_PREFIX = "offboard-feedback-submitted:";
|
||||
|
||||
const FEEDBACK_MAX_CHARS = 500;
|
||||
const FEEDBACK_MAX_CHARS = 4096;
|
||||
|
||||
function formatRemaining(totalSeconds: number): string {
|
||||
if (totalSeconds <= 0) return "0 min";
|
||||
|
||||
@@ -7,6 +7,10 @@ import { authenticatedFetch } from "@/lib/authenticatedFetch";
|
||||
|
||||
type Phase = "loading" | "bubble" | "form" | "thankyou" | "hidden";
|
||||
|
||||
// Aligned with the offboarding-feedback textarea — gives users room for substantive
|
||||
// comments without hitting an artificial 500-char limit. Backend cap matches.
|
||||
const FEEDBACK_MAX_CHARS = 4096;
|
||||
|
||||
export default function FeedbackBubble() {
|
||||
const { isAuthenticated, user, getAccessToken } = useAuth();
|
||||
const [phase, setPhase] = useState<Phase>("loading");
|
||||
@@ -163,13 +167,13 @@ export default function FeedbackBubble() {
|
||||
<div>
|
||||
<textarea
|
||||
value={comment}
|
||||
onChange={(e) => setComment(e.target.value.slice(0, 500))}
|
||||
onChange={(e) => setComment(e.target.value.slice(0, FEEDBACK_MAX_CHARS))}
|
||||
placeholder="Any feedback? (optional)"
|
||||
rows={2}
|
||||
className="w-full px-3 py-2 text-sm bg-gray-50 dark:bg-gray-700 text-gray-900 dark:text-gray-100 border border-gray-300 dark:border-gray-600 rounded-lg resize-none focus:ring-2 focus:ring-blue-500 focus:border-blue-500 outline-none placeholder-gray-400 dark:placeholder-gray-500"
|
||||
/>
|
||||
{comment.length > 0 && (
|
||||
<p className="text-xs text-gray-400 text-right mt-0.5">{comment.length}/500</p>
|
||||
<p className="text-xs text-gray-400 text-right mt-0.5">{comment.length}/{FEEDBACK_MAX_CHARS}</p>
|
||||
)}
|
||||
</div>
|
||||
|
||||
|
||||
Reference in New Issue
Block a user