Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 10 additions & 1 deletion server/client.go
Original file line number Diff line number Diff line change
Expand Up @@ -600,6 +600,7 @@ type subscription struct {
qw int32
closed int32
mqtt *mqttSub
mappedSub []byte
}

// Indicate that this subscription is closed.
Expand Down Expand Up @@ -2898,6 +2899,12 @@ func (c *client) addShadowSubscriptions(acc *Account, sub *subscription, enact b
hasWC = true
}
}
if (c.kind == CLIENT || c.kind == LEAF) && c.in.flags.isSet(hasMappings) {
mappedSub, _ := c.acc.selectMappedSubject(string(sub.subject))
sub.mappedSub = sub.subject
sub.subject = []byte(mappedSub)
subj =mappedSub
}
// Loop over the import subjects. We have 4 scenarios. If we have an
// exact match or a superset match we should use the from field from
// the import. If we are a subset or overlap, we have to dynamically calculate
Expand Down Expand Up @@ -2991,7 +2998,6 @@ func (c *client) addShadowSub(sub *subscription, ime *ime, enact bool) (*subscri

im := ime.im
nsub.im = im

if !im.usePub && ime.dyn && im.tr != nil {
if im.rtr == nil {
im.rtr = im.tr.reverse()
Expand Down Expand Up @@ -4703,6 +4709,9 @@ func (c *client) processMsgResults(acc *Account, r *SublistResult, msg, deliver,
dsubj = append(_dsubj[:0], sub.im.to...)
}

if(sub.mappedSub != nil){
dsubj = append(_dsubj[:0], sub.mappedSub...)
}
if mt != nil {
mt.addStreamExportEvent(sub.client, dsubj)
// If allow_trace is false...
Expand Down
39 changes: 37 additions & 2 deletions server/subject_transform.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,17 @@ import (
"strings"
)


var customMappingFunction func(token string) string

func RegisterCustomMappingFunction(f func(token string) string) error{
if customMappingFunction == nil {
customMappingFunction = f
return nil
}
return fmt.Errorf("custom mapping function already registered")
}

// Subject mapping and transform setups.
var (
commaSeparatorRegEx = regexp.MustCompile(`,\s*`)
Expand All @@ -33,6 +44,7 @@ var (
splitMappingFunctionRegEx = regexp.MustCompile(`{{\s*[sS]plit\s*\((.*)\)\s*}}`)
leftMappingFunctionRegEx = regexp.MustCompile(`{{\s*[lL]eft\s*\((.*)\)\s*}}`)
rightMappingFunctionRegEx = regexp.MustCompile(`{{\s*[rR]ight\s*\((.*)\)\s*}}`)
customtMappingFunctionRegEx = regexp.MustCompile(`{{\s*[cC]ustom\s*\((.*)\)\s*}}`)
)

// Enum for the subject mapping subjectTransform function types
Expand All @@ -48,6 +60,7 @@ const (
Split
Left
Right
Custom
)

// Transforms for arbitrarily mapping subjects from one to another for maps, tees and filters.
Expand Down Expand Up @@ -118,7 +131,7 @@ func NewSubjectTransformWithStrict(src, dest string, strict bool) (*subjectTrans
}

if strict {
if tranformType != NoTransform && tranformType != Wildcard {
if tranformType != NoTransform && tranformType != Wildcard && tranformType != Custom {
return nil, &mappingDestinationErr{token, ErrMappingDestinationNotSupportedForImport}
}
}
Expand Down Expand Up @@ -313,6 +326,23 @@ func indexPlaceHolders(token string) (int16, []int, int32, string, error) {
return transformIndexIntArgsHelper(token, args, Left)
}

// Custom(token)
args = getMappingFunctionArgs(customtMappingFunctionRegEx, token)
if args != nil {
if len(args) == 1 && args[0] == _EMPTY_ {
return BadTransform, []int{}, -1, _EMPTY_, &mappingDestinationErr{token, ErrMappingDestinationNotEnoughArgs}
}
if len(args) == 1 {
tokenIndex, err := strconv.Atoi(strings.Trim(args[0], " "))
if err != nil {
return BadTransform, []int{}, -1, _EMPTY_, &mappingDestinationErr{token, ErrMappingDestinationInvalidArg}
}
return Custom, []int{tokenIndex}, -1, _EMPTY_, nil
} else {
return BadTransform, []int{}, -1, _EMPTY_, &mappingDestinationErr{token, ErrMappingDestinationTooManyArgs}
}
}

// split(token, deliminator)
args = getMappingFunctionArgs(splitMappingFunctionRegEx, token)
if args != nil {
Expand Down Expand Up @@ -458,6 +488,11 @@ func (tr *subjectTransform) TransformTokenizedSubject(tokens []string) string {
keyForHashing = append(keyForHashing, []byte(tokens[sourceToken])...)
}
b.WriteString(tr.getHashPartition(keyForHashing, int(tr.dtokmfintargs[i])))
case Custom:
if(customMappingFunction == nil) {
b.WriteString(tokens[tr.dtokmftokindexesargs[i][0]])
}
b.WriteString(customMappingFunction(tokens[tr.dtokmftokindexesargs[i][0]]))
case Wildcard: // simple substitution
b.WriteString(tokens[tr.dtokmftokindexesargs[i][0]])
case SplitFromLeft:
Expand Down Expand Up @@ -620,4 +655,4 @@ func subjectInfo(subject string) (bool, []string, int, bool) {
}
}
return true, tokens, npwcs, sfwc
}
}
13 changes: 13 additions & 0 deletions server/subject_transform_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -133,6 +133,13 @@ func TestSubjectTransformHelpers(t *testing.T) {
}

func TestSubjectTransforms(t *testing.T) {

var function = func (value string) string {
return "custom" + value
}

RegisterCustomMappingFunction(function)

shouldErr := func(src, dest string, strict bool) {
t.Helper()
if _, err := NewSubjectTransformWithStrict(src, dest, strict); err != ErrBadSubject && !errors.Is(err, ErrInvalidMappingDestination) {
Expand Down Expand Up @@ -185,6 +192,9 @@ func TestSubjectTransforms(t *testing.T) {
shouldBeOK("*.*", "{{partition(10,1,2)}}", false)
shouldBeOK("foo.*.*", "foo.{{wildcard(1)}}.{{wildcard(2)}}.{{partition(5,1,2)}}", false)

shouldBeOK("foo.*", "bar.{{custom(1)}}", false)
shouldBeOK("foo.*", "bar.{{custom(1)}}", true)

shouldMatch := func(src, dest, sample, expected string) {
t.Helper()
tr := shouldBeOK(src, dest, false)
Expand Down Expand Up @@ -220,4 +230,7 @@ func TestSubjectTransforms(t *testing.T) {
shouldMatch("*", "{{left(1,1)}}", "1234", "1")
shouldMatch("*", "{{left(1,3)}}", "1234", "123")
shouldMatch("*", "{{left(1,6)}}", "1234", "1234")
shouldMatch("foo.*", "bar.{{custom(1)}}", "foo.hello","bar.customhello")
shouldMatch("foo.*.*.bar", "bar.{{custom(2)}}.{{custom(1)}}", "foo.1.2.bar","bar.custom2.custom1")
shouldMatch("user.details.*", "user.details.{{custom(1)}}", "user.details.11","user.details.custom11")
}
3 changes: 2 additions & 1 deletion server/sublist.go
Original file line number Diff line number Diff line change
Expand Up @@ -1292,7 +1292,8 @@ func ValidateMapping(src string, dest string) error {
!splitFromRightMappingFunctionRegEx.MatchString(t) &&
!sliceFromLeftMappingFunctionRegEx.MatchString(t) &&
!sliceFromRightMappingFunctionRegEx.MatchString(t) &&
!splitMappingFunctionRegEx.MatchString(t) {
!splitMappingFunctionRegEx.MatchString(t) &&
!customtMappingFunctionRegEx.MatchString(t) {
return &mappingDestinationErr{t, ErrUnknownMappingDestinationFunction}
} else {
continue
Expand Down