@kelpie/server 0.12.0 → 0.13.0
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/app.d.ts +13 -0
- package/dist/app.d.ts.map +1 -1
- package/dist/app.js +8 -1
- package/dist/app.js.map +1 -1
- package/dist/boot.d.ts.map +1 -1
- package/dist/boot.js +1 -0
- package/dist/boot.js.map +1 -1
- package/dist/lib/apiKeyScopes.d.ts.map +1 -1
- package/dist/lib/apiKeyScopes.js +6 -0
- package/dist/lib/apiKeyScopes.js.map +1 -1
- package/dist/lib/columns.d.ts +6 -0
- package/dist/lib/columns.d.ts.map +1 -1
- package/dist/lib/columns.js +5 -3
- package/dist/lib/columns.js.map +1 -1
- package/dist/lib/config.d.ts +17 -0
- package/dist/lib/config.d.ts.map +1 -1
- package/dist/lib/config.js +3 -0
- package/dist/lib/config.js.map +1 -1
- package/dist/lib/egress.d.ts.map +1 -1
- package/dist/lib/egress.js +8 -4
- package/dist/lib/egress.js.map +1 -1
- package/dist/lib/errors.d.ts +3 -3
- package/dist/lib/errors.d.ts.map +1 -1
- package/dist/lib/errors.js +13 -5
- package/dist/lib/errors.js.map +1 -1
- package/dist/lib/ids.d.ts +11 -0
- package/dist/lib/ids.d.ts.map +1 -1
- package/dist/lib/ids.js +11 -0
- package/dist/lib/ids.js.map +1 -1
- package/dist/lib/kelpieConfigFile.d.ts +15 -1
- package/dist/lib/kelpieConfigFile.d.ts.map +1 -1
- package/dist/lib/kelpieConfigFile.js +33 -1
- package/dist/lib/kelpieConfigFile.js.map +1 -1
- package/dist/modules/agent-tasks/catalog.d.ts.map +1 -1
- package/dist/modules/agent-tasks/catalog.js +32 -1
- package/dist/modules/agent-tasks/catalog.js.map +1 -1
- package/dist/modules/agent-tasks/index.d.ts.map +1 -1
- package/dist/modules/agent-tasks/index.js +1 -0
- package/dist/modules/agent-tasks/index.js.map +1 -1
- package/dist/modules/agent-tasks/resolve.d.ts.map +1 -1
- package/dist/modules/agent-tasks/resolve.js +5 -0
- package/dist/modules/agent-tasks/resolve.js.map +1 -1
- package/dist/modules/agent-tasks/routes.d.ts +2 -0
- package/dist/modules/agent-tasks/routes.d.ts.map +1 -1
- package/dist/modules/attachedRecords.d.ts +22 -8
- package/dist/modules/attachedRecords.d.ts.map +1 -1
- package/dist/modules/attachedRecords.js +42 -5
- package/dist/modules/attachedRecords.js.map +1 -1
- package/dist/modules/auth/index.d.ts.map +1 -1
- package/dist/modules/auth/index.js +1 -0
- package/dist/modules/auth/index.js.map +1 -1
- package/dist/modules/auth/repository.d.ts +14 -0
- package/dist/modules/auth/repository.d.ts.map +1 -1
- package/dist/modules/auth/repository.js +20 -2
- package/dist/modules/auth/repository.js.map +1 -1
- package/dist/modules/auth/routes.d.ts.map +1 -1
- package/dist/modules/auth/routes.js +30 -0
- package/dist/modules/auth/routes.js.map +1 -1
- package/dist/modules/auth/service.d.ts +28 -0
- package/dist/modules/auth/service.d.ts.map +1 -1
- package/dist/modules/auth/service.js +72 -4
- package/dist/modules/auth/service.js.map +1 -1
- package/dist/modules/companies/repository.d.ts.map +1 -1
- package/dist/modules/companies/repository.js +1 -1
- package/dist/modules/companies/repository.js.map +1 -1
- package/dist/modules/companies/routes.d.ts +2 -4
- package/dist/modules/companies/routes.d.ts.map +1 -1
- package/dist/modules/companies/routes.js +6 -11
- package/dist/modules/companies/routes.js.map +1 -1
- package/dist/modules/companies/schema.d.ts +13 -28
- package/dist/modules/companies/schema.d.ts.map +1 -1
- package/dist/modules/companies/schema.js +2 -2
- package/dist/modules/companies/schema.js.map +1 -1
- package/dist/modules/companies/service.d.ts +2 -3
- package/dist/modules/companies/service.d.ts.map +1 -1
- package/dist/modules/companies/service.js +4 -7
- package/dist/modules/companies/service.js.map +1 -1
- package/dist/modules/core.d.ts.map +1 -1
- package/dist/modules/core.js +2 -0
- package/dist/modules/core.js.map +1 -1
- package/dist/modules/custom-fields/repository.d.ts.map +1 -1
- package/dist/modules/custom-fields/repository.js +2 -0
- package/dist/modules/custom-fields/repository.js.map +1 -1
- package/dist/modules/custom-fields/routes.d.ts +1 -0
- package/dist/modules/custom-fields/routes.d.ts.map +1 -1
- package/dist/modules/dashboard/index.d.ts.map +1 -1
- package/dist/modules/dashboard/index.js +1 -0
- package/dist/modules/dashboard/index.js.map +1 -1
- package/dist/modules/dashboard/repository.d.ts +13 -0
- package/dist/modules/dashboard/repository.d.ts.map +1 -1
- package/dist/modules/dashboard/repository.js +36 -0
- package/dist/modules/dashboard/repository.js.map +1 -1
- package/dist/modules/dashboard/routes.d.ts.map +1 -1
- package/dist/modules/dashboard/routes.js +13 -0
- package/dist/modules/dashboard/routes.js.map +1 -1
- package/dist/modules/dashboard/service.d.ts +11 -0
- package/dist/modules/dashboard/service.d.ts.map +1 -1
- package/dist/modules/dashboard/service.js +24 -1
- package/dist/modules/dashboard/service.js.map +1 -1
- package/dist/modules/decisions/repository.d.ts.map +1 -1
- package/dist/modules/decisions/repository.js +2 -0
- package/dist/modules/decisions/repository.js.map +1 -1
- package/dist/modules/decisions/routes.d.ts +2 -0
- package/dist/modules/decisions/routes.d.ts.map +1 -1
- package/dist/modules/events/attendances.d.ts +35 -0
- package/dist/modules/events/attendances.d.ts.map +1 -0
- package/dist/modules/events/attendances.js +157 -0
- package/dist/modules/events/attendances.js.map +1 -0
- package/dist/modules/events/catalog.d.ts +35 -0
- package/dist/modules/events/catalog.d.ts.map +1 -0
- package/dist/modules/events/catalog.js +11 -0
- package/dist/modules/events/catalog.js.map +1 -0
- package/dist/modules/events/index.d.ts +10 -0
- package/dist/modules/events/index.d.ts.map +1 -0
- package/dist/modules/events/index.js +47 -0
- package/dist/modules/events/index.js.map +1 -0
- package/dist/modules/events/repository.d.ts +67 -0
- package/dist/modules/events/repository.d.ts.map +1 -0
- package/dist/modules/events/repository.js +212 -0
- package/dist/modules/events/repository.js.map +1 -0
- package/dist/modules/events/routes.d.ts +136 -0
- package/dist/modules/events/routes.d.ts.map +1 -0
- package/dist/modules/events/routes.js +280 -0
- package/dist/modules/events/routes.js.map +1 -0
- package/dist/modules/events/schema.d.ts +626 -0
- package/dist/modules/events/schema.d.ts.map +1 -0
- package/dist/modules/events/schema.js +93 -0
- package/dist/modules/events/schema.js.map +1 -0
- package/dist/modules/events/service.d.ts +68 -0
- package/dist/modules/events/service.d.ts.map +1 -0
- package/dist/modules/events/service.js +262 -0
- package/dist/modules/events/service.js.map +1 -0
- package/dist/modules/events/tools.d.ts +8 -0
- package/dist/modules/events/tools.d.ts.map +1 -0
- package/dist/modules/events/tools.js +80 -0
- package/dist/modules/events/tools.js.map +1 -0
- package/dist/modules/forms/applySubmissionFields.d.ts.map +1 -1
- package/dist/modules/forms/applySubmissionFields.js +7 -5
- package/dist/modules/forms/applySubmissionFields.js.map +1 -1
- package/dist/modules/forms/fillAddresses.d.ts +4 -0
- package/dist/modules/forms/fillAddresses.d.ts.map +1 -0
- package/dist/modules/forms/fillAddresses.js +93 -0
- package/dist/modules/forms/fillAddresses.js.map +1 -0
- package/dist/modules/forms/repository.d.ts +2 -2
- package/dist/modules/forms/repository.d.ts.map +1 -1
- package/dist/modules/forms/repository.js.map +1 -1
- package/dist/modules/forms/routes.d.ts +2 -0
- package/dist/modules/forms/routes.d.ts.map +1 -1
- package/dist/modules/forms/routes.js +2 -2
- package/dist/modules/forms/routes.js.map +1 -1
- package/dist/modules/forms/schema.js +2 -2
- package/dist/modules/forms/schema.js.map +1 -1
- package/dist/modules/forms/service.js +2 -2
- package/dist/modules/forms/service.js.map +1 -1
- package/dist/modules/forms/submission.d.ts +1 -0
- package/dist/modules/forms/submission.d.ts.map +1 -1
- package/dist/modules/forms/submission.js +27 -4
- package/dist/modules/forms/submission.js.map +1 -1
- package/dist/modules/hiring/roles.d.ts.map +1 -1
- package/dist/modules/hiring/roles.js +2 -1
- package/dist/modules/hiring/roles.js.map +1 -1
- package/dist/modules/import-export/addresses.d.ts +22 -0
- package/dist/modules/import-export/addresses.d.ts.map +1 -0
- package/dist/modules/import-export/addresses.js +83 -0
- package/dist/modules/import-export/addresses.js.map +1 -0
- package/dist/modules/import-export/drafts.d.ts +3 -4
- package/dist/modules/import-export/drafts.d.ts.map +1 -1
- package/dist/modules/import-export/drafts.js +4 -5
- package/dist/modules/import-export/drafts.js.map +1 -1
- package/dist/modules/import-export/exportRows.d.ts.map +1 -1
- package/dist/modules/import-export/exportRows.js +3 -3
- package/dist/modules/import-export/exportRows.js.map +1 -1
- package/dist/modules/import-export/plan.d.ts.map +1 -1
- package/dist/modules/import-export/plan.js +17 -2
- package/dist/modules/import-export/plan.js.map +1 -1
- package/dist/modules/import-export/presets.d.ts.map +1 -1
- package/dist/modules/import-export/presets.js +52 -7
- package/dist/modules/import-export/presets.js.map +1 -1
- package/dist/modules/import-export/repository.d.ts +3 -4
- package/dist/modules/import-export/repository.d.ts.map +1 -1
- package/dist/modules/import-export/repository.js +2 -3
- package/dist/modules/import-export/repository.js.map +1 -1
- package/dist/modules/import-export/writes.d.ts.map +1 -1
- package/dist/modules/import-export/writes.js +8 -4
- package/dist/modules/import-export/writes.js.map +1 -1
- package/dist/modules/lists/routes.d.ts +4 -0
- package/dist/modules/lists/routes.d.ts.map +1 -1
- package/dist/modules/notes/routes.d.ts +2 -0
- package/dist/modules/notes/routes.d.ts.map +1 -1
- package/dist/modules/people/repository.d.ts.map +1 -1
- package/dist/modules/people/repository.js +1 -1
- package/dist/modules/people/repository.js.map +1 -1
- package/dist/modules/people/routes.d.ts +2 -2
- package/dist/modules/people/routes.js +6 -6
- package/dist/modules/people/routes.js.map +1 -1
- package/dist/modules/people/schema.d.ts +17 -14
- package/dist/modules/people/schema.d.ts.map +1 -1
- package/dist/modules/people/schema.js +6 -4
- package/dist/modules/people/schema.js.map +1 -1
- package/dist/modules/people/service.d.ts +2 -2
- package/dist/modules/people/service.d.ts.map +1 -1
- package/dist/modules/people/service.js +5 -4
- package/dist/modules/people/service.js.map +1 -1
- package/dist/modules/plans/repository.d.ts +2 -2
- package/dist/modules/plans/repository.d.ts.map +1 -1
- package/dist/modules/plans/routes.d.ts +1 -0
- package/dist/modules/plans/routes.d.ts.map +1 -1
- package/dist/modules/plans/routes.js +4 -4
- package/dist/modules/plans/routes.js.map +1 -1
- package/dist/modules/plans/schema.d.ts +2 -2
- package/dist/modules/plans/schema.d.ts.map +1 -1
- package/dist/modules/plans/schema.js +3 -3
- package/dist/modules/plans/schema.js.map +1 -1
- package/dist/modules/plans/service.d.ts +2 -2
- package/dist/modules/plans/service.d.ts.map +1 -1
- package/dist/modules/plans/service.js.map +1 -1
- package/dist/modules/plans/tools.js +2 -2
- package/dist/modules/plans/tools.js.map +1 -1
- package/dist/modules/recordTargets.d.ts.map +1 -1
- package/dist/modules/recordTargets.js +11 -0
- package/dist/modules/recordTargets.js.map +1 -1
- package/dist/modules/references.d.ts.map +1 -1
- package/dist/modules/references.js +2 -0
- package/dist/modules/references.js.map +1 -1
- package/dist/modules/sample-data/fixture.d.ts +26 -4
- package/dist/modules/sample-data/fixture.d.ts.map +1 -1
- package/dist/modules/sample-data/fixture.js +157 -23
- package/dist/modules/sample-data/fixture.js.map +1 -1
- package/dist/modules/sample-data/index.d.ts +5 -2
- package/dist/modules/sample-data/index.d.ts.map +1 -1
- package/dist/modules/sample-data/index.js +7 -2
- package/dist/modules/sample-data/index.js.map +1 -1
- package/dist/modules/sample-data/routes.d.ts.map +1 -1
- package/dist/modules/sample-data/routes.js +2 -0
- package/dist/modules/sample-data/routes.js.map +1 -1
- package/dist/modules/sample-data/service.d.ts +11 -3
- package/dist/modules/sample-data/service.d.ts.map +1 -1
- package/dist/modules/sample-data/service.js +452 -363
- package/dist/modules/sample-data/service.js.map +1 -1
- package/dist/modules/sample-data/tools.d.ts.map +1 -1
- package/dist/modules/sample-data/tools.js +4 -3
- package/dist/modules/sample-data/tools.js.map +1 -1
- package/dist/modules/search/index.d.ts +1 -1
- package/dist/modules/search/index.d.ts.map +1 -1
- package/dist/modules/search/index.js +2 -1
- package/dist/modules/search/index.js.map +1 -1
- package/dist/modules/search/repository.d.ts +1 -0
- package/dist/modules/search/repository.d.ts.map +1 -1
- package/dist/modules/search/repository.js +22 -0
- package/dist/modules/search/repository.js.map +1 -1
- package/dist/modules/search/service.d.ts +2 -2
- package/dist/modules/search/service.d.ts.map +1 -1
- package/dist/modules/search/service.js +4 -3
- package/dist/modules/search/service.js.map +1 -1
- package/dist/modules/smtp-email/index.d.ts +12 -4
- package/dist/modules/smtp-email/index.d.ts.map +1 -1
- package/dist/modules/smtp-email/index.js +25 -5
- package/dist/modules/smtp-email/index.js.map +1 -1
- package/dist/modules/workspace/repository.d.ts +2 -0
- package/dist/modules/workspace/repository.d.ts.map +1 -1
- package/dist/modules/workspace/repository.js +12 -0
- package/dist/modules/workspace/repository.js.map +1 -1
- package/dist/modules/workspace/routes.d.ts.map +1 -1
- package/dist/modules/workspace/routes.js +13 -0
- package/dist/modules/workspace/routes.js.map +1 -1
- package/dist/modules/workspace/service.d.ts +10 -0
- package/dist/modules/workspace/service.d.ts.map +1 -1
- package/dist/modules/workspace/service.js +34 -10
- package/dist/modules/workspace/service.js.map +1 -1
- package/dist/modules/workspace/starters.d.ts +17 -2
- package/dist/modules/workspace/starters.d.ts.map +1 -1
- package/dist/modules/workspace/starters.js +141 -14
- package/dist/modules/workspace/starters.js.map +1 -1
- package/dist/modules/workspace/workspaceAccessMiddleware.d.ts.map +1 -1
- package/dist/modules/workspace/workspaceAccessMiddleware.js +3 -4
- package/dist/modules/workspace/workspaceAccessMiddleware.js.map +1 -1
- package/dist/runtime/events.d.ts +1 -1
- package/dist/runtime/events.d.ts.map +1 -1
- package/dist/runtime/events.js +2 -0
- package/dist/runtime/events.js.map +1 -1
- package/dist/runtime/module.d.ts +8 -0
- package/dist/runtime/module.d.ts.map +1 -1
- package/dist/runtime/registry.d.ts.map +1 -1
- package/dist/runtime/registry.js +102 -7
- package/dist/runtime/registry.js.map +1 -1
- package/dist/schema/index.d.ts +1 -0
- package/dist/schema/index.d.ts.map +1 -1
- package/dist/schema/index.js +1 -0
- package/dist/schema/index.js.map +1 -1
- package/dist/testing/app.d.ts +8 -0
- package/dist/testing/app.d.ts.map +1 -1
- package/dist/testing/app.js +6 -1
- package/dist/testing/app.js.map +1 -1
- package/dist/testing/client.d.ts.map +1 -1
- package/dist/testing/client.js +9 -1
- package/dist/testing/client.js.map +1 -1
- package/migrations/0044_events.sql +79 -0
- package/migrations/0045_addresses.sql +66 -0
- package/migrations/0046_drop_company_website.sql +66 -0
- package/migrations/meta/_journal.json +21 -0
- package/package.json +3 -3
- package/src/app.ts +25 -1
- package/src/boot.ts +1 -0
- package/src/lib/apiKeyScopes.ts +6 -0
- package/src/lib/columns.ts +12 -3
- package/src/lib/config.ts +21 -0
- package/src/lib/egress.ts +8 -4
- package/src/lib/errors.ts +16 -9
- package/src/lib/ids.ts +11 -0
- package/src/lib/kelpieConfigFile.ts +69 -1
- package/src/modules/agent-tasks/catalog.ts +33 -1
- package/src/modules/agent-tasks/index.ts +1 -0
- package/src/modules/agent-tasks/resolve.ts +11 -0
- package/src/modules/attachedRecords.ts +85 -13
- package/src/modules/auth/index.ts +1 -0
- package/src/modules/auth/repository.ts +34 -3
- package/src/modules/auth/routes.ts +44 -1
- package/src/modules/auth/service.ts +118 -4
- package/src/modules/companies/repository.ts +1 -0
- package/src/modules/companies/routes.ts +6 -11
- package/src/modules/companies/schema.ts +3 -3
- package/src/modules/companies/service.ts +6 -10
- package/src/modules/core.ts +2 -0
- package/src/modules/custom-fields/repository.ts +2 -0
- package/src/modules/dashboard/index.ts +1 -0
- package/src/modules/dashboard/repository.ts +64 -0
- package/src/modules/dashboard/routes.ts +15 -0
- package/src/modules/dashboard/service.ts +48 -0
- package/src/modules/decisions/repository.ts +2 -0
- package/src/modules/events/attendances.ts +245 -0
- package/src/modules/events/catalog.ts +36 -0
- package/src/modules/events/index.ts +54 -0
- package/src/modules/events/repository.ts +396 -0
- package/src/modules/events/routes.ts +405 -0
- package/src/modules/events/schema.ts +148 -0
- package/src/modules/events/service.ts +443 -0
- package/src/modules/events/tools.ts +118 -0
- package/src/modules/forms/applySubmissionFields.ts +7 -5
- package/src/modules/forms/fillAddresses.ts +185 -0
- package/src/modules/forms/repository.ts +3 -3
- package/src/modules/forms/routes.ts +2 -2
- package/src/modules/forms/schema.ts +2 -2
- package/src/modules/forms/service.ts +2 -2
- package/src/modules/forms/submission.ts +34 -4
- package/src/modules/hiring/roles.ts +2 -1
- package/src/modules/import-export/addresses.ts +150 -0
- package/src/modules/import-export/drafts.ts +7 -9
- package/src/modules/import-export/exportRows.ts +3 -3
- package/src/modules/import-export/plan.ts +30 -2
- package/src/modules/import-export/presets.ts +55 -7
- package/src/modules/import-export/repository.ts +5 -7
- package/src/modules/import-export/writes.ts +8 -4
- package/src/modules/people/repository.ts +1 -0
- package/src/modules/people/routes.ts +6 -6
- package/src/modules/people/schema.ts +7 -5
- package/src/modules/people/service.ts +7 -5
- package/src/modules/plans/repository.ts +2 -2
- package/src/modules/plans/routes.ts +6 -6
- package/src/modules/plans/schema.ts +4 -4
- package/src/modules/plans/service.ts +3 -3
- package/src/modules/plans/tools.ts +2 -2
- package/src/modules/recordTargets.ts +14 -0
- package/src/modules/references.ts +2 -0
- package/src/modules/sample-data/fixture.ts +208 -27
- package/src/modules/sample-data/index.ts +7 -2
- package/src/modules/sample-data/routes.ts +2 -0
- package/src/modules/sample-data/service.ts +166 -49
- package/src/modules/sample-data/tools.ts +4 -3
- package/src/modules/search/index.ts +2 -1
- package/src/modules/search/repository.ts +32 -1
- package/src/modules/search/service.ts +6 -4
- package/src/modules/smtp-email/index.ts +32 -10
- package/src/modules/workspace/repository.ts +18 -0
- package/src/modules/workspace/routes.ts +16 -0
- package/src/modules/workspace/service.ts +59 -14
- package/src/modules/workspace/starters.ts +275 -14
- package/src/modules/workspace/workspaceAccessMiddleware.ts +3 -4
- package/src/runtime/events.ts +2 -0
- package/src/runtime/module.ts +8 -0
- package/src/runtime/registry.ts +122 -8
- package/src/schema/index.ts +1 -0
- package/src/testing/app.ts +14 -1
- package/src/testing/client.ts +11 -1
|
@@ -0,0 +1,443 @@
|
|
|
1
|
+
import type { CustomFieldWireValue, EventAssociationTargetType } from '@kelpie/schemas'
|
|
2
|
+
import { EVENT_ASSOCIATION_TARGET_TYPES } from '@kelpie/schemas'
|
|
3
|
+
import { and, eq, inArray } from 'drizzle-orm'
|
|
4
|
+
|
|
5
|
+
import { changedKeys } from '../../lib/changes.ts'
|
|
6
|
+
import type { Database } from '../../lib/database.ts'
|
|
7
|
+
import { AppError } from '../../lib/errors.ts'
|
|
8
|
+
import type { IdFactory } from '../../lib/ids.ts'
|
|
9
|
+
import { readListWindow, toPage } from '../../lib/pagination.ts'
|
|
10
|
+
import type { ListQueryParameters, Page } from '../../lib/pagination.ts'
|
|
11
|
+
import { toEventActor } from '../../lib/actor.ts'
|
|
12
|
+
import type { TransactionScope } from '../../runtime/transaction.ts'
|
|
13
|
+
import type { ActivityRecorder } from '../activities/recorder.ts'
|
|
14
|
+
import { describeCreation, describeUpdate } from '../activities/wording.ts'
|
|
15
|
+
import type { FieldLabels } from '../activities/wording.ts'
|
|
16
|
+
import type { Actor } from '../auth/actor.ts'
|
|
17
|
+
import { actorMemberId, requireWorkspaceId } from '../auth/actor.ts'
|
|
18
|
+
import { deleteRecordsAttachedTo, deleteRecordsAttachedToAttendancesOf } from '../attachedRecords.ts'
|
|
19
|
+
import type { CustomFieldValuesValidator } from '../custom-fields/values.ts'
|
|
20
|
+
import { roles } from '../hiring/schema.ts'
|
|
21
|
+
import { isRecordTargetType, missingTargets } from '../recordTargets.ts'
|
|
22
|
+
import type { RecordTargetType } from '../recordTargets.ts'
|
|
23
|
+
import * as workspaceRepository from '../workspace/repository.ts'
|
|
24
|
+
import './catalog.ts'
|
|
25
|
+
import * as repository from './repository.ts'
|
|
26
|
+
import { DEFAULT_EVENT_SORT, EVENT_SORTS } from './repository.ts'
|
|
27
|
+
import type { AssociationRef, EventFilters, EventRecord } from './repository.ts'
|
|
28
|
+
|
|
29
|
+
export interface EventsDependencies {
|
|
30
|
+
readonly db: Database
|
|
31
|
+
readonly transaction: TransactionScope
|
|
32
|
+
readonly createId: IdFactory
|
|
33
|
+
readonly now: () => Date
|
|
34
|
+
readonly recordActivity: ActivityRecorder
|
|
35
|
+
readonly customFields: CustomFieldValuesValidator
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
const EVENT_FIELD_LABELS: FieldLabels = {
|
|
39
|
+
name: 'Name',
|
|
40
|
+
kind: 'Kind',
|
|
41
|
+
startsAt: 'Starts',
|
|
42
|
+
endsAt: 'Ends',
|
|
43
|
+
timezone: 'Timezone',
|
|
44
|
+
location: 'Location',
|
|
45
|
+
meetingUrl: 'Meeting URL',
|
|
46
|
+
format: 'Format',
|
|
47
|
+
details: 'Details',
|
|
48
|
+
status: 'Status',
|
|
49
|
+
ownerId: 'Owner',
|
|
50
|
+
summary: 'Summary',
|
|
51
|
+
tags: 'Tags',
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
export type EventView = Omit<EventRecord, 'workspaceId'> & {
|
|
55
|
+
readonly associations: readonly AssociationRef[]
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
export interface CreateEventInput {
|
|
59
|
+
readonly name: string
|
|
60
|
+
readonly kind: string
|
|
61
|
+
readonly startsAt: Date
|
|
62
|
+
readonly endsAt: Date
|
|
63
|
+
readonly timezone: string | undefined
|
|
64
|
+
readonly location: string
|
|
65
|
+
readonly meetingUrl: string | null
|
|
66
|
+
readonly format: EventRecord['format']
|
|
67
|
+
readonly details: string
|
|
68
|
+
readonly status: EventRecord['status']
|
|
69
|
+
readonly ownerId: string | null | undefined
|
|
70
|
+
readonly summary: string
|
|
71
|
+
readonly tags: readonly string[]
|
|
72
|
+
readonly associations: readonly AssociationRef[]
|
|
73
|
+
readonly customFields: Readonly<Record<string, CustomFieldWireValue | null>> | undefined
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
export interface UpdateEventInput {
|
|
77
|
+
readonly name?: string | undefined
|
|
78
|
+
readonly kind?: string | undefined
|
|
79
|
+
readonly startsAt?: Date | undefined
|
|
80
|
+
readonly endsAt?: Date | undefined
|
|
81
|
+
readonly timezone?: string | undefined
|
|
82
|
+
readonly location?: string | undefined
|
|
83
|
+
readonly meetingUrl?: string | null | undefined
|
|
84
|
+
readonly format?: EventRecord['format'] | undefined
|
|
85
|
+
readonly details?: string | undefined
|
|
86
|
+
readonly status?: EventRecord['status'] | undefined
|
|
87
|
+
readonly ownerId?: string | null | undefined
|
|
88
|
+
readonly summary?: string | undefined
|
|
89
|
+
readonly tags?: readonly string[] | undefined
|
|
90
|
+
readonly associations?: readonly AssociationRef[] | undefined
|
|
91
|
+
readonly customFields?: Readonly<Record<string, CustomFieldWireValue | null>> | undefined
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
export interface EventsService {
|
|
95
|
+
list(actor: Actor, filters: EventFilters, query: ListQueryParameters): Promise<Page<EventView>>
|
|
96
|
+
get(actor: Actor, id: string): Promise<EventView>
|
|
97
|
+
create(actor: Actor, input: CreateEventInput): Promise<EventView>
|
|
98
|
+
update(actor: Actor, id: string, changes: UpdateEventInput): Promise<EventView>
|
|
99
|
+
remove(actor: Actor, id: string): Promise<void>
|
|
100
|
+
listAssociationsFor(
|
|
101
|
+
actor: Actor,
|
|
102
|
+
targetType: EventAssociationTargetType,
|
|
103
|
+
targetId: string,
|
|
104
|
+
): Promise<readonly { readonly eventId: string; readonly eventName: string }[]>
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
function toView(record: EventRecord, associations: readonly AssociationRef[]): EventView {
|
|
108
|
+
const { workspaceId: _workspaceId, ...view } = record
|
|
109
|
+
|
|
110
|
+
return { ...view, associations }
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
function toStoredColumns(input: UpdateEventInput): Partial<repository.EventColumns> {
|
|
114
|
+
return {
|
|
115
|
+
...(input.name === undefined ? {} : { name: input.name }),
|
|
116
|
+
...(input.kind === undefined ? {} : { kind: input.kind }),
|
|
117
|
+
...(input.startsAt === undefined ? {} : { startsAt: input.startsAt }),
|
|
118
|
+
...(input.endsAt === undefined ? {} : { endsAt: input.endsAt }),
|
|
119
|
+
...(input.timezone === undefined ? {} : { timezone: input.timezone }),
|
|
120
|
+
...(input.location === undefined ? {} : { location: input.location }),
|
|
121
|
+
...(input.meetingUrl === undefined ? {} : { meetingUrl: input.meetingUrl }),
|
|
122
|
+
...(input.format === undefined ? {} : { format: input.format }),
|
|
123
|
+
...(input.details === undefined ? {} : { details: input.details }),
|
|
124
|
+
...(input.status === undefined ? {} : { status: input.status }),
|
|
125
|
+
...(input.ownerId === undefined ? {} : { ownerId: input.ownerId }),
|
|
126
|
+
...(input.summary === undefined ? {} : { summary: input.summary }),
|
|
127
|
+
...(input.tags === undefined ? {} : { tags: [...input.tags] }),
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
function requireRange(startsAt: Date, endsAt: Date): void {
|
|
132
|
+
if (endsAt.getTime() < startsAt.getTime()) {
|
|
133
|
+
throw AppError.validationFailed('The event cannot end before it starts', [
|
|
134
|
+
{ field: 'ends_at', message: 'Must be on or after starts_at' },
|
|
135
|
+
])
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
function uniqueAssociations(rows: readonly AssociationRef[]): AssociationRef[] {
|
|
140
|
+
const seen = new Set<string>()
|
|
141
|
+
const unique: AssociationRef[] = []
|
|
142
|
+
|
|
143
|
+
for (const row of rows) {
|
|
144
|
+
const key = `${row.targetType}:${row.targetId}`
|
|
145
|
+
|
|
146
|
+
if (seen.has(key)) {
|
|
147
|
+
continue
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
seen.add(key)
|
|
151
|
+
unique.push(row)
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
return unique
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
export function createEventsService(dependencies: EventsDependencies): EventsService {
|
|
158
|
+
async function require(workspaceId: string, id: string): Promise<EventRecord> {
|
|
159
|
+
const event = await repository.findEvent(dependencies.db, workspaceId, id)
|
|
160
|
+
|
|
161
|
+
if (event === undefined) {
|
|
162
|
+
throw AppError.notFound('Event not found')
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
return event
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
async function requireOwner(workspaceId: string, ownerId: string): Promise<void> {
|
|
169
|
+
if (!(await repository.memberExists(dependencies.db, workspaceId, ownerId))) {
|
|
170
|
+
throw AppError.notFound('Team member not found')
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
async function defaultTimezone(workspaceId: string): Promise<string> {
|
|
175
|
+
const workspace = await workspaceRepository.findWorkspace(dependencies.db, workspaceId)
|
|
176
|
+
|
|
177
|
+
return workspace?.timezone ?? 'UTC'
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
async function requireAssociations(
|
|
181
|
+
workspaceId: string,
|
|
182
|
+
rows: readonly AssociationRef[],
|
|
183
|
+
): Promise<void> {
|
|
184
|
+
const problems: { field: string; message: string }[] = []
|
|
185
|
+
|
|
186
|
+
for (const kind of EVENT_ASSOCIATION_TARGET_TYPES) {
|
|
187
|
+
const ofKind = rows.filter((row) => row.targetType === kind)
|
|
188
|
+
|
|
189
|
+
if (ofKind.length === 0) {
|
|
190
|
+
continue
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
const ids = ofKind.map((row) => row.targetId)
|
|
194
|
+
let missing: readonly string[]
|
|
195
|
+
|
|
196
|
+
if (kind === 'role') {
|
|
197
|
+
const found = await dependencies.db
|
|
198
|
+
.select({ id: roles.id })
|
|
199
|
+
.from(roles)
|
|
200
|
+
.where(and(eq(roles.workspaceId, workspaceId), inArray(roles.id, [...ids])))
|
|
201
|
+
const existing = new Set(found.map((row) => row.id))
|
|
202
|
+
|
|
203
|
+
missing = ids.filter((id) => !existing.has(id))
|
|
204
|
+
} else if (isRecordTargetType(kind)) {
|
|
205
|
+
missing = await missingTargets(dependencies.db, workspaceId, kind as RecordTargetType, ids)
|
|
206
|
+
} else {
|
|
207
|
+
missing = ids
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
if (missing.length === 0) {
|
|
211
|
+
continue
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
const missingSet = new Set(missing)
|
|
215
|
+
|
|
216
|
+
rows.forEach((row, index) => {
|
|
217
|
+
if (row.targetType === kind && missingSet.has(row.targetId)) {
|
|
218
|
+
problems.push({
|
|
219
|
+
field: `associations.${String(index)}`,
|
|
220
|
+
message: `No ${kind} ${row.targetId} here`,
|
|
221
|
+
})
|
|
222
|
+
}
|
|
223
|
+
})
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
if (problems.length > 0) {
|
|
227
|
+
throw new AppError('not_found', 'Association target not found', problems)
|
|
228
|
+
}
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
async function toViews(
|
|
232
|
+
workspaceId: string,
|
|
233
|
+
records: readonly EventRecord[],
|
|
234
|
+
): Promise<EventView[]> {
|
|
235
|
+
return Promise.all(
|
|
236
|
+
records.map(async (record) =>
|
|
237
|
+
toView(record, await repository.listAssociations(dependencies.db, workspaceId, record.id)),
|
|
238
|
+
),
|
|
239
|
+
)
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
return {
|
|
243
|
+
async list(actor, filters, query) {
|
|
244
|
+
const workspaceId = requireWorkspaceId(actor)
|
|
245
|
+
const window = readListWindow(query, EVENT_SORTS, DEFAULT_EVENT_SORT)
|
|
246
|
+
const rows = await repository.listEvents(dependencies.db, workspaceId, filters, window)
|
|
247
|
+
const page = toPage(rows, window, (event) => event.id)
|
|
248
|
+
|
|
249
|
+
return { items: await toViews(workspaceId, page.items), nextCursor: page.nextCursor }
|
|
250
|
+
},
|
|
251
|
+
|
|
252
|
+
async get(actor, id) {
|
|
253
|
+
const workspaceId = requireWorkspaceId(actor)
|
|
254
|
+
const event = await require(workspaceId, id)
|
|
255
|
+
|
|
256
|
+
return toView(event, await repository.listAssociations(dependencies.db, workspaceId, id))
|
|
257
|
+
},
|
|
258
|
+
|
|
259
|
+
async create(actor, input) {
|
|
260
|
+
const workspaceId = requireWorkspaceId(actor)
|
|
261
|
+
|
|
262
|
+
requireRange(input.startsAt, input.endsAt)
|
|
263
|
+
|
|
264
|
+
const ownerId = input.ownerId === undefined ? actorMemberId(actor) : input.ownerId
|
|
265
|
+
|
|
266
|
+
if (ownerId !== null) {
|
|
267
|
+
await requireOwner(workspaceId, ownerId)
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
const associations = uniqueAssociations(input.associations)
|
|
271
|
+
|
|
272
|
+
await requireAssociations(workspaceId, associations)
|
|
273
|
+
|
|
274
|
+
const timezone = input.timezone ?? (await defaultTimezone(workspaceId))
|
|
275
|
+
const id = dependencies.createId('crmEvent')
|
|
276
|
+
|
|
277
|
+
return dependencies.transaction(
|
|
278
|
+
async ({ tx, events }) => {
|
|
279
|
+
const customFields = await dependencies.customFields.forCreate(
|
|
280
|
+
tx,
|
|
281
|
+
workspaceId,
|
|
282
|
+
'event',
|
|
283
|
+
input.customFields,
|
|
284
|
+
)
|
|
285
|
+
const created = await repository.insertEvent(tx, {
|
|
286
|
+
id,
|
|
287
|
+
workspaceId,
|
|
288
|
+
name: input.name,
|
|
289
|
+
kind: input.kind,
|
|
290
|
+
startsAt: input.startsAt,
|
|
291
|
+
endsAt: input.endsAt,
|
|
292
|
+
timezone,
|
|
293
|
+
location: input.location,
|
|
294
|
+
meetingUrl: input.meetingUrl,
|
|
295
|
+
format: input.format,
|
|
296
|
+
details: input.details,
|
|
297
|
+
status: input.status,
|
|
298
|
+
ownerId,
|
|
299
|
+
summary: input.summary,
|
|
300
|
+
tags: [...input.tags],
|
|
301
|
+
customFields,
|
|
302
|
+
})
|
|
303
|
+
|
|
304
|
+
await repository.replaceAssociations(
|
|
305
|
+
tx,
|
|
306
|
+
dependencies.createId,
|
|
307
|
+
workspaceId,
|
|
308
|
+
id,
|
|
309
|
+
associations,
|
|
310
|
+
)
|
|
311
|
+
|
|
312
|
+
await dependencies.recordActivity(tx, workspaceId, actor, {
|
|
313
|
+
targetType: 'event',
|
|
314
|
+
targetId: id,
|
|
315
|
+
kind: 'created',
|
|
316
|
+
...describeCreation('Event'),
|
|
317
|
+
})
|
|
318
|
+
|
|
319
|
+
events.emit('events.event.created', { type: 'event', id }, {})
|
|
320
|
+
|
|
321
|
+
return toView(created, associations)
|
|
322
|
+
},
|
|
323
|
+
{ workspaceId, actor: toEventActor(actor) },
|
|
324
|
+
)
|
|
325
|
+
},
|
|
326
|
+
|
|
327
|
+
async update(actor, id, changes) {
|
|
328
|
+
const workspaceId = requireWorkspaceId(actor)
|
|
329
|
+
const existing = await require(workspaceId, id)
|
|
330
|
+
|
|
331
|
+
const startsAt = changes.startsAt ?? existing.startsAt
|
|
332
|
+
const endsAt = changes.endsAt ?? existing.endsAt
|
|
333
|
+
|
|
334
|
+
requireRange(startsAt, endsAt)
|
|
335
|
+
|
|
336
|
+
if (typeof changes.ownerId === 'string' && changes.ownerId !== existing.ownerId) {
|
|
337
|
+
await requireOwner(workspaceId, changes.ownerId)
|
|
338
|
+
}
|
|
339
|
+
|
|
340
|
+
const current = await repository.listAssociations(dependencies.db, workspaceId, id)
|
|
341
|
+
const next =
|
|
342
|
+
changes.associations === undefined ? undefined : uniqueAssociations(changes.associations)
|
|
343
|
+
|
|
344
|
+
if (next !== undefined) {
|
|
345
|
+
await requireAssociations(workspaceId, next)
|
|
346
|
+
}
|
|
347
|
+
|
|
348
|
+
const columns = toStoredColumns(changes)
|
|
349
|
+
const scalarChanged = changedKeys(existing, columns)
|
|
350
|
+
const linksChanged =
|
|
351
|
+
next !== undefined && JSON.stringify(current) !== JSON.stringify(next)
|
|
352
|
+
|
|
353
|
+
return dependencies.transaction(
|
|
354
|
+
async ({ tx, events }) => {
|
|
355
|
+
const cf = await dependencies.customFields.forUpdate(
|
|
356
|
+
tx,
|
|
357
|
+
workspaceId,
|
|
358
|
+
'event',
|
|
359
|
+
existing.customFields,
|
|
360
|
+
changes.customFields,
|
|
361
|
+
)
|
|
362
|
+
const customFieldsChanged = cf !== undefined && cf.changedPaths.length > 0
|
|
363
|
+
|
|
364
|
+
if (scalarChanged.length === 0 && !linksChanged && !customFieldsChanged) {
|
|
365
|
+
return toView(existing, current)
|
|
366
|
+
}
|
|
367
|
+
|
|
368
|
+
const updated = await repository.updateEvent(tx, workspaceId, id, {
|
|
369
|
+
...columns,
|
|
370
|
+
...(customFieldsChanged ? { customFields: cf.merged } : {}),
|
|
371
|
+
updatedAt: dependencies.now(),
|
|
372
|
+
})
|
|
373
|
+
|
|
374
|
+
if (updated === undefined) {
|
|
375
|
+
throw AppError.notFound('Event not found')
|
|
376
|
+
}
|
|
377
|
+
|
|
378
|
+
if (next !== undefined) {
|
|
379
|
+
await repository.replaceAssociations(
|
|
380
|
+
tx,
|
|
381
|
+
dependencies.createId,
|
|
382
|
+
workspaceId,
|
|
383
|
+
id,
|
|
384
|
+
next,
|
|
385
|
+
)
|
|
386
|
+
}
|
|
387
|
+
|
|
388
|
+
if (scalarChanged.length > 0 || customFieldsChanged) {
|
|
389
|
+
const labels: Record<string, string> = { ...EVENT_FIELD_LABELS, ...cf?.labels }
|
|
390
|
+
const before: Record<string, unknown> = { ...existing, ...cf?.flatBefore }
|
|
391
|
+
const after: Record<string, unknown> = { ...columns, ...cf?.flatAfter }
|
|
392
|
+
const activityChanged = [
|
|
393
|
+
...scalarChanged,
|
|
394
|
+
...(cf?.changedPaths ?? []),
|
|
395
|
+
]
|
|
396
|
+
|
|
397
|
+
await dependencies.recordActivity(tx, workspaceId, actor, {
|
|
398
|
+
targetType: 'event',
|
|
399
|
+
targetId: id,
|
|
400
|
+
kind: 'updated',
|
|
401
|
+
...describeUpdate(activityChanged, labels, before, after),
|
|
402
|
+
})
|
|
403
|
+
}
|
|
404
|
+
|
|
405
|
+
const changed = [
|
|
406
|
+
...scalarChanged,
|
|
407
|
+
...(linksChanged ? ['associations'] : []),
|
|
408
|
+
...(customFieldsChanged ? cf.changedPaths : []),
|
|
409
|
+
]
|
|
410
|
+
|
|
411
|
+
events.emit('events.event.updated', { type: 'event', id }, { changed })
|
|
412
|
+
|
|
413
|
+
return toView(updated, next ?? current)
|
|
414
|
+
},
|
|
415
|
+
{ workspaceId, actor: toEventActor(actor) },
|
|
416
|
+
)
|
|
417
|
+
},
|
|
418
|
+
|
|
419
|
+
async remove(actor, id) {
|
|
420
|
+
const workspaceId = requireWorkspaceId(actor)
|
|
421
|
+
|
|
422
|
+
await require(workspaceId, id)
|
|
423
|
+
|
|
424
|
+
await dependencies.transaction(
|
|
425
|
+
async ({ tx, events }) => {
|
|
426
|
+
await deleteRecordsAttachedToAttendancesOf(tx, workspaceId, { eventId: id })
|
|
427
|
+
await deleteRecordsAttachedTo(tx, workspaceId, 'event', id)
|
|
428
|
+
await repository.deleteEvent(tx, workspaceId, id)
|
|
429
|
+
events.emit('events.event.deleted', { type: 'event', id }, {})
|
|
430
|
+
},
|
|
431
|
+
{ workspaceId, actor: toEventActor(actor) },
|
|
432
|
+
)
|
|
433
|
+
},
|
|
434
|
+
|
|
435
|
+
async listAssociationsFor(actor, targetType, targetId) {
|
|
436
|
+
const workspaceId = requireWorkspaceId(actor)
|
|
437
|
+
|
|
438
|
+
await requireAssociations(workspaceId, [{ targetType, targetId }])
|
|
439
|
+
|
|
440
|
+
return repository.listAssociationsByTarget(dependencies.db, workspaceId, targetType, targetId)
|
|
441
|
+
},
|
|
442
|
+
}
|
|
443
|
+
}
|
|
@@ -0,0 +1,118 @@
|
|
|
1
|
+
import { z } from 'zod'
|
|
2
|
+
|
|
3
|
+
import type { McpToolRegistry } from '../../runtime/module.ts'
|
|
4
|
+
import {
|
|
5
|
+
enumSetArg,
|
|
6
|
+
idArg,
|
|
7
|
+
idSetArg,
|
|
8
|
+
listWindowShape,
|
|
9
|
+
registerCrudTools,
|
|
10
|
+
termArg,
|
|
11
|
+
toSet,
|
|
12
|
+
} from '../crudTools.ts'
|
|
13
|
+
import { ATTENDANCE_STATUSES, EVENT_ASSOCIATION_TARGET_TYPES, EVENT_FORMATS, EVENT_STATUSES } from './schema.ts'
|
|
14
|
+
import type { AttendancesService } from './attendances.ts'
|
|
15
|
+
import type { EventsService } from './service.ts'
|
|
16
|
+
import {
|
|
17
|
+
attendanceResponse,
|
|
18
|
+
createAttendanceBody,
|
|
19
|
+
createEventBody,
|
|
20
|
+
eventResponse,
|
|
21
|
+
toCreateAttendanceInput,
|
|
22
|
+
toCreateEventInput,
|
|
23
|
+
toUpdateAttendanceInput,
|
|
24
|
+
toUpdateEventInput,
|
|
25
|
+
updateAttendanceBody,
|
|
26
|
+
updateEventBody,
|
|
27
|
+
} from './routes.ts'
|
|
28
|
+
|
|
29
|
+
const eventListArgs = z.strictObject({
|
|
30
|
+
...listWindowShape,
|
|
31
|
+
q: termArg,
|
|
32
|
+
status: enumSetArg(EVENT_STATUSES).optional().describe('draft, scheduled, cancelled.'),
|
|
33
|
+
format: enumSetArg(EVENT_FORMATS).optional().describe('in_person, virtual, hybrid.'),
|
|
34
|
+
owner_id: idSetArg.optional().describe('Events owned by these members.'),
|
|
35
|
+
from: z.iso.datetime().optional().describe('Only events whose start is on or after this instant.'),
|
|
36
|
+
to: z.iso.datetime().optional().describe('Only events whose start is on or before this instant.'),
|
|
37
|
+
})
|
|
38
|
+
|
|
39
|
+
const attendanceListArgs = z.strictObject({
|
|
40
|
+
...listWindowShape,
|
|
41
|
+
event_id: idSetArg.optional().describe('Attendances on these events.'),
|
|
42
|
+
person_id: idSetArg.optional().describe('Attendances for these people.'),
|
|
43
|
+
status: enumSetArg(ATTENDANCE_STATUSES).optional(),
|
|
44
|
+
})
|
|
45
|
+
|
|
46
|
+
export function registerEventsTools(
|
|
47
|
+
mcp: McpToolRegistry,
|
|
48
|
+
services: { readonly events: EventsService; readonly attendances: AttendancesService },
|
|
49
|
+
): void {
|
|
50
|
+
registerCrudTools(mcp, {
|
|
51
|
+
resource: 'events',
|
|
52
|
+
subject: 'event',
|
|
53
|
+
about:
|
|
54
|
+
'A dated gathering (webinar, meetup, dinner). Not a pipeline. People attach as attendances.',
|
|
55
|
+
service: services.events,
|
|
56
|
+
render: eventResponse,
|
|
57
|
+
listArgs: eventListArgs,
|
|
58
|
+
toFilters: (args) => ({
|
|
59
|
+
term: args.q,
|
|
60
|
+
statuses: toSet(args.status),
|
|
61
|
+
formats: toSet(args.format),
|
|
62
|
+
ownerIds: toSet(args.owner_id),
|
|
63
|
+
from: args.from === undefined ? undefined : new Date(args.from),
|
|
64
|
+
to: args.to === undefined ? undefined : new Date(args.to),
|
|
65
|
+
}),
|
|
66
|
+
createArgs: createEventBody,
|
|
67
|
+
toCreateInput: toCreateEventInput,
|
|
68
|
+
updateArgs: updateEventBody.extend({ id: idArg }),
|
|
69
|
+
toUpdateInput: toUpdateEventInput,
|
|
70
|
+
})
|
|
71
|
+
|
|
72
|
+
registerCrudTools(mcp, {
|
|
73
|
+
resource: 'attendances',
|
|
74
|
+
subject: 'attendance',
|
|
75
|
+
about:
|
|
76
|
+
"One person's registration for one event: registered, attended, no_show, or cancelled.",
|
|
77
|
+
service: services.attendances,
|
|
78
|
+
render: attendanceResponse,
|
|
79
|
+
listArgs: attendanceListArgs,
|
|
80
|
+
toFilters: (args) => ({
|
|
81
|
+
eventIds: toSet(args.event_id),
|
|
82
|
+
personIds: toSet(args.person_id),
|
|
83
|
+
statuses: toSet(args.status),
|
|
84
|
+
}),
|
|
85
|
+
createArgs: createAttendanceBody,
|
|
86
|
+
toCreateInput: toCreateAttendanceInput,
|
|
87
|
+
updateArgs: updateAttendanceBody.extend({ id: idArg }),
|
|
88
|
+
toUpdateInput: toUpdateAttendanceInput,
|
|
89
|
+
})
|
|
90
|
+
|
|
91
|
+
mcp.tool({
|
|
92
|
+
name: 'event_associations_list',
|
|
93
|
+
description:
|
|
94
|
+
'Events associated with another record (a Deal, Company, Role, …). ' +
|
|
95
|
+
'Mirrors GET /v1/event-associations.',
|
|
96
|
+
inputSchema: z.strictObject({
|
|
97
|
+
target_type: z.enum(EVENT_ASSOCIATION_TARGET_TYPES),
|
|
98
|
+
target_id: idArg,
|
|
99
|
+
}),
|
|
100
|
+
invoke: async (args, actor) => {
|
|
101
|
+
const rows = await services.events.listAssociationsFor(
|
|
102
|
+
actor,
|
|
103
|
+
args.target_type,
|
|
104
|
+
args.target_id,
|
|
105
|
+
)
|
|
106
|
+
|
|
107
|
+
return {
|
|
108
|
+
data: rows.map((row) => ({
|
|
109
|
+
event_id: row.eventId,
|
|
110
|
+
event_name: row.eventName,
|
|
111
|
+
target_type: args.target_type,
|
|
112
|
+
target_id: args.target_id,
|
|
113
|
+
})),
|
|
114
|
+
next_cursor: null,
|
|
115
|
+
}
|
|
116
|
+
},
|
|
117
|
+
})
|
|
118
|
+
}
|
|
@@ -25,6 +25,7 @@ import {
|
|
|
25
25
|
valuesForObject,
|
|
26
26
|
} from './applyMappedFields.ts'
|
|
27
27
|
import type { MappedAnswers } from './mapping.ts'
|
|
28
|
+
import { fillCompanyAddresses, fillPersonAddresses } from './fillAddresses.ts'
|
|
28
29
|
|
|
29
30
|
const customFieldValues = createCustomFieldValues({ db: null as never })
|
|
30
31
|
|
|
@@ -32,7 +33,6 @@ const PERSON_FIELD_COLUMNS: Readonly<Record<string, keyof PersonRecord>> = {
|
|
|
32
33
|
salutation: 'salutation',
|
|
33
34
|
suffix: 'suffix',
|
|
34
35
|
timezone: 'timezone',
|
|
35
|
-
location: 'location',
|
|
36
36
|
preferred_channel: 'preferredChannel',
|
|
37
37
|
influence: 'influence',
|
|
38
38
|
relationship: 'relationship',
|
|
@@ -46,8 +46,6 @@ const COMPANY_FIELD_COLUMNS: Readonly<Record<string, keyof CompanyRecord>> = {
|
|
|
46
46
|
description: 'description',
|
|
47
47
|
stage: 'stage',
|
|
48
48
|
size_band: 'sizeBand',
|
|
49
|
-
hq: 'hq',
|
|
50
|
-
website: 'website',
|
|
51
49
|
account_type: 'accountType',
|
|
52
50
|
icp_fit: 'icpFit',
|
|
53
51
|
tech_stack: 'techStack',
|
|
@@ -170,6 +168,7 @@ export async function applyPersonMappedFields(
|
|
|
170
168
|
}
|
|
171
169
|
|
|
172
170
|
const standardPatch = applyStandardFillBlank(person, 'person', values, columnLookup(PERSON_FIELD_COLUMNS))
|
|
171
|
+
const addresses = fillPersonAddresses(person.addresses, values.standard)
|
|
173
172
|
const customFields = await buildCustomFieldsPatch(
|
|
174
173
|
tx,
|
|
175
174
|
workspaceId,
|
|
@@ -179,12 +178,13 @@ export async function applyPersonMappedFields(
|
|
|
179
178
|
definitions,
|
|
180
179
|
)
|
|
181
180
|
|
|
182
|
-
if (Object.keys(standardPatch).length === 0 && customFields === undefined) {
|
|
181
|
+
if (Object.keys(standardPatch).length === 0 && addresses === undefined && customFields === undefined) {
|
|
183
182
|
return person
|
|
184
183
|
}
|
|
185
184
|
|
|
186
185
|
const updated = await peopleRepository.updatePerson(tx, workspaceId, person.id, {
|
|
187
186
|
...standardPatch,
|
|
187
|
+
...(addresses === undefined ? {} : { addresses }),
|
|
188
188
|
...(customFields === undefined ? {} : { customFields }),
|
|
189
189
|
updatedAt: now,
|
|
190
190
|
})
|
|
@@ -212,6 +212,7 @@ export async function applyCompanyMappedFields(
|
|
|
212
212
|
values,
|
|
213
213
|
columnLookup(COMPANY_FIELD_COLUMNS),
|
|
214
214
|
)
|
|
215
|
+
const addresses = fillCompanyAddresses(company.addresses, values.standard)
|
|
215
216
|
const customFields = await buildCustomFieldsPatch(
|
|
216
217
|
tx,
|
|
217
218
|
workspaceId,
|
|
@@ -221,12 +222,13 @@ export async function applyCompanyMappedFields(
|
|
|
221
222
|
definitions,
|
|
222
223
|
)
|
|
223
224
|
|
|
224
|
-
if (Object.keys(standardPatch).length === 0 && customFields === undefined) {
|
|
225
|
+
if (Object.keys(standardPatch).length === 0 && addresses === undefined && customFields === undefined) {
|
|
225
226
|
return company
|
|
226
227
|
}
|
|
227
228
|
|
|
228
229
|
const updated = await companyRepository.updateCompany(tx, workspaceId, company.id, {
|
|
229
230
|
...standardPatch,
|
|
231
|
+
...(addresses === undefined ? {} : { addresses }),
|
|
230
232
|
...(customFields === undefined ? {} : { customFields }),
|
|
231
233
|
updatedAt: now,
|
|
232
234
|
})
|