Skip to content

Conversation

@seanspeaks
Copy link
Contributor

@seanspeaks seanspeaks commented Dec 10, 2025

Summary

Adds Admin Script Runner service - enables Frigg adopters to write and execute scripts in hosted environments with VPC/KMS secured database access.

Phase 1: MVP

  • Prisma schemas for AdminApiKey, ScriptExecution (MongoDB + PostgreSQL)
  • Repository implementations with factory pattern (MongoDB, PostgreSQL, DocumentDB)
  • createAdminScriptCommands() command factory
  • AdminScriptBase class and ScriptFactory for script registration
  • AdminFriggCommands helper API for scripts (db access, queue, logging)
  • Admin router with sync/async execution endpoints
  • SQS worker Lambda handler
  • AdminScriptBuilder for infrastructure generation
  • Built-in scripts: oauth-token-refresh, integration-health-check

Phase 2: Hybrid Scheduling

  • ScriptSchedule Prisma model
  • SchedulerAdapter port interface (hexagonal)
  • AWSSchedulerAdapter (EventBridge Scheduler)
  • LocalSchedulerAdapter (dev/test)
  • Schedule management API endpoints (GET/PUT/DELETE)
  • Hybrid logic: DB override > Definition default

Phase 3: Dry-Run Mode

  • DryRunRepositoryWrapper - intercepts DB writes, logs operations
  • DryRunHttpInterceptor - mocks HTTP calls, detects 20+ services
  • Automatic sensitive data sanitization
  • POST /execute { dryRun: true } support

Test Plan

  • 424 unit tests passing
  • Manual test with sample script
  • Deploy to dev environment
📦 Published PR as canary version: 2.0.0--canary.517.21b69ac.0

✨ Test out this PR locally via:

npm install @friggframework/admin-scripts@2.0.0--canary.517.21b69ac.0
npm install @friggframework/core@2.0.0--canary.517.21b69ac.0
npm install @friggframework/devtools@2.0.0--canary.517.21b69ac.0
npm install @friggframework/eslint-config@2.0.0--canary.517.21b69ac.0
npm install @friggframework/prettier-config@2.0.0--canary.517.21b69ac.0
npm install @friggframework/schemas@2.0.0--canary.517.21b69ac.0
npm install @friggframework/serverless-plugin@2.0.0--canary.517.21b69ac.0
npm install @friggframework/test@2.0.0--canary.517.21b69ac.0
npm install @friggframework/ui@2.0.0--canary.517.21b69ac.0
# or 
yarn add @friggframework/admin-scripts@2.0.0--canary.517.21b69ac.0
yarn add @friggframework/core@2.0.0--canary.517.21b69ac.0
yarn add @friggframework/devtools@2.0.0--canary.517.21b69ac.0
yarn add @friggframework/eslint-config@2.0.0--canary.517.21b69ac.0
yarn add @friggframework/prettier-config@2.0.0--canary.517.21b69ac.0
yarn add @friggframework/schemas@2.0.0--canary.517.21b69ac.0
yarn add @friggframework/serverless-plugin@2.0.0--canary.517.21b69ac.0
yarn add @friggframework/test@2.0.0--canary.517.21b69ac.0
yarn add @friggframework/ui@2.0.0--canary.517.21b69ac.0

Add comprehensive plan for Admin Script Runner service aligned with
next branch architecture (command pattern, repository factories,
Prisma schemas, multi-database support).

Includes initial package structure for @friggframework/admin-scripts.
- Add AdminScriptBase.Definition following IntegrationBase pattern
  (ref: packages/core/integrations/integration-base.js:57-69)
- Add appDefinition.adminScripts schema update
  (ref: packages/devtools/infrastructure/domains/shared/types/app-definition.js)
- Make integrationFactory OPTIONAL for scripts that only need DB access
- Add AdminFriggCommands with repository pattern matching existing commands
- Include exact file references throughout plan
- ADR-5: Execution modes - sync (optional) vs async (default)
- ADR-6: Hybrid scheduling (Definition defaults + DB/API overrides)
- ADR-7: DDD/hexagonal architecture layers documented
- ADR-8: SchedulerAdapter port with AWS/local implementations
- ADR-9: AdminScriptBuilder following infrastructure-composer pattern
- Add ScriptSchedule Prisma model for hybrid scheduling
- Document deployment flow and generated serverless resources
- Update package structure with adapters directory
- Update Files to Create/Modify section with all new files
- ADR-10: Dry run via repository wrapper + HTTP interceptor
  - Intercepts DB writes, logs operations, returns unchanged data
  - Intercepts external API calls via mock axios instance
  - Script code unchanged between normal and dry-run modes
