1af76431b6fc22b1f9a9b17ca9408be49a87b27f
[ric-plt/e2mgr.git] / tools / xappmock / sender / jsonSender.go
1 //
2 // Copyright 2019 AT&T Intellectual Property
3 // Copyright 2019 Nokia
4 //
5 // Licensed under the Apache License, Version 2.0 (the "License");
6 // you may not use this file except in compliance with the License.
7 // You may obtain a copy of the License at
8 //
9 //      http://www.apache.org/licenses/LICENSE-2.0
10 //
11 // Unless required by applicable law or agreed to in writing, software
12 // distributed under the License is distributed on an "AS IS" BASIS,
13 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 // See the License for the specific language governing permissions and
15 // limitations under the License.
16 //
17 package sender
18
19 import (
20         "fmt"
21         "github.com/pkg/errors"
22         "log"
23         "reflect"
24         "strconv"
25         "strings"
26         "sync/atomic"
27         "time"
28         "unicode"
29         "xappmock/models"
30         "xappmock/rmr"
31 )
32
33 var counter uint64
34
35 func SendJsonRmrMessage(command models.JsonCommand /*the copy is modified locally*/, xAction *[]byte, r *rmr.Service) error {
36         var payload []byte
37         _, err := fmt.Sscanf(command.PackedPayload, "%x", &payload)
38         if err != nil {
39                 return errors.New(fmt.Sprintf("convert inputPayloadAsStr to payloadAsByte. Error: %v\n", err))
40         }
41         command.PackedPayload = string(payload)
42         command.TransactionId = expandTransactionId(command.TransactionId)
43         if len(command.TransactionId) == 0 {
44                 command.TransactionId = string(*xAction)
45         }
46         command.PayloadHeader = expandPayloadHeader(command.PayloadHeader, &command)
47         log.Printf("#jsonSender.SendJsonRmrMessage - command payload header: %s", command.PayloadHeader)
48         rmrMsgId, err := rmr.MessageIdToUint(command.RmrMessageType)
49         if err != nil {
50                 return errors.New(fmt.Sprintf("invalid rmr message id: %s", command.RmrMessageType))
51         }
52
53         msg := append([]byte(command.PayloadHeader), payload...)
54         messageInfo := models.GetMessageInfoAsJson(int(rmrMsgId), command.Meid, msg, []byte(command.TransactionId))
55         log.Printf("#rmr.Service.SendMessage - %s", messageInfo)
56
57         _, err = r.SendMessage(int(rmrMsgId), command.Meid, msg, []byte(command.TransactionId))
58         return err
59 }
60
61 /*
62  * transactionId (xAction): The value may have a fixed value or $ or <prefix>$.
63  * $ is replaced by a value generated at runtime (possibly unique per message sent).
64  * If the tag does not exist, then the mock shall use the value taken from the incoming message.
65  */
66 func expandTransactionId(id string) string {
67         if len(id) == 1 && id[0] == '$' {
68                 return fmt.Sprintf("%d", incAndGetCounter())
69         }
70         if len(id) > 1 && id[len(id)-1] == '$' {
71                 return fmt.Sprintf("%s%d", id[:len(id)-1], incAndGetCounter())
72         }
73         return id
74 }
75
76 /*
77  * payloadHeader: A prefix to combine with the payload that will be the message’s payload. The value may include variables of the format $<name> or #<name> where:
78  *   $<name> expands to the value of <name> if it exists or the empty string if not.
79  *   #<name> expands to the length of the value of <name> if it exists or omitted if not.
80  * The intention is to allow the Mock to construct the payload header required by the setup messages (ranIp|ranPort|ranName|payload len|<payload>).
81  * Example: “payloadHeader”: “$ranIp|$ranPort|$ranName|#packedPayload|”
82  */
83
84 func expandPayloadHeader(header string, command *models.JsonCommand) string {
85         var name strings.Builder
86         var expandedHeader strings.Builder
87
88         r := strings.NewReader(header)
89         ch, err := r.ReadByte()
90         for {
91                 if err != nil {
92                         break
93                 }
94
95                 switch ch {
96                 case '$':
97                         for {
98                                 ch, err = r.ReadByte() //on error ch == 0
99                                 if unicode.IsDigit(rune(ch)) || unicode.IsLetter(rune(ch)) {
100                                         if name.Len() == 0 {
101                                                 name.WriteByte(byte(unicode.ToUpper(rune(ch))))
102                                         } else {
103                                                 name.WriteByte(ch)
104                                         }
105                                 } else {
106                                         if fieldValue := reflect.Indirect(reflect.ValueOf(command)).FieldByName(name.String()); fieldValue.IsValid() {
107                                                 switch fieldValue.Kind() {
108                                                 case reflect.String:
109                                                         expandedHeader.WriteString(fieldValue.String())
110                                                 case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
111                                                         expandedHeader.WriteString(strconv.FormatInt(fieldValue.Int(), 10))
112                                                 case reflect.Bool:
113                                                         expandedHeader.WriteString(strconv.FormatBool(fieldValue.Bool()))
114                                                 case reflect.Float64, reflect.Float32:
115                                                         expandedHeader.WriteString(fmt.Sprintf("%g", fieldValue.Float()))
116                                                 default:
117                                                         log.Fatalf("#jsonSender.expandPayloadHeader - invalid type for $%s, value must be a string, an int, a bool or a float", name.String())
118                                                 }
119                                         }
120                                         name.Reset()
121                                         break
122                                 }
123                         }
124                 case '#':
125                         for {
126                                 ch, err = r.ReadByte() //on error ch == 0
127                                 if unicode.IsDigit(rune(ch)) || unicode.IsLetter(rune(ch)) {
128                                         if name.Len() == 0 {
129                                                 name.WriteByte(byte(unicode.ToUpper(rune(ch))))
130                                         } else {
131                                                 name.WriteByte(ch)
132                                         }
133                                 } else {
134                                         if fieldValue := reflect.Indirect(reflect.ValueOf(command)).FieldByName(name.String()); fieldValue.IsValid() {
135                                                 if fieldValue.Kind() == reflect.String {
136                                                         expandedHeader.WriteString(strconv.FormatInt(int64(len(fieldValue.String())), 10))
137                                                 } else {
138                                                         log.Fatalf("#jsonSender.expandPayloadHeader - invalid type for #%s, value must be a string", name.String())
139                                                 }
140                                         }
141                                         name.Reset()
142                                         break
143                                 }
144                         }
145                 default:
146                         if unicode.IsPrint(rune(ch)) {
147                                 expandedHeader.WriteByte(ch)
148                         }
149                         ch, err = r.ReadByte()
150                 }
151         }
152         return expandedHeader.String()
153 }
154
155 func incAndGetCounter() uint64 {
156         return atomic.AddUint64(&counter, 1)
157 }
158
159 func init() {
160         counter = uint64(time.Now().Unix() - 1572000000)
161 }