add dukcapil

This commit is contained in:
Aditya Siregar
2026-05-07 04:01:32 +07:00
parent 9a975d146e
commit bc64eb20ea
235 changed files with 1197 additions and 28187 deletions
+1 -238
View File
@@ -1,11 +1,8 @@
package transformer
import (
"eslogad-be/internal/contract"
"eslogad-be/internal/entities"
"go-backend-template/internal/contract"
"math"
"github.com/google/uuid"
)
func PaginationToRequest(page, limit int) (int, int) {
@@ -35,14 +32,6 @@ func CreatePaginationResponse(totalCount, page, limit int) contract.PaginationRe
}
}
func CreateListUsersResponse(users []contract.UserResponse, totalCount, page, limit int) *contract.ListUsersResponse {
pagination := CreatePaginationResponse(totalCount, page, limit)
return &contract.ListUsersResponse{
Users: users,
Pagination: pagination,
}
}
func CreateErrorResponse(message string, code int) *contract.ErrorResponse {
return &contract.ErrorResponse{
Error: "error",
@@ -66,229 +55,3 @@ func CreateSuccessResponse(message string, data interface{}) *contract.SuccessRe
Data: data,
}
}
func RolesToContract(roles []entities.Role) []contract.RoleResponse {
if roles == nil {
return nil
}
res := make([]contract.RoleResponse, 0, len(roles))
for _, r := range roles {
res = append(res, contract.RoleResponse{ID: r.ID, Name: r.Name, Code: r.Code})
}
return res
}
func DepartmentsToContract(positions []entities.Department) []contract.DepartmentResponse {
if positions == nil {
return nil
}
res := make([]contract.DepartmentResponse, 0, len(positions))
for _, p := range positions {
res = append(res, contract.DepartmentResponse{
ID: p.ID,
Name: p.Name,
Code: p.Code,
Path: p.Path,
})
}
return res
}
func DepartmentToContract(p entities.Department) contract.DepartmentResponse {
return contract.DepartmentResponse{
ID: p.ID,
Name: p.Name,
Code: p.Code,
Path: p.Path,
}
}
func ProfileEntityToContract(p *entities.UserProfile) *contract.UserProfileResponse {
if p == nil {
return nil
}
return &contract.UserProfileResponse{
UserID: p.UserID,
FullName: p.FullName,
DisplayName: p.DisplayName,
Phone: p.Phone,
AvatarURL: p.AvatarURL,
JobTitle: p.JobTitle,
EmployeeNo: p.EmployeeNo,
Bio: p.Bio,
Timezone: p.Timezone,
Locale: p.Locale,
Preferences: map[string]interface{}(p.Preferences),
NotificationPrefs: map[string]interface{}(p.NotificationPrefs),
LastSeenAt: p.LastSeenAt,
CreatedAt: p.CreatedAt,
UpdatedAt: p.UpdatedAt,
}
}
func ProfileUpdateToEntity(userID uuid.UUID, req *contract.UpdateUserProfileRequest, existing *entities.UserProfile) *entities.UserProfile {
prof := &entities.UserProfile{}
if existing != nil {
*prof = *existing
} else {
prof.UserID = userID
}
if req.FullName != nil {
prof.FullName = *req.FullName
}
if req.DisplayName != nil {
prof.DisplayName = req.DisplayName
}
if req.Phone != nil {
prof.Phone = req.Phone
}
if req.AvatarURL != nil {
prof.AvatarURL = req.AvatarURL
}
if req.JobTitle != nil {
prof.JobTitle = req.JobTitle
}
if req.EmployeeNo != nil {
prof.EmployeeNo = req.EmployeeNo
}
if req.Bio != nil {
prof.Bio = req.Bio
}
if req.Timezone != nil {
prof.Timezone = *req.Timezone
}
if req.Locale != nil {
prof.Locale = *req.Locale
}
if req.Preferences != nil {
prof.Preferences = entities.JSONB(*req.Preferences)
}
if req.NotificationPrefs != nil {
prof.NotificationPrefs = entities.JSONB(*req.NotificationPrefs)
}
return prof
}
func TitlesToContract(titles []entities.Title) []contract.TitleResponse {
if titles == nil {
return nil
}
out := make([]contract.TitleResponse, 0, len(titles))
for _, t := range titles {
out = append(out, contract.TitleResponse{
ID: t.ID,
Name: t.Name,
Code: t.Code,
Description: t.Description,
})
}
return out
}
func PermissionsToContract(perms []entities.Permission) []contract.PermissionResponse {
out := make([]contract.PermissionResponse, 0, len(perms))
for _, p := range perms {
out = append(out, contract.PermissionResponse{
ID: p.ID,
Code: p.Code,
Action: p.Action,
Description: &p.Description,
})
}
return out
}
func RoleWithPermissionsToContract(role entities.Role, perms []entities.Permission) contract.RoleWithPermissionsResponse {
return contract.RoleWithPermissionsResponse{
ID: role.ID,
Name: role.Name,
Code: role.Code,
Description: &role.Description,
Permissions: PermissionsToContract(perms),
CreatedAt: role.CreatedAt,
UpdatedAt: role.UpdatedAt,
}
}
func LabelsToContract(list []entities.Label) []contract.LabelResponse {
out := make([]contract.LabelResponse, 0, len(list))
for _, e := range list {
out = append(out, contract.LabelResponse{ID: e.ID.String(), Name: e.Name, Color: e.Color, CreatedAt: e.CreatedAt, UpdatedAt: e.UpdatedAt})
}
return out
}
func PrioritiesToContract(list []entities.Priority) []contract.PriorityResponse {
out := make([]contract.PriorityResponse, 0, len(list))
for _, e := range list {
out = append(out, contract.PriorityResponse{ID: e.ID.String(), Name: e.Name, Level: e.Level, CreatedAt: e.CreatedAt, UpdatedAt: e.UpdatedAt})
}
return out
}
func InstitutionsToContract(list []entities.Institution) []contract.InstitutionResponse {
out := make([]contract.InstitutionResponse, 0, len(list))
for _, e := range list {
out = append(out, contract.InstitutionResponse{ID: e.ID.String(), Name: e.Name, Type: string(e.Type), Address: e.Address, ContactPerson: e.ContactPerson, Phone: e.Phone, Email: e.Email, CreatedAt: e.CreatedAt, UpdatedAt: e.UpdatedAt})
}
return out
}
func DispositionActionsToContract(list []entities.DispositionAction) []contract.DispositionActionResponse {
out := make([]contract.DispositionActionResponse, 0, len(list))
for _, e := range list {
out = append(out, contract.DispositionActionResponse{
ID: e.ID.String(),
Code: e.Code,
Label: e.Label,
Description: e.Description,
RequiresNote: e.RequiresNote,
GroupName: e.GroupName,
SortOrder: e.SortOrder,
IsActive: e.IsActive,
CreatedAt: e.CreatedAt,
UpdatedAt: e.UpdatedAt,
})
}
return out
}
func DispositionRoutesToContract(list []entities.DispositionRoute) []contract.DispositionRouteResponse {
out := make([]contract.DispositionRouteResponse, 0, len(list))
for _, e := range list {
var allowed map[string]interface{}
if e.AllowedActions != nil {
allowed = map[string]interface{}(e.AllowedActions)
}
resp := contract.DispositionRouteResponse{
ID: e.ID,
FromDepartmentID: e.FromDepartmentID,
ToDepartmentID: e.ToDepartmentID,
IsActive: e.IsActive,
AllowedActions: allowed,
CreatedAt: e.CreatedAt,
UpdatedAt: e.UpdatedAt,
}
// Add department information if available
if e.FromDepartment.ID != uuid.Nil {
resp.FromDepartment = contract.DepartmentInfo{
ID: e.FromDepartment.ID,
Name: e.FromDepartment.Name,
Code: e.FromDepartment.Code,
}
}
if e.ToDepartment.ID != uuid.Nil {
resp.ToDepartment = contract.DepartmentInfo{
ID: e.ToDepartment.ID,
Name: e.ToDepartment.Name,
Code: e.ToDepartment.Code,
}
}
out = append(out, resp)
}
return out
}
-444
View File
@@ -1,444 +0,0 @@
package transformer
import (
"eslogad-be/internal/contract"
"eslogad-be/internal/entities"
"github.com/google/uuid"
)
func LetterEntityToContract(e *entities.LetterIncoming, attachments []entities.LetterIncomingAttachment, dispositions []entities.LetterIncomingDisposition, refs ...interface{}) *contract.IncomingLetterResponse {
resp := &contract.IncomingLetterResponse{
ID: e.ID,
LetterNumber: e.LetterNumber,
ReferenceNumber: e.ReferenceNumber,
Subject: e.Subject,
Description: e.Description,
SenderName: e.SenderName,
Addressee: e.Addressee,
ReceivedDate: e.ReceivedDate,
DueDate: e.DueDate,
Type: string(e.Type),
Status: string(e.Status),
CreatedBy: e.CreatedBy,
CreatedAt: e.CreatedAt,
UpdatedAt: e.UpdatedAt,
Attachments: make([]contract.IncomingLetterAttachmentResponse, 0, len(attachments)),
Dispositions: make([]contract.EnhancedDispositionResponse, 0, len(dispositions)),
}
// optional refs: allow passing already-fetched related objects
// expected ordering (if provided): *entities.Priority, *entities.Institution
for _, r := range refs {
switch v := r.(type) {
case *entities.Priority:
if v != nil {
resp.Priority = &contract.PriorityResponse{
ID: v.ID.String(),
Name: v.Name,
Level: v.Level,
CreatedAt: v.CreatedAt,
UpdatedAt: v.UpdatedAt,
}
}
case *entities.Institution:
if v != nil {
resp.SenderInstitution = &contract.InstitutionResponse{
ID: v.ID.String(),
Name: v.Name,
Type: string(v.Type),
Address: v.Address,
ContactPerson: v.ContactPerson,
Phone: v.Phone,
Email: v.Email,
CreatedAt: v.CreatedAt,
UpdatedAt: v.UpdatedAt,
}
}
}
}
for _, a := range attachments {
resp.Attachments = append(resp.Attachments, contract.IncomingLetterAttachmentResponse{
ID: a.ID,
FileURL: a.FileURL,
FileName: a.FileName,
FileType: a.FileType,
UploadedAt: a.UploadedAt,
})
}
for _, d := range dispositions {
resp.Dispositions = append(resp.Dispositions, contract.EnhancedDispositionResponse{
ID: d.ID,
LetterID: d.LetterID,
DepartmentID: d.DepartmentID,
Notes: d.Notes,
ReadAt: d.ReadAt,
CreatedBy: d.CreatedBy,
CreatedAt: d.CreatedAt,
UpdatedAt: d.UpdatedAt,
Departments: DispositionDepartmentsWithDetailsToContract(d.Departments),
Actions: DispositionActionSelectionsWithDetailsToContract(d.ActionSelections),
DispositionNotes: DispositionNotesWithDetailsToContract(d.DispositionNotes),
Department: DepartmentToContract(d.Department),
})
}
return resp
}
func LetterIncomingEntityToContract(e *entities.LetterIncoming) *contract.IncomingLetterResponse {
if e == nil {
return nil
}
return &contract.IncomingLetterResponse{
ID: e.ID,
LetterNumber: e.LetterNumber,
ReferenceNumber: e.ReferenceNumber,
Subject: e.Subject,
Description: e.Description,
SenderName: e.SenderName,
Addressee: e.Addressee,
ReceivedDate: e.ReceivedDate,
DueDate: e.DueDate,
Type: string(e.Type),
Status: string(e.Status),
CreatedBy: e.CreatedBy, // Will be set conditionally
CreatedAt: e.CreatedAt,
UpdatedAt: e.UpdatedAt,
Attachments: make([]contract.IncomingLetterAttachmentResponse, 0),
}
}
func DepartmentEntityToContract(e *entities.Department) *contract.DepartmentResponse {
if e == nil {
return nil
}
return &contract.DepartmentResponse{
ID: e.ID,
Code: e.Code,
Name: e.Name,
Path: e.Path,
}
}
func DispositionsToContract(list []entities.LetterIncomingDisposition) []contract.DispositionResponse {
out := make([]contract.DispositionResponse, 0, len(list))
for _, d := range list {
out = append(out, DispoToContract(d))
}
return out
}
func DispoToContract(d entities.LetterIncomingDisposition) contract.DispositionResponse {
return contract.DispositionResponse{
ID: d.ID,
LetterID: d.LetterID,
DepartmentID: d.DepartmentID,
Notes: d.Notes,
ReadAt: d.ReadAt,
CreatedBy: d.CreatedBy,
CreatedAt: d.CreatedAt,
UpdatedAt: d.UpdatedAt,
}
}
func EnhancedDispositionsToContract(list []entities.LetterIncomingDisposition) []contract.EnhancedDispositionResponse {
out := make([]contract.EnhancedDispositionResponse, 0, len(list))
for _, d := range list {
resp := contract.EnhancedDispositionResponse{
ID: d.ID,
LetterID: d.LetterID,
DepartmentID: d.DepartmentID,
Notes: d.Notes,
ReadAt: d.ReadAt,
CreatedBy: d.CreatedBy,
CreatedAt: d.CreatedAt,
UpdatedAt: d.UpdatedAt,
Departments: []contract.DispositionDepartmentResponse{},
Actions: []contract.DispositionActionSelectionResponse{},
DispositionNotes: []contract.DispositionNoteResponse{},
}
out = append(out, resp)
}
return out
}
func DispositionDepartmentsToContract(list []entities.LetterIncomingDispositionDepartment) []contract.DispositionDepartmentResponse {
out := make([]contract.DispositionDepartmentResponse, 0, len(list))
for _, d := range list {
resp := contract.DispositionDepartmentResponse{
ID: d.ID,
DepartmentID: d.DepartmentID,
CreatedAt: d.CreatedAt,
}
out = append(out, resp)
}
return out
}
func DispositionDepartmentsWithDetailsToContract(list []entities.LetterIncomingDispositionDepartment) []contract.DispositionDepartmentResponse {
out := make([]contract.DispositionDepartmentResponse, 0, len(list))
for _, d := range list {
resp := contract.DispositionDepartmentResponse{
ID: d.ID,
DepartmentID: d.DepartmentID,
CreatedAt: d.CreatedAt,
}
// Include department details if preloaded
if d.Department != nil {
resp.Department = &contract.DepartmentResponse{
ID: d.Department.ID,
Name: d.Department.Name,
Code: d.Department.Code,
Path: d.Department.Path,
}
}
out = append(out, resp)
}
return out
}
func DispositionActionSelectionsToContract(list []entities.LetterDispositionActionSelection) []contract.DispositionActionSelectionResponse {
out := make([]contract.DispositionActionSelectionResponse, 0, len(list))
for _, d := range list {
resp := contract.DispositionActionSelectionResponse{
ID: d.ID,
ActionID: d.ActionID,
Action: nil, // Will be populated by processor
Note: d.Note,
CreatedBy: d.CreatedBy,
CreatedAt: d.CreatedAt,
}
out = append(out, resp)
}
return out
}
func DispositionActionSelectionsWithDetailsToContract(list []entities.LetterDispositionActionSelection) []contract.DispositionActionSelectionResponse {
out := make([]contract.DispositionActionSelectionResponse, 0, len(list))
for _, d := range list {
resp := contract.DispositionActionSelectionResponse{
ID: d.ID,
ActionID: d.ActionID,
Action: nil, // Will be populated by processor
Note: d.Note,
CreatedBy: d.CreatedBy,
CreatedAt: d.CreatedAt,
}
// Include action details if preloaded
if d.Action != nil {
resp.Action = &contract.DispositionActionResponse{
ID: d.Action.ID.String(),
Code: d.Action.Code,
Label: d.Action.Label,
Description: d.Action.Description,
RequiresNote: d.Action.RequiresNote,
GroupName: d.Action.GroupName,
SortOrder: d.Action.SortOrder,
IsActive: d.Action.IsActive,
CreatedAt: d.Action.CreatedAt,
UpdatedAt: d.Action.UpdatedAt,
}
}
out = append(out, resp)
}
return out
}
func DispositionNotesToContract(list []entities.DispositionNote) []contract.DispositionNoteResponse {
out := make([]contract.DispositionNoteResponse, 0, len(list))
for _, d := range list {
resp := contract.DispositionNoteResponse{
ID: d.ID,
UserID: d.UserID,
Note: d.Note,
CreatedAt: d.CreatedAt,
}
out = append(out, resp)
}
return out
}
func DispositionNotesWithDetailsToContract(list []entities.DispositionNote) []contract.DispositionNoteResponse {
out := make([]contract.DispositionNoteResponse, 0, len(list))
for _, d := range list {
resp := contract.DispositionNoteResponse{
ID: d.ID,
UserID: d.UserID,
Note: d.Note,
CreatedAt: d.CreatedAt,
}
// Include user details if preloaded
if d.User != nil {
resp.User = &contract.UserResponse{
ID: d.User.ID,
Name: d.User.Name,
Email: d.User.Email,
IsActive: d.User.IsActive,
CreatedAt: d.User.CreatedAt,
UpdatedAt: d.User.UpdatedAt,
}
}
out = append(out, resp)
}
return out
}
func DiscussionEntityToContract(e *entities.LetterDiscussion) *contract.LetterDiscussionResponse {
var mentions map[string]interface{}
if e.Mentions != nil {
mentions = map[string]interface{}(e.Mentions)
}
return &contract.LetterDiscussionResponse{
ID: e.ID,
LetterID: e.LetterID,
ParentID: e.ParentID,
UserID: e.UserID,
Message: e.Message,
Mentions: mentions,
CreatedAt: e.CreatedAt,
UpdatedAt: e.UpdatedAt,
EditedAt: e.EditedAt,
}
}
func DiscussionsWithPreloadedDataToContract(list []entities.LetterDiscussion, mentionedUsers []entities.User) []contract.LetterDiscussionResponse {
// Create a map for efficient user lookup
userMap := make(map[uuid.UUID]entities.User)
for _, user := range mentionedUsers {
userMap[user.ID] = user
}
out := make([]contract.LetterDiscussionResponse, 0, len(list))
for _, d := range list {
resp := contract.LetterDiscussionResponse{
ID: d.ID,
LetterID: d.LetterID,
ParentID: d.ParentID,
UserID: d.UserID,
Message: d.Message,
Mentions: map[string]interface{}(d.Mentions),
CreatedAt: d.CreatedAt,
UpdatedAt: d.UpdatedAt,
EditedAt: d.EditedAt,
}
// Include user profile if preloaded
if d.User != nil {
resp.User = &contract.UserResponse{
ID: d.User.ID,
Name: d.User.Name,
Email: d.User.Email,
IsActive: d.User.IsActive,
CreatedAt: d.User.CreatedAt,
UpdatedAt: d.User.UpdatedAt,
}
// Include user profile if available
if d.User.Profile != nil {
resp.User.Profile = &contract.UserProfileResponse{
UserID: d.User.Profile.UserID,
FullName: d.User.Profile.FullName,
DisplayName: d.User.Profile.DisplayName,
Phone: d.User.Profile.Phone,
AvatarURL: d.User.Profile.AvatarURL,
JobTitle: d.User.Profile.JobTitle,
EmployeeNo: d.User.Profile.EmployeeNo,
Bio: d.User.Profile.Bio,
Timezone: d.User.Profile.Timezone,
Locale: d.User.Profile.Locale,
}
}
}
// Process mentions to get mentioned users with profiles
if d.Mentions != nil {
mentions := map[string]interface{}(d.Mentions)
if userIDs, ok := mentions["user_ids"]; ok {
if userIDList, ok := userIDs.([]interface{}); ok {
mentionedUsersList := make([]contract.UserResponse, 0)
for _, userID := range userIDList {
if userIDStr, ok := userID.(string); ok {
if userUUID, err := uuid.Parse(userIDStr); err == nil {
if user, exists := userMap[userUUID]; exists {
userResp := contract.UserResponse{
ID: user.ID,
Name: user.Name,
Email: user.Email,
IsActive: user.IsActive,
CreatedAt: user.CreatedAt,
UpdatedAt: user.UpdatedAt,
}
// Include user profile if available
if user.Profile != nil {
userResp.Profile = &contract.UserProfileResponse{
UserID: user.Profile.UserID,
FullName: user.Profile.FullName,
DisplayName: user.Profile.DisplayName,
Phone: user.Profile.Phone,
AvatarURL: user.Profile.AvatarURL,
JobTitle: user.Profile.JobTitle,
EmployeeNo: user.Profile.EmployeeNo,
Bio: user.Profile.Bio,
Timezone: user.Profile.Timezone,
Locale: user.Profile.Locale,
}
}
mentionedUsersList = append(mentionedUsersList, userResp)
}
}
}
}
resp.MentionedUsers = mentionedUsersList
}
}
}
out = append(out, resp)
}
return out
}
func EnhancedDispositionsWithPreloadedDataToContract(list []entities.LetterIncomingDisposition) []contract.EnhancedDispositionResponse {
out := make([]contract.EnhancedDispositionResponse, 0, len(list))
for _, d := range list {
resp := contract.EnhancedDispositionResponse{
ID: d.ID,
LetterID: d.LetterID,
DepartmentID: d.DepartmentID,
Notes: d.Notes,
ReadAt: d.ReadAt,
CreatedBy: d.CreatedBy,
CreatedAt: d.CreatedAt,
UpdatedAt: d.UpdatedAt,
Departments: []contract.DispositionDepartmentResponse{},
Actions: []contract.DispositionActionSelectionResponse{},
DispositionNotes: []contract.DispositionNoteResponse{},
Department: DepartmentToContract(d.Department),
}
if len(d.Departments) > 0 {
resp.Departments = DispositionDepartmentsWithDetailsToContract(d.Departments)
}
// Include preloaded action selections with details
if len(d.ActionSelections) > 0 {
resp.Actions = DispositionActionSelectionsWithDetailsToContract(d.ActionSelections)
}
// Include preloaded notes with user details
if len(d.DispositionNotes) > 0 {
resp.DispositionNotes = DispositionNotesWithDetailsToContract(d.DispositionNotes)
}
out = append(out, resp)
}
return out
}
@@ -1,49 +0,0 @@
package transformer
import (
"eslogad-be/internal/contract"
"eslogad-be/internal/entities"
"github.com/google/uuid"
)
func CreateRepositoryAttachmentRequestToEntity(req *contract.CreateRepositoryAttachmentRequest, userId uuid.UUID) *entities.RepositoryAttachment {
if req == nil {
return nil
}
return &entities.RepositoryAttachment{
FileName: req.FileName,
FileType: req.FileType,
FileURL: req.FileURL,
UploadedBy: &userId,
Category: req.Category,
}
}
func RepositoryAttachmentEntityToContract(entity *entities.RepositoryAttachment) *contract.RepositoryAttachmentsResponse {
resp := &contract.RepositoryAttachmentsResponse{
ID: entity.ID,
FileName: entity.FileName,
FileType: entity.FileType,
FileURL: entity.FileURL,
Category: entity.Category,
UploadBy: *entity.UploadedBy,
UploadAt: entity.UploadedAt,
}
return resp
}
func RepositoryAttachmentEntityToContracts(attachments []*entities.RepositoryAttachment) []contract.RepositoryAttachmentsResponse {
if attachments == nil {
return nil
}
responses := make([]contract.RepositoryAttachmentsResponse, len(attachments))
for i, u := range attachments {
resp := RepositoryAttachmentEntityToContract(u)
if resp != nil {
responses[i] = *resp
}
}
return responses
}
+5 -18
View File
@@ -1,8 +1,8 @@
package transformer
import (
"eslogad-be/internal/contract"
"eslogad-be/internal/entities"
"go-backend-template/internal/contract"
"go-backend-template/internal/entities"
)
func CreateUserRequestToEntity(req *contract.CreateUserRequest, passwordHash string) *entities.User {
@@ -29,7 +29,7 @@ func UpdateUserEntity(existing *entities.User, req *contract.UpdateUserRequest)
if req.Email != nil {
existing.Email = *req.Email
}
if req.IsActive != nil {
existing.IsActive = *req.IsActive
}
@@ -41,27 +41,14 @@ func EntityToContract(user *entities.User) *contract.UserResponse {
return nil
}
// Use Profile.FullName if available, otherwise fall back to user.Name
displayName := user.Name
if user.Profile != nil && user.Profile.FullName != "" {
displayName = user.Profile.FullName
}
resp := &contract.UserResponse{
return &contract.UserResponse{
ID: user.ID,
Name: displayName,
Name: user.Name,
Email: user.Email,
IsActive: user.IsActive,
CreatedAt: user.CreatedAt,
UpdatedAt: user.UpdatedAt,
}
if user.Profile != nil {
resp.Profile = ProfileEntityToContract(user.Profile)
}
if user.Departments != nil && len(user.Departments) > 0 {
resp.DepartmentResponse = DepartmentsToContract(user.Departments)
}
return resp
}
func EntitiesToContracts(users []*entities.User) []contract.UserResponse {