pricingapi.go 2.4 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788
  1. package otc
  2. import (
  3. "encoding/json"
  4. "fmt"
  5. "io"
  6. "net/http"
  7. "github.com/opencost/opencost/core/pkg/log"
  8. )
  9. var otcHTTPClient = http.DefaultClient
  10. // Fetches and flattens all product entries across multiple services with pagination
  11. func (otc *OTC) fetchPaginatedProducts(serviceNames []string) ([]Product, error) {
  12. const baseURL = "https://calculator.otc-service.com/de/open-telekom-price-api/"
  13. var allProducts []Product
  14. limitFrom := 0
  15. query := buildServiceNameQueryParam(serviceNames)
  16. for {
  17. url := fmt.Sprintf("%s?%s&columns%%5B0%%5D=productIdParameter&columns%%5B1%%5D=opiFlavour&columns%%5B2%%5D=osUnit&columns%%5B3%%5D=vCpu&columns%%5B4%%5D=ram&columns%%5B5%%5D=priceAmount&limitFrom=%d", baseURL, query, limitFrom)
  18. resp, err := otcHTTPClient.Get(url)
  19. if err != nil {
  20. if resp != nil {
  21. resp.Body.Close()
  22. }
  23. log.Errorf("Error fetching products from OTC API: %v", err)
  24. return nil, err
  25. }
  26. if resp.StatusCode != http.StatusOK {
  27. io.Copy(io.Discard, resp.Body)
  28. resp.Body.Close()
  29. return nil, fmt.Errorf("OTC API returned unexpected status %d", resp.StatusCode)
  30. }
  31. pageData, stats, err := otc.loadPaginatedResponse(resp)
  32. resp.Body.Close()
  33. if err != nil {
  34. log.Errorf("Error loading paginated response: %v", err)
  35. return nil, err
  36. }
  37. for _, products := range pageData {
  38. allProducts = append(allProducts, products...)
  39. }
  40. if stats.CurrentPage >= stats.MaxPages {
  41. log.Infof("Fetched all products for services: %v", serviceNames)
  42. break
  43. }
  44. limitFrom += stats.RecordsPerPage
  45. }
  46. return allProducts, nil
  47. }
  48. // Parses the OTC API response into a map of service → []Product and pagination stats
  49. func (otc *OTC) loadPaginatedResponse(resp *http.Response) (map[string][]Product, *OTCStats, error) {
  50. body, err := io.ReadAll(resp.Body)
  51. if err != nil {
  52. log.Errorf("Error reading OTC API response: %v", err)
  53. return nil, nil, err
  54. }
  55. var raw map[string]map[string]json.RawMessage
  56. if err := json.Unmarshal(body, &raw); err != nil {
  57. log.Errorf("Error unmarshalling OTC API response: %v", err)
  58. return nil, nil, err
  59. }
  60. var data map[string][]Product
  61. if err := json.Unmarshal(raw["response"]["result"], &data); err != nil {
  62. log.Errorf("Error unmarshalling result section: %v", err)
  63. return nil, nil, err
  64. }
  65. var stats OTCStats
  66. if err := json.Unmarshal(raw["response"]["stats"], &stats); err != nil {
  67. log.Errorf("Error unmarshalling stats section: %v", err)
  68. return nil, nil, err
  69. }
  70. return data, &stats, nil
  71. }