Skip to content

Commit

Permalink
Merge pull request #440 from MUzairS15/MUzairS15/feat/csv-parser
Browse files Browse the repository at this point in the history
feat: Add support for `csv` parsing.
  • Loading branch information
MUzairS15 authored Jan 2, 2024
2 parents f4602da + b00a9dc commit 6ca7bbe
Show file tree
Hide file tree
Showing 6 changed files with 115 additions and 10 deletions.
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
}
}
}

0 comments on commit 6ca7bbe

Please sign in to comment.