Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
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
58 changes: 58 additions & 0 deletions api/graphql/employee.graphqls
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
# Employee types and inputs

type Employee {
id: ID!
user: User!
employeeCode: String!
department: String!
position: String!
hireDate: String! # TODO: Change to Time scalar when custom scalar marshaling is implemented
salary: Float!
status: String!
createdAt: String! # TODO: Change to Time scalar when custom scalar marshaling is implemented
updatedAt: String! # TODO: Change to Time scalar when custom scalar marshaling is implemented
}

type EmployeeConnection {
edges: [EmployeeEdge!]!
pageInfo: PageInfo!
}

type EmployeeEdge {
node: Employee!
cursor: String!
}

input CreateEmployeeInput {
userId: ID!
employeeCode: String!
department: String!
position: String!
hireDate: String! # TODO: Change to Time scalar when custom scalar marshaling is implemented
salary: Float!
status: String!
}

input UpdateEmployeeInput {
employeeCode: String
department: String
position: String
hireDate: String # TODO: Change to Time scalar when custom scalar marshaling is implemented
salary: Float
status: String
}

type CreateEmployeePayload {
employee: Employee!
errors: [Error!]
}

type UpdateEmployeePayload {
employee: Employee!
errors: [Error!]
}

type DeleteEmployeePayload {
success: Boolean!
errors: [Error!]
}
5 changes: 4 additions & 1 deletion api/graphql/mutation.graphqls
Original file line number Diff line number Diff line change
@@ -1,7 +1,10 @@
# User mutations
# User and Employee mutations

type Mutation {
createUser(input: CreateUserInput!): CreateUserPayload!
updateUser(id: ID!, input: UpdateUserInput!): UpdateUserPayload!
deleteUser(id: ID!): DeleteUserPayload!
createEmployee(input: CreateEmployeeInput!): CreateEmployeePayload!
updateEmployee(id: ID!, input: UpdateEmployeeInput!): UpdateEmployeePayload!
deleteEmployee(id: ID!): DeleteEmployeePayload!
}
6 changes: 5 additions & 1 deletion api/graphql/query.graphqls
Original file line number Diff line number Diff line change
@@ -1,6 +1,10 @@
# User queries
# User and Employee queries

