Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Additional functionality to util package #136

Open
wants to merge 19 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 9 commits
Commits
Show all changes
19 commits
Select commit Hold shift + click to select a range
71f3b0b
Extend util package with helper functions.
mwieser Oct 21, 2021
e5d3e6a
Add missing golang/text package.
mwieser Oct 21, 2021
ebad9d0
Add starttls support to mailer
mwieser Oct 21, 2021
274dddc
Merge branch 'master' of ssh://git-svc.allaboutapps.at:2222/aw/go-sta…
mwieser Oct 21, 2021
fa21af3
update changelog
mwieser Oct 21, 2021
63e470b
update CHANGELOG with information about breaking mail tls change
mwieser Oct 21, 2021
baccba7
Fix panic in ContainsAll util function.
mwieser Oct 22, 2021
8c7b77a
Add test to mime package and verify interface compliance for mimetype…
mwieser Oct 25, 2021
6828eb0
Changebreaking tls setting to deprecation by supporting both env vari…
mwieser Oct 25, 2021
c94f8e8
Add deprecation warning when using SERVER_SMTP_USE_TLS flag
mwieser Oct 27, 2021
97e1266
Merge remote-tracking branch 'github/master' into mwr/backport
mwieser Oct 27, 2021
886d196
Merge remote-tracking branch 'origin/master' into mwr/backport
mwieser May 30, 2022
ae9f915
Removed override of global OnMailSent hook in mock mailer
mwieser May 30, 2022
d3359f6
Add helper to test package to copy test files into a folder unique to…
mwieser May 30, 2022
4fdca52
Add util for IN and NIN query helper which use pg.StringArray as para…
mwieser May 30, 2022
c845a6f
Fixed bug in test.RunningInTest were the test env did not get recogni…
mwieser May 30, 2022
c443d97
Merge branch 'master' of github.com:allaboutapps/go-starter into mwr/…
anjankow May 10, 2023
333336b
add check if not enough emails have been sent
anjankow May 10, 2023
be48d27
fix typo in changelog
anjankow May 11, 2023
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
6 changes: 6 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,12 @@
## Unreleased

### Changed
- Extend util package with additional helper functions.
- Add MIME interface to use *mimtype.MIME or an already KnownMIME.
- Add function to detach context to avoid context cancelation. Can be used to pass context information to go routines without a deadline or cancel.
- Add oauth2 helper for PKCE extention to generate verifier and challenge.
- **DEPRECATED** Add starttls support to mailer. If you were using the `SERVER_SMTP_USE_TLS` flag before to enable TLS you need to change it to the `SERVER_SMTP_ENCRYPTION` setting and set it to `tls`.
mwieser marked this conversation as resolved.
Show resolved Hide resolved
- Extend mailer mock to support waiting for all expected mails to arrive to check asynchronously sent mails in tests.

## 2021-10-19

Expand Down
2 changes: 1 addition & 1 deletion go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ require (
github.com/volatiletech/strmangle v0.0.1
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519
golang.org/x/sys v0.0.0-20210927094055-39ccf1dd6fa6
golang.org/x/text v0.3.7
google.golang.org/api v0.57.0
)

