-
Notifications
You must be signed in to change notification settings - Fork 44
Preserve Workbench business rule payloads for clearer validation errors #8048
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
8bf89e5
d6dffa2
adfbba1
b5a50dc
9d8e306
f692eeb
64071af
7a4c538
3007e56
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -15,7 +15,7 @@ import { | |
| formatDisjunction, | ||
| } from '../Atoms/Internationalization'; | ||
| import { getField } from '../DataModel/helpers'; | ||
| import { tables } from '../DataModel/tables'; | ||
| import { getTable, tables } from '../DataModel/tables'; | ||
| import type { Tables } from '../DataModel/types'; | ||
|
|
||
| /* | ||
|
|
@@ -256,14 +256,96 @@ export function resolveBackendParsingMessage( | |
| else return undefined; | ||
| } | ||
|
|
||
| function withConflictingRecordIds( | ||
| message: LocalizedString, | ||
| payload: IR<unknown> | ||
| ): LocalizedString { | ||
| const conflicting = payload.conflicting; | ||
| return Array.isArray(conflicting) && conflicting.length > 0 | ||
| ? localized( | ||
| `${message} (Conflicting record IDs: ${conflicting.join(', ')})` | ||
| ) | ||
| : message; | ||
| } | ||
|
Comment on lines
+259
to
+269
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Hardcoded English string defeats localization. The "Conflicting record IDs:" prefix is hardcoded in English while the surrounding message is localized via 🌐 Suggested shape- ? localized(
- `${message} (Conflicting record IDs: ${conflicting.join(', ')})`
- )
+ ? localized(
+ `${message} (${backEndText.conflictingRecordIds({
+ ids: conflicting.join(', '),
+ })})`
+ )
: message;(Add a corresponding 🤖 Prompt for AI Agents |
||
|
|
||
| function getStringPayload(payload: IR<unknown>, key: string): string { | ||
| const value = payload[key]; | ||
| return typeof value === 'string' ? value : ''; | ||
| } | ||
|
|
||
| function getSchemaTableLabel(tableName: string): LocalizedString { | ||
| return getTable(tableName)?.label ?? localized(tableName); | ||
| } | ||
|
|
||
| function getSchemaFieldLabel( | ||
| tableName: string, | ||
| fieldName: string | ||
| ): LocalizedString { | ||
| const lookupFieldName = fieldName.split('__').join('.'); | ||
| return ( | ||
| getTable(tableName)?.getField(lookupFieldName)?.label ?? | ||
| localized(fieldName) | ||
| ); | ||
| } | ||
|
|
||
| function getSchemaFieldLabels( | ||
| tableName: string, | ||
| fieldNames: string | ||
| ): LocalizedString { | ||
| const labels = fieldNames | ||
| .split(',') | ||
| .map((fieldName) => fieldName.trim()) | ||
| .filter((fieldName) => fieldName.length > 0) | ||
| .map((fieldName) => getSchemaFieldLabel(tableName, fieldName)); | ||
| return labels.length === 0 | ||
| ? localized(fieldNames) | ||
| : formatConjunction(labels); | ||
| } | ||
|
|
||
| function resolveBackendBusinessRuleMessage( | ||
| payload: IR<unknown> | ||
| ): LocalizedString | undefined { | ||
| const tableName = getStringPayload(payload, 'table'); | ||
| if (payload.localizationKey === 'fieldNotUnique') | ||
| return withConflictingRecordIds( | ||
| backEndText.fieldNotUnique({ | ||
| tableName: getSchemaTableLabel(tableName), | ||
| fieldName: getSchemaFieldLabels( | ||
| tableName, | ||
| getStringPayload(payload, 'fieldName') | ||
| ), | ||
| }), | ||
| payload | ||
| ); | ||
| else if (payload.localizationKey === 'childFieldNotUnique') | ||
| return withConflictingRecordIds( | ||
| backEndText.childFieldNotUnique({ | ||
| tableName: getSchemaTableLabel(tableName), | ||
| fieldName: getSchemaFieldLabels( | ||
| tableName, | ||
| getStringPayload(payload, 'fieldName') | ||
| ), | ||
| parentField: getSchemaFieldLabels( | ||
| tableName, | ||
| getStringPayload(payload, 'parentField') | ||
| ), | ||
| }), | ||
| payload | ||
| ); | ||
| else return undefined; | ||
| } | ||
|
Comment on lines
+305
to
+336
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Guard against missing If 🤖 Prompt for AI Agents |
||
|
|
||
| /** Back-end sends a validation key. Front-end translates it */ | ||
| export function resolveValidationMessage( | ||
| key: string, | ||
| payload: IR<unknown> | ||
| ): LocalizedString { | ||
| const baseParsedMessage = resolveBackendParsingMessage(key, payload); | ||
| const businessRuleMessage = resolveBackendBusinessRuleMessage(payload); | ||
| if (baseParsedMessage !== undefined) { | ||
| return baseParsedMessage; | ||
| } else if (businessRuleMessage !== undefined) { | ||
| return businessRuleMessage; | ||
| } else if (key === 'failedParsingPickList') | ||
| return backEndText.failedParsingPickList({ | ||
| value: `"${payload.value as string}"`, | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Validate/sanitize preserved payload values before returning
FailedBusinessRule.Right now any
dictinexception.args[1]is accepted. If a business-rule payload includes non-serializable values, upload-result JSON encoding can fail at runtime. Please gate payload contents to the allowed shape (or safely fallback).Proposed fix
Also applies to: 264-268
🤖 Prompt for AI Agents