add missing resolvers
This commit is contained in:
parent
eda8a3920c
commit
cbd4691704
11
src/dto/deleted.model.ts
Normal file
11
src/dto/deleted.model.ts
Normal file
@ -0,0 +1,11 @@
|
||||
import { Field, ObjectType } from '@nestjs/graphql';
|
||||
|
||||
@ObjectType('Deleted')
|
||||
export class DeletedModel {
|
||||
@Field()
|
||||
id: string
|
||||
|
||||
constructor(id: string) {
|
||||
this.id = id
|
||||
}
|
||||
}
|
||||
6
src/dto/form/form.statistic.model.ts
Normal file
6
src/dto/form/form.statistic.model.ts
Normal file
@ -0,0 +1,6 @@
|
||||
import { ObjectType } from '@nestjs/graphql';
|
||||
|
||||
@ObjectType('FormStatistic')
|
||||
export class FormStatisticModel {
|
||||
|
||||
}
|
||||
@ -1,9 +1,9 @@
|
||||
import { Field, ObjectType } from '@nestjs/graphql';
|
||||
import { UserDocument } from '../../schema/user.schema';
|
||||
import { UserModel } from './user.model';
|
||||
import { UserModel } from '../user/user.model';
|
||||
|
||||
@ObjectType('OwnUser')
|
||||
export class OwnUserModel extends UserModel {
|
||||
@ObjectType('Profile')
|
||||
export class ProfileModel extends UserModel {
|
||||
@Field(() => [String])
|
||||
readonly roles: string[]
|
||||
|
||||
25
src/dto/profile/profile.update.input.ts
Normal file
25
src/dto/profile/profile.update.input.ts
Normal file
@ -0,0 +1,25 @@
|
||||
import { Field, ID, InputType } from '@nestjs/graphql';
|
||||
|
||||
@InputType()
|
||||
export class ProfileUpdateInput {
|
||||
@Field(() => ID)
|
||||
readonly id: string
|
||||
|
||||
@Field({ nullable: true })
|
||||
readonly username: string
|
||||
|
||||
@Field({ nullable: true })
|
||||
readonly email: string
|
||||
|
||||
@Field({ nullable: true })
|
||||
readonly firstName: string
|
||||
|
||||
@Field({ nullable: true })
|
||||
readonly lastName: string
|
||||
|
||||
@Field({ nullable: true })
|
||||
readonly password: string
|
||||
|
||||
@Field({ nullable: true })
|
||||
readonly language: string
|
||||
}
|
||||
6
src/dto/submission/submission.statistic.model.ts
Normal file
6
src/dto/submission/submission.statistic.model.ts
Normal file
@ -0,0 +1,6 @@
|
||||
import { ObjectType } from '@nestjs/graphql';
|
||||
|
||||
@ObjectType('SubmissionStatistic')
|
||||
export class SubmissionStatisticModel {
|
||||
|
||||
}
|
||||
25
src/dto/user/pager.user.model.ts
Normal file
25
src/dto/user/pager.user.model.ts
Normal file
@ -0,0 +1,25 @@
|
||||
import { Field, ObjectType } from '@nestjs/graphql';
|
||||
import { GraphQLInt } from 'graphql';
|
||||
import { UserModel } from './user.model';
|
||||
|
||||
@ObjectType('PagerUser')
|
||||
export class PagerUserModel {
|
||||
@Field(() => [UserModel])
|
||||
entries: UserModel[]
|
||||
|
||||
@Field(() => GraphQLInt)
|
||||
total: number
|
||||
|
||||
@Field(() => GraphQLInt)
|
||||
limit: number
|
||||
|
||||
@Field(() => GraphQLInt)
|
||||
start: number
|
||||
|
||||
constructor(entries: UserModel[], total: number, limit: number, start: number) {
|
||||
this.entries = entries
|
||||
this.total = total
|
||||
this.limit = limit
|
||||
this.start = start
|
||||
}
|
||||
}
|
||||
@ -6,6 +6,9 @@ export class UserModel {
|
||||
@Field(() => ID)
|
||||
readonly id: string
|
||||
|
||||
@Field()
|
||||
readonly verifiedEmail: boolean
|
||||
|
||||
@Field()
|
||||
readonly username: string
|
||||
|
||||
@ -15,12 +18,18 @@ export class UserModel {
|
||||
@Field()
|
||||
readonly language: string
|
||||
|
||||
@Field()
|
||||
@Field({ nullable: true })
|
||||
readonly firstName?: string
|
||||
|
||||
@Field()
|
||||
@Field({ nullable: true })
|
||||
readonly lastName?: string
|
||||
|
||||
@Field()
|
||||
readonly created: Date
|
||||
|
||||
@Field({ nullable: true })
|
||||
readonly lastModified: Date
|
||||
|
||||
constructor(user: UserDocument) {
|
||||
this.id = user.id
|
||||
this.username = user.username
|
||||
@ -29,5 +38,10 @@ export class UserModel {
|
||||
this.language = user.language
|
||||
this.firstName = user.firstName
|
||||
this.lastName = user.lastName
|
||||
|
||||
this.verifiedEmail = !user.token
|
||||
|
||||
this.created = user.created
|
||||
this.lastModified = user.lastModified
|
||||
}
|
||||
}
|
||||
|
||||
6
src/dto/user/user.statistic.model.ts
Normal file
6
src/dto/user/user.statistic.model.ts
Normal file
@ -0,0 +1,6 @@
|
||||
import { ObjectType } from '@nestjs/graphql';
|
||||
|
||||
@ObjectType('UserStatistic')
|
||||
export class UserStatisticModel {
|
||||
|
||||
}
|
||||
29
src/dto/user/user.update.input.ts
Normal file
29
src/dto/user/user.update.input.ts
Normal file
@ -0,0 +1,29 @@
|
||||
import { Field, ID, InputType } from '@nestjs/graphql';
|
||||
import { GraphQLString } from 'graphql';
|
||||
|
||||
@InputType()
|
||||
export class UserUpdateInput {
|
||||
@Field(() => ID)
|
||||
readonly id: string
|
||||
|
||||
@Field({ nullable: true })
|
||||
readonly username: string
|
||||
|
||||
@Field({ nullable: true })
|
||||
readonly email: string
|
||||
|
||||
@Field({ nullable: true })
|
||||
readonly firstName: string
|
||||
|
||||
@Field({ nullable: true })
|
||||
readonly lastName: string
|
||||
|
||||
@Field({ nullable: true })
|
||||
readonly password: string
|
||||
|
||||
@Field(() => [GraphQLString], { nullable: true })
|
||||
readonly roles: string[]
|
||||
|
||||
@Field({ nullable: true })
|
||||
readonly language: string
|
||||
}
|
||||
@ -1,5 +1,6 @@
|
||||
import { FormFieldDocument } from '../schema/form.field.schema';
|
||||
import { FormDocument } from '../schema/form.schema';
|
||||
import { SubmissionFieldDocument } from '../schema/submission.field.schema';
|
||||
import { SubmissionDocument } from '../schema/submission.schema';
|
||||
import { UserDocument } from '../schema/user.schema';
|
||||
|
||||
@ -16,7 +17,11 @@ export class ContextCache {
|
||||
[id: string]: SubmissionDocument,
|
||||
} = {}
|
||||
|
||||
private formField: {
|
||||
private submissionFields: {
|
||||
[id: string]: SubmissionFieldDocument,
|
||||
} = {}
|
||||
|
||||
private formFields: {
|
||||
[id: string]: FormFieldDocument,
|
||||
} = {}
|
||||
|
||||
@ -44,11 +49,19 @@ export class ContextCache {
|
||||
return this.submissions[id]
|
||||
}
|
||||
|
||||
public addFormField(formField: FormFieldDocument) {
|
||||
this.formField[formField.id] = formField
|
||||
public addFormField(field: FormFieldDocument) {
|
||||
this.formFields[field.id] = field
|
||||
}
|
||||
|
||||
public async getFormField(id: any): Promise<FormFieldDocument> {
|
||||
return this.formField[id]
|
||||
return this.formFields[id]
|
||||
}
|
||||
|
||||
public addSubmissionField(field: SubmissionFieldDocument) {
|
||||
this.submissionFields[field.id] = field
|
||||
}
|
||||
|
||||
public async getSubmissionField(id: any): Promise<SubmissionFieldDocument> {
|
||||
return this.submissionFields[id]
|
||||
}
|
||||
}
|
||||
|
||||
@ -2,7 +2,7 @@ import { Injectable } from '@nestjs/common';
|
||||
import { Args, ID, Mutation } from '@nestjs/graphql';
|
||||
import { Roles } from '../../decorator/roles.decorator';
|
||||
import { User } from '../../decorator/user.decorator';
|
||||
import { FormModel } from '../../dto/form/form.model';
|
||||
import { DeletedModel } from '../../dto/deleted.model';
|
||||
import { UserDocument } from '../../schema/user.schema';
|
||||
import { FormDeleteService } from '../../service/form/form.delete.service';
|
||||
import { FormService } from '../../service/form/form.service';
|
||||
@ -15,7 +15,7 @@ export class FormDeleteMutation {
|
||||
) {
|
||||
}
|
||||
|
||||
@Mutation(() => FormModel)
|
||||
@Mutation(() => DeletedModel)
|
||||
@Roles('admin')
|
||||
async deleteForm(
|
||||
@User() user: UserDocument,
|
||||
@ -29,6 +29,6 @@ export class FormDeleteMutation {
|
||||
|
||||
await this.deleteService.delete(id)
|
||||
|
||||
return new FormModel(form)
|
||||
return new DeletedModel(id)
|
||||
}
|
||||
}
|
||||
|
||||
24
src/resolver/form/form.statistic.resolver.ts
Normal file
24
src/resolver/form/form.statistic.resolver.ts
Normal file
@ -0,0 +1,24 @@
|
||||
import { Query, ResolveField, Resolver } from '@nestjs/graphql';
|
||||
import { GraphQLInt } from 'graphql';
|
||||
import { Roles } from '../../decorator/roles.decorator';
|
||||
import { FormStatisticModel } from '../../dto/form/form.statistic.model';
|
||||
import { FormStatisticService } from '../../service/form/form.statistic.service';
|
||||
|
||||
@Resolver(() => FormStatisticModel)
|
||||
export class FormStatisticResolver {
|
||||
constructor(
|
||||
private readonly statisticService: FormStatisticService,
|
||||
) {
|
||||
}
|
||||
|
||||
@Query(() => FormStatisticModel)
|
||||
async getFormStatistic(): Promise<FormStatisticModel> {
|
||||
return new FormStatisticModel()
|
||||
}
|
||||
|
||||
@ResolveField('total', () => GraphQLInt)
|
||||
@Roles('admin')
|
||||
getTotal(): Promise<number> {
|
||||
return this.statisticService.getTotal()
|
||||
}
|
||||
}
|
||||
@ -24,13 +24,13 @@ export class FormUpdateMutation {
|
||||
@Args({ name: 'form', type: () => FormUpdateInput }) input: FormUpdateInput,
|
||||
@Context('cache') cache: ContextCache,
|
||||
): Promise<FormModel> {
|
||||
let form = await this.formService.findById(input.id)
|
||||
const form = await this.formService.findById(input.id)
|
||||
|
||||
if (!form.isLive && !await this.formService.isAdmin(form, user)) {
|
||||
throw new Error('invalid form')
|
||||
}
|
||||
|
||||
form = await this.updateService.update(form, input)
|
||||
await this.updateService.update(form, input)
|
||||
|
||||
cache.addForm(form)
|
||||
|
||||
|
||||
@ -2,12 +2,14 @@ import { FormCreateMutation } from './form.create.mutation';
|
||||
import { FormDeleteMutation } from './form.delete.mutation';
|
||||
import { FormResolver } from './form.resolver';
|
||||
import { FormSearchResolver } from './form.search.resolver';
|
||||
import { FormStatisticResolver } from './form.statistic.resolver';
|
||||
import { FormUpdateMutation } from './form.update.mutation';
|
||||
|
||||
export const formResolvers = [
|
||||
FormResolver,
|
||||
FormSearchResolver,
|
||||
FormCreateMutation,
|
||||
FormDeleteMutation,
|
||||
FormResolver,
|
||||
FormSearchResolver,
|
||||
FormStatisticResolver,
|
||||
FormUpdateMutation,
|
||||
]
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
import { authServices } from './auth';
|
||||
import { formResolvers } from './form';
|
||||
import { myResolvers } from './me';
|
||||
import { profileResolvers } from './profile';
|
||||
import { StatusResolver } from './status.resolver';
|
||||
import { submissionResolvers } from './submission';
|
||||
import { userResolvers } from './user';
|
||||
@ -9,7 +9,7 @@ export const resolvers = [
|
||||
StatusResolver,
|
||||
...userResolvers,
|
||||
...authServices,
|
||||
...myResolvers,
|
||||
...profileResolvers,
|
||||
...formResolvers,
|
||||
...submissionResolvers,
|
||||
]
|
||||
|
||||
@ -1,5 +0,0 @@
|
||||
import { ProfileResolver } from './profile.resolver';
|
||||
|
||||
export const myResolvers = [
|
||||
ProfileResolver,
|
||||
]
|
||||
7
src/resolver/profile/index.ts
Normal file
7
src/resolver/profile/index.ts
Normal file
@ -0,0 +1,7 @@
|
||||
import { ProfileResolver } from './profile.resolver';
|
||||
import { ProfileUpdateMutation } from './profile.update.mutation';
|
||||
|
||||
export const profileResolvers = [
|
||||
ProfileResolver,
|
||||
ProfileUpdateMutation,
|
||||
]
|
||||
@ -1,19 +1,19 @@
|
||||
import { Context, Query } from '@nestjs/graphql';
|
||||
import { Roles } from '../../decorator/roles.decorator';
|
||||
import { User } from '../../decorator/user.decorator';
|
||||
import { OwnUserModel } from '../../dto/user/own.user.model';
|
||||
import { ProfileModel } from '../../dto/profile/profile.model';
|
||||
import { UserDocument } from '../../schema/user.schema';
|
||||
import { ContextCache } from '../context.cache';
|
||||
|
||||
export class ProfileResolver {
|
||||
@Query(() => OwnUserModel)
|
||||
@Query(() => ProfileModel)
|
||||
@Roles('user')
|
||||
async me(
|
||||
@User() user: UserDocument,
|
||||
@Context('cache') cache: ContextCache,
|
||||
): Promise<OwnUserModel> {
|
||||
): Promise<ProfileModel> {
|
||||
cache.addUser(user)
|
||||
|
||||
return new OwnUserModel(user)
|
||||
return new ProfileModel(user)
|
||||
}
|
||||
}
|
||||
29
src/resolver/profile/profile.update.mutation.ts
Normal file
29
src/resolver/profile/profile.update.mutation.ts
Normal file
@ -0,0 +1,29 @@
|
||||
import { Injectable } from '@nestjs/common';
|
||||
import { Args, Context, Mutation } from '@nestjs/graphql';
|
||||
import { User } from '../../decorator/user.decorator';
|
||||
import { ProfileModel } from '../../dto/profile/profile.model';
|
||||
import { ProfileUpdateInput } from '../../dto/profile/profile.update.input';
|
||||
import { UserDocument } from '../../schema/user.schema';
|
||||
import { ProfileUpdateService } from '../../service/profile/profile.update.service';
|
||||
import { ContextCache } from '../context.cache';
|
||||
|
||||
@Injectable()
|
||||
export class ProfileUpdateMutation {
|
||||
constructor(
|
||||
private readonly updateService: ProfileUpdateService,
|
||||
) {
|
||||
}
|
||||
|
||||
@Mutation(() => ProfileModel)
|
||||
async updateProfile(
|
||||
@User() user: UserDocument,
|
||||
@Args({ name: 'user', type: () => ProfileUpdateInput }) input: ProfileUpdateInput,
|
||||
@Context('cache') cache: ContextCache,
|
||||
): Promise<ProfileModel> {
|
||||
await this.updateService.update(user, input)
|
||||
|
||||
cache.addUser(user)
|
||||
|
||||
return new ProfileModel(user)
|
||||
}
|
||||
}
|
||||
@ -1,13 +1,17 @@
|
||||
import { SubmissionFieldResolver } from './submission.field.resolver';
|
||||
import { SubmissionProgressResolver } from './submission.progress.resolver';
|
||||
import { SubmissionResolver } from './submission.resolver';
|
||||
import { SubmissionSearchResolver } from './submission.search.resolver';
|
||||
import { SubmissionSetFieldMutation } from './submission.set.field.mutation';
|
||||
import { SubmissionStartMutation } from './submission.start.mutation';
|
||||
import { SubmissionStatisticResolver } from './submission.statistic.resolver';
|
||||
|
||||
export const submissionResolvers = [
|
||||
SubmissionFieldResolver,
|
||||
SubmissionProgressResolver,
|
||||
SubmissionResolver,
|
||||
SubmissionSearchResolver,
|
||||
SubmissionSetFieldMutation,
|
||||
SubmissionStartMutation,
|
||||
SubmissionSearchResolver,
|
||||
SubmissionStatisticResolver,
|
||||
]
|
||||
|
||||
25
src/resolver/submission/submission.field.resolver.ts
Normal file
25
src/resolver/submission/submission.field.resolver.ts
Normal file
@ -0,0 +1,25 @@
|
||||
import { Context, Parent, ResolveField, Resolver } from '@nestjs/graphql';
|
||||
import { FormFieldModel } from '../../dto/form/form.field.model';
|
||||
import { SubmissionFieldModel } from '../../dto/submission/submission.field.model';
|
||||
import { ContextCache } from '../context.cache';
|
||||
|
||||
@Resolver(() => SubmissionFieldModel)
|
||||
export class SubmissionFieldResolver {
|
||||
@ResolveField('field', () => FormFieldModel, { nullable: true })
|
||||
async getFields(
|
||||
@Parent() parent: SubmissionFieldModel,
|
||||
@Context('cache') cache: ContextCache,
|
||||
): Promise<FormFieldModel> {
|
||||
const submissionField = await cache.getSubmissionField(parent.id)
|
||||
|
||||
console.log(submissionField.field)
|
||||
|
||||
const field = await cache.getFormField(submissionField.field)
|
||||
|
||||
if (!field) {
|
||||
return null
|
||||
}
|
||||
|
||||
return new FormFieldModel(field)
|
||||
}
|
||||
}
|
||||
@ -25,6 +25,9 @@ export class SubmissionResolver {
|
||||
cache.addFormField(field)
|
||||
})
|
||||
|
||||
return submission.fields.map(field => new SubmissionFieldModel(field))
|
||||
return submission.fields.map(field => {
|
||||
cache.addSubmissionField(field)
|
||||
return new SubmissionFieldModel(field)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
25
src/resolver/submission/submission.statistic.resolver.ts
Normal file
25
src/resolver/submission/submission.statistic.resolver.ts
Normal file
@ -0,0 +1,25 @@
|
||||
import { Query, ResolveField, Resolver } from '@nestjs/graphql';
|
||||
import { GraphQLInt } from 'graphql';
|
||||
import { Roles } from '../../decorator/roles.decorator';
|
||||
import { SubmissionStatisticModel } from '../../dto/submission/submission.statistic.model';
|
||||
import { SubmissionStatisticService } from '../../service/submission/submission.statistic.service';
|
||||
|
||||
@Resolver(() => SubmissionStatisticModel)
|
||||
export class SubmissionStatisticResolver {
|
||||
constructor(
|
||||
private readonly statisticService: SubmissionStatisticService,
|
||||
) {
|
||||
}
|
||||
|
||||
|
||||
@Query(() => SubmissionStatisticModel)
|
||||
async getSubmissionStatistic(): Promise<SubmissionStatisticModel> {
|
||||
return new SubmissionStatisticModel()
|
||||
}
|
||||
|
||||
@ResolveField('total', () => GraphQLInt)
|
||||
@Roles('admin')
|
||||
getTotal(): Promise<number> {
|
||||
return this.statisticService.getTotal()
|
||||
}
|
||||
}
|
||||
@ -1,5 +1,13 @@
|
||||
import { UserDeleteMutation } from './user.delete.mutation';
|
||||
import { UserResolver } from './user.resolver';
|
||||
import { UserSearchResolver } from './user.search.resolver';
|
||||
import { UserStatisticResolver } from './user.statistic.resolver';
|
||||
import { UserUpdateMutation } from './user.update.mutation';
|
||||
|
||||
export const userResolvers = [
|
||||
UserDeleteMutation,
|
||||
UserResolver,
|
||||
UserSearchResolver,
|
||||
UserStatisticResolver,
|
||||
UserUpdateMutation,
|
||||
]
|
||||
|
||||
30
src/resolver/user/user.delete.mutation.ts
Normal file
30
src/resolver/user/user.delete.mutation.ts
Normal file
@ -0,0 +1,30 @@
|
||||
import { Injectable } from '@nestjs/common';
|
||||
import { Args, ID, Mutation } from '@nestjs/graphql';
|
||||
import { Roles } from '../../decorator/roles.decorator';
|
||||
import { User } from '../../decorator/user.decorator';
|
||||
import { DeletedModel } from '../../dto/deleted.model';
|
||||
import { UserDocument } from '../../schema/user.schema';
|
||||
import { UserDeleteService } from '../../service/user/user.delete.service';
|
||||
|
||||
@Injectable()
|
||||
export class UserDeleteMutation {
|
||||
constructor(
|
||||
private readonly deleteService: UserDeleteService,
|
||||
) {
|
||||
}
|
||||
|
||||
@Mutation(() => DeletedModel)
|
||||
@Roles('admin')
|
||||
async deleteUser(
|
||||
@User() auth: UserDocument,
|
||||
@Args({ name: 'id', type: () => ID}) id: string,
|
||||
): Promise<DeletedModel> {
|
||||
if (auth.id === id) {
|
||||
throw new Error('cannot delete your own user')
|
||||
}
|
||||
|
||||
await this.deleteService.delete(id)
|
||||
|
||||
return new DeletedModel(id)
|
||||
}
|
||||
}
|
||||
@ -1,7 +1,8 @@
|
||||
import { Args, Context, GraphQLExecutionContext, ID, Parent, Query, ResolveField, Resolver } from '@nestjs/graphql';
|
||||
import { rolesType } from '../../config/roles';
|
||||
import { Args, Context, ID, Parent, Query, ResolveField, Resolver } from '@nestjs/graphql';
|
||||
import { Roles } from '../../decorator/roles.decorator';
|
||||
import { User } from '../../decorator/user.decorator';
|
||||
import { UserModel } from '../../dto/user/user.model';
|
||||
import { UserDocument } from '../../schema/user.schema';
|
||||
import { UserService } from '../../service/user/user.service';
|
||||
import { ContextCache } from '../context.cache';
|
||||
|
||||
@ -26,11 +27,24 @@ export class UserResolver {
|
||||
}
|
||||
|
||||
@ResolveField('roles', () => [String])
|
||||
@Roles('superuser')
|
||||
@Roles('user')
|
||||
async getRoles(
|
||||
@Parent() user: UserModel,
|
||||
@User() user: UserDocument,
|
||||
@Parent() parent: UserModel,
|
||||
@Context('cache') cache: ContextCache,
|
||||
): Promise<string[]> {
|
||||
return (await cache.getUser(user.id)).roles
|
||||
return await this.returnFieldForSuperuser(
|
||||
await cache.getUser(parent.id),
|
||||
user,
|
||||
c => c.roles
|
||||
)
|
||||
}
|
||||
|
||||
async returnFieldForSuperuser<T>(parent: UserDocument, user: UserDocument, callback: (user: UserDocument) => T): Promise<T> {
|
||||
if (user.id !== parent.id && !await this.userService.isSuperuser(user)) {
|
||||
throw new Error('No access to roles')
|
||||
}
|
||||
|
||||
return callback(parent)
|
||||
}
|
||||
}
|
||||
|
||||
35
src/resolver/user/user.search.resolver.ts
Normal file
35
src/resolver/user/user.search.resolver.ts
Normal file
@ -0,0 +1,35 @@
|
||||
import { Args, Context, Query, Resolver } from '@nestjs/graphql';
|
||||
import { GraphQLInt } from 'graphql';
|
||||
import { Roles } from '../../decorator/roles.decorator';
|
||||
import { PagerUserModel } from '../../dto/user/pager.user.model';
|
||||
import { UserModel } from '../../dto/user/user.model';
|
||||
import { UserService } from '../../service/user/user.service';
|
||||
import { ContextCache } from '../context.cache';
|
||||
|
||||
@Resolver(() => PagerUserModel)
|
||||
export class UserSearchResolver {
|
||||
constructor(
|
||||
private readonly userService: UserService,
|
||||
) {
|
||||
}
|
||||
|
||||
@Query(() => PagerUserModel)
|
||||
@Roles('superuser')
|
||||
async listUsers(
|
||||
@Args('start', {type: () => GraphQLInt, defaultValue: 0, nullable: true}) start,
|
||||
@Args('limit', {type: () => GraphQLInt, defaultValue: 50, nullable: true}) limit,
|
||||
@Context('cache') cache: ContextCache,
|
||||
): Promise<PagerUserModel> {
|
||||
const [entities, total] = await this.userService.find(start, limit)
|
||||
|
||||
return new PagerUserModel(
|
||||
entities.map(entity => {
|
||||
cache.addUser(entity)
|
||||
return new UserModel(entity)
|
||||
}),
|
||||
total,
|
||||
limit,
|
||||
start,
|
||||
)
|
||||
}
|
||||
}
|
||||
24
src/resolver/user/user.statistic.resolver.ts
Normal file
24
src/resolver/user/user.statistic.resolver.ts
Normal file
@ -0,0 +1,24 @@
|
||||
import { Query, ResolveField, Resolver } from '@nestjs/graphql';
|
||||
import { GraphQLInt } from 'graphql';
|
||||
import { Roles } from '../../decorator/roles.decorator';
|
||||
import { UserStatisticModel } from '../../dto/user/user.statistic.model';
|
||||
import { UserStatisticService } from '../../service/user/user.statistic.service';
|
||||
|
||||
@Resolver(() => UserStatisticModel)
|
||||
export class UserStatisticResolver {
|
||||
constructor(
|
||||
private readonly statisticService: UserStatisticService,
|
||||
) {
|
||||
}
|
||||
|
||||
@Query(() => UserStatisticModel)
|
||||
async getUserStatistic(): Promise<UserStatisticModel> {
|
||||
return new UserStatisticModel()
|
||||
}
|
||||
|
||||
@ResolveField('total', () => GraphQLInt)
|
||||
@Roles('admin')
|
||||
getTotal(): Promise<number> {
|
||||
return this.statisticService.getTotal()
|
||||
}
|
||||
}
|
||||
39
src/resolver/user/user.update.mutation.ts
Normal file
39
src/resolver/user/user.update.mutation.ts
Normal file
@ -0,0 +1,39 @@
|
||||
import { Injectable } from '@nestjs/common';
|
||||
import { Args, Context, Mutation } from '@nestjs/graphql';
|
||||
import { Roles } from '../../decorator/roles.decorator';
|
||||
import { User } from '../../decorator/user.decorator';
|
||||
import { UserModel } from '../../dto/user/user.model';
|
||||
import { UserUpdateInput } from '../../dto/user/user.update.input';
|
||||
import { UserDocument } from '../../schema/user.schema';
|
||||
import { UserService } from '../../service/user/user.service';
|
||||
import { UserUpdateService } from '../../service/user/user.update.service';
|
||||
import { ContextCache } from '../context.cache';
|
||||
|
||||
@Injectable()
|
||||
export class UserUpdateMutation {
|
||||
constructor(
|
||||
private readonly updateService: UserUpdateService,
|
||||
private readonly userService: UserService,
|
||||
) {
|
||||
}
|
||||
|
||||
@Mutation(() => UserModel)
|
||||
@Roles('superuser')
|
||||
async updateUser(
|
||||
@User() auth: UserDocument,
|
||||
@Args({ name: 'user', type: () => UserUpdateInput }) input: UserUpdateInput,
|
||||
@Context('cache') cache: ContextCache,
|
||||
): Promise<UserModel> {
|
||||
if (auth.id === input.id) {
|
||||
throw new Error('cannot update your own user')
|
||||
}
|
||||
|
||||
const user = await this.userService.findById(input.id)
|
||||
|
||||
await this.updateService.update(user, input)
|
||||
|
||||
cache.addUser(user)
|
||||
|
||||
return new UserModel(user)
|
||||
}
|
||||
}
|
||||
@ -2,16 +2,20 @@ import { Injectable } from '@nestjs/common';
|
||||
import { InjectModel } from '@nestjs/mongoose';
|
||||
import { Model } from 'mongoose';
|
||||
import { FormDocument, FormSchemaName } from '../../schema/form.schema';
|
||||
import { SubmissionDocument, SubmissionSchemaName } from '../../schema/submission.schema';
|
||||
|
||||
@Injectable()
|
||||
export class FormDeleteService {
|
||||
constructor(
|
||||
@InjectModel(FormSchemaName) private formModel: Model<FormDocument>,
|
||||
@InjectModel(SubmissionSchemaName) private readonly submissionModel: Model<SubmissionDocument>,
|
||||
) {
|
||||
}
|
||||
|
||||
async delete(id: string): Promise<void> {
|
||||
// TODO
|
||||
throw new Error('form.delete not yet implemented')
|
||||
const form = await this.formModel.findByIdAndDelete(id).exec()
|
||||
await this.submissionModel.deleteMany({
|
||||
form
|
||||
}).exec()
|
||||
}
|
||||
}
|
||||
|
||||
@ -28,15 +28,14 @@ export class FormService {
|
||||
}
|
||||
}
|
||||
|
||||
const qb = this.formModel.find(conditions)
|
||||
|
||||
// TODO apply restrictions based on user!
|
||||
|
||||
return [
|
||||
await qb.sort(sort)
|
||||
await this.formModel
|
||||
.find(conditions)
|
||||
.sort(sort)
|
||||
.skip(start)
|
||||
.limit(limit),
|
||||
await qb.count()
|
||||
await this.formModel
|
||||
.countDocuments(conditions)
|
||||
]
|
||||
}
|
||||
|
||||
|
||||
14
src/service/form/form.statistic.service.ts
Normal file
14
src/service/form/form.statistic.service.ts
Normal file
@ -0,0 +1,14 @@
|
||||
import { InjectModel } from '@nestjs/mongoose';
|
||||
import { Model } from 'mongoose';
|
||||
import { FormDocument, FormSchemaName } from '../../schema/form.schema';
|
||||
|
||||
export class FormStatisticService {
|
||||
constructor(
|
||||
@InjectModel(FormSchemaName) private formModel: Model<FormDocument>,
|
||||
) {
|
||||
}
|
||||
|
||||
async getTotal(): Promise<number> {
|
||||
return await this.formModel.estimatedDocumentCount();
|
||||
}
|
||||
}
|
||||
@ -1,11 +1,13 @@
|
||||
import { FormCreateService } from './form.create.service';
|
||||
import { FormDeleteService } from './form.delete.service';
|
||||
import { FormService } from './form.service';
|
||||
import { FormStatisticService } from './form.statistic.service';
|
||||
import { FormUpdateService } from './form.update.service';
|
||||
|
||||
export const formServices = [
|
||||
FormService,
|
||||
FormCreateService,
|
||||
FormUpdateService,
|
||||
FormDeleteService,
|
||||
FormService,
|
||||
FormStatisticService,
|
||||
FormUpdateService,
|
||||
]
|
||||
|
||||
@ -6,11 +6,13 @@ import { PinoLogger } from 'nestjs-pino/dist';
|
||||
import { authServices } from './auth';
|
||||
import { formServices } from './form';
|
||||
import { MailService } from './mail.service';
|
||||
import { profileServices } from './profile';
|
||||
import { submissionServices } from './submission';
|
||||
import { userServices } from './user';
|
||||
|
||||
export const services = [
|
||||
...userServices,
|
||||
...profileServices,
|
||||
...formServices,
|
||||
...authServices,
|
||||
...submissionServices,
|
||||
|
||||
5
src/service/profile/index.ts
Normal file
5
src/service/profile/index.ts
Normal file
@ -0,0 +1,5 @@
|
||||
import { ProfileUpdateService } from './profile.update.service';
|
||||
|
||||
export const profileServices = [
|
||||
ProfileUpdateService,
|
||||
]
|
||||
38
src/service/profile/profile.update.service.ts
Normal file
38
src/service/profile/profile.update.service.ts
Normal file
@ -0,0 +1,38 @@
|
||||
import { Injectable } from '@nestjs/common';
|
||||
import { ProfileUpdateInput } from '../../dto/profile/profile.update.input';
|
||||
import { UserDocument } from '../../schema/user.schema';
|
||||
|
||||
@Injectable()
|
||||
export class ProfileUpdateService {
|
||||
async update(user: UserDocument, input: ProfileUpdateInput): Promise<UserDocument> {
|
||||
if (input.firstName !== undefined) {
|
||||
user.set('firstName', input.firstName)
|
||||
}
|
||||
|
||||
if (input.lastName !== undefined) {
|
||||
user.set('lastName', input.lastName)
|
||||
}
|
||||
|
||||
if (input.email !== undefined) {
|
||||
user.set('email', input.email)
|
||||
// TODO request email verification
|
||||
}
|
||||
|
||||
if (input.username !== undefined) {
|
||||
user.set('username', input.username)
|
||||
}
|
||||
|
||||
if (input.language !== undefined) {
|
||||
user.set('language', input.language)
|
||||
}
|
||||
|
||||
if (input.password !== undefined) {
|
||||
// user.set('language', input.language)
|
||||
// TODO password handling
|
||||
}
|
||||
|
||||
await user.save()
|
||||
|
||||
return user
|
||||
}
|
||||
}
|
||||
@ -1,11 +1,13 @@
|
||||
import { SubmissionService } from './submission.service';
|
||||
import { SubmissionSetFieldService } from './submission.set.field.service';
|
||||
import { SubmissionStartService } from './submission.start.service';
|
||||
import { SubmissionStatisticService } from './submission.statistic.service';
|
||||
import { SubmissionTokenService } from './submission.token.service';
|
||||
|
||||
export const submissionServices = [
|
||||
SubmissionService,
|
||||
SubmissionSetFieldService,
|
||||
SubmissionStartService,
|
||||
SubmissionService,
|
||||
SubmissionStatisticService,
|
||||
SubmissionTokenService,
|
||||
]
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
import { InjectModel } from '@nestjs/mongoose';
|
||||
import { Model } from 'mongoose';
|
||||
import { FilterQuery, Model } from 'mongoose';
|
||||
import { FormDocument } from '../../schema/form.schema';
|
||||
import { SubmissionDocument, SubmissionSchemaName } from '../../schema/submission.schema';
|
||||
import { SubmissionTokenService } from './submission.token.service';
|
||||
@ -16,15 +16,18 @@ export class SubmissionService {
|
||||
}
|
||||
|
||||
async find(form: FormDocument, start: number, limit: number, sort: any = {}): Promise<[SubmissionDocument[], number]> {
|
||||
const qb = this.submissionModel.find({
|
||||
const conditions: FilterQuery<SubmissionDocument> = {
|
||||
form
|
||||
})
|
||||
}
|
||||
|
||||
return [
|
||||
await qb.sort(sort)
|
||||
await this.submissionModel
|
||||
.find(conditions)
|
||||
.sort(sort)
|
||||
.skip(start)
|
||||
.limit(limit),
|
||||
await qb.count()
|
||||
await this.submissionModel
|
||||
.countDocuments(conditions)
|
||||
]
|
||||
}
|
||||
|
||||
|
||||
14
src/service/submission/submission.statistic.service.ts
Normal file
14
src/service/submission/submission.statistic.service.ts
Normal file
@ -0,0 +1,14 @@
|
||||
import { InjectModel } from '@nestjs/mongoose';
|
||||
import { Model } from 'mongoose';
|
||||
import { SubmissionDocument, SubmissionSchemaName } from '../../schema/submission.schema';
|
||||
|
||||
export class SubmissionStatisticService {
|
||||
constructor(
|
||||
@InjectModel(SubmissionSchemaName) private readonly submissionModel: Model<SubmissionDocument>,
|
||||
) {
|
||||
}
|
||||
|
||||
async getTotal(): Promise<number> {
|
||||
return await this.submissionModel.estimatedDocumentCount();
|
||||
}
|
||||
}
|
||||
@ -1,7 +1,13 @@
|
||||
import { UserCreateService } from './user.create.service';
|
||||
import { UserDeleteService } from './user.delete.service';
|
||||
import { UserService } from './user.service';
|
||||
import { UserStatisticService } from './user.statistic.service';
|
||||
import { UserUpdateService } from './user.update.service';
|
||||
|
||||
export const userServices = [
|
||||
UserCreateService,
|
||||
UserDeleteService,
|
||||
UserService,
|
||||
UserStatisticService,
|
||||
UserUpdateService,
|
||||
]
|
||||
|
||||
16
src/service/user/user.delete.service.ts
Normal file
16
src/service/user/user.delete.service.ts
Normal file
@ -0,0 +1,16 @@
|
||||
import { Injectable } from '@nestjs/common';
|
||||
import { InjectModel } from '@nestjs/mongoose';
|
||||
import { Model } from 'mongoose';
|
||||
import { UserDocument, UserSchemaName } from '../../schema/user.schema';
|
||||
|
||||
@Injectable()
|
||||
export class UserDeleteService {
|
||||
constructor(
|
||||
@InjectModel(UserSchemaName) private userModel: Model<UserDocument>,
|
||||
) {
|
||||
}
|
||||
|
||||
async delete(id: string): Promise<void> {
|
||||
await this.userModel.findByIdAndDelete(id).exec()
|
||||
}
|
||||
}
|
||||
@ -1,6 +1,5 @@
|
||||
import { Injectable } from '@nestjs/common';
|
||||
import { InjectModel } from '@nestjs/mongoose';
|
||||
import { GraphQLError } from 'graphql';
|
||||
import { Model } from 'mongoose';
|
||||
import { UserDocument, UserSchemaName } from '../../schema/user.schema';
|
||||
|
||||
@ -11,6 +10,22 @@ export class UserService {
|
||||
) {
|
||||
}
|
||||
|
||||
async isSuperuser(user: UserDocument): Promise<boolean> {
|
||||
return user.roles.includes('superuser')
|
||||
}
|
||||
|
||||
async find(start: number, limit: number, sort: any = {}): Promise<[UserDocument[], number]> {
|
||||
return [
|
||||
await this.userModel
|
||||
.find()
|
||||
.sort(sort)
|
||||
.skip(start)
|
||||
.limit(limit),
|
||||
await this.userModel
|
||||
.countDocuments()
|
||||
]
|
||||
}
|
||||
|
||||
async findById(id: string): Promise<UserDocument> {
|
||||
const user = await this.userModel.findById(id);
|
||||
|
||||
|
||||
14
src/service/user/user.statistic.service.ts
Normal file
14
src/service/user/user.statistic.service.ts
Normal file
@ -0,0 +1,14 @@
|
||||
import { InjectModel } from '@nestjs/mongoose';
|
||||
import { Model } from 'mongoose';
|
||||
import { UserDocument, UserSchemaName } from '../../schema/user.schema';
|
||||
|
||||
export class UserStatisticService {
|
||||
constructor(
|
||||
@InjectModel(UserSchemaName) private userModel: Model<UserDocument>,
|
||||
) {
|
||||
}
|
||||
|
||||
async getTotal(): Promise<number> {
|
||||
return await this.userModel.estimatedDocumentCount();
|
||||
}
|
||||
}
|
||||
41
src/service/user/user.update.service.ts
Normal file
41
src/service/user/user.update.service.ts
Normal file
@ -0,0 +1,41 @@
|
||||
import { Injectable } from '@nestjs/common';
|
||||
import { UserUpdateInput } from '../../dto/user/user.update.input';
|
||||
import { UserDocument } from '../../schema/user.schema';
|
||||
|
||||
@Injectable()
|
||||
export class UserUpdateService {
|
||||
async update(user: UserDocument, input: UserUpdateInput): Promise<UserDocument> {
|
||||
if (input.firstName !== undefined) {
|
||||
user.set('firstName', input.firstName)
|
||||
}
|
||||
|
||||
if (input.lastName !== undefined) {
|
||||
user.set('lastName', input.lastName)
|
||||
}
|
||||
|
||||
if (input.email !== undefined) {
|
||||
user.set('email', input.email)
|
||||
}
|
||||
|
||||
if (input.username !== undefined) {
|
||||
user.set('username', input.username)
|
||||
}
|
||||
|
||||
if (input.roles !== undefined) {
|
||||
user.set('roles', input.roles)
|
||||
}
|
||||
|
||||
if (input.language !== undefined) {
|
||||
user.set('language', input.language)
|
||||
}
|
||||
|
||||
if (input.password !== undefined) {
|
||||
// user.set('language', input.language)
|
||||
// TODO password handling
|
||||
}
|
||||
|
||||
await user.save()
|
||||
|
||||
return user
|
||||
}
|
||||
}
|
||||
Loading…
x
Reference in New Issue
Block a user