invoker.go raw

   1  // Copyright 2022 Huawei Technologies Co.,Ltd.
   2  //
   3  // Licensed to the Apache Software Foundation (ASF) under one
   4  // or more contributor license agreements.  See the NOTICE file
   5  // distributed with this work for additional information
   6  // regarding copyright ownership.  The ASF licenses this file
   7  // to you under the Apache License, Version 2.0 (the
   8  // "License"); you may not use this file except in compliance
   9  // with the License.  You may obtain a copy of the License at
  10  //
  11  //     http://www.apache.org/licenses/LICENSE-2.0
  12  //
  13  // Unless required by applicable law or agreed to in writing,
  14  // software distributed under the License is distributed on an
  15  // "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
  16  // KIND, either express or implied.  See the License for the
  17  // specific language governing permissions and limitations
  18  // under the License.
  19  
  20  package invoker
  21  
  22  import (
  23  	"time"
  24  
  25  	"github.com/huaweicloud/huaweicloud-sdk-go-v3/core"
  26  	"github.com/huaweicloud/huaweicloud-sdk-go-v3/core/auth"
  27  	"github.com/huaweicloud/huaweicloud-sdk-go-v3/core/def"
  28  	"github.com/huaweicloud/huaweicloud-sdk-go-v3/core/exchange"
  29  	"github.com/huaweicloud/huaweicloud-sdk-go-v3/core/invoker/retry"
  30  )
  31  
  32  type RetryChecker func(interface{}, error) bool
  33  
  34  type BaseInvoker struct {
  35  	Exchange *exchange.SdkExchange
  36  
  37  	client  *core.HcHttpClient
  38  	request interface{}
  39  	meta    *def.HttpRequestDef
  40  	headers map[string]string
  41  
  42  	retryTimes      int
  43  	retryChecker    RetryChecker
  44  	backoffStrategy retry.Strategy
  45  }
  46  
  47  func NewBaseInvoker(client *core.HcHttpClient, request interface{}, meta *def.HttpRequestDef) *BaseInvoker {
  48  	exch := &exchange.SdkExchange{
  49  		ApiReference: &exchange.ApiReference{
  50  			Method: meta.Method,
  51  			Path:   meta.Path,
  52  		},
  53  		Attributes: make(map[string]interface{}),
  54  	}
  55  
  56  	return &BaseInvoker{
  57  		Exchange: exch,
  58  		client:   client,
  59  		request:  request,
  60  		meta:     meta,
  61  		headers:  make(map[string]string),
  62  	}
  63  }
  64  
  65  func (b *BaseInvoker) ReplaceCredentialWhen(fun func(auth.ICredential) auth.ICredential) *BaseInvoker {
  66  	b.client.WithCredential(fun(b.client.GetCredential()))
  67  	return b
  68  }
  69  
  70  // Deprecated: This function will be removed in the future version, use AddHeaders instead.
  71  func (b *BaseInvoker) AddHeader(headers map[string]string) *BaseInvoker {
  72  	return b.AddHeaders(headers)
  73  }
  74  
  75  func (b *BaseInvoker) AddHeaders(headers map[string]string) *BaseInvoker {
  76  	if headers != nil {
  77  		for k, v := range headers {
  78  			b.headers[k] = v
  79  		}
  80  	}
  81  	return b
  82  }
  83  
  84  func (b *BaseInvoker) WithRetry(retryTimes int, checker RetryChecker, backoffStrategy retry.Strategy) *BaseInvoker {
  85  	b.retryTimes = retryTimes
  86  	b.retryChecker = checker
  87  	b.backoffStrategy = backoffStrategy
  88  	return b
  89  }
  90  
  91  func (b *BaseInvoker) Invoke() (interface{}, error) {
  92  	if b.retryTimes == 0 || b.retryChecker == nil {
  93  		return b.client.SyncInvokeWithExtraHeaders(b.request, b.meta, b.Exchange, b.headers)
  94  	}
  95  
  96  	var execTimes int
  97  	var resp interface{}
  98  	var err error
  99  	for {
 100  		resp, err = b.client.SyncInvokeWithExtraHeaders(b.request, b.meta, b.Exchange, b.headers)
 101  		execTimes++
 102  
 103  		if execTimes > b.retryTimes || !b.retryChecker(resp, err) {
 104  			break
 105  		}
 106  
 107  		delay := b.backoffStrategy.ComputeDelayBeforeNextRetry(int32(execTimes))
 108  		if delay > 0 {
 109  			time.Sleep(time.Duration(delay) * time.Millisecond)
 110  		}
 111  	}
 112  	return resp, err
 113  }
 114