fix(09): WR-04 apply a form field's required flag only in the contexts that can supply it
This commit is contained in:
@@ -335,7 +335,7 @@ func (s CRUDService) save(ctx context.Context, cc *CompiledController, id any, i
|
|||||||
return &CapabilityError{ControllerID: controllerID(cc)}
|
return &CapabilityError{ControllerID: controllerID(cc)}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
rules := mergedRules(cc, target)
|
rules := mergedRules(cc, target, op)
|
||||||
msgs, err := lagoon.Validate(ctx, tx, target, rules, valuesForRules(target, rules), nil)
|
msgs, err := lagoon.Validate(ctx, tx, target, rules, valuesForRules(target, rules), nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return &CapabilityError{ControllerID: controllerID(cc)}
|
return &CapabilityError{ControllerID: controllerID(cc)}
|
||||||
@@ -689,7 +689,10 @@ func fillAllowed(cc *CompiledController, model any, op string) []string {
|
|||||||
return out
|
return out
|
||||||
}
|
}
|
||||||
|
|
||||||
func mergedRules(cc *CompiledController, model any) map[string]string {
|
// mergedRules combines the model's rules with the form's `required` flags. A
|
||||||
|
// field whose `context` hides it on op cannot be supplied there, so its form
|
||||||
|
// level `required` does not apply to that operation.
|
||||||
|
func mergedRules(cc *CompiledController, model any, op string) map[string]string {
|
||||||
out := map[string]string{}
|
out := map[string]string{}
|
||||||
if rules, ok := model.(hasRules); ok && rules != nil {
|
if rules, ok := model.(hasRules); ok && rules != nil {
|
||||||
for key, rule := range rules.Rules() {
|
for key, rule := range rules.Rules() {
|
||||||
@@ -702,7 +705,7 @@ func mergedRules(cc *CompiledController, model any) map[string]string {
|
|||||||
for _, field := range cc.Form.Fields {
|
for _, field := range cc.Form.Fields {
|
||||||
// Relation fields are not writable columns. required stays on the
|
// Relation fields are not writable columns. required stays on the
|
||||||
// schema for the client, but it cannot be checked by Fill.
|
// schema for the client, but it cannot be checked by Fill.
|
||||||
if field.Required && scalarFormField(field.Type) {
|
if field.Required && scalarFormField(field.Type) && contextAllows(cc, field.Name, op) {
|
||||||
out[field.Name] = mergeRequired(out[field.Name])
|
out[field.Name] = mergeRequired(out[field.Name])
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -608,3 +608,32 @@ func TestIsJSONScalar(t *testing.T) {
|
|||||||
type scalarAsArray string
|
type scalarAsArray string
|
||||||
|
|
||||||
func (s scalarAsArray) MarshalJSON() ([]byte, error) { return json.Marshal([]string{string(s)}) }
|
func (s scalarAsArray) MarshalJSON() ([]byte, error) { return json.Marshal([]string{string(s)}) }
|
||||||
|
|
||||||
|
// TestCRUDRequiredFollowsContext pins WR-04: a form field that is `required`
|
||||||
|
// but limited to the update context cannot be supplied on create, so it must
|
||||||
|
// not make every create fail; it still binds on update.
|
||||||
|
func TestCRUDRequiredFollowsContext(t *testing.T) {
|
||||||
|
svc, cc, db := crudFixture(t)
|
||||||
|
for i := range cc.Form.Fields {
|
||||||
|
if cc.Form.Fields[i].Name == "note" {
|
||||||
|
cc.Form.Fields[i].Required = true
|
||||||
|
cc.Form.Fields[i].Context = &fieldContext{values: []string{"update"}}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
created, err := svc.CreateRecord(context.Background(), cc, RecordInput{Body: crudBody(t, `{"name":"Ada"}`)})
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("create with an update-only required field: %v", err)
|
||||||
|
}
|
||||||
|
id := created.Data["id"]
|
||||||
|
if _, err := svc.UpdateRecord(context.Background(), cc, id, RecordInput{Body: crudBody(t, `{"name":"Bea"}`)}); err == nil {
|
||||||
|
t.Fatal("update without the required update-context field succeeded")
|
||||||
|
} else {
|
||||||
|
assertValidation(t, err, "note", "The note field is required.")
|
||||||
|
}
|
||||||
|
if _, err := svc.UpdateRecord(context.Background(), cc, id, RecordInput{Body: crudBody(t, `{"name":"Bea","note":"n"}`)}); err != nil {
|
||||||
|
t.Fatalf("update with the field: %v", err)
|
||||||
|
}
|
||||||
|
if row := loadCrud(t, db, "Bea"); row.Note != "n" {
|
||||||
|
t.Fatalf("row=%+v", row)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user