diff --git a/app/graphql/resolvers/memberships_resolver.rb b/app/graphql/resolvers/memberships_resolver.rb new file mode 100644 index 000000000000..ccd04a56a62c --- /dev/null +++ b/app/graphql/resolvers/memberships_resolver.rb @@ -0,0 +1,25 @@ +# frozen_string_literal: true + +module Resolvers + class MembershipsResolver < GraphQL::Schema::Resolver + include AuthenticableApiUser + include RequiredOrganization + + description 'Query memberships of an organization' + + argument :page, Integer, required: false + argument :limit, Integer, required: false + + type Types::MembershipType.collection_type, null: false + + def resolve(page: nil, limit: nil) + validate_organization! + + current_organization + .memberships + .active + .page(page) + .per(limit) + end + end +end diff --git a/app/graphql/types/query_type.rb b/app/graphql/types/query_type.rb index 87dee85397ec..c1cd9c2a25b6 100644 --- a/app/graphql/types/query_type.rb +++ b/app/graphql/types/query_type.rb @@ -26,5 +26,6 @@ class QueryType < Types::BaseObject field :wallet, resolver: Resolvers::WalletResolver field :wallet_transactions, resolver: Resolvers::WalletTransactionsResolver field :wallet_transaction, resolver: Resolvers::WalletTransactionResolver + field :memberships, resolver: Resolvers::MembershipsResolver end end diff --git a/schema.graphql b/schema.graphql index 241aa36ec216..5de52fa16129 100644 --- a/schema.graphql +++ b/schema.graphql @@ -2726,6 +2726,16 @@ type Membership { userId: Int! } +type MembershipCollection { + collection: [Membership!]! + metadata: CollectionMetadata! +} + +enum MembershipStatus { + active + revoked +} + type Mutation { """ Add or update Stripe API keys to the organization @@ -3269,6 +3279,11 @@ type Query { """ events(limit: Int, page: Int): EventCollection + """ + Query memberships of an organization + """ + memberships(limit: Int, page: Int): MembershipCollection! + """ Query a single plan of an organization """ diff --git a/schema.json b/schema.json index 18f7b9550619..8740c161f987 100644 --- a/schema.json +++ b/schema.json @@ -8889,6 +8889,86 @@ "inputFields": null, "enumValues": null }, + { + "kind": "OBJECT", + "name": "MembershipCollection", + "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": "Membership", + "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": "ENUM", + "name": "MembershipStatus", + "description": null, + "interfaces": null, + "possibleTypes": null, + "fields": null, + "inputFields": null, + "enumValues": [ + { + "name": "active", + "description": null, + "isDeprecated": false, + "deprecationReason": null + }, + { + "name": "revoked", + "description": null, + "isDeprecated": false, + "deprecationReason": null + } + ] + }, { "kind": "OBJECT", "name": "Mutation", @@ -11378,6 +11458,47 @@ } ] }, + { + "name": "memberships", + "description": "Query memberships of an organization", + "type": { + "kind": "NON_NULL", + "name": null, + "ofType": { + "kind": "OBJECT", + "name": "MembershipCollection", + "ofType": null + } + }, + "isDeprecated": false, + "deprecationReason": null, + "args": [ + { + "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": "plan", "description": "Query a single plan of an organization", diff --git a/spec/graphql/resolvers/memberships_resolver_spec.rb b/spec/graphql/resolvers/memberships_resolver_spec.rb new file mode 100644 index 000000000000..cbb0766c5f39 --- /dev/null +++ b/spec/graphql/resolvers/memberships_resolver_spec.rb @@ -0,0 +1,51 @@ +# frozen_string_literal: true + +require 'rails_helper' + +RSpec.describe Resolvers::MembershipsResolver, type: :graphql do + let(:query) do + <<~GQL + query { + memberships(limit: 5) { + collection { id } + metadata { currentPage, totalCount } + } + } + GQL + end + + let(:membership) { create(:membership) } + let(:organization) { membership.organization } + + it 'returns a list of memberships' do + result = execute_graphql( + current_user: membership.user, + current_organization: organization, + query: query, + ) + + memberships_response = result['data']['memberships'] + + aggregate_failures do + expect(memberships_response['collection'].count).to eq(organization.memberships.count) + expect(memberships_response['collection'].first['id']).to eq(membership.id) + + expect(memberships_response['metadata']['currentPage']).to eq(1) + expect(memberships_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, + ) + + expect_graphql_error( + result: result, + message: 'Missing organization id', + ) + end + end +end