Skip to content

Commit

Permalink
Merge pull request #202 from pnag90/feature/fix-setting-creation
Browse files Browse the repository at this point in the history
Fix project setting creation
  • Loading branch information
jdamata authored Dec 18, 2023
2 parents b249921 + 17a0383 commit 348f57e
Show file tree
Hide file tree
Showing 6 changed files with 89 additions and 64 deletions.
2 changes: 1 addition & 1 deletion docs/resources/sonarqube_settings.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ resource "sonarqube_setting" "single_setting" {
## Example: create a setting with multiple values
```terraform
resource "sonarqube_setting" "multi_value_setting" {
key = "sonar.global.exclusions"
key = "sonar.global.exclusions"
values = ["foo", "bar/**/*.*"]
}
```
Expand Down
2 changes: 1 addition & 1 deletion sonarqube/resource_sonarqube_portfolio_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -604,7 +604,7 @@ func TestAccSonarqubePortfolioManualImport(t *testing.T) {
ResourceName: name,
ImportState: true,
ImportStateVerify: true,
Check: checks,
Check: checks,
},
},
})
Expand Down
14 changes: 9 additions & 5 deletions sonarqube/resource_sonarqube_project.go
Original file line number Diff line number Diff line change
Expand Up @@ -224,18 +224,22 @@ func resourceSonarqubeProjectRead(d *schema.ResourceData, m interface{}) error {
// Get settings
var projectSettings []Setting
if _, ok := d.GetOk("setting"); ok {
componentSettings := d.Get("setting").([]interface{})
projectSettings, err = getComponentSettings(d.Id(), m)
if err != nil {
return fmt.Errorf("resourceSonarqubeProjectRead: Failed to read project settings: %+v", err)
}

if len(projectSettings) > 0 {
settings := make([]interface{}, len(projectSettings))
for i, s := range projectSettings {
settings[i] = s.ToMap()
settings := make([]interface{}, len(componentSettings))
for i, s := range componentSettings {
key := s.(map[string]interface{})["key"].(string)
for _, apiSetting := range projectSettings {
if key == apiSetting.Key {
settings[i] = apiSetting.ToMap()
}
}
d.Set("setting", settings)
}
d.Set("setting", settings)
}

if len(projectReadResponse.Component.Tags) > 0 {
Expand Down
51 changes: 30 additions & 21 deletions sonarqube/resource_sonarqube_project_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -38,24 +38,24 @@ func testAccSonarqubeProjectTagsConfig(rnd string, name string, project string,
name = "%[2]s"
project = "%[3]s"
visibility = "%[4]s"
tags = %[5]s // Note that the "" should be missing since this is a list
tags = %[5]s // Note that the "" should be missing since this is a list
}
`, rnd, name, project, visibility, formattedTags)
}

func testAccSonarqubeProjectSettingsConfig(rnd string, name string, project string, visibility string) string {
func testAccSonarqubeProjectSettingsConfig(rnd string, name string, project string, visibility string, value string) string {
return fmt.Sprintf(`
resource "sonarqube_project" "%[1]s" {
name = "%[2]s"
project = "%[3]s"
visibility = "%[4]s"
setting {
key = "sonar.demo"
value = "sonarqube@example.org"
key = "sonar.docker.activate"
value = "%[5]s"
}
}
`, rnd, name, project, visibility)
`, rnd, name, project, visibility, value)
}

func testAccSonarqubeProjectSettingsMultiple(rnd string, key string, name string, values []string, fields map[string]string) string {
Expand All @@ -68,12 +68,12 @@ func testAccSonarqubeProjectSettingsMultiple(rnd string, key string, name string
visibility = "public"
setting {
key = "sonar.demo"
value = "sonarqube@example.org"
key = "sonar.terraform.activate"
value = "true"
}
setting {
key = "sonar.global.exclusions"
key = "sonar.terraform.file.suffixes"
values = %[4]s
}
Expand Down Expand Up @@ -272,12 +272,12 @@ func TestAccSonarqubeProjectSettingsCreate(t *testing.T) {
Providers: testAccProviders,
Steps: []resource.TestStep{
{
Config: testAccSonarqubeProjectSettingsConfig(rnd, "testAccSonarqubeProject", "testAccSonarqubeProject", "public"),
Config: testAccSonarqubeProjectSettingsConfig(rnd, "testAccSonarqubeProject", "testAccSonarqubeProject", "public", "false"),
Check: resource.ComposeTestCheckFunc(
resource.TestCheckResourceAttr(name, "project", "testAccSonarqubeProject"),
resource.TestCheckResourceAttr(name, "setting.#", strconv.Itoa(expectedSettings)),
resource.TestCheckResourceAttr(name, "setting.0.key", "sonar.demo"),
resource.TestCheckResourceAttr(name, "setting.0.value", "sonarqube@example.org"),
resource.TestCheckResourceAttr(name, "setting.0.key", "sonar.docker.activate"),
resource.TestCheckResourceAttr(name, "setting.0.value", "false"),
),
},
},
Expand All @@ -300,23 +300,32 @@ func TestAccSonarqubeProjectSettingsUpdate(t *testing.T) {
),
},
{
Config: testAccSonarqubeProjectSettingsConfig(rnd, "testAccSonarqubeProject", "testAccSonarqubeProject", "public"),
Config: testAccSonarqubeProjectSettingsConfig(rnd, "testAccSonarqubeProject", "testAccSonarqubeProject", "public", "false"),
Check: resource.ComposeTestCheckFunc(
resource.TestCheckResourceAttr(name, "project", "testAccSonarqubeProject"),
resource.TestCheckResourceAttr(name, "setting.#", "1"),
resource.TestCheckResourceAttr(name, "setting.0.key", "sonar.docker.activate"),
resource.TestCheckResourceAttr(name, "setting.0.value", "false"),
),
},
{
Config: testAccSonarqubeProjectSettingsConfig(rnd, "testAccSonarqubeProject", "testAccSonarqubeProject", "public", "true"),
Check: resource.ComposeTestCheckFunc(
resource.TestCheckResourceAttr(name, "project", "testAccSonarqubeProject"),
resource.TestCheckResourceAttr(name, "setting.#", "1"),
resource.TestCheckResourceAttr(name, "setting.0.key", "sonar.demo"),
resource.TestCheckResourceAttr(name, "setting.0.value", "sonarqube@example.org"),
resource.TestCheckResourceAttr(name, "setting.0.key", "sonar.docker.activate"),
resource.TestCheckResourceAttr(name, "setting.0.value", "true"),
),
},
},
})
}