- ADR-11: Self-queuing for long-running scripts
  - Scripts chunk work and re-queue via frigg.queueScript()
  - Tracks parentExecutionId for lineage
  - No Step Functions complexity
- Remove VM sandbox (not needed for trusted adopter scripts)
- Update package structure with dry-run files
- Replace raw SQS code with QueuerUtil.send() and batchSend()
- Reference: packages/core/queues/queuer-util.js
- Added queueScriptBatch() for bulk operations
Phase 1 implementation of Admin Script Runner service:

Repository Layer:
- AdminApiKey repositories (MongoDB, PostgreSQL, DocumentDB)
- ScriptExecution repositories (MongoDB, PostgreSQL, DocumentDB)
- Factory pattern for database-agnostic creation
- 70 unit tests passing

Application Layer:
- createAdminScriptCommands() factory with:
  - API key management (create, validate, list, deactivate)
  - Execution lifecycle (create, update, complete)
  - bcrypt hashing for key security
- Error mapping to HTTP status codes

Infrastructure Layer:
- AdminScriptBuilder wired into infrastructure-composer
- Generates SQS queue, Lambda functions, EventBridge scheduler
- 33 unit tests passing

Prisma Schema:
- AdminApiKey model with scopes and expiration
- ScriptExecution model with status, logs, metrics
Application Layer:
- AdminScriptBase: Base class for all admin scripts with Definition pattern
- ScriptFactory: Registry for script registration and instantiation
- AdminFriggCommands: Helper API for scripts (db access, queue, logging)
- ScriptRunner: Orchestrates script execution with error handling

Infrastructure Layer:
- admin-auth-middleware: Bearer token authentication for admin API keys
- admin-script-router: Express router with 5 endpoints for script management
- script-executor-handler: SQS worker Lambda for async execution

Features:
- Sync and async execution modes
- Self-queuing pattern via QueuerUtil for long-running scripts
- Audit trail (API key, IP address)
- Automatic log persistence to execution records

Test Coverage: 110 tests passing
…h check

Built-in Scripts:
- OAuthTokenRefreshScript: Refreshes OAuth tokens near expiry
  - Configurable expiry threshold (default 24h)
  - Dry-run mode for safe testing
  - Filters by integration IDs or all

- IntegrationHealthCheckScript: Checks integration health
  - Validates credential presence and expiry
  - Tests API connectivity
  - Optionally updates integration status
  - Schedule-ready (daily cron expression)

Both scripts:
- Extend AdminScriptBase with Definition pattern
- Use AdminFriggCommands for database/API access
- Include JSON Schema for input/output validation
- Comprehensive error handling and logging

Test Coverage: 41 tests passing for built-in scripts
Fixed a bug where the logs array from Prisma was being mutated
directly instead of creating a copy first. This caused test failures
when the original array reference was used for comparison.
Phase 2 - Hybrid Scheduling:
- ScriptSchedule Prisma model (MongoDB + PostgreSQL)
- ScriptSchedule repository implementations with factory
- SchedulerAdapter port interface (hexagonal pattern)
- AWSSchedulerAdapter for EventBridge Scheduler
- LocalSchedulerAdapter for dev/test
- Schedule management API endpoints:
  - GET /scripts/:name/schedule (DB override > Definition default)
  - PUT /scripts/:name/schedule (create/update override)
  - DELETE /scripts/:name/schedule (revert to default)
- Schedule commands in admin-script-commands.js

Phase 3 - Dry-Run Mode:
- DryRunRepositoryWrapper: Proxy-based write interception
- DryRunHttpInterceptor: Mock HTTP client with service detection
- Automatic sanitization of sensitive data in logs
- Returns preview with operation log and summary
- POST /execute { dryRun: true } support

Features:
- 20+ external service detection (HubSpot, Salesforce, Slack, etc.)
- Smart read vs write operation detection
- Timezone-aware scheduling
- AWS EventBridge rule tracking (ruleArn, ruleName)

