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

Add CRUD survey apis. #22

Open
wants to merge 2 commits into
base: dev
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
24 changes: 24 additions & 0 deletions api/http/dto/create_survey.go
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
// api/http/dto/create_survey.go
package dto

import "time"

type CreateSurveyRequest struct {
Title string `json:"title" validate:"required,min=3,max=100"`
CreationTime time.Time `json:"creation_time" validate:"required"`
StartTime *time.Time `json:"start_time" validate:"required"`
EndTime *time.Time `json:"end_time" validate:"required,gtfield=StartTime"`
RandomOrder bool `json:"random_order"`
AllowReturn bool `json:"allow_return"`
NumParticipationAttempts int `json:"num_participation_attempts" validate:"gte=1,lte=10"`
ResponseTime int `json:"response_time" validate:"gte=60,lte=86400"` // Between 1 minute and 1 day
AnonymityLevel string `json:"anonymity_level" validate:"required,oneof=visible_to_creator visible_to_creator_and_admins anonymous"`
DemographicRestrictions string `json:"demographic_restrictions" validate:"omitempty,json"`
ResponseModification bool `json:"response_modification"`
}

type CreateSurveyResponse struct {
ID uint `json:"id"`
Title string `json:"title"`
OwnerID uint `json:"owner_id"`
}
19 changes: 19 additions & 0 deletions api/http/dto/get_survey.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
// api/http/dto/get_survey.go
package dto

import "time"

type GetSurveyResponse struct {
ID uint `json:"id"`
Title string `json:"title"`
CreationTime time.Time `json:"creation_time"`
StartTime *time.Time `json:"start_time"`
EndTime *time.Time `json:"end_time"`
RandomOrder bool `json:"random_order"`
AllowReturn bool `json:"allow_return"`
NumParticipationAttempts int `json:"num_participation_attempts"`
ResponseTime int `json:"response_time"`
AnonymityLevel string `json:"anonymity_level"`
DemographicRestrictions string `json:"demographic_restrictions"`
ResponseModification bool `json:"response_modification"`
}
22 changes: 22 additions & 0 deletions api/http/dto/update_survey.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
// api/http/dto/update_survey.go
package dto

import "time"

type UpdateSurveyRequest struct {
Title *string `json:"title,omitempty"`
StartTime *time.Time `json:"start_time,omitempty"`
EndTime *time.Time `json:"end_time,omitempty"`
RandomOrder *bool `json:"random_order,omitempty"`
AllowReturn *bool `json:"allow_return,omitempty"`
NumParticipationAttempts *int `json:"num_participation_attempts,omitempty"`
ResponseTime *int `json:"response_time,omitempty"`
AnonymityLevel *string `json:"anonymity_level,omitempty"`
DemographicRestrictions *string `json:"demographic_restrictions,omitempty"`
ResponseModification *bool `json:"response_modification,omitempty"`
}

type UpdateSurveyResponse struct {
ID uint `json:"id"`
Title string `json:"title"`
}
168 changes: 168 additions & 0 deletions api/http/handlers/surevey.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,168 @@
// api/http/handlers/survey.go
package handlers

import (
"fmt"
"golipors/api/http/dto"
"golipors/api/http/mapper"
"golipors/internal/survey/port"
"net/http"
"strconv"

"github.com/go-playground/validator/v10"

"github.com/gofiber/fiber/v2"
)

var validate = validator.New()

type SurveyHandler struct {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

این چیه ؟

service port.Service
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

پورت توی هندلر چیکار میکنه. مگه پورت ریپو توی سرویس ها استفاده نمیشد؟ سرویس ها هم از طریق appContainer اینجکت میشد تو هندلر ها؟
مثال کد های منو ببین

}

func NewSurveyHandler(service port.Service) *SurveyHandler {
return &SurveyHandler{
service: service,
}
}

func (h *SurveyHandler) RegisterRoutes(api fiber.Router) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

api group?

accountGroup := router.Group("/account")

accountGroup.Post("/login", Login(appContainer, cfg))
accountGroup.Post("/register", Register)
accountGroup.Post("/verify-otp", VerifyOtp(appContainer, cfg))
accountGroup.Post("/reset-password", ResetPassword)
accountGroup.Post("/reset-password/verify", ResetPasswordVerify)

api.Post("/surveys", h.CreateSurvey)
api.Get("/surveys/:id", h.GetSurveyByID)
api.Put("/surveys/:id", h.UpdateSurvey)
api.Delete("/surveys/:id", h.DeleteSurvey)
}

