diff --git a/go.mod b/go.mod index dda7b3a..9b5e698 100644 --- a/go.mod +++ b/go.mod @@ -5,7 +5,7 @@ go 1.19 require ( github.com/hashicorp/terraform v0.12.2 github.com/robfig/cron v1.2.0 - github.com/yext/go-teamcity v0.5.3 + github.com/yext/go-teamcity v0.5.4 ) require ( diff --git a/go.sum b/go.sum index cce04fb..86d8496 100644 --- a/go.sum +++ b/go.sum @@ -344,8 +344,8 @@ github.com/vmihailenco/msgpack v4.0.4+incompatible/go.mod h1:fy3FlTQTDXWkZ7Bh6Ac github.com/xanzy/ssh-agent v0.2.1/go.mod h1:mLlQY/MoOhWBj+gOGMQkOeiEvkx+8pJSI+0Bx9h2kr4= github.com/xiang90/probing v0.0.0-20160813154853-07dd2e8dfe18/go.mod h1:UETIi67q53MR2AWcXfiuqkDkRtnGDLqkBTpCHuJHxtU= github.com/xlab/treeprint v0.0.0-20161029104018-1d6e34225557/go.mod h1:ce1O1j6UtZfjr22oyGxGLbauSBp2YVXpARAosm7dHBg= -github.com/yext/go-teamcity v0.5.3 h1:kO8of1jNql70g9fURGs+oM0ArMX3csjGy99FAkPecEQ= -github.com/yext/go-teamcity v0.5.3/go.mod h1:GC2xvLY4y27DU/01mveI8LsSBerjpCEtHk6eQ7ym8oc= +github.com/yext/go-teamcity v0.5.4 h1:KKA1wsNF2DGnq1tkCK5iTu4zzKvBYLcquU9DcYPPpo4= +github.com/yext/go-teamcity v0.5.4/go.mod h1:GC2xvLY4y27DU/01mveI8LsSBerjpCEtHk6eQ7ym8oc= github.com/zclconf/go-cty v0.0.0-20181129180422-88fbe721e0f8/go.mod h1:xnAOWiHeOqg2nWS62VtQ7pbOu17FtxJNW8RLEih+O3s= github.com/zclconf/go-cty v0.0.0-20190426224007-b18a157db9e2/go.mod h1:xnAOWiHeOqg2nWS62VtQ7pbOu17FtxJNW8RLEih+O3s= github.com/zclconf/go-cty v0.0.0-20190516203816-4fecf87372ec/go.mod h1:xnAOWiHeOqg2nWS62VtQ7pbOu17FtxJNW8RLEih+O3s= diff --git a/teamcity/resource_build_config.go b/teamcity/resource_build_config.go index b6301ba..8ab11ba 100644 --- a/teamcity/resource_build_config.go +++ b/teamcity/resource_build_config.go @@ -516,7 +516,10 @@ func resourceBuildConfigRead(d *schema.ResourceData, meta interface{}) error { srv := client.BuildFeatureService(d.Id()) buildFeatures, err := srv.GetBuildFeatures() - buildFeaturesToSave, err := flattenBuildFeatures(buildFeatures) + if err != nil { + return err + } + buildFeaturesToSave := flattenBuildFeatures(buildFeatures) if err := d.Set("feature", buildFeaturesToSave); err != nil { return err } @@ -788,7 +791,7 @@ func flattenBuildStepCmdLine(s *api.StepCommandLine) map[string]interface{} { return m } -func flattenBuildFeatures(bfs []api.BuildFeature) ([]map[string]interface{}, error) { +func flattenBuildFeatures(bfs []api.BuildFeature) []map[string]interface{} { var bfsToSave []map[string]interface{} for _, bf := range bfs { bfToSave := make(map[string]interface{}) @@ -805,7 +808,7 @@ func flattenBuildFeatures(bfs []api.BuildFeature) ([]map[string]interface{}, err } bfsToSave = append(bfsToSave, bfToSave) } - return bfsToSave, nil + return bfsToSave } func expandBuildFeatures(list interface{}) ([]api.BuildFeature, error) { diff --git a/teamcity/resource_project.go b/teamcity/resource_project.go index 8a3325c..ef8e205 100644 --- a/teamcity/resource_project.go +++ b/teamcity/resource_project.go @@ -1,6 +1,7 @@ package teamcity import ( + "errors" "fmt" "log" @@ -56,6 +57,22 @@ func resourceProject() *schema.Resource { Type: schema.TypeMap, Optional: true, }, + "feature": { + Type: schema.TypeList, + Optional: true, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "type": { + Type: schema.TypeString, + Required: true, + }, + "properties": { + Type: schema.TypeMap, + Required: true, + }, + }, + }, + }, }, } } @@ -115,6 +132,28 @@ func resourceProjectUpdate(d *schema.ResourceData, meta interface{}) error { } } + if d.HasChange("feature") { + srv := client.ProjectFeatureService(d.Id()) + err := srv.DeleteAll() + if err != nil { + return err + } + add, err := expandProjectFeatures(d.Get("feature").([]interface{})) + if err != nil { + return err + } + if len(add) > 0 { + for i, s := range add { + _, err := srv.Create(s) + log.Printf("[INFO] Adding project feature '%v' with order = %v", s.Type(), i+1) + if err != nil { + return err + } + } + } + d.SetPartial("feature") + } + dt.Parameters, err = expandParameterCollection(d) if err != nil { return err @@ -149,6 +188,16 @@ func resourceProjectRead(d *schema.ResourceData, meta interface{}) error { return err } + srv := client.ProjectFeatureService(d.Id()) + projectFeatures, err := srv.GetProjectFeatures() + if err != nil { + return err + } + projectFeaturesToSave := flattenProjectFeatures(projectFeatures) + if err := d.Set("feature", projectFeaturesToSave); err != nil { + return err + } + log.Printf("[DEBUG] Project: %v", dt) return nil } @@ -180,3 +229,56 @@ func getProject(c *api.Client, id string) (*api.Project, error) { return dt, nil } + +func flattenProjectFeatures(pfs []api.ProjectFeature) []map[string]interface{} { + var pfsToSave []map[string]interface{} + for _, pf := range pfs { + pfToSave := make(map[string]interface{}) + gpf := pf.(*api.GenericProjectFeature) + pfToSave["type"] = gpf.Type() + + props := gpf.Properties() + pfToSave["properties"] = make(map[string]string) + if props != nil && props.Count > 0 { + propertyMap := pfToSave["properties"].(map[string]string) + for _, property := range props.Items { + propertyMap[property.Name] = property.Value + } + } + pfsToSave = append(pfsToSave, pfToSave) + } + return pfsToSave +} + +func expandProjectFeatures(list interface{}) ([]api.ProjectFeature, error) { + var out []api.ProjectFeature + rawProjectFeatures := list.([]interface{}) + for _, rawPF := range rawProjectFeatures { + expandedPF, err := expandProjectFeature(rawPF) + if err != nil { + return nil, err + } + out = append(out, expandedPF) + } + + return out, nil +} + +func expandProjectFeature(raw interface{}) (api.ProjectFeature, error) { + feature := raw.(map[string]interface{}) + var featureType string + var properties map[string]interface{} + if v, _ := feature["type"]; len(v.(string)) > 0 { + featureType = v.(string) + } else { + return nil, errors.New("feature type cannot be empty") + } + if v, ok := feature["properties"]; ok { + properties = v.(map[string]interface{}) + } + pf, err := api.NewGenericProjectFeature(featureType, properties) + if err != nil { + return nil, err + } + return pf, nil +}