diff --git a/specs/builder.go b/specs/builder.go index 37809f97..d50be2fd 100644 --- a/specs/builder.go +++ b/specs/builder.go @@ -114,31 +114,33 @@ type MessageSpecBuilder interface { type messageSpecBuilder struct{} type specDummy struct { - Name string `json:"name,omitempty" xml:"name,omitempty"` + Name string `json:"name,omitempty" xml:"name,omitempty"` Fields orderedFieldMap `json:"fields,omitempty" xml:"fields,omitempty"` } type fieldDummy struct { - Type string `json:"type,omitempty" xml:"type,omitempty"` - Length int `json:"length,omitempty" xml:"length,omitempty"` - Description string `json:"description,omitempty" xml:"description,omitempty"` - Enc string `json:"enc,omitempty" xml:"enc,omitempty"` - Prefix string `json:"prefix,omitempty" xml:"prefix,omitempty"` - Padding *paddingDummy `json:"padding,omitempty" xml:"padding,omitempty"` - Tag *tagDummy `json:"tag,omitempty" xml:"tag,omitempty"` - Subfields map[string]*fieldDummy `json:"subfields,omitempty" xml:"subfields:omitempty"` + Type string `json:"type,omitempty" xml:"type,omitempty"` + Length int `json:"length,omitempty" xml:"length,omitempty"` + Description string `json:"description,omitempty" xml:"description,omitempty"` + Enc string `json:"enc,omitempty" xml:"enc,omitempty"` + Prefix string `json:"prefix,omitempty" xml:"prefix,omitempty"` + Padding *paddingDummy `json:"padding,omitempty" xml:"padding,omitempty"` + Tag *tagDummy `json:"tag,omitempty" xml:"tag,omitempty"` + Subfields map[string]*fieldDummy `json:"subfields,omitempty" xml:"subfields:omitempty"` + Bitmap *fieldDummy `json:"bitmap,omitempty" xml:"bitmap,omitempty"` + DisableAutoExpand bool `json:"disableAutoExpand,omitempty" xml:"disableAutoExpand,omitempty"` } type paddingDummy struct { Type string `json:"type" xml:"type"` - Pad string `json:"pad" xml:"pad"` + Pad string `json:"pad" xml:"pad"` } type tagDummy struct { - Length int `json:"length,omitempty" xml:"length,omitempty"` - Enc string `json:"enc,omitempty" xml:"enc,omitempty"` + Length int `json:"length,omitempty" xml:"length,omitempty"` + Enc string `json:"enc,omitempty" xml:"enc,omitempty"` Padding *paddingDummy `json:"padding,omitempty" xml:"padding,omitempty"` - Sort string `json:"sort,omitempty" xml:"sort,omitempty"` + Sort string `json:"sort,omitempty" xml:"sort,omitempty"` } func importField(dummyField *fieldDummy, index string) (*field.Spec, error) { @@ -177,17 +179,30 @@ func importField(dummyField *fieldDummy, index string) (*field.Spec, error) { fieldSpec.Subfields[key] = constructor(subfieldSpec) } - fieldSpec.Tag = &field.TagSpec{ - Length: dummyField.Tag.Length, + if dummyField.Tag != nil { + fieldSpec.Tag = &field.TagSpec{ + Length: dummyField.Tag.Length, + } + fieldSpec.Tag.Enc = EncodingsExtToInt[dummyField.Tag.Enc] + if dummyField.Tag.Padding != nil { + if padderConstructor := PaddersExtToInt[dummyField.Tag.Padding.Type]; padderConstructor != nil { + fieldSpec.Tag.Pad = padderConstructor(dummyField.Tag.Padding.Pad) + } + } + fieldSpec.Tag.Sort = SortExtToInt[dummyField.Tag.Sort] } - fieldSpec.Tag.Enc = EncodingsExtToInt[dummyField.Tag.Enc] - if dummyField.Tag.Padding != nil { - if padderConstructor := PaddersExtToInt[dummyField.Tag.Padding.Type]; padderConstructor != nil { - fieldSpec.Tag.Pad = padderConstructor(dummyField.Tag.Padding.Pad) + + if dummyField.Bitmap != nil { + bitmapSpec, err := importField(dummyField.Bitmap, "field bitmap") + if err != nil { + return nil, err } + + fieldSpec.Bitmap = field.NewBitmap(bitmapSpec) } - fieldSpec.Tag.Sort = SortExtToInt[dummyField.Tag.Sort] + } + fieldSpec.DisableAutoExpand = dummyField.DisableAutoExpand return fieldSpec, nil } @@ -218,7 +233,11 @@ func (builder *messageSpecBuilder) ImportJSON(raw []byte) (*iso8583.MessageSpec, } constructor := FieldConstructor[dummyField.Type] if constructor == nil { - return nil, fmt.Errorf("no constructor for filed type: %s for field: %d", dummyField.Type, index) + return nil, fmt.Errorf( + "no constructor for filed type: %s for field: %d", + dummyField.Type, + index, + ) } spec.Fields[index] = constructor(fieldSpec) } @@ -278,7 +297,16 @@ func exportField(internalField field.Field) (*fieldDummy, error) { } dummyField.Tag = tag } + + if spec.Bitmap != nil { + bitmap, err := exportField(spec.Bitmap) + if err != nil { + return nil, err + } + dummyField.Bitmap = bitmap + } } + dummyField.DisableAutoExpand = spec.DisableAutoExpand return dummyField, nil } @@ -304,7 +332,6 @@ func exportTag(tag *field.TagSpec) (*tagDummy, error) { dummy.Sort = getFunctionName(tag.Sort) } return dummy, nil - } func exportPad(pad padding.Padder) (*paddingDummy, error) { @@ -317,6 +344,7 @@ func exportPad(pad padding.Padder) (*paddingDummy, error) { } return nil, fmt.Errorf("unknown padding type: %s", paddingType) } + func exportEnc(enc encoding.Encoder) (string, error) { // set encoding encType := reflect.TypeOf(enc).Elem().Name() diff --git a/specs/builder_test.go b/specs/builder_test.go index 0a06d207..8af45145 100644 --- a/specs/builder_test.go +++ b/specs/builder_test.go @@ -15,7 +15,6 @@ import ( ) func TestBuilder(t *testing.T) { - asciiJson, err := Builder.ExportJSON(Spec87ASCII) require.NoError(t, err) @@ -31,7 +30,6 @@ func TestBuilder(t *testing.T) { require.NoError(t, err) require.Exactly(t, Spec87Hex.Name, hexSpec.Name) - } func TestExampleJSONSpec(t *testing.T) { @@ -87,7 +85,8 @@ func TestSpecWithCompositeFields(t *testing.T) { Pref: prefix.EBCDIC.Fixed, Pad: padding.Left('0'), }), - }}), + }, + }), 30: field.NewNumeric(&field.Spec{ Length: 5, Description: "field key that is not next number", @@ -114,3 +113,138 @@ func TestSpecWithCompositeFields(t *testing.T) { require.Exactly(t, testSpec, importedSpec) } + +func TestSpecWithCompositeBitmapedFields(t *testing.T) { + specJSON := []byte(` +{ + "name": "TEST Spec", + "fields": { + "1": { + "type": "Composite", + "length": 255, + "description": "Private use field", + "prefix": "ASCII.LL", + "bitmap": { + "type": "Bitmap", + "length": 8, + "description": "Bitmap", + "enc": "HexToASCII", + "prefix": "Hex.Fixed", + "disableAutoExpand": true + }, + "subfields": { + "1": { + "type": "String", + "length": 2, + "description": "Cardholder certificate Serial Number", + "enc": "ASCII", + "prefix": "ASCII.Fixed" + }, + "2": { + "type": "String", + "length": 2, + "description": "Merchant certificate Serial Number", + "enc": "ASCII", + "prefix": "ASCII.Fixed" + }, + "3": { + "type": "String", + "length": 2, + "description": "Transaction ID", + "enc": "ASCII", + "prefix": "ASCII.Fixed" + }, + "4": { + "type": "String", + "length": 20, + "description": "CAVV", + "enc": "ASCII", + "prefix": "ASCII.Fixed" + }, + "5": { + "type": "String", + "length": 20, + "description": "CAVV", + "enc": "ASCII", + "prefix": "ASCII.Fixed" + }, + "6": { + "type": "String", + "length": 2, + "description": "Cardholder certificate Serial Number", + "enc": "ASCII", + "prefix": "ASCII.Fixed" + }, + "7": { + "type": "String", + "length": 2, + "description": "Merchant certificate Serial Number", + "enc": "ASCII", + "prefix": "ASCII.Fixed" + }, + "8": { + "type": "String", + "length": 2, + "description": "Transaction ID", + "enc": "ASCII", + "prefix": "ASCII.Fixed" + }, + "9": { + "type": "String", + "length": 20, + "description": "CAVV", + "enc": "ASCII", + "prefix": "ASCII.Fixed" + }, + "10": { + "type": "String", + "length": 6, + "description": "CVV2", + "enc": "ASCII", + "prefix": "ASCII.Fixed" + } + } + } + } +}`) + + spec, err := Builder.ImportJSON(specJSON) + require.NoError(t, err) + + data := struct { + F1 *field.String + F2 *field.String + F3 *field.String + F4 *field.String + F5 *field.String + F6 *field.String + F7 *field.String + F8 *field.String + F9 *field.String + F10 *field.String + }{ + F10: field.NewStringValue("11 456"), + } + + compositeRestored := field.NewComposite(spec.Fields[1].Spec()) + err = compositeRestored.Marshal(&data) + require.NoError(t, err) + + packed, err := compositeRestored.Pack() + require.NoError(t, err) + require.Equal(t, "22004000000000000011 456", string(packed)) + + exportedJSON, err := Builder.ExportJSON(spec) + require.NoError(t, err) + + spec, err = Builder.ImportJSON(exportedJSON) + require.NoError(t, err) + + compositeRestored = field.NewComposite(spec.Fields[1].Spec()) + err = compositeRestored.Marshal(&data) + require.NoError(t, err) + + packed, err = compositeRestored.Pack() + require.NoError(t, err) + require.Equal(t, "22004000000000000011 456", string(packed)) +}