Eldad's review comment: insights about nested resources need a pointer
to the containing parent (the file-in-bucket pattern). Add three
optional fields:
- parentResourceType (plural noun, e.g. `tables`, `collections`)
- parentResourceId
- parentResourceInternalId
so an insight whose `resourceType=indexes` / `resourceId=_idx_status`
can also carry `parentResourceType=tables` / `parentResourceId=orders`
to identify the table that owns the index. All three are nullable for
top-level resources (e.g. a project-wide audit finding).
Schema, response model, manager Create endpoint, and the listInsights
query validator (parent fields are filterable). New compound index
`_key_project_parent_resource(projectInternalId, parentResourceType,
parentResourceId, $sequence)` to support the parent lookup pattern
the console will use ("show all insights for table X").
E2E factory generates a parent by default (engine-aware:
tables for tablesDB, collections for the others). New
testCreateWithoutParentResource exercises the top-level case;
testList gains a parent-resource filter assertion; testUpdate's
preserved-fields check picks up the new attributes.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Insights are produced by internal Appwrite services (edge, executor,
background analyzers) — never by user clients. Move the ingestion
endpoint accordingly.
- Move Http/Insights/Create.php → Http/Manager/Insights/Create.php.
- Path: /v1/insights → /v1/manager/insights. SDK Method marked
`hide: true` and namespaced under `manager` so generated SDKs don't
expose it. Auth narrowed from [ADMIN, KEY] to [KEY] only.
- New scope `insights.manager`. Not granted by any user role
(app/config/roles.php) — Cloud/edge teams configure their internal
key issuance to grant it. `insights.write` description trimmed to
the user-facing surface (update/dismiss/delete) since create is now
manager-only.
- Reports, ListInsights, GetInsight, UpdateInsight, DeleteInsight
remain at /v1/insights/*. Existing scopes unchanged.
- Reports `categories` switched from JSON-encoded string to a native
array<string> column (size 64 per entry, up to 32 entries via the
endpoint validator). MySQL JSON-array indexes are weak and we never
query individual entries — read+rewrite only.
- E2E test API key in tests/e2e/Scopes/ProjectCustom.php gains
insights.read/write/manager + reports.read/write so the manager
endpoint is reachable from the test harness.
- E2E InsightsBase.createInsight() helper now POSTs /manager/insights.
- New testCreateRequiresManagerScope verifies a key with
insights.read/write but no insights.manager is rejected with 401.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
- InsightCTA model now exposes `service` (SDK namespace) and `method`
(function name) instead of a single `action` string. Drops the
string-splitting burden on every consumer and lets the console reach
the right SDK method directly.
- Validator requires both `service` and `method` non-empty; same
16-entry max still enforced.
- Endpoint normalization (Create + Update) splits the new shape into
the persisted CTA descriptor.
- Constants split: INSIGHT_CTA_SERVICE_* (databases / tablesDB /
documentsDB / vectorsDB) and INSIGHT_CTA_METHOD_* (createIndex).
- Insight model + InsightCTA model docs updated with the new field
semantics and per-engine examples.
- E2E factory `sampleCTA($id, $engine)` emits the correct service and
engine-appropriate params keys (tableId/columns for tablesDB;
collectionId/attributes everywhere else). Engine matrix asserts
`service` and `method` independently.
- Added e2e + unit coverage for the new failure modes (missing
service, missing method, empty service, empty method).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
- Restructure InsightsBase trait with explicit helper methods
(createInsight/getInsight/listInsights/updateInsight/deleteInsight,
createReport/getReport/listReports/updateReport/deleteReport, plus
sampleInsight/sampleCTA factories) — same shape ProxyBase uses.
- Add coverage for: report CRUD + duplicate-id rejection, invalid type
rejection, list filtering by all allowed attributes, cursor
pagination + missing-cursor, update preserving untouched fields,
CTA validation edge cases (duplicate ids, empty fields, count > 16),
dismissal round-trip + status filter, report cascade delete,
unauthorized access (no server key), empty-result list.
- Engine-specific insight types (tablesDBIndex, documentsDBIndex,
vectorsDBIndex, plus the legacy databaseIndex) so the CTA's `action`
can map to the matching public API: databases.createIndex,
tablesDB.createIndex, documentsDB.createIndex,
vectorsDB.createIndex. dataProvider drives the engine matrix and
asserts the right action lands in the persisted CTA. Constants for
each action name live in app/init/constants.php.
- InsightCTA model docs spell out which action belongs to which engine
and that the params keys differ between APIs (tableId/columns for
tablesDB vs collectionId/attributes for the legacy / DocumentsDB /
VectorsDB APIs).
- Insight model `type` description now lists every engine variant.
- CTAsTest gains coverage for object-shaped params, empty-action and
empty-label rejection, and the default 16-entry cap.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Address review feedback on PR #12194:
- Pivot CTAs to pure descriptors (id/label/action/params). Drop the
server-side execution layer: Action interface, registry, the
databases.indexes.create CTA action, the params validator, the
/v1/insights/:id/ctas/:id/executions endpoint, the InsightCTAExecution
model, the INSIGHT_CTA_* errors, and the corresponding events. The
console invokes the existing public API directly with the descriptor's
action + params.
- Restore Databases\Indexes\Action.php to its pre-CTA shape and inline
the index-create body back into Create.php (the createIndex helper
was added solely for CTA reuse).
- Move insights collection from project DB to platform DB and add a
parent reports collection alongside it. Insights carry projectId /
projectInternalId for tenant scoping and an optional reportId for
grouping. List endpoints filter by projectInternalId; Get/Update/
Delete also enforce project ownership before touching the document.
- New Reports module with full CRUD (Create/Get/XList/Update/Delete),
Report response model, Reports query validator, REPORT_NOT_FOUND /
REPORT_ALREADY_EXISTS errors, reports.read / reports.write scopes,
and reports.* event tree. Delete cascades to child insights.
- Update.php now mutates the loaded document via setAttribute (instead
of passing a partial new Document), reuses CTAsValidator (instead of
the looser ArrayList<JSON> + isset check), and rejects duplicate CTA
ids.
- Create.php enforces unique CTA ids during normalization.
- CTAsValidator gained a configurable maxCount (default 16) so the
Create path matches the Update path and the DB column size, and
oversized payloads return a clean 400.
- Validator\Queries\Insights adds status and reportId to
ALLOWED_ATTRIBUTES so dismissal / report workflows are filterable.
- Realtime channel parser guards $parts[1] for both insights and
reports event names.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Moves Validator/CTAParams/DatabasesCreateIndex to
Validator/CTA/Databases/Index/Create so the validator hierarchy mirrors
the action hierarchy (Insights/CTA/Action/Databases/Indexes/Create).
Also tightens the Update and CTA execute endpoint descriptions to call
out the dismissal-via-status flow and what the execution result carries.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
The CTA execution dispatcher invokes the action callback positionally
with values that have already been validated (insight fetched and
asserted non-empty in the endpoint, project pulled from the request
context). Re-validating them through param declarations adds noise
without catching anything. Drop the declarations and the validator
classes/tests created for them.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Replace the no-op `fn () => true` validators (gated by skipValidation)
on the `insight` and `project` params with dedicated InsightDocument and
ProjectDocument validators. They check that the injected value is a
non-empty Document with the expected attributes, so a misconfigured
dispatcher or unbound injection fails fast with a useful message.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Move the action class from Action/DatabasesCreateIndex.php to
Action/Databases/Indexes/Create.php so the directory mirrors the
underlying resource hierarchy. Action name follows: databases.createIndex
becomes databases.indexes.create, with the constant renamed to
INSIGHT_CTA_ACTION_DATABASES_INDEXES_CREATE.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Dismissal was a sub-resource (POST /v1/insights/:id/dismissals) but a
dismissal is just a state transition, not a thing the client creates.
Drop the dedicated endpoint and add a `status` enum (`active` |
`dismissed`) to the insights schema, settable via the existing PATCH
update route. The server still derives `dismissedAt` and `dismissedBy`
on transition for audit/sorting, but the client-facing API is just a
single status toggle.
- Schema: add `status` attribute (default `active`)
- Constants: add `INSIGHT_STATUSES`
- Update endpoint: accept `status` param, derive dismissedAt/By on
active <-> dismissed transitions
- Response model: add `status` rule
- Drop Insights/Dismissal/Create.php, the createInsightDismissal SDK
method, the `insights.[id].dismissals.create` event, and the
`insight.dismissal.create` audit
- E2E: replace testCreateDismissal with testDismissViaUpdate covering
both directions of the toggle
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Project-specific override of the default camelCase-acronyms convention:
namespaces, class names, file paths, and SDK method names use `CTA` in
all caps. Touches all insights surfaces — directories, response models,
validators, container resource keys, and SDK method names like
`createInsightCTAExecution`.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Extract the existing test methods into InsightsBase trait and
provide thin Custom{Server,Console}Test classes that compose the
base with the appropriate scope/side traits. Updates the dismissal
and CTA execution tests to the new sub-resource paths.
Replace the bespoke CtaAction/Registry with Utopia\Platform\Action
and Utopia\Registry\Registry. Implement DatabasesCreateIndex with
the full createDocument('indexes') + queue path used by the existing
indexes endpoint, validated via a dedicated Utopia validator. Drop
the obsolete unit tests (custom-Action contract) in favor of
validator-focused tests.
Unit tests cover the CTA registry register/resolve/has/all behaviour
and the DatabasesCreateIndex action's name, scope, validation surface,
and not-implemented execute path. The e2e suite runs the full CRUD
lifecycle, dismiss, and CTA trigger paths against a real cloud project,
including authentication boundaries.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>