_organizers: [UUID!] | _organizers: [UUID!] | ||||
} | } | ||||
type Timeslot { | |||||
_id: UUID! | |||||
time: Time! | |||||
} | |||||
"""A time string HH:MM:SS""" | |||||
scalar Time | |||||
type Event { | |||||
_id: UUID! | |||||
date: Date! | |||||
timeslots: [Timeslot!]! | |||||
_organizer: UUID! | |||||
} | |||||
"""A date string YYYY-MM-DD""" | |||||
scalar Date | |||||
type Query { | type Query { | ||||
Person(id: UUID!): Person | Person(id: UUID!): Person | ||||
PersonFind(offset: Int, limit: Int, email: String, familyName: String, givenName: String): [Person!] | PersonFind(offset: Int, limit: Int, email: String, familyName: String, givenName: String): [Person!] | ||||
ApparatusFind(offset: Int, limit: Int, name: String): [Apparatus!] | ApparatusFind(offset: Int, limit: Int, name: String): [Apparatus!] | ||||
Organizer(id: UUID!): Organizer | Organizer(id: UUID!): Organizer | ||||
OrganizerFind(offset: Int, limit: Int, name: String): [Organizer!] | OrganizerFind(offset: Int, limit: Int, name: String): [Organizer!] | ||||
Event(id: UUID!): Event | |||||
EventFind(offset: Int, limit: Int, organizer: UUID, date: Date): [Event!] | |||||
} | } | ||||
type Mutation { | type Mutation { |
import { PersonModule } from './person/person.module'; | import { PersonModule } from './person/person.module'; | ||||
import { ApparatusModule } from './apparatus/apparatus.module'; | import { ApparatusModule } from './apparatus/apparatus.module'; | ||||
import { OrganizerModule } from './organizer/organizer.module' | import { OrganizerModule } from './organizer/organizer.module' | ||||
import { EventModule } from './event/event.module' | |||||
@Module({ | @Module({ | ||||
imports: [ | imports: [ | ||||
PersonModule, | PersonModule, | ||||
ApparatusModule, | ApparatusModule, | ||||
OrganizerModule, | OrganizerModule, | ||||
EventModule, | |||||
GraphQLModule.forRoot({ | GraphQLModule.forRoot({ | ||||
installSubscriptionHandlers: true, | installSubscriptionHandlers: true, | ||||
autoSchemaFile: 'schema.gql', | autoSchemaFile: 'schema.gql', |
import { Module } from '@nestjs/common'; | |||||
import {EventResolver} from './resolver/event' | |||||
import {EventService} from './event.service' | |||||
import {EventResolverQ} from './resolver/event.query' | |||||
import {EventResolverM} from './resolver/event.mutation' | |||||
@Module({ | |||||
providers: [ | |||||
EventResolverQ, EventResolverM, | |||||
EventService, | |||||
EventResolver, | |||||
], | |||||
}) | |||||
export class EventModule {} |
import { Injectable } from '@nestjs/common'; | |||||
import { db } from '../db'; | |||||
import { Event } from './models/Event'; | |||||
import { Client } from '../client'; | |||||
import { UUID } from '../global/scalars/UUID'; | |||||
@Injectable() | |||||
export class EventService { | |||||
async findOneById(id: UUID): Promise<Event> { | |||||
const data = await db.fetch('event', { _id: id }); | |||||
return data?.[0] || null as Event; | |||||
} | |||||
async find(filter: any, limit?: number, offset?: number): Promise<Event[]> { | |||||
return db.fetch('event', filter, limit, offset); | |||||
} | |||||
async update(id: UUID, ops: any, filter: any, client: Client): Promise<Event> { | |||||
return db.doOps('event', id, ops, filter, client) | |||||
} | |||||
} |
import { Field, ObjectType } from '@nestjs/graphql'; | |||||
import { UUID } from '../../global/scalars/UUID'; | |||||
import { Date } from '../../global/scalars/Date' | |||||
import { Timeslot } from './Timeslot' | |||||
import { Organizer } from '../../organizer/models/Organizer' | |||||
@ObjectType() | |||||
export class Event { | |||||
@Field(() => UUID,{ nullable: false }) | |||||
_id: UUID | |||||
@Field(() => Date, { nullable: false }) | |||||
date: Date | |||||
@Field(() => [Timeslot], { nullable: false }) | |||||
timeslots: Timeslot[] | |||||
@Field(() => UUID, { nullable: false }) | |||||
_organizer: Organizer | |||||
} |
import { Field, ObjectType } from '@nestjs/graphql'; | |||||
import { UUID } from '../../global/scalars/UUID'; | |||||
import { Time } from '../../global/scalars/Time' | |||||
@ObjectType() | |||||
export class Timeslot { | |||||
@Field(() => UUID,{ nullable: false }) | |||||
_id: UUID | |||||
@Field(() => Time, { nullable: false }) | |||||
time: Time | |||||
} |
import { Args, Context, Mutation, Resolver } from '@nestjs/graphql'; | |||||
import { Event } from '../models/Event'; | |||||
import { Client } from '../../client'; | |||||
import { EventService } from '../event.service'; | |||||
import { HttpException } from '@nestjs/common'; | |||||
@Resolver(() => Event) | |||||
export class EventResolverM { | |||||
constructor( | |||||
private readonly service: EventService, | |||||
) {} | |||||
} |
import { Args, Context, Int, Query, Resolver } from '@nestjs/graphql'; | |||||
import { Event } from '../models/Event'; | |||||
import { EventService } from '../event.service'; | |||||
import { Client } from '../../client'; | |||||
import { HttpException } from '@nestjs/common'; | |||||
import { UUID } from '../../global/scalars/UUID'; | |||||
import { Date } from '../../global/scalars/Date'; | |||||
@Resolver(() => Event) | |||||
export class EventResolverQ { | |||||
constructor( | |||||
private readonly service: EventService, | |||||
) {} | |||||
@Query(() => Event, { nullable: true }) | |||||
async Event( | |||||
@Context('client') client: Client, | |||||
@Args('id') id: UUID, | |||||
): Promise<Event> { | |||||
return await this.service.findOneById(id) as Event; | |||||
} | |||||
@Query(() => [Event], { nullable: true }) | |||||
async EventFind( | |||||
@Context('client') client: Client, | |||||
@Args('date', { nullable: true }) date?: Date, | |||||
@Args('organizer', { nullable: true }) organizer?: UUID, | |||||
@Args('limit', { type: () => Int, nullable: true }) limit?: number, | |||||
@Args('offset', { type: () => Int, nullable: true }) offset?: number, | |||||
): Promise<Event[]> { | |||||
const filter: any = {} | |||||
if (date) filter.date = date; | |||||
if (organizer) filter._organizer = organizer; | |||||
let tmp = await this.service.find(filter, limit, offset); | |||||
if (tmp.length > 1000) throw new HttpException('too many results', 413); | |||||
return tmp | |||||
} | |||||
} |
import { Context, Parent, ResolveField, Resolver } from '@nestjs/graphql'; | |||||
import { Event } from '../models/Event'; | |||||
import { Client } from '../../client'; | |||||
import { UUID } from '../../global/scalars/UUID'; | |||||
import {Date} from '../../global/scalars/Date' | |||||
import {Timeslot} from '../models/Timeslot' | |||||
@Resolver(() => Event) | |||||
export class EventResolver { | |||||
@ResolveField(() => UUID, { nullable: false }) | |||||
async _id( | |||||
@Context('client') client: Client, | |||||
@Parent() parent: Event | |||||
): Promise<UUID> { | |||||
return parent._id as UUID; | |||||
} | |||||
@ResolveField(() => Date, { nullable: false }) | |||||
async date( | |||||
@Context('client') client: Client, | |||||
@Parent() parent: Event | |||||
): Promise<Date> { | |||||
return parent.date; | |||||
} | |||||
@ResolveField(() => [Timeslot], { nullable: true }) | |||||
async timeslots( | |||||
@Context('client') client: Client, | |||||
@Parent() parent: Event | |||||
): Promise<Timeslot[]> { | |||||
return parent.timeslots; | |||||
} | |||||
} |
import { Module } from '@nestjs/common'; | import { Module } from '@nestjs/common'; | ||||
import { UUID } from './scalars/UUID'; | import { UUID } from './scalars/UUID'; | ||||
import { EmailAddress } from './scalars/EmailAddress'; | import { EmailAddress } from './scalars/EmailAddress'; | ||||
import { Date } from './scalars/Date' | |||||
import { DateTime } from './scalars/DateTime' | |||||
import { Time } from './scalars/Time' | |||||
@Module({ | @Module({ | ||||
providers: [ | providers: [ | ||||
UUID, | UUID, | ||||
EmailAddress, | EmailAddress, | ||||
Date, | |||||
// DateTime, | |||||
Time, | |||||
] | ] | ||||
}) | }) | ||||
export class GlobalModule {} | export class GlobalModule {} |
import { CustomScalar, Scalar } from '@nestjs/graphql'; | |||||
import { GraphQLError, Kind, ValueNode } from 'graphql'; | |||||
const validate = (value: string): string => { | |||||
const DATE_REGEX = /^(18|19|20)\d{2}-(0?[1-9]|1[0-2])-(0?[1-9]|[12]\d|3[01])$/; | |||||
if (typeof value !== 'string') { | |||||
throw new TypeError(`Value is not string: ${value}`); | |||||
} | |||||
if (!DATE_REGEX.test(value)) { | |||||
throw new TypeError(`Value is not a valid date: ${value}`); | |||||
} | |||||
const tmp = value.split('-'); | |||||
if (tmp[1].length === 1) { | |||||
tmp[1] = '0' + tmp[1]; | |||||
} | |||||
if (tmp[2].length === 1) { | |||||
tmp[2] = '0' + tmp[2]; | |||||
} | |||||
value = tmp.join('-'); | |||||
return value; | |||||
}; | |||||
@Scalar('Date', () => Date) | |||||
export class Date implements CustomScalar<string, string> { | |||||
description = 'A date string YYYY-MM-DD' | |||||
parseValue(value: string): string { | |||||
return validate(value) | |||||
} | |||||
serialize(value: string): string { | |||||
return validate(value) | |||||
} | |||||
parseLiteral(ast: ValueNode): string { | |||||
if (ast.kind !== Kind.STRING) { | |||||
throw new GraphQLError( | |||||
`Can only validate strings as date but got a: ${ast.kind}`, | |||||
); | |||||
} | |||||
return validate(ast.value) | |||||
} | |||||
} |
import { CustomScalar, Scalar } from '@nestjs/graphql'; | |||||
import { GraphQLError, Kind, ValueNode } from 'graphql'; | |||||
const validate = (value: string): string => { | |||||
const DATE_REGEX = /^(18|19|20)\d{2}-(0?[1-9]|1[0-2])-(0?[1-9]|[12]\d|3[01])( (0?[0-9]|1[0-9]|2[0-3]):(0?[0-9]|[1-5][0-9]):(0?[0-9]|[1-5][0-9]))?$/; | |||||
if (typeof value !== 'string') { | |||||
throw new TypeError(`Value is not string: ${value}`); | |||||
} | |||||
if (!DATE_REGEX.test(value)) { | |||||
throw new TypeError(`Value is not a valid datetime: ${value}`); | |||||
} | |||||
const split = value.split(' '); | |||||
if (split.length === 1) split.push('00:00:00'); | |||||
const tmp = split[0].split('-'); | |||||
if (tmp[1].length === 1) tmp[1] = '0' + tmp[1]; | |||||
if (tmp[2].length === 1) tmp[2] = '0' + tmp[2]; | |||||
split[0] = tmp.join('-'); | |||||
const time = split[1].split(':'); | |||||
if(time[0].length === 1) time[0] = '0' + time[0]; | |||||
if(time[1].length === 1) time[1] = '0' + time[1]; | |||||
if(time[2].length === 1) time[2] = '0' + time[2]; | |||||
split[1] = time.join(':'); | |||||
return split.join(' '); | |||||
}; | |||||
@Scalar('DateTime', () => DateTime) | |||||
export class DateTime implements CustomScalar<string, string> { | |||||
description = 'A datetime string YYYY-MM-DD HH:MM:SS' | |||||
parseValue(value: string): string { | |||||
return validate(value) | |||||
} | |||||
serialize(value: string): string { | |||||
return validate(value) | |||||
} | |||||
parseLiteral(ast: ValueNode): string { | |||||
if (ast.kind !== Kind.STRING) { | |||||
throw new GraphQLError( | |||||
`Can only validate strings as date but got a: ${ast.kind}`, | |||||
); | |||||
} | |||||
return validate(ast.value) | |||||
} | |||||
} |
import { CustomScalar, Scalar } from '@nestjs/graphql'; | |||||
import { GraphQLError, Kind, ValueNode } from 'graphql'; | |||||
const validate = (value: string): string => { | |||||
const DATE_REGEX = /^(0?[0-9]|1[0-9]|2[0-3]):(0?[0-9]|[1-5][0-9]):(0?[0-9]|[1-5][0-9])?$/; | |||||
if (typeof value !== 'string') { | |||||
throw new TypeError(`Value is not string: ${value}`); | |||||
} | |||||
if (!DATE_REGEX.test(value)) { | |||||
throw new TypeError(`Value is not a valid time: ${value}`); | |||||
} | |||||
const time = value.split(':'); | |||||
if(time[0].length === 1) time[0] = '0' + time[0]; | |||||
if(time[1].length === 1) time[1] = '0' + time[1]; | |||||
if(time[2].length === 1) time[2] = '0' + time[2]; | |||||
return time.join(':'); | |||||
}; | |||||
@Scalar('Time', () => Time) | |||||
export class Time implements CustomScalar<string, string> { | |||||
description = 'A time string HH:MM:SS' | |||||
parseValue(value: string): string { | |||||
return validate(value) | |||||
} | |||||
serialize(value: string): string { | |||||
return validate(value) | |||||
} | |||||
parseLiteral(ast: ValueNode): string { | |||||
if (ast.kind !== Kind.STRING) { | |||||
throw new GraphQLError( | |||||
`Can only validate strings as time but got a: ${ast.kind}`, | |||||
); | |||||
} | |||||
return validate(ast.value) | |||||
} | |||||
} |