Skip to content

Commit 4b2c4f2

Browse files
Merge pull request #734 from quickfixgo/validate-fields-have-values
ValidateFieldsHaveValues
2 parents e4fe18c + 8e124b4 commit 4b2c4f2

File tree

4 files changed

+115
-23
lines changed

4 files changed

+115
-23
lines changed

config/configuration.go

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -454,6 +454,19 @@ const (
454454
// - N
455455
ValidateFieldsOutOfOrder string = "ValidateFieldsOutOfOrder"
456456

457+
// ValidateFieldsHaveValues if set to N, fields without values (i.e. |11=| for an empty ClOrdID)
458+
// will not be rejected, even if RejectInvalidMessage is set to N.
459+
// Useful for connecting to systems that improperly send empty tags.
460+
//
461+
// Required: No
462+
//
463+
// Default: Y
464+
//
465+
// Valid Values:
466+
// - Y
467+
// - N
468+
ValidateFieldsHaveValues string = "ValidateFieldsHaveValues"
469+
457470
// CheckLatency if set to Y, messages must be received from the counter-party within a defined number of seconds.
458471
// It is useful to turn this off if a system uses localtime for it's timestamps instead of GMT.
459472
//

session_factory.go

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -99,6 +99,12 @@ func (f sessionFactory) newSession(
9999
}
100100
}
101101

