• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

Smirl / steampipe-plugin-cortex / 14803140287

02 May 2025 08:48PM UTC coverage: 35.556%. First build
14803140287

Pull #26

github

web-flow
Merge 734a34b62 into f9cb16a0b
Pull Request #26: Scorecard score tests with coverage reporting

20 of 36 new or added lines in 2 files covered. (55.56%)

160 of 450 relevant lines covered (35.56%)

0.4 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

39.09
/cortex/table_entity.go
1
package cortex
2

3
import (
4
        "context"
5
        "fmt"
6
        "strconv"
7

8
        "github.com/imroc/req/v3"
9
        "github.com/turbot/steampipe-plugin-sdk/v5/grpc/proto"
10
        "github.com/turbot/steampipe-plugin-sdk/v5/plugin"
11
        "github.com/turbot/steampipe-plugin-sdk/v5/plugin/transform"
12
)
13

14
type ScalarOrMap struct {
15
        Scalar interface{}
16
        Map    map[string]interface{}
17
}
18

19
func (s *ScalarOrMap) UnmarshalYAML(unmarshal func(interface{}) error) error {
×
20
        if err := unmarshal(&s.Map); err == nil {
×
21
                return nil
×
22
        }
×
23
        if err := unmarshal(&s.Scalar); err != nil {
×
24
                return err
×
25
        }
×
26
        return nil
×
27
}
28

29
func (s *ScalarOrMap) Value() interface{} {
×
30
        if s.Scalar != nil {
×
31
                return s.Scalar
×
32
        }
×
33
        return s.Map
×
34
}
35

36
type CortexEntityResponse struct {
37
        Entities   []CortexEntityElement `yaml:"entities"`
38
        Page       int                   `yaml:"page"`
39
        TotalPages int                   `yaml:"totalPages"`
40
        Total      int                   `yaml:"total"`
41
}
42

43
type CortexEntityElement struct {
44
        Name        string                        `yaml:"name"`
45
        Tag         string                        `yaml:"tag"`
46
        Description string                        `yaml:"description"`
47
        Type        string                        `yaml:"type"`
48
        Hierarchy   CortexEntityElementHierarchy  `yaml:"hierarchy"`
49
        Groups      []string                      `yaml:"groups"`
50
        Metadata    []CortexEntityElementMetadata `yaml:"metadata"`
51
        LastUpdated string                        `yaml:"lastUpdated"`
52
        Links       []CortexLink                  `yaml:"links"`
53
        Archived    bool                          `yaml:"isArchived"`
54
        Git         CortexGithub                  `yaml:"git"`
55
        Slack       []CortexSlackChannel          `yaml:"slackChannels"`
56
        Owners      CortexEntityOwners            `yaml:"owners"`
57
}
58

59
type CortexEntityElementHierarchy struct {
60
        Parents []CortexTag `yaml:"parents"`
61
}
62

63
type CortexEntityElementMetadata struct {
64
        Key   string      `yaml:"key"`
65
        Value ScalarOrMap `yaml:"value"`
66
}
67

68
type CortexEntityOwners struct {
69
        Teams       []CortexEntityOwnersTeam       `yaml:"teams"`
70
        Individuals []CortexEntityOwnersIndividual `yaml:"individuals"`
71
}
72

73
type CortexEntityOwnersTeam struct {
74
        Tag string `yaml:"tag"`
75
}
76

77
type CortexEntityOwnersIndividual struct {
78
        Email string `yaml:"email"`
79
}
80

81
func tableCortexEntity() *plugin.Table {
×
82
        return &plugin.Table{
×
83
                Name:        "cortex_entity",
×
84
                Description: "Cortex list entities api.",
×
85
                List: &plugin.ListConfig{
×
86
                        Hydrate: listEntitiesHydrator,
×
87
                        KeyColumns: []*plugin.KeyColumn{
×
88
                                {Name: "archived", Require: plugin.Optional},
×
89
                                {Name: "type", Require: plugin.Optional},
×
90
                        },
×
91
                },
×
92
                Columns: []*plugin.Column{
×
93
                        {Name: "name", Type: proto.ColumnType_STRING, Description: "Pretty name of the entity."},
×
94
                        {Name: "tag", Type: proto.ColumnType_STRING, Description: "The x-cortex-tag of the entity."},
×
95
                        {Name: "description", Type: proto.ColumnType_STRING, Description: "Description."},
×
96
                        {Name: "type", Type: proto.ColumnType_STRING, Description: "Entity Type."},
×
97
                        {Name: "parents", Type: proto.ColumnType_JSON, Description: "Parents of the entity.", Transform: FromStructSlice[CortexTag]("Hierarchy.Parents", "Tag")},
×
98
                        {Name: "groups", Type: proto.ColumnType_JSON, Description: "Groups, kind of like tags."},
×
99
                        {Name: "metadata", Type: proto.ColumnType_JSON, Description: "Raw custom metadata", Transform: transform.FromField("Metadata").Transform(TagArrayToMap)},
×
100
                        {Name: "last_updated", Type: proto.ColumnType_TIMESTAMP, Description: "Last updated time."},
×
101
                        {Name: "links", Type: proto.ColumnType_JSON, Description: "List of links", Transform: FromStructSlice[CortexLink]("Links", "Url")},
×
102
                        {Name: "archived", Type: proto.ColumnType_BOOL, Description: "Is archived."},
×
103
                        {Name: "repository", Type: proto.ColumnType_STRING, Description: "Git repo full name", Transform: transform.FromField("Git.Repository")},
×
104
                        {Name: "slack_channels", Type: proto.ColumnType_JSON, Description: "List of string slack channels"},
×
105
                        {Name: "owner_teams", Type: proto.ColumnType_JSON, Description: "List of owning team tags", Transform: FromStructSlice[CortexEntityOwnersTeam]("Owners.Teams", "Tag")},
×
106
                        {Name: "owner_individuals", Type: proto.ColumnType_JSON, Description: "List of owning individuals emails", Transform: FromStructSlice[CortexEntityOwnersIndividual]("Owners.Individuals", "Email")},
×
107
                },
×
108
        }
×
109
}
×
110

