diff --git a/README.md b/README.md index 6380ca98..822c4fa1 100644 --- a/README.md +++ b/README.md @@ -11,31 +11,8 @@ An Odoo API client enabling Go programs to interact with Odoo in a simple and un ### Generate your models -**Note: Generating models require to follow instructions in GOPATH mode. Refactoring for go modules will come soon.** - -Define the environment variables to be able to connect to your odoo instance : - -(Don't set `ODOO_MODELS` if you want all your models to be generated) - -``` -export ODOO_ADMIN=admin // ensure the user has sufficient permissions to generate models -export ODOO_PASSWORD=password -export ODOO_DATABASE=odoo -export ODOO_URL=http://localhost:8069 -export ODOO_MODELS="crm.lead" -``` - -`ODOO_REPO_PATH` is the path where the repository will be downloaded (by default its GOPATH): -``` -export ODOO_REPO_PATH=$(echo $GOPATH | awk -F ':' '{ print $1 }')/src/github.com/skilld-labs/go-odoo -``` - -Download library and generate models : -``` -GO111MODULE="off" go get github.com/skilld-labs/go-odoo -cd $ODOO_REPO_PATH -ls | grep -v "conversion.go\|generator\|go.mod\|go-odoo-generator\|go.sum\|ir_model_fields.go\|ir_model.go\|LICENSE\|odoo.go\|README.md\|types.go\|version.go" // keep only go-odoo core files -GO111MODULE="off" go generate +```bash +./generator/generator -u admin_name -p admin_password -d database_name -o /the/directory/you/want/the/files/to/be/generated/in --url http://localhost:8069 -t ./generator/cmd/tmpl/model.tmpl -m crm.lead,res.users ``` That's it ! Your models have been generated ! @@ -67,9 +44,9 @@ import ( func main() { c, err := odoo.NewClient(&odoo.ClientConfig{ - Admin: "admin", - Password: "password", - Database: "odoo", + Admin: "admin_name", + Password: "admin_password", + Database: "database_name", URL: "http://localhost:8069", }) if err != nil { diff --git a/generator/cmd/generator.go b/generator/cmd/generator.go index 075ef208..87dd430b 100644 --- a/generator/cmd/generator.go +++ b/generator/cmd/generator.go @@ -1,6 +1,7 @@ package cmd import ( + "errors" "fmt" "os" "os/exec" @@ -80,6 +81,9 @@ func (g *generator) getAllModelsName() ([]string, error) { func (g *generator) modelFieldsFromModel(model string) ([]*modelField, error) { imfs, err := g.odoo.FindIrModelFieldss(odoo.NewCriteria().Add("model", "=", model), nil) if err != nil { + if errors.Is(err, odoo.ErrNotFound) { + return nil, nil + } return nil, err } return g.irModelFieldsToModelFields(imfs), nil diff --git a/generator/cmd/root.go b/generator/cmd/root.go index 15806cf6..187377a4 100644 --- a/generator/cmd/root.go +++ b/generator/cmd/root.go @@ -13,7 +13,7 @@ import ( var ( rootCmd = &cobra.Command{ - Use: "./go-odoo -u admin -p admin -d odoo --url http://localhost:8069 --models crm.lead", + Use: "./go-odoo -u admin -p admin -d odoo --url http://localhost:8069 --models crm.lead -t generator/cmd/tmpl/model.tmpl", Short: "Generates your odoo models for go-odoo golang library.", Long: ` Generates your odoo models for go-odoo golang library. @@ -29,16 +29,17 @@ You can provide models name as arguments to specify what models to generate. By } }, } - database string - admin string - password string - url string - noFmt bool - destFolder string - models string - c *odoo.Client - t *template.Template - g *generator + database string + admin string + password string + url string + noFmt bool + destFolder string + models string + modelTemplate string + c *odoo.Client + t *template.Template + g *generator ) // Execute executes the root command. @@ -57,6 +58,7 @@ func init() { rootCmd.PersistentFlags().StringVar(&url, "url", "http://localhost:8069", "the url of your odoo instance") rootCmd.PersistentFlags().StringVarP(&destFolder, "dest", "o", "", "the destination of generated models") rootCmd.PersistentFlags().StringVarP(&models, "models", "m", "", "the models you want to generate, separated by commas, empty means generate all") + rootCmd.PersistentFlags().StringVarP(&modelTemplate, "template", "t", "", "the model template location used to generate the models code") rootCmd.PersistentFlags().BoolVar(&noFmt, "no-fmt", false, "specify if you want to disable auto format of generated models") } @@ -78,7 +80,7 @@ func initOdoo() { func initTemplate() { var err error - if t, err = template.New("model.tmpl").ParseFiles("./generator/cmd/tmpl/model.tmpl"); err != nil { + if t, err = template.New("model.tmpl").ParseFiles(modelTemplate); err != nil { handleError(err) } } diff --git a/generator/cmd/tmpl/model.tmpl b/generator/cmd/tmpl/model.tmpl index c0e0b40d..b1a2a249 100644 --- a/generator/cmd/tmpl/model.tmpl +++ b/generator/cmd/tmpl/model.tmpl @@ -1,9 +1,5 @@ package odoo -import ( - "fmt" -) - // {{.StructName}} represents {{ .Name }} model. type {{.StructName}} struct { {{range .Fields}} {{.VarName}} {{.Type}} `xmlrpc:"{{.Name}},omptempty"`{{end }} @@ -68,10 +64,7 @@ func (c *Client) Get{{.StructName}}(id int64) (*{{.StructName}}, error) { if err != nil { return nil, err } - if {{.VarsName}} != nil && len(*{{.VarsName}}) > 0 { - return &((*{{.VarsName}})[0]), nil - } - return nil, fmt.Errorf("id %v of {{.Name}} not found", id) + return &((*{{.VarsName}})[0]), nil } // Get{{.StructName}}s gets {{ .Name }} existing records. @@ -89,10 +82,7 @@ func (c *Client) Find{{.StructName}}(criteria *Criteria) (*{{.StructName}}, erro if err := c.SearchRead({{.StructName}}Model, criteria, NewOptions().Limit(1), {{.VarsName}}); err != nil { return nil, err } - if {{.VarsName}} != nil && len(*{{.VarsName}}) > 0 { - return &((*{{.VarsName}})[0]), nil - } - return nil, fmt.Errorf("{{ .Name }} was not found with criteria %v", criteria) + return &((*{{.VarsName}})[0]), nil } // Find{{.StructName}}s finds {{ .Name }} records by querying it @@ -108,11 +98,7 @@ func (c *Client) Find{{.StructName}}s(criteria *Criteria, options *Options) (*{{ // Find{{.StructName}}Ids finds records ids by querying it // and filtering it with criteria and options. func (c *Client) Find{{.StructName}}Ids(criteria *Criteria, options *Options) ([]int64, error) { - ids, err := c.Search({{.StructName}}Model, criteria, options) - if err != nil { - return []int64{}, err - } - return ids, nil + return c.Search({{.StructName}}Model, criteria, options) } // Find{{.StructName}}Id finds record id by querying it with criteria. @@ -121,8 +107,5 @@ func (c *Client) Find{{.StructName}}Id(criteria *Criteria, options *Options) (in if err != nil { return -1, err } - if len(ids) > 0 { - return ids[0], nil - } - return -1, fmt.Errorf("{{ .Name }} was not found with criteria %v and options %v", criteria, options) + return ids[0], nil } diff --git a/generator/generator b/generator/generator index 02b65362..89c10568 100755 Binary files a/generator/generator and b/generator/generator differ diff --git a/odoo.go b/odoo.go index f8e0e2d1..f09f6750 100644 --- a/odoo.go +++ b/odoo.go @@ -1,18 +1,21 @@ //Package odoo contains client code of library -//go:generate ./generator/generator -u $ODOO_ADMIN -p $ODOO_PASSWORD -d $ODOO_DATABASE --url $ODOO_URL -o $ODOO_REPO_PATH --models $ODOO_MODELS +//go:generate ./generator/generator -u $ODOO_ADMIN -p $ODOO_PASSWORD -d $ODOO_DATABASE --url $ODOO_URL -o $ODOO_REPO_PATH --models $ODOO_MODELS -t generator/cmd/tmpl/model.tmpl package odoo import ( "errors" + "fmt" "log" "github.com/kolo/xmlrpc" ) var ( - errClientConfigurationInvalid = errors.New("client configuration is invalid") - errClientNotAuthenticate = errors.New("client is not authenticate") - errClientAuthentication = errors.New("client authentication error: please verify client configuration") + ErrClientConfigurationInvalid = errors.New("client configuration is invalid") + ErrClientNotAuthenticate = errors.New("client is not authenticate") + ErrClientAuthentication = errors.New("client authentication error: please verify client configuration") + ErrNotFound = errors.New("not found") + ErrPartiallyFound = errors.New("partially found") ) // ClientConfig is the configuration to create a new *Client by givin connection infomations. @@ -42,7 +45,7 @@ type Client struct { // NewClient creates a new *Client. func NewClient(cfg *ClientConfig) (*Client, error) { if !cfg.valid() { - return nil, errClientConfigurationInvalid + return nil, ErrClientConfigurationInvalid } c := &Client{ cfg: cfg, @@ -279,6 +282,10 @@ func (c *Client) SearchRead(model string, criteria *Criteria, options *Options, if err != nil { return err } + respLen := len(resp.([]interface{})) + if respLen == 0 { + return fmt.Errorf("%s model was %w with criteria %v and options %v", model, ErrNotFound, criteria, options) + } if err := convertFromDynamicToStatic(resp, elem); err != nil { return err } @@ -292,9 +299,16 @@ func (c *Client) Read(model string, ids []int64, options *Options, elem interfac if err != nil { return err } + respLen := len(resp.([]interface{})) + if respLen == 0 { + return fmt.Errorf("%s ids %v was %w with options %v", model, ids, ErrNotFound, options) + } if err := convertFromDynamicToStatic(resp, elem); err != nil { return err } + if respLen != len(ids) { + return fmt.Errorf("%s ids %v was %w with options %v", model, ids, ErrPartiallyFound, options) + } return nil } @@ -315,6 +329,10 @@ func (c *Client) Search(model string, criteria *Criteria, options *Options) ([]i if err != nil { return []int64{}, err } + respLen := len(resp.([]interface{})) + if respLen == 0 { + return []int64{}, fmt.Errorf("%s model was %w with criteria %v and options %v", model, ErrNotFound, criteria, options) + } return sliceInterfaceToInt64Slice(resp.([]interface{})), nil } @@ -348,7 +366,7 @@ func (c *Client) authenticate() error { return err } if _, ok := resp.(bool); ok { - return errClientAuthentication + return ErrClientAuthentication } c.uid = resp.(int64) c.auth = true @@ -407,7 +425,7 @@ func (c *Client) loadXmlrpcClient(x *xmlrpc.Client, path string) error { func (c *Client) checkForAuthentication() error { if !c.isAuthenticate() { - return errClientNotAuthenticate + return ErrClientNotAuthenticate } return nil }