plan.go 5.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175
  1. package billing
  2. import (
  3. "net/http"
  4. "github.com/porter-dev/porter/api/server/handlers"
  5. "github.com/porter-dev/porter/api/server/shared"
  6. "github.com/porter-dev/porter/api/server/shared/apierrors"
  7. "github.com/porter-dev/porter/api/server/shared/config"
  8. "github.com/porter-dev/porter/api/types"
  9. "github.com/porter-dev/porter/internal/models"
  10. "github.com/porter-dev/porter/internal/telemetry"
  11. )
  12. // ListPlansHandler is a handler for getting customer plans
  13. type ListPlansHandler struct {
  14. handlers.PorterHandlerWriter
  15. }
  16. // NewListPlansHandler will create a new ListPlansHandler
  17. func NewListPlansHandler(
  18. config *config.Config,
  19. writer shared.ResultWriter,
  20. ) *ListPlansHandler {
  21. return &ListPlansHandler{
  22. PorterHandlerWriter: handlers.NewDefaultPorterHandler(config, nil, writer),
  23. }
  24. }
  25. func (c *ListPlansHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
  26. ctx, span := telemetry.NewSpan(r.Context(), "serve-list-plans")
  27. defer span.End()
  28. proj, _ := ctx.Value(types.ProjectScope).(*models.Project)
  29. telemetry.WithAttributes(span,
  30. telemetry.AttributeKV{Key: "lago-config-exists", Value: c.Config().BillingManager.LagoConfigLoaded},
  31. telemetry.AttributeKV{Key: "lago-enabled", Value: proj.GetFeatureFlag(models.LagoEnabled, c.Config().LaunchDarklyClient)},
  32. )
  33. if !c.Config().BillingManager.LagoConfigLoaded || !proj.GetFeatureFlag(models.LagoEnabled, c.Config().LaunchDarklyClient) {
  34. c.WriteResult(w, r, "")
  35. return
  36. }
  37. plan, err := c.Config().BillingManager.LagoClient.GetCustomerActivePlan(ctx, proj.ID, proj.EnableSandbox)
  38. if err != nil {
  39. err := telemetry.Error(ctx, span, err, "error getting active subscription")
  40. c.HandleAPIError(w, r, apierrors.NewErrInternal(err))
  41. return
  42. }
  43. telemetry.WithAttributes(span,
  44. telemetry.AttributeKV{Key: "subscription_id", Value: plan.ID},
  45. )
  46. endingBefore, err := c.Config().BillingManager.LagoClient.CheckCustomerCouponExpiration(ctx, proj.ID, proj.EnableSandbox)
  47. if err != nil {
  48. err := telemetry.Error(ctx, span, err, "error listing active coupons")
  49. c.HandleAPIError(w, r, apierrors.NewErrInternal(err))
  50. return
  51. }
  52. // If the customer has a coupon, use its end date instead of the trial end date
  53. if endingBefore != "" {
  54. plan.TrialInfo.EndingBefore = endingBefore
  55. }
  56. telemetry.WithAttributes(span,
  57. telemetry.AttributeKV{Key: "trial-ending-at", Value: plan.TrialInfo.EndingBefore},
  58. )
  59. c.WriteResult(w, r, plan)
  60. }
  61. // ListCreditsHandler is a handler for getting available credits
  62. type ListCreditsHandler struct {
  63. handlers.PorterHandlerWriter
  64. }
  65. // NewListCreditsHandler will create a new ListCreditsHandler
  66. func NewListCreditsHandler(
  67. config *config.Config,
  68. writer shared.ResultWriter,
  69. ) *ListCreditsHandler {
  70. return &ListCreditsHandler{
  71. PorterHandlerWriter: handlers.NewDefaultPorterHandler(config, nil, writer),
  72. }
  73. }
  74. func (c *ListCreditsHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
  75. ctx, span := telemetry.NewSpan(r.Context(), "serve-list-credits")
  76. defer span.End()
  77. proj, _ := ctx.Value(types.ProjectScope).(*models.Project)
  78. telemetry.WithAttributes(span,
  79. telemetry.AttributeKV{Key: "lago-config-exists", Value: c.Config().BillingManager.LagoConfigLoaded},
  80. telemetry.AttributeKV{Key: "lago-enabled", Value: proj.GetFeatureFlag(models.LagoEnabled, c.Config().LaunchDarklyClient)},
  81. )
  82. if !c.Config().BillingManager.LagoConfigLoaded || !proj.GetFeatureFlag(models.LagoEnabled, c.Config().LaunchDarklyClient) {
  83. c.WriteResult(w, r, "")
  84. return
  85. }
  86. credits, err := c.Config().BillingManager.LagoClient.ListCustomerCredits(ctx, proj.ID, proj.EnableSandbox)
  87. if err != nil {
  88. err := telemetry.Error(ctx, span, err, "error listing credits")
  89. c.HandleAPIError(w, r, apierrors.NewErrInternal(err))
  90. return
  91. }
  92. c.WriteResult(w, r, credits)
  93. }
  94. // ListCustomerUsageHandler returns customer usage aggregations like CPU and RAM hours.
  95. type ListCustomerUsageHandler struct {
  96. handlers.PorterHandlerReadWriter
  97. }
  98. // NewListCustomerUsageHandler returns a new ListCustomerUsageHandler
  99. func NewListCustomerUsageHandler(
  100. config *config.Config,
  101. decoderValidator shared.RequestDecoderValidator,
  102. writer shared.ResultWriter,
  103. ) *ListCustomerUsageHandler {
  104. return &ListCustomerUsageHandler{
  105. PorterHandlerReadWriter: handlers.NewDefaultPorterHandler(config, decoderValidator, writer),
  106. }
  107. }
  108. func (c *ListCustomerUsageHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
  109. ctx, span := telemetry.NewSpan(r.Context(), "serve-list-customer-usage")
  110. defer span.End()
  111. proj, _ := ctx.Value(types.ProjectScope).(*models.Project)
  112. telemetry.WithAttributes(span,
  113. telemetry.AttributeKV{Key: "lago-config-exists", Value: c.Config().BillingManager.LagoConfigLoaded},
  114. telemetry.AttributeKV{Key: "lago-enabled", Value: proj.GetFeatureFlag(models.LagoEnabled, c.Config().LaunchDarklyClient)},
  115. )
  116. if !c.Config().BillingManager.LagoConfigLoaded || !proj.GetFeatureFlag(models.LagoEnabled, c.Config().LaunchDarklyClient) {
  117. c.WriteResult(w, r, "")
  118. return
  119. }
  120. req := &types.ListCustomerUsageRequest{}
  121. if ok := c.DecodeAndValidate(w, r, req); !ok {
  122. err := telemetry.Error(ctx, span, nil, "error decoding list customer usage request")
  123. c.HandleAPIError(w, r, apierrors.NewErrPassThroughToClient(err, http.StatusBadRequest))
  124. return
  125. }
  126. plan, err := c.Config().BillingManager.LagoClient.GetCustomerActivePlan(ctx, proj.ID, proj.EnableSandbox)
  127. if err != nil {
  128. err := telemetry.Error(ctx, span, err, "error getting active subscription")
  129. c.HandleAPIError(w, r, apierrors.NewErrInternal(err))
  130. return
  131. }
  132. telemetry.WithAttributes(span,
  133. telemetry.AttributeKV{Key: "subscription_id", Value: plan.ID},
  134. )
  135. usage, err := c.Config().BillingManager.LagoClient.ListCustomerUsage(ctx, plan.CustomerID, plan.ID, req.CurrentPeriod, req.PreviousPeriods)
  136. if err != nil {
  137. err := telemetry.Error(ctx, span, err, "error listing customer usage")
  138. c.HandleAPIError(w, r, apierrors.NewErrInternal(err))
  139. return
  140. }
  141. c.WriteResult(w, r, usage)
  142. }