// CreateSurvey handles POST /api/surveys
func (h *SurveyHandler) CreateSurvey(c *fiber.Ctx) error {
var req dto.CreateSurveyRequest

// Parse request body
if err := c.BodyParser(&req); err != nil {
return c.Status(http.StatusBadRequest).JSON(fiber.Map{
"error": "Invalid request body",
})
}

// Validate request body
if err := validate.Struct(req); err != nil {
// Extract validation errors
validationErrors := make(map[string]string)
for _, err := range err.(validator.ValidationErrors) {
validationErrors[err.Field()] = fmt.Sprintf("Validation failed on '%s' with tag '%s'", err.Field(), err.Tag())
}
return c.Status(http.StatusBadRequest).JSON(fiber.Map{
"error": "Validation failed",
"details": validationErrors,
})
}

// Assuming owner ID is retrieved from context (e.g., after authentication)
ownerID := uint(1) // Placeholder

// Convert DTO to domain model
survey := mapper.CreateSurveyRequestToDomain(req, ownerID)

// Create survey
surveyID, err := h.service.CreateSurvey(c.Context(), survey)
if err != nil {
return c.Status(http.StatusInternalServerError).JSON(fiber.Map{
"error": err.Error(),
})
}

// Build response
response := dto.CreateSurveyResponse{
ID: surveyID,
Title: survey.Title,
OwnerID: ownerID,
}

return c.Status(http.StatusCreated).JSON(response)
}

// GetSurveyByID handles GET /api/surveys/:id
func (h *SurveyHandler) GetSurveyByID(c *fiber.Ctx) error {
idParam := c.Params("id")
id, err := strconv.ParseUint(idParam, 10, 32)
if err != nil {
return c.Status(http.StatusBadRequest).JSON(fiber.Map{
"error": "Invalid survey ID",
})
}

survey, err := h.service.GetSurveyByID(c.Context(), uint(id))
if err != nil {
return c.Status(http.StatusInternalServerError).JSON(fiber.Map{
"error": err.Error(),
})
}
if survey == nil {
return c.Status(http.StatusNotFound).JSON(fiber.Map{
"error": "Survey not found",
})
}

response := mapper.DomainSurveyToGetSurveyResponse(*survey)
return c.Status(http.StatusOK).JSON(response)
}

// UpdateSurvey handles PUT /api/surveys/:id
func (h *SurveyHandler) UpdateSurvey(c *fiber.Ctx) error {
idParam := c.Params("id")
id, err := strconv.ParseUint(idParam, 10, 32)
if err != nil {
return c.Status(http.StatusBadRequest).JSON(fiber.Map{
"error": "Invalid survey ID",
})
}

var req dto.UpdateSurveyRequest
if err := c.BodyParser(&req); err != nil {
return c.Status(http.StatusBadRequest).JSON(fiber.Map{
"error": "Invalid request body",
})
}

survey := mapper.UpdateSurveyRequestToDomain(req)
survey.ID = uint(id)

if err := h.service.UpdateSurvey(c.Context(), survey); err != nil {
if err.Error() == "survey not found" {
return c.Status(http.StatusNotFound).JSON(fiber.Map{
"error": "Survey not found",
})
}
return c.Status(http.StatusInternalServerError).JSON(fiber.Map{
"error": err.Error(),
})
}

updatedSurvey, _ := h.service.GetSurveyByID(c.Context(), uint(id))
response := mapper.DomainSurveyToUpdateSurveyResponse(*updatedSurvey)
return c.Status(http.StatusOK).JSON(response)
}

// DeleteSurvey handles DELETE /api/surveys/:id
func (h *SurveyHandler) DeleteSurvey(c *fiber.Ctx) error {
idParam := c.Params("id")
id, err := strconv.ParseUint(idParam, 10, 32)
if err != nil {
return c.Status(http.StatusBadRequest).JSON(fiber.Map{
"error": "Invalid survey ID",
})
}

if err := h.service.DeleteSurvey(c.Context(), uint(id)); err != nil {
if err.Error() == "survey not found" {
return c.Status(http.StatusNotFound).JSON(fiber.Map{
"error": "Survey not found",
})
}
return c.Status(http.StatusInternalServerError).JSON(fiber.Map{
"error": err.Error(),
})
}

return c.SendStatus(http.StatusOK)
}
93 changes: 93 additions & 0 deletions api/http/mapper/survey_mapper.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
// api/http/mapper/survey_mapper.go
package mapper

import (
"golipors/api/http/dto"
"golipors/internal/survey/domain"
)

func CreateSurveyRequestToDomain(req dto.CreateSurveyRequest, ownerID uint) domain.Survey {
return domain.Survey{
Title: req.Title,
CreationTime: req.CreationTime,
StartTime: req.StartTime,
EndTime: req.EndTime,
RandomOrder: req.RandomOrder,
AllowReturn: req.AllowReturn,
NumParticipationAttempts: req.NumParticipationAttempts,
ResponseTime: req.ResponseTime,
AnonymityLevel: req.AnonymityLevel,
DemographicRestrictions: req.DemographicRestrictions,
ResponseModification: req.ResponseModification,
OwnerID: ownerID,
}
}