Expand Down Expand Up @@ -101,7 +102,6 @@ require (
go.opencensus.io v0.23.0 // indirect
golang.org/x/net v0.0.0-20210913180222-943fd674d43e // indirect
golang.org/x/oauth2 v0.0.0-20210819190943-2bc19b11175f // indirect
golang.org/x/text v0.3.7 // indirect
golang.org/x/time v0.0.0-20201208040808-7e3f01d25324 // indirect
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 // indirect
google.golang.org/appengine v1.6.7 // indirect
Expand Down
7 changes: 7 additions & 0 deletions internal/api/httperrors/common.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
package httperrors

import "net/http"

var (
ErrBadRequestZeroFileSize = NewHTTPError(http.StatusBadRequest, "ZERO_FILE_SIZE", "File size of 0 is not supported.")
)
15 changes: 8 additions & 7 deletions internal/config/server_config.go
Original file line number Diff line number Diff line change
Expand Up @@ -185,13 +185,14 @@ func DefaultServiceConfigFromEnv() Server {
Transporter: util.GetEnvEnum("SERVER_MAILER_TRANSPORTER", MailerTransporterMock.String(), []string{MailerTransporterSMTP.String(), MailerTransporterMock.String()}),
},
SMTP: transport.SMTPMailTransportConfig{
Host: util.GetEnv("SERVER_SMTP_HOST", "mailhog"),
Port: util.GetEnvAsInt("SERVER_SMTP_PORT", 1025),
Username: util.GetEnv("SERVER_SMTP_USERNAME", ""),
Password: util.GetEnv("SERVER_SMTP_PASSWORD", ""),
AuthType: transport.SMTPAuthTypeFromString(util.GetEnv("SERVER_SMTP_AUTH_TYPE", transport.SMTPAuthTypeNone.String())),
UseTLS: util.GetEnvAsBool("SERVER_SMTP_USE_TLS", false),
TLSConfig: nil,
Host: util.GetEnv("SERVER_SMTP_HOST", "mailhog"),
Port: util.GetEnvAsInt("SERVER_SMTP_PORT", 1025),
Username: util.GetEnv("SERVER_SMTP_USERNAME", ""),
Password: util.GetEnv("SERVER_SMTP_PASSWORD", ""),
AuthType: transport.SMTPAuthTypeFromString(util.GetEnv("SERVER_SMTP_AUTH_TYPE", transport.SMTPAuthTypeNone.String())),
Encryption: transport.SMTPEncryption(util.GetEnvEnum("SERVER_SMTP_ENCRYPTION", transport.SMTPEncryptionNone.String(), []string{transport.SMTPEncryptionNone.String(), transport.SMTPEncryptionTLS.String(), transport.SMTPEncryptionStartTLS.String()})),
UseTLS: util.GetEnvAsBool("SERVER_SMTP_USE_TLS", false),
TLSConfig: nil,
},
Frontend: FrontendServer{
BaseURL: util.GetEnv("SERVER_FRONTEND_BASE_URL", "http://localhost:3000"),
Expand Down
26 changes: 23 additions & 3 deletions internal/mailer/transport/mock.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,19 +2,24 @@ package transport

import (
"sync"
"time"

"allaboutapps.dev/aw/go-starter/internal/util"
"github.com/jordan-wright/email"
)

type MockMailTransport struct {
sync.RWMutex
mails []*email.Email
mails []*email.Email
OnMailSent func(mail email.Email) // non pointer to prevent concurrent read errors
wg sync.WaitGroup
}

func NewMock() *MockMailTransport {
return &MockMailTransport{
RWMutex: sync.RWMutex{},
mails: make([]*email.Email, 0),
RWMutex: sync.RWMutex{},
mails: make([]*email.Email, 0),
OnMailSent: func(mail email.Email) {},
}
}

Expand All @@ -23,6 +28,7 @@ func (m *MockMailTransport) Send(mail *email.Email) error {
defer m.Unlock()

m.mails = append(m.mails, mail)
m.OnMailSent(*mail)

return nil
}
Expand All @@ -44,3 +50,17 @@ func (m *MockMailTransport) GetSentMails() []*email.Email {

return m.mails
}

// Expect adds the mailCnt to a waitgroup and sets the OnMailSent callback
// to call wg.Done()
func (m *MockMailTransport) Expect(mailCnt int) {
m.wg.Add(mailCnt)
m.OnMailSent = func(email.Email) {
m.wg.Done()
}
}

// Wait until all expected mails have arrived
func (m *MockMailTransport) Wait() {
_ = util.WaitTimeout(&m.wg, time.Second*10)
}
11 changes: 10 additions & 1 deletion internal/mailer/transport/smtp.go
Original file line number Diff line number Diff line change
Expand Up @@ -38,5 +38,14 @@ func (m *SMTPMailTransport) Send(mail *email.Email) error {
return mail.SendWithTLS(m.addr, m.auth, m.config.TLSConfig)
mwieser marked this conversation as resolved.
Show resolved Hide resolved
}

return mail.Send(m.addr, m.auth)
switch m.config.Encryption {
case SMTPEncryptionNone:
return mail.Send(m.addr, m.auth)
case SMTPEncryptionTLS:
return mail.SendWithTLS(m.addr, m.auth, m.config.TLSConfig)
case SMTPEncryptionStartTLS:
return mail.SendWithStartTLS(m.addr, m.auth, m.config.TLSConfig)
default:
return fmt.Errorf("invalid SMTP encryption %q", m.config.Encryption)
}
}
38 changes: 31 additions & 7 deletions internal/mailer/transport/smtp_config.go
Original file line number Diff line number Diff line change
Expand Up @@ -47,12 +47,36 @@ func SMTPAuthTypeFromString(s string) SMTPAuthType {
}
}

