Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: Add support for csv parsing. #440

Merged
merged 4 commits into from
Jan 2, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion helpers/component_info.json
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
{
"name": "meshkit",
"type": "library",
"next_error_code": 11106
"next_error_code": 11107
}
2 changes: 1 addition & 1 deletion models/meshmodel/core/v1alpha1/component.go
Original file line number Diff line number Diff line change
Expand Up @@ -177,7 +177,7 @@ func GetMeshModelComponents(db *database.Handler, f ComponentFilter) (c []Compon
type ComponentFilter struct {
Name string
APIVersion string
Greedy bool //when set to true - instead of an exact match, name will be prefix matched
Greedy bool //when set to true - instead of an exact match, name will be matched as a substring
Trim bool //when set to true - the schema is not returned
DisplayName string
ModelName string
Expand Down
100 changes: 100 additions & 0 deletions utils/csv/csv.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,100 @@
package csv

import (
"context"
"encoding/csv"
"io"
"os"
"strings"

"github.com/layer5io/meshkit/utils"
)

type CSV[E any] struct {
Context context.Context
cancel context.CancelFunc
reader *csv.Reader
filePath string
lineForColNo int
// Stores the mapping for coumn name to golang equivalent attribute name.
// It is optional and default mapping is the lower case representation with spaces replaced with "_"
// eg: ColumnnName: Descritption, equivalent golang attribute to which it will be mapped during unmarshal "description".
columnToNameMapping map[string]string
predicateFunc func(columns []string, currentRow []string) bool
}

func NewCSVParser[E any](filePath string, lineForColNo int, colToNameMapping map[string]string, predicateFunc func(columns []string, currentRow []string) bool) (*CSV[E], error) {
reader, err := os.Open(filePath)
if err != nil {
return nil, utils.ErrReadFile(err, filePath)
}

ctx, cancel := context.WithCancel(context.Background())
return &CSV[E]{
Context: ctx,
cancel: cancel,
reader: csv.NewReader(reader),
filePath: filePath,
columnToNameMapping: colToNameMapping,
lineForColNo: lineForColNo,
predicateFunc: predicateFunc,
}, nil
}

// "lineForColNo" line number where the columns are defined in the csv
func (c *CSV[E]) ExtractCols(lineForColNo int) ([]string, error) {
data := []string{}
var err error
for i := 0; i <= lineForColNo; i++ {
data, err = c.reader.Read()
if err != nil {
return nil, utils.ErrReadFile(err, c.filePath)
}
}
return data, nil
}

func (c *CSV[E]) Parse(ch chan E) error {
defer func() {
c.cancel()
}()

columnNames, err := c.ExtractCols(c.lineForColNo)
size := len(columnNames)
if err != nil {
return utils.ErrReadFile(err, c.filePath)
}
for {
data := make(map[string]interface{})
values, err := c.reader.Read()
if err == io.EOF {
break
}

if err != nil {
return err
}

if c.predicateFunc != nil && c.predicateFunc(columnNames, values) {
for index, value := range values {
if index < size {
if c.columnToNameMapping != nil {
attribute, ok := c.columnToNameMapping[columnNames[index]]
if !ok {
attribute = strings.ReplaceAll(strings.ToLower(columnNames[index]), " ", "_")
}
data[attribute] = value
}
}
}

parsedData, err := utils.MarshalAndUnmarshal[map[string]interface{}, E](data)
if err != nil {
return utils.ErrReadFile(err, c.filePath)
}
ch <- parsedData
}

}
return nil
}
5 changes: 5 additions & 0 deletions utils/error.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ var (
ErrRemoteFileNotFoundCode = "11052"
ErrReadingRemoteFileCode = "11053"
ErrReadingLocalFileCode = "11054"
ErrReadFileCode = "11106"
ErrGettingLatestReleaseTagCode = "11055"
ErrInvalidProtocol = errors.New(ErrInvalidProtocolCode, errors.Alert, []string{"invalid protocol: only http, https and file are valid protocols"}, []string{}, []string{"Network protocol is incorrect"}, []string{"Make sure to specify the right network protocol"})
ErrMissingFieldCode = "11076"
Expand Down Expand Up @@ -100,6 +101,10 @@ func ErrReadingLocalFile(err error) error {
return errors.New(ErrReadingLocalFileCode, errors.Alert, []string{"error reading local file"}, []string{err.Error()}, []string{"File does not exist in the location (~/.kube/config)", "File is absent. Filename is not 'config'.", "Insufficient permissions to read file"}, []string{"Verify that the available kubeconfig is accessible by Meshery Server - verify sufficient file permissions (only needs read permission)."})
}

func ErrReadFile(err error, filepath string) error {
return errors.New(ErrReadFileCode, errors.Alert, []string{"error reading file"}, []string{err.Error()}, []string{fmt.Sprintf("File does not exist in the location %s", filepath), "Insufficient permissions"}, []string{"Verify that file exist at the provided location", "Verify sufficient file permissions."})
}

func ErrGettingLatestReleaseTag(err error) error {
return errors.New(
ErrGettingLatestReleaseTagCode,
Expand Down
12 changes: 6 additions & 6 deletions utils/kubernetes/crd.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,9 +17,9 @@ type CRDItem struct {
}

type Spec struct {
Names names `json:"names"`
Group string `json:"group"`
Versions []struct{
Names names `json:"names"`
Group string `json:"group"`
Versions []struct {
Name string `json:"name"`
} `json:"versions"`
}
Expand Down Expand Up @@ -47,8 +47,8 @@ func GetAllCustomResourcesInCluster(ctx context.Context, client rest.Interface)

func GetGVRForCustomResources(crd *CRDItem) *schema.GroupVersionResource {
return &schema.GroupVersionResource{
Group: crd.Spec.Group,
Version: crd.Spec.Versions[0].Name,
Group: crd.Spec.Group,
Version: crd.Spec.Versions[0].Name,
Resource: crd.Spec.Names.ResourceName,
}
}
}
4 changes: 2 additions & 2 deletions utils/utils.go
Original file line number Diff line number Diff line change
Expand Up @@ -270,9 +270,9 @@ func MarshalAndUnmarshal[fromType any, toType any](val fromType) (unmarshalledva

func IsClosed[K any](ch chan K) bool {
select {
case <- ch:
case <-ch:
return true
default:
return false
}
}
}
Loading