Files
imphnen-backend-service/imphnen-dimentorin/src/v1/sessions/sessions_controller.rs
T
MythEclipse 97c2fce7be Refactor API endpoints for consistency and clarity
- Updated route paths for hackathon submissions, notifications, registrations, and teams to include more descriptive actions (e.g., "update", "create", "delete").
- Removed deprecated routes and adjusted corresponding test cases to reflect new endpoint structures.
- Enhanced test scripts to ensure compatibility with updated API routes and improved error handling for OTP resend functionality.
- Adjusted server startup script for better Windows compatibility and streamlined process management.
2025-10-29 14:27:07 +07:00

298 lines
9.7 KiB
Rust

use super::{
BookSessionRequestDto, BookSessionResponseDto, MentorAvailabilityDto,
SessionFeedbackRequestDto, SessionFeedbackResponseDto, SessionListResponseDto,
SessionsService, UpdateSessionStatusRequestDto, UpdateSessionStatusResponseDto,
};
use axum::{
extract::{Extension, Path, Query},
http::HeaderMap,
response::Response,
routing::{get, post, put},
Json, Router,
};
use imphnen_libs::AppState;
use imphnen_utils::extract_email;
use serde::Deserialize;
use utoipa::OpenApi;
#[derive(OpenApi)]
#[openapi(
paths(
post_book_session,
get_mentor_sessions,
get_mentor_availability,
put_update_session_status,
post_submit_feedback,
get_my_sessions,
),
components(schemas(
BookSessionRequestDto,
BookSessionResponseDto,
SessionListResponseDto,
super::SessionListItemDto,
MentorAvailabilityDto,
super::AvailabilitySlotDto,
UpdateSessionStatusRequestDto,
UpdateSessionStatusResponseDto,
SessionFeedbackRequestDto,
SessionFeedbackResponseDto,
)),
tags(
(name = "sessions", description = "Mentoring Sessions Management API")
)
)]
pub struct SessionsApiDoc;
// ============================================
// Book Session
// ============================================
#[utoipa::path(
post,
path = "/v1/mentors/{id}/sessions/create",
tag = "sessions",
summary = "Book a mentoring session",
description = "Book a mentoring session with a specific mentor. Requires authentication.",
security(("Bearer" = [])),
params(
("id" = String, Path, description = "Mentor ID"),
),
request_body = BookSessionRequestDto,
responses(
(status = 201, description = "Session booked successfully", body = BookSessionResponseDto),
(status = 400, description = "Invalid request"),
(status = 401, description = "Unauthorized"),
(status = 404, description = "Mentor not found"),
)
)]
pub async fn post_book_session(
headers: HeaderMap,
Extension(state): Extension<AppState>,
Path(mentor_id): Path<String>,
Json(dto): Json<BookSessionRequestDto>,
) -> Response {
let user_email = match extract_email(&headers) {
Some(email) => email,
None => {
return imphnen_utils::common_response(
axum::http::StatusCode::UNAUTHORIZED,
"Token tidak valid",
);
}
};
SessionsService::book_session(&state, mentor_id, user_email, dto).await
}
// ============================================
// Get Mentor's Sessions
// ============================================
#[derive(Deserialize)]
pub struct SessionStatusFilter {
status: Option<String>,
}
#[utoipa::path(
get,
path = "/v1/mentors/{id}/sessions",
tag = "sessions",
summary = "List mentor's sessions",
description = "Get all sessions for a specific mentor. Only accessible by the mentor themselves or admin.",
security(("Bearer" = [])),
params(
("id" = String, Path, description = "Mentor ID"),
("status" = Option<String>, Query, description = "Filter by status (pending, confirmed, completed, cancelled, no_show)"),
),
responses(
(status = 200, description = "Sessions retrieved successfully", body = SessionListResponseDto),
(status = 401, description = "Unauthorized"),
(status = 403, description = "Forbidden"),
(status = 404, description = "Mentor not found"),
)
)]
pub async fn get_mentor_sessions(
headers: HeaderMap,
Extension(state): Extension<AppState>,
Path(mentor_id): Path<String>,
Query(filter): Query<SessionStatusFilter>,
) -> Response {
let user_email = match extract_email(&headers) {
Some(email) => email,
None => {
return imphnen_utils::common_response(
axum::http::StatusCode::UNAUTHORIZED,
"Token tidak valid",
);
}
};
SessionsService::get_mentor_sessions(&state, mentor_id, user_email, filter.status).await
}
// ============================================
// Get Mentor Availability
// ============================================
#[utoipa::path(
get,
path = "/v1/mentors/{id}/availability",
tag = "sessions",
summary = "Get mentor availability",
description = "Get available time slots for booking with a mentor. Public endpoint.",
params(
("id" = String, Path, description = "Mentor ID"),
),
responses(
(status = 200, description = "Availability retrieved successfully", body = MentorAvailabilityDto),
(status = 404, description = "Mentor not found"),
)
)]
pub async fn get_mentor_availability(
Extension(state): Extension<AppState>,
Path(mentor_id): Path<String>,
) -> Response {
SessionsService::get_mentor_availability(&state, mentor_id).await
}
// ============================================
// Update Session Status
// ============================================
#[utoipa::path(
put,
path = "/v1/sessions/update/{id}/status",
tag = "sessions",
summary = "Update session status",
description = "Update the status of a session (confirm, complete, cancel). Only accessible by the mentor.",
security(("Bearer" = [])),
params(
("id" = String, Path, description = "Session ID"),
),
request_body = UpdateSessionStatusRequestDto,
responses(
(status = 200, description = "Status updated successfully", body = UpdateSessionStatusResponseDto),
(status = 400, description = "Invalid request"),
(status = 401, description = "Unauthorized"),
(status = 403, description = "Forbidden"),
(status = 404, description = "Session not found"),
)
)]
pub async fn put_update_session_status(
headers: HeaderMap,
Extension(state): Extension<AppState>,
Path(session_id): Path<String>,
Json(dto): Json<UpdateSessionStatusRequestDto>,
) -> Response {
let user_email = match extract_email(&headers) {
Some(email) => email,
None => {
return imphnen_utils::common_response(
axum::http::StatusCode::UNAUTHORIZED,
"Token tidak valid",
);
}
};
SessionsService::update_session_status(&state, session_id, user_email, dto).await
}
// ============================================
// Submit Feedback
// ============================================
#[utoipa::path(
post,
path = "/v1/sessions/{id}/feedback/create",
tag = "sessions",
summary = "Submit session feedback",
description = "Submit feedback and rating for a completed session. Only accessible by the mentee.",
security(("Bearer" = [])),
params(
("id" = String, Path, description = "Session ID"),
),
request_body = SessionFeedbackRequestDto,
responses(
(status = 200, description = "Feedback submitted successfully", body = SessionFeedbackResponseDto),
(status = 400, description = "Invalid request or session not completed"),
(status = 401, description = "Unauthorized"),
(status = 403, description = "Forbidden"),
(status = 404, description = "Session not found"),
)
)]
pub async fn post_submit_feedback(
headers: HeaderMap,
Extension(state): Extension<AppState>,
Path(session_id): Path<String>,
Json(dto): Json<SessionFeedbackRequestDto>,
) -> Response {
let user_email = match extract_email(&headers) {
Some(email) => email,
None => {
return imphnen_utils::common_response(
axum::http::StatusCode::UNAUTHORIZED,
"Token tidak valid",
);
}
};
SessionsService::submit_feedback(&state, session_id, user_email, dto).await
}
// ============================================
// Get User's Sessions
// ============================================
#[utoipa::path(
get,
path = "/v1/users/me/sessions",
tag = "sessions",
summary = "Get my sessions",
description = "Get all sessions for the authenticated user (as mentee). Requires authentication.",
security(("Bearer" = [])),
params(
("status" = Option<String>, Query, description = "Filter by status (pending, confirmed, completed, cancelled, no_show)"),
),
responses(
(status = 200, description = "Sessions retrieved successfully", body = SessionListResponseDto),
(status = 401, description = "Unauthorized"),
)
)]
pub async fn get_my_sessions(
headers: HeaderMap,
Extension(state): Extension<AppState>,
Query(filter): Query<SessionStatusFilter>,
) -> Response {
let user_email = match extract_email(&headers) {
Some(email) => email,
None => {
return imphnen_utils::common_response(
axum::http::StatusCode::UNAUTHORIZED,
"Token tidak valid",
);
}
};
SessionsService::get_user_sessions(&state, user_email, filter.status).await
}
// ============================================
// Router
// ============================================
pub fn sessions_router() -> Router {
Router::new()
// Book session (under mentors path)
.route("/mentors/{id}/sessions/create", post(post_book_session))
// Get mentor's sessions
.route("/mentors/{id}/sessions", get(get_mentor_sessions))
// Get mentor availability (public - no auth)
.route("/mentors/{id}/availability", get(get_mentor_availability))
// Update session status
.route("/sessions/update/{id}/status", put(put_update_session_status))
// Submit feedback
.route("/sessions/{id}/feedback/create", post(post_submit_feedback))
// Get my sessions
.route("/users/me/sessions", get(get_my_sessions))
}