func TestAccSonarqubePortfolioSettingsTypes(t *testing.T) {
func TestAccSonarqubeProjectSettingsTypes(t *testing.T) {
rnd := generateRandomResourceName()
name := "sonarqube_project." + rnd
expectedConditions := 3
values := []string{"foo", "bar"}
values := []string{".tf", ".tfvars"}
fieldValues := map[string]string{"ruleKey": "foo", "resourceKey": "bar"}

resource.Test(t, resource.TestCase{
Expand All @@ -328,11 +337,11 @@ func TestAccSonarqubePortfolioSettingsTypes(t *testing.T) {
Check: resource.ComposeTestCheckFunc(
resource.TestCheckResourceAttr(name, "project", "testAccSonarqubeProject"),
resource.TestCheckResourceAttr(name, "setting.#", strconv.Itoa(expectedConditions)),
resource.TestCheckResourceAttr(name, "setting.0.key", "sonar.demo"),
resource.TestCheckResourceAttr(name, "setting.0.value", "sonarqube@example.org"),
resource.TestCheckResourceAttr(name, "setting.1.key", "sonar.global.exclusions"),
resource.TestCheckTypeSetElemAttr(name, "setting.1.values.*", "foo"),
resource.TestCheckTypeSetElemAttr(name, "setting.1.values.*", "bar"),
resource.TestCheckResourceAttr(name, "setting.0.key", "sonar.terraform.activate"),
resource.TestCheckResourceAttr(name, "setting.0.value", "true"),
resource.TestCheckResourceAttr(name, "setting.1.key", "sonar.terraform.file.suffixes"),
resource.TestCheckTypeSetElemAttr(name, "setting.1.values.*", ".tf"),
resource.TestCheckTypeSetElemAttr(name, "setting.1.values.*", ".tfvars"),
resource.TestCheckResourceAttr(name, "setting.2.key", "sonar.issue.ignore.multicriteria"),
resource.TestCheckTypeSetElemNestedAttrs(name, "setting.2.field_values.*", fieldValues),
),
Expand Down
80 changes: 46 additions & 34 deletions sonarqube/resource_sonarqube_setting.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package sonarqube
import (
"encoding/json"
"fmt"
"log"
"net/http"
"net/url"
"sort"
Expand All @@ -27,12 +28,13 @@ type GetSettings struct {
func (a Setting) ToMap() map[string]interface{} {
obj := make(map[string]interface{})

obj["key"] = a.Key
obj["value"] = a.Value
if a.Values != nil {
obj["values"] = a.Values
}
if a.FieldValues != nil {
obj["fieldValues"] = a.FieldValues
obj["field_values"] = a.FieldValues
}
return obj
}
Expand Down Expand Up @@ -247,12 +249,8 @@ func getComponentSettings(component string, m interface{}) ([]Setting, error) {
}

settingsList := make([]Setting, 0)
// Filter settings (removing inherited)
for _, e := range settingReadResponse.Setting {
if e.Inherited == false {
settingsList = append(settingsList, e)
}
}
// Filter settings by parameter inherited
settingsList = append(settingsList, settingReadResponse.Setting...)

// Make sure the order is always the same for when we are comparing lists of conditions
sort.Slice(settingsList, func(i, j int) bool {
Expand All @@ -266,30 +264,33 @@ func synchronizeSettings(d *schema.ResourceData, m interface{}) (bool, error) {
changed := false
componentId := d.Id()
componentSettings := d.Get("setting").([]interface{})

apiComponentSettings, _ := getComponentSettings(componentId, m)

// Make sure the order is always the same for when we are comparing lists of conditions
sort.Slice(componentSettings, func(i, j int) bool {
return componentSettings[i].(map[string]interface{})["key"].(string) < componentSettings[j].(map[string]interface{})["key"].(string)
})

// Determine which conditions have been added or changed and update those
for _, s := range componentSettings {
setting := s.(map[string]interface{})
key := setting["key"].(string)

// Update the condition if it already exists
exists := false
for _, apiSetting := range apiComponentSettings {
if key == apiSetting.Key {
exists = true
if checkSettingDiff(setting, apiSetting) {
err := setComponentSetting(componentId, setting, m, &changed)
if err != nil {
return false, fmt.Errorf("addOrUpdateCondition: Failed to update setting '%s': %+v", key, err)
return false, fmt.Errorf("synchronizeSettings: Failed to update setting '%s': %+v", key, err)
}
}
}
}
// Add the condition because it does not already exist
if !exists {
err := setComponentSetting(componentId, setting, m, &changed)
if err != nil {
return false, fmt.Errorf("synchronizeSettings: Failed to create setting '%s': %+v", key, err)
}
}
}

// Determine if any settings have been removed and delete them
Expand All @@ -306,21 +307,7 @@ func synchronizeSettings(d *schema.ResourceData, m interface{}) (bool, error) {
}

func checkSettingDiff(a map[string]interface{}, b Setting) bool {
if a["value"] != nil {
return a["value"].(string) != b.Value
} else if a["values"] != nil {
// array of strings
values := a["field_values"].([]string)
if len(values) != len(b.FieldValues) {
return false
}
for i := range values {
if string(values[i]) != string(b.Values[i]) {
return false
}
}
return true
} else if a["field_values"] != nil {
if a["field_values"] != nil {
// array of objects of key/value pairs
fieldValues := a["field_values"].([]interface{})
if len(fieldValues) != len(b.FieldValues) {
Expand All @@ -334,6 +321,20 @@ func checkSettingDiff(a map[string]interface{}, b Setting) bool {
}
}
return true
} else if a["values"] != nil && len(a["values"].([]string)) > 0 {
// array of strings
values := a["values"].([]string)
if len(values) != len(b.Values) {
return false
}
for i := range values {
if string(values[i]) != string(b.Values[i]) {
return false
}
}
return true
} else if a["value"] != nil && a["value"] != "" {
return a["value"].(string) != b.Value
}
return false
}
Expand All @@ -342,20 +343,31 @@ func getComponentSettingUrlEncode(setting map[string]interface{}) url.Values {
raw := url.Values{
"key": []string{setting["key"].(string)},
}
if setting["value"] != nil {
addedSetting := false
log.Printf("[DEBUG] setting.value '%s'", setting["value"])
log.Printf("[DEBUG] setting.values '%s'", setting["values"])
log.Printf("[DEBUG] setting.field_values '%s'", setting["field_values"])
if setting["value"] != nil && setting["value"] != "" {
raw.Add("value", setting["value"].(string))
} else if setting["values"] != nil {
addedSetting = true
}

if setting["values"] != nil && !addedSetting {
// array of strings
for _, value := range setting["values"].([]interface{}) {
raw.Add("values", value.(string))
addedSetting = true
}
} else if setting["field_values"] != nil {
}

if setting["field_values"] != nil && !addedSetting {
// array of objects of key/value pairs
fieldValues := setting["field_values"].([]interface{})
for _, value := range fieldValues {
b, _ := json.Marshal(value)
fv := string(b)
raw.Add("fieldValues", fv)
addedSetting = true
}
}
return raw
Expand All @@ -372,7 +384,7 @@ func setComponentSetting(component string, setting map[string]interface{}, m int
m.(*ProviderConfiguration).httpClient,
"POST",
sonarQubeURL.String(),
http.StatusOK,
http.StatusNoContent,
"setComponentSettings",
)
if err != nil {
Expand All @@ -398,7 +410,7 @@ func removeComponentSettings(component string, newSettings []interface{}, apiPro
break
}
}
if !found {
if !found && !apiSetting.Inherited {
toDelete = append(toDelete, fmt.Sprint(apiSetting.Key))
}
}
Expand Down
4 changes: 2 additions & 2 deletions sonarqube/resource_sonarqube_webhook_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -110,8 +110,8 @@ func TestAccSonarqubeWebhookProjectBasic(t *testing.T) {
),
},
{
ResourceName: resourceName,
ImportState: true,
ResourceName: resourceName,
ImportState: true,
ImportStateIdFunc: testAccSonarqubeWebhookProjectImportID(resourceName),
ImportStateVerify: true,
ImportStateVerifyIgnore: []string{"secret"},
Expand Down

0 comments on commit 348f57e

Please sign in to comment.