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

Pagination: more consistent, add to changelogs (BREAKING CHANGE) #84

Merged
merged 2 commits into from
Aug 19, 2024
Merged
Show file tree
Hide file tree
Changes from all 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
2 changes: 1 addition & 1 deletion readme/api_registry_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ func Test_APIRegistry_Get(t *testing.T) {

t.Run("when API responds with 404", func(t *testing.T) {
// Arrange
expect := testdata.APISpecResponseVersionEmtpy
expect := testdata.APISpecResponseVersionEmpty
joshbeard marked this conversation as resolved.
Show resolved Hide resolved
gock.New(TestClient.APIURL).
Get(readme.APIRegistryEndpoint + "/invalid").
Reply(404).
Expand Down
45 changes: 8 additions & 37 deletions readme/api_specification.go
Original file line number Diff line number Diff line change
Expand Up @@ -101,47 +101,18 @@ type APISpecificationSaved struct {
//
// API Reference: https://docs.readme.com/reference/getapispecification
func (c APISpecificationClient) GetAll(options ...RequestOptions) ([]APISpecification, *APIResponse, error) {
var specifications []APISpecification
var apiResponse *APIResponse
var err error
hasNextPage := false

// Initialize pagination counter.
page := 1
if len(options) > 0 {
if options[0].Page != 0 {
page = options[0].Page
}
var results []APISpecification
joshbeard marked this conversation as resolved.
Show resolved Hide resolved
opts := parseRequestOptions(options)
apiResponse, err := c.client.fetchAllPages(APISpecificationEndpoint, opts, &results)
if err != nil {
return results, apiResponse, fmt.Errorf("unable to retrieve specifications: %w", err)
}

for {
var specPaginatedResult []APISpecification

apiRequest := &APIRequest{
Method: "GET",
Endpoint: APISpecificationEndpoint,
UseAuth: true,
OkStatusCode: []int{200},
Response: &specPaginatedResult,
}
if len(options) > 0 {
apiRequest.RequestOptions = options[0]
}

apiResponse, hasNextPage, err = c.client.paginatedRequest(apiRequest, page)
if err != nil {
return specifications, apiResponse, fmt.Errorf("unable to retrieve specifications: %w", err)
}
specifications = append(specifications, specPaginatedResult...)

if !hasNextPage {
break
}

page = page + 1
if len(results) == 0 {
return nil, apiResponse, nil
}

return specifications, apiResponse, nil
return results, apiResponse, nil
}

// Get a single API specification with a provided ID.
Expand Down
6 changes: 3 additions & 3 deletions readme/api_specification_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,7 @@ func Test_APISpecification_GetAll(t *testing.T) {
Reply(400).
AddHeader("Link", `<`+apiSpecEndpointPaginated+`&page=2>; rel="next", <>; rel="prev", <>; rel="last"`).
AddHeader("x-total-count", "1").
JSON(testdata.APISpecResponseVersionEmtpy.APIErrorResponse)
JSON(testdata.APISpecResponseVersionEmpty.APIErrorResponse)
defer gock.Off()

// Act
Expand Down Expand Up @@ -272,7 +272,7 @@ func Test_APISpecification_Get(t *testing.T) {
gock.New(TestClient.APIURL).
Get(readme.APISpecificationEndpoint).
Reply(400).
JSON(testdata.APISpecResponseVersionEmtpy.APIErrorResponse)
JSON(testdata.APISpecResponseVersionEmpty.APIErrorResponse)
defer gock.Off()

expect := "unable to retrieve API specifications"
Expand Down Expand Up @@ -442,7 +442,7 @@ func Test_APISpecification_Delete(t *testing.T) {

t.Run("when called with invalid ID and API response with 400", func(t *testing.T) {
// Arrange
expect := testdata.APISpecResponseVersionEmtpy.APIErrorResponse
expect := testdata.APISpecResponseVersionEmpty.APIErrorResponse
gock.New(TestClient.APIURL).
Delete(readme.APISpecificationEndpoint + "/0123456789").
Reply(400).
Expand Down
47 changes: 9 additions & 38 deletions readme/category.go
Original file line number Diff line number Diff line change
Expand Up @@ -104,7 +104,7 @@ type Category struct {
type CategoryParams struct {
// Title is a *required* short title for the category. This is what will show in the sidebar.
Title string `json:"title"`
// Type is tye type of category, which can be "reference" or "guide".
// Type is the type of category, which can be "reference" or "guide".
Type string `json:"type"`
}

Expand Down Expand Up @@ -186,47 +186,18 @@ func validCategoryType(categoryType string) bool {
//
// API Reference: https://docs.readme.com/reference/getcategories
func (c CategoryClient) GetAll(options ...RequestOptions) ([]Category, *APIResponse, error) {
var err error
var categories []Category
var apiResponse *APIResponse
hasNextPage := false

// Initialize pagination counter.
page := 1
if len(options) > 0 {
if options[0].Page != 0 {
page = options[0].Page
}
var results []Category
opts := parseRequestOptions(options)
apiResponse, err := c.client.fetchAllPages(CategoryEndpoint, opts, &results)
if err != nil {
return results, apiResponse, fmt.Errorf("unable to retrieve categories: %w", err)
}

for {
var paginatedResult []Category

apiRequest := &APIRequest{
Method: "GET",
Endpoint: CategoryEndpoint,
UseAuth: true,
OkStatusCode: []int{200},
Response: &paginatedResult,
}
if len(options) > 0 {
apiRequest.RequestOptions = options[0]
}

apiResponse, hasNextPage, err = c.client.paginatedRequest(apiRequest, page)
if err != nil {
return categories, apiResponse, fmt.Errorf("unable to retrieve categories: %w", err)
}
categories = append(categories, paginatedResult...)

if !hasNextPage {
break
}

page = page + 1
if len(results) == 0 {
return nil, apiResponse, nil
}

return categories, apiResponse, nil
return results, apiResponse, nil
}

// Get a single category on ReadMe.com.
Expand Down
93 changes: 0 additions & 93 deletions readme/category_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,99 +9,6 @@ import (
"github.com/stretchr/testify/assert"
)

func Test_Category_GetAll(t *testing.T) {
// Arrange
gock.New(TestClient.APIURL).
Get(readme.CategoryEndpoint).
Reply(200).
AddHeader("Link", `</categories?page=2>; rel="next", <>; rel="prev", <>; rel="last"`).
AddHeader("x-total-count", "3").
JSON(testdata.Categories)
defer gock.Off()

// Act
got, _, err := TestClient.Category.GetAll(readme.RequestOptions{Page: 1})

// Assert
assert.NoError(t, err, "it does not return an error")
assert.Equal(t, testdata.Categories, got, "it returns expected data")
assert.True(t, gock.IsDone(), "it makes the expected API call")
}

func Test_Category_GetAll_Pagination_Invalid(t *testing.T) {
t.Run("when pagination header is invalid", func(t *testing.T) {
// Arrange
var expect []readme.Category
gock.New(TestClient.APIURL).
Get(readme.CategoryEndpoint).
Reply(200).
AddHeader("Link", `</categories?perPage=6&page=10>; rel="next", <>; rel="prev", <>; rel="last"`).
AddHeader("x-total-count", "90").
JSON(expect)
defer gock.Off()

// Act
got, _, err := TestClient.Category.GetAll(readme.RequestOptions{PerPage: 6, Page: 15})

// Asert
assert.NoError(t, err, "it does not return an error")
assert.Equal(t, expect, got, "returns nil []Category")
assert.True(t, gock.IsDone(), "it makes the expected API call")
})

t.Run("when page >= (totalCount / perPage)", func(t *testing.T) {
// Arrange
var expect []readme.Category
gock.New(TestClient.APIURL).
Get(readme.CategoryEndpoint).
MatchParam("page", "14").
MatchParam("perPage", "6").
Reply(200).
AddHeader("Link", `</categories?perPage=6&page=15>; rel="next", <>; rel="prev", <>; rel="last"`).
AddHeader("x-total-count", "90").
JSON(expect)
gock.New(TestClient.APIURL).
Get(readme.CategoryEndpoint).
MatchParam("page", "15").
MatchParam("perPage", "6").
Reply(200).
AddHeader("Link", `</categories?perPage=6&page=15>; rel="next", <>; rel="prev", <>; rel="last"`).
AddHeader("x-total-count", "90").
JSON(expect)
defer gock.Off()

// Act
got, apiResponse, err := TestClient.Category.GetAll(readme.RequestOptions{PerPage: 6, Page: 14})

// Assert
assert.NoError(t, err, "it does not return an error")
assert.Equal(t, "/categories?perPage=6&page=15", apiResponse.Request.Endpoint, "it returns expected endpoint")
assert.Equal(t, expect, got, "it returns expected []Category response")
assert.True(t, gock.IsDone(), "it makes the expected API call")
})

t.Run("when total count header is not a number", func(t *testing.T) {
// Arrange
var expect []readme.Category
gock.New(TestClient.APIURL).
Get(readme.CategoryEndpoint).
Reply(200).
AddHeader("Link", `</categories?page=2>; rel="next", <>; rel="prev", <>; rel="last"`).
AddHeader("x-total-count", "x").
JSON(expect)
defer gock.Off()

// Act
got, _, err := TestClient.Category.GetAll(readme.RequestOptions{PerPage: 6, Page: 15})

// Assert
assert.Error(t, err, "it returns an error")
assert.ErrorContains(t, err, "unable to parse 'x-total-count' header", "it returns the expected error")
assert.Equal(t, expect, got, "it returns nil []Category")
assert.True(t, gock.IsDone(), "it makes the expected API call")
})
}

func Test_Category_Get(t *testing.T) {
// Arrange
expect := testdata.Categories[0]
Expand Down
21 changes: 8 additions & 13 deletions readme/changelog.go
Original file line number Diff line number Diff line change
Expand Up @@ -99,23 +99,18 @@ func validChangelogType(changelogType string) bool {
//
// API Reference: https://docs.readme.com/main/reference/getchangelogs
func (c ChangelogClient) GetAll(options ...RequestOptions) ([]Changelog, *APIResponse, error) {
var response []Changelog

apiRequest := &APIRequest{
Method: "GET",
Endpoint: ChangelogEndpoint,
UseAuth: true,
OkStatusCode: []int{200},
Response: &response,
var results []Changelog
opts := parseRequestOptions(options)
apiResponse, err := c.client.fetchAllPages(ChangelogEndpoint, opts, &results)
if err != nil {
return nil, apiResponse, fmt.Errorf("unable to retrieve changelogs: %w", err)
}

if len(options) > 0 {
apiRequest.RequestOptions = options[0]
if len(results) == 0 {
return nil, apiResponse, nil
}

apiResponse, err := c.client.APIRequest(apiRequest)

return response, apiResponse, err
return results, apiResponse, err
}

// Get retrieves a single changelog from ReadMe.
Expand Down
56 changes: 41 additions & 15 deletions readme/changelog_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -28,21 +28,47 @@ func Test_Changelog_Get(t *testing.T) {
}

func Test_Changelog_GetAll(t *testing.T) {
// Arrange
expect := testdata.Changelogs
gock.New(TestClient.APIURL).
Get(readme.ChangelogEndpoint).
Reply(200).
JSON(expect)
defer gock.Off()

// Act
got, _, err := TestClient.Changelog.GetAll(readme.RequestOptions{Page: 1})

// Assert
assert.NoError(t, err, "it does not return an error")
assert.Equal(t, expect, got, "it returns slice of Changelog structs")
assert.True(t, gock.IsDone(), "it makes the expected API call")
testCases := []struct {
name string
page int
expected []readme.Changelog
}{
{
name: "when called with no page",
page: 1,
expected: testdata.Changelogs,
},
{
name: "when called with page 2",
page: 2,
expected: testdata.Changelogs,
},
{
name: "when there are no changelogs",
page: 1,
},
}

for _, tc := range testCases {
t.Run(tc.name, func(t *testing.T) {
// Arrange
gock.New(TestClient.APIURL).
Get(readme.ChangelogEndpoint).
Reply(200).
AddHeader("Link", `</changelogs&page=2>; rel="next", <>; rel="prev", <>; rel="last"`).
AddHeader("x-total-count", "1").
JSON(tc.expected)
defer gock.Off()

// Act
got, _, err := TestClient.Changelog.GetAll(readme.RequestOptions{Page: 1})

// Assert
assert.NoError(t, err, "it does not return an error")
assert.Equal(t, tc.expected, got, "it returns slice of Changelog structs")
assert.True(t, gock.IsDone(), "it makes the expected API call")
})
}
}

func Test_Changelog_Create(t *testing.T) {
Expand Down
Loading
Loading