add: contactController,tests & refactored tests
This commit is contained in:
@@ -1,5 +1,5 @@
|
|||||||
{
|
{
|
||||||
"base_url": "https://domain.de",
|
"BaseURL": "https://domain.de",
|
||||||
"db": {
|
"db": {
|
||||||
"Path": "data/db.sqlite3"
|
"Path": "data/db.sqlite3"
|
||||||
},
|
},
|
||||||
@@ -18,5 +18,9 @@
|
|||||||
},
|
},
|
||||||
"auth": {
|
"auth": {
|
||||||
"APIKey": ""
|
"APIKey": ""
|
||||||
|
},
|
||||||
|
"recipients": {
|
||||||
|
"ContactForm": "contacts@server.com",
|
||||||
|
"UserRegistration": "registration@server.com"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -43,12 +43,18 @@ type TemplateConfig struct {
|
|||||||
StaticPath string `json:"StaticPath" default:"templates/css" envconfig:"TEMPLATE_STATIC_PATH"`
|
StaticPath string `json:"StaticPath" default:"templates/css" envconfig:"TEMPLATE_STATIC_PATH"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type RecipientsConfig struct {
|
||||||
|
ContactForm string `json:"ContactForm" envconfig:"RECIPIENT_CONTACT_FORM"`
|
||||||
|
UserRegistration string `json:"UserRegistration" envconfig:"RECIPIENT_USER_REGISTRATION"`
|
||||||
|
}
|
||||||
|
|
||||||
type Config struct {
|
type Config struct {
|
||||||
ConfigFilePath string `json:"config_file_path" envconfig:"CONFIG_FILE_PATH"`
|
|
||||||
BaseURL string `json:"base_url" envconfig:"BASE_URL"`
|
|
||||||
Auth AuthenticationConfig `json:"auth"`
|
Auth AuthenticationConfig `json:"auth"`
|
||||||
DB DatabaseConfig `json:"db"`
|
|
||||||
Templates TemplateConfig `json:"templates"`
|
Templates TemplateConfig `json:"templates"`
|
||||||
|
Recipients RecipientsConfig `json:"recipients"`
|
||||||
|
ConfigFilePath string `json:"config_file_path" envconfig:"CONFIG_FILE_PATH"`
|
||||||
|
BaseURL string `json:"BaseUrl" envconfig:"BASE_URL"`
|
||||||
|
DB DatabaseConfig `json:"db"`
|
||||||
SMTP SMTPConfig `json:"smtp"`
|
SMTP SMTPConfig `json:"smtp"`
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -60,6 +66,7 @@ var (
|
|||||||
DB DatabaseConfig
|
DB DatabaseConfig
|
||||||
Templates TemplateConfig
|
Templates TemplateConfig
|
||||||
SMTP SMTPConfig
|
SMTP SMTPConfig
|
||||||
|
Recipients RecipientsConfig
|
||||||
)
|
)
|
||||||
|
|
||||||
// LoadConfig initializes the configuration by reading from a file and environment variables.
|
// LoadConfig initializes the configuration by reading from a file and environment variables.
|
||||||
@@ -86,6 +93,7 @@ func LoadConfig() {
|
|||||||
Templates = CFG.Templates
|
Templates = CFG.Templates
|
||||||
SMTP = CFG.SMTP
|
SMTP = CFG.SMTP
|
||||||
BaseURL = CFG.BaseURL
|
BaseURL = CFG.BaseURL
|
||||||
|
Recipients = CFG.Recipients
|
||||||
}
|
}
|
||||||
|
|
||||||
// readFile reads the configuration from the specified file path into the provided Config struct.
|
// readFile reads the configuration from the specified file path into the provided Config struct.
|
||||||
|
|||||||
@@ -15,30 +15,33 @@ type ContactController struct {
|
|||||||
EmailService *services.EmailService
|
EmailService *services.EmailService
|
||||||
}
|
}
|
||||||
type contactData struct {
|
type contactData struct {
|
||||||
email string `validate:"required,email"`
|
Email string `form:"email" validate:"required,email"`
|
||||||
name string
|
Name string `form:"name"`
|
||||||
message string `validate:"required"`
|
Message string `form:"message" validate:"required"`
|
||||||
|
Honeypot string `form:"username" validate:"eq="`
|
||||||
}
|
}
|
||||||
|
|
||||||
func (cc *ContactController) RelayContactRequest(c *gin.Context) {
|
func (cc *ContactController) RelayContactRequest(c *gin.Context) {
|
||||||
var msgData contactData
|
var msgData contactData
|
||||||
if c.Query("username") != "" {
|
|
||||||
|
if err := c.ShouldBind(&msgData); err != nil {
|
||||||
// A bot is talking to us
|
// A bot is talking to us
|
||||||
|
c.JSON(http.StatusNotAcceptable, gin.H{"error": "Not Acceptable"})
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
msgData.name = c.Query("name")
|
|
||||||
msgData.email = c.Query("email")
|
|
||||||
msgData.message = c.Query("message")
|
|
||||||
|
|
||||||
validate := validator.New()
|
validate := validator.New()
|
||||||
if err := validate.Struct(msgData); err != nil {
|
if err := validate.Struct(msgData); err != nil {
|
||||||
logger.Error.Printf("Couldn't validate contact form data: %v", err)
|
logger.Error.Printf("Couldn't validate contact form data: %v", err)
|
||||||
c.JSON(http.StatusNotAcceptable, gin.H{"error": "Couldn't validate contact form data"})
|
c.JSON(http.StatusNotAcceptable, gin.H{"error": "Couldn't validate contact form data"})
|
||||||
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := cc.EmailService.RelayContactFormMessage(&msgData.email, &msgData.name, &msgData.message); err != nil {
|
if err := cc.EmailService.RelayContactFormMessage(msgData.Email, msgData.Name, msgData.Message); err != nil {
|
||||||
logger.Error.Printf("Couldn't send contact message mail: %v", err)
|
logger.Error.Printf("Couldn't send contact message mail: %v", err)
|
||||||
c.JSON(http.StatusInternalServerError, gin.H{"error": "Couldn't send mail"})
|
c.JSON(http.StatusInternalServerError, gin.H{"error": "Couldn't send mail"})
|
||||||
|
return
|
||||||
}
|
}
|
||||||
c.JSON(http.StatusOK, "Your message has been sent")
|
|
||||||
|
c.JSON(http.StatusAccepted, "Your message has been sent")
|
||||||
}
|
}
|
||||||
|
|||||||
157
internal/controllers/contactController_test.go
Normal file
157
internal/controllers/contactController_test.go
Normal file
@@ -0,0 +1,157 @@
|
|||||||
|
package controllers
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"net/http"
|
||||||
|
"net/http/httptest"
|
||||||
|
"net/url"
|
||||||
|
"strings"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"GoMembership/internal/config"
|
||||||
|
"GoMembership/internal/constants"
|
||||||
|
"GoMembership/internal/utils"
|
||||||
|
|
||||||
|
"github.com/gin-gonic/gin"
|
||||||
|
)
|
||||||
|
|
||||||
|
type RelayContactRequestTest struct {
|
||||||
|
Input url.Values
|
||||||
|
Name string
|
||||||
|
WantResponse int
|
||||||
|
Assert bool
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestContactController(t *testing.T) {
|
||||||
|
|
||||||
|
tests := getContactData()
|
||||||
|
for _, tt := range tests {
|
||||||
|
t.Run(tt.Name, func(t *testing.T) {
|
||||||
|
if err := runSingleTest(&tt); err != nil {
|
||||||
|
t.Errorf("Test failed: %v", err.Error())
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (rt *RelayContactRequestTest) SetupContext() (*gin.Context, *httptest.ResponseRecorder) {
|
||||||
|
return GetMockedFormContext(rt.Input, "/contact")
|
||||||
|
}
|
||||||
|
|
||||||
|
func (rt *RelayContactRequestTest) RunHandler(c *gin.Context) {
|
||||||
|
Cc.RelayContactRequest(c)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (rt *RelayContactRequestTest) ValidateResponse(w *httptest.ResponseRecorder) error {
|
||||||
|
if w.Code != rt.WantResponse {
|
||||||
|
return fmt.Errorf("Didn't get the expected response code: got: %v; expected: %v", w.Code, rt.WantResponse)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (rt *RelayContactRequestTest) ValidateResult() error {
|
||||||
|
|
||||||
|
messages := utils.SMTPGetMessages()
|
||||||
|
|
||||||
|
for _, message := range messages {
|
||||||
|
|
||||||
|
mail, err := utils.DecodeMail(message.MsgRequest())
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if strings.Contains(mail.Subject, constants.MailContactSubject) {
|
||||||
|
|
||||||
|
if err := checkContactRequestMail(mail, rt); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
return fmt.Errorf("Subject not expected: %v", mail.Subject)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func checkContactRequestMail(mail *utils.Email, rt *RelayContactRequestTest) error {
|
||||||
|
|
||||||
|
if !strings.Contains(mail.To, config.Recipients.ContactForm) {
|
||||||
|
return fmt.Errorf("Contact Information didn't reach the admin! Recipient was: %v instead of %v", mail.To, config.Recipients.ContactForm)
|
||||||
|
}
|
||||||
|
if !strings.Contains(mail.From, config.SMTP.User) {
|
||||||
|
return fmt.Errorf("Contact Information was sent from unexpected address! Sender was: %v instead of %v", mail.From, config.SMTP.User)
|
||||||
|
}
|
||||||
|
|
||||||
|
//Check if all the relevant data has been passed to the mail.
|
||||||
|
if !strings.Contains(mail.Body, rt.Input.Get("name")) {
|
||||||
|
return fmt.Errorf("User name(%v) has not been rendered in contact mail.", rt.Input.Get("name"))
|
||||||
|
}
|
||||||
|
|
||||||
|
if !strings.Contains(mail.Body, rt.Input.Get("message")) {
|
||||||
|
return fmt.Errorf("User message(%v) has not been rendered in contact mail.", rt.Input.Get("message"))
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func getBaseRequest() *url.Values {
|
||||||
|
return &url.Values{
|
||||||
|
"username": {""},
|
||||||
|
"name": {"My-First and-Last-Name"},
|
||||||
|
"email": {"name@domain.de"},
|
||||||
|
"message": {"My message to the world"},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func customizeRequest(updates map[string]string) *url.Values {
|
||||||
|
form := getBaseRequest()
|
||||||
|
for key, value := range updates {
|
||||||
|
form.Set(key, value)
|
||||||
|
}
|
||||||
|
return form
|
||||||
|
}
|
||||||
|
|
||||||
|
func getContactData() []RelayContactRequestTest {
|
||||||
|
return []RelayContactRequestTest{
|
||||||
|
{
|
||||||
|
Name: "mail empty, should fail",
|
||||||
|
WantResponse: http.StatusNotAcceptable,
|
||||||
|
Assert: false,
|
||||||
|
Input: *customizeRequest(
|
||||||
|
map[string]string{
|
||||||
|
"email": "",
|
||||||
|
}),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Name: "mail invalid, should fail",
|
||||||
|
WantResponse: http.StatusNotAcceptable,
|
||||||
|
Assert: false,
|
||||||
|
Input: *customizeRequest(
|
||||||
|
map[string]string{
|
||||||
|
"email": "novalid#email.de",
|
||||||
|
}),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Name: "No message should fail",
|
||||||
|
WantResponse: http.StatusNotAcceptable,
|
||||||
|
Assert: true,
|
||||||
|
Input: *customizeRequest(
|
||||||
|
map[string]string{
|
||||||
|
"message": "",
|
||||||
|
}),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Name: "Honeypot set, should fail",
|
||||||
|
WantResponse: http.StatusNotAcceptable,
|
||||||
|
Assert: true,
|
||||||
|
Input: *customizeRequest(
|
||||||
|
map[string]string{
|
||||||
|
"username": "I'm a bot",
|
||||||
|
}),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Name: "Correct message, should pass",
|
||||||
|
WantResponse: http.StatusAccepted,
|
||||||
|
Assert: true,
|
||||||
|
Input: *customizeRequest(
|
||||||
|
map[string]string{}),
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
178
internal/controllers/controllers_test.go
Normal file
178
internal/controllers/controllers_test.go
Normal file
@@ -0,0 +1,178 @@
|
|||||||
|
package controllers
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"encoding/json"
|
||||||
|
"net/http"
|
||||||
|
"net/http/httptest"
|
||||||
|
"net/url"
|
||||||
|
"os"
|
||||||
|
"strconv"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"log"
|
||||||
|
|
||||||
|
"github.com/gin-gonic/gin"
|
||||||
|
|
||||||
|
"GoMembership/internal/config"
|
||||||
|
"GoMembership/internal/database"
|
||||||
|
"GoMembership/internal/models"
|
||||||
|
"GoMembership/internal/repositories"
|
||||||
|
"GoMembership/internal/services"
|
||||||
|
"GoMembership/internal/utils"
|
||||||
|
"GoMembership/pkg/logger"
|
||||||
|
)
|
||||||
|
|
||||||
|
type TestCase interface {
|
||||||
|
SetupContext() (*gin.Context, *httptest.ResponseRecorder)
|
||||||
|
RunHandler(*gin.Context)
|
||||||
|
ValidateResponse(*httptest.ResponseRecorder) error
|
||||||
|
ValidateResult() error
|
||||||
|
}
|
||||||
|
|
||||||
|
const (
|
||||||
|
Host = "127.0.0.1"
|
||||||
|
Port int = 2525
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
Uc *UserController
|
||||||
|
Mc *MembershipController
|
||||||
|
Cc *ContactController
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestSuite(t *testing.T) {
|
||||||
|
_ = deleteTestDB("test.db")
|
||||||
|
if err := database.InitDB("test.db"); err != nil {
|
||||||
|
log.Fatalf("Failed to create DB: %#v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := os.Setenv("SMTP_HOST", Host); err != nil {
|
||||||
|
log.Fatalf("Error setting environment variable: %v", err)
|
||||||
|
}
|
||||||
|
if err := os.Setenv("SMTP_PORT", strconv.Itoa(Port)); err != nil {
|
||||||
|
log.Fatalf("Error setting environment variable: %v", err)
|
||||||
|
}
|
||||||
|
if err := os.Setenv("BASE_URL", "http://"+Host+":2525"); err != nil {
|
||||||
|
log.Fatalf("Error setting environment variable: %v", err)
|
||||||
|
}
|
||||||
|
config.LoadConfig()
|
||||||
|
utils.SMTPStart(Host, Port)
|
||||||
|
emailService := services.NewEmailService(config.SMTP.Host, config.SMTP.Port, config.SMTP.User, config.SMTP.Password)
|
||||||
|
var consentRepo repositories.ConsentRepositoryInterface = &repositories.ConsentRepository{}
|
||||||
|
consentService := &services.ConsentService{Repo: consentRepo}
|
||||||
|
|
||||||
|
var bankAccountRepo repositories.BankAccountRepositoryInterface = &repositories.BankAccountRepository{}
|
||||||
|
bankAccountService := &services.BankAccountService{Repo: bankAccountRepo}
|
||||||
|
|
||||||
|
var membershipRepo repositories.MembershipRepositoryInterface = &repositories.MembershipRepository{}
|
||||||
|
var subscriptionRepo repositories.SubscriptionModelsRepositoryInterface = &repositories.SubscriptionModelsRepository{}
|
||||||
|
membershipService := &services.MembershipService{Repo: membershipRepo, SubscriptionRepo: subscriptionRepo}
|
||||||
|
|
||||||
|
var userRepo repositories.UserRepositoryInterface = &repositories.UserRepository{}
|
||||||
|
userService := &services.UserService{Repo: userRepo}
|
||||||
|
|
||||||
|
Uc = &UserController{Service: userService, EmailService: emailService, ConsentService: consentService, BankAccountService: bankAccountService, MembershipService: membershipService}
|
||||||
|
Mc = &MembershipController{Service: *membershipService}
|
||||||
|
Cc = &ContactController{EmailService: emailService}
|
||||||
|
|
||||||
|
if err := initSubscriptionPlans(); err != nil {
|
||||||
|
log.Fatalf("Failed to init Subscription plans: %#v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Run all tests
|
||||||
|
// code := m.Run()
|
||||||
|
|
||||||
|
t.Run("userController", func(t *testing.T) {
|
||||||
|
TestUserController(t)
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("contactController", func(t *testing.T) {
|
||||||
|
TestContactController(t)
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("membershipController", func(t *testing.T) {
|
||||||
|
TestMembershipController(t)
|
||||||
|
})
|
||||||
|
|
||||||
|
if err := utils.SMTPStop(); err != nil {
|
||||||
|
log.Fatalf("Failed to stop SMTP Mockup Server: %#v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := deleteTestDB("test.db"); err != nil {
|
||||||
|
log.Fatalf("Failed to tear down DB: %#v", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func initSubscriptionPlans() error {
|
||||||
|
subscription := models.SubscriptionModel{
|
||||||
|
Name: "Basic",
|
||||||
|
Details: "Test Plan",
|
||||||
|
MonthlyFee: 2,
|
||||||
|
HourlyRate: 3,
|
||||||
|
}
|
||||||
|
result := database.DB.Create(&subscription)
|
||||||
|
if result.Error != nil {
|
||||||
|
return result.Error
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func GetMockedJSONContext(jsonStr []byte, url string) (*gin.Context, *httptest.ResponseRecorder) {
|
||||||
|
gin.SetMode(gin.TestMode)
|
||||||
|
w := httptest.NewRecorder()
|
||||||
|
c, _ := gin.CreateTestContext(w)
|
||||||
|
var err error
|
||||||
|
c.Request, err = http.NewRequest("POST", url, bytes.NewBuffer(jsonStr))
|
||||||
|
if err != nil {
|
||||||
|
log.Fatalf("Failed to create new Request: %#v", err)
|
||||||
|
}
|
||||||
|
c.Request.Header.Set("Content-Type", "application/json")
|
||||||
|
|
||||||
|
return c, w
|
||||||
|
}
|
||||||
|
|
||||||
|
func GetMockedFormContext(formData url.Values, url string) (*gin.Context, *httptest.ResponseRecorder) {
|
||||||
|
gin.SetMode(gin.TestMode)
|
||||||
|
w := httptest.NewRecorder()
|
||||||
|
c, _ := gin.CreateTestContext(w)
|
||||||
|
|
||||||
|
req, err := http.NewRequest("POST", url, bytes.NewBufferString(formData.Encode()))
|
||||||
|
if err != nil {
|
||||||
|
log.Fatalf("Failed to create new Request: %#v", err)
|
||||||
|
}
|
||||||
|
req.Header.Set("Content-Type", "application/x-www-form-urlencoded")
|
||||||
|
c.Request = req
|
||||||
|
|
||||||
|
return c, w
|
||||||
|
}
|
||||||
|
|
||||||
|
func deleteTestDB(dbPath string) error {
|
||||||
|
err := os.Remove(dbPath)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func runSingleTest(tc TestCase) error {
|
||||||
|
c, w := tc.SetupContext()
|
||||||
|
tc.RunHandler(c)
|
||||||
|
|
||||||
|
if err := tc.ValidateResponse(w); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return tc.ValidateResult()
|
||||||
|
}
|
||||||
|
|
||||||
|
func GenerateInputJSON(aStruct interface{}) string {
|
||||||
|
|
||||||
|
// Marshal the object into JSON
|
||||||
|
jsonBytes, err := json.Marshal(aStruct)
|
||||||
|
if err != nil {
|
||||||
|
logger.Error.Fatalf("Couldn't generate JSON: %#v\nERROR: %#v", aStruct, err)
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
return string(jsonBytes)
|
||||||
|
}
|
||||||
@@ -40,15 +40,17 @@ func (mc *MembershipController) RegisterSubscription(c *gin.Context) {
|
|||||||
c.JSON(http.StatusExpectationFailed, "API Key is missing")
|
c.JSON(http.StatusExpectationFailed, "API Key is missing")
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
logger.Info.Printf("registering subscription: %+v", regData)
|
|
||||||
|
|
||||||
// Register Subscription
|
// Register Subscription
|
||||||
id, err := mc.Service.RegisterSubscription(®Data.Model)
|
id, err := mc.Service.RegisterSubscription(®Data.Model)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
logger.Error.Printf("Couldn't register Membershipmodel: %v", err)
|
logger.Error.Printf("Couldn't register Membershipmodel: %v", err)
|
||||||
c.JSON(http.StatusInternalServerError, "Couldn't register Membershipmodel")
|
c.JSON(int(id), "Couldn't register Membershipmodel")
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
regData.Model.ID = id
|
logger.Info.Printf("registering subscription: %+v", regData)
|
||||||
|
c.JSON(http.StatusCreated, gin.H{
|
||||||
|
"status": "success",
|
||||||
|
"id": id,
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|||||||
119
internal/controllers/membershipController_test.go
Normal file
119
internal/controllers/membershipController_test.go
Normal file
@@ -0,0 +1,119 @@
|
|||||||
|
package controllers
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"net/http"
|
||||||
|
"net/http/httptest"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"GoMembership/internal/config"
|
||||||
|
"GoMembership/internal/models"
|
||||||
|
|
||||||
|
"github.com/gin-gonic/gin"
|
||||||
|
)
|
||||||
|
|
||||||
|
type RegisterSubscriptionTest struct {
|
||||||
|
WantDBData map[string]interface{}
|
||||||
|
Input string
|
||||||
|
Name string
|
||||||
|
WantResponse int
|
||||||
|
Assert bool
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestMembershipController(t *testing.T) {
|
||||||
|
|
||||||
|
tests := getSubscriptionData()
|
||||||
|
for _, tt := range tests {
|
||||||
|
t.Run(tt.Name, func(t *testing.T) {
|
||||||
|
if err := runSingleTest(&tt); err != nil {
|
||||||
|
t.Errorf("Test failed: %v", err.Error())
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (rt *RegisterSubscriptionTest) SetupContext() (*gin.Context, *httptest.ResponseRecorder) {
|
||||||
|
return GetMockedJSONContext([]byte(rt.Input), "register/subscription")
|
||||||
|
}
|
||||||
|
|
||||||
|
func (rt *RegisterSubscriptionTest) RunHandler(c *gin.Context) {
|
||||||
|
Mc.RegisterSubscription(c)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (rt *RegisterSubscriptionTest) ValidateResponse(w *httptest.ResponseRecorder) error {
|
||||||
|
if w.Code != rt.WantResponse {
|
||||||
|
return fmt.Errorf("Didn't get the expected response code: got: %v; expected: %v", w.Code, rt.WantResponse)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (rt *RegisterSubscriptionTest) ValidateResult() error {
|
||||||
|
return validateSubscription(rt.Assert, rt.WantDBData)
|
||||||
|
}
|
||||||
|
|
||||||
|
func validateSubscription(assert bool, wantDBData map[string]interface{}) error {
|
||||||
|
subscriptions, err := Mc.Service.GetSubscriptions(wantDBData)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("Error in database ops: %#v", err)
|
||||||
|
}
|
||||||
|
if assert != (len(*subscriptions) != 0) {
|
||||||
|
return fmt.Errorf("Subscription entry query didn't met expectation: %v != %#v", assert, *subscriptions)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func getBaseSubscription() MembershipData {
|
||||||
|
return MembershipData{
|
||||||
|
APIKey: config.Auth.APIKEY,
|
||||||
|
Model: models.SubscriptionModel{
|
||||||
|
Name: "Just a Subscription",
|
||||||
|
Details: "A subscription detail",
|
||||||
|
MonthlyFee: 12.0,
|
||||||
|
HourlyRate: 14.0,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
func customizeSubscription(customize func(MembershipData) MembershipData) MembershipData {
|
||||||
|
subscription := getBaseSubscription()
|
||||||
|
return customize(subscription)
|
||||||
|
}
|
||||||
|
|
||||||
|
func getSubscriptionData() []RegisterSubscriptionTest {
|
||||||
|
return []RegisterSubscriptionTest{
|
||||||
|
{
|
||||||
|
Name: "No Details should fail",
|
||||||
|
WantResponse: http.StatusNotAcceptable,
|
||||||
|
WantDBData: map[string]interface{}{"name": "Just a Subscription"},
|
||||||
|
Assert: false,
|
||||||
|
Input: GenerateInputJSON(
|
||||||
|
customizeSubscription(func(subscription MembershipData) MembershipData {
|
||||||
|
subscription.Model.Details = ""
|
||||||
|
return subscription
|
||||||
|
})),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Name: "No Model Name should fail",
|
||||||
|
WantResponse: http.StatusNotAcceptable,
|
||||||
|
WantDBData: map[string]interface{}{"name": ""},
|
||||||
|
Assert: false,
|
||||||
|
Input: GenerateInputJSON(
|
||||||
|
customizeSubscription(func(subscription MembershipData) MembershipData {
|
||||||
|
subscription.Model.Name = ""
|
||||||
|
return subscription
|
||||||
|
})),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Name: "correct entry should pass",
|
||||||
|
WantResponse: http.StatusCreated,
|
||||||
|
WantDBData: map[string]interface{}{"name": "Just a Subscription"},
|
||||||
|
Assert: true,
|
||||||
|
Input: GenerateInputJSON(
|
||||||
|
customizeSubscription(func(subscription MembershipData) MembershipData {
|
||||||
|
subscription.Model.Conditions = "Some Condition"
|
||||||
|
subscription.Model.IncludedPerYear = 0
|
||||||
|
subscription.Model.IncludedPerMonth = 1
|
||||||
|
return subscription
|
||||||
|
})),
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -88,7 +88,7 @@ func (uc *UserController) RegisterUser(c *gin.Context) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Notify admin of new user registration
|
// Notify admin of new user registration
|
||||||
if err := uc.EmailService.NotifyAdminOfNewUser(®Data.User); err != nil {
|
if err := uc.EmailService.SendRegistrationNotification(®Data.User); err != nil {
|
||||||
logger.Error.Printf("Failed to notify admin of new user registration: %v", err)
|
logger.Error.Printf("Failed to notify admin of new user registration: %v", err)
|
||||||
// Proceed without returning error since user registration is successful
|
// Proceed without returning error since user registration is successful
|
||||||
}
|
}
|
||||||
@@ -112,6 +112,7 @@ func (uc *UserController) VerifyMailHandler(c *gin.Context) {
|
|||||||
c.HTML(http.StatusUnauthorized, "verification_error.html", gin.H{"ErrorMessage": "Emailadresse wurde schon bestätigt. Sollte dies nicht der Fall sein, wende Dich bitte an info@carsharing-hasloh.de."})
|
c.HTML(http.StatusUnauthorized, "verification_error.html", gin.H{"ErrorMessage": "Emailadresse wurde schon bestätigt. Sollte dies nicht der Fall sein, wende Dich bitte an info@carsharing-hasloh.de."})
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
logger.Info.Printf("User: %#v", user)
|
||||||
|
|
||||||
uc.EmailService.SendWelcomeEmail(user)
|
uc.EmailService.SendWelcomeEmail(user)
|
||||||
c.HTML(http.StatusOK, "verification_success.html", gin.H{"FirstName": user.FirstName})
|
c.HTML(http.StatusOK, "verification_success.html", gin.H{"FirstName": user.FirstName})
|
||||||
|
|||||||
@@ -1,130 +1,66 @@
|
|||||||
package controllers
|
package controllers
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"bytes"
|
|
||||||
"encoding/json"
|
|
||||||
"fmt"
|
"fmt"
|
||||||
"strconv"
|
|
||||||
"strings"
|
|
||||||
|
|
||||||
"net/http"
|
"net/http"
|
||||||
"net/http/httptest"
|
"net/http/httptest"
|
||||||
"os"
|
"net/url"
|
||||||
|
"path/filepath"
|
||||||
|
"regexp"
|
||||||
|
"strings"
|
||||||
"testing"
|
"testing"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/gin-gonic/gin"
|
"github.com/gin-gonic/gin"
|
||||||
smtpmock "github.com/mocktools/go-smtp-mock/v2"
|
|
||||||
|
|
||||||
"GoMembership/internal/config"
|
"GoMembership/internal/config"
|
||||||
"GoMembership/internal/constants"
|
"GoMembership/internal/constants"
|
||||||
"GoMembership/internal/database"
|
|
||||||
"GoMembership/internal/models"
|
"GoMembership/internal/models"
|
||||||
"GoMembership/internal/repositories"
|
|
||||||
"GoMembership/internal/services"
|
|
||||||
"GoMembership/internal/utils"
|
"GoMembership/internal/utils"
|
||||||
"GoMembership/pkg/logger"
|
"GoMembership/pkg/logger"
|
||||||
)
|
)
|
||||||
|
|
||||||
type test struct {
|
type RegisterUserTest struct {
|
||||||
name string
|
WantDBData map[string]interface{}
|
||||||
input string
|
Name string
|
||||||
wantDBData map[string]interface{}
|
Input string
|
||||||
wantResponse uint16
|
WantResponse int
|
||||||
assert bool
|
Assert bool
|
||||||
}
|
}
|
||||||
|
|
||||||
// type RegistrationData struct {
|
func (rt *RegisterUserTest) SetupContext() (*gin.Context, *httptest.ResponseRecorder) {
|
||||||
// User models.User `json:"user"`
|
return GetMockedJSONContext([]byte(rt.Input), "register")
|
||||||
// }
|
}
|
||||||
|
|
||||||
const (
|
func (rt *RegisterUserTest) RunHandler(c *gin.Context) {
|
||||||
Host = "127.0.0.1"
|
Uc.RegisterUser(c)
|
||||||
Port int = 2525
|
}
|
||||||
User = "alex@mail.de"
|
|
||||||
Pass = "secret"
|
|
||||||
AdminMail = "admin@mail.de"
|
|
||||||
)
|
|
||||||
|
|
||||||
var (
|
func (rt *RegisterUserTest) ValidateResponse(w *httptest.ResponseRecorder) error {
|
||||||
uc UserController
|
if w.Code != rt.WantResponse {
|
||||||
)
|
return fmt.Errorf("Didn't get the expected response code: got: %v; expected: %v", w.Code, rt.WantResponse)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (rt *RegisterUserTest) ValidateResult() error {
|
||||||
|
return validateUser(rt.Assert, rt.WantDBData)
|
||||||
|
}
|
||||||
|
|
||||||
func TestUserController(t *testing.T) {
|
func TestUserController(t *testing.T) {
|
||||||
_ = deleteTestDB("test.db")
|
|
||||||
|
|
||||||
if err := database.InitDB("test.db"); err != nil {
|
|
||||||
t.Errorf("Failed to create DB: %#v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
if err := os.Setenv("SMTP_HOST", Host); err != nil {
|
|
||||||
t.Errorf("Error setting environment variable: %v", err)
|
|
||||||
}
|
|
||||||
if err := os.Setenv("SMTP_PORT", strconv.Itoa(Port)); err != nil {
|
|
||||||
t.Errorf("Error setting environment variable: %v", err)
|
|
||||||
}
|
|
||||||
if err := os.Setenv("ADMIN_MAIL", AdminMail); err != nil {
|
|
||||||
t.Errorf("Error setting environment variable: %v", err)
|
|
||||||
}
|
|
||||||
if err := os.Setenv("SMTP_USER", User); err != nil {
|
|
||||||
t.Errorf("Error setting environment variable: %v", err)
|
|
||||||
}
|
|
||||||
if err := os.Setenv("SMTP_PASS", Pass); err != nil {
|
|
||||||
t.Errorf("Error setting environment variable: %v", err)
|
|
||||||
}
|
|
||||||
config.LoadConfig()
|
|
||||||
utils.SMTPStart(Host, Port)
|
|
||||||
emailService := services.NewEmailService(config.SMTP.Host, config.SMTP.Port, config.SMTP.User, config.SMTP.Password, config.SMTP.AdminEmail)
|
|
||||||
var consentRepo repositories.ConsentRepositoryInterface = &repositories.ConsentRepository{}
|
|
||||||
consentService := &services.ConsentService{Repo: consentRepo}
|
|
||||||
|
|
||||||
var bankAccountRepo repositories.BankAccountRepositoryInterface = &repositories.BankAccountRepository{}
|
|
||||||
bankAccountService := &services.BankAccountService{Repo: bankAccountRepo}
|
|
||||||
|
|
||||||
var membershipRepo repositories.MembershipRepositoryInterface = &repositories.MembershipRepository{}
|
|
||||||
var subscriptionRepo repositories.SubscriptionModelsRepositoryInterface = &repositories.SubscriptionModelsRepository{}
|
|
||||||
membershipService := &services.MembershipService{Repo: membershipRepo, SubscriptionRepo: subscriptionRepo}
|
|
||||||
|
|
||||||
var userRepo repositories.UserRepositoryInterface = &repositories.UserRepository{}
|
|
||||||
userService := &services.UserService{Repo: userRepo}
|
|
||||||
|
|
||||||
uc = UserController{Service: userService, EmailService: emailService, ConsentService: consentService, BankAccountService: bankAccountService, MembershipService: membershipService}
|
|
||||||
|
|
||||||
if err := initSubscriptionPlans(); err != nil {
|
|
||||||
t.Errorf("Failed to init Susbcription plans: %#v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
tests := getTestUsers()
|
tests := getTestUsers()
|
||||||
for _, tt := range tests {
|
for _, tt := range tests {
|
||||||
t.Run(tt.name, func(t *testing.T) {
|
t.Run(tt.Name, func(t *testing.T) {
|
||||||
if err := runSingleTest(&tt); err != nil {
|
if err := runSingleTest(&tt); err != nil {
|
||||||
t.Errorf("Test failed: %v", err.Error())
|
t.Errorf("Test failed: %v", err.Error())
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := utils.SMTPStop(); err != nil {
|
|
||||||
t.Errorf("Failed to stop SMTP Mockup Server: %#v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
if err := deleteTestDB("test.db"); err != nil {
|
|
||||||
t.Errorf("Failed to tear down DB: %#v", err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func runSingleTest(tt *test) error {
|
|
||||||
|
|
||||||
c, w := getMockedContext([]byte(tt.input))
|
|
||||||
uc.RegisterUser(c)
|
|
||||||
|
|
||||||
if w.Code != int(tt.wantResponse) {
|
|
||||||
return fmt.Errorf("Didn't get the expected response code: got: %v; expected: %v", w.Code, tt.wantResponse)
|
|
||||||
}
|
|
||||||
return validateUser(tt.assert, tt.wantDBData)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func validateUser(assert bool, wantDBData map[string]interface{}) error {
|
func validateUser(assert bool, wantDBData map[string]interface{}) error {
|
||||||
users, err := uc.Service.GetUsers(wantDBData)
|
users, err := Uc.Service.GetUsers(wantDBData)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("Error in database ops: %#v", err)
|
return fmt.Errorf("Error in database ops: %#v", err)
|
||||||
}
|
}
|
||||||
@@ -137,131 +73,179 @@ func validateUser(assert bool, wantDBData map[string]interface{}) error {
|
|||||||
//check for email delivery
|
//check for email delivery
|
||||||
messages := utils.SMTPGetMessages()
|
messages := utils.SMTPGetMessages()
|
||||||
for _, message := range messages {
|
for _, message := range messages {
|
||||||
|
mail, err := utils.DecodeMail(message.MsgRequest())
|
||||||
if strings.Contains(message.MsgRequest(), constants.MailWelcomeSubject) {
|
if err != nil {
|
||||||
if err := checkWelcomeMail(&message); err != nil {
|
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
} else if strings.Contains(message.MsgRequest(), constants.MailRegistrationSubject) {
|
if strings.Contains(mail.Subject, constants.MailRegistrationSubject) {
|
||||||
if err := checkRegistrationMail(&message, &(*users)[0]); err != nil {
|
if err := checkRegistrationMail(mail, &(*users)[0]); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
} else if strings.Contains(message.MsgRequest(), constants.MailVerificationSubject) {
|
} else if strings.Contains(mail.Subject, constants.MailVerificationSubject) {
|
||||||
if err := checkVerificationMail(&message, &(*users)[0]); err != nil {
|
if err := checkVerificationMail(mail, &(*users)[0]); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
verifiedUsers, err := Uc.Service.GetUsers(wantDBData)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if (*verifiedUsers)[0].Status != constants.VerifiedStatus {
|
||||||
|
return fmt.Errorf("Users status isn't verified after email verification. Status is: %#v", (*verifiedUsers)[0].Status)
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
return fmt.Errorf("Subject not expected: %v", message.MsgRequest())
|
return fmt.Errorf("Subject not expected: %v", mail.Subject)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func checkWelcomeMail(message *smtpmock.Message) error {
|
func checkWelcomeMail(message *utils.Email, user *models.User) error {
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func checkRegistrationMail(message *smtpmock.Message, user *models.User) error {
|
if !strings.Contains(message.To, user.Email) {
|
||||||
|
return fmt.Errorf("Registration Information didn't reach the user! Recipient was: %v instead of %v", message.To, user.Email)
|
||||||
|
}
|
||||||
|
if !strings.Contains(message.From, config.SMTP.User) {
|
||||||
|
return fmt.Errorf("Registration Information was sent from unexpected address! Sender was: %v instead of %v", message.From, config.SMTP.User)
|
||||||
|
}
|
||||||
|
|
||||||
for _, responses := range message.RcpttoRequestResponse() {
|
|
||||||
if !strings.Contains(responses[0], AdminMail) {
|
|
||||||
return fmt.Errorf("Registration Information didn't reach admin! Recipient was: %v instead of %v", responses[0], AdminMail)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if !strings.Contains(message.MailfromRequest(), User) {
|
|
||||||
return fmt.Errorf("Registration Information was sent from unexpected address! Sender was: %v instead of %v", message.MailfromRequest(), User)
|
|
||||||
}
|
|
||||||
//Check if all the relevant data has been passed to the mail.
|
//Check if all the relevant data has been passed to the mail.
|
||||||
if !strings.Contains(message.MsgRequest(), user.FirstName+" "+user.LastName) {
|
if !strings.Contains(message.Body, user.FirstName) {
|
||||||
return fmt.Errorf("User first and last name(%v) has not been rendered in registration mail.", user.FirstName+" "+user.LastName)
|
return fmt.Errorf("User first name(%v) has not been rendered in registration mail.", user.FirstName)
|
||||||
}
|
}
|
||||||
if !strings.Contains(message.MsgRequest(), fmt.Sprintf("Preis/Monat</strong>: %v", user.Membership.SubscriptionModel.MonthlyFee)) {
|
if !strings.Contains(message.Body, fmt.Sprintf("Preis/Monat</strong>: %v", user.Membership.SubscriptionModel.MonthlyFee)) {
|
||||||
return fmt.Errorf("Users monthly subscription fee(%v) has not been rendered in registration mail.", user.Membership.SubscriptionModel.MonthlyFee)
|
return fmt.Errorf("Users monthly subscription fee(%v) has not been rendered in registration mail.", user.Membership.SubscriptionModel.MonthlyFee)
|
||||||
}
|
}
|
||||||
if !strings.Contains(message.MsgRequest(), fmt.Sprintf("Preis/h</strong>: %v", user.Membership.SubscriptionModel.HourlyRate)) {
|
if !strings.Contains(message.Body, fmt.Sprintf("Preis/h</strong>: %v", user.Membership.SubscriptionModel.HourlyRate)) {
|
||||||
return fmt.Errorf("Users hourly subscription fee(%v) has not been rendered in registration mail.", user.Membership.SubscriptionModel.HourlyRate)
|
return fmt.Errorf("Users hourly subscription fee(%v) has not been rendered in registration mail.", user.Membership.SubscriptionModel.HourlyRate)
|
||||||
}
|
}
|
||||||
if user.Company != "" && !strings.Contains(message.MsgRequest(), user.Company) {
|
if user.Company != "" && !strings.Contains(message.Body, user.Company) {
|
||||||
return fmt.Errorf("Users Company(%v) has not been rendered in registration mail.", user.Company)
|
return fmt.Errorf("Users Company(%v) has not been rendered in registration mail.", user.Company)
|
||||||
}
|
}
|
||||||
if !strings.Contains(message.MsgRequest(), fmt.Sprintf("Mitgliedsnr:</strong> %v", user.Membership.ID)) {
|
if !strings.Contains(message.Body, fmt.Sprintf("Mitgliedsnummer</strong>: %v", user.Membership.ID)) {
|
||||||
return fmt.Errorf("Users membership Id(%v) has not been rendered in registration mail.", user.Membership.ID)
|
return fmt.Errorf("Users membership Id(%v) has not been rendered in registration mail.", user.Membership.ID)
|
||||||
}
|
}
|
||||||
if !strings.Contains(message.MsgRequest(), user.Address+","+user.ZipCode) {
|
if !strings.Contains(message.Body, config.BaseURL) {
|
||||||
return fmt.Errorf("Users address(%v) has not been rendered in registration mail.", user.Address+","+user.ZipCode)
|
|
||||||
}
|
|
||||||
if !strings.Contains(message.MsgRequest(), user.City) {
|
|
||||||
return fmt.Errorf("Users city(%v) has not been rendered in registration mail.", user.City)
|
|
||||||
}
|
|
||||||
if !strings.Contains(message.MsgRequest(), user.DateOfBirth.Format("20060102")) {
|
|
||||||
return fmt.Errorf("Users birthday(%v) has not been rendered in registration mail.", user.DateOfBirth.Format("20060102"))
|
|
||||||
}
|
|
||||||
if !strings.Contains(message.MsgRequest(), "Email:</strong> "+user.Email) {
|
|
||||||
return fmt.Errorf("Users email(%v) has not been rendered in registration mail.", user.Email)
|
|
||||||
}
|
|
||||||
if !strings.Contains(message.MsgRequest(), user.Phone) {
|
|
||||||
return fmt.Errorf("Users phone(%v) has not been rendered in registration mail.", user.Phone)
|
|
||||||
}
|
|
||||||
if !strings.Contains(message.MsgRequest(), user.BankAccount.IBAN) {
|
|
||||||
return fmt.Errorf("Users IBAN(%v) has not been rendered in registration mail.", user.BankAccount.IBAN)
|
|
||||||
}
|
|
||||||
if !strings.Contains(message.MsgRequest(), config.BaseURL) {
|
|
||||||
return fmt.Errorf("Base Url (%v) has not been rendered in registration mail.", config.BaseURL)
|
return fmt.Errorf("Base Url (%v) has not been rendered in registration mail.", config.BaseURL)
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func checkVerificationMail(message *smtpmock.Message, user *models.User) error {
|
func checkRegistrationMail(message *utils.Email, user *models.User) error {
|
||||||
for _, responses := range message.RcpttoRequestResponse() {
|
|
||||||
if !strings.Contains(responses[0], "RCPT TO:<"+user.Email) {
|
if !strings.Contains(message.To, config.Recipients.UserRegistration) {
|
||||||
return fmt.Errorf("Registration Information didn't reach client! Recipient was: %v instead of %v", responses[0], user.Email)
|
return fmt.Errorf("Registration Information didn't reach admin! Recipient was: %v instead of %v", message.To, config.Recipients.UserRegistration)
|
||||||
}
|
}
|
||||||
|
if !strings.Contains(message.From, config.SMTP.User) {
|
||||||
|
return fmt.Errorf("Registration Information was sent from unexpected address! Sender was: %v instead of %v", message.From, config.SMTP.User)
|
||||||
}
|
}
|
||||||
// the email is encoded with a lowercase %3d while the url.encodeQuery returns an uppercase %3D. Therefore we remove the last char(padded base64 '='
|
//Check if all the relevant data has been passed to the mail.
|
||||||
if !strings.Contains(message.MsgRequest(), user.Verification.VerificationToken[:len(user.Verification.VerificationToken)-1]) {
|
if !strings.Contains(message.Body, user.FirstName+" "+user.LastName) {
|
||||||
return fmt.Errorf("Users Verification link token(%v) has not been rendered in email verification mail. %v", user.Verification.VerificationToken, message.MsgRequest())
|
return fmt.Errorf("User first and last name(%v) has not been rendered in registration mail.", user.FirstName+" "+user.LastName)
|
||||||
}
|
}
|
||||||
if !strings.Contains(message.MsgRequest(), config.BaseURL) {
|
if !strings.Contains(message.Body, fmt.Sprintf("Preis/Monat</strong>: %v", user.Membership.SubscriptionModel.MonthlyFee)) {
|
||||||
|
return fmt.Errorf("Users monthly subscription fee(%v) has not been rendered in registration mail.", user.Membership.SubscriptionModel.MonthlyFee)
|
||||||
|
}
|
||||||
|
if !strings.Contains(message.Body, fmt.Sprintf("Preis/h</strong>: %v", user.Membership.SubscriptionModel.HourlyRate)) {
|
||||||
|
return fmt.Errorf("Users hourly subscription fee(%v) has not been rendered in registration mail.", user.Membership.SubscriptionModel.HourlyRate)
|
||||||
|
}
|
||||||
|
if user.Company != "" && !strings.Contains(message.Body, user.Company) {
|
||||||
|
return fmt.Errorf("Users Company(%v) has not been rendered in registration mail.", user.Company)
|
||||||
|
}
|
||||||
|
if !strings.Contains(message.Body, fmt.Sprintf("Mitgliedsnr:</strong> %v", user.Membership.ID)) {
|
||||||
|
return fmt.Errorf("Users membership Id(%v) has not been rendered in registration mail.", user.Membership.ID)
|
||||||
|
}
|
||||||
|
if !strings.Contains(message.Body, user.Address+","+user.ZipCode) {
|
||||||
|
return fmt.Errorf("Users address(%v) has not been rendered in registration mail.", user.Address+" sv,"+user.ZipCode)
|
||||||
|
}
|
||||||
|
if !strings.Contains(message.Body, user.City) {
|
||||||
|
return fmt.Errorf("Users city(%v) has not been rendered in registration mail.", user.City)
|
||||||
|
}
|
||||||
|
if !strings.Contains(message.Body, user.DateOfBirth.Format("20060102")) {
|
||||||
|
return fmt.Errorf("Users birthday(%v) has not been rendered in registration mail.", user.DateOfBirth.Format("20060102"))
|
||||||
|
}
|
||||||
|
if !strings.Contains(message.Body, "Email:</strong> "+user.Email) {
|
||||||
|
return fmt.Errorf("Users email(%v) has not been rendered in registration mail.", user.Email)
|
||||||
|
}
|
||||||
|
if !strings.Contains(message.Body, user.Phone) {
|
||||||
|
return fmt.Errorf("Users phone(%v) has not been rendered in registration mail.", user.Phone)
|
||||||
|
}
|
||||||
|
if !strings.Contains(message.Body, user.BankAccount.IBAN) {
|
||||||
|
return fmt.Errorf("Users IBAN(%v) has not been rendered in registration mail.", user.BankAccount.IBAN)
|
||||||
|
}
|
||||||
|
if !strings.Contains(message.Body, config.BaseURL) {
|
||||||
|
return fmt.Errorf("Base Url (%v) has not been rendered in registration mail.", config.BaseURL)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func checkVerificationMail(message *utils.Email, user *models.User) error {
|
||||||
|
if !strings.Contains(message.To, user.Email) {
|
||||||
|
return fmt.Errorf("Registration Information didn't reach client! Recipient was: %v instead of %v", message.To, user.Email)
|
||||||
|
}
|
||||||
|
verificationURL, err := getVerificationURL(message.Body)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("Error parsing verification URL: %#v", err.Error())
|
||||||
|
}
|
||||||
|
if !strings.Contains(verificationURL, user.Verification.VerificationToken) {
|
||||||
|
return fmt.Errorf("Users Verification link token(%v) has not been rendered in email verification mail. %v", user.Verification.VerificationToken, verificationURL)
|
||||||
|
}
|
||||||
|
|
||||||
|
if !strings.Contains(message.Body, config.BaseURL) {
|
||||||
return fmt.Errorf("Base Url (%v) has not been rendered in email verification mail.", config.BaseURL)
|
return fmt.Errorf("Base Url (%v) has not been rendered in email verification mail.", config.BaseURL)
|
||||||
}
|
}
|
||||||
return nil
|
// open the provided link:
|
||||||
|
if err := verifyMail(verificationURL); err != nil {
|
||||||
}
|
return err
|
||||||
func initSubscriptionPlans() error {
|
|
||||||
subscription := models.SubscriptionModel{
|
|
||||||
Name: "Basic",
|
|
||||||
Details: "Test Plan",
|
|
||||||
MonthlyFee: 2,
|
|
||||||
HourlyRate: 3,
|
|
||||||
}
|
}
|
||||||
result := database.DB.Create(&subscription)
|
messages := utils.SMTPGetMessages()
|
||||||
if result.Error != nil {
|
for _, message := range messages {
|
||||||
return result.Error
|
mail, err := utils.DecodeMail(message.MsgRequest())
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func getMockedContext(jsonStr []byte) (*gin.Context, *httptest.ResponseRecorder) {
|
|
||||||
gin.SetMode(gin.TestMode)
|
|
||||||
w := httptest.NewRecorder()
|
|
||||||
c, _ := gin.CreateTestContext(w)
|
|
||||||
var err error
|
|
||||||
c.Request, err = http.NewRequest("POST", "/register", bytes.NewBuffer(jsonStr))
|
|
||||||
if err != nil {
|
|
||||||
logger.Error.Fatalf("Failed to create new Request: %#v", err)
|
|
||||||
}
|
|
||||||
c.Request.Header.Set("Content-Type", "application/json")
|
|
||||||
|
|
||||||
return c, w
|
|
||||||
}
|
|
||||||
|
|
||||||
func deleteTestDB(dbPath string) error {
|
|
||||||
err := os.Remove(dbPath)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
if err := checkWelcomeMail(mail, user); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
return nil
|
return nil
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
func verifyMail(verificationURL string) error {
|
||||||
|
gin.SetMode(gin.TestMode)
|
||||||
|
router := gin.New()
|
||||||
|
router.LoadHTMLGlob(filepath.Join(config.Templates.HTMLPath, "*")) // Adjust the path to your HTML templates
|
||||||
|
|
||||||
|
router.GET("/backend/verify", Uc.VerifyMailHandler)
|
||||||
|
wv := httptest.NewRecorder()
|
||||||
|
cv, _ := gin.CreateTestContext(wv)
|
||||||
|
var err error
|
||||||
|
cv.Request, err = http.NewRequest("GET", verificationURL, nil)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("Failed to create new GET Request: %v", err.Error())
|
||||||
|
}
|
||||||
|
router.ServeHTTP(wv, cv.Request)
|
||||||
|
if wv.Code != 200 {
|
||||||
|
return fmt.Errorf("Didn't get the expected response code: got: %v; expected: %v", wv.Code, 200)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func getVerificationURL(mailBody string) (string, error) {
|
||||||
|
re := regexp.MustCompile(`"([^"]*verify[^"]*)"`)
|
||||||
|
|
||||||
|
// Find the matching URL in the email content
|
||||||
|
match := re.FindStringSubmatch(mailBody)
|
||||||
|
if len(match) == 0 {
|
||||||
|
return "", fmt.Errorf("No mail verification link found in email body: %#v", mailBody)
|
||||||
|
}
|
||||||
|
verificationURL, err := url.QueryUnescape(match[1])
|
||||||
|
if err != nil {
|
||||||
|
return "", fmt.Errorf("Error decoding URL: %v", err)
|
||||||
|
}
|
||||||
|
logger.Info.Printf("VerificationURL: %#v", verificationURL)
|
||||||
|
return verificationURL, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// TEST DATA:
|
// TEST DATA:
|
||||||
@@ -283,158 +267,151 @@ func getBaseUser() models.User {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func generateInputJSON(customize func(models.User) models.User) string {
|
func customizeInput(customize func(models.User) models.User) *RegistrationData {
|
||||||
user := getBaseUser()
|
user := getBaseUser()
|
||||||
user = customize(user) // Apply the customization
|
user = customize(user) // Apply the customization
|
||||||
regData := RegistrationData{User: user}
|
return &RegistrationData{User: user}
|
||||||
|
|
||||||
jsonBytes, err := json.Marshal(regData)
|
|
||||||
if err != nil {
|
|
||||||
logger.Error.Printf("couldn't generate Json from User: %#v\nERROR: %#v", regData, err)
|
|
||||||
return ""
|
|
||||||
}
|
|
||||||
return string(jsonBytes)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func getTestUsers() []test {
|
func getTestUsers() []RegisterUserTest {
|
||||||
return []test{
|
return []RegisterUserTest{
|
||||||
{
|
{
|
||||||
name: "birthday < 18 should fail",
|
Name: "birthday < 18 should fail",
|
||||||
wantResponse: http.StatusNotAcceptable,
|
WantResponse: http.StatusNotAcceptable,
|
||||||
wantDBData: map[string]interface{}{"email": "john.doe@example.com"},
|
WantDBData: map[string]interface{}{"email": "john.doe@example.com"},
|
||||||
assert: false,
|
Assert: false,
|
||||||
input: generateInputJSON(func(user models.User) models.User {
|
Input: GenerateInputJSON(customizeInput(func(user models.User) models.User {
|
||||||
user.DateOfBirth = time.Date(2020, time.January, 1, 0, 0, 0, 0, time.UTC)
|
user.DateOfBirth = time.Date(2020, time.January, 1, 0, 0, 0, 0, time.UTC)
|
||||||
return user
|
return user
|
||||||
}),
|
})),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "FirstName empty, should fail",
|
Name: "FirstName empty, should fail",
|
||||||
wantResponse: http.StatusNotAcceptable,
|
WantResponse: http.StatusNotAcceptable,
|
||||||
wantDBData: map[string]interface{}{"email": "john.doe@example.com"},
|
WantDBData: map[string]interface{}{"email": "john.doe@example.com"},
|
||||||
assert: false,
|
Assert: false,
|
||||||
input: generateInputJSON(func(user models.User) models.User {
|
Input: GenerateInputJSON(customizeInput(func(user models.User) models.User {
|
||||||
user.FirstName = ""
|
user.FirstName = ""
|
||||||
return user
|
return user
|
||||||
}),
|
})),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "LastName Empty should fail",
|
Name: "LastName Empty should fail",
|
||||||
wantResponse: http.StatusNotAcceptable,
|
WantResponse: http.StatusNotAcceptable,
|
||||||
wantDBData: map[string]interface{}{"email": "john.doe@example.com"},
|
WantDBData: map[string]interface{}{"email": "john.doe@example.com"},
|
||||||
assert: false,
|
Assert: false,
|
||||||
input: generateInputJSON(func(user models.User) models.User {
|
Input: GenerateInputJSON(customizeInput(func(user models.User) models.User {
|
||||||
user.LastName = ""
|
user.LastName = ""
|
||||||
return user
|
return user
|
||||||
}),
|
})),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "EMail wrong format should fail",
|
Name: "EMail wrong format should fail",
|
||||||
wantResponse: http.StatusNotAcceptable,
|
WantResponse: http.StatusNotAcceptable,
|
||||||
wantDBData: map[string]interface{}{"email": "johnexample.com"},
|
WantDBData: map[string]interface{}{"email": "johnexample.com"},
|
||||||
assert: false,
|
Assert: false,
|
||||||
input: generateInputJSON(func(user models.User) models.User {
|
Input: GenerateInputJSON(customizeInput(func(user models.User) models.User {
|
||||||
user.Email = "johnexample.com"
|
user.Email = "johnexample.com"
|
||||||
return user
|
return user
|
||||||
}),
|
})),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "Missing Zip Code should fail",
|
Name: "Missing Zip Code should fail",
|
||||||
wantResponse: http.StatusNotAcceptable,
|
WantResponse: http.StatusNotAcceptable,
|
||||||
wantDBData: map[string]interface{}{"email": "john.doe@example.com"},
|
WantDBData: map[string]interface{}{"email": "john.doe@example.com"},
|
||||||
assert: false,
|
Assert: false,
|
||||||
input: generateInputJSON(func(user models.User) models.User {
|
Input: GenerateInputJSON(customizeInput(func(user models.User) models.User {
|
||||||
user.ZipCode = ""
|
user.ZipCode = ""
|
||||||
return user
|
return user
|
||||||
}),
|
})),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "Missing Address should fail",
|
Name: "Missing Address should fail",
|
||||||
wantResponse: http.StatusNotAcceptable,
|
WantResponse: http.StatusNotAcceptable,
|
||||||
wantDBData: map[string]interface{}{"email": "john.doe@example.com"},
|
WantDBData: map[string]interface{}{"email": "john.doe@example.com"},
|
||||||
assert: false,
|
Assert: false,
|
||||||
input: generateInputJSON(func(user models.User) models.User {
|
Input: GenerateInputJSON(customizeInput(func(user models.User) models.User {
|
||||||
user.Address = ""
|
user.Address = ""
|
||||||
return user
|
return user
|
||||||
}),
|
})),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "Missing City should fail",
|
Name: "Missing City should fail",
|
||||||
wantResponse: http.StatusNotAcceptable,
|
WantResponse: http.StatusNotAcceptable,
|
||||||
wantDBData: map[string]interface{}{"email": "john.doe@example.com"},
|
WantDBData: map[string]interface{}{"email": "john.doe@example.com"},
|
||||||
assert: false,
|
Assert: false,
|
||||||
input: generateInputJSON(func(user models.User) models.User {
|
Input: GenerateInputJSON(customizeInput(func(user models.User) models.User {
|
||||||
user.City = ""
|
user.City = ""
|
||||||
return user
|
return user
|
||||||
}),
|
})),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "Missing IBAN should fail",
|
Name: "Missing IBAN should fail",
|
||||||
wantResponse: http.StatusNotAcceptable,
|
WantResponse: http.StatusNotAcceptable,
|
||||||
wantDBData: map[string]interface{}{"email": "john.doe@example.com"},
|
WantDBData: map[string]interface{}{"email": "john.doe@example.com"},
|
||||||
assert: false,
|
Assert: false,
|
||||||
input: generateInputJSON(func(user models.User) models.User {
|
Input: GenerateInputJSON(customizeInput(func(user models.User) models.User {
|
||||||
user.BankAccount.IBAN = ""
|
user.BankAccount.IBAN = ""
|
||||||
return user
|
return user
|
||||||
}),
|
})),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "Invalid IBAN should fail",
|
Name: "Invalid IBAN should fail",
|
||||||
wantResponse: http.StatusNotAcceptable,
|
WantResponse: http.StatusNotAcceptable,
|
||||||
wantDBData: map[string]interface{}{"email": "john.doe@example.com"},
|
WantDBData: map[string]interface{}{"email": "john.doe@example.com"},
|
||||||
assert: false,
|
Assert: false,
|
||||||
input: generateInputJSON(func(user models.User) models.User {
|
Input: GenerateInputJSON(customizeInput(func(user models.User) models.User {
|
||||||
user.BankAccount.IBAN = "DE1234234123134"
|
user.BankAccount.IBAN = "DE1234234123134"
|
||||||
return user
|
return user
|
||||||
}),
|
})),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "Missing subscription plan should fail",
|
Name: "Missing subscription plan should fail",
|
||||||
wantResponse: http.StatusNotAcceptable,
|
WantResponse: http.StatusNotAcceptable,
|
||||||
wantDBData: map[string]interface{}{"email": "john.doe@example.com"},
|
WantDBData: map[string]interface{}{"email": "john.doe@example.com"},
|
||||||
assert: false,
|
Assert: false,
|
||||||
input: generateInputJSON(func(user models.User) models.User {
|
Input: GenerateInputJSON(customizeInput(func(user models.User) models.User {
|
||||||
user.Membership.SubscriptionModel.Name = ""
|
user.Membership.SubscriptionModel.Name = ""
|
||||||
return user
|
return user
|
||||||
}),
|
})),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "Invalid subscription plan should fail",
|
Name: "Invalid subscription plan should fail",
|
||||||
wantResponse: http.StatusNotFound,
|
WantResponse: http.StatusNotFound,
|
||||||
wantDBData: map[string]interface{}{"email": "john.doe@example.com"},
|
WantDBData: map[string]interface{}{"email": "john.doe@example.com"},
|
||||||
assert: false,
|
Assert: false,
|
||||||
input: generateInputJSON(func(user models.User) models.User {
|
Input: GenerateInputJSON(customizeInput(func(user models.User) models.User {
|
||||||
user.Membership.SubscriptionModel.Name = "NOTEXISTENTPLAN"
|
user.Membership.SubscriptionModel.Name = "NOTEXISTENTPLAN"
|
||||||
return user
|
return user
|
||||||
}),
|
})),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "Correct Entry should pass",
|
Name: "Correct Entry should pass",
|
||||||
wantResponse: http.StatusCreated,
|
WantResponse: http.StatusCreated,
|
||||||
wantDBData: map[string]interface{}{"Email": "john.doe@example.com"},
|
WantDBData: map[string]interface{}{"Email": "john.doe@example.com"},
|
||||||
assert: true,
|
Assert: true,
|
||||||
input: generateInputJSON(func(user models.User) models.User { return user }),
|
Input: GenerateInputJSON(customizeInput(func(user models.User) models.User { return user })),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "Email duplicate should fail",
|
Name: "Email duplicate should fail",
|
||||||
wantResponse: http.StatusConflict,
|
WantResponse: http.StatusConflict,
|
||||||
wantDBData: map[string]interface{}{"first_name": "Jane"},
|
WantDBData: map[string]interface{}{"first_name": "Jane"},
|
||||||
assert: false,
|
Assert: false,
|
||||||
input: generateInputJSON(func(user models.User) models.User {
|
Input: GenerateInputJSON(customizeInput(func(user models.User) models.User {
|
||||||
user.FirstName = "Jane"
|
user.FirstName = "Jane"
|
||||||
return user
|
return user
|
||||||
}),
|
})),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "Company present should pass",
|
Name: "Company present should pass",
|
||||||
wantResponse: http.StatusCreated,
|
WantResponse: http.StatusCreated,
|
||||||
wantDBData: map[string]interface{}{"Email": "john.doe2@example.com"},
|
WantDBData: map[string]interface{}{"Email": "john.doe2@example.com"},
|
||||||
assert: true,
|
Assert: true,
|
||||||
input: generateInputJSON(func(user models.User) models.User {
|
Input: GenerateInputJSON(customizeInput(func(user models.User) models.User {
|
||||||
user.Email = "john.doe2@example.com"
|
user.Email = "john.doe2@example.com"
|
||||||
user.Company = "ACME"
|
user.Company = "ACME"
|
||||||
return user
|
return user
|
||||||
}),
|
})),
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,6 +3,8 @@ package repositories
|
|||||||
import (
|
import (
|
||||||
"GoMembership/internal/database"
|
"GoMembership/internal/database"
|
||||||
|
|
||||||
|
"gorm.io/gorm"
|
||||||
|
|
||||||
"GoMembership/internal/models"
|
"GoMembership/internal/models"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -10,6 +12,7 @@ type SubscriptionModelsRepositoryInterface interface {
|
|||||||
CreateSubscriptionModel(subscriptionModel *models.SubscriptionModel) (int64, error)
|
CreateSubscriptionModel(subscriptionModel *models.SubscriptionModel) (int64, error)
|
||||||
GetMembershipModelNames() ([]string, error)
|
GetMembershipModelNames() ([]string, error)
|
||||||
GetModelByName(modelname *string) (*models.SubscriptionModel, error)
|
GetModelByName(modelname *string) (*models.SubscriptionModel, error)
|
||||||
|
GetSubscriptions(where map[string]interface{}) (*[]models.SubscriptionModel, error)
|
||||||
}
|
}
|
||||||
|
|
||||||
type SubscriptionModelsRepository struct{}
|
type SubscriptionModelsRepository struct{}
|
||||||
@@ -38,3 +41,15 @@ func (sr *SubscriptionModelsRepository) GetMembershipModelNames() ([]string, err
|
|||||||
}
|
}
|
||||||
return names, nil
|
return names, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (sr *SubscriptionModelsRepository) GetSubscriptions(where map[string]interface{}) (*[]models.SubscriptionModel, error) {
|
||||||
|
var subscriptions []models.SubscriptionModel
|
||||||
|
result := database.DB.Where(where).Find(&subscriptions)
|
||||||
|
if result.Error != nil {
|
||||||
|
if result.Error == gorm.ErrRecordNotFound {
|
||||||
|
return nil, gorm.ErrRecordNotFound
|
||||||
|
}
|
||||||
|
return nil, result.Error
|
||||||
|
}
|
||||||
|
return &subscriptions, nil
|
||||||
|
}
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ import (
|
|||||||
"github.com/gin-gonic/gin"
|
"github.com/gin-gonic/gin"
|
||||||
)
|
)
|
||||||
|
|
||||||
func RegisterRoutes(router *gin.Engine, userController *controllers.UserController, membershipcontroller *controllers.MembershipController) {
|
func RegisterRoutes(router *gin.Engine, userController *controllers.UserController, membershipcontroller *controllers.MembershipController, contactController *controllers.ContactController) {
|
||||||
router.GET("/backend/verify", userController.VerifyMailHandler)
|
router.GET("/backend/verify", userController.VerifyMailHandler)
|
||||||
router.POST("/backend/api/register", userController.RegisterUser)
|
router.POST("/backend/api/register", userController.RegisterUser)
|
||||||
router.POST("/backend/api/register/subscription", membershipcontroller.RegisterSubscription)
|
router.POST("/backend/api/register/subscription", membershipcontroller.RegisterSubscription)
|
||||||
|
|||||||
@@ -30,7 +30,7 @@ func Run() {
|
|||||||
logger.Error.Fatalf("Couldn't init database: %v", err)
|
logger.Error.Fatalf("Couldn't init database: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
emailService := services.NewEmailService(config.SMTP.Host, config.SMTP.Port, config.SMTP.User, config.SMTP.Password, config.SMTP.AdminEmail)
|
emailService := services.NewEmailService(config.SMTP.Host, config.SMTP.Port, config.SMTP.User, config.SMTP.Password)
|
||||||
var consentRepo repositories.ConsentRepositoryInterface = &repositories.ConsentRepository{}
|
var consentRepo repositories.ConsentRepositoryInterface = &repositories.ConsentRepository{}
|
||||||
consentService := &services.ConsentService{Repo: consentRepo}
|
consentService := &services.ConsentService{Repo: consentRepo}
|
||||||
|
|
||||||
@@ -47,6 +47,7 @@ func Run() {
|
|||||||
userController := &controllers.UserController{Service: userService, EmailService: emailService, ConsentService: consentService, BankAccountService: bankAccountService, MembershipService: membershipService}
|
userController := &controllers.UserController{Service: userService, EmailService: emailService, ConsentService: consentService, BankAccountService: bankAccountService, MembershipService: membershipService}
|
||||||
membershipController := &controllers.MembershipController{Service: *membershipService}
|
membershipController := &controllers.MembershipController{Service: *membershipService}
|
||||||
|
|
||||||
|
contactController := &controllers.ContactController{EmailService: emailService}
|
||||||
router := gin.Default()
|
router := gin.Default()
|
||||||
// gin.SetMode(gin.ReleaseMode)
|
// gin.SetMode(gin.ReleaseMode)
|
||||||
router.Static(config.Templates.StaticPath, "./style")
|
router.Static(config.Templates.StaticPath, "./style")
|
||||||
@@ -55,7 +56,7 @@ func Run() {
|
|||||||
router.Use(gin.Logger())
|
router.Use(gin.Logger())
|
||||||
// router.Use(middlewares.LoggerMiddleware())
|
// router.Use(middlewares.LoggerMiddleware())
|
||||||
|
|
||||||
routes.RegisterRoutes(router, userController, membershipController)
|
routes.RegisterRoutes(router, userController, membershipController, contactController)
|
||||||
// create subrouter for teh authenticated area /account
|
// create subrouter for teh authenticated area /account
|
||||||
// also pthprefix matches everything below /account
|
// also pthprefix matches everything below /account
|
||||||
// accountRouter := router.PathPrefix("/account").Subrouter()
|
// accountRouter := router.PathPrefix("/account").Subrouter()
|
||||||
|
|||||||
@@ -14,12 +14,11 @@ import (
|
|||||||
|
|
||||||
type EmailService struct {
|
type EmailService struct {
|
||||||
dialer *gomail.Dialer
|
dialer *gomail.Dialer
|
||||||
adminEmail string
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewEmailService(host string, port int, username, password, adminEmail string) *EmailService {
|
func NewEmailService(host string, port int, username string, password string) *EmailService {
|
||||||
dialer := gomail.NewDialer(host, port, username, password)
|
dialer := gomail.NewDialer(host, port, username, password)
|
||||||
return &EmailService{dialer: dialer, adminEmail: adminEmail}
|
return &EmailService{dialer: dialer}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *EmailService) SendEmail(to string, subject string, body string) error {
|
func (s *EmailService) SendEmail(to string, subject string, body string) error {
|
||||||
@@ -87,10 +86,10 @@ func (s *EmailService) SendWelcomeEmail(user *models.User) error {
|
|||||||
Company string
|
Company string
|
||||||
FirstName string
|
FirstName string
|
||||||
MembershipModel string
|
MembershipModel string
|
||||||
|
BASEURL string
|
||||||
MembershipID int64
|
MembershipID int64
|
||||||
MembershipFee float32
|
MembershipFee float32
|
||||||
RentalFee float32
|
RentalFee float32
|
||||||
BASEURL string
|
|
||||||
}{
|
}{
|
||||||
Company: user.Company,
|
Company: user.Company,
|
||||||
FirstName: user.FirstName,
|
FirstName: user.FirstName,
|
||||||
@@ -110,24 +109,24 @@ func (s *EmailService) SendWelcomeEmail(user *models.User) error {
|
|||||||
return s.SendEmail(user.Email, subject, body)
|
return s.SendEmail(user.Email, subject, body)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *EmailService) NotifyAdminOfNewUser(user *models.User) error {
|
func (s *EmailService) SendRegistrationNotification(user *models.User) error {
|
||||||
// Prepare data to be injected into the template
|
// Prepare data to be injected into the template
|
||||||
data := struct {
|
data := struct {
|
||||||
City string
|
FirstName string
|
||||||
Email string
|
DateOfBirth string
|
||||||
LastName string
|
LastName string
|
||||||
MembershipModel string
|
MembershipModel string
|
||||||
Address string
|
Address string
|
||||||
IBAN string
|
IBAN string
|
||||||
FirstName string
|
Email string
|
||||||
Phone string
|
Phone string
|
||||||
DateOfBirth string
|
City string
|
||||||
Company string
|
Company string
|
||||||
ZipCode string
|
ZipCode string
|
||||||
|
BASEURL string
|
||||||
MembershipID int64
|
MembershipID int64
|
||||||
RentalFee float32
|
RentalFee float32
|
||||||
MembershipFee float32
|
MembershipFee float32
|
||||||
BASEURL string
|
|
||||||
}{
|
}{
|
||||||
Company: user.Company,
|
Company: user.Company,
|
||||||
FirstName: user.FirstName,
|
FirstName: user.FirstName,
|
||||||
@@ -152,7 +151,7 @@ func (s *EmailService) NotifyAdminOfNewUser(user *models.User) error {
|
|||||||
logger.Error.Print("Couldn't send admin notification mail")
|
logger.Error.Print("Couldn't send admin notification mail")
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
return s.SendEmail(config.SMTP.AdminEmail, subject, body)
|
return s.SendEmail(config.Recipients.UserRegistration, subject, body)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *EmailService) RelayContactFormMessage(sender string, name string, message string) error {
|
func (s *EmailService) RelayContactFormMessage(sender string, name string, message string) error {
|
||||||
@@ -171,5 +170,5 @@ func (s *EmailService) RelayContactFormMessage(sender string, name string, messa
|
|||||||
logger.Error.Print("Couldn't send contact form message mail")
|
logger.Error.Print("Couldn't send contact form message mail")
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
return s.SendEmail(config.SMTP.AdminEmail, subject, body)
|
return s.SendEmail(config.Recipients.ContactForm, subject, body)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,11 +1,15 @@
|
|||||||
package services
|
package services
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"net/http"
|
||||||
|
"slices"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/go-playground/validator/v10"
|
||||||
|
|
||||||
"GoMembership/internal/models"
|
"GoMembership/internal/models"
|
||||||
"GoMembership/internal/repositories"
|
"GoMembership/internal/repositories"
|
||||||
"GoMembership/pkg/errors"
|
"GoMembership/pkg/errors"
|
||||||
"slices"
|
|
||||||
"time"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
type MembershipServiceInterface interface {
|
type MembershipServiceInterface interface {
|
||||||
@@ -14,6 +18,7 @@ type MembershipServiceInterface interface {
|
|||||||
RegisterSubscription(subscription *models.SubscriptionModel) (int64, error)
|
RegisterSubscription(subscription *models.SubscriptionModel) (int64, error)
|
||||||
GetMembershipModelNames() ([]string, error)
|
GetMembershipModelNames() ([]string, error)
|
||||||
GetModelByName(modelname *string) (*models.SubscriptionModel, error)
|
GetModelByName(modelname *string) (*models.SubscriptionModel, error)
|
||||||
|
GetSubscriptions(where map[string]interface{}) (*[]models.SubscriptionModel, error)
|
||||||
}
|
}
|
||||||
|
|
||||||
type MembershipService struct {
|
type MembershipService struct {
|
||||||
@@ -32,6 +37,9 @@ func (service *MembershipService) FindMembershipByUserID(userID int64) (*models.
|
|||||||
|
|
||||||
// Membership_Subscriptions
|
// Membership_Subscriptions
|
||||||
func (service *MembershipService) RegisterSubscription(subscription *models.SubscriptionModel) (int64, error) {
|
func (service *MembershipService) RegisterSubscription(subscription *models.SubscriptionModel) (int64, error) {
|
||||||
|
if err := validateSubscriptionData(subscription); err != nil {
|
||||||
|
return http.StatusNotAcceptable, err
|
||||||
|
}
|
||||||
return service.SubscriptionRepo.CreateSubscriptionModel(subscription)
|
return service.SubscriptionRepo.CreateSubscriptionModel(subscription)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -50,3 +58,15 @@ func (service *MembershipService) GetModelByName(modelname *string) (*models.Sub
|
|||||||
}
|
}
|
||||||
return service.SubscriptionRepo.GetModelByName(modelname)
|
return service.SubscriptionRepo.GetModelByName(modelname)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (service *MembershipService) GetSubscriptions(where map[string]interface{}) (*[]models.SubscriptionModel, error) {
|
||||||
|
return service.SubscriptionRepo.GetSubscriptions(where)
|
||||||
|
}
|
||||||
|
|
||||||
|
func validateSubscriptionData(subscription *models.SubscriptionModel) error {
|
||||||
|
validate := validator.New()
|
||||||
|
|
||||||
|
validate.RegisterValidation("subscriptionModel", func(fl validator.FieldLevel) bool { return true })
|
||||||
|
validate.RegisterValidation("membershipField", func(fl validator.FieldLevel) bool { return true })
|
||||||
|
return validate.Struct(subscription)
|
||||||
|
}
|
||||||
|
|||||||
@@ -34,8 +34,7 @@ func (service *UserService) RegisterUser(user *models.User) (int64, string, erro
|
|||||||
}
|
}
|
||||||
user.Salt = base64.StdEncoding.EncodeToString(salt)
|
user.Salt = base64.StdEncoding.EncodeToString(salt)
|
||||||
*/
|
*/
|
||||||
err := validateRegistrationData(user)
|
if err := validateRegistrationData(user); err != nil {
|
||||||
if err != nil {
|
|
||||||
return http.StatusNotAcceptable, "", err
|
return http.StatusNotAcceptable, "", err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,10 +1,26 @@
|
|||||||
package utils
|
package utils
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"bytes"
|
||||||
"crypto/rand"
|
"crypto/rand"
|
||||||
"encoding/base64"
|
"encoding/base64"
|
||||||
|
"io"
|
||||||
|
"mime"
|
||||||
|
"mime/quotedprintable"
|
||||||
|
"net/mail"
|
||||||
|
"strings"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
type Email struct {
|
||||||
|
MimeVersion string
|
||||||
|
Date string
|
||||||
|
From string
|
||||||
|
To string
|
||||||
|
Subject string
|
||||||
|
ContentType string
|
||||||
|
Body string
|
||||||
|
}
|
||||||
|
|
||||||
func GenerateRandomString(length int) (string, error) {
|
func GenerateRandomString(length int) (string, error) {
|
||||||
bytes := make([]byte, length)
|
bytes := make([]byte, length)
|
||||||
_, err := rand.Read(bytes)
|
_, err := rand.Read(bytes)
|
||||||
@@ -17,3 +33,70 @@ func GenerateRandomString(length int) (string, error) {
|
|||||||
func GenerateVerificationToken() (string, error) {
|
func GenerateVerificationToken() (string, error) {
|
||||||
return GenerateRandomString(32)
|
return GenerateRandomString(32)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func DecodeMail(message string) (*Email, error) {
|
||||||
|
msg, err := mail.ReadMessage(strings.NewReader(message))
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
decodedBody, err := io.ReadAll(msg.Body)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
decodedBodyString, err := DecodeQuotedPrintable(string(decodedBody))
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
decodedSubject, err := DecodeRFC2047(msg.Header.Get("Subject"))
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
email := &Email{}
|
||||||
|
|
||||||
|
// Populate the headers
|
||||||
|
email.MimeVersion = msg.Header.Get("Mime-Version")
|
||||||
|
email.Date = msg.Header.Get("Date")
|
||||||
|
email.From = msg.Header.Get("From")
|
||||||
|
email.To = msg.Header.Get("To")
|
||||||
|
email.Subject = decodedSubject
|
||||||
|
email.Body = decodedBodyString
|
||||||
|
email.ContentType = msg.Header.Get("Content-Type")
|
||||||
|
|
||||||
|
return email, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func DecodeRFC2047(encoded string) (string, error) {
|
||||||
|
decoder := new(mime.WordDecoder)
|
||||||
|
decoded, err := decoder.DecodeHeader(encoded)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
return decoded, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func DecodeQuotedPrintable(encodedString string) (string, error) {
|
||||||
|
// Decode quoted-printable encoding
|
||||||
|
reader := quotedprintable.NewReader(strings.NewReader(encodedString))
|
||||||
|
decodedBytes := new(bytes.Buffer)
|
||||||
|
_, err := decodedBytes.ReadFrom(reader)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
return decodedBytes.String(), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func EncodeQuotedPrintable(s string) string {
|
||||||
|
var buf bytes.Buffer
|
||||||
|
|
||||||
|
// Use Quoted-Printable encoder
|
||||||
|
qp := quotedprintable.NewWriter(&buf)
|
||||||
|
|
||||||
|
// Write the UTF-8 encoded string to the Quoted-Printable encoder
|
||||||
|
qp.Write([]byte(s))
|
||||||
|
qp.Close()
|
||||||
|
|
||||||
|
// Encode the result into a MIME header
|
||||||
|
return mime.QEncoding.Encode("UTF-8", buf.String())
|
||||||
|
|
||||||
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user