type SMTPEncryption string

const (
SMTPEncryptionNone SMTPEncryption = "none"
SMTPEncryptionTLS SMTPEncryption = "tls"
SMTPEncryptionStartTLS SMTPEncryption = "starttls"
)

func (e SMTPEncryption) String() string {
return string(e)
}

func SMTPEncryptionFromString(s string) SMTPEncryption {
switch strings.ToLower(s) {
case "tls":
return SMTPEncryptionTLS
case "starttls":
return SMTPEncryptionStartTLS
default:
return SMTPEncryptionNone
}
}

type SMTPMailTransportConfig struct {
Host string
Port int
AuthType SMTPAuthType `json:"-"` // iota
Username string
Password string `json:"-"` // sensitive
UseTLS bool
TLSConfig *tls.Config `json:"-"` // pointer
Host string
Port int
AuthType SMTPAuthType `json:"-"` // iota
Username string
Password string `json:"-"` // sensitive
Encryption SMTPEncryption `json:"-"` // iota
TLSConfig *tls.Config `json:"-"` // pointer
UseTLS bool // ! deprecated since 2021-10-25, use Encryption type 'SMTPEncryptionTLS' instead
}
10 changes: 10 additions & 0 deletions internal/util/bool.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
package util

// FalseIfNil returns false if the passed pointer is nil. Passing a pointer to a bool will return the value of the bool.
func FalseIfNil(b *bool) bool {
if b == nil {
return false
}

return *b
}
16 changes: 16 additions & 0 deletions internal/util/bool_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
package util_test

import (
"testing"

"allaboutapps.dev/aw/go-starter/internal/util"
"github.com/stretchr/testify/assert"
)

func TestFalseIfNil(t *testing.T) {
b := true
assert.True(t, util.FalseIfNil(&b))
b = false
assert.False(t, util.FalseIfNil(&b))
assert.False(t, util.FalseIfNil(nil))
}
19 changes: 18 additions & 1 deletion internal/util/context.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,18 +3,35 @@ package util
import (
"context"
"errors"
"time"
)

type contextKey string

const (
CTXKeyUser contextKey = "user"
CTXKeyAccessToken contextKey = "access_token"
CTXKeyCacheControl contextKey = "cache_control"
CTXKeyRequestID contextKey = "request_id"
CTXKeyDisableLogger contextKey = "disable_logger"
CTXKeyCacheControl contextKey = "cache_control"
)

type detachedContext struct {
parent context.Context
}

func (c detachedContext) Deadline() (time.Time, bool) { return time.Time{}, false }
func (c detachedContext) Done() <-chan struct{} { return nil }
func (c detachedContext) Err() error { return nil }
func (c detachedContext) Value(key interface{}) interface{} { return c.parent.Value(key) }

