dictyBase/modware-user

View on GitHub
commands/load.go

Summary

Maintainability
B
5 hrs
Test Coverage
package commands

import (
    "context"
    "encoding/csv"
    "fmt"
    "io"
    "os"
    "path/filepath"

    "google.golang.org/grpc/codes"
    "google.golang.org/grpc/status"

    "github.com/dictyBase/apihelpers/aphfile"
    "github.com/dictyBase/go-genproto/dictybaseapis/api/jsonapi"
    pb "github.com/dictyBase/go-genproto/dictybaseapis/user"
    "github.com/urfave/cli"
    "google.golang.org/grpc"
)

type UserStatus struct {
    Exist bool
    User  *pb.User
}

func LoadUser(c *cli.Context) error {
    conn, err := grpc.Dial(
        fmt.Sprintf("%s:%s", c.String("user-grpc-host"), c.String("user-grpc-port")),
        grpc.WithInsecure(),
    )
    if err != nil {
        return cli.NewExitError(
            fmt.Sprintf("cannot connect to grpc server for user microservice %s", err),
            2,
        )
    }
    defer conn.Close()
    client := pb.NewUserServiceClient(conn)
    log := getLogger(c)
    s3Client, err := aphfile.GetS3Client(
        fmt.Sprintf("%s:%s", c.String("s3-server"), c.String("s3-server-port")),
        c.String("access-key"),
        c.String("secret-key"),
    )
    if err != nil {
        return cli.NewExitError(
            fmt.Sprintf("error in getting s3 client %s", err),
            2,
        )
    }
    tmpDir, err := aphfile.FetchAndDecompress(
        s3Client,
        c.String("s3-bucket"),
        c.String("remote-path"),
        "users",
    )
    if err != nil {
        return cli.NewExitError(
            fmt.Sprintf("error in fetching remote file %s", err),
            2,
        )
    }
    log.Debugf("retrieved the remote file in dir %s", tmpDir)
    // open the csv file for reading
    usersFile := filepath.Join(tmpDir, c.String("data-file"))
    handler, err := os.Open(usersFile)
    if err != nil {
        return cli.NewExitError(
            fmt.Sprintf("Unable to open file %s %s", usersFile, err),
            2,
        )
    }
    defer handler.Close()
    r := csv.NewReader(handler)
    _, err = r.Read()
    if err == io.EOF {
        return nil
    }
    if err != nil {
        return cli.NewExitError(
            fmt.Sprintf("Unable to read header from csv file %s", err),
            2,
        )
    }
    // variable for records
    total := 0
    inserted := 0
    updated := 0
    // read the file and insert record as needed
    for {
        record, err := r.Read()
        if err == io.EOF {
            break
        }
        if err != nil {
            return cli.NewExitError(
                fmt.Sprintf("Unable to read from csv file %s", err),
                2,
            )
        }
        ustatus, err := findOrCreateUser(client, record)
        if err != nil {
            return cli.NewExitError(
                fmt.Sprintf("error in finding or creating user %s %s", record[0], err),
                2,
            )
        }
        if ustatus.Exist {
            err := updateUser(client, ustatus)
            if err != nil {
                return cli.NewExitError(
                    fmt.Sprintf("error in updating user %s %s", record[0], err),
                    2,
                )
            }
            updated++
            total++
            log.Debugf("updated record with email %s\n", record[0])
            continue
        } else {
            log.Debugf("created record with email %s\n", record[0])
        }
        inserted++
        total++
    }
    log.Infof("records total:%d new:%d updated%d", total, inserted, updated)
    g, err := client.GetUser(context.Background(), &jsonapi.GetRequest{Id: 1})
    if err != nil {
        log.Errorf("Error in getting user by ID %d: %s", 1, err)
        return err
    }
    log.Infof("user email: %s", g.Data.Attributes.Email)
    log.Infof("user first name: %s", g.Data.Attributes.FirstName)
    log.Infof("user last name: %s", g.Data.Attributes.LastName)
    return nil
}

func updateUser(client pb.UserServiceClient, ustatus *UserStatus) error {
    _, err := client.UpdateUser(
        context.Background(),
        newUpdateUserReq(ustatus.User),
    )
    return err
}

func findOrCreateUser(client pb.UserServiceClient, record []string) (*UserStatus, error) {
    u, err := client.GetUserByEmail(context.Background(), &jsonapi.GetEmailRequest{Email: record[0]})
    if err != nil {
        if st, ok := status.FromError(err); ok {
            if st.Code() == codes.NotFound { // create user
                nuser, err := client.CreateUser(context.Background(), newUser(record))
                if err != nil {
                    return &UserStatus{
                        Exist: false,
                    }, err
                }
                return &UserStatus{
                    Exist: false,
                    User:  nuser,
                }, nil
            }
        }
        return &UserStatus{
            Exist: false,
        }, err
    }
    return &UserStatus{
        Exist: true,
        User:  u,
    }, nil
}

func newUpdateUserReq(existingUser *pb.User) *pb.UpdateUserRequest {
    attr := existingUser.Data.Attributes
    return &pb.UpdateUserRequest{
        Id: existingUser.Data.Id,
        Data: &pb.UpdateUserRequest_Data{
            Id:   existingUser.Data.Id,
            Type: existingUser.Data.Type,
            Attributes: &pb.UserAttributes{
                FirstName:    attr.FirstName,
                LastName:     attr.LastName,
                Organization: attr.Organization,
                GroupName:    attr.GroupName,
                FirstAddress: attr.FirstAddress,
                City:         attr.City,
                State:        attr.State,
                Zipcode:      attr.Zipcode,
                Country:      attr.Country,
                Phone:        attr.Phone,
                IsActive:     attr.IsActive,
            },
        },
    }
}

func newUser(record []string) *pb.CreateUserRequest {
    attr := &pb.UserAttributes{}
    for i, v := range record {
        switch i {
        case 6:
            if len(v) > 0 {
                attr.Organization = v
            }
        case 7:
            if len(v) > 0 {
                attr.FirstAddress = v
            }
        case 8:
            if len(v) > 0 {
                attr.SecondAddress = v
            }
        case 9:
            if len(v) > 0 {
                attr.City = v
            }
        case 10:
            if len(v) > 0 {
                attr.State = v
            }
        case 12:
            if len(v) > 0 {
                attr.Country = v
            }
        case 13:
            if len(v) > 0 {
                attr.Zipcode = v
            }
        case 15:
            if len(v) > 0 {
                attr.Phone = v
            }
        }
    }
    attr.IsActive = getActiveStatus(record)
    attr.Email = record[0]
    attr.FirstName = record[1]
    attr.LastName = record[2]
    return &pb.CreateUserRequest{
        Data: &pb.CreateUserRequest_Data{
            Type:       "users",
            Attributes: attr,
        },
    }
}

func getActiveStatus(record []string) bool {
    if len(record[14]) > 0 {
        if record[14] == "Y" {
            return true
        }
    }
    return false
}