@openinc/parse-server-opendash 4.0.28 → 4.0.30

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.
Files changed (23) hide show
  1. package/dist/features/openservice/schedules/calendarSync/functions/eventToSchedule.d.ts +11 -5
  2. package/dist/features/openservice/schedules/calendarSync/functions/eventToSchedule.js +62 -23
  3. package/dist/features/openservice/schedules/calendarSync/functions/scheduleToEvent.d.ts +2 -1
  4. package/dist/features/openservice/schedules/calendarSync/functions/scheduleToEvent.js +62 -33
  5. package/dist/features/openservice/schedules/calendarSync/service/CalendarManager.d.ts +27 -22
  6. package/dist/features/openservice/schedules/calendarSync/service/CalendarManager.js +101 -48
  7. package/dist/features/openservice/schedules/calendarSync/service/CalendarSyncPoller.d.ts +20 -0
  8. package/dist/features/openservice/schedules/calendarSync/service/CalendarSyncPoller.js +197 -0
  9. package/dist/features/openservice/schedules/calendarSync/service/calendarMetaHelper.d.ts +36 -0
  10. package/dist/features/openservice/schedules/calendarSync/service/calendarMetaHelper.js +89 -0
  11. package/dist/features/openservice/schedules/calendarSync/types/ConfigValues.d.ts +8 -0
  12. package/dist/features/openservice/schedules/calendarSync/types/ConfigValues.js +2 -0
  13. package/dist/features/openservice/schedules/calendarSync/types/Event.d.ts +1 -1
  14. package/dist/features/openservice/schedules/initSchedulesFeature.js +6 -0
  15. package/dist/hooks/Config_open_service.js +2 -0
  16. package/dist/hooks/Maintenance_Schedule.js +47 -22
  17. package/dist/types/Meta_Config.d.ts +4 -3
  18. package/dist/types/Meta_Entry.d.ts +4 -3
  19. package/dist/types/custom/MetaField.d.ts +117 -0
  20. package/dist/types/custom/MetaField.js +21 -0
  21. package/dist/types/index.d.ts +2 -0
  22. package/dist/types/index.js +4 -2
  23. package/package.json +5 -5
