Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

added field connection resolver for organization events #2336

Closed
Closed
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 12 additions & 0 deletions schema.graphql
Original file line number Diff line number Diff line change
Expand Up @@ -769,6 +769,17 @@ input EventWhereInput {
title_starts_with: String
}

type EventsConnection {
edges: [EventsConnectionEdge!]!
pageInfo: DefaultConnectionPageInfo!
totalCount: Int
}

type EventsConnectionEdge {
cursor: String!
node: Event!
}

type ExtendSession {
accessToken: String!
refreshToken: String!
Expand Down Expand Up @@ -1190,6 +1201,7 @@ type Organization {
creator: User
customFields: [OrganizationCustomField!]!
description: String!
events(after: String, before: String, first: Int, last: Int): EventsConnection
funds: [Fund]
image: String
members: [User]
Expand Down
122 changes: 122 additions & 0 deletions src/resolvers/Organization/events.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,122 @@
import type { OrganizationResolvers } from "../../types/generatedGraphQLTypes";
import { MAXIMUM_FETCH_LIMIT } from "../../constants";
import {
type DefaultGraphQLArgumentError,
type ParseGraphQLConnectionCursorArguments,
type ParseGraphQLConnectionCursorResult,
getCommonGraphQLConnectionFilter,
getCommonGraphQLConnectionSort,
parseGraphQLConnectionArguments,
transformToDefaultGraphQLConnection,
} from "../../utilities/graphQLConnection";
import { GraphQLError } from "graphql";
import type { InterfaceEvent } from "../../models";
import { Event } from "../../models";
import type { Types } from "mongoose";

/**
* This is a non-root field connection resolver for fetching events created wihtin an
* organization using relay specification compliant cursor pagination. More info here:-
* {@link https://relay.dev/graphql/connections.htm.}
*/
export const events: OrganizationResolvers["events"] = async (parent, args) => {
const parseArgsResult = await parseGraphQLConnectionArguments({
args,
parseCursor: (args) =>
parseCursor({
...args,
organizationId: parent._id,
}),
maximumLimit: MAXIMUM_FETCH_LIMIT,
});

if (parseArgsResult.isSuccessful === false) {
throw new GraphQLError("Invalid arguments provided.", {
extensions: {
code: "INVALID_ARGUMENTS",
errors: parseArgsResult.errors,
},
});
}

const { parsedArgs } = parseArgsResult;

const filter = getCommonGraphQLConnectionFilter({
cursor: parsedArgs.cursor,
direction: parsedArgs.direction,
});

const sort = getCommonGraphQLConnectionSort({
direction: parsedArgs.direction,
});

const [objectList, totalCount] = await Promise.all([
Event.find({
...filter,
organization: parent._id,
})
.sort(sort)
.limit(parsedArgs.limit)
.lean()
.exec(),

Event.find({
organization: parent._id,
})
.countDocuments()
.exec(),
]);

return transformToDefaultGraphQLConnection<
ParsedCursor,
InterfaceEvent,
InterfaceEvent
>({
objectList,
parsedArgs,
totalCount,
});
};

/*
This is typescript type of the parsed cursor for this connection resolver.
*/
type ParsedCursor = string;

/*
This function is used to validate and transform the cursor passed to this connnection
resolver.
*/
export const parseCursor = async ({
cursorValue,
cursorName,
cursorPath,
organizationId,
}: ParseGraphQLConnectionCursorArguments & {
organizationId: string | Types.ObjectId;
}): ParseGraphQLConnectionCursorResult<ParsedCursor> => {
const errors: DefaultGraphQLArgumentError[] = [];
const event = await Event.findOne({
_id: cursorValue,
organization: organizationId,
});

if (!event) {
errors.push({
message: `Argument ${cursorName} is an invalid cursor.`,
path: cursorPath,
});
}

if (errors.length !== 0) {
return {
errors,
isSuccessful: false,
};
}

return {
isSuccessful: true,
parsedCursor: cursorValue,
};
};
17 changes: 17 additions & 0 deletions src/typeDefs/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -277,6 +277,17 @@ export const types = gql`
agendaItems: [AgendaItem]
}

type EventsConnection {
edges: [EventsConnectionEdge!]!
pageInfo: DefaultConnectionPageInfo!
totalCount: Int
}

type EventsConnectionEdge {
cursor: String!
node: Event!
}

type EventVolunteer {
_id: ID!
createdAt: DateTime!
Expand Down Expand Up @@ -459,6 +470,12 @@ export const types = gql`
actionItemCategories: [ActionItemCategory]
agendaCategories: [AgendaCategory]
admins(adminId: ID): [User!]
events(
after: String
before: String
first: Int
last: Int
): EventsConnection
membershipRequests(
first: Int
skip: Int
Expand Down
46 changes: 44 additions & 2 deletions src/types/generatedGraphQLTypes.ts
Original file line number Diff line number Diff line change
Expand Up @@ -852,6 +852,19 @@ export type EventWhereInput = {
title_starts_with?: InputMaybe<Scalars['String']['input']>;
};

export type EventsConnection = {
__typename?: 'EventsConnection';
edges: Array<EventsConnectionEdge>;
pageInfo: DefaultConnectionPageInfo;
totalCount?: Maybe<Scalars['Int']['output']>;
};

export type EventsConnectionEdge = {
__typename?: 'EventsConnectionEdge';
cursor: Scalars['String']['output'];
node: Event;
};

export type ExtendSession = {
__typename?: 'ExtendSession';
accessToken: Scalars['String']['output'];
Expand Down Expand Up @@ -1945,6 +1958,7 @@ export type Organization = {
creator?: Maybe<User>;
customFields: Array<OrganizationCustomField>;
description: Scalars['String']['output'];
events?: Maybe<EventsConnection>;
funds?: Maybe<Array<Maybe<Fund>>>;
image?: Maybe<Scalars['String']['output']>;
members?: Maybe<Array<Maybe<User>>>;
Expand Down Expand Up @@ -1973,6 +1987,14 @@ export type OrganizationAdvertisementsArgs = {
};


export type OrganizationEventsArgs = {
after?: InputMaybe<Scalars['String']['input']>;
before?: InputMaybe<Scalars['String']['input']>;
first?: InputMaybe<Scalars['Int']['input']>;
last?: InputMaybe<Scalars['Int']['input']>;
};


export type OrganizationMembershipRequestsArgs = {
first?: InputMaybe<Scalars['Int']['input']>;
skip?: InputMaybe<Scalars['Int']['input']>;
Expand Down Expand Up @@ -3161,7 +3183,7 @@ export type DirectiveResolverFn<TResult = {}, TParent = {}, TContext = {}, TArgs
) => TResult | Promise<TResult>;

/** Mapping of union types */
export type ResolversUnionTypes<RefType extends Record<string, unknown>> = {
export type ResolversUnionTypes<_RefType extends Record<string, unknown>> = {
ConnectionError: ( InvalidCursor ) | ( MaximumValueError );
CreateAdminError: ( OrganizationMemberNotFoundError ) | ( OrganizationNotFoundError ) | ( UserNotAuthorizedError ) | ( UserNotFoundError );
CreateCommentError: ( PostNotFoundError );
Expand All @@ -3170,7 +3192,7 @@ export type ResolversUnionTypes<RefType extends Record<string, unknown>> = {
};

/** Mapping of interface types */
export type ResolversInterfaceTypes<RefType extends Record<string, unknown>> = {
export type ResolversInterfaceTypes<_RefType extends Record<string, unknown>> = {
ConnectionPageInfo: ( DefaultConnectionPageInfo );
Error: ( MemberNotFoundError ) | ( OrganizationMemberNotFoundError ) | ( OrganizationNotFoundError ) | ( PostNotFoundError ) | ( UnauthenticatedError ) | ( UnauthorizedError ) | ( UserNotAuthorizedAdminError ) | ( UserNotAuthorizedError ) | ( UserNotFoundError );
FieldError: ( InvalidCursor ) | ( MaximumLengthError ) | ( MaximumValueError ) | ( MinimumLengthError ) | ( MinimumValueError );
Expand Down Expand Up @@ -3248,6 +3270,8 @@ export type ResolversTypes = {
EventVolunteerInput: EventVolunteerInput;
EventVolunteerResponse: EventVolunteerResponse;
EventWhereInput: EventWhereInput;
EventsConnection: ResolverTypeWrapper<Omit<EventsConnection, 'edges'> & { edges: Array<ResolversTypes['EventsConnectionEdge']> }>;
EventsConnectionEdge: ResolverTypeWrapper<Omit<EventsConnectionEdge, 'node'> & { node: ResolversTypes['Event'] }>;
ExtendSession: ResolverTypeWrapper<ExtendSession>;
Feedback: ResolverTypeWrapper<InterfaceFeedbackModel>;
FeedbackInput: FeedbackInput;
Expand Down Expand Up @@ -3451,6 +3475,8 @@ export type ResolversParentTypes = {
EventVolunteerGroupInput: EventVolunteerGroupInput;
EventVolunteerInput: EventVolunteerInput;
EventWhereInput: EventWhereInput;
EventsConnection: Omit<EventsConnection, 'edges'> & { edges: Array<ResolversParentTypes['EventsConnectionEdge']> };
EventsConnectionEdge: Omit<EventsConnectionEdge, 'node'> & { node: ResolversParentTypes['Event'] };
ExtendSession: ExtendSession;
Feedback: InterfaceFeedbackModel;
FeedbackInput: FeedbackInput;
Expand Down Expand Up @@ -3979,6 +4005,19 @@ export type EventVolunteerGroupResolvers<ContextType = any, ParentType extends R
__isTypeOf?: IsTypeOfResolverFn<ParentType, ContextType>;
};

export type EventsConnectionResolvers<ContextType = any, ParentType extends ResolversParentTypes['EventsConnection'] = ResolversParentTypes['EventsConnection']> = {
edges?: Resolver<Array<ResolversTypes['EventsConnectionEdge']>, ParentType, ContextType>;
pageInfo?: Resolver<ResolversTypes['DefaultConnectionPageInfo'], ParentType, ContextType>;
totalCount?: Resolver<Maybe<ResolversTypes['Int']>, ParentType, ContextType>;
__isTypeOf?: IsTypeOfResolverFn<ParentType, ContextType>;
};

export type EventsConnectionEdgeResolvers<ContextType = any, ParentType extends ResolversParentTypes['EventsConnectionEdge'] = ResolversParentTypes['EventsConnectionEdge']> = {
cursor?: Resolver<ResolversTypes['String'], ParentType, ContextType>;
node?: Resolver<ResolversTypes['Event'], ParentType, ContextType>;
__isTypeOf?: IsTypeOfResolverFn<ParentType, ContextType>;
};

export type ExtendSessionResolvers<ContextType = any, ParentType extends ResolversParentTypes['ExtendSession'] = ResolversParentTypes['ExtendSession']> = {
accessToken?: Resolver<ResolversTypes['String'], ParentType, ContextType>;
refreshToken?: Resolver<ResolversTypes['String'], ParentType, ContextType>;
Expand Down Expand Up @@ -4322,6 +4361,7 @@ export type OrganizationResolvers<ContextType = any, ParentType extends Resolver
creator?: Resolver<Maybe<ResolversTypes['User']>, ParentType, ContextType>;
customFields?: Resolver<Array<ResolversTypes['OrganizationCustomField']>, ParentType, ContextType>;
description?: Resolver<ResolversTypes['String'], ParentType, ContextType>;
events?: Resolver<Maybe<ResolversTypes['EventsConnection']>, ParentType, ContextType, Partial<OrganizationEventsArgs>>;
funds?: Resolver<Maybe<Array<Maybe<ResolversTypes['Fund']>>>, ParentType, ContextType>;
image?: Resolver<Maybe<ResolversTypes['String']>, ParentType, ContextType>;
members?: Resolver<Maybe<Array<Maybe<ResolversTypes['User']>>>, ParentType, ContextType>;
Expand Down Expand Up @@ -4741,6 +4781,8 @@ export type Resolvers<ContextType = any> = {
EventAttendee?: EventAttendeeResolvers<ContextType>;
EventVolunteer?: EventVolunteerResolvers<ContextType>;
EventVolunteerGroup?: EventVolunteerGroupResolvers<ContextType>;
EventsConnection?: EventsConnectionResolvers<ContextType>;
EventsConnectionEdge?: EventsConnectionEdgeResolvers<ContextType>;
ExtendSession?: ExtendSessionResolvers<ContextType>;
Feedback?: FeedbackResolvers<ContextType>;
FieldError?: FieldErrorResolvers<ContextType>;
Expand Down
Loading
Loading