Test Coverage: 424 tests passing (141 new tests added)
@seanspeaks seanspeaks added the release Create a release when this pr is merged label Dec 10, 2025
- Remove commented-out domain model placeholders (not needed with repository pattern)
- Remove commented-out factory function placeholder
- Clarify EventBridge Scheduler integration comments as optional enhancement
- Integrate createSchedulerAdapter into PUT /schedule endpoint
- Provision EventBridge rule when schedule is enabled with cron expression
- Delete EventBridge rule when schedule is disabled or deleted
- Store AWS rule ARN/name in database for tracking
- Handle scheduler errors gracefully (non-fatal, with warning in response)
- Add 6 new tests for scheduler integration
Align naming with AWS EventBridge Scheduler terminology:
- awsRuleArn → awsScheduleArn
- awsRuleName → awsScheduleName
- updateScheduleAwsRule → updateScheduleAwsInfo
- ruleArn → scheduleArn (adapter return values)
- ruleName → scheduleName (adapter return values)

This reflects that we use EventBridge Scheduler (the newer service),
not EventBridge Rules (the older approach).
Document the design decisions for the Admin Script Runner:
- Entry point via appDefinition.adminScripts
- Script base class pattern following IntegrationBase
- Infrastructure components (builder, repositories, handlers)
- Execution modes (sync/async)
- Hybrid scheduling with EventBridge Scheduler
- Dry-run mode for safe testing
- Security model with admin API keys

Also update README to include ADR-004 and ADR-005.
The admin-script-router requires express and serverless-http but they
were not declared in package.json, causing the release to fail.
The @unique constraint on keyHash already creates an index, so the
explicit @@index([keyHash]) was causing a "Index already exists" error.
Replace global parseInt/isNaN with Number.parseInt/Number.isNaN
to follow JavaScript best practices and pass SonarCloud analysis.
- Extract ScheduleManagementUseCase from admin-script-router
  - Encapsulates schedule business logic (get/upsert/delete)
  - Handles EventBridge sync with graceful error handling
  - 14 unit tests with full coverage

- Refactor oauth-token-refresh.js
  - Extract _checkRefreshPrerequisites() for token validation
  - Extract _performTokenRefresh() for actual refresh logic
  - Extract _createResult() helper for consistent response format

- Refactor integration-health-check.js
  - Extract _createCheckResult() for initial result structure
  - Extract _runChecks() to orchestrate all checks
  - Extract _addCheckResult() to track issues
  - Extract _determineOverallStatus() for status logic
  - Extract _handleCheckError() for error handling

All 297 tests passing.
- Add id-token: write permission for OIDC authentication
- Add contents: write permission for release commits
- Upgrade npm to latest for trusted publishing support (requires 11.5.1+)
- Remove NPM_TOKEN/NODE_AUTH_TOKEN (OIDC replaces token auth)

Requires configuring trusted publishers on npmjs.com for each package.
Keep both OIDC permissions and NPM_TOKEN for flexibility:
- Packages with trusted publisher configured → use OIDC
- New packages (e.g., admin-scripts) → fall back to token

This allows gradual migration to OIDC while supporting new package publishes.
@seanspeaks seanspeaks marked this pull request as ready for review December 11, 2025 06:06
"bcryptjs": "^2.4.3",
"express": "^4.18.2",
"lodash": "4.17.21",
"mongoose": "6.11.6",
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

why mongoose?

"jest": "^29.7.0",
"prettier": "^2.7.1",
"sinon": "^16.1.1",
"supertest": "^7.1.4"
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We already use nock for http request mocking.

"@friggframework/eslint-config": "^2.0.0-next.0",
"@friggframework/prettier-config": "^2.0.0-next.0",
"@friggframework/test": "^2.0.0-next.0",
"chai": "^4.3.6",
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

chai and sinon should slowly be pushed away in favor or newer test libs like vitest

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I still don't understand why we can not use the user table from the database

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can't we call it just script-repository.js?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

can't the schedule information be included in the current script-execution (hopefully just script) table?

console.log(`\n[${this.name}] Configuring admin scripts...`);
console.log(` Processing ${appDefinition.adminScripts.length} scripts...`);

