Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Port 4645 terraform provider add support in team resource #74

Merged
Merged
Show file tree
Hide file tree
Changes from 8 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
7 changes: 5 additions & 2 deletions docs/index.md
Original file line number Diff line number Diff line change
@@ -1,15 +1,18 @@
---

# generated by https://github.com/hashicorp/terraform-plugin-docs

page_title: "port-labs Provider"
subcategory: ""
description: |-
Interact with Port-labs
---
Interact with Port-labs

# port-labs Provider

[getport.io](https://getport.io)

Interact with Port-labs

<!-- schema generated by tfplugindocs -->

## Schema
Expand Down
34 changes: 34 additions & 0 deletions docs/resources/port_team.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
---
# generated by https://github.com/hashicorp/terraform-plugin-docs
page_title: "port_team Resource - terraform-provider-port-labs"
subcategory: ""
description: |-
Team resource
---

# port_team (Resource)

Team resource



<!-- schema generated by tfplugindocs -->
## Schema

### Required

- `name` (String) The name of the team

### Optional

- `description` (String) The description of the team
- `users` (List of String) The users of the team

### Read-Only

- `created_at` (String) The creation date of the team
- `id` (String) The ID of this resource.
- `provider_name` (String) The provider of the team
- `updated_at` (String) The last update date of the team


1 change: 1 addition & 0 deletions examples/resources/port_team/main.tf
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@

28 changes: 28 additions & 0 deletions internal/cli/models.go
Original file line number Diff line number Diff line change
Expand Up @@ -258,6 +258,15 @@ type (
ItemsToParse *string `json:"itemsToParse,omitempty"`
Entity *EntityProperty `json:"entity,omitempty"`
}

Team struct {
CreatedAt *time.Time `json:"createdAt,omitempty"`
UpdatedAt *time.Time `json:"updatedAt,omitempty"`
Name string `json:"name,omitempty"`
Description string `json:"description,omitempty"`
Users []string `json:"users,omitempty"`
Provider string `json:"provider,omitempty"`
}
)

