Skip to content
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
68 changes: 52 additions & 16 deletions internal/resources/grafana/data_source_organization_user.go
Original file line number Diff line number Diff line change
Expand Up @@ -48,31 +48,67 @@ func dataSourceOrganizationUserRead(ctx context.Context, d *schema.ResourceData,
GetPayload() []*models.UserLookupDTO
}

emailOrLogin := d.Get("email").(string)
if emailOrLogin == "" {
emailOrLogin = d.Get("login").(string)
}
if emailOrLogin == "" {
email := d.Get("email").(string)
login := d.Get("login").(string)

if email == "" && login == "" {
return diag.Errorf("must specify one of email or login")
}

params := org.NewGetOrgUsersForCurrentOrgLookupParams().WithQuery(&emailOrLogin)
// Use email if provided, otherwise use login
query := email
if query == "" {
query = login
}

params := org.NewGetOrgUsersForCurrentOrgLookupParams().WithQuery(&query)
resp, err := client.Org.GetOrgUsersForCurrentOrgLookup(params)
if err != nil {
return diag.FromErr(err)
}

// Make sure that exactly 1 user was returned
if len(resp.GetPayload()) > 1 {
return diag.Errorf("ambiguous query when reading organization user, multiple users returned by query: %q", emailOrLogin)
} else if len(resp.GetPayload()) == 0 {
return diag.Errorf("organization user not found with query: %q", emailOrLogin)
users := resp.GetPayload()

// If no users found, return error
if len(users) == 0 {
return diag.Errorf("organization user not found with query: %q", query)
}

user := resp.GetPayload()[0]
d.Set("user_id", user.UserID)
d.Set("login", user.Login)
// If exactly one user found, use it
if len(users) == 1 {
user := users[0]
d.Set("user_id", user.UserID)
d.Set("login", user.Login)
d.SetId(MakeOrgResourceID(orgID, user.UserID))
return nil
}

// Multiple users found - try to find exact match
var exactMatch *models.UserLookupDTO

if login != "" {
// Look for exact login match
for _, user := range users {
if user.Login == login {
if exactMatch != nil {
// Multiple exact matches found (shouldn't happen with login)
return diag.Errorf("ambiguous query when reading organization user, multiple users with exact login match: %q", login)
}
exactMatch = user
}
}
} else if email != "" {
// For email queries, we can't do exact matching since UserLookupDTO doesn't have Email field, return error
return diag.Errorf("ambiguous query when reading organization user, multiple users returned by query: %q", query)
}

if exactMatch != nil {
d.Set("user_id", exactMatch.UserID)
d.Set("login", exactMatch.Login)
d.SetId(MakeOrgResourceID(orgID, exactMatch.UserID))
return nil
}

d.SetId(MakeOrgResourceID(orgID, user.UserID))
return nil
// No exact match found, return error
return diag.Errorf("ambiguous query when reading organization user, multiple users returned by query: %q", query)
}
108 changes: 108 additions & 0 deletions internal/resources/grafana/data_source_organization_user_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -38,3 +38,111 @@ func TestAccDatasourceOrganizationUser_basic(t *testing.T) {
},
})
}

func TestAccDatasourceOrganizationUser_exactMatch(t *testing.T) {
testutils.CheckOSSTestsEnabled(t)

var user1, user2 models.UserProfileDTO
checks := []resource.TestCheckFunc{
userCheckExists.exists("grafana_user.test1", &user1),
userCheckExists.exists("grafana_user.test2", &user2),
// Test that exact login match works when multiple users are returned
resource.TestCheckResourceAttr(
"data.grafana_organization_user.exact_match", "login", "test-exact-match",
),
resource.TestMatchResourceAttr(
"data.grafana_organization_user.exact_match", "user_id", common.IDRegexp,
),
}

resource.ParallelTest(t, resource.TestCase{
ProtoV5ProviderFactories: testutils.ProtoV5ProviderFactories,
CheckDestroy: userCheckExists.destroyed(&user1, nil),
Steps: []resource.TestStep{
{
Config: testAccDatasourceOrganizationUserExactMatch,
Check: resource.ComposeTestCheckFunc(checks...),
},
},
})
}

// TestDataSourceOrganizationUserExactMatchLogic tests the exact matching logic without requiring a Grafana instance
func TestDataSourceOrganizationUserExactMatchLogic(t *testing.T) {
// Test case 2: Multiple users returned, exact login match exists
usersMultiple := []*models.UserLookupDTO{
{
UserID: 1,
Login: "test-exact-match",
},
{
UserID: 2,
Login: "test-exact-match-other",
},
}

// Test case 3: Multiple users returned, no exact login match
usersNoExact := []*models.UserLookupDTO{
{
UserID: 1,
Login: "test-exact-match-other1",
},
{
UserID: 2,
Login: "test-exact-match-other2",
},
}

// Test that we can identify exact matches
var exactMatch *models.UserLookupDTO
login := "test-exact-match"

for _, user := range usersMultiple {
if user.Login == login {
if exactMatch != nil {
t.Fatal("Multiple exact matches found when there should only be one")
}
exactMatch = user
}
}

if exactMatch == nil {
t.Fatal("Expected to find exact match but didn't")
}

if exactMatch.UserID != 1 {
t.Fatalf("Expected UserID 1, got %d", exactMatch.UserID)
}

// Test that we don't find exact matches when they don't exist
exactMatch = nil
for _, user := range usersNoExact {
if user.Login == login {
exactMatch = user
}
}

if exactMatch != nil {
t.Fatal("Expected no exact match but found one")
}
}

const testAccDatasourceOrganizationUserExactMatch = `
resource "grafana_user" "test1" {
email = "[email protected]"
name = "Test Exact Match 1"
login = "test-exact-match"
password = "my-password"
}

resource "grafana_user" "test2" {
email = "[email protected]"
name = "Test Exact Match 2"
login = "test-exact-match-other"
password = "my-password"
}

data "grafana_organization_user" "exact_match" {
login = grafana_user.test1.login
}
`
Loading