diff --git a/code/frameworks/common-files/go.mod.tmpl b/code/frameworks/common-files/go.mod.tmpl index 438aef5..6b6e16d 100644 --- a/code/frameworks/common-files/go.mod.tmpl +++ b/code/frameworks/common-files/go.mod.tmpl @@ -40,7 +40,8 @@ require ( go.opentelemetry.io/otel/trace v1.21.0 google.golang.org/grpc v1.60.1 gorm.io/driver/sqlite v1.5.4 - gorm.io/gorm v1.25.5 + gorm.io/gorm v1.25.9 + gorm.io/driver/postgres v1.5.7 {{else if .IsGrpcServer }} github.com/go-sql-driver/mysql v1.7.1 github.com/mattn/go-sqlite3 v1.14.17 diff --git a/code/frameworks/common-files/go.sum.tmpl b/code/frameworks/common-files/go.sum.tmpl index 9e76116..23c81a6 100644 --- a/code/frameworks/common-files/go.sum.tmpl +++ b/code/frameworks/common-files/go.sum.tmpl @@ -740,6 +740,8 @@ gorm.io/driver/mysql v1.5.1/go.mod h1:Jo3Xu7mMhCyj8dlrb3WoCaRd1FhsVh+yMXb1jUInf5 gorm.io/driver/sqlite v1.5.2/go.mod h1:qxAuCol+2r6PannQDpOP1FP6ag3mKi4esLnB/jHed+4= gorm.io/gorm v1.25.1/go.mod h1:L4uxeKpfBml98NYqVqwAdmV1a2nBtAec/cf3fpucW/k= gorm.io/gorm v1.25.2/go.mod h1:L4uxeKpfBml98NYqVqwAdmV1a2nBtAec/cf3fpucW/k= +gorm.io/driver/postgres v1.5.7 h1:8ptbNJTDbEmhdr62uReG5BGkdQyeasu/FZHxI0IMGnM= +gorm.io/driver/postgres v1.5.7/go.mod h1:3e019WlBaYI5o5LIdNV+LyxCMNtLOQETBXL2h4chKpA= honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= honnef.co/go/tools v0.0.0-20190418001031-e561f6794a2a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= diff --git a/code/frameworks/go-gin-server/pkg/rest/server/daos/clients/sqls/postgresql-gorm.go.tmpl b/code/frameworks/go-gin-server/pkg/rest/server/daos/clients/sqls/postgresql-gorm.go.tmpl new file mode 100644 index 0000000..f756225 --- /dev/null +++ b/code/frameworks/go-gin-server/pkg/rest/server/daos/clients/sqls/postgresql-gorm.go.tmpl @@ -0,0 +1,68 @@ +package sqls + +import ( + "errors" + "fmt" + "os" + "sync" + + log "github.com/sirupsen/logrus" + "github.com/uptrace/opentelemetry-go-extra/otelgorm" + "gorm.io/driver/postgres" + "gorm.io/gorm" +) + +var ( + ErrDuplicate = errors.New("record already exists") + ErrNotExists = errors.New("row not exists") + ErrUpdateFailed = errors.New("update failed") + ErrDeleteFailed = errors.New("delete failed") +) + +var ( + user = os.Getenv("POSTGRESQL_DB_USER") + password = os.Getenv("POSTGRESQL_DB_PASSWORD") + host = os.Getenv("POSTGRESQL_DB_HOST") + port = os.Getenv("POSTGRESQL_DB_PORT") + database = os.Getenv("POSTGRESQL_DB_DATABASE") +) + + +type PostgreSQLClient struct { + DB *gorm.DB +} + +var postgreSQLClient *PostgreSQLClient + +func InitGormPOSTGRESQLDB() (*PostgreSQLClient, error) { + var o sync.Once + var err error + var db *gorm.DB + + o.Do(func() { + dataSource := fmt.Sprintf("host=%s user=%s password=%s dbname=%s port=%s", + host, user, password, database, port) + + // Open a connection to the PostgreSQL database + db, err = gorm.Open(postgres.Open(dataSource), &gorm.Config{}) + if err != nil { + log.Debugf("database connection error: %v", err) + os.Exit(1) + } + + serviceName := os.Getenv("SERVICE_NAME") + collectorURL := os.Getenv("OTEL_EXPORTER_OTLP_ENDPOINT") + if len(serviceName) > 0 && len(collectorURL) > 0 { + if err := db.Use(otelgorm.NewPlugin()); err != nil { + log.Debugf("unable to attach opentelemetry plugin error: %v", err) + os.Exit(1) + } + } + + postgreSQLClient = &PostgreSQLClient{ + DB: db, + } + }) + + return postgreSQLClient, nil +} diff --git a/code/frameworks/go-gin-server/pkg/rest/server/daos/postgresql-gorm-dao.go.tmpl b/code/frameworks/go-gin-server/pkg/rest/server/daos/postgresql-gorm-dao.go.tmpl new file mode 100644 index 0000000..dc9314e --- /dev/null +++ b/code/frameworks/go-gin-server/pkg/rest/server/daos/postgresql-gorm-dao.go.tmpl @@ -0,0 +1,108 @@ +package daos + +import ( + "errors" + "{{.GitPlatformURL}}/{{.GitPlatformUserName}}/{{.GitRepositoryName}}/{{.NodeName}}/pkg/rest/server/daos/clients/sqls" + "{{.GitPlatformURL}}/{{.GitPlatformUserName}}/{{.GitRepositoryName}}/{{.NodeName}}/pkg/rest/server/models" + log "github.com/sirupsen/logrus" + "gorm.io/gorm" +) + +type {{.CapsResourceNameSingular}}Dao struct { + db *gorm.DB +} + +func New{{.CapsResourceNameSingular}}Dao() (*{{.CapsResourceNameSingular}}Dao, error) { + postgresqlClient, err := sqls.InitGormPOSTGRESQLDB() + if err != nil { + return nil, err + } + err = postgresqlClient.DB.AutoMigrate(models.{{.CapsResourceNameSingular}}{}) + if err != nil { + return nil, err + } + return &{{.CapsResourceNameSingular}}Dao{ + db: postgresqlClient.DB, + }, nil +} + +{{ if .IsRESTCreateAllowed }} +func ({{.SmallResourceNameSingular}}Dao *{{.CapsResourceNameSingular}}Dao) Create{{.CapsResourceNameSingular}}(m *models.{{.CapsResourceNameSingular}}) (*models.{{.CapsResourceNameSingular}}, error) { + if err := {{.SmallResourceNameSingular}}Dao.db.Create(&m).Error; err != nil { + log.Debugf("failed to create {{.SmallResourceNameSingular}}: %v", err) + return nil, err + } + + log.Debugf("{{.SmallResourceNameSingular}} created") + return m, nil +} +{{ end }} + +{{ if .IsRESTListAllowed }} +func ({{.SmallResourceNameSingular}}Dao *{{.CapsResourceNameSingular}}Dao) List{{.CapsResourceNamePlural}}() ([]*models.{{.CapsResourceNameSingular}}, error) { + var {{.SmallResourceNamePlural}} []*models.{{.CapsResourceNameSingular}} + // TODO populate associations here with your own logic - https://gorm.io/docs/belongs_to.html + if err := {{.SmallResourceNameSingular}}Dao.db.Find(&{{.SmallResourceNamePlural}}).Error; err != nil { + log.Debugf("failed to list {{.SmallResourceNamePlural}}: %v", err) + return nil, err + } + + log.Debugf("{{.SmallResourceNameSingular}} listed") + return {{.SmallResourceNamePlural}}, nil +} +{{ end }} + +{{ if .IsRESTGetAllowed }} +func ({{.SmallResourceNameSingular}}Dao *{{.CapsResourceNameSingular}}Dao) Get{{.CapsResourceNameSingular}}(id int64) (*models.{{.CapsResourceNameSingular}}, error) { + var m *models.{{.CapsResourceNameSingular}} + if err := {{.SmallResourceNameSingular}}Dao.db.Where("id = ?", id).First(&m).Error; err != nil { + log.Debugf("failed to get {{.SmallResourceNameSingular}}: %v", err) + if errors.Is(err, gorm.ErrRecordNotFound) { + return nil, sqls.ErrNotExists + } + return nil, err + } + log.Debugf("{{.SmallResourceNameSingular}} retrieved") + return m, nil +} +{{ end }} + +{{ if .IsRESTPutAllowed }} +func ({{.SmallResourceNameSingular}}Dao *{{.CapsResourceNameSingular}}Dao) Update{{.CapsResourceNameSingular}}(id int64, m *models.{{.CapsResourceNameSingular}}) (*models.{{.CapsResourceNameSingular}}, error) { + if id == 0 { + return nil, errors.New("invalid {{.SmallResourceNameSingular}} ID") + } + if id != m.Id { + return nil, errors.New("id and payload don't match") + } + + var {{.SmallResourceNameSingular}} *models.{{.CapsResourceNameSingular}} + if err := {{.SmallResourceNameSingular}}Dao.db.Where("id = ?", id).First(&{{.SmallResourceNameSingular}}).Error; err != nil { + log.Debugf("failed to find {{.SmallResourceNameSingular}} for update: %v", err) + if errors.Is(err, gorm.ErrRecordNotFound) { + return nil, sqls.ErrNotExists + } + return nil, err + } + + if err := {{.SmallResourceNameSingular}}Dao.db.Save(&m).Error; err != nil{ + log.Debugf("failed to update {{.SmallResourceNameSingular}}: %v", err) + return nil, err + } + log.Debugf("{{.SmallResourceNameSingular}} updated") + return m, nil +} +{{ end }} + +{{ if .IsRESTDeleteAllowed }} +func ({{.SmallResourceNameSingular}}Dao *{{.CapsResourceNameSingular}}Dao) Delete{{.CapsResourceNameSingular}}(id int64) error { + var m *models.{{.CapsResourceNameSingular}} + if err := {{.SmallResourceNameSingular}}Dao.db.Where("id = ?", id).Delete(&m).Error; err != nil { + log.Debugf("failed to delete {{.SmallResourceNameSingular}}: %v", err) + return err + } + + log.Debugf("{{.SmallResourceNameSingular}} deleted") + return nil +} +{{ end }}