From 5ba69fb8dc2032d6a8aac98b2ef43297f7cb952e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=A9r=C3=A9my=20Denquin?= Date: Mon, 11 Apr 2022 14:53:13 +0200 Subject: [PATCH] feat: T-983-api-create-a-subscription-graphql-mutation (#63) * feat: T-983-api-create-a-subscription-graphql-mutation * remove useless file --- app/graphql/mutations/subscriptions/create.rb | 31 +++++ app/graphql/types/mutation_type.rb | 2 + app/graphql/types/subscriptions/object.rb | 3 +- schema.graphql | 25 +++- schema.json | 116 ++++++++++++++++-- .../mutations/subscriptions/create_spec.rb | 85 +++++++++++++ 6 files changed, 249 insertions(+), 13 deletions(-) create mode 100644 app/graphql/mutations/subscriptions/create.rb create mode 100644 spec/graphql/mutations/subscriptions/create_spec.rb diff --git a/app/graphql/mutations/subscriptions/create.rb b/app/graphql/mutations/subscriptions/create.rb new file mode 100644 index 00000000000..b1479fd723f --- /dev/null +++ b/app/graphql/mutations/subscriptions/create.rb @@ -0,0 +1,31 @@ +# frozen_string_literal: true + +module Mutations + module Subscriptions + class Create < BaseMutation + include AuthenticableApiUser + include RequiredOrganization + + graphql_name 'CreateSubscription' + description 'Create a new Subscription' + + argument :customer_id, String, required: true + argument :plan_code, String, required: true + + type Types::Subscriptions::Object + + def resolve(**args) + validate_organization! + + result = SubscriptionsService + .new + .create( + organization: current_organization, + params: args, + ) + + result.success? ? result.subscription : execution_error(code: result.error_code, message: result.error) + end + end + end +end diff --git a/app/graphql/types/mutation_type.rb b/app/graphql/types/mutation_type.rb index 218a8f60dfd..ea67446d652 100644 --- a/app/graphql/types/mutation_type.rb +++ b/app/graphql/types/mutation_type.rb @@ -14,5 +14,7 @@ class MutationType < Types::BaseObject field :destroy_plan, mutation: Mutations::Plans::Destroy field :create_customer, mutation: Mutations::Customers::Create + + field :create_subscription, mutation: Mutations::Subscriptions::Create end end diff --git a/app/graphql/types/subscriptions/object.rb b/app/graphql/types/subscriptions/object.rb index cb3d897c65e..f081efb020d 100644 --- a/app/graphql/types/subscriptions/object.rb +++ b/app/graphql/types/subscriptions/object.rb @@ -6,7 +6,8 @@ class Object < Types::BaseObject graphql_name 'Subscription' field :id, ID, null: false - field :plan, [Types::Plans::Object], null: false + field :customer, Types::Customers::Object, null: false + field :plan, Types::Plans::Object, null: false field :status, Types::Subscriptions::StatusTypeEnum diff --git a/schema.graphql b/schema.graphql index 970a7c7f4fc..bac0dcbc15a 100644 --- a/schema.graphql +++ b/schema.graphql @@ -110,6 +110,18 @@ input CreatePlanInput { vatRate: Float } +""" +Autogenerated input type of CreateSubscription +""" +input CreateSubscriptionInput { + """ + A unique identifier for the client performing the mutation. + """ + clientMutationId: String + customerId: String! + planCode: String! +} + enum CurrencyEnum { """ Euro @@ -277,6 +289,16 @@ type Mutation { input: CreatePlanInput! ): Plan + """ + Create a new Subscription + """ + createSubscription( + """ + Parameters for CreateSubscription + """ + input: CreateSubscriptionInput! + ): Subscription + """ Deletes a Billable metric """ @@ -462,8 +484,9 @@ enum StatusTypeEnum { type Subscription { canceledAt: ISO8601DateTime createdAt: ISO8601DateTime! + customer: Customer! id: ID! - plan: [Plan!]! + plan: Plan! startedAt: ISO8601DateTime status: StatusTypeEnum terminatedAt: ISO8601DateTime diff --git a/schema.json b/schema.json index 8d7f486a0fb..6c7fd05acbc 100644 --- a/schema.json +++ b/schema.json @@ -1046,6 +1046,61 @@ ], "enumValues": null }, + { + "kind": "INPUT_OBJECT", + "name": "CreateSubscriptionInput", + "description": "Autogenerated input type of CreateSubscription", + "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": "planCode", + "description": null, + "type": { + "kind": "NON_NULL", + "name": null, + "ofType": { + "kind": "SCALAR", + "name": "String", + "ofType": null + } + }, + "defaultValue": null, + "isDeprecated": false, + "deprecationReason": null + } + ], + "enumValues": null + }, { "kind": "ENUM", "name": "CurrencyEnum", @@ -2169,6 +2224,35 @@ } ] }, + { + "name": "createSubscription", + "description": "Create a new Subscription", + "type": { + "kind": "OBJECT", + "name": "Subscription", + "ofType": null + }, + "isDeprecated": false, + "deprecationReason": null, + "args": [ + { + "name": "input", + "description": "Parameters for CreateSubscription", + "type": { + "kind": "NON_NULL", + "name": null, + "ofType": { + "kind": "INPUT_OBJECT", + "name": "CreateSubscriptionInput", + "ofType": null + } + }, + "defaultValue": null, + "isDeprecated": false, + "deprecationReason": null + } + ] + }, { "name": "destroyBillableMetric", "description": "Deletes a Billable metric", @@ -3338,6 +3422,24 @@ ] }, + { + "name": "customer", + "description": null, + "type": { + "kind": "NON_NULL", + "name": null, + "ofType": { + "kind": "OBJECT", + "name": "Customer", + "ofType": null + } + }, + "isDeprecated": false, + "deprecationReason": null, + "args": [ + + ] + }, { "name": "id", "description": null, @@ -3363,17 +3465,9 @@ "kind": "NON_NULL", "name": null, "ofType": { - "kind": "LIST", - "name": null, - "ofType": { - "kind": "NON_NULL", - "name": null, - "ofType": { - "kind": "OBJECT", - "name": "Plan", - "ofType": null - } - } + "kind": "OBJECT", + "name": "Plan", + "ofType": null } }, "isDeprecated": false, diff --git a/spec/graphql/mutations/subscriptions/create_spec.rb b/spec/graphql/mutations/subscriptions/create_spec.rb new file mode 100644 index 00000000000..c8adfce5f06 --- /dev/null +++ b/spec/graphql/mutations/subscriptions/create_spec.rb @@ -0,0 +1,85 @@ +# frozen_string_literal: true + +require 'rails_helper' + +RSpec.describe Mutations::Plans::Create, type: :graphql do + let(:membership) { create(:membership) } + let(:organization) { membership.organization } + let(:plan) { create(:plan, organization: organization) } + let(:customer) { create(:customer, organization: organization) } + let(:mutation) do + <<~GQL + mutation($input: CreateSubscriptionInput!) { + createSubscription(input: $input) { + id, + status, + startedAt, + customer { + id + }, + plan { + id + } + } + } + GQL + end + + it 'creates a subscription' do + result = execute_graphql( + current_user: membership.user, + current_organization: organization, + query: mutation, + variables: { + input: { + customerId: customer.customer_id, + planCode: plan.code, + }, + }, + ) + + result_data = result['data']['createSubscription'] + + aggregate_failures do + expect(result_data['id']).to be_present + expect(result_data['status'].to_sym).to eq(:active) + expect(result_data['startedAt']).to be_present + expect(result_data['customer']['id']).to eq(customer.id) + expect(result_data['plan']['id']).to eq(plan.id) + end + end + + context 'without current user' do + it 'returns an error' do + result = execute_graphql( + current_organization: membership.organization, + query: mutation, + variables: { + input: { + customerId: customer.customer_id, + planCode: plan.code, + }, + }, + ) + + expect_unauthorized_error(result) + end + end + + context 'without current organization' do + it 'returns an error' do + result = execute_graphql( + current_user: membership.user, + query: mutation, + variables: { + input: { + customerId: customer.customer_id, + planCode: plan.code, + }, + }, + ) + + expect_forbidden_error(result) + end + end +end