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