Fixed some tests

This commit is contained in:
Amit Jakubowicz 2019-11-07 16:18:22 +01:00
parent 91329860de
commit e61ed72dc9
16 changed files with 369 additions and 130 deletions

5
.prettierrc Normal file
View File

@ -0,0 +1,5 @@
{
"trailingComma": "es5",
"tabWidth": 2,
"semi": false
}

63
@types/graphql.d.ts vendored
View File

@ -70,7 +70,7 @@ declare namespace GQL {
status: any; status: any;
location: ILocation; location: ILocation;
occurrences: Array<IEventOccurrence | null> | null; occurrences: Array<IEventOccurrence | null> | null;
meta: IEventMeta | null; tags: Array<IEventTag | null> | null;
} }
interface IInfoOnCalendarEventArguments { interface IInfoOnCalendarEventArguments {
@ -107,9 +107,17 @@ declare namespace GQL {
end: string; end: string;
} }
interface IEventMeta { interface IEventTag {
__typename: 'EventMeta'; __typename: 'EventTag';
tags: Array<string | null>; ID: string;
name: string;
translations: Array<IEventTagTranslation>;
}
interface IEventTagTranslation {
__typename: 'EventTagTranslation';
language: string;
text: string;
} }
interface IUserRole { interface IUserRole {
@ -144,6 +152,8 @@ declare namespace GQL {
createEvent: ICalendarEvent | null; createEvent: ICalendarEvent | null;
updateEvent: ICalendarEvent | null; updateEvent: ICalendarEvent | null;
deleteEvent: IUser; deleteEvent: IUser;
createEventTag: IEventTag | null;
modifyEventTag: IEventTag | null;
} }
interface ISignupOnMutationArguments { interface ISignupOnMutationArguments {
@ -178,6 +188,14 @@ declare namespace GQL {
id: string; id: string;
} }
interface ICreateEventTagOnMutationArguments {
input: ICreateEventTagInput;
}
interface IModifyEventTagOnMutationArguments {
input: IModifyEventTagInput;
}
interface ISignupInput { interface ISignupInput {
email: string; email: string;
username: string; username: string;
@ -215,7 +233,7 @@ declare namespace GQL {
time: IEventTimeInput; time: IEventTimeInput;
infos: Array<IEventInformationInput | null>; infos: Array<IEventInformationInput | null>;
location: IEventLocationInput; location: IEventLocationInput;
meta: IEventMetaInput; tagNames: Array<string>;
status: string; status: string;
} }
@ -238,19 +256,44 @@ declare namespace GQL {
name?: string | null; name?: string | null;
} }
interface IEventMetaInput {
tags: Array<string | null>;
}
interface IUpdateEventInput { interface IUpdateEventInput {
id: string; id: string;
time?: IEventTimeInput | null; time?: IEventTimeInput | null;
infos?: Array<IEventInformationInput> | null; infos?: Array<IEventInformationInput> | null;
location?: IEventLocationInput | null; location?: IEventLocationInput | null;
meta?: IEventMetaInput | null; tagNames: Array<string>;
status?: string | null; status?: string | null;
} }
interface ICreateEventTagInput {
name: string;
translations: Array<ICreateModifyEventTagTranslationInput>;
}
interface ICreateModifyEventTagTranslationInput {
language: string;
text: string;
}
interface IModifyEventTagInput {
id: string;
name: string;
translations: Array<ICreateModifyEventTagTranslationInput>;
}
interface ITag {
__typename: 'Tag';
id: string;
name: string;
translations: Array<any>;
}
interface ITagTranslation {
__typename: 'TagTranslation';
language: string;
text: string;
}
interface IRevokeRoleInput { interface IRevokeRoleInput {
userId: string; userId: string;
roleType: any; roleType: any;

View File

@ -32,8 +32,8 @@
"ramda": "^0.26.1", "ramda": "^0.26.1",
"random-string": "^0.2.0", "random-string": "^0.2.0",
"rrule": "^2.6.2", "rrule": "^2.6.2",
"typeorm": "^0.2.18", "typeorm": "^0.2.20",
"typescript": "^3.5.3", "typescript": "^3.7.2",
"uuid": "^3.3.2" "uuid": "^3.3.2"
}, },
"devDependencies": { "devDependencies": {
@ -47,8 +47,7 @@
"prettier": "^1.18.2", "prettier": "^1.18.2",
"ts-jest": "^24.0.2", "ts-jest": "^24.0.2",
"ts-node": "^8.3.0", "ts-node": "^8.3.0",
"tslint": "^5.18.0", "tslint": "^5.18.0"
"typescript": "^3.5.3"
}, },
"jest": { "jest": {
"globalSetup": "./jest-setup.js", "globalSetup": "./jest-setup.js",

View File

@ -18,6 +18,7 @@ describe('Authentication', () => {
connection = await createConnection({ connection = await createConnection({
...testConfig, ...testConfig,
logging: null logging: null
}) })
}) })
@ -27,7 +28,8 @@ describe('Authentication', () => {
const server = await createServer({ const server = await createServer({
typeormConnection: connection, typeormConnection: connection,
sendEmail: sendEmailMock as PostOffice, sendEmail: sendEmailMock as PostOffice,
domain: 'example.com' domain: 'example.com',
sessionManager: jest.fn(() => Promise.resolve(true)) as any
}) })
testClient = await createTestClient(server as any) testClient = await createTestClient(server as any)
}) })

View File

@ -9,16 +9,7 @@ import {
import {User} from "../Auth/User.entity" import {User} from "../Auth/User.entity"
import {DateTime} from "luxon" import {DateTime} from "luxon"
import {rrulestr} from "rrule" import {rrulestr} from "rrule"
import { EventTag } from "./EventTag.entity";
export const toUTC = (isoTime: string, ianaTZ: string): DateTime => {
const parsed = DateTime.fromISO(isoTime, {zone: ianaTZ})
if (parsed.invalidReason) {
throw new Error(
`${parsed.invalidReason}: ${isoTime} with time-zone: ${ianaTZ}`
)
}
return parsed
}
export const breakTime = (isoString: string) => { export const breakTime = (isoString: string) => {
const tSplit = isoString.split('T') const tSplit = isoString.split('T')
@ -58,11 +49,6 @@ export class EventTime {
exceptions?: string exceptions?: string
} }
export class EventMeta {
@Column({type: "varchar", array: true})
tags: string[]
}
@Entity() @Entity()
export class Event extends BaseEntity { export class Event extends BaseEntity {
@PrimaryGeneratedColumn("uuid") @PrimaryGeneratedColumn("uuid")
@ -84,8 +70,8 @@ export class Event extends BaseEntity {
@Column(type => EventTime) @Column(type => EventTime)
time: EventTime time: EventTime
@Column(type => EventMeta) @OneToMany(type => EventTag, tag => tag.events)
meta: EventMeta tags: Promise<EventTag[]>
@Column({ @Column({
default: "confirmed" default: "confirmed"
@ -95,7 +81,7 @@ export class Event extends BaseEntity {
@Column(type => EventLocation) @Column(type => EventLocation)
location: EventLocation location: EventLocation
getOccurrences() { getOccurrences(): EventOccurrence[] {
const occurences = [] const occurences = []
console.log('this.time.recurrence', this.time.recurrence) console.log('this.time.recurrence', this.time.recurrence)
if (!this.time.recurrence) { if (!this.time.recurrence) {

View File

@ -0,0 +1,32 @@
import { BaseEntity, Column, Entity, ManyToMany, ManyToOne, OneToMany, PrimaryGeneratedColumn } from "typeorm";
import { Event } from "./Event.entity";
@Entity()
export class EventTag extends BaseEntity {
@PrimaryGeneratedColumn("uuid")
id: number
@Column({type: "varchar", unique: true})
name: string
@ManyToMany(type => Event, event => event.tags)
events: Promise<Event[]>
@OneToMany(type => EventTagTranslation, translation => translation.tag)
translations: Promise<EventTagTranslation[]>
}
@Entity()
export class EventTagTranslation extends BaseEntity {
@PrimaryGeneratedColumn("uuid")
id: number
@ManyToOne(type => EventTag, tag => tag.translations)
tag: Promise<EventTag>
@Column("varchar")
language: string
@Column("varchar")
text: string
}

View File

@ -1,18 +1,7 @@
import { createConnection } from "typeorm" import { createConnection } from "typeorm"
import { User } from "../../Auth/User.entity" import { User } from "../../Auth/User.entity"
import { Event, EventInformation, EventOccurrence } from "../Event.entity" import { Event, EventInformation, EventOccurrence } from "../Event.entity"
import { DateTime } from "luxon" import testConfig from "../../__tests__/testORMConfig"
import testConfig from "../../__tests__/testORMConfig";
const toUTC = (isoTime: string, ianaTZ: string) => {
const parsed = DateTime.fromISO(isoTime, { zone: ianaTZ })
if (parsed.invalidReason) {
throw new Error(
`${parsed.invalidReason}: ${isoTime} with time-zone: ${ianaTZ}`
)
}
return parsed.toUTC().toISO()
}
let owner: User = null let owner: User = null
@ -20,7 +9,7 @@ let connection
describe("Recurring events", () => { describe("Recurring events", () => {
beforeAll(async () => { beforeAll(async () => {
connection = await createConnection({ connection = await createConnection({
...testConfig ...testConfig,
}) })
owner = new User() owner = new User()
owner.username = "onetimeguy" owner.username = "onetimeguy"
@ -39,27 +28,26 @@ describe("Recurring events", () => {
info.title = "Test one time event" info.title = "Test one time event"
info.description = "This is a test event that has no repetition rules" info.description = "This is a test event that has no repetition rules"
event.owner = owner event.owner = Promise.resolve(owner)
event.time = { event.time = {
timeZone: "Europe/Madrid", timeZone: "Europe/Madrid",
start: "2019-02-01T10:00Z", start: "2019-02-01T10:00Z",
end: "2019-02-01T11:00Z" end: "2019-02-01T11:00Z",
} }
event.occurrences = Promise.resolve(event.getOccurrences())
await event.save()
await event.updateOccurrences()
await event.save() await event.save()
const occurences = await EventOccurrence.find({ const occurences = await EventOccurrence.find({
where: { eventId: event.id } where: { eventId: event.id },
}) })
expect(occurences).toHaveLength(1) expect(occurences).toHaveLength(1)
expect((await occurences[0].event).time.timeZone).toEqual(event.time.timeZone) expect((await occurences[0].event).time.timeZone).toEqual(
event.time.timeZone
)
done() done()
}) })
it("Event happening once a week", () => { it("Event happening once a week", async () => {
const event = new Event() const event = new Event()
const info = new EventInformation() const info = new EventInformation()
info.language = "en" info.language = "en"
@ -67,7 +55,7 @@ describe("Recurring events", () => {
info.description = "This event takes place every monday at 2pm" info.description = "This event takes place every monday at 2pm"
event.infos = Promise.resolve([info]) event.infos = Promise.resolve([info])
event.owner = owner event.owner = Promise.resolve(owner)
// from 2019-03-04 to 2019-03-25 every monday // from 2019-03-04 to 2019-03-25 every monday
// March 2019 // March 2019
@ -84,10 +72,10 @@ describe("Recurring events", () => {
start: "2019-03-04T14:00", start: "2019-03-04T14:00",
end: "2019-03-04T15:00", end: "2019-03-04T15:00",
recurrence: recurrence:
"DTSTART:20190304T140000Z\nRRULE:FREQ=WEEKLY;BYDAY=MO;INTERVAL=1;UNTIL=20190325T230000Z" // until 15/04/2019 "DTSTART:20190304T140000Z\nRRULE:FREQ=WEEKLY;BYDAY=MO;INTERVAL=1;UNTIL=20190325T230000Z", // until 15/04/2019
} }
event.updateOccurrences() event.occurrences = Promise.resolve(event.getOccurrences())
expect(event.occurrences).toHaveLength(4) expect(await event.occurrences).toHaveLength(4)
}) })
}) })

View File

@ -0,0 +1,42 @@
import { Connection, createConnection } from "typeorm"
import { createServer } from "../../graphql"
import { PostOffice } from "../../post_office"
import { createTestClient } from "apollo-server-testing"
import { User } from "../../Auth/User.entity"
import { Session } from "../../Auth/Session.entity"
import { Event, EventInformation } from "../../Calendar/Event.entity"
import gql from "graphql-tag"
import testConfig from "../../__tests__/testORMConfig"
let testClient
let connection: Connection = null
let sendEmailMock
let sessionManagerMock
describe("Tags resolver", () => {
beforeAll(async () => {
return (connection = await createConnection({
...testConfig
}))
})
beforeEach(async () => {
sendEmailMock = jest.fn(() => Promise.resolve(true))
sessionManagerMock = jest.fn(() => Promise.resolve(true))
const server = await createServer({
typeormConnection: connection,
sendEmail: sendEmailMock as PostOffice,
sessionManager: sessionManagerMock
})
return (testClient = await createTestClient(server as any))
})
afterAll(async () => {
await connection.close()
})
it("Create some tags", async done => {
done()
})
})

View File

@ -0,0 +1,86 @@
import { Context, ResolverMap } from "../@types/graphql-utils"
import { EventTag, EventTagTranslation } from "./EventTag.entity"
const adminOrThrow = async (context: Context) => {
const roles = await context.user?.roles
if (!roles?.find(role => role.type === "admin")) {
throw new Error("Only admin can create and modify tags")
}
}
const resolvers: ResolverMap = {
Mutation: {
createEventTag: async (
_,
req: GQL.ICreateEventTagOnMutationArguments,
context: Context,
info: any
): Promise<EventTag> => {
await adminOrThrow(context)
const tag = new EventTag()
tag.name = req.input.name
tag.events = Promise.resolve([])
const translations: EventTagTranslation[] = req.input.translations.map(
translationInput => {
const translation = new EventTagTranslation()
translation.tag = Promise.resolve(tag)
translation.language = translationInput.language
translation.text = translationInput.text
return translation
}
)
tag.translations = Promise.resolve(translations)
return tag.save()
},
modifyEventTag: async (
_,
req: GQL.IModifyEventTagOnMutationArguments,
context: Context
) => {
await adminOrThrow(context)
const tag = await EventTag.findOne(req.input.id)
const translations = await tag.translations
const langToExistingTranslation: {[lang: string]: EventTagTranslation} = {}
const langToInputTranslations: {[lang: string]: GQL.ICreateModifyEventTagTranslationInput} = {}
translations.forEach(existingTranslation => langToExistingTranslation[existingTranslation.language] = existingTranslation)
req.input.translations.forEach(translationInput => langToInputTranslations[translationInput.language] = translationInput)
const removedTranslationsPromises = Object.keys(langToExistingTranslation).map((existingLang) => {
if (!langToInputTranslations[existingLang]) {
return langToExistingTranslation[existingLang].remove()
}
return null
})
await Promise.all(removedTranslationsPromises.filter(Boolean))
const changedTranslationsPromises = Object.keys(langToExistingTranslation).map(existingLang => {
const existingTranslation = langToExistingTranslation[existingLang]
const matchingInput = langToInputTranslations[existingLang]
if (!(existingTranslation && matchingInput)) {
throw new Error("Coding error, existing translation and matching input must both exist")
}
if (existingTranslation.text !== matchingInput.text ) {
existingTranslation.text = matchingInput.text
return existingTranslation.save()
}
return null
})
await Promise.all(changedTranslationsPromises.filter(Boolean))
const newTranslationsPromises = Object.keys(langToInputTranslations).map(inputLang => {
if (!langToExistingTranslation[inputLang]) {
const newTranslation = new EventTagTranslation()
newTranslation.tag = Promise.resolve(tag)
newTranslation.language = inputLang
newTranslation.text = langToInputTranslations[inputLang].text
return newTranslation
}
return null
})
await Promise.all(newTranslationsPromises.filter(Boolean))
},
},
}

View File

@ -3,8 +3,16 @@ import {
EventInformation, EventInformation,
EventOccurrence EventOccurrence
} from "../Calendar/Event.entity" } from "../Calendar/Event.entity"
import { EventTag } from "../Calendar/EventTag.entity";
export default class EventsService { export const getTags = async (tagNames: string[]): Promise<EventTag[]> => {
const tagPromises = tagNames.map(tagName =>
EventTag.findOne({
where: {
name: tagName,
},
})
)
const tags = await Promise.all(tagPromises)
return tags
} }

View File

@ -11,6 +11,7 @@ import testConfig from "../../__tests__/testORMConfig"
let testClient let testClient
let connection: Connection = null let connection: Connection = null
let sendEmailMock let sendEmailMock
let sessionManagerMock
const createKiteflyingEvent = async () => { const createKiteflyingEvent = async () => {
const owner = new User() const owner = new User()
@ -26,12 +27,12 @@ const createKiteflyingEvent = async () => {
await session.save() await session.save()
const event = new Event() const event = new Event()
event.owner = owner event.owner = Promise.resolve(owner)
event.infos = [{ event.infos = Promise.resolve([{
language: "en", language: "en",
title: "Kite Flying Test Event", title: "Kite Flying Test Event",
description: "Description for test event starting at 3pm" description: "Description for test event starting at 3pm"
}] } as EventInformation])
event.time = { event.time = {
timeZone: "Europe/Madrid", timeZone: "Europe/Madrid",
start: "2019-10-10T13:00", start: "2019-10-10T13:00",
@ -55,19 +56,20 @@ const createKinttingEvent = async () => {
await session.save() await session.save()
const event = new Event() const event = new Event()
event.owner = owner event.owner = Promise.resolve(owner)
event.infos = [{ event.infos = Promise.resolve([{
language: "en", language: "en",
title: "Knitting Test Event", title: "Knitting Test Event",
description: "Description for test event starting at 3pm" description: "Description for test event starting at 3pm"
}] } as EventInformation])
event.time = { event.time = {
timeZone: "Europe/Madrid", timeZone: "Europe/Madrid",
start: "2019-10-10T14:00", start: "2019-10-10T14:00",
end: "2019-10-10T15:00" end: "2019-10-10T15:00"
} }
event.status = "Scheduled" event.status = "Scheduled"
return await event.updateOccurrences().save() event.occurrences = Promise.resolve(event.getOccurrences())
return await event.save()
} }
describe("Events resolver", () => { describe("Events resolver", () => {
@ -79,10 +81,12 @@ describe("Events resolver", () => {
beforeEach(async () => { beforeEach(async () => {
sendEmailMock = jest.fn(() => Promise.resolve(true)) sendEmailMock = jest.fn(() => Promise.resolve(true))
sessionManagerMock = jest.fn(() => Promise.resolve(true))
const server = await createServer({ const server = await createServer({
typeormConnection: connection, typeormConnection: connection,
sendEmail: sendEmailMock as PostOffice sendEmail: sendEmailMock as PostOffice,
sessionManager: sessionManagerMock
}) })
return (testClient = await createTestClient(server as any)) return (testClient = await createTestClient(server as any))
}) })
@ -99,7 +103,8 @@ describe("Events resolver", () => {
query { query {
events(filter: { limit: 10 }) { events(filter: { limit: 10 }) {
id id
info { info(lang: "en") {
language
title title
description description
} }
@ -127,7 +132,7 @@ describe("Events resolver", () => {
query GetEvent($ownerId: ID!) { query GetEvent($ownerId: ID!) {
events(filter: { limit: 10, owner: $ownerId }) { events(filter: { limit: 10, owner: $ownerId }) {
id id
info { info(lang: "en") {
title title
description description
} }

View File

@ -1,6 +1,6 @@
import {User} from "../../Auth/User.entity" import {User} from "../../Auth/User.entity"
import {Session} from "../../Auth/Session.entity" import {Session} from "../../Auth/Session.entity"
import {Event, EventInformation, toUTC} from "../../Calendar/Event.entity" import {Event, EventInformation} from "../../Calendar/Event.entity"
import {Frequency, RRule} from 'rrule' import {Frequency, RRule} from 'rrule'
import {Connection, createConnection} from "typeorm" import {Connection, createConnection} from "typeorm"
import testConfig from "../../__tests__/testORMConfig"; import testConfig from "../../__tests__/testORMConfig";
@ -49,12 +49,12 @@ describe('Occurrences', async () => {
recurrence: new RRule({ recurrence: new RRule({
freq: Frequency.WEEKLY, freq: Frequency.WEEKLY,
interval: 1, interval: 1,
dtstart: new Date(toUTC(new Date("2019-03-01T13:00Z"), "Europe/Madrid")) dtstart: new Date("2019-03-01T13:00Z+02:00")
}).toString() }).toString()
} }
event.status = "Scheduled" event.status = "Scheduled"
event.occurrences = Promise.resolve(event.getOccurrences())
event.updateOccurrences()
await event.save() await event.save()
}) })
}) })

View File

@ -1,15 +1,14 @@
import { import {
Event, Event,
EventInformation, EventInformation,
EventOccurrence EventOccurrence,
} from "../Calendar/Event.entity" } from "../Calendar/Event.entity"
import { Context, ResolverMap } from "../@types/graphql-utils" import { Context, ResolverMap } from "../@types/graphql-utils"
import EventsService from './EventsService' import { getTags } from "./EventsService"
import { equals } from 'ramda' import { equals } from "ramda"
import { User } from "../Auth/User.entity"; import { User } from "../Auth/User.entity"
import { hasAnyRole } from "../Auth/authUtils"; import { hasAnyRole } from "../Auth/authUtils"
import { EventTag } from "../Calendar/EventTag.entity"
const eventsService = new EventsService()
const resolvers: ResolverMap = { const resolvers: ResolverMap = {
Query: { Query: {
@ -22,17 +21,14 @@ const resolvers: ResolverMap = {
where: where:
req.filter && req.filter.owner req.filter && req.filter.owner
? `"ownerId"='${req.filter.owner}'` ? `"ownerId"='${req.filter.owner}'`
: null : null,
}) })
}, },
occurrences: async ( occurrences: async (_, req: GQL.IOccurrencesOnQueryArguments) => {
_,
req: GQL.IOccurrencesOnQueryArguments,
) => {
const { from, to } = req.filter const { from, to } = req.filter
return EventOccurrence.find({ return EventOccurrence.find({
where: `during && tstzrange('${from}', '${to}')`, where: `during && tstzrange('${from}', '${to}')`,
take: req.filter.limit take: req.filter.limit,
}) })
}, },
occurrence: async (_, req: GQL.IOccurrenceOnQueryArguments) => { occurrence: async (_, req: GQL.IOccurrenceOnQueryArguments) => {
@ -41,7 +37,7 @@ const resolvers: ResolverMap = {
throw new Error(`No occurrence found for id ${req.id}`) throw new Error(`No occurrence found for id ${req.id}`)
} }
return occ return occ
} },
}, },
CalendarEvent: { CalendarEvent: {
@ -59,10 +55,10 @@ const resolvers: ResolverMap = {
EventOccurrence: { EventOccurrence: {
start: (eOcc: EventOccurrence) => { start: (eOcc: EventOccurrence) => {
return eOcc.during.split(',')[0].substring(2, 21) return eOcc.during.split(",")[0].substring(2, 21)
}, },
end: (eOcc: EventOccurrence) => { end: (eOcc: EventOccurrence) => {
return eOcc.during.split(',')[1].substring(2, 21) return eOcc.during.split(",")[1].substring(2, 21)
}, },
}, },
@ -70,13 +66,19 @@ const resolvers: ResolverMap = {
createEvent: async ( createEvent: async (
_, _,
{ input }: GQL.ICreateEventOnMutationArguments, { input }: GQL.ICreateEventOnMutationArguments,
context: Context, context: Context
) => { ) => {
if (!context.user) { if (!context.user) {
throw Error("not authenticated") throw Error("not authenticated")
} }
if (!hasAnyRole(await context.user.roles, ['admin','embassador','organizer'])) { if (
!hasAnyRole(await context.user.roles, [
"admin",
"embassador",
"organizer",
])
) {
throw Error("Cannot create event, please validate as organizer") throw Error("Cannot create event, please validate as organizer")
} }
@ -96,11 +98,12 @@ const resolvers: ResolverMap = {
recurrence: input.time.recurrence, recurrence: input.time.recurrence,
timeZone: input.time.timeZone, timeZone: input.time.timeZone,
start: input.time.start, start: input.time.start,
end: input.time.end end: input.time.end,
}
if (input.tagNames) {
event.tags = getTags(input.tagNames)
} }
event.meta = input.meta
event.location = input.location event.location = input.location
event.occurrences = Promise.resolve(event.getOccurrences()) event.occurrences = Promise.resolve(event.getOccurrences())
await event.save() await event.save()
return event return event
@ -108,9 +111,9 @@ const resolvers: ResolverMap = {
updateEvent: async ( updateEvent: async (
_, _,
{ input }: GQL.IUpdateEventOnMutationArguments, { input }: GQL.IUpdateEventOnMutationArguments,
context: Context, context: Context
) => { ) => {
const {id, ...fields} = input const { id, ...fields } = input
if (!context.user) { if (!context.user) {
throw Error("not authenticated") throw Error("not authenticated")
@ -123,10 +126,16 @@ const resolvers: ResolverMap = {
throw Error("No change detected") throw Error("No change detected")
} }
if (input.time && !equals(input.time, event.time)) { if (input.time && !equals(input.time, event.time)) {
console.log(`Event time changed from: ${JSON.stringify(event.time)} to: ${JSON.stringify(input.time)}`) console.log(
`Event time changed from: ${JSON.stringify(
event.time
)} to: ${JSON.stringify(input.time)}`
)
event.time = input.time event.time = input.time
const existingOccurrences = await event.occurrences const existingOccurrences = await event.occurrences
await Promise.all(existingOccurrences.map(occ => EventOccurrence.delete(occ.id))) await Promise.all(
existingOccurrences.map(occ => EventOccurrence.delete(occ.id))
)
event.occurrences = Promise.resolve(event.getOccurrences()) event.occurrences = Promise.resolve(event.getOccurrences())
} }
if (input.infos) { if (input.infos) {
@ -143,31 +152,27 @@ const resolvers: ResolverMap = {
if (input.location) { if (input.location) {
event.location = input.location event.location = input.location
} }
if (input.meta) { if (input.tagNames) {
event.meta = input.meta event.tags = getTags(input.tagNames)
} }
if (input.location) { if (input.location) {
event.location = input.location event.location = input.location
} }
console.log(JSON.stringify(event,null,'\t')) console.log(JSON.stringify(event, null, "\t"))
return event.save() return event.save()
}, },
deleteEvent: async ( deleteEvent: async (_, id: string, context: Context): Promise<User> => {
_,
id: string,
context: Context,
): Promise<User> => {
const event = await Event.findOne(id) const event = await Event.findOne(id)
if ((await event.owner).id !== context.user.id) { if ((await event.owner).id !== context.user.id) {
throw Error("Only the owner can delete this event") throw Error("Only the owner can delete this event")
} }
console.log('will try to remove event now', event.id) console.log("will try to remove event now", event.id)
const removeEventResult = await Event.remove(event) const removeEventResult = await Event.remove(event)
console.log('removeEventResult', JSON.stringify(removeEventResult)) console.log("removeEventResult", JSON.stringify(removeEventResult))
return context.user return context.user
} },
} },
} }
export default resolvers export default resolvers

View File

@ -4,7 +4,8 @@ import {ConnectionOptions} from "typeorm"
export const testConfig: ConnectionOptions = { export const testConfig: ConnectionOptions = {
...config, ...config,
database: 'qpa-test', database: 'qpa-test',
dropSchema: true dropSchema: true,
synchronize: true
} }
export default testConfig export default testConfig

View File

@ -7,12 +7,25 @@ type User {
roles: [UserRole!] roles: [UserRole!]
} }
type Tag {
id: ID!
name: String!
translations: [Translations!]!
}
type TagTranslation {
language: String!
text: String!
}
type UserRole { type UserRole {
user: User! user: User!
type: RoleType! type: RoleType!
} }
scalar Date scalar Date
scalar RoleType scalar RoleType
scalar Translations
type UserSession { type UserSession {
hash: String! hash: String!
@ -49,11 +62,18 @@ type CalendarEvent {
status: EventStatus! status: EventStatus!
location: Location! location: Location!
occurrences: [EventOccurrence] occurrences: [EventOccurrence]
meta: EventMeta tags: [EventTag]
} }
type EventMeta { type EventTag {
tags: [String]! ID: ID!
name: String!
translations: [EventTagTranslation!]!
}
type EventTagTranslation {
language: String!
text: String!
} }
type EventOccurrence { type EventOccurrence {
@ -132,15 +152,11 @@ input EventLocationInput {
name: String name: String
} }
input EventMetaInput {
tags: [String]!
}
input CreateEventInput { input CreateEventInput {
time: EventTimeInput! time: EventTimeInput!
infos: [EventInformationInput]! infos: [EventInformationInput]!
location: EventLocationInput! location: EventLocationInput!
meta: EventMetaInput! tagNames: [String!]!
status: String! status: String!
} }
@ -149,7 +165,7 @@ input UpdateEventInput {
time: EventTimeInput time: EventTimeInput
infos: [EventInformationInput!] infos: [EventInformationInput!]
location: EventLocationInput location: EventLocationInput
meta: EventMetaInput tagNames: [String!]!
status: String status: String
} }
@ -168,6 +184,23 @@ type Error {
message: String! message: String!
} }
input CreateModifyEventTagTranslationInput {
language: String!
text: String!
}
input CreateEventTagInput {
name: String!
translations: [CreateModifyEventTagTranslationInput!]!
}
input ModifyEventTagInput {
id: ID!
name: String!
translations: [CreateModifyEventTagTranslationInput!]!
}
type Mutation { type Mutation {
# Auth # Auth
signup(input: SignupInput!): [Error] signup(input: SignupInput!): [Error]
@ -180,6 +213,10 @@ type Mutation {
createEvent(input: CreateEventInput!): CalendarEvent createEvent(input: CreateEventInput!): CalendarEvent
updateEvent(input: UpdateEventInput!): CalendarEvent updateEvent(input: UpdateEventInput!): CalendarEvent
deleteEvent(id: ID!): User! deleteEvent(id: ID!): User!
# Tags
createEventTag(input: CreateEventTagInput!): EventTag
modifyEventTag(input: ModifyEventTagInput!): EventTag
} }

View File

@ -5400,10 +5400,10 @@ type-is@^1.6.16, type-is@~1.6.17, type-is@~1.6.18:
media-typer "0.3.0" media-typer "0.3.0"
mime-types "~2.1.24" mime-types "~2.1.24"
typeorm@^0.2.18: typeorm@^0.2.20:
version "0.2.18" version "0.2.20"
resolved "https://registry.yarnpkg.com/typeorm/-/typeorm-0.2.18.tgz#8ae1d21104117724af41ddc11035c40a705e1de8" resolved "https://registry.yarnpkg.com/typeorm/-/typeorm-0.2.20.tgz#efb60f2e55a7d08fc365f281ec2a71c87a9ebba5"
integrity sha512-S553GwtG5ab268+VmaLCN7gKDqFPIzUw0eGMTobJ9yr0Np62Ojfx8j1Oa9bIeh5p7Pz1/kmGabAHoP1MYK05pA== integrity sha512-VxB+9qH8D+PM19MIx18Zs3Fqv/ZINnnQvUGmBEiLYDrB9etdSdamgSTCIhWdFNndeJ6ldH4jbD0Z6HWsepMPlA==
dependencies: dependencies:
app-root-path "^2.0.1" app-root-path "^2.0.1"
buffer "^5.1.0" buffer "^5.1.0"
@ -5420,10 +5420,10 @@ typeorm@^0.2.18:
yargonaut "^1.1.2" yargonaut "^1.1.2"
yargs "^13.2.1" yargs "^13.2.1"
typescript@^3.5.3: typescript@^3.7.2:
version "3.5.3" version "3.7.2"
resolved "https://registry.yarnpkg.com/typescript/-/typescript-3.5.3.tgz#c830f657f93f1ea846819e929092f5fe5983e977" resolved "https://registry.yarnpkg.com/typescript/-/typescript-3.7.2.tgz#27e489b95fa5909445e9fef5ee48d81697ad18fb"
integrity sha512-ACzBtm/PhXBDId6a6sDJfroT2pOWt/oOnk4/dElG5G33ZL776N3Y6/6bKZJBFpd+b05F3Ct9qDjMeJmRWtE2/g== integrity sha512-ml7V7JfiN2Xwvcer+XAf2csGO1bPBdRbFCkYBczNZggrBZ9c7G3riSUeJmqEU5uOtXNPMhE3n+R4FA/3YOAWOQ==
uglify-js@^3.1.4: uglify-js@^3.1.4:
version "3.6.0" version "3.6.0"