Skip to content

Commit e2a2f27

Browse files
committed
feat: AllowUnknownMessageFields & CheckUserDefinedFields
1 parent b7e2597 commit e2a2f27

File tree

4 files changed

+244
-20
lines changed

4 files changed

+244
-20
lines changed

config/configuration.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,8 @@ const (
6666
MaxLatency string = "MaxLatency"
6767
PersistMessages string = "PersistMessages"
6868
RejectInvalidMessage string = "RejectInvalidMessage"
69+
AllowUnknownMessageFields string = "AllowUnknownMessageFields"
70+
CheckUserDefinedFields string = "CheckUserDefinedFields"
6971
DynamicSessions string = "DynamicSessions"
7072
DynamicQualifier string = "DynamicQualifier"
7173
)

session_factory.go

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

102+
if settings.HasSetting(config.AllowUnknownMessageFields) {
103+
if validatorSettings.AllowUnknownMessageFields, err = settings.BoolSetting(config.AllowUnknownMessageFields); err != nil {
104+
return
105+
}
106+
}
107+
108+
if settings.HasSetting(config.CheckUserDefinedFields) {
109+
if validatorSettings.CheckUserDefinedFields, err = settings.BoolSetting(config.CheckUserDefinedFields); err != nil {
110+
return
111+
}
112+
}
113+
102114
if sessionID.IsFIXT() {
103115
if s.DefaultApplVerID, err = settings.Setting(config.DefaultApplVerID); err != nil {
104116
return

validation.go

Lines changed: 60 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -19,22 +19,30 @@ import (
1919
"github.com/quickfixgo/quickfix/datadictionary"
2020
)
2121

22+
const (
23+
UserDefinedTagMin int = 5000
24+
)
25+
2226
// Validator validates a FIX message.
2327
type Validator interface {
2428
Validate(*Message) MessageRejectError
2529
}
2630

2731
// ValidatorSettings describe validation behavior.
2832
type ValidatorSettings struct {
29-
CheckFieldsOutOfOrder bool
30-
RejectInvalidMessage bool
33+
CheckFieldsOutOfOrder bool
34+
RejectInvalidMessage bool
35+
AllowUnknownMessageFields bool
36+
CheckUserDefinedFields bool
3137
}
3238

3339
// Default configuration for message validation.
3440
// See http://www.quickfixengine.org/quickfix/doc/html/configuration.html.
3541
var defaultValidatorSettings = ValidatorSettings{
36-
CheckFieldsOutOfOrder: true,
37-
RejectInvalidMessage: true,
42+
CheckFieldsOutOfOrder: true,
43+
RejectInvalidMessage: true,
44+
AllowUnknownMessageFields: false,
45+
CheckUserDefinedFields: true,
3846
}
3947

4048
type fixValidator struct {
@@ -109,11 +117,11 @@ func validateFIX(d *datadictionary.DataDictionary, settings ValidatorSettings, m
109117
}
110118

111119
if settings.RejectInvalidMessage {
112-
if err := validateFields(d, d, msgType, msg); err != nil {
120+
if err := validateFields(d, d, settings, msgType, msg); err != nil {
113121
return err
114122
}
115123

116-
if err := validateWalk(d, d, msgType, msg); err != nil {
124+
if err := validateWalk(d, d, settings, msgType, msg); err != nil {
117125
return err
118126
}
119127
}
@@ -137,11 +145,11 @@ func validateFIXT(transportDD, appDD *datadictionary.DataDictionary, settings Va
137145
}
138146

139147
if settings.RejectInvalidMessage {
140-
if err := validateFields(transportDD, appDD, msgType, msg); err != nil {
148+
if err := validateFields(transportDD, appDD, settings, msgType, msg); err != nil {
141149
return err
142150
}
143151

144-
if err := validateWalk(transportDD, appDD, msgType, msg); err != nil {
152+
if err := validateWalk(transportDD, appDD, settings, msgType, msg); err != nil {
145153
return err
146154
}
147155
}
@@ -156,7 +164,7 @@ func validateMsgType(d *datadictionary.DataDictionary, msgType string, _ *Messag
156164
return nil
157165
}
158166

159-
func validateWalk(transportDD *datadictionary.DataDictionary, appDD *datadictionary.DataDictionary, msgType string, msg *Message) MessageRejectError {
167+
func validateWalk(transportDD *datadictionary.DataDictionary, appDD *datadictionary.DataDictionary, settings ValidatorSettings, msgType string, msg *Message) MessageRejectError {
160168
remainingFields := msg.fields
161169
iteratedTags := make(datadictionary.TagSet)
162170

@@ -178,15 +186,19 @@ func validateWalk(transportDD *datadictionary.DataDictionary, appDD *datadiction
178186
messageDef = appDD.Messages[msgType]
179187
}
180188

181-
if fieldDef, ok = messageDef.Fields[int(tag)]; !ok {
182-
return TagNotDefinedForThisMessageType(tag)
183-
}
184-
185189
if _, duplicate := iteratedTags[int(tag)]; duplicate {
186190
return tagAppearsMoreThanOnce(tag)
187191
}
188192
iteratedTags.Add(int(tag))
189193

194+
if fieldDef, ok = messageDef.Fields[int(tag)]; !ok {
195+
if !checkFieldNotDefined(settings, tag) {
196+
return TagNotDefinedForThisMessageType(tag)
197+
}
198+
remainingFields = remainingFields[1:]
199+
continue
200+
}
201+
190202
if remainingFields, err = validateVisitField(fieldDef, remainingFields); err != nil {
191203
return err
192204
}
@@ -306,19 +318,24 @@ func validateRequiredFieldMap(_ *Message, requiredTags map[int]struct{}, fieldMa
306318
return nil
307319
}
308320

309-
func validateFields(transportDD *datadictionary.DataDictionary, appDD *datadictionary.DataDictionary, msgType string, message *Message) MessageRejectError {
321+
func validateFields(transportDD *datadictionary.DataDictionary,
322+
appDD *datadictionary.DataDictionary,
323+
settings ValidatorSettings,
324+
msgType string,
325+
message *Message,
326+
) MessageRejectError {
310327
for _, field := range message.fields {
311328
switch {
312329
case field.tag.IsHeader():
313-
if err := validateField(transportDD, transportDD.Header.Tags, field); err != nil {
330+
if err := validateField(transportDD, settings, transportDD.Header.Tags, field); err != nil {
314331
return err
315332
}
316333
case field.tag.IsTrailer():
317-
if err := validateField(transportDD, transportDD.Trailer.Tags, field); err != nil {
334+
if err := validateField(transportDD, settings, transportDD.Trailer.Tags, field); err != nil {
318335
return err
319336
}
320337
default:
321-
if err := validateField(appDD, appDD.Messages[msgType].Tags, field); err != nil {
338+
if err := validateField(appDD, settings, appDD.Messages[msgType].Tags, field); err != nil {
322339
return err
323340
}
324341
}
@@ -327,23 +344,46 @@ func validateFields(transportDD *datadictionary.DataDictionary, appDD *datadicti
327344
return nil
328345
}
329346

330-
func validateField(d *datadictionary.DataDictionary, _ datadictionary.TagSet, field TagValue) MessageRejectError {
347+
func getFieldType(d *datadictionary.DataDictionary, field int) (*datadictionary.FieldType, bool) {
348+
fieldType, isMessageField := d.FieldTypeByTag[field]
349+
return fieldType, isMessageField
350+
}
351+
352+
func checkFieldNotDefined(settings ValidatorSettings, field Tag) bool {
353+
fail := false
354+
if int(field) < UserDefinedTagMin {
355+
fail = !settings.AllowUnknownMessageFields
356+
} else {
357+
fail = settings.CheckUserDefinedFields
358+
}
359+
return !fail
360+
}
361+
362+
func validateField(d *datadictionary.DataDictionary,
363+
settings ValidatorSettings,
364+
_ datadictionary.TagSet,
365+
field TagValue,
366+
) MessageRejectError {
331367
if len(field.value) == 0 {
332368
return TagSpecifiedWithoutAValue(field.tag)
333369
}
334370

335-
if _, valid := d.FieldTypeByTag[int(field.tag)]; !valid {
371+
fieldType, isMessageField := getFieldType(d, int(field.tag))
372+
if !isMessageField && !checkFieldNotDefined(settings, field.tag) {
336373
return InvalidTagNumber(field.tag)
337374
}
338375

376+
if !isMessageField {
377+
return nil
378+
}
379+
339380
allowedValues := d.FieldTypeByTag[int(field.tag)].Enums
340381
if len(allowedValues) != 0 {
341382
if _, validValue := allowedValues[string(field.value)]; !validValue {
342383
return ValueIsIncorrect(field.tag)
343384
}
344385
}
345386

346-
fieldType := d.FieldTypeByTag[int(field.tag)]
347387
var prototype FieldValue
348388
switch fieldType.Type {
349389
case "MULTIPLESTRINGVALUE", "MULTIPLEVALUESTRING":

validation_test.go

Lines changed: 170 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -74,6 +74,14 @@ func TestValidate(t *testing.T) {
7474
tcInvalidTagCheckDisabledFixT(),
7575
tcInvalidTagCheckEnabled(),
7676
tcInvalidTagCheckEnabledFixT(),
77+
tcAllowUnknownMessageFieldsEnabled(),
78+
tcAllowUnknownMessageFieldsEnabledFixT(),
79+
tcAllowUnknownMessageFieldsDisabled(),
80+
tcAllowUnknownMessageFieldsDisabledFixT(),
81+
tcCheckUserDefinedFieldsEnabled(),
82+
tcCheckUserDefinedFieldsEnabledFixT(),
83+
tcCheckUserDefinedFieldsDisabled(),
84+
tcCheckUserDefinedFieldsDisabledFixT(),
7785
tcMultipleRepeatingGroupFields(),
7886
}
7987

@@ -786,6 +794,168 @@ func tcInvalidTagCheckEnabledFixT() validateTest {
786794
}
787795
}
788796

797+
func tcAllowUnknownMessageFieldsEnabled() validateTest {
798+
dict, _ := datadictionary.Parse("spec/FIX40.xml")
799+
customValidatorSettings := defaultValidatorSettings
800+
customValidatorSettings.AllowUnknownMessageFields = true
801+
validator := NewValidator(customValidatorSettings, dict, nil)
802+
803+
builder := createFIX40NewOrderSingle()
804+
tag := Tag(41)
805+
builder.Body.SetField(tag, FIXString("hello"))
806+
msgBytes := builder.build()
807+
808+
return validateTest{
809+
TestName: "Allow Unknown Message Fields - Enabled",
810+
Validator: validator,
811+
MessageBytes: msgBytes,
812+
DoNotExpectReject: true,
813+
}
814+
}
815+
816+
func tcAllowUnknownMessageFieldsEnabledFixT() validateTest {
817+
tDict, _ := datadictionary.Parse("spec/FIXT11.xml")
818+
appDict, _ := datadictionary.Parse("spec/FIX50SP2.xml")
819+
customValidatorSettings := defaultValidatorSettings
820+
customValidatorSettings.AllowUnknownMessageFields = true
821+
validator := NewValidator(customValidatorSettings, appDict, tDict)
822+
823+
builder := createFIX50SP2NewOrderSingle()
824+
tag := Tag(41)
825+
builder.Body.SetField(tag, FIXString("hello"))
826+
msgBytes := builder.build()
827+
828+
return validateTest{
829+
TestName: "Allow Unknown Message Fields - Enabled FIXT",
830+
Validator: validator,
831+
MessageBytes: msgBytes,
832+
DoNotExpectReject: true,
833+
}
834+
}
835+
836+
func tcAllowUnknownMessageFieldsDisabled() validateTest {
837+
dict, _ := datadictionary.Parse("spec/FIX40.xml")
838+
customValidatorSettings := defaultValidatorSettings
839+
customValidatorSettings.AllowUnknownMessageFields = false
840+
validator := NewValidator(customValidatorSettings, dict, nil)
841+
842+
builder := createFIX40NewOrderSingle()
843+
tag := Tag(41)
844+
builder.Body.SetField(tag, FIXString("hello"))
845+
msgBytes := builder.build()
846+
847+
return validateTest{
848+
TestName: "Allow Unknown Message Fields - Disabled",
849+
Validator: validator,
850+
MessageBytes: msgBytes,
851+
DoNotExpectReject: false,
852+
ExpectedRejectReason: rejectReasonTagNotDefinedForThisMessageType,
853+
ExpectedRefTagID: &tag,
854+
}
855+
}
856+
857+
func tcAllowUnknownMessageFieldsDisabledFixT() validateTest {
858+
tDict, _ := datadictionary.Parse("spec/FIXT11.xml")
859+
appDict, _ := datadictionary.Parse("spec/FIX50SP2.xml")
860+
customValidatorSettings := defaultValidatorSettings
861+
customValidatorSettings.RejectInvalidMessage = true
862+
validator := NewValidator(customValidatorSettings, appDict, tDict)
863+
864+
builder := createFIX50SP2NewOrderSingle()
865+
tag := Tag(41)
866+
builder.Body.SetField(tag, FIXString("hello"))
867+
msgBytes := builder.build()
868+
869+
return validateTest{
870+
TestName: "Allow Unknown Message Fields - Disabled FIXT",
871+
Validator: validator,
872+
MessageBytes: msgBytes,
873+
DoNotExpectReject: false,
874+
ExpectedRejectReason: rejectReasonTagNotDefinedForThisMessageType,
875+
ExpectedRefTagID: &tag,
876+
}
877+
}
878+
879+
func tcCheckUserDefinedFieldsEnabled() validateTest {
880+
dict, _ := datadictionary.Parse("spec/FIX40.xml")
881+
customValidatorSettings := defaultValidatorSettings
882+
customValidatorSettings.CheckUserDefinedFields = true
883+
validator := NewValidator(customValidatorSettings, dict, nil)
884+
885+
builder := createFIX40NewOrderSingle()
886+
tag := Tag(9999)
887+
builder.Body.SetField(tag, FIXString("hello"))
888+
msgBytes := builder.build()
889+
890+
return validateTest{
891+
TestName: "Check User Defined Fields - Enabled",
892+
Validator: validator,
893+
MessageBytes: msgBytes,
894+
DoNotExpectReject: false,
895+
ExpectedRefTagID: &tag,
896+
}
897+
}
898+
899+
func tcCheckUserDefinedFieldsEnabledFixT() validateTest {
900+
tDict, _ := datadictionary.Parse("spec/FIXT11.xml")
901+
appDict, _ := datadictionary.Parse("spec/FIX50SP2.xml")
902+
customValidatorSettings := defaultValidatorSettings
903+
customValidatorSettings.RejectInvalidMessage = true
904+
validator := NewValidator(customValidatorSettings, appDict, tDict)
905+
906+
builder := createFIX50SP2NewOrderSingle()
907+
tag := Tag(9999)
908+
builder.Body.SetField(tag, FIXString("hello"))
909+
msgBytes := builder.build()
910+
911+
return validateTest{
912+
TestName: "Check User Defined Fields - Enabled FIXT",
913+
Validator: validator,
914+
MessageBytes: msgBytes,
915+
DoNotExpectReject: false,
916+
ExpectedRefTagID: &tag,
917+
}
918+
}
919+
920+
func tcCheckUserDefinedFieldsDisabled() validateTest {
921+
dict, _ := datadictionary.Parse("spec/FIX40.xml")
922+
customValidatorSettings := defaultValidatorSettings
923+
customValidatorSettings.CheckUserDefinedFields = false
924+
validator := NewValidator(customValidatorSettings, dict, nil)
925+
926+
builder := createFIX40NewOrderSingle()
927+
tag := Tag(9999)
928+
builder.Body.SetField(tag, FIXString("hello"))
929+
msgBytes := builder.build()
930+
931+
return validateTest{
932+
TestName: "Check User Defined Fields - Disabled",
933+
Validator: validator,
934+
MessageBytes: msgBytes,
935+
DoNotExpectReject: true,
936+
}
937+
}
938+
939+
func tcCheckUserDefinedFieldsDisabledFixT() validateTest {
940+
tDict, _ := datadictionary.Parse("spec/FIXT11.xml")
941+
appDict, _ := datadictionary.Parse("spec/FIX50SP2.xml")
942+
customValidatorSettings := defaultValidatorSettings
943+
customValidatorSettings.CheckUserDefinedFields = false
944+
validator := NewValidator(customValidatorSettings, appDict, tDict)
945+
946+
builder := createFIX50SP2NewOrderSingle()
947+
tag := Tag(9999)
948+
builder.Body.SetField(tag, FIXString("hello"))
949+
msgBytes := builder.build()
950+
951+
return validateTest{
952+
TestName: "Check User Defined Fields - Disabled FIXT",
953+
Validator: validator,
954+
MessageBytes: msgBytes,
955+
DoNotExpectReject: true,
956+
}
957+
}
958+
789959
func tcTagSpecifiedOutOfRequiredOrderDisabledHeader() validateTest {
790960
dict, _ := datadictionary.Parse("spec/FIX40.xml")
791961
customValidatorSettings := defaultValidatorSettings

0 commit comments

Comments
 (0)