The API automatically paginates to the end of a pagination, and would take into account any items updated and subsequently removed since the last pagination. It now also removes any items removed during the current pagination.
305 lines
9.6 KiB
Go
305 lines
9.6 KiB
Go
// Code generated by "make api"; DO NOT EDIT.
|
|
// Copyright (c) HashiCorp, Inc.
|
|
// SPDX-License-Identifier: MPL-2.0
|
|
|
|
package sessions
|
|
|
|
import (
|
|
"context"
|
|
"encoding/json"
|
|
"fmt"
|
|
"net/url"
|
|
"slices"
|
|
"time"
|
|
|
|
"github.com/hashicorp/boundary/api"
|
|
"github.com/hashicorp/boundary/api/scopes"
|
|
)
|
|
|
|
type Session struct {
|
|
Id string `json:"id,omitempty"`
|
|
TargetId string `json:"target_id,omitempty"`
|
|
Scope *scopes.ScopeInfo `json:"scope,omitempty"`
|
|
CreatedTime time.Time `json:"created_time,omitempty"`
|
|
UpdatedTime time.Time `json:"updated_time,omitempty"`
|
|
Version uint32 `json:"version,omitempty"`
|
|
Type string `json:"type,omitempty"`
|
|
ExpirationTime time.Time `json:"expiration_time,omitempty"`
|
|
AuthTokenId string `json:"auth_token_id,omitempty"`
|
|
UserId string `json:"user_id,omitempty"`
|
|
HostSetId string `json:"host_set_id,omitempty"`
|
|
HostId string `json:"host_id,omitempty"`
|
|
ScopeId string `json:"scope_id,omitempty"`
|
|
Endpoint string `json:"endpoint,omitempty"`
|
|
States []*SessionState `json:"states,omitempty"`
|
|
Status string `json:"status,omitempty"`
|
|
Certificate []byte `json:"certificate,omitempty"`
|
|
TerminationReason string `json:"termination_reason,omitempty"`
|
|
AuthorizedActions []string `json:"authorized_actions,omitempty"`
|
|
Connections []*Connection `json:"connections,omitempty"`
|
|
}
|
|
|
|
type SessionReadResult struct {
|
|
Item *Session
|
|
Response *api.Response
|
|
}
|
|
|
|
func (n SessionReadResult) GetItem() *Session {
|
|
return n.Item
|
|
}
|
|
|
|
func (n SessionReadResult) GetResponse() *api.Response {
|
|
return n.Response
|
|
}
|
|
|
|
type SessionCreateResult = SessionReadResult
|
|
type SessionUpdateResult = SessionReadResult
|
|
|
|
type SessionDeleteResult struct {
|
|
Response *api.Response
|
|
}
|
|
|
|
// GetItem will always be nil for SessionDeleteResult
|
|
func (n SessionDeleteResult) GetItem() interface{} {
|
|
return nil
|
|
}
|
|
|
|
func (n SessionDeleteResult) GetResponse() *api.Response {
|
|
return n.Response
|
|
}
|
|
|
|
type SessionListResult struct {
|
|
Items []*Session `json:"items,omitempty"`
|
|
EstItemCount uint `json:"est_item_count,omitempty"`
|
|
RemovedIds []string `json:"removed_ids,omitempty"`
|
|
ListToken string `json:"list_token,omitempty"`
|
|
ResponseType string `json:"response_type,omitempty"`
|
|
Response *api.Response
|
|
}
|
|
|
|
func (n SessionListResult) GetItems() []*Session {
|
|
return n.Items
|
|
}
|
|
|
|
func (n SessionListResult) GetEstItemCount() uint {
|
|
return n.EstItemCount
|
|
}
|
|
|
|
func (n SessionListResult) GetRemovedIds() []string {
|
|
return n.RemovedIds
|
|
}
|
|
|
|
func (n SessionListResult) GetListToken() string {
|
|
return n.ListToken
|
|
}
|
|
|
|
func (n SessionListResult) GetResponseType() string {
|
|
return n.ResponseType
|
|
}
|
|
|
|
func (n SessionListResult) GetResponse() *api.Response {
|
|
return n.Response
|
|
}
|
|
|
|
// Client is a client for this collection
|
|
type Client struct {
|
|
client *api.Client
|
|
}
|
|
|
|
// Creates a new client for this collection. The submitted API client is cloned;
|
|
// modifications to it after generating this client will not have effect. If you
|
|
// need to make changes to the underlying API client, use ApiClient() to access
|
|
// it.
|
|
func NewClient(c *api.Client) *Client {
|
|
return &Client{client: c.Clone()}
|
|
}
|
|
|
|
// ApiClient returns the underlying API client
|
|
func (c *Client) ApiClient() *api.Client {
|
|
return c.client
|
|
}
|
|
|
|
func (c *Client) Read(ctx context.Context, id string, opt ...Option) (*SessionReadResult, error) {
|
|
if id == "" {
|
|
return nil, fmt.Errorf("empty id value passed into Read request")
|
|
}
|
|
if c.client == nil {
|
|
return nil, fmt.Errorf("nil client")
|
|
}
|
|
|
|
opts, apiOpts := getOpts(opt...)
|
|
|
|
req, err := c.client.NewRequest(ctx, "GET", fmt.Sprintf("sessions/%s", url.PathEscape(id)), nil, apiOpts...)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("error creating Read request: %w", err)
|
|
}
|
|
|
|
if len(opts.queryMap) > 0 {
|
|
q := url.Values{}
|
|
for k, v := range opts.queryMap {
|
|
q.Add(k, v)
|
|
}
|
|
req.URL.RawQuery = q.Encode()
|
|
}
|
|
|
|
resp, err := c.client.Do(req, apiOpts...)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("error performing client request during Read call: %w", err)
|
|
}
|
|
|
|
target := new(SessionReadResult)
|
|
target.Item = new(Session)
|
|
apiErr, err := resp.Decode(target.Item)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("error decoding Read response: %w", err)
|
|
}
|
|
if apiErr != nil {
|
|
return nil, apiErr
|
|
}
|
|
target.Response = resp
|
|
return target, nil
|
|
}
|
|
|
|
func (c *Client) List(ctx context.Context, scopeId string, opt ...Option) (*SessionListResult, error) {
|
|
if scopeId == "" {
|
|
return nil, fmt.Errorf("empty scopeId value passed into List request")
|
|
}
|
|
if c.client == nil {
|
|
return nil, fmt.Errorf("nil client")
|
|
}
|
|
|
|
opts, apiOpts := getOpts(opt...)
|
|
opts.queryMap["scope_id"] = scopeId
|
|
|
|
req, err := c.client.NewRequest(ctx, "GET", "sessions", nil, apiOpts...)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("error creating List request: %w", err)
|
|
}
|
|
|
|
if len(opts.queryMap) > 0 {
|
|
q := url.Values{}
|
|
for k, v := range opts.queryMap {
|
|
q.Add(k, v)
|
|
}
|
|
req.URL.RawQuery = q.Encode()
|
|
}
|
|
|
|
resp, err := c.client.Do(req)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("error performing client request during List call: %w", err)
|
|
}
|
|
|
|
target := new(SessionListResult)
|
|
apiErr, err := resp.Decode(target)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("error decoding List response: %w", err)
|
|
}
|
|
if apiErr != nil {
|
|
return nil, apiErr
|
|
}
|
|
target.Response = resp
|
|
if target.ResponseType == "complete" || target.ResponseType == "" {
|
|
return target, nil
|
|
}
|
|
// If there are more results, automatically fetch the rest of the results.
|
|
// idToIndex keeps a map from the ID of an item to its index in target.Items.
|
|
// This is used to update updated items in-place and remove deleted items
|
|
// from the result after pagination is done.
|
|
idToIndex := map[string]int{}
|
|
for i, item := range target.Items {
|
|
idToIndex[item.Id] = i
|
|
}
|
|
for {
|
|
req, err := c.client.NewRequest(ctx, "GET", "sessions", nil, apiOpts...)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("error creating List request: %w", err)
|
|
}
|
|
|
|
opts.queryMap["list_token"] = target.ListToken
|
|
if len(opts.queryMap) > 0 {
|
|
q := url.Values{}
|
|
for k, v := range opts.queryMap {
|
|
q.Add(k, v)
|
|
}
|
|
req.URL.RawQuery = q.Encode()
|
|
}
|
|
|
|
resp, err := c.client.Do(req)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("error performing client request during List call: %w", err)
|
|
}
|
|
|
|
page := new(SessionListResult)
|
|
apiErr, err := resp.Decode(page)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("error decoding List response: %w", err)
|
|
}
|
|
if apiErr != nil {
|
|
return nil, apiErr
|
|
}
|
|
for _, item := range page.Items {
|
|
if i, ok := idToIndex[item.Id]; ok {
|
|
// Item has already been seen at index i, update in-place
|
|
target.Items[i] = item
|
|
} else {
|
|
target.Items = append(target.Items, item)
|
|
idToIndex[item.Id] = len(target.Items) - 1
|
|
}
|
|
}
|
|
// RemovedIds contain any Session that were deleted since the last response.
|
|
target.RemovedIds = append(target.RemovedIds, page.RemovedIds...)
|
|
target.EstItemCount = page.EstItemCount
|
|
target.ListToken = page.ListToken
|
|
target.ResponseType = page.ResponseType
|
|
target.Response = resp
|
|
if target.ResponseType == "complete" {
|
|
break
|
|
}
|
|
}
|
|
// For now, removedIds will only be populated if this pagination cycle was the result of a
|
|
// "refresh" operation (i.e., the caller provided a list token option to this call).
|
|
//
|
|
// Sort to make response deterministic
|
|
slices.Sort(target.RemovedIds)
|
|
// Remove any duplicates
|
|
target.RemovedIds = slices.Compact(target.RemovedIds)
|
|
// Remove items that were deleted since the end of the last iteration.
|
|
// If a Session has been updated and subsequently removed, we don't want
|
|
// it to appear both in the Items and RemovedIds, so we remove it from the Items.
|
|
for _, removedId := range target.RemovedIds {
|
|
if i, ok := idToIndex[removedId]; ok {
|
|
// Remove the item at index i without preserving order
|
|
// https://github.com/golang/go/wiki/SliceTricks#delete-without-preserving-order
|
|
target.Items[i] = target.Items[len(target.Items)-1]
|
|
target.Items = target.Items[:len(target.Items)-1]
|
|
// Update the index of the previously last element
|
|
idToIndex[target.Items[i].Id] = i
|
|
}
|
|
}
|
|
// 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 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
|
|
}
|