type-graphql#Resolver TypeScript Examples
The following examples show how to use
type-graphql#Resolver.
You can vote up the ones you like or vote down the ones you don't like,
and go to the original project or source file by following the links above each example. You may check out the related API usage on the sidebar.
Example #1
Source File: plugin-newsletter.resolver.ts From Cromwell with MIT License | 6 votes |
@Resolver(PluginNewsletter)
export default class PluginNewsletterResolver {
@Authorized<TAuthRole>("administrator", 'guest')
@Query(() => [PluginNewsletter])
async pluginNewsletterExport(): Promise<PluginNewsletter[]> {
return await getManager().find(PluginNewsletter);
}
/** Restrict via decorator: */
@Authorized<TAuthRole>("administrator", 'guest')
@Query(() => String)
async pluginNewsletterStats(@Ctx() ctx: TGraphQLContext): Promise<string> {
// Or via checking manually user info: (both methods can work independently)
if (ctx.user?.role !== 'administrator')
throw new UnauthorizedException('Forbidden');
return (await getManager().find(PluginNewsletter) ?? []).length + '';
}
}
Example #2
Source File: resolver.ts From typegraphql-nestjs with MIT License | 6 votes |
@Resolver(of => Product)
export default class InventoryResolver {
@Directive(`@requires(fields: "price weight")`)
@FieldResolver(returns => Number)
async shippingEstimate(@Root() product: Product): Promise<number> {
// free for expensive items
if (product.price > 1000) {
return 0;
}
// estimate is based on weight
return product.weight * 0.5;
}
}
Example #3
Source File: image-resolver.ts From convoychat with GNU General Public License v3.0 | 6 votes |
@Resolver()
class ImageResolver {
@UseMiddleware(RateLimit({ limit: 10 }))
@Mutation(() => UploadImageOutput)
async uploadImage(
@Arg("file", () => GraphQLUpload) file: Promise<IUpload>
): Promise<UploadImageOutput> {
const { createReadStream } = await file;
const fileStream = createReadStream();
return new Promise((resolve, reject) => {
const cloudStream = cloudinary.uploader.upload_stream(
{
unique_filename: true,
folder: process.env.MEDIA_FOLDER,
},
(err, fileUploaded) => {
if (err) reject(false);
resolve({
url: fileUploaded.secure_url,
public_id: fileUploaded.public_id,
});
}
);
fileStream.pipe(cloudStream);
});
}
}
Example #4
Source File: CommentStat.resolver.ts From bouncecode-cms with GNU General Public License v3.0 | 6 votes |
@Resolver()
export class CommentStatResolver {
@Query(() => CommentStatObject, {nullable: true})
async commentStat(@Arg('where') where: CommentStatUniqueWhereInput) {
try {
const result = await getRepository(CommentStatEntity)
.createQueryBuilder('commentStat')
.where('commentStat.postId = :postId', {
postId: where.postId,
})
.getOne();
return result;
} catch (e) {
console.log(e);
return new ApolloError(e);
}
}
}
Example #5
Source File: fields.ts From backend with MIT License | 6 votes |
// eslint-disable-next-line camelcase
@Resolver(of => Certificate_of_conduct)
export class ExtendedFieldsCertificateOfConductResolver {
@FieldResolver(returns => [Student])
@Authorized(Role.ADMIN)
// eslint-disable-next-line camelcase
async student(@Root() certificate: Certificate_of_conduct) {
return await prisma.student.findUnique({
where: {
id: certificate.studentId
}
});
}
}
Example #6
Source File: auth.resolver.ts From hakka with MIT License | 6 votes |
@Resolver()
export class AuthResolver {
@Mutation((returns) => Boolean)
async signup() {
// TODO
}
@Mutation((returns) => Boolean)
async login() {
// TODO
}
}
Example #7
Source File: request.ts From opensaas with MIT License | 6 votes |
@Resolver(of => Request)
export class RequestResolver {
@Query(() => [Request])
requests(@Ctx() { tenantId }: Context) {
return Request.find({ where: { tenantId } });
}
@Mutation(() => Request)
async addRequest(@Ctx() { tenantId }: Context, @Arg('newRequestData') newRequestData: NewRequestInput): Promise<Request> {
const request = Request.create({
tenantId,
...newRequestData,
});
return await request.save();
}
@Mutation(() => Boolean)
async removeRequest(@Ctx() { tenantId }: Context, @Arg('id') id: number) {
const request = await Request.find({ where: { tenantId, id } });
await Request.remove(request);
return true;
}
}
Example #8
Source File: config.resolver.ts From liferay-grow with MIT License | 6 votes |
@Resolver()
export class ConfigResolver {
@Query(() => Configuration, { name: `getServerInfo` })
getConfig(): Configuration {
const { APP_NAME = 'Liferay Grow' } = process.env;
const { version: SERVER_VERSION } = PKG;
return {
SERVER_NAME: APP_NAME,
SERVER_VERSION,
};
}
}
Example #9
Source File: CompanyResolver.ts From type-graphql-dataloader with MIT License | 6 votes |
@Resolver((of) => Company)
export default class CompanyResolver {
@Query((returns) => [Company])
async companies(): Promise<Company[]> {
return getRepository(Company).find();
}
@FieldResolver()
@Loader<string, Chair[]>(async (ids) => {
const chairs = await getRepository(Chair).find({
where: { company: { id: In([...ids]) } },
});
const chairsById = groupBy(chairs, "companyId");
return ids.map((id) => chairsById[id] ?? []);
})
chairs(@Root() root: Company) {
return (dataloader: DataLoader<string, Chair[]>) =>
dataloader.load(root.id);
}
}
Example #10
Source File: resolvers.ts From squid with GNU General Public License v3.0 | 6 votes |
@Resolver()
export class ScalarResolver {
constructor(private tx: () => Promise<EntityManager>) {}
@Query(() => [ScalarRow])
async scalarsExtension(): Promise<ScalarRow[]> {
let em = await this.tx()
return em.find(Scalar, {
order: {
id: 'ASC'
}
})
}
}
Example #11
Source File: info.ts From Koa-GraphQL-Template with MIT License | 6 votes |
@Resolver(Info)
export class InfoResolver {
@Query(() => [Info], { nullable: true, description: '查询信息列表' })
async infos() {
return await InfoModal.find({}).sort('-meta.createdAt');
}
@Mutation(() => Info)
async saveInfo(@Arg('data') newInfo: InfoInput) {
const info = new InfoModal(newInfo);
return await info.save();
}
@Mutation(() => Number, { nullable: true })
async removeAllInfo() {
const res = await InfoModal.deleteMany({ weight: { $gte: 0 } });
return res.deletedCount || 0;
}
}
Example #12
Source File: product-showcase.resolver.ts From Cromwell with MIT License | 5 votes |
@Resolver(ProductCategory)
export default class PluginProductShowcaseResolver {
private get productRepo() { return getCustomRepository(ProductRepository) }
@Query(() => PagedProduct)
async pluginProductShowcase(@Arg("slug", { nullable: true }) slug?: string): Promise<TPagedList<TProduct>> {
logger.log('ProductShowcaseResolver::productShowcase slug:' + slug);
const timestamp = Date.now();
let products: TPagedList<TProduct> = {
elements: []
};
const settings = await getCustomRepository(PluginRepository).getPluginSettings<TSettings>('@cromwell/plugin-product-showcase');
const maxSize = settings?.size ?? 20;
if (slug) {
const product = await this.productRepo.getBySlug(slug, ['categories']);
if (!product?.id) throw new HttpException('Product with slug ' + slug + ' was not found!', HttpStatus.NOT_FOUND);
// Gather products from all related categories until reach limit (maxSize)
for (const category of product.categories ?? []) {
if (category?.id) {
const categoryProducts = await this.productRepo.getProductsFromCategory(category.id, {
pageSize: maxSize
});
if (categoryProducts?.elements && products.elements) {
for (const prod of categoryProducts.elements) {
// Differnt categories may contain same products, we don't want to duplicate them
if (products.elements.some(addedProd => addedProd.id === prod.id)) continue;
products.elements.push(prod);
if (products.elements.length >= maxSize) break;
}
}
}
if (products.elements?.length &&
products.elements?.length >= maxSize) break;
}
if (products.elements && products.elements.length < maxSize) {
(await this.productRepo.getProducts({ pageSize: maxSize }))?.elements?.forEach(prod => {
if (products.elements && products.elements.length < maxSize) {
products.elements?.push(prod);
}
})
}
} else {
products = await this.productRepo.getProducts({ pageSize: maxSize });
}
const timestamp2 = Date.now();
logger.log('ProductShowcaseResolver::productShowcase time elapsed: ' + (timestamp2 - timestamp) + 'ms');
return products;
}
}
Example #13
Source File: resolver.ts From typegraphql-nestjs with MIT License | 5 votes |
@Resolver(of => User)
export default class AccountsResolver {
@Query(returns => User)
me(): User {
return users[0];
}
}
Example #14
Source File: hello.ts From lireddit with MIT License | 5 votes |
@Resolver()
export class HelloResolver {
@Query(() => String)
hello() {
return "bye";
}
}
Example #15
Source File: UserAccount.ts From Wern-Fullstack-Template with MIT License | 5 votes |
@Resolver(UserAccount)
export class UserResolver {
@Query(() => UserAccount, { nullable: true })
user(@Arg('id') id: string) {
const user = UserAccount.findOne({ where: { id } })
if (!user) return null
return user
}
@Query(() => [UserAccount], { nullable: true })
users() {
console.log('users query reached')
const users = UserAccount.find()
if (!users) return null
return users
}
@Mutation(() => UserResponse)
async createUser(@Arg('options') options: UserInput): Promise<UserResponse> {
const errors = validateRegister(options)
if (errors) {
return { errors }
}
const hashedPassword = await argon2.hash(options.password)
let user
try {
const result = await getConnection()
.createQueryBuilder()
.insert()
.into(UserAccount)
.values({
username: options.username,
email: options.email,
password: hashedPassword,
})
.returning('*')
.execute()
user = result.raw[0]
} catch (err) {
if (err.code === '23505') {
return {
errors: [
{
field: 'username',
message: 'username already taken',
},
],
}
}
}
return { user }
}
}
Example #16
Source File: fields.ts From backend with MIT License | 5 votes |
@Resolver(of => GraphQLModel.Participation_certificate)
export class ExtendedFieldsParticipationCertificateResolver {
@FieldResolver(returns => [String])
@Authorized(Role.ADMIN, Role.STUDENT)
subjectsFormatted(@Root() certificate: ParticipationCertificate) {
return certificate.subjects.split(",");
}
}
Example #17
Source File: author.resolver.ts From mikro-orm-graphql-example with MIT License | 5 votes |
@Resolver(() => Author)
export class AuthorResolver {
@Query(() => [Author])
public async getAuthors(@Ctx() ctx: MyContext, @Info() info: GraphQLResolveInfo): Promise<Author[]> {
const relationPaths = fieldsToRelations(info);
return ctx.em.getRepository(Author).findAll(relationPaths);
}
@Query(() => Author, { nullable: true })
public async getAuthor(
@Arg('id') id: string,
@Ctx() ctx: MyContext,
@Info() info: GraphQLResolveInfo,
): Promise<Author | null> {
const relationPaths = fieldsToRelations(info);
return ctx.em.getRepository(Author).findOne({ id }, relationPaths);
}
@Mutation(() => Author)
public async addAuthor(@Arg('input') input: AuthorValidator, @Ctx() ctx: MyContext): Promise<Author> {
const author = new Author(input);
await ctx.em.persist(author).flush();
return author;
}
@Mutation(() => Author)
public async updateAuthor(
@Arg('input') input: AuthorValidator,
@Arg('id') id: string,
@Ctx() ctx: MyContext,
@Info() info: GraphQLResolveInfo,
): Promise<Author> {
const relationPaths = fieldsToRelations(info);
const author = await ctx.em.getRepository(Author).findOneOrFail({ id }, relationPaths);
author.assign(input);
await ctx.em.persist(author).flush();
return author;
}
@Mutation(() => Boolean)
public async deleteAuthor(@Arg('id') id: string, @Ctx() ctx: MyContext): Promise<boolean> {
const author = await ctx.em.getRepository(Author).findOneOrFail({ id });
await ctx.em.getRepository(Author).remove(author).flush();
return true;
}
}
Example #18
Source File: notification.resolver.ts From hakka with MIT License | 5 votes |
@Resolver((of) => Notification)
export class NotificationResolver {
@Query((returns) => [Notification])
async notifications(@GqlContext() ctx: Context) {
const user = requireAuth(ctx)
const notifications = await prisma.notification.findMany({
where: {
userId: user.id,
},
take: 100,
orderBy: {
createdAt: 'desc',
},
})
return notifications
}
@Query((returns) => Int)
async notificationsCount(@GqlContext() ctx: Context) {
const user = requireAuth(ctx)
const count = await prisma.notification.count({
where: {
userId: user.id,
isRead: null,
},
})
return count
}
@FieldResolver((returns) => ResolvedDataUnion)
async resolvedData(@Root() notification: Notification) {
const { data } = notification
if (data.type === 'topic-comment') {
const comment = await prisma.comment.findUnique({
where: {
id: data.commentId,
},
})
return { type: data.type, comment }
}
if (data.type === 'comment-reply') {
const replyComment = await prisma.comment.findUnique({
where: {
id: data.replyCommentId,
},
})
return { type: data.type, replyComment }
}
}
@Mutation((returns) => Boolean)
async markAllNotificationsAsRead(@GqlContext() ctx: Context) {
const user = requireAuth(ctx)
await prisma.notification.updateMany({
where: {
userId: user.id,
},
data: {
isRead: true,
},
})
return true
}
}
Example #19
Source File: auth.resolver.ts From liferay-grow with MIT License | 5 votes |
@Resolver(Profile)
export class AuthResolver {
@Mutation(() => String, { name: 'authGithub' })
async authGithub(@Arg('code') code: string): Promise<string> {
const githubUser = await getGithubUser(code);
const {
avatar_url,
email,
id: github_id,
location,
login: github_login,
name,
} = githubUser;
const profile = await Profile.findOne({
relations: ['user', 'user.growMap'],
where: {
github_login,
},
});
let token = '';
if (profile) {
token = await assignToken(profile);
} else {
if (VALIDATE_LIFERAY_ORG) {
const isLiferayMember = await belongsToLiferayOrg(github_login);
if (!isLiferayMember) {
logger.error(`${github_login} is not a liferay member`);
throw new Error('not-a-liferay-member');
}
}
const newUser = await User.create().save();
const newProfile = await Profile.create({
avatar_url,
email,
github_id,
github_login,
location,
name,
user: newUser,
}).save();
newUser.profile = newProfile;
token = await assignToken(newProfile);
await newUser.save();
}
logger.info(`Token generated for ${github_login}`);
return token;
}
}
Example #20
Source File: testSchemaTypeGraphql.ts From ra-data-prisma with MIT License | 5 votes |
@Resolver(() => User)
class CustomUserResolver {
@FieldResolver((type) => Address, { nullable: true })
address(user: User): Address | undefined {
return user.address as unknown as Address;
}
}
Example #21
Source File: CertResolver.ts From type-graphql-dataloader with MIT License | 5 votes |
@Resolver((of) => Cert)
export default class CertResolver {
@Query((returns) => [Cert])
async certs(): Promise<Cert[]> {
return getRepository(Cert).find();
}
}
Example #22
Source File: create-generic-entity.ts From Cromwell with MIT License | 4 votes |
createGenericEntity = <EntityType, EntityInputType = EntityType>(entityName: string,
EntityClass: new (...args: any[]) => EntityType,
InputEntityClass?: new (...args: any[]) => EntityInputType) => {
@EntityRepository(EntityClass)
class GenericRepository extends BaseRepository<EntityType, EntityInputType> {
constructor() {
super(EntityClass)
}
}
@ObjectType(`Paged${entityName}`)
class PagedEntity implements TPagedList<EntityType> {
@Field(() => PagedMeta, { nullable: true })
pagedMeta?: PagedMeta;
@Field(() => [EntityClass], { nullable: true })
elements?: EntityType[];
}
@ArgsType()
class CreateArgs {
@Field(() => InputEntityClass ?? String)
data: EntityInputType;
}
@ArgsType()
class UpdateArgs {
@Field(() => Int)
id: number;
@Field(() => InputEntityClass ?? String)
data: EntityInputType;
}
const getPagedPath = GraphQLPaths.Generic.getManyPaged + entityName;
const getAllPath = GraphQLPaths.Generic.getMany + entityName;
const getBySlugPath = GraphQLPaths.Generic.getOneBySlug + entityName;
const getByIdPath = GraphQLPaths.Generic.getOneById + entityName;
const createPath = GraphQLPaths.Generic.create + entityName;
const updatePath = GraphQLPaths.Generic.update + entityName;
const deletePath = GraphQLPaths.Generic.delete + entityName;
const getFilteredPath = GraphQLPaths.Generic.getFiltered + entityName;
@Resolver(EntityClass, { isAbstract: true })
abstract class GenericResolver {
private repository = getCustomRepository(GenericRepository)
@Authorized<TAuthRole>("administrator", "guest", "author")
@Query(() => PagedEntity)
async [getPagedPath](@Arg("pagedParams") pagedParams: PagedParamsInput<EntityType>):
Promise<TPagedList<EntityType>> {
return this.repository.getPaged(pagedParams);
}
@Authorized<TAuthRole>("administrator", "guest", "author")
@Query(() => [EntityClass])
async [getAllPath](): Promise<EntityType[]> {
return this.repository.getAll();
}
@Authorized<TAuthRole>("administrator", "guest", "author")
@Query(() => EntityClass)
async [getBySlugPath](@Arg("slug") slug: string): Promise<EntityType | undefined> {
return this.repository.getBySlug(slug);
}
@Authorized<TAuthRole>("administrator", "guest", "author")
@Query(() => EntityClass)
async [getByIdPath](@Arg("id", () => Int) id: number): Promise<EntityType | undefined> {
return this.repository.getById(id);
}
@Authorized<TAuthRole>("administrator", "guest", "author")
@Query(() => PagedEntity)
async [getFilteredPath](
@Arg("pagedParams", () => PagedParamsInput, { nullable: true }) pagedParams?: PagedParamsInput<EntityType>,
@Arg("filterParams", () => BaseFilterInput, { nullable: true }) filterParams?: BaseFilterInput,
): Promise<TPagedList<EntityType> | undefined> {
return this.repository.getFilteredEntities(pagedParams, filterParams);
}
@Authorized<TAuthRole>("administrator")
@Mutation(() => EntityClass)
async [createPath](@Args() { data }: CreateArgs): Promise<EntityType> {
return this.repository.createEntity(data);
}
@Authorized<TAuthRole>("administrator")
@Mutation(() => EntityClass)
async [updatePath](@Args() { id, data }: UpdateArgs): Promise<EntityType> {
return this.repository.updateEntity(id, data);
}
@Authorized<TAuthRole>("administrator")
@Mutation(() => Boolean)
async [deletePath](@Arg("id", () => Int) id: number): Promise<boolean> {
return this.repository.deleteEntity(id);
}
}
return {
abstractResolver: GenericResolver as any,
repository: GenericRepository as TObjectType<BaseRepository<EntityType, EntityInputType>>,
pagedEntity: PagedEntity as any,
createArgs: CreateArgs as any,
updateArgs: UpdateArgs as any,
}
}
Example #23
Source File: invitation-resolver.ts From convoychat with GNU General Public License v3.0 | 4 votes |
@Resolver(() => Invitation)
class InvitationResolver {
@Authorized()
@Query(() => InvitationDetails)
async getInvitationInfo(
@Arg("token", { nullable: false }) token: string,
@Ctx() context: Context
) {
const invite = await InvitationModel.findOne({
token: token,
})
.populate("roomId")
.populate("invitedBy");
if (!invite) {
throw new ApolloError("Could not get invitation info");
}
const userid = new ObjectID(context.currentUser.id);
const currentUserInvite = userid.equals(invite.userId as ObjectId);
if (!currentUserInvite && !invite.isPublic) {
throw new ApolloError("Could not get invitation info");
}
return {
id: invite.id,
room: invite.roomId,
invitedBy: invite.invitedBy,
isPublic: invite.isPublic,
createdAt: invite.createdAt,
};
}
@Authorized()
@Mutation(() => InvitationLinkResult)
async createInvitationLink(
@Arg("roomId", { nullable: false }) roomId: ObjectID,
@Ctx() context: Context
) {
const existingInvitation = await InvitationModel.findOne({
roomId: roomId,
invitedBy: context.currentUser.id,
isPublic: true,
});
const baseURL =
process.env.NODE_ENV !== "production"
? "http://localhost:3000"
: "https://convoychat.herokuapp.com";
if (existingInvitation) {
return {
link: `${baseURL}/invitation/${existingInvitation.token}`,
};
}
const validRoom = await RoomModel.findOne({ _id: roomId });
if (!validRoom) throw new Error("Not a valid room");
// TODO: add expiry time in invitation token
const token = crypto.randomBytes(16).toString("hex");
const invite = new InvitationModel({
isPublic: true,
roomId: roomId,
invitedBy: context.currentUser.id,
token: token,
});
await invite.save();
return { link: `${baseURL}/invitation/${token}` };
}
@Authorized()
@UseMiddleware(RateLimit({ limit: 15 }))
@Mutation(() => [Invitation])
async inviteMembers(
@Args() { roomId, members }: inviteMembersArgs,
@Ctx() context: Context
) {
try {
// check if user is a memeber of the specified room
const user = await UserModel.findOne({
_id: context.currentUser.id,
rooms: { $in: [roomId] },
});
if (!user) {
throw new ApolloError(
"You are not a member of room, Cannot invite members"
);
}
let token = null;
// create invitations
const invitations = members.map(memberId => {
token = crypto.randomBytes(16).toString("hex");
const invite = new InvitationModel({
roomId: roomId,
userId: memberId,
invitedBy: context.currentUser.id,
token: token,
});
return invite.save();
});
// @ts-ignore
const savedInvites: Invitation[] = await Promise.all(invitations);
const foundRoom = await RoomModel.findOne({ _id: roomId });
// send notification
const notifications = members.map(async (id, index) => {
return sendNotification({
context: context,
sender: context.currentUser.id,
receiver: id,
type: NOTIFICATION_TYPE.INVITATION,
payload: {
userId: id,
roomName: foundRoom.name,
roomId: roomId,
invitedBy: context.currentUser.id,
token: savedInvites[index].token,
},
});
});
await Promise.all(notifications);
// TODO: Send Email invitations
return savedInvites;
} catch (err) {
console.log(err);
throw new ApolloError(err);
}
}
@Authorized()
@Mutation(() => Boolean)
async acceptInvitation(
@Arg("token", { nullable: false }) token: string,
@Ctx() context: Context
) {
// find invitation with token & userId
const invitation = await InvitationModel.findOne({
token: token,
});
if (!invitation) throw new ApolloError("Invalid Invitation");
let userToAdd: Ref<User> = null;
// if invitation is public add the current user
if (invitation.isPublic === true) {
userToAdd = context.currentUser.id;
}
// if invitation is not public add the invitation.userId
if (
invitation.isPublic === false &&
`${invitation.userId}` === `${context.currentUser.id}`
) {
userToAdd = invitation.userId;
}
if (!userToAdd) {
throw new ApolloError("Something went wrong while accepting invite");
}
// throw error and delete the invitation if maximum uses is reached
if (invitation.uses.length >= invitation.maxUses) {
await InvitationModel.findOneAndRemove({
token: token,
});
throw new ApolloError("Maximum invitation usage limit exceeded");
}
// add user to the room
const room = await RoomModel.findOneAndUpdate(
{ _id: invitation.roomId },
{ $addToSet: { members: userToAdd } },
{ new: true }
);
if (!room) throw new ApolloError("Could not add members to room");
// update user.rooms
await UserModel.update(
{ _id: userToAdd },
{ $addToSet: { rooms: room.id } },
{ new: true }
);
// delete the notification
if (!invitation.isPublic) {
await InvitationModel.findOneAndRemove({ token: token });
} else {
await InvitationModel.findOneAndUpdate(
{ token: token },
{ $addToSet: { uses: userToAdd } }
);
}
return true;
}
}
Example #24
Source File: post.ts From lireddit with MIT License | 4 votes |
@Resolver(Post)
export class PostResolver {
@FieldResolver(() => String)
textSnippet(@Root() post: Post) {
return post.text.slice(0, 50);
}
@FieldResolver(() => User)
creator(@Root() post: Post, @Ctx() { userLoader }: MyContext) {
return userLoader.load(post.creatorId);
}
@FieldResolver(() => Int, { nullable: true })
async voteStatus(
@Root() post: Post,
@Ctx() { updootLoader, req }: MyContext
) {
if (!req.session.userId) {
return null;
}
const updoot = await updootLoader.load({
postId: post.id,
userId: req.session.userId,
});
return updoot ? updoot.value : null;
}
@Mutation(() => Boolean)
@UseMiddleware(isAuth)
async vote(
@Arg("postId", () => Int) postId: number,
@Arg("value", () => Int) value: number,
@Ctx() { req }: MyContext
) {
const isUpdoot = value !== -1;
const realValue = isUpdoot ? 1 : -1;
const { userId } = req.session;
const updoot = await Updoot.findOne({ where: { postId, userId } });
// the user has voted on the post before
// and they are changing their vote
if (updoot && updoot.value !== realValue) {
await getConnection().transaction(async (tm) => {
await tm.query(
`
update updoot
set value = $1
where "postId" = $2 and "userId" = $3
`,
[realValue, postId, userId]
);
await tm.query(
`
update post
set points = points + $1
where id = $2
`,
[2 * realValue, postId]
);
});
} else if (!updoot) {
// has never voted before
await getConnection().transaction(async (tm) => {
await tm.query(
`
insert into updoot ("userId", "postId", value)
values ($1, $2, $3)
`,
[userId, postId, realValue]
);
await tm.query(
`
update post
set points = points + $1
where id = $2
`,
[realValue, postId]
);
});
}
return true;
}
@Query(() => PaginatedPosts)
async posts(
@Arg("limit", () => Int) limit: number,
@Arg("cursor", () => String, { nullable: true }) cursor: string | null
): Promise<PaginatedPosts> {
// 20 -> 21
const realLimit = Math.min(50, limit);
const reaLimitPlusOne = realLimit + 1;
const replacements: any[] = [reaLimitPlusOne];
if (cursor) {
replacements.push(new Date(parseInt(cursor)));
}
const posts = await getConnection().query(
`
select p.*
from post p
${cursor ? `where p."createdAt" < $2` : ""}
order by p."createdAt" DESC
limit $1
`,
replacements
);
// const qb = getConnection()
// .getRepository(Post)
// .createQueryBuilder("p")
// .innerJoinAndSelect("p.creator", "u", 'u.id = p."creatorId"')
// .orderBy('p."createdAt"', "DESC")
// .take(reaLimitPlusOne);
// if (cursor) {
// qb.where('p."createdAt" < :cursor', {
// cursor: new Date(parseInt(cursor)),
// });
// }
// const posts = await qb.getMany();
// console.log("posts: ", posts);
return {
posts: posts.slice(0, realLimit),
hasMore: posts.length === reaLimitPlusOne,
};
}
@Query(() => Post, { nullable: true })
post(@Arg("id", () => Int) id: number): Promise<Post | undefined> {
return Post.findOne(id);
}
@Mutation(() => Post)
@UseMiddleware(isAuth)
async createPost(
@Arg("input") input: PostInput,
@Ctx() { req }: MyContext
): Promise<Post> {
return Post.create({
...input,
creatorId: req.session.userId,
}).save();
}
@Mutation(() => Post, { nullable: true })
@UseMiddleware(isAuth)
async updatePost(
@Arg("id", () => Int) id: number,
@Arg("title") title: string,
@Arg("text") text: string,
@Ctx() { req }: MyContext
): Promise<Post | null> {
const result = await getConnection()
.createQueryBuilder()
.update(Post)
.set({ title, text })
.where('id = :id and "creatorId" = :creatorId', {
id,
creatorId: req.session.userId,
})
.returning("*")
.execute();
return result.raw[0];
}
@Mutation(() => Boolean)
@UseMiddleware(isAuth)
async deletePost(
@Arg("id", () => Int) id: number,
@Ctx() { req }: MyContext
): Promise<boolean> {
// not cascade way
// const post = await Post.findOne(id);
// if (!post) {
// return false;
// }
// if (post.creatorId !== req.session.userId) {
// throw new Error("not authorized");
// }
// await Updoot.delete({ postId: id });
// await Post.delete({ id });
await Post.delete({ id, creatorId: req.session.userId });
return true;
}
}
Example #25
Source File: ChatRoom.resolver.ts From bouncecode-cms with GNU General Public License v3.0 | 4 votes |
@Resolver()
export class ChatRoomResolver {
@Query(() => [ChatRoomObject])
async chatRooms(
@Arg('where') where: ChatRoomWhereInput,
@Arg('skip', {nullable: true}) skip: number = 0,
@Arg('take', {nullable: true}) take: number = 10,
@Ctx() ctx: Context,
) {
const userId = ctx.user.id;
const queryBuilder = getRepository(UserEntity)
.createQueryBuilder('user')
.leftJoinAndSelect('user.chatRooms', 'chatRooms')
.andWhere('user.id = :userId', {
userId: userId,
});
if (where.category) {
queryBuilder
.leftJoinAndSelect('chatRooms.category', 'category')
.andWhere('category.id = :categoryId', {
categoryId: where.category,
});
}
const result = await queryBuilder
.offset(skip)
.limit(take)
.getMany();
console.log(result);
return result;
}
@Mutation(() => Boolean)
async createChatRoom(@Arg('data') data: ChatRoomCreateInput) {
const queryBuilder = await getConnection()
.createQueryBuilder()
.insert()
.into(ChatRoomEntity)
.values([data])
.execute();
console.log(queryBuilder);
return true;
}
@Mutation(() => Boolean)
async updateChatRoom(
@Arg('where') where: ChatRoomUniqueWhereInput,
@Arg('data') data: ChatRoomUpdateInput,
) {
const queryBuilder = await getConnection()
.createQueryBuilder()
.update(ChatRoomEntity)
.set(data)
.where('id = :id', {id: where.id})
.execute();
console.log(queryBuilder);
return true;
}
@Mutation(() => Boolean)
async deleteChatRoom(@Arg('where') where: ChatRoomUniqueWhereInput) {
const queryBuilder = await getConnection()
.createQueryBuilder()
.delete()
.from(ChatRoomEntity)
.where('id = :id', {id: where.id})
.execute();
console.log(queryBuilder);
return true;
}
}
Example #26
Source File: authentication.ts From backend with MIT License | 4 votes |
@Resolver(of => Me)
export class AuthenticationResolver {
@Authorized(Role.UNAUTHENTICATED)
@Mutation(returns => Boolean)
@Deprecated("use loginPassword or loginToken instead")
async loginLegacy(@Ctx() context: GraphQLContext, @Arg("authToken") authToken: string) {
ensureSession(context);
const logger = logInContext(`GraphQL Authentication`, context);
const pupil = await prisma.pupil.findFirst({
where: {
// This drops support for unhashed tokens as present in the REST authentication
authToken: hashToken(authToken),
active: true
}
});
if (pupil) {
if (!pupil.verifiedAt) {
/* Previously there was an extra database field for verifying the E-Mail.
I do not see the purpose of that, as presenting a valid authToken is also proof that the account exists.
This can co-exist with the current "verification" implementation.
TODO: Drop the verification column once we moved to GraphQL on the frontend */
logger.info(`Pupil(${pupil.id}) did not verify their e-mail yet, but presented legacy token (thus proved their ownership)`);
await prisma.pupil.update({
data: {
verification: null,
verifiedAt: new Date()
},
where: { id: pupil.id }
});
}
await loginAsUser(userForPupil(pupil), context);
return true;
}
const student = await prisma.student.findFirst({
where: {
authToken: hashToken(authToken),
active: true
}
});
if (student) {
if (!student.verifiedAt) {
/* Previously there was an extra database field for verifying the E-Mail.
I do not see the purpose of that, as presenting a valid authToken is also proof that the account exists.
This can co-exist with the current "verification" implementation.
TODO: Drop the verification column once we moved to GraphQL on the frontend */
logger.info(`Student(${student.id}) did not verify their e-mail yet, but presented legacy token (thus proved their ownership)`);
await prisma.student.update({
data: {
verification: null,
verifiedAt: new Date()
},
where: { id: student.id }
});
}
await loginAsUser(userForStudent(student), context);
return true;
}
logger.warn(`Invalid authToken`);
throw new AuthenticationError("Invalid authToken");
}
@Authorized(Role.UNAUTHENTICATED)
@Mutation(returns => Boolean)
@Deprecated("Use loginPassword instead")
async loginPasswordLegacy(@Ctx() context: GraphQLContext, @Arg("email") email: string, @Arg("password") password: string) {
ensureSession(context);
const logger = logInContext(`GraphQL Authentication`, context);
const screener = await prisma.screener.findFirst({
where: {
email,
active: true
}
});
const passwordValid = screener && await verifyPassword(password, screener.password);
if (!screener || !passwordValid) {
logger.warn(`Invalid email (${email}) or password`);
throw new AuthenticationError("Invalid email or password");
}
await loginAsUser(userForScreener(screener), context);
return true;
}
@Authorized(Role.UNAUTHENTICATED)
@Mutation(returns => Boolean)
async loginPassword(@Ctx() context: GraphQLContext, @Arg("email") email: string, @Arg("password") password: string) {
try {
const user = await loginPassword(email, password);
await loginAsUser(user, context);
return true;
} catch (error) {
throw new AuthenticationError("Invalid E-Mail or Password");
}
}
@Authorized(Role.UNAUTHENTICATED)
@Mutation(returns => Boolean)
async loginToken(@Ctx() context: GraphQLContext, @Arg("token") token: string) {
try {
const user = await loginToken(token);
await loginAsUser(user, context);
return true;
} catch (error) {
throw new AuthenticationError("Invalid Token");
}
}
@Authorized(Role.USER)
@Mutation(returns => Boolean)
logout(@Ctx() context: GraphQLContext) {
ensureSession(context);
const logger = logInContext(`GraphQL Authentication`, context);
if (!context.user) {
throw new ForbiddenError("User already logged out");
}
const deleted = userSessions.delete(context.sessionToken);
assert(deleted, "User session is successfully deleted");
context.user = undefined;
logger.info(`Successfully logged out`);
return true;
}
}
Example #27
Source File: comment.resolver.ts From hakka with MIT License | 4 votes |
@Resolver((of) => Comment)
export class CommentResolver {
@Query((returns) => CommentsConnection)
async comments(@Args() args: CommentsArgs) {
const skip = (args.page - 1) * args.take
const comments = await prisma.comment.findMany({
where: {
topicId: args.topicId,
},
take: args.take + 1,
skip,
orderBy: {
createdAt: args.order,
},
})
const count = await prisma.comment.count({
where: {
topicId: args.topicId,
},
})
return {
items: comments.slice(0, args.take),
hasNext: comments.length > args.take,
hasPrev: args.page > 1,
total: count,
}
}
@Mutation((returns) => Comment)
async createComment(
@GqlContext() ctx: Context,
@Args() args: CreateCommentArgs,
) {
const user = requireAuth(ctx)
const comment = await prisma.comment.create({
data: {
topic: {
connect: {
id: args.topicId,
},
},
content: args.content,
author: {
connect: {
id: user.id,
},
},
parent: args.parentId
? {
connect: {
id: args.parentId,
},
}
: undefined,
},
})
await prisma.topic.update({
where: {
id: args.topicId,
},
data: {
lastComment: {
connect: {
id: comment.id,
},
},
},
})
// Add notification
notificationQueue.add({ commentId: comment.id })
return comment
}
@FieldResolver((returns) => String)
html(@Root() comment: Comment) {
const html = renderMarkdown(comment.content)
return html
}
@FieldResolver((returns) => UserPublicInfo)
async author(@Root() comment: Comment) {
const author = await prisma.user.findUnique({
where: {
id: comment.authorId,
},
})
return author
}
@FieldResolver((returns) => Comment, {
nullable: true,
})
async parent(@Root() comment: Comment) {
const parentComment =
comment.parentId &&
(await prisma.comment.findUnique({
where: {
id: comment.parentId,
},
}))
return parentComment
}
@FieldResolver((returns) => Topic)
async topic(@Root() comment: Comment) {
const topic = await prisma.topic.findUnique({
where: {
id: comment.topicId,
},
})
return topic
}
@FieldResolver((returns) => Int)
async likesCount(@Root() comment: Comment) {
const count = await prisma.userCommentLike.count({
where: {
commentId: comment.id,
},
})
return count
}
@FieldResolver((returns) => Boolean)
async isLiked(@GqlContext() ctx: Context, @Root() comment: Comment) {
if (!ctx.user) {
return false
}
const record = await prisma.userCommentLike.findFirst({
where: {
userId: ctx.user.id,
commentId: comment.id,
},
})
return Boolean(record)
}
@Mutation((returns) => Boolean)
async likeComment(@GqlContext() ctx: Context, @Args() args: LikeCommentArgs) {
const user = requireAuth(ctx)
const comment = await prisma.comment.findUnique({
where: {
id: args.commentId,
},
})
if (!comment) throw new ApolloError(`comment was not found`)
let userCommentLike = await prisma.userCommentLike.findFirst({
where: {
commentId: comment.id,
userId: user.id,
},
})
let liked = false
if (userCommentLike) {
await prisma.userCommentLike.delete({
where: {
id: userCommentLike.id,
},
})
} else {
userCommentLike = await prisma.userCommentLike.create({
data: {
commentId: comment.id,
userId: user.id,
},
})
liked = true
}
return liked
}
}