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

feat: generic paginator #491

Merged
merged 21 commits into from
Nov 28, 2023
Merged

feat: generic paginator #491

merged 21 commits into from
Nov 28, 2023

Conversation

Integralist
Copy link
Collaborator

@Integralist Integralist commented Nov 17, 2023

This PR replaces the paginators for Services, ACL Entries and Dictionary Items with a generic paginator.

This PR does NOT replace the KV Stores Entries paginator as it has a completely different implementation.

Here is an example of how to consume the generic paginator...

NOTE: I've included some code for custom sorting the returned API data (just in case the user wants to know how to do this themselves) but this code isn't necessary as the APIs already provide a field for sorting the data by a specific field (which again I demonstrate below).

package main

import (
	"fmt"
	"log"
	"os"
	"sort"

	"github.com/fastly/go-fastly/v8/fastly" // uses `replace` directive in go.mod locally
)

const (
	serviceID = "<REDACTED>"
	aclID     = "<REDACTED>"
	dictID    = "<REDACTED>"
)

func main() {
	client, err := fastly.NewClient(os.Getenv("FASTLY_API_KEY"))
	if err != nil {
		log.Fatal(err)
	}

	p := client.GetServices(&fastly.GetServicesInput{
		Direction: "ascend",
		PerPage:   50,
		Sort:      "type",
	})

	var results []*fastly.Service
	for p.HasNext() {
		data, err := p.GetNext()
		if err != nil {
			fmt.Printf("failed to get next page (remaining: %d): %s", p.Remaining(), err)
			return
		}
		results = append(results, data...)
	}

	fmt.Printf("%#v\n", len(results))
	for _, service := range results {
		fmt.Printf("%#v (%s)\n", service.Name, service.Type)
	}

	sort.Stable(servicesByName(results))
	fmt.Printf("\nSorted:\n\n")
	for _, service := range results {
		fmt.Printf("%#v (%s)\n", service.Name, service.Type)
	}

	fmt.Printf("\nUsing ListServices:\n\n")
	listServiceResults, err := client.ListServices(&fastly.ListServicesInput{
		Direction: "ascend",
		Sort:      "type",
	})
	if err != nil {
		log.Fatal(err)
	}
	fmt.Printf("(%d) %#v\n", len(listServiceResults), listServiceResults)

	fmt.Println("---------------------------------------------------------------")

	p2 := client.GetACLEntries(&fastly.GetACLEntriesInput{
		ACLID:     aclID,
		Direction: "ascend",
		PerPage:   50,
		ServiceID: serviceID,
		Sort:      "ip",
	})

	var results2 []*fastly.ACLEntry
	for p2.HasNext() {
		data, err := p2.GetNext()
		if err != nil {
			fmt.Printf("failed to get next page (remaining: %d): %s", p2.Remaining(), err)
			return
		}
		results2 = append(results2, data...)
	}

	fmt.Printf("%#v\n", len(results2))
	for _, aclEntry := range results2 {
		fmt.Printf("%s | %s\n", aclEntry.IP, aclEntry.ID)
	}

	sort.Stable(entriesByID(results2))
	fmt.Printf("\nSorted:\n\n")
	for _, aclEntry := range results2 {
		fmt.Printf("%s | %s\n", aclEntry.IP, aclEntry.ID)
	}

	fmt.Printf("\nUsing ListACLEntries:\n\n")
	listACLResults, err := client.ListACLEntries(&fastly.ListACLEntriesInput{
		ACLID:     aclID,
		Direction: "ascend",
		ServiceID: serviceID,
		Sort:      "ip",
	})
	if err != nil {
		log.Fatal(err)
	}
	fmt.Printf("(%d) %#v\n", len(listACLResults), listACLResults)

	fmt.Println("---------------------------------------------------------------")

	p3 := client.GetDictionaryItems(&fastly.GetDictionaryItemsInput{
		DictionaryID: dictID,
		Direction:    "ascend",
		PerPage:      50,
		ServiceID:    serviceID,
		Sort:         "item_value",
	})

	var results3 []*fastly.DictionaryItem
	for p3.HasNext() {
		data, err := p3.GetNext()
		if err != nil {
			fmt.Printf("failed to get next page (remaining: %d): %s", p3.Remaining(), err)
			return
		}
		results3 = append(results3, data...)
	}

	fmt.Printf("%#v\n", len(results3))
	for _, dictItem := range results3 {
		fmt.Printf("%s | %s\n", dictItem.ItemKey, dictItem.ItemValue)
	}

	sort.Stable(dictionaryItemsByKey(results3))
	fmt.Printf("\nSorted:\n\n")
	for _, dictItem := range results3 {
		fmt.Printf("%s | %s\n", dictItem.ItemKey, dictItem.ItemValue)
	}

	fmt.Printf("\nUsing ListDictionaryItems:\n\n")
	listDictResults, err := client.ListDictionaryItems(&fastly.ListDictionaryItemsInput{
		DictionaryID: dictID,
		Direction:    "ascend",
		ServiceID:    serviceID,
		Sort:         "item_value",
	})
	if err != nil {
		log.Fatal(err)
	}
	fmt.Printf("(%d) %#v\n", len(listDictResults), listDictResults)

	fmt.Println("---------------------------------------------------------------")
}