102+
if settings.HasSetting(config.ValidateFieldsHaveValues) {
103+
if validatorSettings.CheckFieldsHaveValues, err = settings.BoolSetting(config.ValidateFieldsHaveValues); err != nil {
104+
return
105+
}
106+
}
107+
102108
if settings.HasSetting(config.RejectInvalidMessage) {
103109
if validatorSettings.RejectInvalidMessage, err = settings.BoolSetting(config.RejectInvalidMessage); err != nil {
104110
return
@@ -117,6 +123,8 @@ func (f sessionFactory) newSession(
117123
}
118124
}
119125

126+
// Always use a default message validator without data dictionaries
127+
s.Validator = NewValidator(validatorSettings, nil, nil)
120128
if sessionID.IsFIXT() {
121129
if s.DefaultApplVerID, err = settings.Setting(config.DefaultApplVerID); err != nil {
122130
return

validation.go

Lines changed: 31 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -34,12 +34,14 @@ type ValidatorSettings struct {
3434
RejectInvalidMessage bool
3535
AllowUnknownMessageFields bool
3636
CheckUserDefinedFields bool
37+
CheckFieldsHaveValues bool
3738
}
3839

3940
// Default configuration for message validation.
4041
// See http://www.quickfixengine.org/quickfix/doc/html/configuration.html.
4142
var defaultValidatorSettings = ValidatorSettings{
4243
CheckFieldsOutOfOrder: true,
44+
CheckFieldsHaveValues: true,
4345
RejectInvalidMessage: true,
4446
AllowUnknownMessageFields: false,
4547
CheckUserDefinedFields: true,
@@ -102,21 +104,21 @@ func (v *fixtValidator) Validate(msg *Message) MessageRejectError {
102104
}
103105

104106
func validateFIX(d *datadictionary.DataDictionary, settings ValidatorSettings, msgType string, msg *Message) MessageRejectError {
105-
if err := validateMsgType(d, msgType, msg); err != nil {
106-
return err
107-
}
108-
109-
if err := validateRequired(d, d, msgType, msg); err != nil {
110-
return err
111-
}
107+
if d != nil {
108+
if err := validateMsgType(d, msgType, msg); err != nil {
109+
return err
110+
}
112111

113-
if settings.CheckFieldsOutOfOrder {
114-
if err := validateOrder(msg); err != nil {
112+
if err := validateRequired(d, d, msgType, msg); err != nil {
115113
return err
116114
}
117115
}
118116

119-
if settings.RejectInvalidMessage {
117+
if err := validateFieldContent(msg, settings.CheckFieldsHaveValues, settings.CheckFieldsOutOfOrder); err != nil {
118+
return err
119+
}
120+
121+
if settings.RejectInvalidMessage && d != nil {
120122
if err := validateFields(d, d, settings, msgType, msg); err != nil {
121123
return err
122124
}
@@ -130,21 +132,21 @@ func validateFIX(d *datadictionary.DataDictionary, settings ValidatorSettings, m
130132
}
131133

132134
func validateFIXT(transportDD, appDD *datadictionary.DataDictionary, settings ValidatorSettings, msgType string, msg *Message) MessageRejectError {
133-
if err := validateMsgType(appDD, msgType, msg); err != nil {
134-
return err
135-
}
136-
137-
if err := validateRequired(transportDD, appDD, msgType, msg); err != nil {
138-
return err
139-
}
135+
if appDD != nil && transportDD != nil {
136+
if err := validateMsgType(appDD, msgType, msg); err != nil {
137+
return err
138+
}
140139

141-
if settings.CheckFieldsOutOfOrder {
142-
if err := validateOrder(msg); err != nil {
140+
if err := validateRequired(transportDD, appDD, msgType, msg); err != nil {
143141
return err
144142
}
145143
}
146144

147-
if settings.RejectInvalidMessage {
145+
if err := validateFieldContent(msg, settings.CheckFieldsHaveValues, settings.CheckFieldsOutOfOrder); err != nil {
146+
return err
147+
}
148+
149+
if settings.RejectInvalidMessage && appDD != nil && transportDD != nil {
148150
if err := validateFields(transportDD, appDD, settings, msgType, msg); err != nil {
149151
return err
150152
}
@@ -270,20 +272,26 @@ func validateVisitGroupField(fieldDef *datadictionary.FieldDef, fieldStack []Tag
270272
return fieldStack, nil
271273
}
272274

273-
func validateOrder(msg *Message) MessageRejectError {
275+
func validateFieldContent(msg *Message, checkFieldsHaveValues, checkFieldsOutOfOrder bool) MessageRejectError {
276+
if !checkFieldsHaveValues && !checkFieldsOutOfOrder {
277+
return nil
278+
}
274279
inHeader := true
275280
inTrailer := false
276281
for _, field := range msg.fields {
277282
t := field.tag
283+
if checkFieldsHaveValues && len(field.value) == 0 {
284+
return TagSpecifiedWithoutAValue(t)
285+
}
278286
switch {
279287
case inHeader && t.IsHeader():
280288
case inHeader && !t.IsHeader():
281289
inHeader = false
282-
case !inHeader && t.IsHeader():
290+
case !inHeader && t.IsHeader() && checkFieldsOutOfOrder:
283291
return tagSpecifiedOutOfRequiredOrder(t)
284292
case t.IsTrailer():
285293
inTrailer = true
286-
case inTrailer && !t.IsTrailer():
294+
case inTrailer && !t.IsTrailer() && checkFieldsOutOfOrder:
287295
return tagSpecifiedOutOfRequiredOrder(t)
288296
}
289297
}

validation_test.go

Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,9 @@ func TestValidate(t *testing.T) {
4444
tcInvalidTagNumberTrailerFixT(),
4545
tcTagSpecifiedWithoutAValue(),
4646
tcTagSpecifiedWithoutAValueFixT(),
47+
tcTagSpecifiedWithoutAValueValidateHasValues(),
48+
tcTagSpecifiedWithoutAValueFixTValidateHasValues(),
49+
tcTagSpecifiedWithoutAValueValidateHasValuesNoDataDictionary(),
4750
tcInvalidMsgType(),
4851
tcInvalidMsgTypeFixT(),
4952
tcValueIsIncorrect(),
@@ -512,6 +515,47 @@ func tcTagSpecifiedWithoutAValue() validateTest {
512515
}
513516
}
514517

518+
func tcTagSpecifiedWithoutAValueValidateHasValues() validateTest {
519+
dict, _ := datadictionary.Parse("spec/FIX40.xml")
520+
settings := defaultValidatorSettings
521+
settings.RejectInvalidMessage = false
522+
settings.CheckFieldsHaveValues = true
523+
validator := NewValidator(settings, dict, nil)
524+
builder := createFIX40NewOrderSingle()
525+
526+
bogusTag := Tag(109)
527+
builder.Body.SetField(bogusTag, FIXString(""))
528+
msgBytes := builder.build()
529+
530+
return validateTest{
531+
TestName: "Tag SpecifiedWithoutAValue",
532+
Validator: validator,
533+
MessageBytes: msgBytes,
534+
ExpectedRejectReason: rejectReasonTagSpecifiedWithoutAValue,
535+
ExpectedRefTagID: &bogusTag,
536+
}
537+
}
538+
539+
func tcTagSpecifiedWithoutAValueValidateHasValuesNoDataDictionary() validateTest {
540+
settings := defaultValidatorSettings
541+
settings.RejectInvalidMessage = false
542+
settings.CheckFieldsHaveValues = true
543+
validator := NewValidator(settings, nil, nil)
544+
builder := createFIX40NewOrderSingle()
545+
546+
bogusTag := Tag(109)
547+
builder.Body.SetField(bogusTag, FIXString(""))
548+
msgBytes := builder.build()
549+
550+
return validateTest{
551+
TestName: "Tag SpecifiedWithoutAValue",
552+
Validator: validator,
553+
MessageBytes: msgBytes,
554+
ExpectedRejectReason: rejectReasonTagSpecifiedWithoutAValue,
555+
ExpectedRefTagID: &bogusTag,
556+
}
557+
}
558+
515559
func tcTagSpecifiedWithoutAValueFixT() validateTest {
516560
tDict, _ := datadictionary.Parse("spec/FIXT11.xml")
517561
appDict, _ := datadictionary.Parse("spec/FIX50SP2.xml")
@@ -531,6 +575,25 @@ func tcTagSpecifiedWithoutAValueFixT() validateTest {
531575
}
532576
}
533577

578+
func tcTagSpecifiedWithoutAValueFixTValidateHasValues() validateTest {
579+
tDict, _ := datadictionary.Parse("spec/FIXT11.xml")
580+
appDict, _ := datadictionary.Parse("spec/FIX50SP2.xml")
581+
validator := NewValidator(defaultValidatorSettings, appDict, tDict)
582+
builder := createFIX50SP2NewOrderSingle()
583+
584+
bogusTag := Tag(109)
585+
builder.Body.SetField(bogusTag, FIXString(""))
586+
msgBytes := builder.build()
587+
588+
return validateTest{
589+
TestName: "Tag SpecifiedWithoutAValue FIXT",
590+
Validator: validator,
591+
MessageBytes: msgBytes,
592+
ExpectedRejectReason: rejectReasonTagSpecifiedWithoutAValue,
593+
ExpectedRefTagID: &bogusTag,
594+
}
595+
}
596+
534597
func tcInvalidMsgType() validateTest {
535598
dict, _ := datadictionary.Parse("spec/FIX40.xml")
536599
validator := NewValidator(defaultValidatorSettings, dict, nil)

0 commit comments

Comments
 (0)