[ADDED] Ability to drop partial wildcard tokens in some subject transforms (#4152)

- [X] Tests added
- [X] Branch rebased on top of current main (`git pull --rebase origin
main`)
- [X] Changes squashed to a single commit (described
[here](http://gitready.com/advanced/2009/02/10/squashing-commits-with-rebase.html))
 - [X] Build is green in Travis CI
- [X] You have certified that the contribution is your original work and
that you license the work to the project under the [Apache 2
license](https://github.com/nats-io/nats-server/blob/main/LICENSE)

### Changes proposed in this pull request:

There is currently a blanket requirement that subject transforms
destinations MUST use ALL of the partial wildcards defined in the source
of the transform. This is because the subject transformed defined for
imports must be 'reversible' and therefore the destination transform
must use all of the partial wildcard tokens defined in the source of the
transform.

This reversing of a transform is only used for transforms used by
imports, where in any case it doesn't make sense to use any transform
other than Wildcard.

This PR:
- relaxes this requirement to only apply when the transform is used by
an import, adding the ability to drop a wildcard token in transforms
other than as part of an import.
- Improves transform reverse to support both legacy style wildcards $X
and the new transform function {{Wildcard(X)}}- Improves reversible
transform checking to only allow the use of wildcards in the
destination.

---------

Signed-off-by: Jean-Noël Moyne <jnmoyne@gmail.com>
This commit is contained in:
Jean-Noël Moyne
2023-05-14 14:02:19 -07:00
committed by GitHub
parent 3ff9aed192
commit e07ccf9cc1
5 changed files with 123 additions and 73 deletions

View File

@@ -415,7 +415,7 @@ func TestAccountSubjectMapping(t *testing.T) {
}
}
// test token and partition subject mapping within an account
// test token subject mapping within an account
// Alice imports from Bob with subject mapping
func TestAccountImportSubjectMapping(t *testing.T) {
conf := createConfFile(t, []byte(`
@@ -423,7 +423,7 @@ func TestAccountImportSubjectMapping(t *testing.T) {
accounts {
A {
users: [{user: a, pass: x}]
imports [ {stream: {account: B, subject: "foo.*.*"}, to : "foo.$1.{{wildcard(2)}}.{{partition(10,1,2)}}"}]
imports [ {stream: {account: B, subject: "foo.*.*"}, to : "foo.$1.{{wildcard(2)}}"}]
}
B {
users: [{user: b, pass x}]
@@ -442,30 +442,26 @@ func TestAccountImportSubjectMapping(t *testing.T) {
subjectsReceived := make(chan string)
msg := []byte("HELLO")
sub1, err := ncA.Subscribe("foo.*.*.*", func(m *nats.Msg) {
sub1, err := ncA.Subscribe("foo.*.*", func(m *nats.Msg) {
subjectsReceived <- m.Subject
})
if err != nil {
t.Fatalf("Unexpected error: %v", err)
}
sub1.AutoUnsubscribe(numMessages * 2)
sub1.AutoUnsubscribe(numMessages)
ncB := clientConnectToServerWithUP(t, opts, "b", "x")
defer ncB.Close()
// publish numMessages with an increasing id (should map to partition numbers with the range of 10 partitions) - twice
for j := 0; j < 2; j++ {
for i := 0; i < numMessages; i++ {
err = ncB.Publish(fmt.Sprintf("foo.%d.%d", i, numMessages-i), msg)
if err != nil {
t.Fatalf("Unexpected error: %v", err)
}
// publish numMessages with an increasing id
for i := 0; i < numMessages; i++ {
err = ncB.Publish(fmt.Sprintf("foo.%d.%d", i, numMessages-i), msg)
if err != nil {
t.Fatalf("Unexpected error: %v", err)
}
}
// verify all the partition numbers are in the expected range
partitionsReceived := make([]int, numMessages)
for i := 0; i < numMessages; i++ {
subject := <-subjectsReceived
sTokens := strings.Split(subject, ".")
@@ -474,25 +470,9 @@ func TestAccountImportSubjectMapping(t *testing.T) {
}
t1, _ := strconv.Atoi(sTokens[1])
t2, _ := strconv.Atoi(sTokens[2])
partitionsReceived[i], err = strconv.Atoi(sTokens[3])
if err != nil {
t.Fatalf("Unexpected error: %v", err)
}
if partitionsReceived[i] > 9 || partitionsReceived[i] < 0 || t1 != i || t2 != numMessages-i {
t.Fatalf("Error received unexpected %d.%d to partition %d", t1, t2, partitionsReceived[i])
}
}
// verify hashing is deterministic by checking it produces the same exact result twice
for i := 0; i < numMessages; i++ {
subject := <-subjectsReceived
partitionNumber, err := strconv.Atoi(strings.Split(subject, ".")[3])
if err != nil {
t.Fatalf("Unexpected error: %v", err)
}
if partitionsReceived[i] != partitionNumber {
t.Fatalf("Error: same id mapped to two different partitions")
if t1 != i || t2 != numMessages-i {
t.Fatalf("Error received unexpected %d.%d", t1, t2)
}
}
}