Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
33 changes: 5 additions & 28 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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 !
Expand Down Expand Up @@ -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 {
Expand Down
4 changes: 4 additions & 0 deletions generator/cmd/generator.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package cmd

import (
"errors"
"fmt"
"os"
"os/exec"
Expand Down Expand Up @@ -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
Expand Down
26 changes: 14 additions & 12 deletions generator/cmd/root.go
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand All @@ -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.
Expand All @@ -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")
}

Expand All @@ -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)
}
}
Expand Down
25 changes: 4 additions & 21 deletions generator/cmd/tmpl/model.tmpl
Original file line number Diff line number Diff line change
@@ -1,9 +1,5 @@
package odoo

import (
"fmt"
)

// {{.StructName}} represents {{ .Name }} model.
type {{.StructName}} struct { {{range .Fields}}
{{.VarName}} {{.Type}} `xmlrpc:"{{.Name}},omptempty"`{{end }}
Expand Down Expand Up @@ -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.
Expand All @@ -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
Expand All @@ -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.
Expand All @@ -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
}
Binary file modified generator/generator
Binary file not shown.
32 changes: 25 additions & 7 deletions odoo.go
Original file line number Diff line number Diff line change
@@ -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.
Expand Down Expand Up @@ -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,
Expand Down Expand Up @@ -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
}
Expand All @@ -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
}

Expand All @@ -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
}

Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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
}
Expand Down