diff --git a/satellite/console/consoleweb/consoleql/mutation.go b/satellite/console/consoleweb/consoleql/mutation.go index 580a95c79..3e6a6dd76 100644 --- a/satellite/console/consoleweb/consoleql/mutation.go +++ b/satellite/console/consoleweb/consoleql/mutation.go @@ -42,6 +42,13 @@ const ( // DeleteAPIKeysMutation is a mutation name for api key deleting DeleteAPIKeysMutation = "deleteAPIKeys" + // AddPaymentMethodMutation is mutation name for adding new payment method + AddPaymentMethodMutation = "addPaymentMethod" + // DeletePaymentMethodMutation is mutation name for deleting payment method + DeletePaymentMethodMutation = "deletePaymentMethod" + // SetDefaultPaymentMethodMutation is mutation name setting payment method as default payment method + SetDefaultPaymentMethodMutation = "setDefaultPaymentMethod" + // InputArg is argument name for all input types InputArg = "input" // FieldProjectID is field name for projectID @@ -420,6 +427,92 @@ func rootMutation(log *zap.Logger, service *console.Service, mailService *mailse return keys, nil }, }, + AddPaymentMethodMutation: &graphql.Field{ + Type: graphql.Boolean, + Args: graphql.FieldConfigArgument{ + FieldProjectID: &graphql.ArgumentConfig{ + Type: graphql.NewNonNull(graphql.String), + }, + FieldCardToken: &graphql.ArgumentConfig{ + Type: graphql.NewNonNull(graphql.String), + }, + FieldIsDefault: &graphql.ArgumentConfig{ + Type: graphql.NewNonNull(graphql.Boolean), + }, + }, + Resolve: func(p graphql.ResolveParams) (interface{}, error) { + projectID, _ := p.Args[FieldProjectID].(string) + cardToken, _ := p.Args[FieldCardToken].(string) + isDefault, _ := p.Args[FieldIsDefault].(bool) + + projID, err := uuid.Parse(projectID) + if err != nil { + return false, err + } + + _, err = service.AddNewPaymentMethod(p.Context, cardToken, isDefault, *projID) + if err != nil { + return false, err + } + + return true, nil + }, + }, + DeletePaymentMethodMutation: &graphql.Field{ + Type: graphql.Boolean, + Args: graphql.FieldConfigArgument{ + FieldID: &graphql.ArgumentConfig{ + Type: graphql.NewNonNull(graphql.String), + }, + }, + Resolve: func(p graphql.ResolveParams) (interface{}, error) { + fieldProjectPaymentID, _ := p.Args[FieldID].(string) + + paymentID, err := uuid.Parse(fieldProjectPaymentID) + if err != nil { + return false, err + } + + err = service.DeleteProjectPaymentMethod(p.Context, *paymentID) + if err != nil { + return false, err + } + + return true, nil + }, + }, + SetDefaultPaymentMethodMutation: &graphql.Field{ + Type: graphql.Boolean, + Args: graphql.FieldConfigArgument{ + FieldProjectID: &graphql.ArgumentConfig{ + Type: graphql.NewNonNull(graphql.String), + }, + FieldID: &graphql.ArgumentConfig{ + Type: graphql.NewNonNull(graphql.String), + }, + }, + Resolve: func(p graphql.ResolveParams) (interface{}, error) { + fieldProjectID, _ := p.Args[FieldProjectID].(string) + fieldProjectPaymentID, _ := p.Args[FieldID].(string) + + paymentID, err := uuid.Parse(fieldProjectPaymentID) + if err != nil { + return false, err + } + + projectID, err := uuid.Parse(fieldProjectID) + if err != nil { + return false, err + } + + err = service.SetDefaultPaymentMethod(p.Context, *paymentID, *projectID) + if err != nil { + return false, err + } + + return true, nil + }, + }, }, }) } diff --git a/satellite/console/consoleweb/consoleql/project.go b/satellite/console/consoleweb/consoleql/project.go index 9a0a954d2..c631a0a5e 100644 --- a/satellite/console/consoleweb/consoleql/project.go +++ b/satellite/console/consoleweb/consoleql/project.go @@ -25,6 +25,8 @@ const ( BucketUsageType = "bucketUsage" // BucketUsagePageType is a field name for bucket usage page BucketUsagePageType = "bucketUsagePage" + // PaymentMethodType is a field name for payment method + PaymentMethodType = "paymentMethod" // FieldName is a field name for "name" FieldName = "name" // FieldBucketName is a field name for "bucket name" @@ -39,6 +41,8 @@ const ( FieldUsage = "usage" // FieldBucketUsages is a field name for bucket usages FieldBucketUsages = "bucketUsages" + // FieldPaymentMethods is a field name for payments methods + FieldPaymentMethods = "paymentMethods" // FieldStorage is a field name for storage total FieldStorage = "storage" // FieldEgress is a field name for egress total @@ -51,6 +55,14 @@ const ( FieldCurrentPage = "currentPage" // FieldTotalCount is a field name for bucket usage count total FieldTotalCount = "totalCount" + // FieldCardBrand is a field name for credit card brand + FieldCardBrand = "brand" + // FieldCardLastFour is a field name for credit card last four digits + FieldCardLastFour = "lastFour" + // FieldCardToken is a field name for credit card token + FieldCardToken = "cardToken" + // FieldIsDefault is a field name for default payment method + FieldIsDefault = "isDefault" // CursorArg is an argument name for cursor CursorArg = "cursor" // PageArg ia an argument name for page number @@ -189,6 +201,35 @@ func graphqlProject(service *console.Service, types *TypeCreator) *graphql.Objec return service.GetBucketTotals(p.Context, project.ID, cursor, before) }, }, + FieldPaymentMethods: &graphql.Field{ + Type: graphql.NewList(types.paymentMethod), + Resolve: func(p graphql.ResolveParams) (interface{}, error) { + project, _ := p.Source.(*console.Project) + + paymentMethods, err := service.GetProjectPaymentMethods(p.Context, project.ID) + if err != nil { + return nil, err + } + + var projectPaymentMethods []projectPayment + for _, paymentMethod := range paymentMethods { + projectPaymentMethod := projectPayment{ + ID: paymentMethod.ID.String(), + LastFour: paymentMethod.Card.LastFour, + AddedAt: paymentMethod.CreatedAt, + CardBrand: paymentMethod.Card.Brand, + ExpMonth: paymentMethod.Card.ExpirationMonth, + ExpYear: paymentMethod.Card.ExpirationYear, + HolderName: paymentMethod.Card.Name, + IsDefault: paymentMethod.IsDefault, + } + + projectPaymentMethods = append(projectPaymentMethods, projectPaymentMethod) + } + + return projectPaymentMethods, nil + }, + }, }, }) } @@ -307,6 +348,61 @@ func graphqlProjectUsage() *graphql.Object { }) } +const ( + // FieldExpirationYear is field name for expiration year + FieldExpirationYear = "expYear" + // FieldExpirationMonth is field name for expiration month + FieldExpirationMonth = "expMonth" + // FieldHolderName is field name for holder name + FieldHolderName = "holderName" + // FieldAddedAt is field name for added at date + FieldAddedAt = "addedAt" +) + +// graphqlPaymentMethod creates invoice payment method graphql type +func graphqlPaymentMethod() *graphql.Object { + return graphql.NewObject(graphql.ObjectConfig{ + Name: PaymentMethodType, + Fields: graphql.Fields{ + FieldID: &graphql.Field{ + Type: graphql.String, + }, + FieldExpirationYear: &graphql.Field{ + Type: graphql.Int, + }, + FieldExpirationMonth: &graphql.Field{ + Type: graphql.Int, + }, + FieldCardBrand: &graphql.Field{ + Type: graphql.String, + }, + FieldCardLastFour: &graphql.Field{ + Type: graphql.String, + }, + FieldHolderName: &graphql.Field{ + Type: graphql.String, + }, + FieldAddedAt: &graphql.Field{ + Type: graphql.DateTime, + }, + FieldIsDefault: &graphql.Field{ + Type: graphql.Boolean, + }, + }, + }) +} + +type projectPayment struct { + ID string + ExpYear int64 + ExpMonth int64 + CardBrand string + LastFour string + HolderName string + AddedAt time.Time + IsDefault bool +} + // fromMapProjectInfo creates console.ProjectInfo from input args func fromMapProjectInfo(args map[string]interface{}) (project console.ProjectInfo) { project.Name, _ = args[FieldName].(string) diff --git a/satellite/console/consoleweb/consoleql/typecreator.go b/satellite/console/consoleweb/consoleql/typecreator.go index 250dc0c46..b83c4cc60 100644 --- a/satellite/console/consoleweb/consoleql/typecreator.go +++ b/satellite/console/consoleweb/consoleql/typecreator.go @@ -25,6 +25,7 @@ type TypeCreator struct { projectUsage *graphql.Object bucketUsage *graphql.Object bucketUsagePage *graphql.Object + paymentMethod *graphql.Object projectMember *graphql.Object apiKeyInfo *graphql.Object createAPIKey *graphql.Object @@ -78,6 +79,11 @@ func (c *TypeCreator) Create(log *zap.Logger, service *console.Service, mailServ return err } + c.paymentMethod = graphqlPaymentMethod() + if err := c.paymentMethod.Error(); err != nil { + return err + } + c.bucketUsagePage = graphqlBucketUsagePage(c) if err := c.bucketUsagePage.Error(); err != nil { return err diff --git a/satellite/console/projectpayments.go b/satellite/console/projectpayments.go index b0c93b86f..7a0c087d0 100644 --- a/satellite/console/projectpayments.go +++ b/satellite/console/projectpayments.go @@ -13,16 +13,34 @@ import ( // ProjectPayments is project payment infos store interface type ProjectPayments interface { Create(ctx context.Context, info ProjectPayment) (*ProjectPayment, error) - GetByProjectID(ctx context.Context, projectID uuid.UUID) (*ProjectPayment, error) - GetByPayerID(ctx context.Context, payerID uuid.UUID) (*ProjectPayment, error) + Update(ctx context.Context, info ProjectPayment) error + Delete(ctx context.Context, projectPaymentID uuid.UUID) error + GetByProjectID(ctx context.Context, projectID uuid.UUID) ([]*ProjectPayment, error) + GetByID(ctx context.Context, projectPaymentID uuid.UUID) (*ProjectPayment, error) + GetDefaultByProjectID(ctx context.Context, projectID uuid.UUID) (*ProjectPayment, error) + GetByPayerID(ctx context.Context, payerID uuid.UUID) ([]*ProjectPayment, error) } // ProjectPayment contains project payment info type ProjectPayment struct { + ID uuid.UUID + ProjectID uuid.UUID PayerID uuid.UUID PaymentMethodID []byte + Card Card + IsDefault bool CreatedAt time.Time } + +// Card contains customer card info +type Card struct { + Country string + Brand string + Name string + ExpirationMonth int64 + ExpirationYear int64 + LastFour string +} diff --git a/satellite/console/projectpayments_test.go b/satellite/console/projectpayments_test.go index 71189f5b3..c4873c470 100644 --- a/satellite/console/projectpayments_test.go +++ b/satellite/console/projectpayments_test.go @@ -64,18 +64,18 @@ func TestProjectPaymentInfos(t *testing.T) { info, err := consoleDB.ProjectPayments().GetByProjectID(ctx, proj.ID) assert.NoError(t, err) - assert.Equal(t, proj.ID, info.ProjectID) - assert.Equal(t, userPmInfo.UserID, info.PayerID) - assert.Equal(t, paymentMethodID, info.PaymentMethodID) + assert.Equal(t, proj.ID, info[0].ProjectID) + assert.Equal(t, userPmInfo.UserID, info[0].PayerID) + assert.Equal(t, paymentMethodID, info[0].PaymentMethodID) }) t.Run("get by payer id", func(t *testing.T) { info, err := consoleDB.ProjectPayments().GetByPayerID(ctx, userPmInfo.UserID) assert.NoError(t, err) - assert.Equal(t, proj.ID, info.ProjectID) - assert.Equal(t, userPmInfo.UserID, info.PayerID) - assert.Equal(t, paymentMethodID, info.PaymentMethodID) + assert.Equal(t, proj.ID, info[0].ProjectID) + assert.Equal(t, userPmInfo.UserID, info[0].PayerID) + assert.Equal(t, paymentMethodID, info[0].PaymentMethodID) }) }) } diff --git a/satellite/console/service.go b/satellite/console/service.go index be4f35abf..39cc3595d 100644 --- a/satellite/console/service.go +++ b/satellite/console/service.go @@ -6,6 +6,7 @@ package console import ( "context" "crypto/subtle" + "database/sql" "fmt" "time" @@ -168,6 +169,164 @@ func (s *Service) CreateUser(ctx context.Context, user CreateUser, tokenSecret R return u, nil } +// AddNewPaymentMethod adds new payment method for project +func (s *Service) AddNewPaymentMethod(ctx context.Context, paymentMethodToken string, isDefault bool, projectID uuid.UUID) (payment *ProjectPayment, err error) { + defer mon.Task()(&ctx)(&err) + + authorization, err := GetAuth(ctx) + if err != nil { + return nil, err + } + + userPayments, err := s.store.UserPayments().Get(ctx, authorization.User.ID) + if err != nil { + return nil, err + } + + params := payments.AddPaymentMethodParams{ + Token: paymentMethodToken, + CustomerID: string(userPayments.CustomerID), + } + + method, err := s.pm.AddPaymentMethod(ctx, params) + if err != nil { + return nil, err + } + + tx, err := s.store.BeginTx(ctx) + if err != nil { + return nil, err + } + + var pp *ProjectPayment + err = withTx(tx, func(tx DBTx) error { + if isDefault { + projectPayment, err := tx.ProjectPayments().GetDefaultByProjectID(ctx, projectID) + if err != nil { + if err != sql.ErrNoRows { + return err + } + } + if projectPayment != nil { + projectPayment.IsDefault = false + + err = tx.ProjectPayments().Update(ctx, *projectPayment) + if err != nil { + return err + } + } + } + + projectPaymentInfo := ProjectPayment{ + ProjectID: projectID, + PayerID: authorization.User.ID, + PaymentMethodID: method.ID, + CreatedAt: time.Now(), + IsDefault: isDefault, + } + + pp, err = tx.ProjectPayments().Create(ctx, projectPaymentInfo) + return err + }) + + return pp, nil +} + +// SetDefaultPaymentMethod set default payment method for given project +func (s *Service) SetDefaultPaymentMethod(ctx context.Context, projectPaymentID uuid.UUID, projectID uuid.UUID) (err error) { + defer mon.Task()(&ctx)(&err) + + _, err = GetAuth(ctx) + if err != nil { + return err + } + + tx, err := s.store.BeginTx(ctx) + if err != nil { + return err + } + + err = withTx(tx, func(tx DBTx) error { + projectPayment, err := tx.ProjectPayments().GetDefaultByProjectID(ctx, projectID) + if err != nil { + return err + } + projectPayment.IsDefault = false + + err = tx.ProjectPayments().Update(ctx, *projectPayment) + if err != nil { + return err + } + + projectPayment, err = tx.ProjectPayments().GetByID(ctx, projectPaymentID) + if err != nil { + return err + } + projectPayment.IsDefault = true + + return tx.ProjectPayments().Update(ctx, *projectPayment) + }) + + return err +} + +// DeleteProjectPaymentMethod deletes selected payment method +func (s *Service) DeleteProjectPaymentMethod(ctx context.Context, projectPayment uuid.UUID) (err error) { + defer mon.Task()(&ctx)(&err) + + _, err = GetAuth(ctx) + if err != nil { + return err + } + + return s.store.ProjectPayments().Delete(ctx, projectPayment) +} + +// GetProjectPaymentMethods retrieves project payment methods +func (s *Service) GetProjectPaymentMethods(ctx context.Context, projectID uuid.UUID) ([]ProjectPayment, error) { + var err error + defer mon.Task()(&ctx)(&err) + + _, err = GetAuth(ctx) + if err != nil { + return nil, err + } + + projectPaymentInfos, err := s.store.ProjectPayments().GetByProjectID(ctx, projectID) + if err != nil { + return nil, err + } + + var projectPayments []ProjectPayment + for _, payment := range projectPaymentInfos { + pm, err := s.pm.GetPaymentMethod(ctx, payment.PaymentMethodID) + if err != nil { + return nil, err + } + + projectPayment := ProjectPayment{ + ID: payment.ID, + CreatedAt: pm.CreatedAt, + PaymentMethodID: pm.ID, + IsDefault: payment.IsDefault, + PayerID: payment.PayerID, + ProjectID: projectID, + Card: Card{ + LastFour: pm.Card.LastFour, + Name: pm.Card.Name, + Brand: pm.Card.Brand, + Country: pm.Card.Country, + ExpirationMonth: pm.Card.ExpMonth, + ExpirationYear: pm.Card.ExpYear, + }, + } + + projectPayments = append(projectPayments, projectPayment) + } + + return projectPayments, nil +} + // GenerateActivationToken - is a method for generating activation token func (s *Service) GenerateActivationToken(ctx context.Context, id uuid.UUID, email string) (token string, err error) { defer mon.Task()(&ctx)(&err) @@ -931,7 +1090,7 @@ func (s *Service) CreateMonthlyProjectInvoices(ctx context.Context, date time.Ti continue } - paymentInfo, err := s.store.ProjectPayments().GetByProjectID(ctx, proj.ID) + paymentInfo, err := s.store.ProjectPayments().GetDefaultByProjectID(ctx, proj.ID) if err != nil { invoiceError.Add(err) continue diff --git a/satellite/payments/localpayments/local.go b/satellite/payments/localpayments/local.go index 8a6f4b22f..ac53e0c11 100644 --- a/satellite/payments/localpayments/local.go +++ b/satellite/payments/localpayments/local.go @@ -6,10 +6,11 @@ package localpayments import ( "context" "crypto/rand" + mathRand "math/rand" "time" "github.com/zeebo/errs" - monkit "gopkg.in/spacemonkeygo/monkit.v2" + "gopkg.in/spacemonkeygo/monkit.v2" "storj.io/storj/satellite/payments" ) @@ -30,22 +31,6 @@ var storjCustomer = payments.Customer{ CreatedAt: creationDate, } -// defaultPaymentMethod represents one and only payment method for local payments, -// which attached to all customers by default -var defaultPaymentMethod = payments.PaymentMethod{ - ID: []byte("0"), - CustomerID: []byte("0"), - Card: payments.Card{ - Country: "us", - Brand: "visa", - Name: "Storj Labs", - ExpMonth: 12, - ExpYear: 2024, - LastFour: "3567", - }, - CreatedAt: creationDate, -} - // internalPaymentsErr is a wrapper for local payments service errors var internalPaymentsErr = errs.Class("internal payments error") @@ -59,6 +44,10 @@ type service struct { db DB } +func (*service) AddPaymentMethod(ctx context.Context, params payments.AddPaymentMethodParams) (*payments.PaymentMethod, error) { + return paymentMethod("", []byte(params.CustomerID)), nil +} + // NewService create new instance of local payments service func NewService(db DB) payments.Service { return &service{db: db} @@ -89,19 +78,19 @@ func (*service) GetCustomer(ctx context.Context, id []byte) (_ *payments.Custome // GetCustomerDefaultPaymentMethod always returns defaultPaymentMethod func (*service) GetCustomerDefaultPaymentMethod(ctx context.Context, customerID []byte) (_ *payments.PaymentMethod, err error) { defer mon.Task()(&ctx)(&err) - return &defaultPaymentMethod, nil + return paymentMethod("", customerID), nil } // GetCustomerPaymentsMethods always returns payments.Customer list with defaultPaymentMethod func (*service) GetCustomerPaymentsMethods(ctx context.Context, customerID []byte) (_ []payments.PaymentMethod, err error) { defer mon.Task()(&ctx)(&err) - return []payments.PaymentMethod{defaultPaymentMethod}, nil + return []payments.PaymentMethod{*paymentMethod("", customerID)}, nil } // GetPaymentMethod always returns defaultPaymentMethod or error func (*service) GetPaymentMethod(ctx context.Context, id []byte) (_ *payments.PaymentMethod, err error) { defer mon.Task()(&ctx)(&err) - return &defaultPaymentMethod, nil + return paymentMethod(string(id), []byte("")), nil } // CreateProjectInvoice creates invoice from provided params @@ -118,3 +107,39 @@ func (*service) GetInvoice(ctx context.Context, id []byte) (_ *payments.Invoice, // TODO: get project invoice stamp by invoice id from the db and fill data return &payments.Invoice{}, nil } + +// paymentMethod returns paymentMethod object which mocks stripe response +func paymentMethod(methodID string, customerID []byte) *payments.PaymentMethod { + id := methodID + if methodID == "" { + id = "pm_" + randomString(24) + } + + cusID := customerID + if len(customerID) <= 1 { + cusID = []byte("cus_" + randomString(14)) + } + + return &payments.PaymentMethod{ + ID: []byte(id), + CustomerID: cusID, + Card: payments.Card{ + Country: "us", + Brand: "visa", + Name: "Storj Labs", + ExpMonth: 12, + ExpYear: 2024, + LastFour: "3567", + }, + CreatedAt: creationDate, + IsDefault: true, + } +} + +func randomString(len int) string { + bytes := make([]byte, len) + for i := 0; i < len; i++ { + bytes[i] = byte(65 + mathRand.Intn(25)) + } + return string(bytes) +} diff --git a/satellite/payments/service.go b/satellite/payments/service.go index 1b113bcdb..255d00c72 100644 --- a/satellite/payments/service.go +++ b/satellite/payments/service.go @@ -11,6 +11,7 @@ import ( // Service is interfaces that defines behavior for working with payments type Service interface { CreateCustomer(ctx context.Context, params CreateCustomerParams) (*Customer, error) + AddPaymentMethod(ctx context.Context, params AddPaymentMethodParams) (*PaymentMethod, error) GetCustomer(ctx context.Context, id []byte) (*Customer, error) GetCustomerDefaultPaymentMethod(ctx context.Context, customerID []byte) (*PaymentMethod, error) GetCustomerPaymentsMethods(ctx context.Context, customerID []byte) ([]PaymentMethod, error) @@ -25,6 +26,12 @@ type CreateCustomerParams struct { Name string } +// AddPaymentMethodParams contains info needed to create new payment method +type AddPaymentMethodParams struct { + Token string + CustomerID string +} + // Customer contains customer info type Customer struct { ID []byte @@ -50,7 +57,8 @@ type PaymentMethod struct { ID []byte CustomerID []byte - Card Card + Card Card + IsDefault bool CreatedAt time.Time } diff --git a/satellite/payments/stripepayments/stripe.go b/satellite/payments/stripepayments/stripe.go index 856d59d23..2616bb4e4 100644 --- a/satellite/payments/stripepayments/stripe.go +++ b/satellite/payments/stripepayments/stripe.go @@ -8,11 +8,11 @@ import ( "fmt" "time" - stripe "github.com/stripe/stripe-go" + "github.com/stripe/stripe-go" "github.com/stripe/stripe-go/client" "github.com/zeebo/errs" "go.uber.org/zap" - monkit "gopkg.in/spacemonkeygo/monkit.v2" + "gopkg.in/spacemonkeygo/monkit.v2" "storj.io/storj/satellite/payments" ) @@ -90,6 +90,40 @@ func (s *service) GetCustomer(ctx context.Context, id []byte) (_ *payments.Custo }, nil } +// AddPaymentMethod add payment method to defined customer +func (s *service) AddPaymentMethod(ctx context.Context, params payments.AddPaymentMethodParams) (*payments.PaymentMethod, error) { + cparams := &stripe.PaymentMethodParams{ + Type: stripe.String(string(stripe.PaymentMethodTypeCard)), + Card: &stripe.PaymentMethodCardParams{Token: ¶ms.Token}, + } + + method, err := s.client.PaymentMethods.New(cparams) + if err != nil { + return nil, err + } + + pparams := &stripe.PaymentMethodAttachParams{ + Customer: ¶ms.CustomerID, + } + + paymentMethod, err := s.client.PaymentMethods.Attach(method.ID, pparams) + if err != nil { + return nil, err + } + + return &payments.PaymentMethod{ + CustomerID: []byte(paymentMethod.Customer.ID), + ID: []byte(paymentMethod.ID), + Card: payments.Card{ + ExpYear: int64(paymentMethod.Card.ExpYear), + ExpMonth: int64(paymentMethod.Card.ExpMonth), + Brand: string(paymentMethod.Card.Brand), + LastFour: paymentMethod.Card.Last4, + Country: paymentMethod.Card.Country, + }, + }, nil +} + // GetCustomerDefaultPaymentMethod retrieves customer default payment method from stripe network func (s *service) GetCustomerDefaultPaymentMethod(ctx context.Context, customerID []byte) (_ *payments.PaymentMethod, err error) { defer mon.Task()(&ctx)(&err) diff --git a/satellite/satellitedb/consoledb.go b/satellite/satellitedb/consoledb.go index 3a7cd2457..211ed97ce 100644 --- a/satellite/satellitedb/consoledb.go +++ b/satellite/satellitedb/consoledb.go @@ -73,7 +73,7 @@ func (db *ConsoleDB) UserPayments() console.UserPayments { // ProjectPayments is a getter for console.ProjectPayments repository func (db *ConsoleDB) ProjectPayments() console.ProjectPayments { - return &projectpayments{db.methods} + return &projectPayments{db.db, db.methods} } // ProjectInvoiceStamps is a getter for console.ProjectInvoiceStamps repository diff --git a/satellite/satellitedb/dbx/satellitedb.dbx b/satellite/satellitedb/dbx/satellitedb.dbx index a3be4b26e..37b0c8024 100644 --- a/satellite/satellitedb/dbx/satellitedb.dbx +++ b/satellite/satellitedb/dbx/satellitedb.dbx @@ -11,7 +11,7 @@ model value_attribution ( ) create value_attribution () -delete value_attribution ( +delete value_attribution ( where value_attribution.project_id = ? where value_attribution.bucket_name = ? ) @@ -273,24 +273,36 @@ read all ( orderby asc project.name ) -// add is default, make composite key model project_payment ( - key project_id + key id + + field id blob field project_id project.id cascade field payer_id user_payment.user_id cascade field payment_method_id blob + field is_default bool ( updatable ) field created_at timestamp ( autoinsert ) ) create project_payment ( ) +update project_payment ( where project_payment.id = ? ) +delete project_payment ( where project_payment.id = ? ) read one ( + select project_payment + where project_payment.id = ? ) +read one ( + select project_payment + where project_payment.project_id = ? + where project_payment.is_default = true +) +read all ( select project_payment where project_payment.project_id = ? ) -read one ( +read all ( select project_payment where project_payment.payer_id = ? ) diff --git a/satellite/satellitedb/dbx/satellitedb.dbx.go b/satellite/satellitedb/dbx/satellitedb.dbx.go index eaca803e3..13c3f42de 100644 --- a/satellite/satellitedb/dbx/satellitedb.dbx.go +++ b/satellite/satellitedb/dbx/satellitedb.dbx.go @@ -537,11 +537,13 @@ CREATE TABLE user_payments ( UNIQUE ( customer_id ) ); CREATE TABLE project_payments ( + id bytea NOT NULL, project_id bytea NOT NULL REFERENCES projects( id ) ON DELETE CASCADE, payer_id bytea NOT NULL REFERENCES user_payments( user_id ) ON DELETE CASCADE, payment_method_id bytea NOT NULL, + is_default boolean NOT NULL, created_at timestamp with time zone NOT NULL, - PRIMARY KEY ( project_id ) + PRIMARY KEY ( id ) ); CREATE INDEX bucket_name_project_id_interval_start_interval_seconds ON bucket_bandwidth_rollups ( bucket_name, project_id, interval_start, interval_seconds ); CREATE UNIQUE INDEX bucket_id_rollup ON bucket_usages ( bucket_id, rollup_end_time ); @@ -876,11 +878,13 @@ CREATE TABLE user_payments ( UNIQUE ( customer_id ) ); CREATE TABLE project_payments ( + id BLOB NOT NULL, project_id BLOB NOT NULL REFERENCES projects( id ) ON DELETE CASCADE, payer_id BLOB NOT NULL REFERENCES user_payments( user_id ) ON DELETE CASCADE, payment_method_id BLOB NOT NULL, + is_default INTEGER NOT NULL, created_at TIMESTAMP NOT NULL, - PRIMARY KEY ( project_id ) + PRIMARY KEY ( id ) ); CREATE INDEX bucket_name_project_id_interval_start_interval_seconds ON bucket_bandwidth_rollups ( bucket_name, project_id, interval_start, interval_seconds ); CREATE UNIQUE INDEX bucket_id_rollup ON bucket_usages ( bucket_id, rollup_end_time ); @@ -4897,17 +4901,39 @@ func (f UserPayment_CreatedAt_Field) value() interface{} { func (UserPayment_CreatedAt_Field) _Column() string { return "created_at" } type ProjectPayment struct { + Id []byte ProjectId []byte PayerId []byte PaymentMethodId []byte + IsDefault bool CreatedAt time.Time } func (ProjectPayment) _Table() string { return "project_payments" } type ProjectPayment_Update_Fields struct { + IsDefault ProjectPayment_IsDefault_Field } +type ProjectPayment_Id_Field struct { + _set bool + _null bool + _value []byte +} + +func ProjectPayment_Id(v []byte) ProjectPayment_Id_Field { + return ProjectPayment_Id_Field{_set: true, _value: v} +} + +func (f ProjectPayment_Id_Field) value() interface{} { + if !f._set || f._null { + return nil + } + return f._value +} + +func (ProjectPayment_Id_Field) _Column() string { return "id" } + type ProjectPayment_ProjectId_Field struct { _set bool _null bool @@ -4965,6 +4991,25 @@ func (f ProjectPayment_PaymentMethodId_Field) value() interface{} { func (ProjectPayment_PaymentMethodId_Field) _Column() string { return "payment_method_id" } +type ProjectPayment_IsDefault_Field struct { + _set bool + _null bool + _value bool +} + +func ProjectPayment_IsDefault(v bool) ProjectPayment_IsDefault_Field { + return ProjectPayment_IsDefault_Field{_set: true, _value: v} +} + +func (f ProjectPayment_IsDefault_Field) value() interface{} { + if !f._set || f._null { + return nil + } + return f._value +} + +func (ProjectPayment_IsDefault_Field) _Column() string { return "is_default" } + type ProjectPayment_CreatedAt_Field struct { _set bool _null bool @@ -5503,24 +5548,28 @@ func (obj *postgresImpl) Create_Project(ctx context.Context, } func (obj *postgresImpl) Create_ProjectPayment(ctx context.Context, + project_payment_id ProjectPayment_Id_Field, project_payment_project_id ProjectPayment_ProjectId_Field, project_payment_payer_id ProjectPayment_PayerId_Field, - project_payment_payment_method_id ProjectPayment_PaymentMethodId_Field) ( + project_payment_payment_method_id ProjectPayment_PaymentMethodId_Field, + project_payment_is_default ProjectPayment_IsDefault_Field) ( project_payment *ProjectPayment, err error) { __now := obj.db.Hooks.Now().UTC() + __id_val := project_payment_id.value() __project_id_val := project_payment_project_id.value() __payer_id_val := project_payment_payer_id.value() __payment_method_id_val := project_payment_payment_method_id.value() + __is_default_val := project_payment_is_default.value() __created_at_val := __now - var __embed_stmt = __sqlbundle_Literal("INSERT INTO project_payments ( project_id, payer_id, payment_method_id, created_at ) VALUES ( ?, ?, ?, ? ) RETURNING project_payments.project_id, project_payments.payer_id, project_payments.payment_method_id, project_payments.created_at") + var __embed_stmt = __sqlbundle_Literal("INSERT INTO project_payments ( id, project_id, payer_id, payment_method_id, is_default, created_at ) VALUES ( ?, ?, ?, ?, ?, ? ) RETURNING project_payments.id, project_payments.project_id, project_payments.payer_id, project_payments.payment_method_id, project_payments.is_default, project_payments.created_at") var __stmt = __sqlbundle_Render(obj.dialect, __embed_stmt) - obj.logStmt(__stmt, __project_id_val, __payer_id_val, __payment_method_id_val, __created_at_val) + obj.logStmt(__stmt, __id_val, __project_id_val, __payer_id_val, __payment_method_id_val, __is_default_val, __created_at_val) project_payment = &ProjectPayment{} - err = obj.driver.QueryRow(__stmt, __project_id_val, __payer_id_val, __payment_method_id_val, __created_at_val).Scan(&project_payment.ProjectId, &project_payment.PayerId, &project_payment.PaymentMethodId, &project_payment.CreatedAt) + err = obj.driver.QueryRow(__stmt, __id_val, __project_id_val, __payer_id_val, __payment_method_id_val, __is_default_val, __created_at_val).Scan(&project_payment.Id, &project_payment.ProjectId, &project_payment.PayerId, &project_payment.PaymentMethodId, &project_payment.IsDefault, &project_payment.CreatedAt) if err != nil { return nil, obj.makeErr(err) } @@ -6414,20 +6463,20 @@ func (obj *postgresImpl) All_Project_By_ProjectMember_MemberId_OrderBy_Asc_Proje } -func (obj *postgresImpl) Get_ProjectPayment_By_ProjectId(ctx context.Context, - project_payment_project_id ProjectPayment_ProjectId_Field) ( +func (obj *postgresImpl) Get_ProjectPayment_By_Id(ctx context.Context, + project_payment_id ProjectPayment_Id_Field) ( project_payment *ProjectPayment, err error) { - var __embed_stmt = __sqlbundle_Literal("SELECT project_payments.project_id, project_payments.payer_id, project_payments.payment_method_id, project_payments.created_at FROM project_payments WHERE project_payments.project_id = ?") + var __embed_stmt = __sqlbundle_Literal("SELECT project_payments.id, project_payments.project_id, project_payments.payer_id, project_payments.payment_method_id, project_payments.is_default, project_payments.created_at FROM project_payments WHERE project_payments.id = ?") var __values []interface{} - __values = append(__values, project_payment_project_id.value()) + __values = append(__values, project_payment_id.value()) var __stmt = __sqlbundle_Render(obj.dialect, __embed_stmt) obj.logStmt(__stmt, __values...) project_payment = &ProjectPayment{} - err = obj.driver.QueryRow(__stmt, __values...).Scan(&project_payment.ProjectId, &project_payment.PayerId, &project_payment.PaymentMethodId, &project_payment.CreatedAt) + err = obj.driver.QueryRow(__stmt, __values...).Scan(&project_payment.Id, &project_payment.ProjectId, &project_payment.PayerId, &project_payment.PaymentMethodId, &project_payment.IsDefault, &project_payment.CreatedAt) if err != nil { return nil, obj.makeErr(err) } @@ -6435,14 +6484,14 @@ func (obj *postgresImpl) Get_ProjectPayment_By_ProjectId(ctx context.Context, } -func (obj *postgresImpl) Get_ProjectPayment_By_PayerId(ctx context.Context, - project_payment_payer_id ProjectPayment_PayerId_Field) ( +func (obj *postgresImpl) Get_ProjectPayment_By_ProjectId_And_IsDefault_Equal_True(ctx context.Context, + project_payment_project_id ProjectPayment_ProjectId_Field) ( project_payment *ProjectPayment, err error) { - var __embed_stmt = __sqlbundle_Literal("SELECT project_payments.project_id, project_payments.payer_id, project_payments.payment_method_id, project_payments.created_at FROM project_payments WHERE project_payments.payer_id = ? LIMIT 2") + var __embed_stmt = __sqlbundle_Literal("SELECT project_payments.id, project_payments.project_id, project_payments.payer_id, project_payments.payment_method_id, project_payments.is_default, project_payments.created_at FROM project_payments WHERE project_payments.project_id = ? AND project_payments.is_default = true LIMIT 2") var __values []interface{} - __values = append(__values, project_payment_payer_id.value()) + __values = append(__values, project_payment_project_id.value()) var __stmt = __sqlbundle_Render(obj.dialect, __embed_stmt) obj.logStmt(__stmt, __values...) @@ -6461,13 +6510,13 @@ func (obj *postgresImpl) Get_ProjectPayment_By_PayerId(ctx context.Context, } project_payment = &ProjectPayment{} - err = __rows.Scan(&project_payment.ProjectId, &project_payment.PayerId, &project_payment.PaymentMethodId, &project_payment.CreatedAt) + err = __rows.Scan(&project_payment.Id, &project_payment.ProjectId, &project_payment.PayerId, &project_payment.PaymentMethodId, &project_payment.IsDefault, &project_payment.CreatedAt) if err != nil { return nil, obj.makeErr(err) } if __rows.Next() { - return nil, tooManyRows("ProjectPayment_By_PayerId") + return nil, tooManyRows("ProjectPayment_By_ProjectId_And_IsDefault_Equal_True") } if err := __rows.Err(); err != nil { @@ -6478,6 +6527,72 @@ func (obj *postgresImpl) Get_ProjectPayment_By_PayerId(ctx context.Context, } +func (obj *postgresImpl) All_ProjectPayment_By_ProjectId(ctx context.Context, + project_payment_project_id ProjectPayment_ProjectId_Field) ( + rows []*ProjectPayment, err error) { + + var __embed_stmt = __sqlbundle_Literal("SELECT project_payments.id, project_payments.project_id, project_payments.payer_id, project_payments.payment_method_id, project_payments.is_default, project_payments.created_at FROM project_payments WHERE project_payments.project_id = ?") + + var __values []interface{} + __values = append(__values, project_payment_project_id.value()) + + var __stmt = __sqlbundle_Render(obj.dialect, __embed_stmt) + obj.logStmt(__stmt, __values...) + + __rows, err := obj.driver.Query(__stmt, __values...) + if err != nil { + return nil, obj.makeErr(err) + } + defer __rows.Close() + + for __rows.Next() { + project_payment := &ProjectPayment{} + err = __rows.Scan(&project_payment.Id, &project_payment.ProjectId, &project_payment.PayerId, &project_payment.PaymentMethodId, &project_payment.IsDefault, &project_payment.CreatedAt) + if err != nil { + return nil, obj.makeErr(err) + } + rows = append(rows, project_payment) + } + if err := __rows.Err(); err != nil { + return nil, obj.makeErr(err) + } + return rows, nil + +} + +func (obj *postgresImpl) All_ProjectPayment_By_PayerId(ctx context.Context, + project_payment_payer_id ProjectPayment_PayerId_Field) ( + rows []*ProjectPayment, err error) { + + var __embed_stmt = __sqlbundle_Literal("SELECT project_payments.id, project_payments.project_id, project_payments.payer_id, project_payments.payment_method_id, project_payments.is_default, project_payments.created_at FROM project_payments WHERE project_payments.payer_id = ?") + + var __values []interface{} + __values = append(__values, project_payment_payer_id.value()) + + var __stmt = __sqlbundle_Render(obj.dialect, __embed_stmt) + obj.logStmt(__stmt, __values...) + + __rows, err := obj.driver.Query(__stmt, __values...) + if err != nil { + return nil, obj.makeErr(err) + } + defer __rows.Close() + + for __rows.Next() { + project_payment := &ProjectPayment{} + err = __rows.Scan(&project_payment.Id, &project_payment.ProjectId, &project_payment.PayerId, &project_payment.PaymentMethodId, &project_payment.IsDefault, &project_payment.CreatedAt) + if err != nil { + return nil, obj.makeErr(err) + } + rows = append(rows, project_payment) + } + if err := __rows.Err(); err != nil { + return nil, obj.makeErr(err) + } + return rows, nil + +} + func (obj *postgresImpl) Get_ProjectInvoiceStamp_By_ProjectId_And_StartDate(ctx context.Context, project_invoice_stamp_project_id ProjectInvoiceStamp_ProjectId_Field, project_invoice_stamp_start_date ProjectInvoiceStamp_StartDate_Field) ( @@ -7779,6 +7894,46 @@ func (obj *postgresImpl) Update_Project_By_Id(ctx context.Context, return project, nil } +func (obj *postgresImpl) Update_ProjectPayment_By_Id(ctx context.Context, + project_payment_id ProjectPayment_Id_Field, + update ProjectPayment_Update_Fields) ( + project_payment *ProjectPayment, err error) { + var __sets = &__sqlbundle_Hole{} + + var __embed_stmt = __sqlbundle_Literals{Join: "", SQLs: []__sqlbundle_SQL{__sqlbundle_Literal("UPDATE project_payments SET "), __sets, __sqlbundle_Literal(" WHERE project_payments.id = ? RETURNING project_payments.id, project_payments.project_id, project_payments.payer_id, project_payments.payment_method_id, project_payments.is_default, project_payments.created_at")}} + + __sets_sql := __sqlbundle_Literals{Join: ", "} + var __values []interface{} + var __args []interface{} + + if update.IsDefault._set { + __values = append(__values, update.IsDefault.value()) + __sets_sql.SQLs = append(__sets_sql.SQLs, __sqlbundle_Literal("is_default = ?")) + } + + if len(__sets_sql.SQLs) == 0 { + return nil, emptyUpdate() + } + + __args = append(__args, project_payment_id.value()) + + __values = append(__values, __args...) + __sets.SQL = __sets_sql + + var __stmt = __sqlbundle_Render(obj.dialect, __embed_stmt) + obj.logStmt(__stmt, __values...) + + project_payment = &ProjectPayment{} + err = obj.driver.QueryRow(__stmt, __values...).Scan(&project_payment.Id, &project_payment.ProjectId, &project_payment.PayerId, &project_payment.PaymentMethodId, &project_payment.IsDefault, &project_payment.CreatedAt) + if err == sql.ErrNoRows { + return nil, nil + } + if err != nil { + return nil, obj.makeErr(err) + } + return project_payment, nil +} + func (obj *postgresImpl) Update_ApiKey_By_Id(ctx context.Context, api_key_id ApiKey_Id_Field, update ApiKey_Update_Fields) ( @@ -8168,6 +8323,32 @@ func (obj *postgresImpl) Delete_Project_By_Id(ctx context.Context, } +func (obj *postgresImpl) Delete_ProjectPayment_By_Id(ctx context.Context, + project_payment_id ProjectPayment_Id_Field) ( + deleted bool, err error) { + + var __embed_stmt = __sqlbundle_Literal("DELETE FROM project_payments WHERE project_payments.id = ?") + + var __values []interface{} + __values = append(__values, project_payment_id.value()) + + var __stmt = __sqlbundle_Render(obj.dialect, __embed_stmt) + obj.logStmt(__stmt, __values...) + + __res, err := obj.driver.Exec(__stmt, __values...) + if err != nil { + return false, obj.makeErr(err) + } + + __count, err := __res.RowsAffected() + if err != nil { + return false, obj.makeErr(err) + } + + return __count > 0, nil + +} + func (obj *postgresImpl) Delete_ProjectMember_By_MemberId_And_ProjectId(ctx context.Context, project_member_member_id ProjectMember_MemberId_Field, project_member_project_id ProjectMember_ProjectId_Field) ( @@ -8990,23 +9171,27 @@ func (obj *sqlite3Impl) Create_Project(ctx context.Context, } func (obj *sqlite3Impl) Create_ProjectPayment(ctx context.Context, + project_payment_id ProjectPayment_Id_Field, project_payment_project_id ProjectPayment_ProjectId_Field, project_payment_payer_id ProjectPayment_PayerId_Field, - project_payment_payment_method_id ProjectPayment_PaymentMethodId_Field) ( + project_payment_payment_method_id ProjectPayment_PaymentMethodId_Field, + project_payment_is_default ProjectPayment_IsDefault_Field) ( project_payment *ProjectPayment, err error) { __now := obj.db.Hooks.Now().UTC() + __id_val := project_payment_id.value() __project_id_val := project_payment_project_id.value() __payer_id_val := project_payment_payer_id.value() __payment_method_id_val := project_payment_payment_method_id.value() + __is_default_val := project_payment_is_default.value() __created_at_val := __now - var __embed_stmt = __sqlbundle_Literal("INSERT INTO project_payments ( project_id, payer_id, payment_method_id, created_at ) VALUES ( ?, ?, ?, ? )") + var __embed_stmt = __sqlbundle_Literal("INSERT INTO project_payments ( id, project_id, payer_id, payment_method_id, is_default, created_at ) VALUES ( ?, ?, ?, ?, ?, ? )") var __stmt = __sqlbundle_Render(obj.dialect, __embed_stmt) - obj.logStmt(__stmt, __project_id_val, __payer_id_val, __payment_method_id_val, __created_at_val) + obj.logStmt(__stmt, __id_val, __project_id_val, __payer_id_val, __payment_method_id_val, __is_default_val, __created_at_val) - __res, err := obj.driver.Exec(__stmt, __project_id_val, __payer_id_val, __payment_method_id_val, __created_at_val) + __res, err := obj.driver.Exec(__stmt, __id_val, __project_id_val, __payer_id_val, __payment_method_id_val, __is_default_val, __created_at_val) if err != nil { return nil, obj.makeErr(err) } @@ -9946,20 +10131,20 @@ func (obj *sqlite3Impl) All_Project_By_ProjectMember_MemberId_OrderBy_Asc_Projec } -func (obj *sqlite3Impl) Get_ProjectPayment_By_ProjectId(ctx context.Context, - project_payment_project_id ProjectPayment_ProjectId_Field) ( +func (obj *sqlite3Impl) Get_ProjectPayment_By_Id(ctx context.Context, + project_payment_id ProjectPayment_Id_Field) ( project_payment *ProjectPayment, err error) { - var __embed_stmt = __sqlbundle_Literal("SELECT project_payments.project_id, project_payments.payer_id, project_payments.payment_method_id, project_payments.created_at FROM project_payments WHERE project_payments.project_id = ?") + var __embed_stmt = __sqlbundle_Literal("SELECT project_payments.id, project_payments.project_id, project_payments.payer_id, project_payments.payment_method_id, project_payments.is_default, project_payments.created_at FROM project_payments WHERE project_payments.id = ?") var __values []interface{} - __values = append(__values, project_payment_project_id.value()) + __values = append(__values, project_payment_id.value()) var __stmt = __sqlbundle_Render(obj.dialect, __embed_stmt) obj.logStmt(__stmt, __values...) project_payment = &ProjectPayment{} - err = obj.driver.QueryRow(__stmt, __values...).Scan(&project_payment.ProjectId, &project_payment.PayerId, &project_payment.PaymentMethodId, &project_payment.CreatedAt) + err = obj.driver.QueryRow(__stmt, __values...).Scan(&project_payment.Id, &project_payment.ProjectId, &project_payment.PayerId, &project_payment.PaymentMethodId, &project_payment.IsDefault, &project_payment.CreatedAt) if err != nil { return nil, obj.makeErr(err) } @@ -9967,14 +10152,14 @@ func (obj *sqlite3Impl) Get_ProjectPayment_By_ProjectId(ctx context.Context, } -func (obj *sqlite3Impl) Get_ProjectPayment_By_PayerId(ctx context.Context, - project_payment_payer_id ProjectPayment_PayerId_Field) ( +func (obj *sqlite3Impl) Get_ProjectPayment_By_ProjectId_And_IsDefault_Equal_True(ctx context.Context, + project_payment_project_id ProjectPayment_ProjectId_Field) ( project_payment *ProjectPayment, err error) { - var __embed_stmt = __sqlbundle_Literal("SELECT project_payments.project_id, project_payments.payer_id, project_payments.payment_method_id, project_payments.created_at FROM project_payments WHERE project_payments.payer_id = ? LIMIT 2") + var __embed_stmt = __sqlbundle_Literal("SELECT project_payments.id, project_payments.project_id, project_payments.payer_id, project_payments.payment_method_id, project_payments.is_default, project_payments.created_at FROM project_payments WHERE project_payments.project_id = ? AND project_payments.is_default = 1 LIMIT 2") var __values []interface{} - __values = append(__values, project_payment_payer_id.value()) + __values = append(__values, project_payment_project_id.value()) var __stmt = __sqlbundle_Render(obj.dialect, __embed_stmt) obj.logStmt(__stmt, __values...) @@ -9993,13 +10178,13 @@ func (obj *sqlite3Impl) Get_ProjectPayment_By_PayerId(ctx context.Context, } project_payment = &ProjectPayment{} - err = __rows.Scan(&project_payment.ProjectId, &project_payment.PayerId, &project_payment.PaymentMethodId, &project_payment.CreatedAt) + err = __rows.Scan(&project_payment.Id, &project_payment.ProjectId, &project_payment.PayerId, &project_payment.PaymentMethodId, &project_payment.IsDefault, &project_payment.CreatedAt) if err != nil { return nil, obj.makeErr(err) } if __rows.Next() { - return nil, tooManyRows("ProjectPayment_By_PayerId") + return nil, tooManyRows("ProjectPayment_By_ProjectId_And_IsDefault_Equal_True") } if err := __rows.Err(); err != nil { @@ -10010,6 +10195,72 @@ func (obj *sqlite3Impl) Get_ProjectPayment_By_PayerId(ctx context.Context, } +func (obj *sqlite3Impl) All_ProjectPayment_By_ProjectId(ctx context.Context, + project_payment_project_id ProjectPayment_ProjectId_Field) ( + rows []*ProjectPayment, err error) { + + var __embed_stmt = __sqlbundle_Literal("SELECT project_payments.id, project_payments.project_id, project_payments.payer_id, project_payments.payment_method_id, project_payments.is_default, project_payments.created_at FROM project_payments WHERE project_payments.project_id = ?") + + var __values []interface{} + __values = append(__values, project_payment_project_id.value()) + + var __stmt = __sqlbundle_Render(obj.dialect, __embed_stmt) + obj.logStmt(__stmt, __values...) + + __rows, err := obj.driver.Query(__stmt, __values...) + if err != nil { + return nil, obj.makeErr(err) + } + defer __rows.Close() + + for __rows.Next() { + project_payment := &ProjectPayment{} + err = __rows.Scan(&project_payment.Id, &project_payment.ProjectId, &project_payment.PayerId, &project_payment.PaymentMethodId, &project_payment.IsDefault, &project_payment.CreatedAt) + if err != nil { + return nil, obj.makeErr(err) + } + rows = append(rows, project_payment) + } + if err := __rows.Err(); err != nil { + return nil, obj.makeErr(err) + } + return rows, nil + +} + +func (obj *sqlite3Impl) All_ProjectPayment_By_PayerId(ctx context.Context, + project_payment_payer_id ProjectPayment_PayerId_Field) ( + rows []*ProjectPayment, err error) { + + var __embed_stmt = __sqlbundle_Literal("SELECT project_payments.id, project_payments.project_id, project_payments.payer_id, project_payments.payment_method_id, project_payments.is_default, project_payments.created_at FROM project_payments WHERE project_payments.payer_id = ?") + + var __values []interface{} + __values = append(__values, project_payment_payer_id.value()) + + var __stmt = __sqlbundle_Render(obj.dialect, __embed_stmt) + obj.logStmt(__stmt, __values...) + + __rows, err := obj.driver.Query(__stmt, __values...) + if err != nil { + return nil, obj.makeErr(err) + } + defer __rows.Close() + + for __rows.Next() { + project_payment := &ProjectPayment{} + err = __rows.Scan(&project_payment.Id, &project_payment.ProjectId, &project_payment.PayerId, &project_payment.PaymentMethodId, &project_payment.IsDefault, &project_payment.CreatedAt) + if err != nil { + return nil, obj.makeErr(err) + } + rows = append(rows, project_payment) + } + if err := __rows.Err(); err != nil { + return nil, obj.makeErr(err) + } + return rows, nil + +} + func (obj *sqlite3Impl) Get_ProjectInvoiceStamp_By_ProjectId_And_StartDate(ctx context.Context, project_invoice_stamp_project_id ProjectInvoiceStamp_ProjectId_Field, project_invoice_stamp_start_date ProjectInvoiceStamp_StartDate_Field) ( @@ -11371,6 +11622,56 @@ func (obj *sqlite3Impl) Update_Project_By_Id(ctx context.Context, return project, nil } +func (obj *sqlite3Impl) Update_ProjectPayment_By_Id(ctx context.Context, + project_payment_id ProjectPayment_Id_Field, + update ProjectPayment_Update_Fields) ( + project_payment *ProjectPayment, err error) { + var __sets = &__sqlbundle_Hole{} + + var __embed_stmt = __sqlbundle_Literals{Join: "", SQLs: []__sqlbundle_SQL{__sqlbundle_Literal("UPDATE project_payments SET "), __sets, __sqlbundle_Literal(" WHERE project_payments.id = ?")}} + + __sets_sql := __sqlbundle_Literals{Join: ", "} + var __values []interface{} + var __args []interface{} + + if update.IsDefault._set { + __values = append(__values, update.IsDefault.value()) + __sets_sql.SQLs = append(__sets_sql.SQLs, __sqlbundle_Literal("is_default = ?")) + } + + if len(__sets_sql.SQLs) == 0 { + return nil, emptyUpdate() + } + + __args = append(__args, project_payment_id.value()) + + __values = append(__values, __args...) + __sets.SQL = __sets_sql + + var __stmt = __sqlbundle_Render(obj.dialect, __embed_stmt) + obj.logStmt(__stmt, __values...) + + project_payment = &ProjectPayment{} + _, err = obj.driver.Exec(__stmt, __values...) + if err != nil { + return nil, obj.makeErr(err) + } + + var __embed_stmt_get = __sqlbundle_Literal("SELECT project_payments.id, project_payments.project_id, project_payments.payer_id, project_payments.payment_method_id, project_payments.is_default, project_payments.created_at FROM project_payments WHERE project_payments.id = ?") + + var __stmt_get = __sqlbundle_Render(obj.dialect, __embed_stmt_get) + obj.logStmt("(IMPLIED) "+__stmt_get, __args...) + + err = obj.driver.QueryRow(__stmt_get, __args...).Scan(&project_payment.Id, &project_payment.ProjectId, &project_payment.PayerId, &project_payment.PaymentMethodId, &project_payment.IsDefault, &project_payment.CreatedAt) + if err == sql.ErrNoRows { + return nil, nil + } + if err != nil { + return nil, obj.makeErr(err) + } + return project_payment, nil +} + func (obj *sqlite3Impl) Update_ApiKey_By_Id(ctx context.Context, api_key_id ApiKey_Id_Field, update ApiKey_Update_Fields) ( @@ -11800,6 +12101,32 @@ func (obj *sqlite3Impl) Delete_Project_By_Id(ctx context.Context, } +func (obj *sqlite3Impl) Delete_ProjectPayment_By_Id(ctx context.Context, + project_payment_id ProjectPayment_Id_Field) ( + deleted bool, err error) { + + var __embed_stmt = __sqlbundle_Literal("DELETE FROM project_payments WHERE project_payments.id = ?") + + var __values []interface{} + __values = append(__values, project_payment_id.value()) + + var __stmt = __sqlbundle_Render(obj.dialect, __embed_stmt) + obj.logStmt(__stmt, __values...) + + __res, err := obj.driver.Exec(__stmt, __values...) + if err != nil { + return false, obj.makeErr(err) + } + + __count, err := __res.RowsAffected() + if err != nil { + return false, obj.makeErr(err) + } + + return __count > 0, nil + +} + func (obj *sqlite3Impl) Delete_ProjectMember_By_MemberId_And_ProjectId(ctx context.Context, project_member_member_id ProjectMember_MemberId_Field, project_member_project_id ProjectMember_ProjectId_Field) ( @@ -12176,13 +12503,13 @@ func (obj *sqlite3Impl) getLastProjectPayment(ctx context.Context, pk int64) ( project_payment *ProjectPayment, err error) { - var __embed_stmt = __sqlbundle_Literal("SELECT project_payments.project_id, project_payments.payer_id, project_payments.payment_method_id, project_payments.created_at FROM project_payments WHERE _rowid_ = ?") + var __embed_stmt = __sqlbundle_Literal("SELECT project_payments.id, project_payments.project_id, project_payments.payer_id, project_payments.payment_method_id, project_payments.is_default, project_payments.created_at FROM project_payments WHERE _rowid_ = ?") var __stmt = __sqlbundle_Render(obj.dialect, __embed_stmt) obj.logStmt(__stmt, pk) project_payment = &ProjectPayment{} - err = obj.driver.QueryRow(__stmt, pk).Scan(&project_payment.ProjectId, &project_payment.PayerId, &project_payment.PaymentMethodId, &project_payment.CreatedAt) + err = obj.driver.QueryRow(__stmt, pk).Scan(&project_payment.Id, &project_payment.ProjectId, &project_payment.PayerId, &project_payment.PaymentMethodId, &project_payment.IsDefault, &project_payment.CreatedAt) if err != nil { return nil, obj.makeErr(err) } @@ -12857,6 +13184,26 @@ func (rx *Rx) All_ProjectMember_By_MemberId(ctx context.Context, return tx.All_ProjectMember_By_MemberId(ctx, project_member_member_id) } +func (rx *Rx) All_ProjectPayment_By_PayerId(ctx context.Context, + project_payment_payer_id ProjectPayment_PayerId_Field) ( + rows []*ProjectPayment, err error) { + var tx *Tx + if tx, err = rx.getTx(ctx); err != nil { + return + } + return tx.All_ProjectPayment_By_PayerId(ctx, project_payment_payer_id) +} + +func (rx *Rx) All_ProjectPayment_By_ProjectId(ctx context.Context, + project_payment_project_id ProjectPayment_ProjectId_Field) ( + rows []*ProjectPayment, err error) { + var tx *Tx + if tx, err = rx.getTx(ctx); err != nil { + return + } + return tx.All_ProjectPayment_By_ProjectId(ctx, project_payment_project_id) +} + func (rx *Rx) All_Project_By_CreatedAt_Less_OrderBy_Asc_CreatedAt(ctx context.Context, project_created_at_less Project_CreatedAt_Field) ( rows []*Project, err error) { @@ -13179,15 +13526,17 @@ func (rx *Rx) Create_ProjectMember(ctx context.Context, } func (rx *Rx) Create_ProjectPayment(ctx context.Context, + project_payment_id ProjectPayment_Id_Field, project_payment_project_id ProjectPayment_ProjectId_Field, project_payment_payer_id ProjectPayment_PayerId_Field, - project_payment_payment_method_id ProjectPayment_PaymentMethodId_Field) ( + project_payment_payment_method_id ProjectPayment_PaymentMethodId_Field, + project_payment_is_default ProjectPayment_IsDefault_Field) ( project_payment *ProjectPayment, err error) { var tx *Tx if tx, err = rx.getTx(ctx); err != nil { return } - return tx.Create_ProjectPayment(ctx, project_payment_project_id, project_payment_payer_id, project_payment_payment_method_id) + return tx.Create_ProjectPayment(ctx, project_payment_id, project_payment_project_id, project_payment_payer_id, project_payment_payment_method_id, project_payment_is_default) } @@ -13401,6 +13750,16 @@ func (rx *Rx) Delete_ProjectMember_By_MemberId_And_ProjectId(ctx context.Context return tx.Delete_ProjectMember_By_MemberId_And_ProjectId(ctx, project_member_member_id, project_member_project_id) } +func (rx *Rx) Delete_ProjectPayment_By_Id(ctx context.Context, + project_payment_id ProjectPayment_Id_Field) ( + deleted bool, err error) { + var tx *Tx + if tx, err = rx.getTx(ctx); err != nil { + return + } + return tx.Delete_ProjectPayment_By_Id(ctx, project_payment_id) +} + func (rx *Rx) Delete_Project_By_Id(ctx context.Context, project_id Project_Id_Field) ( deleted bool, err error) { @@ -13630,24 +13989,24 @@ func (rx *Rx) Get_ProjectInvoiceStamp_By_ProjectId_And_StartDate(ctx context.Con return tx.Get_ProjectInvoiceStamp_By_ProjectId_And_StartDate(ctx, project_invoice_stamp_project_id, project_invoice_stamp_start_date) } -func (rx *Rx) Get_ProjectPayment_By_PayerId(ctx context.Context, - project_payment_payer_id ProjectPayment_PayerId_Field) ( +func (rx *Rx) Get_ProjectPayment_By_Id(ctx context.Context, + project_payment_id ProjectPayment_Id_Field) ( project_payment *ProjectPayment, err error) { var tx *Tx if tx, err = rx.getTx(ctx); err != nil { return } - return tx.Get_ProjectPayment_By_PayerId(ctx, project_payment_payer_id) + return tx.Get_ProjectPayment_By_Id(ctx, project_payment_id) } -func (rx *Rx) Get_ProjectPayment_By_ProjectId(ctx context.Context, +func (rx *Rx) Get_ProjectPayment_By_ProjectId_And_IsDefault_Equal_True(ctx context.Context, project_payment_project_id ProjectPayment_ProjectId_Field) ( project_payment *ProjectPayment, err error) { var tx *Tx if tx, err = rx.getTx(ctx); err != nil { return } - return tx.Get_ProjectPayment_By_ProjectId(ctx, project_payment_project_id) + return tx.Get_ProjectPayment_By_ProjectId_And_IsDefault_Equal_True(ctx, project_payment_project_id) } func (rx *Rx) Get_Project_By_Id(ctx context.Context, @@ -13898,6 +14257,17 @@ func (rx *Rx) Update_PendingAudits_By_NodeId(ctx context.Context, return tx.Update_PendingAudits_By_NodeId(ctx, pending_audits_node_id, update) } +func (rx *Rx) Update_ProjectPayment_By_Id(ctx context.Context, + project_payment_id ProjectPayment_Id_Field, + update ProjectPayment_Update_Fields) ( + project_payment *ProjectPayment, err error) { + var tx *Tx + if tx, err = rx.getTx(ctx); err != nil { + return + } + return tx.Update_ProjectPayment_By_Id(ctx, project_payment_id, update) +} + func (rx *Rx) Update_Project_By_Id(ctx context.Context, project_id Project_Id_Field, update Project_Update_Fields) ( @@ -13964,6 +14334,14 @@ type Methods interface { project_member_member_id ProjectMember_MemberId_Field) ( rows []*ProjectMember, err error) + All_ProjectPayment_By_PayerId(ctx context.Context, + project_payment_payer_id ProjectPayment_PayerId_Field) ( + rows []*ProjectPayment, err error) + + All_ProjectPayment_By_ProjectId(ctx context.Context, + project_payment_project_id ProjectPayment_ProjectId_Field) ( + rows []*ProjectPayment, err error) + All_Project_By_CreatedAt_Less_OrderBy_Asc_CreatedAt(ctx context.Context, project_created_at_less Project_CreatedAt_Field) ( rows []*Project, err error) @@ -14146,9 +14524,11 @@ type Methods interface { project_member *ProjectMember, err error) Create_ProjectPayment(ctx context.Context, + project_payment_id ProjectPayment_Id_Field, project_payment_project_id ProjectPayment_ProjectId_Field, project_payment_payer_id ProjectPayment_PayerId_Field, - project_payment_payment_method_id ProjectPayment_PaymentMethodId_Field) ( + project_payment_payment_method_id ProjectPayment_PaymentMethodId_Field, + project_payment_is_default ProjectPayment_IsDefault_Field) ( project_payment *ProjectPayment, err error) Create_RegistrationToken(ctx context.Context, @@ -14244,6 +14624,10 @@ type Methods interface { project_member_project_id ProjectMember_ProjectId_Field) ( deleted bool, err error) + Delete_ProjectPayment_By_Id(ctx context.Context, + project_payment_id ProjectPayment_Id_Field) ( + deleted bool, err error) + Delete_Project_By_Id(ctx context.Context, project_id Project_Id_Field) ( deleted bool, err error) @@ -14340,11 +14724,11 @@ type Methods interface { project_invoice_stamp_start_date ProjectInvoiceStamp_StartDate_Field) ( project_invoice_stamp *ProjectInvoiceStamp, err error) - Get_ProjectPayment_By_PayerId(ctx context.Context, - project_payment_payer_id ProjectPayment_PayerId_Field) ( + Get_ProjectPayment_By_Id(ctx context.Context, + project_payment_id ProjectPayment_Id_Field) ( project_payment *ProjectPayment, err error) - Get_ProjectPayment_By_ProjectId(ctx context.Context, + Get_ProjectPayment_By_ProjectId_And_IsDefault_Equal_True(ctx context.Context, project_payment_project_id ProjectPayment_ProjectId_Field) ( project_payment *ProjectPayment, err error) @@ -14458,6 +14842,11 @@ type Methods interface { update PendingAudits_Update_Fields) ( pending_audits *PendingAudits, err error) + Update_ProjectPayment_By_Id(ctx context.Context, + project_payment_id ProjectPayment_Id_Field, + update ProjectPayment_Update_Fields) ( + project_payment *ProjectPayment, err error) + Update_Project_By_Id(ctx context.Context, project_id Project_Id_Field, update Project_Update_Fields) ( diff --git a/satellite/satellitedb/dbx/satellitedb.dbx.postgres.sql b/satellite/satellitedb/dbx/satellitedb.dbx.postgres.sql index 2d7ff9d79..06ff9badb 100644 --- a/satellite/satellitedb/dbx/satellitedb.dbx.postgres.sql +++ b/satellite/satellitedb/dbx/satellitedb.dbx.postgres.sql @@ -264,11 +264,13 @@ CREATE TABLE user_payments ( UNIQUE ( customer_id ) ); CREATE TABLE project_payments ( + id bytea NOT NULL, project_id bytea NOT NULL REFERENCES projects( id ) ON DELETE CASCADE, payer_id bytea NOT NULL REFERENCES user_payments( user_id ) ON DELETE CASCADE, payment_method_id bytea NOT NULL, + is_default boolean NOT NULL, created_at timestamp with time zone NOT NULL, - PRIMARY KEY ( project_id ) + PRIMARY KEY ( id ) ); CREATE INDEX bucket_name_project_id_interval_start_interval_seconds ON bucket_bandwidth_rollups ( bucket_name, project_id, interval_start, interval_seconds ); CREATE UNIQUE INDEX bucket_id_rollup ON bucket_usages ( bucket_id, rollup_end_time ); diff --git a/satellite/satellitedb/dbx/satellitedb.dbx.sqlite3.sql b/satellite/satellitedb/dbx/satellitedb.dbx.sqlite3.sql index 69cafef2d..510da40d1 100644 --- a/satellite/satellitedb/dbx/satellitedb.dbx.sqlite3.sql +++ b/satellite/satellitedb/dbx/satellitedb.dbx.sqlite3.sql @@ -264,11 +264,13 @@ CREATE TABLE user_payments ( UNIQUE ( customer_id ) ); CREATE TABLE project_payments ( + id BLOB NOT NULL, project_id BLOB NOT NULL REFERENCES projects( id ) ON DELETE CASCADE, payer_id BLOB NOT NULL REFERENCES user_payments( user_id ) ON DELETE CASCADE, payment_method_id BLOB NOT NULL, + is_default INTEGER NOT NULL, created_at TIMESTAMP NOT NULL, - PRIMARY KEY ( project_id ) + PRIMARY KEY ( id ) ); CREATE INDEX bucket_name_project_id_interval_start_interval_seconds ON bucket_bandwidth_rollups ( bucket_name, project_id, interval_start, interval_seconds ); CREATE UNIQUE INDEX bucket_id_rollup ON bucket_usages ( bucket_id, rollup_end_time ); diff --git a/satellite/satellitedb/locked.go b/satellite/satellitedb/locked.go index 40e570666..84589fab7 100644 --- a/satellite/satellitedb/locked.go +++ b/satellite/satellitedb/locked.go @@ -346,18 +346,42 @@ func (m *lockedProjectPayments) Create(ctx context.Context, info console.Project return m.db.Create(ctx, info) } -func (m *lockedProjectPayments) GetByPayerID(ctx context.Context, payerID uuid.UUID) (*console.ProjectPayment, error) { +func (m *lockedProjectPayments) Delete(ctx context.Context, projectPaymentID uuid.UUID) error { + m.Lock() + defer m.Unlock() + return m.db.Delete(ctx, projectPaymentID) +} + +func (m *lockedProjectPayments) GetByID(ctx context.Context, projectPaymentID uuid.UUID) (*console.ProjectPayment, error) { + m.Lock() + defer m.Unlock() + return m.db.GetByID(ctx, projectPaymentID) +} + +func (m *lockedProjectPayments) GetByPayerID(ctx context.Context, payerID uuid.UUID) ([]*console.ProjectPayment, error) { m.Lock() defer m.Unlock() return m.db.GetByPayerID(ctx, payerID) } -func (m *lockedProjectPayments) GetByProjectID(ctx context.Context, projectID uuid.UUID) (*console.ProjectPayment, error) { +func (m *lockedProjectPayments) GetByProjectID(ctx context.Context, projectID uuid.UUID) ([]*console.ProjectPayment, error) { m.Lock() defer m.Unlock() return m.db.GetByProjectID(ctx, projectID) } +func (m *lockedProjectPayments) GetDefaultByProjectID(ctx context.Context, projectID uuid.UUID) (*console.ProjectPayment, error) { + m.Lock() + defer m.Unlock() + return m.db.GetDefaultByProjectID(ctx, projectID) +} + +func (m *lockedProjectPayments) Update(ctx context.Context, info console.ProjectPayment) error { + m.Lock() + defer m.Unlock() + return m.db.Update(ctx, info) +} + // Projects is a getter for Projects repository func (m *lockedConsole) Projects() console.Projects { m.Lock() diff --git a/satellite/satellitedb/migrate.go b/satellite/satellitedb/migrate.go index 8ab09f085..8240010c0 100644 --- a/satellite/satellitedb/migrate.go +++ b/satellite/satellitedb/migrate.go @@ -927,6 +927,22 @@ func (db *DB) PostgresMigration() *migrate.Migration { `UPDATE nodes SET disqualified=NULL WHERE disqualified IS NOT NULL AND audit_reputation_alpha / (audit_reputation_alpha + audit_reputation_beta) >= 0.6;`, }, }, + { + Description: "Add unique id for project payments. Add is_default property", + Version: 40, + Action: migrate.SQL{ + `DROP TABLE project_payments CASCADE`, + `CREATE TABLE project_payments ( + id bytea NOT NULL, + project_id bytea NOT NULL REFERENCES projects( id ) ON DELETE CASCADE, + payer_id bytea NOT NULL REFERENCES user_payments( user_id ) ON DELETE CASCADE, + payment_method_id bytea NOT NULL, + is_default boolean NOT NULL, + created_at timestamp with time zone NOT NULL, + PRIMARY KEY ( id ) + );`, + }, + }, }, } } diff --git a/satellite/satellitedb/projectpaymentinfos.go b/satellite/satellitedb/projectpaymentinfos.go index 40e64fce2..c5de8f4ac 100644 --- a/satellite/satellitedb/projectpaymentinfos.go +++ b/satellite/satellitedb/projectpaymentinfos.go @@ -7,53 +7,95 @@ import ( "context" "github.com/skyrings/skyring-common/tools/uuid" + "github.com/zeebo/errs" "storj.io/storj/satellite/console" dbx "storj.io/storj/satellite/satellitedb/dbx" ) -// projectpayments is the an implementation of console.ProjectPayments. +// projectPayments is the an implementation of console.ProjectPayments. // Allows to work with project payment info storage -type projectpayments struct { - db dbx.Methods +type projectPayments struct { + db *dbx.DB + methods dbx.Methods +} + +func (pp *projectPayments) Delete(ctx context.Context, projectPaymentID uuid.UUID) error { + _, err := pp.methods.Delete_ProjectPayment_By_Id(ctx, dbx.ProjectPayment_Id(projectPaymentID[:])) + return err +} + +func (pp *projectPayments) GetByID(ctx context.Context, projectPaymentID uuid.UUID) (*console.ProjectPayment, error) { + dbxInfo, err := pp.methods.Get_ProjectPayment_By_Id(ctx, dbx.ProjectPayment_Id(projectPaymentID[:])) + if err != nil { + return nil, err + } + + return fromDBXProjectPayment(ctx, dbxInfo) +} + +func (pp *projectPayments) Update(ctx context.Context, info console.ProjectPayment) error { + updateFields := dbx.ProjectPayment_Update_Fields{ + IsDefault: dbx.ProjectPayment_IsDefault(info.IsDefault), + } + + _, err := pp.methods.Update_ProjectPayment_By_Id(ctx, dbx.ProjectPayment_Id(info.ID[:]), updateFields) + return err +} + +func (pp *projectPayments) GetDefaultByProjectID(ctx context.Context, projectID uuid.UUID) (*console.ProjectPayment, error) { + dbxInfo, err := pp.methods.Get_ProjectPayment_By_ProjectId_And_IsDefault_Equal_True(ctx, dbx.ProjectPayment_ProjectId(projectID[:])) + if err != nil { + return nil, err + } + + return fromDBXProjectPayment(ctx, dbxInfo) } // Create stores new project payment info into db -func (infos *projectpayments) Create(ctx context.Context, info console.ProjectPayment) (*console.ProjectPayment, error) { - dbxInfo, err := infos.db.Create_ProjectPayment(ctx, +func (pp *projectPayments) Create(ctx context.Context, info console.ProjectPayment) (*console.ProjectPayment, error) { + id, err := uuid.New() + if err != nil { + return nil, err + } + + dbxInfo, err := pp.methods.Create_ProjectPayment(ctx, + dbx.ProjectPayment_Id(id[:]), dbx.ProjectPayment_ProjectId(info.ProjectID[:]), dbx.ProjectPayment_PayerId(info.PayerID[:]), - dbx.ProjectPayment_PaymentMethodId(info.PaymentMethodID)) + dbx.ProjectPayment_PaymentMethodId(info.PaymentMethodID), + dbx.ProjectPayment_IsDefault(info.IsDefault)) if err != nil { return nil, err } - return fromDBXProjectPayment(dbxInfo) + return fromDBXProjectPayment(ctx, dbxInfo) } // GetByProjectID retrieves project payment info from db by projectID -func (infos *projectpayments) GetByProjectID(ctx context.Context, projectID uuid.UUID) (*console.ProjectPayment, error) { - dbxInfo, err := infos.db.Get_ProjectPayment_By_ProjectId(ctx, dbx.ProjectPayment_ProjectId(projectID[:])) +func (pp *projectPayments) GetByProjectID(ctx context.Context, projectID uuid.UUID) ([]*console.ProjectPayment, error) { + dbxInfos, err := pp.methods.All_ProjectPayment_By_ProjectId(ctx, dbx.ProjectPayment_ProjectId(projectID[:])) if err != nil { return nil, err } - return fromDBXProjectPayment(dbxInfo) + return fromDBXProjectPaymentSlice(ctx, dbxInfos) } // GetByPayerID retrieves project payment info from db by payerID(userID) -func (infos *projectpayments) GetByPayerID(ctx context.Context, payerID uuid.UUID) (*console.ProjectPayment, error) { - dbxInfo, err := infos.db.Get_ProjectPayment_By_PayerId(ctx, dbx.ProjectPayment_PayerId(payerID[:])) +func (pp *projectPayments) GetByPayerID(ctx context.Context, payerID uuid.UUID) ([]*console.ProjectPayment, error) { + dbxInfos, err := pp.methods.All_ProjectPayment_By_PayerId(ctx, dbx.ProjectPayment_PayerId(payerID[:])) if err != nil { return nil, err } - return fromDBXProjectPayment(dbxInfo) + return fromDBXProjectPaymentSlice(ctx, dbxInfos) } // fromDBXProjectPayment is a helper method to convert from *dbx.ProjectPayment to *console.ProjectPayment -func fromDBXProjectPayment(dbxInfo *dbx.ProjectPayment) (*console.ProjectPayment, error) { +func fromDBXProjectPayment(ctx context.Context, dbxInfo *dbx.ProjectPayment) (_ *console.ProjectPayment, err error) { + defer mon.Task()(&ctx)(&err) projectID, err := bytesToUUID(dbxInfo.ProjectId) if err != nil { return nil, err @@ -64,10 +106,36 @@ func fromDBXProjectPayment(dbxInfo *dbx.ProjectPayment) (*console.ProjectPayment return nil, err } + id, err := bytesToUUID(dbxInfo.Id) + if err != nil { + return nil, err + } + return &console.ProjectPayment{ + ID: id, ProjectID: projectID, PayerID: payerID, PaymentMethodID: dbxInfo.PaymentMethodId, CreatedAt: dbxInfo.CreatedAt, + IsDefault: dbxInfo.IsDefault, }, nil } + +func fromDBXProjectPaymentSlice(ctx context.Context, dbxInfos []*dbx.ProjectPayment) (_ []*console.ProjectPayment, err error) { + defer mon.Task()(&ctx)(&err) + var projectPayments []*console.ProjectPayment + var errors []error + + // Generating []dbo from []dbx and collecting all errors + for _, paymentMethodDBX := range dbxInfos { + projectPayment, err := fromDBXProjectPayment(ctx, paymentMethodDBX) + if err != nil { + errors = append(errors, err) + continue + } + + projectPayments = append(projectPayments, projectPayment) + } + + return projectPayments, errs.Combine(errors...) +} diff --git a/satellite/satellitedb/testdata/postgres.v40.sql b/satellite/satellitedb/testdata/postgres.v40.sql new file mode 100644 index 000000000..be34249b2 --- /dev/null +++ b/satellite/satellitedb/testdata/postgres.v40.sql @@ -0,0 +1,345 @@ +-- AUTOGENERATED BY gopkg.in/spacemonkeygo/dbx.v1 +-- DO NOT EDIT +CREATE TABLE accounting_rollups ( + id bigserial NOT NULL, + node_id bytea NOT NULL, + start_time timestamp with time zone NOT NULL, + put_total bigint NOT NULL, + get_total bigint NOT NULL, + get_audit_total bigint NOT NULL, + get_repair_total bigint NOT NULL, + put_repair_total bigint NOT NULL, + at_rest_total double precision NOT NULL, + PRIMARY KEY ( id ) +); +CREATE TABLE accounting_timestamps ( + name text NOT NULL, + value timestamp with time zone NOT NULL, + PRIMARY KEY ( name ) +); +CREATE TABLE bucket_bandwidth_rollups ( + bucket_name bytea NOT NULL, + project_id bytea NOT NULL, + interval_start timestamp NOT NULL, + interval_seconds integer NOT NULL, + action integer NOT NULL, + inline bigint NOT NULL, + allocated bigint NOT NULL, + settled bigint NOT NULL, + PRIMARY KEY ( bucket_name, project_id, interval_start, action ) +); +CREATE TABLE bucket_storage_tallies ( + bucket_name bytea NOT NULL, + project_id bytea NOT NULL, + interval_start timestamp NOT NULL, + inline bigint NOT NULL, + remote bigint NOT NULL, + remote_segments_count integer NOT NULL, + inline_segments_count integer NOT NULL, + object_count integer NOT NULL, + metadata_size bigint NOT NULL, + PRIMARY KEY ( bucket_name, project_id, interval_start ) +); +CREATE TABLE bucket_usages ( + id bytea NOT NULL, + bucket_id bytea NOT NULL, + rollup_end_time timestamp with time zone NOT NULL, + remote_stored_data bigint NOT NULL, + inline_stored_data bigint NOT NULL, + remote_segments integer NOT NULL, + inline_segments integer NOT NULL, + objects integer NOT NULL, + metadata_size bigint NOT NULL, + repair_egress bigint NOT NULL, + get_egress bigint NOT NULL, + audit_egress bigint NOT NULL, + PRIMARY KEY ( id ) +); +CREATE TABLE certRecords ( + publickey bytea NOT NULL, + id bytea NOT NULL, + update_at timestamp with time zone NOT NULL, + PRIMARY KEY ( id ) +); +CREATE TABLE injuredsegments ( + path text NOT NULL, + data bytea NOT NULL, + attempted timestamp, + PRIMARY KEY ( path ) +); +CREATE TABLE irreparabledbs ( + segmentpath bytea NOT NULL, + segmentdetail bytea NOT NULL, + pieces_lost_count bigint NOT NULL, + seg_damaged_unix_sec bigint NOT NULL, + repair_attempt_count bigint NOT NULL, + PRIMARY KEY ( segmentpath ) +); +CREATE TABLE nodes ( + id bytea NOT NULL, + address text NOT NULL, + last_net text NOT NULL, + protocol integer NOT NULL, + type integer NOT NULL, + email text NOT NULL, + wallet text NOT NULL, + free_bandwidth bigint NOT NULL, + free_disk bigint NOT NULL, + major bigint NOT NULL, + minor bigint NOT NULL, + patch bigint NOT NULL, + hash text NOT NULL, + timestamp timestamp with time zone NOT NULL, + release boolean NOT NULL, + latency_90 bigint NOT NULL, + audit_success_count bigint NOT NULL, + total_audit_count bigint NOT NULL, + uptime_success_count bigint NOT NULL, + total_uptime_count bigint NOT NULL, + created_at timestamp with time zone NOT NULL, + updated_at timestamp with time zone NOT NULL, + last_contact_success timestamp with time zone NOT NULL, + last_contact_failure timestamp with time zone NOT NULL, + contained boolean NOT NULL, + disqualified timestamp with time zone, + audit_reputation_alpha double precision NOT NULL, + audit_reputation_beta double precision NOT NULL, + uptime_reputation_alpha double precision NOT NULL, + uptime_reputation_beta double precision NOT NULL, + PRIMARY KEY ( id ) +); +CREATE TABLE offers ( + id serial NOT NULL, + name text NOT NULL, + description text NOT NULL, + award_credit_in_cents integer NOT NULL, + invitee_credit_in_cents integer NOT NULL, + award_credit_duration_days integer NOT NULL, + invitee_credit_duration_days integer NOT NULL, + redeemable_cap integer NOT NULL, + num_redeemed integer NOT NULL, + expires_at timestamp with time zone NOT NULL, + created_at timestamp with time zone NOT NULL, + status integer NOT NULL, + type integer NOT NULL, + PRIMARY KEY ( id ) +); +CREATE TABLE pending_audits ( + node_id bytea NOT NULL, + piece_id bytea NOT NULL, + stripe_index bigint NOT NULL, + share_size bigint NOT NULL, + expected_share_hash bytea NOT NULL, + reverify_count bigint NOT NULL, + PRIMARY KEY ( node_id ) +); +CREATE TABLE projects ( + id bytea NOT NULL, + name text NOT NULL, + description text NOT NULL, + usage_limit bigint NOT NULL, + created_at timestamp with time zone NOT NULL, + PRIMARY KEY ( id ) +); +CREATE TABLE registration_tokens ( + secret bytea NOT NULL, + owner_id bytea, + project_limit integer NOT NULL, + created_at timestamp with time zone NOT NULL, + PRIMARY KEY ( secret ), + UNIQUE ( owner_id ) +); +CREATE TABLE reset_password_tokens ( + secret bytea NOT NULL, + owner_id bytea NOT NULL, + created_at timestamp with time zone NOT NULL, + PRIMARY KEY ( secret ), + UNIQUE ( owner_id ) +); +CREATE TABLE serial_numbers ( + id serial NOT NULL, + serial_number bytea NOT NULL, + bucket_id bytea NOT NULL, + expires_at timestamp NOT NULL, + PRIMARY KEY ( id ) +); +CREATE TABLE storagenode_bandwidth_rollups ( + storagenode_id bytea NOT NULL, + interval_start timestamp NOT NULL, + interval_seconds integer NOT NULL, + action integer NOT NULL, + allocated bigint NOT NULL, + settled bigint NOT NULL, + PRIMARY KEY ( storagenode_id, interval_start, action ) +); +CREATE TABLE storagenode_storage_tallies ( + id bigserial NOT NULL, + node_id bytea NOT NULL, + interval_end_time timestamp with time zone NOT NULL, + data_total double precision NOT NULL, + PRIMARY KEY ( id ) +); +CREATE TABLE users ( + id bytea NOT NULL, + email text NOT NULL, + full_name text NOT NULL, + short_name text, + password_hash bytea NOT NULL, + status integer NOT NULL, + created_at timestamp with time zone NOT NULL, + PRIMARY KEY ( id ) +); +CREATE TABLE value_attributions ( + project_id bytea NOT NULL, + bucket_name bytea NOT NULL, + partner_id bytea NOT NULL, + last_updated timestamp NOT NULL, + PRIMARY KEY ( project_id, bucket_name ) +); +CREATE TABLE api_keys ( + id bytea NOT NULL, + project_id bytea NOT NULL REFERENCES projects( id ) ON DELETE CASCADE, + head bytea NOT NULL, + name text NOT NULL, + secret bytea NOT NULL, + created_at timestamp with time zone NOT NULL, + PRIMARY KEY ( id ), + UNIQUE ( head ), + UNIQUE ( name, project_id ) +); +CREATE TABLE bucket_metainfos ( + id bytea NOT NULL, + project_id bytea NOT NULL REFERENCES projects( id ), + name bytea NOT NULL, + path_cipher integer NOT NULL, + created_at timestamp with time zone NOT NULL, + default_segment_size integer NOT NULL, + default_encryption_cipher_suite integer NOT NULL, + default_encryption_block_size integer NOT NULL, + default_redundancy_algorithm integer NOT NULL, + default_redundancy_share_size integer NOT NULL, + default_redundancy_required_shares integer NOT NULL, + default_redundancy_repair_shares integer NOT NULL, + default_redundancy_optimal_shares integer NOT NULL, + default_redundancy_total_shares integer NOT NULL, + PRIMARY KEY ( id ), + UNIQUE ( name, project_id ) +); +CREATE TABLE project_invoice_stamps ( + project_id bytea NOT NULL REFERENCES projects( id ) ON DELETE CASCADE, + invoice_id bytea NOT NULL, + start_date timestamp with time zone NOT NULL, + end_date timestamp with time zone NOT NULL, + created_at timestamp with time zone NOT NULL, + PRIMARY KEY ( project_id, start_date, end_date ), + UNIQUE ( invoice_id ) +); +CREATE TABLE project_members ( + member_id bytea NOT NULL REFERENCES users( id ) ON DELETE CASCADE, + project_id bytea NOT NULL REFERENCES projects( id ) ON DELETE CASCADE, + created_at timestamp with time zone NOT NULL, + PRIMARY KEY ( member_id, project_id ) +); +CREATE TABLE used_serials ( + serial_number_id integer NOT NULL REFERENCES serial_numbers( id ) ON DELETE CASCADE, + storage_node_id bytea NOT NULL, + PRIMARY KEY ( serial_number_id, storage_node_id ) +); +CREATE TABLE user_credits ( + id serial NOT NULL, + user_id bytea NOT NULL REFERENCES users( id ), + offer_id integer NOT NULL REFERENCES offers( id ), + referred_by bytea REFERENCES users( id ), + credits_earned_in_cents integer NOT NULL, + credits_used_in_cents integer NOT NULL, + expires_at timestamp with time zone NOT NULL, + created_at timestamp with time zone NOT NULL, + PRIMARY KEY ( id ) +); +CREATE TABLE user_payments ( + user_id bytea NOT NULL REFERENCES users( id ) ON DELETE CASCADE, + customer_id bytea NOT NULL, + created_at timestamp with time zone NOT NULL, + PRIMARY KEY ( user_id ), + UNIQUE ( customer_id ) +); +CREATE TABLE project_payments ( + id bytea NOT NULL, + project_id bytea NOT NULL REFERENCES projects( id ) ON DELETE CASCADE, + payer_id bytea NOT NULL REFERENCES user_payments( user_id ) ON DELETE CASCADE, + payment_method_id bytea NOT NULL, + is_default boolean NOT NULL, + created_at timestamp with time zone NOT NULL, + PRIMARY KEY ( id ) +); +CREATE INDEX bucket_name_project_id_interval_start_interval_seconds ON bucket_bandwidth_rollups ( bucket_name, project_id, interval_start, interval_seconds ); +CREATE UNIQUE INDEX bucket_id_rollup ON bucket_usages ( bucket_id, rollup_end_time ); +CREATE INDEX node_last_ip ON nodes ( last_net ); +CREATE UNIQUE INDEX serial_number ON serial_numbers ( serial_number ); +CREATE INDEX serial_numbers_expires_at_index ON serial_numbers ( expires_at ); +CREATE INDEX storagenode_id_interval_start_interval_seconds ON storagenode_bandwidth_rollups ( storagenode_id, interval_start, interval_seconds ); + +--- + +INSERT INTO "accounting_rollups"("id", "node_id", "start_time", "put_total", "get_total", "get_audit_total", "get_repair_total", "put_repair_total", "at_rest_total") VALUES (1, E'\\367M\\177\\251]t/\\022\\256\\214\\265\\025\\224\\204:\\217\\212\\0102<\\321\\374\\020&\\271Qc\\325\\261\\354\\246\\233'::bytea, '2019-02-09 00:00:00+00', 1000, 2000, 3000, 4000, 0, 5000); + +INSERT INTO "accounting_timestamps" VALUES ('LastAtRestTally', '0001-01-01 00:00:00+00'); +INSERT INTO "accounting_timestamps" VALUES ('LastRollup', '0001-01-01 00:00:00+00'); +INSERT INTO "accounting_timestamps" VALUES ('LastBandwidthTally', '0001-01-01 00:00:00+00'); + +INSERT INTO "nodes"("id", "address", "last_net", "protocol", "type", "email", "wallet", "free_bandwidth", "free_disk", "major", "minor", "patch", "hash", "timestamp", "release","latency_90", "audit_success_count", "total_audit_count", "uptime_success_count", "total_uptime_count", "created_at", "updated_at", "last_contact_success", "last_contact_failure", "contained", "disqualified", "audit_reputation_alpha", "audit_reputation_beta", "uptime_reputation_alpha", "uptime_reputation_beta") VALUES (E'\\153\\313\\233\\074\\327\\177\\136\\070\\346\\001', '127.0.0.1:55516', '', 0, 4, '', '', -1, -1, 0, 1, 0, '', 'epoch', false, 0, 0, 5, 0, 5, '2019-02-14 08:07:31.028103+00', '2019-02-14 08:07:31.108963+00', 'epoch', 'epoch', false, NULL, 50, 5, 100, 5); +INSERT INTO "nodes"("id", "address", "last_net", "protocol", "type", "email", "wallet", "free_bandwidth", "free_disk", "major", "minor", "patch", "hash", "timestamp", "release","latency_90", "audit_success_count", "total_audit_count", "uptime_success_count", "total_uptime_count", "created_at", "updated_at", "last_contact_success", "last_contact_failure", "contained", "disqualified", "audit_reputation_alpha", "audit_reputation_beta", "uptime_reputation_alpha", "uptime_reputation_beta") VALUES (E'\\006\\223\\250R\\221\\005\\365\\377v>0\\266\\365\\216\\255?\\347\\244\\371?2\\264\\262\\230\\007<\\001\\262\\263\\237\\247n', '127.0.0.1:55518', '', 0, 4, '', '', -1, -1, 0, 1, 0, '', 'epoch', false, 0, 0, 0, 3, 3, '2019-02-14 08:07:31.028103+00', '2019-02-14 08:07:31.108963+00', 'epoch', 'epoch', false, NULL, 50, 0, 100, 0); +INSERT INTO "nodes"("id", "address", "last_net", "protocol", "type", "email", "wallet", "free_bandwidth", "free_disk", "major", "minor", "patch", "hash", "timestamp", "release","latency_90", "audit_success_count", "total_audit_count", "uptime_success_count", "total_uptime_count", "created_at", "updated_at", "last_contact_success", "last_contact_failure", "contained", "disqualified", "audit_reputation_alpha", "audit_reputation_beta", "uptime_reputation_alpha", "uptime_reputation_beta") VALUES (E'\\363\\342\\363\\371>+F\\256\\263\\300\\273|\\342N\\347\\014', '127.0.0.1:55517', '', 0, 4, '', '', -1, -1, 0, 1, 0, '', 'epoch', false, 0, 0, 0, 0, 0, '2019-02-14 08:07:31.028103+00', '2019-02-14 08:07:31.108963+00', 'epoch', 'epoch', false, NULL, 50, 0, 100, 0); +INSERT INTO "nodes"("id", "address", "last_net", "protocol", "type", "email", "wallet", "free_bandwidth", "free_disk", "major", "minor", "patch", "hash", "timestamp", "release","latency_90", "audit_success_count", "total_audit_count", "uptime_success_count", "total_uptime_count", "created_at", "updated_at", "last_contact_success", "last_contact_failure", "contained", "disqualified", "audit_reputation_alpha", "audit_reputation_beta", "uptime_reputation_alpha", "uptime_reputation_beta") VALUES (E'\\363\\342\\363\\371>+F\\256\\263\\300\\273|\\342N\\347\\015', '127.0.0.1:55519', '', 0, 4, '', '', -1, -1, 0, 1, 0, '', 'epoch', false, 0, 1, 2, 1, 2, '2019-02-14 08:07:31.028103+00', '2019-02-14 08:07:31.108963+00', 'epoch', 'epoch', false, NULL, 50, 1, 100, 1); +INSERT INTO "nodes"("id", "address", "last_net", "protocol", "type", "email", "wallet", "free_bandwidth", "free_disk", "major", "minor", "patch", "hash", "timestamp", "release","latency_90", "audit_success_count", "total_audit_count", "uptime_success_count", "total_uptime_count", "created_at", "updated_at", "last_contact_success", "last_contact_failure", "contained", "disqualified", "audit_reputation_alpha", "audit_reputation_beta", "uptime_reputation_alpha", "uptime_reputation_beta") VALUES (E'\\363\\342\\363\\371>+F\\256\\263\\300\\273|\\342N\\347\\016', '127.0.0.1:55520', '', 0, 4, '', '', -1, -1, 0, 1, 0, '', 'epoch', false, 0, 300, 400, 300, 400, '2019-02-14 08:07:31.028103+00', '2019-02-14 08:07:31.108963+00', 'epoch', 'epoch', false, NULL, 300, 100, 300, 100); + +INSERT INTO "projects"("id", "name", "description", "usage_limit","created_at") VALUES (E'\\022\\217/\\014\\376!K\\023\\276\\031\\311}m\\236\\205\\300'::bytea, 'ProjectName', 'projects description', 0, '2019-02-14 08:28:24.254934+00'); + +INSERT INTO "users"("id", "full_name", "short_name", "email", "password_hash", "status", "created_at") VALUES (E'\\363\\311\\033w\\222\\303Ci\\265\\343U\\303\\312\\204",'::bytea, 'Noahson', 'William', '1email1@mail.test', E'some_readable_hash'::bytea, 1, '2019-02-14 08:28:24.614594+00'); +INSERT INTO "projects"("id", "name", "description", "usage_limit", "created_at") VALUES (E'\\363\\342\\363\\371>+F\\256\\263\\300\\273|\\342N\\347\\014'::bytea, 'projName1', 'Test project 1', 0, '2019-02-14 08:28:24.636949+00'); +INSERT INTO "project_members"("member_id", "project_id", "created_at") VALUES (E'\\363\\311\\033w\\222\\303Ci\\265\\343U\\303\\312\\204",'::bytea, E'\\363\\342\\363\\371>+F\\256\\263\\300\\273|\\342N\\347\\014'::bytea, '2019-02-14 08:28:24.677953+00'); + +INSERT INTO "irreparabledbs" ("segmentpath", "segmentdetail", "pieces_lost_count", "seg_damaged_unix_sec", "repair_attempt_count") VALUES ('\x49616d5365676d656e746b6579696e666f30', '\x49616d5365676d656e7464657461696c696e666f30', 10, 1550159554, 10); + +INSERT INTO "injuredsegments" ("path", "data") VALUES ('0', '\x0a0130120100'); +INSERT INTO "injuredsegments" ("path", "data") VALUES ('here''s/a/great/path', '\x0a136865726527732f612f67726561742f70617468120a0102030405060708090a'); +INSERT INTO "injuredsegments" ("path", "data") VALUES ('yet/another/cool/path', '\x0a157965742f616e6f746865722f636f6f6c2f70617468120a0102030405060708090a'); +INSERT INTO "injuredsegments" ("path", "data") VALUES ('so/many/iconic/paths/to/choose/from', '\x0a23736f2f6d616e792f69636f6e69632f70617468732f746f2f63686f6f73652f66726f6d120a0102030405060708090a'); + +INSERT INTO "certrecords" VALUES (E'0Y0\\023\\006\\007*\\206H\\316=\\002\\001\\006\\010*\\206H\\316=\\003\\001\\007\\003B\\000\\004\\360\\267\\227\\377\\253u\\222\\337Y\\324C:GQ\\010\\277v\\010\\315D\\271\\333\\337.\\203\\023=C\\343\\014T%6\\027\\362?\\214\\326\\017U\\334\\000\\260\\224\\260J\\221\\304\\331F\\304\\221\\236zF,\\325\\326l\\215\\306\\365\\200\\022', E'L\\301|\\200\\247}F|1\\320\\232\\037n\\335\\241\\206\\244\\242\\207\\204.\\253\\357\\326\\352\\033Dt\\202`\\022\\325', '2019-02-14 08:07:31.335028+00'); + +INSERT INTO "bucket_usages" ("id", "bucket_id", "rollup_end_time", "remote_stored_data", "inline_stored_data", "remote_segments", "inline_segments", "objects", "metadata_size", "repair_egress", "get_egress", "audit_egress") VALUES (E'\\153\\313\\233\\074\\327\\177\\136\\070\\346\\001",'::bytea, E'\\366\\146\\032\\321\\316\\161\\070\\133\\302\\271",'::bytea, '2019-03-06 08:28:24.677953+00', 10, 11, 12, 13, 14, 15, 16, 17, 18); + +INSERT INTO "registration_tokens" ("secret", "owner_id", "project_limit", "created_at") VALUES (E'\\070\\127\\144\\013\\332\\344\\102\\376\\306\\056\\303\\130\\106\\132\\321\\276\\321\\274\\170\\264\\054\\333\\221\\116\\154\\221\\335\\070\\220\\146\\344\\216'::bytea, null, 1, '2019-02-14 08:28:24.677953+00'); + +INSERT INTO "serial_numbers" ("id", "serial_number", "bucket_id", "expires_at") VALUES (1, E'0123456701234567'::bytea, E'\\363\\342\\363\\371>+F\\256\\263\\300\\273|\\342N\\347\\014/testbucket'::bytea, '2019-03-06 08:28:24.677953+00'); +INSERT INTO "used_serials" ("serial_number_id", "storage_node_id") VALUES (1, E'\\006\\223\\250R\\221\\005\\365\\377v>0\\266\\365\\216\\255?\\347\\244\\371?2\\264\\262\\230\\007<\\001\\262\\263\\237\\247n'); + +INSERT INTO "storagenode_bandwidth_rollups" ("storagenode_id", "interval_start", "interval_seconds", "action", "allocated", "settled") VALUES (E'\\006\\223\\250R\\221\\005\\365\\377v>0\\266\\365\\216\\255?\\347\\244\\371?2\\264\\262\\230\\007<\\001\\262\\263\\237\\247n', '2019-03-06 08:00:00.000000+00', 3600, 1, 1024, 2024); +INSERT INTO "storagenode_storage_tallies" VALUES (1, E'\\3510\\323\\225"~\\036<\\342\\330m\\0253Jhr\\246\\233K\\246#\\2303\\351\\256\\275j\\212UM\\362\\207', '2019-02-14 08:16:57.812849+00', 1000); + +INSERT INTO "bucket_bandwidth_rollups" ("bucket_name", "project_id", "interval_start", "interval_seconds", "action", "inline", "allocated", "settled") VALUES (E'testbucket'::bytea, E'\\363\\342\\363\\371>+F\\256\\263\\300\\273|\\342N\\347\\014'::bytea,'2019-03-06 08:00:00.000000+00', 3600, 1, 1024, 2024, 3024); +INSERT INTO "bucket_storage_tallies" ("bucket_name", "project_id", "interval_start", "inline", "remote", "remote_segments_count", "inline_segments_count", "object_count", "metadata_size") VALUES (E'testbucket'::bytea, E'\\363\\342\\363\\371>+F\\256\\263\\300\\273|\\342N\\347\\014'::bytea,'2019-03-06 08:00:00.000000+00', 4024, 5024, 0, 0, 0, 0); +INSERT INTO "bucket_bandwidth_rollups" ("bucket_name", "project_id", "interval_start", "interval_seconds", "action", "inline", "allocated", "settled") VALUES (E'testbucket'::bytea, E'\\170\\160\\157\\370\\274\\366\\113\\364\\272\\235\\301\\243\\321\\102\\321\\136'::bytea,'2019-03-06 08:00:00.000000+00', 3600, 1, 1024, 2024, 3024); +INSERT INTO "bucket_storage_tallies" ("bucket_name", "project_id", "interval_start", "inline", "remote", "remote_segments_count", "inline_segments_count", "object_count", "metadata_size") VALUES (E'testbucket'::bytea, E'\\170\\160\\157\\370\\274\\366\\113\\364\\272\\235\\301\\243\\321\\102\\321\\136'::bytea,'2019-03-06 08:00:00.000000+00', 4024, 5024, 0, 0, 0, 0); + +INSERT INTO "reset_password_tokens" ("secret", "owner_id", "created_at") VALUES (E'\\070\\127\\144\\013\\332\\344\\102\\376\\306\\056\\303\\130\\106\\132\\321\\276\\321\\274\\170\\264\\054\\333\\221\\116\\154\\221\\335\\070\\220\\146\\344\\216'::bytea, E'\\363\\311\\033w\\222\\303Ci\\265\\343U\\303\\312\\204",'::bytea, '2019-05-08 08:28:24.677953+00'); + +INSERT INTO "pending_audits" ("node_id", "piece_id", "stripe_index", "share_size", "expected_share_hash", "reverify_count") VALUES (E'\\153\\313\\233\\074\\327\\177\\136\\070\\346\\001'::bytea, E'\\363\\311\\033w\\222\\303Ci\\265\\343U\\303\\312\\204",'::bytea, 5, 1024, E'\\070\\127\\144\\013\\332\\344\\102\\376\\306\\056\\303\\130\\106\\132\\321\\276\\321\\274\\170\\264\\054\\333\\221\\116\\154\\221\\335\\070\\220\\146\\344\\216'::bytea, 1); + +INSERT INTO "offers" ("id", "name", "description", "award_credit_in_cents", "invitee_credit_in_cents", "award_credit_duration_days", "invitee_credit_duration_days", "redeemable_cap", "expires_at", "created_at", "num_redeemed", "status", "type") VALUES (1, 'testOffer', 'Test offer 1', 0, 0, 14, 14, 50, '2019-03-14 08:28:24.636949+00', '2019-02-14 08:28:24.636949+00', 0, 0, 0); + +INSERT INTO "api_keys" ("id", "project_id", "head", "name", "secret", "created_at") VALUES (E'\\334/\\302;\\225\\355O\\323\\276f\\247\\354/6\\241\\033'::bytea, E'\\022\\217/\\014\\376!K\\023\\276\\031\\311}m\\236\\205\\300'::bytea, E'\\111\\142\\147\\304\\132\\375\\070\\163\\270\\160\\251\\370\\126\\063\\351\\037\\257\\071\\143\\375\\351\\320\\253\\232\\220\\260\\075\\173\\306\\307\\115\\136'::bytea, 'key 2', E'\\254\\011\\315\\333\\273\\365\\001\\071\\024\\154\\253\\332\\301\\216\\361\\074\\221\\367\\251\\231\\274\\333\\300\\367\\001\\272\\327\\111\\315\\123\\042\\016'::bytea, '2019-02-14 08:28:24.267934+00'); + +INSERT INTO "user_payments" ("user_id", "customer_id", "created_at") VALUES (E'\\363\\311\\033w\\222\\303Ci\\265\\343U\\303\\312\\204",'::bytea, E'\\022\\217/\\014\\376!K\\023\\276'::bytea, '2019-06-01 08:28:24.267934+00'); +INSERT INTO "project_invoice_stamps" ("project_id", "invoice_id", "start_date", "end_date", "created_at") VALUES (E'\\022\\217/\\014\\376!K\\023\\276\\031\\311}m\\236\\205\\300'::bytea, E'\\363\\311\\033w\\222\\303,'::bytea, '2019-06-01 08:28:24.267934+00', '2019-06-29 08:28:24.267934+00', '2019-06-01 08:28:24.267934+00'); + +INSERT INTO "value_attributions" ("project_id", "bucket_name", "partner_id", "last_updated") VALUES (E'\\363\\311\\033w\\222\\303Ci\\265\\343U\\303\\312\\204",'::bytea, E''::bytea, E'\\363\\342\\363\\371>+F\\256\\263\\300\\273|\\342N\\347\\014'::bytea,'2019-02-14 08:07:31.028103+00'); + +INSERT INTO "user_credits" ("id", "user_id", "offer_id", "referred_by", "credits_earned_in_cents", "credits_used_in_cents", "expires_at", "created_at") VALUES (1, E'\\363\\311\\033w\\222\\303Ci\\265\\343U\\303\\312\\204",'::bytea, 1, E'\\363\\311\\033w\\222\\303Ci\\265\\343U\\303\\312\\204",'::bytea, 200, 0, '2019-10-01 08:28:24.267934+00', '2019-06-01 08:28:24.267934+00'); + +INSERT INTO "bucket_metainfos" ("id", "project_id", "name", "created_at", "path_cipher", "default_segment_size", "default_encryption_cipher_suite", "default_encryption_block_size", "default_redundancy_algorithm", "default_redundancy_share_size", "default_redundancy_required_shares", "default_redundancy_repair_shares", "default_redundancy_optimal_shares", "default_redundancy_total_shares") VALUES (E'\\334/\\302;\\225\\355O\\323\\276f\\247\\354/6\\241\\033'::bytea, E'\\022\\217/\\014\\376!K\\023\\276\\031\\311}m\\236\\205\\300'::bytea, E'testbucketuniquename'::bytea, '2019-06-14 08:28:24.677953+00', 1, 65536, 1, 8192, 1, 4096, 4, 6, 8, 10); + +-- NEW DATA -- + +INSERT INTO "project_payments" ("id", "project_id", "payer_id", "payment_method_id", "is_default","created_at") VALUES (E'\\334/\\302;\\225\\355O\\323\\276f\\247\\354/6\\241\\033'::bytea, E'\\363\\342\\363\\371>+F\\256\\263\\300\\273|\\342N\\347\\014'::bytea, E'\\363\\311\\033w\\222\\303Ci\\265\\343U\\303\\312\\204",'::bytea, E'\\022\\217/\\014\\376!K\\023\\276'::bytea, true, '2019-06-01 08:28:24.267934+00'); diff --git a/web/satellite/.env b/web/satellite/.env new file mode 100644 index 000000000..85c6b2253 --- /dev/null +++ b/web/satellite/.env @@ -0,0 +1,3 @@ +VUE_APP_SEGMENTID="segment id" +VUE_APP_STRIPE_PUBLIC_KEY=pk_test + diff --git a/web/satellite/.env.example b/web/satellite/.env.example index d4c97c053..3e3aced25 100644 --- a/web/satellite/.env.example +++ b/web/satellite/.env.example @@ -1 +1,2 @@ -VUE_APP_SEGMENTID="segment id" \ No newline at end of file +VUE_APP_SEGMENTID="segment id" +VUE_APP_STRIPE_PUBLIC_KEY=pk_test diff --git a/web/satellite/index.html b/web/satellite/index.html index 02a33af03..0955412c5 100644 --- a/web/satellite/index.html +++ b/web/satellite/index.html @@ -8,6 +8,7 @@
+ diff --git a/web/satellite/package.json b/web/satellite/package.json index fe48bb051..54b1f5e8c 100644 --- a/web/satellite/package.json +++ b/web/satellite/package.json @@ -19,6 +19,7 @@ "graphql": "^14.0.2", "graphql-tag": "^2.10.0", "moment": "^2.24.0", + "stripe": "^7.0.1", "vue": "^2.5.17", "vue-clipboards": "^1.2.4", "vue-property-decorator": "^7.0.0", @@ -30,6 +31,7 @@ "@babel/plugin-proposal-object-rest-spread": "^7.0.0", "@types/jest": "^23.1.4", "@types/sinon": "^5.0.5", + "@types/stripe": "^6.26.0", "@vue/cli-plugin-babel": "^3.0.5", "@vue/cli-plugin-typescript": "^3.6.0", "@vue/cli-plugin-unit-jest": "^3.6.3", diff --git a/web/satellite/src/App.vue b/web/satellite/src/App.vue index 53af73411..334bd0f0f 100644 --- a/web/satellite/src/App.vue +++ b/web/satellite/src/App.vue @@ -26,6 +26,10 @@ 'sortTeamMemberByDropdownButton', 'notificationArea', 'successfulRegistrationPopup', + 'deletePaymentMethodButton', + 'deletePaymentMethodDialog', + 'makeDefaultPaymentMethodButton', + 'makeDefaultPaymentDialog' ] }; }, @@ -41,6 +45,7 @@ } target = target.parentNode; } + this.$store.dispatch(APP_STATE_ACTIONS.CLOSE_POPUPS); } } diff --git a/web/satellite/src/api/paymentMethods.ts b/web/satellite/src/api/paymentMethods.ts new file mode 100644 index 000000000..1d0bc538b --- /dev/null +++ b/web/satellite/src/api/paymentMethods.ts @@ -0,0 +1,139 @@ +// Copyright (C) 2019 Storj Labs, Inc. +// See LICENSE for copying information. + +import apollo from '@/utils/apolloManager'; +import gql from 'graphql-tag'; + +export async function addProjectPaymentMethodRequest(projectID: string, cardToken: string, makeDefault: boolean): Promise> { + let result: RequestResponse = { + errorMessage: '', + isSuccess: false, + data: null + }; + + let response: any = await apollo.mutate( + { + mutation: gql(` + mutation { + addPaymentMethod( + projectID: "${projectID}", + cardToken: "${cardToken}", + isDefault: ${makeDefault} + ) + } + `), + fetchPolicy: 'no-cache', + errorPolicy: 'all' + } + ); + + if (response.errors) { + result.errorMessage = response.errors[0].message; + } else { + result.isSuccess = true; + } + + return result; +} + +export async function setDefaultPaymentMethodRequest(projectID: string, paymentID: string): Promise> { + let result: RequestResponse = { + errorMessage: '', + isSuccess: false, + data: null + }; + + let response: any = await apollo.mutate( + { + mutation: gql(` + mutation { + setDefaultPaymentMethod( + projectID: "${projectID}", + id: "${paymentID}" + ) + } + `), + fetchPolicy: 'no-cache', + errorPolicy: 'all' + } + ); + + if (response.errors) { + result.errorMessage = response.errors[0].message; + } else { + result.isSuccess = true; + } + + return result; +} + +export async function deletePaymentMethodRequest(paymentID: string):Promise> { + let result: RequestResponse = { + errorMessage: '', + isSuccess: false, + data: null + }; + + let response: any = await apollo.mutate( + { + mutation: gql(` + mutation { + deletePaymentMethod( + id: "${paymentID}" + ) + } + `), + fetchPolicy: 'no-cache', + errorPolicy: 'all' + } + ); + + if (response.errors) { + result.errorMessage = response.errors[0].message; + } else { + result.isSuccess = true; + } + + return result; +} + +// fetchProjectInvoices retrieves project invoices +export async function fetchProjectPaymentMethods(projectID: string): Promise> { + let result: RequestResponse = { + errorMessage: '', + isSuccess: false, + data: [] as PaymentMethod[] + }; + + let response: any = await apollo.query( + { + query: gql(` + query { + project(id: "${projectID}") { + paymentMethods { + id, + expYear, + expMonth, + brand, + lastFour, + holderName, + addedAt, + isDefault + } + } + }` + ), + fetchPolicy: 'no-cache', + errorPolicy: 'all' + } + ); + + if (response.errors) { + result.errorMessage = response.errors[0].message; + } else { + result.isSuccess = true; + result.data = response.data.project.paymentMethods; + } + + return result; +} diff --git a/web/satellite/src/components/common/Checkbox.vue b/web/satellite/src/components/common/Checkbox.vue index c3224a8f8..6a0fe4d3d 100644 --- a/web/satellite/src/components/common/Checkbox.vue +++ b/web/satellite/src/components/common/Checkbox.vue @@ -40,8 +40,8 @@ display: block; position: relative; padding-left: 20px; - height: 25px; - width: 25px; + height: 23px; + width: 23px; cursor: pointer; font-size: 22px; -webkit-user-select: none; diff --git a/web/satellite/src/components/header/projectSelection/ProjectSelectionDropdown.vue b/web/satellite/src/components/header/projectSelection/ProjectSelectionDropdown.vue index 89b506299..e2beb69a9 100644 --- a/web/satellite/src/components/header/projectSelection/ProjectSelectionDropdown.vue +++ b/web/satellite/src/components/header/projectSelection/ProjectSelectionDropdown.vue @@ -19,16 +19,17 @@ - - diff --git a/web/satellite/src/components/project/ProjectPaymentMethods.vue b/web/satellite/src/components/project/ProjectPaymentMethods.vue index b2f0a72fb..7b3cc0e79 100644 --- a/web/satellite/src/components/project/ProjectPaymentMethods.vue +++ b/web/satellite/src/components/project/ProjectPaymentMethods.vue @@ -4,53 +4,30 @@ + + \ No newline at end of file diff --git a/web/satellite/src/components/project/paymentMethods/DeletePaymentMethodDialog.vue b/web/satellite/src/components/project/paymentMethods/DeletePaymentMethodDialog.vue new file mode 100644 index 000000000..27449eac2 --- /dev/null +++ b/web/satellite/src/components/project/paymentMethods/DeletePaymentMethodDialog.vue @@ -0,0 +1,127 @@ +// Copyright (C) 2019 Storj Labs, Inc. +// See LICENSE for copying information. + + + + + + diff --git a/web/satellite/src/components/project/paymentMethods/MakeDefaultPaymentMethodDialog.vue b/web/satellite/src/components/project/paymentMethods/MakeDefaultPaymentMethodDialog.vue new file mode 100644 index 000000000..dce4d5910 --- /dev/null +++ b/web/satellite/src/components/project/paymentMethods/MakeDefaultPaymentMethodDialog.vue @@ -0,0 +1,110 @@ +// Copyright (C) 2019 Storj Labs, Inc. +// See LICENSE for copying information. + + + + + + diff --git a/web/satellite/src/components/project/paymentMethods/NewPaymentMethodComponent.vue b/web/satellite/src/components/project/paymentMethods/NewPaymentMethodComponent.vue new file mode 100644 index 000000000..8d7e72e69 --- /dev/null +++ b/web/satellite/src/components/project/paymentMethods/NewPaymentMethodComponent.vue @@ -0,0 +1,228 @@ +// Copyright (C) 2019 Storj Labs, Inc. +// See LICENSE for copying information. + + + + + + \ No newline at end of file diff --git a/web/satellite/src/store/index.ts b/web/satellite/src/store/index.ts index 2589c251c..7f24f636d 100644 --- a/web/satellite/src/store/index.ts +++ b/web/satellite/src/store/index.ts @@ -12,6 +12,7 @@ import { notificationsModule } from '@/store/modules/notifications'; import { appStateModule } from '@/store/modules/appState'; import { apiKeysModule } from '@/store/modules/apiKeys'; import { bucketUsageModule, usageModule, creditUsageModule } from '@/store/modules/usage'; +import { projectPaymentsMethodsModule } from '@/store/modules/paymentMethods'; Vue.use(Vuex); @@ -26,6 +27,7 @@ const store = new Vuex.Store({ apiKeysModule, usageModule, bucketUsageModule, + projectPaymentsMethodsModule, creditUsageModule } }); diff --git a/web/satellite/src/store/modules/appState.ts b/web/satellite/src/store/modules/appState.ts index 2fcfa8f2a..c23fced22 100644 --- a/web/satellite/src/store/modules/appState.ts +++ b/web/satellite/src/store/modules/appState.ts @@ -22,6 +22,8 @@ export const appStateModule = { isSuccessfulProjectCreationPopupShown: false, isEditProfilePopupShown: false, isChangePasswordPopupShown: false, + deletePaymentMethodID: '', + setDefaultPaymentMethodID: '', }, }, mutations: { @@ -75,12 +77,20 @@ export const appStateModule = { [APP_STATE_MUTATIONS.TOGGLE_EDIT_PROFILE_POPUP](state: any): void { state.appState.isEditProfilePopupShown = !state.appState.isEditProfilePopupShown; }, + [APP_STATE_MUTATIONS.SHOW_SET_DEFAULT_PAYMENT_METHOD_POPUP](state: any, id: string): void { + state.appState.setDefaultPaymentMethodID = id; + }, + [APP_STATE_MUTATIONS.SHOW_DELETE_PAYMENT_METHOD_POPUP](state: any, id: string): void { + state.appState.deletePaymentMethodID = id; + }, // Mutation that closes each popup/dropdown [APP_STATE_MUTATIONS.CLOSE_ALL](state: any): void { state.appState.isProjectsDropdownShown = false; state.appState.isAccountDropdownShown = false; state.appState.isSortProjectMembersByPopupShown = false; state.appState.isSuccessfulRegistrationPopupShown = false; + state.appState.setDefaultPaymentMethodID = ''; + state.appState.deletePaymentMethodID = ''; }, [APP_STATE_MUTATIONS.CHANGE_STATE](state: any, newFetchState: AppState): void { state.appState.fetchState = newFetchState; @@ -164,6 +174,20 @@ export const appStateModule = { [APP_STATE_ACTIONS.TOGGLE_EDIT_PROFILE_POPUP]: function ({commit}: any): void { commit(APP_STATE_MUTATIONS.TOGGLE_EDIT_PROFILE_POPUP); }, + [APP_STATE_ACTIONS.SHOW_SET_DEFAULT_PAYMENT_METHOD_POPUP]: function ({commit, state}: any, methodID: string): void { + if (!state.appState.setDefaultPaymentMethodID) { + commit(APP_STATE_MUTATIONS.CLOSE_ALL); + } + + commit(APP_STATE_MUTATIONS.SHOW_SET_DEFAULT_PAYMENT_METHOD_POPUP, methodID); + }, + [APP_STATE_ACTIONS.SHOW_DELETE_PAYMENT_METHOD_POPUP]: function ({commit, state}: any, methodID: string): void { + if (!state.appState.deletePaymentMethodID) { + commit(APP_STATE_MUTATIONS.CLOSE_ALL); + } + + commit(APP_STATE_MUTATIONS.SHOW_DELETE_PAYMENT_METHOD_POPUP, methodID); + }, [APP_STATE_ACTIONS.CLOSE_POPUPS]: function ({commit}: any): void { commit(APP_STATE_MUTATIONS.CLOSE_ALL); }, diff --git a/web/satellite/src/store/modules/paymentMethods.ts b/web/satellite/src/store/modules/paymentMethods.ts new file mode 100644 index 000000000..2130ed0b4 --- /dev/null +++ b/web/satellite/src/store/modules/paymentMethods.ts @@ -0,0 +1,56 @@ +// Copyright (C) 2019 Storj Labs, Inc. +// See LICENSE for copying information. + +import { PROJECT_PAYMENT_METHODS_MUTATIONS } from '@/store/mutationConstants'; +import { PROJECT_PAYMENT_METHODS_ACTIONS } from '@/utils/constants/actionNames'; +import { + addProjectPaymentMethodRequest, + deletePaymentMethodRequest, + fetchProjectPaymentMethods, + setDefaultPaymentMethodRequest +} from '@/api/paymentMethods'; + +export const projectPaymentsMethodsModule = { + state: { + paymentMethods: [] as PaymentMethod[], + }, + mutations: { + [PROJECT_PAYMENT_METHODS_MUTATIONS.FETCH](state: any, invoices: PaymentMethod[]) { + state.paymentMethods = invoices; + }, + [PROJECT_PAYMENT_METHODS_MUTATIONS.CLEAR](state: any) { + state.paymentMethods = [] as PaymentMethod[]; + } + }, + actions: { + [PROJECT_PAYMENT_METHODS_ACTIONS.ADD]: async function ({commit, rootGetters, state}, input: AddPaymentMethodInput): Promise> { + const projectID = rootGetters.selectedProject.id; + if (state.paymentMethods.length == 0) { + input.makeDefault = true; + } + + return await addProjectPaymentMethodRequest(projectID, input.token, input.makeDefault); + }, + [PROJECT_PAYMENT_METHODS_ACTIONS.FETCH]: async function ({commit, rootGetters}): Promise> { + const projectId = rootGetters.selectedProject.id; + + let result = await fetchProjectPaymentMethods(projectId); + if (result.isSuccess) { + commit(PROJECT_PAYMENT_METHODS_MUTATIONS.FETCH, result.data); + } + + return result; + }, + [PROJECT_PAYMENT_METHODS_ACTIONS.CLEAR]: function ({commit}) { + commit(PROJECT_PAYMENT_METHODS_MUTATIONS.CLEAR); + }, + [PROJECT_PAYMENT_METHODS_ACTIONS.SET_DEFAULT]: async function ({commit, rootGetters}, projectPaymentID: string) { + const projectID = rootGetters.selectedProject.id; + + return await setDefaultPaymentMethodRequest(projectID, projectPaymentID); + }, + [PROJECT_PAYMENT_METHODS_ACTIONS.DELETE]: async function ({commit}, projectPaymentID: string) { + return await deletePaymentMethodRequest(projectPaymentID); + } + }, +}; diff --git a/web/satellite/src/store/mutationConstants.ts b/web/satellite/src/store/mutationConstants.ts index 311c9b9ad..dbcdc1c05 100644 --- a/web/satellite/src/store/mutationConstants.ts +++ b/web/satellite/src/store/mutationConstants.ts @@ -77,6 +77,13 @@ export const APP_STATE_MUTATIONS = { TOGGLE_SUCCESSFUL_PROJECT_CREATION_POPUP: 'TOGGLE_SUCCESSFUL_PROJECT_CREATION_POPUP', TOGGLE_EDIT_PROFILE_POPUP: 'TOGGLE_EDIT_PROFILE_POPUP', TOGGLE_CHANGE_PASSWORD_POPUP: 'TOGGLE_CHANGE_PASSWORD_POPUP', + SHOW_DELETE_PAYMENT_METHOD_POPUP: 'SHOW_DELETE_PAYMENT_METHOD_POPUP', + SHOW_SET_DEFAULT_PAYMENT_METHOD_POPUP: 'SHOW_SET_DEFAULT_PAYMENT_METHOD_POPUP', CLOSE_ALL: 'CLOSE_ALL', CHANGE_STATE: 'CHANGE_STATE', }; + +export const PROJECT_PAYMENT_METHODS_MUTATIONS = { + FETCH: 'FETCH', + CLEAR: 'CLEAR', +}; diff --git a/web/satellite/src/types/invoices.d.ts b/web/satellite/src/types/invoices.d.ts new file mode 100644 index 000000000..3e337e347 --- /dev/null +++ b/web/satellite/src/types/invoices.d.ts @@ -0,0 +1,19 @@ +// Copyright (C) 2019 Storj Labs, Inc. +// See LICENSE for copying information. + +// PaymentMethod holds card information to display +declare type PaymentMethod = { + id: string, + expYear: number, + expMonth: number, + brand: string, + lastFour: string, + holderName: string, + addedAt: Date, + isDefault: boolean, +} + +declare type AddPaymentMethodInput = { + token: string, + makeDefault: boolean, +} diff --git a/web/satellite/src/utils/constants/actionNames.ts b/web/satellite/src/utils/constants/actionNames.ts index e392a91a9..2f023d78a 100644 --- a/web/satellite/src/utils/constants/actionNames.ts +++ b/web/satellite/src/utils/constants/actionNames.ts @@ -14,6 +14,10 @@ export const APP_STATE_ACTIONS = { TOGGLE_SUCCESSFUL_PROJECT_CREATION_POPUP: 'toggleSuccessfulProjectCreationPopup', TOGGLE_EDIT_PROFILE_POPUP: 'toggleEditProfilePopup', TOGGLE_CHANGE_PASSWORD_POPUP: 'toggleChangePasswordPopup', + SHOW_SET_DEFAULT_PAYMENT_METHOD_POPUP: 'showSetDefaultPaymentMethodPopup', + CLOSE_SET_DEFAULT_PAYMENT_METHOD_POPUP: 'closeSetDefaultPaymentMethodPopup', + SHOW_DELETE_PAYMENT_METHOD_POPUP: 'showDeletePaymentMethodPopup', + CLOSE_DELETE_PAYMENT_METHOD_POPUP: 'closeDeletePaymentMethodPopup', CLOSE_POPUPS: 'closePopups', CHANGE_STATE: 'changeFetchState', }; @@ -80,6 +84,14 @@ export const BUCKET_USAGE_ACTIONS = { CLEAR: 'clearBucketUsages' }; +export const PROJECT_PAYMENT_METHODS_ACTIONS = { + ADD: 'addProjectPaymentMethod', + FETCH: 'fetchProjectPaymentMethods', + CLEAR: 'clearProjectPaymentMethods', + SET_DEFAULT: 'setDefaultPaymentMethod', + DELETE: 'deletePaymentMethod' +}; + export const CREDIT_USAGE_ACTIONS = { FETCH: 'fetchCreditUsage', -} \ No newline at end of file +}; diff --git a/web/satellite/src/views/Dashboard.vue b/web/satellite/src/views/Dashboard.vue index c7fce5142..e3cac7e71 100644 --- a/web/satellite/src/views/Dashboard.vue +++ b/web/satellite/src/views/Dashboard.vue @@ -33,8 +33,8 @@ import {AppState} from "../utils/constants/appStateEnum"; PROJETS_ACTIONS, USER_ACTIONS, PROJECT_USAGE_ACTIONS, - BUCKET_USAGE_ACTIONS - } from '@/utils/constants/actionNames'; + BUCKET_USAGE_ACTIONS, PROJECT_PAYMENT_METHODS_ACTIONS + } from "@/utils/constants/actionNames"; import ROUTES from '@/utils/constants/routerConstants'; import ProjectCreationSuccessPopup from '@/components/project/ProjectCreationSuccessPopup.vue'; import { AppState } from '../utils/constants/appStateEnum'; @@ -82,6 +82,11 @@ import {AppState} from "../utils/constants/appStateEnum"; this.$store.dispatch(NOTIFICATION_ACTIONS.ERROR, 'Unable to fetch buckets: ' + bucketsResponse.errorMessage); } + const paymentMethodsResponse = await this.$store.dispatch(PROJECT_PAYMENT_METHODS_ACTIONS.FETCH); + if (!paymentMethodsResponse.isSuccess) { + this.$store.dispatch(NOTIFICATION_ACTIONS.ERROR, 'Unable to fetch payment methods: ' + paymentMethodsResponse.errorMessage); + } + this.$store.dispatch(APP_STATE_ACTIONS.CHANGE_STATE, AppState.LOADED); }, 800); }, diff --git a/web/satellite/src/views/register/register.scss b/web/satellite/src/views/register/register.scss index aee684bce..b1c6f4259 100644 --- a/web/satellite/src/views/register/register.scss +++ b/web/satellite/src/views/register/register.scss @@ -17,8 +17,7 @@ body { display: flex; justify-content: flex-start; flex-direction: column; - align-items: flex-start; - padding: 60px 0px 0px 104px; + align-items: flex-start; padding: 60px 0px 0px 104px; background-color: #F5F6FA; .register-input { @@ -173,8 +172,8 @@ body { display: block; position: relative; padding-left: 20px; - height: 25px; - width: 25px; + height: 23px; + width: 23px; cursor: pointer; font-size: 22px; -webkit-user-select: none; diff --git a/web/satellite/static/images/Card.svg b/web/satellite/static/images/Card.svg new file mode 100644 index 000000000..58993a270 --- /dev/null +++ b/web/satellite/static/images/Card.svg @@ -0,0 +1,35 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/web/satellite/static/images/ContainerCentered.svg b/web/satellite/static/images/ContainerCentered.svg new file mode 100644 index 000000000..f55da8795 --- /dev/null +++ b/web/satellite/static/images/ContainerCentered.svg @@ -0,0 +1,7 @@ + + + + + + + diff --git a/web/satellite/static/images/container.svg b/web/satellite/static/images/container.svg new file mode 100644 index 000000000..01ff09168 --- /dev/null +++ b/web/satellite/static/images/container.svg @@ -0,0 +1,7 @@ + + + + + + +