type Query {
user(id: ID!): User
users(first: Int, after: String): UserConnection!
employee(id: ID!): Employee
employees(first: Int, after: String): EmployeeConnection!
employeesByDepartment(department: String!, first: Int, after: String): EmployeeConnection!
employeesByStatus(status: String!, first: Int, after: String): EmployeeConnection!
}
5 changes: 4 additions & 1 deletion cmd/server/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import (
"syscall"
"time"

"github.com/captain-corgi/go-graphql-example/internal/application/employee"
"github.com/captain-corgi/go-graphql-example/internal/application/user"
"github.com/captain-corgi/go-graphql-example/internal/infrastructure/config"
"github.com/captain-corgi/go-graphql-example/internal/infrastructure/database"
Expand Down Expand Up @@ -63,12 +64,14 @@ func NewApplication() (*Application, error) {

// Initialize repositories
userRepo := sql.NewUserRepository(dbManager.DB, logger)
employeeRepo := sql.NewEmployeeRepository(dbManager.DB.DB)

// Initialize application services
userService := user.NewService(userRepo, logger)
employeeService := employee.NewService(employeeRepo, userRepo, logger)

// Initialize resolver with all dependencies
resolver := resolver.NewResolver(userService, logger)
resolver := resolver.NewResolver(userService, employeeService, logger)

// Create HTTP server
server := httpserver.NewServer(&cfg.Server, resolver, logger)
Expand Down
129 changes: 129 additions & 0 deletions internal/application/employee/dto.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,129 @@
package employee

import (
"time"
)

// GetEmployeeRequest represents a request to get an employee by ID
type GetEmployeeRequest struct {
ID string
}

// GetEmployeeResponse represents a response containing an employee
type GetEmployeeResponse struct {
Employee *EmployeeDTO
}

// ListEmployeesRequest represents a request to list employees with pagination
type ListEmployeesRequest struct {
Limit int
Cursor string
}

// ListEmployeesResponse represents a response containing a list of employees
type ListEmployeesResponse struct {
Employees []*EmployeeDTO
NextCursor string
}

// ListEmployeesByDepartmentRequest represents a request to list employees by department
type ListEmployeesByDepartmentRequest struct {
Department string
Limit int
Cursor string
}

// ListEmployeesByDepartmentResponse represents a response containing a list of employees by department
type ListEmployeesByDepartmentResponse struct {
Employees []*EmployeeDTO
NextCursor string
}

// ListEmployeesByStatusRequest represents a request to list employees by status
type ListEmployeesByStatusRequest struct {
Status string
Limit int
Cursor string
}

// ListEmployeesByStatusResponse represents a response containing a list of employees by status
type ListEmployeesByStatusResponse struct {
Employees []*EmployeeDTO
NextCursor string
}

// CreateEmployeeRequest represents a request to create a new employee
type CreateEmployeeRequest struct {
UserID string
EmployeeCode string
Department string
Position string
HireDate time.Time
Salary float64
Status string
}

// CreateEmployeeResponse represents a response containing the created employee
type CreateEmployeeResponse struct {
Employee *EmployeeDTO
Errors []ErrorDTO
}

// UpdateEmployeeRequest represents a request to update an employee
type UpdateEmployeeRequest struct {
ID string
EmployeeCode *string
Department *string
Position *string
HireDate *time.Time
Salary *float64
Status *string
}

// UpdateEmployeeResponse represents a response containing the updated employee
type UpdateEmployeeResponse struct {
Employee *EmployeeDTO
Errors []ErrorDTO
}

// DeleteEmployeeRequest represents a request to delete an employee
type DeleteEmployeeRequest struct {
ID string
}

// DeleteEmployeeResponse represents a response indicating the result of deletion
type DeleteEmployeeResponse struct {
Success bool
Errors []ErrorDTO
}

// EmployeeDTO represents an employee data transfer object
type EmployeeDTO struct {
ID string
UserID string
User *UserDTO
EmployeeCode string
Department string
Position string
HireDate time.Time
Salary float64
Status string
CreatedAt time.Time
UpdatedAt time.Time
}

// UserDTO represents a user data transfer object (for employee relationship)
type UserDTO struct {
ID string
Email string
Name string
CreatedAt time.Time
UpdatedAt time.Time
}

// ErrorDTO represents an error data transfer object
type ErrorDTO struct {
Message string
Field string
Code string
}
96 changes: 96 additions & 0 deletions internal/application/employee/mapper.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
package employee

import (
"github.com/captain-corgi/go-graphql-example/internal/domain/employee"
"github.com/captain-corgi/go-graphql-example/internal/domain/user"
)

// MapEmployeeToDTO converts a domain employee to a DTO
func MapEmployeeToDTO(emp *employee.Employee, user *user.User) *EmployeeDTO {
if emp == nil {
return nil
}

var userDTO *UserDTO
if user != nil {
userDTO = &UserDTO{
ID: user.ID().String(),
Email: user.Email().String(),
Name: user.Name().String(),
CreatedAt: user.CreatedAt(),
UpdatedAt: user.UpdatedAt(),
}
}

return &EmployeeDTO{
ID: emp.ID().String(),
UserID: emp.UserID().String(),
User: userDTO,
EmployeeCode: emp.EmployeeCode().String(),
Department: emp.Department().String(),
Position: emp.Position().String(),
HireDate: emp.HireDate(),
Salary: emp.Salary().Value(),
Status: emp.Status().String(),
CreatedAt: emp.CreatedAt(),
UpdatedAt: emp.UpdatedAt(),
}
}

// MapEmployeesToDTOs converts a slice of domain employees to DTOs
func MapEmployeesToDTOs(employees []*employee.Employee, users map[string]*user.User) []*EmployeeDTO {
if employees == nil {
return nil
}

dtos := make([]*EmployeeDTO, len(employees))
for i, emp := range employees {
var user *user.User
if users != nil {
user = users[emp.UserID().String()]
}
dtos[i] = MapEmployeeToDTO(emp, user)
}

return dtos
}

// MapErrorToDTO converts a domain error to a DTO
func MapErrorToDTO(err error) ErrorDTO {
if err == nil {
return ErrorDTO{}
}

// Try to cast to domain error
if domainErr, ok := err.(interface {
Error() string
Code() string
Field() string
}); ok {
return ErrorDTO{
Message: domainErr.Error(),
Code: domainErr.Code(),
Field: domainErr.Field(),
}
}

// Fallback for generic errors
return ErrorDTO{
Message: err.Error(),
Code: "UNKNOWN_ERROR",
}
}

// MapErrorsToDTOs converts a slice of domain errors to DTOs
func MapErrorsToDTOs(errors []error) []ErrorDTO {
if errors == nil {
return nil
}

dtos := make([]ErrorDTO, len(errors))
for i, err := range errors {
dtos[i] = MapErrorToDTO(err)
}

return dtos
}
Loading