// servicesByName is a list of services that are sortable by their name.
type servicesByName []*fastly.Service

// Len implement the sortable interface.
func (s servicesByName) Len() int {
	return len(s)
}

// Swap implement the sortable interface.
func (s servicesByName) Swap(i, j int) {
	s[i], s[j] = s[j], s[i]
}

// Less implement the sortable interface.
func (s servicesByName) Less(i, j int) bool {
	return s[i].Name < s[j].Name
}

// entriesByID is a list of ACL entries that are sortable by their ID.
type entriesByID []*fastly.ACLEntry

// Len implements the sortable interface.
func (s entriesByID) Len() int {
	return len(s)
}

// Swap implements the sortable interface.
func (s entriesByID) Swap(i, j int) {
	s[i], s[j] = s[j], s[i]
}

// Less implements the sortable interface.
func (s entriesByID) Less(i, j int) bool {
	return s[i].ID < s[j].ID
}

// dictionaryItemsByKey is a list of Dictionary items that are sortable by their key.
type dictionaryItemsByKey []*fastly.DictionaryItem

// Len implement the sortable interface.
func (s dictionaryItemsByKey) Len() int {
	return len(s)
}

// Swap implement the sortable interface.
func (s dictionaryItemsByKey) Swap(i, j int) {
	s[i], s[j] = s[j], s[i]
}

// Less implement the sortable interface.
func (s dictionaryItemsByKey) Less(i, j int) bool {
	return s[i].ItemKey < s[j].ItemKey
}

fastly/paginator.go Outdated Show resolved Hide resolved
fastly/paginator.go Outdated Show resolved Hide resolved
@Integralist Integralist requested a review from fgsch November 27, 2023 10:53
@fgsch fgsch requested a review from joeshaw November 27, 2023 11:38
@Integralist Integralist marked this pull request as ready for review November 27, 2023 15:42
fastly/acl_entry.go Outdated Show resolved Hide resolved
@Integralist Integralist force-pushed the integralist/generic-paginator branch from 0859580 to c651c8f Compare November 28, 2023 14:46
@Integralist Integralist requested a review from joeshaw November 28, 2023 14:46
Copy link
Member

@joeshaw joeshaw left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks pretty good. One suggestion and one question below.

fastly/acl_entry.go Outdated Show resolved Hide resolved
fastly/acl_entry.go Outdated Show resolved Hide resolved
fastly/paginator.go Outdated Show resolved Hide resolved
@Integralist Integralist requested a review from joeshaw November 28, 2023 16:22
fastly/acl_entry.go Outdated Show resolved Hide resolved
@Integralist Integralist requested a review from joeshaw November 28, 2023 16:43
Copy link
Member

@joeshaw joeshaw left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

👍

@Integralist Integralist merged commit 9a7d6b0 into main Nov 28, 2023
2 checks passed
@Integralist Integralist deleted the integralist/generic-paginator branch November 28, 2023 16:45
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants