/
util.go
131 lines (120 loc) · 4.29 KB
/
util.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
package common
import (
"bytes"
"encoding/json"
"sort"
"github.com/pkg/errors"
"github.com/project-flogo/core/activity"
"github.com/project-flogo/core/data/schema"
"github.com/project-flogo/core/support/log"
jschema "github.com/xeipuuv/gojsonschema"
)
// Create a new logger
var logger = log.ChildLogger(log.RootLogger(), "client-common")
// GetActivityInputSchema returns schema of an activity input attribute
func GetActivityInputSchema(ctx activity.Context, name string) (string, error) {
if sIO, ok := ctx.(schema.HasSchemaIO); ok {
s := sIO.GetInputSchema(name)
if s != nil {
logger.Debugf("schema for attribute '%s': %T, %s\n", name, s, s.Value())
return s.Value(), nil
}
}
return "", errors.Errorf("schema not found for attribute %s", name)
}
// ParameterIndex stores transaction parameters and its location in raw JSON schema string
// start and end location is used to sort the parameter list to match the parameter order in schema
type ParameterIndex struct {
Name string
JSONType string
start int
end int
}
// OrderedParameters returns parameters of a JSON schema object sorted by their position in schema definition
// This is necessary because Golang JSON parser does not maintain the sequence of object parameters.
func OrderedParameters(schemaData []byte) ([]ParameterIndex, error) {
if schemaData == nil || len(schemaData) == 0 {
logger.Debug("schema data is empty")
return nil, nil
}
// extract root object properties from JSON schema
var rawProperties struct {
Data json.RawMessage `json:"properties"`
}
if err := json.Unmarshal(schemaData, &rawProperties); err != nil {
logger.Errorf("failed to extract properties from metadata: %+v", err)
return nil, err
}
// extract parameter names from raw object properties
var params map[string]json.RawMessage
if err := json.Unmarshal(rawProperties.Data, ¶ms); err != nil {
logger.Errorf("failed to extract parameters from object schema: %+v", err)
return nil, err
}
// collect parameter locations in the raw object schema
var paramIndex []ParameterIndex
for p, v := range params {
// encode parameter name with quotes
key, _ := json.Marshal(p)
// key may exist in raw schema multiple times,
// so check each occurence to determine its correct location in the raw schema
items := bytes.Split(rawProperties.Data, key)
pos := 0
for _, seg := range items {
if pos == 0 {
// first segment should not be the key definition
pos += len(seg)
continue
}
vpos := bytes.Index(seg, v)
if vpos >= 0 {
// the segment contains the key definition, so collect its position in raw schema
endPos := pos + len(key) + vpos + len(v)
// extract JSON type of the parameter
var paramDef struct {
RawType string `json:"type"`
}
if err := json.Unmarshal(v, ¶mDef); err != nil {
logger.Errorf("failed to extract JSON type of parameter %s: %+v", p, err)
}
paramType := jschema.TYPE_OBJECT
if paramDef.RawType != "" {
paramType = paramDef.RawType
}
logger.Debugf("add index parameter '%s' type '%s'\n", p, paramType)
paramIndex = addIndex(paramIndex, ParameterIndex{Name: p, JSONType: paramType, start: pos, end: endPos})
}
pos += len(key) + len(seg)
}
}
// sort parameter index by start location in raw schema
if len(paramIndex) > 1 {
sort.Slice(paramIndex, func(i, j int) bool {
return paramIndex[i].start < paramIndex[j].start
})
}
return paramIndex, nil
}
// addIndex adds a new parameter position to the index, ignore or merge index if index region overlaps.
func addIndex(parameters []ParameterIndex, param ParameterIndex) []ParameterIndex {
for i, v := range parameters {
if param.start > v.start && param.start < v.end {
// ignore if new param's start postion falls in region covered by a known parameter
return parameters
} else if v.start > param.start && v.start < param.end {
// replace old parameter region if its start position falls in the region covered by the new parameter
updated := append(parameters[:i], param)
if len(parameters) > i+1 {
// check the remaining knonw parameters
for _, p := range parameters[i+1:] {
if !(p.start > param.start && p.start < param.end) {
updated = append(updated, p)
}
}
}
return updated
}
}
// append new parameter
return append(parameters, param)
}