github.com/diadata-org/diadata@v1.4.593/pkg/utils/downloadResource.go (about)

     1  package utils
     2  
     3  import (
     4  	"bufio"
     5  	"bytes"
     6  	"encoding/json"
     7  	"fmt"
     8  	"io"
     9  	"io/ioutil"
    10  	"net/http"
    11  	"net/url"
    12  	"os"
    13  	"path/filepath"
    14  	"time"
    15  )
    16  
    17  var openseaKey string
    18  
    19  // DownloadResource is a simple utility that downloads a resource
    20  // from @url and stores it into @filepath.
    21  func DownloadResource(path, url string) (err error) {
    22  
    23  	fmt.Println("url: ", url)
    24  	resp, err := http.Get(url) //nolint:noctx,gosec
    25  	if err != nil {
    26  		return
    27  	}
    28  
    29  	defer func() {
    30  		cerr := resp.Body.Close()
    31  		if err == nil {
    32  			err = cerr
    33  		}
    34  	}()
    35  
    36  	// Create the file
    37  	out, err := os.Create(filepath.Clean(path))
    38  	if err != nil {
    39  		return
    40  	}
    41  	defer func() {
    42  		cerr := out.Close()
    43  		if err == nil {
    44  			err = cerr
    45  		}
    46  	}()
    47  
    48  	// Write the body to file
    49  	_, err = io.Copy(out, resp.Body)
    50  	return
    51  }
    52  
    53  // GetRequest performs a get request on @url and returns the response body
    54  // as a slice of byte data.
    55  func GetRequest(url string) ([]byte, int, error) {
    56  
    57  	response, err := http.Get(url) //nolint:noctx,gosec
    58  	if err != nil {
    59  		log.Error("get request: ", err)
    60  		return []byte{}, 0, err
    61  	}
    62  
    63  	// Close response body after function
    64  	defer func() {
    65  		cerr := response.Body.Close()
    66  		if err == nil {
    67  			err = cerr
    68  		}
    69  	}()
    70  
    71  	// Check the status code for a 200 so we know we have received a
    72  	// proper response.
    73  	if response.StatusCode != 200 {
    74  		return []byte{}, response.StatusCode, fmt.Errorf("HTTP Response Error %d", response.StatusCode)
    75  	}
    76  
    77  	// Read the response body
    78  	XMLdata, err := ioutil.ReadAll(response.Body)
    79  	if err != nil {
    80  		log.Error(err)
    81  		return []byte{}, response.StatusCode, err
    82  	}
    83  
    84  	return XMLdata, response.StatusCode, err
    85  }
    86  
    87  // GetRequest performs a get request on @url and returns the response body
    88  // as a slice of byte data.
    89  func GetRequestWithStatus(url string) ([]byte, int, error) {
    90  
    91  	// Get url
    92  	response, err := http.Get(url) //nolint:noctx,gosec
    93  
    94  	// Check, whether the request was successful
    95  	if err != nil {
    96  		log.Error(err)
    97  		return []byte{}, response.StatusCode, err
    98  	}
    99  
   100  	// Close response body after function
   101  	defer func(Body io.ReadCloser) {
   102  		errClose := Body.Close()
   103  		if errClose != nil {
   104  			log.Error("error closing body ", errClose)
   105  		}
   106  	}(response.Body)
   107  
   108  	// Read the response body
   109  	XMLdata, err := ioutil.ReadAll(response.Body)
   110  
   111  	if err != nil {
   112  		log.Error(err)
   113  		return []byte{}, response.StatusCode, err
   114  	}
   115  
   116  	return XMLdata, response.StatusCode, err
   117  }
   118  
   119  // PostRequest performs a POST request on @url and returns the response body
   120  // as a slice of byte data.
   121  func PostRequest(url string, body io.Reader) ([]byte, error) {
   122  
   123  	// Get url
   124  	response, err := http.Post(url, "", body) //nolint:noctx,gosec
   125  
   126  	// Check, whether the request was successful
   127  	if err != nil {
   128  		log.Error(err)
   129  		return []byte{}, err
   130  	}
   131  
   132  	// Close response body after function
   133  	defer func() {
   134  		cerr := response.Body.Close()
   135  		if err == nil {
   136  			err = cerr
   137  		}
   138  	}()
   139  
   140  	// Check the status code for a 200 so we know we have received a
   141  	// proper response.
   142  	if response.StatusCode != 200 {
   143  		log.Error("HTTP Response Error: ", response.StatusCode)
   144  		return []byte{}, fmt.Errorf("HTTP Response Error %d", response.StatusCode)
   145  	}
   146  
   147  	// Read the response body
   148  	XMLdata, err := ioutil.ReadAll(response.Body)
   149  
   150  	if err != nil {
   151  		log.Error(err)
   152  		return []byte{}, err
   153  	}
   154  
   155  	return XMLdata, err
   156  }
   157  
   158  // HTTPRequest returns the request body and defers the closing compliant
   159  // to linting.
   160  func HTTPRequest(request *http.Request) (body []byte, statusCode int, err error) {
   161  	client := &http.Client{}
   162  	resp, err := client.Do(request)
   163  	if err != nil {
   164  		return
   165  	}
   166  	defer func() {
   167  		cerr := resp.Body.Close()
   168  		if err == nil {
   169  			err = cerr
   170  		}
   171  	}()
   172  
   173  	statusCode = resp.StatusCode
   174  	body, err = ioutil.ReadAll(resp.Body)
   175  	if err != nil {
   176  		return
   177  	}
   178  	return
   179  }
   180  
   181  // CloseHTTPResp is a wrapper for closing http response bodies
   182  // while complying with the linter.
   183  func CloseHTTPResp(resp *http.Response) {
   184  	err := resp.Body.Close()
   185  	if err != nil {
   186  		log.Error(err)
   187  	}
   188  }
   189  
   190  // GraphQLGet returns the body of the result of a graphQL GET query.
   191  // @url is the base url of the graphQL API
   192  // @query is a byte slice representing the graphQL query message
   193  // @bearer contains the API key if present
   194  func GraphQLGet(url string, query []byte, bearer string) ([]byte, int, error) {
   195  
   196  	// Form post request with graphQL query
   197  	req, err := http.NewRequest("POST", url, bytes.NewBuffer(query)) //nolint:noctx
   198  	if err != nil {
   199  		return []byte{}, 0, err
   200  	}
   201  
   202  	// Add authorization bearer to header
   203  	req.Header.Add("Authorization", bearer)
   204  
   205  	return HTTPRequest(req)
   206  }
   207  
   208  func getOpenseaApiKey() string {
   209  	if openseaKey != "" {
   210  		return openseaKey
   211  	}
   212  	if Getenv("USE_ENV", "false") == "true" {
   213  		openseaKey = Getenv("API_KEY_OPENSEA", "")
   214  		return openseaKey
   215  	}
   216  
   217  	var lines []string
   218  	var filename string
   219  	if Getenv("EXEC_MODE", "debug") == "production" {
   220  		filename = "/run/secrets/Opensea-API.key"
   221  	} else {
   222  		filename = "../../secrets/Opensea-API.key"
   223  	}
   224  
   225  	file, err := os.Open(filepath.Clean(filename))
   226  	if err != nil {
   227  		log.Fatal(err)
   228  	}
   229  	defer func(file *os.File) {
   230  		errClose := file.Close()
   231  		if errClose != nil {
   232  			log.Error("failure closing opensea-api.key file ", errClose)
   233  		}
   234  	}(file)
   235  
   236  	scanner := bufio.NewScanner(file)
   237  	for scanner.Scan() {
   238  		lines = append(lines, scanner.Text())
   239  	}
   240  	if err = scanner.Err(); err != nil {
   241  		log.Fatal(err)
   242  	}
   243  	if len(lines) != 1 {
   244  		log.Fatal("Secrets file for opensea API key should have exactly one line")
   245  	}
   246  	openseaKey = lines[0]
   247  	return openseaKey
   248  }
   249  
   250  // OpenseaGetRequest returns the data for a get request on @url with an Opensea API key.
   251  func OpenseaGetRequest(OpenseaURL string) ([]byte, int, error) {
   252  
   253  	client := &http.Client{}
   254  	req, err := http.NewRequest("GET", OpenseaURL, nil)
   255  	if err != nil {
   256  		log.Print(err)
   257  		return []byte{}, 0, err
   258  	}
   259  	apiKey := getOpenseaApiKey()
   260  	q := url.Values{}
   261  	req.Header.Set("Accepts", "application/json")
   262  	req.Header.Add("X-API-KEY", apiKey)
   263  	req.URL.RawQuery = q.Encode()
   264  
   265  	resp, err := client.Do(req)
   266  	if err != nil {
   267  		fmt.Println("Error sending request to server")
   268  		os.Exit(1)
   269  	}
   270  	defer func(Body io.ReadCloser) {
   271  		errClose := Body.Close()
   272  		if errClose != nil {
   273  			fmt.Println("Error closing body ", errClose)
   274  		}
   275  	}(resp.Body)
   276  	respData, err := ioutil.ReadAll(resp.Body)
   277  	if err != nil {
   278  		log.Error("read response body: ", err)
   279  		return respData, resp.StatusCode, err
   280  	}
   281  	return respData, resp.StatusCode, nil
   282  }
   283  
   284  // GetCoinPrice Gets the price in USD of coin through our API.
   285  // Looks it up on coingecko in case it doesn't find it there.
   286  func GetCoinPrice(coin string) (float64, error) {
   287  	// TODO Add UNIV2DAIETH quotation
   288  	// log.Info("Get price for ", coin)
   289  	switch coin {
   290  	case "WETH":
   291  		coin = "ETH"
   292  	case "HBTC":
   293  		coin = "BTC"
   294  	}
   295  
   296  	type Quotation struct {
   297  		Symbol             string    `json:"Symbol"`
   298  		Name               string    `json:"Name"`
   299  		Price              float64   `json:"Price"`
   300  		PriceYesterday     *float64  `json:"PriceYesterday"`
   301  		VolumeYesterdayUSD *float64  `json:"VolumeYesterdayUSD"`
   302  		Source             string    `json:"Source"`
   303  		Time               time.Time `json:"Time"`
   304  		ITIN               string    `json:"ITIN"`
   305  	}
   306  	/*
   307  		type QuotationGecko struct {
   308  			ID struct {
   309  				Price string `json:"vs_currencies"`
   310  			} `json:"ids"`
   311  		}
   312  	*/
   313  	type QuotationCrptcmp struct {
   314  		Price float64 `json:"USD"`
   315  	}
   316  	url := "https://api.diadata.org/v1/quotation/" + coin
   317  	data, _, err := GetRequest(url)
   318  	if err == nil {
   319  		Quot := Quotation{}
   320  		err = json.Unmarshal(data, &Quot)
   321  		if err != nil {
   322  			return 0, err
   323  		}
   324  		return Quot.Price, nil
   325  	}
   326  	// Try to get price from cryptocompare in case we don't have it in our API yet.
   327  	data, _, err = GetRequest("https://min-api.cryptocompare.com/data/price?fsym=" + coin + "&tsyms=USD")
   328  	if err != nil {
   329  		log.Error("Could not get price")
   330  		return 0, err
   331  	}
   332  	Quot := QuotationCrptcmp{}
   333  	err = json.Unmarshal(data, &Quot)
   334  	if err != nil {
   335  		return 0, err
   336  	}
   337  	price := Quot.Price
   338  
   339  	// // Retrieval through coingecko. Is down at the moment.
   340  	// data, err = GetRequest("https://api.coingecko.com/api/v3/simple/price?ids=" + coin + "&vs_currencies=USD")
   341  	// if err != nil {
   342  	// 	return 0, err
   343  	// }
   344  	// Quot := QuotationGecko{}
   345  	// err = json.Unmarshal(data, &Quot)
   346  	// price, err := strconv.ParseFloat(Quot.ID.Price, 64)
   347  	// if err != nil {
   348  	// 	return 0, err
   349  	// }
   350  	return price, nil
   351  
   352  }