@friggframework/core 2.0.0-next.11 → 2.0.0-next.110
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/CLAUDE.md +702 -0
- package/README.md +999 -50
- package/admin-scripts/index.js +52 -0
- package/admin-scripts/repositories/admin-script-execution-repository-documentdb.js +21 -0
- package/admin-scripts/repositories/admin-script-execution-repository-factory.js +51 -0
- package/admin-scripts/repositories/admin-script-execution-repository-interface.js +171 -0
- package/admin-scripts/repositories/admin-script-execution-repository-mongo.js +238 -0
- package/admin-scripts/repositories/admin-script-execution-repository-postgres.js +278 -0
- package/admin-scripts/repositories/script-schedule-repository-documentdb.js +21 -0
- package/admin-scripts/repositories/script-schedule-repository-factory.js +51 -0
- package/admin-scripts/repositories/script-schedule-repository-interface.js +108 -0
- package/admin-scripts/repositories/script-schedule-repository-mongo.js +179 -0
- package/admin-scripts/repositories/script-schedule-repository-postgres.js +210 -0
- package/application/commands/README.md +451 -0
- package/application/commands/admin-script-commands.js +359 -0
- package/application/commands/credential-commands.js +262 -0
- package/application/commands/entity-commands.js +336 -0
- package/application/commands/integration-commands.js +356 -0
- package/application/commands/integration-mapping-commands.js +25 -0
- package/application/commands/process-commands.js +135 -0
- package/application/commands/report-commands.js +188 -0
- package/application/commands/scheduler-commands.js +263 -0
- package/application/commands/usage-commands.js +56 -0
- package/application/commands/user-commands.js +312 -0
- package/application/index.js +83 -0
- package/artifacts/repositories/artifact-repository-factory.js +19 -0
- package/artifacts/repositories/artifact-repository-interface.js +27 -0
- package/artifacts/repositories/artifact-repository-local.js +42 -0
- package/artifacts/repositories/artifact-repository-s3.js +61 -0
- package/assertions/index.js +0 -3
- package/core/CLAUDE.md +700 -0
- package/core/Worker.js +60 -24
- package/core/create-handler.js +189 -5
- package/core/parameters-to-env.js +257 -0
- package/core/ssm-preload.mjs +34 -0
- package/credential/repositories/credential-active-type.js +32 -0
- package/credential/repositories/credential-repository-documentdb.js +355 -0
- package/credential/repositories/credential-repository-factory.js +54 -0
- package/credential/repositories/credential-repository-interface.js +113 -0
- package/credential/repositories/credential-repository-mongo.js +294 -0
- package/credential/repositories/credential-repository-postgres.js +312 -0
- package/credential/repositories/credential-repository.js +300 -0
- package/credential/use-cases/get-credential-for-user.js +25 -0
- package/credential/use-cases/update-authentication-status.js +15 -0
- package/database/MONGODB_TRANSACTION_FIX.md +198 -0
- package/database/adapters/lambda-invoker.js +97 -0
- package/database/config.js +154 -0
- package/database/documentdb-encryption-service.js +330 -0
- package/database/documentdb-utils.js +192 -0
- package/database/encryption/README.md +839 -0
- package/database/encryption/documentdb-encryption-service.md +3575 -0
- package/database/encryption/encryption-schema-registry.js +401 -0
- package/database/encryption/field-encryption-service.js +254 -0
- package/database/encryption/logger.js +79 -0
- package/database/encryption/prisma-encryption-extension.js +230 -0
- package/database/index.js +21 -21
- package/database/prisma.js +182 -0
- package/database/repositories/health-check-repository-documentdb.js +138 -0
- package/database/repositories/health-check-repository-factory.js +48 -0
- package/database/repositories/health-check-repository-interface.js +82 -0
- package/database/repositories/health-check-repository-mongodb.js +89 -0
- package/database/repositories/health-check-repository-postgres.js +82 -0
- package/database/repositories/migration-status-repository-s3.js +137 -0
- package/database/use-cases/check-database-health-use-case.js +29 -0
- package/database/use-cases/check-database-state-use-case.js +81 -0
- package/database/use-cases/check-encryption-health-use-case.js +83 -0
- package/database/use-cases/get-database-state-via-worker-use-case.js +61 -0
- package/database/use-cases/get-migration-status-use-case.js +93 -0
- package/database/use-cases/resolve-migration-via-worker-use-case.js +49 -0
- package/database/use-cases/run-database-migration-use-case.js +139 -0
- package/database/use-cases/test-encryption-use-case.js +253 -0
- package/database/use-cases/trigger-database-migration-use-case.js +157 -0
- package/database/utils/mongodb-collection-utils.js +94 -0
- package/database/utils/mongodb-schema-init.js +108 -0
- package/database/utils/prisma-runner.js +491 -0
- package/database/utils/prisma-schema-parser.js +182 -0
- package/docs/PROCESS_MANAGEMENT_QUEUE_SPEC.md +517 -0
- package/encrypt/Cryptor.js +34 -168
- package/encrypt/index.js +1 -2
- package/errors/client-safe-error.js +26 -0
- package/errors/fetch-error.js +15 -7
- package/errors/index.js +2 -0
- package/generated/prisma-mongodb/client.d.ts +1 -0
- package/generated/prisma-mongodb/client.js +5 -0
- package/generated/prisma-mongodb/default.d.ts +1 -0
- package/generated/prisma-mongodb/default.js +5 -0
- package/generated/prisma-mongodb/edge.d.ts +1 -0
- package/generated/prisma-mongodb/edge.js +386 -0
- package/generated/prisma-mongodb/index-browser.js +368 -0
- package/generated/prisma-mongodb/index.d.ts +27065 -0
- package/generated/prisma-mongodb/index.js +411 -0
- package/generated/prisma-mongodb/libquery_engine-debian-openssl-3.0.x.so.node +0 -0
- package/generated/prisma-mongodb/libquery_engine-rhel-openssl-3.0.x.so.node +0 -0
- package/generated/prisma-mongodb/package.json +183 -0
- package/generated/prisma-mongodb/runtime/edge-esm.js +35 -0
- package/generated/prisma-mongodb/runtime/edge.js +35 -0
- package/generated/prisma-mongodb/runtime/index-browser.d.ts +370 -0
- package/generated/prisma-mongodb/runtime/index-browser.js +17 -0
- package/generated/prisma-mongodb/runtime/library.d.ts +3982 -0
- package/generated/prisma-mongodb/runtime/library.js +147 -0
- package/generated/prisma-mongodb/runtime/react-native.js +84 -0
- package/generated/prisma-mongodb/runtime/wasm-compiler-edge.js +85 -0
- package/generated/prisma-mongodb/runtime/wasm-engine-edge.js +38 -0
- package/generated/prisma-mongodb/schema.prisma +455 -0
- package/generated/prisma-mongodb/wasm-edge-light-loader.mjs +5 -0
- package/generated/prisma-mongodb/wasm-worker-loader.mjs +5 -0
- package/generated/prisma-mongodb/wasm.d.ts +1 -0
- package/generated/prisma-mongodb/wasm.js +393 -0
- package/generated/prisma-postgresql/client.d.ts +1 -0
- package/generated/prisma-postgresql/client.js +5 -0
- package/generated/prisma-postgresql/default.d.ts +1 -0
- package/generated/prisma-postgresql/default.js +5 -0
- package/generated/prisma-postgresql/edge.d.ts +1 -0
- package/generated/prisma-postgresql/edge.js +407 -0
- package/generated/prisma-postgresql/index-browser.js +389 -0
- package/generated/prisma-postgresql/index.d.ts +29904 -0
- package/generated/prisma-postgresql/index.js +432 -0
- package/generated/prisma-postgresql/libquery_engine-debian-openssl-3.0.x.so.node +0 -0
- package/generated/prisma-postgresql/libquery_engine-rhel-openssl-3.0.x.so.node +0 -0
- package/generated/prisma-postgresql/package.json +183 -0
- package/generated/prisma-postgresql/query_engine_bg.js +2 -0
- package/generated/prisma-postgresql/query_engine_bg.wasm +0 -0
- package/generated/prisma-postgresql/runtime/edge-esm.js +35 -0
- package/generated/prisma-postgresql/runtime/edge.js +35 -0
- package/generated/prisma-postgresql/runtime/index-browser.d.ts +370 -0
- package/generated/prisma-postgresql/runtime/index-browser.js +17 -0
- package/generated/prisma-postgresql/runtime/library.d.ts +3982 -0
- package/generated/prisma-postgresql/runtime/library.js +147 -0
- package/generated/prisma-postgresql/runtime/react-native.js +84 -0
- package/generated/prisma-postgresql/runtime/wasm-compiler-edge.js +85 -0
- package/generated/prisma-postgresql/runtime/wasm-engine-edge.js +38 -0
- package/generated/prisma-postgresql/schema.prisma +436 -0
- package/generated/prisma-postgresql/wasm-edge-light-loader.mjs +5 -0
- package/generated/prisma-postgresql/wasm-worker-loader.mjs +5 -0
- package/generated/prisma-postgresql/wasm.d.ts +1 -0
- package/generated/prisma-postgresql/wasm.js +414 -0
- package/handlers/WEBHOOKS.md +653 -0
- package/handlers/app-definition-loader.js +67 -0
- package/handlers/app-handler-helpers.js +3 -3
- package/handlers/backend-utils.js +271 -42
- package/handlers/database-migration-handler.js +227 -0
- package/handlers/integration-event-dispatcher.js +68 -0
- package/handlers/middleware/admin-auth.js +73 -0
- package/handlers/routers/HEALTHCHECK.md +342 -0
- package/handlers/routers/auth.js +4 -15
- package/handlers/routers/db-migration.handler.js +29 -0
- package/handlers/routers/db-migration.js +329 -0
- package/handlers/routers/health.js +518 -0
- package/handlers/routers/integration-defined-routers.js +85 -10
- package/handlers/routers/integration-webhook-routers.js +80 -0
- package/handlers/routers/user.js +27 -5
- package/handlers/routers/websocket.js +5 -3
- package/handlers/use-cases/check-external-apis-health-use-case.js +81 -0
- package/handlers/use-cases/check-integrations-health-use-case.js +44 -0
- package/handlers/workers/db-migration.js +427 -0
- package/handlers/workers/dlq-processor.js +63 -0
- package/handlers/workers/integration-defined-workers.js +13 -7
- package/index.js +102 -46
- package/infrastructure/scheduler/eventbridge-scheduler-adapter.js +184 -0
- package/infrastructure/scheduler/index.js +33 -0
- package/infrastructure/scheduler/mock-scheduler-adapter.js +143 -0
- package/infrastructure/scheduler/scheduler-service-factory.js +73 -0
- package/infrastructure/scheduler/scheduler-service-interface.js +47 -0
- package/integrations/EXTENSIONS.md +240 -0
- package/integrations/WEBHOOK-QUICKSTART.md +151 -0
- package/integrations/extension.js +254 -0
- package/integrations/index.js +20 -10
- package/integrations/integration-base.js +730 -74
- package/integrations/integration-router.js +398 -180
- package/integrations/options.js +1 -1
- package/integrations/repositories/config-patch-shared.js +43 -0
- package/integrations/repositories/integration-mapping-repository-documentdb.js +303 -0
- package/integrations/repositories/integration-mapping-repository-factory.js +57 -0
- package/integrations/repositories/integration-mapping-repository-interface.js +120 -0
- package/integrations/repositories/integration-mapping-repository-mongo.js +183 -0
- package/integrations/repositories/integration-mapping-repository-postgres.js +255 -0
- package/integrations/repositories/integration-mapping-repository.js +156 -0
- package/integrations/repositories/integration-repository-documentdb.js +322 -0
- package/integrations/repositories/integration-repository-factory.js +51 -0
- package/integrations/repositories/integration-repository-interface.js +189 -0
- package/integrations/repositories/integration-repository-mongo.js +450 -0
- package/integrations/repositories/integration-repository-postgres.js +492 -0
- package/integrations/repositories/process-repository-documentdb.js +311 -0
- package/integrations/repositories/process-repository-factory.js +53 -0
- package/integrations/repositories/process-repository-interface.js +136 -0
- package/integrations/repositories/process-repository-mongo.js +262 -0
- package/integrations/repositories/process-repository-postgres.js +380 -0
- package/integrations/repositories/process-update-ops-shared.js +112 -0
- package/integrations/repositories/report-id.js +13 -0
- package/integrations/tests/doubles/config-capturing-integration.js +81 -0
- package/integrations/tests/doubles/dummy-integration-class.js +113 -0
- package/integrations/tests/doubles/test-integration-repository.js +146 -0
- package/integrations/use-cases/create-integration.js +215 -0
- package/integrations/use-cases/create-process.js +130 -0
- package/integrations/use-cases/delete-integration-for-user.js +119 -0
- package/integrations/use-cases/find-integration-by-entity-external-id.js +74 -0
- package/integrations/use-cases/find-integration-context-by-external-entity-id.js +76 -0
- package/integrations/use-cases/get-integration-for-user.js +78 -0
- package/integrations/use-cases/get-integration-instance-by-definition.js +67 -0
- package/integrations/use-cases/get-integration-instance.js +83 -0
- package/integrations/use-cases/get-integrations-for-user.js +88 -0
- package/integrations/use-cases/get-possible-integrations.js +27 -0
- package/integrations/use-cases/get-process.js +89 -0
- package/integrations/use-cases/index.js +19 -0
- package/integrations/use-cases/list-integrations-by-entity-external-id.js +46 -0
- package/integrations/use-cases/load-integration-context.js +71 -0
- package/integrations/use-cases/patch-integration-config.js +39 -0
- package/integrations/use-cases/process-errors.js +28 -0
- package/integrations/use-cases/update-integration-config.js +32 -0
- package/integrations/use-cases/update-integration-messages.js +44 -0
- package/integrations/use-cases/update-integration-status.js +32 -0
- package/integrations/use-cases/update-integration.js +92 -0
- package/integrations/use-cases/update-process-metrics.js +222 -0
- package/integrations/use-cases/update-process-state.js +163 -0
- package/integrations/utils/map-integration-dto.js +37 -0
- package/jest-global-setup-noop.js +3 -0
- package/jest-global-teardown-noop.js +3 -0
- package/logs/logger.js +0 -4
- package/{module-plugin → modules}/index.js +0 -10
- package/modules/module-factory.js +56 -0
- package/modules/module.js +307 -0
- package/modules/repositories/module-repository-documentdb.js +350 -0
- package/modules/repositories/module-repository-factory.js +40 -0
- package/modules/repositories/module-repository-interface.js +145 -0
- package/modules/repositories/module-repository-mongo.js +436 -0
- package/modules/repositories/module-repository-postgres.js +481 -0
- package/modules/repositories/module-repository.js +369 -0
- package/modules/requester/api-key.js +52 -0
- package/modules/requester/oauth-2.js +555 -0
- package/modules/requester/requester.js +542 -0
- package/{module-plugin → modules}/test/mock-api/api.js +8 -3
- package/{module-plugin → modules}/test/mock-api/definition.js +14 -10
- package/modules/tests/doubles/test-module-factory.js +16 -0
- package/modules/tests/doubles/test-module-repository.js +39 -0
- package/modules/use-cases/get-entities-for-user.js +32 -0
- package/modules/use-cases/get-entity-options-by-id.js +71 -0
- package/modules/use-cases/get-entity-options-by-type.js +34 -0
- package/modules/use-cases/get-module-instance-from-type.js +34 -0
- package/modules/use-cases/get-module.js +74 -0
- package/modules/use-cases/process-authorization-callback.js +243 -0
- package/modules/use-cases/refresh-entity-options.js +72 -0
- package/modules/use-cases/test-module-auth.js +72 -0
- package/modules/utils/map-module-dto.js +18 -0
- package/package.json +92 -50
- package/prisma-mongodb/schema.prisma +455 -0
- package/prisma-postgresql/migrations/20250930193005_init/migration.sql +315 -0
- package/prisma-postgresql/migrations/20251006135218_init/migration.sql +9 -0
- package/prisma-postgresql/migrations/20251010000000_remove_unused_entity_reference_map/migration.sql +3 -0
- package/prisma-postgresql/migrations/20251112195422_update_user_unique_constraints/migration.sql +25 -0
- package/prisma-postgresql/migrations/20260422120000_add_entity_data_column/migration.sql +10 -0
- package/prisma-postgresql/migrations/20260422120001_create_process_table/migration.sql +48 -0
- package/prisma-postgresql/migrations/20260625000000_add_user_organization_id_index/migration.sql +2 -0
- package/prisma-postgresql/migrations/20260703000000_add_integration_status_in_creation_in_deletion/migration.sql +11 -0
- package/prisma-postgresql/migrations/20260703000001_integration_status_default_in_creation/migration.sql +8 -0
- package/prisma-postgresql/migrations/20260705000000_create_usage_counter/migration.sql +26 -0
- package/prisma-postgresql/migrations/20260706000000_add_admin_script_execution_and_schedule/migration.sql +61 -0
- package/prisma-postgresql/migrations/migration_lock.toml +3 -0
- package/prisma-postgresql/schema.prisma +436 -0
- package/queues/queuer-util.js +103 -21
- package/reporting/README.md +154 -0
- package/reporting/builtin-reports.js +6 -0
- package/reporting/index.js +13 -0
- package/reporting/report-base.js +49 -0
- package/reporting/reports/integrations-report.js +221 -0
- package/syncs/manager.js +468 -443
- package/syncs/repositories/sync-repository-documentdb.js +240 -0
- package/syncs/repositories/sync-repository-factory.js +43 -0
- package/syncs/repositories/sync-repository-interface.js +109 -0
- package/syncs/repositories/sync-repository-mongo.js +239 -0
- package/syncs/repositories/sync-repository-postgres.js +319 -0
- package/syncs/sync.js +0 -1
- package/telemetry/README.md +331 -0
- package/telemetry/bind-telemetry-context.js +73 -0
- package/telemetry/canonical-counters.js +52 -0
- package/telemetry/exporters.js +85 -0
- package/telemetry/index.js +26 -0
- package/telemetry/instrument-handler.js +87 -0
- package/telemetry/no-op-telemetry.js +67 -0
- package/telemetry/north-star.js +103 -0
- package/telemetry/otel-telemetry.js +213 -0
- package/telemetry/plugin-subscribers.js +77 -0
- package/telemetry/telemetry-config.js +120 -0
- package/telemetry/telemetry-context.js +40 -0
- package/telemetry/telemetry-event-bus.js +58 -0
- package/telemetry/telemetry-runtime.js +147 -0
- package/telemetry/telemetry-service.js +51 -0
- package/telemetry/usage-rollup-subscriber.js +116 -0
- package/token/repositories/token-repository-documentdb.js +137 -0
- package/token/repositories/token-repository-factory.js +40 -0
- package/token/repositories/token-repository-interface.js +131 -0
- package/token/repositories/token-repository-mongo.js +219 -0
- package/token/repositories/token-repository-postgres.js +264 -0
- package/token/repositories/token-repository.js +219 -0
- package/types/associations/index.d.ts +0 -17
- package/types/core/index.d.ts +12 -4
- package/types/database/index.d.ts +10 -2
- package/types/encrypt/index.d.ts +5 -3
- package/types/integrations/index.d.ts +3 -8
- package/types/module-plugin/index.d.ts +20 -69
- package/types/syncs/index.d.ts +0 -17
- package/usage/README.md +54 -0
- package/usage/index.js +17 -0
- package/usage/repositories/usage-repository-documentdb.js +194 -0
- package/usage/repositories/usage-repository-factory.js +25 -0
- package/usage/repositories/usage-repository-interface.js +37 -0
- package/usage/repositories/usage-repository-prisma.js +146 -0
- package/usage/tracked-metrics.js +38 -0
- package/usage/usage-windows.js +24 -0
- package/user/repositories/user-repository-documentdb.js +458 -0
- package/user/repositories/user-repository-factory.js +52 -0
- package/user/repositories/user-repository-interface.js +214 -0
- package/user/repositories/user-repository-mongo.js +323 -0
- package/user/repositories/user-repository-postgres.js +377 -0
- package/user/tests/doubles/test-user-repository.js +72 -0
- package/user/use-cases/authenticate-user.js +127 -0
- package/user/use-cases/authenticate-with-shared-secret.js +48 -0
- package/user/use-cases/create-individual-user.js +61 -0
- package/user/use-cases/create-organization-user.js +47 -0
- package/user/use-cases/create-token-for-user-id.js +30 -0
- package/user/use-cases/get-user-from-adopter-jwt.js +149 -0
- package/user/use-cases/get-user-from-bearer-token.js +77 -0
- package/user/use-cases/get-user-from-x-frigg-headers.js +132 -0
- package/user/use-cases/login-user.js +122 -0
- package/user/user.js +125 -0
- package/utils/backend-path.js +38 -0
- package/utils/index.js +6 -0
- package/websocket/repositories/websocket-connection-repository-documentdb.js +119 -0
- package/websocket/repositories/websocket-connection-repository-factory.js +44 -0
- package/websocket/repositories/websocket-connection-repository-interface.js +106 -0
- package/websocket/repositories/websocket-connection-repository-mongo.js +156 -0
- package/websocket/repositories/websocket-connection-repository-postgres.js +196 -0
- package/websocket/repositories/websocket-connection-repository.js +161 -0
- package/assertions/is-equal.js +0 -17
- package/associations/model.js +0 -54
- package/database/models/IndividualUser.js +0 -76
- package/database/models/OrganizationUser.js +0 -29
- package/database/models/State.js +0 -9
- package/database/models/Token.js +0 -70
- package/database/models/UserModel.js +0 -7
- package/database/models/WebsocketConnection.js +0 -49
- package/database/mongo.js +0 -45
- package/database/mongoose.js +0 -5
- package/encrypt/Cryptor.test.js +0 -32
- package/encrypt/encrypt.js +0 -132
- package/encrypt/encrypt.test.js +0 -1069
- package/encrypt/test-encrypt.js +0 -107
- package/errors/base-error.test.js +0 -32
- package/errors/fetch-error.test.js +0 -79
- package/errors/halt-error.test.js +0 -11
- package/errors/validation-errors.test.js +0 -120
- package/handlers/routers/middleware/loadUser.js +0 -15
- package/handlers/routers/middleware/requireLoggedInUser.js +0 -12
- package/integrations/create-frigg-backend.js +0 -31
- package/integrations/integration-factory.js +0 -251
- package/integrations/integration-mapping.js +0 -43
- package/integrations/integration-model.js +0 -46
- package/integrations/integration-user.js +0 -144
- package/integrations/test/integration-base.test.js +0 -144
- package/lambda/TimeoutCatcher.test.js +0 -68
- package/logs/logger.test.js +0 -76
- package/module-plugin/auther.js +0 -393
- package/module-plugin/credential.js +0 -22
- package/module-plugin/entity-manager.js +0 -70
- package/module-plugin/entity.js +0 -46
- package/module-plugin/manager.js +0 -169
- package/module-plugin/module-factory.js +0 -61
- package/module-plugin/requester/api-key.js +0 -36
- package/module-plugin/requester/oauth-2.js +0 -219
- package/module-plugin/requester/requester.js +0 -165
- package/module-plugin/requester/requester.test.js +0 -28
- package/module-plugin/test/auther.test.js +0 -97
- package/syncs/model.js +0 -62
- /package/{module-plugin → modules}/ModuleConstants.js +0 -0
- /package/{module-plugin → modules}/requester/basic.js +0 -0
- /package/{module-plugin → modules}/test/mock-api/mocks/hubspot.js +0 -0
|
@@ -0,0 +1,154 @@
|
|
|
1
|
+
# Reporting
|
|
2
|
+
|
|
3
|
+
Deployment-wide reports for `@friggframework/core`, modelled as **admin
|
|
4
|
+
operations** (ADR-010) — a sibling of the Admin Script Runner (ADR-005) on the
|
|
5
|
+
same primitives: one registry, the shared admin API key, sync/async execution,
|
|
6
|
+
the isolated admin execution store, and EventBridge scheduling.
|
|
7
|
+
|
|
8
|
+
A report is a definition (`ReportBase`) whose output is its payload. **Core
|
|
9
|
+
ships built-in reports; adopters register their own** — both are discovered,
|
|
10
|
+
listed, and executed through the same runner. This package provides `ReportBase`,
|
|
11
|
+
the built-in reports, and `createReportCommands()`. The HTTP router, runner, and
|
|
12
|
+
executor live in `@friggframework/admin-scripts`; the infrastructure (queue,
|
|
13
|
+
executor Lambda, artifact bucket, scheduler) is generated by the devtools
|
|
14
|
+
`AdminScriptBuilder`.
|
|
15
|
+
|
|
16
|
+
## Registering reports
|
|
17
|
+
|
|
18
|
+
```js
|
|
19
|
+
// app definition — same shape as adminScripts
|
|
20
|
+
const Definition = {
|
|
21
|
+
name: 'my-app',
|
|
22
|
+
integrations: [HubSpotIntegration, SalesforceIntegration],
|
|
23
|
+
reports: [ConnectedAccountsActivity], // adopter-defined
|
|
24
|
+
admin: { includeBuiltinReports: true }, // + core built-ins (integrations, ...)
|
|
25
|
+
};
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
A report extends `ReportBase` and reads only through the injected admin command
|
|
29
|
+
bundle (`frigg` / `this.context.commands`), never a repository directly:
|
|
30
|
+
|
|
31
|
+
```js
|
|
32
|
+
const { ReportBase } = require('@friggframework/core');
|
|
33
|
+
|
|
34
|
+
class ConnectedAccountsActivity extends ReportBase {
|
|
35
|
+
static Definition = {
|
|
36
|
+
name: 'connected-accounts-activity',
|
|
37
|
+
version: '1.0.0',
|
|
38
|
+
runModes: ['snapshot', 'recorded', 'live'], // first is the default
|
|
39
|
+
inputSchema: { type: 'object', properties: {
|
|
40
|
+
windowDays: { type: 'integer', enum: [30, 60, 90], default: 30 } } },
|
|
41
|
+
output: { format: 'json' }, // 'csv'|'pdf'|'zip' → artifact storage
|
|
42
|
+
schedule: { enabled: true, cron: 'cron(0 6 * * ? *)', mode: 'snapshot' }, // default mode only; activate via PUT
|
|
43
|
+
};
|
|
44
|
+
|
|
45
|
+
async execute(frigg, params) {
|
|
46
|
+
const since = daysAgo(params.windowDays ?? 30);
|
|
47
|
+
const byType = await frigg.credentials.countActiveByType({ since });
|
|
48
|
+
return { windowDays: params.windowDays ?? 30, byType };
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
## Auth
|
|
54
|
+
|
|
55
|
+
All report endpoints use the **shared admin API key** (ADR-005): the
|
|
56
|
+
`x-frigg-admin-api-key` header, validated against `ADMIN_API_KEY`. The dedicated
|
|
57
|
+
reporting key (`x-frigg-reporting-api-key` / `REPORTING_API_KEY`) is **retired**.
|
|
58
|
+
|
|
59
|
+
## Run modes
|
|
60
|
+
|
|
61
|
+
Each invocation picks a mode; persistence follows from it. All three run through
|
|
62
|
+
the one runner and the isolated admin execution store (`AdminScriptExecution`,
|
|
63
|
+
`type: 'REPORT'` — no user/integration FK, so a user-scoped query can never
|
|
64
|
+
return a report record).
|
|
65
|
+
|
|
66
|
+
| Mode | Persists | Use it for |
|
|
67
|
+
| --- | --- | --- |
|
|
68
|
+
| `live` | nothing — computes and returns inline | cheap, always-fresh queries |
|
|
69
|
+
| `recorded` | an execution record (input + results + logs) | audit trail, async polling, expensive scans |
|
|
70
|
+
| `snapshot` | a recorded run tagged into a named series | trends over time |
|
|
71
|
+
|
|
72
|
+
Non-JSON `output.format` (`csv`/`pdf`/`zip`) is written to artifact storage (S3,
|
|
73
|
+
private + signed URL) instead of inline; JSON stays inline.
|
|
74
|
+
|
|
75
|
+
## Endpoints
|
|
76
|
+
|
|
77
|
+
| Method | Path | Description |
|
|
78
|
+
| --- | --- | --- |
|
|
79
|
+
| `GET` | `/api/v2/reports` | List registered report definitions. |
|
|
80
|
+
| `GET` | `/api/v2/reports/:name` | Report definition detail (run modes, input schema, schedule). |
|
|
81
|
+
| `POST` | `/api/v2/reports/:name/run` | Run `{ mode, params }`. `live` → `200` inline; `recorded`/`snapshot` → `202 { executionId }` (queued). |
|
|
82
|
+
| `GET` | `/api/v2/reports/:name/snapshots?from=&to=` | A report's snapshot series (trend); each point carries a signed `artifactUrl` when the run produced a non-JSON artifact. |
|
|
83
|
+
| `GET` | `/api/v2/reports/executions/:id` | Fetch one report execution (guarded to `type: 'REPORT'`); a stored artifact is returned as a signed `results.artifactUrl`. |
|
|
84
|
+
| `GET`/`PUT`/`DELETE` | `/api/v2/reports/:name/schedule` | Manage a report's recurring schedule. |
|
|
85
|
+
| `GET` | `/api/v2/reports/integrations` | Deprecated back-compat for PR #607 — runs the built-in `integrations` report in `live` mode and returns its payload directly. Prefer `POST /:name/run`. |
|
|
86
|
+
|
|
87
|
+
## Built-in: `integrations`
|
|
88
|
+
|
|
89
|
+
Integrations by status and type, with per-type usage columns. Input query params
|
|
90
|
+
(all strings): `status` (an `IntegrationStatus`), `type` (`config.type` slug),
|
|
91
|
+
`userId`. Response shape (`schemaVersion: 1`):
|
|
92
|
+
|
|
93
|
+
```jsonc
|
|
94
|
+
{
|
|
95
|
+
"schemaVersion": 1,
|
|
96
|
+
"service": "frigg-core-api",
|
|
97
|
+
"generatedAt": "2026-06-29T20:45:05.142Z",
|
|
98
|
+
"filters": { "status": null, "type": null, "userId": null },
|
|
99
|
+
"metrics": {
|
|
100
|
+
"total": 12,
|
|
101
|
+
"byStatus": { "ENABLED": 9, "ERROR": 2, "NEEDS_CONFIG": 1, "PROCESSING": 0, "DISABLED": 0 },
|
|
102
|
+
"byType": [
|
|
103
|
+
{
|
|
104
|
+
"type": "hubspot",
|
|
105
|
+
"label": "HubSpot CRM",
|
|
106
|
+
"total": 5,
|
|
107
|
+
"byStatus": { "ENABLED": 4, "ERROR": 1, "NEEDS_CONFIG": 0, "PROCESSING": 0, "DISABLED": 0 },
|
|
108
|
+
"usage": { "records.synced": 4200, "webhooks.received": 118 }
|
|
109
|
+
}
|
|
110
|
+
],
|
|
111
|
+
"typeLabels": { "hubspot": "HubSpot CRM" },
|
|
112
|
+
"integrations": [ /* lightweight per-integration rows */ ]
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
```
|
|
116
|
+
|
|
117
|
+
### Field reference
|
|
118
|
+
|
|
119
|
+
- `schemaVersion` — contract version; branch on it. Additive changes do **not** bump it.
|
|
120
|
+
- `filters` — echoes the applied filters (nulls when omitted).
|
|
121
|
+
- `metrics.total` — count of integrations matching the filters.
|
|
122
|
+
- `metrics.byStatus` — counts keyed by `IntegrationStatus` value.
|
|
123
|
+
- `metrics.byType[]` — per `type` breakdown: `{ type, label, total, byStatus, usage? }`.
|
|
124
|
+
- `type` — the `config.type` slug. Integrations with no type bucket as `"unknown"`.
|
|
125
|
+
- `usage` — **additive** (ADR-011): present only when the deployment has the usage
|
|
126
|
+
store configured. A map of canonical counter → total for that type (`0` when a
|
|
127
|
+
type has no data). Read only from the Frigg usage store; a usage-store read
|
|
128
|
+
failure never fails the structural report. Populated by the counters an
|
|
129
|
+
integration opts into via `Definition.usage` — see [`../telemetry/README.md`](../telemetry/README.md).
|
|
130
|
+
- `label` — human-readable name from the integration class's `Definition.display.label`,
|
|
131
|
+
falling back to the `type` slug when no registered class supplies one.
|
|
132
|
+
- `metrics.typeLabels` — map of `type` slug → label, so callers label
|
|
133
|
+
`integrations[].type` rows without bloating each row.
|
|
134
|
+
- `metrics.integrations[]` — lightweight per-integration rows (`id`, `type`, `status`,
|
|
135
|
+
`userId`, `version`, `moduleCount`, `errorCount`, `mappedRecordCount`, `createdAt`,
|
|
136
|
+
`updatedAt`). Resolve display names via `metrics.typeLabels`.
|
|
137
|
+
|
|
138
|
+
The built-in report reads its data through the admin command bundle
|
|
139
|
+
(`frigg.integrations.listForReport`, `frigg.integrationMappings.countByIntegrationIds`,
|
|
140
|
+
`frigg.usage.getTotalsByDimension`) — the retired reporting repository triad's
|
|
141
|
+
logic now lives in the canonical integration/mapping repositories, so all
|
|
142
|
+
database adapters (PostgreSQL, MongoDB, DocumentDB) are served through one path.
|
|
143
|
+
|
|
144
|
+
## Caveats
|
|
145
|
+
|
|
146
|
+
- Report output is sensitive (integration types, counts, user IDs, versions).
|
|
147
|
+
Treat the admin API key as an admin secret.
|
|
148
|
+
- The `integrations` back-compat alias is transitional; migrate to
|
|
149
|
+
`POST /api/v2/reports/integrations/run { "mode": "live" }`.
|
|
150
|
+
- Labels only appear once the app registers the integration classes.
|
|
151
|
+
- A `schedule` block in a report's Definition only supplies the default scheduled
|
|
152
|
+
**mode**. It does not create a recurring trigger on its own — activate the
|
|
153
|
+
schedule with `PUT /api/v2/reports/:name/schedule`; the DB schedule
|
|
154
|
+
(`ScriptSchedule`) is the single source of truth.
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
const { IntegrationsReport } = require('./reports/integrations-report');
|
|
2
|
+
|
|
3
|
+
// Registered by the admin-scripts bootstrap when the app definition sets `admin.includeBuiltinReports`.
|
|
4
|
+
const BUILTIN_REPORTS = [IntegrationsReport];
|
|
5
|
+
|
|
6
|
+
module.exports = { BUILTIN_REPORTS };
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
const { ReportBase } = require('./report-base');
|
|
2
|
+
const { IntegrationsReport } = require('./reports/integrations-report');
|
|
3
|
+
const { BUILTIN_REPORTS } = require('./builtin-reports');
|
|
4
|
+
const {
|
|
5
|
+
createReportCommands,
|
|
6
|
+
} = require('../application/commands/report-commands');
|
|
7
|
+
|
|
8
|
+
module.exports = {
|
|
9
|
+
ReportBase,
|
|
10
|
+
IntegrationsReport,
|
|
11
|
+
BUILTIN_REPORTS,
|
|
12
|
+
createReportCommands,
|
|
13
|
+
};
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* ReportBase — a report is an admin operation whose output is its payload.
|
|
3
|
+
*
|
|
4
|
+
* A report Definition declares:
|
|
5
|
+
* - runModes: allowed modes; the first is the default.
|
|
6
|
+
* 'live' — compute and return inline; persist nothing.
|
|
7
|
+
* 'recorded' — persist an execution record (input + results + logs).
|
|
8
|
+
* 'snapshot' — a recorded run tagged as a point in a named series.
|
|
9
|
+
* - output.format: 'json' returns inline; 'csv'|'pdf'|'zip' go to artifact storage.
|
|
10
|
+
* - schedule: optional recurring run via EventBridge.
|
|
11
|
+
*
|
|
12
|
+
* execute(frigg, params): `frigg` is the admin command bundle
|
|
13
|
+
* (=== this.context.commands); do data reads through it, never a repository.
|
|
14
|
+
*/
|
|
15
|
+
class ReportBase {
|
|
16
|
+
static Definition = {
|
|
17
|
+
name: 'Report Name',
|
|
18
|
+
version: '0.0.0',
|
|
19
|
+
description: 'What this report computes',
|
|
20
|
+
source: 'USER_DEFINED', // 'BUILTIN' | 'USER_DEFINED'
|
|
21
|
+
|
|
22
|
+
runModes: ['live', 'recorded', 'snapshot'],
|
|
23
|
+
inputSchema: null,
|
|
24
|
+
outputSchema: null,
|
|
25
|
+
output: { format: 'json' },
|
|
26
|
+
schedule: { enabled: false, cron: null, mode: 'snapshot' },
|
|
27
|
+
|
|
28
|
+
config: {
|
|
29
|
+
timeout: 300000,
|
|
30
|
+
},
|
|
31
|
+
|
|
32
|
+
display: {
|
|
33
|
+
category: 'reporting',
|
|
34
|
+
icon: null,
|
|
35
|
+
},
|
|
36
|
+
};
|
|
37
|
+
|
|
38
|
+
constructor(params = {}) {
|
|
39
|
+
this.context = params.context || null;
|
|
40
|
+
this.executionId = params.executionId || null;
|
|
41
|
+
this.integrationFactory = params.integrationFactory || null;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
async execute(frigg, params) {
|
|
45
|
+
throw new Error('ReportBase.execute() must be implemented by subclass');
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
module.exports = { ReportBase };
|
|
@@ -0,0 +1,221 @@
|
|
|
1
|
+
const { ReportBase } = require('../report-base');
|
|
2
|
+
const { CANONICAL_COUNTERS } = require('../../telemetry/canonical-counters');
|
|
3
|
+
const { loadAppDefinition } = require('../../handlers/app-definition-loader');
|
|
4
|
+
|
|
5
|
+
const SCHEMA_VERSION = 1;
|
|
6
|
+
const SERVICE = 'frigg-core-api';
|
|
7
|
+
|
|
8
|
+
// Seeded so every known status appears in output even at count 0.
|
|
9
|
+
const KNOWN_STATUSES = [
|
|
10
|
+
'IN_CREATION',
|
|
11
|
+
'ENABLED',
|
|
12
|
+
'ERROR',
|
|
13
|
+
'NEEDS_CONFIG',
|
|
14
|
+
'PROCESSING',
|
|
15
|
+
'IN_DELETION',
|
|
16
|
+
'DISABLED',
|
|
17
|
+
];
|
|
18
|
+
|
|
19
|
+
// IntegrationBase.Definition default — skip it so the slug is used instead.
|
|
20
|
+
const PLACEHOLDER_DISPLAY_NAME = 'Integration Name';
|
|
21
|
+
|
|
22
|
+
class IntegrationsReport extends ReportBase {
|
|
23
|
+
static Definition = {
|
|
24
|
+
name: 'integrations',
|
|
25
|
+
version: '1.0.0',
|
|
26
|
+
description: 'Integrations by status and type, with per-type usage counts',
|
|
27
|
+
source: 'BUILTIN',
|
|
28
|
+
runModes: ['live', 'recorded', 'snapshot'],
|
|
29
|
+
inputSchema: {
|
|
30
|
+
type: 'object',
|
|
31
|
+
additionalProperties: false,
|
|
32
|
+
properties: {
|
|
33
|
+
status: { type: 'string' },
|
|
34
|
+
type: { type: 'string' },
|
|
35
|
+
userId: { type: 'string' },
|
|
36
|
+
},
|
|
37
|
+
},
|
|
38
|
+
output: { format: 'json' },
|
|
39
|
+
schedule: { enabled: false, cron: null, mode: 'snapshot' },
|
|
40
|
+
display: { category: 'reporting', icon: null },
|
|
41
|
+
};
|
|
42
|
+
|
|
43
|
+
async execute(frigg, params = {}) {
|
|
44
|
+
const { status, type, userId } = this._validateQuery(params);
|
|
45
|
+
const typeLabels = buildTypeLabels();
|
|
46
|
+
|
|
47
|
+
const rows = await frigg.integrations.listForReport({ status, userId });
|
|
48
|
+
|
|
49
|
+
// type lives in config.type, a JSON path not portably groupable across DBs, so filter here not in the query.
|
|
50
|
+
const filtered =
|
|
51
|
+
type === undefined
|
|
52
|
+
? rows
|
|
53
|
+
: rows.filter((row) => (row.type ?? 'unknown') === type);
|
|
54
|
+
|
|
55
|
+
const ids = filtered.map((row) => row.id);
|
|
56
|
+
const mappingCounts = ids.length
|
|
57
|
+
? await frigg.integrationMappings.countByIntegrationIds(ids)
|
|
58
|
+
: new Map();
|
|
59
|
+
|
|
60
|
+
const integrations = filtered.map((row) => ({
|
|
61
|
+
id: row.id,
|
|
62
|
+
type: row.type ?? 'unknown',
|
|
63
|
+
status: row.status ?? null,
|
|
64
|
+
userId: row.userId ?? null,
|
|
65
|
+
version: row.version ?? null,
|
|
66
|
+
moduleCount: row.moduleCount ?? 0,
|
|
67
|
+
errorCount: row.errorCount ?? 0,
|
|
68
|
+
mappedRecordCount: mappingCounts.get(row.id) ?? 0,
|
|
69
|
+
createdAt: toIso(row.createdAt),
|
|
70
|
+
updatedAt: toIso(row.updatedAt),
|
|
71
|
+
}));
|
|
72
|
+
|
|
73
|
+
const byStatus = emptyStatusCounts();
|
|
74
|
+
const byTypeMap = new Map();
|
|
75
|
+
for (const integration of integrations) {
|
|
76
|
+
// sentinel so a missing status never becomes a literal "null" key
|
|
77
|
+
const statusKey = integration.status ?? 'UNKNOWN';
|
|
78
|
+
byStatus[statusKey] = (byStatus[statusKey] ?? 0) + 1;
|
|
79
|
+
|
|
80
|
+
if (!byTypeMap.has(integration.type)) {
|
|
81
|
+
byTypeMap.set(integration.type, {
|
|
82
|
+
type: integration.type,
|
|
83
|
+
label: typeLabels[integration.type] || integration.type,
|
|
84
|
+
total: 0,
|
|
85
|
+
byStatus: emptyStatusCounts(),
|
|
86
|
+
});
|
|
87
|
+
}
|
|
88
|
+
const bucket = byTypeMap.get(integration.type);
|
|
89
|
+
bucket.total += 1;
|
|
90
|
+
bucket.byStatus[statusKey] = (bucket.byStatus[statusKey] ?? 0) + 1;
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
await this._attachUsageColumns(byTypeMap, frigg);
|
|
94
|
+
|
|
95
|
+
return {
|
|
96
|
+
schemaVersion: SCHEMA_VERSION,
|
|
97
|
+
service: SERVICE,
|
|
98
|
+
generatedAt: new Date().toISOString(),
|
|
99
|
+
filters: {
|
|
100
|
+
status: status ?? null,
|
|
101
|
+
type: type ?? null,
|
|
102
|
+
userId: userId ?? null,
|
|
103
|
+
},
|
|
104
|
+
metrics: {
|
|
105
|
+
total: integrations.length,
|
|
106
|
+
byStatus,
|
|
107
|
+
byType: Array.from(byTypeMap.values()),
|
|
108
|
+
typeLabels: { ...typeLabels },
|
|
109
|
+
integrations,
|
|
110
|
+
},
|
|
111
|
+
};
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
async _attachUsageColumns(byTypeMap, frigg) {
|
|
115
|
+
const metrics = Object.keys(CANONICAL_COUNTERS);
|
|
116
|
+
try {
|
|
117
|
+
const totalsByMetric = await Promise.all(
|
|
118
|
+
metrics.map(async (metric) => {
|
|
119
|
+
const totals = await frigg.usage.getTotalsByDimension({
|
|
120
|
+
metric,
|
|
121
|
+
groupBy: 'integrationType',
|
|
122
|
+
});
|
|
123
|
+
const map = new Map(
|
|
124
|
+
(totals || []).map((t) => [t.integrationType, t.value])
|
|
125
|
+
);
|
|
126
|
+
return [metric, map];
|
|
127
|
+
})
|
|
128
|
+
);
|
|
129
|
+
|
|
130
|
+
for (const bucket of byTypeMap.values()) {
|
|
131
|
+
bucket.usage = {};
|
|
132
|
+
for (const [metric, map] of totalsByMetric) {
|
|
133
|
+
bucket.usage[metric] = map.get(bucket.type) ?? 0;
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
} catch (error) {
|
|
137
|
+
console.warn(
|
|
138
|
+
`[Frigg][reporting] usage columns unavailable: ${
|
|
139
|
+
error && error.message
|
|
140
|
+
}`
|
|
141
|
+
);
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
_validateQuery({ status, type, userId } = {}) {
|
|
146
|
+
for (const [key, value] of Object.entries({ status, type, userId })) {
|
|
147
|
+
if (
|
|
148
|
+
value !== undefined &&
|
|
149
|
+
value !== null &&
|
|
150
|
+
typeof value !== 'string'
|
|
151
|
+
) {
|
|
152
|
+
throw invalidInput(
|
|
153
|
+
`Invalid query parameter '${key}': expected a string`
|
|
154
|
+
);
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
const normalize = (value) => value || undefined;
|
|
158
|
+
const normalized = {
|
|
159
|
+
status: normalize(status),
|
|
160
|
+
type: normalize(type),
|
|
161
|
+
userId: normalize(userId),
|
|
162
|
+
};
|
|
163
|
+
if (normalized.status && !KNOWN_STATUSES.includes(normalized.status)) {
|
|
164
|
+
throw invalidInput(
|
|
165
|
+
`Invalid status '${
|
|
166
|
+
normalized.status
|
|
167
|
+
}'. Expected one of: ${KNOWN_STATUSES.join(', ')}`
|
|
168
|
+
);
|
|
169
|
+
}
|
|
170
|
+
return normalized;
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
// INVALID_INPUT keeps the report protocol-agnostic; the runner/router map it to a 400.
|
|
175
|
+
function invalidInput(message) {
|
|
176
|
+
const error = new Error(message);
|
|
177
|
+
error.code = 'INVALID_INPUT';
|
|
178
|
+
return error;
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
function emptyStatusCounts() {
|
|
182
|
+
return KNOWN_STATUSES.reduce((acc, status) => {
|
|
183
|
+
acc[status] = 0;
|
|
184
|
+
return acc;
|
|
185
|
+
}, {});
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
function toIso(value) {
|
|
189
|
+
if (!value) return null;
|
|
190
|
+
if (value instanceof Date) return value.toISOString();
|
|
191
|
+
// DocumentDB raw reads surface dates as { $date: ... }
|
|
192
|
+
if (typeof value === 'object' && value.$date) {
|
|
193
|
+
const date = new Date(value.$date);
|
|
194
|
+
return Number.isNaN(date.getTime()) ? null : date.toISOString();
|
|
195
|
+
}
|
|
196
|
+
return String(value);
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
function buildTypeLabels() {
|
|
200
|
+
try {
|
|
201
|
+
const { integrations = [] } = loadAppDefinition();
|
|
202
|
+
const labels = {};
|
|
203
|
+
for (const IntegrationClass of integrations) {
|
|
204
|
+
const def = IntegrationClass?.Definition;
|
|
205
|
+
if (!def?.name) continue;
|
|
206
|
+
const label = def.display?.label;
|
|
207
|
+
if (label && label !== PLACEHOLDER_DISPLAY_NAME) {
|
|
208
|
+
labels[def.name] = label;
|
|
209
|
+
}
|
|
210
|
+
}
|
|
211
|
+
return labels;
|
|
212
|
+
} catch (error) {
|
|
213
|
+
console.error(
|
|
214
|
+
'Reporting: failed to load integration labels:',
|
|
215
|
+
error.message
|
|
216
|
+
);
|
|
217
|
+
return {};
|
|
218
|
+
}
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
module.exports = { IntegrationsReport, SCHEMA_VERSION };
|