diff --git a/pkg/providers/clientlists/data_akamai_clientlist_list.go b/pkg/providers/clientlists/data_akamai_clientlist_list.go new file mode 100644 index 000000000..ab7ad6ae3 --- /dev/null +++ b/pkg/providers/clientlists/data_akamai_clientlist_list.go @@ -0,0 +1,275 @@ +package clientlists + +import ( + "context" + "encoding/json" + + "github.com/akamai/AkamaiOPEN-edgegrid-golang/v9/pkg/clientlists" + "github.com/akamai/terraform-provider-akamai/v6/pkg/common/hash" + "github.com/akamai/terraform-provider-akamai/v6/pkg/common/tf" + "github.com/akamai/terraform-provider-akamai/v6/pkg/meta" + "github.com/hashicorp/terraform-plugin-sdk/v2/diag" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/validation" +) + +func dataSourceClientList() *schema.Resource { + return &schema.Resource{ + ReadContext: dataSourceClientListRead, + Schema: map[string]*schema.Schema{ + "list_id": { + Type: schema.TypeString, + Required: true, + Description: "The ID of the client list.", + }, + "version": { + Type: schema.TypeInt, + Computed: true, + Description: "The current version of the client list.", + }, + "items_count": { + Type: schema.TypeInt, + Computed: true, + Description: "The number of items that a client list contains.", + }, + "items": { + Type: schema.TypeList, + Computed: true, + Description: "A set of client list values.", + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "create_date": { + Type: schema.TypeString, + Computed: true, + Description: "The client list item creation date.", + }, + "created_by": { + Type: schema.TypeString, + Computed: true, + Description: "The username of the user who created the client list item.", + }, + "update_date": { + Type: schema.TypeString, + Computed: true, + Description: "The date of last update.", + }, + "updated_by": { + Type: schema.TypeString, + Computed: true, + Description: "The username of the user that updated the client list last.", + }, + "description": { + Type: schema.TypeString, + Optional: true, + Description: "The description of the client list item.", + }, + "expiration_date": { + Type: schema.TypeString, + Computed: true, + Description: "The client list item expiration date.", + }, + "list_id": { + Type: schema.TypeString, + Computed: true, + Description: "The ID of the client list.", + }, + "production_activation_status": { + Type: schema.TypeString, + Computed: true, + Description: "The activation status in production environment.", + }, + "staging_activation_status": { + Type: schema.TypeString, + Computed: true, + Description: "The activation status in staging environment.", + }, + "type": { + Type: schema.TypeString, + Optional: true, + Elem: &schema.Schema{ + Type: schema.TypeString, + ValidateDiagFunc: validation.ToDiagFunc(validation.StringInSlice(getValidListTypes(), false)), + }, + }, + "value": { + Type: schema.TypeString, + Computed: true, + Description: "Value of the client list entry.", + }, + "tags": { + Type: schema.TypeList, + Computed: true, + Description: "A list of tags associated with the client list item.", + Elem: &schema.Schema{ + Type: schema.TypeString, + }, + }, + }, + }, + }, + "create_date": { + Type: schema.TypeString, + Computed: true, + Description: "The client list creation date.", + }, + "created_by": { + Type: schema.TypeString, + Computed: true, + Description: "The username of the user who created the client list.", + }, + "update_date": { + Type: schema.TypeString, + Computed: true, + Description: "The date of last update.", + }, + "updated_by": { + Type: schema.TypeString, + Computed: true, + Description: "The username of the user that updated the client list last.", + }, + "production_activation_status": { + Type: schema.TypeString, + Computed: true, + Description: "The activation status in production environment.", + }, + "staging_activation_status": { + Type: schema.TypeString, + Computed: true, + Description: "The activation status in staging environment.", + }, + "list_type": { + Type: schema.TypeString, + Computed: true, + Description: "The client list type.", + }, + "shared": { + Type: schema.TypeBool, + Computed: true, + Description: "Whether the client list is shared.", + }, + "read_only": { + Type: schema.TypeBool, + Computed: true, + Description: "Whether the client is editable for the authenticated user.", + }, + "deprecated": { + Type: schema.TypeBool, + Computed: true, + Description: "Whether the client list was removed.", + }, + "json": { + Type: schema.TypeString, + Computed: true, + Description: "JSON representation of the client list.", + }, + "output_text": { + Type: schema.TypeString, + Computed: true, + Description: "Tabular representation of the client lists.", + }, + }, + } +} + +func dataSourceClientListRead(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics { + meta := meta.Must(m) + client := inst.Client(meta) + logger := meta.Log("CLIENTLIST", "dataSourceClientListRead") + + listId, err := tf.GetStringValue("list_id", d) + if err != nil { + return diag.FromErr(err) + } + + list, err := client.GetClientList(ctx, clientlists.GetClientListRequest{ + ListID: listId, + IncludeItems: true, + }) + if err != nil { + logger.Errorf("calling 'GetClientList': %s", err.Error()) + return diag.FromErr(err) + } + + if err := d.Set("list_id", list.ListID); err != nil { + return diag.Errorf("%v: %s", tf.ErrValueSet, err.Error()) + } + if err := d.Set("version", list.Version); err != nil { + return diag.Errorf("%v: %s", tf.ErrValueSet, err.Error()) + } + if err := d.Set("items_count", list.ItemsCount); err != nil { + return diag.Errorf("%v: %s", tf.ErrValueSet, err.Error()) + } + if err := d.Set("create_date", list.CreateDate); err != nil { + return diag.Errorf("%v: %s", tf.ErrValueSet, err.Error()) + } + if err := d.Set("created_by", list.CreatedBy); err != nil { + return diag.Errorf("%v: %s", tf.ErrValueSet, err.Error()) + } + if err := d.Set("update_date", list.UpdateDate); err != nil { + return diag.Errorf("%v: %s", tf.ErrValueSet, err.Error()) + } + if err := d.Set("updated_by", list.UpdatedBy); err != nil { + return diag.Errorf("%v: %s", tf.ErrValueSet, err.Error()) + } + if err := d.Set("production_activation_status", list.ProductionActivationStatus); err != nil { + return diag.Errorf("%v: %s", tf.ErrValueSet, err.Error()) + } + if err := d.Set("staging_activation_status", list.StagingActivationStatus); err != nil { + return diag.Errorf("%v: %s", tf.ErrValueSet, err.Error()) + } + if err := d.Set("list_type", list.ListType); err != nil { + return diag.Errorf("%v: %s", tf.ErrValueSet, err.Error()) + } + if err := d.Set("shared", list.Shared); err != nil { + return diag.Errorf("%v: %s", tf.ErrValueSet, err.Error()) + } + if err := d.Set("read_only", list.ReadOnly); err != nil { + return diag.Errorf("%v: %s", tf.ErrValueSet, err.Error()) + } + if err := d.Set("deprecated", list.Deprecated); err != nil { + return diag.Errorf("%v: %s", tf.ErrValueSet, err.Error()) + } + + mappedItems := mapClientListItemsToSchema(list) + if err := d.Set("items", mappedItems); err != nil { + return diag.Errorf("%v: %s", tf.ErrValueSet, err.Error()) + } + + jsonBody, err := json.MarshalIndent(list.Items, "", " ") + if err != nil { + return diag.FromErr(err) + } + if err := d.Set("json", string(jsonBody)); err != nil { + return diag.Errorf("%v: %s", tf.ErrValueSet, err.Error()) + } + + d.SetId(hash.GetSHAString(string(jsonBody))) + + return nil +} + +func mapClientListItemsToSchema(lists *clientlists.GetClientListResponse) []interface{} { + if lists != nil && len(lists.Items) > 0 { + result := make([]interface{}, 0, len(lists.Items)) + + for _, list := range lists.Items { + result = append(result, map[string]interface{}{ + "value": list.Value, + "tags": list.Tags, + "description": list.Description, + "expiration_date": list.ExpirationDate, + "create_date": list.CreateDate, + "created_by": list.CreatedBy, + "production_activation_status": list.ProductionStatus, + "staging_activation_status": list.StagingStatus, + "type": list.Type, + "update_date": list.UpdateDate, + "updated_by": list.UpdatedBy, + }) + } + + return result + } + + return make([]interface{}, 0) +} diff --git a/pkg/providers/clientlists/data_akamai_clientlist_list_test.go b/pkg/providers/clientlists/data_akamai_clientlist_list_test.go new file mode 100644 index 000000000..ca493e732 --- /dev/null +++ b/pkg/providers/clientlists/data_akamai_clientlist_list_test.go @@ -0,0 +1,76 @@ +package clientlists + +import ( + "bytes" + "encoding/json" + "strconv" + "testing" + + "github.com/akamai/AkamaiOPEN-edgegrid-golang/v9/pkg/clientlists" + "github.com/akamai/terraform-provider-akamai/v6/pkg/common/testutils" + "github.com/hashicorp/terraform-plugin-testing/helper/resource" + "github.com/stretchr/testify/mock" + "github.com/stretchr/testify/require" +) + +func TestClientList_data_single_list(t *testing.T) { + dataSourceName := "data.akamai_clientlist_list.list" + tests := map[string]struct { + params clientlists.GetClientListRequest + config string + responseBody []byte + }{ + "List": { + params: clientlists.GetClientListRequest{ + ListID: "123_TEST", + IncludeItems: true, + }, + config: loadFixtureString("testData/TestDSClientList/get_list.tf"), + responseBody: loadFixtureBytes("testData/TestDSClientList/ClientList.json"), + }, + } + + for name, test := range tests { + t.Run(name, func(t *testing.T) { + client := &clientlists.Mock{} + clientListResponse := clientlists.GetClientListResponse{} + err := json.Unmarshal(test.responseBody, &clientListResponse) + require.NoError(t, err) + + r, err := json.Marshal(clientListResponse.Items) + require.NoError(t, err) + + buf := &bytes.Buffer{} + err = json.Indent(buf, r, "", " ") + require.NoError(t, err) + + clientListResponseJSONString := buf.String() + + client.On("GetClientList", + mock.Anything, + test.params, + ).Return(&clientListResponse, nil) + + useClient(client, func() { + checks := []resource.TestCheckFunc{ + resource.TestCheckResourceAttr(dataSourceName, "json", clientListResponseJSONString), + resource.TestCheckResourceAttr(dataSourceName, "list_id", test.params.ListID), + resource.TestCheckResourceAttr(dataSourceName, "items.#", strconv.Itoa(len(clientListResponse.Items))), + } + + resource.Test(t, resource.TestCase{ + IsUnitTest: true, + ProtoV6ProviderFactories: testutils.NewProtoV6ProviderFactory(NewSubprovider()), + Steps: []resource.TestStep{ + { + Config: test.config, + Check: resource.ComposeAggregateTestCheckFunc(checks...), + }, + }, + }) + }) + + client.AssertExpectations(t) + }) + } +} diff --git a/pkg/providers/clientlists/data_akamai_clientlist_lists.go b/pkg/providers/clientlists/data_akamai_clientlist_lists.go index bc6360f62..f95297903 100644 --- a/pkg/providers/clientlists/data_akamai_clientlist_lists.go +++ b/pkg/providers/clientlists/data_akamai_clientlist_lists.go @@ -16,7 +16,7 @@ import ( func dataSourceClientLists() *schema.Resource { return &schema.Resource{ - ReadContext: dataSourceClientListRead, + ReadContext: dataSourceClientListsRead, Schema: map[string]*schema.Schema{ "name": { Type: schema.TypeString, @@ -145,7 +145,7 @@ func dataSourceClientLists() *schema.Resource { } } -func dataSourceClientListRead(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics { +func dataSourceClientListsRead(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics { meta := meta.Must(m) client := inst.Client(meta) logger := meta.Log("CLIENTLIST", "dataSourceClientListRead") diff --git a/pkg/providers/clientlists/provider.go b/pkg/providers/clientlists/provider.go index 46984079e..64ae46143 100644 --- a/pkg/providers/clientlists/provider.go +++ b/pkg/providers/clientlists/provider.go @@ -67,6 +67,7 @@ func (p *Subprovider) SDKResources() map[string]*schema.Resource { func (p *Subprovider) SDKDataSources() map[string]*schema.Resource { return map[string]*schema.Resource{ "akamai_clientlist_lists": dataSourceClientLists(), + "akamai_clientlist_list": dataSourceClientList(), } } diff --git a/pkg/providers/clientlists/testData/TestDSClientList/ClientList.json b/pkg/providers/clientlists/testData/TestDSClientList/ClientList.json new file mode 100644 index 000000000..ee65cd6e0 --- /dev/null +++ b/pkg/providers/clientlists/testData/TestDSClientList/ClientList.json @@ -0,0 +1,103 @@ +{ + "contractId": "ctr_test1", + "createDate": "2023-10-30T17:13:31.575+00:00", + "createdBy": "ccare2", + "deprecated": false, + "groupId": 12345, + "groupName": "group", + "items": [ + { + "createDate": "2023-10-30T17:13:31.603+00:00", + "createdBy": "ccare2", + "description": "test item 1", + "productionStatus": "ACTIVE", + "stagingStatus": "ACTIVE", + "tags": [], + "type": "IP", + "updateDate": "2023-10-30T17:13:31.603+00:00", + "updatedBy": "ccare2", + "value": "107.23.127.59/32" + }, + { + "createDate": "2023-10-30T17:13:31.603+00:00", + "createdBy": "ccare2", + "description": "test item 2", + "productionStatus": "ACTIVE", + "stagingStatus": "ACTIVE", + "tags": [], + "type": "IP", + "updateDate": "2023-10-30T17:13:31.603+00:00", + "updatedBy": "ccare2", + "value": "107.23.127.72/32" + }, + { + "createDate": "2023-10-30T17:13:31.603+00:00", + "createdBy": "ccare2", + "description": "test item 3", + "productionStatus": "ACTIVE", + "stagingStatus": "ACTIVE", + "tags": [], + "type": "IP", + "updateDate": "2023-10-30T17:13:31.603+00:00", + "updatedBy": "ccare2", + "value": "54.246.130.100/32" + }, + { + "createDate": "2023-10-30T17:13:31.603+00:00", + "createdBy": "ccare2", + "description": "test item 4", + "productionStatus": "ACTIVE", + "stagingStatus": "ACTIVE", + "tags": [], + "type": "IP", + "updateDate": "2023-10-30T17:13:31.603+00:00", + "updatedBy": "ccare2", + "value": "54.246.130.102/32" + }, + { + "createDate": "2023-10-30T17:13:31.603+00:00", + "createdBy": "ccare2", + "description": "test item 5", + "productionStatus": "ACTIVE", + "stagingStatus": "ACTIVE", + "tags": [], + "type": "IP", + "updateDate": "2023-10-30T17:13:31.603+00:00", + "updatedBy": "ccare2", + "value": "54.246.130.105/32" + }, + { + "createDate": "2023-10-30T17:13:31.603+00:00", + "createdBy": "ccare2", + "description": "test item 6", + "productionStatus": "ACTIVE", + "stagingStatus": "ACTIVE", + "tags": [], + "type": "IP", + "updateDate": "2023-10-30T17:13:31.603+00:00", + "updatedBy": "ccare2", + "value": "54.246.130.106/32" + } + ], + "itemsCount": 6, + "listId": "123_TEST", + "listType": "CL", + "name": "TEST", + "notes": "Test IP addresses", + "productionActivationStatus": "ACTIVE", + "productionActiveVersion": 1, + "readOnly": false, + "rollbackPossible": false, + "shared": false, + "stagingActivationStatus": "ACTIVE", + "stagingActiveVersion": 1, + "tags": [ + "TEST-TAG-1", + "TEST-TAG-2" + ], + "type": "IP", + "updateDate": "2023-11-21T12:38:39.160+00:00", + "updatedBy": "ccare2", + "upgradedFromNetworkList": false, + "version": 1 +} \ No newline at end of file diff --git a/pkg/providers/clientlists/testData/TestDSClientList/get_list.tf b/pkg/providers/clientlists/testData/TestDSClientList/get_list.tf new file mode 100644 index 000000000..cd904cd44 --- /dev/null +++ b/pkg/providers/clientlists/testData/TestDSClientList/get_list.tf @@ -0,0 +1,7 @@ +provider "akamai" { + edgerc = "../../common/testutils/edgerc" +} + +data "akamai_clientlist_list" "list" { + list_id = "123_TEST" +}