@@ -0,0 +1,197 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.CalendarSyncPoller = void 0;
7
+ const node_1 = __importDefault(require("parse/node"));
8
+ const eventToSchedule_js_1 = require("../functions/eventToSchedule.js");
9
+ const calendarMetaHelper_js_1 = require("./calendarMetaHelper.js");
10
+ const CalendarManager_js_1 = require("./CalendarManager.js");
11
+ const index_js_1 = require("../../../../../types/index.js");
12
+ class CalendarSyncPoller {
13
+ timer = null;
14
+ config;
15
+ constructor(config) {
16
+ this.config = config;
17
+ }
18
+ get manager() {
19
+ const configValues = JSON.parse(this.config.get("value"));
20
+ return new CalendarManager_js_1.CalendarManager({
21
+ tenantId: configValues.tenant_id,
22
+ clientID: configValues.client_id,
23
+ clientSecret: configValues.client_secret,
24
+ userMail: configValues.user_email,
25
+ });
26
+ }
27
+ /**
28
+ * Start polling the Microsoft Calendar delta feed.
29
+ * @param intervalMs polling interval in milliseconds (default: 5 minutes)
30
+ */
31
+ start(intervalMs = 5 * 60 * 1000) {
32
+ if (this.manager?.isValid !== true) {
33
+ console.error(`[CalendarSyncPoller] Not started — invalid configuration: ${this.manager?.isValid}`);
34
+ return;
35
+ }
36
+ this.poll().catch(console.error);
37
+ this.timer = setInterval(() => this.poll().catch(console.error), intervalMs);
38
+ }
39
+ stop() {
40
+ if (this.timer)
41
+ clearInterval(this.timer);
42
+ }
43
+ async poll() {
44
+ const deltaLink = await this.loadDeltaLink();
45
+ const result = await this.manager?.fetchDelta(deltaLink);
46
+ if (!result) {
47
+ console.error("[CalendarSyncPoller] Failed to fetch calendar delta.");
48
+ return;
49
+ }
50
+ for (const event of result.events) {
51
+ try {
52
+ await this.processEvent(event);
53
+ }
54
+ catch (err) {
55
+ console.error("[CalendarSyncPoller] Error processing event:", event.id, err);
56
+ }
57
+ }
58
+ await this.saveDeltaLink(result.deltaLink);
59
+ }
60
+ async processEvent(event) {
61
+ if (event["@removed"]) {
62
+ await this.handleDeleted(event.id);
63
+ return;
64
+ }
65
+ // Exception events are individual moved/edited occurrences of a series.
66
+ // The series master handles the recurrence as a whole; exceptions are noise.
67
+ if (event.type === "exception")
68
+ return;
69
+ const metaEntry = await (0, calendarMetaHelper_js_1.findCalendarMetaByEventId)(event.id);
70
+ if (metaEntry) {
71
+ const storedChangeKey = metaEntry.get("values")?.microsoftCalendarChangeKey;
72
+ // Skip if we caused this change (our write already stored this changeKey)
73
+ if (event.changeKey && storedChangeKey === event.changeKey)
74
+ return;
75
+ await this.updateScheduleFromEvent(metaEntry, event);
76
+ }
77
+ else {
78
+ // Delta responses omit most properties; fetch the full event before creating a schedule
79
+ const fullEvent = await this.manager?.fetchEvent(event.id);
80
+ if (!fullEvent)
81
+ return;
82
+ await this.createScheduleFromEvent(fullEvent);
83
+ }
84
+ }
85
+ async updateScheduleFromEvent(metaEntry, event) {
86
+ const scheduleId = metaEntry.get("entryObjectId");
87
+ if (!scheduleId)
88
+ return;
89
+ // Delta responses omit most fields; fetch the full event for title/body/cron
90
+ const fullEvent = (await this.manager?.fetchEvent(event.id)) ?? event;
91
+ const schedule = await new node_1.default.Query(index_js_1.Maintenance_Schedule).get(scheduleId, { useMasterKey: true });
92
+ if (fullEvent.subject)
93
+ schedule.set("title", fullEvent.subject);
94
+ if (fullEvent.bodyPreview)
95
+ schedule.set("description", fullEvent.bodyPreview);
96
+ const cronData = (0, eventToSchedule_js_1.eventToSchedule)(fullEvent);
97
+ if (cronData) {
98
+ const { notifyBeforeDue, ...cron } = cronData;
99
+ schedule.set("cron", cron);
100
+ if (notifyBeforeDue != null)
101
+ schedule.set("notifyBeforeDue", notifyBeforeDue);
102
+ }
103
+ // useMasterKey prevents afterSaveHook from re-syncing back to calendar
104
+ await schedule.save(null, { useMasterKey: true });
105
+ if (event.changeKey) {
106
+ await (0, calendarMetaHelper_js_1.updateCalendarChangeKey)(metaEntry, event.changeKey, fullEvent);
107
+ }
108
+ }
109
+ async createScheduleFromEvent(event) {
110
+ const parsed = parseEventSubject(event.subject);
111
+ let source;
112
+ const tenant = this.config.get("tenant");
113
+ if (parsed) {
114
+ source = await new node_1.default.Query(index_js_1.Source)
115
+ .equalTo("name", parsed.sourceName)
116
+ .equalTo("tenant", tenant)
117
+ .first({ useMasterKey: true });
118
+ }
119
+ if (!source) {
120
+ const configValues = JSON.parse(this.config.get("value"));
121
+ if (!configValues.default_source_id) {
122
+ console.error(`[CalendarSyncPoller] Skipping event ${event.id} — no source name in subject and no default_source_id configured.`);
123
+ return;
124
+ }
125
+ source = await new node_1.default.Query(index_js_1.Source)
126
+ .equalTo("objectId", configValues.default_source_id)
127
+ .first({ useMasterKey: true });
128
+ if (!source) {
129
+ console.error(`[CalendarSyncPoller] Skipping event ${event.id} — default source "${configValues.default_source_id}" not found.`);
130
+ return;
131
+ }
132
+ }
133
+ const schedule = new index_js_1.Maintenance_Schedule();
134
+ schedule.set("title", event.subject);
135
+ schedule.set("source", source);
136
+ schedule.set("enabled", true);
137
+ schedule.set("tenant", tenant);
138
+ if (event.bodyPreview)
139
+ schedule.set("description", event.bodyPreview);
140
+ const cronData = (0, eventToSchedule_js_1.eventToSchedule)(event);
141
+ if (cronData) {
142
+ const { notifyBeforeDue, ...cron } = cronData;
143
+ schedule.set("cron", cron);
144
+ if (notifyBeforeDue != null)
145
+ schedule.set("notifyBeforeDue", notifyBeforeDue);
146
+ }
147
+ // useMasterKey prevents afterSaveHook from syncing back to calendar
148
+ await schedule.save(null, { useMasterKey: true });
149
+ await (0, calendarMetaHelper_js_1.saveCalendarMeta)(schedule.id, event.id, event.changeKey, tenant, event);
150
+ }
151
+ async handleDeleted(eventId) {
152
+ const metaEntry = await (0, calendarMetaHelper_js_1.findCalendarMetaByEventId)(eventId);
153
+ if (!metaEntry)
154
+ return;
155
+ const scheduleId = metaEntry.get("entryObjectId");
156
+ if (scheduleId) {
157
+ try {
158
+ const schedule = await new node_1.default.Query(index_js_1.Maintenance_Schedule).get(scheduleId, { useMasterKey: true });
159
+ // Disable rather than delete to preserve execution history
160
+ schedule.set("enabled", false);
161
+ await schedule.save(null, { useMasterKey: true });
162
+ }
163
+ catch (err) {
164
+ console.error("[CalendarSyncPoller] Could not disable schedule for deleted event:", scheduleId, err);
165
+ }
166
+ }
167
+ await metaEntry.destroy({ useMasterKey: true });
168
+ }
169
+ async loadDeltaLink() {
170
+ this.config = await this.config.fetch({ useMasterKey: true });
171
+ const values = JSON.parse(this.config.get("value"));
172
+ return values.delta_link;
173
+ }
174
+ async saveDeltaLink(link) {
175
+ this.config = await this.config.fetch({ useMasterKey: true });
176
+ const values = JSON.parse(this.config.get("value"));
177
+ values.delta_link = link;
178
+ this.config.set("value", JSON.stringify(values));
179
+ await this.config.save(null, { useMasterKey: true });
180
+ }
181
+ }
182
+ exports.CalendarSyncPoller = CalendarSyncPoller;
183
+ // Parses the source name out of an Outlook-originated event subject.
184
+ // Expected format for externally-created events: "<source.name>" - <anything>
185
+ // Quotes around the source name are optional.
186
+ function parseEventSubject(subject) {
187
+ if (!subject)
188
+ return null;
189
+ const separatorIdx = subject.indexOf(" - ");
190
+ if (separatorIdx === -1)
191
+ return null;
192
+ const rawSource = subject.slice(0, separatorIdx).trim();
193
+ if (!rawSource)
194
+ return null;
195
+ const sourceName = rawSource.replace(/^"(.*)"$/, "$1");
196
+ return { sourceName };
197
+ }
@@ -0,0 +1,36 @@
1
+ import { Meta_Entry, Tenant } from "../../../../../types/index.js";
2
+ import { MetaConfig } from "../../../../../types/custom/MetaField.js";
3
+ import { GraphEventResponse } from "../types/Event.js";
4
+ export declare const CALENDAR_SYNC_CONTEXT: string;
5
+ export interface CalendarEventSnapshot {
6
+ id: string;
7
+ subject?: string;
8
+ start: {
9
+ dateTime: string;
10
+ timeZone: string;
11
+ };
12
+ end: {
13
+ dateTime: string;
14
+ timeZone: string;
15
+ };
16
+ isAllDay?: boolean;
17
+ isCancelled?: boolean;
18
+ body?: {
19
+ contentType?: string;
20
+ content?: string;
21
+ };
22
+ recurrence?: GraphEventResponse["recurrence"];
23
+ location?: {
24
+ displayName?: string;
25
+ };
26
+ }
27
+ export interface CalendarMetaValues {
28
+ microsoftCalendarEventId: string;
29
+ microsoftCalendarChangeKey?: string;
30
+ microsoftCalendarEvent?: CalendarEventSnapshot;
31
+ }
32
+ export declare const CalendarMetaConfig: MetaConfig;
33
+ export declare function findCalendarMetaForSchedule(scheduleId: string): Promise<Meta_Entry | null>;
34
+ export declare function findCalendarMetaByEventId(eventId: string): Promise<Meta_Entry | null>;
35
+ export declare function saveCalendarMeta(scheduleId: string, eventId: string, changeKey?: string, tenant?: Tenant, event?: GraphEventResponse): Promise<Meta_Entry>;
36
+ export declare function updateCalendarChangeKey(entry: Meta_Entry, changeKey: string, event?: GraphEventResponse): Promise<void>;
@@ -0,0 +1,89 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.CalendarMetaConfig = exports.CALENDAR_SYNC_CONTEXT = void 0;
7
+ exports.findCalendarMetaForSchedule = findCalendarMetaForSchedule;
8
+ exports.findCalendarMetaByEventId = findCalendarMetaByEventId;
9
+ exports.saveCalendarMeta = saveCalendarMeta;
10
+ exports.updateCalendarChangeKey = updateCalendarChangeKey;
11
+ const node_1 = __importDefault(require("parse/node"));
12
+ const index_js_1 = require("../../../../../types/index.js");
13
+ exports.CALENDAR_SYNC_CONTEXT = JSON.stringify([
14
+ "schedule",
15
+ "sync",
16
+ "microsoft",
17
+ ]);
18
+ function toEventSnapshot(event) {
19
+ return {
20
+ id: event.id,
21
+ subject: event.subject,
22
+ start: event.start,
23
+ end: event.end,
24
+ isAllDay: event.isAllDay,
25
+ isCancelled: event.isCancelled,
26
+ body: event.body
27
+ ? { contentType: event.body.contentType, content: event.body.content }
28
+ : undefined,
29
+ recurrence: event.recurrence ?? undefined,
30
+ location: event.location?.displayName
31
+ ? { displayName: event.location.displayName }
32
+ : undefined,
33
+ };
34
+ }
35
+ exports.CalendarMetaConfig = {
36
+ fields: [
37
+ {
38
+ name: "microsoftCalendarEventId",
39
+ type: "string",
40
+ label: "Microsoft Calendar Event ID",
41
+ },
42
+ {
43
+ name: "microsoftCalendarChangeKey",
44
+ type: "string",
45
+ label: "Microsoft Calendar Change Key",
46
+ },
47
+ ],
48
+ };
49
+ async function findCalendarMetaForSchedule(scheduleId) {
50
+ return ((await new node_1.default.Query(index_js_1.Meta_Entry)
51
+ .equalTo("context", exports.CALENDAR_SYNC_CONTEXT)
52
+ .equalTo("entryClassName", index_js_1.Maintenance_Schedule.className)
53
+ .equalTo("entryObjectId", scheduleId)
54
+ .first({ useMasterKey: true })) ?? null);
55
+ }
56
+ async function findCalendarMetaByEventId(eventId) {
57
+ return ((await new node_1.default.Query(index_js_1.Meta_Entry)
58
+ .equalTo("context", exports.CALENDAR_SYNC_CONTEXT)
59
+ .equalTo("entryClassName", index_js_1.Maintenance_Schedule.className)
60
+ // @ts-expect-error
61
+ .equalTo("values.microsoftCalendarEventId", eventId)
62
+ .first({ useMasterKey: true })) ?? null);
63
+ }
64
+ async function saveCalendarMeta(scheduleId, eventId, changeKey, tenant, event) {
65
+ let entry = await findCalendarMetaForSchedule(scheduleId);
66
+ if (!entry) {
67
+ entry = new index_js_1.Meta_Entry();
68
+ entry.set("context", exports.CALENDAR_SYNC_CONTEXT);
69
+ entry.set("entryClassName", index_js_1.Maintenance_Schedule.className);
70
+ entry.set("entryObjectId", scheduleId);
71
+ entry.set("tenant", tenant);
72
+ entry.set("config", {});
73
+ }
74
+ const values = { microsoftCalendarEventId: eventId };
75
+ if (changeKey)
76
+ values.microsoftCalendarChangeKey = changeKey;
77
+ if (event)
78
+ values.microsoftCalendarEvent = toEventSnapshot(event);
79
+ entry.set("values", values);
80
+ return entry.save(null, { useMasterKey: true });
81
+ }
82
+ async function updateCalendarChangeKey(entry, changeKey, event) {
83
+ entry.set("values", {
84
+ ...entry.get("values"),
85
+ microsoftCalendarChangeKey: changeKey,
86
+ ...(event ? { microsoftCalendarEvent: toEventSnapshot(event) } : {}),
87
+ });
88
+ await entry.save(null, { useMasterKey: true });
89
+ }
@@ -0,0 +1,8 @@
1
+ export type ConfigValues = {
2
+ tenant_id: string;
3
+ client_id: string;
4
+ client_secret: string;
5
+ user_email: string;
6
+ default_source_id?: string;
7
+ delta_link?: string;
8
+ };
@@ -0,0 +1,2 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
@@ -60,7 +60,7 @@ export type GraphEvent = {
60
60
  subject?: string;
61
61
  body?: ItemBody;
62
62
  start: DateTimeTimeZone;
63
- end: DateTimeTimeZone;
63
+ end?: DateTimeTimeZone;
64
64
  location?: Location;
65
65
  attendees?: Attendee[];
66
66
  isAllDay?: boolean;
@@ -6,8 +6,14 @@ Object.defineProperty(exports, "__esModule", { value: true });
6
6
  exports.initSchedulesFeature = initSchedulesFeature;
7
7
  const node_1 = __importDefault(require("parse/node"));
8
8
  const index_js_1 = require("../../../types/index.js");
9
+ const CalendarSyncPoller_js_1 = require("./calendarSync/service/CalendarSyncPoller.js");
9
10
  async function initSchedulesFeature() {
10
11
  updateFiinishedSchedules();
12
+ const configs = await new node_1.default.Query(index_js_1.Config)
13
+ .equalTo("key", "OPENSERVICE_MICROSOFT_OUTLOOK_INTEGRATION")
14
+ .includeAll()
15
+ .findAll({ useMasterKey: true });
16
+ const pollers = configs.map((config) => new CalendarSyncPoller_js_1.CalendarSyncPoller(config).start(1000 * 60));
11
17
  }
12
18
  async function updateFiinishedSchedules() {
13
19
  const executionsToUpdate = await new node_1.default.Query(index_js_1.Maintenance_Schedule_Execution)
@@ -45,6 +45,8 @@ async function init() {
45
45
  //object.value can look like this: [{"label":"PSA","allowedValues":[],"required":false},{"label":"Typ","allowedValues":[],"required":false,"onlyOneAllowedValue":false},{"label":"Material","allowedValues":["@OD3_Maintenance_Item"],"required":false,"onlyOneAllowedValue":false},{"label":"Dienstleister","allowedValues":["@OD3_COMPANY","@OD3_CONTACT"],"required":false,"onlyOneAllowedValue":false}]
46
46
  //fields in Maintenance_Step can look like this: [{"value":"Handschuhe","metafield":{"label":"PSA","allowedValues":[],"required":false}},{"value":"Auffüllen","metafield":{"label":"Typ","allowedValues":[],"required":false,"onlyOneAllowedValue":false}}]
47
47
  const metafieldsconfig = JSON.parse(object.get("value"));
48
+ if (!Array.isArray(metafieldsconfig))
49
+ return;
48
50
  const [maintenanceStepQueryError, maintenanceStepQuery] = await (0, catchError_js_1.catchError)(new node_1.default.Query(index_js_2.Maintenance_Schedule_Step).find({
49
51
  useMasterKey: true,
50
52
  }));
@@ -6,12 +6,12 @@ Object.defineProperty(exports, "__esModule", { value: true });
6
6
  exports.init = init;
7
7
  const node_1 = __importDefault(require("parse/node"));
8
8
  const scheduleToEvent_1 = require("../features/openservice/schedules/calendarSync/functions/scheduleToEvent");
9
+ const calendarMetaHelper_1 = require("../features/openservice/schedules/calendarSync/service/calendarMetaHelper");
9
10
  const CalendarManager_1 = require("../features/openservice/schedules/calendarSync/service/CalendarManager");
10
11
  const schema_1 = require("../features/schema");
11
12
  const types_1 = require("../types");
12
13
  async function init() {
13
14
  (0, schema_1.beforeSaveHook)(types_1.Maintenance_Schedule, async (request) => {
14
- const { object, original, user } = request;
15
15
  await (0, schema_1.defaultHandler)(request);
16
16
  // await defaultAclHandler(request, { allowCustomACL: true });
17
17
  });
@@ -51,8 +51,8 @@ async function init() {
51
51
  }
52
52
  });
53
53
  (0, schema_1.beforeDeleteHook)(types_1.Maintenance_Schedule, async (request) => {
54
- const { object, original, user, master } = request;
55
- const scheduleFetched = await object?.fetchWithInclude(["template", "source"], {
54
+ const { object } = request;
55
+ const scheduleFetched = await object?.fetchWithInclude(["template", "source", "tenant"], {
56
56
  useMasterKey: true,
57
57
  });
58
58
  const template = scheduleFetched?.get("template");
@@ -60,9 +60,16 @@ async function init() {
60
60
  if (source)
61
61
  template?.relation("sources").remove(source);
62
62
  await template?.save(null, { useMasterKey: true });
63
- // Delete associated calendar event
64
- if (object.get("meta")?.microsoftCalendarEventId) {
65
- await new CalendarManager_1.CalendarManager().deleteEvent(object.get("meta")?.microsoftCalendarEventId);
63
+ const metaEntry = await (0, calendarMetaHelper_1.findCalendarMetaForSchedule)(object.id);
64
+ if (metaEntry) {
65
+ const eventId = metaEntry.get("values")?.microsoftCalendarEventId;
66
+ if (eventId) {
67
+ const calendarManager = await getScheduleCalendarManager(object);
68
+ if (!calendarManager)
69
+ return;
70
+ await calendarManager.deleteEvent(eventId);
71
+ }
72
+ await metaEntry.destroy({ useMasterKey: true });
66
73
  }
67
74
  });
68
75
  }
@@ -85,15 +92,20 @@ async function addToTemplateSources(schedule) {
85
92
  await template.save(null, { useMasterKey: true });
86
93
  }
87
94
  async function updateCalendarEvent(schedule) {
88
- const event = await (0, scheduleToEvent_1.scheduleToEvent)(schedule);
95
+ const metaEntry = await (0, calendarMetaHelper_1.findCalendarMetaForSchedule)(schedule.id);
96
+ const snapshot = metaEntry?.get("values")?.microsoftCalendarEvent;
97
+ const event = await (0, scheduleToEvent_1.scheduleToEvent)(schedule, snapshot);
89
98
  if (!event)
90
99
  return;
91
- const calendarManager = new CalendarManager_1.CalendarManager();
92
- const meta = schedule.get("meta") || {};
93
- const microsoftCalendarEventId = meta.microsoftCalendarEventId;
94
- if (microsoftCalendarEventId) {
95
- console.log("Updating schedule in calendar...");
96
- await calendarManager.updateEvent(microsoftCalendarEventId, event);
100
+ const calendarManager = await getScheduleCalendarManager(schedule);
101
+ if (!calendarManager)
102
+ return;
103
+ const eventId = metaEntry?.get("values")?.microsoftCalendarEventId;
104
+ if (eventId) {
105
+ const updatedEvent = await calendarManager.updateEvent(eventId, event);
106
+ if (updatedEvent?.changeKey && metaEntry) {
107
+ await (0, calendarMetaHelper_1.updateCalendarChangeKey)(metaEntry, updatedEvent.changeKey, updatedEvent);
108
+ }
97
109
  }
98
110
  else {
99
111
  await addToCalendar(schedule);
@@ -104,18 +116,31 @@ async function updateCalendarEvent(schedule) {
104
116
  * @param schedule
105
117
  */
106
118
  async function addToCalendar(schedule) {
107
- console.log("Creating new calendar event for schedule...");
108
119
  const event = await (0, scheduleToEvent_1.scheduleToEvent)(schedule);
109
120
  if (!event)
110
121
  return;
111
- const calendarManager = new CalendarManager_1.CalendarManager();
122
+ const calendarManager = await getScheduleCalendarManager(schedule);
123
+ if (!calendarManager)
124
+ return;
112
125
  const createdEvent = await calendarManager.createEvent(event);
113
- if (createdEvent && createdEvent.id) {
114
- const existingMeta = schedule.get("meta") || {};
115
- schedule.set("meta", {
116
- ...existingMeta,
117
- microsoftCalendarEventId: createdEvent.id,
118
- });
119
- await schedule.save(null, { useMasterKey: true });
126
+ if (createdEvent?.id) {
127
+ await (0, calendarMetaHelper_1.saveCalendarMeta)(schedule.id, createdEvent.id, createdEvent.changeKey, schedule.get("tenant"), createdEvent);
120
128
  }
121
129
  }
130
+ async function getScheduleCalendarManager(schedule) {
131
+ const config = await new node_1.default.Query(types_1.Config)
132
+ .equalTo("key", "OPENSERVICE_MICROSOFT_OUTLOOK_INTEGRATION")
133
+ .equalTo("tenant", schedule.get("tenant"))
134
+ .first({ useMasterKey: true });
135
+ if (!config) {
136
+ return;
137
+ }
138
+ const configValues = JSON.parse(config.get("value"));
139
+ const calendarManager = new CalendarManager_1.CalendarManager({
140
+ tenantId: configValues.tenant_id,
141
+ clientID: configValues.client_id,
142
+ clientSecret: configValues.client_secret,
143
+ userMail: configValues.user_email,
144
+ });
145
+ return calendarManager;
146
+ }
@@ -1,19 +1,20 @@
1
1
  import Parse from "parse/node";
2
2
  import type { Tenant } from "./Tenant";
3
+ import { MetaConfig } from "./custom/MetaField";
3
4
  export interface Meta_ConfigAttributes {
4
5
  id: string;
5
6
  objectId: string;
6
7
  createdAt: Date;
7
8
  updatedAt: Date;
8
- config: any;
9
+ config: MetaConfig;
9
10
  context?: string | undefined;
10
11
  tenant?: Tenant | undefined;
11
12
  }
12
13
  export declare class Meta_Config extends Parse.Object<Meta_ConfigAttributes> {
13
14
  static className: string;
14
15
  constructor(data?: Partial<Meta_ConfigAttributes>);
15
- get config(): any;
16
- set config(value: any);
16
+ get config(): MetaConfig;
17
+ set config(value: MetaConfig);
17
18
  get context(): string | undefined;
18
19
  set context(value: string | undefined);
19
20
  get tenant(): Tenant | undefined;
@@ -1,12 +1,13 @@
1
1
  import Parse from "parse/node";
2
2
  import type { Meta_Config } from "./Meta_Config";
3
3
  import type { Tenant } from "./Tenant";
4
+ import { MetaConfig } from "./custom/MetaField";
4
5
  export interface Meta_EntryAttributes {
5
6
  id: string;
6
7
  objectId: string;
7
8
  createdAt: Date;
8
9
  updatedAt: Date;
9
- config?: any | undefined;
10
+ config?: MetaConfig | undefined;
10
11
  context?: string | undefined;
11
12
  entryClassName?: string | undefined;
12
13
  entryObjectId?: string | undefined;
@@ -17,8 +18,8 @@ export interface Meta_EntryAttributes {
17
18
  export declare class Meta_Entry extends Parse.Object<Meta_EntryAttributes> {
18
19
  static className: string;
19
20
  constructor(data?: Partial<Meta_EntryAttributes>);
20
- get config(): any | undefined;
21
- set config(value: any | undefined);
21
+ get config(): MetaConfig | undefined;
22
+ set config(value: MetaConfig | undefined);
22
23
  get context(): string | undefined;
23
24
  set context(value: string | undefined);
24
25
  get entryClassName(): string | undefined;
@@ -0,0 +1,117 @@
1
+ /**
2
+ * Base properties shared by all meta field types
3
+ */
4
+ export type MetaFieldBaseConfig = {
5
+ name: string;
6
+ label: string;
7
+ placeholder?: string;
8
+ help?: string;
9
+ required?: boolean;
10
+ /** Default value for the field */
11
+ defaultValue?: any;
12
+ };
13
+ /**
14
+ * String field type with optional validation
15
+ */
16
+ export type MetaFieldString = MetaFieldBaseConfig & {
17
+ type: "string";
18
+ minLength?: number;
19
+ maxLength?: number;
20
+ pattern?: string;
21
+ multiline?: boolean;
22
+ };
23
+ /**
24
+ * Number field type with min/max constraints
25
+ */
26
+ export type MetaFieldNumber = MetaFieldBaseConfig & {
27
+ type: "number";
28
+ min?: number;
29
+ max?: number;
30
+ step?: number;
31
+ unit?: string;
32
+ };
33
+ /**
34
+ * Boolean field type (checkbox/switch)
35
+ */
36
+ export type MetaFieldBoolean = MetaFieldBaseConfig & {
37
+ type: "boolean";
38
+ defaultValue?: boolean;
39
+ };
40
+ /**
41
+ * Date field type with optional date range constraints
42
+ */
43
+ export type MetaFieldDate = MetaFieldBaseConfig & {
44
+ type: "date";
45
+ minDate?: Date;
46
+ maxDate?: Date;
47
+ includeTime?: boolean;
48
+ };
49
+ /**
50
+ * Select field type with predefined options
51
+ */
52
+ export type MetaFieldSelect = MetaFieldBaseConfig & {
53
+ type: "select";
54
+ options: Array<{
55
+ label: string;
56
+ value: string;
57
+ disabled?: boolean;
58
+ }>;
59
+ /** Optional grouping for select options */
60
+ groups?: Array<{
61
+ label: string;
62
+ options: Array<{
63
+ label: string;
64
+ value: string;
65
+ disabled?: boolean;
66
+ }>;
67
+ }>;
68
+ multiple?: boolean;
69
+ defaultValue?: string | string[];
70
+ };
71
+ /**
72
+ * Parse select field type for selecting Parse objects
73
+ */
74
+ export type MetaFieldSelectParse = MetaFieldBaseConfig & {
75
+ type: "select_parse";
76
+ className: string;
77
+ display?: string;
78
+ multiple?: boolean;
79
+ defaultValue?: string | string[];
80
+ };
81
+ /**
82
+ * Array of all available field types
83
+ */
84
+ export declare const META_FIELD_TYPES: readonly ["string", "number", "boolean", "date", "select", "select_parse"];
85
+ /**
86
+ * Literal type for field types derived from the array
87
+ */
88
+ export type MetaFieldTypes = (typeof META_FIELD_TYPES)[number];
89
+ /**
90
+ * Mapped type for accessing MetaField types by their string literal
91
+ * @example MetaFieldMap["string"] => MetaFieldString
92
+ */
93
+ export type MetaFieldMap = {
94
+ string: MetaFieldString;
95
+ number: MetaFieldNumber;
96
+ boolean: MetaFieldBoolean;
97
+ date: MetaFieldDate;
98
+ select: MetaFieldSelect;
99
+ select_parse: MetaFieldSelectParse;
100
+ };
101
+ /**
102
+ * Union type of all specific meta field types
103
+ */
104
+ export type MetaFieldConfig = MetaFieldString | MetaFieldNumber | MetaFieldBoolean | MetaFieldDate | MetaFieldSelect | MetaFieldSelectParse;
105
+ /**
106
+ * Helper type to extract specific field type from MetaField union
107
+ */
108
+ export type MetaFieldOfType<T extends MetaFieldTypes> = Extract<MetaFieldConfig, {
109
+ type: T;
110
+ }>;
111
+ /**
112
+ * Type guard to check if a field is of a specific type
113
+ */
114
+ export declare function isMetaFieldType<T extends MetaFieldTypes>(field: MetaFieldConfig, type: T): field is MetaFieldOfType<T>;
115
+ export type MetaConfig = {
116
+ fields: MetaFieldConfig[];
117
+ };