toga-ai 1.0.172 → 1.0.174
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/knowledge/1.0/apps/test/features/toga2-client-onboarding-sql.md +39 -17
- package/knowledge/2.0/apps/api2/INDEX.md +1 -0
- package/knowledge/2.0/apps/api2/features/tickets-api.md +72 -0
- package/knowledge/2.0/apps/dbchanges2/INDEX.md +1 -0
- package/knowledge/2.0/apps/dbchanges2/architecture.md +29 -2
- package/knowledge/2.0/apps/dbchanges2/workflows/client-onboarding.md +97 -0
- package/knowledge/2.0/apps/worker2/INDEX.md +1 -0
- package/knowledge/2.0/apps/worker2/features/clickup-connectivity-watchdog.md +81 -0
- package/knowledge/2.0/apps/worker2/features/clickup-project-routing.md +66 -22
- package/knowledge/INDEX.md +4 -3
- package/knowledge/clients/fordham/INDEX.md +5 -0
- package/knowledge/clients/fordham/profile.md +49 -0
- package/package.json +1 -1
|
@@ -6,48 +6,70 @@ project: Test
|
|
|
6
6
|
client: shared
|
|
7
7
|
type: feature
|
|
8
8
|
status: active
|
|
9
|
-
updated: 2026-06-
|
|
10
|
-
owners: [jcardinal]
|
|
9
|
+
updated: 2026-06-23
|
|
10
|
+
owners: [jcardinal, mhammontree]
|
|
11
11
|
files:
|
|
12
12
|
- test/team/generate_toga2_onboarding_sql.php
|
|
13
13
|
related:
|
|
14
14
|
- ../architecture.md
|
|
15
15
|
- ./toga2-user-cross-client-access-sql.md
|
|
16
|
+
- ../../../2.0/apps/dbchanges2/workflows/client-onboarding.md
|
|
16
17
|
---
|
|
17
18
|
|
|
18
19
|
## Summary
|
|
19
20
|
|
|
20
21
|
`team/generate_toga2_onboarding_sql.php` generates the SQL needed to **onboard a new client**
|
|
21
22
|
onto the 2.0 platform. Runs on the **1.0 `App_` framework**
|
|
22
|
-
(`App_Framework_Sandbox::initialize()`), but the SQL it produces targets the **2.0**
|
|
23
|
-
databases.
|
|
23
|
+
(`App_Framework_Sandbox::initialize()`), but the SQL it produces targets the **2.0** Core and
|
|
24
|
+
client databases. It is the *insert-generation* half of onboarding; the schema/blank half lives
|
|
25
|
+
in dbchanges2 (see the [2.0 client onboarding workflow](../../../2.0/apps/dbchanges2/workflows/client-onboarding.md)).
|
|
24
26
|
|
|
25
27
|
## How it works
|
|
26
28
|
|
|
27
|
-
Configure the `const` block, then run; the script emits the onboarding SQL to stdout
|
|
29
|
+
Configure the `const` block, then run; the script emits the onboarding SQL to stdout (`<pre>`).
|
|
28
30
|
|
|
29
31
|
Key configuration:
|
|
30
|
-
- `CLIENT_NAME` — formal client name, proper case (e.g. `'
|
|
31
|
-
- `CLIENT_IDENTIFIER` — PascalCase, no spaces (e.g. `'
|
|
32
|
+
- `CLIENT_NAME` — formal client name, proper case (e.g. `'Fordham University'`).
|
|
33
|
+
- `CLIENT_IDENTIFIER` — PascalCase, no spaces (e.g. `'Fordham'`) → drives `Client_<Id>` etc.
|
|
32
34
|
- `CLIENT_SUBDOMAIN_IDENTIFIER` — lowercase, no spaces; the email domain without TLD.
|
|
33
35
|
- `CLIENT_EMAIL_DOMAINS` — array of permitted email domains **with** TLD.
|
|
34
|
-
- `SHOULD_CREATE_AGILANT_API` — when true,
|
|
36
|
+
- `SHOULD_CREATE_AGILANT_API` — when true, creates the internal **Agilant** API key + api-roles.
|
|
37
|
+
- `CREATE_CLIENT_API_NAME` — string (e.g. `'Fordham'`) to also create the **client** API key, or `null` to skip.
|
|
38
|
+
- `RELATIVE_PATH_TO_DBCHANGES2`, `SHOULD_CONSOLIDATE` — declared but **not yet implemented** (no
|
|
39
|
+
consolidation logic in this script; the blank/consolidation lives in dbchanges2).
|
|
35
40
|
|
|
36
|
-
|
|
37
|
-
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
41
|
+
What it emits (UUIDs via `App_Misc::generateUUID()`; it does not touch the DB):
|
|
42
|
+
- **Core inserts** → run on the **core cluster**: `Core.Databases` (Client_/Logs_/Archive_),
|
|
43
|
+
`Core.Clients`, `Core.Domains` (every app × env), `Core.ClientEmailDomains`, `Core.DatabaseHosts`
|
|
44
|
+
(prod null-region + 3 regions).
|
|
45
|
+
- **Client inserts** → run on the **client cluster** (`Client_<Id>`): `Apis` + `Apis_Roles`.
|
|
46
|
+
Note: the `Apis` rows live in the **client** DB, not Core.
|
|
47
|
+
|
|
48
|
+
## Output split (core vs client clusters)
|
|
49
|
+
|
|
50
|
+
The script emits one combined blob; for deployment, split it: Core-table inserts to the core
|
|
51
|
+
cluster, the `Apis`/`Apis_Roles` inserts to the client cluster (against `Client_<Id>`, which must
|
|
52
|
+
already have the `Base`/`API` roles from the blank seed so the `Apis_Roles` lookup resolves).
|
|
42
53
|
|
|
43
54
|
## Gotchas
|
|
44
55
|
|
|
56
|
+
- **`Apis_Roles` needs a unique uuid per row.** The role-link insert is
|
|
57
|
+
`INSERT … SELECT <uuid>, …, r.id FROM Roles WHERE r.name IN ('Base','API')` — that SELECT returns
|
|
58
|
+
**2 rows**, so a hardcoded literal uuid makes both rows share it → `Duplicate entry … for key
|
|
59
|
+
'Apis_Roles.uuid'`. Use MySQL **`UUID()`** in the SELECT so each row gets its own. (Fixed
|
|
60
|
+
2026-06-23; previously bit every client where both roles exist.)
|
|
61
|
+
- **Secrets in output.** The generated `Apis` rows contain real `secret` values. **Never commit**
|
|
62
|
+
the client-insert file (or any artifact containing it) to a repo — run it in prod and distribute
|
|
63
|
+
the secret out-of-band. Core inserts are secret-free and safe to commit.
|
|
45
64
|
- The cluster/endpoint map is environment infrastructure encoded as constants — if RDS endpoints
|
|
46
|
-
change,
|
|
47
|
-
- Identifiers have strict casing rules (formal vs PascalCase vs lowercase-subdomain)
|
|
48
|
-
them wrong produces inconsistent DB/identity records.
|
|
65
|
+
change, update the script.
|
|
66
|
+
- Identifiers have strict casing rules (formal vs PascalCase vs lowercase-subdomain).
|
|
49
67
|
- Output is SQL to run manually — review before executing against production.
|
|
50
68
|
|
|
51
69
|
## Change history
|
|
52
70
|
|
|
71
|
+
- 2026-06-23 — Fixed `Apis_Roles` duplicate-uuid bug (use `UUID()` per row); fixed PHP 8 fatal from
|
|
72
|
+
an unquoted `CREATE_CLIENT_API_NAME` constant; implemented the previously-unused client-API block
|
|
73
|
+
(`CREATE_CLIENT_API_NAME`) so it emits both the Agilant and client keys; documented core/client
|
|
74
|
+
cluster split and the secret-handling rule. (mhammontree)
|
|
53
75
|
- 2026-06-16 — Initial capture.
|
|
@@ -4,5 +4,6 @@
|
|
|
4
4
|
|-----|---------|-------|
|
|
5
5
|
| [API (api2 / TOGa API v2) Architecture](architecture.md) | `api2` is the backend powering the public **TOGa 2.0 API**. | api2/Controller/Index.php, api2/Component/Api/V2/V2.php, api2/Component/Api/Cxml/Cxml.php, api2/Component/Api/V2/Response/Response.php, api2/Config/ |
|
|
6
6
|
| [POST + JSON-body args for scripted APIs](features/scripted-api-post-body-args.md) | The V2 engine can run a Record Script (scripted API) for a **POST** request, and a scripted API can receive its arguments from the **JSON request body** instead | api2/Component/Api/V2/V2.php |
|
|
7
|
+
| [Tickets API (/v2/tickets)](features/tickets-api.md) | The generic ticket endpoint of the 2.0 REST API. | Component/Api/V2/V2.php |
|
|
7
8
|
| [AWS CodePipeline Deployment via CodeConnections (GitHub → Elastic Beanstalk)](workflows/codepipeline-codeconnections-deploy.md) | 2.0 apps (`api2`, `_underscore`) are deployed through **AWS CodePipeline**. | |
|
|
8
9
|
| [New Environment Configuration & Provisioning (api2)](workflows/environment-configuration-and-provisioning.md) | What it takes for a 2.0 API environment (e.g. | api2/Config/<environment>.ini, api2/Controller/Index.php, dbchanges2/Core/2026-06-16a - DatabaseHosts for new QA QC stage demo environments.sql, _underscore/Route.php |
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: Tickets API (/v2/tickets)
|
|
3
|
+
framework: "2.0"
|
|
4
|
+
repo: api2
|
|
5
|
+
project: API
|
|
6
|
+
client: shared
|
|
7
|
+
type: feature
|
|
8
|
+
status: active
|
|
9
|
+
updated: 2026-06-23
|
|
10
|
+
owners: ["mhammontree"]
|
|
11
|
+
files:
|
|
12
|
+
- Component/Api/V2/V2.php
|
|
13
|
+
related:
|
|
14
|
+
- ../architecture.md
|
|
15
|
+
- ../../dbchanges2/workflows/client-onboarding.md
|
|
16
|
+
---
|
|
17
|
+
|
|
18
|
+
## Summary
|
|
19
|
+
|
|
20
|
+
The generic ticket endpoint of the 2.0 REST API. Tickets are a metadata-driven **Record**
|
|
21
|
+
(`tickets` → `_Model_Client_Ticket`, `aclDatabase = CLIENT`), so they're created/updated/read like
|
|
22
|
+
any record: `POST/PUT/GET /v2/tickets` (and `/v2/tickets/{uuid}`). PC Matic instead uses
|
|
23
|
+
`/v2/entitlement-tickets` (entitlement-wrapped); plain clients (e.g. Fordham) use `/v2/tickets`
|
|
24
|
+
with ticket fields at the top level.
|
|
25
|
+
|
|
26
|
+
## Auth & request conventions
|
|
27
|
+
|
|
28
|
+
- **Base URL** `https://api.togahub.com/v2` (sandbox `https://api.client.sandbox.togahub.com/v2`).
|
|
29
|
+
- **Auth**: `POST /v2/auth/api {client, api, secret}` → `data.tokens.access` (Bearer, ~1h) +
|
|
30
|
+
`data.tokens.refresh` (~30d). `client` = `Core.Clients.uuid`, `api`/`secret` = the client's
|
|
31
|
+
`Client_<Id>.Apis` row. Refresh via `POST /v2/auth/refresh` with the refresh token as Bearer.
|
|
32
|
+
- Every request needs a **unique `transactionId`** query param (reuse → `EV-5`).
|
|
33
|
+
- Response envelope: `{transactionId, isSuccess, status, error, messages[], meta{}, data{}}`. A
|
|
34
|
+
created/read ticket comes back under **`data.tickets`** (not bare `data`).
|
|
35
|
+
|
|
36
|
+
## How it works (write model)
|
|
37
|
+
|
|
38
|
+
- **Related objects are nested and resolved by a natural identifier**, not numeric IDs — and **each
|
|
39
|
+
field has its own identifier set**:
|
|
40
|
+
- `urgency` → `name` / `weight` / `uuid` (**NOT `code`**)
|
|
41
|
+
- `ticketStage` → `code`
|
|
42
|
+
- `ticketType`, `ticketTopic` → `name`
|
|
43
|
+
Sending an identifier a field doesn't recognize returns **`EV-12`** (the error lists the valid
|
|
44
|
+
identifier fields). These are **MATCH** (must-exist) references — the value must already exist in
|
|
45
|
+
the client's reference data.
|
|
46
|
+
- **Inline-created** objects (safe to send without pre-existing data): `address` (CREATE),
|
|
47
|
+
`contact`/`contactPhoneNumber`/`location`/`reportedByContact` (MATCH_UPSERT).
|
|
48
|
+
- **Child arrays** created inline: `ticketNotes[]`, `ticketFiles[]`, `ticketUnits[]`, `ticketItems[]`,
|
|
49
|
+
`ticketUsers[]`, `ticketServiceQuestionAnswers[]`.
|
|
50
|
+
- Add a note to an existing ticket via the nested route `POST /v2/tickets/{uuid}/ticket-notes`.
|
|
51
|
+
- `number` is auto-generated (`TK`-prefixed, starts `TK100000`).
|
|
52
|
+
|
|
53
|
+
A minimal create needs only `shortDescription`/`longDescription` (+ optional `impact`, `ticketNotes`);
|
|
54
|
+
the FK references are all nullable.
|
|
55
|
+
|
|
56
|
+
## GET / filtering
|
|
57
|
+
|
|
58
|
+
`GET /v2/tickets` supports `fields`, `where`, `join`/`ojoin`, `sort`, `group`, pagination
|
|
59
|
+
(`page`/`recordsPerPage`), `depth`. To fetch tickets for a contact on the generic flow, join the
|
|
60
|
+
ticket's own contact: `join=Contacts@Contacts:Contacts.id=Tickets.contactId&where=(Contacts.uuid:eq:<uuid>)`.
|
|
61
|
+
(PC Matic instead joins through Entitlements — only valid for its entitlement flow.)
|
|
62
|
+
|
|
63
|
+
## Gotchas
|
|
64
|
+
|
|
65
|
+
- **`urgency` matches by `name`, not `code`** — the most common `EV-12` cause.
|
|
66
|
+
- **Reference data must exist** — a newly onboarded client has no Urgencies/TicketTypes/TicketStages/
|
|
67
|
+
TicketTopics until seeded, so those fields can't be referenced yet (see the onboarding workflow).
|
|
68
|
+
- **Response is under `data.tickets`** — not bare `data`.
|
|
69
|
+
|
|
70
|
+
## Change history
|
|
71
|
+
|
|
72
|
+
- 2026-06-23 — Initial capture from the Fordham ticket integration + customer docs (TRUE-79699). (mhammontree)
|
|
@@ -3,3 +3,4 @@
|
|
|
3
3
|
| Doc | Summary | Files |
|
|
4
4
|
|-----|---------|-------|
|
|
5
5
|
| [Database Changes (dbchanges2) Repository Architecture](architecture.md) | `dbchanges2` is the **schema-migration / SQL change-set repository** for the entire 2.0 platform. | Core/, Client/, Client_<Tenant>/, Logs/, Logs_Client/, _modules/ |
|
|
6
|
+
| [2.0 New-Client Onboarding (manual process)](workflows/client-onboarding.md) | How to manually stand up a new 2.0 client (tenant). | Client/, Client_<Tenant>/, Core/, Logs_Client/ |
|
|
@@ -6,7 +6,7 @@ project: Database Changes
|
|
|
6
6
|
client: shared
|
|
7
7
|
type: architecture
|
|
8
8
|
status: active
|
|
9
|
-
updated: 2026-06-
|
|
9
|
+
updated: 2026-06-23
|
|
10
10
|
owners: [jcardinal, mhammontree]
|
|
11
11
|
files:
|
|
12
12
|
- Core/
|
|
@@ -36,7 +36,9 @@ is the intended execution order.
|
|
|
36
36
|
**Critical rules:** every new `.sql` file **must** be named `YYYY-MM-DD<letter> - <Description>.sql`
|
|
37
37
|
with a **mandatory lower-case letter** right after the date (`a` for the first file of that day
|
|
38
38
|
in a folder, then `b`, `c`, …). One folder per database; place client changes under the matching
|
|
39
|
-
`Client_<Name>` folder. Never edit an already-run migration — add a new dated file instead.
|
|
39
|
+
`Client_<Name>` folder. Never edit an already-run migration — add a new dated file instead. The
|
|
40
|
+
`Client/` **blank must carry the baseline DATA seed** (roles, full ACL, reference/lookup tables,
|
|
41
|
+
UI config) — **not just schema**; a schema-only blank produces non-functional clients.
|
|
40
42
|
|
|
41
43
|
## File naming convention (the execution contract)
|
|
42
44
|
|
|
@@ -124,6 +126,31 @@ already-applied change files moved aside to keep the live list short. Present un
|
|
|
124
126
|
typically bucketed as `FILES BEFORE <date>` (or by year under `Core/`). **Never** rely on a
|
|
125
127
|
file inside `HISTORIC` running; **never** add new changes there.
|
|
126
128
|
|
|
129
|
+
## The Client blank: baseline data + consolidation
|
|
130
|
+
|
|
131
|
+
`Client/<DATE>-BLANK_CLIENT_DATABASE.sql` (the **latest-dated** one is the current blank) is the
|
|
132
|
+
starting point for every new tenant. It is **not schema-only** — it must also carry the **baseline
|
|
133
|
+
data seed**: the standard roles (`Base`, `Developer`, `API`, `Public`), the full ACL
|
|
134
|
+
(`AclRecordPermissions`/`AclFieldPermissions`/`AclRecordExpressions`/`AclLogicGroups`/…), the
|
|
135
|
+
reference/lookup tables (countries, states, statuses, types, …), and UI config (`TableViews` + fields,
|
|
136
|
+
`SectionRecordFields`). A client built from a **schema-only** blank has no roles/ACL and **cannot
|
|
137
|
+
authorize** — this regressed once (the blank was reduced to `EmailTemplates`-only), which is why a new
|
|
138
|
+
client appeared to onboard but failed at the API.
|
|
139
|
+
|
|
140
|
+
**Consolidation lifecycle** (folding loose `Client/` changes back into a fresh blank):
|
|
141
|
+
1. The loose `Client/` files must **already be applied to every existing client DB** before they're
|
|
142
|
+
archived (otherwise the new blank diverges from live clients). This is done manually as the files
|
|
143
|
+
are created.
|
|
144
|
+
2. Rebuild the blank from the **current clean schema + the baseline data** and `mysqldump` **WITH
|
|
145
|
+
data** (never `--no-data` — that is exactly how the seed was lost). Keep secret-bearing tables
|
|
146
|
+
(`Apis`) **out** of the blank.
|
|
147
|
+
3. `git mv` the old blank + the consolidated loose files into `Client/HISTORIC/FILES BEFORE <DATE>/`,
|
|
148
|
+
leaving `Client/` with only the new dated blank.
|
|
149
|
+
|
|
150
|
+
**Applying loose files** requires `SET FOREIGN_KEY_CHECKS=0` — several `ALTER … MODIFY/DROP` a column
|
|
151
|
+
that participates in a foreign key and otherwise fail with error 1832. (The blank dump sets this in
|
|
152
|
+
its own header.)
|
|
153
|
+
|
|
127
154
|
## Adding a new change (the rules)
|
|
128
155
|
|
|
129
156
|
1. Pick the right folder for the target DB: `Core/`, `Logs/`, the shared `Client/` (every
|
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: 2.0 New-Client Onboarding (manual process)
|
|
3
|
+
framework: "2.0"
|
|
4
|
+
repo: dbchanges2
|
|
5
|
+
project: Database Changes
|
|
6
|
+
client: shared
|
|
7
|
+
type: workflow
|
|
8
|
+
status: active
|
|
9
|
+
updated: 2026-06-23
|
|
10
|
+
owners: ["mhammontree"]
|
|
11
|
+
files:
|
|
12
|
+
- Client/
|
|
13
|
+
- Client_<Tenant>/
|
|
14
|
+
- Core/
|
|
15
|
+
- Logs_Client/
|
|
16
|
+
related:
|
|
17
|
+
- ../architecture.md
|
|
18
|
+
- ../../../1.0/apps/test/features/toga2-client-onboarding-sql.md
|
|
19
|
+
- ../../api2/features/tickets-api.md
|
|
20
|
+
---
|
|
21
|
+
|
|
22
|
+
## Summary
|
|
23
|
+
|
|
24
|
+
How to manually stand up a new 2.0 client (tenant). A tenant spans three databases on three
|
|
25
|
+
clusters — `Client_<Name>` (client cluster), `Logs_<Name>` (logs cluster), `Archive_<Name>`
|
|
26
|
+
(archive cluster) — plus registration rows in **Core** and two API keys. The schema + baseline
|
|
27
|
+
data come from the dbchanges2 **blank**; the Core/client registration + API keys are generated by
|
|
28
|
+
the [onboarding SQL generator](../../../1.0/apps/test/features/toga2-client-onboarding-sql.md).
|
|
29
|
+
Goal: every `Client_<Name>` DB is architecturally identical.
|
|
30
|
+
|
|
31
|
+
## Steps
|
|
32
|
+
|
|
33
|
+
Order matters — each step assumes the prior one ran.
|
|
34
|
+
|
|
35
|
+
1. **Decide identifiers** — formal name, identifier (PascalCase → `Client_<Id>`), subdomain
|
|
36
|
+
(lowercase), email domain(s) with TLD, and any modules.
|
|
37
|
+
2. **Create the three databases** — `CREATE DATABASE Client_<Id>` (client cluster),
|
|
38
|
+
`Logs_<Id>` (logs cluster), `Archive_<Id>` (archive cluster). `Archive_<Id>` can be empty.
|
|
39
|
+
3. **Build `Client_<Id>` schema + baseline** — apply the current blank
|
|
40
|
+
(`Client/<DATE>-BLANK_CLIENT_DATABASE.sql` — latest-dated), which creates all tables **and** the
|
|
41
|
+
baseline seed (roles, full ACL, reference/lookup tables, UI config). Run with
|
|
42
|
+
`SET FOREIGN_KEY_CHECKS=0` (the loose schema files assume it; see Gotchas).
|
|
43
|
+
4. **Build `Logs_<Id>`** — apply `Logs_Client/<DATE>_BLANK_CLIENT_LOGS_DATABASE.SQL`. The API logs
|
|
44
|
+
here and checks the auth-rate limit against it, so it **must** exist for the API to work.
|
|
45
|
+
5. **Generate the inserts** — run `test/team/generate_toga2_onboarding_sql.php` (set the client
|
|
46
|
+
consts). It emits Core inserts + the two API-key inserts.
|
|
47
|
+
6. **Run Core inserts on the core cluster** — `Core.Databases`, `Core.Clients`, `Core.Domains`
|
|
48
|
+
(every app × env), `Core.ClientEmailDomains`, `Core.DatabaseHosts`. This is what makes the API
|
|
49
|
+
route to the tenant.
|
|
50
|
+
7. **Run the API-key inserts on the client cluster** (against `Client_<Id>`) — two `Apis` rows
|
|
51
|
+
(internal "Agilant" + the client key) and their `Apis_Roles` links to `Base`+`API`. Distribute
|
|
52
|
+
the secrets out-of-band (internal → our systems; client → the client). **Never commit secrets.**
|
|
53
|
+
8. **Register for shared changes** — append `Client_<Id>` to `test/team/2.0 deployment/Clients_Db.txt`
|
|
54
|
+
so future shared `Client/` changes fan out to it.
|
|
55
|
+
9. **Configure client reference/ACL data** — seed the client's ticket reference data
|
|
56
|
+
(Urgencies, TicketTypes, TicketStages, TicketTopics) and any record/field ACL beyond baseline.
|
|
57
|
+
10. **Verify** — authenticate via the API with `{client = Core.Clients.uuid, api = Apis.uuid,
|
|
58
|
+
secret = Apis.secret}`, then a smoke request (e.g. create a ticket).
|
|
59
|
+
|
|
60
|
+
## Systems involved
|
|
61
|
+
|
|
62
|
+
- **dbchanges2** — the blank (schema + baseline seed), loose `Client/` changes, `Logs_Client/`
|
|
63
|
+
blank, and the generated `Core/` + `Client_<Name>/` onboarding SQL.
|
|
64
|
+
- **test** (1.0) — `generate_toga2_onboarding_sql.php` produces the Core + API-key inserts.
|
|
65
|
+
- **Core DB** — `Clients`, `Databases`, `DatabaseHosts`, `Domains`, `ClientEmailDomains`
|
|
66
|
+
(route/identity metadata; supplies the `client` uuid used at auth).
|
|
67
|
+
- **api2 / _underscore** — the API the client calls; verify here.
|
|
68
|
+
- **Bastion** — production deploy path (upload SQL, source per cluster).
|
|
69
|
+
|
|
70
|
+
## Edge cases & escalation
|
|
71
|
+
|
|
72
|
+
- **Logs/Archive scope** — building the `Logs_`/`Archive_` *schemas* is sometimes treated as a
|
|
73
|
+
later phase, but `Logs_<Id>` is required for the API to authenticate/log. Their **Core
|
|
74
|
+
registration** rows (Databases/DatabaseHosts) are always generated regardless.
|
|
75
|
+
- **Reference data** — a fresh client has roles/ACL/lookups from the blank, but **not** ticket
|
|
76
|
+
reference data (Urgencies/TicketTypes/TicketStages/TicketTopics). Until seeded, ticket creates
|
|
77
|
+
that reference them return `EV-12`.
|
|
78
|
+
- **Drift before consolidation** — loose `Client/` files must already be applied to all existing
|
|
79
|
+
clients before they're consolidated/archived (otherwise the new blank diverges from live
|
|
80
|
+
clients). See the dbchanges2 architecture doc.
|
|
81
|
+
|
|
82
|
+
## Gotchas
|
|
83
|
+
|
|
84
|
+
- **`FOREIGN_KEY_CHECKS=0`** is required when applying the loose schema files — several
|
|
85
|
+
`ALTER … MODIFY/DROP` a column that's in an FK and fail with error 1832 otherwise.
|
|
86
|
+
- **`Apis` rows are in the CLIENT database**, not Core. Core only holds `Clients`/`ClientEmailDomains`
|
|
87
|
+
/`ClientApiIdentities`. Auth needs three values: `client` = `Core.Clients.uuid`, `api` =
|
|
88
|
+
`Client_<Id>.Apis.uuid`, `secret` = `Client_<Id>.Apis.secret`.
|
|
89
|
+
- **`Apis_Roles` needs `Base`+`API` to pre-exist** in `Client_<Id>` — they come from the blank
|
|
90
|
+
seed. If the blank lacks roles, the role links insert zero rows and the key can't authorize.
|
|
91
|
+
- **Never commit secrets** — the generated `Apis` secrets go in a non-committed artifact; the Core
|
|
92
|
+
inserts are secret-free and committable.
|
|
93
|
+
- **Domain URLs** — dev uses `http://<sub>.togaX` (no TLD); beta/prod use `https://…<env>.togaX.com`.
|
|
94
|
+
|
|
95
|
+
## Change history
|
|
96
|
+
|
|
97
|
+
- 2026-06-23 — Initial capture from the Fordham onboarding (TRUE-79702). (mhammontree)
|
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
| Doc | Summary | Files |
|
|
4
4
|
|-----|---------|-------|
|
|
5
5
|
| [Worker (worker2) Architecture](architecture.md) | Worker (repo `worker2`) is an AWS Elastic Beanstalk **Worker Tier** application that processes background jobs. | worker2/Controller/Index.php, worker2/Worker/, worker2/LambdaFunctions/, _underscore/Worker.php |
|
|
6
|
+
| [ClickUp Connectivity Watchdog](features/clickup-connectivity-watchdog.md) | A cron watchdog that emails when the ClickUp integration looks disconnected during business hours. | worker2/Worker/Clickup/Health.php, worker2/Database/ClickupHealthWatchdog.sql |
|
|
6
7
|
| [ClickUp Project & Opportunity Multi-List Routing](features/clickup-project-routing.md) | Routes ClickUp tasks into the correct **secondary multi-list memberships** based on their custom-field values, via the `clickup` webhook. | worker2/Worker/Clickup/Project.php, worker2/Worker/Clickup.php |
|
|
7
8
|
| [ClickUp Work Type Automation (Committed / Conditional / Stretch)](features/clickup-work-type-automation.md) | The ClickUp webhook handler (`_Worker_Clickup`) automatically maintains each task's **Work Type** custom field — `Committed`, `Conditional`, or `Stretch` — base | worker2/Worker/Clickup.php, worker2/Tests/Worker/ClickupWorkTypeTest.php |
|
|
8
9
|
| [Creating Worker Actions](features/creating-worker-actions.md) | How to add a new callable Worker action — a PHP class whose `public static` methods are invoked as background jobs (via webhook, cron, or `_Worker::runTask()`). | worker2/Worker/, worker2/Controller/Index.php, _underscore/Worker.php |
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: ClickUp Connectivity Watchdog
|
|
3
|
+
framework: "2.0"
|
|
4
|
+
repo: worker2
|
|
5
|
+
project: Worker
|
|
6
|
+
client: shared
|
|
7
|
+
type: feature
|
|
8
|
+
status: active
|
|
9
|
+
updated: 2026-06-23
|
|
10
|
+
owners: [ajean]
|
|
11
|
+
files:
|
|
12
|
+
- worker2/Worker/Clickup/Health.php
|
|
13
|
+
- worker2/Database/ClickupHealthWatchdog.sql
|
|
14
|
+
related:
|
|
15
|
+
- ./clickup-project-routing.md
|
|
16
|
+
- ../architecture.md
|
|
17
|
+
---
|
|
18
|
+
|
|
19
|
+
## Summary
|
|
20
|
+
|
|
21
|
+
A cron watchdog that emails when the ClickUp integration looks disconnected during business
|
|
22
|
+
hours. Implemented in `_Worker_Clickup_Health` (action `Clickup/Health/Check`). It exists
|
|
23
|
+
because the ClickUp webhook subscriptions can silently die (a dropped subscription, a ClickUp
|
|
24
|
+
outage, or our endpoint failing), and nobody notices until reporting/automations drift.
|
|
25
|
+
|
|
26
|
+
## Key files / entry points
|
|
27
|
+
|
|
28
|
+
- `worker2/Worker/Clickup/Health.php` — `_Worker_Clickup_Health::Check(): string` (the cron
|
|
29
|
+
action) + `initialize()` (registers the `Team` DB, same pattern as `_Worker_Team_Transcripts`).
|
|
30
|
+
- `worker2/Database/ClickupHealthWatchdog.sql` — run-once reference: the `Team.ClickUpHealthState`
|
|
31
|
+
table + the `Core.CronJobs` row. Canonical migrations live in `dbchanges2`
|
|
32
|
+
(`Team/2026-06-23a …`, `Core/2026-06-23a …`).
|
|
33
|
+
|
|
34
|
+
## How it works
|
|
35
|
+
|
|
36
|
+
Scheduled every 15 min on weekdays 09:00–16:45 Central (cron `0,15,30,45 9-16 * * 1-5`).
|
|
37
|
+
`Check()` evaluates two signals and emails `ajean@togatech.com` if either fires:
|
|
38
|
+
|
|
39
|
+
1. **Inbound silence** — `MAX(dtCreated)` of `Core.WorkerJobs WHERE action = 'Clickup/Webhook'`
|
|
40
|
+
(exact match, so the watchdog's own `Clickup/Health/Check` rows don't count). Elapsed minutes
|
|
41
|
+
are computed by **MySQL `TIMESTAMPDIFF`** (no PHP/DB timezone mismatch). Alerts when ≥ 60 min
|
|
42
|
+
silent, but only from the **second business hour onward** (a one-hour warm-up so the expected
|
|
43
|
+
overnight gap isn't flagged at 09:00).
|
|
44
|
+
2. **Webhook health** — `GET /team/{id}/webhook`; any subscription whose endpoint contains
|
|
45
|
+
`togahub.com` with `health->status == 'failing'`. The probe is wrapped in try/catch — if the
|
|
46
|
+
probe call itself throws, that is treated as a connectivity signal too.
|
|
47
|
+
|
|
48
|
+
De-duplication: `Team.ClickUpHealthState` (single row) records `dtLastAlerted`; an ongoing
|
|
49
|
+
outage re-emails at most once per 60 min (`shouldAlert`), and the flag is cleared when ClickUp
|
|
50
|
+
is healthy again (`clearAlert`) so each fresh outage notifies once.
|
|
51
|
+
|
|
52
|
+
## Data model
|
|
53
|
+
|
|
54
|
+
- **`Team.ClickUpHealthState`** — single-row alert state: `id`, `uuid`, `dtCreated`, `dtUpdated`,
|
|
55
|
+
`dtLastAlerted` (NULL when healthy), `lastReason`. The row is created lazily on first alert.
|
|
56
|
+
- **`Core.CronJobs`** — one row, action `Clickup/Health/Check`, `maxExecutionTime` 120.
|
|
57
|
+
|
|
58
|
+
## Client variations
|
|
59
|
+
|
|
60
|
+
None — internal monitoring for Agilant's ClickUp workspace.
|
|
61
|
+
|
|
62
|
+
## Gotchas / known issues
|
|
63
|
+
|
|
64
|
+
- **Detects ClickUp-side problems only.** It runs *inside* the worker/cron pipeline, so it
|
|
65
|
+
assumes that pipeline is healthy. It will **not** fire if the EB worker tier, the CronScheduler
|
|
66
|
+
Lambda, or the database is itself down — full worker-down detection needs an external uptime
|
|
67
|
+
monitor (not built).
|
|
68
|
+
- **The inbound query must match `Clickup/Webhook` exactly.** A `LIKE 'Clickup/%'` would also
|
|
69
|
+
match the watchdog's own `Clickup/Health/Check` rows and the timer would never expire.
|
|
70
|
+
- **Email identifier is `'True'`** (the internal/team `clientIdentifier`, consistent with other
|
|
71
|
+
team alerts via `_Worker_Notification_Email::Send`).
|
|
72
|
+
- **Business window is enforced in code too** (Mon–Fri, 09:00–17:00 Central), not just in the
|
|
73
|
+
cron schedule, so a manual run outside hours is a safe no-op.
|
|
74
|
+
|
|
75
|
+
## Change history
|
|
76
|
+
- 2026-06-23 — Created. Watchdog cron emails on 60-min inbound webhook silence or a failing subscription during business hours; deduped via `Team.ClickUpHealthState`. (ajean)
|
|
77
|
+
|
|
78
|
+
## Related docs
|
|
79
|
+
|
|
80
|
+
- [ClickUp Project & Opportunity Multi-List Routing](./clickup-project-routing.md) — the integration this watches.
|
|
81
|
+
- [Worker (worker2) Architecture](../architecture.md) — CronJobs, WorkerJobs, always-HTTP-200.
|
|
@@ -6,21 +6,23 @@ project: Worker
|
|
|
6
6
|
client: shared
|
|
7
7
|
type: feature
|
|
8
8
|
status: active
|
|
9
|
-
updated: 2026-06-
|
|
10
|
-
owners: [jcardinal]
|
|
9
|
+
updated: 2026-06-23
|
|
10
|
+
owners: [jcardinal, ajean]
|
|
11
11
|
files:
|
|
12
12
|
- worker2/Worker/Clickup/Project.php
|
|
13
13
|
- worker2/Worker/Clickup.php
|
|
14
14
|
related:
|
|
15
15
|
- ../architecture.md
|
|
16
16
|
- ./creating-worker-actions.md
|
|
17
|
+
- ./clickup-connectivity-watchdog.md
|
|
17
18
|
---
|
|
18
19
|
|
|
19
20
|
## Summary
|
|
20
21
|
|
|
21
22
|
Routes ClickUp tasks into the correct **secondary multi-list memberships** based on their
|
|
22
23
|
custom-field values, via the `clickup` webhook. Implemented in `_Worker_Clickup_Project`
|
|
23
|
-
(an abstract worker class) with three independent entry points covering three ClickUp
|
|
24
|
+
(an abstract worker class) with three independent routing entry points covering three ClickUp
|
|
25
|
+
spaces, plus a **hub-placement guard** that keeps Epics/Opportunities anchored to their hub.
|
|
24
26
|
|
|
25
27
|
**Hybrid design (the central constraint):** ClickUp's public **v2 API forbids changing or
|
|
26
28
|
removing a task's home list** (`400 TASK_035 — Task home list cannot be altered`). So:
|
|
@@ -35,8 +37,9 @@ removing a task's home list** (`400 TASK_035 — Task home list cannot be altere
|
|
|
35
37
|
- `worker2/Worker/Clickup.php` — delegates from `_Worker_Clickup::Webhook()`:
|
|
36
38
|
- `taskStatusUpdated` (~L643): calls `handleEpicUpdate($taskId)` unconditionally.
|
|
37
39
|
- `taskUpdated` (~L826–828): behind a **self-trigger guard**, calls all three handlers.
|
|
40
|
+
- `taskCreated` and `taskMoved`: call `enforceHubPlacement($taskId)` (the guard, below).
|
|
38
41
|
|
|
39
|
-
Three
|
|
42
|
+
Three routing entry points (all `public static function …(string $taskId): void`):
|
|
40
43
|
|
|
41
44
|
| Handler | Space (guard) | Driver fields | Effect |
|
|
42
45
|
|---|---|---|---|
|
|
@@ -44,13 +47,39 @@ Three public entry points (all `public static function …(string $taskId): void
|
|
|
44
47
|
| `handleOpportunityUpdate` | Opportunity/RFP Hub `90113928591` | Business Unit, Project Category, Opportunity Stage | Adds to the LS folder's **Opportunities** list when BU = "Lifecycle Services", category maps to an LS folder, and stage is not `Closed Lost` / not Won/Signed; removes otherwise. |
|
|
45
48
|
| `handleAmOpportunityUpdate` | Opportunity/RFP Hub `90113928591` | Business Unit, Project Category, Opportunity Stage, Project Phase | Two passes: (1) space-level A&M Opportunities list; (2) A&M folder list driven by Opportunity Stage, with Execution→Completion driven by Project Phase = "Complete". |
|
|
46
49
|
|
|
50
|
+
### Hub-placement guard — `enforceHubPlacement(string $taskId): void` (added 2026-06-23)
|
|
51
|
+
|
|
52
|
+
Keeps **Epics in Project Hub** and **Opportunities in Opportunity/RFP Hub**. Called from the
|
|
53
|
+
`taskCreated` and `taskMoved` cases in `Worker/Clickup.php`. Unlike the three routing handlers
|
|
54
|
+
(which identify tasks structurally), it identifies the type by **custom Task Type**
|
|
55
|
+
(`custom_item_id`, configured via the `TASK_TYPE_EPIC_ID` / `TASK_TYPE_OPPORTUNITY_ID`
|
|
56
|
+
constants). A task is **misplaced** when its **home** `space->id` is not its hub — i.e. it was
|
|
57
|
+
created in, or moved into, Lifecycle Services / Advisory & Modernization (or any other space).
|
|
58
|
+
|
|
59
|
+
For a misplaced top-level task it:
|
|
60
|
+
1. **Backfills metadata** (best-effort, LS/A&M only) — Business Unit from the home space
|
|
61
|
+
(`getBusinessUnitForSpaceId()`), Project Category from the home folder
|
|
62
|
+
(`getProjectCategoryForFolderId()`, the **inverse** of `getLsFolderKey`/`getAmFolderKey` —
|
|
63
|
+
keep them in sync). Written via `setDropdownFieldByName()`, which handles both `drop_down`
|
|
64
|
+
and `labels` field shapes and writes only when the value differs.
|
|
65
|
+
2. **Re-adds the correct hub intake list** as a secondary membership (Epic →
|
|
66
|
+
`PROJECT_HUB_INTAKE_LIST_ID`, Opportunity → `OPPORTUNITY_HUB_INTAKE_LIST_ID`). The home list
|
|
67
|
+
itself cannot be moved by the API (TASK_035) — a human must do that.
|
|
68
|
+
3. **Posts a one-time comment** (only when it actually just added the membership) telling the
|
|
69
|
+
user to move the home list back manually.
|
|
70
|
+
|
|
71
|
+
**Ships inert**: with `TASK_TYPE_*_ID` both `null` it is a no-op, so it can deploy before the
|
|
72
|
+
ids are discovered. Configure via the `DiscoverTaskTypes()` / `DiscoverIds()` actions, then
|
|
73
|
+
`RegisterHubGuardWebhooks()`.
|
|
74
|
+
|
|
47
75
|
## How it works
|
|
48
76
|
|
|
49
77
|
1. The `clickup` webhook → `_Worker_Clickup::Webhook()` → delegation in `Worker/Clickup.php`.
|
|
50
78
|
2. Each handler fetches task details via `_Worker_Clickup::getTaskDetails()` (a per-invocation
|
|
51
79
|
`static` cache shared across all handlers in one job), then guards on **space id** and
|
|
52
|
-
**top-level** (`parent` empty).
|
|
53
|
-
|
|
80
|
+
**top-level** (`parent` empty). The three routing handlers identify Epics/tasks
|
|
81
|
+
**structurally** (space + top-level). Custom **Task Types are now configured**, and the
|
|
82
|
+
newer `enforceHubPlacement` guard keys on `custom_item_id` to recognize Epics/Opportunities.
|
|
54
83
|
3. Custom fields are read by name via `extractCustomFields()` (one pass, last-non-null-wins,
|
|
55
84
|
trimmed) → mapped to a target list id → reconciled by `applyMultiListPlacement()`:
|
|
56
85
|
POST the target if absent, DELETE every other in-scope list (DELETE failures are caught
|
|
@@ -80,8 +109,9 @@ None in MySQL — all state lives in ClickUp. Each invocation is recorded as a n
|
|
|
80
109
|
|
|
81
110
|
ClickUp structure encoded as constants in `Project.php`: 4 LS folders
|
|
82
111
|
(Onsite, Factory, Audio-Visual, Managed Services), 4 A&M folders (Retainer, Security,
|
|
83
|
-
AI & Data Center, Cloud), plus space-level lists
|
|
84
|
-
|
|
112
|
+
AI & Data Center, Cloud), plus space-level lists, the hub intake lists, and the Epic/Opportunity
|
|
113
|
+
custom Task Type ids. Regenerate with the `DiscoverIds()` / `DiscoverAmIds()` /
|
|
114
|
+
`DiscoverTaskTypes()` diagnostic actions if the ClickUp structure changes.
|
|
85
115
|
|
|
86
116
|
## Client variations
|
|
87
117
|
|
|
@@ -89,38 +119,52 @@ None — uniform across all clients (shared internal automation for Agilant's Cl
|
|
|
89
119
|
|
|
90
120
|
## Gotchas / known issues
|
|
91
121
|
|
|
122
|
+
- **Hub-guard correctness rests on the HOME space:** `enforceHubPlacement` filters on
|
|
123
|
+
`taskDetails->space->id`, which always follows the task's **home** list. A hub Epic that the
|
|
124
|
+
routing handlers mirror into LS/A&M as a *secondary* membership still reports its home hub as
|
|
125
|
+
the space, so the guard correctly ignores it. Filtering on home space is what stops the guard
|
|
126
|
+
from fighting the secondary-membership routing.
|
|
127
|
+
- **`getProjectCategoryForFolderId()` must stay in sync** with `getLsFolderKey`/`getAmFolderKey`
|
|
128
|
+
and the `*_FOLDER_*` constants — it is their inverse (folder id → Project Category value).
|
|
92
129
|
- **List-name slash spacing differs by space, intentionally:** LS uses
|
|
93
130
|
`"Client Scoping / Discovery"` (space before slash); A&M uses `"Client Scoping/ Discovery"`
|
|
94
131
|
(no space). These match the real ClickUp list names — do not "fix" one to match the other.
|
|
95
132
|
- **String-typed list IDs:** `collectCurrentMemberships()` returns IDs as **strings** and
|
|
96
133
|
avoids array-key dedup, because PHP coerces numeric-string keys to int and would break the
|
|
97
|
-
strict `in_array` comparisons against the string constants.
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
134
|
+
strict `in_array` comparisons against the string constants. (`getProjectCategoryForFolderId`
|
|
135
|
+
relies on that same numeric-string→int coercion being *consistent* between map keys and the
|
|
136
|
+
lookup, so it is correct; a hidden/leading-zero folder id falls through to `null`.)
|
|
137
|
+
- **Shared `getTaskDetails()` cache is stale after writes:** all handlers share one cached task
|
|
138
|
+
fetch per job. After one handler mutates memberships, the cached `locations` are stale for the
|
|
139
|
+
others. Safe today because the space guards are mutually exclusive (a task lives in one space →
|
|
140
|
+
exactly one handler does work).
|
|
141
|
+
- **`RATE_LIMIT_DELAY_US` (120ms):** now used by `enforceHubPlacement` / `setDropdownFieldByName`
|
|
142
|
+
(`usleep`); the three routing handlers still rely on `_Component_Api_Clickup::send()`'s internal
|
|
143
|
+
`sleep(1)`.
|
|
106
144
|
- **POST failures are fatal, DELETE failures are tolerated** — a failed ADD leaves the task
|
|
107
145
|
out of its correct list (surfaced as job failure); a failed DELETE (most likely TASK_035,
|
|
108
146
|
which shouldn't occur on secondary lists) is logged with `[ClickupProject]` and skipped.
|
|
109
|
-
- **Webhook subscriptions** must cover each space
|
|
147
|
+
- **Webhook subscriptions** must cover each space, all → `webhook.togahub.com/clickup`. Routing:
|
|
110
148
|
legacy sprint space `90020178491`, Project Hub `90113939341`, Lifecycle Services
|
|
111
|
-
`90114156087
|
|
149
|
+
`90114156087`; Opportunity Hub via `RegisterOpportunityWebhook()`. The hub guard additionally
|
|
150
|
+
needs **`taskCreated` on LS + A&M** and a **workspace-wide `taskMoved`**, registered via
|
|
151
|
+
`RegisterHubGuardWebhooks()` (which skips any already covered to avoid double-delivery).
|
|
112
152
|
|
|
113
153
|
## Diagnostics
|
|
114
154
|
|
|
115
|
-
Read-only
|
|
155
|
+
Read-only / setup actions, invoked via `curl -X POST https://worker.togahub.com/` with
|
|
116
156
|
`{"action":"Clickup/Project/<Method>","parameters":{...}}`:
|
|
117
|
-
`DiagnoseOpportunity`, `DiagnoseAmTask` (routing verdicts), `DiscoverIds`, `DiscoverAmIds
|
|
118
|
-
(print constant declarations
|
|
157
|
+
`DiagnoseOpportunity`, `DiagnoseAmTask` (routing verdicts), `DiscoverIds`, `DiscoverAmIds`,
|
|
158
|
+
`DiscoverTaskTypes` (print constant declarations / custom-task-type ids),
|
|
159
|
+
`TestHubGuardMappings` (pure self-test of the folder→category & space→BU maps; expect
|
|
160
|
+
`RESULT: PASS`), `RegisterOpportunityWebhook` and `RegisterHubGuardWebhooks` (one-time setup).
|
|
119
161
|
|
|
120
162
|
## Change history
|
|
163
|
+
- 2026-06-23 — Added the hub-placement guard (`enforceHubPlacement` on `taskCreated`/`taskMoved`): re-anchors misplaced Epics/Opportunities to their hub by custom Task Type + home space, backfills Business Unit/Project Category, and notifies. Added `DiscoverTaskTypes`, `RegisterHubGuardWebhooks`, `TestHubGuardMappings`. Corrected the earlier "Task Types not enabled" note. (ajean)
|
|
121
164
|
- 2026-06-09 — Documented ClickUp secondary multi-list routing (hybrid native-automation + worker design, three space handlers, self-trigger guard). (jcardinal)
|
|
122
165
|
|
|
123
166
|
## Related docs
|
|
124
167
|
|
|
125
168
|
- [Worker (worker2) Architecture](../architecture.md) — always-HTTP-200, commit-before-SQS.
|
|
126
169
|
- [Creating Worker Actions](./creating-worker-actions.md) — the worker-action contract.
|
|
170
|
+
- [ClickUp Connectivity Watchdog](./clickup-connectivity-watchdog.md) — the email-alert health check.
|
package/knowledge/INDEX.md
CHANGED
|
@@ -16,9 +16,9 @@ _Auto-generated by `knowledge.js index`. Do not hand-edit._
|
|
|
16
16
|
## 2.0 framework
|
|
17
17
|
|
|
18
18
|
- **_underscore** (_Underscore) _(framework core)_ — 11 doc(s) → [2.0/apps/_underscore/INDEX.md](2.0/apps/_underscore/INDEX.md)
|
|
19
|
-
- **worker2** (Worker) —
|
|
20
|
-
- **api2** (API) —
|
|
21
|
-
- **dbchanges2** (Database Changes) _(framework core)_ —
|
|
19
|
+
- **worker2** (Worker) — 11 doc(s) → [2.0/apps/worker2/INDEX.md](2.0/apps/worker2/INDEX.md)
|
|
20
|
+
- **api2** (API) — 5 doc(s) → [2.0/apps/api2/INDEX.md](2.0/apps/api2/INDEX.md)
|
|
21
|
+
- **dbchanges2** (Database Changes) _(framework core)_ — 2 doc(s) → [2.0/apps/dbchanges2/INDEX.md](2.0/apps/dbchanges2/INDEX.md)
|
|
22
22
|
- **toga2-supply** (TOGa Supply) — 3 doc(s) → [2.0/apps/toga2-supply/INDEX.md](2.0/apps/toga2-supply/INDEX.md)
|
|
23
23
|
- **saml** (SAML SSO Gateway) — 2 doc(s) → [2.0/apps/saml/INDEX.md](2.0/apps/saml/INDEX.md)
|
|
24
24
|
- **toga2-view** (TOGa View Frontend) — 1 doc(s) → [2.0/apps/toga2-view/INDEX.md](2.0/apps/toga2-view/INDEX.md)
|
|
@@ -41,6 +41,7 @@ _Auto-generated by `knowledge.js index`. Do not hand-edit._
|
|
|
41
41
|
- **Compass Canada** (`compass-canada`) → [clients/compass-canada/INDEX.md](clients/compass-canada/INDEX.md)
|
|
42
42
|
- **Compass USA** (`compass-usa`) → [clients/compass-usa/INDEX.md](clients/compass-usa/INDEX.md)
|
|
43
43
|
- **Elite** (`elite`) → [clients/elite/INDEX.md](clients/elite/INDEX.md)
|
|
44
|
+
- **Fordham University** (`fordham`) → [clients/fordham/INDEX.md](clients/fordham/INDEX.md)
|
|
44
45
|
- **GroWrk** (`growrk`) → [clients/growrk/INDEX.md](clients/growrk/INDEX.md)
|
|
45
46
|
- **New York City Department of Education** (`nycdoe`) → [clients/nycdoe/INDEX.md](clients/nycdoe/INDEX.md)
|
|
46
47
|
- **NYC Health & Hospitals** (`nychh`) → [clients/nychh/INDEX.md](clients/nychh/INDEX.md)
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
# Client: Fordham University `fordham`
|
|
2
|
+
|
|
3
|
+
| Doc | Framework | Summary | Files |
|
|
4
|
+
|-----|-----------|---------|-------|
|
|
5
|
+
| [Fordham University](profile.md) | 2.0 | Fordham University (`fordham.edu`), `clientIdentifier` **Fordham**, onboarded onto the 2.0 platform in June 2026 (TRUE-79702). | |
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: "Fordham University"
|
|
3
|
+
framework: "2.0"
|
|
4
|
+
apps:
|
|
5
|
+
- _underscore
|
|
6
|
+
- api2
|
|
7
|
+
- dbchanges2
|
|
8
|
+
project: API
|
|
9
|
+
client: fordham
|
|
10
|
+
type: profile
|
|
11
|
+
status: active
|
|
12
|
+
updated: 2026-06-23
|
|
13
|
+
owners: ["mhammontree"]
|
|
14
|
+
files: []
|
|
15
|
+
related:
|
|
16
|
+
- ../../2.0/apps/api2/features/tickets-api.md
|
|
17
|
+
- ../../2.0/apps/dbchanges2/workflows/client-onboarding.md
|
|
18
|
+
---
|
|
19
|
+
|
|
20
|
+
## Summary
|
|
21
|
+
|
|
22
|
+
Fordham University (`fordham.edu`), `clientIdentifier` **Fordham**, onboarded onto the 2.0 platform
|
|
23
|
+
in June 2026 (TRUE-79702). Their integration uses the **TOGa 2.0 ticket API** (`/v2/tickets`) — they
|
|
24
|
+
create/update/read support tickets in our system via REST. Tickets are the **generic** flow (not
|
|
25
|
+
entitlement-based like PC Matic): the request body is ticket fields at the top level.
|
|
26
|
+
|
|
27
|
+
## Onboarding
|
|
28
|
+
|
|
29
|
+
Provisioned via the standard [2.0 client onboarding process](../../2.0/apps/dbchanges2/workflows/client-onboarding.md):
|
|
30
|
+
`Client_Fordham` / `Logs_Fordham` / `Archive_Fordham`, Core registration, and two API keys
|
|
31
|
+
(internal "Agilant" + the "Fordham" client key). The committed dbchanges2 artifacts are
|
|
32
|
+
`Client/<DATE>-BLANK_CLIENT_DATABASE.sql` (seeded blank) and `Core/<DATE>-onboard_Fordham.sql`
|
|
33
|
+
(Core inserts); the API-key inserts (with secrets) are kept out of the repo.
|
|
34
|
+
|
|
35
|
+
## Integration / API
|
|
36
|
+
|
|
37
|
+
- Uses **generic `/v2/tickets`** (POST/PUT/GET) — see [tickets-api](../../2.0/apps/api2/features/tickets-api.md).
|
|
38
|
+
- Customer-facing docs delivered (TRUE-79699): a Markdown guide + a Postman collection in PC-Matic
|
|
39
|
+
style (globals for `{{url}}`/`{{accessToken}}`, hardcoded creds in the auth body).
|
|
40
|
+
|
|
41
|
+
## Known gaps
|
|
42
|
+
|
|
43
|
+
- **Ticket reference data not yet seeded** — `Urgencies`, `TicketTypes`, `TicketStages`,
|
|
44
|
+
`TicketTopics` are empty for Fordham, so ticket creates can only set descriptions/notes until those
|
|
45
|
+
are configured; referencing them returns `EV-12`.
|
|
46
|
+
|
|
47
|
+
## Change history
|
|
48
|
+
|
|
49
|
+
- 2026-06-23 — Initial onboarding + ticket API integration. (mhammontree)
|
package/package.json
CHANGED