func DomainSurveyToCreateSurveyResponse(survey domain.Survey) dto.CreateSurveyResponse {
return dto.CreateSurveyResponse{
ID: survey.ID,
Title: survey.Title,
OwnerID: survey.OwnerID,
}
}

func DomainSurveyToGetSurveyResponse(survey domain.Survey) dto.GetSurveyResponse {
return dto.GetSurveyResponse{
ID: survey.ID,
Title: survey.Title,
CreationTime: survey.CreationTime,
StartTime: survey.StartTime,
EndTime: survey.EndTime,
RandomOrder: survey.RandomOrder,
AllowReturn: survey.AllowReturn,
NumParticipationAttempts: survey.NumParticipationAttempts,
ResponseTime: survey.ResponseTime,
AnonymityLevel: survey.AnonymityLevel,
DemographicRestrictions: survey.DemographicRestrictions,
ResponseModification: survey.ResponseModification,
}
}

func UpdateSurveyRequestToDomain(req dto.UpdateSurveyRequest) domain.Survey {
survey := domain.Survey{}

if req.Title != nil {
survey.Title = *req.Title
}
if req.StartTime != nil {
survey.StartTime = req.StartTime
}
if req.EndTime != nil {
survey.EndTime = req.EndTime
}
if req.RandomOrder != nil {
survey.RandomOrder = *req.RandomOrder
}
if req.AllowReturn != nil {
survey.AllowReturn = *req.AllowReturn
}
if req.NumParticipationAttempts != nil {
survey.NumParticipationAttempts = *req.NumParticipationAttempts
}
if req.ResponseTime != nil {
survey.ResponseTime = *req.ResponseTime
}
if req.AnonymityLevel != nil {
survey.AnonymityLevel = *req.AnonymityLevel
}
if req.DemographicRestrictions != nil {
survey.DemographicRestrictions = *req.DemographicRestrictions
}
if req.ResponseModification != nil {
survey.ResponseModification = *req.ResponseModification
}

return survey
}

func DomainSurveyToUpdateSurveyResponse(survey domain.Survey) dto.UpdateSurveyResponse {
return dto.UpdateSurveyResponse{
ID: survey.ID,
Title: survey.Title,
}
}
8 changes: 7 additions & 1 deletion go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,12 @@ module golipors
go 1.23.2

require (
github.com/go-playground/validator/v10 v10.23.0
github.com/gofiber/contrib/swagger v1.2.0
github.com/gofiber/fiber/v2 v2.52.5
github.com/golang-jwt/jwt/v5 v5.2.1
github.com/redis/go-redis/v9 v9.7.0
github.com/swaggo/swag v1.16.4
golang.org/x/crypto v0.17.0
gorm.io/driver/postgres v1.5.10
gorm.io/gorm v1.25.12
)
Expand All @@ -19,6 +19,7 @@ require (
github.com/asaskevich/govalidator v0.0.0-20230301143203-a9d515a09cc2 // indirect
github.com/cespare/xxhash/v2 v2.2.0 // indirect
github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f // indirect
github.com/gabriel-vasile/mimetype v1.4.3 // indirect
github.com/go-openapi/analysis v0.21.4 // indirect
github.com/go-openapi/errors v0.20.4 // indirect
github.com/go-openapi/jsonpointer v0.21.0 // indirect
Expand All @@ -29,6 +30,8 @@ require (
github.com/go-openapi/strfmt v0.21.8 // indirect
github.com/go-openapi/swag v0.23.0 // indirect
github.com/go-openapi/validate v0.22.3 // indirect
github.com/go-playground/locales v0.14.1 // indirect
github.com/go-playground/universal-translator v0.18.1 // indirect
github.com/google/uuid v1.5.0 // indirect
github.com/jackc/pgpassfile v1.0.0 // indirect
github.com/jackc/pgservicefile v0.0.0-20221227161230-091c0ba34f0a // indirect
Expand All @@ -38,6 +41,7 @@ require (
github.com/jinzhu/now v1.1.5 // indirect
github.com/josharian/intern v1.0.0 // indirect
github.com/klauspost/compress v1.17.0 // indirect
github.com/leodido/go-urn v1.4.0 // indirect
github.com/mailru/easyjson v0.7.7 // indirect
github.com/mattn/go-colorable v0.1.13 // indirect
github.com/mattn/go-isatty v0.0.20 // indirect
Expand All @@ -51,6 +55,8 @@ require (
github.com/valyala/fasthttp v1.51.0 // indirect
github.com/valyala/tcplisten v1.0.0 // indirect
go.mongodb.org/mongo-driver v1.13.1 // indirect
golang.org/x/crypto v0.29.0 // indirect
golang.org/x/net v0.31.0 // indirect
golang.org/x/sync v0.9.0 // indirect
golang.org/x/sys v0.27.0 // indirect
golang.org/x/text v0.20.0 // indirect
Expand Down
Loading