github.com/openshift-online/ocm-sdk-go@v0.1.473/helpers/helpers.go (about)

     1  /*
     2  Copyright (c) 2020 Red Hat, Inc.
     3  
     4  Licensed under the Apache License, Version 2.0 (the "License");
     5  you may not use this file except in compliance with the License.
     6  You may obtain a copy of the License at
     7  
     8    http://www.apache.org/licenses/LICENSE-2.0
     9  
    10  Unless required by applicable law or agreed to in writing, software
    11  distributed under the License is distributed on an "AS IS" BASIS,
    12  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    13  See the License for the specific language governing permissions and
    14  limitations under the License.
    15  */
    16  
    17  // IMPORTANT: This file has been generated automatically, refrain from modifying it manually as all
    18  // your changes will be lost when the file is generated again.
    19  
    20  package helpers // github.com/openshift-online/ocm-sdk-go/helpers
    21  
    22  import (
    23  	"context"
    24  	"fmt"
    25  	"net/http"
    26  	"net/url"
    27  	"strings"
    28  	"time"
    29  )
    30  
    31  // AddValue creates the given set of query parameters if needed, an then adds
    32  // the given parameter.
    33  func AddValue(query *url.Values, name string, value interface{}) {
    34  	if *query == nil {
    35  		*query = make(url.Values)
    36  	}
    37  	var text string
    38  	switch typed := value.(type) {
    39  	case time.Time:
    40  		text = typed.UTC().Format(time.RFC3339)
    41  	default:
    42  		text = fmt.Sprintf("%v", value)
    43  	}
    44  	query.Add(name, text)
    45  }
    46  
    47  // CopyQuery creates a copy of the given set of query parameters.
    48  func CopyQuery(query url.Values) url.Values {
    49  	if query == nil {
    50  		return nil
    51  	}
    52  	result := make(url.Values)
    53  	for name, values := range query {
    54  		result[name] = CopyValues(values)
    55  	}
    56  	return result
    57  }
    58  
    59  // AddHeader creates the given set of headers if needed, and then adds the given
    60  // header:
    61  func AddHeader(header *http.Header, name string, value interface{}) {
    62  	if *header == nil {
    63  		*header = make(http.Header)
    64  	}
    65  	header.Add(name, fmt.Sprintf("%v", value))
    66  }
    67  
    68  // CopyHeader creates a copy of the given set of headers.
    69  func CopyHeader(header http.Header) http.Header {
    70  	result := make(http.Header)
    71  	for name, values := range header {
    72  		result[name] = CopyValues(values)
    73  	}
    74  	return result
    75  }
    76  
    77  const impersonateUserHeader = "Impersonate-User"
    78  
    79  func AddImpersonationHeader(header *http.Header, user string) {
    80  	AddHeader(header, impersonateUserHeader, user)
    81  }
    82  
    83  // CopyValues copies a slice of strings.
    84  func CopyValues(values []string) []string {
    85  	if values == nil {
    86  		return nil
    87  	}
    88  	result := make([]string, len(values))
    89  	copy(result, values)
    90  	return result
    91  }
    92  
    93  // Segments calculates the path segments for the given path.
    94  func Segments(path string) []string {
    95  	for strings.HasPrefix(path, "/") {
    96  		path = path[1:]
    97  	}
    98  	for strings.HasSuffix(path, "/") {
    99  		path = path[0 : len(path)-1]
   100  	}
   101  	return strings.Split(path, "/")
   102  }
   103  
   104  // PollContext repeatedly executes a task till it returns one of the given statuses and till the result
   105  // satisfies all the given predicates.
   106  func PollContext(
   107  	ctx context.Context,
   108  	interval time.Duration,
   109  	statuses []int,
   110  	predicates []func(interface{}) bool,
   111  	task func(context.Context) (int, interface{}, error),
   112  ) (result interface{}, err error) {
   113  	// Check the deadline:
   114  	deadline, ok := ctx.Deadline()
   115  	if !ok {
   116  		err = fmt.Errorf("context deadline is mandatory")
   117  		return
   118  	}
   119  
   120  	// Check the interval:
   121  	if interval <= 0 {
   122  		err = fmt.Errorf("interval must be greater than zero")
   123  		return
   124  	}
   125  
   126  	// Create a cancellable context so that we can explicitly cancel it when we know that the next
   127  	// iteration of the loop will be after the deadline:
   128  	ctx, cancel := context.WithCancel(ctx)
   129  	defer cancel()
   130  
   131  	// If no expected status has been explicitly specified then add the default:
   132  	if len(statuses) == 0 {
   133  		statuses = []int{http.StatusOK}
   134  	}
   135  	for {
   136  		// Execute the task. If this produces an error and the status code is zero it means that
   137  		// there was an error like a timeout, or a low level communications problem. In that
   138  		// case we want to immediately stop waiting.
   139  		var status int
   140  		status, result, err = task(ctx)
   141  		if err != nil && status == 0 {
   142  			break
   143  		}
   144  
   145  		// Evaluate the status and the predicates:
   146  		statusOK := evalStatus(statuses, status)
   147  		predicatesOK := evalPredicates(predicates, result)
   148  		if statusOK && predicatesOK {
   149  			break
   150  		}
   151  
   152  		// If either the status or the predicates aren't acceptable then we need to check if we
   153  		// have enough time for another iteration before the deadline:
   154  		if time.Now().Add(interval).After(deadline) {
   155  			cancel()
   156  			break
   157  		}
   158  		time.Sleep(interval)
   159  	}
   160  	return
   161  }
   162  
   163  // evalStatus checks if the actual status is one of the expected ones.
   164  func evalStatus(expected []int, actual int) bool {
   165  	for _, current := range expected {
   166  		if actual == current {
   167  			return true
   168  		}
   169  	}
   170  	return false
   171  }
   172  
   173  // evalPredicates checks if the object satisfies all the predicates.
   174  func evalPredicates(predicates []func(interface{}) bool, object interface{}) bool {
   175  	if len(predicates) > 0 && object == nil {
   176  		return false
   177  	}
   178  	for _, predicate := range predicates {
   179  		if !predicate(object) {
   180  			return false
   181  		}
   182  	}
   183  	return true
   184  }