From 37df94db31c9d21a6c7b924298ad2f12298b56f3 Mon Sep 17 00:00:00 2001 From: Siddhartha Basu Date: Mon, 19 Feb 2024 19:40:51 -0600 Subject: [PATCH 01/75] feat(table.go): add field creation functionality and refactor table field creation process The changes introduce a new function 'createFields' that encapsulates the logic for creating fields in a table. This function uses a map to associate field types with their respective creation functions, improving code readability and maintainability. The 'LoadOntologyToTable' function has been refactored to use this new function, simplifying its structure and making it easier to understand. --- internal/baserow/cli/table.go | 118 +++++++++++++++++++++------------- 1 file changed, 73 insertions(+), 45 deletions(-) diff --git a/internal/baserow/cli/table.go b/internal/baserow/cli/table.go index 4f3d5dc9..1d1bdd16 100644 --- a/internal/baserow/cli/table.go +++ b/internal/baserow/cli/table.go @@ -9,6 +9,53 @@ import ( "github.com/urfave/cli/v2" ) +type fieldFn func(string) client.FieldCreateField + +type CreateFieldProperties struct { + Client *client.APIClient + Ctx context.Context + TableId int + Field string + FieldType client.Type712Enum +} + +func MapFieldTypeToFn() map[client.Type712Enum]fieldFn { + fieldFnMap := make(map[client.Type712Enum]fieldFn) + fieldFnMap[client.BOOLEAN] = func(field string) client.FieldCreateField { + return client.FieldCreateField{ + BooleanFieldCreateField: client.NewBooleanFieldCreateField( + field, + client.BOOLEAN, + )} + } + fieldFnMap[client.TEXT] = func(field string) client.FieldCreateField { + return client.FieldCreateField{ + TextFieldCreateField: client.NewTextFieldCreateField( + field, + client.TEXT, + )} + } + return fieldFnMap +} + +func createFields(args *CreateFieldProperties) error { + mapper := MapFieldTypeToFn() + if _, ok := mapper[args.FieldType]; !ok { + return fmt.Errorf("cannot find field type %s", args.FieldType) + } + createFn := mapper[args.FieldType] + _, resp, err := args.Client. + DatabaseTableFieldsApi. + CreateDatabaseTableField(args.Ctx, int32(args.TableId)). + FieldCreateField(createFn(args.Field)). + Execute() + if err != nil { + return fmt.Errorf("error in creating field %s %s", args.Field, err) + } + defer resp.Body.Close() + return nil +} + func LoadOntologyToTable(c *cli.Context) error { logger := registry.GetLogger() bclient := baserowClient(c.String("server")) @@ -17,62 +64,41 @@ func LoadOntologyToTable(c *cli.Context) error { client.ContextDatabaseToken, c.String("token"), ) + fieldMap := map[string]client.Type712Enum{ + "Name": client.TEXT, + "Id": client.TEXT, + "Is_obsolete": client.BOOLEAN, + } tlist, resp, err := bclient. DatabaseTableFieldsApi. ListDatabaseTableFields(authCtx, int32(c.Int("table-id"))). Execute() if err != nil { return cli.Exit( - fmt.Sprintf("error in getting list of table fields %s", err), 2, + fmt.Sprintf("error in getting list of table fields %s", err), + 2, ) } defer resp.Body.Close() - if len(tlist) == 0 { - logger.Debug("need to create fields in the table") - _, trsp, err := bclient. - DatabaseTableFieldsApi. - CreateDatabaseTableField(authCtx, int32(c.Int("table-id"))). - FieldCreateField(client.FieldCreateField{ - BooleanFieldCreateField: client.NewBooleanFieldCreateField( - "Is_obsolete", - client.BOOLEAN, - ), - }).Execute() + if len(tlist) != 0 { + logger.Debug("fields exists in the database table") + return nil + } + logger.Debug("need to create fields in the table") + for field, fieldType := range fieldMap { + err := createFields(&CreateFieldProperties{ + Client: bclient, + Ctx: authCtx, + TableId: c.Int("table-id"), + Field: field, + FieldType: fieldType, + }) if err != nil { - return cli.Exit( - fmt.Errorf( - "error in creating table field Is_obsolete %s", - err, - ), - 2, - ) - } - logger.Info("created field Is_obsolete") - - defer trsp.Body.Close() - for _, field := range []string{"Name", "Id"} { - _, frsp, err := bclient. - DatabaseTableFieldsApi. - CreateDatabaseTableField(authCtx, int32(c.Int("table-id"))). - FieldCreateField(client.FieldCreateField{ - TextFieldCreateField: client.NewTextFieldCreateField( - field, - client.TEXT, - ), - }).Execute() - if err != nil { - return cli.Exit( - fmt.Errorf( - "error in creating table field %s %s", - field, err, - ), - 2, - ) - } - defer frsp.Body.Close() - logger.Infof("created field %s", field) + return cli.Exit(err.Error(), 2) } + logger.Infof("created field %s", field) } + return nil } @@ -84,7 +110,9 @@ func CreateTable(c *cli.Context) error { client.ContextAccessToken, c.String("token"), ) - tbl, resp, err := bclient.DatabaseTablesApi.CreateDatabaseTable(authCtx, int32(c.Int("database-id"))). + tbl, resp, err := bclient. + DatabaseTablesApi. + CreateDatabaseTable(authCtx, int32(c.Int("database-id"))). TableCreate(client.TableCreate{Name: c.String("table")}). Execute() if err != nil { From 694c2c68380a4fa0082c553c09c83e13bd56b637 Mon Sep 17 00:00:00 2001 From: Siddhartha Basu Date: Mon, 19 Feb 2024 19:44:10 -0600 Subject: [PATCH 02/75] refactor(table.go): rename function and variable names for clarity and consistency The function 'createFields' is renamed to 'CreateTableField' to better reflect its purpose. The variable 'c' is renamed to 'cltx' to improve readability and understanding of the code. The changes are made to enhance code readability and maintainability. --- internal/baserow/cli/table.go | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/internal/baserow/cli/table.go b/internal/baserow/cli/table.go index 1d1bdd16..08622d96 100644 --- a/internal/baserow/cli/table.go +++ b/internal/baserow/cli/table.go @@ -38,7 +38,7 @@ func MapFieldTypeToFn() map[client.Type712Enum]fieldFn { return fieldFnMap } -func createFields(args *CreateFieldProperties) error { +func CreateTableField(args *CreateFieldProperties) error { mapper := MapFieldTypeToFn() if _, ok := mapper[args.FieldType]; !ok { return fmt.Errorf("cannot find field type %s", args.FieldType) @@ -56,13 +56,13 @@ func createFields(args *CreateFieldProperties) error { return nil } -func LoadOntologyToTable(c *cli.Context) error { +func LoadOntologyToTable(cltx *cli.Context) error { logger := registry.GetLogger() - bclient := baserowClient(c.String("server")) + bclient := baserowClient(cltx.String("server")) authCtx := context.WithValue( context.Background(), client.ContextDatabaseToken, - c.String("token"), + cltx.String("token"), ) fieldMap := map[string]client.Type712Enum{ "Name": client.TEXT, @@ -71,7 +71,7 @@ func LoadOntologyToTable(c *cli.Context) error { } tlist, resp, err := bclient. DatabaseTableFieldsApi. - ListDatabaseTableFields(authCtx, int32(c.Int("table-id"))). + ListDatabaseTableFields(authCtx, int32(cltx.Int("table-id"))). Execute() if err != nil { return cli.Exit( @@ -86,10 +86,10 @@ func LoadOntologyToTable(c *cli.Context) error { } logger.Debug("need to create fields in the table") for field, fieldType := range fieldMap { - err := createFields(&CreateFieldProperties{ + err := CreateTableField(&CreateFieldProperties{ Client: bclient, Ctx: authCtx, - TableId: c.Int("table-id"), + TableId: cltx.Int("table-id"), Field: field, FieldType: fieldType, }) From c33fa4d8c0418ebb0d3e241421a2fff176efc0b0 Mon Sep 17 00:00:00 2001 From: Siddhartha Basu Date: Mon, 19 Feb 2024 19:54:01 -0600 Subject: [PATCH 03/75] refactor(table.go): extract logic from LoadOntologyToTable to CreateOntologyTableFields for better code organization The LoadOntologyToTable function was doing too much. It was responsible for creating the ontology table fields and loading them into the table. This change separates these two responsibilities into two different functions. Now, the LoadOntologyToTable function is only responsible for loading the ontology into the table, and the CreateOntologyTableFields function is responsible for creating the ontology table fields. --- internal/baserow/cli/table.go | 52 +++++++++++++++++++++++++---------- 1 file changed, 37 insertions(+), 15 deletions(-) diff --git a/internal/baserow/cli/table.go b/internal/baserow/cli/table.go index 08622d96..4ab3c0c0 100644 --- a/internal/baserow/cli/table.go +++ b/internal/baserow/cli/table.go @@ -6,6 +6,7 @@ import ( "github.com/dictyBase/modware-import/internal/baserow/client" "github.com/dictyBase/modware-import/internal/registry" + "github.com/sirupsen/logrus" "github.com/urfave/cli/v2" ) @@ -19,6 +20,13 @@ type CreateFieldProperties struct { FieldType client.Type712Enum } +type OntologyTableFieldsProperties struct { + Client *client.APIClient + Ctx context.Context + Logger *logrus.Entry + TableId int +} + func MapFieldTypeToFn() map[client.Type712Enum]fieldFn { fieldFnMap := make(map[client.Type712Enum]fieldFn) fieldFnMap[client.BOOLEAN] = func(field string) client.FieldCreateField { @@ -56,14 +64,10 @@ func CreateTableField(args *CreateFieldProperties) error { return nil } -func LoadOntologyToTable(cltx *cli.Context) error { - logger := registry.GetLogger() - bclient := baserowClient(cltx.String("server")) - authCtx := context.WithValue( - context.Background(), - client.ContextDatabaseToken, - cltx.String("token"), - ) +func CreateOntologyTableFields(args *OntologyTableFieldsProperties) error { + logger := args.Logger + bclient := args.Client + authCtx := args.Ctx fieldMap := map[string]client.Type712Enum{ "Name": client.TEXT, "Id": client.TEXT, @@ -71,13 +75,10 @@ func LoadOntologyToTable(cltx *cli.Context) error { } tlist, resp, err := bclient. DatabaseTableFieldsApi. - ListDatabaseTableFields(authCtx, int32(cltx.Int("table-id"))). + ListDatabaseTableFields(authCtx, int32(args.TableId)). Execute() if err != nil { - return cli.Exit( - fmt.Sprintf("error in getting list of table fields %s", err), - 2, - ) + return fmt.Errorf("error in getting list of table fields %s", err) } defer resp.Body.Close() if len(tlist) != 0 { @@ -89,12 +90,12 @@ func LoadOntologyToTable(cltx *cli.Context) error { err := CreateTableField(&CreateFieldProperties{ Client: bclient, Ctx: authCtx, - TableId: cltx.Int("table-id"), + TableId: args.TableId, Field: field, FieldType: fieldType, }) if err != nil { - return cli.Exit(err.Error(), 2) + return err } logger.Infof("created field %s", field) } @@ -102,6 +103,27 @@ func LoadOntologyToTable(cltx *cli.Context) error { return nil } +func LoadOntologyToTable(cltx *cli.Context) error { + logger := registry.GetLogger() + bclient := baserowClient(cltx.String("server")) + authCtx := context.WithValue( + context.Background(), + client.ContextDatabaseToken, + cltx.String("token"), + ) + err := CreateOntologyTableFields(&OntologyTableFieldsProperties{ + Client: bclient, + Logger: logger, + Ctx: authCtx, + TableId: cltx.Int("table-id"), + }) + if err != nil { + return cli.Exit(err.Error(), 2) + } + + return nil +} + func CreateTable(c *cli.Context) error { logger := registry.GetLogger() bclient := baserowClient(c.String("server")) From 22fa5deb261ec79cc3859225b3e8cdd81d1863f2 Mon Sep 17 00:00:00 2001 From: Siddhartha Basu Date: Tue, 20 Feb 2024 06:20:07 -0600 Subject: [PATCH 04/75] refactor(cli): move flag functions from table.go to new file flag.go for better organization The flag functions `CreateTableFlag` and `LoadOntologyToTableFlag` were moved from `table.go` to a new file `flag.go`. This change was made to improve the organization of the codebase. By separating these functions into their own file, it makes the code easier to navigate and maintain. --- internal/baserow/cli/flag.go | 40 +++++++++++++++++++++++++++++++++++ internal/baserow/cli/table.go | 37 -------------------------------- 2 files changed, 40 insertions(+), 37 deletions(-) create mode 100644 internal/baserow/cli/flag.go diff --git a/internal/baserow/cli/flag.go b/internal/baserow/cli/flag.go new file mode 100644 index 00000000..f8abec1e --- /dev/null +++ b/internal/baserow/cli/flag.go @@ -0,0 +1,40 @@ +package cli + +import "github.com/urfave/cli/v2" + +func LoadOntologyToTableFlag() []cli.Flag { + return []cli.Flag{ + &cli.StringFlag{ + Name: "token", + Aliases: []string{"t"}, + Usage: "database token with write privilege", + Required: true, + }, + &cli.IntFlag{ + Name: "table-id", + Usage: "Database table id", + Required: true, + }, + } +} + +func CreateTableFlag() []cli.Flag { + return []cli.Flag{ + &cli.StringFlag{ + Name: "token", + Aliases: []string{"t"}, + Usage: "database token with write privilege", + Required: true, + }, + &cli.IntFlag{ + Name: "database-id", + Usage: "Database id", + Required: true, + }, + &cli.StringFlag{ + Name: "table", + Usage: "Database table", + Required: true, + }, + } +} diff --git a/internal/baserow/cli/table.go b/internal/baserow/cli/table.go index 4ab3c0c0..cd8fdcbe 100644 --- a/internal/baserow/cli/table.go +++ b/internal/baserow/cli/table.go @@ -146,40 +146,3 @@ func CreateTable(c *cli.Context) error { logger.Infof("created table %s", tbl.GetName()) return nil } - -func CreateTableFlag() []cli.Flag { - return []cli.Flag{ - &cli.StringFlag{ - Name: "token", - Aliases: []string{"t"}, - Usage: "database token with write privilege", - Required: true, - }, - &cli.IntFlag{ - Name: "database-id", - Usage: "Database id", - Required: true, - }, - &cli.StringFlag{ - Name: "table", - Usage: "Database table", - Required: true, - }, - } -} - -func LoadOntologyToTableFlag() []cli.Flag { - return []cli.Flag{ - &cli.StringFlag{ - Name: "token", - Aliases: []string{"t"}, - Usage: "database token with write privilege", - Required: true, - }, - &cli.IntFlag{ - Name: "table-id", - Usage: "Database table id", - Required: true, - }, - } -} From d3ff5b649a263a148c414097a6cf7aaca176da81 Mon Sep 17 00:00:00 2001 From: Siddhartha Basu Date: Tue, 20 Feb 2024 06:27:23 -0600 Subject: [PATCH 05/75] refactor(cli): move CreateAccessTokenFlag and CreateDatabaseTokenFlag functions to flag.go The CreateAccessTokenFlag and CreateDatabaseTokenFlag functions were moved from database.go to flag.go. This change was made to improve the organization of the codebase. By grouping related functions together in the same file, it becomes easier to locate and understand the functionality of the code. --- internal/baserow/cli/database.go | 35 -------------------------------- internal/baserow/cli/flag.go | 35 ++++++++++++++++++++++++++++++++ 2 files changed, 35 insertions(+), 35 deletions(-) diff --git a/internal/baserow/cli/database.go b/internal/baserow/cli/database.go index da9a7985..3344d270 100644 --- a/internal/baserow/cli/database.go +++ b/internal/baserow/cli/database.go @@ -110,38 +110,3 @@ func CreateDatabaseToken(c *cli.Context) error { fmt.Printf("database token %s\n", tok.GetKey()) return nil } - -func CreateDatabaseTokenFlag() []cli.Flag { - aflags := CreateAccessTokenFlag() - return append(aflags, []cli.Flag{ - &cli.StringFlag{ - Name: "workspace", - Aliases: []string{"w"}, - Usage: "Only tables under this workspaces can be accessed", - Required: true, - }, - &cli.StringFlag{ - Name: "name", - Aliases: []string{"n"}, - Usage: "token name", - Required: true, - }, - }...) -} - -func CreateAccessTokenFlag() []cli.Flag { - return []cli.Flag{ - &cli.StringFlag{ - Name: "email", - Aliases: []string{"e"}, - Usage: "Email of the user", - Required: true, - }, - &cli.StringFlag{ - Name: "password", - Aliases: []string{"p"}, - Usage: "Database password", - Required: true, - }, - } -} diff --git a/internal/baserow/cli/flag.go b/internal/baserow/cli/flag.go index f8abec1e..d775e358 100644 --- a/internal/baserow/cli/flag.go +++ b/internal/baserow/cli/flag.go @@ -2,6 +2,41 @@ package cli import "github.com/urfave/cli/v2" +func CreateAccessTokenFlag() []cli.Flag { + return []cli.Flag{ + &cli.StringFlag{ + Name: "email", + Aliases: []string{"e"}, + Usage: "Email of the user", + Required: true, + }, + &cli.StringFlag{ + Name: "password", + Aliases: []string{"p"}, + Usage: "Database password", + Required: true, + }, + } +} + +func CreateDatabaseTokenFlag() []cli.Flag { + aflags := CreateAccessTokenFlag() + return append(aflags, []cli.Flag{ + &cli.StringFlag{ + Name: "workspace", + Aliases: []string{"w"}, + Usage: "Only tables under this workspaces can be accessed", + Required: true, + }, + &cli.StringFlag{ + Name: "name", + Aliases: []string{"n"}, + Usage: "token name", + Required: true, + }, + }...) +} + func LoadOntologyToTableFlag() []cli.Flag { return []cli.Flag{ &cli.StringFlag{ From eb6fd00a7bf2ddd88467e812e13411305c4939c6 Mon Sep 17 00:00:00 2001 From: Siddhartha Basu Date: Tue, 20 Feb 2024 06:54:36 -0600 Subject: [PATCH 06/75] refactor: move database related functions from cli to database package The database related functions were moved from the cli package to a new database package. This change was made to improve the organization of the codebase, making it easier to maintain and understand. The database package now encapsulates all the database related operations, providing a clear separation of concerns. --- internal/baserow/cli/database.go | 112 -------------------------- internal/baserow/database/database.go | 44 ++++++++++ 2 files changed, 44 insertions(+), 112 deletions(-) delete mode 100644 internal/baserow/cli/database.go create mode 100644 internal/baserow/database/database.go diff --git a/internal/baserow/cli/database.go b/internal/baserow/cli/database.go deleted file mode 100644 index 3344d270..00000000 --- a/internal/baserow/cli/database.go +++ /dev/null @@ -1,112 +0,0 @@ -package cli - -import ( - "context" - "errors" - "fmt" - - "net/http" - - "github.com/dictyBase/modware-import/internal/baserow/client" - "github.com/dictyBase/modware-import/internal/collection" - "github.com/urfave/cli/v2" - "golang.org/x/exp/slices" -) - -type accessTokenProperties struct { - email, password, server string -} - -func baserowClient(server string) *client.APIClient { - conf := client.NewConfiguration() - conf.Host = server - conf.Scheme = "https" - return client.NewAPIClient(conf) -} - -func accessToken(args *accessTokenProperties) (string, error) { - req := baserowClient( - args.server, - ).UserApi.TokenAuth( - context.Background(), - ) - resp, r, err := req.TokenObtainPairWithUser( - client.TokenObtainPairWithUser{ - Email: &args.email, - Password: args.password, - }, - ).Execute() - defer r.Body.Close() - if err != nil { - return "", fmt.Errorf("error in executing API call %s", err) - } - if r != nil && r.StatusCode == http.StatusUnauthorized { - return "", errors.New("unauthrorized access") - } - return resp.GetToken(), nil -} - -func CreateAccessToken(c *cli.Context) error { - token, err := accessToken(&accessTokenProperties{ - email: c.String("email"), - password: c.String("password"), - server: c.String("server"), - }) - if err != nil { - return cli.Exit(err, 2) - } - fmt.Println(token) - return nil -} - -func CreateDatabaseToken(c *cli.Context) error { - atoken, err := accessToken(&accessTokenProperties{ - email: c.String("email"), - password: c.String("password"), - server: c.String("server"), - }) - if err != nil { - return cli.Exit(fmt.Errorf("error in creating access token %s", err), 2) - } - bclient := baserowClient(c.String("server")) - authCtx := context.WithValue( - context.Background(), - client.ContextAccessToken, - atoken, - ) - wlist, r, err := bclient.WorkspacesApi.ListWorkspaces(authCtx). - Execute() - defer r.Body.Close() - if err != nil { - return cli.Exit( - fmt.Errorf("error in executing list workspaces API call %s", err), - 2, - ) - } - wnames := collection.Map( - wlist, - func(w client.WorkspaceUserWorkspace) string { return w.GetName() }, - ) - idx := slices.Index(wnames, c.String("workspace")) - if idx == -1 { - return cli.Exit( - fmt.Errorf("workspace %s cannot be found", c.String("workspace")), - 2, - ) - } - tok, r, err := bclient.DatabaseTokensApi.CreateDatabaseToken(authCtx). - TokenCreate(client.TokenCreate{ - Name: c.String("name"), - Workspace: wlist[idx].GetId(), - }). - Execute() - defer r.Body.Close() - if err != nil { - return cli.Exit( - fmt.Errorf("error in creating token %s", err), - 2, - ) - } - fmt.Printf("database token %s\n", tok.GetKey()) - return nil -} diff --git a/internal/baserow/database/database.go b/internal/baserow/database/database.go new file mode 100644 index 00000000..559809af --- /dev/null +++ b/internal/baserow/database/database.go @@ -0,0 +1,44 @@ +package database + +import ( + "context" + "errors" + "fmt" + + "net/http" + + "github.com/dictyBase/modware-import/internal/baserow/client" +) + +type AccessTokenProperties struct { + Email, Password, Server string +} + +func BaserowClient(server string) *client.APIClient { + conf := client.NewConfiguration() + conf.Host = server + conf.Scheme = "https" + return client.NewAPIClient(conf) +} + +func AccessToken(args *AccessTokenProperties) (string, error) { + req := BaserowClient( + args.Server, + ).UserApi.TokenAuth( + context.Background(), + ) + resp, r, err := req.TokenObtainPairWithUser( + client.TokenObtainPairWithUser{ + Email: &args.Email, + Password: args.Password, + }, + ).Execute() + defer r.Body.Close() + if err != nil { + return "", fmt.Errorf("error in executing API call %s", err) + } + if r != nil && r.StatusCode == http.StatusUnauthorized { + return "", errors.New("unauthrorized access") + } + return resp.GetToken(), nil +} From e059125e19c847cccd1370c6bdd9da131914b271 Mon Sep 17 00:00:00 2001 From: Siddhartha Basu Date: Tue, 20 Feb 2024 08:01:33 -0600 Subject: [PATCH 07/75] feat(database): add table.go for handling database table fields The new file, table.go, is added to handle operations related to database table fields. It includes functions to map field types to their respective functions, create table fields, and create ontology table fields. This addition enhances the modularity of the code and makes it easier to manage database table fields. It also provides error handling for cases where field types are not found or there are issues in creating fields. --- internal/baserow/{cli => database}/table.go | 48 +-------------------- 1 file changed, 1 insertion(+), 47 deletions(-) rename internal/baserow/{cli => database}/table.go (68%) diff --git a/internal/baserow/cli/table.go b/internal/baserow/database/table.go similarity index 68% rename from internal/baserow/cli/table.go rename to internal/baserow/database/table.go index cd8fdcbe..71790629 100644 --- a/internal/baserow/cli/table.go +++ b/internal/baserow/database/table.go @@ -1,13 +1,11 @@ -package cli +package database import ( "context" "fmt" "github.com/dictyBase/modware-import/internal/baserow/client" - "github.com/dictyBase/modware-import/internal/registry" "github.com/sirupsen/logrus" - "github.com/urfave/cli/v2" ) type fieldFn func(string) client.FieldCreateField @@ -102,47 +100,3 @@ func CreateOntologyTableFields(args *OntologyTableFieldsProperties) error { return nil } - -func LoadOntologyToTable(cltx *cli.Context) error { - logger := registry.GetLogger() - bclient := baserowClient(cltx.String("server")) - authCtx := context.WithValue( - context.Background(), - client.ContextDatabaseToken, - cltx.String("token"), - ) - err := CreateOntologyTableFields(&OntologyTableFieldsProperties{ - Client: bclient, - Logger: logger, - Ctx: authCtx, - TableId: cltx.Int("table-id"), - }) - if err != nil { - return cli.Exit(err.Error(), 2) - } - - return nil -} - -func CreateTable(c *cli.Context) error { - logger := registry.GetLogger() - bclient := baserowClient(c.String("server")) - authCtx := context.WithValue( - context.Background(), - client.ContextAccessToken, - c.String("token"), - ) - tbl, resp, err := bclient. - DatabaseTablesApi. - CreateDatabaseTable(authCtx, int32(c.Int("database-id"))). - TableCreate(client.TableCreate{Name: c.String("table")}). - Execute() - if err != nil { - return cli.Exit( - fmt.Errorf("error in creating table %s", err), 2, - ) - } - defer resp.Body.Close() - logger.Infof("created table %s", tbl.GetName()) - return nil -} From 0905f430dc38e99b2af092705e693faea67aa791 Mon Sep 17 00:00:00 2001 From: Siddhartha Basu Date: Tue, 20 Feb 2024 08:01:51 -0600 Subject: [PATCH 08/75] feat(.golangci.yml): enable unused linter's local-variables-are-used option The unused linter's local-variables-are-used option is now enabled to ensure that all local variables are being used in the code. --- .golangci.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.golangci.yml b/.golangci.yml index ad7f9dff..779c4e6e 100644 --- a/.golangci.yml +++ b/.golangci.yml @@ -2,6 +2,8 @@ run: allow-parallel-runners: true timeout: 5m linters-settings: + unused: + local-variables-are-used: true gosec: excludes: - G402 From e9858381bddb276467260cd119f3d69c00d12119 Mon Sep 17 00:00:00 2001 From: Siddhartha Basu Date: Tue, 20 Feb 2024 08:23:33 -0600 Subject: [PATCH 09/75] feat(cli/action.go): add new functions for database token creation, access token creation, ontology table loading, and table creation The new functions in cli/action.go provide additional functionality for the Baserow CLI. The CreateDatabaseToken function generates a new database token, while the CreateAccessToken function creates a new access token. The LoadOntologyToTable function allows ontology data to be loaded into a specified table, and the CreateTable function creates a new table in the database. These changes enhance the CLI's capabilities and provide users with more options for interacting with their Baserow databases. --- internal/baserow/cli/action.go | 124 +++++++++++++++++++++++++++++++++ 1 file changed, 124 insertions(+) create mode 100644 internal/baserow/cli/action.go diff --git a/internal/baserow/cli/action.go b/internal/baserow/cli/action.go new file mode 100644 index 00000000..e2a8333a --- /dev/null +++ b/internal/baserow/cli/action.go @@ -0,0 +1,124 @@ +package cli + +import ( + "context" + "fmt" + + "github.com/dictyBase/modware-import/internal/baserow/client" + "github.com/dictyBase/modware-import/internal/baserow/database" + "github.com/dictyBase/modware-import/internal/collection" + "github.com/dictyBase/modware-import/internal/registry" + "github.com/urfave/cli/v2" + "golang.org/x/exp/slices" +) + +func CreateDatabaseToken(c *cli.Context) error { + atoken, err := database.AccessToken(&database.AccessTokenProperties{ + Email: c.String("email"), + Password: c.String("password"), + Server: c.String("server"), + }) + if err != nil { + return cli.Exit(fmt.Errorf("error in creating access token %s", err), 2) + } + bclient := database.BaserowClient(c.String("server")) + authCtx := context.WithValue( + context.Background(), + client.ContextAccessToken, + atoken, + ) + wlist, r, err := bclient.WorkspacesApi.ListWorkspaces(authCtx). + Execute() + defer r.Body.Close() + if err != nil { + return cli.Exit( + fmt.Errorf("error in executing list workspaces API call %s", err), + 2, + ) + } + wnames := collection.Map( + wlist, + func(w client.WorkspaceUserWorkspace) string { return w.GetName() }, + ) + idx := slices.Index(wnames, c.String("workspace")) + if idx == -1 { + return cli.Exit( + fmt.Errorf("workspace %s cannot be found", c.String("workspace")), + 2, + ) + } + tok, r, err := bclient.DatabaseTokensApi.CreateDatabaseToken(authCtx). + TokenCreate(client.TokenCreate{ + Name: c.String("name"), + Workspace: wlist[idx].GetId(), + }). + Execute() + defer r.Body.Close() + if err != nil { + return cli.Exit( + fmt.Errorf("error in creating token %s", err), + 2, + ) + } + fmt.Printf("database token %s\n", tok.GetKey()) + return nil +} + +func CreateAccessToken(c *cli.Context) error { + token, err := database.AccessToken(&database.AccessTokenProperties{ + Email: c.String("email"), + Password: c.String("password"), + Server: c.String("server"), + }) + if err != nil { + return cli.Exit(err, 2) + } + fmt.Println(token) + return nil +} + +func LoadOntologyToTable(cltx *cli.Context) error { + logger := registry.GetLogger() + bclient := database.BaserowClient(cltx.String("server")) + authCtx := context.WithValue( + context.Background(), + client.ContextDatabaseToken, + cltx.String("token"), + ) + err := database.CreateOntologyTableFields( + &database.OntologyTableFieldsProperties{ + Client: bclient, + Logger: logger, + Ctx: authCtx, + TableId: cltx.Int("table-id"), + }, + ) + if err != nil { + return cli.Exit(err.Error(), 2) + } + + return nil +} + +func CreateTable(c *cli.Context) error { + logger := registry.GetLogger() + bclient := database.BaserowClient(c.String("server")) + authCtx := context.WithValue( + context.Background(), + client.ContextAccessToken, + c.String("token"), + ) + tbl, resp, err := bclient. + DatabaseTablesApi. + CreateDatabaseTable(authCtx, int32(c.Int("database-id"))). + TableCreate(client.TableCreate{Name: c.String("table")}). + Execute() + if err != nil { + return cli.Exit( + fmt.Errorf("error in creating table %s", err), 2, + ) + } + defer resp.Body.Close() + logger.Infof("created table %s", tbl.GetName()) + return nil +} From 7814f2b4677ce3982c108d87a646db7a53d8557c Mon Sep 17 00:00:00 2001 From: Siddhartha Basu Date: Tue, 20 Feb 2024 08:24:33 -0600 Subject: [PATCH 10/75] refactor(action.go): rename function parameter from 'c' to 'cltx' for better readability The function parameter 'c' was renamed to 'cltx' to improve code readability. The new name 'cltx' is more descriptive and provides a better understanding of its role as a CLI context. --- internal/baserow/cli/action.go | 34 +++++++++++++++++----------------- 1 file changed, 17 insertions(+), 17 deletions(-) diff --git a/internal/baserow/cli/action.go b/internal/baserow/cli/action.go index e2a8333a..f301953e 100644 --- a/internal/baserow/cli/action.go +++ b/internal/baserow/cli/action.go @@ -12,16 +12,16 @@ import ( "golang.org/x/exp/slices" ) -func CreateDatabaseToken(c *cli.Context) error { +func CreateDatabaseToken(cltx *cli.Context) error { atoken, err := database.AccessToken(&database.AccessTokenProperties{ - Email: c.String("email"), - Password: c.String("password"), - Server: c.String("server"), + Email: cltx.String("email"), + Password: cltx.String("password"), + Server: cltx.String("server"), }) if err != nil { return cli.Exit(fmt.Errorf("error in creating access token %s", err), 2) } - bclient := database.BaserowClient(c.String("server")) + bclient := database.BaserowClient(cltx.String("server")) authCtx := context.WithValue( context.Background(), client.ContextAccessToken, @@ -40,16 +40,16 @@ func CreateDatabaseToken(c *cli.Context) error { wlist, func(w client.WorkspaceUserWorkspace) string { return w.GetName() }, ) - idx := slices.Index(wnames, c.String("workspace")) + idx := slices.Index(wnames, cltx.String("workspace")) if idx == -1 { return cli.Exit( - fmt.Errorf("workspace %s cannot be found", c.String("workspace")), + fmt.Errorf("workspace %s cannot be found", cltx.String("workspace")), 2, ) } tok, r, err := bclient.DatabaseTokensApi.CreateDatabaseToken(authCtx). TokenCreate(client.TokenCreate{ - Name: c.String("name"), + Name: cltx.String("name"), Workspace: wlist[idx].GetId(), }). Execute() @@ -64,11 +64,11 @@ func CreateDatabaseToken(c *cli.Context) error { return nil } -func CreateAccessToken(c *cli.Context) error { +func CreateAccessToken(cltx *cli.Context) error { token, err := database.AccessToken(&database.AccessTokenProperties{ - Email: c.String("email"), - Password: c.String("password"), - Server: c.String("server"), + Email: cltx.String("email"), + Password: cltx.String("password"), + Server: cltx.String("server"), }) if err != nil { return cli.Exit(err, 2) @@ -100,18 +100,18 @@ func LoadOntologyToTable(cltx *cli.Context) error { return nil } -func CreateTable(c *cli.Context) error { +func CreateTable(cltx *cli.Context) error { logger := registry.GetLogger() - bclient := database.BaserowClient(c.String("server")) + bclient := database.BaserowClient(cltx.String("server")) authCtx := context.WithValue( context.Background(), client.ContextAccessToken, - c.String("token"), + cltx.String("token"), ) tbl, resp, err := bclient. DatabaseTablesApi. - CreateDatabaseTable(authCtx, int32(c.Int("database-id"))). - TableCreate(client.TableCreate{Name: c.String("table")}). + CreateDatabaseTable(authCtx, int32(cltx.Int("database-id"))). + TableCreate(client.TableCreate{Name: cltx.String("table")}). Execute() if err != nil { return cli.Exit( From f23c6255dde76983fb6645bb79a5eb6fd84b5c75 Mon Sep 17 00:00:00 2001 From: Siddhartha Basu Date: Tue, 20 Feb 2024 11:03:10 -0600 Subject: [PATCH 11/75] refactor(database/table.go): move fieldMap from CreateOntologyTableFields to action.go for better separation of concerns The error message formatting in cli/action.go has been improved for better readability. The CreateOntologyTableFields function now accepts a fields map as an argument, allowing for more flexibility in creating table fields. The fieldMap has been moved from database/table.go to cli/action.go to better separate concerns and make the code more maintainable. --- internal/baserow/cli/action.go | 19 ++++++++++++++----- internal/baserow/database/table.go | 16 ++++++---------- 2 files changed, 20 insertions(+), 15 deletions(-) diff --git a/internal/baserow/cli/action.go b/internal/baserow/cli/action.go index f301953e..7ef37fd9 100644 --- a/internal/baserow/cli/action.go +++ b/internal/baserow/cli/action.go @@ -43,7 +43,10 @@ func CreateDatabaseToken(cltx *cli.Context) error { idx := slices.Index(wnames, cltx.String("workspace")) if idx == -1 { return cli.Exit( - fmt.Errorf("workspace %s cannot be found", cltx.String("workspace")), + fmt.Errorf( + "workspace %s cannot be found", + cltx.String("workspace"), + ), 2, ) } @@ -85,12 +88,18 @@ func LoadOntologyToTable(cltx *cli.Context) error { client.ContextDatabaseToken, cltx.String("token"), ) + fields := map[string]client.Type712Enum{ + "Name": client.TEXT, + "Id": client.TEXT, + "Is_obsolete": client.BOOLEAN, + } err := database.CreateOntologyTableFields( &database.OntologyTableFieldsProperties{ - Client: bclient, - Logger: logger, - Ctx: authCtx, - TableId: cltx.Int("table-id"), + Client: bclient, + Logger: logger, + Ctx: authCtx, + FieldMap: fields, + TableId: cltx.Int("table-id"), }, ) if err != nil { diff --git a/internal/baserow/database/table.go b/internal/baserow/database/table.go index 71790629..e00459eb 100644 --- a/internal/baserow/database/table.go +++ b/internal/baserow/database/table.go @@ -19,10 +19,11 @@ type CreateFieldProperties struct { } type OntologyTableFieldsProperties struct { - Client *client.APIClient - Ctx context.Context - Logger *logrus.Entry - TableId int + Client *client.APIClient + Ctx context.Context + Logger *logrus.Entry + TableId int + FieldMap map[string]client.Type712Enum } func MapFieldTypeToFn() map[client.Type712Enum]fieldFn { @@ -66,11 +67,6 @@ func CreateOntologyTableFields(args *OntologyTableFieldsProperties) error { logger := args.Logger bclient := args.Client authCtx := args.Ctx - fieldMap := map[string]client.Type712Enum{ - "Name": client.TEXT, - "Id": client.TEXT, - "Is_obsolete": client.BOOLEAN, - } tlist, resp, err := bclient. DatabaseTableFieldsApi. ListDatabaseTableFields(authCtx, int32(args.TableId)). @@ -84,7 +80,7 @@ func CreateOntologyTableFields(args *OntologyTableFieldsProperties) error { return nil } logger.Debug("need to create fields in the table") - for field, fieldType := range fieldMap { + for field, fieldType := range args.FieldMap { err := CreateTableField(&CreateFieldProperties{ Client: bclient, Ctx: authCtx, From 1afa73bd4f76f18f66d88dff5b4fe638ed85347e Mon Sep 17 00:00:00 2001 From: Siddhartha Basu Date: Tue, 20 Feb 2024 11:06:27 -0600 Subject: [PATCH 12/75] feat(flag.go): add 'input' flag for json formatted ontology file The 'input' flag is added to the LoadOntologyToTableFlag function to allow users to specify a json formatted ontology file. This change provides a more user-friendly way to input ontology data, making the tool more flexible and easier to use. --- internal/baserow/cli/flag.go | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/internal/baserow/cli/flag.go b/internal/baserow/cli/flag.go index d775e358..167c442f 100644 --- a/internal/baserow/cli/flag.go +++ b/internal/baserow/cli/flag.go @@ -50,6 +50,12 @@ func LoadOntologyToTableFlag() []cli.Flag { Usage: "Database table id", Required: true, }, + &cli.StringFlag{ + Name: "input", + Aliases: []string{"i"}, + Usage: "input json formatted ontology file", + Required: true, + }, } } From c3523c5734d45c6a4986f082d986202fa239603a Mon Sep 17 00:00:00 2001 From: Siddhartha Basu Date: Tue, 20 Feb 2024 12:45:47 -0600 Subject: [PATCH 13/75] feat(action.go): add loadOntology function to handle ontology loading The loadOntology function has been added to handle the loading of ontology from a file. This function opens the file, builds the graph, and handles any errors that may occur during these processes. This makes the code more modular and easier to maintain. In the LoadOntologyToTable function, error handling has been improved. Now, if an error occurs while loading the ontology, the function will exit and return the error message. This makes it easier to debug and understand any issues that may occur during the ontology loading process. --- internal/baserow/cli/action.go | 24 +++++++++++++++++++++++- 1 file changed, 23 insertions(+), 1 deletion(-) diff --git a/internal/baserow/cli/action.go b/internal/baserow/cli/action.go index 7ef37fd9..9cc94f7b 100644 --- a/internal/baserow/cli/action.go +++ b/internal/baserow/cli/action.go @@ -3,7 +3,9 @@ package cli import ( "context" "fmt" + "os" + "github.com/dictyBase/go-obograph/graph" "github.com/dictyBase/modware-import/internal/baserow/client" "github.com/dictyBase/modware-import/internal/baserow/database" "github.com/dictyBase/modware-import/internal/collection" @@ -82,6 +84,10 @@ func CreateAccessToken(cltx *cli.Context) error { func LoadOntologyToTable(cltx *cli.Context) error { logger := registry.GetLogger() + oboGraph, err := loadOntology(cltx.String("input")) + if err != nil { + return cli.Exit(err.Error(), 2) + } bclient := database.BaserowClient(cltx.String("server")) authCtx := context.WithValue( context.Background(), @@ -93,7 +99,7 @@ func LoadOntologyToTable(cltx *cli.Context) error { "Id": client.TEXT, "Is_obsolete": client.BOOLEAN, } - err := database.CreateOntologyTableFields( + err = database.CreateOntologyTableFields( &database.OntologyTableFieldsProperties{ Client: bclient, Logger: logger, @@ -131,3 +137,19 @@ func CreateTable(cltx *cli.Context) error { logger.Infof("created table %s", tbl.GetName()) return nil } + +func loadOntology(name string) (graph.OboGraph, error) { + rdr, err := os.Open(name) + if err != nil { + return nil, fmt.Errorf("error in opening file %s %s", name, err) + } + grph, err := graph.BuildGraph(rdr) + if err != nil { + return grph, fmt.Errorf( + "error in building graph from file %s %s", + name, + err, + ) + } + return grph, nil +} From b975d2bb635668d4f99192176592fdf45b9a1841 Mon Sep 17 00:00:00 2001 From: Siddhartha Basu Date: Tue, 20 Feb 2024 16:41:54 -0600 Subject: [PATCH 14/75] feat(load.go): add commonHeader function to set request headers A new function, commonHeader, has been added to the load.go file. This function sets the Content-Type, Accept, and Authorization headers for a given http request. --- internal/baserow/ontology/load.go | 8 ++++++++ 1 file changed, 8 insertions(+) create mode 100644 internal/baserow/ontology/load.go diff --git a/internal/baserow/ontology/load.go b/internal/baserow/ontology/load.go new file mode 100644 index 00000000..48f6f3f2 --- /dev/null +++ b/internal/baserow/ontology/load.go @@ -0,0 +1,8 @@ +import ( + "net/http" +) +func commonHeader(lreq *http.Request, token string) { + lreq.Header.Set("Content-Type", "application/json") + lreq.Header.Set("Accept", "application/json") + lreq.Header.Set("Authorization", fmt.Sprintf("Bearer %s", token)) +} From 3111461e9158d6842924f46f3ca3ed5c9884ea26 Mon Sep 17 00:00:00 2001 From: Siddhartha Basu Date: Tue, 20 Feb 2024 16:43:03 -0600 Subject: [PATCH 15/75] feat(load.go): add reqToResponse function to handle HTTP requests and responses The reqToResponse function has been added to handle HTTP requests and responses. This function makes a request using the provided http.Request object and returns the response. It also handles errors during the request and non-200 status codes in the response, providing more detailed error messages in these cases. --- internal/baserow/ontology/load.go | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+) diff --git a/internal/baserow/ontology/load.go b/internal/baserow/ontology/load.go index 48f6f3f2..4cf92dd8 100644 --- a/internal/baserow/ontology/load.go +++ b/internal/baserow/ontology/load.go @@ -1,6 +1,32 @@ import ( + "fmt" + "io" "net/http" ) +func reqToResponse(creq *http.Request) (*http.Response, error) { + client := &http.Client{} + uresp, err := client.Do(creq) + if err != nil { + return uresp, fmt.Errorf("error in making request %s", err) + } + if uresp.StatusCode != 200 { + cnt, err := io.ReadAll(uresp.Body) + if err != nil { + return uresp, fmt.Errorf( + "error in response and the reading the body %d %s", + uresp.StatusCode, + err, + ) + } + return uresp, fmt.Errorf( + "unexpected error response %d %s", + uresp.StatusCode, + string(cnt), + ) + } + return uresp, nil +} + func commonHeader(lreq *http.Request, token string) { lreq.Header.Set("Content-Type", "application/json") lreq.Header.Set("Accept", "application/json") From abc328d4e5408657db037c2881817eedc3332e87 Mon Sep 17 00:00:00 2001 From: Siddhartha Basu Date: Tue, 20 Feb 2024 16:43:43 -0600 Subject: [PATCH 16/75] feat(load.go): add termStatus function to check if term is deprecated The termStatus function has been added to check if a term is deprecated. This function is necessary to ensure that deprecated terms are properly flagged and handled in the application. It uses the IsDeprecated method from the go-obograph library to determine the status of a term. --- internal/baserow/ontology/load.go | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/internal/baserow/ontology/load.go b/internal/baserow/ontology/load.go index 4cf92dd8..a5989fca 100644 --- a/internal/baserow/ontology/load.go +++ b/internal/baserow/ontology/load.go @@ -2,6 +2,7 @@ import ( "fmt" "io" "net/http" + "github.com/dictyBase/go-obograph/graph" ) func reqToResponse(creq *http.Request) (*http.Response, error) { client := &http.Client{} @@ -32,3 +33,10 @@ func commonHeader(lreq *http.Request, token string) { lreq.Header.Set("Accept", "application/json") lreq.Header.Set("Authorization", fmt.Sprintf("Bearer %s", token)) } + +func termStatus(term graph.Term) string { + if term.IsDeprecated() { + return "true" + } + return "false" +} From cfae340872f2c9cbf40e65022f4c5303ccb0cc27 Mon Sep 17 00:00:00 2001 From: Siddhartha Basu Date: Tue, 20 Feb 2024 16:44:37 -0600 Subject: [PATCH 17/75] feat(load.go): add addTermRow function to post term data to baserow The addTermRow function has been added to post term data to the baserow. This function takes term data, marshals it into JSON, and sends a POST request to the baserow API. --- internal/baserow/ontology/load.go | 36 +++++++++++++++++++++++++++++++ 1 file changed, 36 insertions(+) diff --git a/internal/baserow/ontology/load.go b/internal/baserow/ontology/load.go index a5989fca..7e785edc 100644 --- a/internal/baserow/ontology/load.go +++ b/internal/baserow/ontology/load.go @@ -1,9 +1,45 @@ import ( + "bytes" + "encoding/json" "fmt" "io" "net/http" "github.com/dictyBase/go-obograph/graph" + "github.com/dictyBase/modware-import/internal/baserow/client" ) +func addTermRow(args *addTermRowProperties) error { + term := args.Term + payload := map[string]interface{}{ + "Id": term.ID(), + "Name": term.Label(), + "Is_obsolete": termStatus(term), + } + jsonData, err := json.Marshal(payload) + if err != nil { + return fmt.Errorf("error in encoding body %s", err) + } + req, err := http.NewRequest( + "POST", + fmt.Sprintf( + "%s/api/database/rows/table/%d/?user_field_names=true", + args.Host, + args.TableId, + ), + bytes.NewBuffer(jsonData), + ) + if err != nil { + return fmt.Errorf("error in creating request %s ", err) + } + commonHeader(req, args.Token) + res, err := reqToResponse(req) + if err != nil { + return err + } + defer res.Body.Close() + + return nil +} + func reqToResponse(creq *http.Request) (*http.Response, error) { client := &http.Client{} uresp, err := client.Do(creq) From 4524b727f7f7a10d2ef8e8d1c285f96de92d9a42 Mon Sep 17 00:00:00 2001 From: Siddhartha Basu Date: Tue, 20 Feb 2024 16:46:19 -0600 Subject: [PATCH 18/75] feat(load.go): add LoadNew function to load new ontology terms The LoadNew function has been added to load new ontology terms from a file. This function opens the file, builds a graph from it, and then adds each term as a row. This change allows for more efficient and organized loading of ontology terms, improving the overall functionality of the ontology loading process. --- internal/baserow/ontology/load.go | 50 +++++++++++++++++++++++++++++++ 1 file changed, 50 insertions(+) diff --git a/internal/baserow/ontology/load.go b/internal/baserow/ontology/load.go index 7e785edc..fddba679 100644 --- a/internal/baserow/ontology/load.go +++ b/internal/baserow/ontology/load.go @@ -1,12 +1,62 @@ +package ontology + import ( "bytes" "encoding/json" "fmt" "io" "net/http" + "os" + "github.com/dictyBase/go-obograph/graph" "github.com/dictyBase/modware-import/internal/baserow/client" + "github.com/sirupsen/logrus" ) + +type LoadProperties struct { + File string + TableId int + Token string + Client *client.APIClient + Logger *logrus.Entry +} + +type addTermRowProperties struct { + Term graph.Term + Host string + Token string + TableId int +} + +func LoadNew(args *LoadProperties) error { + rdr, err := os.Open(args.File) + if err != nil { + return fmt.Errorf("error in opening file %s %s", args.File, err) + } + defer rdr.Close() + grph, err := graph.BuildGraph(rdr) + if err != nil { + return fmt.Errorf( + "error in building graph from file %s %s", + args.File, + err, + ) + } + for _, term := range grph.Terms() { + err := addTermRow(&addTermRowProperties{ + Term: term, + Host: args.Client.GetConfig().Host, + Token: args.Token, + TableId: args.TableId, + }) + if err != nil { + return err + } + args.Logger.Infof("add row with id %s", term.ID()) + } + return nil +} + func addTermRow(args *addTermRowProperties) error { term := args.Term payload := map[string]interface{}{ From 4d6b2616e402b3aecbcdc94ff06c03b63c10c552 Mon Sep 17 00:00:00 2001 From: Siddhartha Basu Date: Tue, 20 Feb 2024 16:46:33 -0600 Subject: [PATCH 19/75] fix(action.go): correct error handling in LoadOntologyToTable function The unused import "os" and the unused function "loadOntology" have been removed to improve code cleanliness and readability. The error handling in the LoadOntologyToTable function has been corrected to ensure that errors are properly caught and handled. This change improves the robustness of the code and makes it easier to debug in the future. --- internal/baserow/cli/action.go | 24 +----------------------- 1 file changed, 1 insertion(+), 23 deletions(-) diff --git a/internal/baserow/cli/action.go b/internal/baserow/cli/action.go index 9cc94f7b..7ef37fd9 100644 --- a/internal/baserow/cli/action.go +++ b/internal/baserow/cli/action.go @@ -3,9 +3,7 @@ package cli import ( "context" "fmt" - "os" - "github.com/dictyBase/go-obograph/graph" "github.com/dictyBase/modware-import/internal/baserow/client" "github.com/dictyBase/modware-import/internal/baserow/database" "github.com/dictyBase/modware-import/internal/collection" @@ -84,10 +82,6 @@ func CreateAccessToken(cltx *cli.Context) error { func LoadOntologyToTable(cltx *cli.Context) error { logger := registry.GetLogger() - oboGraph, err := loadOntology(cltx.String("input")) - if err != nil { - return cli.Exit(err.Error(), 2) - } bclient := database.BaserowClient(cltx.String("server")) authCtx := context.WithValue( context.Background(), @@ -99,7 +93,7 @@ func LoadOntologyToTable(cltx *cli.Context) error { "Id": client.TEXT, "Is_obsolete": client.BOOLEAN, } - err = database.CreateOntologyTableFields( + err := database.CreateOntologyTableFields( &database.OntologyTableFieldsProperties{ Client: bclient, Logger: logger, @@ -137,19 +131,3 @@ func CreateTable(cltx *cli.Context) error { logger.Infof("created table %s", tbl.GetName()) return nil } - -func loadOntology(name string) (graph.OboGraph, error) { - rdr, err := os.Open(name) - if err != nil { - return nil, fmt.Errorf("error in opening file %s %s", name, err) - } - grph, err := graph.BuildGraph(rdr) - if err != nil { - return grph, fmt.Errorf( - "error in building graph from file %s %s", - name, - err, - ) - } - return grph, nil -} From 3e4607ace58a18709075ce9bdd43976ac407becb Mon Sep 17 00:00:00 2001 From: Siddhartha Basu Date: Wed, 21 Feb 2024 09:55:05 -0600 Subject: [PATCH 20/75] feat(load.go): add existTermRow function to check term presence in database The existTermRow function has been added to check if a term already exists in the database. This function is necessary to prevent duplicate entries and ensure data integrity. It uses the ListDatabaseTableRows API to search for the term in the database and returns a boolean value indicating the presence of the term. --- internal/baserow/ontology/load.go | 32 +++++++++++++++++++++++++++++++ 1 file changed, 32 insertions(+) diff --git a/internal/baserow/ontology/load.go b/internal/baserow/ontology/load.go index fddba679..f7bec159 100644 --- a/internal/baserow/ontology/load.go +++ b/internal/baserow/ontology/load.go @@ -2,6 +2,7 @@ package ontology import ( "bytes" + "context" "encoding/json" "fmt" "io" @@ -28,6 +29,13 @@ type addTermRowProperties struct { TableId int } +type existTermRowProperties struct { + Term graph.Term + TableId int + Client *client.APIClient + Ctx context.Context +} + func LoadNew(args *LoadProperties) error { rdr, err := os.Open(args.File) if err != nil { @@ -57,6 +65,30 @@ func LoadNew(args *LoadProperties) error { return nil } +func existTermRow(args *existTermRowProperties) (bool, error) { + ok := false + term := args.Term + rows, resp, err := args.Client.DatabaseTableRowsApi. + ListDatabaseTableRows(args.Ctx, int32(args.TableId)). + Size(1). + UserFieldNames(true). + Search(string(term.ID())). + Execute() + if err != nil { + return ok, fmt.Errorf( + "error in checking presence of term %s %s", + string(term.ID()), + err, + ) + } + defer resp.Body.Close() + if rows.Count > 0 { + ok = true + } + + return ok, nil +} + func addTermRow(args *addTermRowProperties) error { term := args.Term payload := map[string]interface{}{ From 9a13a6c3171c5149d26e55d4b2f8fcceb4c84f35 Mon Sep 17 00:00:00 2001 From: Siddhartha Basu Date: Wed, 21 Feb 2024 12:12:33 -0600 Subject: [PATCH 21/75] feat(load.go): add updateTermRow function to update term row The properties for adding and checking the existence of a term row were very similar, so they have been merged into a single struct, termRowProperties, to reduce redundancy and improve code maintainability. A new function, updateTermRow, has been added to allow updating of term rows. This provides more flexibility in managing term rows, as they can now be updated as well as added and checked for existence. --- internal/baserow/ontology/load.go | 40 ++++++++++++++++++++++++------- 1 file changed, 31 insertions(+), 9 deletions(-) diff --git a/internal/baserow/ontology/load.go b/internal/baserow/ontology/load.go index f7bec159..33ece5f6 100644 --- a/internal/baserow/ontology/load.go +++ b/internal/baserow/ontology/load.go @@ -2,7 +2,6 @@ package ontology import ( "bytes" - "context" "encoding/json" "fmt" "io" @@ -22,18 +21,13 @@ type LoadProperties struct { Logger *logrus.Entry } -type addTermRowProperties struct { +type termRowProperties struct { Term graph.Term Host string Token string TableId int } -type existTermRowProperties struct { - Term graph.Term - TableId int - Client *client.APIClient - Ctx context.Context } func LoadNew(args *LoadProperties) error { @@ -84,12 +78,40 @@ func existTermRow(args *existTermRowProperties) (bool, error) { defer resp.Body.Close() if rows.Count > 0 { ok = true +func updateTermRow(args *updateTermRowProperties) error { + term := args.Term + payload := map[string]interface{}{ + "Name": term.Label(), + "Is_obsolete": termStatus(term), } + jsonData, err := json.Marshal(payload) + if err != nil { + return fmt.Errorf("error in encoding body %s", err) + } + req, err := http.NewRequest( + "PATCH", + fmt.Sprintf( + "%s/api/database/rows/table/%d/%d/?user_field_names=true", + args.Host, + args.TableId, + args.RowId, + ), + bytes.NewBuffer(jsonData), + ) + if err != nil { + return fmt.Errorf("error in creating requst %s", err) + } + commonHeader(req, args.Token) + res, err := reqToResponse(req) + if err != nil { + return err + } + defer res.Body.Close() - return ok, nil + return nil } -func addTermRow(args *addTermRowProperties) error { +func existTermRow(args *termRowProperties) (*exisTermRowResp, error) { term := args.Term payload := map[string]interface{}{ "Id": term.ID(), From 36da26c37d10894c1361247b1216602d6ed2891e Mon Sep 17 00:00:00 2001 From: Siddhartha Basu Date: Wed, 21 Feb 2024 12:13:46 -0600 Subject: [PATCH 22/75] feat(load.go): add support for checking if term row exists and is deprecated The new feature allows checking if a term row exists and whether it is deprecated. This is achieved by creating a new request to the database and decoding the response. --- internal/baserow/ontology/load.go | 55 +++++++++++++++++++++++++++++++ 1 file changed, 55 insertions(+) diff --git a/internal/baserow/ontology/load.go b/internal/baserow/ontology/load.go index 33ece5f6..e6f958f2 100644 --- a/internal/baserow/ontology/load.go +++ b/internal/baserow/ontology/load.go @@ -28,9 +28,32 @@ type termRowProperties struct { TableId int } +type updateTermRowProperties struct { + *termRowProperties + RowId int32 } func LoadNew(args *LoadProperties) error { +type exisTermRowResp struct { + Exist bool + IsDeprecated bool + RowId int32 +} + +type ontologyRow struct { + Id int32 `json:"id"` + Order *float64 `json:"order,omitempty"` + TermId string `json:"Id"` + IsObsolete bool `json:"Is_obsolete"` +} + +type ontologyListRows struct { + Count int32 `json:"count"` + Next client.NullableString `json:"next"` + Previous client.NullableString `json:"previous"` + Results []*ontologyRow `json:"results"` +} + rdr, err := os.Open(args.File) if err != nil { return fmt.Errorf("error in opening file %s %s", args.File, err) @@ -112,6 +135,38 @@ func updateTermRow(args *updateTermRowProperties) error { } func existTermRow(args *termRowProperties) (*exisTermRowResp, error) { + term := string(args.Term.ID()) + req, err := http.NewRequest( + "GET", + fmt.Sprintf( + "%s/api/database/rows/table/%d/?user_field_names=true&size=1&search=%s", + args.Host, + args.TableId, + term, + ), nil, + ) + if err != nil { + return nil, fmt.Errorf("error in creating requst %s", err) + } + commonHeader(req, args.Token) + res, err := reqToResponse(req) + if err != nil { + return nil, err + } + defer res.Body.Close() + rowsResp := &ontologyListRows{} + if err := json.NewDecoder(res.Body).Decode(rowsResp); err != nil { + return nil, fmt.Errorf("error in decoding json response %s", err) + } + existResp := &exisTermRowResp{Exist: false} + if rowsResp.Count > 0 { + existResp.Exist = true + existResp.IsDeprecated = rowsResp.Results[0].IsObsolete + existResp.RowId = rowsResp.Results[0].Id + } + return existResp, nil +} + term := args.Term payload := map[string]interface{}{ "Id": term.ID(), From 3a9b4c3f8579dbd1ea6979b1d965e527761d9c8c Mon Sep 17 00:00:00 2001 From: Siddhartha Basu Date: Wed, 21 Feb 2024 12:14:49 -0600 Subject: [PATCH 23/75] feat(load.go): add addTermRow function to add new term rows The existTermRow function was removed as it was redundant and not providing any additional functionality. The addTermRow function was added to allow the addition of new term rows, enhancing the functionality of the application. The term ID is now explicitly converted to a string to ensure correct data type. --- internal/baserow/ontology/load.go | 12 ++---------- 1 file changed, 2 insertions(+), 10 deletions(-) diff --git a/internal/baserow/ontology/load.go b/internal/baserow/ontology/load.go index e6f958f2..522e7395 100644 --- a/internal/baserow/ontology/load.go +++ b/internal/baserow/ontology/load.go @@ -82,15 +82,6 @@ type ontologyListRows struct { return nil } -func existTermRow(args *existTermRowProperties) (bool, error) { - ok := false - term := args.Term - rows, resp, err := args.Client.DatabaseTableRowsApi. - ListDatabaseTableRows(args.Ctx, int32(args.TableId)). - Size(1). - UserFieldNames(true). - Search(string(term.ID())). - Execute() if err != nil { return ok, fmt.Errorf( "error in checking presence of term %s %s", @@ -167,9 +158,10 @@ func existTermRow(args *termRowProperties) (*exisTermRowResp, error) { return existResp, nil } +func addTermRow(args *termRowProperties) error { term := args.Term payload := map[string]interface{}{ - "Id": term.ID(), + "Id": string(term.ID()), "Name": term.Label(), "Is_obsolete": termStatus(term), } From c75f6ac39bccc5579cc02fbdf9a8661038252c1b Mon Sep 17 00:00:00 2001 From: Siddhartha Basu Date: Wed, 21 Feb 2024 12:15:50 -0600 Subject: [PATCH 24/75] refactor(load.go): rename LoadNew to LoadNewOrUpdate and refactor its functionality The LoadNew function was renamed to LoadNewOrUpdate to better reflect its functionality, which now includes updating existing term rows in addition to loading new ones. A new LoadNew function was added to handle only new load properties, improving the separation of concerns in the code. This change makes the code more maintainable and easier to understand. --- internal/baserow/ontology/load.go | 35 ++++++++++++++++++++++++------- 1 file changed, 27 insertions(+), 8 deletions(-) diff --git a/internal/baserow/ontology/load.go b/internal/baserow/ontology/load.go index 522e7395..ef635c11 100644 --- a/internal/baserow/ontology/load.go +++ b/internal/baserow/ontology/load.go @@ -33,7 +33,6 @@ type updateTermRowProperties struct { RowId int32 } -func LoadNew(args *LoadProperties) error { type exisTermRowResp struct { Exist bool IsDeprecated bool @@ -54,6 +53,7 @@ type ontologyListRows struct { Results []*ontologyRow `json:"results"` } +func LoadNewOrUpdate(args *LoadProperties) error { rdr, err := os.Open(args.File) if err != nil { return fmt.Errorf("error in opening file %s %s", args.File, err) @@ -68,7 +68,7 @@ type ontologyListRows struct { ) } for _, term := range grph.Terms() { - err := addTermRow(&addTermRowProperties{ + err := addTermRow(&termRowProperties{ Term: term, Host: args.Client.GetConfig().Host, Token: args.Token, @@ -82,16 +82,35 @@ type ontologyListRows struct { return nil } +func LoadNew(args *LoadProperties) error { + rdr, err := os.Open(args.File) if err != nil { - return ok, fmt.Errorf( - "error in checking presence of term %s %s", - string(term.ID()), + return fmt.Errorf("error in opening file %s %s", args.File, err) + } + defer rdr.Close() + grph, err := graph.BuildGraph(rdr) + if err != nil { + return fmt.Errorf( + "error in building graph from file %s %s", + args.File, err, ) } - defer resp.Body.Close() - if rows.Count > 0 { - ok = true + for _, term := range grph.Terms() { + err := addTermRow(&termRowProperties{ + Term: term, + Host: args.Client.GetConfig().Host, + Token: args.Token, + TableId: args.TableId, + }) + if err != nil { + return err + } + args.Logger.Infof("add row with id %s", term.ID()) + } + return nil +} + func updateTermRow(args *updateTermRowProperties) error { term := args.Term payload := map[string]interface{}{ From 2a29868720e638586ae641d37ecf452b650d37b3 Mon Sep 17 00:00:00 2001 From: Siddhartha Basu Date: Wed, 21 Feb 2024 12:34:02 -0600 Subject: [PATCH 25/75] refactor(load.go): simplify termStatus function call in updateTermRow function The term variable was removed and the termStatus function is now directly called with args.Term. This change simplifies the code and reduces redundancy by removing the unnecessary term variable. --- internal/baserow/ontology/load.go | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/internal/baserow/ontology/load.go b/internal/baserow/ontology/load.go index ef635c11..2c2b35c9 100644 --- a/internal/baserow/ontology/load.go +++ b/internal/baserow/ontology/load.go @@ -112,10 +112,8 @@ func LoadNew(args *LoadProperties) error { } func updateTermRow(args *updateTermRowProperties) error { - term := args.Term payload := map[string]interface{}{ - "Name": term.Label(), - "Is_obsolete": termStatus(term), + "Is_obsolete": termStatus(args.Term), } jsonData, err := json.Marshal(payload) if err != nil { From 43d0498ee5987d6d6b15a6a67f09b1a97c742e41 Mon Sep 17 00:00:00 2001 From: Siddhartha Basu Date: Wed, 21 Feb 2024 13:16:39 -0600 Subject: [PATCH 26/75] feat(load.go): add check for term existence and deprecation status before adding or updating The function LoadNewOrUpdate now checks if a term already exists and whether its deprecation status has changed. If the term exists and its deprecation status is unchanged, the function logs this and continues to the next term. If the term exists but its deprecation status has changed, the function updates the term row and logs this. This change reduces unnecessary database operations and provides more detailed logging. --- internal/baserow/ontology/load.go | 31 ++++++++++++++++++++++++++++++- 1 file changed, 30 insertions(+), 1 deletion(-) diff --git a/internal/baserow/ontology/load.go b/internal/baserow/ontology/load.go index 2c2b35c9..d2abd1b9 100644 --- a/internal/baserow/ontology/load.go +++ b/internal/baserow/ontology/load.go @@ -68,7 +68,36 @@ func LoadNewOrUpdate(args *LoadProperties) error { ) } for _, term := range grph.Terms() { - err := addTermRow(&termRowProperties{ + existResp, err := existTermRow(&termRowProperties{ + Term: term, + Host: args.Client.GetConfig().Host, + Token: args.Token, + TableId: args.TableId, + }) + if err != nil { + return err + } + if existResp.Exist { + if existResp.IsDeprecated == term.IsDeprecated() { + args.Logger.Debugf("term %s has no change", string(term.ID())) + continue + } + err = updateTermRow(&updateTermRowProperties{ + RowId: existResp.RowId, + termRowProperties: &termRowProperties{ + Term: term, + Host: args.Client.GetConfig().Host, + Token: args.Token, + TableId: args.TableId, + }, + }) + if err != nil { + return err + } + args.Logger.Infof("updated row with term %s", string(term.ID())) + continue + } + err = addTermRow(&termRowProperties{ Term: term, Host: args.Client.GetConfig().Host, Token: args.Token, From 3dd1d30897a965bacc251f3c906c161aab7e5b7d Mon Sep 17 00:00:00 2001 From: Siddhartha Basu Date: Wed, 21 Feb 2024 13:29:12 -0600 Subject: [PATCH 27/75] refactor(table.go): modify CreateOntologyTableFields function to return a boolean status along with error The CreateOntologyTableFields function now returns a boolean status along with the error. This change provides more information about the function's execution, specifically whether the fields were created in the table or not. --- internal/baserow/database/table.go | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/internal/baserow/database/table.go b/internal/baserow/database/table.go index e00459eb..48f40234 100644 --- a/internal/baserow/database/table.go +++ b/internal/baserow/database/table.go @@ -63,23 +63,27 @@ func CreateTableField(args *CreateFieldProperties) error { return nil } -func CreateOntologyTableFields(args *OntologyTableFieldsProperties) error { +func CreateOntologyTableFields( + args *OntologyTableFieldsProperties, +) (bool, error) { logger := args.Logger bclient := args.Client authCtx := args.Ctx + ok := false tlist, resp, err := bclient. DatabaseTableFieldsApi. ListDatabaseTableFields(authCtx, int32(args.TableId)). Execute() if err != nil { - return fmt.Errorf("error in getting list of table fields %s", err) + return ok, fmt.Errorf("error in getting list of table fields %s", err) } defer resp.Body.Close() if len(tlist) != 0 { logger.Debug("fields exists in the database table") - return nil + return ok, nil } logger.Debug("need to create fields in the table") + ok = true for field, fieldType := range args.FieldMap { err := CreateTableField(&CreateFieldProperties{ Client: bclient, @@ -89,10 +93,10 @@ func CreateOntologyTableFields(args *OntologyTableFieldsProperties) error { FieldType: fieldType, }) if err != nil { - return err + return ok, err } logger.Infof("created field %s", field) } - return nil + return ok, nil } From 0380405e32062dd729326bf7167f4026d6d67f5c Mon Sep 17 00:00:00 2001 From: Siddhartha Basu Date: Wed, 21 Feb 2024 13:29:33 -0600 Subject: [PATCH 28/75] feat(action.go): add ontology loading functionality to LoadOntologyToTable function The LoadOntologyToTable function now has the ability to load ontology data into the table. This is achieved by adding a new import statement for the ontology package and creating a new LoadProperties object. The function now checks if the ontology table fields were successfully created and if so, it either loads new ontology data or updates existing data. If the table fields were not created, it only loads new data. --- internal/baserow/cli/action.go | 19 ++++++++++++++++++- 1 file changed, 18 insertions(+), 1 deletion(-) diff --git a/internal/baserow/cli/action.go b/internal/baserow/cli/action.go index 7ef37fd9..5e52edc5 100644 --- a/internal/baserow/cli/action.go +++ b/internal/baserow/cli/action.go @@ -6,6 +6,7 @@ import ( "github.com/dictyBase/modware-import/internal/baserow/client" "github.com/dictyBase/modware-import/internal/baserow/database" + "github.com/dictyBase/modware-import/internal/baserow/ontology" "github.com/dictyBase/modware-import/internal/collection" "github.com/dictyBase/modware-import/internal/registry" "github.com/urfave/cli/v2" @@ -93,7 +94,7 @@ func LoadOntologyToTable(cltx *cli.Context) error { "Id": client.TEXT, "Is_obsolete": client.BOOLEAN, } - err := database.CreateOntologyTableFields( + ok, err := database.CreateOntologyTableFields( &database.OntologyTableFieldsProperties{ Client: bclient, Logger: logger, @@ -105,6 +106,22 @@ func LoadOntologyToTable(cltx *cli.Context) error { if err != nil { return cli.Exit(err.Error(), 2) } + props := &ontology.LoadProperties{ + File: cltx.String("input"), + TableId: cltx.Int("table-id"), + Token: cltx.String("token"), + Client: bclient, + Logger: logger, + } + if ok { + if err := ontology.LoadNewOrUpdate(props); err != nil { + return cli.Exit(err.Error(), 2) + } + return nil + } + if err := ontology.LoadNew(props); err != nil { + return cli.Exit(err.Error(), 2) + } return nil } From 7e421a327fa306887f4a6c8bf05ccc7a896182f0 Mon Sep 17 00:00:00 2001 From: Siddhartha Basu Date: Thu, 22 Feb 2024 19:53:40 -0600 Subject: [PATCH 29/75] feat(httpapi): add new httpapi package with common header and request handling functions The new httpapi package includes functions for setting common headers and handling HTTP requests. This centralizes the HTTP request handling logic, making it easier to maintain and update. The CommonHeader function sets standard headers for JSON content and authorization. The ReqToResponse function sends the request and handles any errors, including non-200 status codes. This improves error handling by providing more detailed error messages. --- internal/baserow/httpapi/api.go | 37 +++++++++++++++++++++++++++++++++ 1 file changed, 37 insertions(+) create mode 100644 internal/baserow/httpapi/api.go diff --git a/internal/baserow/httpapi/api.go b/internal/baserow/httpapi/api.go new file mode 100644 index 00000000..e1244c7f --- /dev/null +++ b/internal/baserow/httpapi/api.go @@ -0,0 +1,37 @@ +package httpapi + +import ( + "fmt" + "io" + "net/http" +) + +func CommonHeader(lreq *http.Request, token, format string) { + lreq.Header.Set("Content-Type", "application/json") + lreq.Header.Set("Accept", "application/json") + lreq.Header.Set("Authorization", fmt.Sprintf("%s %s", format, token)) +} + +func ReqToResponse(creq *http.Request) (*http.Response, error) { + client := &http.Client{} + uresp, err := client.Do(creq) + if err != nil { + return uresp, fmt.Errorf("error in making request %s", err) + } + if uresp.StatusCode != 200 { + cnt, err := io.ReadAll(uresp.Body) + if err != nil { + return uresp, fmt.Errorf( + "error in response and the reading the body %d %s", + uresp.StatusCode, + err, + ) + } + return uresp, fmt.Errorf( + "unexpected error response %d %s", + uresp.StatusCode, + string(cnt), + ) + } + return uresp, nil +} From 5e79f30fd861f9646713da9664e5a5cb8a407823 Mon Sep 17 00:00:00 2001 From: Siddhartha Basu Date: Thu, 22 Feb 2024 19:56:39 -0600 Subject: [PATCH 30/75] feat(database): add ontology.go for managing ontology tables The new file ontology.go is added to manage ontology tables. It includes the OntologyTableManager struct and its methods for creating tables, checking all table fields, removing initial fields, and creating fields. --- internal/baserow/database/ontology.go | 128 ++++++++++++++++++++++++++ 1 file changed, 128 insertions(+) create mode 100644 internal/baserow/database/ontology.go diff --git a/internal/baserow/database/ontology.go b/internal/baserow/database/ontology.go new file mode 100644 index 00000000..510a65eb --- /dev/null +++ b/internal/baserow/database/ontology.go @@ -0,0 +1,128 @@ +package database + +import ( + "bytes" + "context" + "encoding/json" + "fmt" + "net/http" + "slices" + + "github.com/dictyBase/modware-import/internal/baserow/client" + "github.com/dictyBase/modware-import/internal/baserow/httpapi" + "github.com/sirupsen/logrus" +) + +type OntologyTableManager struct { + Logger *logrus.Entry + Client *client.APIClient + Ctx context.Context + Token string + DatabaseId int32 +} + +func (ont *OntologyTableManager) CreateTable( + table string, +) (*client.Table, error) { + var row []interface{} + row = append(row, []string{""}) + tbl, resp, err := ont.Client. + DatabaseTablesApi. + CreateDatabaseTable(ont.Ctx, ont.DatabaseId). + TableCreate(client.TableCreate{Name: table, Data: row}). + Execute() + if err != nil { + return tbl, fmt.Errorf( + "error in creating table %s %s", + table, err, + ) + } + defer resp.Body.Close() + + return tbl, nil +} + +func (ont *OntologyTableManager) CheckAllTableFields( + tableId int32, + fields []string, +) (bool, error) { + ok := false + tlist, resp, err := ont.Client. + DatabaseTableFieldsApi. + ListDatabaseTableFields(ont.Ctx, tableId). + Execute() + if err != nil { + return ok, fmt.Errorf( + "error in getting list of table fields %s", + err, + ) + } + defer resp.Body.Close() + existFields := toFieldNames(tlist) + slices.Sort(existFields) + slices.Sort(fields) + return slices.Equal( + fields, + toFieldNames(tlist), + ), nil +} + +func (ont *OntologyTableManager) RemoveInitialFields(tbl *client.Table) error { + return nil +} + +func (ont *OntologyTableManager) CreateFields(tbl *client.Table) error { + reqURL := fmt.Sprintf( + "https://%s/api/database/fields/table/%d/", + ont.Client.GetConfig().Host, + tbl.GetId(), + ) + for _, payload := range fieldDefs() { + jsonData, err := json.Marshal(payload) + if err != nil { + return fmt.Errorf("error in encoding body %s", err) + } + req, err := http.NewRequest("POST", reqURL, bytes.NewBuffer(jsonData)) + if err != nil { + return fmt.Errorf("error in creating request %s ", err) + } + httpapi.CommonHeader(req, ont.Token, "JWT") + res, err := httpapi.ReqToResponse(req) + if err != nil { + return err + } + defer res.Body.Close() + } + + return nil +} + +func fieldInformation(field client.FieldField) (int32, string) { + if field.TextFieldField != nil { + return field.TextFieldField.Id, field.TextFieldField.Name + } + if field.BooleanFieldField != nil { + return field.BooleanFieldField.Id, field.BooleanFieldField.Name + } + if field.LongTextFieldField != nil { + return field.LongTextFieldField.Id, field.LongTextFieldField.Name + } + return field.DateFieldField.Id, *field.DateFieldField.Name +} + +func toFieldNames(fields []client.FieldField) []string { + fieldNames := make([]string, 0) + for _, fld := range fields { + _, name := fieldInformation(fld) + fieldNames = append(fieldNames, name) + } + return fieldNames +} + +func fieldDefs() []map[string]interface{} { + return []map[string]interface{}{ + {"name": "name", "type": "text"}, + {"name": "term_id", "type": "text"}, + {"name": "is_obsolete", "type": "boolean"}, + } +} From cc3b5d7c85fcd817abe93ac38d106d1d0523dd7b Mon Sep 17 00:00:00 2001 From: Siddhartha Basu Date: Thu, 22 Feb 2024 19:58:03 -0600 Subject: [PATCH 31/75] refactor(action.go): streamline table creation and field checking process The changes in action.go streamline the process of creating a table and checking its fields. Instead of creating the table and fields separately, a new OntologyTableManager struct is introduced to handle these operations. This makes the code more maintainable and easier to understand. The CheckAllTableFields method is now used to verify the existence of all required fields in the table, providing a more robust check. The CreateTable method is renamed to CreateOntologyTableHandler for better clarity. --- internal/baserow/cli/action.go | 62 +++++++++++++++++----------------- 1 file changed, 31 insertions(+), 31 deletions(-) diff --git a/internal/baserow/cli/action.go b/internal/baserow/cli/action.go index 5e52edc5..49bf977b 100644 --- a/internal/baserow/cli/action.go +++ b/internal/baserow/cli/action.go @@ -4,13 +4,14 @@ import ( "context" "fmt" + "slices" + "github.com/dictyBase/modware-import/internal/baserow/client" "github.com/dictyBase/modware-import/internal/baserow/database" "github.com/dictyBase/modware-import/internal/baserow/ontology" "github.com/dictyBase/modware-import/internal/collection" "github.com/dictyBase/modware-import/internal/registry" "github.com/urfave/cli/v2" - "golang.org/x/exp/slices" ) func CreateDatabaseToken(cltx *cli.Context) error { @@ -89,23 +90,23 @@ func LoadOntologyToTable(cltx *cli.Context) error { client.ContextDatabaseToken, cltx.String("token"), ) - fields := map[string]client.Type712Enum{ - "Name": client.TEXT, - "Id": client.TEXT, - "Is_obsolete": client.BOOLEAN, + ontTbl := &database.OntologyTableManager{ + Client: bclient, + Logger: logger, + Ctx: authCtx, + DatabaseId: int32(cltx.Int("database-id")), } - ok, err := database.CreateOntologyTableFields( - &database.OntologyTableFieldsProperties{ - Client: bclient, - Logger: logger, - Ctx: authCtx, - FieldMap: fields, - TableId: cltx.Int("table-id"), - }, - ) + fields := make([]string, 0) + for key := range database.TableFieldMap() { + fields = append(fields, key) + } + ok, err := ontTbl.CheckAllTableFields(int32(cltx.Int("table-id")), fields) if err != nil { return cli.Exit(err.Error(), 2) } + if !ok { + return cli.Exit("table %s does not have the required fields", 2) + } props := &ontology.LoadProperties{ File: cltx.String("input"), TableId: cltx.Int("table-id"), @@ -113,20 +114,14 @@ func LoadOntologyToTable(cltx *cli.Context) error { Client: bclient, Logger: logger, } - if ok { - if err := ontology.LoadNewOrUpdate(props); err != nil { - return cli.Exit(err.Error(), 2) - } - return nil - } - if err := ontology.LoadNew(props); err != nil { + if err := ontology.LoadNewOrUpdate(props); err != nil { return cli.Exit(err.Error(), 2) } return nil } -func CreateTable(cltx *cli.Context) error { +func CreateOntologyTableHandler(cltx *cli.Context) error { logger := registry.GetLogger() bclient := database.BaserowClient(cltx.String("server")) authCtx := context.WithValue( @@ -134,17 +129,22 @@ func CreateTable(cltx *cli.Context) error { client.ContextAccessToken, cltx.String("token"), ) - tbl, resp, err := bclient. - DatabaseTablesApi. - CreateDatabaseTable(authCtx, int32(cltx.Int("database-id"))). - TableCreate(client.TableCreate{Name: cltx.String("table")}). - Execute() + ontTbl := &database.OntologyTableManager{ + Client: bclient, + Logger: logger, + Ctx: authCtx, + DatabaseId: int32(cltx.Int("database-id")), + Token: cltx.String("token"), + } + tbl, err := ontTbl.CreateTable(cltx.String("table")) if err != nil { - return cli.Exit( - fmt.Errorf("error in creating table %s", err), 2, - ) + return cli.Exit(err.Error(), 2) } - defer resp.Body.Close() logger.Infof("created table %s", tbl.GetName()) + if err := ontTbl.CreateFields(tbl); err != nil { + return cli.Exit(err.Error(), 2) + } + logger.Infof("created all fields in the ontology table %s", tbl.GetName()) + return nil } From 3b480e2ccadbf5cd87e10a33b0d84208871d794e Mon Sep 17 00:00:00 2001 From: Siddhartha Basu Date: Thu, 22 Feb 2024 20:00:57 -0600 Subject: [PATCH 32/75] feat(ontology.go): add FieldNames function to return field names The FieldNames function was added to return an array of field names. This function will be useful in scenarios where we need to know the names of the fields without having to instantiate a client or access the database. --- internal/baserow/database/ontology.go | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/internal/baserow/database/ontology.go b/internal/baserow/database/ontology.go index 510a65eb..ad3647a0 100644 --- a/internal/baserow/database/ontology.go +++ b/internal/baserow/database/ontology.go @@ -97,6 +97,10 @@ func (ont *OntologyTableManager) CreateFields(tbl *client.Table) error { return nil } +func FieldNames() []string { + return []string{"term_id", "name", "is_obsolete"} +} + func fieldInformation(field client.FieldField) (int32, string) { if field.TextFieldField != nil { return field.TextFieldField.Id, field.TextFieldField.Name From 72813562957314687d9540fbe7f6237b08ae7424 Mon Sep 17 00:00:00 2001 From: Siddhartha Basu Date: Thu, 22 Feb 2024 20:02:30 -0600 Subject: [PATCH 33/75] refactor(flag.go): rename CreateTableFlag to CreateOntologyTableFlag and update usage description The function CreateTableFlag was renamed to CreateOntologyTableFlag to better reflect its purpose. The usage description for the "table" flag was also updated from "Database table" to "Ontology table name" to provide more clarity on what the flag is used for. --- internal/baserow/cli/flag.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/internal/baserow/cli/flag.go b/internal/baserow/cli/flag.go index 167c442f..2b9a593f 100644 --- a/internal/baserow/cli/flag.go +++ b/internal/baserow/cli/flag.go @@ -59,7 +59,7 @@ func LoadOntologyToTableFlag() []cli.Flag { } } -func CreateTableFlag() []cli.Flag { +func CreateOntologyTableFlag() []cli.Flag { return []cli.Flag{ &cli.StringFlag{ Name: "token", @@ -74,7 +74,7 @@ func CreateTableFlag() []cli.Flag { }, &cli.StringFlag{ Name: "table", - Usage: "Database table", + Usage: "Ontology table name", Required: true, }, } From 8d8be352a21d9b0057a4761e4d7eea6eddd3763c Mon Sep 17 00:00:00 2001 From: Siddhartha Basu Date: Thu, 22 Feb 2024 20:03:01 -0600 Subject: [PATCH 34/75] refactor(action.go): replace manual field extraction with FieldNames method The manual extraction of field names from the database's TableFieldMap has been replaced with the FieldNames method. This change simplifies the code and makes it more readable. It also ensures that if the implementation of FieldNames changes in the future, the code here will automatically stay up-to-date. --- internal/baserow/cli/action.go | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/internal/baserow/cli/action.go b/internal/baserow/cli/action.go index 49bf977b..64efac38 100644 --- a/internal/baserow/cli/action.go +++ b/internal/baserow/cli/action.go @@ -96,11 +96,10 @@ func LoadOntologyToTable(cltx *cli.Context) error { Ctx: authCtx, DatabaseId: int32(cltx.Int("database-id")), } - fields := make([]string, 0) - for key := range database.TableFieldMap() { - fields = append(fields, key) - } - ok, err := ontTbl.CheckAllTableFields(int32(cltx.Int("table-id")), fields) + ok, err := ontTbl.CheckAllTableFields( + int32(cltx.Int("table-id")), + database.FieldNames(), + ) if err != nil { return cli.Exit(err.Error(), 2) } From 4f6d3a3d168c7f8eab174c54b58698cdeb7200d7 Mon Sep 17 00:00:00 2001 From: Siddhartha Basu Date: Thu, 22 Feb 2024 20:03:17 -0600 Subject: [PATCH 35/75] refactor(action.go): rearrange the order of struct fields for better readability The order of struct fields in the CreateOntologyTableHandler function has been rearranged to improve readability. The 'Token' field is now placed before the 'DatabaseId' field, which makes the code more organized and easier to understand. --- internal/baserow/cli/action.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/internal/baserow/cli/action.go b/internal/baserow/cli/action.go index 64efac38..228d6d98 100644 --- a/internal/baserow/cli/action.go +++ b/internal/baserow/cli/action.go @@ -132,8 +132,8 @@ func CreateOntologyTableHandler(cltx *cli.Context) error { Client: bclient, Logger: logger, Ctx: authCtx, + Token: cltx.String("token"), DatabaseId: int32(cltx.Int("database-id")), - Token: cltx.String("token"), } tbl, err := ontTbl.CreateTable(cltx.String("table")) if err != nil { From 2e5a8ada7995fe4ce5366639b4227b79d6740a70 Mon Sep 17 00:00:00 2001 From: Siddhartha Basu Date: Thu, 22 Feb 2024 20:04:04 -0600 Subject: [PATCH 36/75] refactor(main.go): rename 'create-table' command to 'create-ontology-table' and update its functionality The 'create-table' command has been renamed to 'create-ontology-table' to better reflect its purpose. The command now creates a table with ontology fields preset, which is more specific and useful for our application's needs. The associated flags and action handler have also been updated to match the new functionality. --- cmd/baserow/main.go | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/cmd/baserow/main.go b/cmd/baserow/main.go index ae229875..1fa5dade 100644 --- a/cmd/baserow/main.go +++ b/cmd/baserow/main.go @@ -52,10 +52,10 @@ func main() { Action: baserow.CreateAccessToken, }, { - Name: "create-table", - Usage: "Create a baserow database table", - Flags: baserow.CreateTableFlag(), - Action: baserow.CreateTable, + Name: "create-ontology-table", + Usage: "Create a baserow table with ontology fields preset", + Flags: baserow.CreateOntologyTableFlag(), + Action: baserow.CreateOntologyTableHandler, }, { Name: "load-ontology", From 57d75417e2cf1dae578987f1e33aeaf1a43273ee Mon Sep 17 00:00:00 2001 From: Siddhartha Basu Date: Thu, 22 Feb 2024 20:04:40 -0600 Subject: [PATCH 37/75] remove(table.go): delete the table.go file from the baserow/database directory The table.go file was removed because it was no longer needed in the project. The functionality provided by this file has been replaced by a more efficient and streamlined solution, reducing the complexity of the codebase and improving maintainability. --- internal/baserow/database/table.go | 102 ----------------------------- 1 file changed, 102 deletions(-) delete mode 100644 internal/baserow/database/table.go diff --git a/internal/baserow/database/table.go b/internal/baserow/database/table.go deleted file mode 100644 index 48f40234..00000000 --- a/internal/baserow/database/table.go +++ /dev/null @@ -1,102 +0,0 @@ -package database - -import ( - "context" - "fmt" - - "github.com/dictyBase/modware-import/internal/baserow/client" - "github.com/sirupsen/logrus" -) - -type fieldFn func(string) client.FieldCreateField - -type CreateFieldProperties struct { - Client *client.APIClient - Ctx context.Context - TableId int - Field string - FieldType client.Type712Enum -} - -type OntologyTableFieldsProperties struct { - Client *client.APIClient - Ctx context.Context - Logger *logrus.Entry - TableId int - FieldMap map[string]client.Type712Enum -} - -func MapFieldTypeToFn() map[client.Type712Enum]fieldFn { - fieldFnMap := make(map[client.Type712Enum]fieldFn) - fieldFnMap[client.BOOLEAN] = func(field string) client.FieldCreateField { - return client.FieldCreateField{ - BooleanFieldCreateField: client.NewBooleanFieldCreateField( - field, - client.BOOLEAN, - )} - } - fieldFnMap[client.TEXT] = func(field string) client.FieldCreateField { - return client.FieldCreateField{ - TextFieldCreateField: client.NewTextFieldCreateField( - field, - client.TEXT, - )} - } - return fieldFnMap -} - -func CreateTableField(args *CreateFieldProperties) error { - mapper := MapFieldTypeToFn() - if _, ok := mapper[args.FieldType]; !ok { - return fmt.Errorf("cannot find field type %s", args.FieldType) - } - createFn := mapper[args.FieldType] - _, resp, err := args.Client. - DatabaseTableFieldsApi. - CreateDatabaseTableField(args.Ctx, int32(args.TableId)). - FieldCreateField(createFn(args.Field)). - Execute() - if err != nil { - return fmt.Errorf("error in creating field %s %s", args.Field, err) - } - defer resp.Body.Close() - return nil -} - -func CreateOntologyTableFields( - args *OntologyTableFieldsProperties, -) (bool, error) { - logger := args.Logger - bclient := args.Client - authCtx := args.Ctx - ok := false - tlist, resp, err := bclient. - DatabaseTableFieldsApi. - ListDatabaseTableFields(authCtx, int32(args.TableId)). - Execute() - if err != nil { - return ok, fmt.Errorf("error in getting list of table fields %s", err) - } - defer resp.Body.Close() - if len(tlist) != 0 { - logger.Debug("fields exists in the database table") - return ok, nil - } - logger.Debug("need to create fields in the table") - ok = true - for field, fieldType := range args.FieldMap { - err := CreateTableField(&CreateFieldProperties{ - Client: bclient, - Ctx: authCtx, - TableId: args.TableId, - Field: field, - FieldType: fieldType, - }) - if err != nil { - return ok, err - } - logger.Infof("created field %s", field) - } - - return ok, nil -} From 4e56951433e4ee1ade1c428ec869b0369275d0fc Mon Sep 17 00:00:00 2001 From: Siddhartha Basu Date: Thu, 22 Feb 2024 20:08:26 -0600 Subject: [PATCH 38/75] refactor(load.go): move commonHeader and reqToResponse functions to httpapi package The commonHeader and reqToResponse functions were moved to the httpapi package to improve code organization and reusability. These functions are not specific to the ontology loading process and can be used in other parts of the application that interact with HTTP APIs. --- internal/baserow/ontology/load.go | 44 +++++-------------------------- 1 file changed, 7 insertions(+), 37 deletions(-) diff --git a/internal/baserow/ontology/load.go b/internal/baserow/ontology/load.go index d2abd1b9..217369d0 100644 --- a/internal/baserow/ontology/load.go +++ b/internal/baserow/ontology/load.go @@ -4,12 +4,12 @@ import ( "bytes" "encoding/json" "fmt" - "io" "net/http" "os" "github.com/dictyBase/go-obograph/graph" "github.com/dictyBase/modware-import/internal/baserow/client" + "github.com/dictyBase/modware-import/internal/baserow/httpapi" "github.com/sirupsen/logrus" ) @@ -161,8 +161,8 @@ func updateTermRow(args *updateTermRowProperties) error { if err != nil { return fmt.Errorf("error in creating requst %s", err) } - commonHeader(req, args.Token) - res, err := reqToResponse(req) + httpapi.CommonHeader(req, args.Token, "Bearer") + res, err := httpapi.ReqToResponse(req) if err != nil { return err } @@ -185,8 +185,8 @@ func existTermRow(args *termRowProperties) (*exisTermRowResp, error) { if err != nil { return nil, fmt.Errorf("error in creating requst %s", err) } - commonHeader(req, args.Token) - res, err := reqToResponse(req) + httpapi.CommonHeader(req, args.Token, "Bearer") + res, err := httpapi.ReqToResponse(req) if err != nil { return nil, err } @@ -227,8 +227,8 @@ func addTermRow(args *termRowProperties) error { if err != nil { return fmt.Errorf("error in creating request %s ", err) } - commonHeader(req, args.Token) - res, err := reqToResponse(req) + httpapi.CommonHeader(req, args.Token, "Bearer") + res, err := httpapi.ReqToResponse(req) if err != nil { return err } @@ -237,36 +237,6 @@ func addTermRow(args *termRowProperties) error { return nil } -func reqToResponse(creq *http.Request) (*http.Response, error) { - client := &http.Client{} - uresp, err := client.Do(creq) - if err != nil { - return uresp, fmt.Errorf("error in making request %s", err) - } - if uresp.StatusCode != 200 { - cnt, err := io.ReadAll(uresp.Body) - if err != nil { - return uresp, fmt.Errorf( - "error in response and the reading the body %d %s", - uresp.StatusCode, - err, - ) - } - return uresp, fmt.Errorf( - "unexpected error response %d %s", - uresp.StatusCode, - string(cnt), - ) - } - return uresp, nil -} - -func commonHeader(lreq *http.Request, token string) { - lreq.Header.Set("Content-Type", "application/json") - lreq.Header.Set("Accept", "application/json") - lreq.Header.Set("Authorization", fmt.Sprintf("Bearer %s", token)) -} - func termStatus(term graph.Term) string { if term.IsDeprecated() { return "true" From e7c5aaef5904a12f4dcd1d560076b841353aac5e Mon Sep 17 00:00:00 2001 From: Siddhartha Basu Date: Fri, 23 Feb 2024 09:24:45 -0600 Subject: [PATCH 39/75] refactor(ontology.go): replace API call with direct HTTP request for checking table fields The API call for checking table fields in the OntologyTableManager has been replaced with a direct HTTP request. This change was made to improve error handling and provide more detailed error messages. The new implementation also includes a JSON decoder for the response body, which simplifies the process of extracting the required data. The refactored code is more verbose but provides better control over the HTTP request and response handling. --- internal/baserow/database/ontology.go | 49 ++++++++++++++++++--------- 1 file changed, 33 insertions(+), 16 deletions(-) diff --git a/internal/baserow/database/ontology.go b/internal/baserow/database/ontology.go index ad3647a0..106b62bc 100644 --- a/internal/baserow/database/ontology.go +++ b/internal/baserow/database/ontology.go @@ -10,6 +10,7 @@ import ( "github.com/dictyBase/modware-import/internal/baserow/client" "github.com/dictyBase/modware-import/internal/baserow/httpapi" + "github.com/dictyBase/modware-import/internal/collection" "github.com/sirupsen/logrus" ) @@ -21,6 +22,10 @@ type OntologyTableManager struct { DatabaseId int32 } +type tableFieldsResponse struct { + Name string `json:"name"` +} + func (ont *OntologyTableManager) CreateTable( table string, ) (*client.Table, error) { @@ -47,24 +52,36 @@ func (ont *OntologyTableManager) CheckAllTableFields( fields []string, ) (bool, error) { ok := false - tlist, resp, err := ont.Client. - DatabaseTableFieldsApi. - ListDatabaseTableFields(ont.Ctx, tableId). - Execute() + reqURL := fmt.Sprintf( + "https://%s/api/database/fields/table/%d/", + ont.Client.GetConfig().Host, + tableId, + ) + req, err := http.NewRequest("GET", reqURL, nil) if err != nil { - return ok, fmt.Errorf( - "error in getting list of table fields %s", - err, - ) + return ok, fmt.Errorf("error in creating request %s ", err) } - defer resp.Body.Close() - existFields := toFieldNames(tlist) - slices.Sort(existFields) - slices.Sort(fields) - return slices.Equal( - fields, - toFieldNames(tlist), - ), nil + httpapi.CommonHeader(req, ont.Token, "Token") + res, err := httpapi.ReqToResponse(req) + if err != nil { + return ok, err + } + defer res.Body.Close() + existing := make([]tableFieldsResponse, 0) + if err := json.NewDecoder(res.Body).Decode(&existing); err != nil { + return ok, fmt.Errorf("error in decoding response %s", err) + } + exFields := collection.Map( + existing, + func(input tableFieldsResponse) string { return input.Name }, + ) + for _, fld := range fields { + if num := slices.Index(exFields, fld); num == -1 { + return ok, nil + } + } + + return true, nil } func (ont *OntologyTableManager) RemoveInitialFields(tbl *client.Table) error { From 5ee39dafaa89be461d4108f7f7c3819d4d14e6fd Mon Sep 17 00:00:00 2001 From: Siddhartha Basu Date: Fri, 23 Feb 2024 09:24:59 -0600 Subject: [PATCH 40/75] refactor(action.go): add token field to LoadOntologyToTable function The token field was added to the LoadOntologyToTable function to allow for secure access to the database. This ensures that only authorized users can load ontology to the table, enhancing the security of the application. --- internal/baserow/cli/action.go | 1 + 1 file changed, 1 insertion(+) diff --git a/internal/baserow/cli/action.go b/internal/baserow/cli/action.go index 228d6d98..0ef33eae 100644 --- a/internal/baserow/cli/action.go +++ b/internal/baserow/cli/action.go @@ -94,6 +94,7 @@ func LoadOntologyToTable(cltx *cli.Context) error { Client: bclient, Logger: logger, Ctx: authCtx, + Token: cltx.String("token"), DatabaseId: int32(cltx.Int("database-id")), } ok, err := ontTbl.CheckAllTableFields( From c0bcd900814a006909fb7682950d091dd0cbee1d Mon Sep 17 00:00:00 2001 From: Siddhartha Basu Date: Fri, 23 Feb 2024 09:31:16 -0600 Subject: [PATCH 41/75] fix(action.go): remove table name from error message in LoadOntologyToTable function The table name was removed from the error message because it was not being populated correctly. This change prevents potential confusion for users by avoiding the display of an incomplete error message. --- internal/baserow/cli/action.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/internal/baserow/cli/action.go b/internal/baserow/cli/action.go index 0ef33eae..29824923 100644 --- a/internal/baserow/cli/action.go +++ b/internal/baserow/cli/action.go @@ -105,7 +105,7 @@ func LoadOntologyToTable(cltx *cli.Context) error { return cli.Exit(err.Error(), 2) } if !ok { - return cli.Exit("table %s does not have the required fields", 2) + return cli.Exit("table does not have the required fields", 2) } props := &ontology.LoadProperties{ File: cltx.String("input"), From 1cb579b3c263be72efd6f5a13cf5deacd59df134 Mon Sep 17 00:00:00 2001 From: Siddhartha Basu Date: Fri, 23 Feb 2024 09:40:12 -0600 Subject: [PATCH 42/75] refactor(load.go): update ontologyRow struct to improve data representation The ontologyRow struct has been updated to better represent the data it holds. The 'Order' field was removed as it was not necessary. The 'TermId' field was renamed from 'Id' to 'term_id' to avoid confusion with the 'Id' field. A new field 'Name' was added to store the name of the ontology term. The 'IsObsolete' field was renamed from 'Is_obsolete' to 'is_obsolete' to maintain consistency in naming conventions. --- internal/baserow/ontology/load.go | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/internal/baserow/ontology/load.go b/internal/baserow/ontology/load.go index 217369d0..c8000773 100644 --- a/internal/baserow/ontology/load.go +++ b/internal/baserow/ontology/load.go @@ -40,10 +40,10 @@ type exisTermRowResp struct { } type ontologyRow struct { - Id int32 `json:"id"` - Order *float64 `json:"order,omitempty"` - TermId string `json:"Id"` - IsObsolete bool `json:"Is_obsolete"` + Id int32 `json:"id"` + TermId string `json:"term_id"` + Name string `json:"name"` + IsObsolete bool `json:"is_obsolete"` } type ontologyListRows struct { From 01494ab2d5c1b34b61d3e9e65b08f588d3c7e585 Mon Sep 17 00:00:00 2001 From: Siddhartha Basu Date: Fri, 23 Feb 2024 09:40:51 -0600 Subject: [PATCH 43/75] fix(load.go): change authentication type from Bearer to Token in CommonHeader function calls The authentication type in the CommonHeader function calls was changed from "Bearer" to "Token". This was done to align with the updated authentication scheme of the API we are interacting with, which now requires a Token-based authentication instead of a Bearer token. --- internal/baserow/ontology/load.go | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/internal/baserow/ontology/load.go b/internal/baserow/ontology/load.go index c8000773..8f9d6342 100644 --- a/internal/baserow/ontology/load.go +++ b/internal/baserow/ontology/load.go @@ -161,7 +161,7 @@ func updateTermRow(args *updateTermRowProperties) error { if err != nil { return fmt.Errorf("error in creating requst %s", err) } - httpapi.CommonHeader(req, args.Token, "Bearer") + httpapi.CommonHeader(req, args.Token, "Token") res, err := httpapi.ReqToResponse(req) if err != nil { return err @@ -185,7 +185,7 @@ func existTermRow(args *termRowProperties) (*exisTermRowResp, error) { if err != nil { return nil, fmt.Errorf("error in creating requst %s", err) } - httpapi.CommonHeader(req, args.Token, "Bearer") + httpapi.CommonHeader(req, args.Token, "Token") res, err := httpapi.ReqToResponse(req) if err != nil { return nil, err @@ -227,7 +227,7 @@ func addTermRow(args *termRowProperties) error { if err != nil { return fmt.Errorf("error in creating request %s ", err) } - httpapi.CommonHeader(req, args.Token, "Bearer") + httpapi.CommonHeader(req, args.Token, "Token") res, err := httpapi.ReqToResponse(req) if err != nil { return err From e9d262579388ebac180155e0c1d42c09ea65dca9 Mon Sep 17 00:00:00 2001 From: Siddhartha Basu Date: Fri, 23 Feb 2024 09:41:37 -0600 Subject: [PATCH 44/75] fix(load.go): ensure secure HTTPS protocol for API requests The API requests were previously using an insecure protocol. This change ensures that all API requests are made over HTTPS, enhancing the security of the application by encrypting the data in transit. --- internal/baserow/ontology/load.go | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/internal/baserow/ontology/load.go b/internal/baserow/ontology/load.go index 8f9d6342..5ad1e5dd 100644 --- a/internal/baserow/ontology/load.go +++ b/internal/baserow/ontology/load.go @@ -151,7 +151,7 @@ func updateTermRow(args *updateTermRowProperties) error { req, err := http.NewRequest( "PATCH", fmt.Sprintf( - "%s/api/database/rows/table/%d/%d/?user_field_names=true", + "https://%s/api/database/rows/table/%d/%d/?user_field_names=true", args.Host, args.TableId, args.RowId, @@ -176,7 +176,7 @@ func existTermRow(args *termRowProperties) (*exisTermRowResp, error) { req, err := http.NewRequest( "GET", fmt.Sprintf( - "%s/api/database/rows/table/%d/?user_field_names=true&size=1&search=%s", + "https://%s/api/database/rows/table/%d/?user_field_names=true&size=1&search=%s", args.Host, args.TableId, term, @@ -218,7 +218,7 @@ func addTermRow(args *termRowProperties) error { req, err := http.NewRequest( "POST", fmt.Sprintf( - "%s/api/database/rows/table/%d/?user_field_names=true", + "https://%s/api/database/rows/table/%d/?user_field_names=true", args.Host, args.TableId, ), From a0da8a55c5e6a8247fb5f720cac47c4b5308ea69 Mon Sep 17 00:00:00 2001 From: Siddhartha Basu Date: Fri, 23 Feb 2024 09:41:59 -0600 Subject: [PATCH 45/75] refactor(load.go): change payload keys to snake_case for consistency The payload keys in the updateTermRow and addTermRow functions have been changed from PascalCase and camelCase to snake_case. This change was made to ensure consistency across the codebase as snake_case is the preferred style for JSON keys in this project. --- internal/baserow/ontology/load.go | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/internal/baserow/ontology/load.go b/internal/baserow/ontology/load.go index 5ad1e5dd..eb20cf03 100644 --- a/internal/baserow/ontology/load.go +++ b/internal/baserow/ontology/load.go @@ -142,7 +142,7 @@ func LoadNew(args *LoadProperties) error { func updateTermRow(args *updateTermRowProperties) error { payload := map[string]interface{}{ - "Is_obsolete": termStatus(args.Term), + "is_obsolete": termStatus(args.Term), } jsonData, err := json.Marshal(payload) if err != nil { @@ -207,9 +207,9 @@ func existTermRow(args *termRowProperties) (*exisTermRowResp, error) { func addTermRow(args *termRowProperties) error { term := args.Term payload := map[string]interface{}{ - "Id": string(term.ID()), - "Name": term.Label(), - "Is_obsolete": termStatus(term), + "term_id": string(term.ID()), + "name": term.Label(), + "is_obsolete": termStatus(term), } jsonData, err := json.Marshal(payload) if err != nil { From 0fadc36393da6b2666e7610edd6451e9a521ec87 Mon Sep 17 00:00:00 2001 From: Siddhartha Basu Date: Fri, 23 Feb 2024 11:07:20 -0600 Subject: [PATCH 46/75] feat(database): add TableManager struct and CreateTable method in table.go A new file, table.go, has been added to the database package. This file introduces a new struct, TableManager, which includes a logger, API client, context, token, and database ID. A new method, CreateTable, has also been added to the TableManager struct. --- internal/baserow/database/table.go | 38 ++++++++++++++++++++++++++++++ 1 file changed, 38 insertions(+) create mode 100644 internal/baserow/database/table.go diff --git a/internal/baserow/database/table.go b/internal/baserow/database/table.go new file mode 100644 index 00000000..91c5cfb1 --- /dev/null +++ b/internal/baserow/database/table.go @@ -0,0 +1,38 @@ +package database + +import ( + "context" + "fmt" + + "github.com/dictyBase/modware-import/internal/baserow/client" + "github.com/sirupsen/logrus" +) + +type TableManager struct { + Logger *logrus.Entry + Client *client.APIClient + Ctx context.Context + Token string + DatabaseId int32 +} + +func (tbm *TableManager) CreateTable( + table string, +) (*client.Table, error) { + var row []interface{} + row = append(row, []string{""}) + tbl, resp, err := tbm.Client. + DatabaseTablesApi. + CreateDatabaseTable(tbm.Ctx, tbm.DatabaseId). + TableCreate(client.TableCreate{Name: table, Data: row}). + Execute() + if err != nil { + return tbl, fmt.Errorf( + "error in creating table %s %s", + table, err, + ) + } + defer resp.Body.Close() + + return tbl, nil +} From 58342b56a47548a37c81b1fe091b79d86fba7edd Mon Sep 17 00:00:00 2001 From: Siddhartha Basu Date: Fri, 23 Feb 2024 11:07:36 -0600 Subject: [PATCH 47/75] refactor(ontology.go): simplify OntologyTableManager struct and remove unused functions The OntologyTableManager struct has been simplified by embedding the TableManager struct, removing the need to duplicate fields. The CreateTable function has been removed as it was not being used. The fieldInformation and toFieldNames functions have also been removed as they were not being used. The fieldDefs function has been renamed to FieldDefs and made public as it is now being used in other parts of the code. --- internal/baserow/database/ontology.go | 102 +++++++------------------- 1 file changed, 25 insertions(+), 77 deletions(-) diff --git a/internal/baserow/database/ontology.go b/internal/baserow/database/ontology.go index 106b62bc..98435d0b 100644 --- a/internal/baserow/database/ontology.go +++ b/internal/baserow/database/ontology.go @@ -2,49 +2,49 @@ package database import ( "bytes" - "context" "encoding/json" "fmt" "net/http" + "slices" "github.com/dictyBase/modware-import/internal/baserow/client" "github.com/dictyBase/modware-import/internal/baserow/httpapi" "github.com/dictyBase/modware-import/internal/collection" - "github.com/sirupsen/logrus" ) type OntologyTableManager struct { - Logger *logrus.Entry - Client *client.APIClient - Ctx context.Context - Token string - DatabaseId int32 + *TableManager } type tableFieldsResponse struct { Name string `json:"name"` } -func (ont *OntologyTableManager) CreateTable( - table string, -) (*client.Table, error) { - var row []interface{} - row = append(row, []string{""}) - tbl, resp, err := ont.Client. - DatabaseTablesApi. - CreateDatabaseTable(ont.Ctx, ont.DatabaseId). - TableCreate(client.TableCreate{Name: table, Data: row}). - Execute() - if err != nil { - return tbl, fmt.Errorf( - "error in creating table %s %s", - table, err, - ) +func (ont *OntologyTableManager) CreateFields(tbl *client.Table) error { + reqURL := fmt.Sprintf( + "https://%s/api/database/fields/table/%d/", + ont.Client.GetConfig().Host, + tbl.GetId(), + ) + for _, payload := range FieldDefs() { + jsonData, err := json.Marshal(payload) + if err != nil { + return fmt.Errorf("error in encoding body %s", err) + } + req, err := http.NewRequest("POST", reqURL, bytes.NewBuffer(jsonData)) + if err != nil { + return fmt.Errorf("error in creating request %s ", err) + } + httpapi.CommonHeader(req, ont.Token, "JWT") + res, err := httpapi.ReqToResponse(req) + if err != nil { + return err + } + defer res.Body.Close() } - defer resp.Body.Close() - return tbl, nil + return nil } func (ont *OntologyTableManager) CheckAllTableFields( @@ -84,63 +84,11 @@ func (ont *OntologyTableManager) CheckAllTableFields( return true, nil } -func (ont *OntologyTableManager) RemoveInitialFields(tbl *client.Table) error { - return nil -} - -func (ont *OntologyTableManager) CreateFields(tbl *client.Table) error { - reqURL := fmt.Sprintf( - "https://%s/api/database/fields/table/%d/", - ont.Client.GetConfig().Host, - tbl.GetId(), - ) - for _, payload := range fieldDefs() { - jsonData, err := json.Marshal(payload) - if err != nil { - return fmt.Errorf("error in encoding body %s", err) - } - req, err := http.NewRequest("POST", reqURL, bytes.NewBuffer(jsonData)) - if err != nil { - return fmt.Errorf("error in creating request %s ", err) - } - httpapi.CommonHeader(req, ont.Token, "JWT") - res, err := httpapi.ReqToResponse(req) - if err != nil { - return err - } - defer res.Body.Close() - } - - return nil -} - func FieldNames() []string { return []string{"term_id", "name", "is_obsolete"} } -func fieldInformation(field client.FieldField) (int32, string) { - if field.TextFieldField != nil { - return field.TextFieldField.Id, field.TextFieldField.Name - } - if field.BooleanFieldField != nil { - return field.BooleanFieldField.Id, field.BooleanFieldField.Name - } - if field.LongTextFieldField != nil { - return field.LongTextFieldField.Id, field.LongTextFieldField.Name - } - return field.DateFieldField.Id, *field.DateFieldField.Name -} - -func toFieldNames(fields []client.FieldField) []string { - fieldNames := make([]string, 0) - for _, fld := range fields { - _, name := fieldInformation(fld) - fieldNames = append(fieldNames, name) - } - return fieldNames -} - -func fieldDefs() []map[string]interface{} { +func FieldDefs() []map[string]interface{} { return []map[string]interface{}{ {"name": "name", "type": "text"}, {"name": "term_id", "type": "text"}, From a3fa318293dceb7a641e510844fc0cb2a168db01 Mon Sep 17 00:00:00 2001 From: Siddhartha Basu Date: Fri, 23 Feb 2024 11:11:08 -0600 Subject: [PATCH 48/75] refactor(ontology.go): replace tableId and fields parameters with a single table object in CheckAllTableFields function The CheckAllTableFields function now takes a single table object as a parameter instead of separate tableId and fields parameters. This change simplifies the function signature and makes the code more readable. --- internal/baserow/database/ontology.go | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/internal/baserow/database/ontology.go b/internal/baserow/database/ontology.go index 98435d0b..8bd58cb6 100644 --- a/internal/baserow/database/ontology.go +++ b/internal/baserow/database/ontology.go @@ -48,14 +48,13 @@ func (ont *OntologyTableManager) CreateFields(tbl *client.Table) error { } func (ont *OntologyTableManager) CheckAllTableFields( - tableId int32, - fields []string, + tbl *client.Table, ) (bool, error) { ok := false reqURL := fmt.Sprintf( "https://%s/api/database/fields/table/%d/", ont.Client.GetConfig().Host, - tableId, + tbl.GetId(), ) req, err := http.NewRequest("GET", reqURL, nil) if err != nil { @@ -75,7 +74,7 @@ func (ont *OntologyTableManager) CheckAllTableFields( existing, func(input tableFieldsResponse) string { return input.Name }, ) - for _, fld := range fields { + for _, fld := range FieldNames() { if num := slices.Index(exFields, fld); num == -1 { return ok, nil } From 6827e1c3d12e85b7115471a8f6a926305030782a Mon Sep 17 00:00:00 2001 From: Siddhartha Basu Date: Fri, 23 Feb 2024 11:23:28 -0600 Subject: [PATCH 49/75] refactor(action.go): encapsulate common fields into TableManager struct for better code organization The common fields (Client, Logger, Ctx, Token, DatabaseId) in OntologyTableManager are now encapsulated into a new struct called TableManager. --- internal/baserow/cli/action.go | 27 +++++++++++++++------------ 1 file changed, 15 insertions(+), 12 deletions(-) diff --git a/internal/baserow/cli/action.go b/internal/baserow/cli/action.go index 29824923..a6dd9ee2 100644 --- a/internal/baserow/cli/action.go +++ b/internal/baserow/cli/action.go @@ -91,15 +91,16 @@ func LoadOntologyToTable(cltx *cli.Context) error { cltx.String("token"), ) ontTbl := &database.OntologyTableManager{ - Client: bclient, - Logger: logger, - Ctx: authCtx, - Token: cltx.String("token"), - DatabaseId: int32(cltx.Int("database-id")), + TableManager: &database.TableManager{ + Client: bclient, + Logger: logger, + Ctx: authCtx, + Token: cltx.String("token"), + DatabaseId: int32(cltx.Int("database-id")), + }, } ok, err := ontTbl.CheckAllTableFields( - int32(cltx.Int("table-id")), - database.FieldNames(), + &client.Table{Id: int32(cltx.Int("table-id"))}, ) if err != nil { return cli.Exit(err.Error(), 2) @@ -130,11 +131,13 @@ func CreateOntologyTableHandler(cltx *cli.Context) error { cltx.String("token"), ) ontTbl := &database.OntologyTableManager{ - Client: bclient, - Logger: logger, - Ctx: authCtx, - Token: cltx.String("token"), - DatabaseId: int32(cltx.Int("database-id")), + TableManager: &database.TableManager{ + Client: bclient, + Logger: logger, + Ctx: authCtx, + Token: cltx.String("token"), + DatabaseId: int32(cltx.Int("database-id")), + }, } tbl, err := ontTbl.CreateTable(cltx.String("table")) if err != nil { From 45c41bdc1006ba6c653747ac4b5442330952edab Mon Sep 17 00:00:00 2001 From: Siddhartha Basu Date: Fri, 23 Feb 2024 11:29:28 -0600 Subject: [PATCH 50/75] feat(table.go): add TableFieldsResp function to fetch table fields The TableFieldsResp function has been added to the TableManager struct. This function is responsible for making a GET request to the Baserow API to fetch the fields of a specific table. This feature is necessary for further processing or manipulation of the table fields. --- internal/baserow/database/table.go | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/internal/baserow/database/table.go b/internal/baserow/database/table.go index 91c5cfb1..20076542 100644 --- a/internal/baserow/database/table.go +++ b/internal/baserow/database/table.go @@ -3,8 +3,10 @@ package database import ( "context" "fmt" + "net/http" "github.com/dictyBase/modware-import/internal/baserow/client" + "github.com/dictyBase/modware-import/internal/baserow/httpapi" "github.com/sirupsen/logrus" ) @@ -36,3 +38,19 @@ func (tbm *TableManager) CreateTable( return tbl, nil } + +func (tbm *TableManager) TableFieldsResp( + tbl *client.Table, +) (*http.Response, error) { + reqURL := fmt.Sprintf( + "https://%s/api/database/fields/table/%d/", + tbm.Client.GetConfig().Host, + tbl.GetId(), + ) + req, err := http.NewRequest("GET", reqURL, nil) + if err != nil { + return nil, fmt.Errorf("error in creating request %s ", err) + } + httpapi.CommonHeader(req, tbm.Token, "Token") + return httpapi.ReqToResponse(req) +} From ed82f1aaa608b6a05633c804d79206fc371afd74 Mon Sep 17 00:00:00 2001 From: Siddhartha Basu Date: Fri, 23 Feb 2024 11:30:53 -0600 Subject: [PATCH 51/75] refactor(ontology.go): extract HTTP request creation and execution to a separate method The HTTP request creation and execution logic was extracted to a separate method named TableFieldsResp. This change was made to improve code readability and maintainability. Now, the CheckAllTableFields method is more focused on its main responsibility, and the HTTP request handling is abstracted away. This also makes the code more DRY (Don't Repeat Yourself), as the HTTP request logic can be reused elsewhere if needed. --- internal/baserow/database/ontology.go | 12 +----------- 1 file changed, 1 insertion(+), 11 deletions(-) diff --git a/internal/baserow/database/ontology.go b/internal/baserow/database/ontology.go index 8bd58cb6..28c01874 100644 --- a/internal/baserow/database/ontology.go +++ b/internal/baserow/database/ontology.go @@ -51,17 +51,7 @@ func (ont *OntologyTableManager) CheckAllTableFields( tbl *client.Table, ) (bool, error) { ok := false - reqURL := fmt.Sprintf( - "https://%s/api/database/fields/table/%d/", - ont.Client.GetConfig().Host, - tbl.GetId(), - ) - req, err := http.NewRequest("GET", reqURL, nil) - if err != nil { - return ok, fmt.Errorf("error in creating request %s ", err) - } - httpapi.CommonHeader(req, ont.Token, "Token") - res, err := httpapi.ReqToResponse(req) + res, err := ont.TableFieldsResp(tbl) if err != nil { return ok, err } From 0743abe6f01ff8e5029c7142c80b4af68c2d99aa Mon Sep 17 00:00:00 2001 From: Siddhartha Basu Date: Fri, 23 Feb 2024 15:51:19 -0600 Subject: [PATCH 52/75] feat(httpapi/api.go): add JWT and Token authorization support Two new functions, SetHeaderWithToken and SetHeaderWithJWT, have been added to support JWT and Token authorization respectively. These functions set the request headers with the provided token or JWT. --- internal/baserow/httpapi/api.go | 43 +++++++++++++++++++++++++++++++++ 1 file changed, 43 insertions(+) diff --git a/internal/baserow/httpapi/api.go b/internal/baserow/httpapi/api.go index e1244c7f..7af54c6c 100644 --- a/internal/baserow/httpapi/api.go +++ b/internal/baserow/httpapi/api.go @@ -4,8 +4,51 @@ import ( "fmt" "io" "net/http" + + F "github.com/IBM/fp-go/function" + H "github.com/IBM/fp-go/http/builder" + C "github.com/IBM/fp-go/http/content" + HD "github.com/IBM/fp-go/http/headers" + S "github.com/IBM/fp-go/string" +) + +var ( + WithJWT = F.Flow2( + S.Format[string]("JWT %s"), + H.WithAuthorization, + ) + WithToken = F.Flow2( + S.Format[string]("Token %s"), + H.WithAuthorization, + ) ) +func SetHeaderWithToken(token string) func(*http.Request) *http.Request { + return func(req *http.Request) *http.Request { + req.Header = F.Pipe3( + H.Default, + H.WithContentType(C.Json), + H.WithHeader(HD.Accept)(C.Json), + WithToken(token), + ).GetHeaders() + + return req + } +} + +func SetHeaderWithJWT(jwt string) func(*http.Request) *http.Request { + return func(req *http.Request) *http.Request { + req.Header = F.Pipe3( + H.Default, + H.WithContentType(C.Json), + H.WithHeader(HD.Accept)(C.Json), + WithJWT(jwt), + ).GetHeaders() + + return req + } +} + func CommonHeader(lreq *http.Request, token, format string) { lreq.Header.Set("Content-Type", "application/json") lreq.Header.Set("Accept", "application/json") From cecdd44c3198c8b32407e7ae59e3d5e923db920a Mon Sep 17 00:00:00 2001 From: Siddhartha Basu Date: Mon, 26 Feb 2024 07:06:59 -0600 Subject: [PATCH 53/75] feat(table.go): add TableFieldsDelURL and TableFieldsURL methods for generating API URLs The addition of these two methods, TableFieldsDelURL and TableFieldsURL, allows for the generation of specific API URLs. This is beneficial as it simplifies the process of creating URLs for deleting and accessing table fields, respectively. --- internal/baserow/database/table.go | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/internal/baserow/database/table.go b/internal/baserow/database/table.go index 20076542..b1518658 100644 --- a/internal/baserow/database/table.go +++ b/internal/baserow/database/table.go @@ -18,6 +18,22 @@ type TableManager struct { DatabaseId int32 } +func (tbm *TableManager) TableFieldsDelURL(field tableFieldsResponse) string { + return fmt.Sprintf( + "https://%s/api/database/fields/%d/", + tbm.Client.GetConfig().Host, + field.Id, + ) +} + +func (tbm *TableManager) TableFieldsURL(tbl *client.Table) string { + return fmt.Sprintf( + "https://%s/api/database/fields/table/%d/", + tbm.Client.GetConfig().Host, + tbl.GetId(), + ) +} + func (tbm *TableManager) CreateTable( table string, ) (*client.Table, error) { From 45e6f9fabdae3fecd06012855d038001b462d1f5 Mon Sep 17 00:00:00 2001 From: Siddhartha Basu Date: Mon, 26 Feb 2024 07:07:24 -0600 Subject: [PATCH 54/75] feat(table.go): add fields parameter to CreateTable function and set FirstRowHeader to true The CreateTable function now accepts an additional parameter 'fields' which allows the user to specify the fields while creating a table. Also, the FirstRowHeader is now set to true, which makes the first row of the table as the header by default. --- internal/baserow/database/table.go | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/internal/baserow/database/table.go b/internal/baserow/database/table.go index b1518658..60a5207d 100644 --- a/internal/baserow/database/table.go +++ b/internal/baserow/database/table.go @@ -35,14 +35,15 @@ func (tbm *TableManager) TableFieldsURL(tbl *client.Table) string { } func (tbm *TableManager) CreateTable( - table string, + table string, fields []string, ) (*client.Table, error) { + isTrue := true var row []interface{} - row = append(row, []string{""}) + row = append(row, fields) tbl, resp, err := tbm.Client. DatabaseTablesApi. CreateDatabaseTable(tbm.Ctx, tbm.DatabaseId). - TableCreate(client.TableCreate{Name: table, Data: row}). + TableCreate(client.TableCreate{Name: table, Data: row, FirstRowHeader: &isTrue}). Execute() if err != nil { return tbl, fmt.Errorf( From 175151f97320d39be63c10d6d66856b255cc6cc5 Mon Sep 17 00:00:00 2001 From: Siddhartha Basu Date: Mon, 26 Feb 2024 07:12:26 -0600 Subject: [PATCH 55/75] feat(ontology.go): add new types and functions to handle field deletion and request feedback The changes introduce new types and functions to handle field deletion and request feedback in the ontology.go file. This includes the addition of new types 'tableFieldDelResponse' and 'fieldsReqFeedback' to handle the response from field deletion requests and feedback from field requests respectively. New functions 'hasExtraField', 'onFieldsReqFeedbackError', 'onFieldDelReqFeedbackSuccess', 'onFieldsReqFeedbackSuccess', and 'onFieldDelReqFeedbackNone' have been added to handle various scenarios during field deletion and request feedback. --- internal/baserow/database/ontology.go | 51 +++++++++++++++++++++++++++ 1 file changed, 51 insertions(+) diff --git a/internal/baserow/database/ontology.go b/internal/baserow/database/ontology.go index 28c01874..732e2d51 100644 --- a/internal/baserow/database/ontology.go +++ b/internal/baserow/database/ontology.go @@ -8,6 +8,12 @@ import ( "slices" + A "github.com/IBM/fp-go/array" + R "github.com/IBM/fp-go/context/readerioeither" + H "github.com/IBM/fp-go/context/readerioeither/http" + E "github.com/IBM/fp-go/either" + F "github.com/IBM/fp-go/function" + O "github.com/IBM/fp-go/option" "github.com/dictyBase/modware-import/internal/baserow/client" "github.com/dictyBase/modware-import/internal/baserow/httpapi" "github.com/dictyBase/modware-import/internal/collection" @@ -19,8 +25,31 @@ type OntologyTableManager struct { type tableFieldsResponse struct { Name string `json:"name"` + Id int `json:"id"` } +type tableFieldDelResponse struct { + RelatedFields []struct { + ID int `json:"id"` + TableID int `json:"table_id"` + } `json:"related_fields,omitempty"` +} + +type fieldsReqFeedback struct { + Error error + Fields []tableFieldsResponse + Msg string +} + +var ( + readFieldDelResp = H.ReadJson[tableFieldDelResponse]( + H.MakeClient(http.DefaultClient), + ) + readFieldsResp = H.ReadJson[[]tableFieldsResponse]( + H.MakeClient(http.DefaultClient), + ) +) + func (ont *OntologyTableManager) CreateFields(tbl *client.Table) error { reqURL := fmt.Sprintf( "https://%s/api/database/fields/table/%d/", @@ -84,3 +113,25 @@ func FieldDefs() []map[string]interface{} { {"name": "is_obsolete", "type": "boolean"}, } } + +func hasExtraField(elem tableFieldsResponse) bool { + return elem.Name == "Field 1" +} + +func onFieldsReqFeedbackError(err error) fieldsReqFeedback { + return fieldsReqFeedback{Error: err} +} + +func onFieldDelReqFeedbackSuccess( + resp tableFieldDelResponse, +) fieldsReqFeedback { + return fieldsReqFeedback{Msg: "deleted extra field"} +} + +func onFieldsReqFeedbackSuccess(resp []tableFieldsResponse) fieldsReqFeedback { + return fieldsReqFeedback{Fields: resp} +} + +func onFieldDelReqFeedbackNone() fieldsReqFeedback { + return fieldsReqFeedback{Msg: "no field found to delete"} +} From 9bc6a389c6b044f48e8dec49d13b9293c42a1e92 Mon Sep 17 00:00:00 2001 From: Siddhartha Basu Date: Mon, 26 Feb 2024 07:13:14 -0600 Subject: [PATCH 56/75] feat(ontology.go): add RemoveExtraField function to handle deletion of unnecessary fields The RemoveExtraField function has been added to the OntologyTableManager. This function is designed to handle the removal of any extra fields that are not required in the table. This is important for maintaining the integrity and cleanliness of the data in the table. The function first checks for any extra fields, and if found, it triggers the deletion process. --- internal/baserow/database/ontology.go | 55 +++++++++++++++++++++++++++ 1 file changed, 55 insertions(+) diff --git a/internal/baserow/database/ontology.go b/internal/baserow/database/ontology.go index 732e2d51..cd1cd78c 100644 --- a/internal/baserow/database/ontology.go +++ b/internal/baserow/database/ontology.go @@ -2,6 +2,7 @@ package database import ( "bytes" + "context" "encoding/json" "fmt" "net/http" @@ -76,6 +77,60 @@ func (ont *OntologyTableManager) CreateFields(tbl *client.Table) error { return nil } +func (ont *OntologyTableManager) onFieldDelReqFeedbackSome( + field tableFieldsResponse, +) fieldsReqFeedback { + resp := F.Pipe3( + ont.TableFieldsDelURL(field), + F.Bind13of3(H.MakeRequest)("DELETE", nil), + R.Map(httpapi.SetHeaderWithJWT(ont.Token)), + readFieldDelResp, + )(context.Background()) + + return F.Pipe1( + resp(), + E.Fold[error, tableFieldDelResponse, fieldsReqFeedback]( + onFieldsReqFeedbackError, + onFieldDelReqFeedbackSuccess, + ), + ) +} + +func (ont *OntologyTableManager) RemoveExtraField( + tbl *client.Table, +) (string, error) { + var empty string + resp := F.Pipe3( + ont.TableFieldsURL(tbl), + H.MakeGetRequest, + R.Map(httpapi.SetHeaderWithJWT(ont.Token)), + readFieldsResp, + )(context.Background()) + output := F.Pipe1( + resp(), + E.Fold[error, []tableFieldsResponse, fieldsReqFeedback]( + onFieldsReqFeedbackError, + onFieldsReqFeedbackSuccess, + ), + ) + if output.Error != nil { + return empty, output.Error + } + delOutput := F.Pipe2( + output.Fields, + A.FindFirst(hasExtraField), + O.Fold[tableFieldsResponse]( + onFieldDelReqFeedbackNone, + ont.onFieldDelReqFeedbackSome, + ), + ) + if delOutput.Error != nil { + return empty, delOutput.Error + } + + return delOutput.Msg, nil +} + func (ont *OntologyTableManager) CheckAllTableFields( tbl *client.Table, ) (bool, error) { From 310aa72ab33b5a9141647fe52116831f9ceb1469 Mon Sep 17 00:00:00 2001 From: Siddhartha Basu Date: Mon, 26 Feb 2024 07:13:56 -0600 Subject: [PATCH 57/75] refactor(ontology.go): move FieldNames and FieldDefs functions to OntologyTableManager methods The FieldNames and FieldDefs functions were previously standalone, but they have been moved to be methods of the OntologyTableManager. This change improves the organization of the code by grouping related functionality together. It also enhances encapsulation by keeping related data and behavior in the same object. --- internal/baserow/database/ontology.go | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/internal/baserow/database/ontology.go b/internal/baserow/database/ontology.go index cd1cd78c..ee3168cb 100644 --- a/internal/baserow/database/ontology.go +++ b/internal/baserow/database/ontology.go @@ -6,7 +6,6 @@ import ( "encoding/json" "fmt" "net/http" - "slices" A "github.com/IBM/fp-go/array" @@ -57,7 +56,7 @@ func (ont *OntologyTableManager) CreateFields(tbl *client.Table) error { ont.Client.GetConfig().Host, tbl.GetId(), ) - for _, payload := range FieldDefs() { + for _, payload := range ont.FieldDefs() { jsonData, err := json.Marshal(payload) if err != nil { return fmt.Errorf("error in encoding body %s", err) @@ -148,7 +147,7 @@ func (ont *OntologyTableManager) CheckAllTableFields( existing, func(input tableFieldsResponse) string { return input.Name }, ) - for _, fld := range FieldNames() { + for _, fld := range ont.FieldNames() { if num := slices.Index(exFields, fld); num == -1 { return ok, nil } @@ -157,11 +156,11 @@ func (ont *OntologyTableManager) CheckAllTableFields( return true, nil } -func FieldNames() []string { +func (ont *OntologyTableManager) FieldNames() []string { return []string{"term_id", "name", "is_obsolete"} } -func FieldDefs() []map[string]interface{} { +func (ont *OntologyTableManager) FieldDefs() []map[string]interface{} { return []map[string]interface{}{ {"name": "name", "type": "text"}, {"name": "term_id", "type": "text"}, From 5e5a5f94f8420aa12ecb340e2e261fd37cb7bae3 Mon Sep 17 00:00:00 2001 From: Siddhartha Basu Date: Mon, 26 Feb 2024 07:14:16 -0600 Subject: [PATCH 58/75] refactor(action.go): modify CreateTable function to include field names The CreateTable function now includes field names as a parameter to provide more detailed information about the table being created. The logger's info message has been updated to reflect this change, improving the clarity of the logs by specifying that a table is created with fields. --- internal/baserow/cli/action.go | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/internal/baserow/cli/action.go b/internal/baserow/cli/action.go index a6dd9ee2..18a98d63 100644 --- a/internal/baserow/cli/action.go +++ b/internal/baserow/cli/action.go @@ -139,15 +139,14 @@ func CreateOntologyTableHandler(cltx *cli.Context) error { DatabaseId: int32(cltx.Int("database-id")), }, } - tbl, err := ontTbl.CreateTable(cltx.String("table")) + tbl, err := ontTbl.CreateTable(cltx.String("table"), ontTbl.FieldNames()) if err != nil { return cli.Exit(err.Error(), 2) } - logger.Infof("created table %s", tbl.GetName()) + logger.Infof("created table with fields %s", tbl.GetName()) if err := ontTbl.CreateFields(tbl); err != nil { return cli.Exit(err.Error(), 2) } - logger.Infof("created all fields in the ontology table %s", tbl.GetName()) return nil } From 304067502fe94f34ffbf0160126209d2df2aa397 Mon Sep 17 00:00:00 2001 From: Siddhartha Basu Date: Mon, 26 Feb 2024 08:13:51 -0600 Subject: [PATCH 59/75] refactor(table.go): rename TableFieldsDelURL to TableFieldsChangeURL for better clarity The function name 'TableFieldsDelURL' was misleading as it suggested that the function is used to delete a URL, while it is actually used to change the URL. The new name 'TableFieldsChangeURL' provides a more accurate description of the function's purpose. --- internal/baserow/database/table.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/internal/baserow/database/table.go b/internal/baserow/database/table.go index 60a5207d..955f5f28 100644 --- a/internal/baserow/database/table.go +++ b/internal/baserow/database/table.go @@ -18,7 +18,7 @@ type TableManager struct { DatabaseId int32 } -func (tbm *TableManager) TableFieldsDelURL(field tableFieldsResponse) string { +func (tbm *TableManager) TableFieldsChangeURL(field tableFieldsResponse) string { return fmt.Sprintf( "https://%s/api/database/fields/%d/", tbm.Client.GetConfig().Host, From 345be4c0db882523728f8b622cae422fd1a0bdad Mon Sep 17 00:00:00 2001 From: Siddhartha Basu Date: Mon, 26 Feb 2024 08:14:03 -0600 Subject: [PATCH 60/75] feat(ontology.go): add ListTableFields function to list all table fields The TableFieldsDelURL function was replaced with TableFieldsChangeURL in the onFieldDelReqFeedbackSome function to better reflect the action being performed. The ListTableFields function was added to provide a way to list all fields in a table. The RemoveExtraField function was refactored to use the new ListTableFields function, improving code reusability and maintainability. --- internal/baserow/database/ontology.go | 21 ++++++++++++++------- 1 file changed, 14 insertions(+), 7 deletions(-) diff --git a/internal/baserow/database/ontology.go b/internal/baserow/database/ontology.go index ee3168cb..e9b82e74 100644 --- a/internal/baserow/database/ontology.go +++ b/internal/baserow/database/ontology.go @@ -80,7 +80,7 @@ func (ont *OntologyTableManager) onFieldDelReqFeedbackSome( field tableFieldsResponse, ) fieldsReqFeedback { resp := F.Pipe3( - ont.TableFieldsDelURL(field), + ont.TableFieldsChangeURL(field), F.Bind13of3(H.MakeRequest)("DELETE", nil), R.Map(httpapi.SetHeaderWithJWT(ont.Token)), readFieldDelResp, @@ -95,10 +95,9 @@ func (ont *OntologyTableManager) onFieldDelReqFeedbackSome( ) } -func (ont *OntologyTableManager) RemoveExtraField( +func (ont *OntologyTableManager) ListTableFields( tbl *client.Table, -) (string, error) { - var empty string +) ([]tableFieldsResponse, error) { resp := F.Pipe3( ont.TableFieldsURL(tbl), H.MakeGetRequest, @@ -112,11 +111,19 @@ func (ont *OntologyTableManager) RemoveExtraField( onFieldsReqFeedbackSuccess, ), ) - if output.Error != nil { - return empty, output.Error + return output.Fields, output.Error +} + +func (ont *OntologyTableManager) RemoveExtraField( + tbl *client.Table, +) (string, error) { + var empty string + fields, err := ont.ListTableFields(tbl) + if err != nil { + return empty, err } delOutput := F.Pipe2( - output.Fields, + fields, A.FindFirst(hasExtraField), O.Fold[tableFieldsResponse]( onFieldDelReqFeedbackNone, From cdb27ccfb1522812252c94ade9e17c9e785af4ff Mon Sep 17 00:00:00 2001 From: Siddhartha Basu Date: Mon, 26 Feb 2024 08:22:06 -0600 Subject: [PATCH 61/75] refactor(database): move table related functions from ontology.go to table.go The table related functions were moved from ontology.go to table.go to improve code organization and readability. This change makes it easier to understand the codebase as functions are now grouped based on their functionality. It also makes future maintenance easier as changes related to table operations can be made in one file. --- internal/baserow/database/ontology.go | 89 ------------------------ internal/baserow/database/table.go | 98 ++++++++++++++++++++++++++- 2 files changed, 97 insertions(+), 90 deletions(-) diff --git a/internal/baserow/database/ontology.go b/internal/baserow/database/ontology.go index e9b82e74..cd4c31c3 100644 --- a/internal/baserow/database/ontology.go +++ b/internal/baserow/database/ontology.go @@ -1,12 +1,8 @@ package database import ( - "bytes" "context" - "encoding/json" - "fmt" "net/http" - "slices" A "github.com/IBM/fp-go/array" R "github.com/IBM/fp-go/context/readerioeither" @@ -16,18 +12,12 @@ import ( O "github.com/IBM/fp-go/option" "github.com/dictyBase/modware-import/internal/baserow/client" "github.com/dictyBase/modware-import/internal/baserow/httpapi" - "github.com/dictyBase/modware-import/internal/collection" ) type OntologyTableManager struct { *TableManager } -type tableFieldsResponse struct { - Name string `json:"name"` - Id int `json:"id"` -} - type tableFieldDelResponse struct { RelatedFields []struct { ID int `json:"id"` @@ -50,32 +40,6 @@ var ( ) ) -func (ont *OntologyTableManager) CreateFields(tbl *client.Table) error { - reqURL := fmt.Sprintf( - "https://%s/api/database/fields/table/%d/", - ont.Client.GetConfig().Host, - tbl.GetId(), - ) - for _, payload := range ont.FieldDefs() { - jsonData, err := json.Marshal(payload) - if err != nil { - return fmt.Errorf("error in encoding body %s", err) - } - req, err := http.NewRequest("POST", reqURL, bytes.NewBuffer(jsonData)) - if err != nil { - return fmt.Errorf("error in creating request %s ", err) - } - httpapi.CommonHeader(req, ont.Token, "JWT") - res, err := httpapi.ReqToResponse(req) - if err != nil { - return err - } - defer res.Body.Close() - } - - return nil -} - func (ont *OntologyTableManager) onFieldDelReqFeedbackSome( field tableFieldsResponse, ) fieldsReqFeedback { @@ -95,25 +59,6 @@ func (ont *OntologyTableManager) onFieldDelReqFeedbackSome( ) } -func (ont *OntologyTableManager) ListTableFields( - tbl *client.Table, -) ([]tableFieldsResponse, error) { - resp := F.Pipe3( - ont.TableFieldsURL(tbl), - H.MakeGetRequest, - R.Map(httpapi.SetHeaderWithJWT(ont.Token)), - readFieldsResp, - )(context.Background()) - output := F.Pipe1( - resp(), - E.Fold[error, []tableFieldsResponse, fieldsReqFeedback]( - onFieldsReqFeedbackError, - onFieldsReqFeedbackSuccess, - ), - ) - return output.Fields, output.Error -} - func (ont *OntologyTableManager) RemoveExtraField( tbl *client.Table, ) (string, error) { @@ -137,32 +82,6 @@ func (ont *OntologyTableManager) RemoveExtraField( return delOutput.Msg, nil } -func (ont *OntologyTableManager) CheckAllTableFields( - tbl *client.Table, -) (bool, error) { - ok := false - res, err := ont.TableFieldsResp(tbl) - if err != nil { - return ok, err - } - defer res.Body.Close() - existing := make([]tableFieldsResponse, 0) - if err := json.NewDecoder(res.Body).Decode(&existing); err != nil { - return ok, fmt.Errorf("error in decoding response %s", err) - } - exFields := collection.Map( - existing, - func(input tableFieldsResponse) string { return input.Name }, - ) - for _, fld := range ont.FieldNames() { - if num := slices.Index(exFields, fld); num == -1 { - return ok, nil - } - } - - return true, nil -} - func (ont *OntologyTableManager) FieldNames() []string { return []string{"term_id", "name", "is_obsolete"} } @@ -179,20 +98,12 @@ func hasExtraField(elem tableFieldsResponse) bool { return elem.Name == "Field 1" } -func onFieldsReqFeedbackError(err error) fieldsReqFeedback { - return fieldsReqFeedback{Error: err} -} - func onFieldDelReqFeedbackSuccess( resp tableFieldDelResponse, ) fieldsReqFeedback { return fieldsReqFeedback{Msg: "deleted extra field"} } -func onFieldsReqFeedbackSuccess(resp []tableFieldsResponse) fieldsReqFeedback { - return fieldsReqFeedback{Fields: resp} -} - func onFieldDelReqFeedbackNone() fieldsReqFeedback { return fieldsReqFeedback{Msg: "no field found to delete"} } diff --git a/internal/baserow/database/table.go b/internal/baserow/database/table.go index 955f5f28..d8459587 100644 --- a/internal/baserow/database/table.go +++ b/internal/baserow/database/table.go @@ -1,15 +1,30 @@ package database import ( + "bytes" "context" + "encoding/json" "fmt" "net/http" + "github.com/dictyBase/modware-import/internal/collection" + "golang.org/x/exp/slices" + "github.com/dictyBase/modware-import/internal/baserow/client" "github.com/dictyBase/modware-import/internal/baserow/httpapi" "github.com/sirupsen/logrus" + + R "github.com/IBM/fp-go/context/readerioeither" + H "github.com/IBM/fp-go/context/readerioeither/http" + E "github.com/IBM/fp-go/either" + F "github.com/IBM/fp-go/function" ) +type tableFieldsResponse struct { + Name string `json:"name"` + Id int `json:"id"` +} + type TableManager struct { Logger *logrus.Entry Client *client.APIClient @@ -18,7 +33,9 @@ type TableManager struct { DatabaseId int32 } -func (tbm *TableManager) TableFieldsChangeURL(field tableFieldsResponse) string { +func (tbm *TableManager) TableFieldsChangeURL( + field tableFieldsResponse, +) string { return fmt.Sprintf( "https://%s/api/database/fields/%d/", tbm.Client.GetConfig().Host, @@ -71,3 +88,82 @@ func (tbm *TableManager) TableFieldsResp( httpapi.CommonHeader(req, tbm.Token, "Token") return httpapi.ReqToResponse(req) } + +func (tbm *OntologyTableManager) ListTableFields( + tbl *client.Table, +) ([]tableFieldsResponse, error) { + resp := F.Pipe3( + tbm.TableFieldsURL(tbl), + H.MakeGetRequest, + R.Map(httpapi.SetHeaderWithJWT(tbm.Token)), + readFieldsResp, + )(context.Background()) + output := F.Pipe1( + resp(), + E.Fold[error, []tableFieldsResponse, fieldsReqFeedback]( + onFieldsReqFeedbackError, + onFieldsReqFeedbackSuccess, + ), + ) + return output.Fields, output.Error +} + +func (tbm *OntologyTableManager) CreateFields(tbl *client.Table) error { + reqURL := fmt.Sprintf( + "https://%s/api/database/fields/table/%d/", + tbm.Client.GetConfig().Host, + tbl.GetId(), + ) + for _, payload := range tbm.FieldDefs() { + jsonData, err := json.Marshal(payload) + if err != nil { + return fmt.Errorf("error in encoding body %s", err) + } + req, err := http.NewRequest("POST", reqURL, bytes.NewBuffer(jsonData)) + if err != nil { + return fmt.Errorf("error in creating request %s ", err) + } + httpapi.CommonHeader(req, tbm.Token, "JWT") + res, err := httpapi.ReqToResponse(req) + if err != nil { + return err + } + defer res.Body.Close() + } + + return nil +} + +func (tbm *OntologyTableManager) CheckAllTableFields( + tbl *client.Table, +) (bool, error) { + ok := false + res, err := tbm.TableFieldsResp(tbl) + if err != nil { + return ok, err + } + defer res.Body.Close() + existing := make([]tableFieldsResponse, 0) + if err := json.NewDecoder(res.Body).Decode(&existing); err != nil { + return ok, fmt.Errorf("error in decoding response %s", err) + } + exFields := collection.Map( + existing, + func(input tableFieldsResponse) string { return input.Name }, + ) + for _, fld := range tbm.FieldNames() { + if num := slices.Index(exFields, fld); num == -1 { + return ok, nil + } + } + + return true, nil +} + +func onFieldsReqFeedbackError(err error) fieldsReqFeedback { + return fieldsReqFeedback{Error: err} +} + +func onFieldsReqFeedbackSuccess(resp []tableFieldsResponse) fieldsReqFeedback { + return fieldsReqFeedback{Fields: resp} +} From 543013f9d520e3a2cee90dbfa53057c1855c7fef Mon Sep 17 00:00:00 2001 From: Siddhartha Basu Date: Mon, 26 Feb 2024 08:29:30 -0600 Subject: [PATCH 62/75] feat(table.go): add HasField function to check if a field exists in tableFieldsResponse The HasField function has been added to check if a specific field exists in the tableFieldsResponse. This function will be useful in scenarios where we need to verify the existence of a field before performing operations on it, thus preventing potential errors. --- internal/baserow/database/table.go | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/internal/baserow/database/table.go b/internal/baserow/database/table.go index d8459587..3cc53c4a 100644 --- a/internal/baserow/database/table.go +++ b/internal/baserow/database/table.go @@ -33,6 +33,10 @@ type TableManager struct { DatabaseId int32 } +var ( + HasField = F.Curry2(uncurriedHasField) +) + func (tbm *TableManager) TableFieldsChangeURL( field tableFieldsResponse, ) string { @@ -160,6 +164,10 @@ func (tbm *OntologyTableManager) CheckAllTableFields( return true, nil } +func uncurriedHasField(name string, fieldResp tableFieldsResponse) bool { + return fieldResp.Name == name +} + func onFieldsReqFeedbackError(err error) fieldsReqFeedback { return fieldsReqFeedback{Error: err} } From c2a7317ad5e95dd355c84df511d19c85215e0b73 Mon Sep 17 00:00:00 2001 From: Siddhartha Basu Date: Mon, 26 Feb 2024 08:31:55 -0600 Subject: [PATCH 63/75] refactor(ontology.go): rename RemoveExtraField to RemoveField and add field parameter The function RemoveExtraField was renamed to RemoveField and a new parameter 'field' was added to make the function more generic and reusable. Now, it can remove any field from the table, not just the 'Field 1'. The hasExtraField function was removed as it is no longer needed because the field to be removed is now passed as a parameter to the RemoveField function. --- internal/baserow/database/ontology.go | 10 +++------- 1 file changed, 3 insertions(+), 7 deletions(-) diff --git a/internal/baserow/database/ontology.go b/internal/baserow/database/ontology.go index cd4c31c3..6b515967 100644 --- a/internal/baserow/database/ontology.go +++ b/internal/baserow/database/ontology.go @@ -59,8 +59,8 @@ func (ont *OntologyTableManager) onFieldDelReqFeedbackSome( ) } -func (ont *OntologyTableManager) RemoveExtraField( - tbl *client.Table, +func (ont *OntologyTableManager) RemoveField( + tbl *client.Table, field string, ) (string, error) { var empty string fields, err := ont.ListTableFields(tbl) @@ -69,7 +69,7 @@ func (ont *OntologyTableManager) RemoveExtraField( } delOutput := F.Pipe2( fields, - A.FindFirst(hasExtraField), + A.FindFirst(HasField(field)), O.Fold[tableFieldsResponse]( onFieldDelReqFeedbackNone, ont.onFieldDelReqFeedbackSome, @@ -94,10 +94,6 @@ func (ont *OntologyTableManager) FieldDefs() []map[string]interface{} { } } -func hasExtraField(elem tableFieldsResponse) bool { - return elem.Name == "Field 1" -} - func onFieldDelReqFeedbackSuccess( resp tableFieldDelResponse, ) fieldsReqFeedback { From e5508d88b9d391568ae05c70432804b0cbb56bcd Mon Sep 17 00:00:00 2001 From: Siddhartha Basu Date: Mon, 26 Feb 2024 08:33:38 -0600 Subject: [PATCH 64/75] refactor(ontology.go, table.go): move RemoveField function from ontology.go to table.go The RemoveField function was moved from ontology.go to table.go. This change was made to improve the organization of the codebase. The function is more related to table operations, so it makes more sense to have it in the table.go file. This will make the code easier to understand and maintain. --- internal/baserow/database/ontology.go | 26 -------------------------- internal/baserow/database/table.go | 26 ++++++++++++++++++++++++++ 2 files changed, 26 insertions(+), 26 deletions(-) diff --git a/internal/baserow/database/ontology.go b/internal/baserow/database/ontology.go index 6b515967..10bb251d 100644 --- a/internal/baserow/database/ontology.go +++ b/internal/baserow/database/ontology.go @@ -4,13 +4,10 @@ import ( "context" "net/http" - A "github.com/IBM/fp-go/array" R "github.com/IBM/fp-go/context/readerioeither" H "github.com/IBM/fp-go/context/readerioeither/http" E "github.com/IBM/fp-go/either" F "github.com/IBM/fp-go/function" - O "github.com/IBM/fp-go/option" - "github.com/dictyBase/modware-import/internal/baserow/client" "github.com/dictyBase/modware-import/internal/baserow/httpapi" ) @@ -59,29 +56,6 @@ func (ont *OntologyTableManager) onFieldDelReqFeedbackSome( ) } -func (ont *OntologyTableManager) RemoveField( - tbl *client.Table, field string, -) (string, error) { - var empty string - fields, err := ont.ListTableFields(tbl) - if err != nil { - return empty, err - } - delOutput := F.Pipe2( - fields, - A.FindFirst(HasField(field)), - O.Fold[tableFieldsResponse]( - onFieldDelReqFeedbackNone, - ont.onFieldDelReqFeedbackSome, - ), - ) - if delOutput.Error != nil { - return empty, delOutput.Error - } - - return delOutput.Msg, nil -} - func (ont *OntologyTableManager) FieldNames() []string { return []string{"term_id", "name", "is_obsolete"} } diff --git a/internal/baserow/database/table.go b/internal/baserow/database/table.go index 3cc53c4a..58ba8eaf 100644 --- a/internal/baserow/database/table.go +++ b/internal/baserow/database/table.go @@ -7,6 +7,9 @@ import ( "fmt" "net/http" + A "github.com/IBM/fp-go/array" + O "github.com/IBM/fp-go/option" + "github.com/dictyBase/modware-import/internal/collection" "golang.org/x/exp/slices" @@ -164,6 +167,29 @@ func (tbm *OntologyTableManager) CheckAllTableFields( return true, nil } +func (tbm *OntologyTableManager) RemoveField( + tbl *client.Table, field string, +) (string, error) { + var empty string + fields, err := tbm.ListTableFields(tbl) + if err != nil { + return empty, err + } + delOutput := F.Pipe2( + fields, + A.FindFirst(HasField(field)), + O.Fold[tableFieldsResponse]( + onFieldDelReqFeedbackNone, + tbm.onFieldDelReqFeedbackSome, + ), + ) + if delOutput.Error != nil { + return empty, delOutput.Error + } + + return delOutput.Msg, nil +} + func uncurriedHasField(name string, fieldResp tableFieldsResponse) bool { return fieldResp.Name == name } From 0f778c6b2906f54bdee4899b0f980cdf24af5f97 Mon Sep 17 00:00:00 2001 From: Siddhartha Basu Date: Mon, 26 Feb 2024 10:56:36 -0600 Subject: [PATCH 65/75] refactor(ontology.go): change success message for field deletion The success message for field deletion has been changed from "deleted extra field" to "deleted field" to make it more generic and applicable to all field deletions, not just extra ones. This change improves the clarity and accuracy of the feedback message. --- internal/baserow/database/ontology.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/internal/baserow/database/ontology.go b/internal/baserow/database/ontology.go index 10bb251d..8ebd8738 100644 --- a/internal/baserow/database/ontology.go +++ b/internal/baserow/database/ontology.go @@ -71,7 +71,7 @@ func (ont *OntologyTableManager) FieldDefs() []map[string]interface{} { func onFieldDelReqFeedbackSuccess( resp tableFieldDelResponse, ) fieldsReqFeedback { - return fieldsReqFeedback{Msg: "deleted extra field"} + return fieldsReqFeedback{Msg: "deleted field"} } func onFieldDelReqFeedbackNone() fieldsReqFeedback { From 98e9f90fc79a50a314d71f9e9171c5002af344d6 Mon Sep 17 00:00:00 2001 From: Siddhartha Basu Date: Mon, 26 Feb 2024 11:04:15 -0600 Subject: [PATCH 66/75] refactor(database): move field deletion related functions and types from ontology.go to table.go The field deletion related functions and types were moved from ontology.go to table.go. This change was made to improve the organization of the codebase. The table.go file is a more appropriate place for these functions and types as they are more related to table operations rather than ontology operations. This change will make the code easier to understand and maintain. --- internal/baserow/database/ontology.go | 62 --------------------------- internal/baserow/database/table.go | 51 ++++++++++++++++++++++ 2 files changed, 51 insertions(+), 62 deletions(-) diff --git a/internal/baserow/database/ontology.go b/internal/baserow/database/ontology.go index 8ebd8738..b1640880 100644 --- a/internal/baserow/database/ontology.go +++ b/internal/baserow/database/ontology.go @@ -1,61 +1,9 @@ package database -import ( - "context" - "net/http" - - R "github.com/IBM/fp-go/context/readerioeither" - H "github.com/IBM/fp-go/context/readerioeither/http" - E "github.com/IBM/fp-go/either" - F "github.com/IBM/fp-go/function" - "github.com/dictyBase/modware-import/internal/baserow/httpapi" -) - type OntologyTableManager struct { *TableManager } -type tableFieldDelResponse struct { - RelatedFields []struct { - ID int `json:"id"` - TableID int `json:"table_id"` - } `json:"related_fields,omitempty"` -} - -type fieldsReqFeedback struct { - Error error - Fields []tableFieldsResponse - Msg string -} - -var ( - readFieldDelResp = H.ReadJson[tableFieldDelResponse]( - H.MakeClient(http.DefaultClient), - ) - readFieldsResp = H.ReadJson[[]tableFieldsResponse]( - H.MakeClient(http.DefaultClient), - ) -) - -func (ont *OntologyTableManager) onFieldDelReqFeedbackSome( - field tableFieldsResponse, -) fieldsReqFeedback { - resp := F.Pipe3( - ont.TableFieldsChangeURL(field), - F.Bind13of3(H.MakeRequest)("DELETE", nil), - R.Map(httpapi.SetHeaderWithJWT(ont.Token)), - readFieldDelResp, - )(context.Background()) - - return F.Pipe1( - resp(), - E.Fold[error, tableFieldDelResponse, fieldsReqFeedback]( - onFieldsReqFeedbackError, - onFieldDelReqFeedbackSuccess, - ), - ) -} - func (ont *OntologyTableManager) FieldNames() []string { return []string{"term_id", "name", "is_obsolete"} } @@ -67,13 +15,3 @@ func (ont *OntologyTableManager) FieldDefs() []map[string]interface{} { {"name": "is_obsolete", "type": "boolean"}, } } - -func onFieldDelReqFeedbackSuccess( - resp tableFieldDelResponse, -) fieldsReqFeedback { - return fieldsReqFeedback{Msg: "deleted field"} -} - -func onFieldDelReqFeedbackNone() fieldsReqFeedback { - return fieldsReqFeedback{Msg: "no field found to delete"} -} diff --git a/internal/baserow/database/table.go b/internal/baserow/database/table.go index 58ba8eaf..e5f586bd 100644 --- a/internal/baserow/database/table.go +++ b/internal/baserow/database/table.go @@ -23,6 +23,28 @@ import ( F "github.com/IBM/fp-go/function" ) +type tableFieldDelResponse struct { + RelatedFields []struct { + ID int `json:"id"` + TableID int `json:"table_id"` + } `json:"related_fields,omitempty"` +} + +type fieldsReqFeedback struct { + Error error + Fields []tableFieldsResponse + Msg string +} + +var ( + readFieldDelResp = H.ReadJson[tableFieldDelResponse]( + H.MakeClient(http.DefaultClient), + ) + readFieldsResp = H.ReadJson[[]tableFieldsResponse]( + H.MakeClient(http.DefaultClient), + ) +) + type tableFieldsResponse struct { Name string `json:"name"` Id int `json:"id"` @@ -190,6 +212,25 @@ func (tbm *OntologyTableManager) RemoveField( return delOutput.Msg, nil } +func (ont *OntologyTableManager) onFieldDelReqFeedbackSome( + field tableFieldsResponse, +) fieldsReqFeedback { + resp := F.Pipe3( + ont.TableFieldsChangeURL(field), + F.Bind13of3(H.MakeRequest)("DELETE", nil), + R.Map(httpapi.SetHeaderWithJWT(ont.Token)), + readFieldDelResp, + )(context.Background()) + + return F.Pipe1( + resp(), + E.Fold[error, tableFieldDelResponse, fieldsReqFeedback]( + onFieldsReqFeedbackError, + onFieldDelReqFeedbackSuccess, + ), + ) +} + func uncurriedHasField(name string, fieldResp tableFieldsResponse) bool { return fieldResp.Name == name } @@ -201,3 +242,13 @@ func onFieldsReqFeedbackError(err error) fieldsReqFeedback { func onFieldsReqFeedbackSuccess(resp []tableFieldsResponse) fieldsReqFeedback { return fieldsReqFeedback{Fields: resp} } + +func onFieldDelReqFeedbackSuccess( + resp tableFieldDelResponse, +) fieldsReqFeedback { + return fieldsReqFeedback{Msg: "deleted field"} +} + +func onFieldDelReqFeedbackNone() fieldsReqFeedback { + return fieldsReqFeedback{Msg: "no field found to delete"} +} From d5668db602027aa1ee01ea62f78756f630b24a2d Mon Sep 17 00:00:00 2001 From: Siddhartha Basu Date: Mon, 26 Feb 2024 11:21:01 -0600 Subject: [PATCH 67/75] feat(table.go): introduce FieldDefinition interface to abstract field definitions The OntologyTableManager was replaced with TableManager as the receiver for several methods to improve code readability and maintainability. This change was made because the methods are more related to the TableManager than the OntologyTableManager. Additionally, a new interface, FieldDefinition, was introduced to abstract the field definitions. This allows for more flexibility and extensibility in handling different types of field definitions in the future. --- internal/baserow/database/table.go | 16 +++++++++++++--- 1 file changed, 13 insertions(+), 3 deletions(-) diff --git a/internal/baserow/database/table.go b/internal/baserow/database/table.go index e5f586bd..9c5cea75 100644 --- a/internal/baserow/database/table.go +++ b/internal/baserow/database/table.go @@ -50,7 +50,13 @@ type tableFieldsResponse struct { Id int `json:"id"` } +type FieldDefinition interface { + FieldNames() []string + FieldDefs() map[string]interface{} +} + type TableManager struct { + FieldDefinition Logger *logrus.Entry Client *client.APIClient Ctx context.Context @@ -118,7 +124,7 @@ func (tbm *TableManager) TableFieldsResp( return httpapi.ReqToResponse(req) } -func (tbm *OntologyTableManager) ListTableFields( +func (tbm *TableManager) ListTableFields( tbl *client.Table, ) ([]tableFieldsResponse, error) { resp := F.Pipe3( @@ -189,7 +195,11 @@ func (tbm *OntologyTableManager) CheckAllTableFields( return true, nil } -func (tbm *OntologyTableManager) RemoveField( +func (tbm *TableManager) method() { + +} + +func (tbm *TableManager) RemoveField( tbl *client.Table, field string, ) (string, error) { var empty string @@ -212,7 +222,7 @@ func (tbm *OntologyTableManager) RemoveField( return delOutput.Msg, nil } -func (ont *OntologyTableManager) onFieldDelReqFeedbackSome( +func (ont *TableManager) onFieldDelReqFeedbackSome( field tableFieldsResponse, ) fieldsReqFeedback { resp := F.Pipe3( From 1b45b79b7044f310943eb8e7ad7bd6a75449da69 Mon Sep 17 00:00:00 2001 From: Siddhartha Basu Date: Tue, 27 Feb 2024 06:46:09 -0600 Subject: [PATCH 68/75] feat(table.go): add UpdateField method to update table fields The tableFieldsResponse struct was renamed to tableFieldReq to better reflect its purpose. The UpdateField method was added to allow updating of table fields. The tableFieldsReq struct was introduced to handle field requests that include parameters. --- internal/baserow/database/table.go | 153 +++++++++++++++++++++++------ 1 file changed, 121 insertions(+), 32 deletions(-) diff --git a/internal/baserow/database/table.go b/internal/baserow/database/table.go index 9c5cea75..d846580e 100644 --- a/internal/baserow/database/table.go +++ b/internal/baserow/database/table.go @@ -8,6 +8,7 @@ import ( "net/http" A "github.com/IBM/fp-go/array" + J "github.com/IBM/fp-go/json" O "github.com/IBM/fp-go/option" "github.com/dictyBase/modware-import/internal/collection" @@ -23,6 +24,31 @@ import ( F "github.com/IBM/fp-go/function" ) +var ( + makeHTTPRequest = F.Bind13of3(H.MakeRequest) + readFieldDelResp = H.ReadJson[tableFieldDelResponse]( + H.MakeClient(http.DefaultClient), + ) + readFieldsResp = H.ReadJson[[]tableFieldReq]( + H.MakeClient(http.DefaultClient), + ) + readUpdateFieldsResp = H.ReadJson[tableFieldUpdateResponse]( + H.MakeClient(http.DefaultClient), + ) + HasField = F.Curry2(uncurriedHasField) + ResToReqTableFields = F.Curry2(uncurriedResToReqTableFields) +) + +type tableFieldUpdateResponse struct { + Id int `json:"id"` + TableId int `json:"table_id"` +} + +type jsonPayload struct { + Error error + Payload []byte +} + type tableFieldDelResponse struct { RelatedFields []struct { ID int `json:"id"` @@ -32,24 +58,20 @@ type tableFieldDelResponse struct { type fieldsReqFeedback struct { Error error - Fields []tableFieldsResponse + Fields []tableFieldReq Msg string } -var ( - readFieldDelResp = H.ReadJson[tableFieldDelResponse]( - H.MakeClient(http.DefaultClient), - ) - readFieldsResp = H.ReadJson[[]tableFieldsResponse]( - H.MakeClient(http.DefaultClient), - ) -) - -type tableFieldsResponse struct { +type tableFieldReq struct { Name string `json:"name"` Id int `json:"id"` } +type tableFieldsReq struct { + tableFieldReq + Params map[string]interface{} +} + type FieldDefinition interface { FieldNames() []string FieldDefs() map[string]interface{} @@ -64,17 +86,13 @@ type TableManager struct { DatabaseId int32 } -var ( - HasField = F.Curry2(uncurriedHasField) -) - func (tbm *TableManager) TableFieldsChangeURL( - field tableFieldsResponse, + req tableFieldsReq, ) string { return fmt.Sprintf( "https://%s/api/database/fields/%d/", tbm.Client.GetConfig().Host, - field.Id, + req.Id, ) } @@ -126,7 +144,7 @@ func (tbm *TableManager) TableFieldsResp( func (tbm *TableManager) ListTableFields( tbl *client.Table, -) ([]tableFieldsResponse, error) { +) ([]tableFieldReq, error) { resp := F.Pipe3( tbm.TableFieldsURL(tbl), H.MakeGetRequest, @@ -135,7 +153,7 @@ func (tbm *TableManager) ListTableFields( )(context.Background()) output := F.Pipe1( resp(), - E.Fold[error, []tableFieldsResponse, fieldsReqFeedback]( + E.Fold[error, []tableFieldReq, fieldsReqFeedback]( onFieldsReqFeedbackError, onFieldsReqFeedbackSuccess, ), @@ -178,13 +196,13 @@ func (tbm *OntologyTableManager) CheckAllTableFields( return ok, err } defer res.Body.Close() - existing := make([]tableFieldsResponse, 0) + existing := make([]tableFieldReq, 0) if err := json.NewDecoder(res.Body).Decode(&existing); err != nil { return ok, fmt.Errorf("error in decoding response %s", err) } exFields := collection.Map( existing, - func(input tableFieldsResponse) string { return input.Name }, + func(input tableFieldReq) string { return input.Name }, ) for _, fld := range tbm.FieldNames() { if num := slices.Index(exFields, fld); num == -1 { @@ -195,12 +213,31 @@ func (tbm *OntologyTableManager) CheckAllTableFields( return true, nil } -func (tbm *TableManager) method() { +func (tbm *TableManager) UpdateField( + tbl *client.Table, + req string, + updateSpec map[string]interface{}, +) (string, error) { + var empty string + fields, err := tbm.ListTableFields(tbl) + if err != nil { + return empty, err + } + updateOutput := F.Pipe3( + fields, + A.FindFirst(HasField(req)), + O.Map(ResToReqTableFields(updateSpec)), + O.Fold[tableFieldsReq]( + onFieldDelReqFeedbackNone, + tbm.onFieldUpdateReqFeedbackSome, + ), + ) + return updateOutput.Msg, updateOutput.Error } func (tbm *TableManager) RemoveField( - tbl *client.Table, field string, + tbl *client.Table, req string, ) (string, error) { var empty string fields, err := tbm.ListTableFields(tbl) @@ -209,24 +246,48 @@ func (tbm *TableManager) RemoveField( } delOutput := F.Pipe2( fields, - A.FindFirst(HasField(field)), - O.Fold[tableFieldsResponse]( + A.FindFirst(HasField(req)), + O.Fold[tableFieldReq]( onFieldDelReqFeedbackNone, tbm.onFieldDelReqFeedbackSome, ), ) - if delOutput.Error != nil { - return empty, delOutput.Error + + return delOutput.Msg, delOutput.Error +} + +func (tbm *TableManager) onFieldUpdateReqFeedbackSome( + req tableFieldsReq, +) fieldsReqFeedback { + payloadResp := F.Pipe2( + req.Params, + J.Marshal, + E.Fold(onJsonPayloadError, onJsonPayloadSuccess), + ) + if payloadResp.Error != nil { + return fieldsReqFeedback{Error: payloadResp.Error} } + resp := F.Pipe3( + tbm.TableFieldsChangeURL(req), + makeHTTPRequest("PATCH", bytes.NewBuffer(payloadResp.Payload)), + R.Map(httpapi.SetHeaderWithJWT(tbm.Token)), + readUpdateFieldsResp, + )(context.Background()) - return delOutput.Msg, nil + return F.Pipe1( + resp(), + E.Fold[error, tableFieldUpdateResponse, fieldsReqFeedback]( + onFieldsReqFeedbackError, + onFieldUpdateReqFeedbackSuccess, + ), + ) } func (ont *TableManager) onFieldDelReqFeedbackSome( - field tableFieldsResponse, + req tableFieldReq, ) fieldsReqFeedback { resp := F.Pipe3( - ont.TableFieldsChangeURL(field), + ont.TableFieldsChangeURL(req), F.Bind13of3(H.MakeRequest)("DELETE", nil), R.Map(httpapi.SetHeaderWithJWT(ont.Token)), readFieldDelResp, @@ -241,7 +302,7 @@ func (ont *TableManager) onFieldDelReqFeedbackSome( ) } -func uncurriedHasField(name string, fieldResp tableFieldsResponse) bool { +func uncurriedHasField(name string, fieldResp tableFieldReq) bool { return fieldResp.Name == name } @@ -249,7 +310,7 @@ func onFieldsReqFeedbackError(err error) fieldsReqFeedback { return fieldsReqFeedback{Error: err} } -func onFieldsReqFeedbackSuccess(resp []tableFieldsResponse) fieldsReqFeedback { +func onFieldsReqFeedbackSuccess(resp []tableFieldReq) fieldsReqFeedback { return fieldsReqFeedback{Fields: resp} } @@ -259,6 +320,34 @@ func onFieldDelReqFeedbackSuccess( return fieldsReqFeedback{Msg: "deleted field"} } +func onFieldUpdateReqFeedbackSuccess( + resp tableFieldUpdateResponse, +) fieldsReqFeedback { + return fieldsReqFeedback{Msg: "updated field"} +} + func onFieldDelReqFeedbackNone() fieldsReqFeedback { return fieldsReqFeedback{Msg: "no field found to delete"} } + +func onJsonPayloadError(err error) jsonPayload { + return jsonPayload{Error: err} +} + +func onJsonPayloadSuccess(resp []byte) jsonPayload { + return jsonPayload{Payload: resp} +} + +func uncurriedResToReqTableFields( + params map[string]interface{}, + req tableFieldReq, +) tableFieldsReq { + return tableFieldsReq{ + tableFieldReq: tableFieldReq{ + Name: req.Name, + Id: req.Id, + }, + Params: params, + } + +} From 130149cf4c566d5106884801738186ab7833493e Mon Sep 17 00:00:00 2001 From: Siddhartha Basu Date: Tue, 27 Feb 2024 07:36:06 -0600 Subject: [PATCH 69/75] feat(table.go): modify ResToReqTableFields to ResToReqTableWithParams to support additional parameters The renaming of tableFieldReq to tableFieldRes and vice versa was done to better reflect their actual usage and improve code readability. The function ResToReqTableFields was modified to ResToReqTableWithParams to support additional parameters, enhancing the function's flexibility and usability in different contexts. --- internal/baserow/database/table.go | 39 +++++++++++++++--------------- 1 file changed, 20 insertions(+), 19 deletions(-) diff --git a/internal/baserow/database/table.go b/internal/baserow/database/table.go index d846580e..8acf9bb2 100644 --- a/internal/baserow/database/table.go +++ b/internal/baserow/database/table.go @@ -35,8 +35,8 @@ var ( readUpdateFieldsResp = H.ReadJson[tableFieldUpdateResponse]( H.MakeClient(http.DefaultClient), ) - HasField = F.Curry2(uncurriedHasField) - ResToReqTableFields = F.Curry2(uncurriedResToReqTableFields) + HasField = F.Curry2(uncurriedHasField) + ResToReqTableWithParams = F.Curry2(uncurriedResToReqTableWithParams) ) type tableFieldUpdateResponse struct { @@ -58,17 +58,17 @@ type tableFieldDelResponse struct { type fieldsReqFeedback struct { Error error - Fields []tableFieldReq + Fields []tableFieldRes Msg string } -type tableFieldReq struct { +type tableFieldRes struct { Name string `json:"name"` Id int `json:"id"` } -type tableFieldsReq struct { - tableFieldReq +type tableFieldReq struct { + tableFieldRes Params map[string]interface{} } @@ -87,7 +87,7 @@ type TableManager struct { } func (tbm *TableManager) TableFieldsChangeURL( - req tableFieldsReq, + req tableFieldReq, ) string { return fmt.Sprintf( "https://%s/api/database/fields/%d/", @@ -144,7 +144,7 @@ func (tbm *TableManager) TableFieldsResp( func (tbm *TableManager) ListTableFields( tbl *client.Table, -) ([]tableFieldReq, error) { +) ([]tableFieldRes, error) { resp := F.Pipe3( tbm.TableFieldsURL(tbl), H.MakeGetRequest, @@ -153,7 +153,7 @@ func (tbm *TableManager) ListTableFields( )(context.Background()) output := F.Pipe1( resp(), - E.Fold[error, []tableFieldReq, fieldsReqFeedback]( + E.Fold[error, []tableFieldRes, fieldsReqFeedback]( onFieldsReqFeedbackError, onFieldsReqFeedbackSuccess, ), @@ -196,13 +196,13 @@ func (tbm *OntologyTableManager) CheckAllTableFields( return ok, err } defer res.Body.Close() - existing := make([]tableFieldReq, 0) + existing := make([]tableFieldRes, 0) if err := json.NewDecoder(res.Body).Decode(&existing); err != nil { return ok, fmt.Errorf("error in decoding response %s", err) } exFields := collection.Map( existing, - func(input tableFieldReq) string { return input.Name }, + func(input tableFieldRes) string { return input.Name }, ) for _, fld := range tbm.FieldNames() { if num := slices.Index(exFields, fld); num == -1 { @@ -226,8 +226,8 @@ func (tbm *TableManager) UpdateField( updateOutput := F.Pipe3( fields, A.FindFirst(HasField(req)), - O.Map(ResToReqTableFields(updateSpec)), - O.Fold[tableFieldsReq]( + O.Map(ResToReqTableWithParams(updateSpec)), + O.Fold[tableFieldReq]( onFieldDelReqFeedbackNone, tbm.onFieldUpdateReqFeedbackSome, ), @@ -244,9 +244,10 @@ func (tbm *TableManager) RemoveField( if err != nil { return empty, err } - delOutput := F.Pipe2( + delOutput := F.Pipe3( fields, A.FindFirst(HasField(req)), + O.Map(ResToReqTable), O.Fold[tableFieldReq]( onFieldDelReqFeedbackNone, tbm.onFieldDelReqFeedbackSome, @@ -257,12 +258,12 @@ func (tbm *TableManager) RemoveField( } func (tbm *TableManager) onFieldUpdateReqFeedbackSome( - req tableFieldsReq, + req tableFieldReq, ) fieldsReqFeedback { payloadResp := F.Pipe2( req.Params, J.Marshal, - E.Fold(onJsonPayloadError, onJsonPayloadSuccess), + E.Fold(onJSONPayloadError, onJSONPayloadSuccess), ) if payloadResp.Error != nil { return fieldsReqFeedback{Error: payloadResp.Error} @@ -288,7 +289,7 @@ func (ont *TableManager) onFieldDelReqFeedbackSome( ) fieldsReqFeedback { resp := F.Pipe3( ont.TableFieldsChangeURL(req), - F.Bind13of3(H.MakeRequest)("DELETE", nil), + makeHTTPRequest("DELETE", nil), R.Map(httpapi.SetHeaderWithJWT(ont.Token)), readFieldDelResp, )(context.Background()) @@ -302,7 +303,7 @@ func (ont *TableManager) onFieldDelReqFeedbackSome( ) } -func uncurriedHasField(name string, fieldResp tableFieldReq) bool { +func uncurriedHasField(name string, fieldResp tableFieldRes) bool { return fieldResp.Name == name } @@ -310,7 +311,7 @@ func onFieldsReqFeedbackError(err error) fieldsReqFeedback { return fieldsReqFeedback{Error: err} } -func onFieldsReqFeedbackSuccess(resp []tableFieldReq) fieldsReqFeedback { +func onFieldsReqFeedbackSuccess(resp []tableFieldRes) fieldsReqFeedback { return fieldsReqFeedback{Fields: resp} } From 76d723b7eb6a56b0ca3076981698dd3b02c533e3 Mon Sep 17 00:00:00 2001 From: Siddhartha Basu Date: Tue, 27 Feb 2024 07:36:35 -0600 Subject: [PATCH 70/75] refactor(table.go): rename functions and variables for clarity and consistency The function names 'onJsonPayloadError' and 'onJsonPayloadSuccess' have been renamed to 'onJSONPayloadError' and 'onJSONPayloadSuccess' respectively, to follow the camelCase naming convention. The variable 'readFieldsResp' has been changed from 'tableFieldReq' to 'tableFieldRes' to accurately reflect its purpose. The function 'uncurriedResToReqTableFields' has been renamed to 'uncurriedResToReqTableWithParams' and its parameters have been updated for better clarity and to reflect the changes in the data structures. A new function 'ResToReqTable' has been added to convert a response to a request, which will be useful in scenarios where the response from one API call needs to be used as the request for another API call. --- internal/baserow/database/table.go | 24 ++++++++++++++++-------- 1 file changed, 16 insertions(+), 8 deletions(-) diff --git a/internal/baserow/database/table.go b/internal/baserow/database/table.go index 8acf9bb2..33df19ce 100644 --- a/internal/baserow/database/table.go +++ b/internal/baserow/database/table.go @@ -29,7 +29,7 @@ var ( readFieldDelResp = H.ReadJson[tableFieldDelResponse]( H.MakeClient(http.DefaultClient), ) - readFieldsResp = H.ReadJson[[]tableFieldReq]( + readFieldsResp = H.ReadJson[[]tableFieldRes]( H.MakeClient(http.DefaultClient), ) readUpdateFieldsResp = H.ReadJson[tableFieldUpdateResponse]( @@ -331,24 +331,32 @@ func onFieldDelReqFeedbackNone() fieldsReqFeedback { return fieldsReqFeedback{Msg: "no field found to delete"} } -func onJsonPayloadError(err error) jsonPayload { +func onJSONPayloadError(err error) jsonPayload { return jsonPayload{Error: err} } -func onJsonPayloadSuccess(resp []byte) jsonPayload { +func onJSONPayloadSuccess(resp []byte) jsonPayload { return jsonPayload{Payload: resp} } -func uncurriedResToReqTableFields( +func uncurriedResToReqTableWithParams( params map[string]interface{}, - req tableFieldReq, -) tableFieldsReq { - return tableFieldsReq{ - tableFieldReq: tableFieldReq{ + req tableFieldRes, +) tableFieldReq { + return tableFieldReq{ + tableFieldRes: tableFieldRes{ Name: req.Name, Id: req.Id, }, Params: params, } +} +func ResToReqTable(req tableFieldRes) tableFieldReq { + return tableFieldReq{ + tableFieldRes: tableFieldRes{ + Name: req.Name, + Id: req.Id, + }, + } } From a6c6264a9e4d4243784a9c0d06a3cbb4e80556de Mon Sep 17 00:00:00 2001 From: Siddhartha Basu Date: Tue, 27 Feb 2024 10:16:50 -0600 Subject: [PATCH 71/75] refactor(table.go): modify CreateTable function to use HTTP request instead of API client The CreateTable function was refactored to use an HTTP request instead of the API client for table creation. This change was made to improve the flexibility and control over the request. A new function, CreteTableURL, was added to generate the URL for table creation. This function improves code readability and maintainability. In the CreateFields function, the variable name was changed from 'payload' to 'params' to better reflect its purpose and improve code readability. --- internal/baserow/database/table.go | 65 ++++++++++++++++++++++-------- 1 file changed, 49 insertions(+), 16 deletions(-) diff --git a/internal/baserow/database/table.go b/internal/baserow/database/table.go index 33df19ce..97f3746b 100644 --- a/internal/baserow/database/table.go +++ b/internal/baserow/database/table.go @@ -35,6 +35,9 @@ var ( readUpdateFieldsResp = H.ReadJson[tableFieldUpdateResponse]( H.MakeClient(http.DefaultClient), ) + readTableCreateResp = H.ReadJson[tableFieldRes]( + H.MakeClient(http.DefaultClient), + ) HasField = F.Curry2(uncurriedHasField) ResToReqTableWithParams = F.Curry2(uncurriedResToReqTableWithParams) ) @@ -60,6 +63,7 @@ type fieldsReqFeedback struct { Error error Fields []tableFieldRes Msg string + Table *client.Table } type tableFieldRes struct { @@ -104,26 +108,46 @@ func (tbm *TableManager) TableFieldsURL(tbl *client.Table) string { ) } +func (tbm *TableManager) CreteTableURL() string { + return fmt.Sprintf( + "https://%s/api/database/tables/database/%d/", + tbm.Client.GetConfig().Host, + tbm.DatabaseId, + ) +} + func (tbm *TableManager) CreateTable( table string, fields []string, ) (*client.Table, error) { - isTrue := true var row []interface{} - row = append(row, fields) - tbl, resp, err := tbm.Client. - DatabaseTablesApi. - CreateDatabaseTable(tbm.Ctx, tbm.DatabaseId). - TableCreate(client.TableCreate{Name: table, Data: row, FirstRowHeader: &isTrue}). - Execute() - if err != nil { - return tbl, fmt.Errorf( - "error in creating table %s %s", - table, err, - ) + params := map[string]interface{}{ + "name": table, + "data": append(row, fields), + "first_row_header": "true", + } + createPayload := F.Pipe2( + params, + J.Marshal, + E.Fold(onJSONPayloadError, onJSONPayloadSuccess), + ) + if createPayload.Error != nil { + return &client.Table{}, createPayload.Error } - defer resp.Body.Close() + resp := F.Pipe3( + tbm.CreteTableURL(), + makeHTTPRequest("POST", bytes.NewBuffer(createPayload.Payload)), + R.Map(httpapi.SetHeaderWithJWT(tbm.Token)), + readTableCreateResp, + )(context.Background()) + output := F.Pipe1( + resp(), + E.Fold[error, tableFieldRes, fieldsReqFeedback]( + onFieldsReqFeedbackError, + onTableCreateFeedbackSuccess, + ), + ) - return tbl, nil + return output.Table, output.Error } func (tbm *TableManager) TableFieldsResp( @@ -167,8 +191,8 @@ func (tbm *OntologyTableManager) CreateFields(tbl *client.Table) error { tbm.Client.GetConfig().Host, tbl.GetId(), ) - for _, payload := range tbm.FieldDefs() { - jsonData, err := json.Marshal(payload) + for _, params := range tbm.FieldDefs() { + jsonData, err := json.Marshal(params) if err != nil { return fmt.Errorf("error in encoding body %s", err) } @@ -307,6 +331,15 @@ func uncurriedHasField(name string, fieldResp tableFieldRes) bool { return fieldResp.Name == name } +func onTableCreateFeedbackSuccess(res tableFieldRes) fieldsReqFeedback { + return fieldsReqFeedback{ + Table: &client.Table{ + Id: int32(res.Id), + Name: res.Name, + }, + } +} + func onFieldsReqFeedbackError(err error) fieldsReqFeedback { return fieldsReqFeedback{Error: err} } From c3b25ab1bb31476431b7a6e47b38e7744bd4537c Mon Sep 17 00:00:00 2001 From: Siddhartha Basu Date: Tue, 27 Feb 2024 10:26:11 -0600 Subject: [PATCH 72/75] refactor(database): move functional handlers from table.go to functional_handlers.go for better code organization The functional handlers and related types were moved from table.go to a new file, functional_handlers.go. This change was made to improve code organization and readability. By separating these handlers into their own file, it's easier to locate and understand their functionality. This also reduces the size of the table.go file, making it less overwhelming to navigate. --- .../baserow/database/functional_handlers.go | 128 ++++++++++++++++++ internal/baserow/database/table.go | 119 ---------------- 2 files changed, 128 insertions(+), 119 deletions(-) create mode 100644 internal/baserow/database/functional_handlers.go diff --git a/internal/baserow/database/functional_handlers.go b/internal/baserow/database/functional_handlers.go new file mode 100644 index 00000000..a98babfd --- /dev/null +++ b/internal/baserow/database/functional_handlers.go @@ -0,0 +1,128 @@ +package database + +import ( + "net/http" + + H "github.com/IBM/fp-go/context/readerioeither/http" + F "github.com/IBM/fp-go/function" + "github.com/dictyBase/modware-import/internal/baserow/client" +) + +var ( + makeHTTPRequest = F.Bind13of3(H.MakeRequest) + readFieldDelResp = H.ReadJson[tableFieldDelResponse]( + H.MakeClient(http.DefaultClient), + ) + readFieldsResp = H.ReadJson[[]tableFieldRes]( + H.MakeClient(http.DefaultClient), + ) + readUpdateFieldsResp = H.ReadJson[tableFieldUpdateResponse]( + H.MakeClient(http.DefaultClient), + ) + readTableCreateResp = H.ReadJson[tableFieldRes]( + H.MakeClient(http.DefaultClient), + ) + HasField = F.Curry2(uncurriedHasField) + ResToReqTableWithParams = F.Curry2(uncurriedResToReqTableWithParams) +) + +type tableFieldUpdateResponse struct { + Id int `json:"id"` + TableId int `json:"table_id"` +} + +type jsonPayload struct { + Error error + Payload []byte +} + +type tableFieldDelResponse struct { + RelatedFields []struct { + ID int `json:"id"` + TableID int `json:"table_id"` + } `json:"related_fields,omitempty"` +} + +type fieldsReqFeedback struct { + Error error + Fields []tableFieldRes + Msg string + Table *client.Table +} + +type tableFieldRes struct { + Name string `json:"name"` + Id int `json:"id"` +} + +type tableFieldReq struct { + tableFieldRes + Params map[string]interface{} +} + +func uncurriedHasField(name string, fieldResp tableFieldRes) bool { + return fieldResp.Name == name +} + +func onTableCreateFeedbackSuccess(res tableFieldRes) fieldsReqFeedback { + return fieldsReqFeedback{ + Table: &client.Table{ + Id: int32(res.Id), + Name: res.Name, + }, + } +} + +func onFieldsReqFeedbackError(err error) fieldsReqFeedback { + return fieldsReqFeedback{Error: err} +} + +func onFieldsReqFeedbackSuccess(resp []tableFieldRes) fieldsReqFeedback { + return fieldsReqFeedback{Fields: resp} +} + +func onFieldDelReqFeedbackSuccess( + resp tableFieldDelResponse, +) fieldsReqFeedback { + return fieldsReqFeedback{Msg: "deleted field"} +} + +func onFieldUpdateReqFeedbackSuccess( + resp tableFieldUpdateResponse, +) fieldsReqFeedback { + return fieldsReqFeedback{Msg: "updated field"} +} + +func onFieldDelReqFeedbackNone() fieldsReqFeedback { + return fieldsReqFeedback{Msg: "no field found to delete"} +} + +func onJSONPayloadError(err error) jsonPayload { + return jsonPayload{Error: err} +} + +func onJSONPayloadSuccess(resp []byte) jsonPayload { + return jsonPayload{Payload: resp} +} + +func uncurriedResToReqTableWithParams( + params map[string]interface{}, + req tableFieldRes, +) tableFieldReq { + return tableFieldReq{ + tableFieldRes: tableFieldRes{ + Name: req.Name, + Id: req.Id, + }, + Params: params, + } +} + +func ResToReqTable(req tableFieldRes) tableFieldReq { + return tableFieldReq{ + tableFieldRes: tableFieldRes{ + Name: req.Name, + Id: req.Id, + }, + } +} diff --git a/internal/baserow/database/table.go b/internal/baserow/database/table.go index 97f3746b..1d019050 100644 --- a/internal/baserow/database/table.go +++ b/internal/baserow/database/table.go @@ -24,58 +24,6 @@ import ( F "github.com/IBM/fp-go/function" ) -var ( - makeHTTPRequest = F.Bind13of3(H.MakeRequest) - readFieldDelResp = H.ReadJson[tableFieldDelResponse]( - H.MakeClient(http.DefaultClient), - ) - readFieldsResp = H.ReadJson[[]tableFieldRes]( - H.MakeClient(http.DefaultClient), - ) - readUpdateFieldsResp = H.ReadJson[tableFieldUpdateResponse]( - H.MakeClient(http.DefaultClient), - ) - readTableCreateResp = H.ReadJson[tableFieldRes]( - H.MakeClient(http.DefaultClient), - ) - HasField = F.Curry2(uncurriedHasField) - ResToReqTableWithParams = F.Curry2(uncurriedResToReqTableWithParams) -) - -type tableFieldUpdateResponse struct { - Id int `json:"id"` - TableId int `json:"table_id"` -} - -type jsonPayload struct { - Error error - Payload []byte -} - -type tableFieldDelResponse struct { - RelatedFields []struct { - ID int `json:"id"` - TableID int `json:"table_id"` - } `json:"related_fields,omitempty"` -} - -type fieldsReqFeedback struct { - Error error - Fields []tableFieldRes - Msg string - Table *client.Table -} - -type tableFieldRes struct { - Name string `json:"name"` - Id int `json:"id"` -} - -type tableFieldReq struct { - tableFieldRes - Params map[string]interface{} -} - type FieldDefinition interface { FieldNames() []string FieldDefs() map[string]interface{} @@ -326,70 +274,3 @@ func (ont *TableManager) onFieldDelReqFeedbackSome( ), ) } - -func uncurriedHasField(name string, fieldResp tableFieldRes) bool { - return fieldResp.Name == name -} - -func onTableCreateFeedbackSuccess(res tableFieldRes) fieldsReqFeedback { - return fieldsReqFeedback{ - Table: &client.Table{ - Id: int32(res.Id), - Name: res.Name, - }, - } -} - -func onFieldsReqFeedbackError(err error) fieldsReqFeedback { - return fieldsReqFeedback{Error: err} -} - -func onFieldsReqFeedbackSuccess(resp []tableFieldRes) fieldsReqFeedback { - return fieldsReqFeedback{Fields: resp} -} - -func onFieldDelReqFeedbackSuccess( - resp tableFieldDelResponse, -) fieldsReqFeedback { - return fieldsReqFeedback{Msg: "deleted field"} -} - -func onFieldUpdateReqFeedbackSuccess( - resp tableFieldUpdateResponse, -) fieldsReqFeedback { - return fieldsReqFeedback{Msg: "updated field"} -} - -func onFieldDelReqFeedbackNone() fieldsReqFeedback { - return fieldsReqFeedback{Msg: "no field found to delete"} -} - -func onJSONPayloadError(err error) jsonPayload { - return jsonPayload{Error: err} -} - -func onJSONPayloadSuccess(resp []byte) jsonPayload { - return jsonPayload{Payload: resp} -} - -func uncurriedResToReqTableWithParams( - params map[string]interface{}, - req tableFieldRes, -) tableFieldReq { - return tableFieldReq{ - tableFieldRes: tableFieldRes{ - Name: req.Name, - Id: req.Id, - }, - Params: params, - } -} - -func ResToReqTable(req tableFieldRes) tableFieldReq { - return tableFieldReq{ - tableFieldRes: tableFieldRes{ - Name: req.Name, - Id: req.Id, - }, - } -} From 15e76c0bd13721811cd2a6882d4a650da8c80b03 Mon Sep 17 00:00:00 2001 From: Siddhartha Basu Date: Tue, 27 Feb 2024 11:00:54 -0600 Subject: [PATCH 73/75] refactor(table.go): rename 'req' parameter to 'field' for clarity The 'req' parameter in the UpdateField function was renamed to 'field'. This change was made to improve code readability and maintainability. The term 'field' is more descriptive and accurately represents the purpose of the parameter in the context of the function. --- internal/baserow/database/table.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/internal/baserow/database/table.go b/internal/baserow/database/table.go index 1d019050..7c3ddd7a 100644 --- a/internal/baserow/database/table.go +++ b/internal/baserow/database/table.go @@ -187,7 +187,7 @@ func (tbm *OntologyTableManager) CheckAllTableFields( func (tbm *TableManager) UpdateField( tbl *client.Table, - req string, + field string, updateSpec map[string]interface{}, ) (string, error) { var empty string @@ -197,7 +197,7 @@ func (tbm *TableManager) UpdateField( } updateOutput := F.Pipe3( fields, - A.FindFirst(HasField(req)), + A.FindFirst(HasField(field)), O.Map(ResToReqTableWithParams(updateSpec)), O.Fold[tableFieldReq]( onFieldDelReqFeedbackNone, From cc713c1c0383cb6032ee31b59a05b5ab6841adbd Mon Sep 17 00:00:00 2001 From: Siddhartha Basu Date: Tue, 27 Feb 2024 11:01:05 -0600 Subject: [PATCH 74/75] feat(action.go): enhance error messages and add field update functionality The error messages have been enhanced to provide more context about the error, specifically mentioning the operation that caused the error. This will help in debugging and understanding the error cause more quickly. Additionally, a new functionality has been added to update the 'is_obsolete' field in the ontology table. This allows for more dynamic and flexible handling of the ontology table. --- internal/baserow/cli/action.go | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/internal/baserow/cli/action.go b/internal/baserow/cli/action.go index 18a98d63..d500e4c1 100644 --- a/internal/baserow/cli/action.go +++ b/internal/baserow/cli/action.go @@ -141,12 +141,20 @@ func CreateOntologyTableHandler(cltx *cli.Context) error { } tbl, err := ontTbl.CreateTable(cltx.String("table"), ontTbl.FieldNames()) if err != nil { - return cli.Exit(err.Error(), 2) + return cli.Exit(fmt.Sprintf("error in creating table %s", err), 2) } logger.Infof("created table with fields %s", tbl.GetName()) - if err := ontTbl.CreateFields(tbl); err != nil { - return cli.Exit(err.Error(), 2) + msg, err := ontTbl.UpdateField( + tbl, + "is_obsolete", + map[string]interface{}{"name": "is_obsolete", "type": "boolean"}, + ) + if err != nil { + return cli.Exit( + fmt.Sprintf("error in updating is_obsolete field %s", err), + 2, + ) } - + logger.Info(msg) return nil } From 91ae54cbabec41ca67ec9cd6e891f348652522fe Mon Sep 17 00:00:00 2001 From: Siddhartha Basu Date: Tue, 27 Feb 2024 11:06:32 -0600 Subject: [PATCH 75/75] refactor(functional_handlers.go): change function name from ReadJson to ReadJSON for better readability The function names were changed from ReadJson to ReadJSON to adhere to the Go naming conventions, which recommend that acronyms and initialisms should be in uppercase. --- internal/baserow/database/functional_handlers.go | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/internal/baserow/database/functional_handlers.go b/internal/baserow/database/functional_handlers.go index a98babfd..984b3161 100644 --- a/internal/baserow/database/functional_handlers.go +++ b/internal/baserow/database/functional_handlers.go @@ -10,16 +10,16 @@ import ( var ( makeHTTPRequest = F.Bind13of3(H.MakeRequest) - readFieldDelResp = H.ReadJson[tableFieldDelResponse]( + readFieldDelResp = H.ReadJSON[tableFieldDelResponse]( H.MakeClient(http.DefaultClient), ) - readFieldsResp = H.ReadJson[[]tableFieldRes]( + readFieldsResp = H.ReadJSON[[]tableFieldRes]( H.MakeClient(http.DefaultClient), ) - readUpdateFieldsResp = H.ReadJson[tableFieldUpdateResponse]( + readUpdateFieldsResp = H.ReadJSON[tableFieldUpdateResponse]( H.MakeClient(http.DefaultClient), ) - readTableCreateResp = H.ReadJson[tableFieldRes]( + readTableCreateResp = H.ReadJSON[tableFieldRes]( H.MakeClient(http.DefaultClient), ) HasField = F.Curry2(uncurriedHasField)