111
func listEntitiesHydrator(ctx context.Context, d *plugin.QueryData, h *plugin.HydrateData) (interface{}, error) {
×
112
        logger := plugin.Logger(ctx)
×
113
        config := GetConfig(d.Connection)
×
114
        client := CortexHTTPClient(ctx, config)
×
115
        hydratorWriter := QueryDataWriter{d}
×
116

×
117
        // Extract parameters from QueryData
×
118
        archived := "false"
×
119
        if d.EqualsQuals["archived"] != nil && d.EqualsQuals["archived"].GetBoolValue() {
×
120
                logger.Debug("listEntitiesHydrator", "archived", d.EqualsQuals["archived"])
×
121
                archived = "true"
×
122
        }
×
123
        types := ""
×
124
        if d.EqualsQuals["type"] != nil {
×
125
                // When doing a "where in ()" steampipe does multiple separate calls to listEntities
×
126
                types = d.EqualsQuals["type"].GetStringValue()
×
127
        }
×
128

129
        logger.Info("listEntitiesHydrator", "archived", archived, "types", types)
×
130
        return nil, listEntities(ctx, client, &hydratorWriter, archived, types)
×
131
}
132

133
func listEntities(ctx context.Context, client *req.Client, writer HydratorWriter, archived string, types string) error {
1✔
134
        logger := plugin.Logger(ctx)
1✔
135

1✔
136
        var response CortexEntityResponse
1✔
137
        var page int = 0
1✔
138
        for {
2✔
139
                logger.Debug("listEntities", "page", page)
1✔
140
                resp := client.
1✔
141
                        Get("/api/v1/catalog").
1✔
142
                        // Filters
1✔
143
                        SetQueryParam("includeArchived", archived).
1✔
144
                        SetQueryParam("types", types).
1✔
145
                        // Options
1✔
146
                        SetQueryParam("yaml", "false").
1✔
147
                        SetQueryParam("includeMetadata", "true").
1✔
148
                        SetQueryParam("includeLinks", "true").
1✔
149
                        SetQueryParam("includeSlackChannels", "true").
1✔
150
                        SetQueryParam("includeOwners", "true").
1✔
151
                        SetQueryParam("includeHierarchyFields", "true").
1✔
152
                        // Pagination
1✔
153
                        SetQueryParam("pageSize", "1000").
1✔
154
                        SetQueryParam("page", strconv.Itoa(page)).
1✔
155
                        Do(ctx)
1✔
156

1✔
157
                // Check for HTTP errors
1✔
158
                if resp.IsErrorState() {
2✔
159
                        logger.Error("listEntities", "Status", resp.Status, "Body", resp.String())
1✔
160
                        return fmt.Errorf("error from cortex API %s: %s", resp.Status, resp.String())
1✔
161
                }
1✔
162

163
                // Unmarshal the response and check for unmarshal errors
164
                err := resp.Into(&response)
1✔
165
                if err != nil {
1✔
NEW
166
                        logger.Error("listEntities", "page", page, "Error", err)
×
167
                        return err
×
168
                }
×
169

170
                logger.Debug("listEntities", "totalPages", response.TotalPages, "total", response.Total)
1✔
171

1✔
172
                for _, result := range response.Entities {
2✔
173
                        // send the item to steampipe
1✔
174
                        writer.StreamListItem(ctx, result)
1✔
175
                        // Context can be cancelled due to manual cancellation or the limit has been hit
1✔
176
                        if writer.RowsRemaining(ctx) == 0 {
1✔
177
                                logger.Debug("listEntities", "RowsRemaining", writer.RowsRemaining(ctx))
×
178
                                return nil
×
179
                        }
×
180
                }
181
                page++
1✔
182
                if page >= response.TotalPages {
2✔
183
                        logger.Debug("listEntities", "page", page, "totalPages", response.TotalPages)
1✔
184
                        break
1✔
185
                }
186
        }
187
        return nil
1✔
188
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc