btcmaps v1: timeout vs caller-cancel split + canonical-case type/subType on write
Addresses thgO-O review id 4274558448 (PR #224) items #2 and #3. #2 OperationCanceledException vs HttpClient.Timeout: the prior catch-when filter excluded OCE wholesale to avoid swallowing client disconnects, but HttpClient.Timeout surfaces as TaskCanceledException (inherits from OCE) so an upstream GitHub timeout slipped through and fell out as a 500 instead of the intended 502. Now split into three arms: caller-cancel rethrows (info-log + drop the connection), upstream-timeout returns 504 with discriminant directory-upstream- timeout, anything else still 502 directory-upstream-failed. #3 type/subType case normalization on write: ValidTypes + ValidMerchantSubTypes match OrdinalIgnoreCase at validation, but the write at BuildMerchantEntry preserved the user-submitted casing. A submission of "Merchants" / "Books" would pass validation and land non-canonical in merchants.json. Normalize to ToLowerInvariant on write so the file stays in the lowercase convention. Country is already validated against a case-sensitive Ordinal set so the validator rejects non-uppercase before reaching the writer; no write- path change needed there.
This commit is contained in:
parent
b84538f2c2
commit
170d56d6d8
@ -49,7 +49,28 @@ public sealed class BtcMapsController(
|
||||
correlationId
|
||||
});
|
||||
}
|
||||
catch (Exception ex) when (ex is not OperationCanceledException)
|
||||
catch (OperationCanceledException ex) when (cancellationToken.IsCancellationRequested)
|
||||
{
|
||||
// Caller cancelled (client disconnect, request abort). Rethrow so
|
||||
// the pipeline drops the connection without producing a response
|
||||
// body the client will never read.
|
||||
logger.LogInformation(ex, "BTCMaps directory submission cancelled by caller (correlationId={CorrelationId})", correlationId);
|
||||
throw;
|
||||
}
|
||||
catch (OperationCanceledException ex)
|
||||
{
|
||||
// OCE without caller cancellation = HttpClient.Timeout surfacing as
|
||||
// TaskCanceledException. Treat as an upstream timeout, distinct
|
||||
// from a generic 502 so ops + the plugin client can tell them apart.
|
||||
logger.LogError(ex, "BTCMaps directory submission timed out upstream (correlationId={CorrelationId}) for {Name} ({Url})",
|
||||
correlationId, request.Name, request.Url);
|
||||
return StatusCode(StatusCodes.Status504GatewayTimeout, new
|
||||
{
|
||||
error = "directory-upstream-timeout",
|
||||
correlationId
|
||||
});
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
logger.LogError(ex, "BTCMaps directory submission failed (correlationId={CorrelationId}) for {Name} ({Url})",
|
||||
correlationId, request.Name, request.Url);
|
||||
|
||||
@ -273,9 +273,15 @@ public sealed class BtcMapsService
|
||||
w.WriteString("name", request.Name!.Trim());
|
||||
w.WriteString("url", request.Url!.Trim());
|
||||
w.WriteString("description", request.Description!.Trim());
|
||||
w.WriteString("type", request.Type!.Trim());
|
||||
// type + subType are validated case-insensitively above
|
||||
// (ValidTypes / ValidMerchantSubTypes use OrdinalIgnoreCase) but
|
||||
// the upstream merchants.json convention is lowercase. Normalize
|
||||
// on write so a submission of "Merchants" / "Books" lands as
|
||||
// "merchants" / "books" in the file. Country uses a case-sensitive
|
||||
// Ordinal set so the validator already rejects non-uppercase.
|
||||
w.WriteString("type", request.Type!.Trim().ToLowerInvariant());
|
||||
if (!string.IsNullOrWhiteSpace(request.SubType))
|
||||
w.WriteString("subType", request.SubType.Trim());
|
||||
w.WriteString("subType", request.SubType.Trim().ToLowerInvariant());
|
||||
if (!string.IsNullOrWhiteSpace(request.Country))
|
||||
w.WriteString("country", request.Country.Trim());
|
||||
if (!string.IsNullOrWhiteSpace(request.Twitter))
|
||||
|
||||
Loading…
Reference in New Issue
Block a user