-
Notifications
You must be signed in to change notification settings - Fork 14
/
table.go
215 lines (186 loc) · 7.93 KB
/
table.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
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
package plugin
import (
"log"
"github.com/turbot/go-kit/helpers"
"github.com/turbot/steampipe-plugin-sdk/v4/plugin/transform"
)
type TableCacheOptions struct {
Enabled bool
}
// Table is a struct representing a plugin table.
// It defines the table columns, the function used to list table results (List)
// as well as (optionally) the function used to retrieve a single result by key (Get)
// and additional the functions required to fetch specific columns (HydrateConfig).
type Table struct {
Name string
// table description
Description string
// column definitions
Columns []*Column
// the function used to list table rows
List *ListConfig
// the function used to efficiently retrieve a row by id
Get *GetConfig
// deprecated
// the function used when retrieving data for multiple 'matrix items', e.g. regions
GetMatrixItem MatrixItemFunc
GetMatrixItemFunc MatrixItemMapFunc
// default transform applied to all columns
DefaultTransform *transform.ColumnTransforms
// function controlling default error handling behaviour
DefaultIgnoreConfig *IgnoreConfig
DefaultRetryConfig *RetryConfig
// deprecated - use DefaultIgnoreConfig
DefaultShouldIgnoreError ErrorPredicate
// the parent plugin object
Plugin *Plugin
// Deprecated: used HydrateConfig
HydrateDependencies []HydrateDependencies
// Config for any required hydrate functions, including dependencies between hydrate functions,
// error handling and concurrency behaviour
HydrateConfig []HydrateConfig
// cache options - allows disabling of cache for this table
Cache *TableCacheOptions
// map of hydrate function name to columns it provides
//hydrateColumnMap map[string][]string
hydrateConfigMap map[string]*HydrateConfig
}
func (t *Table) initialise(p *Plugin) {
log.Printf("[TRACE] initialise table %s", t.Name)
// store the plugin pointer
t.Plugin = p
// create DefaultRetryConfig if needed
if t.DefaultRetryConfig == nil {
log.Printf("[TRACE] no DefaultRetryConfig defined - creating empty")
t.DefaultRetryConfig = &RetryConfig{}
}
// create DefaultIgnoreConfig if needed
if t.DefaultIgnoreConfig == nil {
log.Printf("[TRACE] no DefaultIgnoreConfig defined - creating empty")
t.DefaultIgnoreConfig = &IgnoreConfig{}
}
if t.DefaultShouldIgnoreError != nil && t.DefaultIgnoreConfig.ShouldIgnoreError == nil {
// copy the (deprecated) top level ShouldIgnoreError property into the ignore config
t.DefaultIgnoreConfig.ShouldIgnoreError = t.DefaultShouldIgnoreError
log.Printf("[TRACE] legacy DefaultShouldIgnoreError defined - copying into DefaultIgnoreConfig")
}
// apply plugin defaults for retry and ignore config
log.Printf("[TRACE] apply plugin defaults for DefaultRetryConfig")
t.DefaultRetryConfig.DefaultTo(t.Plugin.DefaultRetryConfig)
log.Printf("[TRACE] apply plugin defaults for DefaultIgnoreConfig, table %v plugin %v", t.DefaultIgnoreConfig, t.Plugin.DefaultIgnoreConfig)
t.DefaultIgnoreConfig.DefaultTo(t.Plugin.DefaultIgnoreConfig)
log.Printf("[TRACE] DefaultRetryConfig: %s", t.DefaultRetryConfig.String())
log.Printf("[TRACE] DefaultIgnoreConfig: %s", t.DefaultIgnoreConfig.String())
// get and list configs are similar to hydrate configs but they cannot specify dependencies
// we initialise them first, then initialise the hydrate configs
if t.Get != nil {
log.Printf("[TRACE] t.Get.initialise")
t.Get.initialise(t)
}
if t.List != nil {
log.Printf("[TRACE] t.List.initialise")
t.List.initialise(t)
}
// HydrateConfig contains explicit config for hydrate functions but there may be other hydrate functions
// declared for specific columns which do not have config defined
// build a map of all hydrate functions, with empty config if needed
// NOTE: this map also includes information from the legacy HydrateDependencies property
t.initialiseHydrateConfigs()
log.Printf("[TRACE] back from initialiseHydrateConfigs")
log.Printf("[TRACE] initialise table %s COMPLETE", t.Name)
}
// build map of all hydrate configs, and initialise them
func (t *Table) initialiseHydrateConfigs() {
// first build a map of all hydrate functions
t.buildHydrateConfigMap()
// initialise all hydrate configs in map
for _, h := range t.hydrateConfigMap {
h.initialise(t)
}
}
// build map of all hydrate configs, including those specified in the legacy HydrateDependencies,
// and those mentioned only in column config
func (t *Table) buildHydrateConfigMap() {
t.hydrateConfigMap = make(map[string]*HydrateConfig)
for i := range t.HydrateConfig {
// as we are converting into a pointer, we cannot use the array value direct from the range as
// this was causing incorrect values - go must be reusing memory addresses for successive items
h := &t.HydrateConfig[i]
funcName := helpers.GetFunctionName(h.Func)
t.hydrateConfigMap[funcName] = h
}
// add in hydrate config for all hydrate dependencies declared using legacy property HydrateDependencies
for _, d := range t.HydrateDependencies {
hydrateName := helpers.GetFunctionName(d.Func)
// if there is already a hydrate config, do nothing here
// (this is a validation error that will be picked up by the validation check later)
if _, ok := t.hydrateConfigMap[hydrateName]; !ok {
t.hydrateConfigMap[hydrateName] = &HydrateConfig{Func: d.Func, Depends: d.Depends}
}
}
// NOTE: the get config may be used as a column hydrate function so add this into the map
if get := t.Get; get != nil {
hydrateName := helpers.GetFunctionName(get.Hydrate)
t.hydrateConfigMap[hydrateName] = &HydrateConfig{
Func: get.Hydrate,
ShouldIgnoreError: get.ShouldIgnoreError,
IgnoreConfig: get.IgnoreConfig,
RetryConfig: get.RetryConfig,
MaxConcurrency: get.MaxConcurrency,
}
}
// now add all hydrate functions with no explicit config
for _, c := range t.Columns {
if c.Hydrate == nil {
continue
}
hydrateName := helpers.GetFunctionName(c.Hydrate)
if _, ok := t.hydrateConfigMap[hydrateName]; !ok {
t.hydrateConfigMap[hydrateName] = &HydrateConfig{Func: c.Hydrate}
}
}
}
// build a list of required hydrate function calls which must be executed, based on the columns which have been requested
// NOTE: 'get' and 'list' calls are hydration functions, but they must be omitted from this list as they are called
// first. BEFORE the other hydration functions
// NOTE2: this function also populates the resolvedHydrateName for each column (used to retrieve column values),
// and the hydrateColumnMap (used to determine which columns to return)
func (d *QueryData) populateRequiredHydrateCalls() {
t := d.Table
colsUsed := d.QueryContext.Columns
fetchType := d.FetchType
// what is the name of the fetch call (i.e. the get/list call)
fetchFunc := t.getFetchFunc(fetchType)
fetchCallName := helpers.GetFunctionName(fetchFunc)
// initialise hydrateColumnMap
d.hydrateColumnMap = make(map[string][]string)
requiredCallBuilder := newRequiredHydrateCallBuilder(t, fetchCallName)
// populate a map keyed by function name to ensure we only store each hydrate function once
for _, column := range t.Columns {
// see if this column specifies a hydrate function
var hydrateName string
if hydrateFunc := column.Hydrate; hydrateFunc == nil {
// so there is NO hydrate call registered for the column
// the column is provided by the fetch call
// do not add to map of hydrate functions as the fetch call will always be called
hydrateFunc = fetchFunc
hydrateName = fetchCallName
} else {
// there is a hydrate call registered
hydrateName = helpers.GetFunctionName(hydrateFunc)
// if this column was requested in query, add the hydrate call to required calls
if helpers.StringSliceContains(colsUsed, column.Name) {
requiredCallBuilder.Add(hydrateFunc)
}
}
// now update hydrateColumnMap
d.hydrateColumnMap[hydrateName] = append(d.hydrateColumnMap[hydrateName], column.Name)
}
d.hydrateCalls = requiredCallBuilder.Get()
}
func (t *Table) getFetchFunc(fetchType fetchType) HydrateFunc {
if fetchType == fetchTypeList {
return t.List.Hydrate
}
return t.Get.Hydrate
}