diff --git a/CHANGELOG.md b/CHANGELOG.md index 783593d8..5a73bf21 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,25 @@ # Changelog +# 2.71.0 (August 11, 2024) + +## Notes +- Golang: **v1.22** + +### ZIA Additions + +#### VPN Credentials and Location Management +- Added `POST /locations/bulkDelete` Bulk delete locations up to a maximum of 100 locations per request. The response returns the location IDs that were successfully deleted. [PR #270](https://github.com/zscaler/zscaler-sdk-go/pull/270) + +- Added `POST /vpnCredentials/bulkDelete` Bulk delete vpn credentails up to a maximum of 100 vpn credentials per request. The response returns the vpn credential IDs that were successfully deleted. [PR #270](https://github.com/zscaler/zscaler-sdk-go/pull/270) + +#### Cloud App Control Policies + +- Added `GET /webApplicationRules/ruleTypeMapping` to return backend keys that match the application type string. + +### Bug Fixes + +[PR #270](https://github.com/zscaler/zscaler-sdk-go/pull/270) - Fixed `ports` attribute from `string` to `slice of string` in `locationmanagement`. + # 2.70.0 (July 23, 2024) ## Notes diff --git a/docs/guides/release-notes.md b/docs/guides/release-notes.md index 8e5e7ddc..db5b115b 100644 --- a/docs/guides/release-notes.md +++ b/docs/guides/release-notes.md @@ -13,10 +13,30 @@ Track all Zscaler SDK GO releases. New resources, features, and bug fixes will b --- -``Last updated: v2.70.0`` +``Last updated: v2.71.0`` --- +# 2.71.0 (August 11, 2024) + +## Notes +- Golang: **v1.22** + +### ZIA Additions + +#### VPN Credentials and Location Management +- Added `POST /locations/bulkDelete` Bulk delete locations up to a maximum of 100 locations per request. The response returns the location IDs that were successfully deleted. [PR #270](https://github.com/zscaler/zscaler-sdk-go/pull/270) + +- Added `POST /vpnCredentials/bulkDelete` Bulk delete vpn credentails up to a maximum of 100 vpn credentials per request. The response returns the vpn credential IDs that were successfully deleted. [PR #270](https://github.com/zscaler/zscaler-sdk-go/pull/270) + +#### Cloud App Control Policies + +- Added `GET /webApplicationRules/ruleTypeMapping` to return backend keys that match the application type string. + +### Bug Fixes + +[PR #270](https://github.com/zscaler/zscaler-sdk-go/pull/270) - Fixed `ports` attribute from `string` to `slice of string` in `locationmanagement`. + # 2.70.0 (July 23, 2024) ## Notes diff --git a/zia/client.go b/zia/client.go index a780793d..85bd69a4 100644 --- a/zia/client.go +++ b/zia/client.go @@ -152,40 +152,6 @@ func (client *Client) WithFreshCache() { client.freshCache = true } -// // Create send HTTP Post request. -// func (c *Client) Create(endpoint string, o interface{}) (interface{}, error) { -// if o == nil { -// return nil, errors.New("tried to create with a nil payload not a Struct") -// } -// t := reflect.TypeOf(o) -// if t.Kind() != reflect.Struct { -// return nil, errors.New("tried to create with a " + t.Kind().String() + " not a Struct") -// } -// data, err := json.Marshal(o) -// if err != nil { -// return nil, err -// } - -// resp, err := c.Request(endpoint, "POST", data, "application/json") -// if err != nil { -// return nil, err -// } -// if len(resp) > 0 { -// responseObject := reflect.New(t).Interface() -// err = json.Unmarshal(resp, &responseObject) -// if err != nil { -// return nil, err -// } -// id := reflect.Indirect(reflect.ValueOf(responseObject)).FieldByName("ID") - -// c.Logger.Printf("Created Object with ID %v", id) -// return responseObject, nil -// } else { -// // in case of 204 no content -// return nil, nil -// } -// } - // Create sends an HTTP POST request. func (c *Client) Create(endpoint string, o interface{}) (interface{}, error) { if o == nil { @@ -335,3 +301,31 @@ func (c *Client) Delete(endpoint string) error { } return nil } + +// BulkDelete sends an HTTP POST request for bulk deletion and expects a 204 No Content response. +func (c *Client) BulkDelete(endpoint string, payload interface{}) (*http.Response, error) { + if payload == nil { + return nil, errors.New("tried to delete with a nil payload, expected a struct") + } + + // Marshal the payload into JSON + data, err := json.Marshal(payload) + if err != nil { + return nil, err + } + + // Send the POST request + resp, err := c.Request(endpoint, "POST", data, "application/json") + if err != nil { + return nil, err + } + + // Check the status code (204 No Content expected) + if len(resp) == 0 { + c.Logger.Printf("[DEBUG] Bulk delete successful with 204 No Content") + return &http.Response{StatusCode: 204}, nil + } + + // If the response is not empty, this might indicate an error or unexpected behavior + return &http.Response{StatusCode: 200}, fmt.Errorf("unexpected response: %s", string(resp)) +} diff --git a/zia/services/cloudappcontrol/cloudappcontrol.go b/zia/services/cloudappcontrol/cloudappcontrol.go index 13934e1d..dac3fbe8 100644 --- a/zia/services/cloudappcontrol/cloudappcontrol.go +++ b/zia/services/cloudappcontrol/cloudappcontrol.go @@ -236,3 +236,20 @@ func AllAvailableActions(service *services.Service, ruleType string, payload Ava service.Client.Logger.Printf("[DEBUG] returning available actions: %+v", availableActions) return availableActions, nil } + +// GetRuleTypeMapping retrieves the rule type mapping from the API. +func GetRuleTypeMapping(service *services.Service) (map[string]string, error) { + // Initialize a map to hold the response + var ruleTypeMapping map[string]string + + // Perform the GET request + err := service.Client.Read(webApplicationRulesEndpoint+"/ruleTypeMapping", &ruleTypeMapping) + if err != nil { + return nil, err + } + + // Log the retrieved data + service.Client.Logger.Printf("[DEBUG] Returning web application rule type mapping: %+v", ruleTypeMapping) + + return ruleTypeMapping, nil +} diff --git a/zia/services/cloudappcontrol/cloudappcontrol_test.go b/zia/services/cloudappcontrol/cloudappcontrol_test.go index b3eb081e..d8d1941a 100644 --- a/zia/services/cloudappcontrol/cloudappcontrol_test.go +++ b/zia/services/cloudappcontrol/cloudappcontrol_test.go @@ -1,6 +1,8 @@ package cloudappcontrol import ( + "bytes" + "encoding/json" "fmt" "log" "strings" @@ -232,6 +234,49 @@ func TestAllAvailableActions(t *testing.T) { } } +func TestRuleTypeMapping(t *testing.T) { + ziaClient, err := tests.NewZiaClient() + if err != nil { + t.Fatalf("Error creating client: %v", err) + } + service := services.New(ziaClient) + + // Invoke the GetRuleTypeMapping function + mappings, err := GetRuleTypeMapping(service) + if err != nil { + t.Fatalf("Error getting rule type mappings: %v", err) + } + + // Check if the response is not empty + if len(mappings) == 0 { + t.Error("Expected rule type mappings, but got an empty response") + } + + // Optionally, check if specific keys are present in the response + expectedKeys := []string{ + "Webmail", "Social Networking", "Finance", "Legal", "AI & ML Applications", + } + + for _, key := range expectedKeys { + if _, exists := mappings[key]; !exists { + t.Errorf("Expected key %s in the response, but it was not found", key) + } + } + + // Convert the map back to JSON for logging with HTML escaping disabled + buffer := &bytes.Buffer{} + encoder := json.NewEncoder(buffer) + encoder.SetEscapeHTML(false) + encoder.SetIndent("", " ") + + err = encoder.Encode(mappings) + if err != nil { + t.Fatalf("Error marshalling rule type mappings to JSON: %v", err) + } + + t.Logf("Rule Type Mappings: %s", buffer.String()) +} + func TestRetrieveNonExistentResource(t *testing.T) { client, err := tests.NewZiaClient() if err != nil { diff --git a/zia/services/location/locationmanagement/locationmanagement.go b/zia/services/location/locationmanagement/locationmanagement.go index 40d35d93..e00c4dad 100644 --- a/zia/services/location/locationmanagement/locationmanagement.go +++ b/zia/services/location/locationmanagement/locationmanagement.go @@ -13,6 +13,7 @@ import ( const ( locationsEndpoint = "/locations" subLocationEndpoint = "/sublocations" + maxBulkDeleteIDs = 100 ) // Gets locations only, not sub-locations. When a location matches the given search parameter criteria only its parent location is included in the result set, not its sub-locations. @@ -55,7 +56,7 @@ type Locations struct { IPAddresses []string `json:"ipAddresses,omitempty"` // IP ports that are associated with the location - Ports string `json:"ports,omitempty"` + Ports []string `json:"ports,omitempty"` // VPN User Credentials that are associated with the location. VPNCredentials []VPNCredentials `json:"vpnCredentials,omitempty"` @@ -355,6 +356,20 @@ func Delete(service *services.Service, locationID int) (*http.Response, error) { return nil, nil } +func BulkDelete(service *services.Service, ids []int) (*http.Response, error) { + if len(ids) > maxBulkDeleteIDs { + // Truncate the list to the first 100 IDs + ids = ids[:maxBulkDeleteIDs] + service.Client.Logger.Printf("[INFO] Truncating IDs list to the first %d items", maxBulkDeleteIDs) + } + + // Define the payload + payload := map[string][]int{ + "ids": ids, + } + return service.Client.BulkDelete(locationsEndpoint+"/bulkDelete", payload) +} + func GetAll(service *services.Service) ([]Locations, error) { var locations []Locations // We are assuming this location name will be in the firsy 1000 obejcts diff --git a/zia/services/trafficforwarding/vpncredentials/vpncredentials.go b/zia/services/trafficforwarding/vpncredentials/vpncredentials.go index aae9ca4c..89e24d35 100644 --- a/zia/services/trafficforwarding/vpncredentials/vpncredentials.go +++ b/zia/services/trafficforwarding/vpncredentials/vpncredentials.go @@ -13,6 +13,7 @@ import ( const ( vpnCredentialsEndpoint = "/vpnCredentials" + maxBulkDeleteIDs = 100 ) type VPNCredentials struct { @@ -145,6 +146,23 @@ func Delete(service *services.Service, vpnCredentialID int) error { return nil } +// BulkDeleteVPNCredentials sends a bulk delete request for VPN credentials. +func BulkDelete(service *services.Service, ids []int) (*http.Response, error) { + if len(ids) > maxBulkDeleteIDs { + // Truncate the list to the first 100 IDs + ids = ids[:maxBulkDeleteIDs] + service.Client.Logger.Printf("[INFO] Truncating IDs list to the first %d items", maxBulkDeleteIDs) + } + + // Define the payload + payload := map[string][]int{ + "ids": ids, + } + + // Call the generalized BulkDelete function from the client + return service.Client.BulkDelete(vpnCredentialsEndpoint+"/bulkDelete", payload) +} + func GetAll(service *services.Service) ([]VPNCredentials, error) { var vpnTypes []VPNCredentials err := common.ReadAllPages(service.Client, vpnCredentialsEndpoint, &vpnTypes)