@things-factory/calendar 10.1.257 → 10.1.259
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist-client/tsconfig.tsbuildinfo +1 -1
- package/dist-server/service/attendee/attendee.d.ts +4 -0
- package/dist-server/service/attendee/attendee.js +10 -0
- package/dist-server/service/attendee/attendee.js.map +1 -1
- package/dist-server/service/calendar/calendar.d.ts +14 -0
- package/dist-server/service/calendar/calendar.js +5 -0
- package/dist-server/service/calendar/calendar.js.map +1 -1
- package/dist-server/service/event/event-import.d.ts +59 -0
- package/dist-server/service/event/event-import.js +169 -0
- package/dist-server/service/event/event-import.js.map +1 -0
- package/dist-server/service/event/event-mutation.d.ts +2 -1
- package/dist-server/service/event/event-mutation.js +19 -0
- package/dist-server/service/event/event-mutation.js.map +1 -1
- package/dist-server/service/event/event-type.d.ts +40 -0
- package/dist-server/service/event/event-type.js +149 -1
- package/dist-server/service/event/event-type.js.map +1 -1
- package/dist-server/service/event/event.d.ts +4 -0
- package/dist-server/service/event/event.js +14 -0
- package/dist-server/service/event/event.js.map +1 -1
- package/dist-server/service/index.d.ts +2 -1
- package/dist-server/service/index.js +5 -1
- package/dist-server/service/index.js.map +1 -1
- package/dist-server/tsconfig.tsbuildinfo +1 -1
- package/package.json +4 -4
- package/tests/open-doors.test.ts +1 -1
|
@@ -18,6 +18,10 @@ export declare class Attendee {
|
|
|
18
18
|
userId?: string;
|
|
19
19
|
email?: string;
|
|
20
20
|
name: string;
|
|
21
|
+
/** The answer to the invitation, as the calendar it was brought in from had it - none for a person named here. */
|
|
22
|
+
response?: string;
|
|
23
|
+
/** How long the person was actually in it, as an online meeting measured it - none when nothing measured it. */
|
|
24
|
+
attendedSeconds?: number;
|
|
21
25
|
createdAt?: Date;
|
|
22
26
|
updatedAt?: Date;
|
|
23
27
|
deletedAt?: Date;
|
|
@@ -67,6 +67,16 @@ tslib_1.__decorate([
|
|
|
67
67
|
(0, type_graphql_1.Field)({ description: 'Name shown for the attendee' }),
|
|
68
68
|
tslib_1.__metadata("design:type", String)
|
|
69
69
|
], Attendee.prototype, "name", void 0);
|
|
70
|
+
tslib_1.__decorate([
|
|
71
|
+
(0, typeorm_1.Column)({ nullable: true }),
|
|
72
|
+
(0, type_graphql_1.Field)({ nullable: true, description: 'The answer to the invitation where the event was brought in from: accepted, tentativelyAccepted, declined, notResponded or organizer' }),
|
|
73
|
+
tslib_1.__metadata("design:type", String)
|
|
74
|
+
], Attendee.prototype, "response", void 0);
|
|
75
|
+
tslib_1.__decorate([
|
|
76
|
+
(0, typeorm_1.Column)({ type: 'int', nullable: true }),
|
|
77
|
+
(0, type_graphql_1.Field)(type => type_graphql_1.Int, { nullable: true, description: 'Seconds the person was actually in the event, as an online meeting measured it; empty when not measured' }),
|
|
78
|
+
tslib_1.__metadata("design:type", Number)
|
|
79
|
+
], Attendee.prototype, "attendedSeconds", void 0);
|
|
70
80
|
tslib_1.__decorate([
|
|
71
81
|
(0, typeorm_1.CreateDateColumn)(),
|
|
72
82
|
(0, type_graphql_1.Field)({ nullable: true, description: 'Creation timestamp' }),
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"attendee.js","sourceRoot":"","sources":["../../../server/service/attendee/attendee.ts"],"names":[],"mappings":";;;;AAAA,qCAAoJ;AACpJ,+
|
|
1
|
+
{"version":3,"file":"attendee.js","sourceRoot":"","sources":["../../../server/service/attendee/attendee.ts"],"names":[],"mappings":"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","sourcesContent":["import { Column, CreateDateColumn, DeleteDateColumn, Entity, Index, ManyToOne, PrimaryGeneratedColumn, RelationId, UpdateDateColumn } from 'typeorm'\nimport { Field, ID, Int, ObjectType } from 'type-graphql'\n\nimport { Domain } from '@things-factory/shell'\nimport { User } from '@things-factory/auth-base'\n\nimport { Event } from '../event/event'\nimport { EventSeries } from '../event-series/event-series'\n\n/**\n * A person named on an event or a series — a user who enters the domain, or someone known only by an e-mail address.\n * Naming a person grants nothing: no role, no privilege, no reading of the event.\n */\n@Entity()\n@Index('ix_attendee_0', (attendee: Attendee) => [attendee.domain, attendee.event, attendee.user], {\n unique: true,\n where: '\"deleted_at\" IS NULL AND \"event_id\" IS NOT NULL AND \"user_id\" IS NOT NULL'\n})\n@Index('ix_attendee_1', (attendee: Attendee) => [attendee.domain, attendee.event, attendee.email], {\n unique: true,\n where: '\"deleted_at\" IS NULL AND \"event_id\" IS NOT NULL AND \"email\" IS NOT NULL'\n})\n@Index('ix_attendee_2', (attendee: Attendee) => [attendee.domain, attendee.series, attendee.user], {\n unique: true,\n where: '\"deleted_at\" IS NULL AND \"series_id\" IS NOT NULL AND \"user_id\" IS NOT NULL'\n})\n@Index('ix_attendee_3', (attendee: Attendee) => [attendee.domain, attendee.series, attendee.email], {\n unique: true,\n where: '\"deleted_at\" IS NULL AND \"series_id\" IS NOT NULL AND \"email\" IS NOT NULL'\n})\n@ObjectType({ description: 'A person named on an event or a series; naming grants nothing' })\nexport class Attendee {\n @PrimaryGeneratedColumn('uuid')\n @Field(type => ID, { description: 'Unique identifier of the attendee row' })\n readonly id: string\n\n @ManyToOne(type => Domain)\n @Field(type => Domain, { nullable: true, description: 'The domain the attendee row belongs to' })\n domain?: Domain\n\n @RelationId((attendee: Attendee) => attendee.domain)\n domainId?: string\n\n @ManyToOne(type => Event, { nullable: true })\n event?: Event\n\n @RelationId((attendee: Attendee) => attendee.event)\n @Field({ nullable: true, description: 'Id of the event the person is named on' })\n eventId?: string\n\n @ManyToOne(type => EventSeries, { nullable: true })\n series?: EventSeries\n\n @RelationId((attendee: Attendee) => attendee.series)\n @Field({ nullable: true, description: 'Id of the series the person is named on' })\n seriesId?: string\n\n @ManyToOne(type => User, { nullable: true })\n @Field(type => User, { nullable: true, description: 'The user, when the attendee signs in to the domain' })\n user?: User\n\n @RelationId((attendee: Attendee) => attendee.user)\n @Field({ nullable: true, description: 'Id of the user, when the attendee signs in to the domain' })\n userId?: string\n\n @Column({ nullable: true })\n @Field({ nullable: true, description: 'E-mail address, when the attendee is not a user of the domain' })\n email?: string\n\n @Column()\n @Field({ description: 'Name shown for the attendee' })\n name: string\n\n /** The answer to the invitation, as the calendar it was brought in from had it - none for a person named here. */\n @Column({ nullable: true })\n @Field({ nullable: true, description: 'The answer to the invitation where the event was brought in from: accepted, tentativelyAccepted, declined, notResponded or organizer' })\n response?: string\n\n /** How long the person was actually in it, as an online meeting measured it - none when nothing measured it. */\n @Column({ type: 'int', nullable: true })\n @Field(type => Int, { nullable: true, description: 'Seconds the person was actually in the event, as an online meeting measured it; empty when not measured' })\n attendedSeconds?: number\n\n @CreateDateColumn()\n @Field({ nullable: true, description: 'Creation timestamp' })\n createdAt?: Date\n\n @UpdateDateColumn()\n @Field({ nullable: true, description: 'Last update timestamp' })\n updatedAt?: Date\n\n @DeleteDateColumn()\n @Field({ nullable: true, description: 'Soft delete timestamp' })\n deletedAt?: Date\n\n @ManyToOne(type => User, { nullable: true })\n @Field(type => User, { nullable: true, description: 'Who named the attendee' })\n creator?: User\n\n @RelationId((attendee: Attendee) => attendee.creator)\n creatorId?: string\n\n @ManyToOne(type => User, { nullable: true })\n @Field(type => User, { nullable: true, description: 'Who last changed the attendee row' })\n updater?: User\n\n @RelationId((attendee: Attendee) => attendee.updater)\n updaterId?: string\n}\n"]}
|
|
@@ -18,6 +18,20 @@ export declare class Calendar {
|
|
|
18
18
|
description?: string;
|
|
19
19
|
active: boolean;
|
|
20
20
|
kind: CalendarKind;
|
|
21
|
+
/**
|
|
22
|
+
* The last reading of another calendar brought in here (`importEvents`): when, from where, the mailboxes it read and
|
|
23
|
+
* those it could not - a person in neither was not asked, which is not the same as read.
|
|
24
|
+
*/
|
|
25
|
+
lastImport?: {
|
|
26
|
+
at: string;
|
|
27
|
+
source: string;
|
|
28
|
+
read: number;
|
|
29
|
+
mailboxes: string[];
|
|
30
|
+
failed: {
|
|
31
|
+
mailbox: string;
|
|
32
|
+
reason: string | null;
|
|
33
|
+
}[];
|
|
34
|
+
} | null;
|
|
21
35
|
createdAt?: Date;
|
|
22
36
|
updatedAt?: Date;
|
|
23
37
|
deletedAt?: Date;
|
|
@@ -63,6 +63,11 @@ tslib_1.__decorate([
|
|
|
63
63
|
(0, type_graphql_1.Field)(type => CalendarKind, { description: 'What the events of the calendar mean for the working days' }),
|
|
64
64
|
tslib_1.__metadata("design:type", String)
|
|
65
65
|
], Calendar.prototype, "kind", void 0);
|
|
66
|
+
tslib_1.__decorate([
|
|
67
|
+
(0, typeorm_1.Column)('simple-json', { nullable: true }),
|
|
68
|
+
(0, type_graphql_1.Field)(type => shell_1.ScalarObject, { nullable: true, description: 'The last reading brought in: { at, source, read, mailboxes, failed: [{ mailbox, reason }] }' }),
|
|
69
|
+
tslib_1.__metadata("design:type", Object)
|
|
70
|
+
], Calendar.prototype, "lastImport", void 0);
|
|
66
71
|
tslib_1.__decorate([
|
|
67
72
|
(0, typeorm_1.CreateDateColumn)(),
|
|
68
73
|
(0, type_graphql_1.Field)({ nullable: true, description: 'Creation timestamp' }),
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"calendar.js","sourceRoot":"","sources":["../../../server/service/calendar/calendar.ts"],"names":[],"mappings":";;;;AAAA,qCAAoJ;AACpJ,+CAAsE;AAEtE,
|
|
1
|
+
{"version":3,"file":"calendar.js","sourceRoot":"","sources":["../../../server/service/calendar/calendar.ts"],"names":[],"mappings":";;;;AAAA,qCAAoJ;AACpJ,+CAAsE;AAEtE,iDAA4D;AAC5D,yDAAgD;AAEhD,0EAA0E;AAC1E,IAAY,YAIX;AAJD,WAAY,YAAY;IACtB,mCAAmB,CAAA;IACnB,qCAAqB,CAAA;IACrB,mDAAmC,CAAA;AACrC,CAAC,EAJW,YAAY,4BAAZ,YAAY,QAIvB;AAED,IAAA,+BAAgB,EAAC,YAAY,EAAE;IAC7B,IAAI,EAAE,cAAc;IACpB,WAAW,EAAE,yDAAyD;IACtE,YAAY,EAAE;QACZ,OAAO,EAAE,EAAE,WAAW,EAAE,yDAAyD,EAAE;QACnF,QAAQ,EAAE,EAAE,WAAW,EAAE,iFAAiF,EAAE;QAC5G,eAAe,EAAE,EAAE,WAAW,EAAE,4EAA4E,EAAE;KAC/G;CACF,CAAC,CAAA;AAEF;;;GAGG;AAII,IAAM,QAAQ,GAAd,MAAM,QAAQ;CA6DpB,CAAA;AA7DY,4BAAQ;AAGV;IAFR,IAAA,gCAAsB,EAAC,MAAM,CAAC;IAC9B,IAAA,oBAAK,EAAC,IAAI,CAAC,EAAE,CAAC,iBAAE,EAAE,EAAE,WAAW,EAAE,mCAAmC,EAAE,CAAC;;oCACrD;AAInB;IAFC,IAAA,mBAAS,EAAC,IAAI,CAAC,EAAE,CAAC,cAAM,CAAC;IACzB,IAAA,oBAAK,EAAC,IAAI,CAAC,EAAE,CAAC,cAAM,EAAE,EAAE,QAAQ,EAAE,IAAI,EAAE,WAAW,EAAE,oCAAoC,EAAE,CAAC;sCACpF,cAAM;wCAAA;AAGf;IADC,IAAA,oBAAU,EAAC,CAAC,QAAkB,EAAE,EAAE,CAAC,QAAQ,CAAC,MAAM,CAAC;;0CACnC;AAIjB;IAFC,IAAA,gBAAM,GAAE;IACR,IAAA,oBAAK,EAAC,EAAE,WAAW,EAAE,4CAA4C,EAAE,CAAC;;sCACzD;AAIZ;IAFC,IAAA,gBAAM,EAAC,EAAE,IAAI,EAAE,MAAM,EAAE,QAAQ,EAAE,IAAI,EAAE,CAAC;IACxC,IAAA,oBAAK,EAAC,EAAE,QAAQ,EAAE,IAAI,EAAE,WAAW,EAAE,0BAA0B,EAAE,CAAC;;6CAC/C;AAIpB;IAFC,IAAA,gBAAM,EAAC,EAAE,OAAO,EAAE,IAAI,EAAE,CAAC;IACzB,IAAA,oBAAK,EAAC,EAAE,WAAW,EAAE,gCAAgC,EAAE,CAAC;;wCAC1C;AAIf;IAFC,IAAA,gBAAM,EAAC,EAAE,OAAO,EAAE,YAAY,CAAC,OAAO,EAAE,CAAC;IACzC,IAAA,oBAAK,EAAC,IAAI,CAAC,EAAE,CAAC,YAAY,EAAE,EAAE,WAAW,EAAE,2DAA2D,EAAE,CAAC;;sCACxF;AAQlB;IAFC,IAAA,gBAAM,EAAC,aAAa,EAAE,EAAE,QAAQ,EAAE,IAAI,EAAE,CAAC;IACzC,IAAA,oBAAK,EAAC,IAAI,CAAC,EAAE,CAAC,oBAAY,EAAE,EAAE,QAAQ,EAAE,IAAI,EAAE,WAAW,EAAE,6FAA6F,EAAE,CAAC;;4CACjB;AAI3I;IAFC,IAAA,0BAAgB,GAAE;IAClB,IAAA,oBAAK,EAAC,EAAE,QAAQ,EAAE,IAAI,EAAE,WAAW,EAAE,oBAAoB,EAAE,CAAC;sCACjD,IAAI;2CAAA;AAIhB;IAFC,IAAA,0BAAgB,GAAE;IAClB,IAAA,oBAAK,EAAC,EAAE,QAAQ,EAAE,IAAI,EAAE,WAAW,EAAE,uBAAuB,EAAE,CAAC;sCACpD,IAAI;2CAAA;AAIhB;IAFC,IAAA,0BAAgB,GAAE;IAClB,IAAA,oBAAK,EAAC,EAAE,QAAQ,EAAE,IAAI,EAAE,WAAW,EAAE,uBAAuB,EAAE,CAAC;sCACpD,IAAI;2CAAA;AAIhB;IAFC,IAAA,mBAAS,EAAC,IAAI,CAAC,EAAE,CAAC,gBAAI,EAAE,EAAE,QAAQ,EAAE,IAAI,EAAE,CAAC;IAC3C,IAAA,oBAAK,EAAC,IAAI,CAAC,EAAE,CAAC,gBAAI,EAAE,EAAE,QAAQ,EAAE,IAAI,EAAE,WAAW,EAAE,0BAA0B,EAAE,CAAC;sCACvE,gBAAI;yCAAA;AAGd;IADC,IAAA,oBAAU,EAAC,CAAC,QAAkB,EAAE,EAAE,CAAC,QAAQ,CAAC,OAAO,CAAC;;2CACnC;AAIlB;IAFC,IAAA,mBAAS,EAAC,IAAI,CAAC,EAAE,CAAC,gBAAI,EAAE,EAAE,QAAQ,EAAE,IAAI,EAAE,CAAC;IAC3C,IAAA,oBAAK,EAAC,IAAI,CAAC,EAAE,CAAC,gBAAI,EAAE,EAAE,QAAQ,EAAE,IAAI,EAAE,WAAW,EAAE,+BAA+B,EAAE,CAAC;sCAC5E,gBAAI;yCAAA;AAGd;IADC,IAAA,oBAAU,EAAC,CAAC,QAAkB,EAAE,EAAE,CAAC,QAAQ,CAAC,OAAO,CAAC;;2CACnC;mBA5DP,QAAQ;IAHpB,IAAA,gBAAM,GAAE;IACR,IAAA,eAAK,EAAC,eAAe,EAAE,CAAC,QAAkB,EAAE,EAAE,CAAC,CAAC,QAAQ,CAAC,MAAM,EAAE,QAAQ,CAAC,IAAI,CAAC,EAAE,EAAE,MAAM,EAAE,IAAI,EAAE,KAAK,EAAE,sBAAsB,EAAE,CAAC;IACjI,IAAA,yBAAU,EAAC,EAAE,WAAW,EAAE,qCAAqC,EAAE,CAAC;GACtD,QAAQ,CA6DpB","sourcesContent":["import { Column, CreateDateColumn, DeleteDateColumn, Entity, Index, ManyToOne, PrimaryGeneratedColumn, RelationId, UpdateDateColumn } from 'typeorm'\nimport { Field, ID, ObjectType, registerEnumType } from 'type-graphql'\n\nimport { Domain, ScalarObject } from '@things-factory/shell'\nimport { User } from '@things-factory/auth-base'\n\n/** What a calendar's events mean for the working days (ADR-0179 결정 2). */\nexport enum CalendarKind {\n GENERAL = 'GENERAL',\n DAYS_OFF = 'DAYS_OFF',\n PUBLIC_HOLIDAYS = 'PUBLIC_HOLIDAYS'\n}\n\nregisterEnumType(CalendarKind, {\n name: 'CalendarKind',\n description: 'What the events of a calendar mean for the working days',\n valuesConfig: {\n GENERAL: { description: 'Meetings and happenings; the working days do not change' },\n DAYS_OFF: { description: \"The company's own days off; a day one of its events covers is not a working day\" },\n PUBLIC_HOLIDAYS: { description: 'The public holidays, one per domain, filled by an import; not working days' }\n }\n})\n\n/**\n * A named group of events in a domain — \"management meetings\", \"plant holidays\". Events and series may belong to\n * one; it is optional.\n */\n@Entity()\n@Index('ix_calendar_0', (calendar: Calendar) => [calendar.domain, calendar.name], { unique: true, where: '\"deleted_at\" IS NULL' })\n@ObjectType({ description: 'A named group of events in a domain' })\nexport class Calendar {\n @PrimaryGeneratedColumn('uuid')\n @Field(type => ID, { description: 'Unique identifier of the calendar' })\n readonly id: string\n\n @ManyToOne(type => Domain)\n @Field(type => Domain, { nullable: true, description: 'The domain the calendar belongs to' })\n domain?: Domain\n\n @RelationId((calendar: Calendar) => calendar.domain)\n domainId?: string\n\n @Column()\n @Field({ description: 'Name of the calendar, unique in the domain' })\n name: string\n\n @Column({ type: 'text', nullable: true })\n @Field({ nullable: true, description: 'What the calendar is for' })\n description?: string\n\n @Column({ default: true })\n @Field({ description: 'Whether the calendar is in use' })\n active: boolean\n\n @Column({ default: CalendarKind.GENERAL })\n @Field(type => CalendarKind, { description: 'What the events of the calendar mean for the working days' })\n kind: CalendarKind\n\n /**\n * The last reading of another calendar brought in here (`importEvents`): when, from where, the mailboxes it read and\n * those it could not - a person in neither was not asked, which is not the same as read.\n */\n @Column('simple-json', { nullable: true })\n @Field(type => ScalarObject, { nullable: true, description: 'The last reading brought in: { at, source, read, mailboxes, failed: [{ mailbox, reason }] }' })\n lastImport?: { at: string; source: string; read: number; mailboxes: string[]; failed: { mailbox: string; reason: string | null }[] } | null\n\n @CreateDateColumn()\n @Field({ nullable: true, description: 'Creation timestamp' })\n createdAt?: Date\n\n @UpdateDateColumn()\n @Field({ nullable: true, description: 'Last update timestamp' })\n updatedAt?: Date\n\n @DeleteDateColumn()\n @Field({ nullable: true, description: 'Soft delete timestamp' })\n deletedAt?: Date\n\n @ManyToOne(type => User, { nullable: true })\n @Field(type => User, { nullable: true, description: 'Who created the calendar' })\n creator?: User\n\n @RelationId((calendar: Calendar) => calendar.creator)\n creatorId?: string\n\n @ManyToOne(type => User, { nullable: true })\n @Field(type => User, { nullable: true, description: 'Who last changed the calendar' })\n updater?: User\n\n @RelationId((calendar: Calendar) => calendar.updater)\n updaterId?: string\n}\n"]}
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
import { EntityManager } from 'typeorm';
|
|
2
|
+
import { User } from '@things-factory/auth-base';
|
|
3
|
+
import { Domain } from '@things-factory/shell';
|
|
4
|
+
export interface ImportedAttendee {
|
|
5
|
+
email: string;
|
|
6
|
+
name?: string | null;
|
|
7
|
+
response?: string | null;
|
|
8
|
+
}
|
|
9
|
+
export interface ImportedEvent {
|
|
10
|
+
/** The event's id in the other calendar. */
|
|
11
|
+
id: string;
|
|
12
|
+
subject?: string | null;
|
|
13
|
+
start: string;
|
|
14
|
+
end: string;
|
|
15
|
+
organizer?: string | null;
|
|
16
|
+
attendees?: ImportedAttendee[] | null;
|
|
17
|
+
/** Seconds each person was in it, when the other calendar measured it. */
|
|
18
|
+
attendance?: {
|
|
19
|
+
email: string;
|
|
20
|
+
seconds: number;
|
|
21
|
+
}[] | null;
|
|
22
|
+
location?: string | null;
|
|
23
|
+
cancelled?: boolean | null;
|
|
24
|
+
}
|
|
25
|
+
/** One event read from several people's calendars is one event: the first reading, with every measured attendance. */
|
|
26
|
+
export declare function mergedReadings(events: readonly ImportedEvent[]): ImportedEvent[];
|
|
27
|
+
/** The people of an event by address - its organizer first, each once - with their answer and measured seconds. */
|
|
28
|
+
export declare function peopleOf(event: ImportedEvent): {
|
|
29
|
+
email: string;
|
|
30
|
+
name: string;
|
|
31
|
+
response: string | null;
|
|
32
|
+
seconds: number | null;
|
|
33
|
+
}[];
|
|
34
|
+
/** What one reading of the other calendar covered: its window and the mailboxes it could read. */
|
|
35
|
+
export interface ImportReading {
|
|
36
|
+
from: string;
|
|
37
|
+
to: string;
|
|
38
|
+
mailboxes: string[];
|
|
39
|
+
failed?: {
|
|
40
|
+
mailbox: string;
|
|
41
|
+
reason?: string | null;
|
|
42
|
+
}[] | null;
|
|
43
|
+
}
|
|
44
|
+
/**
|
|
45
|
+
* Brings `events` from the calendar `source` onto the domain calendar `calendarName`. Answers how many were added,
|
|
46
|
+
* changed, left and cancelled. Given the `reading` they came from, an event of that source in its window that a mailbox
|
|
47
|
+
* it read holds and the reading no longer has was removed there: it is cancelled here - unless it was held.
|
|
48
|
+
*/
|
|
49
|
+
export declare function importEvents(tx: EntityManager, domain: Domain, user: User, input: {
|
|
50
|
+
calendarName: string;
|
|
51
|
+
source: string;
|
|
52
|
+
events: ImportedEvent[];
|
|
53
|
+
reading?: ImportReading | null;
|
|
54
|
+
}): Promise<{
|
|
55
|
+
created: number;
|
|
56
|
+
updated: number;
|
|
57
|
+
left: number;
|
|
58
|
+
cancelled: number;
|
|
59
|
+
}>;
|
|
@@ -0,0 +1,169 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.mergedReadings = mergedReadings;
|
|
4
|
+
exports.peopleOf = peopleOf;
|
|
5
|
+
exports.importEvents = importEvents;
|
|
6
|
+
const typeorm_1 = require("typeorm");
|
|
7
|
+
const auth_base_1 = require("@things-factory/auth-base");
|
|
8
|
+
const shell_1 = require("@things-factory/shell");
|
|
9
|
+
const attendee_1 = require("../attendee/attendee");
|
|
10
|
+
const attendee_service_1 = require("../attendee/attendee-service");
|
|
11
|
+
const calendar_1 = require("../calendar/calendar");
|
|
12
|
+
const event_1 = require("./event");
|
|
13
|
+
const event_rules_1 = require("./event-rules");
|
|
14
|
+
const EMAIL = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
|
|
15
|
+
const NO_TITLE = '(no title)';
|
|
16
|
+
/** One event read from several people's calendars is one event: the first reading, with every measured attendance. */
|
|
17
|
+
function mergedReadings(events) {
|
|
18
|
+
const merged = new Map();
|
|
19
|
+
for (const one of events) {
|
|
20
|
+
if (!one?.id)
|
|
21
|
+
continue;
|
|
22
|
+
const held = merged.get(one.id);
|
|
23
|
+
if (!held) {
|
|
24
|
+
merged.set(one.id, { ...one, attendance: one.attendance ? [...one.attendance] : null });
|
|
25
|
+
continue;
|
|
26
|
+
}
|
|
27
|
+
if (one.attendance?.length) {
|
|
28
|
+
const seen = new Map((held.attendance ?? []).map(row => [row.email.toLowerCase(), row]));
|
|
29
|
+
for (const row of one.attendance)
|
|
30
|
+
if (!seen.has(row.email.toLowerCase()))
|
|
31
|
+
seen.set(row.email.toLowerCase(), row);
|
|
32
|
+
held.attendance = [...seen.values()];
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
return [...merged.values()];
|
|
36
|
+
}
|
|
37
|
+
/** The people of an event by address - its organizer first, each once - with their answer and measured seconds. */
|
|
38
|
+
function peopleOf(event) {
|
|
39
|
+
const seconds = new Map((event.attendance ?? []).map(row => [String(row.email).toLowerCase(), Math.max(0, Math.round(Number(row.seconds) || 0))]));
|
|
40
|
+
const people = new Map();
|
|
41
|
+
const add = (email, name, response) => {
|
|
42
|
+
const address = String(email ?? '').trim().toLowerCase();
|
|
43
|
+
if (!EMAIL.test(address) || people.has(address))
|
|
44
|
+
return;
|
|
45
|
+
people.set(address, { email: address, name: String(name ?? '').trim() || address, response: response ?? null, seconds: seconds.has(address) ? seconds.get(address) : null });
|
|
46
|
+
};
|
|
47
|
+
add(event.organizer, null, 'organizer');
|
|
48
|
+
for (const one of event.attendees ?? [])
|
|
49
|
+
add(one.email, one.name, one.response);
|
|
50
|
+
return [...people.values()];
|
|
51
|
+
}
|
|
52
|
+
/** The domain calendar events are brought in on - made, as a calendar of meetings, when the domain has none by that name. */
|
|
53
|
+
async function calendarNamed(tx, domain, user, name) {
|
|
54
|
+
const repository = tx.getRepository(calendar_1.Calendar);
|
|
55
|
+
const found = await repository.findOne({ where: { domain: { id: domain.id }, name } });
|
|
56
|
+
if (found) {
|
|
57
|
+
if (found.kind !== calendar_1.CalendarKind.GENERAL)
|
|
58
|
+
throw new shell_1.Refusal('event-import-calendar-not-general', { name }, `'${name}' is not a calendar of meetings`);
|
|
59
|
+
return found;
|
|
60
|
+
}
|
|
61
|
+
return repository.save(repository.create({ domain, name, kind: calendar_1.CalendarKind.GENERAL, active: true, creator: user, updater: user }));
|
|
62
|
+
}
|
|
63
|
+
/**
|
|
64
|
+
* Brings `events` from the calendar `source` onto the domain calendar `calendarName`. Answers how many were added,
|
|
65
|
+
* changed, left and cancelled. Given the `reading` they came from, an event of that source in its window that a mailbox
|
|
66
|
+
* it read holds and the reading no longer has was removed there: it is cancelled here - unless it was held.
|
|
67
|
+
*/
|
|
68
|
+
async function importEvents(tx, domain, user, input) {
|
|
69
|
+
const calendarName = String(input.calendarName ?? '').trim();
|
|
70
|
+
const source = String(input.source ?? '').trim().toUpperCase();
|
|
71
|
+
if (!calendarName)
|
|
72
|
+
throw new shell_1.Refusal('event-import-calendar-required', {}, 'Name the calendar to bring the events onto');
|
|
73
|
+
if (!source)
|
|
74
|
+
throw new shell_1.Refusal('event-import-source-required', {}, 'Name the calendar the events come from');
|
|
75
|
+
const calendar = await calendarNamed(tx, domain, user, calendarName);
|
|
76
|
+
const events = mergedReadings(input.events ?? []);
|
|
77
|
+
/* The domain's users among the addresses, read once. */
|
|
78
|
+
const addresses = [...new Set(events.flatMap(one => peopleOf(one).map(person => person.email)))];
|
|
79
|
+
const found = addresses.length ? await tx.getRepository(auth_base_1.User).find({ where: { email: (0, typeorm_1.In)(addresses) } }) : [];
|
|
80
|
+
const users = new Map();
|
|
81
|
+
for (const one of found)
|
|
82
|
+
if (one.status === auth_base_1.UserStatus.ACTIVATED && (await (0, auth_base_1.entersDomainAsAssignee)(domain, one, tx)))
|
|
83
|
+
users.set(String(one.email).toLowerCase(), one);
|
|
84
|
+
const repository = tx.getRepository(event_1.Event);
|
|
85
|
+
let created = 0;
|
|
86
|
+
let updated = 0;
|
|
87
|
+
let left = 0;
|
|
88
|
+
for (const one of events) {
|
|
89
|
+
const startAt = new Date(one.start);
|
|
90
|
+
const endAt = new Date(one.end);
|
|
91
|
+
if (isNaN(startAt.getTime()) || isNaN(endAt.getTime()) || !(endAt > startAt)) {
|
|
92
|
+
left += 1;
|
|
93
|
+
continue;
|
|
94
|
+
}
|
|
95
|
+
const people = peopleOf(one);
|
|
96
|
+
const organizer = users.get(String(one.organizer ?? '').toLowerCase()) ?? null;
|
|
97
|
+
const held = await repository.findOne({ where: { domain: { id: domain.id }, source, externalId: one.id } });
|
|
98
|
+
const status = one.cancelled ? event_rules_1.EventStatus.CANCELLED : held?.status === event_rules_1.EventStatus.HELD ? event_rules_1.EventStatus.HELD : event_rules_1.EventStatus.SCHEDULED;
|
|
99
|
+
const facts = {
|
|
100
|
+
calendar,
|
|
101
|
+
startAt,
|
|
102
|
+
endAt,
|
|
103
|
+
status,
|
|
104
|
+
name: String(one.subject ?? '').trim().slice(0, 300) || NO_TITLE,
|
|
105
|
+
location: one.location?.trim() || null,
|
|
106
|
+
organizer,
|
|
107
|
+
updater: user
|
|
108
|
+
};
|
|
109
|
+
const saved = held
|
|
110
|
+
? (await repository.save({ ...held, ...facts }), held)
|
|
111
|
+
: await repository.save(repository.create({ domain, series: null, scheduledAt: startAt, source, externalId: one.id, creator: user, ...facts }));
|
|
112
|
+
held ? (updated += 1) : (created += 1);
|
|
113
|
+
const entries = people.map(person => {
|
|
114
|
+
const known = users.get(person.email);
|
|
115
|
+
return known ? { key: (0, attendee_service_1.attendeeKey)({ userId: known.id }), userId: known.id, name: known.name || person.name } : { key: (0, attendee_service_1.attendeeKey)({ email: person.email }), email: person.email, name: person.name };
|
|
116
|
+
});
|
|
117
|
+
await (0, attendee_service_1.replaceAttendees)(tx, domain, user, { event: { id: saved.id } }, entries);
|
|
118
|
+
const rows = await (0, attendee_service_1.attendeesOf)(tx, domain, { event: { id: saved.id } });
|
|
119
|
+
const byKey = new Map(people.map((person, index) => [entries[index].key, person]));
|
|
120
|
+
for (const row of rows) {
|
|
121
|
+
const person = byKey.get((0, attendee_service_1.attendeeKey)(row));
|
|
122
|
+
if (!person)
|
|
123
|
+
continue;
|
|
124
|
+
const change = {};
|
|
125
|
+
if ((row.response ?? null) !== person.response)
|
|
126
|
+
change.response = person.response;
|
|
127
|
+
if (person.seconds !== null && row.attendedSeconds !== person.seconds)
|
|
128
|
+
change.attendedSeconds = person.seconds;
|
|
129
|
+
if (Object.keys(change).length)
|
|
130
|
+
await tx.getRepository(attendee_1.Attendee).update({ id: row.id }, { ...change, updater: user });
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
const cancelled = input.reading ? await cancelRemoved(tx, domain, user, source, input.reading, new Set(events.map(one => one.id))) : 0;
|
|
134
|
+
if (input.reading) {
|
|
135
|
+
const lastImport = {
|
|
136
|
+
at: new Date().toISOString(),
|
|
137
|
+
source,
|
|
138
|
+
read: (input.reading.mailboxes ?? []).length,
|
|
139
|
+
mailboxes: (input.reading.mailboxes ?? []).map(one => String(one).toLowerCase()),
|
|
140
|
+
failed: (input.reading.failed ?? []).map(one => ({ mailbox: String(one.mailbox).toLowerCase(), reason: one.reason ?? null }))
|
|
141
|
+
};
|
|
142
|
+
await tx.getRepository(calendar_1.Calendar).update({ id: calendar.id }, { lastImport, updater: user });
|
|
143
|
+
}
|
|
144
|
+
return { created, updated, left, cancelled };
|
|
145
|
+
}
|
|
146
|
+
/** The events of `source` in the reading's window, held by a mailbox it read, that it did not find - removed there. */
|
|
147
|
+
async function cancelRemoved(tx, domain, user, source, reading, found) {
|
|
148
|
+
const from = new Date(reading.from);
|
|
149
|
+
const to = new Date(reading.to);
|
|
150
|
+
const read = new Set((reading.mailboxes ?? []).map(one => String(one).trim().toLowerCase()).filter(Boolean));
|
|
151
|
+
if (isNaN(from.getTime()) || isNaN(to.getTime()) || !read.size)
|
|
152
|
+
return 0;
|
|
153
|
+
const standing = await tx.getRepository(event_1.Event).find({
|
|
154
|
+
where: { domain: { id: domain.id }, source, startAt: (0, typeorm_1.Between)(from, to), status: (0, typeorm_1.In)([event_rules_1.EventStatus.SCHEDULED, event_rules_1.EventStatus.POSTPONED]) },
|
|
155
|
+
relations: ['organizer']
|
|
156
|
+
});
|
|
157
|
+
const gone = standing.filter(one => one.externalId && !found.has(one.externalId));
|
|
158
|
+
let cancelled = 0;
|
|
159
|
+
for (const event of gone) {
|
|
160
|
+
const rows = await tx.getRepository(attendee_1.Attendee).find({ where: { domain: { id: domain.id }, event: { id: event.id } }, relations: ['user'] });
|
|
161
|
+
const addresses = [event.organizer?.email, ...rows.map(row => row.email ?? row.user?.email)].map(one => String(one ?? '').toLowerCase());
|
|
162
|
+
if (!addresses.some(one => read.has(one)))
|
|
163
|
+
continue;
|
|
164
|
+
await tx.getRepository(event_1.Event).update({ id: event.id }, { status: event_rules_1.EventStatus.CANCELLED, statusReason: 'removed from the calendar it was brought in from', updater: user });
|
|
165
|
+
cancelled += 1;
|
|
166
|
+
}
|
|
167
|
+
return cancelled;
|
|
168
|
+
}
|
|
169
|
+
//# sourceMappingURL=event-import.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"event-import.js","sourceRoot":"","sources":["../../../server/service/event/event-import.ts"],"names":[],"mappings":"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","sourcesContent":["import { Between, EntityManager, In } from 'typeorm'\n\nimport { entersDomainAsAssignee, User, UserStatus } from '@things-factory/auth-base'\nimport { Domain, Refusal } from '@things-factory/shell'\n\nimport { Attendee } from '../attendee/attendee'\nimport { attendeeKey, attendeesOf, replaceAttendees, type AttendeeEntry } from '../attendee/attendee-service'\nimport { Calendar, CalendarKind } from '../calendar/calendar'\nimport { Event } from './event'\nimport { EventStatus } from './event-rules'\n\n/*\n * Events brought in from another calendar - a Teams or Outlook calendar read through Microsoft Graph (Office ADR-0191).\n * Each lands on one calendar of the domain, named by its id there: bringing it in again changes the row - its name,\n * time, place, people and their answers - and never adds a second. What this domain decided of it stays: an event\n * recorded as held is still held.\n *\n * A person is the domain's user when their address is an activated user who enters the domain; anyone else is named by\n * the address alone. How long each was in it is kept when the other calendar measured it, and is not wiped by a later\n * reading that did not.\n */\n\nexport interface ImportedAttendee {\n email: string\n name?: string | null\n response?: string | null\n}\n\nexport interface ImportedEvent {\n /** The event's id in the other calendar. */\n id: string\n subject?: string | null\n start: string\n end: string\n organizer?: string | null\n attendees?: ImportedAttendee[] | null\n /** Seconds each person was in it, when the other calendar measured it. */\n attendance?: { email: string; seconds: number }[] | null\n location?: string | null\n cancelled?: boolean | null\n}\n\nconst EMAIL = /^[^\\s@]+@[^\\s@]+\\.[^\\s@]+$/\nconst NO_TITLE = '(no title)'\n\n/** One event read from several people's calendars is one event: the first reading, with every measured attendance. */\nexport function mergedReadings(events: readonly ImportedEvent[]): ImportedEvent[] {\n const merged = new Map<string, ImportedEvent>()\n for (const one of events) {\n if (!one?.id) continue\n const held = merged.get(one.id)\n if (!held) {\n merged.set(one.id, { ...one, attendance: one.attendance ? [...one.attendance] : null })\n continue\n }\n if (one.attendance?.length) {\n const seen = new Map((held.attendance ?? []).map(row => [row.email.toLowerCase(), row]))\n for (const row of one.attendance) if (!seen.has(row.email.toLowerCase())) seen.set(row.email.toLowerCase(), row)\n held.attendance = [...seen.values()]\n }\n }\n return [...merged.values()]\n}\n\n/** The people of an event by address - its organizer first, each once - with their answer and measured seconds. */\nexport function peopleOf(event: ImportedEvent): { email: string; name: string; response: string | null; seconds: number | null }[] {\n const seconds = new Map((event.attendance ?? []).map(row => [String(row.email).toLowerCase(), Math.max(0, Math.round(Number(row.seconds) || 0))]))\n const people = new Map<string, { email: string; name: string; response: string | null; seconds: number | null }>()\n const add = (email: string | null | undefined, name: string | null | undefined, response: string | null | undefined) => {\n const address = String(email ?? '').trim().toLowerCase()\n if (!EMAIL.test(address) || people.has(address)) return\n people.set(address, { email: address, name: String(name ?? '').trim() || address, response: response ?? null, seconds: seconds.has(address) ? seconds.get(address)! : null })\n }\n add(event.organizer, null, 'organizer')\n for (const one of event.attendees ?? []) add(one.email, one.name, one.response)\n return [...people.values()]\n}\n\n/** The domain calendar events are brought in on - made, as a calendar of meetings, when the domain has none by that name. */\nasync function calendarNamed(tx: EntityManager, domain: Domain, user: User, name: string): Promise<Calendar> {\n const repository = tx.getRepository(Calendar)\n const found = await repository.findOne({ where: { domain: { id: domain.id }, name } as any })\n if (found) {\n if (found.kind !== CalendarKind.GENERAL) throw new Refusal('event-import-calendar-not-general', { name }, `'${name}' is not a calendar of meetings`)\n return found\n }\n return repository.save(repository.create({ domain, name, kind: CalendarKind.GENERAL, active: true, creator: user, updater: user } as any) as any) as any\n}\n\n/** What one reading of the other calendar covered: its window and the mailboxes it could read. */\nexport interface ImportReading {\n from: string\n to: string\n mailboxes: string[]\n failed?: { mailbox: string; reason?: string | null }[] | null\n}\n\n/**\n * Brings `events` from the calendar `source` onto the domain calendar `calendarName`. Answers how many were added,\n * changed, left and cancelled. Given the `reading` they came from, an event of that source in its window that a mailbox\n * it read holds and the reading no longer has was removed there: it is cancelled here - unless it was held.\n */\nexport async function importEvents(tx: EntityManager, domain: Domain, user: User, input: { calendarName: string; source: string; events: ImportedEvent[]; reading?: ImportReading | null }) {\n const calendarName = String(input.calendarName ?? '').trim()\n const source = String(input.source ?? '').trim().toUpperCase()\n if (!calendarName) throw new Refusal('event-import-calendar-required', {}, 'Name the calendar to bring the events onto')\n if (!source) throw new Refusal('event-import-source-required', {}, 'Name the calendar the events come from')\n const calendar = await calendarNamed(tx, domain, user, calendarName)\n const events = mergedReadings(input.events ?? [])\n\n /* The domain's users among the addresses, read once. */\n const addresses = [...new Set(events.flatMap(one => peopleOf(one).map(person => person.email)))]\n const found = addresses.length ? await tx.getRepository(User).find({ where: { email: In(addresses) } as any }) : []\n const users = new Map<string, User>()\n for (const one of found)\n if (one.status === UserStatus.ACTIVATED && (await entersDomainAsAssignee(domain, one, tx))) users.set(String(one.email).toLowerCase(), one)\n\n const repository = tx.getRepository(Event)\n let created = 0\n let updated = 0\n let left = 0\n for (const one of events) {\n const startAt = new Date(one.start)\n const endAt = new Date(one.end)\n if (isNaN(startAt.getTime()) || isNaN(endAt.getTime()) || !(endAt > startAt)) {\n left += 1\n continue\n }\n const people = peopleOf(one)\n const organizer = users.get(String(one.organizer ?? '').toLowerCase()) ?? null\n const held = await repository.findOne({ where: { domain: { id: domain.id }, source, externalId: one.id } as any })\n const status = one.cancelled ? EventStatus.CANCELLED : held?.status === EventStatus.HELD ? EventStatus.HELD : EventStatus.SCHEDULED\n const facts = {\n calendar,\n startAt,\n endAt,\n status,\n name: String(one.subject ?? '').trim().slice(0, 300) || NO_TITLE,\n location: one.location?.trim() || null,\n organizer,\n updater: user\n }\n const saved = held\n ? (await repository.save({ ...held, ...facts }), held)\n : await repository.save(repository.create({ domain, series: null, scheduledAt: startAt, source, externalId: one.id, creator: user, ...facts } as any) as any)\n held ? (updated += 1) : (created += 1)\n\n const entries: AttendeeEntry[] = people.map(person => {\n const known = users.get(person.email)\n return known ? { key: attendeeKey({ userId: known.id }), userId: known.id, name: known.name || person.name } : { key: attendeeKey({ email: person.email }), email: person.email, name: person.name }\n })\n await replaceAttendees(tx, domain, user, { event: { id: (saved as any).id } }, entries)\n const rows = await attendeesOf(tx, domain, { event: { id: (saved as any).id } })\n const byKey = new Map(people.map((person, index) => [entries[index].key, person]))\n for (const row of rows) {\n const person = byKey.get(attendeeKey(row))\n if (!person) continue\n const change: Partial<Attendee> = {}\n if ((row.response ?? null) !== person.response) change.response = person.response\n if (person.seconds !== null && row.attendedSeconds !== person.seconds) change.attendedSeconds = person.seconds\n if (Object.keys(change).length) await tx.getRepository(Attendee).update({ id: row.id }, { ...change, updater: user } as any)\n }\n }\n const cancelled = input.reading ? await cancelRemoved(tx, domain, user, source, input.reading, new Set(events.map(one => one.id))) : 0\n if (input.reading) {\n const lastImport = {\n at: new Date().toISOString(),\n source,\n read: (input.reading.mailboxes ?? []).length,\n mailboxes: (input.reading.mailboxes ?? []).map(one => String(one).toLowerCase()),\n failed: (input.reading.failed ?? []).map(one => ({ mailbox: String(one.mailbox).toLowerCase(), reason: one.reason ?? null }))\n }\n await tx.getRepository(Calendar).update({ id: calendar.id }, { lastImport, updater: user } as any)\n }\n return { created, updated, left, cancelled }\n}\n\n/** The events of `source` in the reading's window, held by a mailbox it read, that it did not find - removed there. */\nasync function cancelRemoved(tx: EntityManager, domain: Domain, user: User, source: string, reading: ImportReading, found: Set<string>): Promise<number> {\n const from = new Date(reading.from)\n const to = new Date(reading.to)\n const read = new Set((reading.mailboxes ?? []).map(one => String(one).trim().toLowerCase()).filter(Boolean))\n if (isNaN(from.getTime()) || isNaN(to.getTime()) || !read.size) return 0\n const standing = await tx.getRepository(Event).find({\n where: { domain: { id: domain.id }, source, startAt: Between(from, to), status: In([EventStatus.SCHEDULED, EventStatus.POSTPONED]) } as any,\n relations: ['organizer']\n })\n const gone = standing.filter(one => one.externalId && !found.has(one.externalId))\n let cancelled = 0\n for (const event of gone) {\n const rows = await tx.getRepository(Attendee).find({ where: { domain: { id: domain.id }, event: { id: event.id } } as any, relations: ['user'] })\n const addresses = [event.organizer?.email, ...rows.map(row => row.email ?? row.user?.email)].map(one => String(one ?? '').toLowerCase())\n if (!addresses.some(one => read.has(one))) continue\n await tx.getRepository(Event).update({ id: event.id }, { status: EventStatus.CANCELLED, statusReason: 'removed from the calendar it was brought in from', updater: user } as any)\n cancelled += 1\n }\n return cancelled\n}\n"]}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { AttendeeInput } from '../attendee/attendee-type';
|
|
2
2
|
import { Event } from './event';
|
|
3
|
-
import { EventPatch, NewEvent } from './event-type';
|
|
3
|
+
import { EventImportResult, EventPatch, ImportedEventInput, ImportReadingInput, NewEvent } from './event-type';
|
|
4
4
|
export declare class EventMutation {
|
|
5
5
|
createEvent(event: NewEvent, context: ResolverContext): Promise<Event>;
|
|
6
6
|
updateEvent(id: string, patch: EventPatch, context: ResolverContext): Promise<Event>;
|
|
@@ -9,4 +9,5 @@ export declare class EventMutation {
|
|
|
9
9
|
markEventHeld(id: string, context: ResolverContext): Promise<Event>;
|
|
10
10
|
setEventAttendees(id: string, attendees: AttendeeInput[], context: ResolverContext): Promise<Event>;
|
|
11
11
|
deleteEvent(id: string, context: ResolverContext): Promise<boolean>;
|
|
12
|
+
importEvents(calendar: string, source: string, events: ImportedEventInput[], reading: ImportReadingInput, context: ResolverContext): Promise<EventImportResult>;
|
|
12
13
|
}
|
|
@@ -7,6 +7,7 @@ const shell_1 = require("@things-factory/shell");
|
|
|
7
7
|
const attendee_type_1 = require("../attendee/attendee-type");
|
|
8
8
|
const event_1 = require("./event");
|
|
9
9
|
const event_service_1 = require("./event-service");
|
|
10
|
+
const event_import_1 = require("./event-import");
|
|
10
11
|
const event_type_1 = require("./event-type");
|
|
11
12
|
let EventMutation = class EventMutation {
|
|
12
13
|
createEvent(event, context) {
|
|
@@ -37,6 +38,11 @@ let EventMutation = class EventMutation {
|
|
|
37
38
|
const { domain, user, tx } = context.state;
|
|
38
39
|
return (0, event_service_1.deleteEvent)(tx, domain, user, id);
|
|
39
40
|
}
|
|
41
|
+
/* A scenario's step (Office ADR-0191): the events it read of another calendar, brought onto a calendar of the domain. */
|
|
42
|
+
importEvents(calendar, source, events, reading, context) {
|
|
43
|
+
const { domain, user, tx } = context.state;
|
|
44
|
+
return (0, event_import_1.importEvents)(tx, domain, user, { calendarName: calendar, source, events: (events ?? reading?.meetings ?? []), reading: reading });
|
|
45
|
+
}
|
|
40
46
|
};
|
|
41
47
|
exports.EventMutation = EventMutation;
|
|
42
48
|
tslib_1.__decorate([
|
|
@@ -116,6 +122,19 @@ tslib_1.__decorate([
|
|
|
116
122
|
tslib_1.__metadata("design:paramtypes", [String, Object]),
|
|
117
123
|
tslib_1.__metadata("design:returntype", Promise)
|
|
118
124
|
], EventMutation.prototype, "deleteEvent", null);
|
|
125
|
+
tslib_1.__decorate([
|
|
126
|
+
(0, type_graphql_1.Directive)('@transaction'),
|
|
127
|
+
(0, type_graphql_1.Directive)('@privilege(category: "calendar", privilege: "mutation", domainOwnerGranted: true)'),
|
|
128
|
+
(0, type_graphql_1.Mutation)(returns => event_type_1.EventImportResult, { description: 'To bring events read from another calendar onto a calendar of the domain - each once, changed when read again' }),
|
|
129
|
+
tslib_1.__param(0, (0, type_graphql_1.Arg)('calendar', { description: 'The name of the domain calendar to bring them onto - made when there is none' })),
|
|
130
|
+
tslib_1.__param(1, (0, type_graphql_1.Arg)('source', { description: 'The calendar they come from, such as MSGRAPH' })),
|
|
131
|
+
tslib_1.__param(2, (0, type_graphql_1.Arg)('events', type => [event_type_1.ImportedEventInput], { nullable: true, description: 'The events read; the reading\'s meetings when not given' })),
|
|
132
|
+
tslib_1.__param(3, (0, type_graphql_1.Arg)('reading', type => event_type_1.ImportReadingInput, { nullable: true, description: 'The window and mailboxes read - an event of them no longer read is cancelled' })),
|
|
133
|
+
tslib_1.__param(4, (0, type_graphql_1.Ctx)()),
|
|
134
|
+
tslib_1.__metadata("design:type", Function),
|
|
135
|
+
tslib_1.__metadata("design:paramtypes", [String, String, Array, event_type_1.ImportReadingInput, Object]),
|
|
136
|
+
tslib_1.__metadata("design:returntype", Promise)
|
|
137
|
+
], EventMutation.prototype, "importEvents", null);
|
|
119
138
|
exports.EventMutation = EventMutation = tslib_1.__decorate([
|
|
120
139
|
(0, type_graphql_1.UseMiddleware)(shell_1.SpeakRefusals),
|
|
121
140
|
(0, type_graphql_1.Resolver)(event_1.Event)
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"event-mutation.js","sourceRoot":"","sources":["../../../server/service/event/event-mutation.ts"],"names":[],"mappings":";;;;AAAA,+CAAqF;AAErF,iDAAqD;AAErD,6DAAyD;AACzD,mCAA+B;AAC/B,mDAAqI;AACrI,6CAAmD;AAI5C,IAAM,aAAa,GAAnB,MAAM,aAAa;IAIxB,WAAW,CAAe,KAAe,EAAS,OAAwB;QACxE,MAAM,EAAE,MAAM,EAAE,IAAI,EAAE,EAAE,EAAE,GAAG,OAAO,CAAC,KAAK,CAAA;QAC1C,OAAO,IAAA,2BAAW,EAAC,EAAE,EAAE,MAAM,EAAE,IAAI,EAAE,KAAK,CAAC,CAAA;IAC7C,CAAC;IAKD,WAAW,CAAY,EAAU,EAAgB,KAAiB,EAAS,OAAwB;QACjG,MAAM,EAAE,MAAM,EAAE,IAAI,EAAE,EAAE,EAAE,GAAG,OAAO,CAAC,KAAK,CAAA;QAC1C,OAAO,IAAA,2BAAW,EAAC,EAAE,EAAE,MAAM,EAAE,IAAI,EAAE,EAAE,EAAE,KAAK,CAAC,CAAA;IACjD,CAAC;IAKD,aAAa,CACA,EAAU,EACL,OAAa,EACK,KAAW,EACV,MAAc,EAC1C,OAAwB;QAE/B,MAAM,EAAE,MAAM,EAAE,IAAI,EAAE,EAAE,EAAE,GAAG,OAAO,CAAC,KAAK,CAAA;QAC1C,OAAO,IAAA,6BAAa,EAAC,EAAE,EAAE,MAAM,EAAE,IAAI,EAAE,EAAE,EAAE,EAAE,OAAO,EAAE,KAAK,EAAE,KAAK,IAAI,SAAS,EAAE,MAAM,EAAE,CAAC,CAAA;IAC5F,CAAC;IAKD,WAAW,CAAY,EAAU,EAAqC,MAAc,EAAS,OAAwB;QACnH,MAAM,EAAE,MAAM,EAAE,IAAI,EAAE,EAAE,EAAE,GAAG,OAAO,CAAC,KAAK,CAAA;QAC1C,OAAO,IAAA,2BAAW,EAAC,EAAE,EAAE,MAAM,EAAE,IAAI,EAAE,EAAE,EAAE,MAAM,CAAC,CAAA;IAClD,CAAC;IAKD,aAAa,CAAY,EAAU,EAAS,OAAwB;QAClE,MAAM,EAAE,MAAM,EAAE,IAAI,EAAE,EAAE,EAAE,GAAG,OAAO,CAAC,KAAK,CAAA;QAC1C,OAAO,IAAA,6BAAa,EAAC,EAAE,EAAE,MAAM,EAAE,IAAI,EAAE,EAAE,CAAC,CAAA;IAC5C,CAAC;IAKD,iBAAiB,CAAY,EAAU,EAA6C,SAA0B,EAAS,OAAwB;QAC7I,MAAM,EAAE,MAAM,EAAE,IAAI,EAAE,EAAE,EAAE,GAAG,OAAO,CAAC,KAAK,CAAA;QAC1C,OAAO,IAAA,iCAAiB,EAAC,EAAE,EAAE,MAAM,EAAE,IAAI,EAAE,EAAE,EAAE,SAAS,CAAC,CAAA;IAC3D,CAAC;IAKD,WAAW,CAAY,EAAU,EAAS,OAAwB;QAChE,MAAM,EAAE,MAAM,EAAE,IAAI,EAAE,EAAE,EAAE,GAAG,OAAO,CAAC,KAAK,CAAA;QAC1C,OAAO,IAAA,2BAAW,EAAC,EAAE,EAAE,MAAM,EAAE,IAAI,EAAE,EAAE,CAAC,CAAA;IAC1C,CAAC;CACF,CAAA;AA9DY,sCAAa;AAIxB;IAHC,IAAA,wBAAS,EAAC,cAAc,CAAC;IACzB,IAAA,wBAAS,EAAC,mFAAmF,CAAC;IAC9F,IAAA,uBAAQ,EAAC,OAAO,CAAC,EAAE,CAAC,aAAK,EAAE,EAAE,WAAW,EAAE,2BAA2B,EAAE,CAAC;IAC5D,mBAAA,IAAA,kBAAG,EAAC,OAAO,CAAC,CAAA;IAAmB,mBAAA,IAAA,kBAAG,GAAE,CAAA;;6CAAhB,qBAAQ;;gDAGxC;AAKD;IAHC,IAAA,wBAAS,EAAC,cAAc,CAAC;IACzB,IAAA,wBAAS,EAAC,mFAAmF,CAAC;IAC9F,IAAA,uBAAQ,EAAC,OAAO,CAAC,EAAE,CAAC,aAAK,EAAE,EAAE,WAAW,EAAE,2EAA2E,EAAE,CAAC;IAC5G,mBAAA,IAAA,kBAAG,EAAC,IAAI,CAAC,CAAA;IAAc,mBAAA,IAAA,kBAAG,EAAC,OAAO,CAAC,CAAA;IAAqB,mBAAA,IAAA,kBAAG,GAAE,CAAA;;qDAAlB,uBAAU;;gDAGjE;AAKD;IAHC,IAAA,wBAAS,EAAC,cAAc,CAAC;IACzB,IAAA,wBAAS,EAAC,mFAAmF,CAAC;IAC9F,IAAA,uBAAQ,EAAC,OAAO,CAAC,EAAE,CAAC,aAAK,EAAE,EAAE,WAAW,EAAE,yDAAyD,EAAE,CAAC;IAEpG,mBAAA,IAAA,kBAAG,EAAC,IAAI,CAAC,CAAA;IACT,mBAAA,IAAA,kBAAG,EAAC,SAAS,CAAC,CAAA;IACd,mBAAA,IAAA,kBAAG,EAAC,OAAO,EAAE,EAAE,QAAQ,EAAE,IAAI,EAAE,CAAC,CAAA;IAChC,mBAAA,IAAA,kBAAG,EAAC,QAAQ,EAAE,EAAE,QAAQ,EAAE,IAAI,EAAE,CAAC,CAAA;IACjC,mBAAA,IAAA,kBAAG,GAAE,CAAA;;qDAHmB,IAAI;QACY,IAAI;;kDAM9C;AAKD;IAHC,IAAA,wBAAS,EAAC,cAAc,CAAC;IACzB,IAAA,wBAAS,EAAC,mFAAmF,CAAC;IAC9F,IAAA,uBAAQ,EAAC,OAAO,CAAC,EAAE,CAAC,aAAK,EAAE,EAAE,WAAW,EAAE,2CAA2C,EAAE,CAAC;IAC5E,mBAAA,IAAA,kBAAG,EAAC,IAAI,CAAC,CAAA;IAAc,mBAAA,IAAA,kBAAG,EAAC,QAAQ,EAAE,EAAE,QAAQ,EAAE,IAAI,EAAE,CAAC,CAAA;IAAkB,mBAAA,IAAA,kBAAG,GAAE,CAAA;;;;gDAG3F;AAKD;IAHC,IAAA,wBAAS,EAAC,cAAc,CAAC;IACzB,IAAA,wBAAS,EAAC,mFAAmF,CAAC;IAC9F,IAAA,uBAAQ,EAAC,OAAO,CAAC,EAAE,CAAC,aAAK,EAAE,EAAE,WAAW,EAAE,oCAAoC,EAAE,CAAC;IACnE,mBAAA,IAAA,kBAAG,EAAC,IAAI,CAAC,CAAA;IAAc,mBAAA,IAAA,kBAAG,GAAE,CAAA;;;;kDAG1C;AAKD;IAHC,IAAA,wBAAS,EAAC,cAAc,CAAC;IACzB,IAAA,wBAAS,EAAC,mFAAmF,CAAC;IAC9F,IAAA,uBAAQ,EAAC,OAAO,CAAC,EAAE,CAAC,aAAK,EAAE,EAAE,WAAW,EAAE,4EAA4E,EAAE,CAAC;IACvG,mBAAA,IAAA,kBAAG,EAAC,IAAI,CAAC,CAAA;IAAc,mBAAA,IAAA,kBAAG,EAAC,WAAW,EAAE,IAAI,CAAC,EAAE,CAAC,CAAC,6BAAa,CAAC,CAAC,CAAA;IAA8B,mBAAA,IAAA,kBAAG,GAAE,CAAA;;;;sDAGrH;AAKD;IAHC,IAAA,wBAAS,EAAC,cAAc,CAAC;IACzB,IAAA,wBAAS,EAAC,mFAAmF,CAAC;IAC9F,IAAA,uBAAQ,EAAC,OAAO,CAAC,EAAE,CAAC,OAAO,EAAE,EAAE,WAAW,EAAE,2EAA2E,EAAE,CAAC;IAC9G,mBAAA,IAAA,kBAAG,EAAC,IAAI,CAAC,CAAA;IAAc,mBAAA,IAAA,kBAAG,GAAE,CAAA;;;;gDAGxC;wBA7DU,aAAa;IAFzB,IAAA,4BAAa,EAAC,qBAAa,CAAC;IAC5B,IAAA,uBAAQ,EAAC,aAAK,CAAC;GACH,aAAa,CA8DzB","sourcesContent":["import { Arg, Ctx, Directive, Mutation, Resolver, UseMiddleware } from 'type-graphql'\n\nimport { SpeakRefusals } from '@things-factory/shell'\n\nimport { AttendeeInput } from '../attendee/attendee-type'\nimport { Event } from './event'\nimport { cancelEvent, createEvent, deleteEvent, markEventHeld, postponeEvent, setEventAttendees, updateEvent } from './event-service'\nimport { EventPatch, NewEvent } from './event-type'\n\n@UseMiddleware(SpeakRefusals)\n@Resolver(Event)\nexport class EventMutation {\n @Directive('@transaction')\n @Directive('@privilege(category: \"calendar\", privilege: \"mutation\", domainOwnerGranted: true)')\n @Mutation(returns => Event, { description: 'To create a one-off event' })\n createEvent(@Arg('event') event: NewEvent, @Ctx() context: ResolverContext): Promise<Event> {\n const { domain, user, tx } = context.state\n return createEvent(tx, domain, user, event)\n }\n\n @Directive('@transaction')\n @Directive('@privilege(category: \"calendar\", privilege: \"mutation\", domainOwnerGranted: true)')\n @Mutation(returns => Event, { description: 'To change the name, description, place or person responsible of one event' })\n updateEvent(@Arg('id') id: string, @Arg('patch') patch: EventPatch, @Ctx() context: ResolverContext): Promise<Event> {\n const { domain, user, tx } = context.state\n return updateEvent(tx, domain, user, id, patch)\n }\n\n @Directive('@transaction')\n @Directive('@privilege(category: \"calendar\", privilege: \"mutation\", domainOwnerGranted: true)')\n @Mutation(returns => Event, { description: 'To move one event to another time; nothing else changes' })\n postponeEvent(\n @Arg('id') id: string,\n @Arg('startAt') startAt: Date,\n @Arg('endAt', { nullable: true }) endAt: Date,\n @Arg('reason', { nullable: true }) reason: string,\n @Ctx() context: ResolverContext\n ): Promise<Event> {\n const { domain, user, tx } = context.state\n return postponeEvent(tx, domain, user, id, { startAt, endAt: endAt ?? undefined, reason })\n }\n\n @Directive('@transaction')\n @Directive('@privilege(category: \"calendar\", privilege: \"mutation\", domainOwnerGranted: true)')\n @Mutation(returns => Event, { description: 'To cancel one event; nothing else changes' })\n cancelEvent(@Arg('id') id: string, @Arg('reason', { nullable: true }) reason: string, @Ctx() context: ResolverContext): Promise<Event> {\n const { domain, user, tx } = context.state\n return cancelEvent(tx, domain, user, id, reason)\n }\n\n @Directive('@transaction')\n @Directive('@privilege(category: \"calendar\", privilege: \"mutation\", domainOwnerGranted: true)')\n @Mutation(returns => Event, { description: 'To record that an event took place' })\n markEventHeld(@Arg('id') id: string, @Ctx() context: ResolverContext): Promise<Event> {\n const { domain, user, tx } = context.state\n return markEventHeld(tx, domain, user, id)\n }\n\n @Directive('@transaction')\n @Directive('@privilege(category: \"calendar\", privilege: \"mutation\", domainOwnerGranted: true)')\n @Mutation(returns => Event, { description: 'To make the people named on one event exactly these; naming grants nothing' })\n setEventAttendees(@Arg('id') id: string, @Arg('attendees', type => [AttendeeInput]) attendees: AttendeeInput[], @Ctx() context: ResolverContext): Promise<Event> {\n const { domain, user, tx } = context.state\n return setEventAttendees(tx, domain, user, id, attendees)\n }\n\n @Directive('@transaction')\n @Directive('@privilege(category: \"calendar\", privilege: \"mutation\", domainOwnerGranted: true)')\n @Mutation(returns => Boolean, { description: 'To delete a one-off event; an occurrence of a series is cancelled instead' })\n deleteEvent(@Arg('id') id: string, @Ctx() context: ResolverContext): Promise<boolean> {\n const { domain, user, tx } = context.state\n return deleteEvent(tx, domain, user, id)\n }\n}\n"]}
|
|
1
|
+
{"version":3,"file":"event-mutation.js","sourceRoot":"","sources":["../../../server/service/event/event-mutation.ts"],"names":[],"mappings":"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","sourcesContent":["import { Arg, Ctx, Directive, Mutation, Resolver, UseMiddleware } from 'type-graphql'\n\nimport { SpeakRefusals } from '@things-factory/shell'\n\nimport { AttendeeInput } from '../attendee/attendee-type'\nimport { Event } from './event'\nimport { cancelEvent, createEvent, deleteEvent, markEventHeld, postponeEvent, setEventAttendees, updateEvent } from './event-service'\nimport { importEvents } from './event-import'\nimport { EventImportResult, EventPatch, ImportedEventInput, ImportReadingInput, NewEvent } from './event-type'\n\n@UseMiddleware(SpeakRefusals)\n@Resolver(Event)\nexport class EventMutation {\n @Directive('@transaction')\n @Directive('@privilege(category: \"calendar\", privilege: \"mutation\", domainOwnerGranted: true)')\n @Mutation(returns => Event, { description: 'To create a one-off event' })\n createEvent(@Arg('event') event: NewEvent, @Ctx() context: ResolverContext): Promise<Event> {\n const { domain, user, tx } = context.state\n return createEvent(tx, domain, user, event)\n }\n\n @Directive('@transaction')\n @Directive('@privilege(category: \"calendar\", privilege: \"mutation\", domainOwnerGranted: true)')\n @Mutation(returns => Event, { description: 'To change the name, description, place or person responsible of one event' })\n updateEvent(@Arg('id') id: string, @Arg('patch') patch: EventPatch, @Ctx() context: ResolverContext): Promise<Event> {\n const { domain, user, tx } = context.state\n return updateEvent(tx, domain, user, id, patch)\n }\n\n @Directive('@transaction')\n @Directive('@privilege(category: \"calendar\", privilege: \"mutation\", domainOwnerGranted: true)')\n @Mutation(returns => Event, { description: 'To move one event to another time; nothing else changes' })\n postponeEvent(\n @Arg('id') id: string,\n @Arg('startAt') startAt: Date,\n @Arg('endAt', { nullable: true }) endAt: Date,\n @Arg('reason', { nullable: true }) reason: string,\n @Ctx() context: ResolverContext\n ): Promise<Event> {\n const { domain, user, tx } = context.state\n return postponeEvent(tx, domain, user, id, { startAt, endAt: endAt ?? undefined, reason })\n }\n\n @Directive('@transaction')\n @Directive('@privilege(category: \"calendar\", privilege: \"mutation\", domainOwnerGranted: true)')\n @Mutation(returns => Event, { description: 'To cancel one event; nothing else changes' })\n cancelEvent(@Arg('id') id: string, @Arg('reason', { nullable: true }) reason: string, @Ctx() context: ResolverContext): Promise<Event> {\n const { domain, user, tx } = context.state\n return cancelEvent(tx, domain, user, id, reason)\n }\n\n @Directive('@transaction')\n @Directive('@privilege(category: \"calendar\", privilege: \"mutation\", domainOwnerGranted: true)')\n @Mutation(returns => Event, { description: 'To record that an event took place' })\n markEventHeld(@Arg('id') id: string, @Ctx() context: ResolverContext): Promise<Event> {\n const { domain, user, tx } = context.state\n return markEventHeld(tx, domain, user, id)\n }\n\n @Directive('@transaction')\n @Directive('@privilege(category: \"calendar\", privilege: \"mutation\", domainOwnerGranted: true)')\n @Mutation(returns => Event, { description: 'To make the people named on one event exactly these; naming grants nothing' })\n setEventAttendees(@Arg('id') id: string, @Arg('attendees', type => [AttendeeInput]) attendees: AttendeeInput[], @Ctx() context: ResolverContext): Promise<Event> {\n const { domain, user, tx } = context.state\n return setEventAttendees(tx, domain, user, id, attendees)\n }\n\n @Directive('@transaction')\n @Directive('@privilege(category: \"calendar\", privilege: \"mutation\", domainOwnerGranted: true)')\n @Mutation(returns => Boolean, { description: 'To delete a one-off event; an occurrence of a series is cancelled instead' })\n deleteEvent(@Arg('id') id: string, @Ctx() context: ResolverContext): Promise<boolean> {\n const { domain, user, tx } = context.state\n return deleteEvent(tx, domain, user, id)\n }\n\n /* A scenario's step (Office ADR-0191): the events it read of another calendar, brought onto a calendar of the domain. */\n @Directive('@transaction')\n @Directive('@privilege(category: \"calendar\", privilege: \"mutation\", domainOwnerGranted: true)')\n @Mutation(returns => EventImportResult, { description: 'To bring events read from another calendar onto a calendar of the domain - each once, changed when read again' })\n importEvents(\n @Arg('calendar', { description: 'The name of the domain calendar to bring them onto - made when there is none' }) calendar: string,\n @Arg('source', { description: 'The calendar they come from, such as MSGRAPH' }) source: string,\n @Arg('events', type => [ImportedEventInput], { nullable: true, description: 'The events read; the reading\\'s meetings when not given' }) events: ImportedEventInput[],\n @Arg('reading', type => ImportReadingInput, { nullable: true, description: 'The window and mailboxes read - an event of them no longer read is cancelled' }) reading: ImportReadingInput,\n @Ctx() context: ResolverContext\n ): Promise<EventImportResult> {\n const { domain, user, tx } = context.state\n return importEvents(tx, domain, user, { calendarName: calendar, source, events: (events ?? reading?.meetings ?? []) as any, reading: reading as any })\n }\n}\n"]}
|
|
@@ -20,3 +20,43 @@ export declare class EventList {
|
|
|
20
20
|
items: Event[];
|
|
21
21
|
total: number;
|
|
22
22
|
}
|
|
23
|
+
export declare class ImportedAttendeeInput {
|
|
24
|
+
email: string;
|
|
25
|
+
name?: string;
|
|
26
|
+
response?: string;
|
|
27
|
+
}
|
|
28
|
+
export declare class ImportedAttendanceInput {
|
|
29
|
+
email: string;
|
|
30
|
+
seconds: number;
|
|
31
|
+
}
|
|
32
|
+
export declare class ImportedEventInput {
|
|
33
|
+
id: string;
|
|
34
|
+
mailbox?: string;
|
|
35
|
+
subject?: string;
|
|
36
|
+
start: string;
|
|
37
|
+
end: string;
|
|
38
|
+
organizer?: string;
|
|
39
|
+
attendees?: ImportedAttendeeInput[];
|
|
40
|
+
attendance?: ImportedAttendanceInput[];
|
|
41
|
+
online?: boolean;
|
|
42
|
+
location?: string;
|
|
43
|
+
response?: string;
|
|
44
|
+
cancelled?: boolean;
|
|
45
|
+
}
|
|
46
|
+
export declare class ImportReadingFailureInput {
|
|
47
|
+
mailbox: string;
|
|
48
|
+
reason?: string;
|
|
49
|
+
}
|
|
50
|
+
export declare class ImportReadingInput {
|
|
51
|
+
from: string;
|
|
52
|
+
to: string;
|
|
53
|
+
mailboxes: string[];
|
|
54
|
+
failed?: ImportReadingFailureInput[];
|
|
55
|
+
meetings?: ImportedEventInput[];
|
|
56
|
+
}
|
|
57
|
+
export declare class EventImportResult {
|
|
58
|
+
created: number;
|
|
59
|
+
updated: number;
|
|
60
|
+
left: number;
|
|
61
|
+
cancelled: number;
|
|
62
|
+
}
|