internal/genapi: correct list response body and map

When using the new auto-pagination logic, we would previously return
the Body and Map of the last response, instead of one corresponding
to the returned result. Correct this by overwriting the Body and Map
after pagination has finished.
This commit is contained in:
Johan Brandhorst-Satzkorn committed 2024-01-05 07:43:20 -08:00
1 parent af61b688c6
commit acc048a1d1
21 files changed
+687 -81

No files matched your search

+34 -4
View File
@@ -6,6 +6,7 @@ package accounts
import (
"context"
"encoding/json"
"errors"
"fmt"
"net/url"
@@ -352,6 +353,9 @@ func (c *Client) List(ctx context.Context, authMethodId string, opt ...Option) (
for i, item := range target.Items {
idToIndex[item.Id] = i
}
// Removed IDs in the response may contain duplicates,
// maintain a set to avoid returning duplicates to the user.
removedIds := map[string]struct{}{}
for {
req, err := c.client.NewRequest(ctx, "GET", "accounts", nil, apiOpts...)
if err != nil {
@@ -389,7 +393,9 @@ func (c *Client) List(ctx context.Context, authMethodId string, opt ...Option) (
idToIndex[item.Id] = len(target.Items) - 1
}
}
target.RemovedIds = append(target.RemovedIds, page.RemovedIds...)
for _, removedId := range page.RemovedIds {
removedIds[removedId] = struct{}{}
}
target.EstItemCount = page.EstItemCount
target.ListToken = page.ListToken
target.ResponseType = page.ResponseType
@@ -408,10 +414,34 @@ func (c *Client) List(ctx context.Context, authMethodId string, opt ...Option) (
idToIndex[target.Items[i].Id] = i
}
}
// Finally, sort the results again since in-place updates and deletes
// may have shuffled items.
for deletedId := range removedIds {
target.RemovedIds = append(target.RemovedIds, deletedId)
}
// Sort to make response deterministic
slices.Sort(target.RemovedIds)
// Since we paginated to the end, we can avoid confusion
// for the user by setting the estimated item count to the
// length of the items slice. If we don't set this here, it
// will equal the value returned in the last response, which is
// often much smaller than the total number returned.
target.EstItemCount = uint(len(target.Items))
// Sort the results again since in-place updates and deletes
// may have shuffled items. We sort by created time descending
// (most recently created first), same as the API.
slices.SortFunc(target.Items, func(i, j *Account) int {
return i.UpdatedTime.Compare(j.UpdatedTime)
return j.CreatedTime.Compare(i.CreatedTime)
})
// Finally, since we made at least 2 requests to the server to fulfill this
// function call, resp.Body and resp.Map will only contain the most recent response.
// Overwrite them with the true response.
target.response.Body.Reset()
if err := json.NewEncoder(target.response.Body).Encode(target); err != nil {
return nil, fmt.Errorf("error encoding final JSON list response: %w", err)
}
if err := json.Unmarshal(target.response.Body.Bytes(), &target.response.Map); err != nil {
return nil, fmt.Errorf("error encoding final map list response: %w", err)
}
// Note: the HTTP response body is consumed by resp.Decode in the loop,
// so it doesn't need to be updated (it will always be, and has always been, empty).
return target, nil
}
+34 -4
View File
@@ -6,6 +6,7 @@ package authmethods
import (
"context"
"encoding/json"
"errors"
"fmt"
"net/url"
@@ -358,6 +359,9 @@ func (c *Client) List(ctx context.Context, scopeId string, opt ...Option) (*Auth
for i, item := range target.Items {
idToIndex[item.Id] = i
}
// Removed IDs in the response may contain duplicates,
// maintain a set to avoid returning duplicates to the user.
removedIds := map[string]struct{}{}
for {
req, err := c.client.NewRequest(ctx, "GET", "auth-methods", nil, apiOpts...)
if err != nil {
@@ -395,7 +399,9 @@ func (c *Client) List(ctx context.Context, scopeId string, opt ...Option) (*Auth
idToIndex[item.Id] = len(target.Items) - 1
}
}
target.RemovedIds = append(target.RemovedIds, page.RemovedIds...)
for _, removedId := range page.RemovedIds {
removedIds[removedId] = struct{}{}
}
target.EstItemCount = page.EstItemCount
target.ListToken = page.ListToken
target.ResponseType = page.ResponseType
@@ -414,10 +420,34 @@ func (c *Client) List(ctx context.Context, scopeId string, opt ...Option) (*Auth
idToIndex[target.Items[i].Id] = i
}
}
// Finally, sort the results again since in-place updates and deletes
// may have shuffled items.
for deletedId := range removedIds {
target.RemovedIds = append(target.RemovedIds, deletedId)
}
// Sort to make response deterministic
slices.Sort(target.RemovedIds)
// Since we paginated to the end, we can avoid confusion
// for the user by setting the estimated item count to the
// length of the items slice. If we don't set this here, it
// will equal the value returned in the last response, which is
// often much smaller than the total number returned.
target.EstItemCount = uint(len(target.Items))
// Sort the results again since in-place updates and deletes
// may have shuffled items. We sort by created time descending
// (most recently created first), same as the API.
slices.SortFunc(target.Items, func(i, j *AuthMethod) int {
return i.UpdatedTime.Compare(j.UpdatedTime)
return j.CreatedTime.Compare(i.CreatedTime)
})
// Finally, since we made at least 2 requests to the server to fulfill this
// function call, resp.Body and resp.Map will only contain the most recent response.
// Overwrite them with the true response.
target.response.Body.Reset()
if err := json.NewEncoder(target.response.Body).Encode(target); err != nil {
return nil, fmt.Errorf("error encoding final JSON list response: %w", err)
}
if err := json.Unmarshal(target.response.Body.Bytes(), &target.response.Map); err != nil {
return nil, fmt.Errorf("error encoding final map list response: %w", err)
}
// Note: the HTTP response body is consumed by resp.Decode in the loop,
// so it doesn't need to be updated (it will always be, and has always been, empty).
return target, nil
}
+34 -4
View File
@@ -6,6 +6,7 @@ package authtokens
import (
"context"
"encoding/json"
"fmt"
"net/url"
"slices"
@@ -243,6 +244,9 @@ func (c *Client) List(ctx context.Context, scopeId string, opt ...Option) (*Auth
for i, item := range target.Items {
idToIndex[item.Id] = i
}
// Removed IDs in the response may contain duplicates,
// maintain a set to avoid returning duplicates to the user.
removedIds := map[string]struct{}{}
for {
req, err := c.client.NewRequest(ctx, "GET", "auth-tokens", nil, apiOpts...)
if err != nil {
@@ -280,7 +284,9 @@ func (c *Client) List(ctx context.Context, scopeId string, opt ...Option) (*Auth
idToIndex[item.Id] = len(target.Items) - 1
}
}
target.RemovedIds = append(target.RemovedIds, page.RemovedIds...)
for _, removedId := range page.RemovedIds {
removedIds[removedId] = struct{}{}
}
target.EstItemCount = page.EstItemCount
target.ListToken = page.ListToken
target.ResponseType = page.ResponseType
@@ -299,10 +305,34 @@ func (c *Client) List(ctx context.Context, scopeId string, opt ...Option) (*Auth
idToIndex[target.Items[i].Id] = i
}
}
// Finally, sort the results again since in-place updates and deletes
// may have shuffled items.
for deletedId := range removedIds {
target.RemovedIds = append(target.RemovedIds, deletedId)
}
// Sort to make response deterministic
slices.Sort(target.RemovedIds)
// Since we paginated to the end, we can avoid confusion
// for the user by setting the estimated item count to the
// length of the items slice. If we don't set this here, it
// will equal the value returned in the last response, which is
// often much smaller than the total number returned.
target.EstItemCount = uint(len(target.Items))
// Sort the results again since in-place updates and deletes
// may have shuffled items. We sort by created time descending
// (most recently created first), same as the API.
slices.SortFunc(target.Items, func(i, j *AuthToken) int {
return i.UpdatedTime.Compare(j.UpdatedTime)
return j.CreatedTime.Compare(i.CreatedTime)
})
// Finally, since we made at least 2 requests to the server to fulfill this
// function call, resp.Body and resp.Map will only contain the most recent response.
// Overwrite them with the true response.
target.response.Body.Reset()
if err := json.NewEncoder(target.response.Body).Encode(target); err != nil {
return nil, fmt.Errorf("error encoding final JSON list response: %w", err)
}
if err := json.Unmarshal(target.response.Body.Bytes(), &target.response.Map); err != nil {
return nil, fmt.Errorf("error encoding final map list response: %w", err)
}
// Note: the HTTP response body is consumed by resp.Decode in the loop,
// so it doesn't need to be updated (it will always be, and has always been, empty).
return target, nil
}
@@ -6,6 +6,7 @@ package credentiallibraries
import (
"context"
"encoding/json"
"errors"
"fmt"
"net/url"
@@ -358,6 +359,9 @@ func (c *Client) List(ctx context.Context, credentialStoreId string, opt ...Opti
for i, item := range target.Items {
idToIndex[item.Id] = i
}
// Removed IDs in the response may contain duplicates,
// maintain a set to avoid returning duplicates to the user.
removedIds := map[string]struct{}{}
for {
req, err := c.client.NewRequest(ctx, "GET", "credential-libraries", nil, apiOpts...)
if err != nil {
@@ -395,7 +399,9 @@ func (c *Client) List(ctx context.Context, credentialStoreId string, opt ...Opti
idToIndex[item.Id] = len(target.Items) - 1
}
}
target.RemovedIds = append(target.RemovedIds, page.RemovedIds...)
for _, removedId := range page.RemovedIds {
removedIds[removedId] = struct{}{}
}
target.EstItemCount = page.EstItemCount
target.ListToken = page.ListToken
target.ResponseType = page.ResponseType
@@ -414,10 +420,34 @@ func (c *Client) List(ctx context.Context, credentialStoreId string, opt ...Opti
idToIndex[target.Items[i].Id] = i
}
}
// Finally, sort the results again since in-place updates and deletes
// may have shuffled items.
for deletedId := range removedIds {
target.RemovedIds = append(target.RemovedIds, deletedId)
}
// Sort to make response deterministic
slices.Sort(target.RemovedIds)
// Since we paginated to the end, we can avoid confusion
// for the user by setting the estimated item count to the
// length of the items slice. If we don't set this here, it
// will equal the value returned in the last response, which is
// often much smaller than the total number returned.
target.EstItemCount = uint(len(target.Items))
// Sort the results again since in-place updates and deletes
// may have shuffled items. We sort by created time descending
// (most recently created first), same as the API.
slices.SortFunc(target.Items, func(i, j *CredentialLibrary) int {
return i.UpdatedTime.Compare(j.UpdatedTime)
return j.CreatedTime.Compare(i.CreatedTime)
})
// Finally, since we made at least 2 requests to the server to fulfill this
// function call, resp.Body and resp.Map will only contain the most recent response.
// Overwrite them with the true response.
target.response.Body.Reset()
if err := json.NewEncoder(target.response.Body).Encode(target); err != nil {
return nil, fmt.Errorf("error encoding final JSON list response: %w", err)
}
if err := json.Unmarshal(target.response.Body.Bytes(), &target.response.Map); err != nil {
return nil, fmt.Errorf("error encoding final map list response: %w", err)
}
// Note: the HTTP response body is consumed by resp.Decode in the loop,
// so it doesn't need to be updated (it will always be, and has always been, empty).
return target, nil
}
+34 -4
View File
@@ -6,6 +6,7 @@ package credentials
import (
"context"
"encoding/json"
"errors"
"fmt"
"net/url"
@@ -356,6 +357,9 @@ func (c *Client) List(ctx context.Context, credentialStoreId string, opt ...Opti
for i, item := range target.Items {
idToIndex[item.Id] = i
}
// Removed IDs in the response may contain duplicates,
// maintain a set to avoid returning duplicates to the user.
removedIds := map[string]struct{}{}
for {
req, err := c.client.NewRequest(ctx, "GET", "credentials", nil, apiOpts...)
if err != nil {
@@ -393,7 +397,9 @@ func (c *Client) List(ctx context.Context, credentialStoreId string, opt ...Opti
idToIndex[item.Id] = len(target.Items) - 1
}
}
target.RemovedIds = append(target.RemovedIds, page.RemovedIds...)
for _, removedId := range page.RemovedIds {
removedIds[removedId] = struct{}{}
}
target.EstItemCount = page.EstItemCount
target.ListToken = page.ListToken
target.ResponseType = page.ResponseType
@@ -412,10 +418,34 @@ func (c *Client) List(ctx context.Context, credentialStoreId string, opt ...Opti
idToIndex[target.Items[i].Id] = i
}
}
// Finally, sort the results again since in-place updates and deletes
// may have shuffled items.
for deletedId := range removedIds {
target.RemovedIds = append(target.RemovedIds, deletedId)
}
// Sort to make response deterministic
slices.Sort(target.RemovedIds)
// Since we paginated to the end, we can avoid confusion
// for the user by setting the estimated item count to the
// length of the items slice. If we don't set this here, it
// will equal the value returned in the last response, which is
// often much smaller than the total number returned.
target.EstItemCount = uint(len(target.Items))
// Sort the results again since in-place updates and deletes
// may have shuffled items. We sort by created time descending
// (most recently created first), same as the API.
slices.SortFunc(target.Items, func(i, j *Credential) int {
return i.UpdatedTime.Compare(j.UpdatedTime)
return j.CreatedTime.Compare(i.CreatedTime)
})
// Finally, since we made at least 2 requests to the server to fulfill this
// function call, resp.Body and resp.Map will only contain the most recent response.
// Overwrite them with the true response.
target.response.Body.Reset()
if err := json.NewEncoder(target.response.Body).Encode(target); err != nil {
return nil, fmt.Errorf("error encoding final JSON list response: %w", err)
}
if err := json.Unmarshal(target.response.Body.Bytes(), &target.response.Map); err != nil {
return nil, fmt.Errorf("error encoding final map list response: %w", err)
}
// Note: the HTTP response body is consumed by resp.Decode in the loop,
// so it doesn't need to be updated (it will always be, and has always been, empty).
return target, nil
}
+34 -4
View File
@@ -6,6 +6,7 @@ package credentialstores
import (
"context"
"encoding/json"
"errors"
"fmt"
"net/url"
@@ -357,6 +358,9 @@ func (c *Client) List(ctx context.Context, scopeId string, opt ...Option) (*Cred
for i, item := range target.Items {
idToIndex[item.Id] = i
}
// Removed IDs in the response may contain duplicates,
// maintain a set to avoid returning duplicates to the user.
removedIds := map[string]struct{}{}
for {
req, err := c.client.NewRequest(ctx, "GET", "credential-stores", nil, apiOpts...)
if err != nil {
@@ -394,7 +398,9 @@ func (c *Client) List(ctx context.Context, scopeId string, opt ...Option) (*Cred
idToIndex[item.Id] = len(target.Items) - 1
}
}
target.RemovedIds = append(target.RemovedIds, page.RemovedIds...)
for _, removedId := range page.RemovedIds {
removedIds[removedId] = struct{}{}
}
target.EstItemCount = page.EstItemCount
target.ListToken = page.ListToken
target.ResponseType = page.ResponseType
@@ -413,10 +419,34 @@ func (c *Client) List(ctx context.Context, scopeId string, opt ...Option) (*Cred
idToIndex[target.Items[i].Id] = i
}
}
// Finally, sort the results again since in-place updates and deletes
// may have shuffled items.
for deletedId := range removedIds {
target.RemovedIds = append(target.RemovedIds, deletedId)
}
// Sort to make response deterministic
slices.Sort(target.RemovedIds)
// Since we paginated to the end, we can avoid confusion
// for the user by setting the estimated item count to the
// length of the items slice. If we don't set this here, it
// will equal the value returned in the last response, which is
// often much smaller than the total number returned.
target.EstItemCount = uint(len(target.Items))
// Sort the results again since in-place updates and deletes
// may have shuffled items. We sort by created time descending
// (most recently created first), same as the API.
slices.SortFunc(target.Items, func(i, j *CredentialStore) int {
return i.UpdatedTime.Compare(j.UpdatedTime)
return j.CreatedTime.Compare(i.CreatedTime)
})
// Finally, since we made at least 2 requests to the server to fulfill this
// function call, resp.Body and resp.Map will only contain the most recent response.
// Overwrite them with the true response.
target.response.Body.Reset()
if err := json.NewEncoder(target.response.Body).Encode(target); err != nil {
return nil, fmt.Errorf("error encoding final JSON list response: %w", err)
}
if err := json.Unmarshal(target.response.Body.Bytes(), &target.response.Map); err != nil {
return nil, fmt.Errorf("error encoding final map list response: %w", err)
}
// Note: the HTTP response body is consumed by resp.Decode in the loop,
// so it doesn't need to be updated (it will always be, and has always been, empty).
return target, nil
}
+34 -4
View File
@@ -6,6 +6,7 @@ package groups
import (
"context"
"encoding/json"
"errors"
"fmt"
"net/url"
@@ -351,6 +352,9 @@ func (c *Client) List(ctx context.Context, scopeId string, opt ...Option) (*Grou
for i, item := range target.Items {
idToIndex[item.Id] = i
}
// Removed IDs in the response may contain duplicates,
// maintain a set to avoid returning duplicates to the user.
removedIds := map[string]struct{}{}
for {
req, err := c.client.NewRequest(ctx, "GET", "groups", nil, apiOpts...)
if err != nil {
@@ -388,7 +392,9 @@ func (c *Client) List(ctx context.Context, scopeId string, opt ...Option) (*Grou
idToIndex[item.Id] = len(target.Items) - 1
}
}
target.RemovedIds = append(target.RemovedIds, page.RemovedIds...)
for _, removedId := range page.RemovedIds {
removedIds[removedId] = struct{}{}
}
target.EstItemCount = page.EstItemCount
target.ListToken = page.ListToken
target.ResponseType = page.ResponseType
@@ -407,11 +413,35 @@ func (c *Client) List(ctx context.Context, scopeId string, opt ...Option) (*Grou
idToIndex[target.Items[i].Id] = i
}
}
// Finally, sort the results again since in-place updates and deletes
// may have shuffled items.
for deletedId := range removedIds {
target.RemovedIds = append(target.RemovedIds, deletedId)
}
// Sort to make response deterministic
slices.Sort(target.RemovedIds)
// Since we paginated to the end, we can avoid confusion
// for the user by setting the estimated item count to the
// length of the items slice. If we don't set this here, it
// will equal the value returned in the last response, which is
// often much smaller than the total number returned.
target.EstItemCount = uint(len(target.Items))
// Sort the results again since in-place updates and deletes
// may have shuffled items. We sort by created time descending
// (most recently created first), same as the API.
slices.SortFunc(target.Items, func(i, j *Group) int {
return i.UpdatedTime.Compare(j.UpdatedTime)
return j.CreatedTime.Compare(i.CreatedTime)
})
// Finally, since we made at least 2 requests to the server to fulfill this
// function call, resp.Body and resp.Map will only contain the most recent response.
// Overwrite them with the true response.
target.response.Body.Reset()
if err := json.NewEncoder(target.response.Body).Encode(target); err != nil {
return nil, fmt.Errorf("error encoding final JSON list response: %w", err)
}
if err := json.Unmarshal(target.response.Body.Bytes(), &target.response.Map); err != nil {
return nil, fmt.Errorf("error encoding final map list response: %w", err)
}
// Note: the HTTP response body is consumed by resp.Decode in the loop,
// so it doesn't need to be updated (it will always be, and has always been, empty).
return target, nil
}
+34 -4
View File
@@ -6,6 +6,7 @@ package hostcatalogs
import (
"context"
"encoding/json"
"errors"
"fmt"
"net/url"
@@ -362,6 +363,9 @@ func (c *Client) List(ctx context.Context, scopeId string, opt ...Option) (*Host
for i, item := range target.Items {
idToIndex[item.Id] = i
}
// Removed IDs in the response may contain duplicates,
// maintain a set to avoid returning duplicates to the user.
removedIds := map[string]struct{}{}
for {
req, err := c.client.NewRequest(ctx, "GET", "host-catalogs", nil, apiOpts...)
if err != nil {
@@ -399,7 +403,9 @@ func (c *Client) List(ctx context.Context, scopeId string, opt ...Option) (*Host
idToIndex[item.Id] = len(target.Items) - 1
}
}
target.RemovedIds = append(target.RemovedIds, page.RemovedIds...)
for _, removedId := range page.RemovedIds {
removedIds[removedId] = struct{}{}
}
target.EstItemCount = page.EstItemCount
target.ListToken = page.ListToken
target.ResponseType = page.ResponseType
@@ -418,10 +424,34 @@ func (c *Client) List(ctx context.Context, scopeId string, opt ...Option) (*Host
idToIndex[target.Items[i].Id] = i
}
}
// Finally, sort the results again since in-place updates and deletes
// may have shuffled items.
for deletedId := range removedIds {
target.RemovedIds = append(target.RemovedIds, deletedId)
}
// Sort to make response deterministic
slices.Sort(target.RemovedIds)
// Since we paginated to the end, we can avoid confusion
// for the user by setting the estimated item count to the
// length of the items slice. If we don't set this here, it
// will equal the value returned in the last response, which is
// often much smaller than the total number returned.
target.EstItemCount = uint(len(target.Items))
// Sort the results again since in-place updates and deletes
// may have shuffled items. We sort by created time descending
// (most recently created first), same as the API.
slices.SortFunc(target.Items, func(i, j *HostCatalog) int {
return i.UpdatedTime.Compare(j.UpdatedTime)
return j.CreatedTime.Compare(i.CreatedTime)
})
// Finally, since we made at least 2 requests to the server to fulfill this
// function call, resp.Body and resp.Map will only contain the most recent response.
// Overwrite them with the true response.
target.response.Body.Reset()
if err := json.NewEncoder(target.response.Body).Encode(target); err != nil {
return nil, fmt.Errorf("error encoding final JSON list response: %w", err)
}
if err := json.Unmarshal(target.response.Body.Bytes(), &target.response.Map); err != nil {
return nil, fmt.Errorf("error encoding final map list response: %w", err)
}
// Note: the HTTP response body is consumed by resp.Decode in the loop,
// so it doesn't need to be updated (it will always be, and has always been, empty).
return target, nil
}
+34 -4
View File
@@ -6,6 +6,7 @@ package hosts
import (
"context"
"encoding/json"
"errors"
"fmt"
"net/url"
@@ -358,6 +359,9 @@ func (c *Client) List(ctx context.Context, hostCatalogId string, opt ...Option)
for i, item := range target.Items {
idToIndex[item.Id] = i
}
// Removed IDs in the response may contain duplicates,
// maintain a set to avoid returning duplicates to the user.
removedIds := map[string]struct{}{}
for {
req, err := c.client.NewRequest(ctx, "GET", "hosts", nil, apiOpts...)
if err != nil {
@@ -395,7 +399,9 @@ func (c *Client) List(ctx context.Context, hostCatalogId string, opt ...Option)
idToIndex[item.Id] = len(target.Items) - 1
}
}
target.RemovedIds = append(target.RemovedIds, page.RemovedIds...)
for _, removedId := range page.RemovedIds {
removedIds[removedId] = struct{}{}
}
target.EstItemCount = page.EstItemCount
target.ListToken = page.ListToken
target.ResponseType = page.ResponseType
@@ -414,10 +420,34 @@ func (c *Client) List(ctx context.Context, hostCatalogId string, opt ...Option)
idToIndex[target.Items[i].Id] = i
}
}
// Finally, sort the results again since in-place updates and deletes
// may have shuffled items.
for deletedId := range removedIds {
target.RemovedIds = append(target.RemovedIds, deletedId)
}
// Sort to make response deterministic
slices.Sort(target.RemovedIds)
// Since we paginated to the end, we can avoid confusion
// for the user by setting the estimated item count to the
// length of the items slice. If we don't set this here, it
// will equal the value returned in the last response, which is
// often much smaller than the total number returned.
target.EstItemCount = uint(len(target.Items))
// Sort the results again since in-place updates and deletes
// may have shuffled items. We sort by created time descending
// (most recently created first), same as the API.
slices.SortFunc(target.Items, func(i, j *Host) int {
return i.UpdatedTime.Compare(j.UpdatedTime)
return j.CreatedTime.Compare(i.CreatedTime)
})
// Finally, since we made at least 2 requests to the server to fulfill this
// function call, resp.Body and resp.Map will only contain the most recent response.
// Overwrite them with the true response.
target.response.Body.Reset()
if err := json.NewEncoder(target.response.Body).Encode(target); err != nil {
return nil, fmt.Errorf("error encoding final JSON list response: %w", err)
}
if err := json.Unmarshal(target.response.Body.Bytes(), &target.response.Map); err != nil {
return nil, fmt.Errorf("error encoding final map list response: %w", err)
}
// Note: the HTTP response body is consumed by resp.Decode in the loop,
// so it doesn't need to be updated (it will always be, and has always been, empty).
return target, nil
}
+34 -4
View File
@@ -6,6 +6,7 @@ package hostsets
import (
"context"
"encoding/json"
"errors"
"fmt"
"net/url"
@@ -356,6 +357,9 @@ func (c *Client) List(ctx context.Context, hostCatalogId string, opt ...Option)
for i, item := range target.Items {
idToIndex[item.Id] = i
}
// Removed IDs in the response may contain duplicates,
// maintain a set to avoid returning duplicates to the user.
removedIds := map[string]struct{}{}
for {
req, err := c.client.NewRequest(ctx, "GET", "host-sets", nil, apiOpts...)
if err != nil {
@@ -393,7 +397,9 @@ func (c *Client) List(ctx context.Context, hostCatalogId string, opt ...Option)
idToIndex[item.Id] = len(target.Items) - 1
}
}
target.RemovedIds = append(target.RemovedIds, page.RemovedIds...)
for _, removedId := range page.RemovedIds {
removedIds[removedId] = struct{}{}
}
target.EstItemCount = page.EstItemCount
target.ListToken = page.ListToken
target.ResponseType = page.ResponseType
@@ -412,11 +418,35 @@ func (c *Client) List(ctx context.Context, hostCatalogId string, opt ...Option)
idToIndex[target.Items[i].Id] = i
}
}
// Finally, sort the results again since in-place updates and deletes
// may have shuffled items.
for deletedId := range removedIds {
target.RemovedIds = append(target.RemovedIds, deletedId)
}
// Sort to make response deterministic
slices.Sort(target.RemovedIds)
// Since we paginated to the end, we can avoid confusion
// for the user by setting the estimated item count to the
// length of the items slice. If we don't set this here, it
// will equal the value returned in the last response, which is
// often much smaller than the total number returned.
target.EstItemCount = uint(len(target.Items))
// Sort the results again since in-place updates and deletes
// may have shuffled items. We sort by created time descending
// (most recently created first), same as the API.
slices.SortFunc(target.Items, func(i, j *HostSet) int {
return i.UpdatedTime.Compare(j.UpdatedTime)
return j.CreatedTime.Compare(i.CreatedTime)
})
// Finally, since we made at least 2 requests to the server to fulfill this
// function call, resp.Body and resp.Map will only contain the most recent response.
// Overwrite them with the true response.
target.response.Body.Reset()
if err := json.NewEncoder(target.response.Body).Encode(target); err != nil {
return nil, fmt.Errorf("error encoding final JSON list response: %w", err)
}
if err := json.Unmarshal(target.response.Body.Bytes(), &target.response.Map); err != nil {
return nil, fmt.Errorf("error encoding final map list response: %w", err)
}
// Note: the HTTP response body is consumed by resp.Decode in the loop,
// so it doesn't need to be updated (it will always be, and has always been, empty).
return target, nil
}
+34 -4
View File
@@ -6,6 +6,7 @@ package managedgroups
import (
"context"
"encoding/json"
"errors"
"fmt"
"net/url"
@@ -352,6 +353,9 @@ func (c *Client) List(ctx context.Context, authMethodId string, opt ...Option) (
for i, item := range target.Items {
idToIndex[item.Id] = i
}
// Removed IDs in the response may contain duplicates,
// maintain a set to avoid returning duplicates to the user.
removedIds := map[string]struct{}{}
for {
req, err := c.client.NewRequest(ctx, "GET", "managed-groups", nil, apiOpts...)
if err != nil {
@@ -389,7 +393,9 @@ func (c *Client) List(ctx context.Context, authMethodId string, opt ...Option) (
idToIndex[item.Id] = len(target.Items) - 1
}
}
target.RemovedIds = append(target.RemovedIds, page.RemovedIds...)
for _, removedId := range page.RemovedIds {
removedIds[removedId] = struct{}{}
}
target.EstItemCount = page.EstItemCount
target.ListToken = page.ListToken
target.ResponseType = page.ResponseType
@@ -408,10 +414,34 @@ func (c *Client) List(ctx context.Context, authMethodId string, opt ...Option) (
idToIndex[target.Items[i].Id] = i
}
}
// Finally, sort the results again since in-place updates and deletes
// may have shuffled items.
for deletedId := range removedIds {
target.RemovedIds = append(target.RemovedIds, deletedId)
}
// Sort to make response deterministic
slices.Sort(target.RemovedIds)
// Since we paginated to the end, we can avoid confusion
// for the user by setting the estimated item count to the
// length of the items slice. If we don't set this here, it
// will equal the value returned in the last response, which is
// often much smaller than the total number returned.
target.EstItemCount = uint(len(target.Items))
// Sort the results again since in-place updates and deletes
// may have shuffled items. We sort by created time descending
// (most recently created first), same as the API.
slices.SortFunc(target.Items, func(i, j *ManagedGroup) int {
return i.UpdatedTime.Compare(j.UpdatedTime)
return j.CreatedTime.Compare(i.CreatedTime)
})
// Finally, since we made at least 2 requests to the server to fulfill this
// function call, resp.Body and resp.Map will only contain the most recent response.
// Overwrite them with the true response.
target.response.Body.Reset()
if err := json.NewEncoder(target.response.Body).Encode(target); err != nil {
return nil, fmt.Errorf("error encoding final JSON list response: %w", err)
}
if err := json.Unmarshal(target.response.Body.Bytes(), &target.response.Map); err != nil {
return nil, fmt.Errorf("error encoding final map list response: %w", err)
}
// Note: the HTTP response body is consumed by resp.Decode in the loop,
// so it doesn't need to be updated (it will always be, and has always been, empty).
return target, nil
}
+34 -4
View File
@@ -6,6 +6,7 @@ package roles
import (
"context"
"encoding/json"
"errors"
"fmt"
"net/url"
@@ -354,6 +355,9 @@ func (c *Client) List(ctx context.Context, scopeId string, opt ...Option) (*Role
for i, item := range target.Items {
idToIndex[item.Id] = i
}
// Removed IDs in the response may contain duplicates,
// maintain a set to avoid returning duplicates to the user.
removedIds := map[string]struct{}{}
for {
req, err := c.client.NewRequest(ctx, "GET", "roles", nil, apiOpts...)
if err != nil {
@@ -391,7 +395,9 @@ func (c *Client) List(ctx context.Context, scopeId string, opt ...Option) (*Role
idToIndex[item.Id] = len(target.Items) - 1
}
}
target.RemovedIds = append(target.RemovedIds, page.RemovedIds...)
for _, removedId := range page.RemovedIds {
removedIds[removedId] = struct{}{}
}
target.EstItemCount = page.EstItemCount
target.ListToken = page.ListToken
target.ResponseType = page.ResponseType
@@ -410,11 +416,35 @@ func (c *Client) List(ctx context.Context, scopeId string, opt ...Option) (*Role
idToIndex[target.Items[i].Id] = i
}
}
// Finally, sort the results again since in-place updates and deletes
// may have shuffled items.
for deletedId := range removedIds {
target.RemovedIds = append(target.RemovedIds, deletedId)
}
// Sort to make response deterministic
slices.Sort(target.RemovedIds)
// Since we paginated to the end, we can avoid confusion
// for the user by setting the estimated item count to the
// length of the items slice. If we don't set this here, it
// will equal the value returned in the last response, which is
// often much smaller than the total number returned.
target.EstItemCount = uint(len(target.Items))
// Sort the results again since in-place updates and deletes
// may have shuffled items. We sort by created time descending
// (most recently created first), same as the API.
slices.SortFunc(target.Items, func(i, j *Role) int {
return i.UpdatedTime.Compare(j.UpdatedTime)
return j.CreatedTime.Compare(i.CreatedTime)
})
// Finally, since we made at least 2 requests to the server to fulfill this
// function call, resp.Body and resp.Map will only contain the most recent response.
// Overwrite them with the true response.
target.response.Body.Reset()
if err := json.NewEncoder(target.response.Body).Encode(target); err != nil {
return nil, fmt.Errorf("error encoding final JSON list response: %w", err)
}
if err := json.Unmarshal(target.response.Body.Bytes(), &target.response.Map); err != nil {
return nil, fmt.Errorf("error encoding final map list response: %w", err)
}
// Note: the HTTP response body is consumed by resp.Decode in the loop,
// so it doesn't need to be updated (it will always be, and has always been, empty).
return target, nil
}
+34 -4
View File
@@ -6,6 +6,7 @@ package scopes
import (
"context"
"encoding/json"
"errors"
"fmt"
"net/url"
@@ -351,6 +352,9 @@ func (c *Client) List(ctx context.Context, scopeId string, opt ...Option) (*Scop
for i, item := range target.Items {
idToIndex[item.Id] = i
}
// Removed IDs in the response may contain duplicates,
// maintain a set to avoid returning duplicates to the user.
removedIds := map[string]struct{}{}
for {
req, err := c.client.NewRequest(ctx, "GET", "scopes", nil, apiOpts...)
if err != nil {
@@ -388,7 +392,9 @@ func (c *Client) List(ctx context.Context, scopeId string, opt ...Option) (*Scop
idToIndex[item.Id] = len(target.Items) - 1
}
}
target.RemovedIds = append(target.RemovedIds, page.RemovedIds...)
for _, removedId := range page.RemovedIds {
removedIds[removedId] = struct{}{}
}
target.EstItemCount = page.EstItemCount
target.ListToken = page.ListToken
target.ResponseType = page.ResponseType
@@ -407,10 +413,34 @@ func (c *Client) List(ctx context.Context, scopeId string, opt ...Option) (*Scop
idToIndex[target.Items[i].Id] = i
}
}
// Finally, sort the results again since in-place updates and deletes
// may have shuffled items.
for deletedId := range removedIds {
target.RemovedIds = append(target.RemovedIds, deletedId)
}
// Sort to make response deterministic
slices.Sort(target.RemovedIds)
// Since we paginated to the end, we can avoid confusion
// for the user by setting the estimated item count to the
// length of the items slice. If we don't set this here, it
// will equal the value returned in the last response, which is
// often much smaller than the total number returned.
target.EstItemCount = uint(len(target.Items))
// Sort the results again since in-place updates and deletes
// may have shuffled items. We sort by created time descending
// (most recently created first), same as the API.
slices.SortFunc(target.Items, func(i, j *Scope) int {
return i.UpdatedTime.Compare(j.UpdatedTime)
return j.CreatedTime.Compare(i.CreatedTime)
})
// Finally, since we made at least 2 requests to the server to fulfill this
// function call, resp.Body and resp.Map will only contain the most recent response.
// Overwrite them with the true response.
target.response.Body.Reset()
if err := json.NewEncoder(target.response.Body).Encode(target); err != nil {
return nil, fmt.Errorf("error encoding final JSON list response: %w", err)
}
if err := json.Unmarshal(target.response.Body.Bytes(), &target.response.Map); err != nil {
return nil, fmt.Errorf("error encoding final map list response: %w", err)
}
// Note: the HTTP response body is consumed by resp.Decode in the loop,
// so it doesn't need to be updated (it will always be, and has always been, empty).
return target, nil
}
+34 -4
View File
@@ -6,6 +6,7 @@ package sessionrecordings
import (
"context"
"encoding/json"
"fmt"
"net/url"
"slices"
@@ -193,6 +194,9 @@ func (c *Client) List(ctx context.Context, scopeId string, opt ...Option) (*Sess
for i, item := range target.Items {
idToIndex[item.Id] = i
}
// Removed IDs in the response may contain duplicates,
// maintain a set to avoid returning duplicates to the user.
removedIds := map[string]struct{}{}
for {
req, err := c.client.NewRequest(ctx, "GET", "session-recordings", nil, apiOpts...)
if err != nil {
@@ -230,7 +234,9 @@ func (c *Client) List(ctx context.Context, scopeId string, opt ...Option) (*Sess
idToIndex[item.Id] = len(target.Items) - 1
}
}
target.RemovedIds = append(target.RemovedIds, page.RemovedIds...)
for _, removedId := range page.RemovedIds {
removedIds[removedId] = struct{}{}
}
target.EstItemCount = page.EstItemCount
target.ListToken = page.ListToken
target.ResponseType = page.ResponseType
@@ -249,10 +255,34 @@ func (c *Client) List(ctx context.Context, scopeId string, opt ...Option) (*Sess
idToIndex[target.Items[i].Id] = i
}
}
// Finally, sort the results again since in-place updates and deletes
// may have shuffled items.
for deletedId := range removedIds {
target.RemovedIds = append(target.RemovedIds, deletedId)
}
// Sort to make response deterministic
slices.Sort(target.RemovedIds)
// Since we paginated to the end, we can avoid confusion
// for the user by setting the estimated item count to the
// length of the items slice. If we don't set this here, it
// will equal the value returned in the last response, which is
// often much smaller than the total number returned.
target.EstItemCount = uint(len(target.Items))
// Sort the results again since in-place updates and deletes
// may have shuffled items. We sort by created time descending
// (most recently created first), same as the API.
slices.SortFunc(target.Items, func(i, j *SessionRecording) int {
return i.UpdatedTime.Compare(j.UpdatedTime)
return j.CreatedTime.Compare(i.CreatedTime)
})
// Finally, since we made at least 2 requests to the server to fulfill this
// function call, resp.Body and resp.Map will only contain the most recent response.
// Overwrite them with the true response.
target.response.Body.Reset()
if err := json.NewEncoder(target.response.Body).Encode(target); err != nil {
return nil, fmt.Errorf("error encoding final JSON list response: %w", err)
}
if err := json.Unmarshal(target.response.Body.Bytes(), &target.response.Map); err != nil {
return nil, fmt.Errorf("error encoding final map list response: %w", err)
}
// Note: the HTTP response body is consumed by resp.Decode in the loop,
// so it doesn't need to be updated (it will always be, and has always been, empty).
return target, nil
}
+34 -4
View File
@@ -6,6 +6,7 @@ package sessions
import (
"context"
"encoding/json"
"fmt"
"net/url"
"slices"
@@ -210,6 +211,9 @@ func (c *Client) List(ctx context.Context, scopeId string, opt ...Option) (*Sess
for i, item := range target.Items {
idToIndex[item.Id] = i
}
// Removed IDs in the response may contain duplicates,
// maintain a set to avoid returning duplicates to the user.
removedIds := map[string]struct{}{}
for {
req, err := c.client.NewRequest(ctx, "GET", "sessions", nil, apiOpts...)
if err != nil {
@@ -247,7 +251,9 @@ func (c *Client) List(ctx context.Context, scopeId string, opt ...Option) (*Sess
idToIndex[item.Id] = len(target.Items) - 1
}
}
target.RemovedIds = append(target.RemovedIds, page.RemovedIds...)
for _, removedId := range page.RemovedIds {
removedIds[removedId] = struct{}{}
}
target.EstItemCount = page.EstItemCount
target.ListToken = page.ListToken
target.ResponseType = page.ResponseType
@@ -266,10 +272,34 @@ func (c *Client) List(ctx context.Context, scopeId string, opt ...Option) (*Sess
idToIndex[target.Items[i].Id] = i
}
}
// Finally, sort the results again since in-place updates and deletes
// may have shuffled items.
for deletedId := range removedIds {
target.RemovedIds = append(target.RemovedIds, deletedId)
}
// Sort to make response deterministic
slices.Sort(target.RemovedIds)
// Since we paginated to the end, we can avoid confusion
// for the user by setting the estimated item count to the
// length of the items slice. If we don't set this here, it
// will equal the value returned in the last response, which is
// often much smaller than the total number returned.
target.EstItemCount = uint(len(target.Items))
// Sort the results again since in-place updates and deletes
// may have shuffled items. We sort by created time descending
// (most recently created first), same as the API.
slices.SortFunc(target.Items, func(i, j *Session) int {
return i.UpdatedTime.Compare(j.UpdatedTime)
return j.CreatedTime.Compare(i.CreatedTime)
})
// Finally, since we made at least 2 requests to the server to fulfill this
// function call, resp.Body and resp.Map will only contain the most recent response.
// Overwrite them with the true response.
target.response.Body.Reset()
if err := json.NewEncoder(target.response.Body).Encode(target); err != nil {
return nil, fmt.Errorf("error encoding final JSON list response: %w", err)
}
if err := json.Unmarshal(target.response.Body.Bytes(), &target.response.Map); err != nil {
return nil, fmt.Errorf("error encoding final map list response: %w", err)
}
// Note: the HTTP response body is consumed by resp.Decode in the loop,
// so it doesn't need to be updated (it will always be, and has always been, empty).
return target, nil
}
+34 -4
View File
@@ -6,6 +6,7 @@ package storagebuckets
import (
"context"
"encoding/json"
"errors"
"fmt"
"net/url"
@@ -359,6 +360,9 @@ func (c *Client) List(ctx context.Context, scopeId string, opt ...Option) (*Stor
for i, item := range target.Items {
idToIndex[item.Id] = i
}
// Removed IDs in the response may contain duplicates,
// maintain a set to avoid returning duplicates to the user.
removedIds := map[string]struct{}{}
for {
req, err := c.client.NewRequest(ctx, "GET", "storage-buckets", nil, apiOpts...)
if err != nil {
@@ -396,7 +400,9 @@ func (c *Client) List(ctx context.Context, scopeId string, opt ...Option) (*Stor
idToIndex[item.Id] = len(target.Items) - 1
}
}
target.RemovedIds = append(target.RemovedIds, page.RemovedIds...)
for _, removedId := range page.RemovedIds {
removedIds[removedId] = struct{}{}
}
target.EstItemCount = page.EstItemCount
target.ListToken = page.ListToken
target.ResponseType = page.ResponseType
@@ -415,10 +421,34 @@ func (c *Client) List(ctx context.Context, scopeId string, opt ...Option) (*Stor
idToIndex[target.Items[i].Id] = i
}
}
// Finally, sort the results again since in-place updates and deletes
// may have shuffled items.
for deletedId := range removedIds {
target.RemovedIds = append(target.RemovedIds, deletedId)
}
// Sort to make response deterministic
slices.Sort(target.RemovedIds)
// Since we paginated to the end, we can avoid confusion
// for the user by setting the estimated item count to the
// length of the items slice. If we don't set this here, it
// will equal the value returned in the last response, which is
// often much smaller than the total number returned.
target.EstItemCount = uint(len(target.Items))
// Sort the results again since in-place updates and deletes
// may have shuffled items. We sort by created time descending
// (most recently created first), same as the API.
slices.SortFunc(target.Items, func(i, j *StorageBucket) int {
return i.UpdatedTime.Compare(j.UpdatedTime)
return j.CreatedTime.Compare(i.CreatedTime)
})
// Finally, since we made at least 2 requests to the server to fulfill this
// function call, resp.Body and resp.Map will only contain the most recent response.
// Overwrite them with the true response.
target.response.Body.Reset()
if err := json.NewEncoder(target.response.Body).Encode(target); err != nil {
return nil, fmt.Errorf("error encoding final JSON list response: %w", err)
}
if err := json.Unmarshal(target.response.Body.Bytes(), &target.response.Map); err != nil {
return nil, fmt.Errorf("error encoding final map list response: %w", err)
}
// Note: the HTTP response body is consumed by resp.Decode in the loop,
// so it doesn't need to be updated (it will always be, and has always been, empty).
return target, nil
}
+3 -1
View File
@@ -4,7 +4,9 @@
package targets
import "encoding/json"
import (
"encoding/json"
)
type SessionSecret struct {
Raw json.RawMessage `json:"raw,omitempty"`
+34 -4
View File
@@ -6,6 +6,7 @@ package targets
import (
"context"
"encoding/json"
"errors"
"fmt"
"net/url"
@@ -368,6 +369,9 @@ func (c *Client) List(ctx context.Context, scopeId string, opt ...Option) (*Targ
for i, item := range target.Items {
idToIndex[item.Id] = i
}
// Removed IDs in the response may contain duplicates,
// maintain a set to avoid returning duplicates to the user.
removedIds := map[string]struct{}{}
for {
req, err := c.client.NewRequest(ctx, "GET", "targets", nil, apiOpts...)
if err != nil {
@@ -405,7 +409,9 @@ func (c *Client) List(ctx context.Context, scopeId string, opt ...Option) (*Targ
idToIndex[item.Id] = len(target.Items) - 1
}
}
target.RemovedIds = append(target.RemovedIds, page.RemovedIds...)
for _, removedId := range page.RemovedIds {
removedIds[removedId] = struct{}{}
}
target.EstItemCount = page.EstItemCount
target.ListToken = page.ListToken
target.ResponseType = page.ResponseType
@@ -424,11 +430,35 @@ func (c *Client) List(ctx context.Context, scopeId string, opt ...Option) (*Targ
idToIndex[target.Items[i].Id] = i
}
}
// Finally, sort the results again since in-place updates and deletes
// may have shuffled items.
for deletedId := range removedIds {
target.RemovedIds = append(target.RemovedIds, deletedId)
}
// Sort to make response deterministic
slices.Sort(target.RemovedIds)
// Since we paginated to the end, we can avoid confusion
// for the user by setting the estimated item count to the
// length of the items slice. If we don't set this here, it
// will equal the value returned in the last response, which is
// often much smaller than the total number returned.
target.EstItemCount = uint(len(target.Items))
// Sort the results again since in-place updates and deletes
// may have shuffled items. We sort by created time descending
// (most recently created first), same as the API.
slices.SortFunc(target.Items, func(i, j *Target) int {
return i.UpdatedTime.Compare(j.UpdatedTime)
return j.CreatedTime.Compare(i.CreatedTime)
})
// Finally, since we made at least 2 requests to the server to fulfill this
// function call, resp.Body and resp.Map will only contain the most recent response.
// Overwrite them with the true response.
target.response.Body.Reset()
if err := json.NewEncoder(target.response.Body).Encode(target); err != nil {
return nil, fmt.Errorf("error encoding final JSON list response: %w", err)
}
if err := json.Unmarshal(target.response.Body.Bytes(), &target.response.Map); err != nil {
return nil, fmt.Errorf("error encoding final map list response: %w", err)
}
// Note: the HTTP response body is consumed by resp.Decode in the loop,
// so it doesn't need to be updated (it will always be, and has always been, empty).
return target, nil
}
+34 -4
View File
@@ -6,6 +6,7 @@ package users
import (
"context"
"encoding/json"
"errors"
"fmt"
"net/url"
@@ -355,6 +356,9 @@ func (c *Client) List(ctx context.Context, scopeId string, opt ...Option) (*User
for i, item := range target.Items {
idToIndex[item.Id] = i
}
// Removed IDs in the response may contain duplicates,
// maintain a set to avoid returning duplicates to the user.
removedIds := map[string]struct{}{}
for {
req, err := c.client.NewRequest(ctx, "GET", "users", nil, apiOpts...)
if err != nil {
@@ -392,7 +396,9 @@ func (c *Client) List(ctx context.Context, scopeId string, opt ...Option) (*User
idToIndex[item.Id] = len(target.Items) - 1
}
}
target.RemovedIds = append(target.RemovedIds, page.RemovedIds...)
for _, removedId := range page.RemovedIds {
removedIds[removedId] = struct{}{}
}
target.EstItemCount = page.EstItemCount
target.ListToken = page.ListToken
target.ResponseType = page.ResponseType
@@ -411,11 +417,35 @@ func (c *Client) List(ctx context.Context, scopeId string, opt ...Option) (*User
idToIndex[target.Items[i].Id] = i
}
}
// Finally, sort the results again since in-place updates and deletes
// may have shuffled items.
for deletedId := range removedIds {
target.RemovedIds = append(target.RemovedIds, deletedId)
}
// Sort to make response deterministic
slices.Sort(target.RemovedIds)
// Since we paginated to the end, we can avoid confusion
// for the user by setting the estimated item count to the
// length of the items slice. If we don't set this here, it
// will equal the value returned in the last response, which is
// often much smaller than the total number returned.
target.EstItemCount = uint(len(target.Items))
// Sort the results again since in-place updates and deletes
// may have shuffled items. We sort by created time descending
// (most recently created first), same as the API.
slices.SortFunc(target.Items, func(i, j *User) int {
return i.UpdatedTime.Compare(j.UpdatedTime)
return j.CreatedTime.Compare(i.CreatedTime)
})
// Finally, since we made at least 2 requests to the server to fulfill this
// function call, resp.Body and resp.Map will only contain the most recent response.
// Overwrite them with the true response.
target.response.Body.Reset()
if err := json.NewEncoder(target.response.Body).Encode(target); err != nil {
return nil, fmt.Errorf("error encoding final JSON list response: %w", err)
}
if err := json.Unmarshal(target.response.Body.Bytes(), &target.response.Map); err != nil {
return nil, fmt.Errorf("error encoding final map list response: %w", err)
}
// Note: the HTTP response body is consumed by resp.Decode in the loop,
// so it doesn't need to be updated (it will always be, and has always been, empty).
return target, nil
}
+34 -4
View File
@@ -6,6 +6,7 @@ package workers
import (
"context"
"encoding/json"
"errors"
"fmt"
"net/url"
@@ -409,6 +410,9 @@ func (c *Client) List(ctx context.Context, scopeId string, opt ...Option) (*Work
for i, item := range target.Items {
idToIndex[item.Id] = i
}
// Removed IDs in the response may contain duplicates,
// maintain a set to avoid returning duplicates to the user.
removedIds := map[string]struct{}{}
for {
req, err := c.client.NewRequest(ctx, "GET", "workers", nil, apiOpts...)
if err != nil {
@@ -446,7 +450,9 @@ func (c *Client) List(ctx context.Context, scopeId string, opt ...Option) (*Work
idToIndex[item.Id] = len(target.Items) - 1
}
}
target.RemovedIds = append(target.RemovedIds, page.RemovedIds...)
for _, removedId := range page.RemovedIds {
removedIds[removedId] = struct{}{}
}
target.EstItemCount = page.EstItemCount
target.ListToken = page.ListToken
target.ResponseType = page.ResponseType
@@ -465,11 +471,35 @@ func (c *Client) List(ctx context.Context, scopeId string, opt ...Option) (*Work
idToIndex[target.Items[i].Id] = i
}
}
// Finally, sort the results again since in-place updates and deletes
// may have shuffled items.
for deletedId := range removedIds {
target.RemovedIds = append(target.RemovedIds, deletedId)
}
// Sort to make response deterministic
slices.Sort(target.RemovedIds)
// Since we paginated to the end, we can avoid confusion
// for the user by setting the estimated item count to the
// length of the items slice. If we don't set this here, it
// will equal the value returned in the last response, which is
// often much smaller than the total number returned.
target.EstItemCount = uint(len(target.Items))
// Sort the results again since in-place updates and deletes
// may have shuffled items. We sort by created time descending
// (most recently created first), same as the API.
slices.SortFunc(target.Items, func(i, j *Worker) int {
return i.UpdatedTime.Compare(j.UpdatedTime)
return j.CreatedTime.Compare(i.CreatedTime)
})
// Finally, since we made at least 2 requests to the server to fulfill this
// function call, resp.Body and resp.Map will only contain the most recent response.
// Overwrite them with the true response.
target.response.Body.Reset()
if err := json.NewEncoder(target.response.Body).Encode(target); err != nil {
return nil, fmt.Errorf("error encoding final JSON list response: %w", err)
}
if err := json.Unmarshal(target.response.Body.Bytes(), &target.response.Map); err != nil {
return nil, fmt.Errorf("error encoding final map list response: %w", err)
}
// Note: the HTTP response body is consumed by resp.Decode in the loop,
// so it doesn't need to be updated (it will always be, and has always been, empty).
return target, nil
}
+38 -4
View File
@@ -276,6 +276,9 @@ func (c *Client) List(ctx context.Context, {{ .CollectionFunctionArg }} string,
for i, item := range target.Items {
idToIndex[item.Id] = i
}
// Removed IDs in the response may contain duplicates,
// maintain a set to avoid returning duplicates to the user.
removedIds := map[string]struct{}{}
for {
req, err := c.client.NewRequest(ctx, "GET", "{{ .CollectionPath }}", nil, apiOpts...)
if err != nil {
@@ -313,7 +316,9 @@ func (c *Client) List(ctx context.Context, {{ .CollectionFunctionArg }} string,
idToIndex[item.Id] = len(target.Items)-1
}
}
target.RemovedIds = append(target.RemovedIds, page.RemovedIds...)
for _, removedId := range page.RemovedIds {
removedIds[removedId] = struct{}{}
}
target.EstItemCount = page.EstItemCount
target.ListToken = page.ListToken
target.ResponseType = page.ResponseType
@@ -332,11 +337,35 @@ func (c *Client) List(ctx context.Context, {{ .CollectionFunctionArg }} string,
idToIndex[target.Items[i].Id] = i
}
}
// Finally, sort the results again since in-place updates and deletes
// may have shuffled items.
for deletedId := range removedIds {
target.RemovedIds = append(target.RemovedIds, deletedId)
}
// Sort to make response deterministic
slices.Sort(target.RemovedIds)
// Since we paginated to the end, we can avoid confusion
// for the user by setting the estimated item count to the
// length of the items slice. If we don't set this here, it
// will equal the value returned in the last response, which is
// often much smaller than the total number returned.
target.EstItemCount = uint(len(target.Items))
// Sort the results again since in-place updates and deletes
// may have shuffled items. We sort by created time descending
// (most recently created first), same as the API.
slices.SortFunc(target.Items, func(i, j *{{ .Name }}) int {
return i.UpdatedTime.Compare(j.UpdatedTime)
return j.CreatedTime.Compare(i.CreatedTime)
})
// Finally, since we made at least 2 requests to the server to fulfill this
// function call, resp.Body and resp.Map will only contain the most recent response.
// Overwrite them with the true response.
target.response.Body.Reset()
if err := json.NewEncoder(target.response.Body).Encode(target); err != nil {
return nil, fmt.Errorf("error encoding final JSON list response: %w", err)
}
if err := json.Unmarshal(target.response.Body.Bytes(), &target.response.Map); err != nil {
return nil, fmt.Errorf("error encoding final map list response: %w", err)
}
// Note: the HTTP response body is consumed by resp.Decode in the loop,
// so it doesn't need to be updated (it will always be, and has always been, empty).
return target, nil
}
`))
@@ -671,8 +700,13 @@ var structTemplate = template.Must(template.New("").Funcs(
package {{ .Package }}
import (
"bytes"
"context"
"encoding/json"
"errors"
"fmt"
"io"
"net/url"
"slices"
"time"