const usePrismaLayer = appDefinition.usePrismaLambdaLayer !== false;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

why is prisma layer important here?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this a composer class?

claude and others added 4 commits December 15, 2025 03:06
Phase 1 of admin refactoring based on Daniel's PR review:

1. Auth simplification (ENV-based like db-migrate):
   - Add shared validateAdminApiKey middleware in core/handlers/middleware
   - Delete AdminApiKey model, repositories, and tests
   - Remove API key commands from admin-script-commands.js

2. Schema changes:
   - Replace AdminApiKey + ScriptExecution with AdminProcess model
   - AdminProcess mirrors Process but without user/integration FK
   - Supports hierarchy (parentProcessId, childProcesses)
   - Used for: admin scripts, db migrations, system tasks

3. Files deleted:
   - admin-api-key-repository-*.js (all variants)
   - admin-api-key tests

Next steps: Create AdminProcess repository, refactor routes to
/admin/scripts/:name convention, unify db-migrate under /admin.
…r /admin

Major refactoring based on PR feedback:

1. AdminProcess Repository (replaces ScriptExecution):
   - New: admin-process-repository-interface.js
   - New: admin-process-repository-mongo.js
   - New: admin-process-repository-postgres.js
   - New: admin-process-repository-documentdb.js
   - New: admin-process-repository-factory.js
   - Deleted: All script-execution-repository-* files

2. Updated admin-scripts package:
   - All files now use AdminProcess methods
   - Updated: admin-script-base.js, admin-frigg-commands.js, script-runner.js
   - Updated: admin-script-router.js, script-executor-handler.js
   - Fixed export: validateAdminApiKey (not adminAuthMiddleware)

3. Moved db-migrate under /admin path:
   - Routes: /admin/db-migrate/*
   - Uses shared validateAdminApiKey middleware
   - Updated use-cases and tests

4. Cleaned up obsolete code:
   - Removed AdminApiKey tests from admin-script-commands.test.js
   - Updated all tests for AdminProcess methods

All 295 tests passing.
Address PR feedback from Daniel:

1. Simplified dry-run implementation:
   - Deleted over-engineered dry-run-repository-wrapper.js (262 lines)
   - Deleted over-engineered dry-run-http-interceptor.js (297 lines)
   - New dry-run validates inputs and returns preview without executing
   - Added JSON schema validation for script parameters
   - Net reduction: ~990 lines removed

2. Cleaned up package.json:
   - Removed unused mongoose dependency
   - Removed unused chai devDependency
   - Kept supertest for Express route testing (different from nock)

3. Documented admin-script-commands architecture:
   - Explained why separate from integration-commands
   - integration-commands: user-context operations (requires integrationClass)
   - admin-script-commands: system operations (no user context)
   - Separation follows SRP and avoids coupling

Tests: 262 passing
…Runner

Changes:
- Rename requiresIntegrationFactory to requireIntegrationInstance (PR feedback)
- Add JSDoc documentation for executionId parameter
- Split schedule-management-use-case.js into 3 separate use cases following SRP:
  - GetEffectiveScheduleUseCase
  - UpsertScheduleUseCase
  - DeleteScheduleUseCase
- Abstract AWS-specific naming to generic external scheduler terminology:
  - awsScheduleArn → externalScheduleId
  - awsScheduleName → externalScheduleName
  - updateScheduleAwsInfo → updateScheduleExternalInfo
- Remove sinon dependency, use Jest mocks instead
- Update Prisma schemas for both MongoDB and PostgreSQL
- Update ADR documentation

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
…ename context

- Rename AdminFriggCommands → AdminScriptContext (facade pattern)
- Refactor to constructor injection (context via constructor, not execute)
- Remove logging from AdminScriptBase (use context.log instead)
- Clean up display object - only UI-specific overrides
- Strip verbose JSDoc comments (keep code sparse)
- Update all tests for new API
- Add PR review tracker for remaining items

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
@sonarqubecloud
Copy link

Quality Gate Failed Quality Gate failed

Failed conditions
6 Security Hotspots
C Reliability Rating on New Code (required ≥ A)

See analysis details on SonarQube Cloud

Catch issues before they fail your Quality Gate with our IDE extension SonarQube for IDE

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

release Create a release when this pr is merged

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants