diff --git a/app/graphql/mutations/wallets/create.rb b/app/graphql/mutations/wallets/create.rb new file mode 100644 index 00000000000..0b51ce587c6 --- /dev/null +++ b/app/graphql/mutations/wallets/create.rb @@ -0,0 +1,26 @@ +# frozen_string_literal: true + +module Mutations + module Wallets + class Create < BaseMutation + include AuthenticableApiUser + include RequiredOrganization + + graphql_name 'CreateCustomerWallet' + description 'Creates a new Customer Wallet' + + argument :customer_id, String, required: true + argument :rate_amount, String, required: true + argument :name, String, required: false + argument :paid_credits, String, required: true + argument :granted_credits, String, required: true + argument :expiration_date, GraphQL::Types::ISO8601Date, required: false + + type Types::Wallets::Object + + def resolve(**args) + # Empty + end + end + end +end diff --git a/app/graphql/mutations/wallets/terminate.rb b/app/graphql/mutations/wallets/terminate.rb new file mode 100644 index 00000000000..a127588d599 --- /dev/null +++ b/app/graphql/mutations/wallets/terminate.rb @@ -0,0 +1,21 @@ +# frozen_string_literal: true + +module Mutations + module Wallets + class Terminate < BaseMutation + include AuthenticableApiUser + include RequiredOrganization + + graphql_name 'TerminateCustomerWallet' + description 'Terminates a new Customer Wallet' + + argument :id, String, required: true + + type Types::Wallets::Object + + def resolve(**args) + # Empty + end + end + end +end diff --git a/app/graphql/mutations/wallets/update.rb b/app/graphql/mutations/wallets/update.rb new file mode 100644 index 00000000000..0f70b0b3701 --- /dev/null +++ b/app/graphql/mutations/wallets/update.rb @@ -0,0 +1,22 @@ +# frozen_string_literal: true + +module Mutations + module Wallets + class Update < BaseMutation + include AuthenticableApiUser + include RequiredOrganization + + graphql_name 'UpdateCustomerWallet' + description 'Updates a new Customer Wallet' + + argument :id, String, required: true + argument :expiration_date, GraphQL::Types::ISO8601Date, required: false + + type Types::Wallets::Object + + def resolve(**args) + # Empty + end + end + end +end diff --git a/app/graphql/resolvers/wallet_resolver.rb b/app/graphql/resolvers/wallet_resolver.rb new file mode 100644 index 00000000000..0b3aa954251 --- /dev/null +++ b/app/graphql/resolvers/wallet_resolver.rb @@ -0,0 +1,22 @@ +# frozen_string_literal: true + +module Resolvers + class WalletResolver < Resolvers::BaseResolver + include AuthenticableApiUser + include RequiredOrganization + + description 'Query a single wallet' + + argument :id, ID, required: true, description: 'Uniq ID of the wallet' + + type Types::Wallets::SingleObject, null: true + + def resolve(id: nil) + validate_organization! + + Wallet.find(id) + rescue ActiveRecord::RecordNotFound + not_found_error + end + end +end diff --git a/app/graphql/resolvers/wallets_resolver.rb b/app/graphql/resolvers/wallets_resolver.rb new file mode 100644 index 00000000000..a18bb866100 --- /dev/null +++ b/app/graphql/resolvers/wallets_resolver.rb @@ -0,0 +1,36 @@ +# frozen_string_literal + +module Resolvers + class WalletsResolver < Resolvers::BaseResolver + include AuthenticableApiUser + include RequiredOrganization + + description 'Query wallets' + + argument :ids, [ID], required: false, description: 'List of wallet IDs to fetch' + argument :customer_id, ID, required: true, description: 'Uniq ID of the customer' + argument :page, Integer, required: false + argument :limit, Integer, required: false + argument :status, Types::Wallets::StatusEnum, required: false + + type Types::Wallets::Object.collection_type, null: false + + def resolve(customer_id: nil, ids: nil, page: nil, limit: nil, status: nil) + validate_organization! + + current_customer = Customer.find(customer_id) + + wallets = current_customer + .wallets + .page(page) + .limit(limit) + + wallets = wallets.where(status: status) if status.present? + wallets = wallets.where(id: ids) if ids.present? + + wallets + rescue ActiveRecord::RecordNotFound + not_found_error + end + end +end diff --git a/app/graphql/types/mutation_type.rb b/app/graphql/types/mutation_type.rb index cbb7934549a..c1da5d6e43c 100644 --- a/app/graphql/types/mutation_type.rb +++ b/app/graphql/types/mutation_type.rb @@ -41,5 +41,9 @@ class MutationType < Types::BaseObject field :add_stripe_payment_provider, mutation: Mutations::PaymentProviders::Stripe field :download_invoice, mutation: Mutations::Invoices::Download + + field :create_customer_wallet, mutation: Mutations::Wallets::Create + field :update_customer_wallet, mutation: Mutations::Wallets::Update + field :terminate_customer_wallet, mutation: Mutations::Wallets::Terminate end end diff --git a/app/graphql/types/query_type.rb b/app/graphql/types/query_type.rb index bb150c629e7..099e7b55c8a 100644 --- a/app/graphql/types/query_type.rb +++ b/app/graphql/types/query_type.rb @@ -22,5 +22,7 @@ class QueryType < Types::BaseObject field :plans, resolver: Resolvers::PlansResolver field :plan, resolver: Resolvers::PlanResolver field :current_version, resolver: Resolvers::VersionResolver + field :wallets, resolver: Resolvers::WalletsResolver + field :wallet, resolver: Resolvers::WalletResolver end end diff --git a/app/graphql/types/wallets/object.rb b/app/graphql/types/wallets/object.rb new file mode 100644 index 00000000000..b3a5500bdf8 --- /dev/null +++ b/app/graphql/types/wallets/object.rb @@ -0,0 +1,27 @@ +# frozen_string_literal: true + +module Types + module Wallets + class Object < Types::BaseObject + graphql_name 'Wallet' + + field :id, ID, null: false + field :customer, Types::Customers::Object + + field :name, String, null: false + field :status, Types::Wallets::StatusEnum, null: false + field :rate_amount, String, null: false + field :currency, Types::CurrencyEnum, null: false + field :credits_balance, String, null: false + field :balance, String, null: false + field :consumed_credits, String, null: false + + field :expiration_date, GraphQL::Types::ISO8601Date, null: true + + field :created_at, GraphQL::Types::ISO8601DateTime, null: false + field :updated_at, GraphQL::Types::ISO8601DateTime, null: false + field :last_balance_sync_at, GraphQL::Types::ISO8601DateTime, null: true + field :last_consumed_credit_sync_at, GraphQL::Types::ISO8601DateTime, null: true + end + end +end diff --git a/app/graphql/types/wallets/single_object.rb b/app/graphql/types/wallets/single_object.rb new file mode 100644 index 00000000000..6bd0e6fb571 --- /dev/null +++ b/app/graphql/types/wallets/single_object.rb @@ -0,0 +1,9 @@ +# frozen_string_literal: true + +module Types + module Wallets + class SingleObject < Types::Wallets::Object + graphql_name 'WalletDetails' + end + end +end diff --git a/app/graphql/types/wallets/status_enum.rb b/app/graphql/types/wallets/status_enum.rb new file mode 100644 index 00000000000..cd2032567e1 --- /dev/null +++ b/app/graphql/types/wallets/status_enum.rb @@ -0,0 +1,13 @@ +# frozen_string_literal: true + +module Types + module Wallets + class StatusEnum < Types::BaseEnum + graphql_name 'WalletStatusEnum' + + Wallet::STATUSES.each do |type| + value type + end + end + end +end diff --git a/app/models/customer.rb b/app/models/customer.rb index f40a8e60f19..ffdc6a083c1 100644 --- a/app/models/customer.rb +++ b/app/models/customer.rb @@ -14,6 +14,7 @@ class Customer < ApplicationRecord has_many :coupons, through: :applied_coupons has_many :applied_add_ons has_many :add_ons, through: :applied_add_ons + has_many :wallets has_many :payment_provider_customers, class_name: 'PaymentProviderCustomers::BaseCustomer', dependent: :destroy diff --git a/app/models/wallet.rb b/app/models/wallet.rb new file mode 100644 index 00000000000..29f420c2434 --- /dev/null +++ b/app/models/wallet.rb @@ -0,0 +1,14 @@ +# frozen_string_literal: true + +class Wallet < ApplicationRecord + belongs_to :customer + + has_one :organization, through: :customer + + STATUSES = [ + :active, + :terminated, + ].freeze + + enum status: STATUSES +end diff --git a/db/migrate/20220718083657_create_wallets.rb b/db/migrate/20220718083657_create_wallets.rb new file mode 100644 index 00000000000..23eb01c6987 --- /dev/null +++ b/db/migrate/20220718083657_create_wallets.rb @@ -0,0 +1,25 @@ +# frozen_string_literal: true + +class CreateWallets < ActiveRecord::Migration[7.0] + def change + create_table :wallets, id: :uuid do |t| + t.references :customer, type: :uuid, null: false, foreign_key: true, index: true + + t.integer :status, null: false + t.string :currency, null: false + + t.string :name + t.decimal :rate_amount, null: false, default: 0, precision: 5 + t.decimal :credits_balance, null: false, default: 0, precision: 5 + t.decimal :balance, null: false, default: 0, precision: 5 + t.decimal :consumed_credits, null: false, default: 0, precision: 5 + + t.timestamp :expiration_date + t.timestamp :last_balance_sync_at + t.timestamp :last_consumed_credit_at + t.timestamp :terminated_at + + t.timestamps + end + end +end diff --git a/db/schema.rb b/db/schema.rb index 9d93d5c0d72..8c31c35e563 100644 --- a/db/schema.rb +++ b/db/schema.rb @@ -10,7 +10,7 @@ # # It's strongly recommended that you check this file into your version control system. -ActiveRecord::Schema[7.0].define(version: 2022_07_13_171816) do +ActiveRecord::Schema[7.0].define(version: 2022_07_18_083657) do # These are extensions that must be enabled in order to support this database enable_extension "pgcrypto" enable_extension "plpgsql" @@ -327,6 +327,24 @@ t.datetime "updated_at", null: false end + create_table "wallets", id: :uuid, default: -> { "gen_random_uuid()" }, force: :cascade do |t| + t.uuid "customer_id", null: false + t.integer "status", null: false + t.string "currency", null: false + t.string "name" + t.decimal "rate_amount", precision: 5, default: "0", null: false + t.decimal "credits_balance", precision: 5, default: "0", null: false + t.decimal "balance", precision: 5, default: "0", null: false + t.decimal "consumed_credits", precision: 5, default: "0", null: false + t.datetime "expiration_date", precision: nil + t.datetime "last_balance_sync_at", precision: nil + t.datetime "last_consumed_credit_at", precision: nil + t.datetime "terminated_at", precision: nil + t.datetime "created_at", null: false + t.datetime "updated_at", null: false + t.index ["customer_id"], name: "index_wallets_on_customer_id" + end + add_foreign_key "active_storage_attachments", "active_storage_blobs", column: "blob_id" add_foreign_key "active_storage_variant_records", "active_storage_blobs", column: "blob_id" add_foreign_key "add_ons", "organizations" @@ -355,4 +373,5 @@ add_foreign_key "plans", "organizations" add_foreign_key "subscriptions", "customers" add_foreign_key "subscriptions", "plans" + add_foreign_key "wallets", "customers" end diff --git a/schema.graphql b/schema.graphql index f4b21f0cc7a..d185253660d 100644 --- a/schema.graphql +++ b/schema.graphql @@ -1601,6 +1601,22 @@ input CreateCustomerInput { zipcode: String } +""" +Autogenerated input type of CreateCustomerWallet +""" +input CreateCustomerWalletInput { + """ + A unique identifier for the client performing the mutation. + """ + clientMutationId: String + customerId: String! + expirationDate: ISO8601Date + grantedCredits: String! + name: String + paidCredits: String! + rateAmount: String! +} + """ Autogenerated input type of CreatePlan """ @@ -2741,6 +2757,16 @@ type Mutation { input: CreateCustomerInput! ): Customer + """ + Creates a new Customer Wallet + """ + createCustomerWallet( + """ + Parameters for CreateCustomerWallet + """ + input: CreateCustomerWalletInput! + ): Wallet + """ Creates a new Plan """ @@ -2871,6 +2897,16 @@ type Mutation { input: TerminateCouponInput! ): Coupon + """ + Terminates a new Customer Wallet + """ + terminateCustomerWallet( + """ + Parameters for TerminateCustomerWallet + """ + input: TerminateCustomerWalletInput! + ): Wallet + """ Terminate a Subscription """ @@ -2931,6 +2967,16 @@ type Mutation { input: UpdateCustomerVatRateInput! ): CustomerDetails + """ + Updates a new Customer Wallet + """ + updateCustomerWallet( + """ + Parameters for UpdateCustomerWallet + """ + input: UpdateCustomerWalletInput! + ): Wallet + """ Updates an Organization """ @@ -3184,6 +3230,34 @@ type Query { limit: Int page: Int ): PlanCollection! + + """ + Query a single wallet + """ + wallet( + """ + Uniq ID of the wallet + """ + id: ID! + ): WalletDetails + + """ + Query wallets + """ + wallets( + """ + Uniq ID of the customer + """ + customerId: ID! + + """ + List of wallet IDs to fetch + """ + ids: [ID!] + limit: Int + page: Int + status: WalletStatusEnum + ): WalletCollection! } type RegisterUser { @@ -3265,6 +3339,17 @@ input TerminateCouponInput { id: ID! } +""" +Autogenerated input type of TerminateCustomerWallet +""" +input TerminateCustomerWalletInput { + """ + A unique identifier for the client performing the mutation. + """ + clientMutationId: String + id: String! +} + """ Autogenerated input type of TerminateSubscription """ @@ -3369,6 +3454,18 @@ input UpdateCustomerVatRateInput { vatRate: Float } +""" +Autogenerated input type of UpdateCustomerWallet +""" +input UpdateCustomerWalletInput { + """ + A unique identifier for the client performing the mutation. + """ + clientMutationId: String + expirationDate: ISO8601Date + id: String! +} + """ Autogenerated input type of UpdateOrganization """ @@ -3422,3 +3519,47 @@ type User { organizations: [Organization!] updatedAt: ISO8601DateTime! } + +type Wallet { + balance: String! + consumedCredits: String! + createdAt: ISO8601DateTime! + creditsBalance: String! + currency: CurrencyEnum! + customer: Customer + expirationDate: ISO8601Date + id: ID! + lastBalanceSyncAt: ISO8601DateTime + lastConsumedCreditSyncAt: ISO8601DateTime + name: String! + rateAmount: String! + status: WalletStatusEnum! + updatedAt: ISO8601DateTime! +} + +type WalletCollection { + collection: [Wallet!]! + metadata: CollectionMetadata! +} + +type WalletDetails { + balance: String! + consumedCredits: String! + createdAt: ISO8601DateTime! + creditsBalance: String! + currency: CurrencyEnum! + customer: Customer + expirationDate: ISO8601Date + id: ID! + lastBalanceSyncAt: ISO8601DateTime + lastConsumedCreditSyncAt: ISO8601DateTime + name: String! + rateAmount: String! + status: WalletStatusEnum! + updatedAt: ISO8601DateTime! +} + +enum WalletStatusEnum { + active + terminated +} diff --git a/schema.json b/schema.json index 19db7e1e5fc..839aae511f6 100644 --- a/schema.json +++ b/schema.json @@ -4662,6 +4662,117 @@ ], "enumValues": null }, + { + "kind": "INPUT_OBJECT", + "name": "CreateCustomerWalletInput", + "description": "Autogenerated input type of CreateCustomerWallet", + "interfaces": null, + "possibleTypes": null, + "fields": null, + "inputFields": [ + { + "name": "clientMutationId", + "description": "A unique identifier for the client performing the mutation.", + "type": { + "kind": "SCALAR", + "name": "String", + "ofType": null + }, + "defaultValue": null, + "isDeprecated": false, + "deprecationReason": null + }, + { + "name": "customerId", + "description": null, + "type": { + "kind": "NON_NULL", + "name": null, + "ofType": { + "kind": "SCALAR", + "name": "String", + "ofType": null + } + }, + "defaultValue": null, + "isDeprecated": false, + "deprecationReason": null + }, + { + "name": "rateAmount", + "description": null, + "type": { + "kind": "NON_NULL", + "name": null, + "ofType": { + "kind": "SCALAR", + "name": "String", + "ofType": null + } + }, + "defaultValue": null, + "isDeprecated": false, + "deprecationReason": null + }, + { + "name": "name", + "description": null, + "type": { + "kind": "SCALAR", + "name": "String", + "ofType": null + }, + "defaultValue": null, + "isDeprecated": false, + "deprecationReason": null + }, + { + "name": "paidCredits", + "description": null, + "type": { + "kind": "NON_NULL", + "name": null, + "ofType": { + "kind": "SCALAR", + "name": "String", + "ofType": null + } + }, + "defaultValue": null, + "isDeprecated": false, + "deprecationReason": null + }, + { + "name": "grantedCredits", + "description": null, + "type": { + "kind": "NON_NULL", + "name": null, + "ofType": { + "kind": "SCALAR", + "name": "String", + "ofType": null + } + }, + "defaultValue": null, + "isDeprecated": false, + "deprecationReason": null + }, + { + "name": "expirationDate", + "description": null, + "type": { + "kind": "SCALAR", + "name": "ISO8601Date", + "ofType": null + }, + "defaultValue": null, + "isDeprecated": false, + "deprecationReason": null + } + ], + "enumValues": null + }, { "kind": "INPUT_OBJECT", "name": "CreatePlanInput", @@ -8751,6 +8862,35 @@ } ] }, + { + "name": "createCustomerWallet", + "description": "Creates a new Customer Wallet", + "type": { + "kind": "OBJECT", + "name": "Wallet", + "ofType": null + }, + "isDeprecated": false, + "deprecationReason": null, + "args": [ + { + "name": "input", + "description": "Parameters for CreateCustomerWallet", + "type": { + "kind": "NON_NULL", + "name": null, + "ofType": { + "kind": "INPUT_OBJECT", + "name": "CreateCustomerWalletInput", + "ofType": null + } + }, + "defaultValue": null, + "isDeprecated": false, + "deprecationReason": null + } + ] + }, { "name": "createPlan", "description": "Creates a new Plan", @@ -9128,6 +9268,35 @@ } ] }, + { + "name": "terminateCustomerWallet", + "description": "Terminates a new Customer Wallet", + "type": { + "kind": "OBJECT", + "name": "Wallet", + "ofType": null + }, + "isDeprecated": false, + "deprecationReason": null, + "args": [ + { + "name": "input", + "description": "Parameters for TerminateCustomerWallet", + "type": { + "kind": "NON_NULL", + "name": null, + "ofType": { + "kind": "INPUT_OBJECT", + "name": "TerminateCustomerWalletInput", + "ofType": null + } + }, + "defaultValue": null, + "isDeprecated": false, + "deprecationReason": null + } + ] + }, { "name": "terminateSubscription", "description": "Terminate a Subscription", @@ -9302,6 +9471,35 @@ } ] }, + { + "name": "updateCustomerWallet", + "description": "Updates a new Customer Wallet", + "type": { + "kind": "OBJECT", + "name": "Wallet", + "ofType": null + }, + "isDeprecated": false, + "deprecationReason": null, + "args": [ + { + "name": "input", + "description": "Parameters for UpdateCustomerWallet", + "type": { + "kind": "NON_NULL", + "name": null, + "ofType": { + "kind": "INPUT_OBJECT", + "name": "UpdateCustomerWalletInput", + "ofType": null + } + }, + "defaultValue": null, + "isDeprecated": false, + "deprecationReason": null + } + ] + }, { "name": "updateOrganization", "description": "Updates an Organization", @@ -10956,90 +11154,208 @@ "deprecationReason": null } ] - } - ], - "inputFields": null, - "enumValues": null - }, - { - "kind": "OBJECT", - "name": "RegisterUser", - "description": null, - "interfaces": [ - - ], - "possibleTypes": null, - "fields": [ - { - "name": "membership", - "description": null, - "type": { - "kind": "NON_NULL", - "name": null, - "ofType": { - "kind": "OBJECT", - "name": "Membership", - "ofType": null - } - }, - "isDeprecated": false, - "deprecationReason": null, - "args": [ - - ] }, { - "name": "organization", - "description": null, + "name": "wallet", + "description": "Query a single wallet", "type": { - "kind": "NON_NULL", - "name": null, - "ofType": { - "kind": "OBJECT", - "name": "Organization", - "ofType": null - } + "kind": "OBJECT", + "name": "WalletDetails", + "ofType": null }, "isDeprecated": false, "deprecationReason": null, "args": [ - - ] - }, - { - "name": "token", - "description": null, - "type": { - "kind": "NON_NULL", - "name": null, - "ofType": { - "kind": "SCALAR", - "name": "String", - "ofType": null + { + "name": "id", + "description": "Uniq ID of the wallet", + "type": { + "kind": "NON_NULL", + "name": null, + "ofType": { + "kind": "SCALAR", + "name": "ID", + "ofType": null + } + }, + "defaultValue": null, + "isDeprecated": false, + "deprecationReason": null } - }, - "isDeprecated": false, - "deprecationReason": null, - "args": [ - ] }, { - "name": "user", - "description": null, + "name": "wallets", + "description": "Query wallets", "type": { "kind": "NON_NULL", "name": null, "ofType": { "kind": "OBJECT", - "name": "User", + "name": "WalletCollection", "ofType": null } }, "isDeprecated": false, "deprecationReason": null, "args": [ - + { + "name": "ids", + "description": "List of wallet IDs to fetch", + "type": { + "kind": "LIST", + "name": null, + "ofType": { + "kind": "NON_NULL", + "name": null, + "ofType": { + "kind": "SCALAR", + "name": "ID", + "ofType": null + } + } + }, + "defaultValue": null, + "isDeprecated": false, + "deprecationReason": null + }, + { + "name": "customerId", + "description": "Uniq ID of the customer", + "type": { + "kind": "NON_NULL", + "name": null, + "ofType": { + "kind": "SCALAR", + "name": "ID", + "ofType": null + } + }, + "defaultValue": null, + "isDeprecated": false, + "deprecationReason": null + }, + { + "name": "page", + "description": null, + "type": { + "kind": "SCALAR", + "name": "Int", + "ofType": null + }, + "defaultValue": null, + "isDeprecated": false, + "deprecationReason": null + }, + { + "name": "limit", + "description": null, + "type": { + "kind": "SCALAR", + "name": "Int", + "ofType": null + }, + "defaultValue": null, + "isDeprecated": false, + "deprecationReason": null + }, + { + "name": "status", + "description": null, + "type": { + "kind": "ENUM", + "name": "WalletStatusEnum", + "ofType": null + }, + "defaultValue": null, + "isDeprecated": false, + "deprecationReason": null + } + ] + } + ], + "inputFields": null, + "enumValues": null + }, + { + "kind": "OBJECT", + "name": "RegisterUser", + "description": null, + "interfaces": [ + + ], + "possibleTypes": null, + "fields": [ + { + "name": "membership", + "description": null, + "type": { + "kind": "NON_NULL", + "name": null, + "ofType": { + "kind": "OBJECT", + "name": "Membership", + "ofType": null + } + }, + "isDeprecated": false, + "deprecationReason": null, + "args": [ + + ] + }, + { + "name": "organization", + "description": null, + "type": { + "kind": "NON_NULL", + "name": null, + "ofType": { + "kind": "OBJECT", + "name": "Organization", + "ofType": null + } + }, + "isDeprecated": false, + "deprecationReason": null, + "args": [ + + ] + }, + { + "name": "token", + "description": null, + "type": { + "kind": "NON_NULL", + "name": null, + "ofType": { + "kind": "SCALAR", + "name": "String", + "ofType": null + } + }, + "isDeprecated": false, + "deprecationReason": null, + "args": [ + + ] + }, + { + "name": "user", + "description": null, + "type": { + "kind": "NON_NULL", + "name": null, + "ofType": { + "kind": "OBJECT", + "name": "User", + "ofType": null + } + }, + "isDeprecated": false, + "deprecationReason": null, + "args": [ + ] } ], @@ -11576,6 +11892,45 @@ ], "enumValues": null }, + { + "kind": "INPUT_OBJECT", + "name": "TerminateCustomerWalletInput", + "description": "Autogenerated input type of TerminateCustomerWallet", + "interfaces": null, + "possibleTypes": null, + "fields": null, + "inputFields": [ + { + "name": "clientMutationId", + "description": "A unique identifier for the client performing the mutation.", + "type": { + "kind": "SCALAR", + "name": "String", + "ofType": null + }, + "defaultValue": null, + "isDeprecated": false, + "deprecationReason": null + }, + { + "name": "id", + "description": null, + "type": { + "kind": "NON_NULL", + "name": null, + "ofType": { + "kind": "SCALAR", + "name": "String", + "ofType": null + } + }, + "defaultValue": null, + "isDeprecated": false, + "deprecationReason": null + } + ], + "enumValues": null + }, { "kind": "INPUT_OBJECT", "name": "TerminateSubscriptionInput", @@ -12274,6 +12629,57 @@ ], "enumValues": null }, + { + "kind": "INPUT_OBJECT", + "name": "UpdateCustomerWalletInput", + "description": "Autogenerated input type of UpdateCustomerWallet", + "interfaces": null, + "possibleTypes": null, + "fields": null, + "inputFields": [ + { + "name": "clientMutationId", + "description": "A unique identifier for the client performing the mutation.", + "type": { + "kind": "SCALAR", + "name": "String", + "ofType": null + }, + "defaultValue": null, + "isDeprecated": false, + "deprecationReason": null + }, + { + "name": "id", + "description": null, + "type": { + "kind": "NON_NULL", + "name": null, + "ofType": { + "kind": "SCALAR", + "name": "String", + "ofType": null + } + }, + "defaultValue": null, + "isDeprecated": false, + "deprecationReason": null + }, + { + "name": "expirationDate", + "description": null, + "type": { + "kind": "SCALAR", + "name": "ISO8601Date", + "ofType": null + }, + "defaultValue": null, + "isDeprecated": false, + "deprecationReason": null + } + ], + "enumValues": null + }, { "kind": "INPUT_OBJECT", "name": "UpdateOrganizationInput", @@ -12751,6 +13157,584 @@ "inputFields": null, "enumValues": null }, + { + "kind": "OBJECT", + "name": "Wallet", + "description": null, + "interfaces": [ + + ], + "possibleTypes": null, + "fields": [ + { + "name": "balance", + "description": null, + "type": { + "kind": "NON_NULL", + "name": null, + "ofType": { + "kind": "SCALAR", + "name": "String", + "ofType": null + } + }, + "isDeprecated": false, + "deprecationReason": null, + "args": [ + + ] + }, + { + "name": "consumedCredits", + "description": null, + "type": { + "kind": "NON_NULL", + "name": null, + "ofType": { + "kind": "SCALAR", + "name": "String", + "ofType": null + } + }, + "isDeprecated": false, + "deprecationReason": null, + "args": [ + + ] + }, + { + "name": "createdAt", + "description": null, + "type": { + "kind": "NON_NULL", + "name": null, + "ofType": { + "kind": "SCALAR", + "name": "ISO8601DateTime", + "ofType": null + } + }, + "isDeprecated": false, + "deprecationReason": null, + "args": [ + + ] + }, + { + "name": "creditsBalance", + "description": null, + "type": { + "kind": "NON_NULL", + "name": null, + "ofType": { + "kind": "SCALAR", + "name": "String", + "ofType": null + } + }, + "isDeprecated": false, + "deprecationReason": null, + "args": [ + + ] + }, + { + "name": "currency", + "description": null, + "type": { + "kind": "NON_NULL", + "name": null, + "ofType": { + "kind": "ENUM", + "name": "CurrencyEnum", + "ofType": null + } + }, + "isDeprecated": false, + "deprecationReason": null, + "args": [ + + ] + }, + { + "name": "customer", + "description": null, + "type": { + "kind": "OBJECT", + "name": "Customer", + "ofType": null + }, + "isDeprecated": false, + "deprecationReason": null, + "args": [ + + ] + }, + { + "name": "expirationDate", + "description": null, + "type": { + "kind": "SCALAR", + "name": "ISO8601Date", + "ofType": null + }, + "isDeprecated": false, + "deprecationReason": null, + "args": [ + + ] + }, + { + "name": "id", + "description": null, + "type": { + "kind": "NON_NULL", + "name": null, + "ofType": { + "kind": "SCALAR", + "name": "ID", + "ofType": null + } + }, + "isDeprecated": false, + "deprecationReason": null, + "args": [ + + ] + }, + { + "name": "lastBalanceSyncAt", + "description": null, + "type": { + "kind": "SCALAR", + "name": "ISO8601DateTime", + "ofType": null + }, + "isDeprecated": false, + "deprecationReason": null, + "args": [ + + ] + }, + { + "name": "lastConsumedCreditSyncAt", + "description": null, + "type": { + "kind": "SCALAR", + "name": "ISO8601DateTime", + "ofType": null + }, + "isDeprecated": false, + "deprecationReason": null, + "args": [ + + ] + }, + { + "name": "name", + "description": null, + "type": { + "kind": "NON_NULL", + "name": null, + "ofType": { + "kind": "SCALAR", + "name": "String", + "ofType": null + } + }, + "isDeprecated": false, + "deprecationReason": null, + "args": [ + + ] + }, + { + "name": "rateAmount", + "description": null, + "type": { + "kind": "NON_NULL", + "name": null, + "ofType": { + "kind": "SCALAR", + "name": "String", + "ofType": null + } + }, + "isDeprecated": false, + "deprecationReason": null, + "args": [ + + ] + }, + { + "name": "status", + "description": null, + "type": { + "kind": "NON_NULL", + "name": null, + "ofType": { + "kind": "ENUM", + "name": "WalletStatusEnum", + "ofType": null + } + }, + "isDeprecated": false, + "deprecationReason": null, + "args": [ + + ] + }, + { + "name": "updatedAt", + "description": null, + "type": { + "kind": "NON_NULL", + "name": null, + "ofType": { + "kind": "SCALAR", + "name": "ISO8601DateTime", + "ofType": null + } + }, + "isDeprecated": false, + "deprecationReason": null, + "args": [ + + ] + } + ], + "inputFields": null, + "enumValues": null + }, + { + "kind": "OBJECT", + "name": "WalletCollection", + "description": null, + "interfaces": [ + + ], + "possibleTypes": null, + "fields": [ + { + "name": "collection", + "description": null, + "type": { + "kind": "NON_NULL", + "name": null, + "ofType": { + "kind": "LIST", + "name": null, + "ofType": { + "kind": "NON_NULL", + "name": null, + "ofType": { + "kind": "OBJECT", + "name": "Wallet", + "ofType": null + } + } + } + }, + "isDeprecated": false, + "deprecationReason": null, + "args": [ + + ] + }, + { + "name": "metadata", + "description": null, + "type": { + "kind": "NON_NULL", + "name": null, + "ofType": { + "kind": "OBJECT", + "name": "CollectionMetadata", + "ofType": null + } + }, + "isDeprecated": false, + "deprecationReason": null, + "args": [ + + ] + } + ], + "inputFields": null, + "enumValues": null + }, + { + "kind": "OBJECT", + "name": "WalletDetails", + "description": null, + "interfaces": [ + + ], + "possibleTypes": null, + "fields": [ + { + "name": "balance", + "description": null, + "type": { + "kind": "NON_NULL", + "name": null, + "ofType": { + "kind": "SCALAR", + "name": "String", + "ofType": null + } + }, + "isDeprecated": false, + "deprecationReason": null, + "args": [ + + ] + }, + { + "name": "consumedCredits", + "description": null, + "type": { + "kind": "NON_NULL", + "name": null, + "ofType": { + "kind": "SCALAR", + "name": "String", + "ofType": null + } + }, + "isDeprecated": false, + "deprecationReason": null, + "args": [ + + ] + }, + { + "name": "createdAt", + "description": null, + "type": { + "kind": "NON_NULL", + "name": null, + "ofType": { + "kind": "SCALAR", + "name": "ISO8601DateTime", + "ofType": null + } + }, + "isDeprecated": false, + "deprecationReason": null, + "args": [ + + ] + }, + { + "name": "creditsBalance", + "description": null, + "type": { + "kind": "NON_NULL", + "name": null, + "ofType": { + "kind": "SCALAR", + "name": "String", + "ofType": null + } + }, + "isDeprecated": false, + "deprecationReason": null, + "args": [ + + ] + }, + { + "name": "currency", + "description": null, + "type": { + "kind": "NON_NULL", + "name": null, + "ofType": { + "kind": "ENUM", + "name": "CurrencyEnum", + "ofType": null + } + }, + "isDeprecated": false, + "deprecationReason": null, + "args": [ + + ] + }, + { + "name": "customer", + "description": null, + "type": { + "kind": "OBJECT", + "name": "Customer", + "ofType": null + }, + "isDeprecated": false, + "deprecationReason": null, + "args": [ + + ] + }, + { + "name": "expirationDate", + "description": null, + "type": { + "kind": "SCALAR", + "name": "ISO8601Date", + "ofType": null + }, + "isDeprecated": false, + "deprecationReason": null, + "args": [ + + ] + }, + { + "name": "id", + "description": null, + "type": { + "kind": "NON_NULL", + "name": null, + "ofType": { + "kind": "SCALAR", + "name": "ID", + "ofType": null + } + }, + "isDeprecated": false, + "deprecationReason": null, + "args": [ + + ] + }, + { + "name": "lastBalanceSyncAt", + "description": null, + "type": { + "kind": "SCALAR", + "name": "ISO8601DateTime", + "ofType": null + }, + "isDeprecated": false, + "deprecationReason": null, + "args": [ + + ] + }, + { + "name": "lastConsumedCreditSyncAt", + "description": null, + "type": { + "kind": "SCALAR", + "name": "ISO8601DateTime", + "ofType": null + }, + "isDeprecated": false, + "deprecationReason": null, + "args": [ + + ] + }, + { + "name": "name", + "description": null, + "type": { + "kind": "NON_NULL", + "name": null, + "ofType": { + "kind": "SCALAR", + "name": "String", + "ofType": null + } + }, + "isDeprecated": false, + "deprecationReason": null, + "args": [ + + ] + }, + { + "name": "rateAmount", + "description": null, + "type": { + "kind": "NON_NULL", + "name": null, + "ofType": { + "kind": "SCALAR", + "name": "String", + "ofType": null + } + }, + "isDeprecated": false, + "deprecationReason": null, + "args": [ + + ] + }, + { + "name": "status", + "description": null, + "type": { + "kind": "NON_NULL", + "name": null, + "ofType": { + "kind": "ENUM", + "name": "WalletStatusEnum", + "ofType": null + } + }, + "isDeprecated": false, + "deprecationReason": null, + "args": [ + + ] + }, + { + "name": "updatedAt", + "description": null, + "type": { + "kind": "NON_NULL", + "name": null, + "ofType": { + "kind": "SCALAR", + "name": "ISO8601DateTime", + "ofType": null + } + }, + "isDeprecated": false, + "deprecationReason": null, + "args": [ + + ] + } + ], + "inputFields": null, + "enumValues": null + }, + { + "kind": "ENUM", + "name": "WalletStatusEnum", + "description": null, + "interfaces": null, + "possibleTypes": null, + "fields": null, + "inputFields": null, + "enumValues": [ + { + "name": "active", + "description": null, + "isDeprecated": false, + "deprecationReason": null + }, + { + "name": "terminated", + "description": null, + "isDeprecated": false, + "deprecationReason": null + } + ] + }, { "kind": "OBJECT", "name": "__Directive", diff --git a/spec/factories/wallet_factory.rb b/spec/factories/wallet_factory.rb new file mode 100644 index 00000000000..662fcfc9709 --- /dev/null +++ b/spec/factories/wallet_factory.rb @@ -0,0 +1,14 @@ +# frozen_string_literal: true + +FactoryBot.define do + factory :wallet do + customer + name { Faker::Name.name } + status { 'active' } + rate_amount { '1.00' } + currency { 'EUR' } + credits_balance { '0.00' } + balance { '0.00' } + consumed_credits { '0.00' } + end +end diff --git a/spec/graphql/resolvers/wallet_resolver_spec.rb b/spec/graphql/resolvers/wallet_resolver_spec.rb new file mode 100644 index 00000000000..1814de4dad8 --- /dev/null +++ b/spec/graphql/resolvers/wallet_resolver_spec.rb @@ -0,0 +1,72 @@ +# frozen_string_literal: true + +require 'rails_helper' + +RSpec.describe Resolvers::WalletResolver, type: :graphql do + let(:query) do + <<~GQL + query($walletId: ID!) { + wallet(id: $walletId) { + id, name, expirationDate + } + } + GQL + end + + let(:membership) { create(:membership) } + let(:organization) { membership.organization } + let(:customer) { create(:customer, organization: organization) } + let(:wallet) { create(:wallet, customer: customer) } + + it 'returns a single wallet' do + result = execute_graphql( + current_user: membership.user, + current_organization: organization, + query: query, + variables: { + walletId: wallet.id, + }, + ) + + wallet_response = result['data']['wallet'] + + aggregate_failures do + expect(wallet_response['id']).to eq(wallet.id) + end + end + + context 'without current organization' do + it 'returns an error' do + result = execute_graphql( + current_user: membership.user, + query: query, + variables: { + walletId: wallet.id, + }, + ) + + expect_graphql_error( + result: result, + message: 'Missing organization id', + ) + end + end + + context 'when wallet is not found' do + it 'returns an error' do + result = execute_graphql( + current_user: membership.user, + current_organization: organization, + query: query, + variables: { + walletId: 'foo', + }, + ) + + expect_graphql_error( + result: result, + message: 'Resource not found', + ) + end + end +end diff --git a/spec/graphql/resolvers/wallets_resolver_spec.rb b/spec/graphql/resolvers/wallets_resolver_spec.rb new file mode 100644 index 00000000000..affa32fc547 --- /dev/null +++ b/spec/graphql/resolvers/wallets_resolver_spec.rb @@ -0,0 +1,101 @@ +# frozen_string_literal: true + +require 'rails_helper' + +RSpec.describe Resolvers::WalletsResolver, type: :graphql do + let(:query) do + <<~GQL + query($customerId: ID!) { + wallets(customerId: $customerId, limit: 5, status: active) { + collection { id } + metadata { currentPage, totalCount } + } + } + GQL + end + + let(:membership) { create(:membership) } + let(:organization) { membership.organization } + let(:customer) { create(:customer, organization: organization) } + let(:wallet) { create(:wallet, organization: organization, customer: customer) } + + before do + wallet + + create(:wallet, status: :terminated, customer: customer) + end + + it 'returns a list of wallets' do + result = execute_graphql( + current_user: membership.user, + current_organization: organization, + query: query, + variables: { + customerId: customer.id, + }, + ) + + wallets_response = result['data']['wallets'] + + aggregate_failures do + expect(wallets_response['collection'].count).to eq(customer.wallets.active.count) + expect(wallets_response['collection'].first['id']).to eq(wallet.id) + + expect(wallets_response['metadata']['currentPage']).to eq(1) + expect(wallets_response['metadata']['totalCount']).to eq(1) + end + end + + context 'without current organization' do + it 'returns an error' do + result = execute_graphql( + current_user: membership.user, + query: query, + variables: { + customerId: customer.id, + }, + ) + + expect_graphql_error( + result: result, + message: 'Missing organization id', + ) + end + end + + context 'when not member of the organization' do + it 'returns an error' do + result = execute_graphql( + current_user: membership.user, + current_organization: create(:organization), + query: query, + variables: { + customerId: customer.id, + }, + ) + + expect_graphql_error( + result: result, + message: 'Not in organization', + ) + end + end + + context 'when customer does not exists' do + it 'returns an error' do + result = execute_graphql( + current_user: membership.user, + current_organization: organization, + query: query, + variables: { + customerId: '123456', + }, + ) + + expect_graphql_error( + result: result, + message: 'Resource not found', + ) + end + end +end diff --git a/spec/support/graphql_helper.rb b/spec/support/graphql_helper.rb index 92bc59d9fec..f02f55266ec 100644 --- a/spec/support/graphql_helper.rb +++ b/spec/support/graphql_helper.rb @@ -12,13 +12,13 @@ def execute_graphql(current_user: nil, query: nil, current_organization: nil, ** context: { controller: controller, current_user: current_user, - current_organization: current_organization - } + current_organization: current_organization, + }, ) LagoApiSchema.execute( query, - **args + **args, ) end @@ -37,14 +37,14 @@ def expect_graphql_error(result:, message:) def expect_unauthorized_error(result) expect_graphql_error( result: result, - message: :unauthorized + message: :unauthorized, ) end def expect_forbidden_error(result) expect_graphql_error( result: result, - message: :forbidden + message: :forbidden, ) end end