I have a following piece of code in which i am trying to send an email using gopkg.in/gomail.v2. I am perfectly able to send the email when the email template is placed in the root directory of the project like this
./
main.go
template.html
// Info defines
type Info struct {
Age int
Name string
}
func (i Info) sendMail() {
fp := filepath.Join("template.html")
t := template.New(fp)
var err error
t, err = t.ParseFiles(fp)
if err != nil {
log.Println(err)
}
var tpl bytes.Buffer
if err := t.Execute(&tpl, i); err != nil {
log.Println(err)
}
result := tpl.String()
// ... email sending logic
}
func main() {
info := &Info{
Name: "name 1",
Age: 20,
}
info.sendMail()
}
but when i change the template directory to emails/template.html and change the filepath to
fp := filepath.Join("emails", "template.html")
then I get the error from t.Execute()
template: "emails/template.html" is an incomplete or empty template
I have also tried
fp, _ := filepath.Abs("emails/template.html")
but got error
template: "/mnt/data/go/test/emails/template.html" is an incomplete or empty template
the path mentioned is correct though.
I changed
if err := t.Execute(&tpl, i); err != nil {
log.Println(err)
}
to
if err := t2.ExecuteTemplate(&tpl, "template.html", i); err != nil {
log.Println(err)
}
and it worked
If I want to use t.Execute(&tpl, i) instead, then I have to specify the templates name as filename while creating the template
t := template.New("template.html")
Related
go version: go 1.19
my problem is with the database/sql package. I have plsql code available in service. values are sent to the service via map. but the number is not limited,
I need to send to db.exec() via sql.Named, I checked with loop and with interface, it didn't work.
Please help me.
my codes are:
in the controller
const begin = "
jsonData.Put('stateid', :stateid);
"
array_for_param := map[string]string{
"stateid": "a",
}
temp, err := services.Perform(c, begin, array_for_param, i_User_Id)
if err != nil {
log.Fatal(err)
}
service code:
var params []interface{}
for key, value := range array_for_param {
params = append(params, sql.Named(key, value))
}
if _, err := db.Exec(declare+begin+end,
sql.Named("i_User_Id", i_User_Id),
params...,
); err != nil {
log.Fatal(err)
}
The main problem I have is that I need to send the sql.Named code using a for, which is an unknown number
I did that too
if _, err := db.Exec(declare+begin+end,
sql.Named("i_User_Id", i_User_Id),
for key, value := range array_for_param {
return sql.Named(key, value)
}
); err != nil {
log.Fatal(err)
}
I don't know how to upload image or file in Go. Here I share my code
this is my repository, what i must change or add more code?
func (db *reportConnection) CreateReport(report entity.Report) entity.Report {
db.connection.Save(&report)
db.connection.Preload("User").Find(&report)
return report
}
this is my service, what i must change or add more code?
func (service *reportService) Create(r dto.ReportCreateDTO) entity.Report {
report := entity.Report{}
err := smapping.FillStruct(&report, smapping.MapFields(&r))
if err != nil {
log.Fatalf("failed map %v: ", err)
}
res := service.reportRepo.CreateReport(report)
return res
}
this is my controller, what i must change or add more code?
func (c *reportController) Create(ctx *gin.Context) {
var createReport dto.ReportCreateDTO
err := ctx.ShouldBind(&createReport)
if err != nil {
response := response.BuildErrorResponse("Failed to process!", err.Error(), response.EmptyObj{})
ctx.AbortWithStatusJSON(http.StatusBadRequest, response)
} else {
authHeader := ctx.GetHeader("Authorization")
userID := c.GetUserIDByToken(authHeader)
convertUserID, err := strconv.ParseUint(userID, 10, 64)
if err == nil {
createReport.UserID = convertUserID
}
result := c.reportService.Create(createReport)
response := response.BuildResponse(true, "OK!", result)
ctx.JSON(http.StatusOK, response)
}
}
```
i think i need to set a header, but not sure how
I am writing some code to send logs with gRPC to a server. The problem I'm having is that the logs are different and not all have every field, but . I'm wondering how I can set only the fields they do have inside logpb.Log short of creating several different types of logs in the proto file?
This is all one methods, but the formatting on StackOverflow isn't with it.
The code:
func (*Client) SendWithgRPC(entry *log.Entry) error {
conn, err := grpc.Dial("localhost:50051", grpc.WithInsecure())
if err != nil {
log.Fatalf("Could not connect: %v", err)
}
defer conn.Close()
c := logpb.NewLogServiceClient(conn)
stream, err := c.SendLogs(context.Background())
if err != nil {
log.Fatalf("Error while calling SendLogs: %v", err)
}
logFields := entry.Data
category := ""
debug_id := ""
request_id := ""
tipi := ""
uri := ""
var user_id int32 = 0
ip := ""
if logFields["category"] != nil {
category = logFields["category"].(string)
}
if logFields["debug_id"] != nil {
debug_id = logFields["debug_id"].(string)
}
if logFields["request_id"] != nil {
request_id = logFields["request_id"].(string)
}
if logFields["type"] != nil {
tipi = logFields["type"].(string)
}
if logFields["uri"] != nil {
uri = logFields["uri"].(string)
}
if logFields["user_id"] != nil {
user_id = int32(logFields["user_id"].(int))
}
if logFields["ip"] != nil {
ip = logFields["ip"].(string)
}
logEntry := &logpb.Log{
Time: entry.Time.String(),
Level: entry.Level.String(),
Msg: entry.Message,
Category: category,
DebugId: debug_id,
Ip: ip,
RequestId: request_id,
Type: tipi,
Uri: uri,
Id: user_id,
}
logs := []*logpb.Log{logEntry}
logarray := []*logpb.SendLogsRequest{
&logpb.SendLogsRequest{
Logs: logs,
},
}
fmt.Print(logarray)
for _, req := range logarray {
stream.Send(req)
}
_, err = stream.CloseAndRecv()
if err != nil {
fmt.Printf("Error with response: %v", err)
}
return nil
}
You can use something like https://github.com/go-playground/validator
It offers validation using tags. There is a lot more to this lib apart from basic validation. Do check it out.
Sample example https://github.com/go-playground/validator/blob/master/_examples/simple/main.go
As easy as it gets :
package main
import (
"fmt"
"github.com/go-playground/validator/v10"
)
type Name struct {
First string `validate:"required"`
Last string
}
func main() {
validName := Name{First: "John"} // Last name is empty and is not required
invalidName := Name{Last: "Doe"} // first name is empty but is required
validate := validator.New()
err := validate.Struct(invalidName)
if err != nil {
fmt.Println("invalid name struct caused error")
}
err = validate.Struct(validName)
if err != nil {
fmt.Println("valid name struct caused error")
}
fmt.Println("GoodBye")
}
The output of above :
invalid name struct caused error
GoodBye
working code on go-playground : https://play.golang.org/p/S4Yj1yr16t2
I am creating a buffer and then writing into that buffer via zip.NewWriter()
So my code looks like this
// Create a buffer to write our archive to.
buf := new(bytes.Buffer)
// Create a new zip archive.
w := zip.NewWriter(buf)
// Create file in this writer
f, err := w.Create("test.json")
/ Writing data to file
_, _ = f.Write([]byte("some data"))
// Close writer
_ = w.Close()
// TODO: Return this data in the form of io.ReadCloser
My end goal is to write files into this zip writer and return the data in the form of io.ReadCloser, my calling function expects data in io.ReadCloser
I tried to find and try multiple ways but none of them succeeded.
Check if this example works for you
func ExampleWriter() {
// Create a buffer to write our archive to.
buf := new(bytes.Buffer)
// Create a new zip archive.
w := zip.NewWriter(buf)
// Add some files to the archive.
var files = []struct {
Name, Body string
}{
{"readme.txt", "This archive contains some text files."},
{"gopher.txt", "Gopher names:\nGeorge\nGeoffrey\nGonzo"},
{"todo.txt", "Get animal handling licence.\nWrite more examples."},
}
for _, file := range files {
f, err := w.Create(file.Name)
if err != nil {
log.Fatal(err)
}
_, err = f.Write([]byte(file.Body))
if err != nil {
log.Fatal(err)
}
}
// Make sure to check the error on Close.
err := w.Close()
if err != nil {
log.Fatal(err)
}
}
By systematically check the error for those operations, you will see if there are any issue.
After some help from comments I got it working
So we can directly convert buf to io.ReadCloser and return it, no need for further conversion.
func ExampleWriter() (io.ReadCloser, error) {
// Create a buffer to write our archive to.
buf := new(bytes.Buffer)
// Create a new zip archive.
w := zip.NewWriter(buf)
// Add some files to the archive.
var files = []struct {
Name, Body string
}{
{"readme.txt", "This archive contains some text files."},
{"gopher.txt", "Gopher names:\nGeorge\nGeoffrey\nGonzo"},
{"todo.txt", "Get animal handling licence.\nWrite more examples."},
}
for _, file := range files {
f, err := w.Create(file.Name)
if err != nil {
log.Fatal(err)
}
_, err = f.Write([]byte(file.Body))
if err != nil {
log.Fatal(err)
}
}
// Make sure to check the error on Close.
err := w.Close()
if err != nil {
log.Fatal(err)
}
return ioutil.NopCloser(buf), nil // This returns io.ReadCloser
}
I'm trying to do some simple work with the text/template package. The sample given at the top of template is what I'm working with.
How do I write the 'parsed' file so template.ParseFiles() properly reads and executes it?
package main
import (
"text/template"
"os"
)
type Inventory struct {
Material string
Count uint
}
func main() {
sweaters := Inventory{"wool", 17}
tmpl, err := template.New("test").Parse("{{.Count}} items are made of {{.Material}}")
// tmpl, err := template.New("test").ParseFiles("file.txt")
if err != nil { panic(err) }
err = tmpl.Execute(os.Stdout, sweaters)
if err != nil { panic(err) }
}
/*
Contents of file.txt:
{{.Count}} items are made of {{.Material}}
Error thrown:
panic: template: test:1: "test" is an incomplete or empty template
goroutine 1 [running]:
main.main()
/tmp/templates/t.go:19 +0x21a
goroutine 2 [syscall]:
created by runtime.main
/var/tmp/portage/dev-lang/go-1.0.1/work/go/src/pkg/runtime/proc.c:221
*/
I have a copy of this code posted at the golang playground here
Edit #1:
I've been doing some research on this issue... since it's the Execute() method that actually throws the exception, and not the ParseFiles() part, I checked the method definition:
// Execute applies a parsed template to the specified data object,
// and writes the output to wr.
func (t *Template) Execute(wr io.Writer, data interface{}) (err error) {
defer errRecover(&err)
value := reflect.ValueOf(data)
state := &state{
tmpl: t,
wr: wr,
line: 1,
vars: []variable{{"$", value}},
}
if t.Tree == nil || t.Root == nil {
state.errorf("%q is an incomplete or empty template", t.name)
}
state.walk(value, t.Root)
return
}
So, on a hunch, I dumped the value of t.Tree for the inline 'non-file' style, tmpl is: &parse.Tree{Name:"test", Root:(*parse.ListNode)(0xf840030700), funcs:[]map[string]interface {}(nil), lex:(*parse.lexer)(nil), token:[2]parse.item{parse.item{typ:6, val:""}, parse.item{typ:9, val:"{{"}}, peekCount:1, vars:[]string(nil)} and
when ran with ParseFiles(), tmpl is: (*parse.Tree)(nil). I find it odd that one is a dereference, and one value is a pointer. This may help solve the riddle
sweaters := Inventory{"wool", 17}
tmpl, err := template.ParseFiles("file.txt")
if err != nil {
panic(err)
}
err = tmpl.ExecuteTemplate(os.Stdout, "file.txt", sweaters)
if err != nil {
panic(err)
}
If you have many files, you can use ParseGlob:
tmpl, err := template.ParseGlob("*.txt")
if err != nil {
panic(err)
}
err = tmpl.ExecuteTemplate(os.Stdout, "file.txt", sweaters)
if err != nil {
panic(err)
}
err = tmpl.ExecuteTemplate(os.Stdout, "file2.txt", sweaters)
if err != nil {
panic(err)
}
There is a little trick in Go template parseFiles.
func parseFiles(t *Template, filenames ...string) (*Template, error) {
if len(filenames) == 0 {
// Not really a problem, but be consistent.
return nil, fmt.Errorf("template: no files named in call to ParseFiles")
}
for _, filename := range filenames {
b, err := ioutil.ReadFile(filename)
if err != nil {
return nil, err
}
s := string(b)
name := filepath.Base(filename)
// First template becomes return value if not already defined,
// and we use that one for subsequent New calls to associate
// all the templates together. Also, if this file has the same name
// as t, this file becomes the contents of t, so
// t, err := New(name).Funcs(xxx).ParseFiles(name)
// works. Otherwise we create a new template associated with t.
var tmpl *Template
if t == nil {
t = New(name)
}
if name == t.Name() {
tmpl = t
} else {
tmpl = t.New(name)
}
_, err = tmpl.Parse(s)
if err != nil {
return nil, err
}
}
return t, nil
}
Only the template with same name will be reuse, otherwise create new one.
as your sample:
tmpl, err := template.New("test").ParseFiles("file.txt")
tmpl is the template named "test", and associated another template named "file.txt", you call Execute on "test" template, this template is a empty template, so raise the error "test is an incomplete or empty template".
It worked when you change the template name to file.txt
tmpl, err := template.New("file.txt").ParseFiles("file.txt")