From eb7df792fa3fa59871ec0b102c34cfccab4822e4 Mon Sep 17 00:00:00 2001 From: Diogo Sousa Date: Tue, 31 May 2022 15:35:44 +0100 Subject: [PATCH] Added support for redirect lists. --- list.go | 21 ++++- list_test.go | 211 +++++++++++++++++++++++++++++++++++++++++++++++++-- 2 files changed, 222 insertions(+), 10 deletions(-) diff --git a/list.go b/list.go index 8b1c0699f..e831fb708 100644 --- a/list.go +++ b/list.go @@ -13,6 +13,8 @@ import ( const ( // ListTypeIP specifies a list containing IP addresses. ListTypeIP = "ip" + // ListTypeRedirect specifies a list containing redirects. + ListTypeRedirect = "redirect" ) // ListBulkOperation contains information about a Bulk Operation. @@ -35,10 +37,22 @@ type List struct { ModifiedOn *time.Time `json:"modified_on"` } +// Redirect represents a redirect item in a List. +type Redirect struct { + SourceUrl string `json:"source_url"` + IncludeSubdomains *bool `json:"include_subdomains,omitempty"` + TargetUrl string `json:"target_url"` + StatusCode *int `json:"status_code,omitempty"` + PreserveQueryString *bool `json:"preserve_query_string,omitempty"` + SubpathMatching *bool `json:"subpath_matching,omitempty"` + PreservePathSuffix *bool `json:"preserve_path_suffix,omitempty"` +} + // ListItem contains information about a single List Item. type ListItem struct { ID string `json:"id"` - IP string `json:"ip"` + IP *string `json:"ip,omitempty"` + Redirect *Redirect `json:"redirect,omitempty"` Comment string `json:"comment"` CreatedOn *time.Time `json:"created_on"` ModifiedOn *time.Time `json:"modified_on"` @@ -53,8 +67,9 @@ type ListCreateRequest struct { // ListItemCreateRequest contains data for a new List Item. type ListItemCreateRequest struct { - IP string `json:"ip"` - Comment string `json:"comment"` + IP *string `json:"ip,omitempty"` + Redirect *Redirect `json:"redirect,omitempty"` + Comment string `json:"comment"` } // ListItemDeleteRequest wraps List Items that shall be deleted. diff --git a/list_test.go b/list_test.go index 8c80c2022..6d806ce80 100644 --- a/list_test.go +++ b/list_test.go @@ -301,14 +301,14 @@ func TestListsItemsIP(t *testing.T) { want := []ListItem{ { ID: "2c0fc9fa937b11eaa1b71c4d701ab86e", - IP: "192.0.2.1", + IP: StringPtr("192.0.2.1"), Comment: "Private IP address", CreatedOn: &createdOn, ModifiedOn: &modifiedOn, }, { ID: "2c0fc9fa937b11eaa1b71c4d701ab86e", - IP: "192.0.2.2", + IP: StringPtr("192.0.2.2"), Comment: "Another Private IP address", CreatedOn: &createdOn, ModifiedOn: &modifiedOn, @@ -323,6 +323,111 @@ func TestListsItemsIP(t *testing.T) { } } +func TestListsItemsRedirect(t *testing.T) { + setup() + defer teardown() + + handler := func(w http.ResponseWriter, r *http.Request) { + assert.Equal(t, http.MethodGet, r.Method, "Expected method 'GET', got %s", r.Method) + w.Header().Set("content-type", "application/json") + + if len(r.URL.Query().Get("cursor")) > 0 && r.URL.Query().Get("cursor") == "yyy" { + fmt.Fprint(w, `{ + "result": [ + { + "id": "1c0fc9fa937b11eaa1b71c4d701ab86e", + "redirect": { + "source_url": "www.3fonteinen.be", + "target_url": "https://shop.3fonteinen.be" + }, + "comment": "3F redirect", + "created_on": "2020-01-01T08:00:00Z", + "modified_on": "2020-01-10T14:00:00Z" + } + ], + "result_info": { + "cursors": { + "before": "xxx" + } + }, + "success": true, + "errors": [], + "messages": [] + }`) + } else { + fmt.Fprint(w, `{ + "result": [ + { + "id": "0c0fc9fa937b11eaa1b71c4d701ab86e", + "redirect": { + "source_url": "http://cloudflare.com", + "include_subdomains": true, + "target_url": "https://cloudflare.com", + "status_code": 302, + "preserve_query_string": true, + "subpath_matching": true, + "preserve_path_suffix": false + }, + "comment": "Cloudflare http redirect", + "created_on": "2020-01-01T08:00:00Z", + "modified_on": "2020-01-10T14:00:00Z" + } + ], + "result_info": { + "cursors": { + "before": "xxx", + "after": "yyy" + } + }, + "success": true, + "errors": [], + "messages": [] + }`) + } + } + + mux.HandleFunc("/accounts/"+testAccountID+"/rules/lists/0c0fc9fa937b11eaa1b71c4d701ab86e/items", handler) + + createdOn, _ := time.Parse(time.RFC3339, "2020-01-01T08:00:00Z") + modifiedOn, _ := time.Parse(time.RFC3339, "2020-01-10T14:00:00Z") + + want := []ListItem{ + { + ID: "0c0fc9fa937b11eaa1b71c4d701ab86e", + Redirect: &Redirect{ + SourceUrl: "http://cloudflare.com", + IncludeSubdomains: BoolPtr(true), + TargetUrl: "https://cloudflare.com", + StatusCode: IntPtr(302), + PreserveQueryString: BoolPtr(true), + SubpathMatching: BoolPtr(true), + PreservePathSuffix: BoolPtr(false), + }, + Comment: "Cloudflare http redirect", + CreatedOn: &createdOn, + ModifiedOn: &modifiedOn, + }, + { + ID: "1c0fc9fa937b11eaa1b71c4d701ab86e", + Redirect: &Redirect{ + SourceUrl: "www.3fonteinen.be", + TargetUrl: "https://shop.3fonteinen.be", + }, + Comment: "3F redirect", + CreatedOn: &createdOn, + ModifiedOn: &modifiedOn, + }, + } + + actual, err := client.ListListItems( + context.Background(), + ListListItemsParams{AccountID: testAccountID, ID: "0c0fc9fa937b11eaa1b71c4d701ab86e"}, + ) + if assert.NoError(t, err) { + assert.Equal(t, want, actual) + } +} + func TestCreateListItemsIP(t *testing.T) { setup() defer teardown() @@ -352,10 +457,10 @@ func TestCreateListItemsIP(t *testing.T) { AccountID: testAccountID, ID: "2c0fc9fa937b11eaa1b71c4d701ab86e", Items: []ListItemCreateRequest{{ - IP: "192.0.2.1", + IP: StringPtr("192.0.2.1"), Comment: "Private IP", }, { - IP: "192.0.2.2", + IP: StringPtr("192.0.2.2"), Comment: "Another Private IP", }}}) if assert.NoError(t, err) { @@ -363,6 +468,52 @@ func TestCreateListItemsIP(t *testing.T) { } } +func TestCreateListItemsRedirect(t *testing.T) { + setup() + defer teardown() + + handler := func(w http.ResponseWriter, r *http.Request) { + assert.Equal(t, http.MethodPost, r.Method, "Expected method 'POST', got %s", r.Method) + w.Header().Set("content-type", "application/json") + fmt.Fprint(w, `{ + "result": { + "operation_id": "4da8780eeb215e6cb7f48dd981c4ea02" + }, + "success": true, + "errors": [], + "messages": [] + }`) + } + + mux.HandleFunc("/accounts/"+testAccountID+"/rules/lists/0c0fc9fa937b11eaa1b71c4d701ab86e/items", handler) + + want := ListItemCreateResponse{} + want.Success = true + want.Errors = []ResponseInfo{} + want.Messages = []ResponseInfo{} + want.Result.OperationID = "4da8780eeb215e6cb7f48dd981c4ea02" + + actual, err := client.CreateListItemsAsync(context.Background(), ListCreateItemsParams{ + AccountID: testAccountID, + ID: "0c0fc9fa937b11eaa1b71c4d701ab86e", + Items: []ListItemCreateRequest{{ + Redirect: &Redirect{ + SourceUrl: "www.3fonteinen.be", + TargetUrl: "https://shop.3fonteinen.be", + }, + Comment: "redirect 3F", + }, { + Redirect: &Redirect{ + SourceUrl: "www.cf.com", + TargetUrl: "https://cloudflare.com", + }, + Comment: "Redirect cf", + }}}) + if assert.NoError(t, err) { + assert.Equal(t, want, actual) + } +} + func TestReplaceListItemsIP(t *testing.T) { setup() defer teardown() @@ -392,10 +543,10 @@ func TestReplaceListItemsIP(t *testing.T) { AccountID: testAccountID, ID: "2c0fc9fa937b11eaa1b71c4d701ab86e", Items: []ListItemCreateRequest{{ - IP: "192.0.2.1", + IP: StringPtr("192.0.2.1"), Comment: "Private IP", }, { - IP: "192.0.2.2", + IP: StringPtr("192.0.2.2"), Comment: "Another Private IP", }}}) if assert.NoError(t, err) { @@ -403,6 +554,52 @@ func TestReplaceListItemsIP(t *testing.T) { } } +func TestReplaceListItemsRedirect(t *testing.T) { + setup() + defer teardown() + + handler := func(w http.ResponseWriter, r *http.Request) { + assert.Equal(t, http.MethodPut, r.Method, "Expected method 'PUT', got %s", r.Method) + w.Header().Set("content-type", "application/json") + fmt.Fprint(w, `{ + "result": { + "operation_id": "4da8780eeb215e6cb7f48dd981c4ea02" + }, + "success": true, + "errors": [], + "messages": [] + }`) + } + + mux.HandleFunc("/accounts/"+testAccountID+"/rules/lists/2c0fc9fa937b11eaa1b71c4d701ab86e/items", handler) + + want := ListItemCreateResponse{} + want.Success = true + want.Errors = []ResponseInfo{} + want.Messages = []ResponseInfo{} + want.Result.OperationID = "4da8780eeb215e6cb7f48dd981c4ea02" + + actual, err := client.ReplaceListItemsAsync(context.Background(), ListReplaceItemsParams{ + AccountID: testAccountID, + ID: "2c0fc9fa937b11eaa1b71c4d701ab86e", + Items: []ListItemCreateRequest{{ + Redirect: &Redirect{ + SourceUrl: "www.3fonteinen.be", + TargetUrl: "https://shop.3fonteinen.be", + }, + Comment: "redirect 3F", + }, { + Redirect: &Redirect{ + SourceUrl: "www.cf.com", + TargetUrl: "https://cloudflare.com", + }, + Comment: "Redirect cf", + }}}) + if assert.NoError(t, err) { + assert.Equal(t, want, actual) + } +} + func TestDeleteListItems(t *testing.T) { setup() defer teardown() @@ -468,7 +665,7 @@ func TestGetListItemIP(t *testing.T) { want := ListItem{ ID: "2c0fc9fa937b11eaa1b71c4d701ab86e", - IP: "192.0.2.1", + IP: StringPtr("192.0.2.1"), Comment: "Private IP address", CreatedOn: &createdOn, ModifiedOn: &modifiedOn,