// DetachContext detaches a context by returning a wrapped struct implementing the context interface, but omitting the deadline, done and error functionality.
// Mainly used to pass context information to go routines that should not be cancelled by the context.
// ! USE THIS DETACHED CONTEXT SPARINGLY, ONLY IF ABSOLUTELY NEEDED. DO *NOT* KEEP USING A DETACHED CONTEXT FOR A PROLONGED TIME OUT OF CHAIN
func DetachContext(ctx context.Context) context.Context {
return detachedContext{ctx}
}

// RequestIDFromContext returns the ID of the (HTTP) request, returning an error if it is not present.
func RequestIDFromContext(ctx context.Context) (string, error) {
val := ctx.Value(CTXKeyRequestID)
Expand Down
83 changes: 83 additions & 0 deletions internal/util/context_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
package util_test

import (
"context"
"testing"
"time"

"allaboutapps.dev/aw/go-starter/internal/util"
"github.com/stretchr/testify/assert"
)

type contextKey string

func TestDetachContextWithCancel(t *testing.T) {
ctx, cancel := context.WithCancel(context.Background())

var key contextKey = "test"
val := 42
ctx2 := context.WithValue(ctx, key, val)
detachedContext := util.DetachContext(ctx2)

cancel()

select {
case <-ctx.Done():
t.Log("Context cancelled")
default:
t.Error("Context is not canceled")
}

select {
case <-ctx2.Done():
t.Log("Context with value cancelled")
default:
t.Error("Context with value is not canceled")
}

select {
case <-detachedContext.Done():
t.Error("Detached context is cancelled")
default:
t.Log("Detached context is not cancelled")
}

res := detachedContext.Value(key).(int)
assert.Equal(t, val, res)
}

func TestDetachContextWithDeadline(t *testing.T) {
ctx, cancel := context.WithTimeout(context.Background(), time.Second*1)
defer cancel()

var key contextKey = "test"
val := 42
ctx2 := context.WithValue(ctx, key, val)
detachedContext := util.DetachContext(ctx2)

time.Sleep(time.Second * 2)

select {
case <-ctx.Done():
t.Log("Context cancelled")
default:
t.Error("Context is not canceled")
}

select {
case <-ctx2.Done():
t.Log("Context with value cancelled")
default:
t.Error("Context with value is not canceled")
}

select {
case <-detachedContext.Done():
t.Error("Detached context is cancelled")
default:
t.Log("Detached context is not cancelled")
}

res := detachedContext.Value(key).(int)
assert.Equal(t, val, res)
}
4 changes: 4 additions & 0 deletions internal/util/currency.go
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,10 @@ func Float64PtrToInt64WithCents(f *float64) int64 {
return int64(swag.Float64Value(f) * 100)
}

func Float64ToInt64WithCents(f float64) int64 {
return int64(f * 100)
}

func Float64PtrToIntPtrWithCents(f *float64) *int {
if f == nil {
return nil
Expand Down
2 changes: 2 additions & 0 deletions internal/util/currency_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,8 @@ func TestCurrencyConversion(t *testing.T) {
res = util.IntPtrWithCentsToFloat64Ptr(&inInt)
outInt := util.Float64PtrToIntPtrWithCents(res)
assert.Equal(t, inInt, *outInt)
outInt2 := util.Float64ToInt64WithCents(*res)
assert.Equal(t, int64(inInt), outInt2)
})
}
}
Expand Down
22 changes: 22 additions & 0 deletions internal/util/db/db.go
Original file line number Diff line number Diff line change
Expand Up @@ -63,3 +63,25 @@ func NullFloat32FromFloat64Ptr(f *float64) null.Float32 {
}
return null.NewFloat32(float32(*f), true)
}

func NullIntFromInt16Ptr(i *int16) null.Int {
if i == nil {
return null.NewInt(0, false)
}
return null.NewInt(int(*i), true)
}

func Int16PtrFromNullInt(i null.Int) *int16 {
if !i.Valid {
return nil
}

res := int16(i.Int)
return &res
}

func Int16PtrFromInt(i int) *int16 {
res := int16(i)

return &res
}
Loading