type PortBody struct {
Expand All @@ -267,6 +276,25 @@ type PortBody struct {
Action Action `json:"action"`
Integration Webhook `json:"integration"`
Scorecard Scorecard `json:"Scorecard"`
Team Team `json:"team"`
}

type TeamUserBody struct {
Email string `json:"email"`
}

type TeamPortBody struct {
CreatedAt *time.Time `json:"createdAt,omitempty"`
UpdatedAt *time.Time `json:"updatedAt,omitempty"`
Name string `json:"name,omitempty"`
Description string `json:"description,omitempty"`
Users []TeamUserBody `json:"users,omitempty"`
Provider string `json:"provider,omitempty"`
}

type PortTeamBody struct {
OK bool `json:"ok"`
Team TeamPortBody `json:"team"`
}

type PortProviderModel struct {
Expand Down
106 changes: 106 additions & 0 deletions internal/cli/team.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,106 @@
package cli

import (
"context"
"encoding/json"
"fmt"
)

func (c *PortClient) ReadTeam(ctx context.Context, teamName string) (*Team, int, error) {
url := "v1/teams/{name}?fields=name&fields=provider&fields=description&fields=createdAt&fields=updatedAt&fields=users.firstName&fields=users.status&fields=users.email"
resp, err := c.Client.R().
SetContext(ctx).
SetHeader("Accept", "application/json").
SetPathParam("name", teamName).
Get(url)
if err != nil {
return nil, resp.StatusCode(), err
}

var pt PortTeamBody
err = json.Unmarshal(resp.Body(), &pt)
if err != nil {
return nil, resp.StatusCode(), err
}

if !pt.OK {
return nil, resp.StatusCode(), fmt.Errorf("failed to read team, got: %s", resp.Body())
}
team := &Team{
Name: pt.Team.Name,
Description: pt.Team.Description,
CreatedAt: pt.Team.CreatedAt,
UpdatedAt: pt.Team.UpdatedAt,
}

team.Users = make([]string, len(pt.Team.Users))

for i, u := range pt.Team.Users {
team.Users[i] = u.Email
}

return team, resp.StatusCode(), nil
}

func (c *PortClient) CreateTeam(ctx context.Context, team *Team) (*Team, error) {
url := "v1/teams"
resp, err := c.Client.R().
SetBody(team).
SetContext(ctx).
Post(url)
if err != nil {
dvirsegev marked this conversation as resolved.
Show resolved Hide resolved
return nil, err
}
var pb PortBody
err = json.Unmarshal(resp.Body(), &pb)
if err != nil {
return nil, err
}
if !pb.OK {
return nil, fmt.Errorf("failed to create team, got: %s", resp.Body())
}
return &pb.Team, nil
}

func (c *PortClient) UpdateTeam(ctx context.Context, teamName string, team *Team) (*Team, error) {
url := "v1/teams/{name}"
resp, err := c.Client.R().
SetBody(team).
SetContext(ctx).
SetPathParam("name", teamName).
Patch(url)
if err != nil {
dvirsegev marked this conversation as resolved.
Show resolved Hide resolved
return nil, err
}
var pb PortBody
err = json.Unmarshal(resp.Body(), &pb)
if err != nil {
return nil, err
}
if !pb.OK {
return nil, fmt.Errorf("failed to update team, got: %s", resp.Body())
}
return &pb.Team, nil
}

func (c *PortClient) DeleteTeam(ctx context.Context, teamName string) error {
url := "v1/teams/{name}"
resp, err := c.Client.R().
SetContext(ctx).
SetHeader("Accept", "application/json").
SetPathParam("name", teamName).
Delete(url)
if err != nil {
dvirsegev marked this conversation as resolved.
Show resolved Hide resolved
return err
}
var pb PortBodyDelete
err = json.Unmarshal(resp.Body(), &pb)
if err != nil {
return err
}

if !(pb.Ok) {
return fmt.Errorf("failed to delete team. got:\n%s", string(resp.Body()))
}
return nil
}
15 changes: 15 additions & 0 deletions port/team/model.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
package team

import (
"github.com/hashicorp/terraform-plugin-framework/types"
)

type TeamModel struct {
ID types.String `tfsdk:"id"`
Name types.String `tfsdk:"name"`
Description types.String `tfsdk:"description"`
Users []types.String `tfsdk:"users"`
CreatedAt types.String `tfsdk:"created_at"`
UpdatedAt types.String `tfsdk:"updated_at"`
ProviderName types.String `tfsdk:"provider_name"`
}
26 changes: 26 additions & 0 deletions port/team/refreshTeamState.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
package team

import (
"context"

"github.com/hashicorp/terraform-plugin-framework/types"
"github.com/port-labs/terraform-provider-port-labs/internal/cli"
"github.com/port-labs/terraform-provider-port-labs/internal/flex"
)

func refreshTeamState(ctx context.Context, state *TeamModel, t *cli.Team) error {
state.CreatedAt = types.StringValue(t.CreatedAt.String())
state.UpdatedAt = types.StringValue(t.UpdatedAt.String())
state.ID = types.StringValue(t.Name)
state.Name = types.StringValue(t.Name)
state.Description = flex.GoStringToFramework(&t.Description)

if len(t.Users) != 0 {
state.Users = make([]types.String, len(t.Users))
for i, u := range t.Users {
state.Users[i] = types.StringValue(u)
}
}

return nil
}
141 changes: 141 additions & 0 deletions port/team/resource.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,141 @@
package team

import (
"context"

"github.com/hashicorp/terraform-plugin-framework/path"
"github.com/hashicorp/terraform-plugin-framework/resource"
"github.com/hashicorp/terraform-plugin-framework/types"
"github.com/port-labs/terraform-provider-port-labs/internal/cli"
)

// Ensure provider defined types fully satisfy framework interfaces
var _ resource.Resource = &TeamResource{}
var _ resource.ResourceWithImportState = &TeamResource{}

func NewTeamResource() resource.Resource {
return &TeamResource{}
}

type TeamResource struct {
portClient *cli.PortClient
}

func (r *TeamResource) Metadata(ctx context.Context, req resource.MetadataRequest, resp *resource.MetadataResponse) {
resp.TypeName = req.ProviderTypeName + "_team"
}

func (r *TeamResource) Configure(ctx context.Context, req resource.ConfigureRequest, resp *resource.ConfigureResponse) {
if req.ProviderData == nil {
return
}

r.portClient = req.ProviderData.(*cli.PortClient)
}

func (r *TeamResource) Read(ctx context.Context, req resource.ReadRequest, resp *resource.ReadResponse) {
var state *TeamModel

resp.Diagnostics.Append(req.State.Get(ctx, &state)...)

if resp.Diagnostics.HasError() {
return
}

name := state.Name.ValueString()
t, statusCode, err := r.portClient.ReadTeam(ctx, name)
if err != nil {
if statusCode == 404 {
resp.State.RemoveResource(ctx)
return
}
resp.Diagnostics.AddError("failed to read team", err.Error())
return
}

err = refreshTeamState(ctx, state, t)
if err != nil {
resp.Diagnostics.AddError("failed writing team fields to resource", err.Error())
return
}
resp.Diagnostics.Append(resp.State.Set(ctx, &state)...)
}

func (r *TeamResource) Create(ctx context.Context, req resource.CreateRequest, resp *resource.CreateResponse) {
var state *TeamModel
resp.Diagnostics.Append(req.Plan.Get(ctx, &state)...)

if resp.Diagnostics.HasError() {
return
}

t, err := TeamResourceToPortBody(ctx, state)
if err != nil {
resp.Diagnostics.AddError("failed to convert team resource to body", err.Error())
return
}

tp, err := r.portClient.CreateTeam(ctx, t)
if err != nil {
resp.Diagnostics.AddError("failed to create team", err.Error())
return
}

writeTeamComputedFieldsToState(state, tp)

resp.Diagnostics.Append(resp.State.Set(ctx, &state)...)
}

func writeTeamComputedFieldsToState(state *TeamModel, tp *cli.Team) {
state.ID = types.StringValue(tp.Name)
state.CreatedAt = types.StringValue(tp.CreatedAt.String())
state.UpdatedAt = types.StringValue(tp.UpdatedAt.String())
state.ProviderName = types.StringValue(tp.Provider)
}

func (r *TeamResource) Update(ctx context.Context, req resource.UpdateRequest, resp *resource.UpdateResponse) {
var state *TeamModel
resp.Diagnostics.Append(req.Plan.Get(ctx, &state)...)

if resp.Diagnostics.HasError() {
return
}

t, err := TeamResourceToPortBody(ctx, state)
if err != nil {
resp.Diagnostics.AddError("failed to convert team resource to body", err.Error())
return
}

tp, err := r.portClient.UpdateTeam(ctx, t.Name, t)
if err != nil {
resp.Diagnostics.AddError("failed to update the team", err.Error())
return
}

writeTeamComputedFieldsToState(state, tp)

resp.Diagnostics.Append(resp.State.Set(ctx, &state)...)
}

func (r *TeamResource) Delete(ctx context.Context, req resource.DeleteRequest, resp *resource.DeleteResponse) {
var state *TeamModel
resp.Diagnostics.Append(req.State.Get(ctx, &state)...)
if resp.Diagnostics.HasError() {
return
}

err := r.portClient.DeleteTeam(ctx, state.Name.ValueString())

if err != nil {
resp.Diagnostics.AddError("failed to delete team", err.Error())
return
}

}

func (r *TeamResource) ImportState(ctx context.Context, req resource.ImportStateRequest, resp *resource.ImportStateResponse) {
resp.Diagnostics.Append(resp.State.SetAttribute(
ctx, path.Root("name"), req.ID,
)...)
}
Loading
Loading