diff --git a/internal/cli/entity.go b/internal/cli/entity.go index 5b01808f..075335f8 100644 --- a/internal/cli/entity.go +++ b/internal/cli/entity.go @@ -47,6 +47,25 @@ func (c *PortClient) CreateEntity(ctx context.Context, e *Entity, runID string) return &pb.Entity, nil } +func (c *PortClient) UpdateEntity(ctx context.Context, id string, blueprint string, e *Entity, runID string) (*Entity, error) { + url := "v1/blueprints/{blueprint}/entities/{identifier}" + pb := &PortBody{} + resp, err := c.Client.R(). + SetBody(e). + SetPathParam(("blueprint"), e.Blueprint). + SetPathParam("identifier", id). + SetQueryParam("run_id", runID). + SetResult(&pb). + Put(url) + if err != nil { + return nil, err + } + if !pb.OK { + return nil, fmt.Errorf("failed to update entity, got: %s", resp.Body()) + } + return &pb.Entity, nil +} + func (c *PortClient) DeleteEntity(ctx context.Context, id string, blueprint string) error { url := "v1/blueprints/{blueprint}/entities/{identifier}" pb := &PortBody{} diff --git a/port/action/resource.go b/port/action/resource.go index 28cd67e4..31585256 100644 --- a/port/action/resource.go +++ b/port/action/resource.go @@ -44,6 +44,7 @@ func (r *ActionResource) ImportState(ctx context.Context, req resource.ImportSta resp.Diagnostics.Append(resp.State.SetAttribute(ctx, path.Root("blueprint"), idParts[0])...) resp.Diagnostics.Append(resp.State.SetAttribute(ctx, path.Root("identifier"), idParts[1])...) + } func (r *ActionResource) Read(ctx context.Context, req resource.ReadRequest, resp *resource.ReadResponse) { @@ -126,7 +127,10 @@ func (r *ActionResource) Create(ctx context.Context, req resource.CreateRequest, func (r *ActionResource) Update(ctx context.Context, req resource.UpdateRequest, resp *resource.UpdateResponse) { var state *ActionModel + var previousState *ActionModel + resp.Diagnostics.Append(req.Plan.Get(ctx, &state)...) + resp.Diagnostics.Append(req.State.Get(ctx, &previousState)...) if resp.Diagnostics.HasError() { return @@ -144,7 +148,12 @@ func (r *ActionResource) Update(ctx context.Context, req resource.UpdateRequest, return } - a, err := r.portClient.UpdateAction(ctx, bp.Identifier, action.Identifier, action) + var a *cli.Action + if previousState.Identifier.IsNull() { + a, err = r.portClient.CreateAction(ctx, bp.Identifier, action) + } else { + a, err = r.portClient.UpdateAction(ctx, bp.Identifier, previousState.Identifier.ValueString(), action) + } if err != nil { resp.Diagnostics.AddError("failed to create action", err.Error()) return diff --git a/port/action/resource_test.go b/port/action/resource_test.go index 04007411..20c17fd8 100644 --- a/port/action/resource_test.go +++ b/port/action/resource_test.go @@ -854,3 +854,82 @@ func TestAccPortActionEncryption(t *testing.T) { }, }) } + +func TestAccPortActionUpdateIdentifier(t *testing.T) { + identifier := utils.GenID() + actionIdentifier := utils.GenID() + actionUpdatedIdentifier := utils.GenID() + var testAccActionConfigCreate = testAccCreateBlueprintConfig(identifier) + fmt.Sprintf(` + resource "port_action" "create_microservice" { + title = "TF Provider Test" + identifier = "%s" + icon = "Terraform" + blueprint = port_blueprint.microservice.id + trigger = "DAY-2" + webhook_method = { + url = "https://getport.io" + } + user_properties = { + "string_props" = { + "myStringIdentifier" = { + "title" = "My String Identifier" + "required" = true + } + } + } + }`, actionIdentifier) + + var testAccActionConfigUpdate = testAccCreateBlueprintConfig(identifier) + fmt.Sprintf(` + resource "port_action" "create_microservice" { + title = "TF Provider Test" + identifier = "%s" + icon = "Terraform" + blueprint = port_blueprint.microservice.id + trigger = "DAY-2" + webhook_method = { + url = "https://getport.io" + } + user_properties = { + "string_props" = { + "myStringIdentifier" = { + "title" = "My String Identifier" + "required" = true + } + } + } + }`, actionUpdatedIdentifier) + + resource.Test(t, resource.TestCase{ + PreCheck: func() { acctest.TestAccPreCheck(t) }, + ProtoV6ProviderFactories: acctest.TestAccProtoV6ProviderFactories, + + Steps: []resource.TestStep{ + { + Config: acctest.ProviderConfig + testAccActionConfigCreate, + Check: resource.ComposeTestCheckFunc( + resource.TestCheckResourceAttr("port_action.create_microservice", "title", "TF Provider Test"), + resource.TestCheckResourceAttr("port_action.create_microservice", "identifier", actionIdentifier), + resource.TestCheckResourceAttr("port_action.create_microservice", "icon", "Terraform"), + resource.TestCheckResourceAttr("port_action.create_microservice", "blueprint", identifier), + resource.TestCheckResourceAttr("port_action.create_microservice", "trigger", "DAY-2"), + resource.TestCheckResourceAttr("port_action.create_microservice", "webhook_method.url", "https://getport.io"), + resource.TestCheckResourceAttr("port_action.create_microservice", "user_properties.string_props.myStringIdentifier.title", "My String Identifier"), + resource.TestCheckResourceAttr("port_action.create_microservice", "user_properties.string_props.myStringIdentifier.required", "true"), + ), + }, + { + Config: acctest.ProviderConfig + testAccActionConfigUpdate, + Check: resource.ComposeTestCheckFunc( + resource.TestCheckResourceAttr("port_action.create_microservice", "title", "TF Provider Test"), + resource.TestCheckResourceAttr("port_action.create_microservice", "identifier", actionUpdatedIdentifier), + resource.TestCheckResourceAttr("port_action.create_microservice", "icon", "Terraform"), + resource.TestCheckResourceAttr("port_action.create_microservice", "blueprint", identifier), + resource.TestCheckResourceAttr("port_action.create_microservice", "trigger", "DAY-2"), + resource.TestCheckResourceAttr("port_action.create_microservice", "webhook_method.url", "https://getport.io"), + resource.TestCheckResourceAttr("port_action.create_microservice", "user_properties.string_props.myStringIdentifier.title", "My String Identifier"), + resource.TestCheckResourceAttr("port_action.create_microservice", "user_properties.string_props.myStringIdentifier.required", "true"), + ), + }, + }, + }) +} diff --git a/port/blueprint/resource.go b/port/blueprint/resource.go index 64ec3873..efe3a76b 100644 --- a/port/blueprint/resource.go +++ b/port/blueprint/resource.go @@ -150,7 +150,10 @@ func writeBlueprintComputedFieldsToState(state *BlueprintModel, bp *cli.Blueprin func (r *BlueprintResource) Update(ctx context.Context, req resource.UpdateRequest, resp *resource.UpdateResponse) { var state *BlueprintModel + var previousState *BlueprintModel + resp.Diagnostics.Append(req.Plan.Get(ctx, &state)...) + resp.Diagnostics.Append(req.State.Get(ctx, &previousState)...) if resp.Diagnostics.HasError() { return @@ -164,10 +167,10 @@ func (r *BlueprintResource) Update(ctx context.Context, req resource.UpdateReque var bp *cli.Blueprint - if state.Identifier.IsNull() { + if previousState.Identifier.IsNull() { bp, err = r.portClient.CreateBlueprint(ctx, b) } else { - bp, err = r.portClient.UpdateBlueprint(ctx, b, state.Identifier.ValueString()) + bp, err = r.portClient.UpdateBlueprint(ctx, b, previousState.ID.ValueString()) } if err != nil { @@ -207,6 +210,10 @@ func (r *BlueprintResource) ImportState(ctx context.Context, req resource.Import resp.Diagnostics.Append(resp.State.SetAttribute( ctx, path.Root("identifier"), req.ID, )...) + + resp.Diagnostics.Append(resp.State.SetAttribute( + ctx, path.Root("id"), req.ID, + )...) } func blueprintResourceToPortRequest(ctx context.Context, state *BlueprintModel) (*cli.Blueprint, error) { diff --git a/port/blueprint/resource_test.go b/port/blueprint/resource_test.go index 261b6445..da139303 100644 --- a/port/blueprint/resource_test.go +++ b/port/blueprint/resource_test.go @@ -678,3 +678,48 @@ func TestAccPortBlueprintWithCalculationProperty(t *testing.T) { }, }) } + +func TestAccPortUpdateBlueprintIdentifier(t *testing.T) { + identifier := utils.GenID() + updatedIdentifier := utils.GenID() + var testAccActionConfigCreate = fmt.Sprintf(` + resource "port_blueprint" "microservice" { + title = "TF Provider Test" + icon = "Terraform" + identifier = "%s" + description = "" + } +`, identifier) + + var testAccActionConfigUpdate = fmt.Sprintf(` + resource "port_blueprint" "microservice" { + title = "TF Provider Test" + icon = "Terraform" + identifier = "%s" + description = "" + } +`, updatedIdentifier) + + resource.Test(t, resource.TestCase{ + PreCheck: func() { acctest.TestAccPreCheck(t) }, + ProtoV6ProviderFactories: acctest.TestAccProtoV6ProviderFactories, + Steps: []resource.TestStep{ + { + Config: acctest.ProviderConfig + testAccActionConfigCreate, + Check: resource.ComposeTestCheckFunc( + resource.TestCheckResourceAttr("port_blueprint.microservice", "title", "TF Provider Test"), + resource.TestCheckResourceAttr("port_blueprint.microservice", "identifier", identifier), + resource.TestCheckResourceAttr("port_blueprint.microservice", "icon", "Terraform"), + ), + }, + { + Config: acctest.ProviderConfig + testAccActionConfigUpdate, + Check: resource.ComposeTestCheckFunc( + resource.TestCheckResourceAttr("port_blueprint.microservice", "title", "TF Provider Test"), + resource.TestCheckResourceAttr("port_blueprint.microservice", "identifier", updatedIdentifier), + resource.TestCheckResourceAttr("port_blueprint.microservice", "icon", "Terraform"), + ), + }, + }, + }) +} diff --git a/port/entity/resource.go b/port/entity/resource.go index 1f70f1dd..2432feaf 100644 --- a/port/entity/resource.go +++ b/port/entity/resource.go @@ -115,7 +115,10 @@ func writeEntityComputedFieldsToState(state *EntityModel, e *cli.Entity) { func (r *EntityResource) Update(ctx context.Context, req resource.UpdateRequest, resp *resource.UpdateResponse) { var state *EntityModel + var previousState *EntityModel + resp.Diagnostics.Append(req.Plan.Get(ctx, &state)...) + resp.Diagnostics.Append(req.State.Get(ctx, &previousState)...) if resp.Diagnostics.HasError() { return @@ -138,7 +141,14 @@ func (r *EntityResource) Update(ctx context.Context, req resource.UpdateRequest, runID = state.RunID.ValueString() } - en, err := r.portClient.CreateEntity(ctx, e, runID) + var en *cli.Entity + + if previousState.Identifier.IsNull() { + en, err = r.portClient.CreateEntity(ctx, e, runID) + } else { + en, err = r.portClient.UpdateEntity(ctx, previousState.Identifier.ValueString(), previousState.Blueprint.ValueString(), e, runID) + } + if err != nil { resp.Diagnostics.AddError("failed to create entity", err.Error()) return diff --git a/port/entity/resource_test.go b/port/entity/resource_test.go index 85afd390..fd15987c 100644 --- a/port/entity/resource_test.go +++ b/port/entity/resource_test.go @@ -419,3 +419,83 @@ func TestAccPortEntityUpdateProp(t *testing.T) { }, }) } + +func TestAccPortEntityUpdateIdentifier(t *testing.T) { + blueprintIdentifier := utils.GenID() + entityIdentifier := utils.GenID() + entityUpdatedIdentifier := utils.GenID() + var testAccActionConfigCreate = fmt.Sprintf(` + resource "port_blueprint" "microservice" { + title = "TF Provider Test BP0" + icon = "Terraform" + identifier = "%s" + properties = { + "string_props" = { + "myStringIdentifier" = { + "title" = "My String Identifier" + } + } + } + } + resource "port_entity" "microservice" { + title = "TF Provider Test Entity0" + blueprint = port_blueprint.microservice.identifier + identifier = "%s" + properties = { + "string_props" = { + "myStringIdentifier" = "My String Value" + } + } + }`, blueprintIdentifier, entityIdentifier) + + var testAccActionConfigUpdate = fmt.Sprintf(` + resource "port_blueprint" "microservice" { + title = "TF Provider Test BP0" + icon = "Terraform" + identifier = "%s" + properties = { + "string_props" = { + "myStringIdentifier" = { + "title" = "My String Identifier" + } + } + } + } + resource "port_entity" "microservice" { + title = "TF Provider Test Entity0" + blueprint = port_blueprint.microservice.identifier + identifier = "%s" + properties = { + "string_props" = { + "myStringIdentifier" = "My String Value2" + } + } + }`, blueprintIdentifier, entityUpdatedIdentifier) + + resource.Test(t, resource.TestCase{ + PreCheck: func() { acctest.TestAccPreCheck(t) }, + ProtoV6ProviderFactories: acctest.TestAccProtoV6ProviderFactories, + + Steps: []resource.TestStep{ + { + Config: acctest.ProviderConfig + testAccActionConfigCreate, + Check: resource.ComposeTestCheckFunc( + resource.TestCheckResourceAttr("port_entity.microservice", "identifier", entityIdentifier), + resource.TestCheckResourceAttr("port_entity.microservice", "title", "TF Provider Test Entity0"), + resource.TestCheckResourceAttr("port_entity.microservice", "blueprint", blueprintIdentifier), + resource.TestCheckResourceAttr("port_entity.microservice", "properties.string_props.myStringIdentifier", "My String Value"), + ), + }, + { + Config: acctest.ProviderConfig + testAccActionConfigUpdate, + Check: resource.ComposeTestCheckFunc( + resource.TestCheckResourceAttr("port_entity.microservice", "identifier", entityUpdatedIdentifier), + resource.TestCheckResourceAttr("port_entity.microservice", "title", "TF Provider Test Entity0"), + resource.TestCheckResourceAttr("port_entity.microservice", "blueprint", blueprintIdentifier), + resource.TestCheckResourceAttr("port_entity.microservice", "properties.string_props.myStringIdentifier", "My String Value2"), + ), + }, + }, + }) + +} diff --git a/port/scorecard/resouce.go b/port/scorecard/resouce.go index c9c4cbe4..f827a04a 100644 --- a/port/scorecard/resouce.go +++ b/port/scorecard/resouce.go @@ -97,7 +97,10 @@ func writeScorecardComputedFieldsToState(state *ScorecardModel, wp *cli.Scorecar func (r *ScorecardResource) Update(ctx context.Context, req resource.UpdateRequest, resp *resource.UpdateResponse) { var state *ScorecardModel + var previousState *ScorecardModel + resp.Diagnostics.Append(req.Plan.Get(ctx, &state)...) + resp.Diagnostics.Append(req.State.Get(ctx, &previousState)...) if resp.Diagnostics.HasError() { return @@ -109,7 +112,14 @@ func (r *ScorecardResource) Update(ctx context.Context, req resource.UpdateReque return } - sp, err := r.portClient.UpdateScorecard(ctx, state.Blueprint.ValueString(), state.Identifier.ValueString(), s) + var sp *cli.Scorecard + + if previousState.Identifier.IsNull() { + sp, err = r.portClient.CreateScorecard(ctx, state.Blueprint.ValueString(), s) + } else { + sp, err = r.portClient.UpdateScorecard(ctx, state.Blueprint.ValueString(), previousState.Identifier.ValueString(), s) + } + if err != nil { resp.Diagnostics.AddError("failed to update the scorecard", err.Error()) return diff --git a/port/scorecard/resource_test.go b/port/scorecard/resource_test.go index ff6be6ec..b7a55abf 100644 --- a/port/scorecard/resource_test.go +++ b/port/scorecard/resource_test.go @@ -296,3 +296,93 @@ func TestAccPortScorecardImport(t *testing.T) { }, }) } + +func TestAccPortScorecardUpdateIdentifier(t *testing.T) { + blueprintIdentifier := utils.GenID() + scorecardIdentifier := utils.GenID() + scorecardIdentifierUpdated := utils.GenID() + var testAccActionConfigCreate = testAccCreateBlueprintConfig(blueprintIdentifier) + fmt.Sprintf(` + resource "port_scorecard" "test" { + identifier = "%s" + title = "Scorecard 1" + blueprint = "%s" + rules = [{ + identifier = "hasTeam" + title = "Has Team" + level = "Gold" + query = { + combinator = "and" + conditions = [{ + property = "$team" + operator = "isNotEmpty" + }] + } + }] + + depends_on = [ + port_blueprint.microservice + ] + }`, scorecardIdentifier, blueprintIdentifier) + + var testAccActionConfigUpdate = testAccCreateBlueprintConfig(blueprintIdentifier) + fmt.Sprintf(` + resource "port_scorecard" "test" { + identifier = "%s" + title = "Scorecard 1" + blueprint = "%s" + rules = [{ + identifier = "hasTeam" + title = "Has Team" + level = "Gold" + query = { + combinator = "and" + conditions = [{ + property = "$team" + operator = "isNotEmpty" + }] + } + }] + depends_on = [ + port_blueprint.microservice + ] + }`, scorecardIdentifierUpdated, blueprintIdentifier) + + resource.Test(t, resource.TestCase{ + PreCheck: func() { acctest.TestAccPreCheck(t) }, + ProtoV6ProviderFactories: acctest.TestAccProtoV6ProviderFactories, + + Steps: []resource.TestStep{ + { + Config: acctest.ProviderConfig + testAccActionConfigCreate, + Check: resource.ComposeTestCheckFunc( + resource.TestCheckResourceAttr("port_scorecard.test", "identifier", scorecardIdentifier), + resource.TestCheckResourceAttr("port_scorecard.test", "title", "Scorecard 1"), + resource.TestCheckResourceAttr("port_scorecard.test", "blueprint", blueprintIdentifier), + resource.TestCheckResourceAttr("port_scorecard.test", "rules.#", "1"), + resource.TestCheckResourceAttr("port_scorecard.test", "rules.0.identifier", "hasTeam"), + resource.TestCheckResourceAttr("port_scorecard.test", "rules.0.title", "Has Team"), + resource.TestCheckResourceAttr("port_scorecard.test", "rules.0.level", "Gold"), + resource.TestCheckResourceAttr("port_scorecard.test", "rules.0.query.combinator", "and"), + resource.TestCheckResourceAttr("port_scorecard.test", "rules.0.query.conditions.#", "1"), + resource.TestCheckResourceAttr("port_scorecard.test", "rules.0.query.conditions.0.property", "$team"), + resource.TestCheckResourceAttr("port_scorecard.test", "rules.0.query.conditions.0.operator", "isNotEmpty"), + ), + }, + { + Config: acctest.ProviderConfig + testAccActionConfigUpdate, + Check: resource.ComposeTestCheckFunc( + resource.TestCheckResourceAttr("port_scorecard.test", "identifier", scorecardIdentifierUpdated), + resource.TestCheckResourceAttr("port_scorecard.test", "title", "Scorecard 1"), + resource.TestCheckResourceAttr("port_scorecard.test", "blueprint", blueprintIdentifier), + resource.TestCheckResourceAttr("port_scorecard.test", "rules.#", "1"), + resource.TestCheckResourceAttr("port_scorecard.test", "rules.0.identifier", "hasTeam"), + resource.TestCheckResourceAttr("port_scorecard.test", "rules.0.title", "Has Team"), + resource.TestCheckResourceAttr("port_scorecard.test", "rules.0.level", "Gold"), + resource.TestCheckResourceAttr("port_scorecard.test", "rules.0.query.combinator", "and"), + resource.TestCheckResourceAttr("port_scorecard.test", "rules.0.query.conditions.#", "1"), + resource.TestCheckResourceAttr("port_scorecard.test", "rules.0.query.conditions.0.property", "$team"), + resource.TestCheckResourceAttr("port_scorecard.test", "rules.0.query.conditions.0.operator", "isNotEmpty"), + ), + }, + }, + }) +} diff --git a/port/webhook/resource.go b/port/webhook/resource.go index bdc29927..6a946999 100644 --- a/port/webhook/resource.go +++ b/port/webhook/resource.go @@ -99,7 +99,10 @@ func writeWebhookComputedFieldsToState(state *WebhookModel, wp *cli.Webhook) { func (r *WebhookResource) Update(ctx context.Context, req resource.UpdateRequest, resp *resource.UpdateResponse) { var state *WebhookModel + var previousState *WebhookModel + resp.Diagnostics.Append(req.Plan.Get(ctx, &state)...) + resp.Diagnostics.Append(req.State.Get(ctx, &previousState)...) if resp.Diagnostics.HasError() { return @@ -111,7 +114,14 @@ func (r *WebhookResource) Update(ctx context.Context, req resource.UpdateRequest return } - wp, err := r.portClient.UpdateWebhook(ctx, w.Identifier, w) + var wp *cli.Webhook + + if previousState.Identifier.IsNull() { + wp, err = r.portClient.CreateWebhook(ctx, w) + } else { + wp, err = r.portClient.UpdateWebhook(ctx, previousState.Identifier.ValueString(), w) + } + if err != nil { resp.Diagnostics.AddError("failed to update the webhook", err.Error()) return diff --git a/port/webhook/resource_test.go b/port/webhook/resource_test.go index d710ea2c..d306c002 100644 --- a/port/webhook/resource_test.go +++ b/port/webhook/resource_test.go @@ -248,3 +248,49 @@ func TestAccPortWebhookImport(t *testing.T) { }, }) } + +func TestAccPortWebhookUpdateIdentifier(t *testing.T) { + identifier := utils.GenID() + webhookIdentifier := utils.GenID() + webhookIdentifierUpdated := utils.GenID() + var testAccActionConfigCreate = testAccCreateBlueprintConfig(identifier) + fmt.Sprintf(` + resource "port_webhook" "create_pr" { + identifier = "%s" + title = "Test" + icon = "Terraform" + enabled = true + }`, webhookIdentifier) + + var testAccActionConfigUpdate = testAccCreateBlueprintConfig(identifier) + fmt.Sprintf(` + resource "port_webhook" "create_pr" { + identifier = "%s" + title = "Test" + icon = "Terraform" + enabled = true + }`, webhookIdentifierUpdated) + + resource.Test(t, resource.TestCase{ + PreCheck: func() { acctest.TestAccPreCheck(t) }, + ProtoV6ProviderFactories: acctest.TestAccProtoV6ProviderFactories, + Steps: []resource.TestStep{ + { + Config: acctest.ProviderConfig + testAccActionConfigCreate, + Check: resource.ComposeTestCheckFunc( + resource.TestCheckResourceAttr("port_webhook.create_pr", "identifier", webhookIdentifier), + resource.TestCheckResourceAttr("port_webhook.create_pr", "title", "Test"), + resource.TestCheckResourceAttr("port_webhook.create_pr", "icon", "Terraform"), + resource.TestCheckResourceAttr("port_webhook.create_pr", "enabled", "true"), + ), + }, + { + Config: acctest.ProviderConfig + testAccActionConfigUpdate, + Check: resource.ComposeTestCheckFunc( + resource.TestCheckResourceAttr("port_webhook.create_pr", "identifier", webhookIdentifierUpdated), + resource.TestCheckResourceAttr("port_webhook.create_pr", "title", "Test"), + resource.TestCheckResourceAttr("port_webhook.create_pr", "icon", "Terraform"), + resource.TestCheckResourceAttr("port_webhook.create_pr", "enabled", "true"), + ), + }, + }, + }) +}