Translations and set tags to events

This commit is contained in:
Amit Jakubowicz 2019-11-08 11:16:55 +01:00
parent e61ed72dc9
commit f66a297e23
7 changed files with 72 additions and 38 deletions

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

@ -109,13 +109,14 @@ declare namespace GQL {
interface IEventTag {
__typename: 'EventTag';
ID: string;
id: string;
name: string;
translations: Array<IEventTagTranslation>;
}
interface IEventTagTranslation {
__typename: 'EventTagTranslation';
id: string;
language: string;
text: string;
}

View File

@ -5,24 +5,25 @@ import {
BaseEntity,
OneToMany,
ManyToOne,
} from "typeorm"
import {User} from "../Auth/User.entity"
import {DateTime} from "luxon"
import {rrulestr} from "rrule"
import { EventTag } from "./EventTag.entity";
ManyToMany, JoinTable
} from "typeorm";
import { User } from "../Auth/User.entity"
import { DateTime } from "luxon"
import { rrulestr } from "rrule"
import { EventTag } from "./EventTag.entity"
export const breakTime = (isoString: string) => {
const tSplit = isoString.split('T')
const tSplit = isoString.split("T")
return {
date: tSplit[0],
time: tSplit[1].substr(0, 8)
time: tSplit[1].substr(0, 8),
}
}
export class EventLocation {
@Column({nullable: true})
@Column({ nullable: true })
address?: string
@Column({nullable: true})
@Column({ nullable: true })
name?: string
}
@ -31,7 +32,6 @@ export class EventLocation {
* therefore all times are string and not Date objects. TODO: Add joi validation
*/
export class EventTime {
@Column()
timeZone: string
@ -42,10 +42,10 @@ export class EventTime {
@Column()
end: string
@Column({nullable: true})
@Column({ nullable: true })
recurrence?: string
@Column({nullable: true})
@Column({ nullable: true })
exceptions?: string
}
@ -58,23 +58,24 @@ export class Event extends BaseEntity {
owner: Promise<User>
@OneToMany(type => EventOccurrence, occurrence => occurrence.event, {
cascade: true
cascade: true,
})
occurrences: Promise<EventOccurrence[]>
@OneToMany(type => EventInformation, eventInfo => eventInfo.event, {
cascade: true
cascade: true,
})
infos: Promise<EventInformation[]>
@Column(type => EventTime)
time: EventTime
@OneToMany(type => EventTag, tag => tag.events)
@ManyToMany(type => EventTag, tag => tag.events)
@JoinTable()
tags: Promise<EventTag[]>
@Column({
default: "confirmed"
default: "confirmed",
})
status: string
@ -83,19 +84,29 @@ export class Event extends BaseEntity {
getOccurrences(): EventOccurrence[] {
const occurences = []
console.log('this.time.recurrence', this.time.recurrence)
console.log("this.time.recurrence", this.time.recurrence)
if (!this.time.recurrence) {
const occ = new EventOccurrence()
occ.during = `[${this.time.start},${this.time.end}]`
occurences.push(occ)
} else {
const dates = rrulestr(this.time.recurrence).all((occurenceDate, i) => i < 30)
const eventDuration = DateTime.fromISO(this.time.start).diff(DateTime.fromISO(this.time.end))
const dates = rrulestr(this.time.recurrence).all(
(occurenceDate, i) => i < 30
)
const eventDuration = DateTime.fromISO(this.time.start).diff(
DateTime.fromISO(this.time.end)
)
dates.forEach(recurrenceDateStart => {
const occ = new EventOccurrence()
const brokenRecurrenceDateStart = breakTime(recurrenceDateStart.toISOString())
const brokenRecurrenceDateEnd = breakTime(DateTime.fromJSDate(recurrenceDateStart).plus(eventDuration).toISO())
const brokenRecurrenceDateStart = breakTime(
recurrenceDateStart.toISOString()
)
const brokenRecurrenceDateEnd = breakTime(
DateTime.fromJSDate(recurrenceDateStart)
.plus(eventDuration)
.toISO()
)
const userInputStart = breakTime(this.time.start)
const userInputEnd = breakTime(this.time.end)
@ -103,7 +114,6 @@ export class Event extends BaseEntity {
const duringTo = `${brokenRecurrenceDateEnd.date} ${userInputEnd.time} ${this.time.timeZone}`
occ.during = `[${duringFrom}, ${duringTo}]`
})
}
occurences.forEach(occ => {
occ.start = this.time.start
@ -123,10 +133,10 @@ export class EventInformation {
language: string
@Column()
title: string
@Column({nullable: true})
@Column({ nullable: true })
description: string
@ManyToOne(type => Event, event => event.infos, {
onDelete: "CASCADE"
onDelete: "CASCADE",
})
event: Event
}
@ -138,11 +148,11 @@ export class EventOccurrence extends BaseEntity {
@ManyToOne(type => Event, event => event.occurrences, {
nullable: false,
onDelete: "CASCADE"
onDelete: "CASCADE",
})
event: Promise<Event>
@Column({type: "tstzrange", nullable: true})
@Column({ type: "tstzrange", nullable: true })
during: string
@Column()
@ -150,5 +160,4 @@ export class EventOccurrence extends BaseEntity {
@Column()
end: string
}

View File

@ -1,4 +1,13 @@
import { BaseEntity, Column, Entity, ManyToMany, ManyToOne, OneToMany, PrimaryGeneratedColumn } from "typeorm";
import {
BaseEntity,
Column,
Entity,
JoinTable,
ManyToMany,
ManyToOne,
OneToMany,
PrimaryGeneratedColumn
} from "typeorm";
import { Event } from "./Event.entity";
@Entity()
@ -12,7 +21,9 @@ export class EventTag extends BaseEntity {
@ManyToMany(type => Event, event => event.tags)
events: Promise<Event[]>
@OneToMany(type => EventTagTranslation, translation => translation.tag)
@OneToMany(type => EventTagTranslation, translation => translation.tag, {
cascade: true
})
translations: Promise<EventTagTranslation[]>
}
@ -21,7 +32,9 @@ export class EventTagTranslation extends BaseEntity {
@PrimaryGeneratedColumn("uuid")
id: number
@ManyToOne(type => EventTag, tag => tag.translations)
@ManyToOne(type => EventTag, tag => tag.translations, {
nullable: false
})
tag: Promise<EventTag>
@Column("varchar")

View File

@ -7,7 +7,7 @@ const adminOrThrow = async (context: Context) => {
throw new Error("Only admin can create and modify tags")
}
}
const resolvers: ResolverMap = {
export const tagResolvers: ResolverMap = {
Mutation: {
createEventTag: async (
_,
@ -20,6 +20,7 @@ const resolvers: ResolverMap = {
tag.name = req.input.name
tag.events = Promise.resolve([])
const translations: EventTagTranslation[] = req.input.translations.map(
translationInput => {
const translation = new EventTagTranslation()
@ -40,6 +41,7 @@ const resolvers: ResolverMap = {
) => {
await adminOrThrow(context)
const tag = await EventTag.findOne(req.input.id)
tag.name = req.input.name
const translations = await tag.translations
const langToExistingTranslation: {[lang: string]: EventTagTranslation} = {}
const langToInputTranslations: {[lang: string]: GQL.ICreateModifyEventTagTranslationInput} = {}
@ -49,6 +51,7 @@ const resolvers: ResolverMap = {
const removedTranslationsPromises = Object.keys(langToExistingTranslation).map((existingLang) => {
if (!langToInputTranslations[existingLang]) {
translations.splice(translations.findIndex(translation => translation.language === existingLang), 1)
return langToExistingTranslation[existingLang].remove()
}
return null
@ -58,11 +61,8 @@ const resolvers: ResolverMap = {
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 ) {
if (existingTranslation && matchingInput && existingTranslation.text !== matchingInput.text ) {
existingTranslation.text = matchingInput.text
return existingTranslation.save()
}
@ -74,13 +74,16 @@ const resolvers: ResolverMap = {
if (!langToExistingTranslation[inputLang]) {
const newTranslation = new EventTagTranslation()
newTranslation.tag = Promise.resolve(tag)
translations.push(newTranslation)
newTranslation.language = inputLang
newTranslation.text = langToInputTranslations[inputLang].text
return newTranslation
return newTranslation.save()
}
return null
})
await Promise.all(newTranslationsPromises.filter(Boolean))
tag.translations = Promise.resolve(translations)
return tag.save()
},
},
}

View File

@ -51,6 +51,9 @@ const resolvers: ResolverMap = {
const infos = await event.infos
return infos.find(info => info.language === req.lang)
},
tags: async (event: Event) => {
return event.tags
}
},
EventOccurrence: {

View File

@ -9,6 +9,7 @@ import {PostOffice} from "./post_office"
import {Session} from "./Auth/Session.entity"
import {Context} from "./@types/graphql-utils"
import SessionManager from "./Auth/SessionManager"
import { tagResolvers } from "./Calendar/tagsResolvers";
interface Dependencies {
typeormConnection: Connection
@ -46,12 +47,15 @@ export const createServer = async (dependencies: Dependencies) => {
...EventQueryResolvers,
...authResolvers.Query,
...UserQueryResolvers,
...tagResolvers.Query
},
Mutation: {
...resolvers.Mutation,
...EventResolversMutation,
...authResolvers.Mutation,
...UserMutationResolvers,
...tagResolvers.Mutation
},
UserSession: {
...authResolvers.UserSession

View File

@ -66,12 +66,13 @@ type CalendarEvent {
}
type EventTag {
ID: ID!
id: ID!
name: String!
translations: [EventTagTranslation!]!
}
type EventTagTranslation {
id: ID!
language: String!
text: String!
}