Merge pull request #1179 from trheyi/main

Enhance invitation handling and team configuration tests
This commit is contained in:
Max 2025-10-08 17:57:23 +08:00 committed by GitHub
commit 6a4e2a627f
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
5 changed files with 40 additions and 7 deletions

View file

@ -115,6 +115,8 @@ func CleanupTestCollections(t *testing.T) {
// source $YAO_SOURCE_ROOT/env.local.sh
//
// This loads the required environment variables for the test environment.
// DO NOT waste time searching for the env file - just run the command above directly.
// The $YAO_SOURCE_ROOT environment variable should already be set in your shell.
//
// WHAT THIS FUNCTION DOES:
// Step 1: Calls test.Prepare(t, config.Conf) to initialize the base Yao test environment

View file

@ -114,10 +114,13 @@ func TestTeamConfigAPI(t *testing.T) {
baseURL = openapi.Server.Config.BaseURL
}
// Register a test client first (needed for user.Load validation)
// Register a test client and get access token (team config endpoint now requires authentication)
testClient := testutils.RegisterTestClient(t, "Team Config Test Client", []string{"https://localhost/callback"})
defer testutils.CleanupTestClient(t, testClient.ClientID)
// Obtain access token for authentication
tokenInfo := testutils.ObtainAccessToken(t, serverURL, testClient.ClientID, testClient.ClientSecret, "https://localhost/callback", "openid profile")
// Test API endpoints for team configuration
testCases := []struct {
name string
@ -133,7 +136,13 @@ func TestTeamConfigAPI(t *testing.T) {
for _, tc := range testCases {
t.Run(tc.name, func(t *testing.T) {
requestURL := serverURL + baseURL + tc.endpoint
resp, err := http.Get(requestURL)
// Create request with Authorization header
req, err := http.NewRequest("GET", requestURL, nil)
assert.NoError(t, err, "Should create HTTP request")
req.Header.Set("Authorization", "Bearer "+tokenInfo.AccessToken)
resp, err := http.DefaultClient.Do(req)
assert.NoError(t, err, "HTTP request should succeed")
if resp != nil {

View file

@ -249,8 +249,20 @@ func GinInvitationCreate(c *gin.Context) {
return
}
// Return created invitation ID
c.JSON(http.StatusCreated, gin.H{"invitation_id": invitationID})
// Get the created invitation to return complete data
invitation, err := invitationGet(c.Request.Context(), authInfo.UserID, teamID, invitationID)
if err != nil {
log.Error("Failed to retrieve created invitation: %v", err)
// Fallback to returning just the ID if retrieval fails
c.JSON(http.StatusCreated, gin.H{"invitation_id": invitationID})
return
}
// Convert to InvitationResponse
invitationResp := convertToInvitationResponse(invitation)
// Return created invitation with full details (including token)
c.JSON(http.StatusCreated, invitationResp)
}
// GinInvitationResend handles PUT /teams/:team_id/invitations/:invitation_id/resend - Resend invitation
@ -1079,10 +1091,16 @@ func sendInvitationEmail(ctx context.Context, email, inviterName, teamName, toke
return nil
}
// convertToInvitationResponse converts a map to InvitationResponse (alias for mapToInvitationResponse)
func convertToInvitationResponse(data maps.MapStrAny) InvitationResponse {
return mapToInvitationResponse(maps.MapStr(data))
}
// mapToInvitationResponse converts a map to InvitationResponse
func mapToInvitationResponse(data maps.MapStr) InvitationResponse {
invitation := InvitationResponse{
ID: toInt64(data["id"]),
InvitationID: toString(data["invitation_id"]),
TeamID: toString(data["team_id"]),
UserID: toString(data["user_id"]),
MemberType: toString(data["member_type"]),

View file

@ -306,6 +306,7 @@ type UpdateMemberRequest struct {
// InvitationResponse represents a team invitation in API responses
type InvitationResponse struct {
ID int64 `json:"id"`
InvitationID string `json:"invitation_id"`
TeamID string `json:"team_id"`
UserID string `json:"user_id"`
MemberType string `json:"member_type"`
@ -354,11 +355,14 @@ type TeamRole struct {
RoleID string `json:"role_id"`
Label string `json:"label"`
Description string `json:"description"`
Default bool `json:"default"` // Whether this role is the default role
Hidden bool `json:"hidden"` // Whether this role is hidden from UI
}
// InviteConfig represents the invitation configuration
type InviteConfig struct {
Channel string `json:"channel,omitempty"`
Expiry string `json:"expiry,omitempty"`
BaseURL string `json:"base_url,omitempty"` // Base URL for invitation links
Templates map[string]string `json:"templates,omitempty"`
}

View file

@ -53,12 +53,12 @@ func Attach(group *gin.RouterGroup, oauth types.OAuth) {
func attachTeam(group *gin.RouterGroup, oauth types.OAuth) {
team := group.Group("/teams")
// Public endpoints (no authentication required)
team.GET("/config", GinTeamConfig) // Get team configuration (public)
// Protected endpoints (authentication required)
team.Use(oauth.Guard)
// Team Configuration
team.GET("/config", GinTeamConfig) // Get team configuration (requires authentication)
// Team CRUD - Standard REST endpoints
team.GET("/", GinTeamList) // GET /teams - List user teams
team.POST("/", GinTeamCreate) // POST /teams - Create new team