-
-
Notifications
You must be signed in to change notification settings - Fork 1
/
Copy pathentitystore.go
189 lines (164 loc) · 5 KB
/
entitystore.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
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
package polai
import (
"bufio"
"encoding/json"
"fmt"
"io"
"io/ioutil"
"reflect"
"golang.org/x/exp/maps"
)
type rawEntity struct {
Uid string `json:"uid"`
LowerParents []string `json:"parents"`
Attrs map[string]interface{} `json:"attrs"`
EntityId *complexEntityName `json:"EntityId"`
Identifier *complexEntityName `json:"Identifier"`
Parents []complexEntityName `json:"Parents"`
Attributes map[string]complexAttribute `json:"Attributes"`
}
type complexEntityName struct {
EntityID string `json:"EntityId"`
EntityType string `json:"EntityType"`
}
type complexAttribute struct {
String *string `json:"String"`
Long *int64 `json:"Long"`
Boolean *bool `json:"Boolean"`
Record *map[string]interface{} `json:"Record"`
Set *[]interface{} `json:"Set"`
}
type Entity struct {
Identifier string
Parents []string
Attributes []Attribute
}
type Attribute struct {
Name string
StringValue *string
LongValue *int64
BooleanValue *bool
RecordValue *map[string]interface{}
SetValue *[]interface{}
}
// EntityStore represents the complete set of known entities within the system.
type EntityStore struct {
r *bufio.Reader
entities *[]Entity
}
// NewEntityStore returns a new instance of EntityStore.
func NewEntityStore(r io.Reader) *EntityStore {
return &EntityStore{r: bufio.NewReader(r)}
}
// SetEntities overrides all entities.
func (e *EntityStore) SetEntities(r io.Reader) {
e.r = bufio.NewReader(r)
e.entities = nil
}
// GetEntities retrieves all entities.
func (e *EntityStore) GetEntities() ([]Entity, error) {
if e.entities == nil {
b, err := ioutil.ReadAll(e.r)
if err != nil && err != io.EOF {
return nil, err
}
var rawEntities []rawEntity
if err := json.Unmarshal(b, &rawEntities); err != nil {
return nil, fmt.Errorf("error parsing entity store json, %s", err.Error())
}
var entities []Entity
for _, rawEntity := range rawEntities {
if rawEntity.EntityId != nil {
rawEntity.Identifier = rawEntity.EntityId
}
if rawEntity.Uid != "" {
var attributes []Attribute
for attrName, attrVal := range rawEntity.Attrs {
attribute := Attribute{
Name: attrName,
}
switch attrVal.(type) {
case int:
val := int64(attrVal.(int))
attribute.LongValue = &val
case int64:
val := attrVal.(int64)
attribute.LongValue = &val
case float64:
val := int64(attrVal.(float64))
attribute.LongValue = &val
case string:
val := attrVal.(string)
attribute.StringValue = &val
case bool:
val := attrVal.(bool)
attribute.BooleanValue = &val
case map[string]interface{}:
val := attrVal.(map[string]interface{})
attribute.RecordValue = &val
case []interface{}:
val := attrVal.([]interface{})
attribute.SetValue = &val
default:
return nil, fmt.Errorf("unknown type in attribute block: %v (%s)", attrVal, reflect.TypeOf(attrVal).String())
}
attributes = append(attributes, attribute)
}
entities = append(entities, Entity{
Identifier: rawEntity.Uid,
Parents: rawEntity.LowerParents,
Attributes: attributes,
})
} else if rawEntity.Identifier != nil {
b, _ := json.Marshal(rawEntity.Identifier.EntityID)
entity := Entity{
Identifier: fmt.Sprintf("%s::%s", rawEntity.Identifier.EntityType, string(b)),
}
for _, parent := range rawEntity.Parents {
b, _ := json.Marshal(parent.EntityID)
entity.Parents = append(entity.Parents, fmt.Sprintf("%s::%s", parent.EntityType, string(b)))
}
for attrName, attrVal := range rawEntity.Attributes {
// TODO: validate only one field set
entity.Attributes = append(entity.Attributes, Attribute{
Name: attrName,
BooleanValue: attrVal.Boolean,
StringValue: attrVal.String,
LongValue: attrVal.Long,
RecordValue: attrVal.Record,
SetValue: attrVal.Set,
})
}
entities = append(entities, entity)
} else {
return nil, fmt.Errorf("no entity identifier found in entity list item")
}
}
e.entities = &entities
}
return *e.entities, nil
}
// GetEntityDescendents retrieves all entities that match or are descendents of those passed in.
func (e *EntityStore) GetEntityDescendents(parents []string) ([]Entity, error) {
baseEntities, err := e.GetEntities()
if err != nil {
return nil, err
}
foundEntities := map[string]Entity{} // using map[string] for dedup purposes
i := 0
for i < len(parents) {
parent := parents[i]
for _, baseEntity := range baseEntities {
for _, baseEntityParent := range baseEntity.Parents {
if baseEntityParent == parent && !contains(parents, baseEntity.Identifier) {
parents = append(parents, baseEntity.Identifier)
}
}
if baseEntity.Identifier == parent {
foundEntities[baseEntity.Identifier] = baseEntity
}
}
i++
}
return maps.Values(foundEntities), nil
}