init push
This commit is contained in:
67
internal/transport/http/dto/after_flight_inspection_dto.go
Normal file
67
internal/transport/http/dto/after_flight_inspection_dto.go
Normal file
@@ -0,0 +1,67 @@
|
||||
package dto
|
||||
|
||||
import responsedto "wucher/internal/transport/http/dto/response"
|
||||
|
||||
type AfterFlightInspectionAttributes struct {
|
||||
ID string `json:"id,omitempty" example:"019d6772-471c-7b70-b42a-d94020ef61e4"`
|
||||
FlightInspectionID string `json:"flight_inspection_id,omitempty" example:"019d6772-471c-7b70-b42a-d94020ef61e4"`
|
||||
FlightInspection *FlightInspectionResource `json:"flight_inspection,omitempty"`
|
||||
Helicopter *responsedto.HelicopterResource `json:"helicopter,omitempty"`
|
||||
MissionCode string `json:"mission_code,omitempty" example:"M-2026-001"`
|
||||
MissionType []string `json:"mission_type,omitempty"`
|
||||
TotalFlight int `json:"total_flight" example:"3"`
|
||||
FlightDataCount int `json:"flight_data_count" example:"2"`
|
||||
FlightTimeSeconds int64 `json:"flight_time_seconds" example:"9000"`
|
||||
Route []string `json:"route"`
|
||||
OilEngineNR1Checked *string `json:"oil_engine_nr1_checked" example:"1"`
|
||||
OilEngineNR2Checked *string `json:"oil_engine_nr2_checked" example:"1"`
|
||||
HydraulicLHChecked *string `json:"hydraulic_lh_checked" example:"1"`
|
||||
HydraulicRHChecked *string `json:"hydraulic_rh_checked" example:"1"`
|
||||
OilTransmissionMGBChecked *string `json:"oil_transmission_mgb_checked" example:"1"`
|
||||
OilTransmissionIGBChecked *string `json:"oil_transmission_igb_checked" example:"1"`
|
||||
OilTransmissionTGBChecked *string `json:"oil_transmission_tgb_checked" example:"1"`
|
||||
Note *string `json:"note" example:"All checks completed"`
|
||||
Total int `json:"total" example:"3"`
|
||||
Completed int `json:"completed" example:"1"`
|
||||
DoneFileChecklist []AfterFlightInspectionFileChecklistItem `json:"done_file_checklist,omitempty"`
|
||||
FileChecklist []AfterFlightInspectionFileChecklistItem `json:"file_checklist"`
|
||||
CreatedAt string `json:"created_at,omitempty" example:"2026-04-09T05:00:00Z"`
|
||||
UpdatedAt string `json:"updated_at,omitempty" example:"2026-04-09T05:00:00Z"`
|
||||
}
|
||||
|
||||
type AfterFlightInspectionFileChecklistItem struct {
|
||||
HelicopterFileID string `json:"helicopter_file_id" example:"019d7000-aaaa-7bbb-8ccc-111111111111"`
|
||||
FileName string `json:"file_name,omitempty" example:"After Flight Checklist.pdf"`
|
||||
DownloadURL string `json:"download_url,omitempty" example:"https://s3.localhost.localstack.cloud:4566/wucher-file-dev/files/2026/04/05/policy.pdf?X-Amz-Signature=..."`
|
||||
IsDone bool `json:"is_done" example:"false"`
|
||||
}
|
||||
|
||||
type AfterFlightInspectionResource struct {
|
||||
Type string `json:"type" example:"after_flight_inspection"`
|
||||
ID string `json:"id,omitempty" example:"019d6772-471c-7b70-b42a-d94020ef61e4"`
|
||||
Attributes AfterFlightInspectionAttributes `json:"attributes"`
|
||||
}
|
||||
|
||||
type AfterFlightInspectionResponse struct {
|
||||
Data AfterFlightInspectionResource `json:"data"`
|
||||
}
|
||||
|
||||
type AfterFlightInspectionUpsertAttributes struct {
|
||||
OilEngineNR1Checked *string `json:"oil_engine_nr1_checked,omitempty"`
|
||||
OilEngineNR2Checked *string `json:"oil_engine_nr2_checked,omitempty"`
|
||||
HydraulicLHChecked *string `json:"hydraulic_lh_checked,omitempty"`
|
||||
HydraulicRHChecked *string `json:"hydraulic_rh_checked,omitempty"`
|
||||
OilTransmissionMGBChecked *string `json:"oil_transmission_mgb_checked,omitempty"`
|
||||
OilTransmissionIGBChecked *string `json:"oil_transmission_igb_checked,omitempty"`
|
||||
OilTransmissionTGBChecked *string `json:"oil_transmission_tgb_checked,omitempty"`
|
||||
Note *string `json:"note,omitempty"`
|
||||
}
|
||||
|
||||
type AfterFlightInspectionUpsertData struct {
|
||||
Type string `json:"type" validate:"required,oneof=after_flight_inspection_upsert after_flight_inspection"`
|
||||
Attributes AfterFlightInspectionUpsertAttributes `json:"attributes" validate:"required"`
|
||||
}
|
||||
|
||||
type AfterFlightInspectionUpsertRequest struct {
|
||||
Data AfterFlightInspectionUpsertData `json:"data" validate:"required"`
|
||||
}
|
||||
35
internal/transport/http/dto/audit_dto.go
Normal file
35
internal/transport/http/dto/audit_dto.go
Normal file
@@ -0,0 +1,35 @@
|
||||
package dto
|
||||
|
||||
// JSON:API Audit DTOs
|
||||
|
||||
type AuditLogAttributes struct {
|
||||
RequestID string `json:"request_id,omitempty" example:"req-12345"`
|
||||
ActorUserID string `json:"actor_user_id,omitempty" example:"018f3a5c-7a6f-7c2a-8d4a-4b5b1f6c8e9d"`
|
||||
Layer string `json:"layer" example:"api"`
|
||||
Module string `json:"module" example:"reserve_ac"`
|
||||
Action string `json:"action" example:"http.request"`
|
||||
Method string `json:"method,omitempty" example:"GET"`
|
||||
Path string `json:"path,omitempty" example:"/api/v1/users/get-all"`
|
||||
StatusCode int `json:"status_code,omitempty" example:"200"`
|
||||
Success bool `json:"success" example:"true"`
|
||||
IP string `json:"ip,omitempty" example:"127.0.0.1"`
|
||||
UserAgent string `json:"user_agent,omitempty" example:"Mozilla/5.0"`
|
||||
Message string `json:"message,omitempty" example:"HTTP 200"`
|
||||
Metadata interface{} `json:"metadata,omitempty"`
|
||||
CreatedAt string `json:"created_at,omitempty" example:"2026-03-11T12:00:00Z"`
|
||||
}
|
||||
|
||||
type AuditLogResource struct {
|
||||
Type string `json:"type" example:"audit_log"`
|
||||
ID string `json:"id" example:"018f3a5c-7a6f-7c2a-8d4a-4b5b1f6c8e9d"`
|
||||
Attributes AuditLogAttributes `json:"attributes"`
|
||||
}
|
||||
|
||||
type AuditLogListResponse struct {
|
||||
Data []AuditLogResource `json:"data"`
|
||||
Meta struct {
|
||||
PageNumber int `json:"page_number" example:"1"`
|
||||
PageSize int `json:"page_size" example:"20"`
|
||||
Total int64 `json:"total" example:"120"`
|
||||
} `json:"meta"`
|
||||
}
|
||||
813
internal/transport/http/dto/auth_dto.go
Normal file
813
internal/transport/http/dto/auth_dto.go
Normal file
@@ -0,0 +1,813 @@
|
||||
package dto
|
||||
|
||||
import "encoding/json"
|
||||
|
||||
// JSON:API Request/Response DTOs for Auth
|
||||
|
||||
// Register
|
||||
type RegisterAttributes struct {
|
||||
Email string `json:"email" validate:"required,email" example:"user@example.com"`
|
||||
Username string `json:"username" validate:"required,min=3,max=100" example:"john.doe"`
|
||||
Password string `json:"password" validate:"required,min=8" example:"StrongP@ssw0rd"`
|
||||
FirstName string `json:"first_name" validate:"required" example:"John"`
|
||||
LastName string `json:"last_name" validate:"required" example:"Doe"`
|
||||
MobilePhone string `json:"mobile_phone,omitempty" example:"+628123456789"`
|
||||
}
|
||||
|
||||
type RegisterData struct {
|
||||
Type string `json:"type" validate:"required,eq=auth_register" example:"auth_register"`
|
||||
Attributes RegisterAttributes `json:"attributes" validate:"required"`
|
||||
}
|
||||
|
||||
type RegisterRequest struct {
|
||||
Data RegisterData `json:"data" validate:"required"`
|
||||
}
|
||||
|
||||
// Invite user
|
||||
type InviteAttributes struct {
|
||||
Email string `json:"email" validate:"required,email" example:"user@example.com"`
|
||||
Username string `json:"username" validate:"required,min=3,max=100" example:"john.doe"`
|
||||
FirstName string `json:"first_name" validate:"required" example:"John"`
|
||||
LastName string `json:"last_name" validate:"required" example:"Doe"`
|
||||
MobilePhone string `json:"mobile_phone,omitempty" example:"+628123456789"`
|
||||
RoleID string `json:"role_id" validate:"required" example:"018f3a5c-7a6f-7c2a-8d4a-4b5b1f6c8e9d"`
|
||||
RoleIDs []string `json:"role_ids,omitempty" example:"[\"018f3a5c-7a6f-7c2a-8d4a-4b5b1f6c8e9d\",\"018f3a5c-7a6f-7c2a-8d4a-4b5b1f6c8e9e\"]"`
|
||||
}
|
||||
|
||||
type InviteData struct {
|
||||
Type string `json:"type" validate:"required,eq=auth_invite" example:"auth_invite"`
|
||||
Attributes InviteAttributes `json:"attributes" validate:"required"`
|
||||
}
|
||||
|
||||
type InviteRequest struct {
|
||||
Data InviteData `json:"data" validate:"required"`
|
||||
}
|
||||
|
||||
// Set password from invite token
|
||||
type SetPasswordAttributes struct {
|
||||
Token string `json:"token" validate:"required" example:"kP8k2..."`
|
||||
Password string `json:"password" validate:"required,min=8" example:"StrongP@ssw0rd"`
|
||||
}
|
||||
|
||||
type SetPasswordData struct {
|
||||
Type string `json:"type" validate:"required,eq=auth_set_password" example:"auth_set_password"`
|
||||
Attributes SetPasswordAttributes `json:"attributes" validate:"required"`
|
||||
}
|
||||
|
||||
type SetPasswordRequest struct {
|
||||
Data SetPasswordData `json:"data" validate:"required"`
|
||||
}
|
||||
|
||||
// Resend set-password invite
|
||||
type ResendSetPasswordInviteAttributes struct {
|
||||
UserID string `json:"user_id" validate:"required" example:"018f3a5c-7a6f-7c2a-8d4a-4b5b1f6c8e9d"`
|
||||
}
|
||||
|
||||
type ResendSetPasswordInviteData struct {
|
||||
Type string `json:"type" validate:"required,eq=auth_resend_set_password" example:"auth_resend_set_password"`
|
||||
Attributes ResendSetPasswordInviteAttributes `json:"attributes" validate:"required"`
|
||||
}
|
||||
|
||||
type ResendSetPasswordInviteRequest struct {
|
||||
Data ResendSetPasswordInviteData `json:"data" validate:"required"`
|
||||
}
|
||||
|
||||
// Forgot password
|
||||
type ForgotPasswordAttributes struct {
|
||||
Email string `json:"email" validate:"required,email" example:"user@example.com"`
|
||||
}
|
||||
|
||||
type ForgotPasswordData struct {
|
||||
Type string `json:"type" validate:"required,eq=auth_forgot_password" example:"auth_forgot_password"`
|
||||
Attributes ForgotPasswordAttributes `json:"attributes" validate:"required"`
|
||||
}
|
||||
|
||||
type ForgotPasswordRequest struct {
|
||||
Data ForgotPasswordData `json:"data" validate:"required"`
|
||||
}
|
||||
|
||||
// Reset password
|
||||
type ResetPasswordAttributes struct {
|
||||
Token string `json:"token" validate:"required" example:"kP8k2..."`
|
||||
NewPassword string `json:"new_password" validate:"required,min=8" example:"StrongP@ssw0rd!LongPassphrase"`
|
||||
ConfirmPassword string `json:"confirm_password" validate:"required,min=8" example:"StrongP@ssw0rd!LongPassphrase"`
|
||||
}
|
||||
|
||||
type ResetPasswordData struct {
|
||||
Type string `json:"type" validate:"required,eq=auth_reset_password" example:"auth_reset_password"`
|
||||
Attributes ResetPasswordAttributes `json:"attributes" validate:"required"`
|
||||
}
|
||||
|
||||
type ResetPasswordRequest struct {
|
||||
Data ResetPasswordData `json:"data" validate:"required"`
|
||||
}
|
||||
|
||||
// Setup security PIN
|
||||
type SecurityPINSetupAttributes struct {
|
||||
PIN string `json:"pin" validate:"required,len=6,numeric" example:"123456"`
|
||||
ConfirmPIN string `json:"confirm_pin" validate:"required,len=6,numeric" example:"123456"`
|
||||
}
|
||||
|
||||
type SecurityPINSetupData struct {
|
||||
Type string `json:"type" validate:"required,eq=auth_pin_setup" example:"auth_pin_setup"`
|
||||
Attributes SecurityPINSetupAttributes `json:"attributes" validate:"required"`
|
||||
}
|
||||
|
||||
type SecurityPINSetupRequest struct {
|
||||
Data SecurityPINSetupData `json:"data" validate:"required"`
|
||||
}
|
||||
|
||||
type SecurityPINSetupResponse struct {
|
||||
Data struct {
|
||||
Type string `json:"type" example:"auth_pin_setup"`
|
||||
Attributes struct {
|
||||
Configured bool `json:"configured" example:"true"`
|
||||
} `json:"attributes"`
|
||||
} `json:"data"`
|
||||
}
|
||||
|
||||
// Verify security PIN
|
||||
type SecurityPINVerifyAttributes struct {
|
||||
PIN string `json:"pin" validate:"required,len=6,numeric" example:"123456"`
|
||||
Action string `json:"action" validate:"required,uuid4" example:"a546de2f-f1e6-4c2c-ab03-9b4bf39f4d74"`
|
||||
}
|
||||
|
||||
type SecurityPINVerifyData struct {
|
||||
Type string `json:"type" validate:"required,eq=auth_pin_verify" example:"auth_pin_verify"`
|
||||
Attributes SecurityPINVerifyAttributes `json:"attributes" validate:"required"`
|
||||
}
|
||||
|
||||
type SecurityPINVerifyRequest struct {
|
||||
Data SecurityPINVerifyData `json:"data" validate:"required"`
|
||||
}
|
||||
|
||||
type SecurityPINVerifyResponse struct {
|
||||
Data struct {
|
||||
Type string `json:"type" example:"auth_pin_verify"`
|
||||
Attributes struct {
|
||||
Verified bool `json:"verified" example:"true"`
|
||||
ActionToken string `json:"action_token" example:"pinv_1234567890abcdef"`
|
||||
ActionTokenTTLMS int64 `json:"action_token_ttl_ms" example:"900000"`
|
||||
} `json:"attributes"`
|
||||
} `json:"data"`
|
||||
}
|
||||
|
||||
// Change security PIN
|
||||
type SecurityPINChangeAttributes struct {
|
||||
CurrentPIN string `json:"current_pin" validate:"required,len=6,numeric" example:"123456"`
|
||||
NewPIN string `json:"new_pin" validate:"required,len=6,numeric" example:"654321"`
|
||||
ConfirmPIN string `json:"confirm_pin" validate:"required,len=6,numeric" example:"654321"`
|
||||
}
|
||||
|
||||
type SecurityPINChangeData struct {
|
||||
Type string `json:"type" validate:"required,eq=auth_pin_change" example:"auth_pin_change"`
|
||||
Attributes SecurityPINChangeAttributes `json:"attributes" validate:"required"`
|
||||
}
|
||||
|
||||
type SecurityPINChangeRequest struct {
|
||||
Data SecurityPINChangeData `json:"data" validate:"required"`
|
||||
}
|
||||
|
||||
// Forgot security PIN
|
||||
type ForgotSecurityPINAttributes struct {
|
||||
Email string `json:"email" validate:"required,email" example:"user@example.com"`
|
||||
}
|
||||
|
||||
type ForgotSecurityPINData struct {
|
||||
Type string `json:"type" validate:"required,eq=auth_forgot_pin" example:"auth_forgot_pin"`
|
||||
Attributes ForgotSecurityPINAttributes `json:"attributes" validate:"required"`
|
||||
}
|
||||
|
||||
type ForgotSecurityPINRequest struct {
|
||||
Data ForgotSecurityPINData `json:"data" validate:"required"`
|
||||
}
|
||||
|
||||
// Request security PIN reset
|
||||
type RequestSecurityPINAttributes struct {
|
||||
Method string `json:"method,omitempty" example:"password"`
|
||||
Password string `json:"password,omitempty" example:"StrongP@ssw0rd"`
|
||||
ReauthToken string `json:"reauth_token,omitempty" example:"sso_reauth_1234567890abcdef"`
|
||||
}
|
||||
|
||||
type RequestSecurityPINData struct {
|
||||
Type string `json:"type" validate:"required,eq=auth_pin_request_reset" example:"auth_pin_request_reset"`
|
||||
Attributes RequestSecurityPINAttributes `json:"attributes" validate:"required"`
|
||||
}
|
||||
|
||||
type RequestSecurityPINRequest struct {
|
||||
Data RequestSecurityPINData `json:"data" validate:"required"`
|
||||
}
|
||||
|
||||
type RequestSecurityPINResetResponse struct {
|
||||
Data struct {
|
||||
Type string `json:"type" example:"auth_pin_request_reset"`
|
||||
Attributes struct {
|
||||
Message string `json:"message" example:"If allowed, a security PIN reset link has been sent to your email."`
|
||||
} `json:"attributes"`
|
||||
} `json:"data"`
|
||||
}
|
||||
|
||||
// Reset security PIN
|
||||
type ResetSecurityPINAttributes struct {
|
||||
Token string `json:"token,omitempty" example:"kP8k2..."`
|
||||
Method string `json:"method,omitempty" example:"password"`
|
||||
Password string `json:"password,omitempty" example:"StrongP@ssw0rd"`
|
||||
ReauthToken string `json:"reauth_token,omitempty" example:"sso_reauth_1234567890abcdef"`
|
||||
NewPIN string `json:"new_pin" validate:"required,len=6,numeric" example:"123456"`
|
||||
ConfirmPIN string `json:"confirm_pin" validate:"required,len=6,numeric" example:"123456"`
|
||||
}
|
||||
|
||||
type ResetSecurityPINData struct {
|
||||
Type string `json:"type" validate:"required,eq=auth_reset_pin" example:"auth_reset_pin"`
|
||||
Attributes ResetSecurityPINAttributes `json:"attributes" validate:"required"`
|
||||
}
|
||||
|
||||
type ResetSecurityPINRequest struct {
|
||||
Data ResetSecurityPINData `json:"data" validate:"required"`
|
||||
}
|
||||
|
||||
// Login
|
||||
type LoginAttributes struct {
|
||||
Email string `json:"email" validate:"required,email" example:"user@example.com"`
|
||||
Password string `json:"password" validate:"required" example:"StrongP@ssw0rd"`
|
||||
}
|
||||
|
||||
type LoginData struct {
|
||||
Type string `json:"type" validate:"required,eq=auth_login" example:"auth_login"`
|
||||
Attributes LoginAttributes `json:"attributes" validate:"required"`
|
||||
}
|
||||
|
||||
type LoginRequest struct {
|
||||
Data LoginData `json:"data" validate:"required"`
|
||||
}
|
||||
|
||||
// Exchange frontend Microsoft token result into backend session tokens
|
||||
type ExchangeIDTokenClaims struct {
|
||||
Aud string `json:"aud,omitempty" example:"12345678-1234-1234-1234-123456789012"`
|
||||
Iss string `json:"iss,omitempty" example:"https://login.microsoftonline.com/abc-tenant-id/v2.0"`
|
||||
Iat int64 `json:"iat,omitempty" example:"1715600000"`
|
||||
Nbf int64 `json:"nbf,omitempty" example:"1715600000"`
|
||||
Exp int64 `json:"exp,omitempty" example:"1715603600"`
|
||||
Name string `json:"name,omitempty" example:"Andrio Effendi"`
|
||||
PreferredUsername string `json:"preferred_username,omitempty" example:"andrio@mybit-innovation.com"`
|
||||
Email string `json:"email,omitempty" example:"andrio@mybit-innovation.com"`
|
||||
OID string `json:"oid,omitempty" example:"00000000-0000-0000-1234-567890abcdef"`
|
||||
Sub string `json:"sub,omitempty" example:"AbCdEfGhIjKlMnOpQrStUvWxYz"`
|
||||
TID string `json:"tid,omitempty" example:"abc-tenant-id"`
|
||||
Ver string `json:"ver,omitempty" example:"2.0"`
|
||||
}
|
||||
|
||||
type ExchangeAttributes struct {
|
||||
AccessToken string `json:"access_token" validate:"required" example:"eyJ0eXAiOiJKV1Qi..."`
|
||||
IDToken string `json:"id_token" validate:"required" example:"eyJ0eXAiOiJKV1Qi..."`
|
||||
IDTokenClaims ExchangeIDTokenClaims `json:"id_token_claims"`
|
||||
}
|
||||
|
||||
type ExchangeData struct {
|
||||
Type string `json:"type" validate:"required,eq=auth_exchange" example:"auth_exchange"`
|
||||
Attributes ExchangeAttributes `json:"attributes" validate:"required"`
|
||||
}
|
||||
|
||||
type ExchangeRequest struct {
|
||||
Data ExchangeData `json:"data" validate:"required"`
|
||||
}
|
||||
|
||||
func (c ExchangeIDTokenClaims) ToMap() map[string]any {
|
||||
out := map[string]any{}
|
||||
if c.Aud != "" {
|
||||
out["aud"] = c.Aud
|
||||
}
|
||||
if c.Iss != "" {
|
||||
out["iss"] = c.Iss
|
||||
}
|
||||
if c.Iat > 0 {
|
||||
out["iat"] = c.Iat
|
||||
}
|
||||
if c.Nbf > 0 {
|
||||
out["nbf"] = c.Nbf
|
||||
}
|
||||
if c.Exp > 0 {
|
||||
out["exp"] = c.Exp
|
||||
}
|
||||
if c.Name != "" {
|
||||
out["name"] = c.Name
|
||||
}
|
||||
if c.PreferredUsername != "" {
|
||||
out["preferred_username"] = c.PreferredUsername
|
||||
}
|
||||
if c.Email != "" {
|
||||
out["email"] = c.Email
|
||||
}
|
||||
if c.OID != "" {
|
||||
out["oid"] = c.OID
|
||||
}
|
||||
if c.Sub != "" {
|
||||
out["sub"] = c.Sub
|
||||
}
|
||||
if c.TID != "" {
|
||||
out["tid"] = c.TID
|
||||
}
|
||||
if c.Ver != "" {
|
||||
out["ver"] = c.Ver
|
||||
}
|
||||
return out
|
||||
}
|
||||
|
||||
// WebAuthn register begin
|
||||
type WebAuthnRegisterBeginData struct {
|
||||
Type string `json:"type" validate:"required,eq=auth_webauthn_register_begin" example:"auth_webauthn_register_begin"`
|
||||
}
|
||||
|
||||
type WebAuthnRegisterBeginRequest struct {
|
||||
Data WebAuthnRegisterBeginData `json:"data" validate:"required"`
|
||||
}
|
||||
|
||||
// WebAuthn register finish
|
||||
type WebAuthnRegisterFinishAttributes struct {
|
||||
ChallengeToken string `json:"challenge_token" validate:"required" example:"kP8k2..."`
|
||||
Credential json.RawMessage `json:"credential" validate:"required"`
|
||||
Name string `json:"name,omitempty" validate:"omitempty,max=100" example:"MacBook Touch ID"`
|
||||
}
|
||||
|
||||
type WebAuthnRegisterFinishData struct {
|
||||
Type string `json:"type" validate:"required,eq=auth_webauthn_register_finish" example:"auth_webauthn_register_finish"`
|
||||
Attributes WebAuthnRegisterFinishAttributes `json:"attributes" validate:"required"`
|
||||
}
|
||||
|
||||
type WebAuthnRegisterFinishRequest struct {
|
||||
Data WebAuthnRegisterFinishData `json:"data" validate:"required"`
|
||||
}
|
||||
|
||||
// WebAuthn login begin
|
||||
type WebAuthnLoginBeginAttributes struct {
|
||||
Email string `json:"email" validate:"required,email" example:"user@example.com"`
|
||||
}
|
||||
|
||||
type WebAuthnLoginBeginData struct {
|
||||
Type string `json:"type" validate:"required,eq=auth_webauthn_login_begin" example:"auth_webauthn_login_begin"`
|
||||
Attributes WebAuthnLoginBeginAttributes `json:"attributes" validate:"required"`
|
||||
}
|
||||
|
||||
type WebAuthnLoginBeginRequest struct {
|
||||
Data WebAuthnLoginBeginData `json:"data" validate:"required"`
|
||||
}
|
||||
|
||||
// WebAuthn login finish
|
||||
type WebAuthnLoginFinishAttributes struct {
|
||||
ChallengeToken string `json:"challenge_token" validate:"required" example:"kP8k2..."`
|
||||
Credential json.RawMessage `json:"credential" validate:"required"`
|
||||
}
|
||||
|
||||
type WebAuthnLoginFinishData struct {
|
||||
Type string `json:"type" validate:"required,eq=auth_webauthn_login_finish" example:"auth_webauthn_login_finish"`
|
||||
Attributes WebAuthnLoginFinishAttributes `json:"attributes" validate:"required"`
|
||||
}
|
||||
|
||||
type WebAuthnLoginFinishRequest struct {
|
||||
Data WebAuthnLoginFinishData `json:"data" validate:"required"`
|
||||
}
|
||||
|
||||
// Sensitive-action re-auth attributes
|
||||
type ReauthAttributes struct {
|
||||
Method string `json:"method" validate:"required,oneof=pin password" example:"pin"`
|
||||
Password string `json:"password,omitempty" validate:"omitempty,min=1" example:"StrongP@ssw0rd"`
|
||||
}
|
||||
|
||||
// WebAuthn reset setup
|
||||
type WebAuthnResetSetupData struct {
|
||||
Type string `json:"type" validate:"required,eq=auth_webauthn_reset_setup" example:"auth_webauthn_reset_setup"`
|
||||
Attributes ReauthAttributes `json:"attributes" validate:"required"`
|
||||
}
|
||||
|
||||
type WebAuthnResetSetupRequest struct {
|
||||
Data WebAuthnResetSetupData `json:"data" validate:"required"`
|
||||
}
|
||||
|
||||
type WebAuthnResetSetupResponse struct {
|
||||
Data struct {
|
||||
Type string `json:"type" example:"auth_webauthn_reset_setup"`
|
||||
Attributes struct {
|
||||
Reset bool `json:"reset" example:"true"`
|
||||
} `json:"attributes"`
|
||||
} `json:"data"`
|
||||
}
|
||||
|
||||
// Login response when TOTP required
|
||||
type LoginTOTPRequiredResponse struct {
|
||||
Data struct {
|
||||
Type string `json:"type" example:"auth_totp_required"`
|
||||
Attributes struct {
|
||||
RequiresTOTP bool `json:"requires_totp" example:"true"`
|
||||
ChallengeToken string `json:"challenge_token" example:"kP8k2..."`
|
||||
TOTPEnabled bool `json:"totp_enabled" example:"true"`
|
||||
EmailOTPEnabled bool `json:"email_otp_enabled" example:"false"`
|
||||
EmailOTPChallengeToken string `json:"email_otp_challenge_token,omitempty" example:"qA9j1..."`
|
||||
EmailOTPResendCooldownSec int `json:"email_otp_resend_cooldown_seconds,omitempty" example:"60"`
|
||||
EmailOTPMaxResendPerHour int `json:"email_otp_max_resend_per_hour,omitempty" example:"5"`
|
||||
WebAuthnConfigured bool `json:"webauthn_configured" example:"false"`
|
||||
LinkedSSO bool `json:"linked_sso" example:"false"`
|
||||
PINConfigured bool `json:"pin_configured" example:"false"`
|
||||
} `json:"attributes"`
|
||||
} `json:"data"`
|
||||
}
|
||||
|
||||
type LoginEmailOTPRequiredResponse struct {
|
||||
Data struct {
|
||||
Type string `json:"type" example:"auth_email_otp_required"`
|
||||
Attributes struct {
|
||||
RequiresEmailOTP bool `json:"requires_email_otp" example:"true"`
|
||||
ChallengeToken string `json:"challenge_token" example:"kP8k2..."`
|
||||
ResendCooldownSeconds int `json:"resend_cooldown_seconds" example:"60"`
|
||||
MaxResendPerHour int `json:"max_resend_per_hour" example:"5"`
|
||||
RequiresTOTPSetup bool `json:"requires_totp_setup" example:"true"`
|
||||
WebAuthnConfigured bool `json:"webauthn_configured" example:"false"`
|
||||
LinkedSSO bool `json:"linked_sso" example:"false"`
|
||||
PINConfigured bool `json:"pin_configured" example:"false"`
|
||||
} `json:"attributes"`
|
||||
} `json:"data"`
|
||||
}
|
||||
|
||||
type EmailOTPSendAttributes struct {
|
||||
ChallengeToken string `json:"challenge_token" validate:"required" example:"kP8k2..."`
|
||||
}
|
||||
|
||||
type EmailOTPSendData struct {
|
||||
Type string `json:"type" validate:"required,eq=auth_email_otp_send" example:"auth_email_otp_send"`
|
||||
Attributes EmailOTPSendAttributes `json:"attributes" validate:"required"`
|
||||
}
|
||||
|
||||
type EmailOTPSendRequest struct {
|
||||
Data EmailOTPSendData `json:"data" validate:"required"`
|
||||
}
|
||||
|
||||
type EmailOTPVerifyAttributes struct {
|
||||
ChallengeToken string `json:"challenge_token" validate:"required" example:"kP8k2..."`
|
||||
Code string `json:"code" validate:"required,len=6,numeric" example:"123456"`
|
||||
}
|
||||
|
||||
type EmailOTPVerifyData struct {
|
||||
Type string `json:"type" validate:"required,eq=auth_email_otp_verify" example:"auth_email_otp_verify"`
|
||||
Attributes EmailOTPVerifyAttributes `json:"attributes" validate:"required"`
|
||||
}
|
||||
|
||||
type EmailOTPVerifyRequest struct {
|
||||
Data EmailOTPVerifyData `json:"data" validate:"required"`
|
||||
}
|
||||
|
||||
type EmailOTPSendResponse struct {
|
||||
Data struct {
|
||||
Type string `json:"type" example:"auth_email_otp_send"`
|
||||
Attributes struct {
|
||||
Sent bool `json:"sent" example:"true"`
|
||||
} `json:"attributes"`
|
||||
} `json:"data"`
|
||||
}
|
||||
|
||||
type EmailOTPVerifyResponse struct {
|
||||
Data struct {
|
||||
Type string `json:"type" example:"auth_email_otp_verify"`
|
||||
Attributes struct {
|
||||
Verified bool `json:"verified" example:"true"`
|
||||
RequiresTOTPSetup bool `json:"requires_totp_setup" example:"true"`
|
||||
} `json:"attributes"`
|
||||
} `json:"data"`
|
||||
}
|
||||
|
||||
// Email verify response
|
||||
type VerifyEmailResponse struct {
|
||||
Data struct {
|
||||
Type string `json:"type" example:"auth_verify_email"`
|
||||
Attributes struct {
|
||||
Verified bool `json:"verified" example:"true"`
|
||||
} `json:"attributes"`
|
||||
} `json:"data"`
|
||||
}
|
||||
|
||||
// Microsoft login
|
||||
type MicrosoftLoginResponse struct {
|
||||
Data struct {
|
||||
Type string `json:"type" example:"auth_microsoft_url"`
|
||||
Attributes struct {
|
||||
URL string `json:"url" example:"https://login.microsoftonline.com/..."`
|
||||
RedirectURL string `json:"redirect_url" example:"https://login.microsoftonline.com/..."`
|
||||
} `json:"attributes"`
|
||||
} `json:"data"`
|
||||
}
|
||||
|
||||
// Microsoft callback
|
||||
type MicrosoftCallbackResponse struct {
|
||||
Data struct {
|
||||
Type string `json:"type" example:"auth_identity"`
|
||||
ID string `json:"id" example:"018f3a5c-7a6f-7c2a-8d4a-4b5b1f6c8e9d"`
|
||||
Attributes struct {
|
||||
Provider string `json:"provider" example:"microsoft"`
|
||||
ProviderSubject string `json:"provider_subject" example:"00000000-0000-0000-0000-000000000000"`
|
||||
Email string `json:"email" example:"user@example.com"`
|
||||
} `json:"attributes"`
|
||||
} `json:"data"`
|
||||
}
|
||||
|
||||
type SSOLinkAttributes struct {
|
||||
Provider string `json:"provider" validate:"required,eq=microsoft" example:"microsoft"`
|
||||
Code string `json:"code" validate:"required" example:"0.AXkA..."`
|
||||
}
|
||||
|
||||
type SSOLinkData struct {
|
||||
Type string `json:"type" validate:"required,eq=auth_sso_link" example:"auth_sso_link"`
|
||||
Attributes SSOLinkAttributes `json:"attributes" validate:"required"`
|
||||
}
|
||||
|
||||
type SSOLinkRequest struct {
|
||||
Data SSOLinkData `json:"data" validate:"required"`
|
||||
}
|
||||
|
||||
type SSOLinkResponse struct {
|
||||
Data struct {
|
||||
Type string `json:"type" example:"auth_sso_link"`
|
||||
Attributes struct {
|
||||
Provider string `json:"provider" example:"microsoft"`
|
||||
ProviderSubject string `json:"provider_subject" example:"00000000-0000-0000-0000-000000000000"`
|
||||
Email string `json:"email" example:"user@example.com"`
|
||||
} `json:"attributes"`
|
||||
} `json:"data"`
|
||||
}
|
||||
|
||||
type SSOUnlinkAttributes struct {
|
||||
Provider string `json:"provider" validate:"required,eq=microsoft" example:"microsoft"`
|
||||
}
|
||||
|
||||
type SSOUnlinkData struct {
|
||||
Type string `json:"type" validate:"required,eq=auth_sso_unlink" example:"auth_sso_unlink"`
|
||||
Attributes SSOUnlinkAttributes `json:"attributes" validate:"required"`
|
||||
}
|
||||
|
||||
type SSOUnlinkRequest struct {
|
||||
Data SSOUnlinkData `json:"data" validate:"required"`
|
||||
}
|
||||
|
||||
type SSOUnlinkResponse struct {
|
||||
Data struct {
|
||||
Type string `json:"type" example:"auth_sso_unlink"`
|
||||
Attributes struct {
|
||||
Provider string `json:"provider" example:"microsoft"`
|
||||
Unlinked bool `json:"unlinked" example:"true"`
|
||||
} `json:"attributes"`
|
||||
} `json:"data"`
|
||||
}
|
||||
|
||||
// WebAuthn begin response
|
||||
type WebAuthnBeginResponse struct {
|
||||
Data struct {
|
||||
Type string `json:"type" example:"auth_webauthn_register_options"`
|
||||
Attributes struct {
|
||||
ChallengeToken string `json:"challenge_token" example:"kP8k2..."`
|
||||
ChallengeTTLMS int64 `json:"challenge_ttl_ms" example:"300000"`
|
||||
PublicKey map[string]interface{} `json:"public_key"`
|
||||
} `json:"attributes"`
|
||||
} `json:"data"`
|
||||
}
|
||||
|
||||
// TOTP setup
|
||||
type TOTPSetupAttributes struct {
|
||||
UserID string `json:"user_id" validate:"required" example:"018f3a5c-7a6f-7c2a-8d4a-4b5b1f6c8e9d"`
|
||||
Email string `json:"email" validate:"required,email" example:"user@example.com"`
|
||||
}
|
||||
|
||||
type TOTPSetupData struct {
|
||||
Type string `json:"type" validate:"required,eq=auth_totp_setup" example:"auth_totp_setup"`
|
||||
Attributes TOTPSetupAttributes `json:"attributes" validate:"required"`
|
||||
}
|
||||
|
||||
type TOTPSetupRequest struct {
|
||||
Data TOTPSetupData `json:"data" validate:"required"`
|
||||
}
|
||||
|
||||
type TOTPSetupResponse struct {
|
||||
Data struct {
|
||||
Type string `json:"type" example:"auth_totp_setup"`
|
||||
Attributes struct {
|
||||
Secret string `json:"secret" example:"JBSWY3DPEHPK3PXP"`
|
||||
OtpauthURL string `json:"otpauth_url" example:"otpauth://totp/Wucher:user@example.com?..."`
|
||||
Issuer string `json:"issuer" example:"Wucher"`
|
||||
} `json:"attributes"`
|
||||
} `json:"data"`
|
||||
}
|
||||
|
||||
// TOTP verify
|
||||
type TOTPVerifyAttributes struct {
|
||||
ChallengeToken string `json:"challenge_token" validate:"required" example:"kP8k2..."`
|
||||
Code string `json:"code" validate:"required,len=6" example:"123456"`
|
||||
}
|
||||
|
||||
type TOTPVerifyData struct {
|
||||
Type string `json:"type" validate:"required,eq=auth_totp_verify" example:"auth_totp_verify"`
|
||||
Attributes TOTPVerifyAttributes `json:"attributes" validate:"required"`
|
||||
}
|
||||
|
||||
type TOTPVerifyRequest struct {
|
||||
Data TOTPVerifyData `json:"data" validate:"required"`
|
||||
}
|
||||
|
||||
// TOTP confirm (enable)
|
||||
type TOTPConfirmAttributes struct {
|
||||
UserID string `json:"user_id" validate:"required" example:"018f3a5c-7a6f-7c2a-8d4a-4b5b1f6c8e9d"`
|
||||
Code string `json:"code" validate:"required,len=6" example:"123456"`
|
||||
}
|
||||
|
||||
type TOTPConfirmData struct {
|
||||
Type string `json:"type" validate:"required,eq=auth_totp_confirm" example:"auth_totp_confirm"`
|
||||
Attributes TOTPConfirmAttributes `json:"attributes" validate:"required"`
|
||||
}
|
||||
|
||||
type TOTPConfirmRequest struct {
|
||||
Data TOTPConfirmData `json:"data" validate:"required"`
|
||||
}
|
||||
|
||||
// Disable TOTP
|
||||
type TOTPDisableAttributes struct {
|
||||
UserID string `json:"user_id" validate:"required" example:"018f3a5c-7a6f-7c2a-8d4a-4b5b1f6c8e9d"`
|
||||
}
|
||||
|
||||
type TOTPDisableData struct {
|
||||
Type string `json:"type" validate:"required,eq=auth_totp_disable" example:"auth_totp_disable"`
|
||||
Attributes TOTPDisableAttributes `json:"attributes" validate:"required"`
|
||||
}
|
||||
|
||||
type TOTPDisableRequest struct {
|
||||
Data TOTPDisableData `json:"data" validate:"required"`
|
||||
}
|
||||
|
||||
// TOTP reset setup
|
||||
type TOTPResetSetupData struct {
|
||||
Type string `json:"type" validate:"required,eq=auth_totp_reset_setup" example:"auth_totp_reset_setup"`
|
||||
Attributes ReauthAttributes `json:"attributes" validate:"required"`
|
||||
}
|
||||
|
||||
type TOTPResetSetupRequest struct {
|
||||
Data TOTPResetSetupData `json:"data" validate:"required"`
|
||||
}
|
||||
|
||||
type TOTPResetSetupResponse struct {
|
||||
Data struct {
|
||||
Type string `json:"type" example:"auth_totp_reset_setup"`
|
||||
Attributes struct {
|
||||
Reset bool `json:"reset" example:"true"`
|
||||
} `json:"attributes"`
|
||||
} `json:"data"`
|
||||
}
|
||||
|
||||
type AuthSessionItem struct {
|
||||
ID string `json:"id" example:"018f3a5c-7a6f-7c2a-8d4a-4b5b1f6c8e9d"`
|
||||
DeviceName string `json:"device_name" example:"Chrome on macOS"`
|
||||
UserAgent string `json:"user_agent,omitempty" example:"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) ..."`
|
||||
IPAddress string `json:"ip_address,omitempty" example:"127.0.0.1"`
|
||||
CreatedAt string `json:"created_at" example:"2026-03-13T12:00:00Z"`
|
||||
LastActiveAt string `json:"last_active_at" example:"2026-03-13T12:10:00Z"`
|
||||
ActiveNow bool `json:"active_now" example:"true"`
|
||||
}
|
||||
|
||||
type AuthSessionsResponse struct {
|
||||
Data struct {
|
||||
Type string `json:"type" example:"auth_sessions"`
|
||||
Attributes struct {
|
||||
Sessions []AuthSessionItem `json:"sessions"`
|
||||
} `json:"attributes"`
|
||||
} `json:"data"`
|
||||
}
|
||||
|
||||
type AuthSessionDeleteResponse struct {
|
||||
Data struct {
|
||||
Type string `json:"type" example:"auth_session_delete"`
|
||||
Attributes struct {
|
||||
Deleted bool `json:"deleted" example:"true"`
|
||||
} `json:"attributes"`
|
||||
} `json:"data"`
|
||||
}
|
||||
|
||||
// Update timezone
|
||||
type TimezoneUpdateAttributes struct {
|
||||
Timezone string `json:"timezone" validate:"required" example:"Asia/Jakarta"`
|
||||
}
|
||||
|
||||
type TimezoneUpdateData struct {
|
||||
Type string `json:"type" validate:"required,eq=auth_timezone_update" example:"auth_timezone_update"`
|
||||
Attributes TimezoneUpdateAttributes `json:"attributes" validate:"required"`
|
||||
}
|
||||
|
||||
type TimezoneUpdateRequest struct {
|
||||
Data TimezoneUpdateData `json:"data" validate:"required"`
|
||||
}
|
||||
|
||||
// Token response
|
||||
type TokenResponse struct {
|
||||
Data struct {
|
||||
Type string `json:"type" example:"auth_tokens"`
|
||||
Attributes struct {
|
||||
AccessTokenExpiresIn int64 `json:"access_expires_in" example:"900"`
|
||||
RefreshTokenExpiresIn int64 `json:"refresh_expires_in" example:"2592000"`
|
||||
} `json:"attributes"`
|
||||
} `json:"data"`
|
||||
}
|
||||
|
||||
// User response
|
||||
type UserAttributes struct {
|
||||
Email string `json:"email" example:"user@example.com"`
|
||||
Username string `json:"username,omitempty" example:"john.doe"`
|
||||
FirstName string `json:"first_name" example:"John"`
|
||||
LastName string `json:"last_name" example:"Doe"`
|
||||
MobilePhone string `json:"mobile_phone,omitempty" example:"+628123456789"`
|
||||
Timezone string `json:"timezone" example:"Asia/Jakarta"`
|
||||
ProfilePicture *UserImage `json:"profile_picture,omitempty"`
|
||||
RoleID string `json:"role_id,omitempty" example:"018f3a5c-7a6f-7c2a-8d4a-4b5b1f6c8e9d"`
|
||||
RoleIDs []string `json:"role_ids,omitempty" example:"[\"018f3a5c-7a6f-7c2a-8d4a-4b5b1f6c8e9d\",\"018f3a5c-7a6f-7c2a-8d4a-4b5b1f6c8e9e\"]"`
|
||||
CreatedBy string `json:"created_by,omitempty" example:"018f3a5c-7a6f-7c2a-8d4a-4b5b1f6c8e9d"`
|
||||
EmailVerifiedAt string `json:"email_verified_at,omitempty" example:"2026-02-05T10:00:00Z"`
|
||||
TOTPEnabled bool `json:"totp_enabled" example:"false"`
|
||||
IsPreviouslyConfigured bool `json:"is_previously_configured" example:"false"`
|
||||
WebAuthnConfigured bool `json:"webauthn_configured" example:"false"`
|
||||
LinkedSSO bool `json:"linked_sso" example:"false"`
|
||||
PINConfigured bool `json:"pin_configured" example:"false"`
|
||||
Role RoleMini `json:"role,omitempty"`
|
||||
Roles []RoleMini `json:"roles,omitempty"`
|
||||
}
|
||||
|
||||
type UserImage struct {
|
||||
UUID string `json:"uuid,omitempty" example:"018f3a5c-7a6f-7c2a-8d4a-4b5b1f6c8e9f"`
|
||||
DownloadURL string `json:"download_url,omitempty" example:"https://example.com/presigned-url"`
|
||||
ThumbnailDownloadURL string `json:"thumbnail_download_url,omitempty" example:"https://example.com/presigned-thumb-url"`
|
||||
OriginalSizeBytes *int64 `json:"original_size_bytes,omitempty" example:"1024000"`
|
||||
}
|
||||
|
||||
type UserFoto struct {
|
||||
UUID string `json:"uuid,omitempty" example:"018f3a5c-7a6f-7c2a-8d4a-4b5b1f6c8e9f"`
|
||||
DownloadURL string `json:"download_url,omitempty" example:"https://storage.example.com/base/lude.jpg?X-Amz-Algorithm=AWS4-HMAC-SHA256&X-Amz-Expires=900&X-Amz-Signature=abc123"`
|
||||
ThumbnailDownloadURL string `json:"thumbnail_download_url,omitempty" example:"https://storage.example.com/base/lude.thumb.jpg?X-Amz-Algorithm=AWS4-HMAC-SHA256&X-Amz-Expires=900&X-Amz-Signature=abc123"`
|
||||
OriginalSizeBytes *int64 `json:"original_size_bytes,omitempty" example:"1024000"`
|
||||
}
|
||||
|
||||
type RoleMini struct {
|
||||
ID string `json:"id" example:"018f3a5c-7a6f-7c2a-8d4a-4b5b1f6c8e9d"`
|
||||
Name string `json:"name" example:"admin"`
|
||||
Description string `json:"description,omitempty" example:"Full access to all resources."`
|
||||
}
|
||||
|
||||
type PermissionMini struct {
|
||||
ID string `json:"id" example:"018f3a5c-7a6f-7c2a-8d4a-4b5b1f6c8e9a"`
|
||||
Name string `json:"name" example:"View Users"`
|
||||
Key string `json:"key" example:"users.read"`
|
||||
Description string `json:"description,omitempty" example:"Permission to view users."`
|
||||
RequiresPIN bool `json:"requires_pin" example:"false"`
|
||||
}
|
||||
|
||||
// Auth current-user payload used by /auth/login and /auth/me endpoints.
|
||||
type AuthCurrentUserAttributes struct {
|
||||
Email string `json:"email" example:"user@example.com"`
|
||||
EmailSSO string `json:"email_sso,omitempty" example:"user.sso@example.com"`
|
||||
FirstName string `json:"first_name" example:"John"`
|
||||
LastName string `json:"last_name" example:"Doe"`
|
||||
MobilePhone string `json:"mobile_phone,omitempty" example:"+628123456789"`
|
||||
Timezone string `json:"timezone" example:"Asia/Jakarta"`
|
||||
Foto *UserFoto `json:"foto,omitempty"`
|
||||
EmailVerifiedAt string `json:"email_verified_at,omitempty" example:"2026-02-05T10:00:00Z"`
|
||||
Roles []RoleMini `json:"roles,omitempty"`
|
||||
Permissions []PermissionMini `json:"permissions,omitempty"`
|
||||
PermissionModules []PermissionGroupResource `json:"permission_modules,omitempty"`
|
||||
TOTPEnabled bool `json:"totp_enabled" example:"false"`
|
||||
IsPreviouslyConfigured bool `json:"is_previously_configured" example:"false"`
|
||||
WebAuthnConfigured bool `json:"webauthn_configured" example:"false"`
|
||||
LinkedSSO bool `json:"linked_sso" example:"false"`
|
||||
PINConfigured bool `json:"pin_configured" example:"false"`
|
||||
PINBlocked bool `json:"pin_blocked" example:"false"`
|
||||
LoginMethod string `json:"login_method,omitempty" example:"email"`
|
||||
AuthProvider string `json:"auth_provider,omitempty" example:"email"`
|
||||
MicrosoftScope string `json:"microsoft_scope,omitempty" example:"openid profile email offline_access User.Read"`
|
||||
DULBases []DULBase `json:"dul_bases,omitempty"`
|
||||
}
|
||||
|
||||
type AuthCurrentUserResponse struct {
|
||||
Data struct {
|
||||
Type string `json:"type" example:"user"`
|
||||
ID string `json:"id" example:"018f3a5c-7a6f-7c2a-8d4a-4b5b1f6c8e9d"`
|
||||
Attributes AuthCurrentUserAttributes `json:"attributes"`
|
||||
} `json:"data"`
|
||||
}
|
||||
|
||||
type UserResponse struct {
|
||||
Data struct {
|
||||
Type string `json:"type" example:"user"`
|
||||
ID string `json:"id" example:"018f3a5c-7a6f-7c2a-8d4a-4b5b1f6c8e9d"`
|
||||
Attributes UserAttributes `json:"attributes"`
|
||||
} `json:"data"`
|
||||
}
|
||||
|
||||
// Error response for Swagger
|
||||
type ErrorResponse struct {
|
||||
Errors []struct {
|
||||
Status string `json:"status" example:"422"`
|
||||
Title string `json:"title" example:"Validation error"`
|
||||
Detail string `json:"detail" example:"email is required"`
|
||||
Source struct {
|
||||
Pointer string `json:"pointer" example:"/data/attributes/email"`
|
||||
} `json:"source"`
|
||||
} `json:"errors"`
|
||||
}
|
||||
569
internal/transport/http/dto/base_dto.go
Normal file
569
internal/transport/http/dto/base_dto.go
Normal file
@@ -0,0 +1,569 @@
|
||||
package dto
|
||||
|
||||
import (
|
||||
"context"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"wucher/internal/domain/base"
|
||||
"wucher/internal/shared/pkg/userctx"
|
||||
"wucher/internal/shared/pkg/uuidv7"
|
||||
)
|
||||
|
||||
// JSON:API Base DTOs
|
||||
|
||||
type BaseAttributes struct {
|
||||
BaseName string `json:"base" example:"Lude"`
|
||||
BaseAbbreviation string `json:"base_abbreviation,omitempty" example:"LOIG"`
|
||||
BaseCategory string `json:"base_category,omitempty" example:"regular" enums:"regular,hems"`
|
||||
Address string `json:"address,omitempty" example:"Lude Airfield"`
|
||||
Latitude float64 `json:"latitude" example:"47.3769"`
|
||||
Longitude float64 `json:"longitude" example:"8.5417"`
|
||||
LandlineNumber string `json:"landline_number,omitempty" example:"+62-21-1234"`
|
||||
MobileNumber string `json:"mobile_number,omitempty" example:"+62-812-1234"`
|
||||
Email string `json:"email,omitempty" example:"lude@wucher.local"`
|
||||
SortKey *int `json:"sortkey" example:"1"`
|
||||
SMSAlert bool `json:"sms_alert" example:"false"`
|
||||
Checklist bool `json:"checklist" example:"false"`
|
||||
LegTime string `json:"leg_time,omitempty" example:"00:20"`
|
||||
DefaultStartTimeType string `json:"default_start_time_type,omitempty" example:"FIXED" enums:"FIXED,BMCT,ECET"`
|
||||
DefaultEndTimeType string `json:"default_end_time_type,omitempty" example:"FIXED" enums:"FIXED,BMCT,ECET"`
|
||||
ShiftTime BaseShiftTime `json:"shift_time"`
|
||||
UTC string `json:"utc" example:"0"`
|
||||
Foto *BaseFoto `json:"foto,omitempty"`
|
||||
Dry bool `json:"dry" example:"false"`
|
||||
ControlCenter bool `json:"control_center" example:"false"`
|
||||
DUL bool `json:"dul" example:"false"`
|
||||
Notes string `json:"notes,omitempty" example:"Main base"`
|
||||
HEMSEDCContactIDs []string `json:"hems_edc_contact_ids,omitempty"`
|
||||
HEMSEDCs []BaseContact `json:"hems_edc,omitempty"`
|
||||
MedPaxContactIDs []string `json:"med_pax_contact_ids,omitempty"`
|
||||
MedPax []BaseContact `json:"med_pax,omitempty"`
|
||||
ResponsiblePilotContactIDs []string `json:"responsible_pilot_contact_ids,omitempty"`
|
||||
ResponsiblePilots []BaseContact `json:"responsible_pilots,omitempty"`
|
||||
OperationalShiftTimes []BaseOperationalShiftTime `json:"operational_shift_times,omitempty"`
|
||||
IsActive bool `json:"is_active" example:"true"`
|
||||
CreatedBy string `json:"created_by,omitempty" example:"018f3a5c-7a6f-7c2a-8d4a-4b5b1f6c8e9d"`
|
||||
UpdatedBy string `json:"updated_by,omitempty" example:"018f3a5c-7a6f-7c2a-8d4a-4b5b1f6c8e9d"`
|
||||
CreatedAt string `json:"created_at,omitempty" example:"2026-03-12T03:04:05Z"`
|
||||
UpdatedAt string `json:"updated_at,omitempty" example:"2026-03-12T03:04:05Z"`
|
||||
DeletedAt string `json:"deleted_at,omitempty" example:"2026-03-12T03:04:05Z" swaggerignore:"true"`
|
||||
DeletedBy string `json:"deleted_by,omitempty" example:"018f3a5c-7a6f-7c2a-8d4a-4b5b1f6c8e9d" swaggerignore:"true"`
|
||||
}
|
||||
|
||||
type BaseContact struct {
|
||||
ID string `json:"id"`
|
||||
FirstName string `json:"first_name,omitempty"`
|
||||
LastName string `json:"last_name,omitempty"`
|
||||
}
|
||||
|
||||
type BaseShiftTime struct {
|
||||
Start string `json:"start,omitempty" example:"06:00"`
|
||||
End string `json:"end,omitempty" example:"21:00"`
|
||||
}
|
||||
|
||||
type BaseOperationalShiftTime struct {
|
||||
DateStart string `json:"date_start" example:"2026-04-12"`
|
||||
DateEnd string `json:"date_end" example:"2026-04-12"`
|
||||
StartTimeType string `json:"start_time_type,omitempty" example:"FIXED" enums:"FIXED,BMCT,ECET"`
|
||||
EndTimeType string `json:"end_time_type,omitempty" example:"FIXED" enums:"FIXED,BMCT,ECET"`
|
||||
ShiftTime BaseShiftTime `json:"shift_time"`
|
||||
}
|
||||
|
||||
type BaseFoto struct {
|
||||
UUID string `json:"uuid" example:"018f3a5c-7a6f-7c2a-8d4a-4b5b1f6c8e9d"`
|
||||
DownloadURL string `json:"download_url" example:"https://storage.example.com/base/lude.jpg?X-Amz-Algorithm=AWS4-HMAC-SHA256&X-Amz-Expires=900&X-Amz-Signature=abc123"`
|
||||
ThumbnailDownloadURL string `json:"thumbnail_download_url,omitempty" example:"https://storage.example.com/base/lude.thumb.jpg?X-Amz-Algorithm=AWS4-HMAC-SHA256&X-Amz-Expires=900&X-Amz-Signature=abc123"`
|
||||
OriginalSizeBytes *int64 `json:"original_size_bytes,omitempty" example:"1024000"`
|
||||
}
|
||||
|
||||
type BaseResource struct {
|
||||
Type string `json:"type" example:"base"`
|
||||
ID string `json:"id" example:"018f3a5c-7a6f-7c2a-8d4a-4b5b1f6c8e9d"`
|
||||
Attributes BaseAttributes `json:"attributes"`
|
||||
}
|
||||
|
||||
type BaseResponse struct {
|
||||
Data BaseResource `json:"data"`
|
||||
}
|
||||
|
||||
type BaseListResponse struct {
|
||||
Data []BaseResource `json:"data"`
|
||||
Meta struct {
|
||||
PageNumber int `json:"page_number" example:"1"`
|
||||
PageSize int `json:"page_size" example:"20"`
|
||||
Total int64 `json:"total" example:"120"`
|
||||
} `json:"meta"`
|
||||
}
|
||||
|
||||
type BaseDataTableResponse struct {
|
||||
Data []BaseResource `json:"data"`
|
||||
Meta struct {
|
||||
Draw int `json:"draw" example:"1"`
|
||||
RecordsTotal int64 `json:"records_total" example:"120"`
|
||||
RecordsFiltered int64 `json:"records_filtered" example:"12"`
|
||||
} `json:"meta"`
|
||||
}
|
||||
|
||||
type BaseCreateAttributes struct {
|
||||
BaseName string `json:"base" validate:"required" example:"Lude"`
|
||||
BaseAbbreviation *string `json:"base_abbreviation,omitempty" example:"LOIG"`
|
||||
BaseCategory *string `json:"base_category,omitempty" example:"regular" enums:"regular,hems"`
|
||||
Address *string `json:"address,omitempty" example:"Lude Airfield"`
|
||||
Latitude *float64 `json:"latitude" validate:"required,gte=-90,lte=90" example:"47.3769"`
|
||||
Longitude *float64 `json:"longitude" validate:"required,gte=-180,lte=180" example:"8.5417"`
|
||||
LandlineNumber *string `json:"landline_number,omitempty" example:"+62-21-1234"`
|
||||
MobileNumber *string `json:"mobile_number,omitempty" example:"+62-812-1234"`
|
||||
Email *string `json:"email,omitempty" example:"lude@wucher.local"`
|
||||
SortKey *int `json:"sortkey" example:"1"`
|
||||
SMSAlert *bool `json:"sms_alert,omitempty" example:"false"`
|
||||
Checklist *bool `json:"checklist,omitempty" example:"false"`
|
||||
LegTime *string `json:"leg_time,omitempty" example:"00:20"`
|
||||
DefaultStartTimeType *string `json:"default_start_time_type,omitempty" example:"FIXED" enums:"FIXED,BMCT,ECET"`
|
||||
DefaultEndTimeType *string `json:"default_end_time_type,omitempty" example:"FIXED" enums:"FIXED,BMCT,ECET"`
|
||||
ShiftTime *BaseShiftTime `json:"shift_time,omitempty"`
|
||||
DefaultShiftStart *string `json:"default_shift_start,omitempty" example:"06:00:00"`
|
||||
DefaultShiftEnd *string `json:"default_shift_end,omitempty" example:"21:00:00"`
|
||||
DefaultShiftTime *string `json:"default_shift_time,omitempty" example:"12:00"`
|
||||
UTC *string `json:"utc,omitempty" example:"0"`
|
||||
FileUUID *string `json:"file_uuid,omitempty" example:"018f3a5c-7a6f-7c2a-8d4a-4b5b1f6c8e90"`
|
||||
Dry *bool `json:"dry,omitempty" example:"false"`
|
||||
ControlCenter *bool `json:"control_center,omitempty" example:"false"`
|
||||
DUL *bool `json:"dul,omitempty" example:"false"`
|
||||
Notes *string `json:"notes,omitempty" example:"Main base"`
|
||||
HEMSEDCContactIDs []string `json:"hems_edc_contact_ids,omitempty"`
|
||||
MedPaxContactIDs []string `json:"med_pax_contact_ids,omitempty"`
|
||||
ResponsiblePilotContactIDs []string `json:"responsible_pilot_contact_ids,omitempty"`
|
||||
OperationalShiftTimes []BaseOperationalShiftTime `json:"operational_shift_times,omitempty"`
|
||||
IsActive *bool `json:"is_active,omitempty" example:"true"`
|
||||
CreatedBy *string `json:"created_by,omitempty" example:"018f3a5c-7a6f-7c2a-8d4a-4b5b1f6c8e9d" swaggerignore:"true"`
|
||||
UpdatedBy *string `json:"updated_by,omitempty" example:"018f3a5c-7a6f-7c2a-8d4a-4b5b1f6c8e9d" swaggerignore:"true"`
|
||||
}
|
||||
|
||||
type BaseCreateData struct {
|
||||
Type string `json:"type" validate:"required,oneof=base_create base hems_base" enums:"base" example:"base"`
|
||||
Attributes BaseCreateAttributes `json:"attributes" validate:"required"`
|
||||
}
|
||||
|
||||
type BaseCreateRequest struct {
|
||||
Data BaseCreateData `json:"data" validate:"required"`
|
||||
}
|
||||
|
||||
type BaseUpdateAttributes struct {
|
||||
BaseName *string `json:"base,omitempty" example:"Lude"`
|
||||
BaseAbbreviation *string `json:"base_abbreviation,omitempty" example:"LOIG"`
|
||||
BaseCategory *string `json:"base_category,omitempty" example:"regular" enums:"regular,hems"`
|
||||
Address *string `json:"address,omitempty" example:"Lude Airfield"`
|
||||
Latitude *float64 `json:"latitude,omitempty" validate:"omitempty,gte=-90,lte=90" example:"47.3769"`
|
||||
Longitude *float64 `json:"longitude,omitempty" validate:"omitempty,gte=-180,lte=180" example:"8.5417"`
|
||||
LandlineNumber *string `json:"landline_number,omitempty" example:"+62-21-1234"`
|
||||
MobileNumber *string `json:"mobile_number,omitempty" example:"+62-812-1234"`
|
||||
Email *string `json:"email,omitempty" example:"lude@wucher.local"`
|
||||
SortKey NullInt `json:"sortkey,omitempty" swaggertype:"integer" example:"1"`
|
||||
SMSAlert *bool `json:"sms_alert,omitempty" example:"false"`
|
||||
Checklist *bool `json:"checklist,omitempty" example:"false"`
|
||||
LegTime *string `json:"leg_time,omitempty" example:"00:20"`
|
||||
DefaultStartTimeType *string `json:"default_start_time_type,omitempty" example:"FIXED" enums:"FIXED,BMCT,ECET"`
|
||||
DefaultEndTimeType *string `json:"default_end_time_type,omitempty" example:"FIXED" enums:"FIXED,BMCT,ECET"`
|
||||
DefaultShiftStart *string `json:"default_shift_start,omitempty" example:"06:00:00"`
|
||||
DefaultShiftEnd *string `json:"default_shift_end,omitempty" example:"21:00:00"`
|
||||
DefaultShiftTime *string `json:"default_shift_time,omitempty" example:"12:00"`
|
||||
ShiftTime *BaseShiftTime `json:"shift_time,omitempty"`
|
||||
UTC *string `json:"utc,omitempty" example:"0"`
|
||||
FileUUID *string `json:"file_uuid,omitempty" example:"018f3a5c-7a6f-7c2a-8d4a-4b5b1f6c8e90"`
|
||||
Dry *bool `json:"dry,omitempty" example:"false"`
|
||||
ControlCenter *bool `json:"control_center,omitempty" example:"false"`
|
||||
DUL *bool `json:"dul,omitempty" example:"false"`
|
||||
Notes *string `json:"notes,omitempty" example:"Main base"`
|
||||
HEMSEDCContactIDs *[]string `json:"hems_edc_contact_ids,omitempty"`
|
||||
MedPaxContactIDs *[]string `json:"med_pax_contact_ids,omitempty"`
|
||||
ResponsiblePilotContactIDs *[]string `json:"responsible_pilot_contact_ids,omitempty"`
|
||||
OperationalShiftTimes []BaseOperationalShiftTime `json:"operational_shift_times,omitempty"`
|
||||
IsActive *bool `json:"is_active,omitempty" example:"true"`
|
||||
CreatedBy *string `json:"created_by,omitempty" example:"018f3a5c-7a6f-7c2a-8d4a-4b5b1f6c8e9d" swaggerignore:"true"`
|
||||
UpdatedBy *string `json:"updated_by,omitempty" example:"018f3a5c-7a6f-7c2a-8d4a-4b5b1f6c8e9d" swaggerignore:"true"`
|
||||
}
|
||||
|
||||
type BaseUpdateData struct {
|
||||
Type string `json:"type" validate:"required,oneof=base_update base hems_base" enums:"base_update" example:"base_update"`
|
||||
ID string `json:"id,omitempty" example:"018f3a5c-7a6f-7c2a-8d4a-4b5b1f6c8e9d"`
|
||||
Attributes BaseUpdateAttributes `json:"attributes" validate:"required"`
|
||||
}
|
||||
|
||||
type BaseUpdateRequest struct {
|
||||
Data BaseUpdateData `json:"data" validate:"required"`
|
||||
}
|
||||
|
||||
type BaseDeleteAttributes struct {
|
||||
Deleted bool `json:"deleted" example:"true"`
|
||||
}
|
||||
|
||||
type BaseDeleteResource struct {
|
||||
Type string `json:"type" example:"base_delete"`
|
||||
ID string `json:"id" example:"019d6b10-a385-7e9e-a553-8e892bcec0ef"`
|
||||
Attributes BaseDeleteAttributes `json:"attributes"`
|
||||
}
|
||||
|
||||
type BaseDeleteResponse struct {
|
||||
Data BaseDeleteResource `json:"data"`
|
||||
}
|
||||
|
||||
type BaseResponseMappingInput struct {
|
||||
ResourceType string
|
||||
BaseCategory string
|
||||
ID string
|
||||
DefaultStartTimeType string
|
||||
DefaultEndTimeType string
|
||||
ShiftTime BaseShiftTime
|
||||
Foto *BaseFoto
|
||||
Latitude float64
|
||||
Longitude float64
|
||||
HEMSEDCContactIDs []string
|
||||
HEMSEDCs []BaseContact
|
||||
MedPaxContactIDs []string
|
||||
MedPax []BaseContact
|
||||
ResponsiblePilotContactIDs []string
|
||||
ResponsiblePilots []BaseContact
|
||||
OperationalShiftTimes []BaseOperationalShiftTime
|
||||
}
|
||||
|
||||
func MapBaseCreateAttributesToDomain(attrs BaseCreateAttributes) *base.Base {
|
||||
row := &base.Base{}
|
||||
row.BaseName = strings.TrimSpace(attrs.BaseName)
|
||||
row.BaseAbbreviation = strings.TrimSpace(stringOrDefault(attrs.BaseAbbreviation, ""))
|
||||
row.Address = strings.TrimSpace(stringOrDefault(attrs.Address, ""))
|
||||
row.Latitude = floatOrDefault(attrs.Latitude, 0)
|
||||
row.Longitude = floatOrDefault(attrs.Longitude, 0)
|
||||
row.LandlineNumber = strings.TrimSpace(stringOrDefault(attrs.LandlineNumber, ""))
|
||||
row.MobileNumber = strings.TrimSpace(stringOrDefault(attrs.MobileNumber, ""))
|
||||
row.Email = strings.TrimSpace(stringOrDefault(attrs.Email, ""))
|
||||
row.SortKey = cloneIntPtr(attrs.SortKey)
|
||||
row.SMSAlert = boolOrDefault(attrs.SMSAlert, false)
|
||||
row.Checklist = boolOrDefault(attrs.Checklist, false)
|
||||
row.LegTime = strings.TrimSpace(stringOrDefault(attrs.LegTime, ""))
|
||||
row.DefaultStartTimeType = normalizeShiftTimeType(stringOrDefault(attrs.DefaultStartTimeType, ""))
|
||||
row.DefaultEndTimeType = normalizeShiftTimeType(stringOrDefault(attrs.DefaultEndTimeType, ""))
|
||||
row.UTC = normalizeUTC(attrs.UTC)
|
||||
row.Dry = boolOrDefault(attrs.Dry, false)
|
||||
row.ControlCenter = boolOrDefault(attrs.ControlCenter, false)
|
||||
row.DUL = boolOrDefault(attrs.DUL, false)
|
||||
row.Notes = strings.TrimSpace(stringOrDefault(attrs.Notes, ""))
|
||||
row.IsActive = boolOrDefault(attrs.IsActive, true)
|
||||
row.OperationalShiftTimes = mapBaseOperationalShiftTimesToDomain(attrs.OperationalShiftTimes)
|
||||
return row
|
||||
}
|
||||
|
||||
func ApplyBaseUpdateAttributesToDomain(row *base.Base, attrs BaseUpdateAttributes) {
|
||||
if attrs.BaseName != nil {
|
||||
row.BaseName = strings.TrimSpace(*attrs.BaseName)
|
||||
}
|
||||
if attrs.BaseAbbreviation != nil {
|
||||
row.BaseAbbreviation = strings.TrimSpace(*attrs.BaseAbbreviation)
|
||||
}
|
||||
if attrs.Address != nil {
|
||||
row.Address = strings.TrimSpace(*attrs.Address)
|
||||
}
|
||||
if attrs.Latitude != nil {
|
||||
row.Latitude = *attrs.Latitude
|
||||
}
|
||||
if attrs.Longitude != nil {
|
||||
row.Longitude = *attrs.Longitude
|
||||
}
|
||||
if attrs.LandlineNumber != nil {
|
||||
row.LandlineNumber = strings.TrimSpace(*attrs.LandlineNumber)
|
||||
}
|
||||
if attrs.MobileNumber != nil {
|
||||
row.MobileNumber = strings.TrimSpace(*attrs.MobileNumber)
|
||||
}
|
||||
if attrs.Email != nil {
|
||||
row.Email = strings.TrimSpace(*attrs.Email)
|
||||
}
|
||||
if attrs.SortKey.Set {
|
||||
if attrs.SortKey.Valid {
|
||||
v := attrs.SortKey.Value
|
||||
row.SortKey = &v
|
||||
} else {
|
||||
row.SortKey = nil
|
||||
}
|
||||
}
|
||||
if attrs.SMSAlert != nil {
|
||||
row.SMSAlert = *attrs.SMSAlert
|
||||
}
|
||||
if attrs.Checklist != nil {
|
||||
row.Checklist = *attrs.Checklist
|
||||
}
|
||||
if attrs.LegTime != nil {
|
||||
row.LegTime = strings.TrimSpace(*attrs.LegTime)
|
||||
}
|
||||
if attrs.DefaultStartTimeType != nil {
|
||||
row.DefaultStartTimeType = normalizeShiftTimeType(*attrs.DefaultStartTimeType)
|
||||
}
|
||||
if attrs.DefaultEndTimeType != nil {
|
||||
row.DefaultEndTimeType = normalizeShiftTimeType(*attrs.DefaultEndTimeType)
|
||||
}
|
||||
if attrs.UTC != nil {
|
||||
row.UTC = normalizeUTC(attrs.UTC)
|
||||
}
|
||||
if attrs.Dry != nil {
|
||||
row.Dry = *attrs.Dry
|
||||
}
|
||||
if attrs.ControlCenter != nil {
|
||||
row.ControlCenter = *attrs.ControlCenter
|
||||
}
|
||||
if attrs.DUL != nil {
|
||||
row.DUL = *attrs.DUL
|
||||
}
|
||||
if attrs.Notes != nil {
|
||||
row.Notes = strings.TrimSpace(*attrs.Notes)
|
||||
}
|
||||
if attrs.IsActive != nil {
|
||||
row.IsActive = *attrs.IsActive
|
||||
}
|
||||
}
|
||||
|
||||
func mapBaseOperationalShiftTimesToDomain(items []BaseOperationalShiftTime) []base.BaseOperationalShiftTime {
|
||||
if len(items) == 0 {
|
||||
return nil
|
||||
}
|
||||
out := make([]base.BaseOperationalShiftTime, 0, len(items))
|
||||
for i := range items {
|
||||
dateStart := parseBaseOperationDatePtr(items[i].DateStart)
|
||||
dateEnd := parseBaseOperationDatePtr(items[i].DateEnd)
|
||||
out = append(out, base.BaseOperationalShiftTime{
|
||||
DateStart: dateStart,
|
||||
DateEnd: dateEnd,
|
||||
StartTimeType: normalizeShiftTimeType(items[i].StartTimeType),
|
||||
EndTimeType: normalizeShiftTimeType(items[i].EndTimeType),
|
||||
ShiftStart: normalizeBaseClockValue(items[i].ShiftTime.Start),
|
||||
ShiftEnd: normalizeBaseClockValue(items[i].ShiftTime.End),
|
||||
})
|
||||
}
|
||||
return out
|
||||
}
|
||||
|
||||
func MapBaseOperationalShiftTimesToDTO(items []base.BaseOperationalShiftTime) []BaseOperationalShiftTime {
|
||||
if len(items) == 0 {
|
||||
return nil
|
||||
}
|
||||
out := make([]BaseOperationalShiftTime, 0, len(items))
|
||||
for i := range items {
|
||||
out = append(out, BaseOperationalShiftTime{
|
||||
DateStart: timePtrToDateString(items[i].DateStart),
|
||||
DateEnd: timePtrToDateString(items[i].DateEnd),
|
||||
StartTimeType: normalizeShiftTimeType(items[i].StartTimeType),
|
||||
EndTimeType: normalizeShiftTimeType(items[i].EndTimeType),
|
||||
ShiftTime: BaseShiftTime{
|
||||
Start: DisplayBaseShiftValue(items[i].StartTimeType, items[i].ShiftStart),
|
||||
End: DisplayBaseShiftValue(items[i].EndTimeType, items[i].ShiftEnd),
|
||||
},
|
||||
})
|
||||
}
|
||||
return out
|
||||
}
|
||||
|
||||
func parseBaseOperationDate(raw string) time.Time {
|
||||
parsed, err := time.Parse("2006-01-02", strings.TrimSpace(raw))
|
||||
if err != nil {
|
||||
return time.Time{}
|
||||
}
|
||||
return parsed.UTC()
|
||||
}
|
||||
|
||||
func parseBaseOperationDatePtr(raw string) *time.Time {
|
||||
parsed := parseBaseOperationDate(raw)
|
||||
if parsed.IsZero() {
|
||||
return nil
|
||||
}
|
||||
return &parsed
|
||||
}
|
||||
|
||||
func timePtrToDateString(v *time.Time) string {
|
||||
if v == nil || v.IsZero() {
|
||||
return ""
|
||||
}
|
||||
return v.UTC().Format("2006-01-02")
|
||||
}
|
||||
|
||||
func normalizeBaseClockValue(raw string) string {
|
||||
trimmed := strings.TrimSpace(raw)
|
||||
if trimmed == "" {
|
||||
return ""
|
||||
}
|
||||
if len(trimmed) == 5 {
|
||||
trimmed += ":00"
|
||||
}
|
||||
parsed, err := time.Parse("15:04:05", trimmed)
|
||||
if err != nil {
|
||||
return ""
|
||||
}
|
||||
return parsed.UTC().Format("15:04:05")
|
||||
}
|
||||
|
||||
func normalizeBaseShiftDisplay(raw string) string {
|
||||
s := strings.TrimSpace(raw)
|
||||
if s == "" {
|
||||
return s
|
||||
}
|
||||
layouts := []string{
|
||||
"15:04:05",
|
||||
"15:04",
|
||||
"2006-01-02 15:04:05",
|
||||
"2006-01-02 15:04",
|
||||
time.RFC3339,
|
||||
time.RFC3339Nano,
|
||||
}
|
||||
for _, layout := range layouts {
|
||||
if parsed, err := time.Parse(layout, s); err == nil {
|
||||
return parsed.UTC().Format("15:04")
|
||||
}
|
||||
}
|
||||
if len(s) >= 5 && isDigitBaseClock(s[0]) && isDigitBaseClock(s[1]) && s[2] == ':' && isDigitBaseClock(s[3]) && isDigitBaseClock(s[4]) {
|
||||
return s[:5]
|
||||
}
|
||||
return s
|
||||
}
|
||||
|
||||
// DisplayBaseShiftValue returns the user-facing representation for a shift value.
|
||||
// Fixed shifts are shown as HH:MM, while BMCT/ECET are shown as labels.
|
||||
func DisplayBaseShiftValue(timeType, raw string) string {
|
||||
switch normalizeShiftTimeType(timeType) {
|
||||
case "BMCT", "ECET":
|
||||
return normalizeShiftTimeType(timeType)
|
||||
default:
|
||||
return normalizeBaseShiftDisplay(raw)
|
||||
}
|
||||
}
|
||||
|
||||
func normalizeShiftTimeType(raw string) string {
|
||||
switch strings.ToUpper(strings.TrimSpace(raw)) {
|
||||
case "", "FIXED":
|
||||
return "FIXED"
|
||||
case "BMCT":
|
||||
return "BMCT"
|
||||
case "ECET":
|
||||
return "ECET"
|
||||
default:
|
||||
return strings.ToUpper(strings.TrimSpace(raw))
|
||||
}
|
||||
}
|
||||
|
||||
func isDigitBaseClock(b byte) bool {
|
||||
return b >= '0' && b <= '9'
|
||||
}
|
||||
|
||||
func MapBaseToResource(row *base.Base, in BaseResponseMappingInput) BaseResource {
|
||||
if row == nil {
|
||||
return BaseResource{
|
||||
Type: in.ResourceType,
|
||||
Attributes: BaseAttributes{
|
||||
BaseCategory: in.BaseCategory,
|
||||
DefaultStartTimeType: normalizeShiftTimeType(in.DefaultStartTimeType),
|
||||
DefaultEndTimeType: normalizeShiftTimeType(in.DefaultEndTimeType),
|
||||
ShiftTime: in.ShiftTime,
|
||||
OperationalShiftTimes: in.OperationalShiftTimes,
|
||||
Latitude: in.Latitude,
|
||||
Longitude: in.Longitude,
|
||||
},
|
||||
}
|
||||
}
|
||||
return BaseResource{
|
||||
Type: in.ResourceType,
|
||||
ID: in.ID,
|
||||
Attributes: BaseAttributes{
|
||||
BaseName: row.BaseName,
|
||||
BaseAbbreviation: row.BaseAbbreviation,
|
||||
BaseCategory: in.BaseCategory,
|
||||
Address: row.Address,
|
||||
Latitude: row.Latitude,
|
||||
Longitude: row.Longitude,
|
||||
LandlineNumber: row.LandlineNumber,
|
||||
MobileNumber: row.MobileNumber,
|
||||
Email: row.Email,
|
||||
SortKey: cloneIntPtr(row.SortKey),
|
||||
SMSAlert: row.SMSAlert,
|
||||
Checklist: row.Checklist,
|
||||
LegTime: row.LegTime,
|
||||
DefaultStartTimeType: normalizeShiftTimeType(row.DefaultStartTimeType),
|
||||
DefaultEndTimeType: normalizeShiftTimeType(row.DefaultEndTimeType),
|
||||
ShiftTime: in.ShiftTime,
|
||||
UTC: row.UTC,
|
||||
Foto: in.Foto,
|
||||
Dry: row.Dry,
|
||||
ControlCenter: row.ControlCenter,
|
||||
DUL: row.DUL,
|
||||
Notes: row.Notes,
|
||||
HEMSEDCContactIDs: in.HEMSEDCContactIDs,
|
||||
HEMSEDCs: in.HEMSEDCs,
|
||||
MedPaxContactIDs: in.MedPaxContactIDs,
|
||||
MedPax: in.MedPax,
|
||||
ResponsiblePilotContactIDs: in.ResponsiblePilotContactIDs,
|
||||
ResponsiblePilots: in.ResponsiblePilots,
|
||||
OperationalShiftTimes: in.OperationalShiftTimes,
|
||||
IsActive: row.IsActive,
|
||||
CreatedBy: bytesToUUID(row.CreatedBy),
|
||||
UpdatedBy: bytesToUUID(row.UpdatedBy),
|
||||
CreatedAt: row.CreatedAt.UTC().Format(time.RFC3339),
|
||||
UpdatedAt: row.UpdatedAt.UTC().Format(time.RFC3339),
|
||||
DeletedAt: timePtrToString(row.DeletedAt),
|
||||
DeletedBy: bytesToUUID(row.DeletedBy),
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func stringOrDefault(v *string, d string) string {
|
||||
if v == nil {
|
||||
return d
|
||||
}
|
||||
return *v
|
||||
}
|
||||
|
||||
func boolOrDefault(v *bool, d bool) bool {
|
||||
if v == nil {
|
||||
return d
|
||||
}
|
||||
return *v
|
||||
}
|
||||
|
||||
func normalizeUTC(v *string) string {
|
||||
s := strings.TrimSpace(stringOrDefault(v, "0"))
|
||||
if s == "" {
|
||||
return "0"
|
||||
}
|
||||
return s
|
||||
}
|
||||
|
||||
func floatOrDefault(v *float64, d float64) float64 {
|
||||
if v == nil {
|
||||
return d
|
||||
}
|
||||
return *v
|
||||
}
|
||||
|
||||
func cloneIntPtr(v *int) *int {
|
||||
if v == nil {
|
||||
return nil
|
||||
}
|
||||
x := *v
|
||||
return &x
|
||||
}
|
||||
|
||||
func bytesToUUID(v []byte) string {
|
||||
if len(v) == 0 {
|
||||
return ""
|
||||
}
|
||||
if name := userctx.GetDisplayName(context.TODO(), v); name != "" {
|
||||
return name
|
||||
}
|
||||
s, err := uuidv7.BytesToString(v)
|
||||
if err != nil {
|
||||
return ""
|
||||
}
|
||||
return s
|
||||
}
|
||||
|
||||
func timePtrToString(v *time.Time) string {
|
||||
if v == nil {
|
||||
return ""
|
||||
}
|
||||
return v.UTC().Format(time.RFC3339)
|
||||
}
|
||||
53
internal/transport/http/dto/before_flight_inspection_dto.go
Normal file
53
internal/transport/http/dto/before_flight_inspection_dto.go
Normal file
@@ -0,0 +1,53 @@
|
||||
package dto
|
||||
|
||||
type BeforeFlightInspectionAttributes struct {
|
||||
ID string `json:"id,omitempty" example:"019d6772-471c-7b70-b42a-d94020ef61e4"`
|
||||
FlightInspectionID string `json:"flight_inspection_id,omitempty" example:"019d6772-471c-7b70-b42a-d94020ef61e4"`
|
||||
FlightInspection *FlightInspectionResource `json:"flight_inspection,omitempty"`
|
||||
OilEngineNR1Checked *bool `json:"oil_engine_nr1_checked,omitempty"`
|
||||
OilEngineNR2Checked *bool `json:"oil_engine_nr2_checked,omitempty"`
|
||||
HydraulicLHChecked *bool `json:"hydraulic_lh_checked,omitempty"`
|
||||
HydraulicRHChecked *bool `json:"hydraulic_rh_checked,omitempty"`
|
||||
OilTransmissionMGBChecked *bool `json:"oil_transmission_mgb_checked,omitempty"`
|
||||
OilTransmissionIGBChecked *bool `json:"oil_transmission_igb_checked,omitempty"`
|
||||
OilTransmissionTGBChecked *bool `json:"oil_transmission_tgb_checked,omitempty"`
|
||||
FuelAmount *float64 `json:"fuel_amount,omitempty" example:"500.5"`
|
||||
FuelUnit string `json:"fuel_unit,omitempty" example:"LT"`
|
||||
FuelAddedAmount *float64 `json:"fuel_added_amount,omitempty" example:"100.0"`
|
||||
Note *string `json:"note,omitempty" example:"All checks completed"`
|
||||
CreatedAt string `json:"created_at,omitempty" example:"2026-04-09T05:00:00Z"`
|
||||
UpdatedAt string `json:"updated_at,omitempty" example:"2026-04-09T05:00:00Z"`
|
||||
}
|
||||
|
||||
type BeforeFlightInspectionResource struct {
|
||||
Type string `json:"type" example:"before_flight_inspection"`
|
||||
ID string `json:"id,omitempty" example:"019d6772-471c-7b70-b42a-d94020ef61e4"`
|
||||
Attributes BeforeFlightInspectionAttributes `json:"attributes"`
|
||||
}
|
||||
|
||||
type BeforeFlightInspectionResponse struct {
|
||||
Data BeforeFlightInspectionResource `json:"data"`
|
||||
}
|
||||
|
||||
type BeforeFlightInspectionUpsertAttributes struct {
|
||||
OilEngineNR1Checked *bool `json:"oil_engine_nr1_checked,omitempty"`
|
||||
OilEngineNR2Checked *bool `json:"oil_engine_nr2_checked,omitempty"`
|
||||
HydraulicLHChecked *bool `json:"hydraulic_lh_checked,omitempty"`
|
||||
HydraulicRHChecked *bool `json:"hydraulic_rh_checked,omitempty"`
|
||||
OilTransmissionMGBChecked *bool `json:"oil_transmission_mgb_checked,omitempty"`
|
||||
OilTransmissionIGBChecked *bool `json:"oil_transmission_igb_checked,omitempty"`
|
||||
OilTransmissionTGBChecked *bool `json:"oil_transmission_tgb_checked,omitempty"`
|
||||
FuelAmount *float64 `json:"fuel_amount,omitempty" validate:"omitempty,gte=0"`
|
||||
FuelUnit *string `json:"fuel_unit,omitempty" validate:"omitempty,oneof=LT KG POUND"`
|
||||
FuelAddedAmount *float64 `json:"fuel_added_amount,omitempty" validate:"omitempty,gte=0"`
|
||||
Note *string `json:"note,omitempty"`
|
||||
}
|
||||
|
||||
type BeforeFlightInspectionUpsertData struct {
|
||||
Type string `json:"type" validate:"required,oneof=before_flight_inspection_upsert before_flight_inspection"`
|
||||
Attributes BeforeFlightInspectionUpsertAttributes `json:"attributes" validate:"required"`
|
||||
}
|
||||
|
||||
type BeforeFlightInspectionUpsertRequest struct {
|
||||
Data BeforeFlightInspectionUpsertData `json:"data" validate:"required"`
|
||||
}
|
||||
423
internal/transport/http/dto/contacts_dto.go
Normal file
423
internal/transport/http/dto/contacts_dto.go
Normal file
@@ -0,0 +1,423 @@
|
||||
package dto
|
||||
|
||||
import "time"
|
||||
|
||||
type ContactBaseAttributes struct {
|
||||
Email string `json:"email,omitempty" validate:"omitempty,email"`
|
||||
SSOEmail string `json:"sso_email,omitempty" validate:"omitempty,email"`
|
||||
IsAdmin *bool `json:"is_admin,omitempty"`
|
||||
Username string `json:"username,omitempty" validate:"omitempty,max=100"`
|
||||
FirstName string `json:"first_name,omitempty" validate:"omitempty,max=100"`
|
||||
LastName string `json:"last_name,omitempty" validate:"omitempty,max=100"`
|
||||
MobilePhone string `json:"mobile_phone,omitempty" validate:"omitempty,max=32"`
|
||||
Timezone string `json:"timezone,omitempty" validate:"omitempty,max=64"`
|
||||
FileUUID string `json:"file_uuid,omitempty" validate:"omitempty,max=64"`
|
||||
SortKey *int `json:"sortkey"`
|
||||
IsActive *bool `json:"is_active,omitempty"`
|
||||
}
|
||||
|
||||
type PilotProfileAttributes struct {
|
||||
PilotCategory string `json:"pilot_category,omitempty" validate:"omitempty,oneof=regular freelance dry_lease"`
|
||||
ShortName string `json:"short_name,omitempty" validate:"omitempty,max=120"`
|
||||
LicenseNo string `json:"license_no,omitempty" validate:"omitempty,max=120"`
|
||||
LicenseIssuedAt *time.Time `json:"license_issued_at,omitempty"`
|
||||
LicenseExpiredAt *time.Time `json:"license_expired_at,omitempty"`
|
||||
TechnicianLicenseNo string `json:"technician_license_no,omitempty" validate:"omitempty,max=120"`
|
||||
HasIFRQualification *bool `json:"has_ifr_qualification,omitempty"`
|
||||
IsChiefPilot *bool `json:"is_chief_pilot,omitempty"`
|
||||
DULBaseIDs []string `json:"dul_base_ids,omitempty" validate:"omitempty,dive,uuid" swaggerignore:"true"`
|
||||
DULBase []DULBase `json:"dul_base,omitempty"`
|
||||
TotalFlightMinutes *int `json:"total_flight_minutes,omitempty"`
|
||||
ResponsiblePilotMinutes *int `json:"responsible_pilot_minutes,omitempty"`
|
||||
SecondPilotMinutes *int `json:"second_pilot_minutes,omitempty"`
|
||||
DoubleCommandMinutes *int `json:"double_command_minutes,omitempty"`
|
||||
FlightInstructorMinutes *int `json:"flight_instructor_minutes,omitempty"`
|
||||
NightFlightMinutes *int `json:"night_flight_minutes,omitempty"`
|
||||
IFRFlightMinutes *int `json:"ifr_flight_minutes,omitempty"`
|
||||
Location string `json:"location,omitempty" validate:"omitempty,max=255"`
|
||||
Postcode string `json:"postcode,omitempty" validate:"omitempty,max=32"`
|
||||
StreetLine string `json:"street_line,omitempty" validate:"omitempty,max=255"`
|
||||
Note string `json:"note,omitempty"`
|
||||
WeightKG *float64 `json:"weight_kg,omitempty"`
|
||||
}
|
||||
|
||||
type DULBase struct {
|
||||
ID string `json:"id" example:"0197f3b2-c4d8-4b12-a1f9-c1de23456789"`
|
||||
Name string `json:"name" example:"Halim Base"`
|
||||
Category string `json:"category" example:"hems"`
|
||||
}
|
||||
|
||||
type DoctorProfileAttributes struct {
|
||||
ShortName string `json:"short_name,omitempty" validate:"omitempty,max=120"`
|
||||
Specialization string `json:"specialization,omitempty" validate:"omitempty,max=120"`
|
||||
SubSpecialization string `json:"sub_specialization,omitempty" validate:"omitempty,max=120"`
|
||||
IsChiefPhysician *bool `json:"is_chief_physician,omitempty"`
|
||||
Location string `json:"location,omitempty" validate:"omitempty,max=255"`
|
||||
Postcode string `json:"postcode,omitempty" validate:"omitempty,max=32"`
|
||||
StreetLine string `json:"street_line,omitempty" validate:"omitempty,max=255"`
|
||||
Note string `json:"note,omitempty"`
|
||||
}
|
||||
|
||||
type AirRescuerProfileAttributes struct {
|
||||
ShortName string `json:"short_name,omitempty" validate:"omitempty,max=120"`
|
||||
ResponsibleFlightRescuer *bool `json:"responsible_flight_rescuer,omitempty"`
|
||||
Location string `json:"location,omitempty" validate:"omitempty,max=255"`
|
||||
Postcode string `json:"postcode,omitempty" validate:"omitempty,max=32"`
|
||||
StreetLine string `json:"street_line,omitempty" validate:"omitempty,max=255"`
|
||||
Note string `json:"note,omitempty"`
|
||||
}
|
||||
|
||||
type TechnicianProfileAttributes struct {
|
||||
ShortName string `json:"short_name,omitempty" validate:"omitempty,max=120"`
|
||||
LicenseNo string `json:"license_no,omitempty" validate:"omitempty,max=120"`
|
||||
IsChiefTechnician *bool `json:"is_chief_technician,omitempty"`
|
||||
IsResponsibleComplaints *bool `json:"is_responsible_complaints,omitempty"`
|
||||
Location string `json:"location,omitempty" validate:"omitempty,max=255"`
|
||||
Postcode string `json:"postcode,omitempty" validate:"omitempty,max=32"`
|
||||
StreetLine string `json:"street_line,omitempty" validate:"omitempty,max=255"`
|
||||
Note string `json:"note,omitempty"`
|
||||
}
|
||||
|
||||
type FlightAssistantProfileAttributes struct {
|
||||
ShortName string `json:"short_name,omitempty" validate:"omitempty,max=120"`
|
||||
Location string `json:"location,omitempty" validate:"omitempty,max=255"`
|
||||
Postcode string `json:"postcode,omitempty" validate:"omitempty,max=32"`
|
||||
StreetLine string `json:"street_line,omitempty" validate:"omitempty,max=255"`
|
||||
Note string `json:"note,omitempty"`
|
||||
}
|
||||
|
||||
type StaffProfileAttributes struct {
|
||||
ShortName string `json:"short_name,omitempty" validate:"omitempty,max=120"`
|
||||
RoleLabel string `json:"role_label,omitempty" validate:"omitempty,max=120"`
|
||||
Location string `json:"location,omitempty" validate:"omitempty,max=255"`
|
||||
Postcode string `json:"postcode,omitempty" validate:"omitempty,max=32"`
|
||||
StreetLine string `json:"street_line,omitempty" validate:"omitempty,max=255"`
|
||||
Note string `json:"note,omitempty"`
|
||||
}
|
||||
|
||||
type ContactCreateAttributes struct {
|
||||
RoleID string `json:"role_id,omitempty" example:"0195f6f8-0f8d-7e3e-b8f2-8abff9b5c1d1"`
|
||||
RoleIDs []string `json:"role_ids,omitempty" example:"0195f6f8-0f8d-7e3e-b8f2-8abff9b5c1d1,0195f6f8-0f8d-7e3e-b8f2-8abff9b5c1d2"`
|
||||
ContactBaseAttributes
|
||||
AirRescuer *AirRescuerProfileAttributes `json:"air_rescuer,omitempty"`
|
||||
Doctor *DoctorProfileAttributes `json:"doctor,omitempty"`
|
||||
FlightAssistant *FlightAssistantProfileAttributes `json:"flight_assistant,omitempty"`
|
||||
Pilot *PilotProfileAttributes `json:"pilot,omitempty"`
|
||||
Staff *StaffProfileAttributes `json:"staff,omitempty"`
|
||||
Technician *TechnicianProfileAttributes `json:"technician,omitempty"`
|
||||
}
|
||||
|
||||
type ContactCreateData struct {
|
||||
Type string `json:"type" validate:"required" example:"contact_create"`
|
||||
Attributes ContactCreateAttributes `json:"attributes" validate:"required"`
|
||||
}
|
||||
|
||||
type ContactCreateRequest struct {
|
||||
Data ContactCreateData `json:"data" validate:"required"`
|
||||
}
|
||||
|
||||
type PilotProfileUpdateAttributes struct {
|
||||
PilotCategory *string `json:"pilot_category,omitempty" validate:"omitempty,oneof=regular freelance dry_lease"`
|
||||
ShortName *string `json:"short_name,omitempty" validate:"omitempty,max=120"`
|
||||
LicenseNo *string `json:"license_no,omitempty" validate:"omitempty,max=120"`
|
||||
LicenseIssuedAt *time.Time `json:"license_issued_at,omitempty"`
|
||||
LicenseExpiredAt *time.Time `json:"license_expired_at,omitempty"`
|
||||
TechnicianLicenseNo *string `json:"technician_license_no,omitempty" validate:"omitempty,max=120"`
|
||||
HasIFRQualification *bool `json:"has_ifr_qualification,omitempty"`
|
||||
IsChiefPilot *bool `json:"is_chief_pilot,omitempty"`
|
||||
DULBaseIDs []string `json:"dul_base_ids,omitempty" validate:"omitempty,dive,uuid" swaggerignore:"true"`
|
||||
DULBase []DULBase `json:"dul_base,omitempty"`
|
||||
TotalFlightMinutes *int `json:"total_flight_minutes,omitempty"`
|
||||
ResponsiblePilotMinutes *int `json:"responsible_pilot_minutes,omitempty"`
|
||||
SecondPilotMinutes *int `json:"second_pilot_minutes,omitempty"`
|
||||
DoubleCommandMinutes *int `json:"double_command_minutes,omitempty"`
|
||||
FlightInstructorMinutes *int `json:"flight_instructor_minutes,omitempty"`
|
||||
NightFlightMinutes *int `json:"night_flight_minutes,omitempty"`
|
||||
IFRFlightMinutes *int `json:"ifr_flight_minutes,omitempty"`
|
||||
Location *string `json:"location,omitempty" validate:"omitempty,max=255"`
|
||||
Postcode *string `json:"postcode,omitempty" validate:"omitempty,max=32"`
|
||||
StreetLine *string `json:"street_line,omitempty" validate:"omitempty,max=255"`
|
||||
Note *string `json:"note,omitempty"`
|
||||
WeightKG *float64 `json:"weight_kg,omitempty"`
|
||||
}
|
||||
|
||||
type DoctorProfileUpdateAttributes struct {
|
||||
ShortName *string `json:"short_name,omitempty" validate:"omitempty,max=120"`
|
||||
Specialization *string `json:"specialization,omitempty" validate:"omitempty,max=120"`
|
||||
SubSpecialization *string `json:"sub_specialization,omitempty" validate:"omitempty,max=120"`
|
||||
IsChiefPhysician *bool `json:"is_chief_physician,omitempty"`
|
||||
Location *string `json:"location,omitempty" validate:"omitempty,max=255"`
|
||||
Postcode *string `json:"postcode,omitempty" validate:"omitempty,max=32"`
|
||||
StreetLine *string `json:"street_line,omitempty" validate:"omitempty,max=255"`
|
||||
Note *string `json:"note,omitempty"`
|
||||
}
|
||||
|
||||
type AirRescuerProfileUpdateAttributes struct {
|
||||
ShortName *string `json:"short_name,omitempty" validate:"omitempty,max=120"`
|
||||
ResponsibleFlightRescuer *bool `json:"responsible_flight_rescuer,omitempty"`
|
||||
Location *string `json:"location,omitempty" validate:"omitempty,max=255"`
|
||||
Postcode *string `json:"postcode,omitempty" validate:"omitempty,max=32"`
|
||||
StreetLine *string `json:"street_line,omitempty" validate:"omitempty,max=255"`
|
||||
Note *string `json:"note,omitempty"`
|
||||
}
|
||||
|
||||
type TechnicianProfileUpdateAttributes struct {
|
||||
ShortName *string `json:"short_name,omitempty" validate:"omitempty,max=120"`
|
||||
LicenseNo *string `json:"license_no,omitempty" validate:"omitempty,max=120"`
|
||||
IsChiefTechnician *bool `json:"is_chief_technician,omitempty"`
|
||||
IsResponsibleComplaints *bool `json:"is_responsible_complaints,omitempty"`
|
||||
Location *string `json:"location,omitempty" validate:"omitempty,max=255"`
|
||||
Postcode *string `json:"postcode,omitempty" validate:"omitempty,max=32"`
|
||||
StreetLine *string `json:"street_line,omitempty" validate:"omitempty,max=255"`
|
||||
Note *string `json:"note,omitempty"`
|
||||
}
|
||||
|
||||
type FlightAssistantProfileUpdateAttributes struct {
|
||||
ShortName *string `json:"short_name,omitempty" validate:"omitempty,max=120"`
|
||||
Location *string `json:"location,omitempty" validate:"omitempty,max=255"`
|
||||
Postcode *string `json:"postcode,omitempty" validate:"omitempty,max=32"`
|
||||
StreetLine *string `json:"street_line,omitempty" validate:"omitempty,max=255"`
|
||||
Note *string `json:"note,omitempty"`
|
||||
}
|
||||
|
||||
type StaffProfileUpdateAttributes struct {
|
||||
ShortName *string `json:"short_name,omitempty" validate:"omitempty,max=120"`
|
||||
RoleLabel *string `json:"role_label,omitempty" validate:"omitempty,max=120"`
|
||||
Location *string `json:"location,omitempty" validate:"omitempty,max=255"`
|
||||
Postcode *string `json:"postcode,omitempty" validate:"omitempty,max=32"`
|
||||
StreetLine *string `json:"street_line,omitempty" validate:"omitempty,max=255"`
|
||||
Note *string `json:"note,omitempty"`
|
||||
}
|
||||
|
||||
type ContactUpdateAttributes struct {
|
||||
RoleID *string `json:"role_id,omitempty" example:"0195f6f8-0f8d-7e3e-b8f2-8abff9b5c1d1"`
|
||||
RoleIDs []string `json:"role_ids,omitempty" example:"0195f6f8-0f8d-7e3e-b8f2-8abff9b5c1d1,0195f6f8-0f8d-7e3e-b8f2-8abff9b5c1d2"`
|
||||
Email *string `json:"email,omitempty" validate:"omitempty,email"`
|
||||
SSOEmail *string `json:"sso_email,omitempty" validate:"omitempty,email"`
|
||||
IsAdmin *bool `json:"is_admin,omitempty"`
|
||||
Username *string `json:"username,omitempty" validate:"omitempty,max=100"`
|
||||
FirstName *string `json:"first_name,omitempty" validate:"omitempty,max=100"`
|
||||
LastName *string `json:"last_name,omitempty" validate:"omitempty,max=100"`
|
||||
MobilePhone *string `json:"mobile_phone,omitempty" validate:"omitempty,max=32"`
|
||||
Timezone *string `json:"timezone,omitempty" validate:"omitempty,max=64"`
|
||||
FileUUID *string `json:"file_uuid,omitempty" validate:"omitempty,max=64"`
|
||||
SortKey NullInt `json:"sortkey,omitempty" swaggertype:"integer"`
|
||||
IsActive *bool `json:"is_active,omitempty"`
|
||||
AirRescuer *AirRescuerProfileUpdateAttributes `json:"air_rescuer,omitempty"`
|
||||
Doctor *DoctorProfileUpdateAttributes `json:"doctor,omitempty"`
|
||||
FlightAssistant *FlightAssistantProfileUpdateAttributes `json:"flight_assistant,omitempty"`
|
||||
Pilot *PilotProfileUpdateAttributes `json:"pilot,omitempty"`
|
||||
Staff *StaffProfileUpdateAttributes `json:"staff,omitempty"`
|
||||
Technician *TechnicianProfileUpdateAttributes `json:"technician,omitempty"`
|
||||
}
|
||||
|
||||
type ContactUpdateData struct {
|
||||
Type string `json:"type" validate:"required" example:"contact_update"`
|
||||
Attributes ContactUpdateAttributes `json:"attributes" validate:"required"`
|
||||
}
|
||||
|
||||
type ContactUpdateRequest struct {
|
||||
Data ContactUpdateData `json:"data" validate:"required"`
|
||||
}
|
||||
|
||||
type ContactListQuery struct {
|
||||
Role string `query:"role" validate:"omitempty,oneof=pilot doctor air_rescuer technician flight_assistant staff"`
|
||||
PilotCategory string `query:"pilot_category" validate:"omitempty,oneof=regular freelance dry_lease"`
|
||||
Search string `query:"search"`
|
||||
Sort string `query:"sort"`
|
||||
Limit int `query:"limit" validate:"omitempty,min=1,max=200"`
|
||||
Offset int `query:"offset" validate:"omitempty,min=0"`
|
||||
}
|
||||
|
||||
type ContactListItem struct {
|
||||
ID string `json:"id"`
|
||||
CreatedBy string `json:"created_by,omitempty"`
|
||||
UpdatedBy string `json:"updated_by,omitempty"`
|
||||
CreatedAt string `json:"created_at,omitempty"`
|
||||
UpdatedAt string `json:"updated_at,omitempty"`
|
||||
RoleCode string `json:"role_code"`
|
||||
RoleName string `json:"role_name"`
|
||||
PilotCategory string `json:"pilot_category,omitempty"`
|
||||
Username string `json:"username,omitempty"`
|
||||
Email string `json:"email"`
|
||||
IsAdmin bool `json:"is_admin"`
|
||||
FirstName string `json:"first_name"`
|
||||
LastName string `json:"last_name"`
|
||||
ShortName string `json:"short_name,omitempty"`
|
||||
LicenseNo string `json:"license_no,omitempty"`
|
||||
Location string `json:"location,omitempty"`
|
||||
MobilePhone string `json:"mobile_phone,omitempty"`
|
||||
SortKey *int `json:"sortkey"`
|
||||
Note string `json:"note,omitempty"`
|
||||
IsActive bool `json:"is_active"`
|
||||
SetPassword bool `json:"set_password"`
|
||||
}
|
||||
|
||||
type ContactBaseRole struct {
|
||||
BaseID string `json:"base_id"`
|
||||
BaseName string `json:"base_name"`
|
||||
RoleCode string `json:"role_code"`
|
||||
}
|
||||
|
||||
type ContactRole struct {
|
||||
ID string `json:"id"`
|
||||
RoleCode string `json:"role_code"`
|
||||
RoleName string `json:"role_name"`
|
||||
}
|
||||
|
||||
type ContactListAttributes struct {
|
||||
Email string `json:"email,omitempty"`
|
||||
Username string `json:"username,omitempty"`
|
||||
FirstName string `json:"first_name,omitempty"`
|
||||
LastName string `json:"last_name,omitempty"`
|
||||
MobilePhone string `json:"mobile_phone,omitempty"`
|
||||
IsAdmin bool `json:"is_admin"`
|
||||
IsActive bool `json:"is_active"`
|
||||
SetPassword bool `json:"set_password"`
|
||||
SortKey *int `json:"sortkey"`
|
||||
SSOEmail *string `json:"sso_email"`
|
||||
CreatedAt string `json:"created_at,omitempty"`
|
||||
CreatedBy string `json:"created_by,omitempty"`
|
||||
UpdatedAt string `json:"updated_at,omitempty"`
|
||||
UpdatedBy string `json:"updated_by,omitempty"`
|
||||
BaseRoles []ContactBaseRole `json:"base_roles"`
|
||||
RoleCode string `json:"role_code,omitempty"`
|
||||
RoleName string `json:"role_name,omitempty"`
|
||||
Roles []ContactRole `json:"roles"`
|
||||
PilotCategory string `json:"pilot_category,omitempty"`
|
||||
ShortName string `json:"short_name,omitempty"`
|
||||
LicenseNo string `json:"license_no,omitempty"`
|
||||
Location string `json:"location,omitempty"`
|
||||
Note string `json:"note,omitempty"`
|
||||
Pilot *PilotProfileAttributes `json:"pilot,omitempty"`
|
||||
Doctor *DoctorProfileAttributes `json:"doctor,omitempty"`
|
||||
AirRescuer *AirRescuerProfileAttributes `json:"air_rescuer,omitempty"`
|
||||
Technician *TechnicianProfileAttributes `json:"technician,omitempty"`
|
||||
FlightAssistant *FlightAssistantProfileAttributes `json:"flight_assistant,omitempty"`
|
||||
Staff *StaffProfileAttributes `json:"staff,omitempty"`
|
||||
}
|
||||
|
||||
type ContactListResponse struct {
|
||||
Data []struct {
|
||||
Type string `json:"type" example:"contact"`
|
||||
ID string `json:"id" example:"019dcce4-f15e-7685-8e4c-76f8ad20594b"`
|
||||
Attributes ContactListAttributes `json:"attributes"`
|
||||
} `json:"data"`
|
||||
Meta struct {
|
||||
Total int64 `json:"total"`
|
||||
Limit int `json:"limit"`
|
||||
Offset int `json:"offset"`
|
||||
} `json:"meta"`
|
||||
}
|
||||
|
||||
type ContactDataTableResponse struct {
|
||||
Data []ContactListItem `json:"data"`
|
||||
Meta struct {
|
||||
Draw int `json:"draw" example:"1"`
|
||||
RecordsTotal int64 `json:"records_total" example:"120"`
|
||||
RecordsFiltered int64 `json:"records_filtered" example:"12"`
|
||||
} `json:"meta"`
|
||||
}
|
||||
|
||||
type SensitiveActionRequiredResponse struct {
|
||||
Data struct {
|
||||
Type string `json:"type" example:"pin_verification_required"`
|
||||
Attributes struct {
|
||||
Required bool `json:"required" example:"true"`
|
||||
Action string `json:"action"`
|
||||
Method string `json:"method" example:"pin"`
|
||||
Status int `json:"status" example:"202"`
|
||||
} `json:"attributes"`
|
||||
} `json:"data"`
|
||||
}
|
||||
|
||||
type ContactStatusUpdateData struct {
|
||||
Type string `json:"type" validate:"required,eq=contact_status_update"`
|
||||
Attributes struct {
|
||||
IsActive bool `json:"is_active"`
|
||||
} `json:"attributes" validate:"required"`
|
||||
}
|
||||
|
||||
type ContactStatusUpdateRequest struct {
|
||||
Data ContactStatusUpdateData `json:"data" validate:"required"`
|
||||
}
|
||||
|
||||
type ContactRoleUpdateData struct {
|
||||
Type string `json:"type" validate:"required,eq=contact_role_update"`
|
||||
Attributes struct {
|
||||
RoleCode string `json:"role_code" validate:"required,oneof=pilot doctor air_rescuer technician flight_assistant staff"`
|
||||
} `json:"attributes" validate:"required"`
|
||||
}
|
||||
|
||||
type ContactRoleUpdateRequest struct {
|
||||
Data ContactRoleUpdateData `json:"data" validate:"required"`
|
||||
}
|
||||
|
||||
type ContactPilotCategoryUpdateData struct {
|
||||
Type string `json:"type" validate:"required,eq=contact_pilot_category_update"`
|
||||
Attributes struct {
|
||||
PilotCategory string `json:"pilot_category" validate:"required,oneof=regular freelance dry_lease"`
|
||||
} `json:"attributes" validate:"required"`
|
||||
}
|
||||
|
||||
type ContactPilotCategoryUpdateRequest struct {
|
||||
Data ContactPilotCategoryUpdateData `json:"data" validate:"required"`
|
||||
}
|
||||
|
||||
type ContactLicenseUpdateData struct {
|
||||
Type string `json:"type" validate:"required,eq=contact_license_update"`
|
||||
Attributes struct {
|
||||
LicenseNo *string `json:"license_no,omitempty"`
|
||||
} `json:"attributes" validate:"required"`
|
||||
}
|
||||
|
||||
type ContactLicenseUpdateRequest struct {
|
||||
Data ContactLicenseUpdateData `json:"data" validate:"required"`
|
||||
}
|
||||
|
||||
type ContactChiefFlagsUpdateData struct {
|
||||
Type string `json:"type" validate:"required,eq=contact_chief_flags_update"`
|
||||
Attributes struct {
|
||||
IsChiefPilot *bool `json:"is_chief_pilot,omitempty"`
|
||||
IsChiefPhysician *bool `json:"is_chief_physician,omitempty"`
|
||||
IsChiefTechnician *bool `json:"is_chief_technician,omitempty"`
|
||||
} `json:"attributes" validate:"required"`
|
||||
}
|
||||
|
||||
type ContactChiefFlagsUpdateRequest struct {
|
||||
Data ContactChiefFlagsUpdateData `json:"data" validate:"required"`
|
||||
}
|
||||
|
||||
type ContactResponsibleComplaintsData struct {
|
||||
Type string `json:"type" validate:"required,eq=contact_responsible_complaints_update"`
|
||||
Attributes struct {
|
||||
IsResponsibleComplaints bool `json:"is_responsible_complaints"`
|
||||
} `json:"attributes" validate:"required"`
|
||||
}
|
||||
|
||||
type ContactResponsibleComplaintsRequest struct {
|
||||
Data ContactResponsibleComplaintsData `json:"data" validate:"required"`
|
||||
}
|
||||
|
||||
type ContactBulkUpdateData struct {
|
||||
Type string `json:"type" validate:"required,eq=contact_bulk_update" example:"contact_bulk_update"`
|
||||
Attributes struct {
|
||||
UserIDs []string `json:"user_ids" validate:"required,min=1,dive,required" example:"0195f6f8-0f8d-7e3e-b8f2-8abff9b5c1d1"`
|
||||
IsActive bool `json:"is_active" example:"true"`
|
||||
} `json:"attributes" validate:"required"`
|
||||
}
|
||||
|
||||
type ContactBulkUpdateRequest struct {
|
||||
Data ContactBulkUpdateData `json:"data" validate:"required"`
|
||||
}
|
||||
|
||||
type ContactSimpleSuccessResponse struct {
|
||||
Data struct {
|
||||
Type string `json:"type"`
|
||||
Attributes struct {
|
||||
Updated bool `json:"updated,omitempty"`
|
||||
Deleted bool `json:"deleted,omitempty"`
|
||||
Count int64 `json:"count,omitempty"`
|
||||
} `json:"attributes"`
|
||||
} `json:"data"`
|
||||
}
|
||||
26
internal/transport/http/dto/delete_dto.go
Normal file
26
internal/transport/http/dto/delete_dto.go
Normal file
@@ -0,0 +1,26 @@
|
||||
package dto
|
||||
|
||||
// GenericDeleteResponse is a common success payload for DELETE endpoints.
|
||||
type GenericDeleteResponse struct {
|
||||
Data GenericDeleteResponseData `json:"data"`
|
||||
}
|
||||
|
||||
type GenericDeleteResponseData struct {
|
||||
Type string `json:"type" example:"delete_result"`
|
||||
Attributes GenericDeleteAttributes `json:"attributes"`
|
||||
}
|
||||
|
||||
type GenericDeleteAttributes struct {
|
||||
Deleted bool `json:"deleted" example:"true"`
|
||||
}
|
||||
|
||||
func NewGenericDeleteResponse(resourceType string) GenericDeleteResponse {
|
||||
return GenericDeleteResponse{
|
||||
Data: GenericDeleteResponseData{
|
||||
Type: resourceType,
|
||||
Attributes: GenericDeleteAttributes{
|
||||
Deleted: true,
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
38
internal/transport/http/dto/dul_request_dto.go
Normal file
38
internal/transport/http/dto/dul_request_dto.go
Normal file
@@ -0,0 +1,38 @@
|
||||
package dto
|
||||
|
||||
type DULCreateAttributes struct {
|
||||
Name string `json:"name" validate:"required,max=128"`
|
||||
Date string `json:"date" validate:"required" example:"2026-05-25"`
|
||||
Info *string `json:"info,omitempty"`
|
||||
BaseID string `json:"base_id" validate:"required" example:"019e3ec1-94f6-7b6b-9cc5-1b5d05f46d74"`
|
||||
FileUUID []string `json:"file_uuid,omitempty"`
|
||||
ImageAttachmentIDs []string `json:"image_attachment_ids,omitempty" swaggerignore:"true"`
|
||||
}
|
||||
|
||||
type DULCreateData struct {
|
||||
Type string `json:"type" validate:"required,oneof=dul_create dul"`
|
||||
Attributes DULCreateAttributes `json:"attributes" validate:"required"`
|
||||
}
|
||||
|
||||
type DULCreateRequest struct {
|
||||
Data DULCreateData `json:"data" validate:"required"`
|
||||
}
|
||||
|
||||
type DULUpdateAttributes struct {
|
||||
Name *string `json:"name,omitempty"`
|
||||
Date *string `json:"date,omitempty" example:"2026-05-25"`
|
||||
Info *string `json:"info,omitempty"`
|
||||
BaseID *string `json:"base_id,omitempty"`
|
||||
FileUUID []string `json:"file_uuid,omitempty"`
|
||||
ImageAttachmentIDs []string `json:"image_attachment_ids,omitempty" swaggerignore:"true"`
|
||||
}
|
||||
|
||||
type DULUpdateData struct {
|
||||
Type string `json:"type" validate:"required,oneof=dul_update dul"`
|
||||
ID string `json:"id" swaggerignore:"true"`
|
||||
Attributes DULUpdateAttributes `json:"attributes" validate:"required"`
|
||||
}
|
||||
|
||||
type DULUpdateRequest struct {
|
||||
Data DULUpdateData `json:"data" validate:"required"`
|
||||
}
|
||||
74
internal/transport/http/dto/dul_response_dto.go
Normal file
74
internal/transport/http/dto/dul_response_dto.go
Normal file
@@ -0,0 +1,74 @@
|
||||
package dto
|
||||
|
||||
type DULImage struct {
|
||||
UUID string `json:"uuid"`
|
||||
DownloadURL string `json:"download_url,omitempty"`
|
||||
OriginalSizeBytes int64 `json:"original_size_bytes,omitempty"`
|
||||
ThumbnailDownloadURL string `json:"thumbnail_download_url,omitempty"`
|
||||
}
|
||||
|
||||
type DULAttributes struct {
|
||||
No uint64 `json:"no"`
|
||||
Name string `json:"name"`
|
||||
Date string `json:"date" example:"2026-05-25"`
|
||||
Info string `json:"info,omitempty"`
|
||||
Base DULBaseRef `json:"base"`
|
||||
Images []DULImage `json:"images"`
|
||||
CreatedAt string `json:"created_at,omitempty"`
|
||||
CreatedBy string `json:"created_by,omitempty"`
|
||||
UpdatedAt string `json:"updated_at,omitempty"`
|
||||
UpdatedBy string `json:"updated_by,omitempty"`
|
||||
}
|
||||
|
||||
type DULBaseRef struct {
|
||||
ID string `json:"id"`
|
||||
Name string `json:"name"`
|
||||
Category string `json:"category,omitempty"`
|
||||
}
|
||||
|
||||
type DULResource struct {
|
||||
Type string `json:"type"`
|
||||
ID string `json:"id"`
|
||||
Attributes DULAttributes `json:"attributes"`
|
||||
}
|
||||
|
||||
type DULResponse struct {
|
||||
Data DULResource `json:"data"`
|
||||
}
|
||||
|
||||
type DULDeleteResponse struct {
|
||||
Data struct {
|
||||
Type string `json:"type" example:"dul_delete"`
|
||||
Attributes struct {
|
||||
Deleted bool `json:"deleted" example:"true"`
|
||||
} `json:"attributes"`
|
||||
} `json:"data"`
|
||||
}
|
||||
|
||||
type DULGroup struct {
|
||||
Base DULBaseRef `json:"base"`
|
||||
DULs []DULResource `json:"duls"`
|
||||
}
|
||||
|
||||
type DULListResponse struct {
|
||||
Data []DULGroup `json:"data"`
|
||||
Meta struct {
|
||||
PageNumber int `json:"page_number" example:"1"`
|
||||
PageSize int `json:"page_size" example:"20"`
|
||||
Total int64 `json:"total" example:"120"`
|
||||
TotalBases int `json:"total_bases" example:"8"`
|
||||
} `json:"meta"`
|
||||
}
|
||||
|
||||
type DULDataTableResponse struct {
|
||||
Data []DULGroup `json:"data"`
|
||||
Meta struct {
|
||||
Draw int `json:"draw" example:"1"`
|
||||
PageNumber int `json:"page_number" example:"1"`
|
||||
PageSize int `json:"page_size" example:"20"`
|
||||
Total int64 `json:"total" example:"120"`
|
||||
TotalBases int `json:"total_bases" example:"8"`
|
||||
RecordsTotal int64 `json:"records_total,omitempty" example:"120"`
|
||||
RecordsFiltered int64 `json:"records_filtered,omitempty" example:"120"`
|
||||
} `json:"meta"`
|
||||
}
|
||||
99
internal/transport/http/dto/facility_dto.go
Normal file
99
internal/transport/http/dto/facility_dto.go
Normal file
@@ -0,0 +1,99 @@
|
||||
package dto
|
||||
|
||||
// JSON:API Facility DTOs
|
||||
|
||||
type FacilityAttributes struct {
|
||||
Category string `json:"category" example:"heslo-rope-label"`
|
||||
Name string `json:"name" example:"Fuel Truck Rope Label"`
|
||||
Type string `json:"type" example:"Sling"`
|
||||
SortKey *int `json:"sortkey" example:"1"`
|
||||
Length string `json:"length,omitempty" example:"10m"`
|
||||
Weight string `json:"weight,omitempty" example:"10kg"`
|
||||
Electric string `json:"electric" example:"available"`
|
||||
Note string `json:"note,omitempty" example:"Dry lease fuel truck"`
|
||||
IsActive bool `json:"is_active" example:"true"`
|
||||
CreatedBy string `json:"created_by,omitempty" example:"018f3a5c-7a6f-7c2a-8d4a-4b5b1f6c8e9d"`
|
||||
UpdatedBy string `json:"updated_by,omitempty" example:"018f3a5c-7a6f-7c2a-8d4a-4b5b1f6c8e9d"`
|
||||
CreatedAt string `json:"created_at,omitempty" example:"2026-03-12T03:04:05Z"`
|
||||
UpdatedAt string `json:"updated_at,omitempty" example:"2026-03-12T03:04:05Z"`
|
||||
}
|
||||
|
||||
type FacilityResource struct {
|
||||
Type string `json:"type" example:"facility"`
|
||||
ID string `json:"id" example:"018f3a5c-7a6f-7c2a-8d4a-4b5b1f6c8e9d"`
|
||||
Attributes FacilityAttributes `json:"attributes"`
|
||||
}
|
||||
|
||||
type FacilityResponse struct {
|
||||
Data FacilityResource `json:"data"`
|
||||
}
|
||||
|
||||
type FacilityDeleteResponse struct {
|
||||
Data struct {
|
||||
Type string `json:"type" example:"facility_delete"`
|
||||
Attributes struct {
|
||||
Deleted bool `json:"deleted" example:"true"`
|
||||
} `json:"attributes"`
|
||||
} `json:"data"`
|
||||
}
|
||||
|
||||
type FacilityListResponse struct {
|
||||
Data []FacilityResource `json:"data"`
|
||||
Meta struct {
|
||||
PageNumber int `json:"page_number" example:"1"`
|
||||
PageSize int `json:"page_size" example:"20"`
|
||||
Total int64 `json:"total" example:"120"`
|
||||
} `json:"meta"`
|
||||
}
|
||||
|
||||
type FacilityDataTableResponse struct {
|
||||
Data []FacilityResource `json:"data"`
|
||||
Meta struct {
|
||||
Draw int `json:"draw" example:"1"`
|
||||
RecordsTotal int64 `json:"records_total" example:"120"`
|
||||
RecordsFiltered int64 `json:"records_filtered" example:"12"`
|
||||
} `json:"meta"`
|
||||
}
|
||||
|
||||
type FacilityCreateAttributes struct {
|
||||
Category string `json:"category" validate:"required" example:"heslo-rope-label"`
|
||||
Name string `json:"name" validate:"required" example:"Fuel Truck Rope Label"`
|
||||
Type string `json:"type" example:"Sling"`
|
||||
SortKey *int `json:"sortkey" example:"1"`
|
||||
Length *string `json:"length,omitempty" example:"10m"`
|
||||
Weight *string `json:"weight,omitempty" example:"10kg"`
|
||||
Electric *string `json:"electric,omitempty" example:"available"`
|
||||
Note *string `json:"note,omitempty" example:"Dry lease fuel truck"`
|
||||
IsActive *bool `json:"is_active,omitempty" example:"true"`
|
||||
}
|
||||
|
||||
type FacilityCreateData struct {
|
||||
Type string `json:"type" validate:"required,oneof=facility_create facility" example:"facility_create"`
|
||||
Attributes FacilityCreateAttributes `json:"attributes" validate:"required"`
|
||||
}
|
||||
|
||||
type FacilityCreateRequest struct {
|
||||
Data FacilityCreateData `json:"data" validate:"required"`
|
||||
}
|
||||
|
||||
type FacilityUpdateAttributes struct {
|
||||
Category *string `json:"category,omitempty" example:"heslo-rope-label"`
|
||||
Name *string `json:"name,omitempty" example:"Fuel Truck Rope Label"`
|
||||
Type *string `json:"type,omitempty" example:"Sling"`
|
||||
SortKey NullInt `json:"sortkey,omitempty" swaggertype:"integer" example:"1"`
|
||||
Length *string `json:"length,omitempty" example:"10m"`
|
||||
Weight *string `json:"weight,omitempty" example:"10kg"`
|
||||
Electric *string `json:"electric,omitempty" example:"available"`
|
||||
Note *string `json:"note,omitempty" example:"Dry lease fuel truck"`
|
||||
IsActive *bool `json:"is_active,omitempty" example:"true"`
|
||||
}
|
||||
|
||||
type FacilityUpdateData struct {
|
||||
Type string `json:"type" validate:"required,oneof=facility_update facility" example:"facility_update"`
|
||||
ID string `json:"id" example:"018f3a5c-7a6f-7c2a-8d4a-4b5b1f6c8e9d"`
|
||||
Attributes FacilityUpdateAttributes `json:"attributes" validate:"required"`
|
||||
}
|
||||
|
||||
type FacilityUpdateRequest struct {
|
||||
Data FacilityUpdateData `json:"data" validate:"required"`
|
||||
}
|
||||
91
internal/transport/http/dto/federal_state_dto.go
Normal file
91
internal/transport/http/dto/federal_state_dto.go
Normal file
@@ -0,0 +1,91 @@
|
||||
package dto
|
||||
|
||||
// JSON:API Federal State DTOs
|
||||
|
||||
type FederalStateAttributes struct {
|
||||
Name string `json:"name" example:"Bayern"`
|
||||
Note string `json:"note,omitempty" example:"Central region"`
|
||||
LandID string `json:"land_id" example:"018f3a5c-7a6f-7c2a-8d4a-4b5b1f6c8e9d"`
|
||||
LandName string `json:"land_name,omitempty" example:"Germany"`
|
||||
LandISOCode string `json:"land_iso_code,omitempty" example:"DE"`
|
||||
SortKey *int `json:"sortkey" example:"1"`
|
||||
IsActive bool `json:"is_active" example:"true"`
|
||||
CreatedAt string `json:"created_at,omitempty" example:"2026-03-12T03:04:05Z"`
|
||||
CreatedBy string `json:"created_by,omitempty" example:"018f3a5c-7a6f-7c2a-8d4a-4b5b1f6c8e9d"`
|
||||
UpdatedAt string `json:"updated_at,omitempty" example:"2026-03-12T03:04:05Z"`
|
||||
UpdatedBy string `json:"updated_by,omitempty" example:"018f3a5c-7a6f-7c2a-8d4a-4b5b1f6c8e9d"`
|
||||
DeletedAt string `json:"deleted_at,omitempty" example:"2026-03-12T03:04:05Z"`
|
||||
DeletedBy string `json:"deleted_by,omitempty" example:"018f3a5c-7a6f-7c2a-8d4a-4b5b1f6c8e9d"`
|
||||
}
|
||||
|
||||
type FederalStateResource struct {
|
||||
Type string `json:"type" example:"federal_state"`
|
||||
ID string `json:"id" example:"018f3a5c-7a6f-7c2a-8d4a-4b5b1f6c8e9d"`
|
||||
Attributes FederalStateAttributes `json:"attributes"`
|
||||
}
|
||||
|
||||
type FederalStateResponse struct {
|
||||
Data FederalStateResource `json:"data"`
|
||||
}
|
||||
|
||||
type FederalStateDeleteResponse struct {
|
||||
Data struct {
|
||||
Type string `json:"type" example:"federal_state_delete"`
|
||||
Attributes struct {
|
||||
Deleted bool `json:"deleted" example:"true"`
|
||||
} `json:"attributes"`
|
||||
} `json:"data"`
|
||||
}
|
||||
|
||||
type FederalStateListResponse struct {
|
||||
Data []FederalStateResource `json:"data"`
|
||||
Meta struct {
|
||||
PageNumber int `json:"page_number" example:"1"`
|
||||
PageSize int `json:"page_size" example:"20"`
|
||||
Total int64 `json:"total" example:"120"`
|
||||
} `json:"meta"`
|
||||
}
|
||||
|
||||
type FederalStateDataTableResponse struct {
|
||||
Data []FederalStateResource `json:"data"`
|
||||
Meta struct {
|
||||
Draw int `json:"draw" example:"1"`
|
||||
RecordsTotal int64 `json:"records_total" example:"120"`
|
||||
RecordsFiltered int64 `json:"records_filtered" example:"12"`
|
||||
} `json:"meta"`
|
||||
}
|
||||
|
||||
type FederalStateCreateAttributes struct {
|
||||
Name string `json:"name" validate:"required" example:"Bayern"`
|
||||
Note *string `json:"note,omitempty" example:"Central region"`
|
||||
LandID string `json:"land_id" validate:"required" example:"018f3a5c-7a6f-7c2a-8d4a-4b5b1f6c8e9d"`
|
||||
SortKey *int `json:"sortkey" example:"1"`
|
||||
IsActive *bool `json:"is_active,omitempty" example:"true"`
|
||||
}
|
||||
|
||||
type FederalStateCreateData struct {
|
||||
Type string `json:"type" validate:"required,oneof=federal_state_create federal_state" example:"federal_state_create"`
|
||||
Attributes FederalStateCreateAttributes `json:"attributes" validate:"required"`
|
||||
}
|
||||
|
||||
type FederalStateCreateRequest struct {
|
||||
Data FederalStateCreateData `json:"data" validate:"required"`
|
||||
}
|
||||
|
||||
type FederalStateUpdateAttributes struct {
|
||||
Name *string `json:"name,omitempty" example:"Bayern"`
|
||||
Note *string `json:"note,omitempty" example:"Central region"`
|
||||
LandID *string `json:"land_id,omitempty" example:"018f3a5c-7a6f-7c2a-8d4a-4b5b1f6c8e9d"`
|
||||
SortKey NullInt `json:"sortkey,omitempty" swaggertype:"integer" example:"1"`
|
||||
IsActive *bool `json:"is_active,omitempty" example:"true"`
|
||||
}
|
||||
|
||||
type FederalStateUpdateData struct {
|
||||
Type string `json:"type" validate:"required,oneof=federal_state_update federal_state" example:"federal_state_update"`
|
||||
ID string `json:"id" example:"018f3a5c-7a6f-7c2a-8d4a-4b5b1f6c8e9d"`
|
||||
Attributes FederalStateUpdateAttributes `json:"attributes" validate:"required"`
|
||||
}
|
||||
|
||||
type FederalStateUpdateRequest struct {
|
||||
Data FederalStateUpdateData `json:"data" validate:"required"`
|
||||
}
|
||||
57
internal/transport/http/dto/flight_inspection_dto.go
Normal file
57
internal/transport/http/dto/flight_inspection_dto.go
Normal file
@@ -0,0 +1,57 @@
|
||||
package dto
|
||||
|
||||
type FlightInspectionAttributes struct {
|
||||
InspectionDate string `json:"inspection_date,omitempty" example:"2026-04-09"`
|
||||
Status string `json:"status,omitempty" example:"Draft"`
|
||||
CreatedAt string `json:"created_at,omitempty" example:"2026-04-09T05:00:00Z"`
|
||||
CreatedBy string `json:"created_by,omitempty" example:"019d61db-53c6-7280-87ec-e65f205c6d3b"`
|
||||
UpdatedAt string `json:"updated_at,omitempty" example:"2026-04-09T05:00:00Z"`
|
||||
UpdatedBy string `json:"updated_by,omitempty" example:"019d61db-53c6-7280-87ec-e65f205c6d3b"`
|
||||
}
|
||||
|
||||
type FlightInspectionResource struct {
|
||||
Type string `json:"type" example:"flight_inspection"`
|
||||
ID string `json:"id,omitempty" example:"019d6772-471c-7b70-b42a-d94020ef61e4"`
|
||||
Attributes FlightInspectionAttributes `json:"attributes"`
|
||||
}
|
||||
|
||||
type FlightInspectionResponse struct {
|
||||
Data FlightInspectionResource `json:"data"`
|
||||
}
|
||||
|
||||
type FlightInspectionListResponse struct {
|
||||
Data []FlightInspectionResource `json:"data"`
|
||||
Meta struct {
|
||||
PageNumber int `json:"page_number" example:"1"`
|
||||
PageSize int `json:"page_size" example:"20"`
|
||||
Total int64 `json:"total" example:"1"`
|
||||
} `json:"meta"`
|
||||
}
|
||||
|
||||
type FlightInspectionCreateAttributes struct {
|
||||
InspectionDate string `json:"inspection_date" validate:"required" example:"2026-04-09"`
|
||||
}
|
||||
|
||||
type FlightInspectionCreateData struct {
|
||||
Type string `json:"type" validate:"required,oneof=flight_inspection_create flight_inspection"`
|
||||
Attributes FlightInspectionCreateAttributes `json:"attributes" validate:"required"`
|
||||
}
|
||||
|
||||
type FlightInspectionCreateRequest struct {
|
||||
Data FlightInspectionCreateData `json:"data" validate:"required"`
|
||||
}
|
||||
|
||||
type FlightInspectionUpdateAttributes struct {
|
||||
InspectionDate *string `json:"inspection_date,omitempty" example:"2026-04-09"`
|
||||
Status *string `json:"status,omitempty" example:"InProgress"`
|
||||
}
|
||||
|
||||
type FlightInspectionUpdateData struct {
|
||||
Type string `json:"type" validate:"required,oneof=flight_inspection_update flight_inspection"`
|
||||
ID string `json:"id" example:"019d6772-471c-7b70-b42a-d94020ef61e4"`
|
||||
Attributes FlightInspectionUpdateAttributes `json:"attributes" validate:"required"`
|
||||
}
|
||||
|
||||
type FlightInspectionUpdateRequest struct {
|
||||
Data FlightInspectionUpdateData `json:"data" validate:"required"`
|
||||
}
|
||||
73
internal/transport/http/dto/flight_prep_check_dto.go
Normal file
73
internal/transport/http/dto/flight_prep_check_dto.go
Normal file
@@ -0,0 +1,73 @@
|
||||
package dto
|
||||
|
||||
import (
|
||||
"time"
|
||||
|
||||
flightprepcheck "wucher/internal/domain/flight_prep_check"
|
||||
"wucher/internal/shared/pkg/uuidv7"
|
||||
)
|
||||
|
||||
type FlightPrepCheckAttributes struct {
|
||||
ID string `json:"id,omitempty" example:"019d6772-471c-7b70-b42a-d94020ef61e4"`
|
||||
FlightInspectionID string `json:"flight_inspection_id,omitempty" example:"019d6772-471c-7b70-b42a-d94020ef61e4"`
|
||||
NotamBriefing bool `json:"notam_briefing" example:"true"`
|
||||
WeatherBriefing bool `json:"weather_briefing" example:"false"`
|
||||
OperationalFlightPlan bool `json:"operational_flight_plan" example:"true"`
|
||||
CreatedAt string `json:"created_at,omitempty" example:"2026-04-09T05:00:00Z"`
|
||||
UpdatedAt string `json:"updated_at,omitempty" example:"2026-04-09T05:00:00Z"`
|
||||
}
|
||||
|
||||
type FlightPrepCheckResource struct {
|
||||
Type string `json:"type" example:"flight_prep_check"`
|
||||
ID string `json:"id,omitempty" example:"019d6772-471c-7b70-b42a-d94020ef61e4"`
|
||||
Attributes FlightPrepCheckAttributes `json:"attributes"`
|
||||
}
|
||||
|
||||
type FlightPrepCheckResponse struct {
|
||||
Data FlightPrepCheckResource `json:"data"`
|
||||
}
|
||||
|
||||
type FlightPrepCheckUpsertRequest struct {
|
||||
Data struct {
|
||||
Type string `json:"type" validate:"required,oneof=flight_prep_check_upsert flight_prep_check" example:"flight_prep_check_upsert"`
|
||||
Attributes FlightPrepCheckUpsertAttributes `json:"attributes" validate:"required"`
|
||||
} `json:"data" validate:"required"`
|
||||
}
|
||||
|
||||
type FlightPrepCheckUpsertAttributes struct {
|
||||
NotamBriefing *bool `json:"notam_briefing,omitempty" example:"true"`
|
||||
WeatherBriefing *bool `json:"weather_briefing,omitempty" example:"true"`
|
||||
OperationalFlightPlan *bool `json:"operational_flight_plan,omitempty" example:"false"`
|
||||
}
|
||||
|
||||
func ToFlightPrepCheckDTO(check *flightprepcheck.FlightPrepCheck) *FlightPrepCheckResource {
|
||||
if check == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
idStr, _ := uuidv7.BytesToString(check.ID)
|
||||
flightInspectionIDStr, _ := uuidv7.BytesToString(check.FlightInspectionID)
|
||||
|
||||
createdAt := ""
|
||||
updatedAt := ""
|
||||
if !check.CreatedAt.IsZero() {
|
||||
createdAt = check.CreatedAt.UTC().Format(time.RFC3339)
|
||||
}
|
||||
if !check.UpdatedAt.IsZero() {
|
||||
updatedAt = check.UpdatedAt.UTC().Format(time.RFC3339)
|
||||
}
|
||||
|
||||
return &FlightPrepCheckResource{
|
||||
Type: "flight_prep_check",
|
||||
ID: idStr,
|
||||
Attributes: FlightPrepCheckAttributes{
|
||||
ID: idStr,
|
||||
FlightInspectionID: flightInspectionIDStr,
|
||||
NotamBriefing: check.NOTAMBriefing,
|
||||
WeatherBriefing: check.WeatherBriefing,
|
||||
OperationalFlightPlan: check.OperationalFlightPlan,
|
||||
CreatedAt: createdAt,
|
||||
UpdatedAt: updatedAt,
|
||||
},
|
||||
}
|
||||
}
|
||||
100
internal/transport/http/dto/fm_report_dto.go
Normal file
100
internal/transport/http/dto/fm_report_dto.go
Normal file
@@ -0,0 +1,100 @@
|
||||
package dto
|
||||
|
||||
import responsedto "wucher/internal/transport/http/dto/response"
|
||||
|
||||
type FMReportAttributes struct {
|
||||
ID string `json:"id,omitempty" example:"019d6772-471c-7b70-b42a-d94020ef61e4"`
|
||||
ReportCode *string `json:"report_code" example:"EC-135-1"`
|
||||
TakeoverID string `json:"takeover_id,omitempty" example:"019e8713-aa19-7fb7-a510-a0332dc69697"`
|
||||
FlightID string `json:"flight_id,omitempty" example:"019d6774-98e8-7fba-9ef4-3795c0da8a13"`
|
||||
FlightInspectionID string `json:"flight_inspection_id,omitempty" example:"019d6772-471c-7b70-b42a-d94020ef61e4"`
|
||||
Engine1GpcN1 *string `json:"engine1_gpc_n1" example:"GPC/N1 63.2"`
|
||||
Engine1PtcN2 *string `json:"engine1_ptc_n2" example:"PTC/N2 61.8"`
|
||||
Engine2GpcN1 *string `json:"engine2_gpc_n1" example:"GPC/N1 62.9"`
|
||||
Engine2PtcN2 *string `json:"engine2_ptc_n2" example:"PTC/N2 61.5"`
|
||||
CompletedAt string `json:"completed_at,omitempty" example:"2026-04-20T10:15:00Z"`
|
||||
CompletedBy string `json:"completed_by,omitempty" example:"019d61db-53c6-7280-87ec-e65f205c6d3b"`
|
||||
PilotName *string `json:"pilot_name,omitempty" example:"John Doe"`
|
||||
HelicopterIdentifier *string `json:"helicopter_identifier,omitempty" example:"OE-TEST"`
|
||||
Flight *responsedto.FlightResource `json:"flight,omitempty"`
|
||||
Missions []responsedto.MissionResource `json:"missions,omitempty"`
|
||||
FlightInspection *FlightInspectionResource `json:"flight_inspection,omitempty"`
|
||||
AfterFlightInspection *AfterFlightInspectionResource `json:"after_flight_inspection,omitempty"`
|
||||
FleetStatus *responsedto.FleetStatusResource `json:"fleet_status,omitempty"`
|
||||
HelicopterUsage *HelicopterUsageResource `json:"helicopter_usage,omitempty"`
|
||||
UsageBreakdown *FMReportUsageBreakdown `json:"usage_breakdown,omitempty"`
|
||||
TakeoverFiles []responsedto.TakeoverFileResource `json:"takeover_files,omitempty"`
|
||||
FMReportAttachments *FMReportAttachments `json:"fm_report_attachments,omitempty"`
|
||||
CreatedAt string `json:"created_at,omitempty" example:"2026-04-20T08:00:00Z"`
|
||||
UpdatedAt string `json:"updated_at,omitempty" example:"2026-04-20T08:00:00Z"`
|
||||
}
|
||||
|
||||
type FMReportAirframeValues struct {
|
||||
Hours float64 `json:"hours" example:"1523.5"`
|
||||
Cycles float64 `json:"cycles" example:"320"`
|
||||
}
|
||||
|
||||
type FMReportEngineValues struct {
|
||||
Hours float64 `json:"hours" example:"1523.5"`
|
||||
GpcNgN1 float64 `json:"gpc_ng_n1" example:"63.2"`
|
||||
PtcNfN2 float64 `json:"ptc_nf_n2" example:"61.8"`
|
||||
Ccc float64 `json:"ccc" example:"44"`
|
||||
}
|
||||
|
||||
// FMReportCountersValues are the "Landings, Complaints & Sign-off" counters.
|
||||
type FMReportCountersValues struct {
|
||||
Landings float64 `json:"landings" example:"120"`
|
||||
FlightReports float64 `json:"flight_reports" example:"80"`
|
||||
HookReleases float64 `json:"hook_releases" example:"10"`
|
||||
RotorBrakeCycles float64 `json:"rotor_brake_cycles" example:"8"`
|
||||
}
|
||||
|
||||
// FMReportUsageSnapshot is one row of the breakdown table (all columns for a single
|
||||
// prev/today/total bucket). An absent engine (single-engine aircraft) stays zero-valued.
|
||||
type FMReportUsageSnapshot struct {
|
||||
Airframe FMReportAirframeValues `json:"airframe"`
|
||||
Engine1 FMReportEngineValues `json:"engine_1"`
|
||||
Engine2 FMReportEngineValues `json:"engine_2"`
|
||||
Counters FMReportCountersValues `json:"counters"`
|
||||
}
|
||||
|
||||
// FMReportUsageBreakdown is the prev/today/total table shown on the FM report page,
|
||||
// grouped by row: prev = total - today (the prior-day cumulative), today = this flight's
|
||||
// contribution, total = the cumulative to date.
|
||||
type FMReportUsageBreakdown struct {
|
||||
Prev FMReportUsageSnapshot `json:"prev"`
|
||||
Today FMReportUsageSnapshot `json:"today"`
|
||||
Total FMReportUsageSnapshot `json:"total"`
|
||||
}
|
||||
|
||||
type FMReportAttachments struct {
|
||||
AdditionalFMReport []FMReportAttachmentItem `json:"additional_fm_report,omitempty"`
|
||||
Briefing []FMReportAttachmentItem `json:"briefing,omitempty"`
|
||||
WeightBalance []FMReportAttachmentItem `json:"weight_balance,omitempty"`
|
||||
OFP []FMReportAttachmentItem `json:"ofp,omitempty"`
|
||||
}
|
||||
|
||||
type FMReportAttachmentItem struct {
|
||||
ID string `json:"id"`
|
||||
FileID string `json:"file_id"`
|
||||
DownloadURL *string `json:"download_url,omitempty"`
|
||||
}
|
||||
|
||||
type FMReportResource struct {
|
||||
Type string `json:"type" example:"fm_report"`
|
||||
ID string `json:"id,omitempty" example:"019d6772-471c-7b70-b42a-d94020ef61e4"`
|
||||
Attributes FMReportAttributes `json:"attributes"`
|
||||
}
|
||||
|
||||
type FMReportResponse struct {
|
||||
Data FMReportResource `json:"data"`
|
||||
}
|
||||
|
||||
type FMReportListResponse struct {
|
||||
Data []FMReportResource `json:"data"`
|
||||
Meta struct {
|
||||
PageNumber int `json:"page_number" example:"1"`
|
||||
PageSize int `json:"page_size" example:"20"`
|
||||
Total int64 `json:"total" example:"1"`
|
||||
} `json:"meta"`
|
||||
}
|
||||
77
internal/transport/http/dto/forces_present_dto.go
Normal file
77
internal/transport/http/dto/forces_present_dto.go
Normal file
@@ -0,0 +1,77 @@
|
||||
package dto
|
||||
|
||||
// JSON:API Forces Present DTOs
|
||||
|
||||
type ForcesPresentAttributes struct {
|
||||
Name string `json:"name" example:"Firefighter Team A"`
|
||||
SortKey *int `json:"sortkey" example:"1"`
|
||||
Note string `json:"note,omitempty" example:"On-site responders"`
|
||||
IsActive bool `json:"is_active" example:"true"`
|
||||
CreatedAt string `json:"created_at,omitempty" example:"2026-03-12T03:04:05Z"`
|
||||
CreatedBy string `json:"created_by" example:"018f3a5c-7a6f-7c2a-8d4a-4b5b1f6c8e9d"`
|
||||
UpdatedAt string `json:"updated_at,omitempty" example:"2026-03-12T03:04:05Z"`
|
||||
UpdatedBy string `json:"updated_by" example:"018f3a5c-7a6f-7c2a-8d4a-4b5b1f6c8e9d"`
|
||||
DeletedAt string `json:"deleted_at,omitempty" example:"2026-03-12T03:04:05Z"`
|
||||
DeletedBy string `json:"deleted_by,omitempty" example:"018f3a5c-7a6f-7c2a-8d4a-4b5b1f6c8e9d"`
|
||||
}
|
||||
|
||||
type ForcesPresentResource struct {
|
||||
Type string `json:"type" example:"forces_present"`
|
||||
ID string `json:"id" example:"018f3a5c-7a6f-7c2a-8d4a-4b5b1f6c8e9d"`
|
||||
Attributes ForcesPresentAttributes `json:"attributes"`
|
||||
}
|
||||
|
||||
type ForcesPresentResponse struct {
|
||||
Data ForcesPresentResource `json:"data"`
|
||||
}
|
||||
|
||||
type ForcesPresentListResponse struct {
|
||||
Data []ForcesPresentResource `json:"data"`
|
||||
Meta struct {
|
||||
PageNumber int `json:"page_number" example:"1"`
|
||||
PageSize int `json:"page_size" example:"20"`
|
||||
Total int64 `json:"total" example:"120"`
|
||||
} `json:"meta"`
|
||||
}
|
||||
|
||||
type ForcesPresentDataTableResponse struct {
|
||||
Data []ForcesPresentResource `json:"data"`
|
||||
Meta struct {
|
||||
Draw int `json:"draw" example:"1"`
|
||||
RecordsTotal int64 `json:"records_total" example:"120"`
|
||||
RecordsFiltered int64 `json:"records_filtered" example:"12"`
|
||||
} `json:"meta"`
|
||||
}
|
||||
|
||||
type ForcesPresentCreateAttributes struct {
|
||||
Name string `json:"name" validate:"required" example:"Firefighter Team A"`
|
||||
SortKey *int `json:"sortkey" example:"1"`
|
||||
Note *string `json:"note,omitempty" example:"On-site responders"`
|
||||
IsActive *bool `json:"is_active,omitempty" example:"true"`
|
||||
}
|
||||
|
||||
type ForcesPresentCreateData struct {
|
||||
Type string `json:"type" validate:"required,oneof=forces_present_create forces_present" example:"forces_present_create"`
|
||||
Attributes ForcesPresentCreateAttributes `json:"attributes" validate:"required"`
|
||||
}
|
||||
|
||||
type ForcesPresentCreateRequest struct {
|
||||
Data ForcesPresentCreateData `json:"data" validate:"required"`
|
||||
}
|
||||
|
||||
type ForcesPresentUpdateAttributes struct {
|
||||
Name *string `json:"name,omitempty" example:"Firefighter Team A"`
|
||||
SortKey NullInt `json:"sortkey,omitempty" swaggertype:"integer" example:"1"`
|
||||
Note *string `json:"note,omitempty" example:"On-site responders"`
|
||||
IsActive *bool `json:"is_active,omitempty" example:"true"`
|
||||
}
|
||||
|
||||
type ForcesPresentUpdateData struct {
|
||||
Type string `json:"type" validate:"required,oneof=forces_present_update forces_present" example:"forces_present_update"`
|
||||
ID string `json:"id" example:"018f3a5c-7a6f-7c2a-8d4a-4b5b1f6c8e9d"`
|
||||
Attributes ForcesPresentUpdateAttributes `json:"attributes" validate:"required"`
|
||||
}
|
||||
|
||||
type ForcesPresentUpdateRequest struct {
|
||||
Data ForcesPresentUpdateData `json:"data" validate:"required"`
|
||||
}
|
||||
@@ -0,0 +1,96 @@
|
||||
package dto
|
||||
|
||||
// JSON:API Health Insurance Companies DTOs
|
||||
|
||||
type HealthInsuranceCompanyAttributes struct {
|
||||
LandID string `json:"land_id,omitempty" example:"018f3a5c-7a6f-7c2a-8d4a-4b5b1f6c8e9d"`
|
||||
FederalStateID string `json:"federal_state_id,omitempty" example:"018f3a5c-7a6f-7c2a-8d4a-4b5b1f6c8e9d"`
|
||||
FederalState string `json:"federal_state,omitempty" example:"Bayern"`
|
||||
LandName string `json:"land_name" example:"Germany"`
|
||||
LandISOCode string `json:"land_iso_code,omitempty" example:"DE"`
|
||||
Name string `json:"name" example:"ABC Insurance"`
|
||||
SortKey *int `json:"sortkey" example:"1"`
|
||||
State string `json:"state,omitempty" example:"Berlin"`
|
||||
Address string `json:"address,omitempty" example:"Main Street 10"`
|
||||
MobileNumber string `json:"mobile_number,omitempty" example:"+4912345678"`
|
||||
Email string `json:"email,omitempty" example:"info@abc-insurance.local"`
|
||||
Note string `json:"note,omitempty" example:"Preferred insurance provider"`
|
||||
IsActive bool `json:"is_active" example:"true"`
|
||||
CreatedAt string `json:"created_at,omitempty" example:"2026-03-12T03:04:05Z"`
|
||||
CreatedBy string `json:"created_by,omitempty" example:"018f3a5c-7a6f-7c2a-8d4a-4b5b1f6c8e9d"`
|
||||
UpdatedAt string `json:"updated_at,omitempty" example:"2026-03-12T03:04:05Z"`
|
||||
UpdatedBy string `json:"updated_by,omitempty" example:"018f3a5c-7a6f-7c2a-8d4a-4b5b1f6c8e9d"`
|
||||
DeletedAt string `json:"deleted_at,omitempty" example:"2026-03-12T03:04:05Z"`
|
||||
DeletedBy string `json:"deleted_by,omitempty" example:"018f3a5c-7a6f-7c2a-8d4a-4b5b1f6c8e9d"`
|
||||
}
|
||||
|
||||
type HealthInsuranceCompanyResource struct {
|
||||
Type string `json:"type" example:"health_insurance_company"`
|
||||
ID string `json:"id" example:"018f3a5c-7a6f-7c2a-8d4a-4b5b1f6c8e9d"`
|
||||
Attributes HealthInsuranceCompanyAttributes `json:"attributes"`
|
||||
}
|
||||
|
||||
type HealthInsuranceCompanyResponse struct {
|
||||
Data HealthInsuranceCompanyResource `json:"data"`
|
||||
}
|
||||
|
||||
type HealthInsuranceCompanyListResponse struct {
|
||||
Data []HealthInsuranceCompanyResource `json:"data"`
|
||||
Meta struct {
|
||||
PageNumber int `json:"page_number" example:"1"`
|
||||
PageSize int `json:"page_size" example:"20"`
|
||||
Total int64 `json:"total" example:"120"`
|
||||
} `json:"meta"`
|
||||
}
|
||||
|
||||
type HealthInsuranceCompanyDataTableResponse struct {
|
||||
Data []HealthInsuranceCompanyResource `json:"data"`
|
||||
Meta struct {
|
||||
Draw int `json:"draw" example:"1"`
|
||||
RecordsTotal int64 `json:"records_total" example:"120"`
|
||||
RecordsFiltered int64 `json:"records_filtered" example:"12"`
|
||||
} `json:"meta"`
|
||||
}
|
||||
|
||||
type HealthInsuranceCompanyCreateAttributes struct {
|
||||
Name string `json:"name" validate:"required" example:"ABC Insurance"`
|
||||
SortKey *int `json:"sortkey" example:"1"`
|
||||
LandID *string `json:"land_id,omitempty" example:"018f3a5c-7a6f-7c2a-8d4a-4b5b1f6c8e9d"`
|
||||
State *string `json:"state,omitempty" example:"Berlin"`
|
||||
Address *string `json:"address,omitempty" example:"Main Street 10"`
|
||||
MobileNumber *string `json:"mobile_number,omitempty" example:"+4912345678"`
|
||||
Email *string `json:"email,omitempty" example:"info@abc-insurance.local"`
|
||||
Note *string `json:"note,omitempty" example:"Preferred insurance provider"`
|
||||
IsActive *bool `json:"is_active,omitempty" example:"true"`
|
||||
}
|
||||
|
||||
type HealthInsuranceCompanyCreateData struct {
|
||||
Type string `json:"type" validate:"required,oneof=health_insurance_company_create health_insurance_company" example:"health_insurance_company_create"`
|
||||
Attributes HealthInsuranceCompanyCreateAttributes `json:"attributes" validate:"required"`
|
||||
}
|
||||
|
||||
type HealthInsuranceCompanyCreateRequest struct {
|
||||
Data HealthInsuranceCompanyCreateData `json:"data" validate:"required"`
|
||||
}
|
||||
|
||||
type HealthInsuranceCompanyUpdateAttributes struct {
|
||||
Name *string `json:"name,omitempty" example:"ABC Insurance"`
|
||||
SortKey NullInt `json:"sortkey,omitempty" swaggertype:"integer" example:"1"`
|
||||
LandID *string `json:"land_id,omitempty" example:"018f3a5c-7a6f-7c2a-8d4a-4b5b1f6c8e9d"`
|
||||
State *string `json:"state,omitempty" example:"Berlin"`
|
||||
Address *string `json:"address,omitempty" example:"Main Street 10"`
|
||||
MobileNumber *string `json:"mobile_number,omitempty" example:"+4912345678"`
|
||||
Email *string `json:"email,omitempty" example:"info@abc-insurance.local"`
|
||||
Note *string `json:"note,omitempty" example:"Preferred insurance provider"`
|
||||
IsActive *bool `json:"is_active,omitempty" example:"true"`
|
||||
}
|
||||
|
||||
type HealthInsuranceCompanyUpdateData struct {
|
||||
Type string `json:"type" validate:"required,oneof=health_insurance_company_update health_insurance_company" example:"health_insurance_company_update"`
|
||||
ID string `json:"id" example:"018f3a5c-7a6f-7c2a-8d4a-4b5b1f6c8e9d"`
|
||||
Attributes HealthInsuranceCompanyUpdateAttributes `json:"attributes" validate:"required"`
|
||||
}
|
||||
|
||||
type HealthInsuranceCompanyUpdateRequest struct {
|
||||
Data HealthInsuranceCompanyUpdateData `json:"data" validate:"required"`
|
||||
}
|
||||
101
internal/transport/http/dto/helicopter_usage_dto.go
Normal file
101
internal/transport/http/dto/helicopter_usage_dto.go
Normal file
@@ -0,0 +1,101 @@
|
||||
package dto
|
||||
|
||||
// JSON:API Helicopter Usage DTOs
|
||||
|
||||
type HelicopterUsageAttributes struct {
|
||||
HelicopterID string `json:"helicopter_id" example:"018f3a5c-7a6f-7c2a-8d4a-4b5b1f6c8e9d"`
|
||||
TotalLanding float64 `json:"total_landing" example:"120"`
|
||||
TotalAirframeHours float64 `json:"total_airframe_hours" example:"1523.5"`
|
||||
TotalAirframeCycles float64 `json:"total_airframe_cycles" example:"320"`
|
||||
TotalEngine1Hours float64 `json:"total_engine_1_hours" example:"1523.5"`
|
||||
TotalEngine1GpcNgN1 float64 `json:"total_engine_1_gpc_ng_n1" example:"98.7"`
|
||||
TotalEngine1PtcNfN2 float64 `json:"total_engine_1_ptc_nf_n2" example:"97.2"`
|
||||
TotalEngine1Ccc float64 `json:"total_engine_1_ccc" example:"44"`
|
||||
TotalEngine2Hours float64 `json:"total_engine_2_hours" example:"1523.5"`
|
||||
TotalEngine2GpcNgN1 float64 `json:"total_engine_2_gpc_ng_n1" example:"98.7"`
|
||||
TotalEngine2PtcNfN2 float64 `json:"total_engine_2_ptc_nf_n2" example:"97.2"`
|
||||
TotalEngine2Ccc float64 `json:"total_engine_2_ccc" example:"44"`
|
||||
TotalFlightReport float64 `json:"total_flight_report" example:"80"`
|
||||
TotalHookRelease float64 `json:"total_hook_release" example:"10"`
|
||||
TotalRotorBrakeCycle float64 `json:"total_rotor_brake_cycle" example:"8"`
|
||||
CreatedAt string `json:"created_at,omitempty" example:"2026-03-12T03:04:05Z"`
|
||||
CreatedBy string `json:"created_by,omitempty" example:"018f3a5c-7a6f-7c2a-8d4a-4b5b1f6c8e9d"`
|
||||
UpdatedAt string `json:"updated_at,omitempty" example:"2026-03-12T03:04:05Z"`
|
||||
UpdatedBy string `json:"updated_by,omitempty" example:"018f3a5c-7a6f-7c2a-8d4a-4b5b1f6c8e9d"`
|
||||
DeletedAt string `json:"deleted_at,omitempty" example:"2026-03-12T03:04:05Z"`
|
||||
DeletedBy string `json:"deleted_by,omitempty" example:"018f3a5c-7a6f-7c2a-8d4a-4b5b1f6c8e9d"`
|
||||
}
|
||||
|
||||
type HelicopterUsageResource struct {
|
||||
Type string `json:"type" example:"helicopter_usage"`
|
||||
ID string `json:"id" example:"018f3a5c-7a6f-7c2a-8d4a-4b5b1f6c8e9d"`
|
||||
Attributes HelicopterUsageAttributes `json:"attributes"`
|
||||
}
|
||||
|
||||
type HelicopterUsageResponse struct {
|
||||
Data HelicopterUsageResource `json:"data"`
|
||||
}
|
||||
|
||||
type HelicopterUsageListResponse struct {
|
||||
Data []HelicopterUsageResource `json:"data"`
|
||||
Meta struct {
|
||||
PageNumber int `json:"page_number" example:"1"`
|
||||
PageSize int `json:"page_size" example:"20"`
|
||||
Total int64 `json:"total" example:"120"`
|
||||
} `json:"meta"`
|
||||
}
|
||||
|
||||
type HelicopterUsageCreateAttributes struct {
|
||||
HelicopterID string `json:"helicopter_id" validate:"required" example:"018f3a5c-7a6f-7c2a-8d4a-4b5b1f6c8e9d"`
|
||||
TotalLanding *float64 `json:"total_landing,omitempty" example:"120"`
|
||||
TotalAirframeHours *float64 `json:"total_airframe_hours,omitempty" example:"1523.5"`
|
||||
TotalAirframeCycles *float64 `json:"total_airframe_cycles,omitempty" example:"320"`
|
||||
TotalEngine1Hours *float64 `json:"total_engine_1_hours,omitempty" example:"1523.5"`
|
||||
TotalEngine1GpcNgN1 *float64 `json:"total_engine_1_gpc_ng_n1,omitempty" example:"98.7"`
|
||||
TotalEngine1PtcNfN2 *float64 `json:"total_engine_1_ptc_nf_n2,omitempty" example:"97.2"`
|
||||
TotalEngine1Ccc *float64 `json:"total_engine_1_ccc,omitempty" example:"44"`
|
||||
TotalEngine2Hours *float64 `json:"total_engine_2_hours,omitempty" example:"1523.5"`
|
||||
TotalEngine2GpcNgN1 *float64 `json:"total_engine_2_gpc_ng_n1,omitempty" example:"98.7"`
|
||||
TotalEngine2PtcNfN2 *float64 `json:"total_engine_2_ptc_nf_n2,omitempty" example:"97.2"`
|
||||
TotalEngine2Ccc *float64 `json:"total_engine_2_ccc,omitempty" example:"44"`
|
||||
TotalFlightReport *float64 `json:"total_flight_report,omitempty" example:"80"`
|
||||
TotalHookRelease *float64 `json:"total_hook_release,omitempty" example:"10"`
|
||||
TotalRotorBrakeCycle *float64 `json:"total_rotor_brake_cycle,omitempty" example:"8"`
|
||||
}
|
||||
|
||||
type HelicopterUsageCreateData struct {
|
||||
Type string `json:"type" validate:"required,oneof=helicopter_usage_create helicopter_usage" example:"helicopter_usage_create"`
|
||||
Attributes HelicopterUsageCreateAttributes `json:"attributes" validate:"required"`
|
||||
}
|
||||
|
||||
type HelicopterUsageCreateRequest struct {
|
||||
Data HelicopterUsageCreateData `json:"data" validate:"required"`
|
||||
}
|
||||
|
||||
type HelicopterUsageUpdateAttributes struct {
|
||||
HelicopterID *string `json:"helicopter_id,omitempty" example:"018f3a5c-7a6f-7c2a-8d4a-4b5b1f6c8e9d"`
|
||||
TotalLanding *float64 `json:"total_landing,omitempty" example:"120"`
|
||||
TotalAirframeHours *float64 `json:"total_airframe_hours,omitempty" example:"1523.5"`
|
||||
TotalAirframeCycles *float64 `json:"total_airframe_cycles,omitempty" example:"320"`
|
||||
TotalEngine1Hours *float64 `json:"total_engine_1_hours,omitempty" example:"1523.5"`
|
||||
TotalEngine1GpcNgN1 *float64 `json:"total_engine_1_gpc_ng_n1,omitempty" example:"98.7"`
|
||||
TotalEngine1PtcNfN2 *float64 `json:"total_engine_1_ptc_nf_n2,omitempty" example:"97.2"`
|
||||
TotalEngine1Ccc *float64 `json:"total_engine_1_ccc,omitempty" example:"44"`
|
||||
TotalEngine2Hours *float64 `json:"total_engine_2_hours,omitempty" example:"1523.5"`
|
||||
TotalEngine2GpcNgN1 *float64 `json:"total_engine_2_gpc_ng_n1,omitempty" example:"98.7"`
|
||||
TotalEngine2PtcNfN2 *float64 `json:"total_engine_2_ptc_nf_n2,omitempty" example:"97.2"`
|
||||
TotalEngine2Ccc *float64 `json:"total_engine_2_ccc,omitempty" example:"44"`
|
||||
TotalFlightReport *float64 `json:"total_flight_report,omitempty" example:"80"`
|
||||
TotalHookRelease *float64 `json:"total_hook_release,omitempty" example:"10"`
|
||||
TotalRotorBrakeCycle *float64 `json:"total_rotor_brake_cycle,omitempty" example:"8"`
|
||||
}
|
||||
|
||||
type HelicopterUsageUpdateData struct {
|
||||
Type string `json:"type" validate:"required,oneof=helicopter_usage_update helicopter_usage" example:"helicopter_usage_update"`
|
||||
ID string `json:"id" example:"018f3a5c-7a6f-7c2a-8d4a-4b5b1f6c8e9d"`
|
||||
Attributes HelicopterUsageUpdateAttributes `json:"attributes" validate:"required"`
|
||||
}
|
||||
|
||||
type HelicopterUsageUpdateRequest struct {
|
||||
Data HelicopterUsageUpdateData `json:"data" validate:"required"`
|
||||
}
|
||||
70
internal/transport/http/dto/hems_operation_category_dto.go
Normal file
70
internal/transport/http/dto/hems_operation_category_dto.go
Normal file
@@ -0,0 +1,70 @@
|
||||
package dto
|
||||
|
||||
// JSON:API HEMS Operation Category DTOs
|
||||
|
||||
type HEMSOperationCategoryAttributes struct {
|
||||
Name string `json:"name" example:"Firefighting"`
|
||||
Type string `json:"type" example:"first"`
|
||||
CreatedAt string `json:"created_at,omitempty" example:"2026-03-12T03:04:05Z"`
|
||||
CreatedBy string `json:"created_by,omitempty" example:"018f3a5c-7a6f-7c2a-8d4a-4b5b1f6c8e9d"`
|
||||
UpdatedAt string `json:"updated_at,omitempty" example:"2026-03-12T03:04:05Z"`
|
||||
UpdatedBy string `json:"updated_by,omitempty" example:"018f3a5c-7a6f-7c2a-8d4a-4b5b1f6c8e9d"`
|
||||
DeletedAt string `json:"deleted_at,omitempty" example:"2026-03-12T03:04:05Z"`
|
||||
DeletedBy string `json:"deleted_by,omitempty" example:"018f3a5c-7a6f-7c2a-8d4a-4b5b1f6c8e9d"`
|
||||
}
|
||||
|
||||
type HEMSOperationCategoryResource struct {
|
||||
Type string `json:"type" example:"hems_operation_category"`
|
||||
ID string `json:"id" example:"018f3a5c-7a6f-7c2a-8d4a-4b5b1f6c8e9d"`
|
||||
Attributes HEMSOperationCategoryAttributes `json:"attributes"`
|
||||
}
|
||||
|
||||
type HEMSOperationCategoryResponse struct {
|
||||
Data HEMSOperationCategoryResource `json:"data"`
|
||||
}
|
||||
|
||||
type HEMSOperationCategoryListResponse struct {
|
||||
Data []HEMSOperationCategoryResource `json:"data"`
|
||||
Meta struct {
|
||||
PageNumber int `json:"page_number" example:"1"`
|
||||
PageSize int `json:"page_size" example:"20"`
|
||||
Total int64 `json:"total" example:"120"`
|
||||
} `json:"meta"`
|
||||
}
|
||||
|
||||
type HEMSOperationCategoryDataTableResponse struct {
|
||||
Data []HEMSOperationCategoryResource `json:"data"`
|
||||
Meta struct {
|
||||
Draw int `json:"draw" example:"1"`
|
||||
RecordsTotal int64 `json:"records_total" example:"120"`
|
||||
RecordsFiltered int64 `json:"records_filtered" example:"12"`
|
||||
} `json:"meta"`
|
||||
}
|
||||
|
||||
type HEMSOperationCategoryCreateAttributes struct {
|
||||
Name string `json:"name" validate:"required" example:"Firefighting"`
|
||||
Type string `json:"type" validate:"required,oneof=first secondary misuse" example:"first"`
|
||||
}
|
||||
|
||||
type HEMSOperationCategoryCreateData struct {
|
||||
Type string `json:"type" validate:"required,oneof=hems_operation_category_create hems_operation_category" example:"hems_operation_category_create"`
|
||||
Attributes HEMSOperationCategoryCreateAttributes `json:"attributes" validate:"required"`
|
||||
}
|
||||
|
||||
type HEMSOperationCategoryCreateRequest struct {
|
||||
Data HEMSOperationCategoryCreateData `json:"data" validate:"required"`
|
||||
}
|
||||
|
||||
type HEMSOperationCategoryUpdateAttributes struct {
|
||||
Name *string `json:"name,omitempty" example:"Firefighting"`
|
||||
Type *string `json:"type,omitempty" validate:"omitempty,oneof=first secondary misuse" example:"first"`
|
||||
}
|
||||
|
||||
type HEMSOperationCategoryUpdateData struct {
|
||||
Type string `json:"type" validate:"required,oneof=hems_operation_category_update hems_operation_category" example:"hems_operation_category_update"`
|
||||
Attributes HEMSOperationCategoryUpdateAttributes `json:"attributes" validate:"required"`
|
||||
}
|
||||
|
||||
type HEMSOperationCategoryUpdateRequest struct {
|
||||
Data HEMSOperationCategoryUpdateData `json:"data" validate:"required"`
|
||||
}
|
||||
95
internal/transport/http/dto/hems_operation_dto.go
Normal file
95
internal/transport/http/dto/hems_operation_dto.go
Normal file
@@ -0,0 +1,95 @@
|
||||
package dto
|
||||
|
||||
// JSON:API HEMS Operation DTOs
|
||||
|
||||
type HEMSOperationOperationalDataRef struct {
|
||||
ID string `json:"id,omitempty" example:"018f3a5c-7a6f-7c2a-8d4a-4b5b1f6c8e9d"`
|
||||
}
|
||||
|
||||
type HEMSOperationCategoryRef struct {
|
||||
ID string `json:"id,omitempty" example:"018f3a5c-7a6f-7c2a-8d4a-4b5b1f6c8e9d"`
|
||||
Name string `json:"name,omitempty" example:"Firefighting"`
|
||||
Type string `json:"type,omitempty" example:"first"`
|
||||
}
|
||||
|
||||
type HEMSOperationMissionRef struct {
|
||||
ID string `json:"id,omitempty" example:"018f3a5c-7a6f-7c2a-8d4a-4b5b1f6c8e9d"`
|
||||
FlightID string `json:"flight_id,omitempty" example:"018f3a5c-7a6f-7c2a-8d4a-4b5b1f6c8e9d"`
|
||||
Type string `json:"type,omitempty" example:"HEMS"`
|
||||
}
|
||||
|
||||
type HEMSOperationAttributes struct {
|
||||
Date string `json:"date" example:"2026-03-12T03:04:05Z"`
|
||||
OperationalDataID string `json:"operational_data_id,omitempty" example:"018f3a5c-7a6f-7c2a-8d4a-4b5b1f6c8e9d"`
|
||||
MissionID string `json:"mission_id,omitempty" example:"018f3a5c-7a6f-7c2a-8d4a-4b5b1f6c8e9d"`
|
||||
HEMSOperationCategoryID string `json:"hems_operation_category_id,omitempty" example:"018f3a5c-7a6f-7c2a-8d4a-4b5b1f6c8e9d"`
|
||||
OperationalData *HEMSOperationOperationalDataRef `json:"operational_data,omitempty"`
|
||||
Mission *HEMSOperationMissionRef `json:"mission,omitempty"`
|
||||
HEMSOperationCategory *HEMSOperationCategoryRef `json:"hems_operation_category,omitempty"`
|
||||
CreatedAt string `json:"created_at,omitempty" example:"2026-03-12T03:04:05Z"`
|
||||
CreatedBy string `json:"created_by,omitempty" example:"018f3a5c-7a6f-7c2a-8d4a-4b5b1f6c8e9d"`
|
||||
UpdatedAt string `json:"updated_at,omitempty" example:"2026-03-12T03:04:05Z"`
|
||||
UpdatedBy string `json:"updated_by,omitempty" example:"018f3a5c-7a6f-7c2a-8d4a-4b5b1f6c8e9d"`
|
||||
DeletedAt string `json:"deleted_at,omitempty" example:"2026-03-12T03:04:05Z"`
|
||||
DeletedBy string `json:"deleted_by,omitempty" example:"018f3a5c-7a6f-7c2a-8d4a-4b5b1f6c8e9d"`
|
||||
}
|
||||
|
||||
type HEMSOperationResource struct {
|
||||
Type string `json:"type" example:"hems_operation"`
|
||||
ID string `json:"id" example:"018f3a5c-7a6f-7c2a-8d4a-4b5b1f6c8e9d"`
|
||||
Attributes HEMSOperationAttributes `json:"attributes"`
|
||||
}
|
||||
|
||||
type HEMSOperationResponse struct {
|
||||
Data HEMSOperationResource `json:"data"`
|
||||
}
|
||||
|
||||
type HEMSOperationListResponse struct {
|
||||
Data []HEMSOperationResource `json:"data"`
|
||||
Meta struct {
|
||||
PageNumber int `json:"page_number" example:"1"`
|
||||
PageSize int `json:"page_size" example:"20"`
|
||||
Total int64 `json:"total" example:"120"`
|
||||
} `json:"meta"`
|
||||
}
|
||||
|
||||
type HEMSOperationDataTableResponse struct {
|
||||
Data []HEMSOperationResource `json:"data"`
|
||||
Meta struct {
|
||||
Draw int `json:"draw" example:"1"`
|
||||
RecordsTotal int64 `json:"records_total" example:"120"`
|
||||
RecordsFiltered int64 `json:"records_filtered" example:"12"`
|
||||
} `json:"meta"`
|
||||
}
|
||||
|
||||
type HEMSOperationCreateAttributes struct {
|
||||
Date string `json:"date" validate:"required" example:"2026-03-12T03:04:05Z"`
|
||||
OperationalDataID *string `json:"operational_data_id,omitempty" example:"018f3a5c-7a6f-7c2a-8d4a-4b5b1f6c8e9d"`
|
||||
MissionID *string `json:"mission_id,omitempty" example:"018f3a5c-7a6f-7c2a-8d4a-4b5b1f6c8e9d"`
|
||||
HEMSOperationCategoryID *string `json:"hems_operation_category_id,omitempty" example:"018f3a5c-7a6f-7c2a-8d4a-4b5b1f6c8e9d"`
|
||||
}
|
||||
|
||||
type HEMSOperationCreateData struct {
|
||||
Type string `json:"type" validate:"required,oneof=hems_operation_create hems_operation" example:"hems_operation_create"`
|
||||
Attributes HEMSOperationCreateAttributes `json:"attributes" validate:"required"`
|
||||
}
|
||||
|
||||
type HEMSOperationCreateRequest struct {
|
||||
Data HEMSOperationCreateData `json:"data" validate:"required"`
|
||||
}
|
||||
|
||||
type HEMSOperationUpdateAttributes struct {
|
||||
Date *string `json:"date,omitempty" example:"2026-03-12T03:04:05Z"`
|
||||
OperationalDataID *string `json:"operational_data_id,omitempty" example:"018f3a5c-7a6f-7c2a-8d4a-4b5b1f6c8e9d"`
|
||||
MissionID *string `json:"mission_id,omitempty" example:"018f3a5c-7a6f-7c2a-8d4a-4b5b1f6c8e9d"`
|
||||
HEMSOperationCategoryID *string `json:"hems_operation_category_id,omitempty" example:"018f3a5c-7a6f-7c2a-8d4a-4b5b1f6c8e9d"`
|
||||
}
|
||||
|
||||
type HEMSOperationUpdateData struct {
|
||||
Type string `json:"type" validate:"required,oneof=hems_operation_update hems_operation" example:"hems_operation_update"`
|
||||
Attributes HEMSOperationUpdateAttributes `json:"attributes" validate:"required"`
|
||||
}
|
||||
|
||||
type HEMSOperationUpdateRequest struct {
|
||||
Data HEMSOperationUpdateData `json:"data" validate:"required"`
|
||||
}
|
||||
141
internal/transport/http/dto/hems_operational_data_dto.go
Normal file
141
internal/transport/http/dto/hems_operational_data_dto.go
Normal file
@@ -0,0 +1,141 @@
|
||||
package dto
|
||||
|
||||
// JSON:API HEMS Operational Data DTOs
|
||||
|
||||
type HEMSOperationalDataFileAttributes struct {
|
||||
ID string `json:"id" example:"018f3a5c-7a6f-7c2a-8d4a-4b5b1f6c8e9d"`
|
||||
FileAttachmentID string `json:"file_attachment_id" example:"018f3a5c-7a6f-7c2a-8d4a-4b5b1f6c8e9d"`
|
||||
}
|
||||
|
||||
type HEMSOperationalDataAttributes struct {
|
||||
Time string `json:"time" example:"2026-03-12T03:04:05Z"`
|
||||
LandID string `json:"land_id,omitempty" example:"018f3a5c-7a6f-7c2a-8d4a-4b5b1f6c8e9d"`
|
||||
VocationID string `json:"vocation_id,omitempty" example:"018f3a5c-7a6f-7c2a-8d4a-4b5b1f6c8e9d"`
|
||||
ForcePresentIDs []string `json:"force_present_ids,omitempty" example:"018f3a5c-7a6f-7c2a-8d4a-4b5b1f6c8e9d"`
|
||||
StateID string `json:"state_id,omitempty" example:"018f3a5c-7a6f-7c2a-8d4a-4b5b1f6c8e9d"`
|
||||
Location string `json:"location" example:"123 Main St, Anytown, USA"`
|
||||
Postcode string `json:"postcode,omitempty" example:"12345"`
|
||||
Notes string `json:"notes" example:"Notes about the operational data"`
|
||||
Darkness bool `json:"darkness"`
|
||||
FalseInformation bool `json:"false_information"`
|
||||
Fog bool `json:"fog"`
|
||||
Precipitation bool `json:"precipitation"`
|
||||
MountainUse bool `json:"mountain_use"`
|
||||
Wind bool `json:"wind"`
|
||||
NFOSearch bool `json:"nfo_search"`
|
||||
LandingSiteSearch bool `json:"landing_site_search"`
|
||||
Temperature float64 `json:"temperature" example:"23.5"`
|
||||
Altitude float64 `json:"altitude" example:"5.2"`
|
||||
TerrainIndex string `json:"terrain_index" example:"Only helicopter landing"`
|
||||
RopeRecovery bool `json:"rope_recovery"`
|
||||
NightFlight bool `json:"night_flight"`
|
||||
InstrumentFlight bool `json:"instrument_flight"`
|
||||
AdditionalInfo string `json:"additional_info" example:"Additional information about the operational data"`
|
||||
Files []HEMSOperationalDataFileAttributes `json:"files,omitempty"`
|
||||
CreatedAt string `json:"created_at,omitempty" example:"2026-03-12T03:04:05Z"`
|
||||
CreatedBy string `json:"created_by,omitempty" example:"018f3a5c-7a6f-7c2a-8d4a-4b5b1f6c8e9d"`
|
||||
UpdatedAt string `json:"updated_at,omitempty" example:"2026-03-12T03:04:05Z"`
|
||||
UpdatedBy string `json:"updated_by,omitempty" example:"018f3a5c-7a6f-7c2a-8d4a-4b5b1f6c8e9d"`
|
||||
DeletedAt string `json:"deleted_at,omitempty" example:"2026-03-12T03:04:05Z"`
|
||||
DeletedBy string `json:"deleted_by,omitempty" example:"018f3a5c-7a6f-7c2a-8d4a-4b5b1f6c8e9d"`
|
||||
}
|
||||
|
||||
type HEMSOperationalDataResource struct {
|
||||
Type string `json:"type" example:"hems_operational_data"`
|
||||
ID string `json:"id" example:"018f3a5c-7a6f-7c2a-8d4a-4b5b1f6c8e9d"`
|
||||
Attributes HEMSOperationalDataAttributes `json:"attributes"`
|
||||
}
|
||||
|
||||
type HEMSOperationalDataResponse struct {
|
||||
Data HEMSOperationalDataResource `json:"data"`
|
||||
}
|
||||
|
||||
type HEMSOperationalDataListResponse struct {
|
||||
Data []HEMSOperationalDataResource `json:"data"`
|
||||
Meta struct {
|
||||
PageNumber int `json:"page_number" example:"1"`
|
||||
PageSize int `json:"page_size" example:"20"`
|
||||
Total int64 `json:"total" example:"120"`
|
||||
} `json:"meta"`
|
||||
}
|
||||
|
||||
type HEMSOperationalDataDataTableResponse struct {
|
||||
Data []HEMSOperationalDataResource `json:"data"`
|
||||
Meta struct {
|
||||
Draw int `json:"draw" example:"1"`
|
||||
RecordsTotal int64 `json:"records_total" example:"120"`
|
||||
RecordsFiltered int64 `json:"records_filtered" example:"12"`
|
||||
} `json:"meta"`
|
||||
}
|
||||
|
||||
type HEMSOperationalDataCreateAttributes struct {
|
||||
Time string `json:"time" validate:"required" example:"2026-03-12T03:04:05Z"`
|
||||
LandID *string `json:"land_id,omitempty" example:"018f3a5c-7a6f-7c2a-8d4a-4b5b1f6c8e9d"`
|
||||
VocationID *string `json:"vocation_id,omitempty" example:"018f3a5c-7a6f-7c2a-8d4a-4b5b1f6c8e9d"`
|
||||
ForcePresentIDs []string `json:"force_present_ids,omitempty" example:"018f3a5c-7a6f-7c2a-8d4a-4b5b1f6c8e9d"`
|
||||
StateID *string `json:"state_id,omitempty" example:"018f3a5c-7a6f-7c2a-8d4a-4b5b1f6c8e9d"`
|
||||
Location string `json:"location" validate:"required" example:"123 Main St, Anytown, USA"`
|
||||
Postcode *string `json:"postcode,omitempty" example:"12345"`
|
||||
Notes *string `json:"notes,omitempty" example:"Notes about the operational data"`
|
||||
Darkness *bool `json:"darkness,omitempty"`
|
||||
FalseInformation *bool `json:"false_information,omitempty"`
|
||||
Fog *bool `json:"fog,omitempty"`
|
||||
Precipitation *bool `json:"precipitation,omitempty"`
|
||||
MountainUse *bool `json:"mountain_use,omitempty"`
|
||||
Wind *bool `json:"wind,omitempty"`
|
||||
NFOSearch *bool `json:"nfo_search,omitempty"`
|
||||
LandingSiteSearch *bool `json:"landing_site_search,omitempty"`
|
||||
Temperature *float64 `json:"temperature,omitempty" example:"23.5"`
|
||||
Altitude *float64 `json:"altitude,omitempty" example:"5.2"`
|
||||
TerrainIndex *string `json:"terrain_index,omitempty" example:"Only helicopter landing"`
|
||||
RopeRecovery *bool `json:"rope_recovery,omitempty"`
|
||||
NightFlight *bool `json:"night_flight,omitempty"`
|
||||
InstrumentFlight *bool `json:"instrument_flight,omitempty"`
|
||||
AdditionalInfo *string `json:"additional_info,omitempty" example:"Additional information about the operational data"`
|
||||
FileAttachmentIDs []string `json:"file_attachment_ids,omitempty" example:"018f3a5c-7a6f-7c2a-8d4a-4b5b1f6c8e9d"`
|
||||
}
|
||||
|
||||
type HEMSOperationalDataCreateData struct {
|
||||
Type string `json:"type" validate:"required,oneof=hems_operational_data_create hems_operational_data" example:"hems_operational_data_create"`
|
||||
Attributes HEMSOperationalDataCreateAttributes `json:"attributes" validate:"required"`
|
||||
}
|
||||
|
||||
type HEMSOperationalDataCreateRequest struct {
|
||||
Data HEMSOperationalDataCreateData `json:"data" validate:"required"`
|
||||
}
|
||||
|
||||
type HEMSOperationalDataUpdateAttributes struct {
|
||||
Time *string `json:"time,omitempty" example:"2026-03-12T03:04:05Z"`
|
||||
LandID *string `json:"land_id,omitempty" example:"018f3a5c-7a6f-7c2a-8d4a-4b5b1f6c8e9d"`
|
||||
VocationID *string `json:"vocation_id,omitempty" example:"018f3a5c-7a6f-7c2a-8d4a-4b5b1f6c8e9d"`
|
||||
ForcePresentIDs *[]string `json:"force_present_ids,omitempty"`
|
||||
StateID *string `json:"state_id,omitempty" example:"018f3a5c-7a6f-7c2a-8d4a-4b5b1f6c8e9d"`
|
||||
Location *string `json:"location,omitempty" example:"123 Main St, Anytown, USA"`
|
||||
Postcode *string `json:"postcode,omitempty" example:"12345"`
|
||||
Notes *string `json:"notes,omitempty" example:"Notes about the operational data"`
|
||||
Darkness *bool `json:"darkness,omitempty"`
|
||||
FalseInformation *bool `json:"false_information,omitempty"`
|
||||
Fog *bool `json:"fog,omitempty"`
|
||||
Precipitation *bool `json:"precipitation,omitempty"`
|
||||
MountainUse *bool `json:"mountain_use,omitempty"`
|
||||
Wind *bool `json:"wind,omitempty"`
|
||||
NFOSearch *bool `json:"nfo_search,omitempty"`
|
||||
LandingSiteSearch *bool `json:"landing_site_search,omitempty"`
|
||||
Temperature *float64 `json:"temperature,omitempty" example:"23.5"`
|
||||
Altitude *float64 `json:"altitude,omitempty" example:"5.2"`
|
||||
TerrainIndex *string `json:"terrain_index,omitempty" example:"Only helicopter landing"`
|
||||
RopeRecovery *bool `json:"rope_recovery,omitempty"`
|
||||
NightFlight *bool `json:"night_flight,omitempty"`
|
||||
InstrumentFlight *bool `json:"instrument_flight,omitempty"`
|
||||
AdditionalInfo *string `json:"additional_info,omitempty" example:"Additional information about the operational data"`
|
||||
FileAttachmentIDs *[]string `json:"file_attachment_ids,omitempty"`
|
||||
}
|
||||
|
||||
type HEMSOperationalDataUpdateData struct {
|
||||
Type string `json:"type" validate:"required,oneof=hems_operational_data_update hems_operational_data" example:"hems_operational_data_update"`
|
||||
Attributes HEMSOperationalDataUpdateAttributes `json:"attributes" validate:"required"`
|
||||
}
|
||||
|
||||
type HEMSOperationalDataUpdateRequest struct {
|
||||
Data HEMSOperationalDataUpdateData `json:"data" validate:"required"`
|
||||
}
|
||||
89
internal/transport/http/dto/hospital_dto.go
Normal file
89
internal/transport/http/dto/hospital_dto.go
Normal file
@@ -0,0 +1,89 @@
|
||||
package dto
|
||||
|
||||
// JSON:API Hospital DTOs
|
||||
|
||||
type HospitalAttributes struct {
|
||||
Name string `json:"name" example:"RSUD Kota"`
|
||||
SortKey *int `json:"sortkey" example:"1"`
|
||||
Address string `json:"address,omitempty" example:"Jl. Merdeka No. 10"`
|
||||
LandlineNumber string `json:"landline_number,omitempty" example:"0211234567"`
|
||||
MobileNumber string `json:"mobile_number,omitempty" example:"08123456789"`
|
||||
Email string `json:"email,omitempty" example:"info@hospital.local"`
|
||||
Note string `json:"note,omitempty" example:"Nearest trauma center"`
|
||||
IsActive bool `json:"is_active" example:"true"`
|
||||
CreatedAt string `json:"created_at,omitempty" example:"2026-03-12T03:04:05Z"`
|
||||
CreatedBy string `json:"created_by,omitempty" example:"018f3a5c-7a6f-7c2a-8d4a-4b5b1f6c8e9d"`
|
||||
UpdatedAt string `json:"updated_at,omitempty" example:"2026-03-12T03:04:05Z"`
|
||||
UpdatedBy string `json:"updated_by,omitempty" example:"018f3a5c-7a6f-7c2a-8d4a-4b5b1f6c8e9d"`
|
||||
DeletedAt string `json:"deleted_at,omitempty" example:"2026-03-12T03:04:05Z"`
|
||||
DeletedBy string `json:"deleted_by,omitempty" example:"018f3a5c-7a6f-7c2a-8d4a-4b5b1f6c8e9d"`
|
||||
}
|
||||
|
||||
type HospitalResource struct {
|
||||
Type string `json:"type" example:"hospital"`
|
||||
ID string `json:"id" example:"018f3a5c-7a6f-7c2a-8d4a-4b5b1f6c8e9d"`
|
||||
Attributes HospitalAttributes `json:"attributes"`
|
||||
}
|
||||
|
||||
type HospitalResponse struct {
|
||||
Data HospitalResource `json:"data"`
|
||||
}
|
||||
|
||||
type HospitalListResponse struct {
|
||||
Data []HospitalResource `json:"data"`
|
||||
Meta struct {
|
||||
PageNumber int `json:"page_number" example:"1"`
|
||||
PageSize int `json:"page_size" example:"20"`
|
||||
Total int64 `json:"total" example:"120"`
|
||||
} `json:"meta"`
|
||||
}
|
||||
|
||||
type HospitalDataTableResponse struct {
|
||||
Data []HospitalResource `json:"data"`
|
||||
Meta struct {
|
||||
Draw int `json:"draw" example:"1"`
|
||||
RecordsTotal int64 `json:"records_total" example:"120"`
|
||||
RecordsFiltered int64 `json:"records_filtered" example:"12"`
|
||||
} `json:"meta"`
|
||||
}
|
||||
|
||||
type HospitalCreateAttributes struct {
|
||||
Name string `json:"name" validate:"required" example:"RSUD Kota"`
|
||||
SortKey *int `json:"sortkey" example:"1"`
|
||||
Address *string `json:"address,omitempty" example:"Jl. Merdeka No. 10"`
|
||||
LandlineNumber *string `json:"landline_number,omitempty" example:"0211234567"`
|
||||
MobileNumber *string `json:"mobile_number,omitempty" example:"08123456789"`
|
||||
Email *string `json:"email,omitempty" example:"info@hospital.local"`
|
||||
Note *string `json:"note,omitempty" example:"Nearest trauma center"`
|
||||
IsActive *bool `json:"is_active,omitempty" example:"true"`
|
||||
}
|
||||
|
||||
type HospitalCreateData struct {
|
||||
Type string `json:"type" validate:"required,oneof=hospital_create hospital" example:"hospital_create"`
|
||||
Attributes HospitalCreateAttributes `json:"attributes" validate:"required"`
|
||||
}
|
||||
|
||||
type HospitalCreateRequest struct {
|
||||
Data HospitalCreateData `json:"data" validate:"required"`
|
||||
}
|
||||
|
||||
type HospitalUpdateAttributes struct {
|
||||
Name *string `json:"name,omitempty" example:"RSUD Kota"`
|
||||
SortKey NullInt `json:"sortkey,omitempty" swaggertype:"integer" example:"1"`
|
||||
Address *string `json:"address,omitempty" example:"Jl. Merdeka No. 10"`
|
||||
LandlineNumber *string `json:"landline_number,omitempty" example:"0211234567"`
|
||||
MobileNumber *string `json:"mobile_number,omitempty" example:"08123456789"`
|
||||
Email *string `json:"email,omitempty" example:"info@hospital.local"`
|
||||
Note *string `json:"note,omitempty" example:"Nearest trauma center"`
|
||||
IsActive *bool `json:"is_active,omitempty" example:"true"`
|
||||
}
|
||||
|
||||
type HospitalUpdateData struct {
|
||||
Type string `json:"type" validate:"required,oneof=hospital_update hospital" example:"hospital_update"`
|
||||
ID string `json:"id" example:"018f3a5c-7a6f-7c2a-8d4a-4b5b1f6c8e9d"`
|
||||
Attributes HospitalUpdateAttributes `json:"attributes" validate:"required"`
|
||||
}
|
||||
|
||||
type HospitalUpdateRequest struct {
|
||||
Data HospitalUpdateData `json:"data" validate:"required"`
|
||||
}
|
||||
101
internal/transport/http/dto/icao_dto.go
Normal file
101
internal/transport/http/dto/icao_dto.go
Normal file
@@ -0,0 +1,101 @@
|
||||
package dto
|
||||
|
||||
// JSON:API ICAO DTOs
|
||||
|
||||
type ICAOAttributes struct {
|
||||
LandID string `json:"land_id,omitempty" example:"018f3a5c-7a6f-7c2a-8d4a-4b5b1f6c8e9d"`
|
||||
FederalStateID string `json:"federal_state_id,omitempty" example:"018f3a5c-7a6f-7c2a-8d4a-4b5b1f6c8e9d"`
|
||||
FederalState string `json:"federal_state,omitempty" example:"Bayern"`
|
||||
LandName string `json:"land_name,omitempty" example:"Germany"`
|
||||
LandISOCode string `json:"land_iso_code,omitempty" example:"DE"`
|
||||
ICAOCode string `json:"icao_code" example:"EDDB"`
|
||||
Name string `json:"name,omitempty" example:"Berlin Base"`
|
||||
SortKey *int `json:"sortkey" example:"1"`
|
||||
Address string `json:"address,omitempty" example:"Muehlenstrasse 1"`
|
||||
LandlineNumber string `json:"landline_number,omitempty" example:"030123456"`
|
||||
MobileNumber string `json:"mobile_number,omitempty" example:"0170123456"`
|
||||
Email string `json:"email,omitempty" example:"ops@example.local"`
|
||||
Note string `json:"note,omitempty" example:"Night operations supported"`
|
||||
IsActive bool `json:"is_active" example:"true"`
|
||||
CreatedAt string `json:"created_at,omitempty" example:"2026-03-12T03:04:05Z"`
|
||||
CreatedBy string `json:"created_by,omitempty" example:"018f3a5c-7a6f-7c2a-8d4a-4b5b1f6c8e9d"`
|
||||
UpdatedAt string `json:"updated_at,omitempty" example:"2026-03-12T03:04:05Z"`
|
||||
UpdatedBy string `json:"updated_by,omitempty" example:"018f3a5c-7a6f-7c2a-8d4a-4b5b1f6c8e9d"`
|
||||
DeletedAt string `json:"deleted_at,omitempty" example:"2026-03-12T03:04:05Z"`
|
||||
DeletedBy string `json:"deleted_by,omitempty" example:"018f3a5c-7a6f-7c2a-8d4a-4b5b1f6c8e9d"`
|
||||
}
|
||||
|
||||
type ICAOResource struct {
|
||||
Type string `json:"type" example:"icao"`
|
||||
ID string `json:"id" example:"018f3a5c-7a6f-7c2a-8d4a-4b5b1f6c8e9d"`
|
||||
Attributes ICAOAttributes `json:"attributes"`
|
||||
}
|
||||
|
||||
type ICAOResponse struct {
|
||||
Data ICAOResource `json:"data"`
|
||||
}
|
||||
|
||||
type ICAOListResponse struct {
|
||||
Data []ICAOResource `json:"data"`
|
||||
Meta struct {
|
||||
PageNumber int `json:"page_number" example:"1"`
|
||||
PageSize int `json:"page_size" example:"20"`
|
||||
Total int64 `json:"total" example:"120"`
|
||||
} `json:"meta"`
|
||||
}
|
||||
|
||||
type ICAODataTableResponse struct {
|
||||
Data []ICAOResource `json:"data"`
|
||||
Meta struct {
|
||||
Draw int `json:"draw" example:"1"`
|
||||
RecordsTotal int64 `json:"records_total" example:"120"`
|
||||
RecordsFiltered int64 `json:"records_filtered" example:"12"`
|
||||
} `json:"meta"`
|
||||
}
|
||||
|
||||
type ICAOCreateAttributes struct {
|
||||
LandID *string `json:"land_id,omitempty" example:"018f3a5c-7a6f-7c2a-8d4a-4b5b1f6c8e9d"`
|
||||
FederalStateID *string `json:"federal_state_id,omitempty" example:"018f3a5c-7a6f-7c2a-8d4a-4b5b1f6c8e9d"`
|
||||
ICAOCode string `json:"icao_code" validate:"required" example:"EDDB"`
|
||||
Name *string `json:"name,omitempty" example:"Berlin Base"`
|
||||
SortKey *int `json:"sortkey" example:"1"`
|
||||
Address *string `json:"address,omitempty" example:"Muehlenstrasse 1"`
|
||||
LandlineNumber *string `json:"landline_number,omitempty" example:"030123456"`
|
||||
MobileNumber *string `json:"mobile_number,omitempty" example:"0170123456"`
|
||||
Email *string `json:"email,omitempty" example:"ops@example.local"`
|
||||
Note *string `json:"note,omitempty" example:"Night operations supported"`
|
||||
IsActive *bool `json:"is_active,omitempty" example:"true"`
|
||||
}
|
||||
|
||||
type ICAOCreateData struct {
|
||||
Type string `json:"type" validate:"required,oneof=icao_create icao" example:"icao_create"`
|
||||
Attributes ICAOCreateAttributes `json:"attributes" validate:"required"`
|
||||
}
|
||||
|
||||
type ICAOCreateRequest struct {
|
||||
Data ICAOCreateData `json:"data" validate:"required"`
|
||||
}
|
||||
|
||||
type ICAOUpdateAttributes struct {
|
||||
LandID *string `json:"land_id,omitempty" example:"018f3a5c-7a6f-7c2a-8d4a-4b5b1f6c8e9d"`
|
||||
FederalStateID *string `json:"federal_state_id,omitempty" example:"018f3a5c-7a6f-7c2a-8d4a-4b5b1f6c8e9d"`
|
||||
ICAOCode *string `json:"icao_code,omitempty" example:"EDDB"`
|
||||
Name *string `json:"name,omitempty" example:"Berlin Base"`
|
||||
SortKey NullInt `json:"sortkey,omitempty" swaggertype:"integer" example:"1"`
|
||||
Address *string `json:"address,omitempty" example:"Muehlenstrasse 1"`
|
||||
LandlineNumber *string `json:"landline_number,omitempty" example:"030123456"`
|
||||
MobileNumber *string `json:"mobile_number,omitempty" example:"0170123456"`
|
||||
Email *string `json:"email,omitempty" example:"ops@example.local"`
|
||||
Note *string `json:"note,omitempty" example:"Night operations supported"`
|
||||
IsActive *bool `json:"is_active,omitempty" example:"true"`
|
||||
}
|
||||
|
||||
type ICAOUpdateData struct {
|
||||
Type string `json:"type" validate:"required,oneof=icao_update icao" example:"icao_update"`
|
||||
ID string `json:"id" example:"018f3a5c-7a6f-7c2a-8d4a-4b5b1f6c8e9d"`
|
||||
Attributes ICAOUpdateAttributes `json:"attributes" validate:"required"`
|
||||
}
|
||||
|
||||
type ICAOUpdateRequest struct {
|
||||
Data ICAOUpdateData `json:"data" validate:"required"`
|
||||
}
|
||||
87
internal/transport/http/dto/insurance_patient_data_dto.go
Normal file
87
internal/transport/http/dto/insurance_patient_data_dto.go
Normal file
@@ -0,0 +1,87 @@
|
||||
package dto
|
||||
|
||||
// JSON:API InsurancePatientData DTOs
|
||||
|
||||
type InsurancePatientDataHealthInsuranceCompany struct {
|
||||
ID string `json:"id,omitempty" example:"018f3a5c-7a6f-7c2a-8d4a-4b5b1f6c8e9d"`
|
||||
Name string `json:"name,omitempty" example:"ABC Insurance"`
|
||||
State string `json:"state,omitempty" example:"Berlin"`
|
||||
Address string `json:"address,omitempty" example:"Main Street 10"`
|
||||
MobileNumber string `json:"mobile_number,omitempty" example:"+4912345678"`
|
||||
Email string `json:"email,omitempty" example:"info@abc-insurance.local"`
|
||||
}
|
||||
|
||||
type InsurancePatientDataFederalState struct {
|
||||
ID string `json:"id,omitempty" example:"018f3a5c-7a6f-7c2a-8d4a-4b5b1f6c8e9d"`
|
||||
Name string `json:"name,omitempty" example:"Bayern"`
|
||||
}
|
||||
|
||||
type InsurancePatientDataAttributes struct {
|
||||
HealthInsuranceCompaniesId string `json:"health_insurance_companies_id,omitempty" example:"018f3a5c-7a6f-7c2a-8d4a-4b5b1f6c8e9d"`
|
||||
FederalStateId string `json:"federal_state_id,omitempty" example:"018f3a5c-7a6f-7c2a-8d4a-4b5b1f6c8e9d"`
|
||||
HealthInsuranceCompany *InsurancePatientDataHealthInsuranceCompany `json:"health_insurance_company,omitempty"`
|
||||
FederalState *InsurancePatientDataFederalState `json:"federal_state,omitempty"`
|
||||
CreatedAt string `json:"created_at,omitempty" example:"2026-03-12T03:04:05Z"`
|
||||
CreatedBy string `json:"created_by,omitempty" example:"018f3a5c-7a6f-7c2a-8d4a-4b5b1f6c8e9d"`
|
||||
UpdatedAt string `json:"updated_at,omitempty" example:"2026-03-12T03:04:05Z"`
|
||||
UpdatedBy string `json:"updated_by,omitempty" example:"018f3a5c-7a6f-7c2a-8d4a-4b5b1f6c8e9d"`
|
||||
DeletedAt string `json:"deleted_at,omitempty" example:"2026-03-12T03:04:05Z"`
|
||||
DeletedBy string `json:"deleted_by,omitempty" example:"018f3a5c-7a6f-7c2a-8d4a-4b5b1f6c8e9d"`
|
||||
}
|
||||
|
||||
type InsurancePatientDataResource struct {
|
||||
Type string `json:"type" example:"insurance_patient_data"`
|
||||
ID string `json:"id" example:"018f3a5c-7a6f-7c2a-8d4a-4b5b1f6c8e9d"`
|
||||
Attributes InsurancePatientDataAttributes `json:"attributes"`
|
||||
}
|
||||
|
||||
type InsurancePatientDataResponse struct {
|
||||
Data InsurancePatientDataResource `json:"data"`
|
||||
}
|
||||
|
||||
type InsurancePatientDataListResponse struct {
|
||||
Data []InsurancePatientDataResource `json:"data"`
|
||||
Meta struct {
|
||||
PageNumber int `json:"page_number" example:"1"`
|
||||
PageSize int `json:"page_size" example:"20"`
|
||||
Total int64 `json:"total" example:"120"`
|
||||
} `json:"meta"`
|
||||
}
|
||||
|
||||
type InsurancePatientDataDataTableResponse struct {
|
||||
Data []InsurancePatientDataResource `json:"data"`
|
||||
Meta struct {
|
||||
Draw int `json:"draw" example:"1"`
|
||||
RecordsTotal int64 `json:"records_total" example:"120"`
|
||||
RecordsFiltered int64 `json:"records_filtered" example:"12"`
|
||||
} `json:"meta"`
|
||||
}
|
||||
|
||||
type InsurancePatientDataCreateAttributes struct {
|
||||
HealthInsuranceCompaniesId string `json:"health_insurance_companies_id" validate:"required" example:"018f3a5c-7a6f-7c2a-8d4a-4b5b1f6c8e9d"`
|
||||
FederalStateId string `json:"federal_state_id" validate:"required" example:"018f3a5c-7a6f-7c2a-8d4a-4b5b1f6c8e9d"`
|
||||
}
|
||||
|
||||
type InsurancePatientDataCreateData struct {
|
||||
Type string `json:"type" validate:"required,oneof=insurance_patient_data_create insurance_patient_data" example:"insurance_patient_data_create"`
|
||||
Attributes InsurancePatientDataCreateAttributes `json:"attributes" validate:"required"`
|
||||
}
|
||||
|
||||
type InsurancePatientDataCreateRequest struct {
|
||||
Data InsurancePatientDataCreateData `json:"data" validate:"required"`
|
||||
}
|
||||
|
||||
type InsurancePatientDataUpdateAttributes struct {
|
||||
HealthInsuranceCompaniesId *string `json:"health_insurance_companies_id,omitempty" example:"018f3a5c-7a6f-7c2a-8d4a-4b5b1f6c8e9d"`
|
||||
FederalStateId *string `json:"federal_state_id,omitempty" example:"018f3a5c-7a6f-7c2a-8d4a-4b5b1f6c8e9d"`
|
||||
}
|
||||
|
||||
type InsurancePatientDataUpdateData struct {
|
||||
Type string `json:"type" validate:"required,oneof=insurance_patient_data_update insurance_patient_data" example:"insurance_patient_data_update"`
|
||||
ID string `json:"id" example:"018f3a5c-7a6f-7c2a-8d4a-4b5b1f6c8e9d"`
|
||||
Attributes InsurancePatientDataUpdateAttributes `json:"attributes" validate:"required"`
|
||||
}
|
||||
|
||||
type InsurancePatientDataUpdateRequest struct {
|
||||
Data InsurancePatientDataUpdateData `json:"data" validate:"required"`
|
||||
}
|
||||
103
internal/transport/http/dto/land_dto.go
Normal file
103
internal/transport/http/dto/land_dto.go
Normal file
@@ -0,0 +1,103 @@
|
||||
package dto
|
||||
|
||||
import "wucher/internal/domain/land"
|
||||
|
||||
// JSON:API Land DTOs
|
||||
|
||||
type LandAttributes struct {
|
||||
Name string `json:"name" example:"Germany"`
|
||||
Note string `json:"note,omitempty" example:"Central Europe"`
|
||||
ISOCode string `json:"iso_code,omitempty" example:"DE"`
|
||||
BMDExportID *string `json:"bmd_export_id,omitempty" example:"DE001"`
|
||||
FederalStateTotal int `json:"federal_state_total" example:"2"`
|
||||
FederalStateList []LandFederalStateDTO `json:"federal_state_list,omitempty"`
|
||||
SortKey *int `json:"sortkey" example:"1"`
|
||||
IsActive bool `json:"is_active" example:"true"`
|
||||
CreatedAt string `json:"created_at,omitempty" example:"2026-03-12T03:04:05Z"`
|
||||
CreatedBy string `json:"created_by,omitempty" example:"018f3a5c-7a6f-7c2a-8d4a-4b5b1f6c8e9d"`
|
||||
UpdatedAt string `json:"updated_at,omitempty" example:"2026-03-12T03:04:05Z"`
|
||||
UpdatedBy string `json:"updated_by,omitempty" example:"018f3a5c-7a6f-7c2a-8d4a-4b5b1f6c8e9d"`
|
||||
DeletedAt string `json:"deleted_at,omitempty" example:"2026-03-12T03:04:05Z"`
|
||||
DeletedBy string `json:"deleted_by,omitempty" example:"018f3a5c-7a6f-7c2a-8d4a-4b5b1f6c8e9d"`
|
||||
}
|
||||
|
||||
type LandFederalStateDTO struct {
|
||||
ID string `json:"id" example:"018f3a5c-7a6f-7c2a-8d4a-4b5b1f6c8e9d"`
|
||||
Name string `json:"name" example:"Bayern"`
|
||||
}
|
||||
|
||||
type LandFederalStateView struct {
|
||||
ID []byte
|
||||
Name string
|
||||
}
|
||||
|
||||
type LandView struct {
|
||||
Row land.Land
|
||||
FederalStateTotal int
|
||||
FederalStateList []LandFederalStateView
|
||||
}
|
||||
|
||||
type LandResource struct {
|
||||
Type string `json:"type" example:"land"`
|
||||
ID string `json:"id" example:"018f3a5c-7a6f-7c2a-8d4a-4b5b1f6c8e9d"`
|
||||
Attributes LandAttributes `json:"attributes"`
|
||||
}
|
||||
|
||||
type LandResponse struct {
|
||||
Data LandResource `json:"data"`
|
||||
}
|
||||
|
||||
type LandListResponse struct {
|
||||
Data []LandResource `json:"data"`
|
||||
Meta struct {
|
||||
PageNumber int `json:"page_number" example:"1"`
|
||||
PageSize int `json:"page_size" example:"20"`
|
||||
Total int64 `json:"total" example:"120"`
|
||||
} `json:"meta"`
|
||||
}
|
||||
|
||||
type LandDataTableResponse struct {
|
||||
Data []LandResource `json:"data"`
|
||||
Meta struct {
|
||||
Draw int `json:"draw" example:"1"`
|
||||
RecordsTotal int64 `json:"records_total" example:"120"`
|
||||
RecordsFiltered int64 `json:"records_filtered" example:"12"`
|
||||
} `json:"meta"`
|
||||
}
|
||||
|
||||
type LandCreateAttributes struct {
|
||||
Name string `json:"name" validate:"required" example:"Germany"`
|
||||
Note *string `json:"note,omitempty" example:"Central Europe"`
|
||||
ISOCode string `json:"iso_code,omitempty" example:"DE"`
|
||||
BMDExportID *string `json:"bmd_export_id,omitempty" example:"DE001"`
|
||||
SortKey *int `json:"sortkey" example:"1"`
|
||||
IsActive *bool `json:"is_active,omitempty" example:"true"`
|
||||
}
|
||||
|
||||
type LandCreateData struct {
|
||||
Type string `json:"type" validate:"required,oneof=land_create land" example:"land_create"`
|
||||
Attributes LandCreateAttributes `json:"attributes" validate:"required"`
|
||||
}
|
||||
|
||||
type LandCreateRequest struct {
|
||||
Data LandCreateData `json:"data" validate:"required"`
|
||||
}
|
||||
|
||||
type LandUpdateAttributes struct {
|
||||
Name *string `json:"name,omitempty" example:"Germany"`
|
||||
Note *string `json:"note,omitempty" example:"Central Europe"`
|
||||
ISOCode *string `json:"iso_code,omitempty" example:"DE"`
|
||||
BMDExportID *string `json:"bmd_export_id,omitempty" example:"DE001"`
|
||||
SortKey NullInt `json:"sortkey,omitempty" swaggertype:"integer" example:"1"`
|
||||
IsActive *bool `json:"is_active,omitempty" example:"true"`
|
||||
}
|
||||
|
||||
type LandUpdateData struct {
|
||||
Type string `json:"type" validate:"required,oneof=land_update land" example:"land_update"`
|
||||
ID string `json:"id" example:"018f3a5c-7a6f-7c2a-8d4a-4b5b1f6c8e9d"`
|
||||
Attributes LandUpdateAttributes `json:"attributes" validate:"required"`
|
||||
}
|
||||
|
||||
type LandUpdateRequest struct {
|
||||
Data LandUpdateData `json:"data" validate:"required"`
|
||||
}
|
||||
263
internal/transport/http/dto/medicine_dto.go
Normal file
263
internal/transport/http/dto/medicine_dto.go
Normal file
@@ -0,0 +1,263 @@
|
||||
package dto
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
)
|
||||
|
||||
// JSON:API Medicine DTOs
|
||||
|
||||
type MedicineAttributes struct {
|
||||
Name string `json:"name" example:"Paracetamol"`
|
||||
SortKey *int `json:"sortkey" example:"1"`
|
||||
Note string `json:"note,omitempty" example:"Main stock medicine"`
|
||||
Pack string `json:"pack,omitempty" example:"500mg"`
|
||||
Unit string `json:"unit,omitempty" example:"tablet"`
|
||||
Column int64 `json:"column" example:"1"`
|
||||
MedicineGroupID string `json:"medicine_group_id,omitempty" example:"018f3a5c-7a6f-7c2a-8d4a-4b5b1f6c8e9d"`
|
||||
MotorReactionID string `json:"motor_reaction_id,omitempty" example:"018f3a5c-7a6f-7c2a-8d4a-4b5b1f6c8e9d"`
|
||||
MotorReactionIDs []string `json:"motor_reaction_ids,omitempty" example:"018f3a5c-7a6f-7c2a-8d4a-4b5b1f6c8e9d"`
|
||||
MedicineGroup *MedicineGroupResource `json:"medicine_group,omitempty"`
|
||||
MotorReaction *MotorReactionResource `json:"motor_reaction,omitempty"`
|
||||
MotorReactions []MotorReactionResource `json:"motor_reactions,omitempty"`
|
||||
IsActive bool `json:"is_active" example:"true"`
|
||||
CreatedAt string `json:"created_at,omitempty" example:"2026-03-12T03:04:05Z"`
|
||||
CreatedBy string `json:"created_by" example:"018f3a5c-7a6f-7c2a-8d4a-4b5b1f6c8e9d"`
|
||||
UpdatedAt string `json:"updated_at,omitempty" example:"2026-03-12T03:04:05Z"`
|
||||
UpdatedBy string `json:"updated_by" example:"018f3a5c-7a6f-7c2a-8d4a-4b5b1f6c8e9d"`
|
||||
DeletedAt string `json:"deleted_at,omitempty" example:"2026-03-12T03:04:05Z"`
|
||||
DeletedBy string `json:"deleted_by,omitempty" example:"018f3a5c-7a6f-7c2a-8d4a-4b5b1f6c8e9d"`
|
||||
}
|
||||
|
||||
type MedicineResource struct {
|
||||
Type string `json:"type" example:"medicine"`
|
||||
ID string `json:"id" example:"018f3a5c-7a6f-7c2a-8d4a-4b5b1f6c8e9d"`
|
||||
Attributes MedicineAttributes `json:"attributes"`
|
||||
}
|
||||
|
||||
type MedicineResponse struct {
|
||||
Data MedicineResource `json:"data"`
|
||||
}
|
||||
|
||||
type MedicineListResponse struct {
|
||||
Data []MedicineResource `json:"data"`
|
||||
Meta struct {
|
||||
PageNumber int `json:"page_number" example:"1"`
|
||||
PageSize int `json:"page_size" example:"20"`
|
||||
Total int64 `json:"total" example:"120"`
|
||||
} `json:"meta"`
|
||||
}
|
||||
|
||||
type MedicineDataTableResponse struct {
|
||||
Data []MedicineResource `json:"data"`
|
||||
Meta struct {
|
||||
Draw int `json:"draw" example:"1"`
|
||||
RecordsTotal int64 `json:"records_total" example:"120"`
|
||||
RecordsFiltered int64 `json:"records_filtered" example:"12"`
|
||||
} `json:"meta"`
|
||||
}
|
||||
|
||||
type MedicineCreateAttributes struct {
|
||||
Name string `json:"name" validate:"required" example:"Paracetamol"`
|
||||
SortKey *int `json:"sortkey" example:"1"`
|
||||
Note *string `json:"note,omitempty" example:"Main stock medicine"`
|
||||
Pack *string `json:"pack,omitempty" example:"500mg"`
|
||||
Unit *string `json:"unit,omitempty" validate:"omitempty,max=50" example:"tablet"`
|
||||
Column *int64 `json:"column,omitempty" example:"1"`
|
||||
MedicineGroupID *string `json:"medicine_group_id,omitempty" example:"018f3a5c-7a6f-7c2a-8d4a-4b5b1f6c8e9d"`
|
||||
MotorReactionID *string `json:"motor_reaction_id,omitempty" example:"018f3a5c-7a6f-7c2a-8d4a-4b5b1f6c8e9d"`
|
||||
MotorReactionIDs []string `json:"motor_reaction_ids,omitempty" example:"018f3a5c-7a6f-7c2a-8d4a-4b5b1f6c8e9d"`
|
||||
IsActive *bool `json:"is_active,omitempty" example:"true"`
|
||||
}
|
||||
|
||||
type MedicineCreateData struct {
|
||||
Type string `json:"type" validate:"required,oneof=medicine_create medicine" example:"medicine_create"`
|
||||
Attributes MedicineCreateAttributes `json:"attributes" validate:"required"`
|
||||
}
|
||||
|
||||
type MedicineCreateRequest struct {
|
||||
Data MedicineCreateData `json:"data" validate:"required"`
|
||||
}
|
||||
|
||||
type MedicineUpdateAttributes struct {
|
||||
Name *string `json:"name,omitempty" example:"Paracetamol"`
|
||||
SortKey NullInt `json:"sortkey,omitempty" swaggertype:"integer" example:"1"`
|
||||
Note *string `json:"note,omitempty" example:"Main stock medicine"`
|
||||
Pack *string `json:"pack,omitempty" example:"500mg"`
|
||||
Unit *string `json:"unit,omitempty" validate:"omitempty,max=50" example:"tablet"`
|
||||
Column *int64 `json:"column,omitempty" example:"1"`
|
||||
MedicineGroupID NullString `json:"medicine_group_id,omitempty" swaggertype:"string" example:"018f3a5c-7a6f-7c2a-8d4a-4b5b1f6c8e9d"`
|
||||
MotorReactionID NullString `json:"motor_reaction_id,omitempty" swaggertype:"string" example:"018f3a5c-7a6f-7c2a-8d4a-4b5b1f6c8e9d"`
|
||||
MotorReactionIDs []string `json:"motor_reaction_ids,omitempty" example:"018f3a5c-7a6f-7c2a-8d4a-4b5b1f6c8e9d"`
|
||||
IsActive *bool `json:"is_active,omitempty" example:"true"`
|
||||
}
|
||||
|
||||
type MedicineUpdateData struct {
|
||||
Type string `json:"type" validate:"required,oneof=medicine_update medicine" example:"medicine_update"`
|
||||
ID string `json:"id" example:"018f3a5c-7a6f-7c2a-8d4a-4b5b1f6c8e9d"`
|
||||
Attributes MedicineUpdateAttributes `json:"attributes" validate:"required"`
|
||||
}
|
||||
|
||||
type MedicineUpdateRequest struct {
|
||||
Data MedicineUpdateData `json:"data" validate:"required"`
|
||||
}
|
||||
|
||||
type MedicineGroupAttributes struct {
|
||||
Name string `json:"name" example:"Spritze"`
|
||||
CreatedAt string `json:"created_at,omitempty" example:"2026-03-12T03:04:05Z"`
|
||||
CreatedBy string `json:"created_by" example:"018f3a5c-7a6f-7c2a-8d4a-4b5b1f6c8e9d"`
|
||||
UpdatedAt string `json:"updated_at,omitempty" example:"2026-03-12T03:04:05Z"`
|
||||
UpdatedBy string `json:"updated_by" example:"018f3a5c-7a6f-7c2a-8d4a-4b5b1f6c8e9d"`
|
||||
DeletedAt string `json:"deleted_at,omitempty" example:"2026-03-12T03:04:05Z"`
|
||||
DeletedBy string `json:"deleted_by,omitempty" example:"018f3a5c-7a6f-7c2a-8d4a-4b5b1f6c8e9d"`
|
||||
}
|
||||
|
||||
type MedicineGroupResource struct {
|
||||
Type string `json:"type" example:"medicine_group"`
|
||||
ID string `json:"id" example:"018f3a5c-7a6f-7c2a-8d4a-4b5b1f6c8e9d"`
|
||||
Attributes MedicineGroupAttributes `json:"attributes"`
|
||||
}
|
||||
|
||||
type MedicineGroupResponse struct {
|
||||
Data MedicineGroupResource `json:"data"`
|
||||
}
|
||||
|
||||
type MedicineGroupListResponse struct {
|
||||
Data []MedicineGroupResource `json:"data"`
|
||||
Meta struct {
|
||||
PageNumber int `json:"page_number" example:"1"`
|
||||
PageSize int `json:"page_size" example:"20"`
|
||||
Total int64 `json:"total" example:"120"`
|
||||
} `json:"meta"`
|
||||
}
|
||||
|
||||
type MedicineGroupDataTableResponse struct {
|
||||
Data []MedicineGroupResource `json:"data"`
|
||||
Meta struct {
|
||||
Draw int `json:"draw" example:"1"`
|
||||
RecordsTotal int64 `json:"records_total" example:"120"`
|
||||
RecordsFiltered int64 `json:"records_filtered" example:"12"`
|
||||
} `json:"meta"`
|
||||
}
|
||||
|
||||
type MedicineGroupCreateAttributes struct {
|
||||
Name string `json:"name" validate:"required" example:"Spritze"`
|
||||
}
|
||||
|
||||
type MedicineGroupCreateData struct {
|
||||
Type string `json:"type" validate:"required,oneof=medicine_group_create medicine_group" example:"medicine_group_create"`
|
||||
Attributes MedicineGroupCreateAttributes `json:"attributes" validate:"required"`
|
||||
}
|
||||
|
||||
type MedicineGroupCreateRequest struct {
|
||||
Data MedicineGroupCreateData `json:"data" validate:"required"`
|
||||
}
|
||||
|
||||
type MedicineGroupUpdateAttributes struct {
|
||||
Name *string `json:"name,omitempty" example:"Spritze"`
|
||||
}
|
||||
|
||||
type MedicineGroupUpdateData struct {
|
||||
Type string `json:"type" validate:"required,oneof=medicine_group_update medicine_group" example:"medicine_group_update"`
|
||||
ID string `json:"id" example:"018f3a5c-7a6f-7c2a-8d4a-4b5b1f6c8e9d"`
|
||||
Attributes MedicineGroupUpdateAttributes `json:"attributes" validate:"required"`
|
||||
}
|
||||
|
||||
type MedicineGroupUpdateRequest struct {
|
||||
Data MedicineGroupUpdateData `json:"data" validate:"required"`
|
||||
}
|
||||
|
||||
type MotorReactionAttributes struct {
|
||||
Name string `json:"name" example:"gezielt auf Schmerzreiz"`
|
||||
Score *int64 `json:"score" example:"5"`
|
||||
CreatedAt string `json:"created_at,omitempty" example:"2026-03-12T03:04:05Z"`
|
||||
CreatedBy string `json:"created_by" example:"018f3a5c-7a6f-7c2a-8d4a-4b5b1f6c8e9d"`
|
||||
UpdatedAt string `json:"updated_at,omitempty" example:"2026-03-12T03:04:05Z"`
|
||||
UpdatedBy string `json:"updated_by" example:"018f3a5c-7a6f-7c2a-8d4a-4b5b1f6c8e9d"`
|
||||
DeletedAt string `json:"deleted_at,omitempty" example:"2026-03-12T03:04:05Z"`
|
||||
DeletedBy string `json:"deleted_by,omitempty" example:"018f3a5c-7a6f-7c2a-8d4a-4b5b1f6c8e9d"`
|
||||
}
|
||||
|
||||
type MotorReactionResource struct {
|
||||
Type string `json:"type" example:"motor_reaction"`
|
||||
ID string `json:"id" example:"018f3a5c-7a6f-7c2a-8d4a-4b5b1f6c8e9d"`
|
||||
Attributes MotorReactionAttributes `json:"attributes"`
|
||||
}
|
||||
|
||||
type MotorReactionResponse struct {
|
||||
Data MotorReactionResource `json:"data"`
|
||||
}
|
||||
|
||||
type MotorReactionListResponse struct {
|
||||
Data []MotorReactionResource `json:"data"`
|
||||
Meta struct {
|
||||
PageNumber int `json:"page_number" example:"1"`
|
||||
PageSize int `json:"page_size" example:"20"`
|
||||
Total int64 `json:"total" example:"120"`
|
||||
} `json:"meta"`
|
||||
}
|
||||
|
||||
type MotorReactionDataTableResponse struct {
|
||||
Data []MotorReactionResource `json:"data"`
|
||||
Meta struct {
|
||||
Draw int `json:"draw" example:"1"`
|
||||
RecordsTotal int64 `json:"records_total" example:"120"`
|
||||
RecordsFiltered int64 `json:"records_filtered" example:"12"`
|
||||
} `json:"meta"`
|
||||
}
|
||||
|
||||
type MotorReactionCreateAttributes struct {
|
||||
Name string `json:"name" validate:"required" example:"gezielt auf Schmerzreiz"`
|
||||
Score *int64 `json:"score,omitempty" example:"5"`
|
||||
}
|
||||
|
||||
type MotorReactionCreateData struct {
|
||||
Type string `json:"type" validate:"required,oneof=motor_reaction_create motor_reaction" example:"motor_reaction_create"`
|
||||
Attributes MotorReactionCreateAttributes `json:"attributes" validate:"required"`
|
||||
}
|
||||
|
||||
type MotorReactionCreateRequest struct {
|
||||
Data MotorReactionCreateData `json:"data" validate:"required"`
|
||||
}
|
||||
|
||||
type MotorReactionUpdateAttributes struct {
|
||||
Name *string `json:"name,omitempty" example:"gezielt auf Schmerzreiz"`
|
||||
Score NullInt64 `json:"score,omitempty" swaggertype:"integer" example:"5"`
|
||||
}
|
||||
|
||||
// NullInt64 allows PATCH semantics:
|
||||
// - field omitted => Set=false (no change)
|
||||
// - value null => Set=true, Valid=false (set DB NULL)
|
||||
// - value number => Set=true, Valid=true, Value=<number>
|
||||
type NullInt64 struct {
|
||||
Set bool
|
||||
Valid bool
|
||||
Value int64
|
||||
}
|
||||
|
||||
func (n *NullInt64) UnmarshalJSON(data []byte) error {
|
||||
n.Set = true
|
||||
trimmed := bytes.TrimSpace(data)
|
||||
if bytes.Equal(trimmed, []byte("null")) {
|
||||
n.Valid = false
|
||||
n.Value = 0
|
||||
return nil
|
||||
}
|
||||
var v int64
|
||||
if err := json.Unmarshal(trimmed, &v); err != nil {
|
||||
return err
|
||||
}
|
||||
n.Valid = true
|
||||
n.Value = v
|
||||
return nil
|
||||
}
|
||||
|
||||
type MotorReactionUpdateData struct {
|
||||
Type string `json:"type" validate:"required,oneof=motor_reaction_update motor_reaction" example:"motor_reaction_update"`
|
||||
ID string `json:"id" example:"018f3a5c-7a6f-7c2a-8d4a-4b5b1f6c8e9d"`
|
||||
Attributes MotorReactionUpdateAttributes `json:"attributes" validate:"required"`
|
||||
}
|
||||
|
||||
type MotorReactionUpdateRequest struct {
|
||||
Data MotorReactionUpdateData `json:"data" validate:"required"`
|
||||
}
|
||||
33
internal/transport/http/dto/null_int.go
Normal file
33
internal/transport/http/dto/null_int.go
Normal file
@@ -0,0 +1,33 @@
|
||||
package dto
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
)
|
||||
|
||||
// NullInt allows PATCH semantics:
|
||||
// - field omitted => Set=false (no change)
|
||||
// - value null => Set=true, Valid=false (set DB NULL)
|
||||
// - value number => Set=true, Valid=true, Value=<number>
|
||||
type NullInt struct {
|
||||
Set bool
|
||||
Valid bool
|
||||
Value int
|
||||
}
|
||||
|
||||
func (n *NullInt) UnmarshalJSON(data []byte) error {
|
||||
n.Set = true
|
||||
trimmed := bytes.TrimSpace(data)
|
||||
if bytes.Equal(trimmed, []byte("null")) {
|
||||
n.Valid = false
|
||||
n.Value = 0
|
||||
return nil
|
||||
}
|
||||
var v int
|
||||
if err := json.Unmarshal(trimmed, &v); err != nil {
|
||||
return err
|
||||
}
|
||||
n.Valid = true
|
||||
n.Value = v
|
||||
return nil
|
||||
}
|
||||
77
internal/transport/http/dto/opc_dto.go
Normal file
77
internal/transport/http/dto/opc_dto.go
Normal file
@@ -0,0 +1,77 @@
|
||||
package dto
|
||||
|
||||
// JSON:API OPC DTOs
|
||||
|
||||
type OpcAttributes struct {
|
||||
Title string `json:"title" example:"Main OPC Base"`
|
||||
SortKey *int `json:"sortkey" example:"1"`
|
||||
Note string `json:"note,omitempty" example:"Primary operation control center"`
|
||||
IsActive bool `json:"is_active" example:"true"`
|
||||
CreatedAt string `json:"created_at,omitempty" example:"2026-03-12T03:04:05Z"`
|
||||
CreatedBy string `json:"created_by" example:"018f3a5c-7a6f-7c2a-8d4a-4b5b1f6c8e9d"`
|
||||
UpdatedAt string `json:"updated_at,omitempty" example:"2026-03-12T03:04:05Z"`
|
||||
UpdatedBy string `json:"updated_by" example:"018f3a5c-7a6f-7c2a-8d4a-4b5b1f6c8e9d"`
|
||||
DeletedAt string `json:"deleted_at,omitempty" example:"2026-03-12T03:04:05Z"`
|
||||
DeletedBy string `json:"deleted_by,omitempty" example:"018f3a5c-7a6f-7c2a-8d4a-4b5b1f6c8e9d"`
|
||||
}
|
||||
|
||||
type OpcResource struct {
|
||||
Type string `json:"type" example:"opc"`
|
||||
ID string `json:"id" example:"018f3a5c-7a6f-7c2a-8d4a-4b5b1f6c8e9d"`
|
||||
Attributes OpcAttributes `json:"attributes"`
|
||||
}
|
||||
|
||||
type OpcResponse struct {
|
||||
Data OpcResource `json:"data"`
|
||||
}
|
||||
|
||||
type OpcListResponse struct {
|
||||
Data []OpcResource `json:"data"`
|
||||
Meta struct {
|
||||
PageNumber int `json:"page_number" example:"1"`
|
||||
PageSize int `json:"page_size" example:"20"`
|
||||
Total int64 `json:"total" example:"120"`
|
||||
} `json:"meta"`
|
||||
}
|
||||
|
||||
type OpcDataTableResponse struct {
|
||||
Data []OpcResource `json:"data"`
|
||||
Meta struct {
|
||||
Draw int `json:"draw" example:"1"`
|
||||
RecordsTotal int64 `json:"records_total" example:"120"`
|
||||
RecordsFiltered int64 `json:"records_filtered" example:"12"`
|
||||
} `json:"meta"`
|
||||
}
|
||||
|
||||
type OpcCreateAttributes struct {
|
||||
Title string `json:"title" validate:"required" example:"Main OPC Base"`
|
||||
SortKey *int `json:"sortkey" example:"1"`
|
||||
Note *string `json:"note,omitempty" example:"Primary operation control center"`
|
||||
IsActive *bool `json:"is_active,omitempty" example:"true"`
|
||||
}
|
||||
|
||||
type OpcCreateData struct {
|
||||
Type string `json:"type" validate:"required,oneof=opc_create opc" example:"opc_create"`
|
||||
Attributes OpcCreateAttributes `json:"attributes" validate:"required"`
|
||||
}
|
||||
|
||||
type OpcCreateRequest struct {
|
||||
Data OpcCreateData `json:"data" validate:"required"`
|
||||
}
|
||||
|
||||
type OpcUpdateAttributes struct {
|
||||
Title *string `json:"title,omitempty" example:"Main OPC Base"`
|
||||
SortKey NullInt `json:"sortkey,omitempty" swaggertype:"integer" example:"1"`
|
||||
Note *string `json:"note,omitempty" example:"Primary operation control center"`
|
||||
IsActive *bool `json:"is_active,omitempty" example:"true"`
|
||||
}
|
||||
|
||||
type OpcUpdateData struct {
|
||||
Type string `json:"type" validate:"required,oneof=opc_update opc" example:"opc_update"`
|
||||
ID string `json:"id" example:"018f3a5c-7a6f-7c2a-8d4a-4b5b1f6c8e9d"`
|
||||
Attributes OpcUpdateAttributes `json:"attributes" validate:"required"`
|
||||
}
|
||||
|
||||
type OpcUpdateRequest struct {
|
||||
Data OpcUpdateData `json:"data" validate:"required"`
|
||||
}
|
||||
114
internal/transport/http/dto/patient_data_dto.go
Normal file
114
internal/transport/http/dto/patient_data_dto.go
Normal file
@@ -0,0 +1,114 @@
|
||||
package dto
|
||||
|
||||
// JSON:API PatientData DTOs
|
||||
|
||||
type PatientDataOPC struct {
|
||||
ID string `json:"id,omitempty" example:"018f3a5c-7a6f-7c2a-8d4a-4b5b1f6c8e9d"`
|
||||
Title string `json:"title,omitempty" example:"Primary OPC"`
|
||||
}
|
||||
|
||||
type PatientDataInsuranceDetail struct {
|
||||
ID string `json:"id,omitempty" example:"018f3a5c-7a6f-7c2a-8d4a-4b5b1f6c8e9d"`
|
||||
HealthInsuranceCompanyID string `json:"health_insurance_companies_id,omitempty" example:"018f3a5c-7a6f-7c2a-8d4a-4b5b1f6c8e9d"`
|
||||
FederalStateID string `json:"federal_state_id,omitempty" example:"018f3a5c-7a6f-7c2a-8d4a-4b5b1f6c8e9d"`
|
||||
HealthInsuranceCompany *InsurancePatientDataHealthInsuranceCompany `json:"health_insurance_company,omitempty"`
|
||||
FederalState *InsurancePatientDataFederalState `json:"federal_state,omitempty"`
|
||||
}
|
||||
|
||||
type PatientDataAttributes struct {
|
||||
FamilyName string `json:"family_name" example:"Muster"`
|
||||
FirstName string `json:"first_name" example:"Max"`
|
||||
OPCId string `json:"opc_id,omitempty" example:"018f3a5c-7a6f-7c2a-8d4a-4b5b1f6c8e9d"`
|
||||
BirthDate string `json:"birth_date,omitempty" example:"1990-01-15"`
|
||||
Age *int `json:"age,omitempty" example:"35"`
|
||||
Gender string `json:"gender" example:"male"`
|
||||
Street string `json:"street,omitempty" example:"Hauptstraße 1"`
|
||||
SVNR string `json:"svnr,omitempty" example:"1234150190"`
|
||||
Email string `json:"email,omitempty" example:"max.muster@example.com"`
|
||||
Phone string `json:"phone,omitempty" example:"+431234567"`
|
||||
CoInsured bool `json:"co_insured" example:"false"`
|
||||
InsurancePatientDataId string `json:"insurance_patient_data_id,omitempty" example:"018f3a5c-7a6f-7c2a-8d4a-4b5b1f6c8e9d"`
|
||||
OPC *PatientDataOPC `json:"opc,omitempty"`
|
||||
InsurancePatientData *PatientDataInsuranceDetail `json:"insurance_patient_data,omitempty"`
|
||||
CreatedAt string `json:"created_at,omitempty" example:"2026-03-12T03:04:05Z"`
|
||||
CreatedBy string `json:"created_by,omitempty" example:"018f3a5c-7a6f-7c2a-8d4a-4b5b1f6c8e9d"`
|
||||
UpdatedAt string `json:"updated_at,omitempty" example:"2026-03-12T03:04:05Z"`
|
||||
UpdatedBy string `json:"updated_by,omitempty" example:"018f3a5c-7a6f-7c2a-8d4a-4b5b1f6c8e9d"`
|
||||
DeletedAt string `json:"deleted_at,omitempty" example:"2026-03-12T03:04:05Z"`
|
||||
DeletedBy string `json:"deleted_by,omitempty" example:"018f3a5c-7a6f-7c2a-8d4a-4b5b1f6c8e9d"`
|
||||
}
|
||||
|
||||
type PatientDataResource struct {
|
||||
Type string `json:"type" example:"patient_data"`
|
||||
ID string `json:"id" example:"018f3a5c-7a6f-7c2a-8d4a-4b5b1f6c8e9d"`
|
||||
Attributes PatientDataAttributes `json:"attributes"`
|
||||
}
|
||||
|
||||
type PatientDataResponse struct {
|
||||
Data PatientDataResource `json:"data"`
|
||||
}
|
||||
|
||||
type PatientDataListResponse struct {
|
||||
Data []PatientDataResource `json:"data"`
|
||||
Meta struct {
|
||||
PageNumber int `json:"page_number" example:"1"`
|
||||
PageSize int `json:"page_size" example:"20"`
|
||||
Total int64 `json:"total" example:"120"`
|
||||
} `json:"meta"`
|
||||
}
|
||||
|
||||
type PatientDataDataTableResponse struct {
|
||||
Data []PatientDataResource `json:"data"`
|
||||
Meta struct {
|
||||
Draw int `json:"draw" example:"1"`
|
||||
RecordsTotal int64 `json:"records_total" example:"120"`
|
||||
RecordsFiltered int64 `json:"records_filtered" example:"12"`
|
||||
} `json:"meta"`
|
||||
}
|
||||
|
||||
type PatientDataCreateAttributes struct {
|
||||
FamilyName string `json:"family_name" validate:"required" example:"Muster"`
|
||||
FirstName string `json:"first_name" validate:"required" example:"Max"`
|
||||
OPCId *string `json:"opc_id,omitempty" example:"018f3a5c-7a6f-7c2a-8d4a-4b5b1f6c8e9d"`
|
||||
BirthDate *string `json:"birth_date,omitempty" example:"1990-01-15"`
|
||||
Gender string `json:"gender" validate:"required,oneof=male female" example:"male"`
|
||||
Street *string `json:"street,omitempty" example:"Hauptstraße 1"`
|
||||
SVNR *string `json:"svnr,omitempty" example:"1234150190"`
|
||||
Email *string `json:"email,omitempty" example:"max.muster@example.com"`
|
||||
Phone *string `json:"phone,omitempty" example:"+431234567"`
|
||||
CoInsured *bool `json:"co_insured,omitempty" example:"false"`
|
||||
InsurancePatientDataId *string `json:"insurance_patient_data_id,omitempty" example:"018f3a5c-7a6f-7c2a-8d4a-4b5b1f6c8e9d"`
|
||||
}
|
||||
|
||||
type PatientDataCreateData struct {
|
||||
Type string `json:"type" validate:"required,oneof=patient_data_create patient_data" example:"patient_data_create"`
|
||||
Attributes PatientDataCreateAttributes `json:"attributes" validate:"required"`
|
||||
}
|
||||
|
||||
type PatientDataCreateRequest struct {
|
||||
Data PatientDataCreateData `json:"data" validate:"required"`
|
||||
}
|
||||
|
||||
type PatientDataUpdateAttributes struct {
|
||||
FamilyName *string `json:"family_name,omitempty" example:"Muster"`
|
||||
FirstName *string `json:"first_name,omitempty" example:"Max"`
|
||||
OPCId *string `json:"opc_id,omitempty" example:"018f3a5c-7a6f-7c2a-8d4a-4b5b1f6c8e9d"`
|
||||
BirthDate *string `json:"birth_date,omitempty" example:"1990-01-15"`
|
||||
Gender *string `json:"gender,omitempty" example:"male"`
|
||||
Street *string `json:"street,omitempty" example:"Hauptstraße 1"`
|
||||
SVNR *string `json:"svnr,omitempty" example:"1234150190"`
|
||||
Email *string `json:"email,omitempty" example:"max.muster@example.com"`
|
||||
Phone *string `json:"phone,omitempty" example:"+431234567"`
|
||||
CoInsured *bool `json:"co_insured,omitempty" example:"false"`
|
||||
InsurancePatientDataId *string `json:"insurance_patient_data_id,omitempty" example:"018f3a5c-7a6f-7c2a-8d4a-4b5b1f6c8e9d"`
|
||||
}
|
||||
|
||||
type PatientDataUpdateData struct {
|
||||
Type string `json:"type" validate:"required,oneof=patient_data_update patient_data" example:"patient_data_update"`
|
||||
ID string `json:"id" example:"018f3a5c-7a6f-7c2a-8d4a-4b5b1f6c8e9d"`
|
||||
Attributes PatientDataUpdateAttributes `json:"attributes" validate:"required"`
|
||||
}
|
||||
|
||||
type PatientDataUpdateRequest struct {
|
||||
Data PatientDataUpdateData `json:"data" validate:"required"`
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
package request
|
||||
|
||||
// ActionSignoffSignAttributes — optional fields for the sign-off.
|
||||
// - ComplaintID: when set, the sign-off marks that complaint's corrective action as
|
||||
// signed off (the complaint must already have action_taken recorded, else 422).
|
||||
// - When ComplaintID is omitted it is a complaint-independent (fleet) sign-off, which
|
||||
// still allows an EASA release.
|
||||
type ActionSignoffSignAttributes struct {
|
||||
// Sign toggles the sign-off: true (or omitted) signs it, false unsigns (clears the
|
||||
// signature). Combine with complaint_id to toggle a specific complaint's sign-off.
|
||||
Sign *bool `json:"sign,omitempty" example:"true"`
|
||||
ComplaintID *string `json:"complaint_id,omitempty" example:"019d6772-471c-7b70-b42a-d94020ef61e4"`
|
||||
// FlightID — REQUIRED for the fleet (complaint-independent) sign-off; scopes it to a
|
||||
// flight so it no longer leaks across takeovers of the same aircraft. Ignored when
|
||||
// complaint_id is set (that sign-off is keyed by the complaint).
|
||||
FlightID *string `json:"flight_id,omitempty" example:"019d6774-98e8-7fba-9ef4-3795c0da8a13"`
|
||||
}
|
||||
|
||||
type ActionSignoffSignData struct {
|
||||
Type string `json:"type,omitempty" example:"action_signoff"`
|
||||
Attributes ActionSignoffSignAttributes `json:"attributes"`
|
||||
}
|
||||
|
||||
// ActionSignoffSignRequest — body is OPTIONAL for the sign endpoint.
|
||||
type ActionSignoffSignRequest struct {
|
||||
Data ActionSignoffSignData `json:"data"`
|
||||
}
|
||||
@@ -0,0 +1,87 @@
|
||||
package request
|
||||
|
||||
import shareddto "wucher/internal/transport/http/dto/shared"
|
||||
|
||||
type AirRescuerChecklistCreateEntry struct {
|
||||
ScopeCode string `json:"scope_code" validate:"required" example:"MO"`
|
||||
Title string `json:"title" validate:"required" example:"Monday Checklist"`
|
||||
Position *int `json:"position,omitempty" example:"1"`
|
||||
Items []shareddto.AirRescuerChecklistItemInput `json:"items,omitempty" validate:"omitempty,dive"`
|
||||
}
|
||||
|
||||
type AirRescuerChecklistCreateAttributes struct {
|
||||
BaseID string `json:"base_id" validate:"required" example:"018f3a5c-7a6f-7c2a-8d4a-4b5b1f6c8e9d"`
|
||||
Checklists []AirRescuerChecklistCreateEntry `json:"checklists" validate:"required,min=1,dive"`
|
||||
}
|
||||
|
||||
type AirRescuerChecklistCreateData struct {
|
||||
Type string `json:"type" validate:"required,oneof=air_rescuer_checklist_create"`
|
||||
Attributes AirRescuerChecklistCreateAttributes `json:"attributes" validate:"required"`
|
||||
}
|
||||
|
||||
type AirRescuerChecklistCreateRequest struct {
|
||||
Data AirRescuerChecklistCreateData `json:"data" validate:"required"`
|
||||
}
|
||||
|
||||
type AirRescuerChecklistUpdateAttributes struct {
|
||||
ScopeCode *string `json:"scope_code,omitempty" example:"MO"`
|
||||
Title *string `json:"title,omitempty" example:"Monday Checklist Updated"`
|
||||
Position *int `json:"position,omitempty" example:"1"`
|
||||
NewItems []shareddto.AirRescuerChecklistItemInput `json:"new_items,omitempty" validate:"omitempty,dive"`
|
||||
}
|
||||
|
||||
type AirRescuerChecklistUpdateData struct {
|
||||
Type string `json:"type" validate:"required,oneof=air_rescuer_checklist_update"`
|
||||
ID string `json:"id" example:"018f3a5c-7a6f-7c2a-8d4a-4b5b1f6c8e9d"`
|
||||
Attributes AirRescuerChecklistUpdateAttributes `json:"attributes" validate:"required"`
|
||||
}
|
||||
|
||||
type AirRescuerChecklistUpdateRequest struct {
|
||||
Data AirRescuerChecklistUpdateData `json:"data" validate:"required"`
|
||||
}
|
||||
|
||||
type AirRescuerChecklistItemCreateAttributes struct {
|
||||
Items []shareddto.AirRescuerChecklistItemInput `json:"items" validate:"required,min=1,dive"`
|
||||
}
|
||||
|
||||
type AirRescuerChecklistItemCreateData struct {
|
||||
Type string `json:"type" validate:"required,oneof=air_rescuer_checklist_item_create"`
|
||||
Attributes AirRescuerChecklistItemCreateAttributes `json:"attributes" validate:"required"`
|
||||
}
|
||||
|
||||
type AirRescuerChecklistItemCreateRequest struct {
|
||||
Data AirRescuerChecklistItemCreateData `json:"data" validate:"required"`
|
||||
}
|
||||
|
||||
type AirRescuerChecklistItemUpdateAttributes struct {
|
||||
Name *string `json:"name,omitempty" example:"Check radio"`
|
||||
Position *int `json:"position,omitempty" example:"2"`
|
||||
}
|
||||
|
||||
type AirRescuerChecklistItemUpdateData struct {
|
||||
Type string `json:"type" validate:"required,oneof=air_rescuer_checklist_item_update"`
|
||||
ID string `json:"id" example:"018f3a5c-7a6f-7c2a-8d4a-4b5b1f6c8e9d"`
|
||||
Attributes AirRescuerChecklistItemUpdateAttributes `json:"attributes" validate:"required"`
|
||||
}
|
||||
|
||||
type AirRescuerChecklistItemUpdateRequest struct {
|
||||
Data AirRescuerChecklistItemUpdateData `json:"data" validate:"required"`
|
||||
}
|
||||
|
||||
type AirRescuerChecklistItemReorderEntry struct {
|
||||
ItemUUID string `json:"item_uuid" validate:"required" example:"018f3a5c-7a6f-7c2a-8d4a-4b5b1f6c8e9d"`
|
||||
Position *int `json:"position" validate:"required" example:"2"`
|
||||
}
|
||||
|
||||
type AirRescuerChecklistItemReorderAttributes struct {
|
||||
Items []AirRescuerChecklistItemReorderEntry `json:"items" validate:"required,min=1,dive"`
|
||||
}
|
||||
|
||||
type AirRescuerChecklistItemReorderData struct {
|
||||
Type string `json:"type" validate:"required,oneof=air_rescuer_checklist_item_reorder"`
|
||||
Attributes AirRescuerChecklistItemReorderAttributes `json:"attributes" validate:"required"`
|
||||
}
|
||||
|
||||
type AirRescuerChecklistItemReorderRequest struct {
|
||||
Data AirRescuerChecklistItemReorderData `json:"data" validate:"required"`
|
||||
}
|
||||
82
internal/transport/http/dto/request/complaint_request.go
Normal file
82
internal/transport/http/dto/request/complaint_request.go
Normal file
@@ -0,0 +1,82 @@
|
||||
package request
|
||||
|
||||
type ComplaintCreateAttributes struct {
|
||||
// HelicopterID — the helicopter this complaint is filed against. REQUIRED.
|
||||
HelicopterID string `json:"helicopter_id" validate:"required,uuid" example:"019d6771-5fb5-7337-837f-bc5ed85181a1"`
|
||||
// FlightID — the flight/report this complaint was filed in (attribution). OPTIONAL;
|
||||
// open-defect tracking stays per-helicopter so defects carry forward across flights.
|
||||
FlightID string `json:"flight_id,omitempty" validate:"omitempty,uuid" example:"019d6774-98e8-7fba-9ef4-3795c0da8a13"`
|
||||
// Description — the defect description. REQUIRED.
|
||||
Description string `json:"description" validate:"required" example:"Engine 2 N2 vibration intermittent during cruise above 110 KIAS"`
|
||||
// MELSeverity — MEL (Minimum Equipment List) classification. OPTIONAL at creation:
|
||||
// omit it to file the complaint as "pending_mel" and classify later. It MUST be
|
||||
// classified (via update) before recording action taken. A pending complaint does
|
||||
// NOT ground the aircraft — grounding starts at classification. Values: 0 = NON-MEL
|
||||
// (grounds immediately), 1 = A (grace 1 day), 2 = B (3 days), 3 = C (10 days), 4 = D (120 days).
|
||||
MELSeverity *int8 `json:"mel_severity,omitempty" validate:"omitempty,oneof=0 1 2 3 4" enums:"0,1,2,3,4" example:"2"`
|
||||
// AircraftHoursAtReport — OPTIONAL airframe hours recorded when the defect was reported.
|
||||
AircraftHoursAtReport *string `json:"aircraft_hours_at_report,omitempty" example:"1245.3"`
|
||||
// NSR is no longer set here — it is its own flow: POST /complaints/nsr/sign/{id}.
|
||||
}
|
||||
|
||||
type ComplaintCreateData struct {
|
||||
Type string `json:"type" validate:"required,oneof=complaint_create complaint" example:"complaint_create"`
|
||||
Attributes ComplaintCreateAttributes `json:"attributes" validate:"required"`
|
||||
}
|
||||
|
||||
type ComplaintCreateRequest struct {
|
||||
Data ComplaintCreateData `json:"data" validate:"required"`
|
||||
}
|
||||
|
||||
type ComplaintUpdateAttributes struct {
|
||||
// Description — defect description.
|
||||
Description *string `json:"description,omitempty" example:"Engine 2 vibration confirmed"`
|
||||
// MELSeverity — MEL classification. 0 = NON-MEL (immediate AOG), 1 = A (1 day),
|
||||
// 2 = B (3 days), 3 = C (10 days), 4 = D (120 days). AOG after grace passes.
|
||||
MELSeverity *int8 `json:"mel_severity,omitempty" validate:"omitempty,oneof=0 1 2 3 4" enums:"0,1,2,3,4" example:"2"`
|
||||
// AircraftHoursAtReport — airframe hours at report time.
|
||||
AircraftHoursAtReport *string `json:"aircraft_hours_at_report,omitempty" example:"1245.3"`
|
||||
// NSR is no longer set here — it is its own flow: POST /complaints/nsr/sign/{id}.
|
||||
}
|
||||
|
||||
// ComplaintNSRSignAttributes carries the (optional) justification for a Non-Safety
|
||||
// Release decision. NSR is signed via its own endpoint, separate from complaint update.
|
||||
type ComplaintNSRSignAttributes struct {
|
||||
// NSRReason — OPTIONAL free-text justification for the NSR (MEL deferral) decision.
|
||||
NSRReason *string `json:"nsr_reason,omitempty" example:"Released under MEL A, defect deferred"`
|
||||
}
|
||||
|
||||
type ComplaintNSRSignData struct {
|
||||
Type string `json:"type" validate:"omitempty,oneof=complaint_nsr_sign complaint_nsr" example:"complaint_nsr_sign"`
|
||||
Attributes ComplaintNSRSignAttributes `json:"attributes"`
|
||||
}
|
||||
|
||||
type ComplaintNSRSignRequest struct {
|
||||
Data ComplaintNSRSignData `json:"data"`
|
||||
}
|
||||
|
||||
type ComplaintUpdateData struct {
|
||||
Type string `json:"type" validate:"required,oneof=complaint_update complaint" example:"complaint_update"`
|
||||
ID string `json:"id" validate:"required" example:"019d6771-5fb5-7337-837f-bc5ed85181a1"`
|
||||
Attributes ComplaintUpdateAttributes `json:"attributes" validate:"required"`
|
||||
}
|
||||
|
||||
type ComplaintUpdateRequest struct {
|
||||
Data ComplaintUpdateData `json:"data" validate:"required"`
|
||||
}
|
||||
|
||||
type ComplaintActionTakenAttributes struct {
|
||||
// ActionTaken — corrective action text. Required when recording (create); on update an
|
||||
// empty value clears it (allowed only while the action has not been signed off).
|
||||
ActionTaken string `json:"action_taken" example:"Tightened engine mount and rechecked torque"`
|
||||
AircraftHoursAtFix *string `json:"aircraft_hours_at_fix,omitempty" example:"1250.1"`
|
||||
}
|
||||
|
||||
type ComplaintActionTakenData struct {
|
||||
Type string `json:"type" validate:"required,oneof=complaint_action_taken complaint" example:"complaint_action_taken"`
|
||||
Attributes ComplaintActionTakenAttributes `json:"attributes" validate:"required"`
|
||||
}
|
||||
|
||||
type ComplaintActionTakenRequest struct {
|
||||
Data ComplaintActionTakenData `json:"data" validate:"required"`
|
||||
}
|
||||
143
internal/transport/http/dto/request/duty_roster_request.go
Normal file
143
internal/transport/http/dto/request/duty_roster_request.go
Normal file
@@ -0,0 +1,143 @@
|
||||
package request
|
||||
|
||||
type DutyRosterShiftTime struct {
|
||||
ShiftStart string `json:"shift_start,omitempty" example:"06:00"`
|
||||
ShiftEnd string `json:"shift_end,omitempty" example:"21:00"`
|
||||
}
|
||||
|
||||
type DutyRosterShiftDate struct {
|
||||
DateStart string `json:"date_start,omitempty" example:"2026-03-16"`
|
||||
DateEnd string `json:"date_end,omitempty" example:"2026-03-16"`
|
||||
}
|
||||
|
||||
type DutyRosterCrewInput struct {
|
||||
ID string `json:"id,omitempty" example:"018f3a5c-7a6f-7c2a-8d4a-4b5b1f6c8e9d"`
|
||||
ShiftDate *DutyRosterShiftDate `json:"shift_date,omitempty"`
|
||||
}
|
||||
|
||||
type DutyRosterPilotCrewInput struct {
|
||||
ID string `json:"id,omitempty" example:"018f3a5c-7a6f-7c2a-8d4a-4b5b1f6c8e9d"`
|
||||
ShiftDate *DutyRosterShiftDate `json:"shift_date,omitempty"`
|
||||
FlightInstructor bool `json:"flight_instructor,omitempty"`
|
||||
LineChecker bool `json:"line_checker,omitempty"`
|
||||
Supervisor bool `json:"supervisor,omitempty"`
|
||||
Examiner bool `json:"examiner,omitempty"`
|
||||
CoPilot bool `json:"co_pilot,omitempty"`
|
||||
CrewType string `json:"crew_type,omitempty" example:"main" enums:"main,additional"`
|
||||
}
|
||||
|
||||
type DutyRosterOtherPersonInput struct {
|
||||
// OtherPersonID — OPTIONAL master person id to reuse (from GET /contact/other).
|
||||
// When set, name/mobile_phone/email are snapshotted from the master (may be omitted).
|
||||
OtherPersonID string `json:"other_person_id,omitempty" example:"019d6771-5fb5-7337-837f-bc5ed85181a1"`
|
||||
Name string `json:"name,omitempty" example:"Guest Person"`
|
||||
MobilePhone string `json:"mobile_phone,omitempty" example:"+43-1234"`
|
||||
Email string `json:"email,omitempty" example:"guest@example.com"`
|
||||
}
|
||||
|
||||
type DutyRosterDetailsInput struct {
|
||||
Pilot []DutyRosterPilotCrewInput `json:"pilot"`
|
||||
Doctor []DutyRosterCrewInput `json:"doctor"`
|
||||
Rescuer []DutyRosterCrewInput `json:"rescuer"`
|
||||
Helper []DutyRosterCrewInput `json:"helper"`
|
||||
OtherPerson []DutyRosterOtherPersonInput `json:"other_person"`
|
||||
}
|
||||
|
||||
type DutyRosterCreateAttributes struct {
|
||||
BaseID string `json:"base_id" validate:"required"`
|
||||
DutyDate string `json:"duty_date" validate:"required"`
|
||||
ShiftTime DutyRosterShiftTime `json:"shift_time"`
|
||||
Detail DutyRosterDetailsInput `json:"roster_detail"`
|
||||
}
|
||||
|
||||
type DutyRosterCreateData struct {
|
||||
Type string `json:"type" validate:"required,oneof=duty_roster_create duty_roster"`
|
||||
Attributes DutyRosterCreateAttributes `json:"attributes" validate:"required"`
|
||||
}
|
||||
|
||||
type DutyRosterCreateRequest struct {
|
||||
Data DutyRosterCreateData `json:"data" validate:"required"`
|
||||
}
|
||||
|
||||
type DutyRosterUpdateAttributes struct {
|
||||
BaseID *string `json:"base_id,omitempty"`
|
||||
DutyDate *string `json:"duty_date,omitempty"`
|
||||
ShiftTime *DutyRosterShiftTime `json:"shift_time,omitempty"`
|
||||
Detail *DutyRosterDetailsInput `json:"roster_detail,omitempty"`
|
||||
}
|
||||
|
||||
type DutyRosterUpdateData struct {
|
||||
Type string `json:"type" validate:"required,oneof=duty_roster_update duty_roster"`
|
||||
ID string `json:"id"`
|
||||
Attributes DutyRosterUpdateAttributes `json:"attributes" validate:"required"`
|
||||
}
|
||||
|
||||
type DutyRosterUpdateRequest struct {
|
||||
Data DutyRosterUpdateData `json:"data" validate:"required"`
|
||||
}
|
||||
|
||||
type DutyRosterDeleteCrewAttributes struct {
|
||||
Detail DutyRosterDetailsInput `json:"roster_detail"`
|
||||
}
|
||||
|
||||
type DutyRosterDeleteCrewData struct {
|
||||
Type string `json:"type" validate:"required,oneof=duty_roster_crew_delete duty_roster_update duty_roster"`
|
||||
Attributes DutyRosterDeleteCrewAttributes `json:"attributes" validate:"required"`
|
||||
}
|
||||
|
||||
type DutyRosterDeleteCrewRequest struct {
|
||||
Data DutyRosterDeleteCrewData `json:"data" validate:"required"`
|
||||
}
|
||||
|
||||
type DutyRosterDeleteCrewByIDAttributes struct {
|
||||
RosterID string `json:"roster_id" validate:"required"`
|
||||
CrewID string `json:"crew_id" validate:"required"`
|
||||
}
|
||||
|
||||
type DutyRosterDeleteCrewByIDData struct {
|
||||
Type string `json:"type" validate:"required,oneof=duty_roster_crew_delete duty_roster_update duty_roster"`
|
||||
Attributes DutyRosterDeleteCrewByIDAttributes `json:"attributes" validate:"required"`
|
||||
}
|
||||
|
||||
type DutyRosterDeleteCrewByIDRequest struct {
|
||||
Data DutyRosterDeleteCrewByIDData `json:"data" validate:"required"`
|
||||
}
|
||||
|
||||
type DutyRosterAnyDeleteData struct {
|
||||
ID string `json:"id" validate:"required" example:"018f3a5c-7a6f-7c2a-8d4a-4b5b1f6c8e9d"`
|
||||
}
|
||||
|
||||
type DutyRosterAnyDeleteRequest struct {
|
||||
Data DutyRosterAnyDeleteData `json:"data" validate:"required"`
|
||||
}
|
||||
|
||||
// DutyRosterAnyCreateRequestDoc is documentation-only schema for cleaner Swagger examples.
|
||||
type DutyRosterAnyCreateRequestDoc struct {
|
||||
Data DutyRosterAnyCreateDataDoc `json:"data"`
|
||||
}
|
||||
|
||||
type DutyRosterAnyCreateDataDoc struct {
|
||||
Type string `json:"type" example:"duty_roster_create" enums:"duty_roster_create,duty_roster"`
|
||||
Attributes DutyRosterAnyCreateAttributesDoc `json:"attributes"`
|
||||
}
|
||||
|
||||
type DutyRosterAnyCreateAttributesDoc struct {
|
||||
BaseID string `json:"base_id" example:"018f3a5c-7a6f-7c2a-8d4a-4b5b1f6c8e9d"`
|
||||
DutyDate string `json:"duty_date" example:"2026-03-16"`
|
||||
ShiftTime DutyRosterShiftTime `json:"shift_time"`
|
||||
Detail DutyRosterAnyCreateDetailDoc `json:"roster_detail"`
|
||||
}
|
||||
|
||||
type DutyRosterAnyCreateDetailDoc struct {
|
||||
Pilot []DutyRosterPilotAdditionalExampleDoc `json:"pilot"`
|
||||
Doctor []DutyRosterCrewInput `json:"doctor"`
|
||||
Rescuer []DutyRosterCrewInput `json:"rescuer"`
|
||||
Helper []DutyRosterCrewInput `json:"helper"`
|
||||
OtherPerson []DutyRosterOtherPersonInput `json:"other_person"`
|
||||
}
|
||||
|
||||
type DutyRosterPilotAdditionalExampleDoc struct {
|
||||
ID string `json:"id" example:"018f3a5c-7a6f-7c2a-8d4a-4b5b1f6c8e9d"`
|
||||
CrewType string `json:"crew_type" example:"additional" enums:"main,additional"`
|
||||
FlightInstructor bool `json:"flight_instructor" example:"true"`
|
||||
}
|
||||
53
internal/transport/http/dto/request/easa_release_request.go
Normal file
53
internal/transport/http/dto/request/easa_release_request.go
Normal file
@@ -0,0 +1,53 @@
|
||||
package request
|
||||
|
||||
type EASAReleaseCreateAttributes struct {
|
||||
HelicopterID string `json:"helicopter_id" validate:"required,uuid" example:"019d6771-5fb5-7337-837f-bc5ed85181a1"`
|
||||
// ComplaintID — OPTIONAL complaint (UUIDv7) this release closes. Set it to scope the
|
||||
// release to a specific defect; leave empty for a standalone (aircraft-clear) release.
|
||||
ComplaintID string `json:"complaint_id,omitempty" validate:"omitempty,uuid" example:"019d6774-1111-7fba-9ef4-3795c0da8a13"`
|
||||
// FlightID — OPTIONAL flight context. Used for the standalone (no-complaint) sign-off gate.
|
||||
FlightID string `json:"flight_id,omitempty" validate:"omitempty,uuid" example:"019d6774-98e8-7fba-9ef4-3795c0da8a13"`
|
||||
EASADate *string `json:"easa_date,omitempty" example:"2026-06-16"`
|
||||
EASAACHours *string `json:"easa_ac_hours,omitempty" example:"1245.3"`
|
||||
EASALocation *string `json:"easa_location,omitempty" example:"LOWI"`
|
||||
EASAWONo *string `json:"easa_wo_no,omitempty" example:"WO-2026-001"`
|
||||
EASAMaintManualRevAirframe *string `json:"easa_maint_manual_rev_airframe,omitempty" example:"Rev 12"`
|
||||
EASAMaintManualRevEngine *string `json:"easa_maint_manual_rev_engine,omitempty" example:"Rev 8"`
|
||||
// EASASignerID — OPTIONAL contact id (UUIDv7) of the signer/technician. Required only to sign.
|
||||
EASASignerID *string `json:"easa_signer_id,omitempty" example:"019d61db-53c6-7280-87ec-e65f205c6d3b"`
|
||||
EASAPermitNo *string `json:"easa_permit_no,omitempty" example:"145.A.50"`
|
||||
// Sign, when true, signs the release immediately at creation. Requires all
|
||||
// required fields to be present; otherwise the request is rejected.
|
||||
Sign bool `json:"sign,omitempty" example:"false"`
|
||||
}
|
||||
|
||||
type EASAReleaseCreateData struct {
|
||||
Type string `json:"type" validate:"required,oneof=easa_release_create easa_release" example:"easa_release_create"`
|
||||
Attributes EASAReleaseCreateAttributes `json:"attributes" validate:"required"`
|
||||
}
|
||||
|
||||
type EASAReleaseCreateRequest struct {
|
||||
Data EASAReleaseCreateData `json:"data" validate:"required"`
|
||||
}
|
||||
|
||||
type EASAReleaseUpdateAttributes struct {
|
||||
EASADate *string `json:"easa_date,omitempty" example:"2026-06-16"`
|
||||
EASAACHours *string `json:"easa_ac_hours,omitempty" example:"1245.3"`
|
||||
EASALocation *string `json:"easa_location,omitempty" example:"LOWI"`
|
||||
EASAWONo *string `json:"easa_wo_no,omitempty" example:"WO-2026-001"`
|
||||
EASAMaintManualRevAirframe *string `json:"easa_maint_manual_rev_airframe,omitempty" example:"Rev 12"`
|
||||
EASAMaintManualRevEngine *string `json:"easa_maint_manual_rev_engine,omitempty" example:"Rev 8"`
|
||||
// EASASignerID — OPTIONAL contact id (UUIDv7) of the signer/technician. Required only to sign.
|
||||
EASASignerID *string `json:"easa_signer_id,omitempty" example:"019d61db-53c6-7280-87ec-e65f205c6d3b"`
|
||||
EASAPermitNo *string `json:"easa_permit_no,omitempty" example:"145.A.50"`
|
||||
}
|
||||
|
||||
type EASAReleaseUpdateData struct {
|
||||
Type string `json:"type" validate:"required,oneof=easa_release_update easa_release" example:"easa_release_update"`
|
||||
ID string `json:"id" validate:"required" example:"019d6771-5fb5-7337-837f-bc5ed85181a1"`
|
||||
Attributes EASAReleaseUpdateAttributes `json:"attributes" validate:"required"`
|
||||
}
|
||||
|
||||
type EASAReleaseUpdateRequest struct {
|
||||
Data EASAReleaseUpdateData `json:"data" validate:"required"`
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
package request
|
||||
|
||||
type FileManagerAttachmentCreateAttributes struct {
|
||||
FileID string `json:"file_id" validate:"required" example:"018f3a5c-7a6f-7c2a-8d4a-4b5b1f6c8e9d"`
|
||||
RefType string `json:"ref_type" validate:"required" example:"helicopter"`
|
||||
RefID string `json:"ref_id" validate:"required" example:"018f3a5c-7a6f-7c2a-8d4a-4b5b1f6c8e9d"`
|
||||
Category *string `json:"category,omitempty" example:"document"`
|
||||
SortOrder *int `json:"sort_order,omitempty" example:"0"`
|
||||
IsPrimary *bool `json:"is_primary,omitempty" example:"false"`
|
||||
}
|
||||
|
||||
type FileManagerAttachmentCreateData struct {
|
||||
Type string `json:"type" validate:"required,oneof=file_manager_attachment_create file_manager_attachment" example:"file_manager_attachment_create"`
|
||||
Attributes FileManagerAttachmentCreateAttributes `json:"attributes" validate:"required"`
|
||||
}
|
||||
|
||||
type FileManagerAttachmentCreateRequest struct {
|
||||
Data FileManagerAttachmentCreateData `json:"data" validate:"required"`
|
||||
}
|
||||
197
internal/transport/http/dto/request/file_manager_file_request.go
Normal file
197
internal/transport/http/dto/request/file_manager_file_request.go
Normal file
@@ -0,0 +1,197 @@
|
||||
package request
|
||||
|
||||
type FileManagerFileCreateAttributes struct {
|
||||
UploadIntentUUID string `json:"upload_intent_uuid" validate:"required" example:"018f3a5c-7a6f-7c2a-8d4a-4b5b1f6c8e9d"`
|
||||
FolderID *string `json:"folder_id,omitempty" example:"018f3a5c-7a6f-7c2a-8d4a-4b5b1f6c8e9d"`
|
||||
Name string `json:"name" validate:"required" example:"policy-final.pdf"`
|
||||
}
|
||||
|
||||
type FileManagerFileUploadIntentAttributes struct {
|
||||
Name string `json:"name" validate:"required" example:"policy-final.pdf"`
|
||||
ContentType string `json:"content_type" validate:"required" example:"application/pdf"`
|
||||
SizeBytes int64 `json:"size_bytes" validate:"required,gte=0" example:"1024"`
|
||||
}
|
||||
|
||||
type FileManagerFileUploadIntentData struct {
|
||||
Type string `json:"type" validate:"required,oneof=file_manager_file_upload_intent" example:"file_manager_file_upload_intent"`
|
||||
Attributes FileManagerFileUploadIntentAttributes `json:"attributes" validate:"required"`
|
||||
}
|
||||
|
||||
type FileManagerFileUploadIntentRequest struct {
|
||||
Data FileManagerFileUploadIntentData `json:"data" validate:"required"`
|
||||
}
|
||||
|
||||
type FileManagerFileUploadIntentBulkRequest struct {
|
||||
Data []FileManagerFileUploadIntentData `json:"data" validate:"required,min=1"`
|
||||
}
|
||||
|
||||
type FileManagerFileCancelUploadAttributes struct {
|
||||
UploadIntentUUID string `json:"upload_intent_uuid" validate:"required" example:"018f3a5c-7a6f-7c2a-8d4a-4b5b1f6c8e9d"`
|
||||
}
|
||||
|
||||
type FileManagerFileCancelUploadData struct {
|
||||
Type string `json:"type" validate:"required,oneof=file_manager_file_cancel_upload" example:"file_manager_file_cancel_upload"`
|
||||
Attributes FileManagerFileCancelUploadAttributes `json:"attributes" validate:"required"`
|
||||
}
|
||||
|
||||
type FileManagerFileCancelUploadBulkRequest struct {
|
||||
Data []FileManagerFileCancelUploadData `json:"data" validate:"required,min=1"`
|
||||
}
|
||||
|
||||
type FileManagerFileUploadCompleteAttributes struct {
|
||||
UploadIntentUUID string `json:"upload_intent_uuid" validate:"required" example:"018f3a5c-7a6f-7c2a-8d4a-4b5b1f6c8e9d"`
|
||||
}
|
||||
|
||||
type FileManagerFileUploadCompleteData struct {
|
||||
Type string `json:"type" validate:"required,oneof=file_manager_file_upload_complete" example:"file_manager_file_upload_complete"`
|
||||
Attributes FileManagerFileUploadCompleteAttributes `json:"attributes" validate:"required"`
|
||||
}
|
||||
|
||||
type FileManagerFileUploadCompleteRequest struct {
|
||||
Data FileManagerFileUploadCompleteData `json:"data" validate:"required"`
|
||||
}
|
||||
|
||||
type FileManagerFileCreateData struct {
|
||||
Type string `json:"type" validate:"required,oneof=file_manager_file_create file_manager_file" example:"file_manager_file_create"`
|
||||
Attributes FileManagerFileCreateAttributes `json:"attributes" validate:"required"`
|
||||
}
|
||||
|
||||
type FileManagerFileCreateRequest struct {
|
||||
Data FileManagerFileCreateData `json:"data" validate:"required"`
|
||||
}
|
||||
|
||||
// FileManagerFileCreateFlexibleRequest is used for swagger documentation.
|
||||
// Runtime accepts:
|
||||
// - single: {"data": { ...FileManagerFileCreateData }}
|
||||
// - bulk: {"data": [ ...FileManagerFileBulkCreateItem ]}
|
||||
type FileManagerFileCreateFlexibleRequest struct {
|
||||
Data interface{} `json:"data"`
|
||||
}
|
||||
|
||||
type FileManagerFileBulkCreateItem struct {
|
||||
Type string `json:"type" validate:"required,oneof=file_manager_file_create file_manager_file" example:"file_manager_file_create"`
|
||||
Attributes FileManagerFileCreateAttributes `json:"attributes" validate:"required"`
|
||||
}
|
||||
|
||||
type FileManagerFileBulkCreateRequest struct {
|
||||
Data []FileManagerFileBulkCreateItem `json:"data" validate:"required,min=1"`
|
||||
}
|
||||
|
||||
type FileManagerFileUserCreateAttributes struct {
|
||||
UploadIntentUUID string `json:"upload_intent_uuid" validate:"required" example:"018f3a5c-7a6f-7c2a-8d4a-4b5b1f6c8e9d"`
|
||||
UserUUID string `json:"user_uuid" validate:"required" example:"018f3a5c-7a6f-7c2a-8d4a-4b5b1f6c8e9d"`
|
||||
Name string `json:"name" validate:"required" example:"profile.jpg"`
|
||||
}
|
||||
|
||||
type FileManagerFileUserBulkCreateItem struct {
|
||||
Type string `json:"type" validate:"required,oneof=file_manager_file_create file_manager_file" example:"file_manager_file_create"`
|
||||
Attributes FileManagerFileUserCreateAttributes `json:"attributes" validate:"required"`
|
||||
}
|
||||
|
||||
type FileManagerFileUserBulkCreateRequest struct {
|
||||
Data []FileManagerFileUserBulkCreateItem `json:"data" validate:"required,min=1"`
|
||||
}
|
||||
|
||||
type FileManagerFileAircraftCreateAttributes struct {
|
||||
UploadIntentUUID string `json:"upload_intent_uuid" validate:"required" example:"018f3a5c-7a6f-7c2a-8d4a-4b5b1f6c8e9d"`
|
||||
AircraftUUID string `json:"aircraft_uuid" validate:"required" example:"018f3a5c-7a6f-7c2a-8d4a-4b5b1f6c8e9d"`
|
||||
Name string `json:"name" validate:"required" example:"aircraft.jpg"`
|
||||
}
|
||||
|
||||
type FileManagerFileAircraftBulkCreateItem struct {
|
||||
Type string `json:"type" validate:"required,oneof=file_manager_file_create file_manager_file" example:"file_manager_file_create"`
|
||||
Attributes FileManagerFileAircraftCreateAttributes `json:"attributes" validate:"required"`
|
||||
}
|
||||
|
||||
type FileManagerFileAircraftBulkCreateRequest struct {
|
||||
Data []FileManagerFileAircraftBulkCreateItem `json:"data" validate:"required,min=1"`
|
||||
}
|
||||
|
||||
type FileManagerFileBaseCreateAttributes struct {
|
||||
UploadIntentUUID string `json:"upload_intent_uuid" validate:"required" example:"018f3a5c-7a6f-7c2a-8d4a-4b5b1f6c8e9d"`
|
||||
BaseUUID string `json:"base_uuid" validate:"required" example:"018f3a5c-7a6f-7c2a-8d4a-4b5b1f6c8e9d"`
|
||||
Name string `json:"name" validate:"required" example:"base.jpg"`
|
||||
}
|
||||
|
||||
type FileManagerFileBaseBulkCreateItem struct {
|
||||
Type string `json:"type" validate:"required,oneof=file_manager_file_create file_manager_file" example:"file_manager_file_create"`
|
||||
Attributes FileManagerFileBaseCreateAttributes `json:"attributes" validate:"required"`
|
||||
}
|
||||
|
||||
type FileManagerFileBaseBulkCreateRequest struct {
|
||||
Data []FileManagerFileBaseBulkCreateItem `json:"data" validate:"required,min=1"`
|
||||
}
|
||||
|
||||
type FileManagerFileDULCreateAttributes struct {
|
||||
UploadIntentUUID string `json:"upload_intent_uuid" validate:"required" example:"018f3a5c-7a6f-7c2a-8d4a-4b5b1f6c8e9d"`
|
||||
DULUUID string `json:"dul_uuid,omitempty" example:"018f3a5c-7a6f-7c2a-8d4a-4b5b1f6c8e9d"`
|
||||
Name string `json:"name" validate:"required" example:"dul.jpg"`
|
||||
}
|
||||
|
||||
type FileManagerFileDULBulkCreateItem struct {
|
||||
Type string `json:"type" validate:"required,oneof=file_manager_file_create file_manager_file" example:"file_manager_file_create"`
|
||||
Attributes FileManagerFileDULCreateAttributes `json:"attributes" validate:"required"`
|
||||
}
|
||||
|
||||
type FileManagerFileDULBulkCreateRequest struct {
|
||||
Data []FileManagerFileDULBulkCreateItem `json:"data" validate:"required,min=1"`
|
||||
}
|
||||
|
||||
type FileManagerFileTakeoverCreateAttributes struct {
|
||||
UploadIntentUUID string `json:"upload_intent_uuid" validate:"required" example:"018f3a5c-7a6f-7c2a-8d4a-4b5b1f6c8e9d"`
|
||||
TakeoverUUID string `json:"takeover_uuid,omitempty" example:"019e8713-aa19-7fb7-a510-a0332dc69697"`
|
||||
Name string `json:"name" validate:"required" example:"takeover-report.pdf"`
|
||||
}
|
||||
|
||||
type FileManagerFileTakeoverBulkCreateItem struct {
|
||||
Type string `json:"type" validate:"required,oneof=file_manager_file_create file_manager_file" example:"file_manager_file_create"`
|
||||
Attributes FileManagerFileTakeoverCreateAttributes `json:"attributes" validate:"required"`
|
||||
}
|
||||
|
||||
type FileManagerFileTakeoverBulkCreateRequest struct {
|
||||
Data []FileManagerFileTakeoverBulkCreateItem `json:"data" validate:"required,min=1"`
|
||||
}
|
||||
|
||||
type FileManagerFileTemplateCreateAttributes struct {
|
||||
TemplateUUID string `json:"template_uuid" validate:"required" example:"019f0386-0a00-7a4a-a2d8-30afc0e0c3ad"`
|
||||
HelicopterID string `json:"helicopter_id" validate:"required" example:"019e8713-aa19-7fb7-a510-a0332dc69697"`
|
||||
TakeoverID string `json:"takeover_id,omitempty" example:"019e8713-aa19-7fb7-a510-a0332dc69697"`
|
||||
Name string `json:"name" validate:"required" example:"collabora-draft.docx"`
|
||||
}
|
||||
|
||||
type FileManagerFileTemplateCreateRequest struct {
|
||||
Data struct {
|
||||
Type string `json:"type" validate:"required,oneof=file_manager_file_create_from_template file_manager_file_create"`
|
||||
Attributes FileManagerFileTemplateCreateAttributes `json:"attributes" validate:"required"`
|
||||
} `json:"data" validate:"required"`
|
||||
}
|
||||
|
||||
type FileManagerFileMissionCreateAttributes struct {
|
||||
UploadIntentUUID string `json:"upload_intent_uuid" validate:"required" example:"018f3a5c-7a6f-7c2a-8d4a-4b5b1f6c8e9d"`
|
||||
MissionUUID string `json:"mission_uuid" validate:"required" example:"019d6774-98e8-7fba-9ef4-3795c0da8a13"`
|
||||
Name string `json:"name" validate:"required" example:"mission-brief.pdf"`
|
||||
}
|
||||
|
||||
type FileManagerFileMissionBulkCreateItem struct {
|
||||
Type string `json:"type" validate:"required,oneof=file_manager_file_create file_manager_file" example:"file_manager_file_create"`
|
||||
Attributes FileManagerFileMissionCreateAttributes `json:"attributes" validate:"required"`
|
||||
}
|
||||
|
||||
type FileManagerFileMissionBulkCreateRequest struct {
|
||||
Data []FileManagerFileMissionBulkCreateItem `json:"data" validate:"required,min=1"`
|
||||
}
|
||||
|
||||
type FileManagerFileUpdateAttributes struct {
|
||||
FolderID *string `json:"folder_id,omitempty" example:"018f3a5c-7a6f-7c2a-8d4a-4b5b1f6c8e9d"`
|
||||
Name *string `json:"name,omitempty" example:"policy-v2.pdf"`
|
||||
}
|
||||
|
||||
type FileManagerFileUpdateData struct {
|
||||
Type string `json:"type" validate:"required,oneof=file_manager_file_update file_manager_file" example:"file_manager_file_update"`
|
||||
ID string `json:"id" example:"018f3a5c-7a6f-7c2a-8d4a-4b5b1f6c8e9d"`
|
||||
Attributes FileManagerFileUpdateAttributes `json:"attributes" validate:"required"`
|
||||
}
|
||||
|
||||
type FileManagerFileUpdateRequest struct {
|
||||
Data FileManagerFileUpdateData `json:"data" validate:"required"`
|
||||
}
|
||||
@@ -0,0 +1,47 @@
|
||||
package request
|
||||
|
||||
type FileManagerFolderCreateAttributes struct {
|
||||
ParentID *string `json:"parent_id,omitempty" example:"018f3a5c-7a6f-7c2a-8d4a-4b5b1f6c8e9d"`
|
||||
Name string `json:"name" validate:"required" example:"operations"`
|
||||
}
|
||||
|
||||
type FileManagerFolderCreateData struct {
|
||||
Type string `json:"type" validate:"required,oneof=file_manager_folder_create file_manager_folder" example:"file_manager_folder_create"`
|
||||
Attributes FileManagerFolderCreateAttributes `json:"attributes" validate:"required"`
|
||||
}
|
||||
|
||||
type FileManagerFolderCreateRequest struct {
|
||||
Data FileManagerFolderCreateData `json:"data" validate:"required"`
|
||||
}
|
||||
|
||||
type FileManagerFolderUpdateAttributes struct {
|
||||
Name *string `json:"name,omitempty" example:"operations-updated"`
|
||||
ParentID *string `json:"parent_id,omitempty" example:"018f3a5c-7a6f-7c2a-8d4a-4b5b1f6c8e9d"`
|
||||
}
|
||||
|
||||
type FileManagerFolderUpdateData struct {
|
||||
Type string `json:"type" validate:"required,oneof=file_manager_folder_update file_manager_folder" example:"file_manager_folder_update"`
|
||||
ID string `json:"id" example:"018f3a5c-7a6f-7c2a-8d4a-4b5b1f6c8e9d"`
|
||||
Attributes FileManagerFolderUpdateAttributes `json:"attributes" validate:"required"`
|
||||
}
|
||||
|
||||
type FileManagerFolderUpdateRequest struct {
|
||||
Data FileManagerFolderUpdateData `json:"data" validate:"required"`
|
||||
}
|
||||
|
||||
type FileManagerNodeMoveItem struct {
|
||||
UUID string `json:"uuid" validate:"required" example:"018f3a5c-7a6f-7c2a-8d4a-4b5b1f6c8e9d"`
|
||||
DestinationFolderUUID *string `json:"destination_folder_uuid" example:"018f3a5c-7a6f-7c2a-8d4a-4b5b1f6c8e9d"`
|
||||
}
|
||||
|
||||
type FileManagerNodeMoveBulkRequest struct {
|
||||
Data []FileManagerNodeMoveItem `json:"data" validate:"required,min=1"`
|
||||
}
|
||||
|
||||
type FileManagerNodePurgeItem struct {
|
||||
UUID string `json:"uuid" validate:"required" example:"018f3a5c-7a6f-7c2a-8d4a-4b5b1f6c8e9d"`
|
||||
}
|
||||
|
||||
type FileManagerNodePurgeBulkRequest struct {
|
||||
Data []FileManagerNodePurgeItem `json:"data" validate:"required,min=1"`
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
package request
|
||||
|
||||
type FileManagerRestoreTargetAttributes struct {
|
||||
TargetFolderID *string `json:"target_folder_id,omitempty" example:"018f3a5c-7a6f-7c2a-8d4a-4b5b1f6c8e9d"`
|
||||
}
|
||||
|
||||
type FileManagerRestoreTargetData struct {
|
||||
Attributes FileManagerRestoreTargetAttributes `json:"attributes"`
|
||||
}
|
||||
|
||||
type FileManagerRestoreTargetRequest struct {
|
||||
Data *FileManagerRestoreTargetData `json:"data,omitempty"`
|
||||
}
|
||||
70
internal/transport/http/dto/request/fleet_status_request.go
Normal file
70
internal/transport/http/dto/request/fleet_status_request.go
Normal file
@@ -0,0 +1,70 @@
|
||||
package request
|
||||
|
||||
import shared "wucher/internal/transport/http/dto/shared"
|
||||
|
||||
type FleetStatusMarkServicedAttributes struct {
|
||||
ServicedAt string `json:"serviced_at,omitempty" example:"2026-05-29T07:43:06Z"`
|
||||
}
|
||||
|
||||
type FleetStatusMarkServicedData struct {
|
||||
Type string `json:"type" validate:"required,oneof=fleet_status_mark_serviced fleet_status" example:"fleet_status_mark_serviced"`
|
||||
Attributes FleetStatusMarkServicedAttributes `json:"attributes"`
|
||||
}
|
||||
|
||||
type FleetStatusMarkServicedRequest struct {
|
||||
Data FleetStatusMarkServicedData `json:"data" validate:"required"`
|
||||
}
|
||||
|
||||
type FleetStatusCreateAttributes struct {
|
||||
HelicopterID string `json:"helicopter_id" validate:"required" example:"019d6771-5fb5-7337-837f-bc5ed85181a1"`
|
||||
MaintenanceSchedules []shared.MaintenanceScheduleAttributes `json:"maintenance_schedules" validate:"omitempty,dive"`
|
||||
Files []shared.FleetStatusFileAttributes `json:"files"`
|
||||
}
|
||||
|
||||
type FleetStatusCreateData struct {
|
||||
Type string `json:"type" validate:"required,oneof=fleet_status_create fleet_status" example:"fleet_status_create"`
|
||||
Attributes FleetStatusCreateAttributes `json:"attributes" validate:"required"`
|
||||
}
|
||||
|
||||
type FleetStatusCreateRequest struct {
|
||||
Data FleetStatusCreateData `json:"data" validate:"required"`
|
||||
}
|
||||
|
||||
type FleetStatusUpdateAttributes struct {
|
||||
HelicopterID *string `json:"helicopter_id,omitempty"`
|
||||
MaintenanceSchedules []shared.MaintenanceScheduleAttributes `json:"maintenance_schedules" validate:"omitempty,dive"`
|
||||
Files []shared.FleetStatusFileAttributes `json:"files"`
|
||||
Summary *FleetStatusSummaryUpdate `json:"summary,omitempty"`
|
||||
}
|
||||
|
||||
type FleetStatusSummaryUpdate struct {
|
||||
Airframe *FleetStatusSummaryAirframeUpdate `json:"airframe,omitempty"`
|
||||
Engine1 *FleetStatusSummaryEngineUpdate `json:"engine_1,omitempty"`
|
||||
Engine2 *FleetStatusSummaryEngineUpdate `json:"engine_2,omitempty"`
|
||||
Landings *float64 `json:"landings,omitempty" example:"120"`
|
||||
FlightReports *float64 `json:"flight_reports,omitempty" example:"80"`
|
||||
RotorBrakeCycles *float64 `json:"rotor_brake_cycles,omitempty" example:"8"`
|
||||
HookReleases *float64 `json:"hook_releases,omitempty" example:"10"`
|
||||
}
|
||||
|
||||
type FleetStatusSummaryAirframeUpdate struct {
|
||||
Hours *float64 `json:"hours,omitempty" example:"6610.5"`
|
||||
Cycles *float64 `json:"cycles,omitempty" example:"320"`
|
||||
}
|
||||
|
||||
type FleetStatusSummaryEngineUpdate struct {
|
||||
Hours *float64 `json:"hours,omitempty" example:"8001.7"`
|
||||
GpcNgN1 *float64 `json:"gpc_ng_n1,omitempty" example:"98.7"`
|
||||
PtcNfN2 *float64 `json:"ptc_nf_n2,omitempty" example:"97.2"`
|
||||
Ccc *float64 `json:"ccc,omitempty" example:"44"`
|
||||
}
|
||||
|
||||
type FleetStatusUpdateData struct {
|
||||
Type string `json:"type" validate:"required,oneof=fleet_status_update fleet_status" example:"fleet_status_update"`
|
||||
ID string `json:"id" example:"019d6773-ccca-71dd-b9c7-8e6f531deb90"`
|
||||
Attributes FleetStatusUpdateAttributes `json:"attributes" validate:"required"`
|
||||
}
|
||||
|
||||
type FleetStatusUpdateRequest struct {
|
||||
Data FleetStatusUpdateData `json:"data" validate:"required"`
|
||||
}
|
||||
135
internal/transport/http/dto/request/flight_data_request.go
Normal file
135
internal/transport/http/dto/request/flight_data_request.go
Normal file
@@ -0,0 +1,135 @@
|
||||
package request
|
||||
|
||||
type FlightDataCreateAttributes struct {
|
||||
MissionID string `json:"mission_id" validate:"required"`
|
||||
CoPilotID *string `json:"co_pilot_id,omitempty"`
|
||||
FromICAOID *string `json:"from_icao_id,omitempty"`
|
||||
FromHospitalID *string `json:"from_hospital_id,omitempty"`
|
||||
ToICAOID *string `json:"to_icao_id,omitempty"`
|
||||
ToHospitalID *string `json:"to_hospital_id,omitempty"`
|
||||
FlightTakeOff *string `json:"flight_take_off,omitempty"`
|
||||
FlightLanding *string `json:"flight_landing,omitempty"`
|
||||
FlightDurationSecs *int64 `json:"flight_duration_seconds,omitempty"`
|
||||
FlightREDSecs *int64 `json:"flight_red_seconds,omitempty"`
|
||||
MaxN1 *float64 `json:"max_n1,omitempty"`
|
||||
MaxN2 *float64 `json:"max_n2,omitempty"`
|
||||
PaxCount *int `json:"pax_count,omitempty"`
|
||||
LandingCount *int `json:"landing_count,omitempty"`
|
||||
RotorBrakeCycle *int `json:"rotor_brake_cycle,omitempty"`
|
||||
HookReleases *int `json:"hook_releases,omitempty"`
|
||||
TicketNo *string `json:"ticket_no,omitempty"`
|
||||
Engine *string `json:"engine,omitempty"`
|
||||
DeliveryNoteNumber *string `json:"delivery_note_number,omitempty"`
|
||||
CustomerName *string `json:"customer_name,omitempty"`
|
||||
FlightPlanDistance *float64 `json:"flight_plan_distance,omitempty"`
|
||||
FlightPlanTimeSecs *int64 `json:"flight_plan_time_seconds,omitempty"`
|
||||
FlightPlanTrueCourse *float64 `json:"flight_plan_true_course,omitempty"`
|
||||
FuelBeforeFlight *float64 `json:"fuel_before_flight,omitempty"`
|
||||
FuelUpload *float64 `json:"fuel_upload,omitempty"`
|
||||
FuelAfterFlight *float64 `json:"fuel_after_flight,omitempty"`
|
||||
FuelPlanning *float64 `json:"fuel_planning,omitempty"`
|
||||
FlightPositioning *bool `json:"flight_positioning,omitempty" example:"false"`
|
||||
HESLO *FlightDataCreateSPOHESLOAttributes `json:"heslo,omitempty"`
|
||||
Logging *FlightDataCreateSPOLoggingAttributes `json:"logging,omitempty"`
|
||||
HEC *FlightDataCreateSPOHECAttributes `json:"hec,omitempty"`
|
||||
OtherInformation *string `json:"other_information,omitempty"`
|
||||
}
|
||||
|
||||
type FlightDataCreateSPOHESLOAttributes struct {
|
||||
Flights []FlightDataCreateSPOHESLOFlight `json:"flights,omitempty"`
|
||||
}
|
||||
|
||||
type FlightDataCreateSPOHESLOFlight struct {
|
||||
ROT int `json:"rot" example:"0"`
|
||||
// Facility category: heslo-rope-label.
|
||||
HESLORopeLabelID string `json:"heslo_rope_label_id,omitempty" example:"string"`
|
||||
HESLORopeLabelName string `json:"heslo_rope_label_name,omitempty" example:"string"`
|
||||
// Facility category: heslo-rope-length.
|
||||
Slings []FlightDataCreateSPOHESLOSling `json:"slings,omitempty"`
|
||||
}
|
||||
|
||||
type FlightDataCreateSPOHESLOSling struct {
|
||||
// Facility category: heslo-rope-length.
|
||||
SlingID string `json:"sling_id,omitempty" example:"string"`
|
||||
SlingName string `json:"sling_name,omitempty" example:"string"`
|
||||
}
|
||||
|
||||
type FlightDataCreateSPOLoggingAttributes struct {
|
||||
Slings []FlightDataCreateSPOLoggingSling `json:"slings,omitempty"`
|
||||
}
|
||||
|
||||
type FlightDataCreateSPOLoggingSling struct {
|
||||
// Facility category: heslo-hook.
|
||||
SlingID string `json:"sling_id,omitempty" example:"string"`
|
||||
SlingName string `json:"sling_name,omitempty" example:"string"`
|
||||
}
|
||||
|
||||
type FlightDataCreateSPOHECAttributes struct {
|
||||
Flights []FlightDataCreateSPOHECFlight `json:"flights,omitempty"`
|
||||
Loads []FlightDataCreateSPOHECLoad `json:"loads,omitempty"`
|
||||
Slings []FlightDataCreateSPOHECSling `json:"slings,omitempty"`
|
||||
}
|
||||
|
||||
type FlightDataCreateSPOHECFlight struct {
|
||||
// Facility category: hec-rope-label.
|
||||
EquipmentID string `json:"equipment_id,omitempty" example:"string"`
|
||||
EquipmentName string `json:"equipment_name,omitempty" example:"string"`
|
||||
HECCycle int `json:"hec_cycle" example:"0"`
|
||||
ROT int `json:"rot" example:"0"`
|
||||
}
|
||||
|
||||
type FlightDataCreateSPOHECLoad struct {
|
||||
// Weight band for the HEC load, e.g. "bis 199 kg", "200-600 kg", "600-800 kg", "800-1000 kg".
|
||||
LoadCategory string `json:"load_category,omitempty" example:"200-600 kg"`
|
||||
Quantity int `json:"quantity" example:"3"`
|
||||
}
|
||||
|
||||
type FlightDataCreateSPOHECSling struct {
|
||||
// Facility category: hec-rope-length.
|
||||
SlingID string `json:"sling_id,omitempty" example:"string"`
|
||||
SlingName string `json:"sling_name,omitempty" example:"string"`
|
||||
}
|
||||
|
||||
type FlightDataCreateData struct {
|
||||
Type string `json:"type" validate:"required,oneof=flight_data_create flight_data" example:"flight_data_create"`
|
||||
Attributes FlightDataCreateAttributes `json:"attributes" validate:"required"`
|
||||
}
|
||||
|
||||
type FlightDataCreateRequest struct {
|
||||
Data FlightDataCreateData `json:"data" validate:"required"`
|
||||
}
|
||||
|
||||
type FlightDataUpdateData struct {
|
||||
Type string `json:"type" validate:"required,oneof=flight_data_update" example:"flight_data_update"`
|
||||
Attributes FlightDataCreateAttributes `json:"attributes" validate:"required"`
|
||||
}
|
||||
|
||||
type FlightDataUpdateRequest struct {
|
||||
Data FlightDataUpdateData `json:"data" validate:"required"`
|
||||
}
|
||||
|
||||
// FlightDataCreateSPOExampleRequest is used only for Swagger examples.
|
||||
// Runtime parsing still uses FlightDataCreateRequest.
|
||||
type FlightDataCreateSPOExampleRequest struct {
|
||||
Data FlightDataCreateSPOExampleData `json:"data"`
|
||||
}
|
||||
|
||||
type FlightDataCreateSPOExampleData struct {
|
||||
Type string `json:"type" example:"flight_data_create"`
|
||||
Attributes FlightDataCreateSPOExampleAttributes `json:"attributes"`
|
||||
}
|
||||
|
||||
type FlightDataCreateSPOExampleAttributes struct {
|
||||
MissionID string `json:"mission_id" example:"019d6774-98e8-7fba-9ef4-3795c0da8a13"`
|
||||
CoPilotID string `json:"co_pilot_id,omitempty" example:"019d61db-53c6-7280-87ec-e65f205c6d3b"`
|
||||
FlightTakeOff string `json:"flight_take_off,omitempty" example:"2026-04-20T08:00:00Z"`
|
||||
FlightLanding string `json:"flight_landing,omitempty" example:"2026-04-20T09:15:00Z"`
|
||||
FromICAOID string `json:"from_icao_id,omitempty" example:"019d6774-98e8-7fba-9ef4-3795c0da8a13"`
|
||||
ToICAOID string `json:"to_icao_id,omitempty" example:"019d6774-98e8-7fba-9ef4-3795c0da8a13"`
|
||||
HookReleases int `json:"hook_releases,omitempty" example:"0"`
|
||||
TicketNo string `json:"ticket_no,omitempty" example:"SPO-001"`
|
||||
Engine string `json:"engine,omitempty" example:"AIRBUS H145"`
|
||||
DeliveryNoteNumber string `json:"delivery_note_number,omitempty" example:"DN-2026-0001"`
|
||||
CustomerName string `json:"customer_name,omitempty" example:"Patient Transfer"`
|
||||
OtherInformation string `json:"other_information,omitempty" example:"SPO mission sample"`
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
package request
|
||||
|
||||
type FlightInspectionFileChecklistCreateAttributes struct {
|
||||
IsDone *bool `json:"is_done,omitempty" example:"false"`
|
||||
HelicopterFileID string `json:"helicopter_file_id" validate:"required" example:"019d6771-5fb5-7337-837f-bc5ed85181a1"`
|
||||
FlightInspectionID string `json:"flight_inspection_id" validate:"required" example:"019d6772-471c-7b70-b42a-d94020ef61e4"`
|
||||
}
|
||||
|
||||
type FlightInspectionFileChecklistCreateData struct {
|
||||
Type string `json:"type" validate:"required,oneof=flight_inspection_file_checklist_create flight_inspection_file_checklist" example:"flight_inspection_file_checklist_create"`
|
||||
Attributes FlightInspectionFileChecklistCreateAttributes `json:"attributes" validate:"required"`
|
||||
}
|
||||
|
||||
type FlightInspectionFileChecklistCreateRequest struct {
|
||||
Data FlightInspectionFileChecklistCreateData `json:"data" validate:"required"`
|
||||
}
|
||||
|
||||
type FlightInspectionFileChecklistUpdateAttributes struct {
|
||||
IsDone *bool `json:"is_done,omitempty" example:"true"`
|
||||
HelicopterFileID *string `json:"helicopter_file_id,omitempty" example:"019d6771-5fb5-7337-837f-bc5ed85181a1"`
|
||||
FlightInspectionID *string `json:"flight_inspection_id,omitempty" example:"019d6772-471c-7b70-b42a-d94020ef61e4"`
|
||||
}
|
||||
|
||||
type FlightInspectionFileChecklistUpdateData struct {
|
||||
Type string `json:"type" validate:"required,oneof=flight_inspection_file_checklist_update flight_inspection_file_checklist" example:"flight_inspection_file_checklist_update"`
|
||||
ID string `json:"id" example:"019d6773-ccca-71dd-b9c7-8e6f531deb90"`
|
||||
Attributes FlightInspectionFileChecklistUpdateAttributes `json:"attributes" validate:"required"`
|
||||
}
|
||||
|
||||
type FlightInspectionFileChecklistUpdateRequest struct {
|
||||
Data FlightInspectionFileChecklistUpdateData `json:"data" validate:"required"`
|
||||
}
|
||||
32
internal/transport/http/dto/request/flight_request.go
Normal file
32
internal/transport/http/dto/request/flight_request.go
Normal file
@@ -0,0 +1,32 @@
|
||||
package request
|
||||
|
||||
type FlightCreateAttributes struct {
|
||||
Date string `json:"date" validate:"required" example:"2026-04-14"`
|
||||
DutyRosterID *string `json:"duty_roster_id,omitempty" example:"019d6772-471c-7b70-b42a-d94020ef61e4"`
|
||||
ReserveAcID *string `json:"reserve_ac_id,omitempty" example:"019d6773-ccca-71dd-b9c7-8e6f531deb90"`
|
||||
}
|
||||
|
||||
type FlightCreateData struct {
|
||||
Type string `json:"type" validate:"required,oneof=flight_create flight" example:"flight_create"`
|
||||
Attributes FlightCreateAttributes `json:"attributes" validate:"required"`
|
||||
}
|
||||
|
||||
type FlightCreateRequest struct {
|
||||
Data FlightCreateData `json:"data" validate:"required"`
|
||||
}
|
||||
|
||||
type FlightUpdateAttributes struct {
|
||||
Date *string `json:"date,omitempty" example:"2026-04-14"`
|
||||
DutyRosterID *string `json:"duty_roster_id,omitempty" example:"019d6772-471c-7b70-b42a-d94020ef61e4"`
|
||||
ReserveAcID *string `json:"reserve_ac_id,omitempty" example:"019d6773-ccca-71dd-b9c7-8e6f531deb90"`
|
||||
}
|
||||
|
||||
type FlightUpdateData struct {
|
||||
Type string `json:"type" validate:"required,oneof=flight_update flight" example:"flight_update"`
|
||||
ID string `json:"id" example:"019d6774-98e8-7fba-9ef4-3795c0da8a13"`
|
||||
Attributes FlightUpdateAttributes `json:"attributes" validate:"required"`
|
||||
}
|
||||
|
||||
type FlightUpdateRequest struct {
|
||||
Data FlightUpdateData `json:"data" validate:"required"`
|
||||
}
|
||||
21
internal/transport/http/dto/request/fm_report_request.go
Normal file
21
internal/transport/http/dto/request/fm_report_request.go
Normal file
@@ -0,0 +1,21 @@
|
||||
package request
|
||||
|
||||
type FMReportUpdateAttributes struct {
|
||||
// Engine1GpcN1 - GPC/N1 value for engine 1. Optional.
|
||||
Engine1GpcN1 *string `json:"engine1_gpc_n1,omitempty" example:"GPC/N1 63.2"`
|
||||
// Engine1PtcN2 - PTC/N2 value for engine 1. Optional.
|
||||
Engine1PtcN2 *string `json:"engine1_ptc_n2,omitempty" example:"PTC/N2 61.8"`
|
||||
// Engine2GpcN1 - GPC/N1 value for engine 2. Optional.
|
||||
Engine2GpcN1 *string `json:"engine2_gpc_n1,omitempty" example:"GPC/N1 63.2"`
|
||||
// Engine2PtcN2 - PTC/N2 value for engine 2. Optional.
|
||||
Engine2PtcN2 *string `json:"engine2_ptc_n2,omitempty" example:"PTC/N2 61.8"`
|
||||
}
|
||||
|
||||
type FMReportUpdateData struct {
|
||||
Type string `json:"type" validate:"required,oneof=fm_report_update fm_report" example:"fm_report_update"`
|
||||
Attributes FMReportUpdateAttributes `json:"attributes" validate:"required"`
|
||||
}
|
||||
|
||||
type FMReportUpdateRequest struct {
|
||||
Data FMReportUpdateData `json:"data" validate:"required"`
|
||||
}
|
||||
@@ -0,0 +1,87 @@
|
||||
package request
|
||||
|
||||
type HelicopterFileCreateAttributes struct {
|
||||
Section string `json:"section" validate:"required,oneof=bfi fpwb afi" enums:"bfi,fpwb,afi" example:"bfi"`
|
||||
Position *int `json:"position,omitempty" example:"1"`
|
||||
IsMandatory *bool `json:"is_mandatory,omitempty" example:"true"`
|
||||
HelicopterID string `json:"helicopter_id" example:"019d6771-5fb5-7337-837f-bc5ed85181a1"`
|
||||
UploadIntentUUID string `json:"upload_intent_uuid" validate:"required" example:"019d6d0d-2de2-7db1-80a2-2862d43af620"`
|
||||
FileName string `json:"file_name" validate:"required" example:"Before Flight Checklist.pdf"`
|
||||
FolderUUID *string `json:"folder_uuid,omitempty" example:"019d6d0d-8c24-72e3-9f58-c9ea0fbf0bb1"`
|
||||
}
|
||||
|
||||
type HelicopterFileUploadAttributes struct {
|
||||
Name string `json:"name" validate:"required" example:"Before Flight Checklist.pdf"`
|
||||
ContentType string `json:"content_type" validate:"required" example:"application/pdf"`
|
||||
SizeBytes int64 `json:"size_bytes" validate:"required,gte=0" example:"1024"`
|
||||
}
|
||||
|
||||
type HelicopterFileUploadData struct {
|
||||
Type string `json:"type" validate:"required,oneof=helicopter_file_upload" example:"helicopter_file_upload"`
|
||||
Attributes HelicopterFileUploadAttributes `json:"attributes" validate:"required"`
|
||||
}
|
||||
|
||||
type HelicopterFileUploadRequest struct {
|
||||
Data HelicopterFileUploadData `json:"data" validate:"required"`
|
||||
}
|
||||
|
||||
type HelicopterFileUploadBulkRequest struct {
|
||||
Data []HelicopterFileUploadData `json:"data" validate:"required,min=1"`
|
||||
}
|
||||
|
||||
type HelicopterFileCancelUploadAttributes struct {
|
||||
UploadIntentUUID string `json:"upload_intent_uuid" validate:"required" example:"019d6d0d-2de2-7db1-80a2-2862d43af620"`
|
||||
}
|
||||
|
||||
type HelicopterFileCancelUploadData struct {
|
||||
Type string `json:"type" validate:"required,oneof=helicopter_file_cancel_upload" example:"helicopter_file_cancel_upload"`
|
||||
Attributes HelicopterFileCancelUploadAttributes `json:"attributes" validate:"required"`
|
||||
}
|
||||
|
||||
type HelicopterFileCancelUploadBulkRequest struct {
|
||||
Data []HelicopterFileCancelUploadData `json:"data" validate:"required,min=1"`
|
||||
}
|
||||
|
||||
type HelicopterFileCreateData struct {
|
||||
Type string `json:"type" validate:"required,oneof=helicopter_file_create helicopter_file" example:"helicopter_file_create"`
|
||||
Attributes HelicopterFileCreateAttributes `json:"attributes" validate:"required"`
|
||||
}
|
||||
|
||||
type HelicopterFileCreateRequest struct {
|
||||
Data HelicopterFileCreateData `json:"data" validate:"required"`
|
||||
}
|
||||
|
||||
type HelicopterFileCreateBulkRequest struct {
|
||||
Data []HelicopterFileCreateData `json:"data" validate:"required,min=1"`
|
||||
}
|
||||
|
||||
type HelicopterFileUpdateAttributes struct {
|
||||
Section *string `json:"section,omitempty" validate:"omitempty,oneof=bfi fpwb afi" enums:"bfi,fpwb,afi" example:"fpwb"`
|
||||
Position *int `json:"position,omitempty" example:"1"`
|
||||
IsMandatory *bool `json:"is_mandatory,omitempty" example:"true"`
|
||||
HelicopterID *string `json:"helicopter_id,omitempty" example:"019d6771-5fb5-7337-837f-bc5ed85181a1"`
|
||||
FileName *string `json:"file_name,omitempty" example:"Before Flight Checklist v2.pdf"`
|
||||
}
|
||||
|
||||
type HelicopterFileUpdateData struct {
|
||||
Type string `json:"type" validate:"required,oneof=helicopter_file_update helicopter_file" example:"helicopter_file_update"`
|
||||
ID string `json:"id" validate:"required" example:"019d6773-ccca-71dd-b9c7-8e6f531deb90"`
|
||||
Attributes HelicopterFileUpdateAttributes `json:"attributes" validate:"required"`
|
||||
}
|
||||
|
||||
type HelicopterFileUpdateRequest struct {
|
||||
Data HelicopterFileUpdateData `json:"data" validate:"required"`
|
||||
}
|
||||
|
||||
type HelicopterFileUpdateBulkRequest struct {
|
||||
Data []HelicopterFileUpdateData `json:"data" validate:"required,min=1"`
|
||||
}
|
||||
|
||||
type HelicopterFileDeleteData struct {
|
||||
Type string `json:"type" validate:"required,oneof=helicopter_file_delete helicopter_file" example:"helicopter_file_delete"`
|
||||
ID string `json:"id" validate:"required" example:"019d6773-ccca-71dd-b9c7-8e6f531deb90"`
|
||||
}
|
||||
|
||||
type HelicopterFileDeleteRequest struct {
|
||||
Data HelicopterFileDeleteData `json:"data" validate:"required"`
|
||||
}
|
||||
70
internal/transport/http/dto/request/helicopter_request.go
Normal file
70
internal/transport/http/dto/request/helicopter_request.go
Normal file
@@ -0,0 +1,70 @@
|
||||
package request
|
||||
|
||||
import "wucher/internal/transport/http/dto"
|
||||
|
||||
type HelicopterCreateAttributes struct {
|
||||
Designation string `json:"designation" validate:"required" example:"OE-XHZ"`
|
||||
Identifier string `json:"identifier" validate:"required" example:"ABC"`
|
||||
Type string `json:"type" validate:"required" example:"EC-135"`
|
||||
SortKey *int `json:"sortkey" example:"10"`
|
||||
Engine1 *string `json:"engine_1,omitempty" example:"TM"`
|
||||
Engine2 *string `json:"engine_2,omitempty" example:"TM"`
|
||||
Consumption *float64 `json:"consumption_lt_min,omitempty" example:"4.1"`
|
||||
NR1 *bool `json:"nr1,omitempty" example:"true"`
|
||||
NR2 *bool `json:"nr2,omitempty" example:"true"`
|
||||
LH *bool `json:"lh,omitempty" example:"true"`
|
||||
RH *bool `json:"rh,omitempty" example:"true"`
|
||||
MGB *bool `json:"mgb,omitempty" example:"true"`
|
||||
IGB *bool `json:"igb,omitempty" example:"false"`
|
||||
TGB *bool `json:"tgb,omitempty" example:"true"`
|
||||
UTCOffset *int `json:"utc_offset,omitempty" example:"-1"`
|
||||
FileUUID *string `json:"file_uuid,omitempty" example:"018f3a5c-7a6f-7c2a-8d4a-4b5b1f6c8e90"`
|
||||
Dry *bool `json:"dry,omitempty" example:"false"`
|
||||
AOG *bool `json:"aog,omitempty" example:"false"`
|
||||
AOGReason *string `json:"aog_reason,omitempty" example:"Main rotor gearbox fault"`
|
||||
Note *string `json:"note,omitempty" example:"Primary HEMS helicopter"`
|
||||
IsActive *bool `json:"is_active,omitempty" example:"true"`
|
||||
}
|
||||
|
||||
type HelicopterCreateData struct {
|
||||
Type string `json:"type" validate:"required,oneof=helicopter_create helicopter" example:"helicopter_create"`
|
||||
Attributes HelicopterCreateAttributes `json:"attributes" validate:"required"`
|
||||
}
|
||||
|
||||
type HelicopterCreateRequest struct {
|
||||
Data HelicopterCreateData `json:"data" validate:"required"`
|
||||
}
|
||||
|
||||
type HelicopterUpdateAttributes struct {
|
||||
Designation *string `json:"designation,omitempty" example:"OE-XHZ"`
|
||||
Identifier *string `json:"identifier,omitempty" example:"ABC"`
|
||||
Type *string `json:"type,omitempty" example:"EC-135"`
|
||||
SortKey dto.NullInt `json:"sortkey,omitempty" swaggertype:"integer" example:"10"`
|
||||
Engine1 *string `json:"engine_1,omitempty" example:"TM"`
|
||||
Engine2 *string `json:"engine_2,omitempty" example:"TM"`
|
||||
Consumption *float64 `json:"consumption_lt_min,omitempty" example:"4.1"`
|
||||
NR1 *bool `json:"nr1,omitempty" example:"true"`
|
||||
NR2 *bool `json:"nr2,omitempty" example:"true"`
|
||||
LH *bool `json:"lh,omitempty" example:"true"`
|
||||
RH *bool `json:"rh,omitempty" example:"true"`
|
||||
MGB *bool `json:"mgb,omitempty" example:"true"`
|
||||
IGB *bool `json:"igb,omitempty" example:"false"`
|
||||
TGB *bool `json:"tgb,omitempty" example:"true"`
|
||||
UTCOffset *int `json:"utc_offset,omitempty" example:"-1"`
|
||||
FileUUID *string `json:"file_uuid,omitempty" example:"018f3a5c-7a6f-7c2a-8d4a-4b5b1f6c8e90"`
|
||||
Dry *bool `json:"dry,omitempty" example:"false"`
|
||||
AOG *bool `json:"aog,omitempty" example:"false"`
|
||||
AOGReason *string `json:"aog_reason,omitempty" example:"Main rotor gearbox fault"`
|
||||
Note *string `json:"note,omitempty" example:"Primary HEMS helicopter"`
|
||||
IsActive *bool `json:"is_active,omitempty" example:"true"`
|
||||
}
|
||||
|
||||
type HelicopterUpdateData struct {
|
||||
Type string `json:"type" validate:"required,oneof=helicopter_update helicopter" example:"helicopter_update"`
|
||||
ID string `json:"id" example:"018f3a5c-7a6f-7c2a-8d4a-4b5b1f6c8e9d"`
|
||||
Attributes HelicopterUpdateAttributes `json:"attributes" validate:"required"`
|
||||
}
|
||||
|
||||
type HelicopterUpdateRequest struct {
|
||||
Data HelicopterUpdateData `json:"data" validate:"required"`
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
package request
|
||||
|
||||
type MasterSettingsMicrosoftEntraUpsertAttributes struct {
|
||||
TenantID string `json:"MS_ENTRA_TENANT_ID" validate:"required" example:"your-tenant-id"`
|
||||
ClientID string `json:"MS_ENTRA_CLIENT_ID" validate:"required" example:"your-client-id"`
|
||||
ClientSecret string `json:"MS_ENTRA_CLIENT_SECRET" example:"********"`
|
||||
RedirectURL string `json:"MS_ENTRA_REDIRECT_URL" validate:"required" example:"https://example.com/api/v1/auth/microsoft/callback"`
|
||||
Authority string `json:"MS_ENTRA_AUTHORITY" validate:"required" example:"https://login.microsoftonline.com/"`
|
||||
Scopes string `json:"MS_ENTRA_SCOPES" validate:"required" example:"openid,profile,email"`
|
||||
}
|
||||
|
||||
type MasterSettingsMicrosoftEntraUpsertData struct {
|
||||
Type string `json:"type" validate:"required,oneof=master_settings_microsoft_entra_upsert master_settings" example:"master_settings_microsoft_entra_upsert"`
|
||||
Attributes MasterSettingsMicrosoftEntraUpsertAttributes `json:"attributes" validate:"required"`
|
||||
}
|
||||
|
||||
type MasterSettingsMicrosoftEntraUpsertRequest struct {
|
||||
Data MasterSettingsMicrosoftEntraUpsertData `json:"data" validate:"required"`
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
package request
|
||||
|
||||
type MasterSettingsCreateAttributes struct {
|
||||
CompanyName string `json:"company_name" validate:"required" example:"Wucher"`
|
||||
Title *string `json:"title,omitempty" example:"Welcome to Wucher"`
|
||||
Subtitle *string `json:"subtitle,omitempty" example:"Flight operations platform"`
|
||||
LogoFileUUID *string `json:"logo_file_uuid,omitempty" example:"018f3a5c-7a6f-7c2a-8d4a-4b5b1f6c8e9d"`
|
||||
CoverFileUUID *string `json:"cover_file_uuid,omitempty" example:"018f3a5c-7a6f-7c2a-8d4a-4b5b1f6c8e9d"`
|
||||
}
|
||||
|
||||
type MasterSettingsCreateData struct {
|
||||
Type string `json:"type" validate:"required,oneof=master_settings_create master_settings" example:"master_settings_create"`
|
||||
Attributes MasterSettingsCreateAttributes `json:"attributes" validate:"required"`
|
||||
}
|
||||
|
||||
type MasterSettingsCreateRequest struct {
|
||||
Data MasterSettingsCreateData `json:"data" validate:"required"`
|
||||
}
|
||||
|
||||
type MasterSettingsUpdateAttributes struct {
|
||||
CompanyName *string `json:"company_name,omitempty" example:"Wucher"`
|
||||
Title *string `json:"title,omitempty" example:"Welcome to Wucher"`
|
||||
Subtitle *string `json:"subtitle,omitempty" example:"Flight operations platform"`
|
||||
LogoFileUUID *string `json:"logo_file_uuid,omitempty" example:"018f3a5c-7a6f-7c2a-8d4a-4b5b1f6c8e9d"`
|
||||
CoverFileUUID *string `json:"cover_file_uuid,omitempty" example:"018f3a5c-7a6f-7c2a-8d4a-4b5b1f6c8e9d"`
|
||||
}
|
||||
|
||||
type MasterSettingsUpdateData struct {
|
||||
Type string `json:"type" validate:"required,oneof=master_settings_update master_settings" example:"master_settings_update"`
|
||||
ID string `json:"id" example:"018f3a5c-7a6f-7c2a-8d4a-4b5b1f6c8e9d"`
|
||||
Attributes MasterSettingsUpdateAttributes `json:"attributes" validate:"required"`
|
||||
}
|
||||
|
||||
type MasterSettingsUpdateRequest struct {
|
||||
Data MasterSettingsUpdateData `json:"data" validate:"required"`
|
||||
}
|
||||
52
internal/transport/http/dto/request/mcf_request.go
Normal file
52
internal/transport/http/dto/request/mcf_request.go
Normal file
@@ -0,0 +1,52 @@
|
||||
package request
|
||||
|
||||
// MCFSetAttributes toggles a helicopter's MCF (Yes/No) and is the DRAFT work endpoint.
|
||||
// active=true (or omitted) ACTIVATES it — creates the draft MCF if none is open, or
|
||||
// UPDATES the open draft with the provided check-flight fields (all optional; partial
|
||||
// edits allowed). Finalize it later via POST /mcf/sign (sign:true).
|
||||
// active=false DEACTIVATES it — cancels the open draft ("tidak jadi") and releases the
|
||||
// MCF status; the cancelled record stays in the list. A signed MCF is a real event and
|
||||
// is not cancelled by this.
|
||||
type MCFSetAttributes struct {
|
||||
Active *bool `json:"active,omitempty" example:"true"`
|
||||
// FlightID — OPTIONAL flight this check flight is performed on (on activate).
|
||||
FlightID string `json:"flight_id,omitempty" validate:"omitempty,uuid" example:"019d6774-98e8-7fba-9ef4-3795c0da8a13"`
|
||||
AFHours string `json:"af_hours,omitempty" example:"1245.3"`
|
||||
LandingCount *int `json:"landing_count,omitempty" example:"3"`
|
||||
UTCTime string `json:"utc_time,omitempty" example:"2026-06-17T08:30:00Z"`
|
||||
Date string `json:"date,omitempty" example:"2026-06-17"`
|
||||
Result string `json:"result,omitempty" validate:"omitempty,oneof=passed failed" enums:"passed,failed" example:"passed"`
|
||||
Notes *string `json:"notes,omitempty" example:"MCF required after engine mount work"`
|
||||
}
|
||||
|
||||
type MCFSetData struct {
|
||||
Type string `json:"type" validate:"required,oneof=mcf_set mcf" example:"mcf_set"`
|
||||
Attributes MCFSetAttributes `json:"attributes" validate:"required"`
|
||||
}
|
||||
|
||||
type MCFSetRequest struct {
|
||||
Data MCFSetData `json:"data" validate:"required"`
|
||||
}
|
||||
|
||||
// MCFCompleteAttributes signs (completes) or unsigns an MCF. sign toggles the state:
|
||||
// - sign=true (or omitted) SIGNS it — af_hours, landing_count, utc_time, date and
|
||||
// result are required. "passed" clears the aircraft; "failed" keeps it grounded.
|
||||
// - sign=false UNSIGNS it — reverts the MCF to a draft (the fields below are ignored).
|
||||
type MCFCompleteAttributes struct {
|
||||
Sign *bool `json:"sign,omitempty" example:"true"`
|
||||
AFHours string `json:"af_hours,omitempty" example:"1245.3"`
|
||||
LandingCount *int `json:"landing_count,omitempty" example:"3"`
|
||||
UTCTime string `json:"utc_time,omitempty" example:"2026-06-17T08:30:00Z"`
|
||||
Date string `json:"date,omitempty" example:"2026-06-17"`
|
||||
Result string `json:"result,omitempty" validate:"omitempty,oneof=passed failed" enums:"passed,failed" example:"passed"`
|
||||
Notes *string `json:"notes,omitempty" example:"Vibration within limits after check flight"`
|
||||
}
|
||||
|
||||
type MCFCompleteData struct {
|
||||
Type string `json:"type" validate:"required,oneof=mcf_complete mcf_sign mcf" example:"mcf_sign"`
|
||||
Attributes MCFCompleteAttributes `json:"attributes" validate:"required"`
|
||||
}
|
||||
|
||||
type MCFCompleteRequest struct {
|
||||
Data MCFCompleteData `json:"data" validate:"required"`
|
||||
}
|
||||
53
internal/transport/http/dto/request/mission_request.go
Normal file
53
internal/transport/http/dto/request/mission_request.go
Normal file
@@ -0,0 +1,53 @@
|
||||
package request
|
||||
|
||||
type MissionFileInput struct {
|
||||
UploadIntentUUID string `json:"upload_intent_uuid" example:"018f3a5c-7a6f-7c2a-8d4a-4b5b1f6c8e9d"`
|
||||
Name string `json:"name" example:"mission-brief.pdf"`
|
||||
}
|
||||
|
||||
type MissionCreateAttributes struct {
|
||||
FlightID string `json:"flight_id" validate:"required" example:"019d6774-98e8-7fba-9ef4-3795c0da8a13"`
|
||||
Type string `json:"type" validate:"required" example:"HEMS"`
|
||||
SubtypeID string `json:"subtype_id,omitempty" example:"019d6774-98e8-7fba-9ef4-3795c0da8a99"`
|
||||
Note string `json:"note,omitempty" example:"Mission note"`
|
||||
Files []MissionFileInput `json:"files,omitempty"`
|
||||
}
|
||||
|
||||
type MissionCreateData struct {
|
||||
Type string `json:"type" validate:"required,oneof=mission_create mission" example:"mission_create"`
|
||||
Attributes MissionCreateAttributes `json:"attributes" validate:"required"`
|
||||
}
|
||||
|
||||
type MissionCreateRequest struct {
|
||||
Data MissionCreateData `json:"data" validate:"required"`
|
||||
}
|
||||
|
||||
type MissionUpdateAttributes struct {
|
||||
Type string `json:"type" validate:"required" example:"SPO"`
|
||||
SubtypeID string `json:"subtype_id,omitempty" example:"019d6774-98e8-7fba-9ef4-3795c0da8a99"`
|
||||
Note string `json:"note,omitempty" example:"Mission note"`
|
||||
Files []MissionFileInput `json:"files,omitempty"`
|
||||
}
|
||||
|
||||
type MissionUpdateData struct {
|
||||
Type string `json:"type" validate:"required,oneof=mission_update mission" example:"mission_update"`
|
||||
Attributes MissionUpdateAttributes `json:"attributes" validate:"required"`
|
||||
}
|
||||
|
||||
type MissionUpdateRequest struct {
|
||||
Data MissionUpdateData `json:"data" validate:"required"`
|
||||
}
|
||||
|
||||
type MissionTypeCreateAttributes struct {
|
||||
CodeType string `json:"code_type" validate:"required" example:"HEMS"`
|
||||
TypeName string `json:"name" validate:"required" example:"HEMS"`
|
||||
}
|
||||
|
||||
type MissionTypeCreateData struct {
|
||||
Type string `json:"type" validate:"required,oneof=mission_type_create mission_type" example:"mission_type_create"`
|
||||
Attributes MissionTypeCreateAttributes `json:"attributes" validate:"required"`
|
||||
}
|
||||
|
||||
type MissionTypeCreateRequest struct {
|
||||
Data MissionTypeCreateData `json:"data" validate:"required"`
|
||||
}
|
||||
18
internal/transport/http/dto/request/other_person_request.go
Normal file
18
internal/transport/http/dto/request/other_person_request.go
Normal file
@@ -0,0 +1,18 @@
|
||||
package request
|
||||
|
||||
// OtherPersonAttributesInput carries the editable fields of a reusable non-staff person.
|
||||
type OtherPersonAttributesInput struct {
|
||||
Name string `json:"name" example:"Guest Person"`
|
||||
MobilePhone string `json:"mobile_phone,omitempty" example:"+43-1234"`
|
||||
Email string `json:"email,omitempty" example:"guest@example.com"`
|
||||
}
|
||||
|
||||
type OtherPersonRequestData struct {
|
||||
Type string `json:"type,omitempty" example:"other_person"`
|
||||
Attributes OtherPersonAttributesInput `json:"attributes"`
|
||||
}
|
||||
|
||||
// OtherPersonRequest is the JSON:API body for create/update of the other-people master.
|
||||
type OtherPersonRequest struct {
|
||||
Data OtherPersonRequestData `json:"data"`
|
||||
}
|
||||
249
internal/transport/http/dto/request/reserve_ac_request.go
Normal file
249
internal/transport/http/dto/request/reserve_ac_request.go
Normal file
@@ -0,0 +1,249 @@
|
||||
package request
|
||||
|
||||
import "encoding/json"
|
||||
|
||||
type ReserveAcCreateAttributes struct {
|
||||
AircraftID string `json:"helicopter_id" validate:"required" example:"019d6771-5fb5-7337-837f-bc5ed85181a1"`
|
||||
FlightID string `json:"flight_id" validate:"required" example:"019d6773-ccca-71dd-b9c7-8e6f531deb90"`
|
||||
Inspection ReserveAcInspectionCreateAttributes `json:"inspection" validate:"required"`
|
||||
}
|
||||
|
||||
type ReserveAcInspectionCreateAttributes struct {
|
||||
InspectionDate string `json:"inspection_date" validate:"required" example:"2026-04-15"`
|
||||
Before ReserveAcBeforeInspectionAttributes `json:"before" validate:"required"`
|
||||
Prepare ReserveAcPrepareInspectionAttributes `json:"prepare" validate:"required"`
|
||||
FleetStatusFile ReserveAcFleetStatusFileAttributes `json:"fleet_status_file,omitempty"`
|
||||
}
|
||||
|
||||
type ReserveAcBeforeInspectionAttributes struct {
|
||||
OilEngineNR1Checked *bool `json:"oil_engine_nr1_checked,omitempty"`
|
||||
OilEngineNR2Checked *bool `json:"oil_engine_nr2_checked,omitempty"`
|
||||
HydraulicLHChecked *bool `json:"hydraulic_lh_checked,omitempty"`
|
||||
HydraulicRHChecked *bool `json:"hydraulic_rh_checked,omitempty"`
|
||||
OilTransmissionMGBChecked *bool `json:"oil_transmission_mgb_checked,omitempty"`
|
||||
OilTransmissionIGBChecked *bool `json:"oil_transmission_igb_checked,omitempty"`
|
||||
OilTransmissionTGBChecked *bool `json:"oil_transmission_tgb_checked,omitempty"`
|
||||
FuelAmount *float64 `json:"fuel_amount,omitempty"`
|
||||
FuelUnit *string `json:"fuel_unit,omitempty"`
|
||||
FuelAddedAmount *float64 `json:"fuel_added_amount,omitempty"`
|
||||
Note *string `json:"note,omitempty"`
|
||||
FileChecklist []ReserveAcFileChecklistItem `json:"file_checklist,omitempty"`
|
||||
}
|
||||
|
||||
type ReserveAcFileChecklistItem struct {
|
||||
HelicopterFileID string `json:"helicopter_file_id" validate:"required" example:"019d7000-aaaa-7bbb-8ccc-111111111111"`
|
||||
IsDone bool `json:"is_done" example:"true"`
|
||||
}
|
||||
|
||||
type ReserveAcPrepareInspectionAttributes struct {
|
||||
NotamBriefing *bool `json:"notam_briefing,omitempty"`
|
||||
WeatherBriefing *bool `json:"weather_briefing,omitempty"`
|
||||
OperationalFlightPlan *bool `json:"operational_flight_plan,omitempty"`
|
||||
FileChecklist []ReserveAcFileChecklistItem `json:"file_checklist,omitempty"`
|
||||
}
|
||||
|
||||
type ReserveAcFleetStatusFileAttributes struct {
|
||||
FileChecklist []ReserveAcFileChecklistItem `json:"file_checklist,omitempty"`
|
||||
}
|
||||
|
||||
type ReserveAcAfterFlightInspectionAttributes struct {
|
||||
OilEngineNR1Checked *string `json:"oil_engine_nr1_checked,omitempty"`
|
||||
OilEngineNR2Checked *string `json:"oil_engine_nr2_checked,omitempty"`
|
||||
HydraulicLHChecked *string `json:"hydraulic_lh_checked,omitempty"`
|
||||
HydraulicRHChecked *string `json:"hydraulic_rh_checked,omitempty"`
|
||||
OilTransmissionMGBChecked *string `json:"oil_transmission_mgb_checked,omitempty"`
|
||||
OilTransmissionIGBChecked *string `json:"oil_transmission_igb_checked,omitempty"`
|
||||
OilTransmissionTGBChecked *string `json:"oil_transmission_tgb_checked,omitempty"`
|
||||
Note *string `json:"note,omitempty"`
|
||||
FileChecklist []ReserveAcFileChecklistItem `json:"file_checklist,omitempty"`
|
||||
}
|
||||
|
||||
type ReserveAcAfterFlightInspectionUpsertData struct {
|
||||
Type string `json:"type" validate:"required,oneof=after_flight_inspection_upsert after_flight_inspection"`
|
||||
Attributes ReserveAcAfterFlightInspectionAttributes `json:"attributes" validate:"required"`
|
||||
}
|
||||
|
||||
type ReserveAcAfterFlightInspectionUpsertRequest struct {
|
||||
Data ReserveAcAfterFlightInspectionUpsertData `json:"data" validate:"required"`
|
||||
}
|
||||
|
||||
type LastFlightsInspectionUpsertAttributes struct {
|
||||
FlightInspectionID string `json:"flight_inspection_id" validate:"required"`
|
||||
ReserveAcAfterFlightInspectionAttributes
|
||||
}
|
||||
|
||||
type LastFlightsInspectionCreateData struct {
|
||||
Type string `json:"type" validate:"required,oneof=last_flights_inspection_create last_flights_inspection"`
|
||||
Attributes LastFlightsInspectionUpsertAttributes `json:"attributes" validate:"required"`
|
||||
}
|
||||
|
||||
type LastFlightsInspectionCreateRequest struct {
|
||||
Data LastFlightsInspectionCreateData `json:"data" validate:"required"`
|
||||
}
|
||||
|
||||
type LastFlightsInspectionUpdateData struct {
|
||||
Type string `json:"type" validate:"required,oneof=last_flights_inspection_update last_flights_inspection"`
|
||||
Attributes LastFlightsInspectionUpsertAttributes `json:"attributes" validate:"required"`
|
||||
}
|
||||
|
||||
type LastFlightsInspectionUpdateRequest struct {
|
||||
Data LastFlightsInspectionUpdateData `json:"data" validate:"required"`
|
||||
}
|
||||
|
||||
type LastFlightsInspectionDeleteAttributes struct {
|
||||
FlightInspectionID string `json:"flight_inspection_id" validate:"required"`
|
||||
}
|
||||
|
||||
type LastFlightsInspectionDeleteData struct {
|
||||
Type string `json:"type" validate:"required,oneof=last_flights_inspection_delete last_flights_inspection"`
|
||||
Attributes LastFlightsInspectionDeleteAttributes `json:"attributes" validate:"required"`
|
||||
}
|
||||
|
||||
type LastFlightsInspectionDeleteRequest struct {
|
||||
Data LastFlightsInspectionDeleteData `json:"data" validate:"required"`
|
||||
}
|
||||
|
||||
type ReserveAcCreateData struct {
|
||||
Type string `json:"type" validate:"required,oneof=reserve_ac_create reserve_ac" example:"reserve_ac_create"`
|
||||
Attributes ReserveAcCreateAttributes `json:"attributes" validate:"required"`
|
||||
}
|
||||
|
||||
type ReserveAcCreateRequest struct {
|
||||
Data ReserveAcCreateData `json:"data" validate:"required"`
|
||||
}
|
||||
|
||||
type ReserveAcUpdateAttributes struct {
|
||||
AircraftID *string `json:"helicopter_id,omitempty" example:"019d6771-5fb5-7337-837f-bc5ed85181a1"`
|
||||
FlightID *string `json:"flight_id,omitempty" example:"019d6773-ccca-71dd-b9c7-8e6f531deb90"`
|
||||
Inspection *ReserveAcInspectionUpdateAttributes `json:"inspection,omitempty"`
|
||||
}
|
||||
|
||||
type ReserveAcInspectionUpdateAttributes struct {
|
||||
InspectionDate *string `json:"inspection_date,omitempty" example:"2026-04-15"`
|
||||
Before *ReserveAcBeforeInspectionAttributes `json:"before,omitempty"`
|
||||
Prepare *ReserveAcPrepareInspectionAttributes `json:"prepare,omitempty"`
|
||||
}
|
||||
|
||||
type ReserveAcUpdateData struct {
|
||||
Type string `json:"type" validate:"required,oneof=reserve_ac_update reserve_ac" example:"reserve_ac_update"`
|
||||
ID string `json:"id" example:"019d6773-ccca-71dd-b9c7-8e6f531deb90"`
|
||||
Attributes ReserveAcUpdateAttributes `json:"attributes" validate:"required"`
|
||||
}
|
||||
|
||||
type ReserveAcUpdateRequest struct {
|
||||
Data ReserveAcUpdateData `json:"data" validate:"required"`
|
||||
}
|
||||
|
||||
type ReserveAcDeleteData struct {
|
||||
ID string `json:"id" validate:"required" example:"019d6773-ccca-71dd-b9c7-8e6f531deb90"`
|
||||
}
|
||||
|
||||
type ReserveAcDeleteRequest struct {
|
||||
Data ReserveAcDeleteData `json:"data" validate:"required"`
|
||||
}
|
||||
|
||||
type TakeoverCreateAttributes struct {
|
||||
BaseID *string `json:"base_id,omitempty"`
|
||||
DutyDate string `json:"duty_date" validate:"required"`
|
||||
Notes *string `json:"notes,omitempty"`
|
||||
Detail *TakeoverRosterDetailsInput `json:"roster_detail,omitempty"`
|
||||
AircraftID *string `json:"helicopter_id,omitempty"`
|
||||
Inspection *ReserveAcInspectionCreateAttributes `json:"inspection,omitempty"`
|
||||
Files []TakeoverCreateFileInput `json:"files,omitempty" validate:"omitempty,dive"`
|
||||
EditedTemplateFileIDs []string `json:"edited_template_file_ids,omitempty" validate:"omitempty,dive,uuid"`
|
||||
}
|
||||
|
||||
type TakeoverCreateData struct {
|
||||
Type string `json:"type" validate:"required,oneof=takeover_create takeover"`
|
||||
Attributes TakeoverCreateAttributes `json:"attributes" validate:"required"`
|
||||
}
|
||||
|
||||
type TakeoverCreateRequest struct {
|
||||
Data TakeoverCreateData `json:"data" validate:"required"`
|
||||
}
|
||||
|
||||
type TakeoverCreateFileInput struct {
|
||||
FileID string `json:"file_id,omitempty" validate:"omitempty,uuid" example:"019e8713-aa19-7fb7-a510-a0332dc69697"`
|
||||
UploadIntentUUID string `json:"upload_intent_uuid,omitempty" validate:"omitempty,uuid" example:"019e8713-aa19-7fb7-a510-a0332dc69697"`
|
||||
Name string `json:"name,omitempty" example:"takeover-report.pdf"`
|
||||
}
|
||||
|
||||
type TakeoverUpdateAttributes struct {
|
||||
BaseID *string `json:"base_id,omitempty"`
|
||||
Bases *TakeoverBaseRef `json:"bases,omitempty"`
|
||||
DutyDate *string `json:"duty_date,omitempty"`
|
||||
Notes *string `json:"notes,omitempty"`
|
||||
Detail *TakeoverRosterDetailsInput `json:"roster_detail,omitempty"`
|
||||
RosterDetail *TakeoverRosterDetailsInput `json:"roster_details,omitempty"`
|
||||
AircraftID *string `json:"helicopter_id,omitempty"`
|
||||
Inspection *ReserveAcInspectionCreateAttributes `json:"inspection,omitempty"`
|
||||
Files []TakeoverCreateFileInput `json:"files,omitempty" validate:"omitempty,dive"`
|
||||
FilesAdd []TakeoverCreateFileInput `json:"files_add,omitempty" validate:"omitempty,dive"`
|
||||
FilesRemove []string `json:"files_remove,omitempty" validate:"omitempty,dive,uuid"`
|
||||
FilesReplace []TakeoverCreateFileInput `json:"files_replace,omitempty" validate:"omitempty,dive"`
|
||||
}
|
||||
|
||||
type TakeoverUpdateData struct {
|
||||
Type string `json:"type" validate:"required,oneof=takeover_update takeover"`
|
||||
ID string `json:"id"`
|
||||
Attributes TakeoverUpdateAttributes `json:"attributes" validate:"required"`
|
||||
}
|
||||
|
||||
type TakeoverUpdateRequest struct {
|
||||
Data TakeoverUpdateData `json:"data" validate:"required"`
|
||||
}
|
||||
|
||||
func (a *TakeoverUpdateAttributes) UnmarshalJSON(data []byte) error {
|
||||
type alias TakeoverUpdateAttributes
|
||||
var raw struct {
|
||||
alias
|
||||
Bases *TakeoverBaseRef `json:"bases,omitempty"`
|
||||
RosterDetail *TakeoverRosterDetailsInput `json:"roster_details,omitempty"`
|
||||
}
|
||||
if err := json.Unmarshal(data, &raw); err != nil {
|
||||
return err
|
||||
}
|
||||
*a = TakeoverUpdateAttributes(raw.alias)
|
||||
if a.BaseID == nil && raw.Bases != nil && raw.Bases.ID != "" {
|
||||
id := raw.Bases.ID
|
||||
a.BaseID = &id
|
||||
}
|
||||
if a.Detail == nil && raw.RosterDetail != nil {
|
||||
a.Detail = raw.RosterDetail
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
type TakeoverBaseRef struct {
|
||||
ID string `json:"id,omitempty"`
|
||||
}
|
||||
|
||||
type TakeoverRosterCrewInput struct {
|
||||
ID string `json:"id,omitempty" example:"018f3a5c-7a6f-7c2a-8d4a-4b5b1f6c8e9d"`
|
||||
}
|
||||
|
||||
type TakeoverRosterPilotInput struct {
|
||||
ID string `json:"id,omitempty" example:"018f3a5c-7a6f-7c2a-8d4a-4b5b1f6c8e9d"`
|
||||
FlightInstructor bool `json:"flight_instructor,omitempty"`
|
||||
LineChecker bool `json:"line_checker,omitempty"`
|
||||
Supervisor bool `json:"supervisor,omitempty"`
|
||||
Examiner bool `json:"examiner,omitempty"`
|
||||
CoPilot bool `json:"co_pilot,omitempty"`
|
||||
CrewType string `json:"crew_type,omitempty" example:"main" enums:"main,additional"`
|
||||
}
|
||||
|
||||
type TakeoverRosterOtherPersonInput struct {
|
||||
// OtherPersonID — OPTIONAL master person id to reuse (from GET /contact/other).
|
||||
OtherPersonID string `json:"other_person_id,omitempty" example:"019d6771-5fb5-7337-837f-bc5ed85181a1"`
|
||||
Name string `json:"name,omitempty" example:"Guest Person"`
|
||||
MobilePhone string `json:"mobile_phone,omitempty" example:"+43-1234"`
|
||||
Email string `json:"email,omitempty" example:"guest@example.com"`
|
||||
}
|
||||
|
||||
type TakeoverRosterDetailsInput struct {
|
||||
Pilot []TakeoverRosterPilotInput `json:"pilot"`
|
||||
Doctor []TakeoverRosterCrewInput `json:"doctor"`
|
||||
Rescuer []TakeoverRosterCrewInput `json:"rescuer"`
|
||||
Helper []TakeoverRosterCrewInput `json:"helper"`
|
||||
OtherPerson []TakeoverRosterOtherPersonInput `json:"other_person"`
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
package response
|
||||
|
||||
type ActionSignoffAttributes struct {
|
||||
HelicopterID string `json:"helicopter_id" example:"019d6771-5fb5-7337-837f-bc5ed85181a1"`
|
||||
FlightID string `json:"flight_id,omitempty" example:"019d6774-98e8-7fba-9ef4-3795c0da8a13"`
|
||||
// Signed — true once checked; gates the maintenance release form.
|
||||
Signed bool `json:"signed" example:"false"`
|
||||
// SignedAt / SignedBy — when/who signed; empty until signed.
|
||||
SignedAt *string `json:"signed_at,omitempty" example:"2026-06-16T03:04:05Z"`
|
||||
SignedBy string `json:"signed_by,omitempty" example:"019d61db-53c6-7280-87ec-e65f205c6d3b"`
|
||||
// SignedByName / SignedByShort — resolved display name + 3-letter short of the signer.
|
||||
SignedByName *string `json:"signed_by_name,omitempty" example:"John Doe"`
|
||||
SignedByShort *string `json:"signed_by_short,omitempty" example:"JDO"`
|
||||
CreatedAt string `json:"created_at,omitempty" example:"2026-06-16T03:04:05Z"`
|
||||
UpdatedAt string `json:"updated_at,omitempty" example:"2026-06-16T03:04:05Z"`
|
||||
}
|
||||
|
||||
type ActionSignoffResource struct {
|
||||
Type string `json:"type" example:"action_signoff"`
|
||||
ID string `json:"id,omitempty" example:"019d6771-5fb5-7337-837f-bc5ed85181a1"`
|
||||
Attributes ActionSignoffAttributes `json:"attributes"`
|
||||
}
|
||||
|
||||
type ActionSignoffResponse struct {
|
||||
Data ActionSignoffResource `json:"data"`
|
||||
}
|
||||
@@ -0,0 +1,124 @@
|
||||
package response
|
||||
|
||||
import shareddto "wucher/internal/transport/http/dto/shared"
|
||||
|
||||
type AirRescuerChecklistItemResource struct {
|
||||
ID string `json:"id,omitempty" example:"018f3a5c-7a6f-7c2a-8d4a-4b5b1f6c8e9d"`
|
||||
Name string `json:"name,omitempty" example:"Oxygen supply check"`
|
||||
Position int `json:"position,omitempty" example:"1"`
|
||||
CreatedBy string `json:"created_by,omitempty" example:"018f3a5c-7a6f-7c2a-8d4a-4b5b1f6c8e9d"`
|
||||
CreatedAt string `json:"created_at,omitempty" example:"2026-03-12T03:04:05Z"`
|
||||
UpdatedAt string `json:"updated_at,omitempty" example:"2026-03-12T03:04:05Z"`
|
||||
}
|
||||
|
||||
type AirRescuerChecklistAttributes struct {
|
||||
Bases shareddto.AirRescuerChecklistBaseInfo `json:"bases"`
|
||||
ScopeCode string `json:"scope_code,omitempty" example:"TU"`
|
||||
ScopeCodeDe string `json:"scope_code_de,omitempty" example:"DI"`
|
||||
Title string `json:"title,omitempty" example:"Monday Checklist"`
|
||||
Position int `json:"position,omitempty" example:"1"`
|
||||
Items []AirRescuerChecklistItemResource `json:"items"`
|
||||
CreatedBy string `json:"created_by,omitempty" example:"018f3a5c-7a6f-7c2a-8d4a-4b5b1f6c8e9d"`
|
||||
CreatedAt string `json:"created_at,omitempty" example:"2026-03-12T03:04:05Z"`
|
||||
UpdatedAt string `json:"updated_at,omitempty" example:"2026-03-12T03:04:05Z"`
|
||||
}
|
||||
|
||||
type AirRescuerChecklistResource struct {
|
||||
Type string `json:"type" example:"air_rescuer_checklist"`
|
||||
ID string `json:"id,omitempty" example:"018f3a5c-7a6f-7c2a-8d4a-4b5b1f6c8e9d"`
|
||||
Attributes AirRescuerChecklistAttributes `json:"attributes"`
|
||||
}
|
||||
|
||||
type AirRescuerChecklistResponse struct {
|
||||
Data AirRescuerChecklistResource `json:"data"`
|
||||
}
|
||||
|
||||
type AirRescuerChecklistBulkResponse struct {
|
||||
Data []AirRescuerChecklistResource `json:"data"`
|
||||
}
|
||||
|
||||
type AirRescuerChecklistItemAttributes struct {
|
||||
ChecklistID string `json:"checklist_id,omitempty" example:"018f3a5c-7a6f-7c2a-8d4a-4b5b1f6c8e9d"`
|
||||
Name string `json:"name,omitempty" example:"Check radio"`
|
||||
Position int `json:"position,omitempty" example:"1"`
|
||||
CreatedBy string `json:"created_by,omitempty" example:"018f3a5c-7a6f-7c2a-8d4a-4b5b1f6c8e9d"`
|
||||
CreatedAt string `json:"created_at,omitempty" example:"2026-03-12T03:04:05Z"`
|
||||
UpdatedAt string `json:"updated_at,omitempty" example:"2026-03-12T03:04:05Z"`
|
||||
}
|
||||
|
||||
type AirRescuerChecklistItemData struct {
|
||||
Type string `json:"type" example:"air_rescuer_checklist_item"`
|
||||
ID string `json:"id,omitempty" example:"018f3a5c-7a6f-7c2a-8d4a-4b5b1f6c8e9d"`
|
||||
Attributes AirRescuerChecklistItemAttributes `json:"attributes"`
|
||||
}
|
||||
|
||||
type AirRescuerChecklistItemResponse struct {
|
||||
Data AirRescuerChecklistItemData `json:"data"`
|
||||
}
|
||||
|
||||
type AirRescuerChecklistItemBulkResponse struct {
|
||||
Data []AirRescuerChecklistItemData `json:"data"`
|
||||
}
|
||||
|
||||
type AirRescuerChecklistCATChecklistSummary struct {
|
||||
ID string `json:"id,omitempty" example:"018f3a5c-7a6f-7c2a-8d4a-4b5b1f6c8e9d"`
|
||||
Title string `json:"title,omitempty" example:"Cockpit Flugretter"`
|
||||
Position int `json:"position,omitempty" example:"1"`
|
||||
CreatedBy string `json:"created_by,omitempty" example:"018f3a5c-7a6f-7c2a-8d4a-4b5b1f6c8e9d"`
|
||||
CreatedAt string `json:"created_at,omitempty" example:"2026-03-12T03:04:05Z"`
|
||||
UpdatedAt string `json:"updated_at,omitempty" example:"2026-03-12T03:04:05Z"`
|
||||
}
|
||||
|
||||
type AirRescuerChecklistCATGroup struct {
|
||||
ScopeCode string `json:"scope_code,omitempty" example:"DA"`
|
||||
ScopeCodeDe string `json:"scope_code_de,omitempty" example:"TA"`
|
||||
ScopeCodeName string `json:"scope_code_name,omitempty" example:"Daily"`
|
||||
ScopeCodeNameDe string `json:"scope_code_name_de,omitempty" example:"Taeglich"`
|
||||
Checklists []AirRescuerChecklistCATChecklistSummary `json:"checklists"`
|
||||
}
|
||||
|
||||
type AirRescuerChecklistTabChecklistDetail struct {
|
||||
ID string `json:"id,omitempty" example:"018f3a5c-7a6f-7c2a-8d4a-4b5b1f6c8e9d"`
|
||||
Title string `json:"title,omitempty" example:"Monday Checklist"`
|
||||
Position int `json:"position,omitempty" example:"1"`
|
||||
ChecklistItems []AirRescuerChecklistItemResource `json:"checklist_items"`
|
||||
CreatedBy string `json:"created_by,omitempty" example:"018f3a5c-7a6f-7c2a-8d4a-4b5b1f6c8e9d"`
|
||||
CreatedAt string `json:"created_at,omitempty" example:"2026-03-12T03:04:05Z"`
|
||||
UpdatedAt string `json:"updated_at,omitempty" example:"2026-03-12T03:04:05Z"`
|
||||
}
|
||||
|
||||
type AirRescuerChecklistTabResource struct {
|
||||
ScopeCode string `json:"scope_code,omitempty" example:"DA"`
|
||||
ScopeCodeDe string `json:"scope_code_de,omitempty" example:"TA"`
|
||||
ScopeCodeName string `json:"scope_code_name,omitempty" example:"Daily"`
|
||||
ScopeCodeNameDe string `json:"scope_code_name_de,omitempty" example:"Taeglich"`
|
||||
Groups []AirRescuerChecklistCATGroup `json:"groups,omitempty"`
|
||||
Checklists *[]AirRescuerChecklistTabChecklistDetail `json:"checklists,omitempty"`
|
||||
}
|
||||
|
||||
type AirRescuerChecklistBaseGroupAttributes struct {
|
||||
Bases shareddto.AirRescuerChecklistBaseInfo `json:"bases"`
|
||||
Tabs []AirRescuerChecklistTabResource `json:"tabs"`
|
||||
}
|
||||
|
||||
type AirRescuerChecklistBaseGroupResource struct {
|
||||
Type string `json:"type" example:"air_rescuer_checklist_group"`
|
||||
ID string `json:"id,omitempty" example:"base-018f3a5c-7a6f-7c2a-8d4a-4b5b1f6c8e9d"`
|
||||
Attributes AirRescuerChecklistBaseGroupAttributes `json:"attributes"`
|
||||
}
|
||||
|
||||
type AirRescuerChecklistGroupedListResponse struct {
|
||||
Data []AirRescuerChecklistBaseGroupResource `json:"data"`
|
||||
Meta struct {
|
||||
TotalBases int `json:"total_bases" example:"8"`
|
||||
} `json:"meta"`
|
||||
}
|
||||
|
||||
type AirRescuerChecklistGroupedDataTableResponse struct {
|
||||
Data []AirRescuerChecklistBaseGroupResource `json:"data"`
|
||||
Meta struct {
|
||||
Draw int `json:"draw" example:"1"`
|
||||
RecordsTotal int `json:"records_total" example:"8"`
|
||||
RecordsFiltered int `json:"records_filtered" example:"8"`
|
||||
} `json:"meta"`
|
||||
}
|
||||
98
internal/transport/http/dto/response/complaint_response.go
Normal file
98
internal/transport/http/dto/response/complaint_response.go
Normal file
@@ -0,0 +1,98 @@
|
||||
package response
|
||||
|
||||
import shareddto "wucher/internal/transport/http/dto/shared"
|
||||
|
||||
// ComplaintMEL groups the MEL deferral attributes. All fields are always present
|
||||
// (null when not set) so the response shape is predictable for clients.
|
||||
type ComplaintMEL struct {
|
||||
// Severity — MEL class: 0=NON-MEL, 1=A, 2=B, 3=C, 4=D. Always set (classification is required at creation).
|
||||
Severity int8 `json:"severity" example:"2" enums:"0,1,2,3,4"`
|
||||
// SeverityLabel — human label of the severity (NON-MEL/A/B/C/D).
|
||||
SeverityLabel *string `json:"severity_label" example:"B"`
|
||||
// ClassifiedAt / ClassifiedByName / ClassifiedByShort — when/who classified the MEL.
|
||||
ClassifiedAt *string `json:"classified_at" example:"2026-06-15T03:04:05Z"`
|
||||
ClassifiedByName *string `json:"classified_by_name" example:"John Doe"`
|
||||
ClassifiedByShort *string `json:"classified_by_short" example:"JDO"`
|
||||
// Deadline — grace deadline; after this the aircraft is AOG. null for NON-MEL.
|
||||
Deadline *string `json:"deadline" example:"2026-06-18T03:04:05Z"`
|
||||
}
|
||||
|
||||
type ComplaintNSR struct {
|
||||
// IsNSR — Non-Safety Release: true = aircraft released to fly despite the open defect.
|
||||
IsNSR bool `json:"is_nsr" example:"false"`
|
||||
// Reason — optional justification for the NSR decision.
|
||||
Reason *string `json:"reason" example:"not required"`
|
||||
// DecidedAt / DecidedByName / DecidedByShort — when/who decided NSR (may differ from the reporter).
|
||||
DecidedAt *string `json:"decided_at" example:"2026-06-15T03:04:05Z"`
|
||||
DecidedByName *string `json:"decided_by_name" example:"John Doe"`
|
||||
DecidedByShort *string `json:"decided_by_short" example:"JDO"`
|
||||
}
|
||||
|
||||
type ComplaintAttributes struct {
|
||||
HelicopterID string `json:"helicopter_id" example:"019d6771-5fb5-7337-837f-bc5ed85181a1"`
|
||||
FlightID string `json:"flight_id,omitempty" example:"019d6774-98e8-7fba-9ef4-3795c0da8a13"`
|
||||
Description string `json:"description" example:"Engine 2 N2 vibration intermittent during cruise above 110 KIAS"`
|
||||
Helicopter *shareddto.FleetStatusHelicopter `json:"helicopter,omitempty"`
|
||||
ReportedBy string `json:"reported_by" example:"John Doe"`
|
||||
ReportedByShort string `json:"reported_by_short,omitempty" example:"JDO"`
|
||||
ReportedAt string `json:"reported_at" example:"2026-06-15T03:04:05Z"`
|
||||
AircraftHoursAtReport *string `json:"aircraft_hours_at_report" example:"1245.3"`
|
||||
MEL ComplaintMEL `json:"mel"`
|
||||
NSR ComplaintNSR `json:"nsr"`
|
||||
ActionSigned bool `json:"action_signed" example:"true"`
|
||||
ActionTaken *string `json:"action_taken" example:"tightened mount"`
|
||||
ActionSignedAt *string `json:"action_signed_at" example:"2026-06-15T03:04:05Z"`
|
||||
ActionSignedByName *string `json:"action_signed_by_name" example:"John Doe"`
|
||||
ActionSignedByShort *string `json:"action_signed_by_short" example:"JDO"`
|
||||
AircraftHoursAtFix *string `json:"aircraft_hours_at_fix" example:"1250.1"`
|
||||
Status string `json:"status" example:"active_mel" enums:"active_non_mel,active_mel,serviced"`
|
||||
CreatedAt string `json:"created_at" example:"2026-06-15T03:04:05Z"`
|
||||
UpdatedAt string `json:"updated_at" example:"2026-06-15T03:04:05Z"`
|
||||
}
|
||||
|
||||
type ComplaintResource struct {
|
||||
Type string `json:"type" example:"complaint"`
|
||||
ID string `json:"id" example:"019d6771-5fb5-7337-837f-bc5ed85181a1"`
|
||||
Attributes ComplaintAttributes `json:"attributes"`
|
||||
}
|
||||
|
||||
type ComplaintResponse struct {
|
||||
Data ComplaintResource `json:"data"`
|
||||
}
|
||||
|
||||
type ComplaintListResponse struct {
|
||||
Data []ComplaintResource `json:"data"`
|
||||
Meta struct {
|
||||
PageNumber int `json:"page_number" example:"1"`
|
||||
PageSize int `json:"page_size" example:"20"`
|
||||
Total int64 `json:"total" example:"1"`
|
||||
} `json:"meta"`
|
||||
}
|
||||
|
||||
type ComplaintHelicopterGroupAttributes struct {
|
||||
Helicopter *shareddto.FleetStatusHelicopter `json:"helicopter"`
|
||||
Complaints []ComplaintResource `json:"complaints"`
|
||||
}
|
||||
|
||||
type ComplaintHelicopterGroupResource struct {
|
||||
Type string `json:"type" example:"complaint_helicopter_group"`
|
||||
Attributes ComplaintHelicopterGroupAttributes `json:"attributes"`
|
||||
}
|
||||
|
||||
type ComplaintGroupedListResponse struct {
|
||||
Data []ComplaintHelicopterGroupResource `json:"data"`
|
||||
Meta struct {
|
||||
PageNumber int `json:"page_number" example:"1"`
|
||||
PageSize int `json:"page_size" example:"20"`
|
||||
Total int64 `json:"total" example:"1"`
|
||||
} `json:"meta"`
|
||||
}
|
||||
|
||||
type ComplaintGroupedDataTableResponse struct {
|
||||
Data []ComplaintHelicopterGroupResource `json:"data"`
|
||||
Meta struct {
|
||||
Draw int `json:"draw" example:"1"`
|
||||
RecordsTotal int64 `json:"records_total" example:"1"`
|
||||
RecordsFiltered int64 `json:"records_filtered" example:"1"`
|
||||
} `json:"meta"`
|
||||
}
|
||||
227
internal/transport/http/dto/response/duty_roster_response.go
Normal file
227
internal/transport/http/dto/response/duty_roster_response.go
Normal file
@@ -0,0 +1,227 @@
|
||||
package response
|
||||
|
||||
type DutyRosterShiftTime struct {
|
||||
ShiftStart string `json:"shift_start,omitempty" example:"06:00"`
|
||||
ShiftEnd string `json:"shift_end,omitempty" example:"21:00"`
|
||||
}
|
||||
|
||||
type DutyRosterShiftDate struct {
|
||||
DateStart string `json:"date_start,omitempty" example:"2026-03-16"`
|
||||
DateEnd string `json:"date_end,omitempty" example:"2026-03-16"`
|
||||
}
|
||||
|
||||
type DutyRosterCrew struct {
|
||||
ID string `json:"id,omitempty" example:"018f3a5c-7a6f-7c2a-8d4a-4b5b1f6c8e9d"`
|
||||
Name string `json:"name,omitempty" example:"Hunter Wolfgang"`
|
||||
Role string `json:"role,omitempty" example:"doctor"`
|
||||
MobilePhone string `json:"mobile_phone,omitempty" example:"+43-1234"`
|
||||
Email string `json:"email,omitempty" example:"doctor@example.com"`
|
||||
ShiftDate *DutyRosterShiftDate `json:"shift_date,omitempty"`
|
||||
ShiftTime *DutyRosterShiftTime `json:"shift_time,omitempty"`
|
||||
}
|
||||
|
||||
type DutyRosterPilotCrew struct {
|
||||
ID string `json:"id,omitempty" example:"018f3a5c-7a6f-7c2a-8d4a-4b5b1f6c8e9d"`
|
||||
Name string `json:"name,omitempty" example:"Hunter Wolfgang"`
|
||||
Role string `json:"role,omitempty" example:"pilot"`
|
||||
MobilePhone string `json:"mobile_phone,omitempty" example:"+43-1234"`
|
||||
Email string `json:"email,omitempty" example:"pilot@example.com"`
|
||||
ShiftDate *DutyRosterShiftDate `json:"shift_date,omitempty"`
|
||||
CrewType string `json:"crew_type,omitempty" example:"main"`
|
||||
FlightInstructor *bool `json:"flight_instructor,omitempty"`
|
||||
LineChecker *bool `json:"line_checker,omitempty"`
|
||||
Supervisor *bool `json:"supervisor,omitempty"`
|
||||
Examiner *bool `json:"examiner,omitempty"`
|
||||
CoPilot *bool `json:"co_pilot,omitempty"`
|
||||
}
|
||||
|
||||
type DutyRosterDetailsResponse struct {
|
||||
Pilot []DutyRosterPilotCrew `json:"pilot"`
|
||||
Doctor []DutyRosterCrew `json:"doctor"`
|
||||
Rescuer []DutyRosterCrew `json:"rescuer"`
|
||||
Helper []DutyRosterCrew `json:"helper"`
|
||||
OtherPerson []DutyRosterCrew `json:"other_person"`
|
||||
}
|
||||
|
||||
type DutyRosterBaseInfo struct {
|
||||
ID string `json:"id,omitempty" example:"018f3a5c-7a6f-7c2a-8d4a-4b5b1f6c8e9d"`
|
||||
Type string `json:"type,omitempty" example:"hems"`
|
||||
BaseName string `json:"base_name,omitempty" example:"Gallus 1 - Zurs Lech"`
|
||||
BaseAbbreviation string `json:"base_abbreviation,omitempty" example:"G1"`
|
||||
ShiftTime DutyRosterShiftTime `json:"shift_time,omitempty"`
|
||||
}
|
||||
|
||||
type DutyRosterHelicopterSummary struct {
|
||||
ID string `json:"id" example:"018f3a5c-7a6f-7c2a-8d4a-4b5b1f6c8e9d"`
|
||||
Designation string `json:"designation" example:"OE-XHZ"`
|
||||
Identifier string `json:"identifier" example:"1234"`
|
||||
Type string `json:"type" example:"EC-135"`
|
||||
}
|
||||
|
||||
type DutyRosterAttributes struct {
|
||||
Bases DutyRosterBaseInfo `json:"bases"`
|
||||
FlightID string `json:"flight_id,omitempty" example:"018f3a5c-7a6f-7c2a-8d4a-4b5b1f6c8e9d"`
|
||||
Helicopter DutyRosterHelicopterSummary `json:"helicopter"`
|
||||
DutyDate string `json:"duty_date,omitempty" example:"2026-03-16"`
|
||||
ShiftTime DutyRosterShiftTime `json:"shift_time"`
|
||||
Detail DutyRosterDetailsResponse `json:"roster_detail"`
|
||||
CreatedBy string `json:"created_by,omitempty" example:"018f3a5c-7a6f-7c2a-8d4a-4b5b1f6c8e9d"`
|
||||
CreatedAt string `json:"created_at,omitempty" example:"2026-03-12T03:04:05Z"`
|
||||
UpdatedAt string `json:"updated_at,omitempty" example:"2026-03-12T03:04:05Z"`
|
||||
}
|
||||
|
||||
type DutyRosterResource struct {
|
||||
Type string `json:"type" example:"duty_roster"`
|
||||
ID string `json:"id,omitempty" example:"018f3a5c-7a6f-7c2a-8d4a-4b5b1f6c8e9d"`
|
||||
Attributes DutyRosterAttributes `json:"attributes"`
|
||||
}
|
||||
|
||||
type DutyRosterResponse struct {
|
||||
Data DutyRosterResource `json:"data"`
|
||||
}
|
||||
|
||||
type DutyRosterListResponse struct {
|
||||
Data []DutyRosterResource `json:"data"`
|
||||
Meta struct {
|
||||
Total int `json:"total" example:"31"`
|
||||
} `json:"meta"`
|
||||
}
|
||||
|
||||
type DutyRosterGetAllMeta struct {
|
||||
PageNumber int `json:"page_number"`
|
||||
PageSize int `json:"page_size"`
|
||||
RecordsTotal int `json:"records_total"`
|
||||
RecordsFiltered int `json:"records_filtered"`
|
||||
From string `json:"from"`
|
||||
To string `json:"to"`
|
||||
Timezone string `json:"timezone"`
|
||||
Total int `json:"total"`
|
||||
}
|
||||
|
||||
type DutyRosterGetAllData struct {
|
||||
HEMS []DutyRosterBaseGroupResource `json:"hems_base"`
|
||||
Base []DutyRosterBaseGroupResource `json:"base"`
|
||||
}
|
||||
|
||||
type DutyRosterGetAllResponse struct {
|
||||
Data DutyRosterGetAllData `json:"data"`
|
||||
Meta DutyRosterGetAllMeta `json:"meta"`
|
||||
}
|
||||
|
||||
type DutyRosterAnyResource struct {
|
||||
Type string `json:"type"`
|
||||
ID string `json:"id,omitempty"`
|
||||
Attributes map[string]any `json:"attributes"`
|
||||
}
|
||||
|
||||
type DutyRosterAnyResponse struct {
|
||||
Data DutyRosterAnyResource `json:"data"`
|
||||
}
|
||||
|
||||
type DutyRosterAnyListResponse struct {
|
||||
Data []DutyRosterAnyResource `json:"data"`
|
||||
Meta struct {
|
||||
Total int `json:"total" example:"31"`
|
||||
} `json:"meta"`
|
||||
}
|
||||
|
||||
type DutyRosterAnyDeleteResponse struct {
|
||||
Data DutyRosterAnyDeleteResource `json:"data"`
|
||||
}
|
||||
|
||||
type DutyRosterAnyDeleteResource struct {
|
||||
Type string `json:"type" example:"duty_roster_delete"`
|
||||
Attributes DutyRosterAnyDeleteAttributes `json:"attributes"`
|
||||
}
|
||||
|
||||
type DutyRosterAnyDeleteAttributes struct {
|
||||
Deleted bool `json:"deleted" example:"true"`
|
||||
ID string `json:"id" example:"018f3a5c-7a6f-7c2a-8d4a-4b5b1f6c8e9d"`
|
||||
Base DutyRosterBaseInfo `json:"base"`
|
||||
}
|
||||
|
||||
type DutyRosterUnifiedDataTableResponse struct {
|
||||
Data DutyRosterGetAllData `json:"data"`
|
||||
Meta DutyRosterUnifiedDataTableMeta `json:"meta"`
|
||||
}
|
||||
|
||||
type DutyRosterUnifiedDataTableMeta struct {
|
||||
Draw int `json:"draw" example:"1"`
|
||||
RecordsTotal int `json:"records_total" example:"20"`
|
||||
RecordsFiltered int `json:"records_filtered" example:"20"`
|
||||
PageNumber int `json:"page_number" example:"1"`
|
||||
PageSize int `json:"page_size" example:"20"`
|
||||
From string `json:"from" example:"2026-03-01"`
|
||||
To string `json:"to" example:"2026-03-31"`
|
||||
Timezone string `json:"timezone" example:"UTC"`
|
||||
Total int `json:"total" example:"20"`
|
||||
TotalGroups int `json:"total_groups" example:"20"`
|
||||
TotalRosters int `json:"total_rosters" example:"45"`
|
||||
NonEmptyGroups int `json:"non_empty_groups" example:"18"`
|
||||
}
|
||||
|
||||
type DutyRosterGroupRoster struct {
|
||||
ID string `json:"id,omitempty" example:"018f3a5c-7a6f-7c2a-8d4a-4b5b1f6c8e9d"`
|
||||
FlightID string `json:"flight_id,omitempty" example:"018f3a5c-7a6f-7c2a-8d4a-4b5b1f6c8e9d"`
|
||||
DutyDate string `json:"duty_date,omitempty" example:"2026-03-16"`
|
||||
ShiftTime DutyRosterShiftTime `json:"shift_time"`
|
||||
Helicopter DutyRosterHelicopterSummary `json:"helicopter"`
|
||||
Detail DutyRosterDetailsResponse `json:"roster_detail"`
|
||||
CreatedBy string `json:"created_by,omitempty" example:"018f3a5c-7a6f-7c2a-8d4a-4b5b1f6c8e9d"`
|
||||
CreatedAt string `json:"created_at,omitempty" example:"2026-03-12T03:04:05Z"`
|
||||
UpdatedAt string `json:"updated_at,omitempty" example:"2026-03-12T03:04:05Z"`
|
||||
}
|
||||
|
||||
type DutyRosterBaseGroupAttributes struct {
|
||||
Bases DutyRosterBaseInfo `json:"bases"`
|
||||
Rosters []DutyRosterGroupRoster `json:"rosters"`
|
||||
}
|
||||
|
||||
type DutyRosterBaseGroupResource struct {
|
||||
Type string `json:"type" example:"hems_base_roster_group"`
|
||||
Attributes DutyRosterBaseGroupAttributes `json:"attributes"`
|
||||
}
|
||||
|
||||
type DutyRosterGroupedListResponse struct {
|
||||
Data []DutyRosterBaseGroupResource `json:"data"`
|
||||
Meta struct {
|
||||
From string `json:"from,omitempty" example:"2026-03-16"`
|
||||
To string `json:"to,omitempty" example:"2026-04-15"`
|
||||
TotalBases int `json:"total_bases" example:"8"`
|
||||
} `json:"meta"`
|
||||
}
|
||||
|
||||
type DutyRosterGroupedDataTableResponse struct {
|
||||
Data []DutyRosterBaseGroupResource `json:"data"`
|
||||
Meta struct {
|
||||
Draw int `json:"draw" example:"1"`
|
||||
RecordsTotal int `json:"records_total" example:"8"`
|
||||
RecordsFiltered int `json:"records_filtered" example:"8"`
|
||||
} `json:"meta"`
|
||||
}
|
||||
|
||||
type DutyRosterDeletedCrewItem struct {
|
||||
ID string `json:"id,omitempty" example:"018f3a5c-7a6f-7c2a-8d4a-4b5b1f6c8e9d"`
|
||||
RosterID string `json:"roster_id,omitempty" example:"018f3a5c-7a6f-7c2a-8d4a-4b5b1f6c8e9d"`
|
||||
UserID string `json:"user_id,omitempty" example:"018f3a5c-7a6f-7c2a-8d4a-4b5b1f6c8e9d"`
|
||||
Name string `json:"name,omitempty" example:"Pilot One"`
|
||||
RoleCode string `json:"role_code,omitempty" example:"pilot"`
|
||||
CrewType string `json:"crew_type,omitempty" example:"main"`
|
||||
DateStart string `json:"date_start,omitempty" example:"2026-03-20"`
|
||||
DateEnd string `json:"date_end,omitempty" example:"2026-03-22"`
|
||||
}
|
||||
|
||||
type DutyRosterCrewDeleteAttributes struct {
|
||||
Deleted bool `json:"deleted" example:"true"`
|
||||
DeletedCount int `json:"deleted_count" example:"1"`
|
||||
DeletedItems []DutyRosterDeletedCrewItem `json:"deleted_items"`
|
||||
}
|
||||
|
||||
type DutyRosterCrewDeleteResource struct {
|
||||
Type string `json:"type" example:"duty_roster_crew_delete"`
|
||||
Attributes DutyRosterCrewDeleteAttributes `json:"attributes"`
|
||||
}
|
||||
|
||||
type DutyRosterCrewDeleteResponse struct {
|
||||
Data DutyRosterCrewDeleteResource `json:"data"`
|
||||
}
|
||||
@@ -0,0 +1,55 @@
|
||||
package response
|
||||
|
||||
type EASAReleaseAttributes struct {
|
||||
HelicopterID string `json:"helicopter_id" example:"019d6771-5fb5-7337-837f-bc5ed85181a1"`
|
||||
ComplaintID string `json:"complaint_id,omitempty" example:"019d6774-1111-7fba-9ef4-3795c0da8a13"`
|
||||
FlightID string `json:"flight_id,omitempty" example:"019d6774-98e8-7fba-9ef4-3795c0da8a13"`
|
||||
EASADate *string `json:"easa_date,omitempty" example:"2026-06-16"`
|
||||
EASAACHours *string `json:"easa_ac_hours,omitempty" example:"1245.3"`
|
||||
EASALocation *string `json:"easa_location,omitempty" example:"LOWI"`
|
||||
EASAWONo *string `json:"easa_wo_no,omitempty" example:"WO-2026-001"`
|
||||
EASAMaintManualRevAirframe *string `json:"easa_maint_manual_rev_airframe,omitempty" example:"Rev 12"`
|
||||
EASAMaintManualRevEngine *string `json:"easa_maint_manual_rev_engine,omitempty" example:"Rev 8"`
|
||||
// EASASignerID — contact id (UUIDv7) of the signer/technician.
|
||||
EASASignerID *string `json:"easa_signer_id,omitempty" example:"019d61db-53c6-7280-87ec-e65f205c6d3b"`
|
||||
// EASASignerName / EASASignerShort — resolved display name + 3-letter short of the signer contact.
|
||||
EASASignerName *string `json:"easa_signer_name,omitempty" example:"John Doe"`
|
||||
EASASignerShort *string `json:"easa_signer_short,omitempty" example:"JDO"`
|
||||
EASAPermitNo *string `json:"easa_permit_no,omitempty" example:"145.A.50"`
|
||||
// IsSigned — true once the release is signed (Release to Service); then the complaint becomes fixed.
|
||||
IsSigned bool `json:"is_signed" example:"false"`
|
||||
// SignedAt / SignedBy — when/who signed; empty until signed.
|
||||
SignedAt *string `json:"signed_at,omitempty" example:"2026-06-16T03:04:05Z"`
|
||||
SignedBy string `json:"signed_by,omitempty" example:"019d61db-53c6-7280-87ec-e65f205c6d3b"`
|
||||
// MissingSignFields — required fields still empty; must all be filled before it can be signed.
|
||||
MissingSignFields []string `json:"missing_sign_fields,omitempty"`
|
||||
CreatedAt string `json:"created_at" example:"2026-06-16T03:04:05Z"`
|
||||
UpdatedAt string `json:"updated_at" example:"2026-06-16T03:04:05Z"`
|
||||
}
|
||||
|
||||
type EASAReleaseResource struct {
|
||||
Type string `json:"type" example:"easa_release"`
|
||||
ID string `json:"id" example:"019d6771-5fb5-7337-837f-bc5ed85181a1"`
|
||||
Attributes EASAReleaseAttributes `json:"attributes"`
|
||||
}
|
||||
|
||||
type EASAReleaseResponse struct {
|
||||
Data EASAReleaseResource `json:"data"`
|
||||
}
|
||||
|
||||
// EASAReleaseDeleteResponse is the ack returned when an EASA release is voided/deleted.
|
||||
type EASAReleaseDeleteResponse struct {
|
||||
Data struct {
|
||||
Type string `json:"type" example:"easa_release_delete"`
|
||||
Attributes struct {
|
||||
Deleted bool `json:"deleted" example:"true"`
|
||||
} `json:"attributes"`
|
||||
} `json:"data"`
|
||||
}
|
||||
|
||||
type EASAReleaseListResponse struct {
|
||||
Data []EASAReleaseResource `json:"data"`
|
||||
Meta struct {
|
||||
Total int64 `json:"total" example:"1"`
|
||||
} `json:"meta"`
|
||||
}
|
||||
@@ -0,0 +1,85 @@
|
||||
package response
|
||||
|
||||
type FileManagerActionLocation struct {
|
||||
FolderID string `json:"folder_id,omitempty" example:"018f3a5c-7a6f-7c2a-8d4a-4b5b1f6c8e9d"`
|
||||
}
|
||||
|
||||
type FileManagerActionResultAttributes struct {
|
||||
Status string `json:"status" example:"trashed"`
|
||||
Location *FileManagerActionLocation `json:"location,omitempty"`
|
||||
}
|
||||
|
||||
type FileManagerActionResultResource struct {
|
||||
Type string `json:"type" example:"file_manager_delete_result"`
|
||||
ID string `json:"id" example:"018f3a5c-7a6f-7c2a-8d4a-4b5b1f6c8e9d"`
|
||||
Attributes FileManagerActionResultAttributes `json:"attributes"`
|
||||
}
|
||||
|
||||
type FileManagerActionResultResponse struct {
|
||||
Data FileManagerActionResultResource `json:"data"`
|
||||
}
|
||||
|
||||
type FileManagerMoveNodeResultAttributes struct {
|
||||
NodeType string `json:"node_type" example:"folder"`
|
||||
Status string `json:"status" example:"moved"`
|
||||
Location *FileManagerActionLocation `json:"location,omitempty"`
|
||||
}
|
||||
|
||||
type FileManagerMoveNodeResultResource struct {
|
||||
Type string `json:"type" example:"file_manager_move_result"`
|
||||
ID string `json:"id" example:"018f3a5c-7a6f-7c2a-8d4a-4b5b1f6c8e9d"`
|
||||
Attributes FileManagerMoveNodeResultAttributes `json:"attributes"`
|
||||
}
|
||||
|
||||
type FileManagerActionBulkError struct {
|
||||
Status int `json:"status" example:"422"`
|
||||
Code string `json:"code,omitempty" example:"FILE_MANAGER_VALIDATION_FAILED"`
|
||||
ErrorCode string `json:"error_code,omitempty" example:"4222111"`
|
||||
Title string `json:"title" example:"Validation error"`
|
||||
Detail string `json:"detail" example:"duplicate name in target folder"`
|
||||
}
|
||||
|
||||
type FileManagerMoveNodeBulkItem struct {
|
||||
Index int `json:"index" example:"0"`
|
||||
Success bool `json:"success" example:"true"`
|
||||
Resource *FileManagerMoveNodeResultResource `json:"resource,omitempty"`
|
||||
Error *FileManagerActionBulkError `json:"error,omitempty"`
|
||||
}
|
||||
|
||||
type FileManagerMoveNodeBulkResponse struct {
|
||||
Data []FileManagerMoveNodeBulkItem `json:"data"`
|
||||
Meta struct {
|
||||
Total int `json:"total" example:"3"`
|
||||
Success int `json:"success" example:"2"`
|
||||
Failed int `json:"failed" example:"1"`
|
||||
PartialSuccess bool `json:"partial_success" example:"true"`
|
||||
} `json:"meta"`
|
||||
}
|
||||
|
||||
type FileManagerPurgeNodeResultAttributes struct {
|
||||
NodeType string `json:"node_type" example:"folder"`
|
||||
Status string `json:"status" example:"purged"`
|
||||
}
|
||||
|
||||
type FileManagerPurgeNodeResultResource struct {
|
||||
Type string `json:"type" example:"file_manager_purge_result"`
|
||||
ID string `json:"id" example:"018f3a5c-7a6f-7c2a-8d4a-4b5b1f6c8e9d"`
|
||||
Attributes FileManagerPurgeNodeResultAttributes `json:"attributes"`
|
||||
}
|
||||
|
||||
type FileManagerPurgeNodeBulkItem struct {
|
||||
Index int `json:"index" example:"0"`
|
||||
Success bool `json:"success" example:"true"`
|
||||
Resource *FileManagerPurgeNodeResultResource `json:"resource,omitempty"`
|
||||
Error *FileManagerActionBulkError `json:"error,omitempty"`
|
||||
}
|
||||
|
||||
type FileManagerPurgeNodeBulkResponse struct {
|
||||
Data []FileManagerPurgeNodeBulkItem `json:"data"`
|
||||
Meta struct {
|
||||
Total int `json:"total" example:"3"`
|
||||
Success int `json:"success" example:"2"`
|
||||
Failed int `json:"failed" example:"1"`
|
||||
PartialSuccess bool `json:"partial_success" example:"true"`
|
||||
} `json:"meta"`
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
package response
|
||||
|
||||
import shareddto "wucher/internal/transport/http/dto/shared"
|
||||
|
||||
type FileManagerAttachmentResource struct {
|
||||
Type string `json:"type" example:"file_manager_attachment"`
|
||||
ID string `json:"id" example:"018f3a5c-7a6f-7c2a-8d4a-4b5b1f6c8e9d"`
|
||||
Attributes shareddto.FileManagerAttachmentAttributes `json:"attributes"`
|
||||
}
|
||||
|
||||
type FileManagerAttachmentResponse struct {
|
||||
Data FileManagerAttachmentResource `json:"data"`
|
||||
}
|
||||
|
||||
type FileManagerAttachmentListResponse struct {
|
||||
Data []FileManagerAttachmentResource `json:"data"`
|
||||
Meta struct {
|
||||
PageNumber int `json:"page_number" example:"1"`
|
||||
PageSize int `json:"page_size" example:"20"`
|
||||
Total int64 `json:"total" example:"120"`
|
||||
} `json:"meta"`
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
package response
|
||||
|
||||
type FileManagerFileUpdatedSSEPayload struct {
|
||||
FileID string `json:"file_id"`
|
||||
Status string `json:"status"`
|
||||
FolderID *string `json:"folder_id"`
|
||||
Name string `json:"name"`
|
||||
MimeType string `json:"mime_type"`
|
||||
UpdatedAt string `json:"updated_at"`
|
||||
}
|
||||
@@ -0,0 +1,145 @@
|
||||
package response
|
||||
|
||||
import shareddto "wucher/internal/transport/http/dto/shared"
|
||||
|
||||
type FileManagerFileResource struct {
|
||||
Type string `json:"type" example:"file_manager_file"`
|
||||
ID string `json:"id" example:"018f3a5c-7a6f-7c2a-8d4a-4b5b1f6c8e9d"`
|
||||
Attributes shareddto.FileManagerFileAttributes `json:"attributes"`
|
||||
}
|
||||
|
||||
type FileManagerFileListResource struct {
|
||||
Type string `json:"type" example:"file_manager_file"`
|
||||
ID string `json:"id" example:"018f3a5c-7a6f-7c2a-8d4a-4b5b1f6c8e9d"`
|
||||
Attributes shareddto.FileManagerFileListAttributes `json:"attributes"`
|
||||
}
|
||||
|
||||
type FileManagerFileResponse struct {
|
||||
Data struct {
|
||||
Type string `json:"type" example:"file_manager_file"`
|
||||
ID string `json:"id" example:"018f3a5c-7a6f-7c2a-8d4a-4b5b1f6c8e9d"`
|
||||
Attributes shareddto.FileManagerFileAttributes `json:"attributes"`
|
||||
} `json:"data"`
|
||||
}
|
||||
|
||||
type FileManagerFileDetailResource struct {
|
||||
Type string `json:"type" example:"file_manager_file"`
|
||||
ID string `json:"id" example:"018f3a5c-7a6f-7c2a-8d4a-4b5b1f6c8e9d"`
|
||||
Attributes shareddto.FileManagerFileDetailAttributes `json:"attributes"`
|
||||
}
|
||||
|
||||
type FileManagerFileDetailResponse struct {
|
||||
Data FileManagerFileDetailResource `json:"data"`
|
||||
}
|
||||
|
||||
type FileManagerFileListResponse struct {
|
||||
Data []FileManagerFileListResource `json:"data"`
|
||||
Meta struct {
|
||||
PageNumber int `json:"page_number" example:"1"`
|
||||
PageSize int `json:"page_size" example:"20"`
|
||||
Total int64 `json:"total" example:"120"`
|
||||
} `json:"meta"`
|
||||
}
|
||||
|
||||
type FileManagerFileDownloadURLResponse struct {
|
||||
Data struct {
|
||||
Type string `json:"type" example:"file_manager_file_download"`
|
||||
ID string `json:"id" example:"018f3a5c-7a6f-7c2a-8d4a-4b5b1f6c8e9d"`
|
||||
Attributes shareddto.FileManagerFileDownloadAttributes `json:"attributes"`
|
||||
} `json:"data"`
|
||||
}
|
||||
|
||||
type FileManagerFileUploadIntentResponse struct {
|
||||
Data struct {
|
||||
Type string `json:"type" example:"file_manager_file_upload_intent"`
|
||||
ID string `json:"id" example:"018f3a5c-7a6f-7c2a-8d4a-4b5b1f6c8e9d"`
|
||||
Attributes shareddto.FileManagerFileUploadIntentAttributes `json:"attributes"`
|
||||
} `json:"data"`
|
||||
}
|
||||
|
||||
type FileManagerFileUploadIntentResource struct {
|
||||
Type string `json:"type" example:"file_manager_file_upload_intent"`
|
||||
ID string `json:"id" example:"018f3a5c-7a6f-7c2a-8d4a-4b5b1f6c8e9d"`
|
||||
Attributes shareddto.FileManagerFileUploadIntentAttributes `json:"attributes"`
|
||||
}
|
||||
|
||||
type FileManagerFileWOPISessionData struct {
|
||||
FileID string `json:"file_id" example:"019f0386-0a00-7a4a-a2d8-30afc0e0c3ad"`
|
||||
CollaboraAction string `json:"collabora_action" example:"https://collabora.example.com/browser/123?WOPISrc=..."`
|
||||
AccessToken string `json:"access_token" example:"eyJhbGciOiJIUzI1NiIs..."`
|
||||
// AccessTokenTTL is the access token's absolute expiry time, in epoch
|
||||
// milliseconds (equal to the JWT exp claim), as required by WOPI/Collabora.
|
||||
AccessTokenTTL int64 `json:"access_token_ttl" example:"1782971996000"`
|
||||
}
|
||||
|
||||
type FileManagerFileWOPISessionResponse struct {
|
||||
Data FileManagerFileWOPISessionData `json:"data"`
|
||||
}
|
||||
|
||||
type FileManagerFileBulkError struct {
|
||||
Status int `json:"status" example:"422"`
|
||||
Code string `json:"code,omitempty" example:"FILE_MANAGER_VALIDATION_FAILED"`
|
||||
ErrorCode string `json:"error_code,omitempty" example:"4222111"`
|
||||
Title string `json:"title" example:"Validation error"`
|
||||
Detail string `json:"detail" example:"duplicate name in target folder"`
|
||||
}
|
||||
|
||||
type FileManagerFileBulkItem struct {
|
||||
Index int `json:"index" example:"0"`
|
||||
Success bool `json:"success" example:"true"`
|
||||
Message string `json:"message,omitempty" example:"created"`
|
||||
Resource *FileManagerFileResource `json:"resource,omitempty"`
|
||||
Error *FileManagerFileBulkError `json:"error,omitempty"`
|
||||
}
|
||||
|
||||
type FileManagerFileBulkResponse struct {
|
||||
Data []FileManagerFileBulkItem `json:"data"`
|
||||
Meta struct {
|
||||
Total int `json:"total" example:"3"`
|
||||
Success int `json:"success" example:"2"`
|
||||
Failed int `json:"failed" example:"1"`
|
||||
PartialSuccess bool `json:"partial_success" example:"true"`
|
||||
} `json:"meta"`
|
||||
}
|
||||
|
||||
type FileManagerFileUploadIntentBulkItem struct {
|
||||
Index int `json:"index" example:"0"`
|
||||
Success bool `json:"success" example:"true"`
|
||||
Resource *FileManagerFileUploadIntentResource `json:"resource,omitempty"`
|
||||
Error *FileManagerFileBulkError `json:"error,omitempty"`
|
||||
}
|
||||
|
||||
type FileManagerFileUploadIntentBulkResponse struct {
|
||||
Data []FileManagerFileUploadIntentBulkItem `json:"data"`
|
||||
Meta struct {
|
||||
Total int `json:"total" example:"3"`
|
||||
Success int `json:"success" example:"2"`
|
||||
Failed int `json:"failed" example:"1"`
|
||||
PartialSuccess bool `json:"partial_success" example:"true"`
|
||||
} `json:"meta"`
|
||||
}
|
||||
|
||||
type FileManagerFileCancelUploadResource struct {
|
||||
Type string `json:"type" example:"file_manager_file_cancel_upload_result"`
|
||||
ID string `json:"id" example:"018f3a5c-7a6f-7c2a-8d4a-4b5b1f6c8e9d"`
|
||||
Attributes struct {
|
||||
Status string `json:"status" example:"canceled"`
|
||||
} `json:"attributes"`
|
||||
}
|
||||
|
||||
type FileManagerFileCancelUploadBulkItem struct {
|
||||
Index int `json:"index" example:"0"`
|
||||
Success bool `json:"success" example:"true"`
|
||||
Resource *FileManagerFileCancelUploadResource `json:"resource,omitempty"`
|
||||
Error *FileManagerFileBulkError `json:"error,omitempty"`
|
||||
}
|
||||
|
||||
type FileManagerFileCancelUploadBulkResponse struct {
|
||||
Data []FileManagerFileCancelUploadBulkItem `json:"data"`
|
||||
Meta struct {
|
||||
Total int `json:"total" example:"3"`
|
||||
Success int `json:"success" example:"2"`
|
||||
Failed int `json:"failed" example:"1"`
|
||||
PartialSuccess bool `json:"partial_success" example:"true"`
|
||||
} `json:"meta"`
|
||||
}
|
||||
@@ -0,0 +1,99 @@
|
||||
package response
|
||||
|
||||
import shareddto "wucher/internal/transport/http/dto/shared"
|
||||
|
||||
type FileManagerFolderResource struct {
|
||||
Type string `json:"type" example:"file_manager_folder"`
|
||||
ID string `json:"id" example:"018f3a5c-7a6f-7c2a-8d4a-4b5b1f6c8e9d"`
|
||||
Attributes shareddto.FileManagerFolderAttributes `json:"attributes"`
|
||||
}
|
||||
|
||||
type FileManagerFolderResponse struct {
|
||||
Data FileManagerFolderResource `json:"data"`
|
||||
}
|
||||
|
||||
type FileManagerFolderDetailResource struct {
|
||||
Type string `json:"type" example:"file_manager_folder"`
|
||||
ID string `json:"id" example:"018f3a5c-7a6f-7c2a-8d4a-4b5b1f6c8e9d"`
|
||||
Attributes shareddto.FileManagerFolderDetailAttributes `json:"attributes"`
|
||||
}
|
||||
|
||||
type FileManagerFolderDetailResponse struct {
|
||||
Data FileManagerFolderDetailResource `json:"data"`
|
||||
}
|
||||
|
||||
type FileManagerFolderListResponse struct {
|
||||
Data []FileManagerFolderResource `json:"data"`
|
||||
Meta struct {
|
||||
PageNumber int `json:"page_number" example:"1"`
|
||||
PageSize int `json:"page_size" example:"20"`
|
||||
Total int64 `json:"total" example:"120"`
|
||||
} `json:"meta"`
|
||||
}
|
||||
|
||||
type FileManagerNodeAttributes struct {
|
||||
NodeType string `json:"node_type" example:"folder"`
|
||||
ParentID *string `json:"parent_id,omitempty" example:"018f3a5c-7a6f-7c2a-8d4a-4b5b1f6c8e9d"`
|
||||
Name string `json:"name" example:"operations"`
|
||||
Extension string `json:"extension,omitempty" example:"pdf"`
|
||||
SizeBytes int64 `json:"size_bytes,omitempty" example:"1024"`
|
||||
MimeType string `json:"mime_type,omitempty" example:"application/pdf"`
|
||||
DownloadURL string `json:"download_url,omitempty" example:"https://s3.localhost.localstack.cloud:4566/wucher-file-dev/files/2026/04/05/policy.pdf?X-Amz-Signature=..."`
|
||||
Status string `json:"status" example:"ready"`
|
||||
PathCache string `json:"path_cache,omitempty" example:"/root/operations"`
|
||||
CreatedAt string `json:"created_at,omitempty" example:"2026-03-12T03:04:05Z"`
|
||||
UpdatedAt string `json:"updated_at,omitempty" example:"2026-03-12T03:04:05Z"`
|
||||
}
|
||||
|
||||
type FileManagerNodeResource struct {
|
||||
Type string `json:"type" example:"file_manager_node"`
|
||||
ID string `json:"id" example:"018f3a5c-7a6f-7c2a-8d4a-4b5b1f6c8e9d"`
|
||||
Attributes FileManagerNodeAttributes `json:"attributes"`
|
||||
}
|
||||
|
||||
type FileManagerNodeListResponse struct {
|
||||
Data []FileManagerNodeResource `json:"data"`
|
||||
Meta struct {
|
||||
ParentUUID *string `json:"parent_uuid,omitempty" example:"018f3a5c-7a6f-7c2a-8d4a-4b5b1f6c8e9d"`
|
||||
Breadcrumb []FileManagerBreadcrumbItem `json:"breadcrumb,omitempty"`
|
||||
IsRoot bool `json:"is_root" example:"true"`
|
||||
Folders int `json:"folders" example:"4"`
|
||||
Files int `json:"files" example:"8"`
|
||||
Total int `json:"total" example:"12"`
|
||||
} `json:"meta"`
|
||||
}
|
||||
|
||||
type FileManagerBreadcrumbItem struct {
|
||||
ID string `json:"id" example:"018f3a5c-7a6f-7c2a-8d4a-4b5b1f6c8e9d"`
|
||||
Name string `json:"name" example:"operations"`
|
||||
}
|
||||
|
||||
type FileManagerTrashNodeAttributes struct {
|
||||
NodeType string `json:"node_type" example:"folder"`
|
||||
Name string `json:"name" example:"Documents"`
|
||||
Extension string `json:"extension,omitempty" example:"pdf"`
|
||||
SizeBytes int64 `json:"size_bytes,omitempty" example:"123456"`
|
||||
MimeType string `json:"mime_type,omitempty" example:"application/pdf"`
|
||||
DownloadURL string `json:"download_url,omitempty" example:"https://s3.localhost.localstack.cloud:4566/wucher-file-dev/files/2026/04/05/policy.pdf?X-Amz-Signature=..."`
|
||||
PathCache string `json:"path_cache,omitempty" example:"/Documents"`
|
||||
TrashedAt string `json:"trashed_at" example:"2026-04-01T10:00:00Z"`
|
||||
CreatedAt string `json:"created_at,omitempty" example:"2026-03-31T08:52:03Z"`
|
||||
UpdatedAt string `json:"updated_at,omitempty" example:"2026-03-31T08:52:03Z"`
|
||||
}
|
||||
|
||||
type FileManagerTrashNodeResource struct {
|
||||
Type string `json:"type" example:"file_manager_trash_node"`
|
||||
ID string `json:"id" example:"018f3a5c-7a6f-7c2a-8d4a-4b5b1f6c8e9d"`
|
||||
Attributes FileManagerTrashNodeAttributes `json:"attributes"`
|
||||
}
|
||||
|
||||
type FileManagerTrashListResponse struct {
|
||||
Data []FileManagerTrashNodeResource `json:"data"`
|
||||
Meta struct {
|
||||
PageNumber int `json:"page_number" example:"1"`
|
||||
PageSize int `json:"page_size" example:"20"`
|
||||
Total int `json:"total" example:"120"`
|
||||
Folders int `json:"folders" example:"50"`
|
||||
Files int `json:"files" example:"70"`
|
||||
} `json:"meta"`
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
package response
|
||||
|
||||
// FleetHistoryEntry is one event in a helicopter's unified timeline (complaint /
|
||||
// MEL / NSR / action / MCF / EASA / AOG events plus maintenance servicing).
|
||||
type FleetHistoryEntry struct {
|
||||
At string `json:"at" example:"2026-06-17T10:15:00Z"`
|
||||
EntityType string `json:"entity_type" example:"easa_release"`
|
||||
EntityID string `json:"entity_id,omitempty" example:"019d6771-5fb5-7337-837f-bc5ed85181a1"`
|
||||
Action string `json:"action" example:"easa_signed"`
|
||||
Message string `json:"message" example:"Release to Service (EASA signed)"`
|
||||
Actor string `json:"actor,omitempty" example:"019e91e6-2908-7f37-93e6-015ed1b7587d"`
|
||||
ActorName string `json:"actor_name,omitempty" example:"John Doe"`
|
||||
}
|
||||
|
||||
type FleetHistoryListResponse struct {
|
||||
Data []FleetHistoryEntry `json:"data"`
|
||||
Meta struct {
|
||||
PageNumber int `json:"page_number" example:"1"`
|
||||
PageSize int `json:"page_size" example:"20"`
|
||||
Total int64 `json:"total" example:"1"`
|
||||
} `json:"meta"`
|
||||
}
|
||||
107
internal/transport/http/dto/response/fleet_status_response.go
Normal file
107
internal/transport/http/dto/response/fleet_status_response.go
Normal file
@@ -0,0 +1,107 @@
|
||||
package response
|
||||
|
||||
import shareddto "wucher/internal/transport/http/dto/shared"
|
||||
|
||||
type FleetStatusAttributes struct {
|
||||
Helicopter *shareddto.FleetStatusHelicopter `json:"helicopter,omitempty"`
|
||||
Status string `json:"status" example:"active" enums:"active,serviced"`
|
||||
ServicedAt string `json:"serviced_at,omitempty" example:"2026-05-29T07:43:06Z"`
|
||||
MaintenanceSchedules []shareddto.MaintenanceScheduleAttributes `json:"maintenance_schedules"`
|
||||
Files []FleetStatusFile `json:"files"`
|
||||
Summary shareddto.FleetStatusSummary `json:"summary"`
|
||||
MCF *shareddto.FleetStatusMCF `json:"mcf,omitempty"`
|
||||
Complaints []shareddto.FleetStatusComplaint `json:"complaints,omitempty"`
|
||||
EASARelease *shareddto.FleetStatusEASARelease `json:"easa_release,omitempty"`
|
||||
CreatedAt string `json:"created_at,omitempty" example:"2026-05-29T07:43:06Z"`
|
||||
CreatedBy string `json:"created_by,omitempty" example:"John Doe"`
|
||||
UpdatedAt string `json:"updated_at,omitempty" example:"2026-05-29T07:43:06Z"`
|
||||
UpdatedBy string `json:"updated_by,omitempty" example:"John Doe"`
|
||||
}
|
||||
|
||||
type FleetStatusFile struct {
|
||||
ID string `json:"id,omitempty" example:"019d6773-ccca-71dd-b9c7-8e6f531deb90"`
|
||||
FileAttachmentID string `json:"file_attachment_id" example:"019d7000-aaaa-7bbb-8ccc-111111111111"`
|
||||
Category string `json:"category" example:"wb"`
|
||||
IsMandatory bool `json:"is_mandatory" example:"true"`
|
||||
Name string `json:"name,omitempty" example:"weight_balance.pdf"`
|
||||
DownloadURL string `json:"download_url,omitempty" example:"https://storage.example.com/fleet/wb.pdf?X-Amz-Signature=abc123"`
|
||||
ThumbnailDownloadURL string `json:"thumbnail_download_url,omitempty" example:"https://storage.example.com/fleet/wb.thumb.jpg?X-Amz-Signature=abc123"`
|
||||
SizeBytes *int64 `json:"size_bytes,omitempty" example:"1024000"`
|
||||
}
|
||||
|
||||
type FleetStatusResource struct {
|
||||
Type string `json:"type" example:"fleet_status"`
|
||||
ID string `json:"id" example:"019d6773-ccca-71dd-b9c7-8e6f531deb90"`
|
||||
Attributes FleetStatusAttributes `json:"attributes"`
|
||||
}
|
||||
|
||||
type FleetStatusResponse struct {
|
||||
Data FleetStatusResource `json:"data"`
|
||||
}
|
||||
|
||||
type FleetStatusListResponse struct {
|
||||
Data []FleetStatusResource `json:"data"`
|
||||
Meta struct {
|
||||
PageNumber int `json:"page_number" example:"1"`
|
||||
PageSize int `json:"page_size" example:"20"`
|
||||
Total int64 `json:"total" example:"100"`
|
||||
} `json:"meta"`
|
||||
}
|
||||
|
||||
type FleetStatusDataTableResponse struct {
|
||||
Data []FleetStatusResource `json:"data"`
|
||||
Meta struct {
|
||||
Draw int `json:"draw" example:"1"`
|
||||
RecordsTotal int64 `json:"records_total" example:"100"`
|
||||
RecordsFiltered int64 `json:"records_filtered" example:"20"`
|
||||
} `json:"meta"`
|
||||
}
|
||||
|
||||
// FleetStatusDeleteResponse is the ack returned when a fleet status is deleted.
|
||||
type FleetStatusDeleteResponse struct {
|
||||
Data struct {
|
||||
Type string `json:"type" example:"fleet_status_delete"`
|
||||
Attributes struct {
|
||||
Deleted bool `json:"deleted" example:"true"`
|
||||
} `json:"attributes"`
|
||||
} `json:"data"`
|
||||
}
|
||||
|
||||
// FleetStatusMarkServicedResponse is the ack returned when a fleet status is marked serviced.
|
||||
type FleetStatusMarkServicedResponse struct {
|
||||
Data struct {
|
||||
Type string `json:"type" example:"fleet_status_mark_serviced"`
|
||||
Attributes struct {
|
||||
Serviced bool `json:"serviced" example:"true"`
|
||||
} `json:"attributes"`
|
||||
} `json:"data"`
|
||||
}
|
||||
|
||||
type FleetStatusServiceLogAttributes struct {
|
||||
FleetStatusID string `json:"fleet_status_id" example:"019d6773-ccca-71dd-b9c7-8e6f531deb90"`
|
||||
HelicopterID string `json:"helicopter_id" example:"019d6771-5fb5-7337-837f-bc5ed85181a1"`
|
||||
Helicopter *shareddto.FleetStatusHelicopter `json:"helicopter,omitempty"`
|
||||
FleetStatusStatus string `json:"fleet_status_status,omitempty" example:"serviced"`
|
||||
InspectionType string `json:"inspection_type" example:"A/F"`
|
||||
Type string `json:"type" example:"100H"`
|
||||
Due string `json:"due" example:"1200 FH"`
|
||||
Ext string `json:"ext" example:"1300 FH"`
|
||||
ServicedAt string `json:"serviced_at" example:"2026-05-29T07:43:06Z"`
|
||||
CreatedAt string `json:"created_at" example:"2026-05-29T07:43:06Z"`
|
||||
CreatedBy string `json:"created_by" example:"John Doe"`
|
||||
}
|
||||
|
||||
type FleetStatusServiceLogResource struct {
|
||||
Type string `json:"type" example:"fleet_status_service_log"`
|
||||
ID string `json:"id" example:"019d6773-ccca-71dd-b9c7-8e6f531deb90"`
|
||||
Attributes FleetStatusServiceLogAttributes `json:"attributes"`
|
||||
}
|
||||
|
||||
type FleetStatusServiceLogListResponse struct {
|
||||
Data []FleetStatusServiceLogResource `json:"data"`
|
||||
Meta struct {
|
||||
PageNumber int `json:"page_number" example:"1"`
|
||||
PageSize int `json:"page_size" example:"20"`
|
||||
Total int64 `json:"total" example:"100"`
|
||||
} `json:"meta"`
|
||||
}
|
||||
139
internal/transport/http/dto/response/flight_data_response.go
Normal file
139
internal/transport/http/dto/response/flight_data_response.go
Normal file
@@ -0,0 +1,139 @@
|
||||
package response
|
||||
|
||||
type FlightDataAttributes struct {
|
||||
Status string `json:"status" example:"in_progress"`
|
||||
Mission *FlightDataMissionRef `json:"mission,omitempty"`
|
||||
RosterDetails *TakeoverDutyRosterDetailsResponse `json:"roster_details,omitempty"`
|
||||
HESLO *FlightDataSPOHESLODetails `json:"heslo,omitempty" extensions:"x-note=Only returned for SPO missions"`
|
||||
Logging *FlightDataSPOLoggingDetails `json:"logging,omitempty" extensions:"x-note=Only returned for SPO missions"`
|
||||
HEC *FlightDataSPOHECDetails `json:"hec,omitempty" extensions:"x-note=Only returned for SPO missions"`
|
||||
CoPilot *FlightDataCoPilotRef `json:"co_pilot,omitempty"`
|
||||
From *FlightDataLocationRef `json:"from,omitempty"`
|
||||
To *FlightDataLocationRef `json:"to,omitempty"`
|
||||
FlightTakeOff string `json:"flight_take_off,omitempty" example:"2026-04-20T08:00:00Z"`
|
||||
FlightLanding string `json:"flight_landing,omitempty" example:"2026-04-20T09:15:00Z"`
|
||||
FlightDuration int64 `json:"flight_duration_seconds" example:"4500"`
|
||||
FlightRED int64 `json:"flight_red_seconds" example:"300"`
|
||||
MaxN1 float64 `json:"max_n1" example:"97.5"`
|
||||
MaxN2 float64 `json:"max_n2" example:"98.1"`
|
||||
PaxCount int `json:"pax_count" example:"4"`
|
||||
LandingCount int `json:"landing_count" example:"1"`
|
||||
RotorBrakeCycle int `json:"rotor_brake_cycle" example:"1"`
|
||||
HookReleases int `json:"hook_releases" example:"0"`
|
||||
TicketNo string `json:"ticket_no,omitempty" example:"TKT-001"`
|
||||
Engine string `json:"engine,omitempty" example:"PW207D1"`
|
||||
DeliveryNoteNumber string `json:"delivery_note_number,omitempty" example:"DN-1001"`
|
||||
CustomerName string `json:"customer_name,omitempty" example:"Air Service"`
|
||||
FlightPlanDistance float64 `json:"flight_plan_distance" example:"120.5"`
|
||||
FlightPlanTimeSeconds int64 `json:"flight_plan_time_seconds" example:"4200"`
|
||||
FlightPlanTrueCourse float64 `json:"flight_plan_true_course" example:"182.3"`
|
||||
FuelBeforeFlight float64 `json:"fuel_before_flight" example:"420"`
|
||||
FuelUpload float64 `json:"fuel_upload" example:"100"`
|
||||
FuelAfterFlight float64 `json:"fuel_after_flight" example:"280"`
|
||||
FuelPlanning float64 `json:"fuel_planning" example:"390"`
|
||||
FlightPositioning bool `json:"flight_positioning" example:"false"`
|
||||
OtherInformation string `json:"other_information,omitempty"`
|
||||
CreatedAt string `json:"created_at,omitempty" example:"2026-04-20T08:00:00Z"`
|
||||
CreatedBy string `json:"created_by,omitempty" example:"019d61db-53c6-7280-87ec-e65f205c6d3b"`
|
||||
UpdatedAt string `json:"updated_at,omitempty" example:"2026-04-20T08:00:00Z"`
|
||||
UpdatedBy string `json:"updated_by,omitempty" example:"019d61db-53c6-7280-87ec-e65f205c6d3b"`
|
||||
}
|
||||
|
||||
type FlightDataSPOHESLODetails struct {
|
||||
Flights []FlightDataSPOHESLOFlight `json:"flights"`
|
||||
}
|
||||
|
||||
type FlightDataSPOHESLOFlight struct {
|
||||
ID string `json:"id"`
|
||||
ROT int `json:"rot"`
|
||||
// Facility category: heslo-rope-label.
|
||||
HESLORopeLabelID string `json:"heslo_rope_label_id,omitempty"`
|
||||
HESLORopeLabelName string `json:"heslo_rope_label_name,omitempty"`
|
||||
Slings []FlightDataSPOHESLOSling `json:"slings"`
|
||||
}
|
||||
|
||||
type FlightDataSPOHESLOSling struct {
|
||||
ID string `json:"id"`
|
||||
// Facility category: heslo-rope-length.
|
||||
SlingID string `json:"sling_id,omitempty"`
|
||||
SlingName string `json:"sling_name,omitempty"`
|
||||
}
|
||||
|
||||
type FlightDataSPOLoggingDetails struct {
|
||||
Slings []FlightDataSPOLoggingSling `json:"slings"`
|
||||
}
|
||||
|
||||
type FlightDataSPOLoggingSling struct {
|
||||
ID string `json:"id"`
|
||||
// Facility category: heslo-hook.
|
||||
SlingID string `json:"sling_id,omitempty"`
|
||||
SlingName string `json:"sling_name,omitempty"`
|
||||
}
|
||||
|
||||
type FlightDataSPOHECDetails struct {
|
||||
Flights []FlightDataSPOHECFlight `json:"flights"`
|
||||
Slings []FlightDataSPOHECSling `json:"slings"`
|
||||
Loads []FlightDataSPOHECLoad `json:"loads"`
|
||||
}
|
||||
|
||||
type FlightDataSPOHECFlight struct {
|
||||
ID string `json:"id"`
|
||||
ROT int `json:"rot"`
|
||||
HECCycle int `json:"hec_cycle"`
|
||||
// Facility category: hec-rope-label.
|
||||
EquipmentID string `json:"equipment_id,omitempty"`
|
||||
EquipmentName string `json:"equipment_name,omitempty"`
|
||||
}
|
||||
|
||||
type FlightDataSPOHECSling struct {
|
||||
ID string `json:"id"`
|
||||
// Facility category: hec-rope-length.
|
||||
SlingID string `json:"sling_id,omitempty"`
|
||||
SlingName string `json:"sling_name,omitempty"`
|
||||
}
|
||||
|
||||
type FlightDataSPOHECLoad struct {
|
||||
ID string `json:"id"`
|
||||
// Weight band for the HEC load, e.g. "bis 199 kg", "200-600 kg", "600-800 kg", "800-1000 kg".
|
||||
LoadCategory string `json:"load_category,omitempty" example:"200-600 kg"`
|
||||
Quantity int `json:"quantity" example:"3"`
|
||||
}
|
||||
|
||||
type FlightDataMissionRef struct {
|
||||
ID string `json:"id,omitempty" example:"019d6774-98e8-7fba-9ef4-3795c0da8a13"`
|
||||
Type string `json:"type,omitempty" example:"HEMS"`
|
||||
}
|
||||
|
||||
type FlightDataCoPilotRef struct {
|
||||
ID string `json:"id,omitempty" example:"019d61db-53c6-7280-87ec-e65f205c6d3b"`
|
||||
FirstName string `json:"first_name,omitempty" example:"John"`
|
||||
LastName string `json:"last_name,omitempty" example:"Doe"`
|
||||
Email string `json:"email,omitempty" example:"john.doe@example.com"`
|
||||
}
|
||||
|
||||
type FlightDataLocationRef struct {
|
||||
Type string `json:"type,omitempty" example:"icao"`
|
||||
ID string `json:"id,omitempty" example:"019d6774-98e8-7fba-9ef4-3795c0da8a13"`
|
||||
Code string `json:"code,omitempty" example:"WIII"`
|
||||
Name string `json:"name,omitempty" example:"General Hospital"`
|
||||
Label string `json:"label,omitempty" example:"WIII"`
|
||||
}
|
||||
|
||||
type FlightDataResource struct {
|
||||
Type string `json:"type" example:"flight_data"`
|
||||
ID string `json:"id" example:"019d6774-98e8-7fba-9ef4-3795c0da8a13"`
|
||||
Attributes FlightDataAttributes `json:"attributes"`
|
||||
}
|
||||
|
||||
type FlightDataResponse struct {
|
||||
Data FlightDataResource `json:"data"`
|
||||
}
|
||||
|
||||
type FlightDataListResponse struct {
|
||||
Data []FlightDataResource `json:"data"`
|
||||
Meta struct {
|
||||
PageNumber int `json:"page_number" example:"1"`
|
||||
PageSize int `json:"page_size" example:"20"`
|
||||
Total int64 `json:"total" example:"1"`
|
||||
} `json:"meta"`
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
package response
|
||||
|
||||
import shareddto "wucher/internal/transport/http/dto/shared"
|
||||
|
||||
type FlightInspectionFileChecklistResource struct {
|
||||
Type string `json:"type" example:"flight_inspection_file_checklist"`
|
||||
ID string `json:"id" example:"019d6773-ccca-71dd-b9c7-8e6f531deb90"`
|
||||
Attributes shareddto.FlightInspectionFileChecklistAttributes `json:"attributes"`
|
||||
}
|
||||
|
||||
type FlightInspectionFileChecklistResponse struct {
|
||||
Data FlightInspectionFileChecklistResource `json:"data"`
|
||||
}
|
||||
|
||||
type FlightInspectionFileChecklistListResponse struct {
|
||||
Data []FlightInspectionFileChecklistResource `json:"data"`
|
||||
Meta struct {
|
||||
PageNumber int `json:"page_number" example:"1"`
|
||||
PageSize int `json:"page_size" example:"20"`
|
||||
Total int64 `json:"total" example:"1"`
|
||||
} `json:"meta"`
|
||||
}
|
||||
91
internal/transport/http/dto/response/flight_response.go
Normal file
91
internal/transport/http/dto/response/flight_response.go
Normal file
@@ -0,0 +1,91 @@
|
||||
package response
|
||||
|
||||
type FlightAttributes struct {
|
||||
Status string `json:"status" example:"draft"`
|
||||
MissionID string `json:"mission_id,omitempty" example:"019e9640-f7af-7940-80e8-1e9a83013afb"`
|
||||
Code string `json:"code" example:"M-062226-001"`
|
||||
Date string `json:"date" example:"2026-04-14"`
|
||||
TotalDraft int `json:"total_draft" example:"6"`
|
||||
Takeover *FlightTakeoverSummary `json:"takeover,omitempty"`
|
||||
Steps []FlightStepItem `json:"steps"`
|
||||
Draft []FlightDraftGroup `json:"draft,omitempty"`
|
||||
CreatedAt string `json:"created_at,omitempty" example:"2026-04-14T08:00:00Z"`
|
||||
CreatedBy *UserRef `json:"created_by,omitempty"`
|
||||
UpdatedAt string `json:"updated_at,omitempty" example:"2026-04-14T08:00:00Z"`
|
||||
UpdatedBy *UserRef `json:"updated_by,omitempty"`
|
||||
DeletedAt string `json:"deleted_at,omitempty" example:"2026-04-14T08:00:00Z"`
|
||||
DeletedBy *UserRef `json:"deleted_by,omitempty"`
|
||||
}
|
||||
|
||||
type FlightStepItem struct {
|
||||
Phase string `json:"phase" example:"duty_roster"`
|
||||
Position string `json:"position,omitempty" example:"takeover"`
|
||||
Step int `json:"step,omitempty" example:"1"`
|
||||
Complete bool `json:"complete" example:"true"`
|
||||
ID string `json:"id,omitempty" example:"019d6772-471c-7b70-b42a-d94020ef61e4"`
|
||||
Status string `json:"status" example:"completed"`
|
||||
MissionType []FlightMissionTypeSummary `json:"mission_type,omitempty"`
|
||||
CompletedAt string `json:"completed_at" example:"2026-04-14T08:00:00Z"`
|
||||
CompletedBy string `json:"completed_by" example:"019d61db-53c6-7280-87ec-e65f205c6d3b"`
|
||||
}
|
||||
|
||||
type FlightHelicopterSummary struct {
|
||||
AOG bool `json:"aog" example:"false"`
|
||||
Designation string `json:"designation" example:"AIRBUS H145"`
|
||||
ID string `json:"id" example:"019d6771-5fb5-7337-837f-bc5ed85181a1"`
|
||||
Identifier string `json:"identifier" example:"D-HYAR"`
|
||||
IsActive bool `json:"is_active" example:"true"`
|
||||
Type string `json:"type" example:"H145"`
|
||||
}
|
||||
|
||||
type FlightTakeoverSummary struct {
|
||||
ID string `json:"id,omitempty" example:"019e8713-aa19-7fb7-a510-a0332dc69697"`
|
||||
Helicopter *FlightHelicopterSummary `json:"helicopter,omitempty"`
|
||||
DailyInspection *FlightDailyInspectionSummary `json:"daily_inspection,omitempty"`
|
||||
RosterDetail *TakeoverDutyRosterDetailsResponse `json:"roster_detail,omitempty"`
|
||||
}
|
||||
|
||||
type FlightDailyInspectionSummary struct {
|
||||
ID string `json:"id" example:"019d6772-471c-7b70-b42a-d94020ef61e4"`
|
||||
Name string `json:"name" example:"Ganahl Stefan"`
|
||||
ShortName string `json:"short_name" example:"GAN"`
|
||||
Date string `json:"date" example:"17.04.2026"`
|
||||
UTCTime string `json:"utc_time" example:"10:57"`
|
||||
}
|
||||
|
||||
type FlightDraftItem struct {
|
||||
ID string `json:"id,omitempty" example:"019d6774-98e8-7fba-9ef4-3795c0da8a13"`
|
||||
Type string `json:"type" example:"HEMS"`
|
||||
Status string `json:"status" example:"draft"`
|
||||
FlightDataID string `json:"flight_data_id,omitempty" example:"019d6774-98e8-7fba-9ef4-3795c0da8a55"`
|
||||
}
|
||||
|
||||
type FlightDraftGroup struct {
|
||||
Type string `json:"type" example:"HEMS"`
|
||||
Items []FlightDraftItem `json:"items"`
|
||||
}
|
||||
|
||||
type FlightMissionTypeSummary struct {
|
||||
Type string `json:"type" example:"HEMS"`
|
||||
Draft int `json:"draft" example:"5"`
|
||||
Completed int `json:"completed" example:"0"`
|
||||
}
|
||||
|
||||
type FlightResource struct {
|
||||
Type string `json:"type" example:"flight"`
|
||||
ID string `json:"id" example:"019d6774-98e8-7fba-9ef4-3795c0da8a13"`
|
||||
Attributes FlightAttributes `json:"attributes"`
|
||||
}
|
||||
|
||||
type FlightResponse struct {
|
||||
Data FlightResource `json:"data"`
|
||||
}
|
||||
|
||||
type FlightListResponse struct {
|
||||
Data []FlightResource `json:"data"`
|
||||
Meta struct {
|
||||
PageNumber int `json:"page_number" example:"1"`
|
||||
PageSize int `json:"page_size" example:"20"`
|
||||
Total int64 `json:"total" example:"1"`
|
||||
} `json:"meta"`
|
||||
}
|
||||
170
internal/transport/http/dto/response/helicopter_file_response.go
Normal file
170
internal/transport/http/dto/response/helicopter_file_response.go
Normal file
@@ -0,0 +1,170 @@
|
||||
package response
|
||||
|
||||
import (
|
||||
shareddto "wucher/internal/transport/http/dto/shared"
|
||||
"wucher/internal/transport/http/jsonapi"
|
||||
)
|
||||
|
||||
type HelicopterFileResource struct {
|
||||
Type string `json:"type" example:"helicopter_file"`
|
||||
ID string `json:"id" example:"019d6773-ccca-71dd-b9c7-8e6f531deb90"`
|
||||
Attributes shareddto.HelicopterFileAttributes `json:"attributes"`
|
||||
}
|
||||
|
||||
type HelicopterFileResponse struct {
|
||||
Data HelicopterFileResource `json:"data"`
|
||||
}
|
||||
|
||||
type HelicopterFileListResponse struct {
|
||||
Data []HelicopterFileResource `json:"data"`
|
||||
Meta struct {
|
||||
PageNumber int `json:"page_number" example:"1"`
|
||||
PageSize int `json:"page_size" example:"20"`
|
||||
Total int64 `json:"total" example:"1"`
|
||||
} `json:"meta"`
|
||||
}
|
||||
|
||||
type HelicopterFileCollectionHelicopter struct {
|
||||
ID string `json:"id" example:"019d6771-5fb5-7337-837f-bc5ed85181a1"`
|
||||
Name string `json:"name" example:"AIRBUS H145"`
|
||||
}
|
||||
|
||||
type HelicopterFileCollectionAttachment struct {
|
||||
ID string `json:"id" example:"019d6771-6c8e-7a2d-a7b8-03bcb65fcb3e"`
|
||||
FileName string `json:"file_name" example:"Before Flight Checklist.pdf"`
|
||||
DownloadURL string `json:"download_url" example:"https://s3.localhost.localstack.cloud:4566/wucher-file-dev/files/2026/04/05/policy.pdf?X-Amz-Signature=..."`
|
||||
}
|
||||
|
||||
type HelicopterFileCollectionItem struct {
|
||||
HelicopterFileID string `json:"helicopter_file_id" example:"019d6773-ccca-71dd-b9c7-8e6f531deb90"`
|
||||
Section string `json:"section" example:"bfi"`
|
||||
Position int `json:"position" example:"1"`
|
||||
IsMandatory bool `json:"is_mandatory" example:"true"`
|
||||
IsTemplate bool `json:"is_template" example:"true"`
|
||||
AttachmentPending bool `json:"attachment_pending" example:"true"`
|
||||
Attachment HelicopterFileCollectionAttachment `json:"attachment"`
|
||||
CreatedAt string `json:"created_at" example:"2026-04-14T08:00:00Z"`
|
||||
CreatedBy string `json:"created_by" example:"019d61db-53c6-7280-87ec-e65f205c6d3b"`
|
||||
UpdatedAt string `json:"updated_at" example:"2026-04-14T08:00:00Z"`
|
||||
UpdatedBy string `json:"updated_by" example:"019d61db-53c6-7280-87ec-e65f205c6d3b"`
|
||||
}
|
||||
|
||||
type HelicopterFileCollectionSections struct {
|
||||
Before []HelicopterFileCollectionItem `json:"before"`
|
||||
Prepare []HelicopterFileCollectionItem `json:"prepare"`
|
||||
After []HelicopterFileCollectionItem `json:"after"`
|
||||
}
|
||||
|
||||
type HelicopterFileCollectionAttributes struct {
|
||||
Helicopter HelicopterFileCollectionHelicopter `json:"helicopter"`
|
||||
Files HelicopterFileCollectionSections `json:"files"`
|
||||
}
|
||||
|
||||
type HelicopterFileCollectionResponse struct {
|
||||
Type string `json:"type" example:"helicopter_file_collection"`
|
||||
Attributes HelicopterFileCollectionAttributes `json:"attributes"`
|
||||
}
|
||||
|
||||
type HelicopterFileCollectionListResponse struct {
|
||||
Data []HelicopterFileCollectionResponse `json:"data"`
|
||||
Meta struct {
|
||||
PageNumber int `json:"page_number" example:"1"`
|
||||
PageSize int `json:"page_size" example:"20"`
|
||||
Total int64 `json:"total" example:"1"`
|
||||
} `json:"meta"`
|
||||
}
|
||||
|
||||
type HelicopterFileDeleteAttributes struct {
|
||||
Deleted bool `json:"deleted" example:"true"`
|
||||
}
|
||||
|
||||
type HelicopterFileDeleteResource struct {
|
||||
Type string `json:"type" example:"helicopter_file_delete"`
|
||||
Attributes HelicopterFileDeleteAttributes `json:"attributes"`
|
||||
}
|
||||
|
||||
type HelicopterFileDeleteResponse struct {
|
||||
Data HelicopterFileDeleteResource `json:"data"`
|
||||
}
|
||||
|
||||
type HelicopterFileUploadIntentResource struct {
|
||||
Type string `json:"type" example:"helicopter_file_upload_intent"`
|
||||
ID string `json:"id" example:"018f3a5c-7a6f-7c2a-8d4a-4b5b1f6c8e9d"`
|
||||
Attributes shareddto.FileManagerFileUploadIntentAttributes `json:"attributes"`
|
||||
}
|
||||
|
||||
type HelicopterFileUploadIntentResponse struct {
|
||||
Data HelicopterFileUploadIntentResource `json:"data"`
|
||||
}
|
||||
|
||||
type HelicopterFileBulkError struct {
|
||||
Status int `json:"status" example:"422"`
|
||||
Code string `json:"code,omitempty" example:"HELICOPTER_FILE_INVALID_PAYLOAD"`
|
||||
ErrorCode string `json:"error_code,omitempty" example:"4220318"`
|
||||
Title string `json:"title" example:"Validation error"`
|
||||
Detail string `json:"detail" example:"helicopter_id is invalid UUID"`
|
||||
}
|
||||
|
||||
type HelicopterFileUploadIntentBulkItem struct {
|
||||
Index int `json:"index" example:"0"`
|
||||
Success bool `json:"success" example:"true"`
|
||||
Resource *HelicopterFileUploadIntentResource `json:"resource,omitempty"`
|
||||
Error *HelicopterFileBulkError `json:"error,omitempty"`
|
||||
}
|
||||
|
||||
type HelicopterFileUploadIntentBulkResponse struct {
|
||||
Data []HelicopterFileUploadIntentBulkItem `json:"data"`
|
||||
Meta struct {
|
||||
Total int `json:"total" example:"3"`
|
||||
Success int `json:"success" example:"2"`
|
||||
Failed int `json:"failed" example:"1"`
|
||||
PartialSuccess bool `json:"partial_success" example:"true"`
|
||||
} `json:"meta"`
|
||||
}
|
||||
|
||||
type HelicopterFileCreateBulkItem struct {
|
||||
Index int `json:"index" example:"0"`
|
||||
Success bool `json:"success" example:"true"`
|
||||
Resource *jsonapi.Resource `json:"resource,omitempty"`
|
||||
Error *HelicopterFileBulkError `json:"error,omitempty"`
|
||||
}
|
||||
|
||||
type HelicopterFileCreateBulkResponse struct {
|
||||
Data []HelicopterFileCreateBulkItem `json:"data"`
|
||||
Meta struct {
|
||||
Total int `json:"total" example:"3"`
|
||||
Success int `json:"success" example:"2"`
|
||||
Failed int `json:"failed" example:"1"`
|
||||
PartialSuccess bool `json:"partial_success" example:"true"`
|
||||
} `json:"meta"`
|
||||
}
|
||||
|
||||
type HelicopterFileUpdateBulkItem struct {
|
||||
Index int `json:"index" example:"0"`
|
||||
Success bool `json:"success" example:"true"`
|
||||
Resource *jsonapi.Resource `json:"resource,omitempty"`
|
||||
Error *HelicopterFileBulkError `json:"error,omitempty"`
|
||||
}
|
||||
|
||||
type HelicopterFileUpdateBulkResponse struct {
|
||||
Data []HelicopterFileUpdateBulkItem `json:"data"`
|
||||
Meta struct {
|
||||
Total int `json:"total" example:"3"`
|
||||
Success int `json:"success" example:"2"`
|
||||
Failed int `json:"failed" example:"1"`
|
||||
PartialSuccess bool `json:"partial_success" example:"true"`
|
||||
} `json:"meta"`
|
||||
}
|
||||
|
||||
type HelicopterFileCreatedSSEPayload struct {
|
||||
HelicopterID string `json:"helicopter_id" example:"019d6771-5fb5-7337-837f-bc5ed85181a1"`
|
||||
HelicopterFileID string `json:"helicopter_file_id" example:"019d6773-ccca-71dd-b9c7-8e6f531deb90"`
|
||||
FileID string `json:"file_id" example:"019d6773-ccca-71dd-b9c7-8e6f531deb90"`
|
||||
Section string `json:"section" example:"fpwb"`
|
||||
Position int `json:"position" example:"1"`
|
||||
IsMandatory bool `json:"is_mandatory" example:"true"`
|
||||
AttachmentPending bool `json:"attachment_pending" example:"true"`
|
||||
Name string `json:"name" example:"Before Flight Checklist.pdf"`
|
||||
MimeType string `json:"mime_type,omitempty" example:"application/pdf"`
|
||||
UpdatedAt string `json:"updated_at" example:"2026-07-01T06:00:00Z"`
|
||||
}
|
||||
88
internal/transport/http/dto/response/helicopter_response.go
Normal file
88
internal/transport/http/dto/response/helicopter_response.go
Normal file
@@ -0,0 +1,88 @@
|
||||
package response
|
||||
|
||||
import shareddto "wucher/internal/transport/http/dto/shared"
|
||||
|
||||
// JSON:API Helicopter response DTOs
|
||||
|
||||
type HelicopterAttributes struct {
|
||||
Designation string `json:"designation" example:"OE-XHZ"`
|
||||
Identifier string `json:"identifier" example:"ABC-0001"`
|
||||
Type string `json:"type" example:"EC-135"`
|
||||
SortKey *int `json:"sortkey" example:"10"`
|
||||
Engine1 string `json:"engine_1,omitempty" example:"TM"`
|
||||
Engine2 string `json:"engine_2,omitempty" example:"TM"`
|
||||
Consumption float64 `json:"consumption_lt_min" example:"4.1"`
|
||||
NR1 bool `json:"nr1" example:"true"`
|
||||
NR2 bool `json:"nr2" example:"true"`
|
||||
LH bool `json:"lh" example:"true"`
|
||||
RH bool `json:"rh" example:"true"`
|
||||
MGB bool `json:"mgb" example:"true"`
|
||||
IGB bool `json:"igb" example:"false"`
|
||||
TGB bool `json:"tgb" example:"true"`
|
||||
UTCOffset int `json:"utc_offset" example:"-1"`
|
||||
Foto *shareddto.HelicopterFoto `json:"foto,omitempty"`
|
||||
Dry bool `json:"dry" example:"false"`
|
||||
AOG bool `json:"aog" example:"false"`
|
||||
IsInUse bool `json:"is_in_use" example:"false"`
|
||||
// Status is the operational status of the helicopter (read-time derived).
|
||||
// Values: "available", "booked", "aog", "mcf". Precedence: mcf > aog > booked > available.
|
||||
// "booked" = active flight assignment; "aog" = air_on_ground flag OR an open complaint
|
||||
// grounding it (non-MEL, or MEL past its grace deadline); "mcf" = a pending maintenance
|
||||
// check flight (MCF created, not yet passed).
|
||||
Status string `json:"status" example:"available" enums:"available,booked,aog,mcf"`
|
||||
AOGReason string `json:"aog_reason,omitempty" example:"Main rotor gearbox fault"`
|
||||
Note string `json:"note,omitempty" example:"Primary HEMS helicopter"`
|
||||
IsActive bool `json:"is_active" example:"true"`
|
||||
CreatedBy *shareddto.HelicopterAuditUser `json:"created_by,omitempty"`
|
||||
CreatedAt string `json:"created_at,omitempty" example:"2026-03-12T03:04:05Z"`
|
||||
UpdatedBy *shareddto.HelicopterAuditUser `json:"updated_by,omitempty"`
|
||||
UpdatedAt string `json:"updated_at,omitempty" example:"2026-03-12T03:04:05Z"`
|
||||
LastDailyInspection *shareddto.HelicopterLastDailyInspection `json:"last_daily_inspection"`
|
||||
// MaintenanceSchedules mirrors the helicopter's latest fleet status. It
|
||||
// always lists every inspection type; entries without data are empty.
|
||||
MaintenanceSchedules []shareddto.MaintenanceScheduleAttributes `json:"maintenance_schedules"`
|
||||
}
|
||||
|
||||
type HelicopterResource struct {
|
||||
Type string `json:"type" example:"helicopter"`
|
||||
ID string `json:"id" example:"018f3a5c-7a6f-7c2a-8d4a-4b5b1f6c8e9d"`
|
||||
Attributes HelicopterAttributes `json:"attributes"`
|
||||
}
|
||||
|
||||
type HelicopterResponse struct {
|
||||
Data HelicopterResource `json:"data"`
|
||||
}
|
||||
|
||||
type HelicopterListResponse struct {
|
||||
Data []HelicopterResource `json:"data"`
|
||||
Meta struct {
|
||||
PageNumber int `json:"page_number" example:"1"`
|
||||
PageSize int `json:"page_size" example:"20"`
|
||||
Total int64 `json:"total" example:"120"`
|
||||
} `json:"meta"`
|
||||
}
|
||||
|
||||
type HelicopterDataTableResponse struct {
|
||||
Data []HelicopterResource `json:"data"`
|
||||
Meta struct {
|
||||
Draw int `json:"draw" example:"1"`
|
||||
RecordsTotal int64 `json:"records_total" example:"120"`
|
||||
RecordsFiltered int64 `json:"records_filtered" example:"12"`
|
||||
} `json:"meta"`
|
||||
}
|
||||
|
||||
type HelicopterNextReportNumberAttributes struct {
|
||||
Identifier string `json:"identifier" example:"ABC"`
|
||||
Sequence int `json:"sequence" example:"1"`
|
||||
ReportNumber string `json:"report_number" example:"ABC-0001"`
|
||||
}
|
||||
|
||||
type HelicopterNextReportNumberResource struct {
|
||||
Type string `json:"type" example:"helicopter_report_number"`
|
||||
ID string `json:"id" example:"018f3a5c-7a6f-7c2a-8d4a-4b5b1f6c8e9d"`
|
||||
Attributes HelicopterNextReportNumberAttributes `json:"attributes"`
|
||||
}
|
||||
|
||||
type HelicopterNextReportNumberResponse struct {
|
||||
Data HelicopterNextReportNumberResource `json:"data"`
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
package response
|
||||
|
||||
type MasterSettingsMicrosoftEntraAttributes struct {
|
||||
TenantID string `json:"MS_ENTRA_TENANT_ID" example:"your-tenant-id"`
|
||||
ClientID string `json:"MS_ENTRA_CLIENT_ID" example:"your-client-id"`
|
||||
ClientSecretMasked string `json:"MS_ENTRA_CLIENT_SECRET" example:"********"`
|
||||
RedirectURL string `json:"MS_ENTRA_REDIRECT_URL" example:"https://example.com/api/v1/auth/microsoft/callback"`
|
||||
Authority string `json:"MS_ENTRA_AUTHORITY" example:"https://login.microsoftonline.com/"`
|
||||
Scopes string `json:"MS_ENTRA_SCOPES" example:"openid,profile,email"`
|
||||
}
|
||||
|
||||
type MasterSettingsMicrosoftEntraResource struct {
|
||||
Type string `json:"type" example:"master_settings_microsoft_entra_sso"`
|
||||
Attributes MasterSettingsMicrosoftEntraAttributes `json:"attributes"`
|
||||
}
|
||||
|
||||
type MasterSettingsMicrosoftEntraResponse struct {
|
||||
Data MasterSettingsMicrosoftEntraResource `json:"data"`
|
||||
}
|
||||
@@ -0,0 +1,49 @@
|
||||
package response
|
||||
|
||||
type MasterSettingsAttributes struct {
|
||||
CompanyName string `json:"company_name" example:"Wucher"`
|
||||
Title string `json:"title,omitempty" example:"Welcome to Wucher"`
|
||||
Subtitle string `json:"subtitle,omitempty" example:"Flight operations platform"`
|
||||
LogoFileUUID *string `json:"logo_file_uuid" example:"018f3a5c-7a6f-7c2a-8d4a-4b5b1f6c8e9d"`
|
||||
LogoURL *string `json:"logo_url" example:"https://cdn.example.com/logo.png"`
|
||||
LogoThumbURL *string `json:"logo_thumbnail_url,omitempty" example:"https://cdn.example.com/logo.thumb.png"`
|
||||
LogoSizeBytes *int64 `json:"logo_original_size_bytes,omitempty" example:"1024000"`
|
||||
CoverFileUUID *string `json:"cover_file_uuid" example:"018f3a5c-7a6f-7c2a-8d4a-4b5b1f6c8e9d"`
|
||||
CoverURL *string `json:"cover_url" example:"https://cdn.example.com/cover.png"`
|
||||
CoverThumbURL *string `json:"cover_thumbnail_url,omitempty" example:"https://cdn.example.com/cover.thumb.png"`
|
||||
CoverSizeBytes *int64 `json:"cover_original_size_bytes,omitempty" example:"2048000"`
|
||||
CreatedAt string `json:"created_at,omitempty" example:"2026-03-12T03:04:05Z"`
|
||||
CreatedBy string `json:"created_by,omitempty" example:"018f3a5c-7a6f-7c2a-8d4a-4b5b1f6c8e9d"`
|
||||
UpdatedAt string `json:"updated_at,omitempty" example:"2026-03-12T03:04:05Z"`
|
||||
UpdatedBy string `json:"updated_by,omitempty" example:"018f3a5c-7a6f-7c2a-8d4a-4b5b1f6c8e9d"`
|
||||
DeletedAt string `json:"deleted_at,omitempty" example:"2026-03-12T03:04:05Z"`
|
||||
DeletedBy string `json:"deleted_by,omitempty" example:"018f3a5c-7a6f-7c2a-8d4a-4b5b1f6c8e9d"`
|
||||
}
|
||||
|
||||
type MasterSettingsResource struct {
|
||||
Type string `json:"type" example:"master_settings"`
|
||||
ID string `json:"id" example:"018f3a5c-7a6f-7c2a-8d4a-4b5b1f6c8e9d"`
|
||||
Attributes MasterSettingsAttributes `json:"attributes"`
|
||||
}
|
||||
|
||||
type MasterSettingsResponse struct {
|
||||
Data MasterSettingsResource `json:"data"`
|
||||
}
|
||||
|
||||
type MasterSettingsListResponse struct {
|
||||
Data []MasterSettingsResource `json:"data"`
|
||||
Meta struct {
|
||||
PageNumber int `json:"page_number" example:"1"`
|
||||
PageSize int `json:"page_size" example:"20"`
|
||||
Total int64 `json:"total" example:"120"`
|
||||
} `json:"meta"`
|
||||
}
|
||||
|
||||
type MasterSettingsDataTableResponse struct {
|
||||
Data []MasterSettingsResource `json:"data"`
|
||||
Meta struct {
|
||||
Draw int `json:"draw" example:"1"`
|
||||
RecordsTotal int64 `json:"records_total" example:"120"`
|
||||
RecordsFiltered int64 `json:"records_filtered" example:"12"`
|
||||
} `json:"meta"`
|
||||
}
|
||||
40
internal/transport/http/dto/response/mcf_response.go
Normal file
40
internal/transport/http/dto/response/mcf_response.go
Normal file
@@ -0,0 +1,40 @@
|
||||
package response
|
||||
|
||||
type MCFAttributes struct {
|
||||
HelicopterID string `json:"helicopter_id" example:"019d6771-5fb5-7337-837f-bc5ed85181a1"`
|
||||
FlightID string `json:"flight_id,omitempty" example:"019d6774-98e8-7fba-9ef4-3795c0da8a13"`
|
||||
Date string `json:"date" example:"2026-06-17"`
|
||||
AFHours string `json:"af_hours,omitempty" example:"1245.3"`
|
||||
LandingCount int `json:"landing_count" example:"3"`
|
||||
UTCTime *string `json:"utc_time,omitempty" example:"2026-06-17T08:30:00Z"`
|
||||
DecidedBy string `json:"decided_by,omitempty" example:"019d61db-53c6-7280-87ec-e65f205c6d3b"`
|
||||
DecidedAt *string `json:"decided_at,omitempty" example:"2026-06-16T03:04:05Z"`
|
||||
Result *string `json:"result,omitempty" example:"passed" enums:"passed,failed"`
|
||||
|
||||
Notes *string `json:"notes,omitempty" example:"Vibration within limits"`
|
||||
CompletedBy string `json:"completed_by,omitempty" example:"019d61db-53c6-7280-87ec-e65f205c6d3b"`
|
||||
CompletedAt *string `json:"completed_at,omitempty" example:"2026-06-16T03:04:05Z"`
|
||||
|
||||
IsComplete bool `json:"is_complete" example:"false"`
|
||||
IsCleared bool `json:"is_cleared" example:"false"`
|
||||
IsPending bool `json:"is_pending" example:"true"`
|
||||
CreatedAt string `json:"created_at,omitempty" example:"2026-06-16T03:04:05Z"`
|
||||
UpdatedAt string `json:"updated_at,omitempty" example:"2026-06-16T03:04:05Z"`
|
||||
}
|
||||
|
||||
type MCFResource struct {
|
||||
Type string `json:"type" example:"mcf"`
|
||||
ID string `json:"id" example:"019d6771-5fb5-7337-837f-bc5ed85181a1"`
|
||||
Attributes MCFAttributes `json:"attributes"`
|
||||
}
|
||||
|
||||
type MCFResponse struct {
|
||||
Data MCFResource `json:"data"`
|
||||
}
|
||||
|
||||
type MCFListResponse struct {
|
||||
Data []MCFResource `json:"data"`
|
||||
Meta struct {
|
||||
Total int64 `json:"total" example:"1"`
|
||||
} `json:"meta"`
|
||||
}
|
||||
93
internal/transport/http/dto/response/mission_response.go
Normal file
93
internal/transport/http/dto/response/mission_response.go
Normal file
@@ -0,0 +1,93 @@
|
||||
package response
|
||||
|
||||
type MissionAttributes struct {
|
||||
Flight *MissionFlightRef `json:"flight,omitempty"`
|
||||
Type string `json:"type" example:"HEMS"`
|
||||
MissionCode string `json:"mission_code" example:"HEMS-26-1"`
|
||||
Subtypes *MissionSubtypeRef `json:"subtypes,omitempty"`
|
||||
Note string `json:"note,omitempty" example:"Mission note"`
|
||||
StartTime string `json:"start_time,omitempty" example:"04:49"`
|
||||
EndTime string `json:"end_time,omitempty" example:"22:05"`
|
||||
Status string `json:"status" example:"completed"`
|
||||
Forms *MissionForms `json:"forms,omitempty"`
|
||||
Files []MissionFileItem `json:"files,omitempty"`
|
||||
CreatedAt string `json:"created_at,omitempty" example:"2026-04-20T08:00:00Z"`
|
||||
CreatedBy string `json:"created_by,omitempty" example:"019d61db-53c6-7280-87ec-e65f205c6d3b"`
|
||||
UpdatedAt string `json:"updated_at,omitempty" example:"2026-04-20T08:00:00Z"`
|
||||
UpdatedBy string `json:"updated_by,omitempty" example:"019d61db-53c6-7280-87ec-e65f205c6d3b"`
|
||||
}
|
||||
|
||||
type MissionForms struct {
|
||||
FlightData []FlightDataResource `json:"flight_data,omitempty"`
|
||||
}
|
||||
|
||||
type MissionFileItem struct {
|
||||
ID string `json:"id" example:"019d6771-6c8e-7a2d-a7b8-03bcb65fcb3e"`
|
||||
AttachmentID string `json:"attachment_id,omitempty" example:"019d6771-6c8e-7a2d-a7b8-03bcb65fcb3e"`
|
||||
FileName string `json:"file_name,omitempty" example:"mission-brief.pdf"`
|
||||
DownloadURL string `json:"download_url,omitempty" example:"https://s3.localhost.localstack.cloud:4566/wucher-file-dev/files/2026/04/05/brief.pdf?X-Amz-Signature=..."`
|
||||
ThumbnailURL string `json:"thumbnail_url,omitempty" example:"https://s3.localhost.localstack.cloud:4566/wucher-file-dev/thumbnails/2026/04/05/brief.png?X-Amz-Signature=..."`
|
||||
}
|
||||
|
||||
type MissionFlightRef struct {
|
||||
ID string `json:"id,omitempty" example:"019d6774-98e8-7fba-9ef4-3795c0da8a13"`
|
||||
Code string `json:"code,omitempty" example:"M-062226-001"`
|
||||
Date string `json:"date,omitempty" example:"2026-04-20"`
|
||||
}
|
||||
|
||||
type MissionSubtypeRef struct {
|
||||
ID string `json:"id,omitempty" example:"019d6774-98e8-7fba-9ef4-3795c0da8a99"`
|
||||
Subtype string `json:"subtype,omitempty" example:"PRIM"`
|
||||
SubtypeName string `json:"subtype_name,omitempty" example:"Nicht transportfaehig"`
|
||||
}
|
||||
|
||||
type MissionResource struct {
|
||||
Type string `json:"type" example:"mission"`
|
||||
ID string `json:"id" example:"019d6774-98e8-7fba-9ef4-3795c0da8a13"`
|
||||
Attributes MissionAttributes `json:"attributes"`
|
||||
}
|
||||
|
||||
type MissionResponse struct {
|
||||
Data MissionResource `json:"data"`
|
||||
}
|
||||
|
||||
type MissionListResponse struct {
|
||||
Data []MissionResource `json:"data"`
|
||||
Meta struct {
|
||||
PageNumber int `json:"page_number" example:"1"`
|
||||
PageSize int `json:"page_size" example:"20"`
|
||||
Total int64 `json:"total" example:"120"`
|
||||
} `json:"meta"`
|
||||
}
|
||||
|
||||
type MissionDataTableResponse struct {
|
||||
Data []MissionResource `json:"data"`
|
||||
Meta struct {
|
||||
Draw int `json:"draw" example:"1"`
|
||||
RecordsTotal int64 `json:"records_total" example:"120"`
|
||||
RecordsFiltered int64 `json:"records_filtered" example:"42"`
|
||||
} `json:"meta"`
|
||||
}
|
||||
|
||||
type MissionTypeSubtypeItem struct {
|
||||
ID string `json:"id" example:"019d6774-98e8-7fba-9ef4-3795c0da8a99"`
|
||||
Code string `json:"code" example:"PRIM"`
|
||||
Name string `json:"name" example:"Primary"`
|
||||
TaskName string `json:"task_name" example:"Nicht transportfaehig"`
|
||||
}
|
||||
|
||||
type MissionTypeAttributes struct {
|
||||
Code string `json:"code" example:"HEMS"`
|
||||
Name string `json:"name" example:"HEMS"`
|
||||
Subtypes []MissionTypeSubtypeItem `json:"subtypes,omitempty"`
|
||||
}
|
||||
|
||||
type MissionTypeResource struct {
|
||||
Type string `json:"type" example:"mission_type"`
|
||||
ID string `json:"id" example:"019d6774-98e8-7fba-9ef4-3795c0da8a77"`
|
||||
Attributes MissionTypeAttributes `json:"attributes"`
|
||||
}
|
||||
|
||||
type MissionTypeListResponse struct {
|
||||
Data []MissionTypeResource `json:"data"`
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
package response
|
||||
|
||||
type OtherPersonAttributes struct {
|
||||
Name string `json:"name" example:"Guest Person"`
|
||||
MobilePhone string `json:"mobile_phone,omitempty" example:"+43-1234"`
|
||||
Email string `json:"email,omitempty" example:"guest@example.com"`
|
||||
}
|
||||
|
||||
type OtherPersonResource struct {
|
||||
Type string `json:"type" example:"other_person"`
|
||||
ID string `json:"id" example:"019d6771-5fb5-7337-837f-bc5ed85181a1"`
|
||||
Attributes OtherPersonAttributes `json:"attributes"`
|
||||
}
|
||||
|
||||
type OtherPersonListResponse struct {
|
||||
Data []OtherPersonResource `json:"data"`
|
||||
Meta struct {
|
||||
Total int64 `json:"total" example:"1"`
|
||||
} `json:"meta"`
|
||||
}
|
||||
|
||||
type OtherPersonResponse struct {
|
||||
Data OtherPersonResource `json:"data"`
|
||||
}
|
||||
98
internal/transport/http/dto/response/reserve_ac_response.go
Normal file
98
internal/transport/http/dto/response/reserve_ac_response.go
Normal file
@@ -0,0 +1,98 @@
|
||||
package response
|
||||
|
||||
import shareddto "wucher/internal/transport/http/dto/shared"
|
||||
|
||||
type ReserveAcResource struct {
|
||||
Type string `json:"type" example:"reserve_ac"`
|
||||
ID string `json:"id" example:"019d6773-ccca-71dd-b9c7-8e6f531deb90"`
|
||||
Attributes shareddto.ReserveAcAttributes `json:"attributes"`
|
||||
}
|
||||
|
||||
type ReserveAcResponse struct {
|
||||
Data ReserveAcResource `json:"data"`
|
||||
}
|
||||
|
||||
type ReserveAcListResponse struct {
|
||||
Data []ReserveAcResource `json:"data"`
|
||||
Meta struct {
|
||||
PageNumber int `json:"page_number" example:"1"`
|
||||
PageSize int `json:"page_size" example:"20"`
|
||||
Total int64 `json:"total" example:"1"`
|
||||
} `json:"meta"`
|
||||
}
|
||||
|
||||
type ReserveAcHelicopterGroupResource struct {
|
||||
Type string `json:"type"`
|
||||
Attributes ReserveAcHelicopterGroupAttributes `json:"attributes"`
|
||||
}
|
||||
|
||||
type ReserveAcHelicopterGroupAttributes struct {
|
||||
Helicopter ReserveAcHelicopterIdentity `json:"helicopter"`
|
||||
LastDailyInspection ReserveAcLastDailyInspectionDTO `json:"last_daily_inspection"`
|
||||
BFI ReserveAcBFISection `json:"bfi"`
|
||||
FPWB ReserveAcFPWBSection `json:"fpwb"`
|
||||
}
|
||||
|
||||
type ReserveAcHelicopterIdentity struct {
|
||||
ID string `json:"id"`
|
||||
Designation string `json:"designation"`
|
||||
Identifier string `json:"identifier"`
|
||||
Type string `json:"type"`
|
||||
Status string `json:"status"`
|
||||
BookingDetails *ReserveAcBookingDetailsDTO `json:"booking_details,omitempty"`
|
||||
}
|
||||
|
||||
type ReserveAcLastDailyInspectionDTO struct {
|
||||
ID any `json:"id"`
|
||||
InspectionBy ReserveAcInspectionBySummary `json:"inspection_by"`
|
||||
InspectionAt any `json:"inspection_at"`
|
||||
}
|
||||
|
||||
type ReserveAcInspectionBySummary struct {
|
||||
ID any `json:"id"`
|
||||
Name string `json:"name"`
|
||||
ShortName string `json:"short_name"`
|
||||
}
|
||||
|
||||
type ReserveAcBookingDetailsDTO struct {
|
||||
ReserveAcID any `json:"reserve_ac_id,omitempty"`
|
||||
FlightID any `json:"flight_id,omitempty"`
|
||||
MissionCode any `json:"mission_code,omitempty"`
|
||||
DutyDate any `json:"duty_date,omitempty"`
|
||||
BookedAt any `json:"booked_at,omitempty"`
|
||||
BookedBy map[string]any `json:"booked_by,omitempty"`
|
||||
}
|
||||
|
||||
type ReserveAcHelicopterFileItem struct {
|
||||
ID string `json:"id"`
|
||||
FileName string `json:"file_name"`
|
||||
URL string `json:"url"`
|
||||
UploadedAt string `json:"uploaded_at"`
|
||||
UploadedBy string `json:"uploaded_by"`
|
||||
}
|
||||
|
||||
type ReserveAcHelicopterFileSection struct {
|
||||
Files []ReserveAcHelicopterFileItem `json:"files"`
|
||||
}
|
||||
|
||||
type ReserveAcBFISection struct {
|
||||
FuelAddedAmount *float64 `json:"fuel_added_amount,omitempty"`
|
||||
FuelAmount *float64 `json:"fuel_amount,omitempty"`
|
||||
FuelUnit string `json:"fuel_unit,omitempty"`
|
||||
HydraulicLHChecked *bool `json:"hydraulic_lh_checked,omitempty"`
|
||||
HydraulicRHChecked *bool `json:"hydraulic_rh_checked,omitempty"`
|
||||
Note *string `json:"note,omitempty"`
|
||||
OilEngineNR1Checked *bool `json:"oil_engine_nr1_checked,omitempty"`
|
||||
OilEngineNR2Checked *bool `json:"oil_engine_nr2_checked,omitempty"`
|
||||
OilTransmissionIGBChecked *bool `json:"oil_transmission_igb_checked,omitempty"`
|
||||
OilTransmissionMGBChecked *bool `json:"oil_transmission_mgb_checked,omitempty"`
|
||||
OilTransmissionTGBChecked *bool `json:"oil_transmission_tgb_checked,omitempty"`
|
||||
Files []ReserveAcHelicopterFileItem `json:"files"`
|
||||
}
|
||||
|
||||
type ReserveAcFPWBSection struct {
|
||||
NotamBriefing *bool `json:"notam_briefing,omitempty"`
|
||||
WeatherBriefing *bool `json:"weather_briefing,omitempty"`
|
||||
OperationalFlightPlan *bool `json:"operational_flight_plan,omitempty"`
|
||||
Files []ReserveAcHelicopterFileItem `json:"files"`
|
||||
}
|
||||
112
internal/transport/http/dto/response/takeover_response.go
Normal file
112
internal/transport/http/dto/response/takeover_response.go
Normal file
@@ -0,0 +1,112 @@
|
||||
package response
|
||||
|
||||
import (
|
||||
shareddto "wucher/internal/transport/http/dto/shared"
|
||||
)
|
||||
|
||||
type TakeoverResponseAttributes struct {
|
||||
ID string `json:"id,omitempty"`
|
||||
FlightID string `json:"flight_id,omitempty"`
|
||||
ReserveAcID string `json:"reserve_ac_id,omitempty"`
|
||||
FlightInspectionID string `json:"flight_inspection_id,omitempty"`
|
||||
ShiftStart string `json:"shift_start,omitempty"`
|
||||
ShiftEnd string `json:"shift_end,omitempty"`
|
||||
BaseID string `json:"base_id,omitempty"`
|
||||
Base *DutyRosterBaseInfo `json:"base,omitempty"`
|
||||
DutyDate string `json:"duty_date,omitempty"`
|
||||
HelicopterID string `json:"helicopter_id,omitempty"`
|
||||
Helicopter *TakeoverHelicopter `json:"helicopter,omitempty"`
|
||||
Notes *string `json:"notes,omitempty"`
|
||||
CreatedBy string `json:"created_by,omitempty" example:"018f3a5c-7a6f-7c2a-8d4a-4b5b1f6c8e9d"`
|
||||
UpdatedBy string `json:"updated_by,omitempty" example:"018f3a5c-7a6f-7c2a-8d4a-4b5b1f6c8e9d"`
|
||||
CreatedAt string `json:"created_at,omitempty" example:"2026-03-12T03:04:05Z"`
|
||||
UpdatedAt string `json:"updated_at,omitempty" example:"2026-03-12T03:04:05Z"`
|
||||
RosterDetail TakeoverDutyRosterDetailsResponse `json:"roster_detail"`
|
||||
Inspection shareddto.ReserveAcInspectionSummary `json:"inspection"`
|
||||
Files []TakeoverFileResource `json:"files,omitempty"`
|
||||
}
|
||||
|
||||
// TakeoverHelicopter is the selected helicopter shown on a takeover, enriched
|
||||
// with its last daily inspection and its maintenance schedule (the "next due"
|
||||
// per inspection type) from the helicopter's latest fleet status.
|
||||
type TakeoverHelicopter struct {
|
||||
ID string `json:"id" example:"018f3a5c-7a6f-7c2a-8d4a-4b5b1f6c8e9d"`
|
||||
Designation string `json:"designation" example:"OE-XHZ"`
|
||||
Identifier string `json:"identifier" example:"1234"`
|
||||
Type string `json:"type" example:"EC-135"`
|
||||
LastDailyInspection *shareddto.HelicopterLastDailyInspection `json:"last_daily_inspection"`
|
||||
MaintenanceSchedules []shareddto.MaintenanceScheduleAttributes `json:"maintenance_schedules"`
|
||||
}
|
||||
|
||||
type TakeoverDutyRosterAttributes struct {
|
||||
Bases DutyRosterBaseInfo `json:"bases"`
|
||||
FlightID string `json:"flight_id,omitempty" example:"018f3a5c-7a6f-7c2a-8d4a-4b5b1f6c8e9d"`
|
||||
Helicopter DutyRosterHelicopterSummary `json:"helicopter"`
|
||||
DutyDate string `json:"duty_date,omitempty" example:"2026-03-16"`
|
||||
Detail TakeoverDutyRosterDetailsResponse `json:"roster_detail"`
|
||||
CreatedBy string `json:"created_by,omitempty" example:"018f3a5c-7a6f-7c2a-8d4a-4b5b1f6c8e9d"`
|
||||
CreatedAt string `json:"created_at,omitempty" example:"2026-03-12T03:04:05Z"`
|
||||
UpdatedAt string `json:"updated_at,omitempty" example:"2026-03-12T03:04:05Z"`
|
||||
}
|
||||
|
||||
type TakeoverDutyRosterResource struct {
|
||||
Type string `json:"type" example:"duty_roster"`
|
||||
ID string `json:"id,omitempty" example:"018f3a5c-7a6f-7c2a-8d4a-4b5b1f6c8e9d"`
|
||||
Attributes TakeoverDutyRosterAttributes `json:"attributes"`
|
||||
}
|
||||
|
||||
type TakeoverDutyRosterCrew struct {
|
||||
ID string `json:"id,omitempty" example:"018f3a5c-7a6f-7c2a-8d4a-4b5b1f6c8e9d"`
|
||||
Name string `json:"name,omitempty" example:"Hunter Wolfgang"`
|
||||
Role string `json:"role,omitempty" example:"doctor"`
|
||||
MobilePhone string `json:"mobile_phone,omitempty" example:"+43-1234"`
|
||||
Email string `json:"email,omitempty" example:"doctor@example.com"`
|
||||
}
|
||||
|
||||
type TakeoverDutyRosterPilotCrew struct {
|
||||
ID string `json:"id,omitempty" example:"018f3a5c-7a6f-7c2a-8d4a-4b5b1f6c8e9d"`
|
||||
Name string `json:"name,omitempty" example:"Hunter Wolfgang"`
|
||||
Role string `json:"role,omitempty" example:"pilot"`
|
||||
MobilePhone string `json:"mobile_phone,omitempty" example:"+43-1234"`
|
||||
Email string `json:"email,omitempty" example:"pilot@example.com"`
|
||||
CrewType string `json:"crew_type,omitempty" example:"main"`
|
||||
FlightInstructor *bool `json:"flight_instructor,omitempty"`
|
||||
LineChecker *bool `json:"line_checker,omitempty"`
|
||||
Supervisor *bool `json:"supervisor,omitempty"`
|
||||
Examiner *bool `json:"examiner,omitempty"`
|
||||
CoPilot *bool `json:"co_pilot,omitempty"`
|
||||
}
|
||||
|
||||
type TakeoverDutyRosterDetailsResponse struct {
|
||||
Pilot []TakeoverDutyRosterPilotCrew `json:"pilot"`
|
||||
Doctor []TakeoverDutyRosterCrew `json:"doctor"`
|
||||
Rescuer []TakeoverDutyRosterCrew `json:"rescuer"`
|
||||
Helper []TakeoverDutyRosterCrew `json:"helper"`
|
||||
OtherPerson []TakeoverDutyRosterCrew `json:"other_person"`
|
||||
}
|
||||
|
||||
type TakeoverResource struct {
|
||||
Type string `json:"type" example:"takeover"`
|
||||
Attributes TakeoverResponseAttributes `json:"attributes"`
|
||||
}
|
||||
|
||||
type TakeoverFileAttributes struct {
|
||||
FileAttachmentID string `json:"file_attachment_id" example:"019d7000-aaaa-7bbb-8ccc-222222222222"`
|
||||
FileName string `json:"file_name,omitempty" example:"takeover-report.pdf"`
|
||||
DownloadURL string `json:"download_url,omitempty" example:"https://s3.localhost.localstack.cloud:4566/wucher-file-dev/files/2026/04/05/takeover-report.pdf?X-Amz-Signature=..."`
|
||||
}
|
||||
|
||||
type TakeoverFileResource struct {
|
||||
Type string `json:"type" example:"takeover_file"`
|
||||
ID string `json:"id,omitempty" example:"019d7000-aaaa-7bbb-8ccc-111111111111"`
|
||||
Attributes TakeoverFileAttributes `json:"attributes"`
|
||||
}
|
||||
|
||||
type TakeoverResponse struct {
|
||||
Data TakeoverResource `json:"data"`
|
||||
}
|
||||
|
||||
type TakeoverListResponse struct {
|
||||
Data []TakeoverResource `json:"data"`
|
||||
Meta map[string]any `json:"meta,omitempty"`
|
||||
}
|
||||
8
internal/transport/http/dto/response/user_ref.go
Normal file
8
internal/transport/http/dto/response/user_ref.go
Normal file
@@ -0,0 +1,8 @@
|
||||
package response
|
||||
|
||||
// UserRef is an audit user reference exposing both the raw UUID and a resolved
|
||||
// display name, e.g. for created_by / updated_by fields.
|
||||
type UserRef struct {
|
||||
UUID string `json:"uuid" example:"019d61db-53c6-7280-87ec-e65f205c6d3b"`
|
||||
Name string `json:"name,omitempty" example:"Ganahl Stefan"`
|
||||
}
|
||||
174
internal/transport/http/dto/role_dto.go
Normal file
174
internal/transport/http/dto/role_dto.go
Normal file
@@ -0,0 +1,174 @@
|
||||
package dto
|
||||
|
||||
// JSON:API Role DTOs
|
||||
|
||||
type RoleAttributes struct {
|
||||
Name string `json:"name" example:"admin"`
|
||||
Description string `json:"description,omitempty" example:"Full access to all resources."`
|
||||
CreatedBy string `json:"created_by,omitempty" example:"018f3a5c-7a6f-7c2a-8d4a-4b5b1f6c8e9d"`
|
||||
CreatedAt string `json:"created_at,omitempty" example:"2026-02-05T10:00:00Z"`
|
||||
UpdatedAt string `json:"updated_at,omitempty" example:"2026-02-05T10:00:00Z"`
|
||||
}
|
||||
|
||||
type RoleResource struct {
|
||||
Type string `json:"type" example:"role"`
|
||||
ID string `json:"id" example:"018f3a5c-7a6f-7c2a-8d4a-4b5b1f6c8e9d"`
|
||||
Attributes RoleAttributes `json:"attributes"`
|
||||
Permissions []PermissionResource `json:"permissions,omitempty"`
|
||||
}
|
||||
|
||||
type RoleResponse struct {
|
||||
Data RoleResource `json:"data"`
|
||||
}
|
||||
|
||||
type RoleListResponse struct {
|
||||
Data []RoleResource `json:"data"`
|
||||
Meta struct {
|
||||
PageNumber int `json:"page_number" example:"1"`
|
||||
PageSize int `json:"page_size" example:"20"`
|
||||
Total int64 `json:"total" example:"120"`
|
||||
} `json:"meta"`
|
||||
}
|
||||
|
||||
type RoleDataTableResponse struct {
|
||||
Data []RoleResource `json:"data"`
|
||||
Meta struct {
|
||||
Draw int `json:"draw" example:"1"`
|
||||
RecordsTotal int64 `json:"records_total" example:"120"`
|
||||
RecordsFiltered int64 `json:"records_filtered" example:"12"`
|
||||
} `json:"meta"`
|
||||
}
|
||||
|
||||
type PermissionAttributes struct {
|
||||
Name string `json:"name" example:"User Management"`
|
||||
Key string `json:"key" example:"user.manage"`
|
||||
Description string `json:"description,omitempty" example:"Allows managing users."`
|
||||
RequiresPIN bool `json:"requires_pin" example:"true"`
|
||||
CreatedAt string `json:"created_at,omitempty" example:"2026-02-05T10:00:00Z"`
|
||||
UpdatedAt string `json:"updated_at,omitempty" example:"2026-02-05T10:00:00Z"`
|
||||
}
|
||||
|
||||
type PermissionResource struct {
|
||||
Type string `json:"type" example:"permission"`
|
||||
ID string `json:"id" example:"018f3a5c-7a6f-7c2a-8d4a-4b5b1f6c8e9d"`
|
||||
Attributes PermissionAttributes `json:"attributes"`
|
||||
}
|
||||
|
||||
type PermissionListResponse struct {
|
||||
Data []PermissionResource `json:"data"`
|
||||
Meta struct {
|
||||
PageNumber int `json:"page_number" example:"1"`
|
||||
PageSize int `json:"page_size" example:"20"`
|
||||
Total int64 `json:"total" example:"120"`
|
||||
} `json:"meta"`
|
||||
}
|
||||
|
||||
type PermissionGroupResource struct {
|
||||
Module string `json:"module" example:"dul"`
|
||||
ModuleLabel string `json:"module_label" example:"DUL"`
|
||||
Permissions []PermissionResource `json:"permissions"`
|
||||
}
|
||||
|
||||
type PermissionGroupedResponse struct {
|
||||
Data []PermissionGroupResource `json:"data"`
|
||||
}
|
||||
|
||||
// Create
|
||||
type RoleCreateAttributes struct {
|
||||
Name string `json:"name" validate:"required" example:"admin"`
|
||||
Description string `json:"description,omitempty" example:"Full access to all resources."`
|
||||
}
|
||||
|
||||
type RoleCreateData struct {
|
||||
Type string `json:"type" validate:"required,oneof=role_create role" example:"role_create"`
|
||||
Attributes RoleCreateAttributes `json:"attributes" validate:"required"`
|
||||
}
|
||||
|
||||
type RoleCreateRequest struct {
|
||||
Data RoleCreateData `json:"data" validate:"required"`
|
||||
}
|
||||
|
||||
// Update
|
||||
type RoleUpdateAttributes struct {
|
||||
Name *string `json:"name,omitempty" example:"admin"`
|
||||
Description *string `json:"description,omitempty" example:"Full access to all resources."`
|
||||
}
|
||||
|
||||
type RoleUpdateData struct {
|
||||
Type string `json:"type" validate:"required,oneof=role_update role" example:"role_update"`
|
||||
ID string `json:"id" example:"018f3a5c-7a6f-7c2a-8d4a-4b5b1f6c8e9d"`
|
||||
Attributes RoleUpdateAttributes `json:"attributes" validate:"required"`
|
||||
}
|
||||
|
||||
type RoleUpdateRequest struct {
|
||||
Data RoleUpdateData `json:"data" validate:"required"`
|
||||
}
|
||||
|
||||
// Role-Permission
|
||||
type RolePermissionAttributes struct {
|
||||
RoleID string `json:"role_id" example:"018f3a5c-7a6f-7c2a-8d4a-4b5b1f6c8e9d"`
|
||||
PermissionID string `json:"permission_id" example:"018f3a5c-7a6f-7c2a-8d4a-4b5b1f6c8e9e"`
|
||||
}
|
||||
|
||||
type RolePermissionResource struct {
|
||||
Type string `json:"type" example:"role_permission"`
|
||||
ID string `json:"id" example:"018f3a5c-7a6f-7c2a-8d4a-4b5b1f6c8e9f"`
|
||||
Attributes RolePermissionAttributes `json:"attributes"`
|
||||
}
|
||||
|
||||
type RolePermissionResponse struct {
|
||||
Data RolePermissionResource `json:"data"`
|
||||
}
|
||||
|
||||
type RolePermissionListResponse struct {
|
||||
Data []RolePermissionResource `json:"data"`
|
||||
}
|
||||
|
||||
type RolePermissionAssignAttributes struct {
|
||||
PermissionID string `json:"permission_id" validate:"required" example:"018f3a5c-7a6f-7c2a-8d4a-4b5b1f6c8e9e"`
|
||||
}
|
||||
|
||||
type RolePermissionAssignData struct {
|
||||
Type string `json:"type" validate:"required,eq=role_permission" example:"role_permission"`
|
||||
Attributes RolePermissionAssignAttributes `json:"attributes" validate:"required"`
|
||||
}
|
||||
|
||||
type RolePermissionAssignRequest struct {
|
||||
Data RolePermissionAssignData `json:"data" validate:"required"`
|
||||
}
|
||||
|
||||
type RolePermissionBulkAttributes struct {
|
||||
PermissionIDs []string `json:"permission_ids" validate:"required" example:"[\"018f3a5c-7a6f-7c2a-8d4a-4b5b1f6c8e9e\",\"018f3a5c-7a6f-7c2a-8d4a-4b5b1f6c8e9f\"]"`
|
||||
}
|
||||
|
||||
type RolePermissionAssignBulkData struct {
|
||||
Type string `json:"type" validate:"required,eq=role_assign_permission_bulk" example:"role_assign_permission_bulk"`
|
||||
Attributes RolePermissionBulkAttributes `json:"attributes" validate:"required"`
|
||||
}
|
||||
|
||||
type RolePermissionAssignBulkRequest struct {
|
||||
Data RolePermissionAssignBulkData `json:"data" validate:"required"`
|
||||
}
|
||||
|
||||
type RolePermissionRemoveBulkData struct {
|
||||
Type string `json:"type" validate:"required,eq=role_remove_permission_bulk" example:"role_remove_permission_bulk"`
|
||||
Attributes RolePermissionBulkAttributes `json:"attributes" validate:"required"`
|
||||
}
|
||||
|
||||
type RolePermissionRemoveBulkRequest struct {
|
||||
Data RolePermissionRemoveBulkData `json:"data" validate:"required"`
|
||||
}
|
||||
|
||||
type PermissionUpdateAttributes struct {
|
||||
RequiresPIN *bool `json:"requires_pin" validate:"required" example:"true"`
|
||||
}
|
||||
|
||||
type PermissionUpdateData struct {
|
||||
Type string `json:"type" validate:"required,oneof=permission_update permission" example:"permission_update"`
|
||||
ID string `json:"id" example:"018f3a5c-7a6f-7c2a-8d4a-4b5b1f6c8e9d"`
|
||||
Attributes PermissionUpdateAttributes `json:"attributes" validate:"required"`
|
||||
}
|
||||
|
||||
type PermissionUpdateRequest struct {
|
||||
Data PermissionUpdateData `json:"data" validate:"required"`
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
package shared
|
||||
|
||||
type AirRescuerChecklistBaseInfo struct {
|
||||
ID string `json:"id,omitempty" example:"018f3a5c-7a6f-7c2a-8d4a-4b5b1f6c8e9d"`
|
||||
BaseName string `json:"base_name,omitempty" example:"Gallus 1 - Zurs Lech"`
|
||||
BaseAbbreviation string `json:"base_abbreviation,omitempty" example:"G1"`
|
||||
BaseCategory string `json:"base_category,omitempty" example:"hems"`
|
||||
}
|
||||
|
||||
type AirRescuerChecklistItemInput struct {
|
||||
Name string `json:"name" validate:"required" example:"Oxygen supply check"`
|
||||
Position *int `json:"position,omitempty" example:"1"`
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
package shared
|
||||
|
||||
type FileManagerAttachmentAttributes struct {
|
||||
FileID string `json:"file_id" example:"018f3a5c-7a6f-7c2a-8d4a-4b5b1f6c8e9d"`
|
||||
RefType string `json:"ref_type" example:"helicopter"`
|
||||
RefID string `json:"ref_id" example:"018f3a5c-7a6f-7c2a-8d4a-4b5b1f6c8e9d"`
|
||||
Category string `json:"category,omitempty" example:"document"`
|
||||
SortOrder int `json:"sort_order" example:"0"`
|
||||
IsPrimary bool `json:"is_primary" example:"false"`
|
||||
AttachedBy string `json:"attached_by,omitempty" example:"018f3a5c-7a6f-7c2a-8d4a-4b5b1f6c8e9d"`
|
||||
CreatedAt string `json:"created_at,omitempty" example:"2026-03-12T03:04:05Z"`
|
||||
UpdatedAt string `json:"updated_at,omitempty" example:"2026-03-12T03:04:05Z"`
|
||||
}
|
||||
104
internal/transport/http/dto/shared/file_manager_file_shared.go
Normal file
104
internal/transport/http/dto/shared/file_manager_file_shared.go
Normal file
@@ -0,0 +1,104 @@
|
||||
package shared
|
||||
|
||||
type FileManagerFileAttributes struct {
|
||||
FileUUID string `json:"file_uuid,omitempty" example:"018f3a5c-7a6f-7c2a-8d4a-4b5b1f6c8e9d"`
|
||||
FolderID string `json:"folder_id" example:"018f3a5c-7a6f-7c2a-8d4a-4b5b1f6c8e9d"`
|
||||
Name string `json:"name" example:"policy.pdf"`
|
||||
NameNormalized string `json:"name_normalized,omitempty" example:"policy.pdf"`
|
||||
Extension string `json:"extension,omitempty" example:"pdf"`
|
||||
SizeBytes int64 `json:"size_bytes" example:"1024"`
|
||||
MimeType string `json:"mime_type" example:"application/pdf"`
|
||||
IsTemplate bool `json:"is_template" example:"true"`
|
||||
DownloadURL string `json:"download_url,omitempty" example:"https://s3.localhost.localstack.cloud:4566/wucher-file-dev/files/2026/04/05/policy.pdf?X-Amz-Signature=..."`
|
||||
Status string `json:"status" example:"validated"`
|
||||
FailureReason string `json:"failure_reason,omitempty" example:""`
|
||||
UploadError string `json:"upload_error,omitempty" example:""`
|
||||
ProcessingStartedAt string `json:"processing_started_at,omitempty" example:"2026-03-12T03:04:05Z"`
|
||||
ProcessedAt string `json:"processed_at,omitempty" example:"2026-03-12T03:04:05Z"`
|
||||
ValidatedAt string `json:"validated_at,omitempty" example:"2026-03-12T03:04:05Z"`
|
||||
FailedAt string `json:"failed_at,omitempty" example:"2026-03-12T03:04:05Z"`
|
||||
CreatedAt string `json:"created_at,omitempty" example:"2026-03-12T03:04:05Z"`
|
||||
CreatedBy string `json:"created_by,omitempty" example:"018f3a5c-7a6f-7c2a-8d4a-4b5b1f6c8e9d"`
|
||||
UpdatedAt string `json:"updated_at,omitempty" example:"2026-03-12T03:04:05Z"`
|
||||
UpdatedBy string `json:"updated_by,omitempty" example:"018f3a5c-7a6f-7c2a-8d4a-4b5b1f6c8e9d"`
|
||||
DeletedAt string `json:"deleted_at,omitempty" example:"2026-03-12T03:04:05Z"`
|
||||
DeletedBy string `json:"deleted_by,omitempty" example:"018f3a5c-7a6f-7c2a-8d4a-4b5b1f6c8e9d"`
|
||||
TrashedAt string `json:"trashed_at,omitempty" example:"2026-03-12T03:04:05Z"`
|
||||
PurgeAt string `json:"purge_at,omitempty" example:"2026-04-12T03:04:05Z"`
|
||||
}
|
||||
|
||||
type FileManagerFileListAttributes struct {
|
||||
Name string `json:"name" example:"policy.pdf"`
|
||||
NameNormalized string `json:"name_normalized,omitempty" example:"policy.pdf"`
|
||||
Extension string `json:"extension,omitempty" example:"pdf"`
|
||||
SizeBytes int64 `json:"size_bytes" example:"1024"`
|
||||
MimeType string `json:"mime_type" example:"application/pdf"`
|
||||
IsTemplate bool `json:"is_template" example:"true"`
|
||||
DownloadURL string `json:"download_url,omitempty" example:"https://s3.localhost.localstack.cloud:4566/wucher-file-dev/files/2026/04/05/policy.pdf?X-Amz-Signature=..."`
|
||||
Status string `json:"status" example:"ready"`
|
||||
CreatedAt string `json:"created_at,omitempty" example:"2026-03-12T03:04:05Z"`
|
||||
CreatedBy string `json:"created_by,omitempty" example:"018f3a5c-7a6f-7c2a-8d4a-4b5b1f6c8e9d"`
|
||||
UpdatedAt string `json:"updated_at,omitempty" example:"2026-03-12T03:04:05Z"`
|
||||
UpdatedBy string `json:"updated_by,omitempty" example:"018f3a5c-7a6f-7c2a-8d4a-4b5b1f6c8e9d"`
|
||||
}
|
||||
|
||||
type FileManagerFileLocationAttributes struct {
|
||||
FolderID string `json:"folder_id" example:"018f3a5c-7a6f-7c2a-8d4a-4b5b1f6c8e9d"`
|
||||
}
|
||||
|
||||
type FileManagerFileProcessingAttributes struct {
|
||||
ProcessingStartedAt string `json:"processing_started_at,omitempty" example:"2026-03-12T03:04:05Z"`
|
||||
ProcessedAt string `json:"processed_at,omitempty" example:"2026-03-12T03:04:05Z"`
|
||||
ValidatedAt string `json:"validated_at,omitempty" example:"2026-03-12T03:04:05Z"`
|
||||
FailedAt string `json:"failed_at,omitempty" example:"2026-03-12T03:04:05Z"`
|
||||
FailureReason string `json:"failure_reason,omitempty" example:"processing validation failed"`
|
||||
}
|
||||
|
||||
type FileManagerFileAuditAttributes struct {
|
||||
CreatedAt string `json:"created_at,omitempty" example:"2026-03-12T03:04:05Z"`
|
||||
CreatedBy string `json:"created_by,omitempty" example:"018f3a5c-7a6f-7c2a-8d4a-4b5b1f6c8e9d"`
|
||||
UpdatedAt string `json:"updated_at,omitempty" example:"2026-03-12T03:04:05Z"`
|
||||
UpdatedBy string `json:"updated_by,omitempty" example:"018f3a5c-7a6f-7c2a-8d4a-4b5b1f6c8e9d"`
|
||||
}
|
||||
|
||||
type FileManagerFileDetailAttributes struct {
|
||||
Name string `json:"name" example:"policy.pdf"`
|
||||
NameNormalized string `json:"name_normalized,omitempty" example:"policy.pdf"`
|
||||
Extension string `json:"extension,omitempty" example:"pdf"`
|
||||
SizeBytes int64 `json:"size_bytes" example:"1024"`
|
||||
MimeType string `json:"mime_type" example:"application/pdf"`
|
||||
IsTemplate bool `json:"is_template" example:"true"`
|
||||
DownloadURL string `json:"download_url,omitempty" example:"https://s3.localhost.localstack.cloud:4566/wucher-file-dev/files/2026/04/05/policy.pdf?X-Amz-Signature=..."`
|
||||
PreviewURL string `json:"preview_url,omitempty" example:"https://s3.localhost.localstack.cloud:4566/wucher-file-dev/files/2026/04/05/policy.preview.pdf?X-Amz-Signature=..."`
|
||||
Status string `json:"status" example:"ready"`
|
||||
Location *FileManagerFileLocationAttributes `json:"location,omitempty"`
|
||||
Processing *FileManagerFileProcessingAttributes `json:"processing,omitempty"`
|
||||
Audit FileManagerFileAuditAttributes `json:"audit"`
|
||||
}
|
||||
|
||||
type FileManagerFileDownloadAttributes struct {
|
||||
URL string `json:"url" example:"https://s3.localhost.localstack.cloud:4566/..."`
|
||||
ExpiresInSecond int64 `json:"expires_in_second" example:"900"`
|
||||
}
|
||||
|
||||
type FileManagerFileUploadIntentAttributes struct {
|
||||
UploadIntentID string `json:"upload_intent_uuid" example:"018f3a5c-7a6f-7c2a-8d4a-4b5b1f6c8e9d"`
|
||||
UploadURL string `json:"upload_url" example:"https://s3.localhost.localstack.cloud:4566/wucher-file-dev/..."`
|
||||
Method string `json:"method" example:"PUT"`
|
||||
RequiredHeaders map[string]string `json:"required_headers,omitempty"`
|
||||
Object FileUploadObject `json:"object"`
|
||||
File FileUploadFile `json:"file"`
|
||||
ExpiresAt string `json:"expires_at" example:"2026-04-05T07:29:34Z"`
|
||||
ExpiresInSeconds int64 `json:"expires_in_seconds" example:"900"`
|
||||
}
|
||||
|
||||
type FileUploadObject struct {
|
||||
Bucket string `json:"bucket" example:"wucher-file-dev"`
|
||||
Key string `json:"key" example:"files/2026/04/05/018f3a5c7a6f7c2a8d4a4b5b1f6c8e9d.pdf"`
|
||||
}
|
||||
|
||||
type FileUploadFile struct {
|
||||
Name string `json:"name" example:"policy-final.pdf"`
|
||||
MimeType string `json:"mime_type" example:"application/pdf"`
|
||||
SizeBytes int64 `json:"size_bytes" example:"1024"`
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
package shared
|
||||
|
||||
type FileManagerFolderAttributes struct {
|
||||
ParentID string `json:"parent_id,omitempty" example:"018f3a5c-7a6f-7c2a-8d4a-4b5b1f6c8e9d"`
|
||||
Name string `json:"name" example:"operations"`
|
||||
NameNormalized string `json:"name_normalized,omitempty" example:"operations"`
|
||||
Depth int `json:"depth" example:"1"`
|
||||
PathCache string `json:"path_cache,omitempty" example:"/root/operations"`
|
||||
CreatedAt string `json:"created_at,omitempty" example:"2026-03-12T03:04:05Z"`
|
||||
CreatedBy string `json:"created_by,omitempty" example:"018f3a5c-7a6f-7c2a-8d4a-4b5b1f6c8e9d"`
|
||||
UpdatedAt string `json:"updated_at,omitempty" example:"2026-03-12T03:04:05Z"`
|
||||
UpdatedBy string `json:"updated_by,omitempty" example:"018f3a5c-7a6f-7c2a-8d4a-4b5b1f6c8e9d"`
|
||||
DeletedAt string `json:"deleted_at,omitempty" example:"2026-03-12T03:04:05Z"`
|
||||
DeletedBy string `json:"deleted_by,omitempty" example:"018f3a5c-7a6f-7c2a-8d4a-4b5b1f6c8e9d"`
|
||||
PurgeAt string `json:"purge_at,omitempty" example:"2026-04-12T03:04:05Z"`
|
||||
}
|
||||
|
||||
type FileManagerFolderLocationAttributes struct {
|
||||
ParentID string `json:"parent_id" example:"018f3a5c-7a6f-7c2a-8d4a-4b5b1f6c8e9d"`
|
||||
}
|
||||
|
||||
type FileManagerFolderAuditAttributes struct {
|
||||
CreatedAt string `json:"created_at,omitempty" example:"2026-03-12T03:04:05Z"`
|
||||
CreatedBy string `json:"created_by,omitempty" example:"018f3a5c-7a6f-7c2a-8d4a-4b5b1f6c8e9d"`
|
||||
UpdatedAt string `json:"updated_at,omitempty" example:"2026-03-12T03:04:05Z"`
|
||||
UpdatedBy string `json:"updated_by,omitempty" example:"018f3a5c-7a6f-7c2a-8d4a-4b5b1f6c8e9d"`
|
||||
}
|
||||
|
||||
type FileManagerFolderDetailAttributes struct {
|
||||
Name string `json:"name" example:"operations"`
|
||||
NameNormalized string `json:"name_normalized,omitempty" example:"operations"`
|
||||
Depth *int `json:"depth,omitempty" example:"1"`
|
||||
PathCache string `json:"path_cache,omitempty" example:"/root/operations"`
|
||||
Location *FileManagerFolderLocationAttributes `json:"location,omitempty"`
|
||||
Audit FileManagerFolderAuditAttributes `json:"audit"`
|
||||
}
|
||||
174
internal/transport/http/dto/shared/fleet_status_shared.go
Normal file
174
internal/transport/http/dto/shared/fleet_status_shared.go
Normal file
@@ -0,0 +1,174 @@
|
||||
package shared
|
||||
|
||||
type FleetStatusHelicopter struct {
|
||||
ID string `json:"id" example:"019d6771-5fb5-7337-837f-bc5ed85181a1"`
|
||||
Designation string `json:"designation,omitempty" example:"AIRBUS H145"`
|
||||
Identifier string `json:"identifier,omitempty" example:"D-HEMS"`
|
||||
Type string `json:"type,omitempty" example:"EC-135"`
|
||||
Status string `json:"status,omitempty" example:"available" enums:"available,booked,aog,mcf"`
|
||||
// FlightID — id of the aircraft's current active flight (present only when it is in
|
||||
// use / booked). The fleet page uses it to create an EASA release scoped to the flight.
|
||||
FlightID string `json:"flight_id,omitempty" example:"019d6774-98e8-7fba-9ef4-3795c0da8a13"`
|
||||
StatusReasons []FleetStatusHelicopterReason `json:"status_reasons,omitempty"`
|
||||
}
|
||||
|
||||
// FleetStatusMCF is the helicopter's maintenance check flight shown on the fleet page.
|
||||
// When the helicopter has no MCF yet, id is empty and sign is false. af_hours /
|
||||
// landing_count are the SIGNED check-flight values — present only once the MCF is signed.
|
||||
type FleetStatusMCF struct {
|
||||
// Active — true when the helicopter is currently in MCF (an open draft, or a signed
|
||||
// but failed MCF still needing a re-flight); false when cleared/cancelled/none.
|
||||
Active bool `json:"active"`
|
||||
ID string `json:"id"`
|
||||
Date *string `json:"date"`
|
||||
// AFHours / LandingCount present only once the MCF is signed; omitted for none/draft/cancelled.
|
||||
AFHours *string `json:"af_hours,omitempty"`
|
||||
LandingCount *int `json:"landing_count,omitempty"`
|
||||
// Sign — true once the MCF is completed/signed; false while it is still a draft.
|
||||
Sign bool `json:"sign"`
|
||||
// SignedBy / SignedByName / SignedByShort — who signed (completed) the MCF; empty while unsigned.
|
||||
SignedBy string `json:"signed_by,omitempty"`
|
||||
SignedByName *string `json:"signed_by_name,omitempty"`
|
||||
SignedByShort *string `json:"signed_by_short,omitempty"`
|
||||
}
|
||||
|
||||
type MaintenanceScheduleAttributes struct {
|
||||
InspectionType string `json:"inspection_type" example:"A/F"`
|
||||
Type string `json:"type" example:"100H"`
|
||||
Due string `json:"due" validate:"omitempty,hours_or_date" example:"1200"`
|
||||
Ext string `json:"ext" validate:"omitempty,hours_or_date" example:"1300"`
|
||||
}
|
||||
|
||||
type FleetStatusFileAttributes struct {
|
||||
FileID string `json:"file_id" validate:"required" example:"019d6773-ccca-71dd-b9c7-8e6f531deb90"`
|
||||
Category string `json:"category" validate:"required,oneof=wb bfi" example:"wb"`
|
||||
// IsMandatory — when true, this file must be checked (is_done) during takeover.
|
||||
IsMandatory *bool `json:"is_mandatory,omitempty" example:"true"`
|
||||
}
|
||||
|
||||
type FleetStatusSummaryAirframe struct {
|
||||
Hours *float64 `json:"hours"`
|
||||
Cycles *float64 `json:"cycles"`
|
||||
}
|
||||
|
||||
type FleetStatusSummaryEngine struct {
|
||||
Hours *float64 `json:"hours"`
|
||||
GpcNgN1 *float64 `json:"gpc_ng_n1"`
|
||||
PtcNfN2 *float64 `json:"ptc_nf_n2"`
|
||||
Ccc *float64 `json:"ccc"`
|
||||
}
|
||||
|
||||
type FleetStatusSummary struct {
|
||||
Airframe FleetStatusSummaryAirframe `json:"airframe"`
|
||||
Engine1 FleetStatusSummaryEngine `json:"engine_1"`
|
||||
Engine2 FleetStatusSummaryEngine `json:"engine_2"`
|
||||
Landings *float64 `json:"landings"`
|
||||
FlightReports *float64 `json:"flight_reports"`
|
||||
RotorBrakeCycles *float64 `json:"rotor_brake_cycles"`
|
||||
HookReleases *float64 `json:"hook_releases"`
|
||||
}
|
||||
|
||||
type FleetStatusServiceLogAttributes struct {
|
||||
FleetStatusID string `json:"fleet_status_id" example:"019d6773-ccca-71dd-b9c7-8e6f531deb90"`
|
||||
HelicopterID string `json:"helicopter_id" example:"019d6771-5fb5-7337-837f-bc5ed85181a1"`
|
||||
Helicopter *FleetStatusHelicopter `json:"helicopter,omitempty"`
|
||||
FleetStatusStatus string `json:"fleet_status_status,omitempty" example:"serviced"`
|
||||
InspectionType string `json:"inspection_type" example:"A/F"`
|
||||
Type string `json:"type" example:"100H"`
|
||||
Due string `json:"due" example:"1200 FH"`
|
||||
Ext string `json:"ext" example:"1300 FH"`
|
||||
ServicedAt string `json:"serviced_at" example:"2026-05-29T07:43:06Z"`
|
||||
CreatedAt string `json:"created_at" example:"2026-05-29T07:43:06Z"`
|
||||
CreatedBy string `json:"created_by" example:"John Doe"`
|
||||
}
|
||||
|
||||
const (
|
||||
ReasonCodeManualAOG = "manual_aog"
|
||||
ReasonCodeMELExpired = "mel_expired"
|
||||
ReasonCodeNonMELPending = "non_mel_pending"
|
||||
ReasonCodeInspectionOverdue = "inspection_overdue"
|
||||
)
|
||||
|
||||
type FleetStatusComplaint struct {
|
||||
ID string `json:"id"`
|
||||
FlightID string `json:"flight_id,omitempty"`
|
||||
Description string `json:"description"`
|
||||
Status string `json:"status" enums:"active_non_mel,active_mel,serviced"`
|
||||
ReportedAt string `json:"reported_at"`
|
||||
ReportedBy string `json:"reported_by,omitempty"`
|
||||
ReportedByName string `json:"reported_by_name,omitempty"`
|
||||
ReportedByShort string `json:"reported_by_short,omitempty"`
|
||||
AircraftHoursAtReport *string `json:"aircraft_hours_at_report,omitempty"`
|
||||
MEL FleetStatusComplaintMEL `json:"mel"`
|
||||
NSR FleetStatusComplaintNSR `json:"nsr"`
|
||||
Action FleetStatusComplaintAction `json:"action"`
|
||||
}
|
||||
|
||||
type FleetStatusComplaintMEL struct {
|
||||
Severity int8 `json:"severity" enums:"0,1,2,3,4"`
|
||||
SeverityLabel *string `json:"severity_label"`
|
||||
ClassifiedAt *string `json:"classified_at,omitempty"`
|
||||
ClassifiedBy string `json:"classified_by,omitempty"`
|
||||
ClassifiedByName string `json:"classified_by_name,omitempty"`
|
||||
ClassifiedByShort string `json:"classified_by_short,omitempty"`
|
||||
Deadline *string `json:"deadline,omitempty"`
|
||||
}
|
||||
|
||||
type FleetStatusComplaintNSR struct {
|
||||
IsNSR bool `json:"is_nsr"`
|
||||
DecidedAt *string `json:"decided_at,omitempty"`
|
||||
DecidedBy string `json:"decided_by,omitempty"`
|
||||
DecidedByName string `json:"decided_by_name,omitempty"`
|
||||
DecidedByShort string `json:"decided_by_short,omitempty"`
|
||||
Reason *string `json:"reason,omitempty"`
|
||||
}
|
||||
|
||||
type FleetStatusComplaintAction struct {
|
||||
Taken *string `json:"taken"`
|
||||
SignedAt *string `json:"signed_at,omitempty"`
|
||||
SignedBy string `json:"signed_by,omitempty"`
|
||||
SignedByName string `json:"signed_by_name,omitempty"`
|
||||
SignedByShort string `json:"signed_by_short,omitempty"`
|
||||
AircraftHoursAtFix *string `json:"aircraft_hours_at_fix,omitempty"`
|
||||
}
|
||||
|
||||
type FleetStatusEASARelease struct {
|
||||
ID string `json:"id"`
|
||||
IsSigned bool `json:"is_signed"`
|
||||
EASADate *string `json:"easa_date,omitempty"`
|
||||
EASAACHours *string `json:"easa_ac_hours,omitempty"`
|
||||
EASALocation *string `json:"easa_location,omitempty"`
|
||||
EASAWONo *string `json:"easa_wo_no,omitempty"`
|
||||
EASAMaintManualRevAirframe *string `json:"easa_maint_manual_rev_airframe,omitempty"`
|
||||
EASAMaintManualRevEngine *string `json:"easa_maint_manual_rev_engine,omitempty"`
|
||||
EASASignerID *string `json:"easa_signer_id,omitempty"`
|
||||
EASAPermitNo *string `json:"easa_permit_no,omitempty"`
|
||||
SignedAt *string `json:"signed_at,omitempty"`
|
||||
SignedBy string `json:"signed_by,omitempty"`
|
||||
SignedByName string `json:"signed_by_name,omitempty"`
|
||||
SignedByShort string `json:"signed_by_short,omitempty"`
|
||||
MissingSignFields []string `json:"missing_sign_fields,omitempty"`
|
||||
}
|
||||
|
||||
func MELLabel(severity int8) string {
|
||||
switch severity {
|
||||
case 1:
|
||||
return "A"
|
||||
case 2:
|
||||
return "B"
|
||||
case 3:
|
||||
return "C"
|
||||
case 4:
|
||||
return "D"
|
||||
default:
|
||||
return ""
|
||||
}
|
||||
}
|
||||
|
||||
type FleetStatusHelicopterReason struct {
|
||||
Code string `json:"code" enums:"manual_aog,mel_expired,non_mel_pending,inspection_overdue"`
|
||||
Detail string `json:"detail"`
|
||||
Since *string `json:"since,omitempty"`
|
||||
ComplaintID string `json:"complaint_id,omitempty"`
|
||||
ScheduleID string `json:"schedule_id,omitempty"`
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
package shared
|
||||
|
||||
type FlightInspectionFileChecklistAttributes struct {
|
||||
IsDone bool `json:"is_done" example:"false"`
|
||||
HelicopterFileID string `json:"helicopter_file_id" example:"019d6771-5fb5-7337-837f-bc5ed85181a1"`
|
||||
FlightInspectionID string `json:"flight_inspection_id" example:"019d6772-471c-7b70-b42a-d94020ef61e4"`
|
||||
CreatedAt string `json:"created_at,omitempty" example:"2026-04-14T08:00:00Z"`
|
||||
CreatedBy string `json:"created_by,omitempty" example:"019d61db-53c6-7280-87ec-e65f205c6d3b"`
|
||||
UpdatedAt string `json:"updated_at,omitempty" example:"2026-04-14T08:00:00Z"`
|
||||
UpdatedBy string `json:"updated_by,omitempty" example:"019d61db-53c6-7280-87ec-e65f205c6d3b"`
|
||||
}
|
||||
18
internal/transport/http/dto/shared/helicopter_file_shared.go
Normal file
18
internal/transport/http/dto/shared/helicopter_file_shared.go
Normal file
@@ -0,0 +1,18 @@
|
||||
package shared
|
||||
|
||||
type HelicopterFileAttributes struct {
|
||||
Section string `json:"section" example:"bfi"`
|
||||
Position int `json:"position" example:"1"`
|
||||
IsMandatory bool `json:"is_mandatory" example:"true"`
|
||||
IsTemplate bool `json:"is_template" example:"true"`
|
||||
HelicopterID string `json:"helicopter_id" example:"019d6771-5fb5-7337-837f-bc5ed85181a1"`
|
||||
HelicopterName string `json:"helicopter_name,omitempty" example:"AIRBUS H145"`
|
||||
FileAttachmentID string `json:"file_attachment_id" example:"019d6771-6c8e-7a2d-a7b8-03bcb65fcb3e"`
|
||||
AttachmentPending bool `json:"attachment_pending" example:"true"`
|
||||
FileName string `json:"file_name,omitempty" example:"Before Flight Checklist.pdf"`
|
||||
DownloadURL string `json:"download_url,omitempty" example:"https://s3.localhost.localstack.cloud:4566/wucher-file-dev/files/2026/04/05/policy.pdf?X-Amz-Signature=..."`
|
||||
CreatedAt string `json:"created_at,omitempty" example:"2026-04-14T08:00:00Z"`
|
||||
CreatedBy string `json:"created_by,omitempty" example:"019d61db-53c6-7280-87ec-e65f205c6d3b"`
|
||||
UpdatedAt string `json:"updated_at,omitempty" example:"2026-04-14T08:00:00Z"`
|
||||
UpdatedBy string `json:"updated_by,omitempty" example:"019d61db-53c6-7280-87ec-e65f205c6d3b"`
|
||||
}
|
||||
45
internal/transport/http/dto/shared/helicopter_shared.go
Normal file
45
internal/transport/http/dto/shared/helicopter_shared.go
Normal file
@@ -0,0 +1,45 @@
|
||||
package shared
|
||||
|
||||
// Helicopter nested value objects reused as building blocks by helicopter
|
||||
// response DTOs (and available to any other module that embeds helicopter
|
||||
// summaries).
|
||||
|
||||
type HelicopterLastDailyInspection struct {
|
||||
InspectionID string `json:"inspection_id,omitempty" example:"019d6772-471c-7b70-b42a-d94020ef61e4"`
|
||||
Status string `json:"status" example:"inspected_cleared"`
|
||||
Inspector HelicopterLastDailyInspector `json:"inspector"`
|
||||
Base HelicopterLastDailyBase `json:"base"`
|
||||
InspectedAt string `json:"inspected_at,omitempty" example:"2026-05-25T07:47:00Z"`
|
||||
UTCTime string `json:"utc_time,omitempty" example:"07:47"`
|
||||
InspectionFilesChecklist HelicopterInspectionFilesChecklist `json:"inspection_files_checklist"`
|
||||
}
|
||||
|
||||
type HelicopterLastDailyInspector struct {
|
||||
ID string `json:"id,omitempty" example:"019d61db-53c6-7280-87ec-e65f205c6d3b"`
|
||||
Name string `json:"name,omitempty" example:"Teodora Lesmana"`
|
||||
LicenseNo string `json:"license_no,omitempty" example:"4521"`
|
||||
}
|
||||
|
||||
type HelicopterLastDailyBase struct {
|
||||
ID string `json:"id,omitempty" example:"018f3a5c-7a6f-7c2a-8d4a-4b5b1f6c8e9d"`
|
||||
BaseAbbreviation string `json:"base_abbreviation,omitempty" example:"G1"`
|
||||
BaseName string `json:"base_name,omitempty" example:"Gallus 1 - Zurs Lech"`
|
||||
Type string `json:"type,omitempty" example:"hems"`
|
||||
}
|
||||
|
||||
type HelicopterInspectionFilesChecklist struct {
|
||||
Completed int `json:"completed" example:"47"`
|
||||
Total int `json:"total" example:"47"`
|
||||
}
|
||||
|
||||
type HelicopterFoto struct {
|
||||
UUID string `json:"uuid" example:"018f3a5c-7a6f-7c2a-8d4a-4b5b1f6c8e9d"`
|
||||
DownloadURL string `json:"download_url" example:"https://storage.example.com/helicopter/foto.jpg?X-Amz-Algorithm=AWS4-HMAC-SHA256&X-Amz-Expires=900&X-Amz-Signature=abc123"`
|
||||
ThumbnailDownloadURL string `json:"thumbnail_download_url,omitempty" example:"https://storage.example.com/helicopter/foto.thumb.jpg?X-Amz-Algorithm=AWS4-HMAC-SHA256&X-Amz-Expires=900&X-Amz-Signature=abc123"`
|
||||
OriginalSizeBytes *int64 `json:"original_size_bytes,omitempty" example:"1024000"`
|
||||
}
|
||||
|
||||
type HelicopterAuditUser struct {
|
||||
ID string `json:"id" example:"018f3a5c-7a6f-7c2a-8d4a-4b5b1f6c8e9d"`
|
||||
Name string `json:"name" example:"John Doe"`
|
||||
}
|
||||
120
internal/transport/http/dto/shared/reserve_ac_shared.go
Normal file
120
internal/transport/http/dto/shared/reserve_ac_shared.go
Normal file
@@ -0,0 +1,120 @@
|
||||
package shared
|
||||
|
||||
import "time"
|
||||
|
||||
type ReserveAcAttributes struct {
|
||||
Status string `json:"status" example:"pending"`
|
||||
AircraftID string `json:"helicopter_id" example:"019d6771-5fb5-7337-837f-bc5ed85181a1"`
|
||||
Aircraft *ReserveAcAircraftSummary `json:"helicopter,omitempty"`
|
||||
LastDaily *ReserveAcLastDailyInfo `json:"last_daily_inspection,omitempty"`
|
||||
FlightID string `json:"flight_id,omitempty" example:"019d6773-ccca-71dd-b9c7-8e6f531deb90"`
|
||||
Inspection *ReserveAcInspectionSummary `json:"inspection,omitempty"`
|
||||
CreatedAt string `json:"created_at,omitempty" example:"2026-04-14T08:00:00Z"`
|
||||
CreatedBy string `json:"created_by,omitempty" example:"019d61db-53c6-7280-87ec-e65f205c6d3b"`
|
||||
UpdatedAt string `json:"updated_at,omitempty" example:"2026-04-14T08:00:00Z"`
|
||||
UpdatedBy string `json:"updated_by,omitempty" example:"019d61db-53c6-7280-87ec-e65f205c6d3b"`
|
||||
DeletedAt string `json:"deleted_at,omitempty" example:"2026-04-14T08:00:00Z"`
|
||||
DeletedBy string `json:"deleted_by,omitempty" example:"019d61db-53c6-7280-87ec-e65f205c6d3b"`
|
||||
}
|
||||
|
||||
type ReserveAcLastDailyInfo struct {
|
||||
Name string `json:"name" example:"Ganahl Stefan"`
|
||||
ShortName string `json:"short_name" example:"GAN"`
|
||||
Date string `json:"date" example:"17.04.2026"`
|
||||
UTCTime string `json:"utc_time" example:"10:57"`
|
||||
}
|
||||
|
||||
type ReserveAcAircraftSummary struct {
|
||||
ID string `json:"id" example:"019d6771-5fb5-7337-837f-bc5ed85181a1"`
|
||||
Designation string `json:"designation" example:"AIRBUS H145"`
|
||||
Identifier string `json:"identifier" example:"D-HYAR"`
|
||||
Type string `json:"type" example:"H145"`
|
||||
AOG bool `json:"aog" example:"false"`
|
||||
IsActive bool `json:"is_active" example:"true"`
|
||||
}
|
||||
|
||||
type ReserveAcInspectionSummary struct {
|
||||
ID string `json:"id" example:"019d6772-471c-7b70-b42a-d94020ef61e4"`
|
||||
InspectionDate string `json:"inspection_date,omitempty" example:"2026-04-15"`
|
||||
Before ReserveAcBeforeSectionDTO `json:"before"`
|
||||
Prepare ReserveAcPrepareSectionDTO `json:"prepare"`
|
||||
FleetStatusFile ReserveAcFleetStatusFileSectionDTO `json:"fleet_status_file"`
|
||||
}
|
||||
|
||||
type ReserveAcBeforeSectionDTO struct {
|
||||
Total int `json:"total" example:"3"`
|
||||
Completed int `json:"completed" example:"1"`
|
||||
DoneFiles []ReserveAcFileChecklistItemDTO `json:"done_file_checklist,omitempty"`
|
||||
Fields *ReserveAcBeforeInspectionFields `json:"fields,omitempty"`
|
||||
FileChecklist []ReserveAcFileChecklistItemDTO `json:"file_checklist"`
|
||||
}
|
||||
|
||||
type ReserveAcPrepareSectionDTO struct {
|
||||
Total int `json:"total" example:"3"`
|
||||
Completed int `json:"completed" example:"1"`
|
||||
DoneFiles []ReserveAcFileChecklistItemDTO `json:"done_file_checklist,omitempty"`
|
||||
PrepareFields *ReserveAcPrepareFields `json:"prepare_fields,omitempty"`
|
||||
FileChecklist []ReserveAcFileChecklistItemDTO `json:"file_checklist"`
|
||||
}
|
||||
|
||||
type ReserveAcFleetStatusFileSectionDTO struct {
|
||||
Total int `json:"total" example:"2"`
|
||||
Completed int `json:"completed" example:"1"`
|
||||
DoneFiles []ReserveAcFileChecklistItemDTO `json:"done_file_checklist,omitempty"`
|
||||
FileChecklist []ReserveAcFileChecklistItemDTO `json:"file_checklist"`
|
||||
}
|
||||
|
||||
type ReserveAcFileChecklistItemDTO struct {
|
||||
HelicopterFileID string `json:"helicopter_file_id" example:"019d7000-aaaa-7bbb-8ccc-111111111111"`
|
||||
FileAttachmentID string `json:"file_attachment_id,omitempty" example:"019d7000-aaaa-7bbb-8ccc-222222222222"`
|
||||
FileName string `json:"file_name,omitempty" example:"Before Flight Checklist.pdf"`
|
||||
DownloadURL string `json:"download_url,omitempty" example:"https://s3.localhost.localstack.cloud:4566/wucher-file-dev/files/2026/04/05/policy.pdf?X-Amz-Signature=..."`
|
||||
IsDone bool `json:"is_done" example:"false"`
|
||||
IsMandatory bool `json:"is_mandatory" example:"true"`
|
||||
}
|
||||
|
||||
type ReserveAcBeforeInspectionFields struct {
|
||||
OilEngineNR1Checked *bool `json:"oil_engine_nr1_checked,omitempty"`
|
||||
OilEngineNR2Checked *bool `json:"oil_engine_nr2_checked,omitempty"`
|
||||
HydraulicLHChecked *bool `json:"hydraulic_lh_checked,omitempty"`
|
||||
HydraulicRHChecked *bool `json:"hydraulic_rh_checked,omitempty"`
|
||||
OilTransmissionMGBChecked *bool `json:"oil_transmission_mgb_checked,omitempty"`
|
||||
OilTransmissionIGBChecked *bool `json:"oil_transmission_igb_checked,omitempty"`
|
||||
OilTransmissionTGBChecked *bool `json:"oil_transmission_tgb_checked,omitempty"`
|
||||
FuelAmount *float64 `json:"fuel_amount,omitempty"`
|
||||
FuelUnit string `json:"fuel_unit,omitempty"`
|
||||
FuelAddedAmount *float64 `json:"fuel_added_amount,omitempty"`
|
||||
Note *string `json:"note,omitempty"`
|
||||
}
|
||||
|
||||
type ReserveAcPrepareFields struct {
|
||||
NotamBriefing *bool `json:"notam_briefing,omitempty"`
|
||||
WeatherBriefing *bool `json:"weather_briefing,omitempty"`
|
||||
OperationalFlightPlan *bool `json:"operational_flight_plan,omitempty"`
|
||||
}
|
||||
|
||||
type LastDailyInspection struct {
|
||||
ID []byte
|
||||
Name string
|
||||
ShortName string
|
||||
At time.Time
|
||||
}
|
||||
|
||||
type LastDailyInspectionSummary struct {
|
||||
InspectionID string
|
||||
Status string
|
||||
|
||||
InspectorID string
|
||||
InspectorName string
|
||||
InspectorLicenseNo string
|
||||
|
||||
BaseID string
|
||||
BaseName string
|
||||
BaseAbbreviation string
|
||||
BaseType string
|
||||
|
||||
InspectedAt time.Time
|
||||
|
||||
InspectionFilesChecklistCompleted int
|
||||
InspectionFilesChecklistTotal int
|
||||
}
|
||||
103
internal/transport/http/dto/user_dto.go
Normal file
103
internal/transport/http/dto/user_dto.go
Normal file
@@ -0,0 +1,103 @@
|
||||
package dto
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
)
|
||||
|
||||
// JSON:API User DTOs
|
||||
|
||||
type UserResource struct {
|
||||
Type string `json:"type" example:"user"`
|
||||
ID string `json:"id" example:"018f3a5c-7a6f-7c2a-8d4a-4b5b1f6c8e9d"`
|
||||
Attributes UserAttributes `json:"attributes"`
|
||||
}
|
||||
|
||||
type UserListResponse struct {
|
||||
Data []UserResource `json:"data"`
|
||||
Meta struct {
|
||||
PageNumber int `json:"page_number" example:"1"`
|
||||
PageSize int `json:"page_size" example:"20"`
|
||||
Total int64 `json:"total" example:"120"`
|
||||
} `json:"meta"`
|
||||
}
|
||||
|
||||
type UserDataTableResponse struct {
|
||||
Data []UserResource `json:"data"`
|
||||
Meta struct {
|
||||
Draw int `json:"draw" example:"1"`
|
||||
RecordsTotal int64 `json:"records_total" example:"120"`
|
||||
RecordsFiltered int64 `json:"records_filtered" example:"12"`
|
||||
} `json:"meta"`
|
||||
}
|
||||
|
||||
// Create
|
||||
|
||||
type UserCreateAttributes struct {
|
||||
Email string `json:"email" validate:"required,email" example:"user@example.com"`
|
||||
Username string `json:"username" validate:"required,min=3,max=100" example:"john.doe"`
|
||||
FirstName string `json:"first_name" validate:"required" example:"John"`
|
||||
LastName string `json:"last_name" validate:"required" example:"Doe"`
|
||||
MobilePhone string `json:"mobile_phone,omitempty" example:"+628123456789"`
|
||||
Timezone *string `json:"timezone,omitempty" example:"Asia/Jakarta"`
|
||||
ImageUUID *string `json:"image_uuid,omitempty" example:"018f3a5c-7a6f-7c2a-8d4a-4b5b1f6c8e90"`
|
||||
RoleID string `json:"role_id" validate:"required" example:"018f3a5c-7a6f-7c2a-8d4a-4b5b1f6c8e9d"`
|
||||
RoleIDs []string `json:"role_ids,omitempty" example:"[\"018f3a5c-7a6f-7c2a-8d4a-4b5b1f6c8e9d\",\"018f3a5c-7a6f-7c2a-8d4a-4b5b1f6c8e9e\"]"`
|
||||
}
|
||||
|
||||
type UserCreateData struct {
|
||||
Type string `json:"type" validate:"required,oneof=user_create user" example:"user_create"`
|
||||
Attributes UserCreateAttributes `json:"attributes" validate:"required"`
|
||||
}
|
||||
|
||||
type UserCreateRequest struct {
|
||||
Data UserCreateData `json:"data" validate:"required"`
|
||||
}
|
||||
|
||||
// Update
|
||||
|
||||
type UserUpdateAttributes struct {
|
||||
Email *string `json:"email,omitempty" validate:"omitempty,email" example:"user@example.com"`
|
||||
FirstName *string `json:"first_name,omitempty" example:"John"`
|
||||
LastName *string `json:"last_name,omitempty" example:"Doe"`
|
||||
MobilePhone *string `json:"mobile_phone,omitempty" example:"+628123456789"`
|
||||
Timezone *string `json:"timezone,omitempty" example:"Asia/Jakarta"`
|
||||
ImageUUID NullString `json:"image_uuid,omitempty" swaggertype:"string" example:"018f3a5c-7a6f-7c2a-8d4a-4b5b1f6c8e90"`
|
||||
}
|
||||
|
||||
// NullString allows PATCH semantics:
|
||||
// - field omitted => Set=false (no change)
|
||||
// - value null => Set=true, Valid=false (clear value)
|
||||
// - value string => Set=true, Valid=true, Value=<string>
|
||||
type NullString struct {
|
||||
Set bool
|
||||
Valid bool
|
||||
Value string
|
||||
}
|
||||
|
||||
func (n *NullString) UnmarshalJSON(data []byte) error {
|
||||
n.Set = true
|
||||
trimmed := bytes.TrimSpace(data)
|
||||
if bytes.Equal(trimmed, []byte("null")) {
|
||||
n.Valid = false
|
||||
n.Value = ""
|
||||
return nil
|
||||
}
|
||||
var v string
|
||||
if err := json.Unmarshal(trimmed, &v); err != nil {
|
||||
return err
|
||||
}
|
||||
n.Valid = true
|
||||
n.Value = v
|
||||
return nil
|
||||
}
|
||||
|
||||
type UserUpdateData struct {
|
||||
Type string `json:"type" validate:"required,oneof=user_update user" example:"user_update"`
|
||||
ID string `json:"id" example:"018f3a5c-7a6f-7c2a-8d4a-4b5b1f6c8e9d"`
|
||||
Attributes UserUpdateAttributes `json:"attributes" validate:"required"`
|
||||
}
|
||||
|
||||
type UserUpdateRequest struct {
|
||||
Data UserUpdateData `json:"data" validate:"required"`
|
||||
}
|
||||
77
internal/transport/http/dto/vocation_dto.go
Normal file
77
internal/transport/http/dto/vocation_dto.go
Normal file
@@ -0,0 +1,77 @@
|
||||
package dto
|
||||
|
||||
// JSON:API Vocation DTOs
|
||||
|
||||
type VocationAttributes struct {
|
||||
Name string `json:"name" example:"Pilot"`
|
||||
SortKey *int `json:"sortkey" example:"1"`
|
||||
Note string `json:"note,omitempty" example:"Helicopter pilot vocation"`
|
||||
IsActive bool `json:"is_active" example:"true"`
|
||||
CreatedAt string `json:"created_at,omitempty" example:"2026-03-12T03:04:05Z"`
|
||||
CreatedBy string `json:"created_by" example:"018f3a5c-7a6f-7c2a-8d4a-4b5b1f6c8e9d"`
|
||||
UpdatedAt string `json:"updated_at,omitempty" example:"2026-03-12T03:04:05Z"`
|
||||
UpdatedBy string `json:"updated_by" example:"018f3a5c-7a6f-7c2a-8d4a-4b5b1f6c8e9d"`
|
||||
DeletedAt string `json:"deleted_at,omitempty" example:"2026-03-12T03:04:05Z"`
|
||||
DeletedBy string `json:"deleted_by,omitempty" example:"018f3a5c-7a6f-7c2a-8d4a-4b5b1f6c8e9d"`
|
||||
}
|
||||
|
||||
type VocationResource struct {
|
||||
Type string `json:"type" example:"vocation"`
|
||||
ID string `json:"id" example:"018f3a5c-7a6f-7c2a-8d4a-4b5b1f6c8e9d"`
|
||||
Attributes VocationAttributes `json:"attributes"`
|
||||
}
|
||||
|
||||
type VocationResponse struct {
|
||||
Data VocationResource `json:"data"`
|
||||
}
|
||||
|
||||
type VocationListResponse struct {
|
||||
Data []VocationResource `json:"data"`
|
||||
Meta struct {
|
||||
PageNumber int `json:"page_number" example:"1"`
|
||||
PageSize int `json:"page_size" example:"20"`
|
||||
Total int64 `json:"total" example:"120"`
|
||||
} `json:"meta"`
|
||||
}
|
||||
|
||||
type VocationDataTableResponse struct {
|
||||
Data []VocationResource `json:"data"`
|
||||
Meta struct {
|
||||
Draw int `json:"draw" example:"1"`
|
||||
RecordsTotal int64 `json:"records_total" example:"120"`
|
||||
RecordsFiltered int64 `json:"records_filtered" example:"12"`
|
||||
} `json:"meta"`
|
||||
}
|
||||
|
||||
type VocationCreateAttributes struct {
|
||||
Name string `json:"name" validate:"required" example:"Pilot"`
|
||||
SortKey *int `json:"sortkey" example:"1"`
|
||||
Note *string `json:"note,omitempty" example:"Helicopter pilot vocation"`
|
||||
IsActive *bool `json:"is_active,omitempty" example:"true"`
|
||||
}
|
||||
|
||||
type VocationCreateData struct {
|
||||
Type string `json:"type" validate:"required,oneof=vocation_create vocation" example:"vocation_create"`
|
||||
Attributes VocationCreateAttributes `json:"attributes" validate:"required"`
|
||||
}
|
||||
|
||||
type VocationCreateRequest struct {
|
||||
Data VocationCreateData `json:"data" validate:"required"`
|
||||
}
|
||||
|
||||
type VocationUpdateAttributes struct {
|
||||
Name *string `json:"name,omitempty" example:"Pilot"`
|
||||
SortKey NullInt `json:"sortkey,omitempty" swaggertype:"integer" example:"1"`
|
||||
Note *string `json:"note,omitempty" example:"Helicopter pilot vocation"`
|
||||
IsActive *bool `json:"is_active,omitempty" example:"true"`
|
||||
}
|
||||
|
||||
type VocationUpdateData struct {
|
||||
Type string `json:"type" validate:"required,oneof=vocation_update vocation" example:"vocation_update"`
|
||||
ID string `json:"id" example:"018f3a5c-7a6f-7c2a-8d4a-4b5b1f6c8e9d"`
|
||||
Attributes VocationUpdateAttributes `json:"attributes" validate:"required"`
|
||||
}
|
||||
|
||||
type VocationUpdateRequest struct {
|
||||
Data VocationUpdateData `json:"data" validate:"required"`
|
||||
}
|
||||
256
internal/transport/http/handlers/action_signoff_handler.go
Normal file
256
internal/transport/http/handlers/action_signoff_handler.go
Normal file
@@ -0,0 +1,256 @@
|
||||
package handlers
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/gofiber/fiber/v2"
|
||||
|
||||
actionsignoff "wucher/internal/domain/action_signoff"
|
||||
complaint "wucher/internal/domain/complaint"
|
||||
"wucher/internal/shared/pkg/apperrorsx"
|
||||
"wucher/internal/shared/pkg/userctx"
|
||||
"wucher/internal/shared/pkg/uuidv7"
|
||||
requestdto "wucher/internal/transport/http/dto/request"
|
||||
responsedto "wucher/internal/transport/http/dto/response"
|
||||
"wucher/internal/transport/http/jsonapi"
|
||||
"wucher/internal/transport/http/response"
|
||||
)
|
||||
|
||||
// ActionSignoffHandler serves the corrective-action sign-off (sign/unsign) for a
|
||||
// helicopter — both complaint-linked and complaint-independent.
|
||||
type ActionSignoffHandler struct {
|
||||
svc actionsignoff.Service
|
||||
complaintSvc actionSignoffComplaintLookup
|
||||
}
|
||||
|
||||
type actionSignoffComplaintLookup interface {
|
||||
GetByID(ctx context.Context, id []byte) (*complaint.Complaint, error)
|
||||
}
|
||||
|
||||
func NewActionSignoffHandler(svc actionsignoff.Service) *ActionSignoffHandler {
|
||||
return &ActionSignoffHandler{svc: svc}
|
||||
}
|
||||
|
||||
// WithComplaintService wires the complaint lookup used to validate a complaint-linked
|
||||
// sign-off (complaint exists and has action_taken recorded).
|
||||
func (h *ActionSignoffHandler) WithComplaintService(svc actionSignoffComplaintLookup) *ActionSignoffHandler {
|
||||
h.complaintSvc = svc
|
||||
return h
|
||||
}
|
||||
|
||||
func (h *ActionSignoffHandler) resource(ctx context.Context, row *actionsignoff.ActionSignoff) responsedto.ActionSignoffResource {
|
||||
if row == nil {
|
||||
return responsedto.ActionSignoffResource{}
|
||||
}
|
||||
id, _ := uuidv7.BytesToString(row.ID)
|
||||
out := responsedto.ActionSignoffResource{
|
||||
Type: "action_signoff",
|
||||
ID: id,
|
||||
Attributes: responsedto.ActionSignoffAttributes{
|
||||
HelicopterID: idString(row.HelicopterID),
|
||||
FlightID: idString(row.FlightID),
|
||||
Signed: row.IsSigned(),
|
||||
SignedBy: idString(row.SignedBy),
|
||||
},
|
||||
}
|
||||
if row.SignedAt != nil {
|
||||
v := row.SignedAt.UTC().Format(time.RFC3339)
|
||||
out.Attributes.SignedAt = &v
|
||||
}
|
||||
if n := userctx.GetDisplayName(ctx, row.SignedBy); n != "" {
|
||||
out.Attributes.SignedByName = &n
|
||||
}
|
||||
if s := userctx.GetShortName(ctx, row.SignedBy); s != "" {
|
||||
out.Attributes.SignedByShort = &s
|
||||
}
|
||||
if !row.CreatedAt.IsZero() {
|
||||
out.Attributes.CreatedAt = row.CreatedAt.UTC().Format(time.RFC3339)
|
||||
}
|
||||
if !row.UpdatedAt.IsZero() {
|
||||
out.Attributes.UpdatedAt = row.UpdatedAt.UTC().Format(time.RFC3339)
|
||||
}
|
||||
return out
|
||||
}
|
||||
|
||||
func actionSignoffHelicopterID(c *fiber.Ctx) ([]byte, *apperrorsx.AppError) {
|
||||
id, err := uuidv7.ParseString(strings.TrimSpace(c.Params("helicopter_id")))
|
||||
if err != nil {
|
||||
appErr := apperrorsx.New(apperrorsx.ErrActionSignoffInvalidUUID).WithCause(err)
|
||||
appErr.Source = &jsonapi.ErrorSource{Pointer: "/path/helicopter_id"}
|
||||
return nil, appErr
|
||||
}
|
||||
return id, nil
|
||||
}
|
||||
|
||||
func actionSignoffFlightID(c *fiber.Ctx) ([]byte, *apperrorsx.AppError) {
|
||||
id, err := uuidv7.ParseString(strings.TrimSpace(c.Params("flight_id")))
|
||||
if err != nil {
|
||||
appErr := apperrorsx.New(apperrorsx.ErrActionSignoffInvalidUUID).WithCause(err)
|
||||
appErr.Source = &jsonapi.ErrorSource{Pointer: "/path/flight_id"}
|
||||
return nil, appErr
|
||||
}
|
||||
return id, nil
|
||||
}
|
||||
|
||||
// actionSignoffBodyFlightID parses the REQUIRED flight_id from the sign body — the
|
||||
// flight the sign-off is attributed to (standalone) or performed in (complaint-linked).
|
||||
func actionSignoffBodyFlightID(req requestdto.ActionSignoffSignRequest) ([]byte, *apperrorsx.AppError) {
|
||||
raw := ""
|
||||
if req.Data.Attributes.FlightID != nil {
|
||||
raw = strings.TrimSpace(*req.Data.Attributes.FlightID)
|
||||
}
|
||||
if raw == "" {
|
||||
appErr := apperrorsx.New(apperrorsx.ErrActionSignoffInvalidUUID)
|
||||
appErr.Source = &jsonapi.ErrorSource{Pointer: "/data/attributes/flight_id"}
|
||||
return nil, appErr
|
||||
}
|
||||
id, err := uuidv7.ParseString(raw)
|
||||
if err != nil {
|
||||
appErr := apperrorsx.New(apperrorsx.ErrActionSignoffInvalidUUID).WithCause(err)
|
||||
appErr.Source = &jsonapi.ErrorSource{Pointer: "/data/attributes/flight_id"}
|
||||
return nil, appErr
|
||||
}
|
||||
return id, nil
|
||||
}
|
||||
|
||||
// GetByHelicopter godoc
|
||||
// @Summary Get the current corrective-action sign-off for a helicopter
|
||||
// @Description Returns the fleet-level (complaint-independent) corrective-action sign-off state for a helicopter — `signed`, `signed_at`, `signed_by`. When none exists yet it returns an unsigned placeholder. A signed sign-off is what gates the EASA maintenance release.
|
||||
// @Tags Complaints - Sign Off
|
||||
// @Produce json
|
||||
// @Param flight_id path string true "Flight ID (UUIDv7)"
|
||||
// @Success 200 {object} responsedto.ActionSignoffResponse
|
||||
// @Router /api/v1/action-signoffs/get-by-flight/{flight_id} [get]
|
||||
func (h *ActionSignoffHandler) GetByFlight(c *fiber.Ctx) error {
|
||||
flightID, aerr := actionSignoffFlightID(c)
|
||||
if aerr != nil {
|
||||
return apperrorsx.Write(c, aerr)
|
||||
}
|
||||
row, err := h.svc.GetByFlight(c.UserContext(), flightID)
|
||||
if err != nil {
|
||||
return apperrorsx.Write(c, apperrorsx.New(apperrorsx.ErrActionSignoffNotFound).WithCause(err))
|
||||
}
|
||||
if row == nil {
|
||||
// No row yet: return an unsigned placeholder so the client can render.
|
||||
return response.Write(c, fiber.StatusOK, responsedto.ActionSignoffResource{
|
||||
Type: "action_signoff",
|
||||
Attributes: responsedto.ActionSignoffAttributes{
|
||||
FlightID: idString(flightID),
|
||||
Signed: false,
|
||||
},
|
||||
})
|
||||
}
|
||||
return response.Write(c, fiber.StatusOK, h.resource(c.UserContext(), row))
|
||||
}
|
||||
|
||||
// Sign godoc
|
||||
// @Summary Toggle the corrective-action sign-off (per complaint or fleet-wide)
|
||||
// @Description Toggles the corrective-action sign-off for a helicopter. `sign` (optional, default true) chooses the direction: true signs, false unsigns. Send `complaint_id` to target a specific complaint's sign-off — signing it requires `action_taken` recorded (else 422 `ACTION_SIGNOFF_ACTION_REQUIRED`), and 404 `ACTION_SIGNOFF_COMPLAINT_NOT_FOUND` if the complaint does not exist. Omit `complaint_id` for a fleet-wide (complaint-independent) sign-off. A signed sign-off is what allows an EASA maintenance release to be created for the helicopter.
|
||||
// @Tags Complaints - Sign Off
|
||||
// @Accept json
|
||||
// @Produce json
|
||||
// @Param helicopter_id path string true "Helicopter ID (UUIDv7)"
|
||||
// @Param request body requestdto.ActionSignoffSignRequest false "Optional complaint_id + note"
|
||||
// @Success 200 {object} responsedto.ActionSignoffResponse
|
||||
// @Failure 404 {object} jsonapi.ErrorDocument
|
||||
// @Failure 422 {object} jsonapi.ErrorDocument
|
||||
// @Router /api/v1/action-signoffs/sign/{helicopter_id} [post]
|
||||
func (h *ActionSignoffHandler) Sign(c *fiber.Ctx) error {
|
||||
helicopterID, err := actionSignoffHelicopterID(c)
|
||||
if err != nil {
|
||||
return apperrorsx.Write(c, err)
|
||||
}
|
||||
// Body is optional — the sign-off can be checked with nothing filled.
|
||||
var req requestdto.ActionSignoffSignRequest
|
||||
_ = c.BodyParser(&req)
|
||||
actor := actorUserID(c)
|
||||
// sign toggles the state: true (or omitted) signs, false unsigns.
|
||||
signWanted := req.Data.Attributes.Sign == nil || *req.Data.Attributes.Sign
|
||||
|
||||
complaintIDRaw := ""
|
||||
if req.Data.Attributes.ComplaintID != nil {
|
||||
complaintIDRaw = strings.TrimSpace(*req.Data.Attributes.ComplaintID)
|
||||
}
|
||||
|
||||
// Complaint-linked toggle: sign/unsign that complaint's corrective action.
|
||||
if complaintIDRaw != "" {
|
||||
complaintID, perr := uuidv7.ParseString(complaintIDRaw)
|
||||
if perr != nil {
|
||||
appErr := apperrorsx.New(apperrorsx.ErrActionSignoffInvalidUUID).WithCause(perr)
|
||||
appErr.Source = &jsonapi.ErrorSource{Pointer: "/data/attributes/complaint_id"}
|
||||
return apperrorsx.Write(c, appErr)
|
||||
}
|
||||
if !signWanted {
|
||||
row, serr := h.svc.UnsignByComplaint(c.UserContext(), complaintID)
|
||||
if serr != nil {
|
||||
return apperrorsx.Write(c, apperrorsx.New(apperrorsx.ErrActionSignoffUnsignFailed).WithCause(serr))
|
||||
}
|
||||
if row == nil {
|
||||
return response.Write(c, fiber.StatusOK, unsignedActionSignoffPlaceholder(helicopterID))
|
||||
}
|
||||
return response.Write(c, fiber.StatusOK, h.resource(c.UserContext(), row))
|
||||
}
|
||||
if h.complaintSvc == nil {
|
||||
return apperrorsx.Write(c, apperrorsx.New(apperrorsx.ErrActionSignoffSignFailed))
|
||||
}
|
||||
comp, cerr := h.complaintSvc.GetByID(c.UserContext(), complaintID)
|
||||
if cerr != nil {
|
||||
return apperrorsx.Write(c, apperrorsx.New(apperrorsx.ErrActionSignoffSignFailed).WithCause(cerr))
|
||||
}
|
||||
if comp == nil {
|
||||
return apperrorsx.Write(c, apperrorsx.New(apperrorsx.ErrActionSignoffComplaintNotFound))
|
||||
}
|
||||
// The complaint's corrective action must be recorded before it can be signed off.
|
||||
if comp.ActionTaken == nil || strings.TrimSpace(*comp.ActionTaken) == "" {
|
||||
appErr := apperrorsx.New(apperrorsx.ErrActionSignoffActionRequired)
|
||||
appErr.Source = &jsonapi.ErrorSource{Pointer: "/data/attributes/complaint_id"}
|
||||
return apperrorsx.Write(c, appErr)
|
||||
}
|
||||
flightID, ferr := actionSignoffBodyFlightID(req)
|
||||
if ferr != nil {
|
||||
return apperrorsx.Write(c, ferr)
|
||||
}
|
||||
row, serr := h.svc.SignForComplaint(c.UserContext(), complaintID, flightID, comp.HelicopterID, actor)
|
||||
if serr != nil {
|
||||
return apperrorsx.Write(c, apperrorsx.New(apperrorsx.ErrActionSignoffSignFailed).WithCause(serr))
|
||||
}
|
||||
return response.Write(c, fiber.StatusOK, h.resource(c.UserContext(), row))
|
||||
}
|
||||
|
||||
// Complaint-independent (fleet) toggle — scoped to a flight.
|
||||
flightID, ferr := actionSignoffBodyFlightID(req)
|
||||
if ferr != nil {
|
||||
return apperrorsx.Write(c, ferr)
|
||||
}
|
||||
if !signWanted {
|
||||
row, serr := h.svc.Unsign(c.UserContext(), flightID, actor)
|
||||
if errors.Is(serr, actionsignoff.ErrNotFound) {
|
||||
return response.Write(c, fiber.StatusOK, unsignedActionSignoffPlaceholder(helicopterID))
|
||||
}
|
||||
if serr != nil {
|
||||
return apperrorsx.Write(c, apperrorsx.New(apperrorsx.ErrActionSignoffUnsignFailed).WithCause(serr))
|
||||
}
|
||||
return response.Write(c, fiber.StatusOK, h.resource(c.UserContext(), row))
|
||||
}
|
||||
// Signing a fleet sign-off — this is what allows an EASA release for the flight.
|
||||
row, serr := h.svc.Sign(c.UserContext(), flightID, helicopterID, actor)
|
||||
if serr != nil {
|
||||
return apperrorsx.Write(c, apperrorsx.New(apperrorsx.ErrActionSignoffSignFailed).WithCause(serr))
|
||||
}
|
||||
return response.Write(c, fiber.StatusOK, h.resource(c.UserContext(), row))
|
||||
}
|
||||
|
||||
// unsignedActionSignoffPlaceholder renders an unsigned resource for a helicopter that
|
||||
// has no sign-off row (e.g. unsigning something that was never signed).
|
||||
func unsignedActionSignoffPlaceholder(helicopterID []byte) responsedto.ActionSignoffResource {
|
||||
return responsedto.ActionSignoffResource{
|
||||
Type: "action_signoff",
|
||||
Attributes: responsedto.ActionSignoffAttributes{
|
||||
HelicopterID: idString(helicopterID),
|
||||
Signed: false,
|
||||
},
|
||||
}
|
||||
}
|
||||
367
internal/transport/http/handlers/action_signoff_handler_test.go
Normal file
367
internal/transport/http/handlers/action_signoff_handler_test.go
Normal file
@@ -0,0 +1,367 @@
|
||||
package handlers
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"io"
|
||||
"net/http"
|
||||
"strings"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/gofiber/fiber/v2"
|
||||
|
||||
actionsignoff "wucher/internal/domain/action_signoff"
|
||||
complaint "wucher/internal/domain/complaint"
|
||||
"wucher/internal/shared/pkg/uuidv7"
|
||||
)
|
||||
|
||||
type actionSignoffComplaintMock struct {
|
||||
getByIDFn func(context.Context, []byte) (*complaint.Complaint, error)
|
||||
}
|
||||
|
||||
func (m *actionSignoffComplaintMock) GetByID(ctx context.Context, id []byte) (*complaint.Complaint, error) {
|
||||
if m.getByIDFn != nil {
|
||||
return m.getByIDFn(ctx, id)
|
||||
}
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
type actionSignoffServiceMock struct {
|
||||
getByHelicopterFn func(context.Context, []byte) (*actionsignoff.ActionSignoff, error)
|
||||
getByComplaintFn func(context.Context, []byte) (*actionsignoff.ActionSignoff, error)
|
||||
getByComplaintIDsFn func(context.Context, [][]byte) (map[string]*actionsignoff.ActionSignoff, error)
|
||||
signFn func(context.Context, []byte, []byte) (*actionsignoff.ActionSignoff, error)
|
||||
signForComplaintFn func(context.Context, []byte, []byte, []byte) (*actionsignoff.ActionSignoff, error)
|
||||
unsignFn func(context.Context, []byte, []byte) (*actionsignoff.ActionSignoff, error)
|
||||
unsignByComplaintFn func(context.Context, []byte) (*actionsignoff.ActionSignoff, error)
|
||||
}
|
||||
|
||||
func (m *actionSignoffServiceMock) GetByFlight(ctx context.Context, flightID []byte) (*actionsignoff.ActionSignoff, error) {
|
||||
if m.getByHelicopterFn != nil {
|
||||
return m.getByHelicopterFn(ctx, flightID)
|
||||
}
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
func (m *actionSignoffServiceMock) GetByComplaint(ctx context.Context, complaintID []byte) (*actionsignoff.ActionSignoff, error) {
|
||||
if m.getByComplaintFn != nil {
|
||||
return m.getByComplaintFn(ctx, complaintID)
|
||||
}
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
func (m *actionSignoffServiceMock) GetByComplaintIDs(ctx context.Context, complaintIDs [][]byte) (map[string]*actionsignoff.ActionSignoff, error) {
|
||||
if m.getByComplaintIDsFn != nil {
|
||||
return m.getByComplaintIDsFn(ctx, complaintIDs)
|
||||
}
|
||||
return map[string]*actionsignoff.ActionSignoff{}, nil
|
||||
}
|
||||
|
||||
func (m *actionSignoffServiceMock) SignForComplaint(ctx context.Context, complaintID, _ []byte, helicopterID []byte, signer []byte) (*actionsignoff.ActionSignoff, error) {
|
||||
if m.signForComplaintFn != nil {
|
||||
return m.signForComplaintFn(ctx, complaintID, helicopterID, signer)
|
||||
}
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
func (m *actionSignoffServiceMock) Sign(ctx context.Context, _ []byte, helicopterID []byte, signer []byte) (*actionsignoff.ActionSignoff, error) {
|
||||
if m.signFn != nil {
|
||||
return m.signFn(ctx, helicopterID, signer)
|
||||
}
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
func (m *actionSignoffServiceMock) Unsign(ctx context.Context, helicopterID []byte, actor []byte) (*actionsignoff.ActionSignoff, error) {
|
||||
if m.unsignFn != nil {
|
||||
return m.unsignFn(ctx, helicopterID, actor)
|
||||
}
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
func (m *actionSignoffServiceMock) UnsignByComplaint(ctx context.Context, complaintID []byte) (*actionsignoff.ActionSignoff, error) {
|
||||
if m.unsignByComplaintFn != nil {
|
||||
return m.unsignByComplaintFn(ctx, complaintID)
|
||||
}
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
func newActionSignoffApp(h *ActionSignoffHandler) *fiber.App {
|
||||
app := fiber.New()
|
||||
app.Get("/api/v1/action-signoffs/get-by-flight/:flight_id", h.GetByFlight)
|
||||
app.Post("/api/v1/action-signoffs/sign/:helicopter_id", h.Sign)
|
||||
return app
|
||||
}
|
||||
|
||||
func TestActionSignoffSign_ComplaintLinked_RequiresActionTaken(t *testing.T) {
|
||||
complaintID := uuidv7.MustBytes()
|
||||
complaintIDStr, _ := uuidv7.BytesToString(complaintID)
|
||||
heliID := uuidv7.MustBytes()
|
||||
heliStr, _ := uuidv7.BytesToString(heliID)
|
||||
|
||||
svc := &actionSignoffServiceMock{
|
||||
signForComplaintFn: func(context.Context, []byte, []byte, []byte) (*actionsignoff.ActionSignoff, error) {
|
||||
t.Fatal("must not sign a complaint whose action_taken is empty")
|
||||
return nil, nil
|
||||
},
|
||||
}
|
||||
comp := &actionSignoffComplaintMock{
|
||||
getByIDFn: func(context.Context, []byte) (*complaint.Complaint, error) {
|
||||
// Complaint exists but action_taken not recorded yet.
|
||||
return &complaint.Complaint{ID: complaintID, HelicopterID: heliID}, nil
|
||||
},
|
||||
}
|
||||
app := newActionSignoffApp(NewActionSignoffHandler(svc).WithComplaintService(comp))
|
||||
|
||||
body := `{"data":{"attributes":{"complaint_id":"` + complaintIDStr + `"}}}`
|
||||
req, _ := http.NewRequest(http.MethodPost, "/api/v1/action-signoffs/sign/"+heliStr, strings.NewReader(body))
|
||||
req.Header.Set("Content-Type", "application/json")
|
||||
resp, err := app.Test(req)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
bodyBytes, _ := io.ReadAll(resp.Body)
|
||||
if resp.StatusCode != http.StatusUnprocessableEntity {
|
||||
t.Fatalf("expected 422, got %d body=%s", resp.StatusCode, string(bodyBytes))
|
||||
}
|
||||
var payload map[string]any
|
||||
_ = json.Unmarshal(bodyBytes, &payload)
|
||||
errs, _ := payload["errors"].([]any)
|
||||
if len(errs) == 0 || errs[0].(map[string]any)["code"] != "ACTION_SIGNOFF_ACTION_REQUIRED" {
|
||||
t.Fatalf("expected ACTION_SIGNOFF_ACTION_REQUIRED, got %s", string(bodyBytes))
|
||||
}
|
||||
}
|
||||
|
||||
func TestActionSignoffSign_ComplaintLinked_Signs(t *testing.T) {
|
||||
complaintID := uuidv7.MustBytes()
|
||||
complaintIDStr, _ := uuidv7.BytesToString(complaintID)
|
||||
heliID := uuidv7.MustBytes()
|
||||
heliStr, _ := uuidv7.BytesToString(heliID)
|
||||
action := "tightened mount"
|
||||
|
||||
var signedComplaint []byte
|
||||
svc := &actionSignoffServiceMock{
|
||||
signForComplaintFn: func(_ context.Context, cid, hid []byte, _ []byte) (*actionsignoff.ActionSignoff, error) {
|
||||
signedComplaint = cid
|
||||
if string(hid) != string(heliID) {
|
||||
t.Fatalf("unexpected helicopter id: %x", hid)
|
||||
}
|
||||
now := time.Now().UTC()
|
||||
return &actionsignoff.ActionSignoff{HelicopterID: hid, ComplaintID: cid, SignedAt: &now}, nil
|
||||
},
|
||||
}
|
||||
comp := &actionSignoffComplaintMock{
|
||||
getByIDFn: func(context.Context, []byte) (*complaint.Complaint, error) {
|
||||
return &complaint.Complaint{ID: complaintID, HelicopterID: heliID, ActionTaken: &action}, nil
|
||||
},
|
||||
}
|
||||
app := newActionSignoffApp(NewActionSignoffHandler(svc).WithComplaintService(comp))
|
||||
|
||||
flightStr, _ := uuidv7.BytesToString(uuidv7.MustBytes())
|
||||
body := `{"data":{"attributes":{"complaint_id":"` + complaintIDStr + `","flight_id":"` + flightStr + `"}}}`
|
||||
req, _ := http.NewRequest(http.MethodPost, "/api/v1/action-signoffs/sign/"+heliStr, strings.NewReader(body))
|
||||
req.Header.Set("Content-Type", "application/json")
|
||||
resp, err := app.Test(req)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if resp.StatusCode != http.StatusOK {
|
||||
bodyBytes, _ := io.ReadAll(resp.Body)
|
||||
t.Fatalf("expected 200, got %d body=%s", resp.StatusCode, string(bodyBytes))
|
||||
}
|
||||
if string(signedComplaint) != string(complaintID) {
|
||||
t.Fatalf("expected SignForComplaint with complaint id, got %x", signedComplaint)
|
||||
}
|
||||
}
|
||||
|
||||
func TestActionSignoffSign_Standalone_NoComplaint(t *testing.T) {
|
||||
heliID := uuidv7.MustBytes()
|
||||
heliStr, _ := uuidv7.BytesToString(heliID)
|
||||
|
||||
var standaloneSigned bool
|
||||
svc := &actionSignoffServiceMock{
|
||||
signFn: func(_ context.Context, hid []byte, _ []byte) (*actionsignoff.ActionSignoff, error) {
|
||||
standaloneSigned = true
|
||||
now := time.Now().UTC()
|
||||
return &actionsignoff.ActionSignoff{HelicopterID: hid, SignedAt: &now}, nil
|
||||
},
|
||||
signForComplaintFn: func(context.Context, []byte, []byte, []byte) (*actionsignoff.ActionSignoff, error) {
|
||||
t.Fatal("standalone sign must not call SignForComplaint")
|
||||
return nil, nil
|
||||
},
|
||||
}
|
||||
app := newActionSignoffApp(NewActionSignoffHandler(svc))
|
||||
|
||||
// No complaint_id → standalone fleet sign-off (scoped to a flight).
|
||||
flightStr, _ := uuidv7.BytesToString(uuidv7.MustBytes())
|
||||
body := `{"data":{"attributes":{"flight_id":"` + flightStr + `"}}}`
|
||||
req, _ := http.NewRequest(http.MethodPost, "/api/v1/action-signoffs/sign/"+heliStr, strings.NewReader(body))
|
||||
req.Header.Set("Content-Type", "application/json")
|
||||
resp, err := app.Test(req)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if resp.StatusCode != http.StatusOK || !standaloneSigned {
|
||||
t.Fatalf("expected standalone sign, status=%d signed=%v", resp.StatusCode, standaloneSigned)
|
||||
}
|
||||
}
|
||||
|
||||
func TestActionSignoffSign_ComplaintLinked_Unsign(t *testing.T) {
|
||||
complaintID := uuidv7.MustBytes()
|
||||
complaintIDStr, _ := uuidv7.BytesToString(complaintID)
|
||||
heliID := uuidv7.MustBytes()
|
||||
heliStr, _ := uuidv7.BytesToString(heliID)
|
||||
|
||||
var unsigned []byte
|
||||
svc := &actionSignoffServiceMock{
|
||||
unsignByComplaintFn: func(_ context.Context, cid []byte) (*actionsignoff.ActionSignoff, error) {
|
||||
unsigned = cid
|
||||
return &actionsignoff.ActionSignoff{HelicopterID: heliID, ComplaintID: cid}, nil // SignedAt nil → unsigned
|
||||
},
|
||||
signForComplaintFn: func(context.Context, []byte, []byte, []byte) (*actionsignoff.ActionSignoff, error) {
|
||||
t.Fatal("sign:false must not call SignForComplaint")
|
||||
return nil, nil
|
||||
},
|
||||
}
|
||||
app := newActionSignoffApp(NewActionSignoffHandler(svc))
|
||||
|
||||
body := `{"data":{"attributes":{"sign":false,"complaint_id":"` + complaintIDStr + `"}}}`
|
||||
req, _ := http.NewRequest(http.MethodPost, "/api/v1/action-signoffs/sign/"+heliStr, strings.NewReader(body))
|
||||
req.Header.Set("Content-Type", "application/json")
|
||||
resp, err := app.Test(req)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
bodyBytes, _ := io.ReadAll(resp.Body)
|
||||
if resp.StatusCode != http.StatusOK {
|
||||
t.Fatalf("expected 200, got %d body=%s", resp.StatusCode, string(bodyBytes))
|
||||
}
|
||||
if string(unsigned) != string(complaintID) {
|
||||
t.Fatalf("expected UnsignByComplaint with complaint id, got %x", unsigned)
|
||||
}
|
||||
}
|
||||
|
||||
func TestActionSignoffSign_Standalone_Unsign(t *testing.T) {
|
||||
heliID := uuidv7.MustBytes()
|
||||
heliStr, _ := uuidv7.BytesToString(heliID)
|
||||
flightID := uuidv7.MustBytes()
|
||||
flightStr, _ := uuidv7.BytesToString(flightID)
|
||||
|
||||
var unsignedFlight []byte
|
||||
svc := &actionSignoffServiceMock{
|
||||
unsignFn: func(_ context.Context, fid []byte, _ []byte) (*actionsignoff.ActionSignoff, error) {
|
||||
unsignedFlight = fid
|
||||
return &actionsignoff.ActionSignoff{HelicopterID: heliID}, nil // unsigned
|
||||
},
|
||||
signFn: func(context.Context, []byte, []byte) (*actionsignoff.ActionSignoff, error) {
|
||||
t.Fatal("sign:false must not call Sign")
|
||||
return nil, nil
|
||||
},
|
||||
}
|
||||
app := newActionSignoffApp(NewActionSignoffHandler(svc))
|
||||
|
||||
body := `{"data":{"attributes":{"sign":false,"flight_id":"` + flightStr + `"}}}`
|
||||
req, _ := http.NewRequest(http.MethodPost, "/api/v1/action-signoffs/sign/"+heliStr, strings.NewReader(body))
|
||||
req.Header.Set("Content-Type", "application/json")
|
||||
resp, err := app.Test(req)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if resp.StatusCode != http.StatusOK || string(unsignedFlight) != string(flightID) {
|
||||
b, _ := io.ReadAll(resp.Body)
|
||||
t.Fatalf("expected standalone unsign, status=%d body=%s", resp.StatusCode, string(b))
|
||||
}
|
||||
}
|
||||
|
||||
// The response must be single-`data` enveloped (response.Write/jsonapi adds the
|
||||
// envelope). Wrapping the resource in ActionSignoffResponse here would double-nest
|
||||
// it as {"data":{"data":...}}, which the frontend mapper reads as unsigned —
|
||||
// leaving the SIGN checkbox unchecked even after a successful sign.
|
||||
func TestActionSignoffGetByHelicopterIsSingleDataEnvelope(t *testing.T) {
|
||||
signedAt := time.Date(2026, 6, 30, 13, 18, 58, 0, time.UTC)
|
||||
heliID := uuidv7.MustBytes()
|
||||
heliStr, _ := uuidv7.BytesToString(heliID)
|
||||
|
||||
svc := &actionSignoffServiceMock{
|
||||
getByHelicopterFn: func(_ context.Context, _ []byte) (*actionsignoff.ActionSignoff, error) {
|
||||
return &actionsignoff.ActionSignoff{
|
||||
ID: uuidv7.MustBytes(),
|
||||
HelicopterID: heliID,
|
||||
SignedAt: &signedAt,
|
||||
SignedBy: uuidv7.MustBytes(),
|
||||
}, nil
|
||||
},
|
||||
}
|
||||
app := newActionSignoffApp(NewActionSignoffHandler(svc))
|
||||
|
||||
req, _ := http.NewRequest(http.MethodGet, "/api/v1/action-signoffs/get-by-flight/"+heliStr, nil)
|
||||
resp, err := app.Test(req)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if resp.StatusCode != http.StatusOK {
|
||||
body, _ := io.ReadAll(resp.Body)
|
||||
t.Fatalf("expected 200, got %d body=%s", resp.StatusCode, string(body))
|
||||
}
|
||||
|
||||
var body map[string]any
|
||||
if err := json.NewDecoder(resp.Body).Decode(&body); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
data, ok := body["data"].(map[string]any)
|
||||
if !ok {
|
||||
t.Fatalf("expected object at data, got %#v", body["data"])
|
||||
}
|
||||
// Regression guard: there must be no nested data.data.
|
||||
if _, nested := data["data"]; nested {
|
||||
t.Fatalf("response is double-wrapped (data.data present): %#v", data)
|
||||
}
|
||||
attrs, ok := data["attributes"].(map[string]any)
|
||||
if !ok {
|
||||
t.Fatalf("expected data.attributes object, got %#v", data["attributes"])
|
||||
}
|
||||
if signed, _ := attrs["signed"].(bool); !signed {
|
||||
t.Fatalf("expected attributes.signed=true, got %#v", attrs["signed"])
|
||||
}
|
||||
}
|
||||
|
||||
// Placeholder (no row yet) must also be single-`data` enveloped.
|
||||
func TestActionSignoffGetByHelicopterPlaceholderIsSingleDataEnvelope(t *testing.T) {
|
||||
heliID := uuidv7.MustBytes()
|
||||
heliStr, _ := uuidv7.BytesToString(heliID)
|
||||
|
||||
svc := &actionSignoffServiceMock{
|
||||
getByHelicopterFn: func(_ context.Context, _ []byte) (*actionsignoff.ActionSignoff, error) {
|
||||
return nil, nil
|
||||
},
|
||||
}
|
||||
app := newActionSignoffApp(NewActionSignoffHandler(svc))
|
||||
|
||||
req, _ := http.NewRequest(http.MethodGet, "/api/v1/action-signoffs/get-by-flight/"+heliStr, nil)
|
||||
resp, err := app.Test(req)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if resp.StatusCode != http.StatusOK {
|
||||
t.Fatalf("expected 200, got %d", resp.StatusCode)
|
||||
}
|
||||
|
||||
var body map[string]any
|
||||
if err := json.NewDecoder(resp.Body).Decode(&body); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
data, ok := body["data"].(map[string]any)
|
||||
if !ok {
|
||||
t.Fatalf("expected object at data, got %#v", body["data"])
|
||||
}
|
||||
if _, nested := data["data"]; nested {
|
||||
t.Fatalf("placeholder is double-wrapped (data.data present): %#v", data)
|
||||
}
|
||||
attrs, ok := data["attributes"].(map[string]any)
|
||||
if !ok {
|
||||
t.Fatalf("expected data.attributes object, got %#v", data["attributes"])
|
||||
}
|
||||
if signed, _ := attrs["signed"].(bool); signed {
|
||||
t.Fatalf("expected placeholder attributes.signed=false, got %#v", attrs["signed"])
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,215 @@
|
||||
package handlers
|
||||
|
||||
import (
|
||||
"context"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/gofiber/fiber/v2"
|
||||
|
||||
afterflightinspection "wucher/internal/domain/after_flight_inspection"
|
||||
flightinspection "wucher/internal/domain/flight_inspection"
|
||||
"wucher/internal/shared/pkg/apperrorsx"
|
||||
"wucher/internal/shared/pkg/uuidv7"
|
||||
"wucher/internal/transport/http/dto"
|
||||
"wucher/internal/transport/http/jsonapi"
|
||||
"wucher/internal/transport/http/response"
|
||||
"wucher/internal/transport/http/validators"
|
||||
)
|
||||
|
||||
type AfterFlightInspectionHandler struct {
|
||||
svc afterflightinspection.Service
|
||||
flightSvc flightinspection.FlightInspectionService
|
||||
validate *validators.Validator
|
||||
}
|
||||
|
||||
func NewAfterFlightInspectionHandler(svc afterflightinspection.Service, flightSvc flightinspection.FlightInspectionService) *AfterFlightInspectionHandler {
|
||||
return &AfterFlightInspectionHandler{
|
||||
svc: svc,
|
||||
flightSvc: flightSvc,
|
||||
validate: validators.New(),
|
||||
}
|
||||
}
|
||||
|
||||
// GetAfterFlightInspection godoc
|
||||
// @Summary Get after flight inspection by flight inspection ID
|
||||
// @Tags Flights - After Flight Inspection
|
||||
// @Accept json
|
||||
// @Produce json
|
||||
// @Param id path string true "Flight Inspection ID"
|
||||
// @Success 200 {object} dto.AfterFlightInspectionResponse
|
||||
// @Failure 404 {object} dto.ErrorResponse
|
||||
// @Failure 400 {object} dto.ErrorResponse
|
||||
// Public route disabled (managed via reserve AC flow).
|
||||
func (h *AfterFlightInspectionHandler) Get(c *fiber.Ctx) error {
|
||||
flightInspectionID, err := uuidv7.ParseString(strings.TrimSpace(c.Params("id")))
|
||||
if err != nil {
|
||||
return writeAfterFlightAppError(c, apperrorsx.ErrAfterFlightInvalidID, &jsonapi.ErrorSource{Pointer: "/path/id"})
|
||||
}
|
||||
|
||||
row, err := h.svc.GetByFlightInspectionID(c.Context(), flightInspectionID)
|
||||
if err != nil {
|
||||
return writeAfterFlightServiceError(c, err)
|
||||
}
|
||||
|
||||
if row == nil {
|
||||
return writeAfterFlightAppError(c, apperrorsx.ErrAfterFlightInspectionNotFound, nil)
|
||||
}
|
||||
|
||||
flightInspectionData, err := h.getFlightInspectionResource(c.Context(), flightInspectionID)
|
||||
if err != nil {
|
||||
return writeAfterFlightServiceError(c, err)
|
||||
}
|
||||
|
||||
resp := dto.AfterFlightInspectionResponse{
|
||||
Data: dto.AfterFlightInspectionResource{
|
||||
Type: "after_flight_inspection",
|
||||
Attributes: dto.AfterFlightInspectionAttributes{
|
||||
FlightInspection: flightInspectionData,
|
||||
OilEngineNR1Checked: row.OilEngineNR1Checked,
|
||||
OilEngineNR2Checked: row.OilEngineNR2Checked,
|
||||
HydraulicLHChecked: row.HydraulicLHChecked,
|
||||
HydraulicRHChecked: row.HydraulicRHChecked,
|
||||
OilTransmissionMGBChecked: row.OilTransmissionMGBChecked,
|
||||
OilTransmissionIGBChecked: row.OilTransmissionIGBChecked,
|
||||
OilTransmissionTGBChecked: row.OilTransmissionTGBChecked,
|
||||
Note: row.Note,
|
||||
CreatedAt: row.CreatedAt.Format(time.RFC3339),
|
||||
UpdatedAt: row.UpdatedAt.Format(time.RFC3339),
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
idStr, _ := uuidv7.BytesToString(row.ID)
|
||||
flightInspectionIDStr, _ := uuidv7.BytesToString(row.FlightInspectionID)
|
||||
resp.Data.ID = idStr
|
||||
resp.Data.Attributes.ID = idStr
|
||||
resp.Data.Attributes.FlightInspectionID = flightInspectionIDStr
|
||||
|
||||
return c.JSON(resp)
|
||||
}
|
||||
|
||||
// UpsertAfterFlightInspection godoc
|
||||
// @Summary Upsert after flight inspection by flight inspection ID
|
||||
// @Tags Flights - After Flight Inspection
|
||||
// @Accept json
|
||||
// @Produce json
|
||||
// @Param id path string true "Flight Inspection ID"
|
||||
// @Param request body dto.AfterFlightInspectionUpsertRequest true "JSON:API after flight inspection upsert request"
|
||||
// @Success 200 {object} dto.AfterFlightInspectionResponse
|
||||
// @Failure 422 {object} dto.ErrorResponse
|
||||
// @Failure 404 {object} dto.ErrorResponse
|
||||
// @Failure 400 {object} dto.ErrorResponse
|
||||
// Public route disabled (managed via reserve AC flow).
|
||||
func (h *AfterFlightInspectionHandler) Upsert(c *fiber.Ctx) error {
|
||||
flightInspectionID, err := uuidv7.ParseString(strings.TrimSpace(c.Params("id")))
|
||||
if err != nil {
|
||||
return writeAfterFlightAppError(c, apperrorsx.ErrAfterFlightInvalidID, &jsonapi.ErrorSource{Pointer: "/path/id"})
|
||||
}
|
||||
|
||||
var req dto.AfterFlightInspectionUpsertRequest
|
||||
if err := c.BodyParser(&req); err != nil {
|
||||
return writeAfterFlightAppError(c, apperrorsx.ErrAfterFlightInvalidJSON, nil)
|
||||
}
|
||||
if errs := h.validate.ValidateStruct(req); errs != nil {
|
||||
return response.WriteErrors(c, fiber.StatusUnprocessableEntity, errs)
|
||||
}
|
||||
|
||||
upsertReq := &afterflightinspection.UpsertRequest{
|
||||
OilEngineNR1Checked: req.Data.Attributes.OilEngineNR1Checked,
|
||||
OilEngineNR2Checked: req.Data.Attributes.OilEngineNR2Checked,
|
||||
HydraulicLHChecked: req.Data.Attributes.HydraulicLHChecked,
|
||||
HydraulicRHChecked: req.Data.Attributes.HydraulicRHChecked,
|
||||
OilTransmissionMGBChecked: req.Data.Attributes.OilTransmissionMGBChecked,
|
||||
OilTransmissionIGBChecked: req.Data.Attributes.OilTransmissionIGBChecked,
|
||||
OilTransmissionTGBChecked: req.Data.Attributes.OilTransmissionTGBChecked,
|
||||
Note: req.Data.Attributes.Note,
|
||||
}
|
||||
|
||||
row, err := h.svc.Upsert(c.Context(), flightInspectionID, upsertReq)
|
||||
if err != nil {
|
||||
return writeAfterFlightServiceError(c, err)
|
||||
}
|
||||
|
||||
flightInspectionData, err := h.getFlightInspectionResource(c.Context(), flightInspectionID)
|
||||
if err != nil {
|
||||
return writeAfterFlightServiceError(c, err)
|
||||
}
|
||||
|
||||
idStr, _ := uuidv7.BytesToString(row.ID)
|
||||
flightInspectionIDStr, _ := uuidv7.BytesToString(row.FlightInspectionID)
|
||||
resp := dto.AfterFlightInspectionResponse{
|
||||
Data: dto.AfterFlightInspectionResource{
|
||||
Type: "after_flight_inspection",
|
||||
ID: idStr,
|
||||
Attributes: dto.AfterFlightInspectionAttributes{
|
||||
ID: idStr,
|
||||
FlightInspectionID: flightInspectionIDStr,
|
||||
FlightInspection: flightInspectionData,
|
||||
OilEngineNR1Checked: row.OilEngineNR1Checked,
|
||||
OilEngineNR2Checked: row.OilEngineNR2Checked,
|
||||
HydraulicLHChecked: row.HydraulicLHChecked,
|
||||
HydraulicRHChecked: row.HydraulicRHChecked,
|
||||
OilTransmissionMGBChecked: row.OilTransmissionMGBChecked,
|
||||
OilTransmissionIGBChecked: row.OilTransmissionIGBChecked,
|
||||
OilTransmissionTGBChecked: row.OilTransmissionTGBChecked,
|
||||
Note: row.Note,
|
||||
CreatedAt: row.CreatedAt.Format(time.RFC3339),
|
||||
UpdatedAt: row.UpdatedAt.Format(time.RFC3339),
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
return c.JSON(resp)
|
||||
}
|
||||
|
||||
func (h *AfterFlightInspectionHandler) getFlightInspectionResource(ctx context.Context, flightInspectionID []byte) (*dto.FlightInspectionResource, error) {
|
||||
if h.flightSvc == nil {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
row, err := h.flightSvc.GetByID(ctx, flightInspectionID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if row == nil {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
return afterFlightInspectionResource(row), nil
|
||||
}
|
||||
|
||||
// writeAfterFlightAppError writes a known apperrorsx definition, optionally
|
||||
// attaching a JSON:API source pointer.
|
||||
func writeAfterFlightAppError(c *fiber.Ctx, def apperrorsx.Def, source *jsonapi.ErrorSource) error {
|
||||
appErr := apperrorsx.New(def)
|
||||
if source != nil {
|
||||
appErr.Source = source
|
||||
}
|
||||
return apperrorsx.Write(c, appErr)
|
||||
}
|
||||
|
||||
// writeAfterFlightServiceError translates a domain/service error into its
|
||||
// apperrorsx definition; unknown errors fall back to a generic internal error.
|
||||
func writeAfterFlightServiceError(c *fiber.Ctx, err error) error {
|
||||
if mapped := apperrorsx.Translate(apperrorsx.ModuleAfterFlightInspection, err); mapped != nil && mapped.Code != apperrorsx.CodeInternalServerError {
|
||||
return apperrorsx.Write(c, mapped)
|
||||
}
|
||||
return apperrorsx.Write(c, apperrorsx.New(apperrorsx.ErrInternal))
|
||||
}
|
||||
|
||||
func afterFlightInspectionResource(row *flightinspection.FlightInspection) *dto.FlightInspectionResource {
|
||||
id, _ := uuidv7.BytesToString(row.ID)
|
||||
return &dto.FlightInspectionResource{
|
||||
Type: "flight_inspection",
|
||||
ID: id,
|
||||
Attributes: dto.FlightInspectionAttributes{
|
||||
InspectionDate: row.InspectionDate.UTC().Format("2006-01-02"),
|
||||
Status: row.Status,
|
||||
CreatedAt: row.CreatedAt.UTC().Format(time.RFC3339),
|
||||
CreatedBy: uuidStringOrEmpty(row.CreatedBy),
|
||||
UpdatedAt: row.UpdatedAt.UTC().Format(time.RFC3339),
|
||||
UpdatedBy: uuidStringOrEmpty(row.UpdatedBy),
|
||||
},
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,894 @@
|
||||
package handlers
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"github.com/gofiber/fiber/v2"
|
||||
|
||||
airrescuechecklist "wucher/internal/domain/air_rescue_checklist"
|
||||
basedomain "wucher/internal/domain/base"
|
||||
"wucher/internal/shared/pkg/apperrorsx"
|
||||
"wucher/internal/shared/pkg/uuidv7"
|
||||
requestdto "wucher/internal/transport/http/dto/request"
|
||||
responsedto "wucher/internal/transport/http/dto/response"
|
||||
"wucher/internal/transport/http/jsonapi"
|
||||
"wucher/internal/transport/http/response"
|
||||
"wucher/internal/transport/http/validators"
|
||||
)
|
||||
|
||||
type AirRescuerChecklistHandler struct {
|
||||
svc airrescuechecklist.Service
|
||||
validate *validators.Validator
|
||||
}
|
||||
|
||||
func NewAirRescuerChecklistHandler(svc airrescuechecklist.Service) *AirRescuerChecklistHandler {
|
||||
return &AirRescuerChecklistHandler{svc: svc, validate: validators.New()}
|
||||
}
|
||||
|
||||
// Create godoc
|
||||
// @Summary Create air rescuer checklist (bulk)
|
||||
// @Description Create one or more checklist headers for a single base.
|
||||
// @Description Request uses `base_id`.
|
||||
// @Description Each checklist can be created with or without initial `items`.
|
||||
// @Description `scope_code` accepts persisted codes and English aliases. Stored codes remain: `TA`, `MO`, `DI`, `MI`, `DO`, `FR`, `SA`, `SO`, `M10`, `M15`, `M20`, `M25`, `DS`.
|
||||
// @Description Example aliases: `DAILY` -> `TA`, `TU/TUE/TUESDAY` -> `DI`, `WE/WED/WEDNESDAY` -> `MI`, `TH/THU/THURSDAY` -> `DO`, `SU/SUN/SUNDAY` -> `SO`, `DEADLINE` -> `DS`.
|
||||
// @Description `CAT` is a virtual overview tab and is not allowed for create/update payloads.
|
||||
// @Description Duplicate `position` is allowed for both checklist headers and checklist items.
|
||||
// @Description Display order is stable: checklist headers `position ASC, title ASC`; checklist_items `position ASC, name ASC`.
|
||||
// @Description Request is transactional: if one checklist/item fails, the whole batch is rolled back.
|
||||
// @Tags Air Rescuer Checklist
|
||||
// @Accept json
|
||||
// @Produce json
|
||||
// @Param request body requestdto.AirRescuerChecklistCreateRequest true "Bulk checklist create payload"
|
||||
// @Success 201 {object} responsedto.AirRescuerChecklistBulkResponse
|
||||
// @Failure 400 {object} jsonapi.ErrorDocument
|
||||
// @Failure 404 {object} jsonapi.ErrorDocument
|
||||
// @Failure 409 {object} jsonapi.ErrorDocument
|
||||
// @Failure 422 {object} jsonapi.ErrorDocument
|
||||
// @Router /api/v1/air-rescuer-checklist/create [post]
|
||||
func (h *AirRescuerChecklistHandler) Create(c *fiber.Ctx) error {
|
||||
var req requestdto.AirRescuerChecklistCreateRequest
|
||||
if err := c.BodyParser(&req); err != nil {
|
||||
return writeAirRescuerChecklistErrors(c, fiber.StatusBadRequest, []jsonapi.ErrorObject{{
|
||||
Status: "400",
|
||||
Title: "Invalid JSON",
|
||||
Detail: safeInternalDetail(),
|
||||
}})
|
||||
}
|
||||
if errs := h.validate.ValidateStruct(req); errs != nil {
|
||||
return writeAirRescuerChecklistErrors(c, fiber.StatusUnprocessableEntity, errs)
|
||||
}
|
||||
|
||||
baseID, err := uuidv7.ParseString(strings.TrimSpace(req.Data.Attributes.BaseID))
|
||||
if err != nil {
|
||||
return validationError(c, "/data/attributes/base_id", "base_id is invalid UUID")
|
||||
}
|
||||
|
||||
baseRows, err := h.svc.ListHEMSBases(c.UserContext(), airrescuechecklist.HEMSBaseFilter{HEMSBaseID: baseID})
|
||||
if err != nil {
|
||||
status, errObj := buildServiceErrorObject("Create failed", err, "")
|
||||
return writeAirRescuerChecklistErrors(c, status, []jsonapi.ErrorObject{errObj})
|
||||
}
|
||||
if len(baseRows) == 0 {
|
||||
return writeAirRescuerChecklistErrors(c, fiber.StatusNotFound, []jsonapi.ErrorObject{{
|
||||
Status: "404",
|
||||
Title: "Not found",
|
||||
Detail: "base not found",
|
||||
}})
|
||||
}
|
||||
|
||||
rows := make([]airrescuechecklist.ChecklistCreateInput, 0, len(req.Data.Attributes.Checklists))
|
||||
actor := actorUserID(c)
|
||||
for i := range req.Data.Attributes.Checklists {
|
||||
scopeCode := airrescuechecklist.CanonicalizeAirRescuerChecklistScope(req.Data.Attributes.Checklists[i].ScopeCode)
|
||||
if scopeCode == "" {
|
||||
return validationError(c, fmt.Sprintf("/data/attributes/checklists/%d/scope_code", i), "scope_code is required")
|
||||
}
|
||||
if !airrescuechecklist.IsAirRescuerChecklistPersistedScope(scopeCode) {
|
||||
return validationError(c, fmt.Sprintf("/data/attributes/checklists/%d/scope_code", i), "scope_code is invalid")
|
||||
}
|
||||
|
||||
title := strings.TrimSpace(req.Data.Attributes.Checklists[i].Title)
|
||||
if title == "" {
|
||||
return validationError(c, fmt.Sprintf("/data/attributes/checklists/%d/title", i), "title is required")
|
||||
}
|
||||
|
||||
position := checklistPositionOrZero(req.Data.Attributes.Checklists[i].Position)
|
||||
|
||||
items := make([]airrescuechecklist.AirRescuerChecklistItem, 0, len(req.Data.Attributes.Checklists[i].Items))
|
||||
for j := range req.Data.Attributes.Checklists[i].Items {
|
||||
itemName := strings.TrimSpace(req.Data.Attributes.Checklists[i].Items[j].Name)
|
||||
if itemName == "" {
|
||||
return validationError(c, fmt.Sprintf("/data/attributes/checklists/%d/items/%d/name", i, j), "name is required")
|
||||
}
|
||||
items = append(items, airrescuechecklist.AirRescuerChecklistItem{
|
||||
Name: itemName,
|
||||
Position: checklistPositionOrZero(req.Data.Attributes.Checklists[i].Items[j].Position),
|
||||
CreatedBy: actor,
|
||||
UpdatedBy: actor,
|
||||
})
|
||||
}
|
||||
|
||||
rows = append(rows, airrescuechecklist.ChecklistCreateInput{
|
||||
Checklist: airrescuechecklist.AirRescuerChecklist{
|
||||
HEMSBaseID: baseID,
|
||||
ScopeCode: scopeCode,
|
||||
Title: title,
|
||||
Position: position,
|
||||
CreatedBy: actor,
|
||||
UpdatedBy: actor,
|
||||
},
|
||||
Items: items,
|
||||
})
|
||||
}
|
||||
|
||||
if err := h.svc.CreateBulk(c.UserContext(), rows); err != nil {
|
||||
status, errObj := buildServiceErrorObject("Create failed", err, "")
|
||||
return writeAirRescuerChecklistErrors(c, status, []jsonapi.ErrorObject{errObj})
|
||||
}
|
||||
|
||||
data := make([]responsedto.AirRescuerChecklistResource, 0, len(rows))
|
||||
for i := range rows {
|
||||
resource, found, err := h.loadChecklistResource(c.UserContext(), rows[i].Checklist.ID)
|
||||
if err != nil {
|
||||
status, errObj := buildServiceErrorObject("Create failed", err, "")
|
||||
return writeAirRescuerChecklistErrors(c, status, []jsonapi.ErrorObject{errObj})
|
||||
}
|
||||
if found {
|
||||
data = append(data, resource)
|
||||
}
|
||||
}
|
||||
|
||||
return response.Write(c, fiber.StatusCreated, data)
|
||||
}
|
||||
|
||||
// Get godoc
|
||||
// @Summary Get air rescuer checklist by UUID
|
||||
// @Description Return one checklist detail by UUID.
|
||||
// @Description Includes checklist header, base info, and `items` (`checklist_items`).
|
||||
// @Tags Air Rescuer Checklist
|
||||
// @Produce json
|
||||
// @Param uuid path string true "Checklist UUID"
|
||||
// @Success 200 {object} responsedto.AirRescuerChecklistResponse
|
||||
// @Failure 404 {object} jsonapi.ErrorDocument
|
||||
// @Failure 422 {object} jsonapi.ErrorDocument
|
||||
// @Router /api/v1/air-rescuer-checklist/get/{uuid} [get]
|
||||
func (h *AirRescuerChecklistHandler) Get(c *fiber.Ctx) error {
|
||||
id, err := uuidv7.ParseString(c.Params("uuid"))
|
||||
if err != nil {
|
||||
return validationError(c, "/path/uuid", "uuid is invalid UUID")
|
||||
}
|
||||
return h.respondChecklistByID(c, fiber.StatusOK, id)
|
||||
}
|
||||
|
||||
// Update godoc
|
||||
// @Summary Update air rescuer checklist header and append new items
|
||||
// @Description Update checklist header fields (`scope_code`, `title`, `position`) and/or append `new_items`.
|
||||
// @Description `new_items` is append-only. Existing items are not updated/deleted by this endpoint.
|
||||
// @Description Item update/delete lifecycle must use dedicated item endpoints.
|
||||
// @Description `scope_code` accepts persisted codes and English aliases, but `CAT` (or `OVERVIEW`) is not allowed for update payload.
|
||||
// @Description Duplicate `position` is allowed.
|
||||
// @Description Request is transactional.
|
||||
// @Tags Air Rescuer Checklist
|
||||
// @Accept json
|
||||
// @Produce json
|
||||
// @Param uuid path string true "Checklist UUID"
|
||||
// @Param request body requestdto.AirRescuerChecklistUpdateRequest true "Checklist update payload"
|
||||
// @Success 200 {object} responsedto.AirRescuerChecklistResponse
|
||||
// @Failure 400 {object} jsonapi.ErrorDocument
|
||||
// @Failure 404 {object} jsonapi.ErrorDocument
|
||||
// @Failure 409 {object} jsonapi.ErrorDocument
|
||||
// @Failure 422 {object} jsonapi.ErrorDocument
|
||||
// @Router /api/v1/air-rescuer-checklist/update/{uuid} [patch]
|
||||
func (h *AirRescuerChecklistHandler) Update(c *fiber.Ctx) error {
|
||||
id, err := uuidv7.ParseString(c.Params("uuid"))
|
||||
if err != nil {
|
||||
return validationError(c, "/path/uuid", "uuid is invalid UUID")
|
||||
}
|
||||
|
||||
var req requestdto.AirRescuerChecklistUpdateRequest
|
||||
if err := c.BodyParser(&req); err != nil {
|
||||
return writeAirRescuerChecklistErrors(c, fiber.StatusBadRequest, []jsonapi.ErrorObject{{
|
||||
Status: "400",
|
||||
Title: "Invalid JSON",
|
||||
Detail: safeInternalDetail(),
|
||||
}})
|
||||
}
|
||||
if errs := h.validate.ValidateStruct(req); errs != nil {
|
||||
return writeAirRescuerChecklistErrors(c, fiber.StatusUnprocessableEntity, errs)
|
||||
}
|
||||
|
||||
if strings.TrimSpace(req.Data.ID) != "" {
|
||||
reqID, err := uuidv7.ParseString(strings.TrimSpace(req.Data.ID))
|
||||
if err != nil {
|
||||
return validationError(c, "/data/id", "id is invalid UUID")
|
||||
}
|
||||
if !bytesEqualUUID(reqID, id) {
|
||||
return validationError(c, "/data/id", "id does not match path uuid")
|
||||
}
|
||||
}
|
||||
|
||||
existing, err := h.svc.GetChecklistByID(c.UserContext(), id)
|
||||
if err != nil {
|
||||
status, errObj := buildServiceErrorObject("Update failed", err, "")
|
||||
return writeAirRescuerChecklistErrors(c, status, []jsonapi.ErrorObject{errObj})
|
||||
}
|
||||
if existing == nil {
|
||||
return writeAirRescuerChecklistErrors(c, fiber.StatusNotFound, []jsonapi.ErrorObject{{
|
||||
Status: "404",
|
||||
Title: "Not found",
|
||||
Detail: "air rescuer checklist not found",
|
||||
}})
|
||||
}
|
||||
|
||||
attrs := req.Data.Attributes
|
||||
hasHeaderUpdate := attrs.ScopeCode != nil || attrs.Title != nil || attrs.Position != nil
|
||||
if attrs.NewItems != nil && len(attrs.NewItems) == 0 {
|
||||
return validationError(c, "/data/attributes/new_items", "new_items must contain at least 1 item")
|
||||
}
|
||||
hasNewItems := len(attrs.NewItems) > 0
|
||||
if !hasHeaderUpdate && !hasNewItems {
|
||||
return validationError(c, "/data/attributes", "at least one attribute must be provided")
|
||||
}
|
||||
|
||||
actor := actorUserID(c)
|
||||
|
||||
var row *airrescuechecklist.AirRescuerChecklist
|
||||
if hasHeaderUpdate {
|
||||
scopeCode := airrescuechecklist.CanonicalizeAirRescuerChecklistScope(existing.ScopeCode)
|
||||
if attrs.ScopeCode != nil {
|
||||
scopeCode = airrescuechecklist.CanonicalizeAirRescuerChecklistScope(*attrs.ScopeCode)
|
||||
if scopeCode == "" {
|
||||
return validationError(c, "/data/attributes/scope_code", "scope_code is required")
|
||||
}
|
||||
if !airrescuechecklist.IsAirRescuerChecklistPersistedScope(scopeCode) {
|
||||
return validationError(c, "/data/attributes/scope_code", "scope_code is invalid")
|
||||
}
|
||||
}
|
||||
|
||||
title := strings.TrimSpace(existing.Title)
|
||||
if attrs.Title != nil {
|
||||
title = strings.TrimSpace(*attrs.Title)
|
||||
if title == "" {
|
||||
return validationError(c, "/data/attributes/title", "title is required")
|
||||
}
|
||||
}
|
||||
|
||||
position := existing.Position
|
||||
if attrs.Position != nil {
|
||||
position = checklistPositionOrDefault(attrs.Position)
|
||||
}
|
||||
|
||||
row = &airrescuechecklist.AirRescuerChecklist{
|
||||
ID: id,
|
||||
ScopeCode: scopeCode,
|
||||
Title: title,
|
||||
Position: position,
|
||||
UpdatedBy: actor,
|
||||
}
|
||||
}
|
||||
|
||||
newItems := make([]airrescuechecklist.AirRescuerChecklistItem, 0, len(attrs.NewItems))
|
||||
for i := range attrs.NewItems {
|
||||
name := strings.TrimSpace(attrs.NewItems[i].Name)
|
||||
if name == "" {
|
||||
return validationError(c, fmt.Sprintf("/data/attributes/new_items/%d/name", i), "name is required")
|
||||
}
|
||||
newItems = append(newItems, airrescuechecklist.AirRescuerChecklistItem{
|
||||
ChecklistID: id,
|
||||
Name: name,
|
||||
Position: checklistPositionOrZero(attrs.NewItems[i].Position),
|
||||
CreatedBy: actor,
|
||||
UpdatedBy: actor,
|
||||
})
|
||||
}
|
||||
|
||||
if err := h.svc.UpdateChecklistWithNewItems(c.UserContext(), row, newItems); err != nil {
|
||||
status, errObj := buildServiceErrorObject("Update failed", err, "air rescuer checklist not found")
|
||||
return writeAirRescuerChecklistErrors(c, status, []jsonapi.ErrorObject{errObj})
|
||||
}
|
||||
|
||||
return h.respondChecklistByID(c, fiber.StatusOK, id)
|
||||
}
|
||||
|
||||
// Delete godoc
|
||||
// @Summary Delete air rescuer checklist
|
||||
// @Description Delete one checklist header by UUID.
|
||||
// @Description Related checklist items are removed together following current implementation behavior.
|
||||
// @Tags Air Rescuer Checklist
|
||||
// @Produce json
|
||||
// @Param uuid path string true "Checklist UUID"
|
||||
// @Success 200 {object} responsedto.AirRescuerChecklistGroupedListResponse
|
||||
// @Failure 404 {object} jsonapi.ErrorDocument
|
||||
// @Failure 422 {object} jsonapi.ErrorDocument
|
||||
// @Router /api/v1/air-rescuer-checklist/delete/{uuid} [delete]
|
||||
func (h *AirRescuerChecklistHandler) Delete(c *fiber.Ctx) error {
|
||||
id, err := uuidv7.ParseString(c.Params("uuid"))
|
||||
if err != nil {
|
||||
return validationError(c, "/path/uuid", "uuid is invalid UUID")
|
||||
}
|
||||
if err := h.svc.DeleteChecklist(c.UserContext(), id, actorUserID(c)); err != nil {
|
||||
status, errObj := buildServiceErrorObject("Delete failed", err, "air rescuer checklist not found")
|
||||
return writeAirRescuerChecklistErrors(c, status, []jsonapi.ErrorObject{errObj})
|
||||
}
|
||||
return response.Write(c, fiber.StatusOK, jsonapi.Resource{Type: "air_rescuer_checklist_delete", Attributes: map[string]any{"deleted": true}})
|
||||
}
|
||||
|
||||
// List godoc
|
||||
// @Summary List air rescuer checklist grouped by base
|
||||
// @Description Grouped by base and returned in `data[].attributes.bases`.
|
||||
// @Description Response includes `tabs[]` with default tab order: `CAT`, `TA`, `MO`, `DI`, `MI`, `DO`, `FR`, `SA`, `SO`, `M10`, `M15`, `M20`, `M25`, `DS`.
|
||||
// @Description `CAT` is a virtual overview tab (not persisted in DB).
|
||||
// @Description `CAT.groups[]` only includes non-empty persisted scopes and does not include `checklist_items`.
|
||||
// @Description Normal tabs are always present. Empty normal tabs are explicitly returned as `checklists: []`.
|
||||
// @Description Scope display names: `CAT=Category`, `TA=Daily`, `MO=Monday`, `DI=Tuesday`, `MI=Wednesday`, `DO=Thursday`, `FR=Friday`, `SA=Saturday`, `SO=Sunday`, `M10=M10`, `M15=M15`, `M20=M20`, `M25=M25`, `DS=Deadline`.
|
||||
// @Description Duplicate `position` is allowed. Display order is stable: checklist headers `position ASC, title ASC`; checklist_items `position ASC, name ASC`.
|
||||
// @Description Optional filters: `base_id`, `category_type` (`regular|hems`), `base_name`, `base_abbreviation`, `scope_code` (`CAT`/`OVERVIEW` or persisted scope / aliases).
|
||||
// @Tags Air Rescuer Checklist
|
||||
// @Produce json
|
||||
// @Param base_id query string false "Filter by base UUID"
|
||||
// @Param category_type query string false "Filter by base category" Enums(regular,hems)
|
||||
// @Param base_name query string false "Filter by base name (contains, case-insensitive)"
|
||||
// @Param base_abbreviation query string false "Filter by base abbreviation (contains, case-insensitive)"
|
||||
// @Param scope_code query string false "Filter by scope code. Allowed canonical: CAT, TA, MO, DI, MI, DO, FR, SA, SO, M10, M15, M20, M25, DS. English aliases are also accepted."
|
||||
// @Success 200 {object} responsedto.AirRescuerChecklistGroupedDataTableResponse
|
||||
// @Failure 400 {object} jsonapi.ErrorDocument
|
||||
// @Failure 422 {object} jsonapi.ErrorDocument
|
||||
// @Router /api/v1/air-rescuer-checklist/get-all [get]
|
||||
func (h *AirRescuerChecklistHandler) List(c *fiber.Ctx) error {
|
||||
headerFilter, baseFilter, requestedScope, _ := parseAirRescuerChecklistFilters(c)
|
||||
if c.Response().StatusCode() >= fiber.StatusBadRequest {
|
||||
return nil
|
||||
}
|
||||
|
||||
groups, err := h.loadChecklistGroups(c.UserContext(), headerFilter, baseFilter, requestedScope)
|
||||
if err != nil {
|
||||
status, errObj := buildServiceErrorObject("List failed", err, "")
|
||||
return writeAirRescuerChecklistErrors(c, status, []jsonapi.ErrorObject{errObj})
|
||||
}
|
||||
meta := map[string]any{"total_bases": len(groups)}
|
||||
return response.WriteWithMeta(c, fiber.StatusOK, groups, meta)
|
||||
}
|
||||
|
||||
// ListDatatable godoc
|
||||
// @Summary List air rescuer checklist grouped by base (datatable)
|
||||
// @Description Datatable variant for grouped air rescuer checklist data.
|
||||
// @Description Semantic data is the same as `get-all`: grouped by base with `tabs[]`, virtual `CAT`, and normal tabs always present.
|
||||
// @Description `CAT.groups[]` only includes non-empty persisted scopes and does not include `checklist_items`.
|
||||
// @Description Empty normal tabs are explicitly returned as `checklists: []`.
|
||||
// @Description Scope display names: `CAT=Category`, `TA=Daily`, `MO=Monday`, `DI=Tuesday`, `MI=Wednesday`, `DO=Thursday`, `FR=Friday`, `SA=Saturday`, `SO=Sunday`, `M10=M10`, `M15=M15`, `M20=M20`, `M25=M25`, `DS=Deadline`.
|
||||
// @Description Duplicate `position` is allowed. Display order is stable: checklist headers `position ASC, title ASC`; checklist_items `position ASC, name ASC`.
|
||||
// @Description Optional filters: `base_id`, `category_type` (`regular|hems`), `base_name`, `base_abbreviation`, `scope_code`.
|
||||
// @Description Pagination/query controls: `page`, `size`, `limit`, `draw`.
|
||||
// @Tags Air Rescuer Checklist
|
||||
// @Produce json
|
||||
// @Param base_id query string false "Filter by base UUID"
|
||||
// @Param category_type query string false "Filter by base category" Enums(regular,hems)
|
||||
// @Param base_name query string false "Filter by base name (contains, case-insensitive)"
|
||||
// @Param base_abbreviation query string false "Filter by base abbreviation (contains, case-insensitive)"
|
||||
// @Param scope_code query string false "Filter by scope code. Allowed canonical: CAT, TA, MO, DI, MI, DO, FR, SA, SO, M10, M15, M20, M25, DS. English aliases are also accepted."
|
||||
// @Param page query int false "Page number (default 1)"
|
||||
// @Param size query int false "Page size (default 20)"
|
||||
// @Param limit query int false "Page size override (max 100)"
|
||||
// @Param draw query int false "Datatable draw counter"
|
||||
// @Success 200 {object} responsedto.AirRescuerChecklistGroupedDataTableResponse
|
||||
// @Failure 400 {object} jsonapi.ErrorDocument
|
||||
// @Failure 422 {object} jsonapi.ErrorDocument
|
||||
// @Router /api/v1/air-rescuer-checklist/get-all/dt [get]
|
||||
func (h *AirRescuerChecklistHandler) ListDatatable(c *fiber.Ctx) error {
|
||||
headerFilter, baseFilter, requestedScope, _ := parseAirRescuerChecklistFilters(c)
|
||||
if c.Response().StatusCode() >= fiber.StatusBadRequest {
|
||||
return nil
|
||||
}
|
||||
|
||||
groups, err := h.loadChecklistGroups(c.UserContext(), headerFilter, baseFilter, requestedScope)
|
||||
if err != nil {
|
||||
status, errObj := buildServiceErrorObject("List failed", err, "")
|
||||
return writeAirRescuerChecklistErrors(c, status, []jsonapi.ErrorObject{errObj})
|
||||
}
|
||||
|
||||
pageNumber := parseIntDefault(c.Query("page"), 1)
|
||||
if pageNumber < 1 {
|
||||
pageNumber = 1
|
||||
}
|
||||
length := parseIntDefault(c.Query("limit"), 0)
|
||||
if length <= 0 {
|
||||
length = parseIntDefault(c.Query("size"), 20)
|
||||
}
|
||||
if length > 100 {
|
||||
length = 100
|
||||
}
|
||||
if length < 1 {
|
||||
length = 20
|
||||
}
|
||||
start := (pageNumber - 1) * length
|
||||
draw := parseIntDefault(c.Query("draw"), pageNumber)
|
||||
|
||||
pagedGroups, total := paginateAirRescuerChecklistGroups(groups, start, length)
|
||||
meta := map[string]any{"draw": draw, "records_total": total, "records_filtered": total}
|
||||
return response.WriteWithMeta(c, fiber.StatusOK, pagedGroups, meta)
|
||||
}
|
||||
|
||||
// CreateItem godoc
|
||||
// @Summary Create checklist items (bulk)
|
||||
// @Description Create one or more items for an existing checklist.
|
||||
// @Description Parent checklist is taken from path `uuid`.
|
||||
// @Description Duplicate `position` is allowed. Display order for checklist_items is `position ASC, name ASC`.
|
||||
// @Description Request is transactional: if one item fails, all item inserts in the batch are rolled back.
|
||||
// @Tags Air Rescuer Checklist
|
||||
// @Accept json
|
||||
// @Produce json
|
||||
// @Param uuid path string true "Checklist UUID"
|
||||
// @Param request body requestdto.AirRescuerChecklistItemCreateRequest true "Bulk checklist item create payload"
|
||||
// @Success 201 {object} responsedto.AirRescuerChecklistItemBulkResponse
|
||||
// @Failure 400 {object} jsonapi.ErrorDocument
|
||||
// @Failure 404 {object} jsonapi.ErrorDocument
|
||||
// @Failure 409 {object} jsonapi.ErrorDocument
|
||||
// @Failure 422 {object} jsonapi.ErrorDocument
|
||||
// @Router /api/v1/air-rescuer-checklist/{uuid}/items/create [post]
|
||||
func (h *AirRescuerChecklistHandler) CreateItem(c *fiber.Ctx) error {
|
||||
checklistID, err := uuidv7.ParseString(c.Params("uuid"))
|
||||
if err != nil {
|
||||
return validationError(c, "/path/uuid", "uuid is invalid UUID")
|
||||
}
|
||||
|
||||
var req requestdto.AirRescuerChecklistItemCreateRequest
|
||||
if err := c.BodyParser(&req); err != nil {
|
||||
return writeAirRescuerChecklistErrors(c, fiber.StatusBadRequest, []jsonapi.ErrorObject{{
|
||||
Status: "400",
|
||||
Title: "Invalid JSON",
|
||||
Detail: safeInternalDetail(),
|
||||
}})
|
||||
}
|
||||
if errs := h.validate.ValidateStruct(req); errs != nil {
|
||||
return writeAirRescuerChecklistErrors(c, fiber.StatusUnprocessableEntity, errs)
|
||||
}
|
||||
|
||||
actor := actorUserID(c)
|
||||
rows := make([]airrescuechecklist.AirRescuerChecklistItem, 0, len(req.Data.Attributes.Items))
|
||||
for i := range req.Data.Attributes.Items {
|
||||
name := strings.TrimSpace(req.Data.Attributes.Items[i].Name)
|
||||
if name == "" {
|
||||
return validationError(c, fmt.Sprintf("/data/attributes/items/%d/name", i), "name is required")
|
||||
}
|
||||
rows = append(rows, airrescuechecklist.AirRescuerChecklistItem{
|
||||
ChecklistID: checklistID,
|
||||
Name: name,
|
||||
Position: checklistPositionOrZero(req.Data.Attributes.Items[i].Position),
|
||||
CreatedBy: actor,
|
||||
UpdatedBy: actor,
|
||||
})
|
||||
}
|
||||
|
||||
if err := h.svc.CreateChecklistItems(c.UserContext(), checklistID, rows); err != nil {
|
||||
status, errObj := buildServiceErrorObject("Create failed", err, "air rescuer checklist not found")
|
||||
return writeAirRescuerChecklistErrors(c, status, []jsonapi.ErrorObject{errObj})
|
||||
}
|
||||
|
||||
data := make([]responsedto.AirRescuerChecklistItemData, 0, len(rows))
|
||||
for i := range rows {
|
||||
created, err := h.svc.GetChecklistItemByID(c.UserContext(), rows[i].ID)
|
||||
if err != nil {
|
||||
status, errObj := buildServiceErrorObject("Create failed", err, "")
|
||||
return writeAirRescuerChecklistErrors(c, status, []jsonapi.ErrorObject{errObj})
|
||||
}
|
||||
if created == nil {
|
||||
data = append(data, responsedto.AirRescuerChecklistItemData{
|
||||
Type: "air_rescuer_checklist_item",
|
||||
ID: idString(rows[i].ID),
|
||||
Attributes: responsedto.AirRescuerChecklistItemAttributes{
|
||||
ChecklistID: idString(checklistID),
|
||||
Name: rows[i].Name,
|
||||
Position: rows[i].Position,
|
||||
},
|
||||
})
|
||||
continue
|
||||
}
|
||||
data = append(data, airRescuerChecklistItemResponseData(*created))
|
||||
}
|
||||
|
||||
return response.Write(c, fiber.StatusCreated, data)
|
||||
}
|
||||
|
||||
// ReorderItems godoc
|
||||
// @Summary Reorder checklist items in one request
|
||||
// @Description Reorder one or more checklist items for the checklist in path `uuid`.
|
||||
// @Description Use this endpoint to swap or reorder positions with a single PIN validation.
|
||||
// @Description Duplicate `position` values remain allowed by domain rules.
|
||||
// @Description Request is transactional: if one item fails, all position updates are rolled back.
|
||||
// @Tags Air Rescuer Checklist
|
||||
// @Accept json
|
||||
// @Produce json
|
||||
// @Param uuid path string true "Checklist UUID"
|
||||
// @Param request body requestdto.AirRescuerChecklistItemReorderRequest true "Checklist item reorder payload"
|
||||
// @Success 200 {object} responsedto.AirRescuerChecklistResponse
|
||||
// @Failure 400 {object} jsonapi.ErrorDocument
|
||||
// @Failure 404 {object} jsonapi.ErrorDocument
|
||||
// @Failure 409 {object} jsonapi.ErrorDocument
|
||||
// @Failure 422 {object} jsonapi.ErrorDocument
|
||||
// @Router /api/v1/air-rescuer-checklist/{uuid}/items/reorder [patch]
|
||||
func (h *AirRescuerChecklistHandler) ReorderItems(c *fiber.Ctx) error {
|
||||
checklistID, err := uuidv7.ParseString(c.Params("uuid"))
|
||||
if err != nil {
|
||||
return validationError(c, "/path/uuid", "uuid is invalid UUID")
|
||||
}
|
||||
|
||||
var req requestdto.AirRescuerChecklistItemReorderRequest
|
||||
if err := c.BodyParser(&req); err != nil {
|
||||
return writeAirRescuerChecklistErrors(c, fiber.StatusBadRequest, []jsonapi.ErrorObject{{
|
||||
Status: "400",
|
||||
Title: "Invalid JSON",
|
||||
Detail: safeInternalDetail(),
|
||||
}})
|
||||
}
|
||||
if errs := h.validate.ValidateStruct(req); errs != nil {
|
||||
return writeAirRescuerChecklistErrors(c, fiber.StatusUnprocessableEntity, errs)
|
||||
}
|
||||
|
||||
seen := make(map[string]struct{}, len(req.Data.Attributes.Items))
|
||||
rows := make([]airrescuechecklist.ChecklistItemReorderInput, 0, len(req.Data.Attributes.Items))
|
||||
for i := range req.Data.Attributes.Items {
|
||||
itemUUID := strings.TrimSpace(req.Data.Attributes.Items[i].ItemUUID)
|
||||
itemID, err := uuidv7.ParseString(itemUUID)
|
||||
if err != nil {
|
||||
return validationError(c, fmt.Sprintf("/data/attributes/items/%d/item_uuid", i), "item_uuid is invalid UUID")
|
||||
}
|
||||
|
||||
itemKey := idString(itemID)
|
||||
if _, exists := seen[itemKey]; exists {
|
||||
return validationError(c, fmt.Sprintf("/data/attributes/items/%d/item_uuid", i), "item_uuid must be unique")
|
||||
}
|
||||
seen[itemKey] = struct{}{}
|
||||
|
||||
rows = append(rows, airrescuechecklist.ChecklistItemReorderInput{
|
||||
ID: itemID,
|
||||
Position: checklistPositionOrDefault(req.Data.Attributes.Items[i].Position),
|
||||
})
|
||||
}
|
||||
|
||||
if err := h.svc.ReorderChecklistItems(c.UserContext(), checklistID, rows, actorUserID(c)); err != nil {
|
||||
status, errObj := buildServiceErrorObject("Reorder failed", err, "air rescuer checklist item not found")
|
||||
return writeAirRescuerChecklistErrors(c, status, []jsonapi.ErrorObject{errObj})
|
||||
}
|
||||
|
||||
return h.respondChecklistByID(c, fiber.StatusOK, checklistID)
|
||||
}
|
||||
|
||||
// UpdateItem godoc
|
||||
// @Summary Update one checklist item
|
||||
// @Description Update one existing checklist item by `item_uuid`.
|
||||
// @Description This endpoint only affects the selected item.
|
||||
// @Description Duplicate `position` is allowed.
|
||||
// @Tags Air Rescuer Checklist
|
||||
// @Accept json
|
||||
// @Produce json
|
||||
// @Param item_uuid path string true "Checklist item UUID"
|
||||
// @Param request body requestdto.AirRescuerChecklistItemUpdateRequest true "Checklist item update payload"
|
||||
// @Success 200 {object} responsedto.AirRescuerChecklistItemResponse
|
||||
// @Failure 400 {object} jsonapi.ErrorDocument
|
||||
// @Failure 409 {object} jsonapi.ErrorDocument
|
||||
// @Failure 404 {object} jsonapi.ErrorDocument
|
||||
// @Failure 422 {object} jsonapi.ErrorDocument
|
||||
// @Router /api/v1/air-rescuer-checklist/items/update/{item_uuid} [patch]
|
||||
func (h *AirRescuerChecklistHandler) UpdateItem(c *fiber.Ctx) error {
|
||||
itemID, err := uuidv7.ParseString(c.Params("item_uuid"))
|
||||
if err != nil {
|
||||
return validationError(c, "/path/item_uuid", "item_uuid is invalid UUID")
|
||||
}
|
||||
|
||||
var req requestdto.AirRescuerChecklistItemUpdateRequest
|
||||
if err := c.BodyParser(&req); err != nil {
|
||||
return writeAirRescuerChecklistErrors(c, fiber.StatusBadRequest, []jsonapi.ErrorObject{{
|
||||
Status: "400",
|
||||
Title: "Invalid JSON",
|
||||
Detail: safeInternalDetail(),
|
||||
}})
|
||||
}
|
||||
if errs := h.validate.ValidateStruct(req); errs != nil {
|
||||
return writeAirRescuerChecklistErrors(c, fiber.StatusUnprocessableEntity, errs)
|
||||
}
|
||||
if strings.TrimSpace(req.Data.ID) != "" {
|
||||
reqID, err := uuidv7.ParseString(strings.TrimSpace(req.Data.ID))
|
||||
if err != nil {
|
||||
return validationError(c, "/data/id", "id is invalid UUID")
|
||||
}
|
||||
if !bytesEqualUUID(reqID, itemID) {
|
||||
return validationError(c, "/data/id", "id does not match path item_uuid")
|
||||
}
|
||||
}
|
||||
|
||||
attrs := req.Data.Attributes
|
||||
if attrs.Name == nil && attrs.Position == nil {
|
||||
return validationError(c, "/data/attributes", "at least one attribute must be provided")
|
||||
}
|
||||
|
||||
existing, err := h.svc.GetChecklistItemByID(c.UserContext(), itemID)
|
||||
if err != nil {
|
||||
status, errObj := buildServiceErrorObject("Update failed", err, "")
|
||||
return writeAirRescuerChecklistErrors(c, status, []jsonapi.ErrorObject{errObj})
|
||||
}
|
||||
if existing == nil {
|
||||
return writeAirRescuerChecklistErrors(c, fiber.StatusNotFound, []jsonapi.ErrorObject{{
|
||||
Status: "404",
|
||||
Title: "Not found",
|
||||
Detail: "air rescuer checklist item not found",
|
||||
}})
|
||||
}
|
||||
|
||||
name := strings.TrimSpace(existing.Name)
|
||||
if attrs.Name != nil {
|
||||
name = strings.TrimSpace(*attrs.Name)
|
||||
if name == "" {
|
||||
return validationError(c, "/data/attributes/name", "name is required")
|
||||
}
|
||||
}
|
||||
|
||||
position := existing.Position
|
||||
if attrs.Position != nil {
|
||||
position = checklistPositionOrDefault(attrs.Position)
|
||||
}
|
||||
|
||||
row := &airrescuechecklist.AirRescuerChecklistItem{
|
||||
ID: itemID,
|
||||
Name: name,
|
||||
Position: position,
|
||||
UpdatedBy: actorUserID(c),
|
||||
}
|
||||
if err := h.svc.UpdateChecklistItem(c.UserContext(), row); err != nil {
|
||||
status, errObj := buildServiceErrorObject("Update failed", err, "air rescuer checklist item not found")
|
||||
return writeAirRescuerChecklistErrors(c, status, []jsonapi.ErrorObject{errObj})
|
||||
}
|
||||
|
||||
updated, err := h.svc.GetChecklistItemByID(c.UserContext(), itemID)
|
||||
if err != nil {
|
||||
status, errObj := buildServiceErrorObject("Update failed", err, "")
|
||||
return writeAirRescuerChecklistErrors(c, status, []jsonapi.ErrorObject{errObj})
|
||||
}
|
||||
if updated == nil {
|
||||
return writeAirRescuerChecklistErrors(c, fiber.StatusNotFound, []jsonapi.ErrorObject{{
|
||||
Status: "404",
|
||||
Title: "Not found",
|
||||
Detail: "air rescuer checklist item not found",
|
||||
}})
|
||||
}
|
||||
return response.Write(c, fiber.StatusOK, airRescuerChecklistItemResponseData(*updated))
|
||||
}
|
||||
|
||||
// DeleteItem godoc
|
||||
// @Summary Delete one checklist item
|
||||
// @Description Delete one existing checklist item by `item_uuid`.
|
||||
// @Tags Air Rescuer Checklist
|
||||
// @Produce json
|
||||
// @Param item_uuid path string true "Checklist item UUID"
|
||||
// @Success 200 {object} jsonapi.Document
|
||||
// @Failure 409 {object} jsonapi.ErrorDocument
|
||||
// @Failure 404 {object} jsonapi.ErrorDocument
|
||||
// @Failure 422 {object} jsonapi.ErrorDocument
|
||||
// @Router /api/v1/air-rescuer-checklist/items/delete/{item_uuid} [delete]
|
||||
func (h *AirRescuerChecklistHandler) DeleteItem(c *fiber.Ctx) error {
|
||||
itemID, err := uuidv7.ParseString(c.Params("item_uuid"))
|
||||
if err != nil {
|
||||
return validationError(c, "/path/item_uuid", "item_uuid is invalid UUID")
|
||||
}
|
||||
|
||||
if err := h.svc.DeleteChecklistItem(c.UserContext(), itemID, actorUserID(c)); err != nil {
|
||||
status, errObj := buildServiceErrorObject("Delete failed", err, "air rescuer checklist item not found")
|
||||
return writeAirRescuerChecklistErrors(c, status, []jsonapi.ErrorObject{errObj})
|
||||
}
|
||||
|
||||
return response.Write(c, fiber.StatusOK, jsonapi.Resource{Type: "air_rescuer_checklist_item_delete", Attributes: map[string]any{"deleted": true}})
|
||||
}
|
||||
|
||||
func (h *AirRescuerChecklistHandler) respondChecklistByID(c *fiber.Ctx, status int, id []byte) error {
|
||||
resource, found, err := h.loadChecklistResource(c.UserContext(), id)
|
||||
if err != nil {
|
||||
statusCode, errObj := buildServiceErrorObject("Get failed", err, "")
|
||||
return writeAirRescuerChecklistErrors(c, statusCode, []jsonapi.ErrorObject{errObj})
|
||||
}
|
||||
if !found {
|
||||
return writeAirRescuerChecklistErrors(c, fiber.StatusNotFound, []jsonapi.ErrorObject{{
|
||||
Status: "404",
|
||||
Title: "Not found",
|
||||
Detail: "air rescuer checklist not found",
|
||||
}})
|
||||
}
|
||||
return response.Write(c, status, resource)
|
||||
}
|
||||
|
||||
func (h *AirRescuerChecklistHandler) loadChecklistResource(ctx context.Context, id []byte) (responsedto.AirRescuerChecklistResource, bool, error) {
|
||||
head, err := h.svc.GetChecklistByID(ctx, id)
|
||||
if err != nil {
|
||||
return responsedto.AirRescuerChecklistResource{}, false, err
|
||||
}
|
||||
if head == nil {
|
||||
return responsedto.AirRescuerChecklistResource{}, false, nil
|
||||
}
|
||||
itemRows, err := h.svc.ListChecklistItemsByChecklistIDs(ctx, [][]byte{id})
|
||||
if err != nil {
|
||||
return responsedto.AirRescuerChecklistResource{}, false, err
|
||||
}
|
||||
return airRescuerChecklistResource(*head, itemRows), true, nil
|
||||
}
|
||||
|
||||
func (h *AirRescuerChecklistHandler) loadChecklistGroups(
|
||||
ctx context.Context,
|
||||
headerFilter airrescuechecklist.ChecklistHeaderFilter,
|
||||
baseFilter airrescuechecklist.HEMSBaseFilter,
|
||||
requestedScope string,
|
||||
) ([]responsedto.AirRescuerChecklistBaseGroupResource, error) {
|
||||
bases, err := h.svc.ListHEMSBases(ctx, baseFilter)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
headers, err := h.svc.ListChecklistHeaders(ctx, headerFilter)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
ids := make([][]byte, 0, len(headers))
|
||||
for i := range headers {
|
||||
ids = append(ids, headers[i].ID)
|
||||
}
|
||||
items, err := h.svc.ListChecklistItemsByChecklistIDs(ctx, ids)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return buildAirRescuerChecklistGroups(bases, headers, items, requestedScope), nil
|
||||
}
|
||||
|
||||
func parseAirRescuerChecklistFilters(c *fiber.Ctx) (airrescuechecklist.ChecklistHeaderFilter, airrescuechecklist.HEMSBaseFilter, string, error) {
|
||||
headerFilter := airrescuechecklist.ChecklistHeaderFilter{}
|
||||
baseFilter := airrescuechecklist.HEMSBaseFilter{}
|
||||
requestedScope := ""
|
||||
|
||||
if categoryType, hasAnyCategory, ok := normalizeAirRescuerChecklistCategoryFromQuery(c); hasAnyCategory {
|
||||
if !ok {
|
||||
return headerFilter, baseFilter, requestedScope, validationError(c, "/query/category_type", "category_type/base_category must be one of: regular, hems")
|
||||
}
|
||||
headerFilter.CategoryType = categoryType
|
||||
baseFilter.CategoryType = categoryType
|
||||
}
|
||||
|
||||
baseRaw := strings.TrimSpace(c.Query("base_id"))
|
||||
if baseRaw != "" {
|
||||
baseID, err := uuidv7.ParseString(baseRaw)
|
||||
if err != nil {
|
||||
return headerFilter, baseFilter, requestedScope, validationError(c, "/query/base_id", "base_id is invalid UUID")
|
||||
}
|
||||
headerFilter.HEMSBaseID = baseID
|
||||
baseFilter.HEMSBaseID = baseID
|
||||
}
|
||||
|
||||
headerFilter.BaseName = strings.TrimSpace(c.Query("base_name"))
|
||||
headerFilter.BaseAbbreviation = strings.TrimSpace(c.Query("base_abbreviation"))
|
||||
baseFilter.BaseName = headerFilter.BaseName
|
||||
baseFilter.BaseAbbreviation = headerFilter.BaseAbbreviation
|
||||
|
||||
scopeCode := airrescuechecklist.CanonicalizeAirRescuerChecklistScope(c.Query("scope_code"))
|
||||
if scopeCode != "" {
|
||||
if !airrescuechecklist.IsAirRescuerChecklistQueryScope(scopeCode) {
|
||||
return headerFilter, baseFilter, requestedScope, validationError(c, "/query/scope_code", "scope_code is invalid")
|
||||
}
|
||||
requestedScope = scopeCode
|
||||
if airrescuechecklist.IsAirRescuerChecklistPersistedScope(scopeCode) {
|
||||
headerFilter.ScopeCode = scopeCode
|
||||
}
|
||||
}
|
||||
|
||||
return headerFilter, baseFilter, requestedScope, nil
|
||||
}
|
||||
|
||||
func normalizeAirRescuerChecklistCategoryFromQuery(c *fiber.Ctx) (categoryType string, hasAny bool, ok bool) {
|
||||
// Prefer new alias first, then backward-compatible query key.
|
||||
candidates := []string{
|
||||
strings.TrimSpace(c.Query("base_category")),
|
||||
strings.TrimSpace(c.Query("category_type")),
|
||||
}
|
||||
for _, raw := range candidates {
|
||||
if raw == "" {
|
||||
continue
|
||||
}
|
||||
hasAny = true
|
||||
if normalized, valid := basedomain.NormalizeCategoryType(raw); valid {
|
||||
return normalized, true, true
|
||||
}
|
||||
}
|
||||
return "", hasAny, false
|
||||
}
|
||||
|
||||
func checklistPositionOrDefault(v *int) int {
|
||||
if v == nil || *v <= 0 {
|
||||
return 1
|
||||
}
|
||||
return *v
|
||||
}
|
||||
|
||||
func checklistPositionOrZero(v *int) int {
|
||||
if v == nil || *v <= 0 {
|
||||
return 0
|
||||
}
|
||||
return *v
|
||||
}
|
||||
|
||||
func writeAirRescuerChecklistErrors(c *fiber.Ctx, status int, errs []jsonapi.ErrorObject) error {
|
||||
enriched := make([]jsonapi.ErrorObject, 0, len(errs))
|
||||
statusStr := strconv.Itoa(status)
|
||||
for i := range errs {
|
||||
e := errs[i]
|
||||
e.Status = statusStr
|
||||
if strings.TrimSpace(e.Code) == "" || strings.TrimSpace(e.ErrorCode) == "" {
|
||||
code, errorCode := inferAirRescuerChecklistErrorCode(status, e.Title, e.Detail)
|
||||
if strings.TrimSpace(e.Code) == "" {
|
||||
e.Code = code
|
||||
}
|
||||
if strings.TrimSpace(e.ErrorCode) == "" {
|
||||
e.ErrorCode = errorCode
|
||||
}
|
||||
}
|
||||
enriched = append(enriched, e)
|
||||
}
|
||||
return response.WriteErrors(c, status, enriched)
|
||||
}
|
||||
|
||||
func inferAirRescuerChecklistErrorCode(status int, title, detail string) (string, string) {
|
||||
lowerTitle := strings.ToLower(strings.TrimSpace(title))
|
||||
lowerDetail := strings.ToLower(strings.TrimSpace(detail))
|
||||
switch status {
|
||||
case fiber.StatusNotFound:
|
||||
switch {
|
||||
case strings.Contains(lowerDetail, "item not found"):
|
||||
return apperrorsx.CodeAirRescuerChecklistItemNotFound, apperrorsx.ErrAirRescuerChecklistItemNotFound.ErrorCode
|
||||
case strings.Contains(lowerDetail, "base not found"):
|
||||
return apperrorsx.CodeAirRescuerChecklistBaseNotFound, apperrorsx.ErrAirRescuerChecklistBaseNotFound.ErrorCode
|
||||
default:
|
||||
return apperrorsx.CodeAirRescuerChecklistNotFound, apperrorsx.ErrAirRescuerChecklistNotFound.ErrorCode
|
||||
}
|
||||
case fiber.StatusUnprocessableEntity:
|
||||
switch {
|
||||
case strings.Contains(lowerDetail, "scope_code is required"):
|
||||
return apperrorsx.CodeAirRescuerChecklistScopeRequired, apperrorsx.ErrAirRescuerChecklistScopeRequired.ErrorCode
|
||||
case strings.Contains(lowerDetail, "scope_code is invalid"):
|
||||
return apperrorsx.CodeAirRescuerChecklistScopeInvalid, apperrorsx.ErrAirRescuerChecklistScopeInvalid.ErrorCode
|
||||
case strings.Contains(lowerDetail, "title is required"):
|
||||
return apperrorsx.CodeAirRescuerChecklistTitleRequired, apperrorsx.ErrAirRescuerChecklistTitleRequired.ErrorCode
|
||||
case strings.Contains(lowerDetail, "name is required"):
|
||||
return apperrorsx.CodeAirRescuerChecklistNameRequired, apperrorsx.ErrAirRescuerChecklistNameRequired.ErrorCode
|
||||
case strings.Contains(lowerDetail, "id does not match path"):
|
||||
return apperrorsx.CodeAirRescuerChecklistIDMismatch, apperrorsx.ErrAirRescuerChecklistIDMismatch.ErrorCode
|
||||
case strings.Contains(lowerDetail, "invalid uuid"):
|
||||
return apperrorsx.CodeAirRescuerChecklistInvalidUUID, apperrorsx.ErrAirRescuerChecklistInvalidUUID.ErrorCode
|
||||
default:
|
||||
return apperrorsx.CodeAirRescuerChecklistValidationFailed, apperrorsx.ErrAirRescuerChecklistValidationFailed.ErrorCode
|
||||
}
|
||||
case fiber.StatusBadRequest:
|
||||
switch {
|
||||
case strings.Contains(lowerTitle, "invalid json"):
|
||||
return apperrorsx.CodeAirRescuerChecklistInvalidJSON, apperrorsx.ErrAirRescuerChecklistInvalidJSON.ErrorCode
|
||||
case strings.Contains(lowerTitle, "create failed"):
|
||||
return apperrorsx.CodeAirRescuerChecklistCreateFailed, apperrorsx.ErrAirRescuerChecklistCreateFailed.ErrorCode
|
||||
case strings.Contains(lowerTitle, "update failed"):
|
||||
return apperrorsx.CodeAirRescuerChecklistUpdateFailed, apperrorsx.ErrAirRescuerChecklistUpdateFailed.ErrorCode
|
||||
case strings.Contains(lowerTitle, "delete failed"):
|
||||
return apperrorsx.CodeAirRescuerChecklistDeleteFailed, apperrorsx.ErrAirRescuerChecklistDeleteFailed.ErrorCode
|
||||
case strings.Contains(lowerTitle, "list failed"), strings.Contains(lowerTitle, "get failed"):
|
||||
return apperrorsx.CodeAirRescuerChecklistListFailed, apperrorsx.ErrAirRescuerChecklistListFailed.ErrorCode
|
||||
case strings.Contains(lowerTitle, "reorder failed"):
|
||||
return apperrorsx.CodeAirRescuerChecklistReorderFailed, apperrorsx.ErrAirRescuerChecklistReorderFailed.ErrorCode
|
||||
default:
|
||||
return apperrorsx.CodeAirRescuerChecklistInvalidPayload, apperrorsx.ErrAirRescuerChecklistInvalidPayload.ErrorCode
|
||||
}
|
||||
default:
|
||||
return apperrorsx.CodeInternalServerError, apperrorsx.ErrInternal.ErrorCode
|
||||
}
|
||||
}
|
||||
|
||||
func validationError(c *fiber.Ctx, pointer, detail string) error {
|
||||
return writeAirRescuerChecklistErrors(c, fiber.StatusUnprocessableEntity, []jsonapi.ErrorObject{{
|
||||
Status: "422",
|
||||
Title: "Validation error",
|
||||
Detail: detail,
|
||||
Source: &jsonapi.ErrorSource{Pointer: pointer},
|
||||
}})
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
215
internal/transport/http/handlers/air_rescuer_checklist_mapper.go
Normal file
215
internal/transport/http/handlers/air_rescuer_checklist_mapper.go
Normal file
@@ -0,0 +1,215 @@
|
||||
package handlers
|
||||
|
||||
import (
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
airrescuechecklist "wucher/internal/domain/air_rescue_checklist"
|
||||
responsedto "wucher/internal/transport/http/dto/response"
|
||||
shareddto "wucher/internal/transport/http/dto/shared"
|
||||
)
|
||||
|
||||
func airRescuerChecklistResource(header airrescuechecklist.ChecklistHeaderView, items []airrescuechecklist.ChecklistItemView) responsedto.AirRescuerChecklistResource {
|
||||
return responsedto.AirRescuerChecklistResource{
|
||||
Type: "air_rescuer_checklist",
|
||||
ID: idString(header.ID),
|
||||
Attributes: responsedto.AirRescuerChecklistAttributes{
|
||||
Bases: shareddto.AirRescuerChecklistBaseInfo{
|
||||
ID: idString(header.HEMSBaseID),
|
||||
BaseName: strings.TrimSpace(header.BaseName),
|
||||
BaseAbbreviation: strings.TrimSpace(header.BaseAbbreviation),
|
||||
BaseCategory: strings.TrimSpace(header.BaseCategory),
|
||||
},
|
||||
ScopeCode: airrescuechecklist.AirRescuerChecklistScopeCodeEN(header.ScopeCode),
|
||||
ScopeCodeDe: airrescuechecklist.AirRescuerChecklistScopeCodeDE(header.ScopeCode),
|
||||
Title: strings.TrimSpace(header.Title),
|
||||
Position: header.Position,
|
||||
Items: mapAirRescuerChecklistItemResources(items),
|
||||
CreatedBy: uuidStringOrEmpty(header.CreatedBy),
|
||||
CreatedAt: header.CreatedAt.UTC().Format(time.RFC3339),
|
||||
UpdatedAt: header.UpdatedAt.UTC().Format(time.RFC3339),
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func airRescuerChecklistItemResponseData(item airrescuechecklist.ChecklistItemView) responsedto.AirRescuerChecklistItemData {
|
||||
return responsedto.AirRescuerChecklistItemData{
|
||||
Type: "air_rescuer_checklist_item",
|
||||
ID: idString(item.ID),
|
||||
Attributes: responsedto.AirRescuerChecklistItemAttributes{
|
||||
ChecklistID: idString(item.ChecklistID),
|
||||
Name: strings.TrimSpace(item.Name),
|
||||
Position: item.Position,
|
||||
CreatedBy: uuidStringOrEmpty(item.CreatedBy),
|
||||
CreatedAt: item.CreatedAt.UTC().Format(time.RFC3339),
|
||||
UpdatedAt: item.UpdatedAt.UTC().Format(time.RFC3339),
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func buildAirRescuerChecklistGroups(
|
||||
bases []airrescuechecklist.HEMSBaseView,
|
||||
headers []airrescuechecklist.ChecklistHeaderView,
|
||||
items []airrescuechecklist.ChecklistItemView,
|
||||
requestedScope string,
|
||||
) []responsedto.AirRescuerChecklistBaseGroupResource {
|
||||
itemsByChecklist := make(map[string][]airrescuechecklist.ChecklistItemView, len(items))
|
||||
for i := range items {
|
||||
key := idString(items[i].ChecklistID)
|
||||
itemsByChecklist[key] = append(itemsByChecklist[key], items[i])
|
||||
}
|
||||
|
||||
headersByBaseScope := make(map[string]map[string][]airrescuechecklist.ChecklistHeaderView)
|
||||
for i := range headers {
|
||||
baseKey := idString(headers[i].HEMSBaseID)
|
||||
scopeCode := strings.ToUpper(strings.TrimSpace(headers[i].ScopeCode))
|
||||
if headersByBaseScope[baseKey] == nil {
|
||||
headersByBaseScope[baseKey] = map[string][]airrescuechecklist.ChecklistHeaderView{}
|
||||
}
|
||||
headersByBaseScope[baseKey][scopeCode] = append(headersByBaseScope[baseKey][scopeCode], headers[i])
|
||||
}
|
||||
|
||||
groups := make([]responsedto.AirRescuerChecklistBaseGroupResource, 0, len(bases))
|
||||
for i := range bases {
|
||||
baseID := idString(bases[i].ID)
|
||||
groups = append(groups, responsedto.AirRescuerChecklistBaseGroupResource{
|
||||
Type: "air_rescuer_checklist_group",
|
||||
ID: "base-" + baseID,
|
||||
Attributes: responsedto.AirRescuerChecklistBaseGroupAttributes{
|
||||
Bases: shareddto.AirRescuerChecklistBaseInfo{
|
||||
ID: baseID,
|
||||
BaseName: strings.TrimSpace(bases[i].BaseName),
|
||||
BaseAbbreviation: strings.TrimSpace(bases[i].BaseAbbreviation),
|
||||
BaseCategory: strings.TrimSpace(bases[i].BaseCategory),
|
||||
},
|
||||
Tabs: buildAirRescuerChecklistTabs(headersByBaseScope[baseID], itemsByChecklist, requestedScope),
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
return groups
|
||||
}
|
||||
|
||||
func buildAirRescuerChecklistTabs(
|
||||
headersByScope map[string][]airrescuechecklist.ChecklistHeaderView,
|
||||
itemsByChecklist map[string][]airrescuechecklist.ChecklistItemView,
|
||||
requestedScope string,
|
||||
) []responsedto.AirRescuerChecklistTabResource {
|
||||
normalizedScope := strings.ToUpper(strings.TrimSpace(requestedScope))
|
||||
if normalizedScope == airrescuechecklist.AirRescuerChecklistScopeCAT {
|
||||
return []responsedto.AirRescuerChecklistTabResource{
|
||||
buildAirRescuerChecklistCATTab(headersByScope),
|
||||
}
|
||||
}
|
||||
if airrescuechecklist.IsAirRescuerChecklistPersistedScope(normalizedScope) {
|
||||
return []responsedto.AirRescuerChecklistTabResource{
|
||||
buildAirRescuerChecklistDetailTab(headersByScope, itemsByChecklist, normalizedScope),
|
||||
}
|
||||
}
|
||||
|
||||
scopeOrder := airrescuechecklist.AirRescuerChecklistPersistedScopeOrder()
|
||||
tabs := make([]responsedto.AirRescuerChecklistTabResource, 0, 1+len(scopeOrder))
|
||||
tabs = append(tabs, buildAirRescuerChecklistCATTab(headersByScope))
|
||||
for i := range scopeOrder {
|
||||
tabs = append(tabs, buildAirRescuerChecklistDetailTab(headersByScope, itemsByChecklist, scopeOrder[i]))
|
||||
}
|
||||
return tabs
|
||||
}
|
||||
|
||||
func buildAirRescuerChecklistCATTab(
|
||||
headersByScope map[string][]airrescuechecklist.ChecklistHeaderView,
|
||||
) responsedto.AirRescuerChecklistTabResource {
|
||||
scopeOrder := airrescuechecklist.AirRescuerChecklistPersistedScopeOrder()
|
||||
groups := make([]responsedto.AirRescuerChecklistCATGroup, 0, len(scopeOrder))
|
||||
for i := range scopeOrder {
|
||||
scopeCode := scopeOrder[i]
|
||||
headers := headersByScope[scopeCode]
|
||||
if len(headers) == 0 {
|
||||
continue
|
||||
}
|
||||
checklists := make([]responsedto.AirRescuerChecklistCATChecklistSummary, 0, len(headers))
|
||||
for j := range headers {
|
||||
checklists = append(checklists, responsedto.AirRescuerChecklistCATChecklistSummary{
|
||||
ID: idString(headers[j].ID),
|
||||
Title: strings.TrimSpace(headers[j].Title),
|
||||
Position: headers[j].Position,
|
||||
CreatedBy: uuidStringOrEmpty(headers[j].CreatedBy),
|
||||
CreatedAt: headers[j].CreatedAt.UTC().Format(time.RFC3339),
|
||||
UpdatedAt: headers[j].UpdatedAt.UTC().Format(time.RFC3339),
|
||||
})
|
||||
}
|
||||
groups = append(groups, responsedto.AirRescuerChecklistCATGroup{
|
||||
ScopeCode: airrescuechecklist.AirRescuerChecklistScopeCodeEN(scopeCode),
|
||||
ScopeCodeDe: airrescuechecklist.AirRescuerChecklistScopeCodeDE(scopeCode),
|
||||
ScopeCodeName: airrescuechecklist.AirRescuerChecklistScopeDisplayName(scopeCode),
|
||||
ScopeCodeNameDe: airrescuechecklist.AirRescuerChecklistScopeDisplayNameDE(scopeCode),
|
||||
Checklists: checklists,
|
||||
})
|
||||
}
|
||||
return responsedto.AirRescuerChecklistTabResource{
|
||||
ScopeCode: airrescuechecklist.AirRescuerChecklistScopeCodeEN(airrescuechecklist.AirRescuerChecklistScopeCAT),
|
||||
ScopeCodeDe: airrescuechecklist.AirRescuerChecklistScopeCodeDE(airrescuechecklist.AirRescuerChecklistScopeCAT),
|
||||
ScopeCodeName: airrescuechecklist.AirRescuerChecklistScopeDisplayName(airrescuechecklist.AirRescuerChecklistScopeCAT),
|
||||
ScopeCodeNameDe: airrescuechecklist.AirRescuerChecklistScopeDisplayNameDE(airrescuechecklist.AirRescuerChecklistScopeCAT),
|
||||
Groups: groups,
|
||||
}
|
||||
}
|
||||
|
||||
func buildAirRescuerChecklistDetailTab(
|
||||
headersByScope map[string][]airrescuechecklist.ChecklistHeaderView,
|
||||
itemsByChecklist map[string][]airrescuechecklist.ChecklistItemView,
|
||||
scopeCode string,
|
||||
) responsedto.AirRescuerChecklistTabResource {
|
||||
headers := headersByScope[scopeCode]
|
||||
checklists := make([]responsedto.AirRescuerChecklistTabChecklistDetail, 0, len(headers))
|
||||
for i := range headers {
|
||||
key := idString(headers[i].ID)
|
||||
checklists = append(checklists, responsedto.AirRescuerChecklistTabChecklistDetail{
|
||||
ID: idString(headers[i].ID),
|
||||
Title: strings.TrimSpace(headers[i].Title),
|
||||
Position: headers[i].Position,
|
||||
ChecklistItems: mapAirRescuerChecklistItemResources(itemsByChecklist[key]),
|
||||
CreatedBy: uuidStringOrEmpty(headers[i].CreatedBy),
|
||||
CreatedAt: headers[i].CreatedAt.UTC().Format(time.RFC3339),
|
||||
UpdatedAt: headers[i].UpdatedAt.UTC().Format(time.RFC3339),
|
||||
})
|
||||
}
|
||||
return responsedto.AirRescuerChecklistTabResource{
|
||||
ScopeCode: airrescuechecklist.AirRescuerChecklistScopeCodeEN(scopeCode),
|
||||
ScopeCodeDe: airrescuechecklist.AirRescuerChecklistScopeCodeDE(scopeCode),
|
||||
ScopeCodeName: airrescuechecklist.AirRescuerChecklistScopeDisplayName(scopeCode),
|
||||
ScopeCodeNameDe: airrescuechecklist.AirRescuerChecklistScopeDisplayNameDE(scopeCode),
|
||||
Checklists: &checklists,
|
||||
}
|
||||
}
|
||||
|
||||
func mapAirRescuerChecklistItemResources(items []airrescuechecklist.ChecklistItemView) []responsedto.AirRescuerChecklistItemResource {
|
||||
out := make([]responsedto.AirRescuerChecklistItemResource, 0, len(items))
|
||||
for i := range items {
|
||||
out = append(out, responsedto.AirRescuerChecklistItemResource{
|
||||
ID: idString(items[i].ID),
|
||||
Name: strings.TrimSpace(items[i].Name),
|
||||
Position: items[i].Position,
|
||||
CreatedBy: uuidStringOrEmpty(items[i].CreatedBy),
|
||||
CreatedAt: items[i].CreatedAt.UTC().Format(time.RFC3339),
|
||||
UpdatedAt: items[i].UpdatedAt.UTC().Format(time.RFC3339),
|
||||
})
|
||||
}
|
||||
return out
|
||||
}
|
||||
|
||||
func paginateAirRescuerChecklistGroups(
|
||||
groups []responsedto.AirRescuerChecklistBaseGroupResource,
|
||||
start,
|
||||
length int,
|
||||
) ([]responsedto.AirRescuerChecklistBaseGroupResource, int) {
|
||||
total := len(groups)
|
||||
if start > total {
|
||||
start = total
|
||||
}
|
||||
end := start + length
|
||||
if end > total {
|
||||
end = total
|
||||
}
|
||||
return groups[start:end], total
|
||||
}
|
||||
@@ -0,0 +1,359 @@
|
||||
package handlers
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
airrescuechecklist "wucher/internal/domain/air_rescue_checklist"
|
||||
"wucher/internal/shared/pkg/uuidv7"
|
||||
responsedto "wucher/internal/transport/http/dto/response"
|
||||
)
|
||||
|
||||
func TestBuildAirRescuerChecklistGroups_DefaultTabsIncludeCATAndDetail(t *testing.T) {
|
||||
baseID := uuidv7.MustBytes()
|
||||
checklistTA := uuidv7.MustBytes()
|
||||
checklistMO := uuidv7.MustBytes()
|
||||
itemTA := uuidv7.MustBytes()
|
||||
now := time.Now().UTC()
|
||||
|
||||
bases := []airrescuechecklist.HEMSBaseView{
|
||||
{ID: baseID, BaseName: "Gallus 1 - Zurs Lech", BaseAbbreviation: "G1"},
|
||||
}
|
||||
headers := []airrescuechecklist.ChecklistHeaderView{
|
||||
{ID: checklistTA, HEMSBaseID: baseID, ScopeCode: airrescuechecklist.AirRescuerChecklistScopeTA, Title: "Cockpit Flugretter", Position: 1, CreatedAt: now, UpdatedAt: now},
|
||||
{ID: checklistMO, HEMSBaseID: baseID, ScopeCode: airrescuechecklist.AirRescuerChecklistScopeMO, Title: "Monday Checklist", Position: 2, CreatedAt: now, UpdatedAt: now},
|
||||
}
|
||||
items := []airrescuechecklist.ChecklistItemView{
|
||||
{ID: itemTA, ChecklistID: checklistTA, Name: "Kartenmaterial", Position: 1, CreatedAt: now, UpdatedAt: now},
|
||||
}
|
||||
|
||||
groups := buildAirRescuerChecklistGroups(bases, headers, items, "")
|
||||
if len(groups) != 1 {
|
||||
t.Fatalf("expected 1 base group, got %d", len(groups))
|
||||
}
|
||||
|
||||
tabs := groups[0].Attributes.Tabs
|
||||
if len(tabs) != 1+len(airrescuechecklist.AirRescuerChecklistPersistedScopeOrder()) {
|
||||
t.Fatalf("unexpected tabs count: %d", len(tabs))
|
||||
}
|
||||
|
||||
cat := mustFindChecklistTab(t, tabs, airrescuechecklist.AirRescuerChecklistScopeCAT)
|
||||
if cat.ScopeCodeName != "Category" || cat.ScopeCodeNameDe != "Category" {
|
||||
t.Fatalf("unexpected CAT scope names: en=%q de=%q", cat.ScopeCodeName, cat.ScopeCodeNameDe)
|
||||
}
|
||||
if len(cat.Groups) != 2 {
|
||||
t.Fatalf("expected CAT to include only non-empty scopes, got %d groups", len(cat.Groups))
|
||||
}
|
||||
for i := range cat.Groups {
|
||||
if len(cat.Groups[i].Checklists) == 0 {
|
||||
t.Fatalf("CAT group must have checklists")
|
||||
}
|
||||
}
|
||||
if cat.Groups[0].ScopeCodeDe == airrescuechecklist.AirRescuerChecklistScopeTA &&
|
||||
(cat.Groups[0].ScopeCodeName != "Daily" || cat.Groups[0].ScopeCodeNameDe != "Taeglich") {
|
||||
t.Fatalf("unexpected TA group scope names: en=%q de=%q", cat.Groups[0].ScopeCodeName, cat.Groups[0].ScopeCodeNameDe)
|
||||
}
|
||||
|
||||
ta := mustFindChecklistTab(t, tabs, airrescuechecklist.AirRescuerChecklistScopeTA)
|
||||
if ta.ScopeCodeName != "Daily" || ta.ScopeCodeNameDe != "Taeglich" {
|
||||
t.Fatalf("unexpected TA scope names: en=%q de=%q", ta.ScopeCodeName, ta.ScopeCodeNameDe)
|
||||
}
|
||||
if ta.Checklists == nil || len(*ta.Checklists) != 1 {
|
||||
t.Fatalf("expected TA detail to have 1 checklist")
|
||||
}
|
||||
if len((*ta.Checklists)[0].ChecklistItems) != 1 {
|
||||
t.Fatalf("expected TA detail checklist to include items")
|
||||
}
|
||||
|
||||
di := mustFindChecklistTab(t, tabs, airrescuechecklist.AirRescuerChecklistScopeDI)
|
||||
if di.Checklists == nil || len(*di.Checklists) != 0 {
|
||||
t.Fatalf("expected empty scope detail tab to return checklists: []")
|
||||
}
|
||||
|
||||
raw, err := json.Marshal(groups)
|
||||
if err != nil {
|
||||
t.Fatalf("marshal groups: %v", err)
|
||||
}
|
||||
if !jsonContainsTabWithEmptyChecklists(raw, airrescuechecklist.AirRescuerChecklistScopeDI) {
|
||||
t.Fatalf("expected DI tab serialized with checklists: []")
|
||||
}
|
||||
}
|
||||
|
||||
func TestBuildAirRescuerChecklistGroups_CATOnlyRequest(t *testing.T) {
|
||||
baseID := uuidv7.MustBytes()
|
||||
checklistTA := uuidv7.MustBytes()
|
||||
now := time.Now().UTC()
|
||||
|
||||
bases := []airrescuechecklist.HEMSBaseView{
|
||||
{ID: baseID, BaseName: "Gallus 1 - Zurs Lech", BaseAbbreviation: "G1"},
|
||||
}
|
||||
headers := []airrescuechecklist.ChecklistHeaderView{
|
||||
{ID: checklistTA, HEMSBaseID: baseID, ScopeCode: airrescuechecklist.AirRescuerChecklistScopeTA, Title: "Cockpit Flugretter", Position: 1, CreatedAt: now, UpdatedAt: now},
|
||||
}
|
||||
|
||||
groups := buildAirRescuerChecklistGroups(bases, headers, nil, airrescuechecklist.AirRescuerChecklistScopeCAT)
|
||||
if len(groups) != 1 {
|
||||
t.Fatalf("expected 1 base group, got %d", len(groups))
|
||||
}
|
||||
if len(groups[0].Attributes.Tabs) != 1 {
|
||||
t.Fatalf("expected CAT query to return only CAT tab")
|
||||
}
|
||||
cat := groups[0].Attributes.Tabs[0]
|
||||
if cat.ScopeCodeDe != airrescuechecklist.AirRescuerChecklistScopeCAT {
|
||||
t.Fatalf("expected CAT tab")
|
||||
}
|
||||
if cat.ScopeCodeName != "Category" || cat.ScopeCodeNameDe != "Category" {
|
||||
t.Fatalf("unexpected CAT scope names: en=%q de=%q", cat.ScopeCodeName, cat.ScopeCodeNameDe)
|
||||
}
|
||||
if len(cat.Groups) != 1 || cat.Groups[0].ScopeCodeDe != airrescuechecklist.AirRescuerChecklistScopeTA {
|
||||
t.Fatalf("expected CAT to include only non-empty TA group")
|
||||
}
|
||||
if cat.Groups[0].ScopeCodeName != "Daily" || cat.Groups[0].ScopeCodeNameDe != "Taeglich" {
|
||||
t.Fatalf("unexpected TA group scope names: en=%q de=%q", cat.Groups[0].ScopeCodeName, cat.Groups[0].ScopeCodeNameDe)
|
||||
}
|
||||
if cat.Checklists != nil {
|
||||
t.Fatalf("expected CAT tab to omit detail checklists")
|
||||
}
|
||||
}
|
||||
|
||||
func TestBuildAirRescuerChecklistGroups_RequestedPersistedScopeCanBeEmpty(t *testing.T) {
|
||||
baseID := uuidv7.MustBytes()
|
||||
bases := []airrescuechecklist.HEMSBaseView{
|
||||
{ID: baseID, BaseName: "Gallus 1 - Zurs Lech", BaseAbbreviation: "G1"},
|
||||
}
|
||||
|
||||
groups := buildAirRescuerChecklistGroups(bases, nil, nil, airrescuechecklist.AirRescuerChecklistScopeM10)
|
||||
if len(groups) != 1 {
|
||||
t.Fatalf("expected 1 base group, got %d", len(groups))
|
||||
}
|
||||
if len(groups[0].Attributes.Tabs) != 1 {
|
||||
t.Fatalf("expected one requested tab")
|
||||
}
|
||||
tab := groups[0].Attributes.Tabs[0]
|
||||
if tab.ScopeCodeDe != airrescuechecklist.AirRescuerChecklistScopeM10 {
|
||||
t.Fatalf("expected M10 tab, got %s", tab.ScopeCodeDe)
|
||||
}
|
||||
if tab.ScopeCodeName != "M10" || tab.ScopeCodeNameDe != "M10" {
|
||||
t.Fatalf("unexpected M10 scope names: en=%q de=%q", tab.ScopeCodeName, tab.ScopeCodeNameDe)
|
||||
}
|
||||
if tab.Checklists == nil || len(*tab.Checklists) != 0 {
|
||||
t.Fatalf("expected requested empty persisted tab to return checklists: []")
|
||||
}
|
||||
}
|
||||
|
||||
func TestBuildAirRescuerChecklistGroups_CATAndDetailOrderingWithDuplicatePosition(t *testing.T) {
|
||||
baseID := uuidv7.MustBytes()
|
||||
taChecklistAlpha := uuidv7.MustBytes()
|
||||
taChecklistBravo := uuidv7.MustBytes()
|
||||
taChecklistCharlie := uuidv7.MustBytes()
|
||||
moChecklist := uuidv7.MustBytes()
|
||||
now := time.Now().UTC()
|
||||
|
||||
bases := []airrescuechecklist.HEMSBaseView{
|
||||
{ID: baseID, BaseName: "Gallus 1 - Zurs Lech", BaseAbbreviation: "G1"},
|
||||
}
|
||||
|
||||
// Header order here follows repository contract: position ASC, title ASC.
|
||||
headers := []airrescuechecklist.ChecklistHeaderView{
|
||||
{ID: taChecklistAlpha, HEMSBaseID: baseID, ScopeCode: airrescuechecklist.AirRescuerChecklistScopeTA, Title: "Alpha", Position: 1, CreatedAt: now, UpdatedAt: now},
|
||||
{ID: taChecklistBravo, HEMSBaseID: baseID, ScopeCode: airrescuechecklist.AirRescuerChecklistScopeTA, Title: "Bravo", Position: 1, CreatedAt: now, UpdatedAt: now},
|
||||
{ID: moChecklist, HEMSBaseID: baseID, ScopeCode: airrescuechecklist.AirRescuerChecklistScopeMO, Title: "Lagerbestand", Position: 1, CreatedAt: now, UpdatedAt: now},
|
||||
{ID: taChecklistCharlie, HEMSBaseID: baseID, ScopeCode: airrescuechecklist.AirRescuerChecklistScopeTA, Title: "Charlie", Position: 2, CreatedAt: now, UpdatedAt: now},
|
||||
}
|
||||
|
||||
// Item order here follows repository contract: position ASC, name ASC.
|
||||
items := []airrescuechecklist.ChecklistItemView{
|
||||
{ID: uuidv7.MustBytes(), ChecklistID: taChecklistAlpha, Name: "Oxygen", Position: 1, CreatedAt: now, UpdatedAt: now},
|
||||
{ID: uuidv7.MustBytes(), ChecklistID: taChecklistAlpha, Name: "Battery", Position: 2, CreatedAt: now, UpdatedAt: now},
|
||||
{ID: uuidv7.MustBytes(), ChecklistID: taChecklistAlpha, Name: "Radio", Position: 2, CreatedAt: now, UpdatedAt: now},
|
||||
}
|
||||
|
||||
groups := buildAirRescuerChecklistGroups(bases, headers, items, "")
|
||||
if len(groups) != 1 {
|
||||
t.Fatalf("expected 1 base group, got %d", len(groups))
|
||||
}
|
||||
tabs := groups[0].Attributes.Tabs
|
||||
|
||||
cat := mustFindChecklistTab(t, tabs, airrescuechecklist.AirRescuerChecklistScopeCAT)
|
||||
if cat.Checklists != nil {
|
||||
t.Fatalf("expected CAT tab to omit detail checklists")
|
||||
}
|
||||
if len(cat.Groups) != 2 {
|
||||
t.Fatalf("expected CAT to include only non-empty TA and MO groups, got %d groups", len(cat.Groups))
|
||||
}
|
||||
if cat.Groups[0].ScopeCodeDe != airrescuechecklist.AirRescuerChecklistScopeTA || cat.Groups[1].ScopeCodeDe != airrescuechecklist.AirRescuerChecklistScopeMO {
|
||||
t.Fatalf("unexpected CAT group order: %+v", cat.Groups)
|
||||
}
|
||||
if len(cat.Groups[0].Checklists) != 3 {
|
||||
t.Fatalf("expected CAT TA group to include 3 checklist summaries")
|
||||
}
|
||||
if cat.Groups[0].Checklists[0].Title != "Alpha" || cat.Groups[0].Checklists[1].Title != "Bravo" || cat.Groups[0].Checklists[2].Title != "Charlie" {
|
||||
t.Fatalf("unexpected CAT TA checklist order: %+v", cat.Groups[0].Checklists)
|
||||
}
|
||||
|
||||
catJSON, err := json.Marshal(cat)
|
||||
if err != nil {
|
||||
t.Fatalf("marshal CAT tab: %v", err)
|
||||
}
|
||||
if jsonContainsChecklistItemsField(catJSON) {
|
||||
t.Fatalf("expected CAT summary payload without checklist_items")
|
||||
}
|
||||
|
||||
ta := mustFindChecklistTab(t, tabs, airrescuechecklist.AirRescuerChecklistScopeTA)
|
||||
if ta.Checklists == nil || len(*ta.Checklists) != 3 {
|
||||
t.Fatalf("expected TA detail to include 3 checklists")
|
||||
}
|
||||
if (*ta.Checklists)[0].Title != "Alpha" || (*ta.Checklists)[1].Title != "Bravo" || (*ta.Checklists)[2].Title != "Charlie" {
|
||||
t.Fatalf("unexpected TA detail checklist order: %+v", *ta.Checklists)
|
||||
}
|
||||
if len((*ta.Checklists)[0].ChecklistItems) != 3 {
|
||||
t.Fatalf("expected Alpha checklist to include 3 items")
|
||||
}
|
||||
if (*ta.Checklists)[0].ChecklistItems[0].Name != "Oxygen" || (*ta.Checklists)[0].ChecklistItems[1].Name != "Battery" || (*ta.Checklists)[0].ChecklistItems[2].Name != "Radio" {
|
||||
t.Fatalf("unexpected TA checklist item order: %+v", (*ta.Checklists)[0].ChecklistItems)
|
||||
}
|
||||
}
|
||||
|
||||
func mustFindChecklistTab(t *testing.T, tabs []responsedto.AirRescuerChecklistTabResource, scopeCode string) responsedto.AirRescuerChecklistTabResource {
|
||||
t.Helper()
|
||||
for i := range tabs {
|
||||
if tabs[i].ScopeCodeDe == scopeCode {
|
||||
return tabs[i]
|
||||
}
|
||||
}
|
||||
t.Fatalf("scope tab %s not found", scopeCode)
|
||||
return responsedto.AirRescuerChecklistTabResource{}
|
||||
}
|
||||
|
||||
func jsonContainsTabWithEmptyChecklists(raw []byte, scopeCode string) bool {
|
||||
var payload []map[string]any
|
||||
if err := json.Unmarshal(raw, &payload); err != nil {
|
||||
return false
|
||||
}
|
||||
for _, group := range payload {
|
||||
attrsRaw, ok := group["attributes"].(map[string]any)
|
||||
if !ok {
|
||||
continue
|
||||
}
|
||||
tabsRaw, ok := attrsRaw["tabs"].([]any)
|
||||
if !ok {
|
||||
continue
|
||||
}
|
||||
for _, tab := range tabsRaw {
|
||||
tabMap, ok := tab.(map[string]any)
|
||||
if !ok {
|
||||
continue
|
||||
}
|
||||
if tabMap["scope_code_de"] != scopeCode {
|
||||
continue
|
||||
}
|
||||
checklists, ok := tabMap["checklists"].([]any)
|
||||
return ok && len(checklists) == 0
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func jsonContainsChecklistItemsField(raw []byte) bool {
|
||||
return bytes.Contains(raw, []byte(`"checklist_items"`))
|
||||
}
|
||||
|
||||
func TestAirRescuerChecklistMapperResourceAndItemData(t *testing.T) {
|
||||
now := time.Date(2026, 3, 25, 8, 0, 0, 0, time.UTC)
|
||||
baseID := []byte("1234567890123456")
|
||||
checklistID := []byte("abcdefghijklmnop")
|
||||
itemID := []byte("itemitemitemitem")
|
||||
creatorID := uuidv7.MustBytes()
|
||||
|
||||
resource := airRescuerChecklistResource(airrescuechecklist.ChecklistHeaderView{
|
||||
ID: checklistID,
|
||||
HEMSBaseID: baseID,
|
||||
BaseName: " Gallus 1 - Zurs Lech ",
|
||||
BaseAbbreviation: " G1 ",
|
||||
BaseCategory: " regular ",
|
||||
ScopeCode: " TA ",
|
||||
Title: " Daily Tasks ",
|
||||
Position: 2,
|
||||
CreatedBy: creatorID,
|
||||
CreatedAt: now,
|
||||
UpdatedAt: now,
|
||||
}, []airrescuechecklist.ChecklistItemView{{
|
||||
ID: itemID,
|
||||
ChecklistID: checklistID,
|
||||
Name: " Battery ",
|
||||
Position: 1,
|
||||
CreatedBy: creatorID,
|
||||
CreatedAt: now,
|
||||
UpdatedAt: now,
|
||||
}})
|
||||
|
||||
if resource.Type != "air_rescuer_checklist" {
|
||||
t.Fatalf("unexpected type: %s", resource.Type)
|
||||
}
|
||||
if resource.Attributes.Bases.BaseName != "Gallus 1 - Zurs Lech" {
|
||||
t.Fatalf("expected trimmed base name, got %q", resource.Attributes.Bases.BaseName)
|
||||
}
|
||||
if resource.Attributes.Bases.BaseCategory != "regular" {
|
||||
t.Fatalf("expected base_category regular, got %q", resource.Attributes.Bases.BaseCategory)
|
||||
}
|
||||
if resource.Attributes.ScopeCode != "DA" || resource.Attributes.ScopeCodeDe != "TA" || resource.Attributes.Title != "Daily Tasks" {
|
||||
t.Fatalf("expected mapped scope/title, got scope=%q scope_de=%q title=%q", resource.Attributes.ScopeCode, resource.Attributes.ScopeCodeDe, resource.Attributes.Title)
|
||||
}
|
||||
if len(resource.Attributes.Items) != 1 || resource.Attributes.Items[0].Name != "Battery" {
|
||||
t.Fatalf("expected mapped and trimmed items")
|
||||
}
|
||||
if resource.Attributes.CreatedAt != "2026-03-25T08:00:00Z" {
|
||||
t.Fatalf("unexpected created_at: %s", resource.Attributes.CreatedAt)
|
||||
}
|
||||
if resource.Attributes.CreatedBy == "" || resource.Attributes.Items[0].CreatedBy == "" {
|
||||
t.Fatalf("expected created_by on checklist resource and item")
|
||||
}
|
||||
|
||||
itemData := airRescuerChecklistItemResponseData(airrescuechecklist.ChecklistItemView{
|
||||
ID: itemID,
|
||||
ChecklistID: checklistID,
|
||||
Name: " Radio ",
|
||||
Position: 3,
|
||||
CreatedBy: creatorID,
|
||||
CreatedAt: now,
|
||||
UpdatedAt: now,
|
||||
})
|
||||
if itemData.Type != "air_rescuer_checklist_item" {
|
||||
t.Fatalf("unexpected item type: %s", itemData.Type)
|
||||
}
|
||||
if itemData.Attributes.Name != "Radio" {
|
||||
t.Fatalf("expected trimmed item name, got %q", itemData.Attributes.Name)
|
||||
}
|
||||
if itemData.Attributes.Position != 3 {
|
||||
t.Fatalf("expected position=3, got %d", itemData.Attributes.Position)
|
||||
}
|
||||
if itemData.Attributes.CreatedBy == "" {
|
||||
t.Fatalf("expected created_by on item response")
|
||||
}
|
||||
}
|
||||
|
||||
func TestPaginateAirRescuerChecklistGroups(t *testing.T) {
|
||||
groups := []responsedto.AirRescuerChecklistBaseGroupResource{
|
||||
{ID: "base-1"},
|
||||
{ID: "base-2"},
|
||||
{ID: "base-3"},
|
||||
}
|
||||
|
||||
got, total := paginateAirRescuerChecklistGroups(groups, 10, 2)
|
||||
if total != 3 {
|
||||
t.Fatalf("expected total=3, got %d", total)
|
||||
}
|
||||
if len(got) != 0 {
|
||||
t.Fatalf("expected empty page when start exceeds total")
|
||||
}
|
||||
|
||||
got, total = paginateAirRescuerChecklistGroups(groups, 1, 5)
|
||||
if total != 3 {
|
||||
t.Fatalf("expected total=3, got %d", total)
|
||||
}
|
||||
if len(got) != 2 || got[0].ID != "base-2" || got[1].ID != "base-3" {
|
||||
t.Fatalf("unexpected paged groups: %+v", got)
|
||||
}
|
||||
}
|
||||
85
internal/transport/http/handlers/attachment_resolver.go
Normal file
85
internal/transport/http/handlers/attachment_resolver.go
Normal file
@@ -0,0 +1,85 @@
|
||||
package handlers
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"github.com/gofiber/fiber/v2"
|
||||
|
||||
"wucher/internal/shared/pkg/attachmentresolver"
|
||||
"wucher/internal/transport/http/jsonapi"
|
||||
"wucher/internal/transport/http/response"
|
||||
)
|
||||
|
||||
func resolveAttachmentIDFromInput(c *fiber.Ctx, params attachmentresolver.Params, errorTitle string) ([]byte, bool) {
|
||||
attachmentID, err := attachmentresolver.Resolve(c.UserContext(), params)
|
||||
if err == nil {
|
||||
return attachmentID, false
|
||||
}
|
||||
|
||||
var resolveErr *attachmentresolver.ResolveError
|
||||
if errors.As(err, &resolveErr) {
|
||||
switch resolveErr.Code {
|
||||
case attachmentresolver.ErrorInvalidAttachmentUUID:
|
||||
writeValidationError(c, fmt.Sprintf("%s is invalid UUID", fieldOrDefault(resolveErr.AttachmentField, "attachment_id")), "/data/attributes/"+fieldOrDefault(resolveErr.AttachmentField, "attachment_id"))
|
||||
return nil, true
|
||||
case attachmentresolver.ErrorInvalidFileUUID:
|
||||
writeValidationError(c, fmt.Sprintf("%s is invalid UUID", fieldOrDefault(resolveErr.FileField, "file_id")), "/data/attributes/"+fieldOrDefault(resolveErr.FileField, "file_id"))
|
||||
return nil, true
|
||||
case attachmentresolver.ErrorAttachmentNotFound:
|
||||
writeValidationError(c, fmt.Sprintf("%s not found", fieldOrDefault(resolveErr.AttachmentField, "attachment_id")), "/data/attributes/"+fieldOrDefault(resolveErr.AttachmentField, "attachment_id"))
|
||||
return nil, true
|
||||
case attachmentresolver.ErrorRequireValue:
|
||||
writeValidationError(c, fmt.Sprintf("%s or %s is required", fieldOrDefault(resolveErr.AttachmentField, "attachment_id"), fieldOrDefault(resolveErr.FileField, "file_id")), "/data/attributes")
|
||||
return nil, true
|
||||
case attachmentresolver.ErrorCannotResolveFromFile:
|
||||
writeValidationError(c, fmt.Sprintf("cannot resolve attachment from %s", fieldOrDefault(resolveErr.FileField, "file_id")), "/data/attributes/"+fieldOrDefault(resolveErr.FileField, "file_id"))
|
||||
return nil, true
|
||||
case attachmentresolver.ErrorFileNotReadyForAttachment:
|
||||
writeValidationError(c, fmt.Sprintf("%s is not ready yet; wait for file.updated SSE event", fieldOrDefault(resolveErr.FileField, "file_id")), "/data/attributes/"+fieldOrDefault(resolveErr.FileField, "file_id"))
|
||||
return nil, true
|
||||
case attachmentresolver.ErrorFileManagerUnavailable:
|
||||
writeBadRequestError(c, errorTitle, resolveErr.Error())
|
||||
return nil, true
|
||||
}
|
||||
}
|
||||
|
||||
writeBadRequestError(c, errorTitle, err.Error())
|
||||
return nil, true
|
||||
}
|
||||
|
||||
func writeValidationError(c *fiber.Ctx, detail, pointer string) {
|
||||
_ = response.WriteErrors(c, fiber.StatusUnprocessableEntity, []jsonapi.ErrorObject{{
|
||||
Status: "422",
|
||||
Title: "Validation error",
|
||||
Detail: detail,
|
||||
Source: &jsonapi.ErrorSource{Pointer: pointer},
|
||||
}})
|
||||
}
|
||||
|
||||
func writeBadRequestError(c *fiber.Ctx, title, detail string) {
|
||||
if title == "" {
|
||||
title = "Request failed"
|
||||
}
|
||||
_ = response.WriteErrors(c, fiber.StatusBadRequest, []jsonapi.ErrorObject{{
|
||||
Status: "400",
|
||||
Title: title,
|
||||
Detail: detail,
|
||||
}})
|
||||
}
|
||||
|
||||
func fieldOrDefault(v string, fallback string) string {
|
||||
if v == "" {
|
||||
return fallback
|
||||
}
|
||||
return v
|
||||
}
|
||||
|
||||
func hasNonEmptyOptionalString(v *string) bool {
|
||||
return v != nil && strings.TrimSpace(*v) != ""
|
||||
}
|
||||
|
||||
func hasBlankOptionalString(v *string) bool {
|
||||
return v != nil && strings.TrimSpace(*v) == ""
|
||||
}
|
||||
119
internal/transport/http/handlers/attachment_resolver_test.go
Normal file
119
internal/transport/http/handlers/attachment_resolver_test.go
Normal file
@@ -0,0 +1,119 @@
|
||||
package handlers
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"testing"
|
||||
|
||||
"github.com/gofiber/fiber/v2"
|
||||
|
||||
filemanager "wucher/internal/domain/file_manager"
|
||||
"wucher/internal/shared/pkg/attachmentresolver"
|
||||
"wucher/internal/shared/pkg/uuidv7"
|
||||
)
|
||||
|
||||
func TestAttachmentResolverHelpers(t *testing.T) {
|
||||
app := fiber.New()
|
||||
app.Get("/invalid-attachment", func(c *fiber.Ctx) error {
|
||||
raw := "bad"
|
||||
_, handled := resolveAttachmentIDFromInput(c, attachmentresolver.Params{
|
||||
RawAttachmentID: &raw,
|
||||
AttachmentField: "attachment_id",
|
||||
FileField: "file_uuid",
|
||||
}, "Update failed")
|
||||
if !handled {
|
||||
t.Fatalf("expected handled invalid attachment")
|
||||
}
|
||||
return nil
|
||||
})
|
||||
app.Get("/invalid-file", func(c *fiber.Ctx) error {
|
||||
raw := "bad"
|
||||
_, handled := resolveAttachmentIDFromInput(c, attachmentresolver.Params{
|
||||
RawFileID: &raw,
|
||||
FileManager: &fileManagerAttachmentHandlerServiceMock{},
|
||||
}, "Update failed")
|
||||
if !handled {
|
||||
t.Fatalf("expected handled invalid file")
|
||||
}
|
||||
return nil
|
||||
})
|
||||
app.Get("/cannot-resolve", func(c *fiber.Ctx) error {
|
||||
raw := mustUUIDStringContact(t, uuidv7.MustBytes())
|
||||
_, handled := resolveAttachmentIDFromInput(c, attachmentresolver.Params{
|
||||
RawFileID: &raw,
|
||||
FileManager: &fileManagerAttachmentHandlerServiceMock{
|
||||
createAttachmentFn: func(context.Context, filemanager.CreateAttachmentParams) (*filemanager.Attachment, error) {
|
||||
return nil, filemanager.ErrAttachmentAlreadyExists
|
||||
},
|
||||
listAttachmentsFn: func(context.Context, filemanager.ListAttachmentsParams) ([]filemanager.Attachment, int64, error) {
|
||||
return nil, 0, nil
|
||||
},
|
||||
},
|
||||
}, "Update failed")
|
||||
if !handled {
|
||||
t.Fatalf("expected handled cannot resolve")
|
||||
}
|
||||
return nil
|
||||
})
|
||||
app.Get("/not-ready", func(c *fiber.Ctx) error {
|
||||
raw := mustUUIDStringContact(t, uuidv7.MustBytes())
|
||||
_, handled := resolveAttachmentIDFromInput(c, attachmentresolver.Params{
|
||||
RawFileID: &raw,
|
||||
FileManager: &fileManagerAttachmentHandlerServiceMock{
|
||||
createAttachmentFn: func(context.Context, filemanager.CreateAttachmentParams) (*filemanager.Attachment, error) {
|
||||
return nil, filemanager.ErrFileNotReadyForAttachment
|
||||
},
|
||||
getFileFn: func(context.Context, []byte) (*filemanager.File, error) {
|
||||
return &filemanager.File{Status: filemanager.FileStatusUploaded}, nil
|
||||
},
|
||||
},
|
||||
}, "Update failed")
|
||||
if !handled {
|
||||
t.Fatalf("expected handled file-not-ready")
|
||||
}
|
||||
return nil
|
||||
})
|
||||
app.Get("/bad-request", func(c *fiber.Ctx) error {
|
||||
raw := mustUUIDStringContact(t, uuidv7.MustBytes())
|
||||
_, handled := resolveAttachmentIDFromInput(c, attachmentresolver.Params{
|
||||
RawAttachmentID: &raw,
|
||||
ValidateAttachmentExists: true,
|
||||
AttachmentExists: func(context.Context, []byte) (bool, error) {
|
||||
return false, errors.New("db down")
|
||||
},
|
||||
}, "Update failed")
|
||||
if !handled {
|
||||
t.Fatalf("expected handled bad request")
|
||||
}
|
||||
return nil
|
||||
})
|
||||
|
||||
for _, path := range []string{"/invalid-attachment", "/invalid-file", "/cannot-resolve", "/not-ready", "/bad-request"} {
|
||||
resp, err := app.Test(httptest.NewRequest(http.MethodGet, path, nil), 30000)
|
||||
if err != nil {
|
||||
t.Fatalf("%s err: %v", path, err)
|
||||
}
|
||||
if resp.StatusCode == http.StatusOK {
|
||||
t.Fatalf("expected non-200 for %s", path)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestOptionalStringHelpers(t *testing.T) {
|
||||
if hasNonEmptyOptionalString(nil) || hasBlankOptionalString(nil) {
|
||||
t.Fatalf("nil optional helpers should be false")
|
||||
}
|
||||
blank := " "
|
||||
if hasNonEmptyOptionalString(&blank) || !hasBlankOptionalString(&blank) {
|
||||
t.Fatalf("blank optional helper mismatch")
|
||||
}
|
||||
val := "x"
|
||||
if !hasNonEmptyOptionalString(&val) || hasBlankOptionalString(&val) {
|
||||
t.Fatalf("non-blank optional helper mismatch")
|
||||
}
|
||||
if fieldOrDefault("", "a") != "a" || fieldOrDefault("x", "a") != "x" {
|
||||
t.Fatalf("fieldOrDefault mismatch")
|
||||
}
|
||||
}
|
||||
93
internal/transport/http/handlers/audit_fields.go
Normal file
93
internal/transport/http/handlers/audit_fields.go
Normal file
@@ -0,0 +1,93 @@
|
||||
package handlers
|
||||
|
||||
import (
|
||||
"context"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/gofiber/fiber/v2"
|
||||
|
||||
"wucher/internal/shared/pkg/userctx"
|
||||
"wucher/internal/shared/pkg/uuidv7"
|
||||
responsedto "wucher/internal/transport/http/dto/response"
|
||||
)
|
||||
|
||||
func userRef(id []byte, name string) *responsedto.UserRef {
|
||||
trimmed := strings.TrimSpace(name)
|
||||
if len(id) == 0 && trimmed == "" {
|
||||
return nil
|
||||
}
|
||||
if trimmed == "" && len(id) > 0 {
|
||||
trimmed = userctx.GetDisplayName(context.TODO(), id)
|
||||
}
|
||||
return &responsedto.UserRef{
|
||||
UUID: idString(id),
|
||||
Name: trimmed,
|
||||
}
|
||||
}
|
||||
|
||||
// updatedAuditOrCreated returns the "last modified" timestamp (RFC3339) and actor for an
|
||||
// audited row, falling back to the creation timestamp/actor when the row was never
|
||||
// updated after creation (updated_by unset — e.g. seed/legacy rows). This guarantees an
|
||||
// updated_at/updated_by is always presentable instead of silently omitted.
|
||||
func updatedAuditOrCreated(updatedAt, createdAt time.Time, updatedBy []byte, updatedByName string, createdBy []byte, createdByName string) (string, *responsedto.UserRef) {
|
||||
if len(updatedBy) == 0 {
|
||||
updatedBy, updatedByName = createdBy, createdByName
|
||||
}
|
||||
if updatedAt.IsZero() {
|
||||
updatedAt = createdAt
|
||||
}
|
||||
at := ""
|
||||
if !updatedAt.IsZero() {
|
||||
at = updatedAt.UTC().Format(time.RFC3339)
|
||||
}
|
||||
return at, userRef(updatedBy, updatedByName)
|
||||
}
|
||||
|
||||
func actorUserID(c *fiber.Ctx) []byte {
|
||||
raw := c.Locals("user_id")
|
||||
userID, ok := raw.([]byte)
|
||||
if !ok || len(userID) == 0 {
|
||||
return nil
|
||||
}
|
||||
// Return a copy to avoid accidental mutation of local context values.
|
||||
return append([]byte(nil), userID...)
|
||||
}
|
||||
|
||||
// uuidStringOrEmpty resolves a user ID to their last name using the global userctx getter
|
||||
// if set; otherwise falls back to the UUID string representation.
|
||||
func uuidStringOrEmpty(id []byte) string {
|
||||
if len(id) == 0 {
|
||||
return ""
|
||||
}
|
||||
if name := userctx.GetDisplayName(context.TODO(), id); name != "" {
|
||||
return name
|
||||
}
|
||||
// Fallback to UUID string when no getter is available or user not found.
|
||||
val, err := uuidv7.BytesToString(id)
|
||||
if err != nil {
|
||||
return ""
|
||||
}
|
||||
return val
|
||||
}
|
||||
|
||||
// idString converts a resource ID byte slice to a UUID string.
|
||||
// Unlike uuidStringOrEmpty, this does not attempt user name resolution.
|
||||
// Use this for primary keys and resource IDs, not for audit/user reference fields.
|
||||
func idString(id []byte) string {
|
||||
if len(id) == 0 {
|
||||
return ""
|
||||
}
|
||||
val, err := uuidv7.BytesToString(id)
|
||||
if err != nil {
|
||||
return ""
|
||||
}
|
||||
return val
|
||||
}
|
||||
|
||||
func timeStringOrEmpty(t *time.Time) string {
|
||||
if t == nil || t.IsZero() {
|
||||
return ""
|
||||
}
|
||||
return t.UTC().Format(time.RFC3339)
|
||||
}
|
||||
359
internal/transport/http/handlers/audit_handler.go
Normal file
359
internal/transport/http/handlers/audit_handler.go
Normal file
@@ -0,0 +1,359 @@
|
||||
package handlers
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"slices"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/gofiber/fiber/v2"
|
||||
|
||||
"wucher/internal/domain/audit"
|
||||
"wucher/internal/service"
|
||||
sharedconst "wucher/internal/shared/const"
|
||||
"wucher/internal/shared/pkg/uuidv7"
|
||||
"wucher/internal/transport/http/dto"
|
||||
"wucher/internal/transport/http/jsonapi"
|
||||
"wucher/internal/transport/http/response"
|
||||
)
|
||||
|
||||
type AuditHandler struct {
|
||||
svc *service.AuditLogService
|
||||
}
|
||||
|
||||
func NewAuditHandler(svc *service.AuditLogService) *AuditHandler {
|
||||
return &AuditHandler{svc: svc}
|
||||
}
|
||||
|
||||
// ListAuditLogs godoc
|
||||
// @Summary List audit logs
|
||||
// @Description JSON:API list with pagination, filtering and sorting.
|
||||
// @Tags Audit Logs
|
||||
// @Produce json
|
||||
// @Param filter[search] query string false "Search by request_id/layer/action/method/path/message (contains)"
|
||||
// @Param filter[method] query string false "Method filter; single/comma-separated values or ALL/*" Enums(POST,PUT,PATCH,DELETE,UPDATE,ALL,*)
|
||||
// @Param method query string false "Alias for filter[method]" Enums(POST,PUT,PATCH,DELETE,UPDATE,ALL,*)
|
||||
// @Param filter[module] query string false "Module filter; single/comma-separated values or ALL/*" Enums(air_rescuer_checklist,audit_log,auth,base,base_regular,base_hems,base_roster,branding,contact,duty_roster,facility,federal_state,file_manager,flight,flight_data,forces_present,health_insurance_company,helicopter,helicopter_file,hems_roster,hospital,icao,insurance_patient_data,land,master_setting,medicine,mission,opc,patient_data,reserve_ac,role,user,vocation,unknown,ALL,*)
|
||||
// @Param module query string false "Alias for filter[module]" Enums(air_rescuer_checklist,audit_log,auth,base,base_regular,base_hems,base_roster,branding,contact,duty_roster,facility,federal_state,file_manager,flight,flight_data,forces_present,health_insurance_company,helicopter,helicopter_file,hems_roster,hospital,icao,insurance_patient_data,land,master_setting,medicine,mission,opc,patient_data,reserve_ac,role,user,vocation,unknown,ALL,*)
|
||||
// @Param filter[action] query string false "Action filter; single/comma-separated values or ALL/*" Enums(list,get,create,update,delete,export,custom,ALL,*)
|
||||
// @Param action query string false "Alias for filter[action]" Enums(list,get,create,update,delete,export,custom,ALL,*)
|
||||
// @Param page[number] query int false "Page number (default 1)"
|
||||
// @Param page[size] query int false "Page size (default 20, max 200)"
|
||||
// @Param sort query string false "Sort order" Enums(created_at,-created_at,module,-module,action,-action,layer,-layer,status_code,-status_code,success,-success)
|
||||
// @Success 200 {object} jsonapi.Document
|
||||
// @Failure 400 {object} dto.ErrorResponse
|
||||
// @Failure 422 {object} dto.ErrorResponse
|
||||
// @Router /api/v1/audit-logs/get-all [get]
|
||||
func (h *AuditHandler) List(c *fiber.Ctx) error {
|
||||
filter := c.Query("filter[search]")
|
||||
if filter == "" {
|
||||
filter = c.Query("filter[action]")
|
||||
}
|
||||
if filter == "" {
|
||||
filter = c.Query("filter[layer]")
|
||||
}
|
||||
methodFilter := c.Query("filter[method]")
|
||||
if methodFilter == "" {
|
||||
methodFilter = c.Query("method")
|
||||
}
|
||||
methods := parseAuditMethodFilter(methodFilter)
|
||||
moduleFilter := c.Query("filter[module]")
|
||||
if moduleFilter == "" {
|
||||
moduleFilter = c.Query("module")
|
||||
}
|
||||
modules := parseAuditTokenFilter(moduleFilter)
|
||||
actionFilter := c.Query("filter[action]")
|
||||
if actionFilter == "" {
|
||||
actionFilter = c.Query("action")
|
||||
}
|
||||
actions := parseAuditTokenFilter(actionFilter)
|
||||
|
||||
pageNumber := parseIntDefault(c.Query("page[number]"), 1)
|
||||
pageSize := parseIntDefault(c.Query("page[size]"), 20)
|
||||
if pageSize > 200 {
|
||||
pageSize = 200
|
||||
}
|
||||
if pageNumber < 1 {
|
||||
pageNumber = 1
|
||||
}
|
||||
|
||||
offset := (pageNumber - 1) * pageSize
|
||||
_ = offset
|
||||
sort := c.Query("sort")
|
||||
if errObj := validateAuditListQuery(methods, modules, actions, sort); errObj != nil {
|
||||
return response.WriteErrors(c, fiber.StatusUnprocessableEntity, []jsonapi.ErrorObject{*errObj})
|
||||
}
|
||||
|
||||
logs, total, err := h.svc.List(c.UserContext(), filter, methods, modules, actions, sort, 0, 0)
|
||||
if err != nil {
|
||||
return response.WriteErrors(c, fiber.StatusBadRequest, []jsonapi.ErrorObject{{
|
||||
Status: "400",
|
||||
Title: "List failed",
|
||||
Detail: safeInternalDetail(),
|
||||
}})
|
||||
}
|
||||
|
||||
data := make([]dto.AuditLogResource, 0, len(logs))
|
||||
for i := range logs {
|
||||
data = append(data, auditLogResource(&logs[i]))
|
||||
}
|
||||
|
||||
meta := map[string]any{
|
||||
"total": total,
|
||||
}
|
||||
return response.WriteWithMeta(c, fiber.StatusOK, data, meta)
|
||||
}
|
||||
|
||||
func parseAuditMethodFilter(raw string) []string {
|
||||
v := strings.TrimSpace(raw)
|
||||
if v == "" {
|
||||
return nil
|
||||
}
|
||||
parts := strings.Split(v, ",")
|
||||
out := make([]string, 0, len(parts))
|
||||
for _, part := range parts {
|
||||
token := strings.TrimSpace(part)
|
||||
if token == "" {
|
||||
continue
|
||||
}
|
||||
out = append(out, token)
|
||||
}
|
||||
return out
|
||||
}
|
||||
|
||||
func auditLogResource(v *audit.AuditLog) dto.AuditLogResource {
|
||||
id, _ := uuidv7.BytesToString(v.ID)
|
||||
actorID := ""
|
||||
if len(v.ActorUserID) == 16 {
|
||||
actorID, _ = uuidv7.BytesToString(v.ActorUserID)
|
||||
}
|
||||
|
||||
var metadata any
|
||||
if len(v.Metadata) > 0 {
|
||||
_ = json.Unmarshal(v.Metadata, &metadata)
|
||||
}
|
||||
module := strings.TrimSpace(v.Module)
|
||||
if module == "" {
|
||||
module = inferAuditModuleFromPath(strings.TrimSpace(v.Path))
|
||||
}
|
||||
|
||||
return dto.AuditLogResource{
|
||||
Type: "audit_log",
|
||||
ID: id,
|
||||
Attributes: dto.AuditLogAttributes{
|
||||
RequestID: strings.TrimSpace(v.RequestID),
|
||||
ActorUserID: actorID,
|
||||
Layer: strings.TrimSpace(v.Layer),
|
||||
Module: module,
|
||||
Action: strings.TrimSpace(v.Action),
|
||||
Method: strings.TrimSpace(v.Method),
|
||||
Path: strings.TrimSpace(v.Path),
|
||||
StatusCode: v.StatusCode,
|
||||
Success: v.Success,
|
||||
IP: strings.TrimSpace(v.IP),
|
||||
UserAgent: strings.TrimSpace(v.UserAgent),
|
||||
Message: strings.TrimSpace(v.Message),
|
||||
Metadata: metadata,
|
||||
CreatedAt: v.CreatedAt.UTC().Format(time.RFC3339),
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func inferAuditModuleFromPath(path string) string {
|
||||
parts := strings.Split(strings.Trim(strings.ToLower(strings.TrimSpace(path)), "/"), "/")
|
||||
if len(parts) < 3 || parts[0] != "api" || parts[1] != "v1" {
|
||||
return sharedconst.AuditModuleUnknown
|
||||
}
|
||||
switch parts[2] {
|
||||
case "air-rescuer-checklist":
|
||||
return sharedconst.AuditModuleAirRescuerChecklist
|
||||
case "audit-logs":
|
||||
return sharedconst.AuditModuleAuditLog
|
||||
case "auth":
|
||||
return sharedconst.AuditModuleAuth
|
||||
case "bases":
|
||||
return sharedconst.AuditModuleBase
|
||||
case "branding", "public":
|
||||
return sharedconst.AuditModuleBranding
|
||||
case "contacts":
|
||||
return sharedconst.AuditModuleContact
|
||||
case "duty-roster":
|
||||
return sharedconst.AuditModuleDutyRoster
|
||||
case "dul":
|
||||
return sharedconst.AuditModuleDUL
|
||||
case "facilities":
|
||||
return sharedconst.AuditModuleFacility
|
||||
case "federal-state":
|
||||
return sharedconst.AuditModuleFederalState
|
||||
case "file-manager":
|
||||
return sharedconst.AuditModuleFileManager
|
||||
case "flight-data":
|
||||
return sharedconst.AuditModuleFlightData
|
||||
case "flights":
|
||||
return sharedconst.AuditModuleFlight
|
||||
case "forces-present":
|
||||
return sharedconst.AuditModuleForcesPresent
|
||||
case "health-insurance-companies":
|
||||
return sharedconst.AuditModuleHealthInsuranceCompany
|
||||
case "helicopter-files":
|
||||
return sharedconst.AuditModuleHelicopterFile
|
||||
case "helicopters":
|
||||
return sharedconst.AuditModuleHelicopter
|
||||
case "hems-operation", "hems-operation-category", "hems-operational-data":
|
||||
return sharedconst.AuditModuleUnknown
|
||||
case "hospital":
|
||||
return sharedconst.AuditModuleHospital
|
||||
case "icao":
|
||||
return sharedconst.AuditModuleICAO
|
||||
case "insurance-patient-data":
|
||||
return sharedconst.AuditModuleInsurancePatientData
|
||||
case "land":
|
||||
return sharedconst.AuditModuleLand
|
||||
case "master-settings":
|
||||
return sharedconst.AuditModuleMasterSetting
|
||||
case "medicine":
|
||||
return sharedconst.AuditModuleMedicine
|
||||
case "mission":
|
||||
return sharedconst.AuditModuleMission
|
||||
case "opc":
|
||||
return sharedconst.AuditModuleOPC
|
||||
case "patient-data":
|
||||
return sharedconst.AuditModulePatientData
|
||||
case "reserve-acs":
|
||||
return sharedconst.AuditModuleReserveAC
|
||||
case "roles":
|
||||
return sharedconst.AuditModuleRole
|
||||
case "users":
|
||||
return sharedconst.AuditModuleUser
|
||||
case "vocation":
|
||||
return sharedconst.AuditModuleVocation
|
||||
default:
|
||||
return sharedconst.AuditModuleUnknown
|
||||
}
|
||||
}
|
||||
|
||||
func parseAuditTokenFilter(raw string) []string {
|
||||
v := strings.TrimSpace(raw)
|
||||
if v == "" {
|
||||
return nil
|
||||
}
|
||||
parts := strings.Split(v, ",")
|
||||
out := make([]string, 0, len(parts))
|
||||
for _, part := range parts {
|
||||
token := strings.TrimSpace(part)
|
||||
if token == "" {
|
||||
continue
|
||||
}
|
||||
out = append(out, token)
|
||||
}
|
||||
return out
|
||||
}
|
||||
|
||||
func validateAuditListQuery(methods, modules, actions []string, sort string) *jsonapi.ErrorObject {
|
||||
for _, method := range methods {
|
||||
v := strings.ToUpper(strings.TrimSpace(method))
|
||||
if v == "" || v == "ALL" || v == "*" {
|
||||
continue
|
||||
}
|
||||
if !slices.Contains([]string{"POST", "PUT", "PATCH", "DELETE", "UPDATE"}, v) {
|
||||
return &jsonapi.ErrorObject{
|
||||
Status: "422",
|
||||
Title: "Validation error",
|
||||
Detail: fmt.Sprintf("invalid method filter: %s", method),
|
||||
Source: &jsonapi.ErrorSource{Pointer: "/query/method"},
|
||||
}
|
||||
}
|
||||
}
|
||||
validModules := []string{
|
||||
sharedconst.AuditModuleAirRescuerChecklist,
|
||||
sharedconst.AuditModuleAuditLog,
|
||||
sharedconst.AuditModuleAuth,
|
||||
sharedconst.AuditModuleBase,
|
||||
sharedconst.AuditModuleBaseRegular,
|
||||
sharedconst.AuditModuleBaseHEMS,
|
||||
sharedconst.AuditModuleBranding,
|
||||
sharedconst.AuditModuleContact,
|
||||
sharedconst.AuditModuleDutyRoster,
|
||||
sharedconst.AuditModuleDUL,
|
||||
sharedconst.AuditModuleFacility,
|
||||
sharedconst.AuditModuleFederalState,
|
||||
sharedconst.AuditModuleFileManager,
|
||||
sharedconst.AuditModuleFlightData,
|
||||
sharedconst.AuditModuleFlight,
|
||||
sharedconst.AuditModuleForcesPresent,
|
||||
sharedconst.AuditModuleHealthInsuranceCompany,
|
||||
sharedconst.AuditModuleHelicopterFile,
|
||||
sharedconst.AuditModuleHelicopter,
|
||||
sharedconst.AuditModuleHospital,
|
||||
sharedconst.AuditModuleICAO,
|
||||
sharedconst.AuditModuleInsurancePatientData,
|
||||
sharedconst.AuditModuleLand,
|
||||
sharedconst.AuditModuleMasterSetting,
|
||||
sharedconst.AuditModuleMedicine,
|
||||
sharedconst.AuditModuleMission,
|
||||
sharedconst.AuditModuleOPC,
|
||||
sharedconst.AuditModulePatientData,
|
||||
sharedconst.AuditModuleReserveAC,
|
||||
sharedconst.AuditModuleRole,
|
||||
sharedconst.AuditModuleUser,
|
||||
sharedconst.AuditModuleVocation,
|
||||
sharedconst.AuditModuleUnknown,
|
||||
}
|
||||
for _, module := range modules {
|
||||
v := strings.ToLower(strings.TrimSpace(module))
|
||||
if v == "" || v == "all" || v == "*" {
|
||||
continue
|
||||
}
|
||||
if !slices.Contains(validModules, v) {
|
||||
return &jsonapi.ErrorObject{
|
||||
Status: "422",
|
||||
Title: "Validation error",
|
||||
Detail: fmt.Sprintf("invalid module filter: %s", module),
|
||||
Source: &jsonapi.ErrorSource{Pointer: "/query/module"},
|
||||
}
|
||||
}
|
||||
}
|
||||
validActions := []string{
|
||||
sharedconst.AuditActionList,
|
||||
sharedconst.AuditActionGet,
|
||||
sharedconst.AuditActionCreate,
|
||||
sharedconst.AuditActionUpdate,
|
||||
sharedconst.AuditActionDelete,
|
||||
sharedconst.AuditActionExport,
|
||||
sharedconst.AuditActionCustom,
|
||||
}
|
||||
for _, action := range actions {
|
||||
v := strings.ToLower(strings.TrimSpace(action))
|
||||
if v == "" || v == "all" || v == "*" {
|
||||
continue
|
||||
}
|
||||
if !slices.Contains(validActions, v) {
|
||||
return &jsonapi.ErrorObject{
|
||||
Status: "422",
|
||||
Title: "Validation error",
|
||||
Detail: fmt.Sprintf("invalid action filter: %s", action),
|
||||
Source: &jsonapi.ErrorSource{Pointer: "/query/action"},
|
||||
}
|
||||
}
|
||||
}
|
||||
if strings.TrimSpace(sort) != "" {
|
||||
allowed := map[string]struct{}{
|
||||
"created_at": {}, "-created_at": {},
|
||||
"module": {}, "-module": {},
|
||||
"action": {}, "-action": {},
|
||||
"layer": {}, "-layer": {},
|
||||
"status_code": {}, "-status_code": {},
|
||||
"success": {}, "-success": {},
|
||||
}
|
||||
if _, ok := allowed[strings.TrimSpace(sort)]; !ok {
|
||||
return &jsonapi.ErrorObject{
|
||||
Status: "422",
|
||||
Title: "Validation error",
|
||||
Detail: fmt.Sprintf("invalid sort: %s", sort),
|
||||
Source: &jsonapi.ErrorSource{Pointer: "/query/sort"},
|
||||
}
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
286
internal/transport/http/handlers/audit_handler_test.go
Normal file
286
internal/transport/http/handlers/audit_handler_test.go
Normal file
@@ -0,0 +1,286 @@
|
||||
package handlers
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"io"
|
||||
"net/http"
|
||||
"strings"
|
||||
"sync"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/gofiber/fiber/v2"
|
||||
|
||||
"wucher/internal/domain/audit"
|
||||
"wucher/internal/service"
|
||||
"wucher/internal/shared/pkg/uuidv7"
|
||||
)
|
||||
|
||||
type mockAuditListRepo struct {
|
||||
mu sync.Mutex
|
||||
rows []audit.AuditLog
|
||||
total int64
|
||||
err error
|
||||
lastFilter string
|
||||
lastMethods []string
|
||||
lastModules []string
|
||||
lastActions []string
|
||||
lastSort string
|
||||
lastLimit int
|
||||
lastOffset int
|
||||
}
|
||||
|
||||
func (m *mockAuditListRepo) Create(_ context.Context, _ *audit.AuditLog) error { return nil }
|
||||
|
||||
func (m *mockAuditListRepo) List(_ context.Context, filter string, methods []string, modules []string, actions []string, sort string, limit, offset int) ([]audit.AuditLog, int64, error) {
|
||||
m.mu.Lock()
|
||||
defer m.mu.Unlock()
|
||||
m.lastFilter = filter
|
||||
m.lastMethods = append([]string(nil), methods...)
|
||||
m.lastModules = append([]string(nil), modules...)
|
||||
m.lastActions = append([]string(nil), actions...)
|
||||
m.lastSort = sort
|
||||
m.lastLimit = limit
|
||||
m.lastOffset = offset
|
||||
if m.err != nil {
|
||||
return nil, 0, m.err
|
||||
}
|
||||
out := make([]audit.AuditLog, len(m.rows))
|
||||
copy(out, m.rows)
|
||||
return out, m.total, nil
|
||||
}
|
||||
|
||||
func TestAuditHandler_List(t *testing.T) {
|
||||
repo := &mockAuditListRepo{
|
||||
rows: []audit.AuditLog{
|
||||
{
|
||||
ID: uuidv7.MustBytes(),
|
||||
Layer: "api",
|
||||
Module: "users",
|
||||
Action: "list",
|
||||
Method: "GET",
|
||||
Path: "/api/v1/users/get-all",
|
||||
StatusCode: 200,
|
||||
Success: true,
|
||||
IP: "127.0.0.1",
|
||||
CreatedAt: time.Now().UTC(),
|
||||
},
|
||||
},
|
||||
total: 1,
|
||||
}
|
||||
svc := service.NewAuditLogService(repo, service.AuditLogServiceDependencies{QueueSize: 8, Workers: 1})
|
||||
|
||||
h := NewAuditHandler(svc)
|
||||
app := fiber.New()
|
||||
app.Get("/api/v1/audit-logs/get-all", h.List)
|
||||
|
||||
req, _ := http.NewRequest(http.MethodGet, "/api/v1/audit-logs/get-all?filter[search]=http&method=UPDATE&filter[module]=user&filter[action]=list&sort=-created_at&page[number]=1&page[size]=20", nil)
|
||||
resp, err := app.Test(req)
|
||||
if err != nil {
|
||||
t.Fatalf("app.Test: %v", err)
|
||||
}
|
||||
if resp.StatusCode != http.StatusOK {
|
||||
t.Fatalf("expected 200, got %d", resp.StatusCode)
|
||||
}
|
||||
|
||||
body, _ := io.ReadAll(resp.Body)
|
||||
bodyStr := string(body)
|
||||
if !containsAll(bodyStr, `"type":"audit_log"`, `"total":1`, `"ip":"127.0.0.1"`) {
|
||||
t.Fatalf("unexpected body: %s", bodyStr)
|
||||
}
|
||||
|
||||
repo.mu.Lock()
|
||||
defer repo.mu.Unlock()
|
||||
if repo.lastFilter != "http" {
|
||||
t.Fatalf("expected filter to be propagated, got %q", repo.lastFilter)
|
||||
}
|
||||
if repo.lastSort != "created_at DESC" {
|
||||
t.Fatalf("expected normalized sort, got %q", repo.lastSort)
|
||||
}
|
||||
if len(repo.lastMethods) != 2 || repo.lastMethods[0] != "PUT" || repo.lastMethods[1] != "PATCH" {
|
||||
t.Fatalf("expected method filter [PUT PATCH], got %#v", repo.lastMethods)
|
||||
}
|
||||
if len(repo.lastModules) != 1 || repo.lastModules[0] != "user" {
|
||||
t.Fatalf("expected module filter [user], got %#v", repo.lastModules)
|
||||
}
|
||||
if len(repo.lastActions) != 3 || repo.lastActions[0] != "create" || repo.lastActions[1] != "update" || repo.lastActions[2] != "delete" {
|
||||
t.Fatalf("expected action filter [create update delete], got %#v", repo.lastActions)
|
||||
}
|
||||
if repo.lastLimit != 0 || repo.lastOffset != 0 {
|
||||
t.Fatalf("unexpected paging limit=%d offset=%d", repo.lastLimit, repo.lastOffset)
|
||||
}
|
||||
}
|
||||
|
||||
func containsAll(v string, terms ...string) bool {
|
||||
for i := range terms {
|
||||
if !strings.Contains(v, terms[i]) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
func TestAuditHandler_List_Branches(t *testing.T) {
|
||||
t.Run("fallback filter action and clamp page", func(t *testing.T) {
|
||||
repo := &mockAuditListRepo{
|
||||
rows: []audit.AuditLog{{ID: uuidv7.MustBytes(), Action: "create", CreatedAt: time.Now().UTC()}},
|
||||
total: 1,
|
||||
}
|
||||
svc := service.NewAuditLogService(repo, service.AuditLogServiceDependencies{QueueSize: 8, Workers: 1})
|
||||
h := NewAuditHandler(svc)
|
||||
app := fiber.New()
|
||||
app.Get("/api/v1/audit-logs/get-all", h.List)
|
||||
|
||||
req, _ := http.NewRequest(http.MethodGet, "/api/v1/audit-logs/get-all?filter[action]=create&page[number]=0&page[size]=500&sort=action", nil)
|
||||
resp, err := app.Test(req)
|
||||
if err != nil {
|
||||
t.Fatalf("app.Test: %v", err)
|
||||
}
|
||||
if resp.StatusCode != http.StatusOK {
|
||||
t.Fatalf("expected 200, got %d", resp.StatusCode)
|
||||
}
|
||||
|
||||
repo.mu.Lock()
|
||||
defer repo.mu.Unlock()
|
||||
if repo.lastFilter != "create" {
|
||||
t.Fatalf("expected action fallback filter, got %q", repo.lastFilter)
|
||||
}
|
||||
if repo.lastSort != "action ASC" {
|
||||
t.Fatalf("expected normalized sort, got %q", repo.lastSort)
|
||||
}
|
||||
if repo.lastLimit != 0 || repo.lastOffset != 0 {
|
||||
t.Fatalf("unexpected paging limit=%d offset=%d", repo.lastLimit, repo.lastOffset)
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("fallback filter layer", func(t *testing.T) {
|
||||
repo := &mockAuditListRepo{
|
||||
rows: []audit.AuditLog{{ID: uuidv7.MustBytes(), Layer: "service", CreatedAt: time.Now().UTC()}},
|
||||
total: 1,
|
||||
}
|
||||
svc := service.NewAuditLogService(repo, service.AuditLogServiceDependencies{QueueSize: 8, Workers: 1})
|
||||
h := NewAuditHandler(svc)
|
||||
app := fiber.New()
|
||||
app.Get("/api/v1/audit-logs/get-all", h.List)
|
||||
|
||||
req, _ := http.NewRequest(http.MethodGet, "/api/v1/audit-logs/get-all?filter[layer]=service&page[number]=1&page[size]=20", nil)
|
||||
resp, err := app.Test(req)
|
||||
if err != nil {
|
||||
t.Fatalf("app.Test: %v", err)
|
||||
}
|
||||
if resp.StatusCode != http.StatusOK {
|
||||
t.Fatalf("expected 200, got %d", resp.StatusCode)
|
||||
}
|
||||
|
||||
repo.mu.Lock()
|
||||
defer repo.mu.Unlock()
|
||||
if repo.lastFilter != "service" {
|
||||
t.Fatalf("expected layer fallback filter, got %q", repo.lastFilter)
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("list error", func(t *testing.T) {
|
||||
repo := &mockAuditListRepo{err: errors.New("list failed")}
|
||||
svc := service.NewAuditLogService(repo, service.AuditLogServiceDependencies{QueueSize: 8, Workers: 1})
|
||||
h := NewAuditHandler(svc)
|
||||
app := fiber.New()
|
||||
app.Get("/api/v1/audit-logs/get-all", h.List)
|
||||
|
||||
req, _ := http.NewRequest(http.MethodGet, "/api/v1/audit-logs/get-all", nil)
|
||||
resp, err := app.Test(req)
|
||||
if err != nil {
|
||||
t.Fatalf("app.Test: %v", err)
|
||||
}
|
||||
if resp.StatusCode != http.StatusBadRequest {
|
||||
t.Fatalf("expected 400, got %d", resp.StatusCode)
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("default mutation filter applied when query is empty", func(t *testing.T) {
|
||||
repo := &mockAuditListRepo{
|
||||
rows: []audit.AuditLog{{ID: uuidv7.MustBytes(), Action: "update", Method: "PATCH", CreatedAt: time.Now().UTC()}},
|
||||
total: 1,
|
||||
}
|
||||
svc := service.NewAuditLogService(repo, service.AuditLogServiceDependencies{QueueSize: 8, Workers: 1})
|
||||
h := NewAuditHandler(svc)
|
||||
app := fiber.New()
|
||||
app.Get("/api/v1/audit-logs/get-all", h.List)
|
||||
|
||||
req, _ := http.NewRequest(http.MethodGet, "/api/v1/audit-logs/get-all", nil)
|
||||
resp, err := app.Test(req)
|
||||
if err != nil {
|
||||
t.Fatalf("app.Test: %v", err)
|
||||
}
|
||||
if resp.StatusCode != http.StatusOK {
|
||||
t.Fatalf("expected 200, got %d", resp.StatusCode)
|
||||
}
|
||||
|
||||
repo.mu.Lock()
|
||||
defer repo.mu.Unlock()
|
||||
if len(repo.lastMethods) != 4 || repo.lastMethods[0] != "POST" || repo.lastMethods[1] != "PUT" || repo.lastMethods[2] != "PATCH" || repo.lastMethods[3] != "DELETE" {
|
||||
t.Fatalf("expected default method filter [POST PUT PATCH DELETE], got %#v", repo.lastMethods)
|
||||
}
|
||||
if len(repo.lastActions) != 3 || repo.lastActions[0] != "create" || repo.lastActions[1] != "update" || repo.lastActions[2] != "delete" {
|
||||
t.Fatalf("expected default action filter [create update delete], got %#v", repo.lastActions)
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("invalid module query returns 422", func(t *testing.T) {
|
||||
repo := &mockAuditListRepo{}
|
||||
svc := service.NewAuditLogService(repo, service.AuditLogServiceDependencies{QueueSize: 8, Workers: 1})
|
||||
h := NewAuditHandler(svc)
|
||||
app := fiber.New()
|
||||
app.Get("/api/v1/audit-logs/get-all", h.List)
|
||||
|
||||
req, _ := http.NewRequest(http.MethodGet, "/api/v1/audit-logs/get-all?module=bad-module", nil)
|
||||
resp, err := app.Test(req)
|
||||
if err != nil {
|
||||
t.Fatalf("app.Test: %v", err)
|
||||
}
|
||||
if resp.StatusCode != http.StatusUnprocessableEntity {
|
||||
t.Fatalf("expected 422, got %d", resp.StatusCode)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
func TestAuditLogResource_Branches(t *testing.T) {
|
||||
now := time.Now().UTC().Truncate(time.Second)
|
||||
row := &audit.AuditLog{
|
||||
ID: uuidv7.MustBytes(),
|
||||
RequestID: " req-1 ",
|
||||
ActorUserID: uuidv7.MustBytes(),
|
||||
Layer: " service ",
|
||||
Action: " create ",
|
||||
Method: " POST ",
|
||||
Path: " /api/v1/reserve-acs/get-all ",
|
||||
StatusCode: 201,
|
||||
Success: true,
|
||||
IP: " 127.0.0.1 ",
|
||||
UserAgent: " ua ",
|
||||
Message: " ok ",
|
||||
Metadata: []byte(`{"module":"auth","count":2}`),
|
||||
CreatedAt: now,
|
||||
}
|
||||
|
||||
res := auditLogResource(row)
|
||||
if res.Type != "audit_log" || res.ID == "" {
|
||||
t.Fatalf("expected audit resource type/id")
|
||||
}
|
||||
if res.Attributes.ActorUserID == "" {
|
||||
t.Fatalf("expected actor_user_id to be encoded")
|
||||
}
|
||||
if res.Attributes.RequestID != "req-1" || res.Attributes.Layer != "service" || res.Attributes.Action != "create" {
|
||||
t.Fatalf("expected trimmed string attributes")
|
||||
}
|
||||
if res.Attributes.Module != "reserve_ac" {
|
||||
t.Fatalf("expected inferred module reserve_ac, got %q", res.Attributes.Module)
|
||||
}
|
||||
md, ok := res.Attributes.Metadata.(map[string]any)
|
||||
if !ok || md["module"] != "auth" {
|
||||
t.Fatalf("expected metadata map decoded, got %#v", res.Attributes.Metadata)
|
||||
}
|
||||
if res.Attributes.CreatedAt == "" {
|
||||
t.Fatalf("expected created_at in resource")
|
||||
}
|
||||
}
|
||||
3608
internal/transport/http/handlers/auth_handler.go
Normal file
3608
internal/transport/http/handlers/auth_handler.go
Normal file
File diff suppressed because it is too large
Load Diff
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user