@friggframework/admin-scripts 2.0.0--canary.545.b4ca16d.0 → 2.0.0--canary.517.ff03f2c.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (45) hide show
  1. package/README.md +272 -0
  2. package/index.js +22 -19
  3. package/package.json +6 -6
  4. package/src/adapters/__tests__/aws-scheduler-adapter.test.js +106 -45
  5. package/src/adapters/__tests__/local-scheduler-adapter.test.js +24 -10
  6. package/src/adapters/__tests__/scheduler-adapter-factory.test.js +30 -9
  7. package/src/adapters/__tests__/scheduler-adapter.test.js +6 -2
  8. package/src/adapters/aws-scheduler-adapter.js +55 -28
  9. package/src/adapters/local-scheduler-adapter.js +2 -2
  10. package/src/adapters/scheduler-adapter-factory.js +3 -1
  11. package/src/adapters/scheduler-adapter.js +9 -3
  12. package/src/application/__tests__/admin-frigg-commands.test.js +73 -30
  13. package/src/application/__tests__/admin-script-base.test.js +23 -6
  14. package/src/application/__tests__/script-factory.test.js +30 -6
  15. package/src/application/__tests__/script-runner.test.js +113 -24
  16. package/src/application/__tests__/validate-script-input.test.js +54 -15
  17. package/src/application/admin-frigg-commands.js +21 -9
  18. package/src/application/admin-script-base.js +3 -2
  19. package/src/application/script-factory.js +3 -1
  20. package/src/application/script-runner.js +90 -48
  21. package/src/application/use-cases/__tests__/delete-schedule-use-case.test.js +16 -7
  22. package/src/application/use-cases/__tests__/get-effective-schedule-use-case.test.js +9 -4
  23. package/src/application/use-cases/__tests__/upsert-schedule-use-case.test.js +21 -10
  24. package/src/application/use-cases/delete-schedule-use-case.js +6 -4
  25. package/src/application/use-cases/get-effective-schedule-use-case.js +3 -1
  26. package/src/application/use-cases/index.js +3 -1
  27. package/src/application/use-cases/upsert-schedule-use-case.js +30 -11
  28. package/src/application/validate-script-input.js +10 -3
  29. package/src/infrastructure/__tests__/admin-auth-middleware.test.js +9 -3
  30. package/src/infrastructure/__tests__/admin-script-router.test.js +125 -52
  31. package/src/infrastructure/admin-auth-middleware.js +3 -1
  32. package/src/infrastructure/admin-script-router.js +129 -14
  33. package/src/infrastructure/bootstrap.js +82 -0
  34. package/src/infrastructure/script-executor-handler.js +119 -52
  35. package/src/application/__tests__/dry-run-http-interceptor.test.js +0 -313
  36. package/src/application/__tests__/dry-run-repository-wrapper.test.js +0 -257
  37. package/src/application/__tests__/schedule-management-use-case.test.js +0 -276
  38. package/src/application/dry-run-http-interceptor.js +0 -296
  39. package/src/application/dry-run-repository-wrapper.js +0 -261
  40. package/src/application/schedule-management-use-case.js +0 -230
  41. package/src/builtins/__tests__/integration-health-check.test.js +0 -607
  42. package/src/builtins/__tests__/oauth-token-refresh.test.js +0 -354
  43. package/src/builtins/index.js +0 -28
  44. package/src/builtins/integration-health-check.js +0 -278
  45. package/src/builtins/oauth-token-refresh.js +0 -220
@@ -1,220 +0,0 @@
1
- const { AdminScriptBase } = require('../application/admin-script-base');
2
-
3
- /**
4
- * OAuth Token Refresh Script
5
- *
6
- * Refreshes OAuth tokens for integrations that are near expiry.
7
- * This helps prevent authentication failures due to expired tokens.
8
- */
9
- class OAuthTokenRefreshScript extends AdminScriptBase {
10
- static Definition = {
11
- name: 'oauth-token-refresh',
12
- version: '1.0.0',
13
- description: 'Refreshes OAuth tokens for integrations near expiry',
14
- source: 'BUILTIN',
15
-
16
- inputSchema: {
17
- type: 'object',
18
- properties: {
19
- integrationIds: {
20
- type: 'array',
21
- items: { type: 'string' },
22
- description: 'Specific integration IDs to refresh (optional, defaults to all)'
23
- },
24
- expiryThresholdHours: {
25
- type: 'number',
26
- default: 24,
27
- description: 'Refresh tokens expiring within this many hours'
28
- },
29
- dryRun: {
30
- type: 'boolean',
31
- default: false,
32
- description: 'Preview without making changes'
33
- }
34
- }
35
- },
36
-
37
- outputSchema: {
38
- type: 'object',
39
- properties: {
40
- refreshed: { type: 'number' },
41
- failed: { type: 'number' },
42
- skipped: { type: 'number' },
43
- details: { type: 'array' }
44
- }
45
- },
46
-
47
- config: {
48
- timeout: 600000, // 10 minutes
49
- maxRetries: 1,
50
- requireIntegrationInstance: true, // Needs to call external APIs
51
- },
52
-
53
- // UI-specific overrides
54
- display: {
55
- category: 'maintenance',
56
- },
57
- };
58
-
59
- async execute(params = {}) {
60
- const {
61
- integrationIds = null,
62
- expiryThresholdHours = 24,
63
- dryRun = false
64
- } = params;
65
-
66
- const results = {
67
- refreshed: 0,
68
- failed: 0,
69
- skipped: 0,
70
- details: []
71
- };
72
-
73
- this.context.log('info', 'Starting OAuth token refresh', {
74
- expiryThresholdHours,
75
- dryRun,
76
- specificIds: integrationIds?.length || 'all'
77
- });
78
-
79
- // Get integrations to check
80
- let integrations;
81
- if (integrationIds && integrationIds.length > 0) {
82
- integrations = await Promise.all(
83
- integrationIds.map(id => this.context.integrationRepository.findIntegrationById(id).catch(() => null))
84
- );
85
- integrations = integrations.filter(Boolean);
86
- } else {
87
- // Get all integrations (this would need to be paginated for large deployments)
88
- integrations = await this.getAllIntegrations();
89
- }
90
-
91
- this.context.log('info', `Found ${integrations.length} integrations to check`);
92
-
93
- for (const integration of integrations) {
94
- try {
95
- const detail = await this.processIntegration(integration, {
96
- expiryThresholdHours,
97
- dryRun
98
- });
99
-
100
- results.details.push(detail);
101
-
102
- if (detail.action === 'refreshed') {
103
- results.refreshed++;
104
- } else if (detail.action === 'skipped') {
105
- results.skipped++;
106
- } else if (detail.action === 'failed') {
107
- results.failed++;
108
- }
109
- } catch (error) {
110
- this.context.log('error', `Error processing integration ${integration.id}`, {
111
- error: error.message
112
- });
113
- results.failed++;
114
- results.details.push({
115
- integrationId: integration.id,
116
- action: 'failed',
117
- reason: error.message
118
- });
119
- }
120
- }
121
-
122
- this.context.log('info', 'OAuth token refresh completed', {
123
- refreshed: results.refreshed,
124
- failed: results.failed,
125
- skipped: results.skipped
126
- });
127
-
128
- return results;
129
- }
130
-
131
- async getAllIntegrations() {
132
- // This is a simplified implementation
133
- // In production, would need pagination for large datasets
134
- return this.context.integrationRepository.findIntegrations({});
135
- }
136
-
137
- async processIntegration(integration, options) {
138
- const { expiryThresholdHours, dryRun } = options;
139
-
140
- // Check prerequisites
141
- const skipReason = this._checkRefreshPrerequisites(integration, expiryThresholdHours);
142
- if (skipReason) {
143
- return this._createResult(integration.id, 'skipped', skipReason);
144
- }
145
-
146
- // Handle dry run
147
- if (dryRun) {
148
- this.context.log('info', `[DRY RUN] Would refresh token for ${integration.id}`);
149
- return this._createResult(integration.id, 'skipped', 'Dry run - would have refreshed');
150
- }
151
-
152
- // Perform refresh
153
- return this._performTokenRefresh(integration);
154
- }
155
-
156
- /**
157
- * Check if integration meets prerequisites for token refresh
158
- * @private
159
- * @returns {string|null} Skip reason or null if eligible
160
- */
161
- _checkRefreshPrerequisites(integration, expiryThresholdHours) {
162
- if (!integration.config?.credentials?.access_token) {
163
- return 'No OAuth credentials found';
164
- }
165
-
166
- const expiresAt = integration.config?.credentials?.expires_at;
167
- if (!expiresAt) {
168
- return 'No expiry time found';
169
- }
170
-
171
- const expiryTime = new Date(expiresAt);
172
- const thresholdTime = new Date(Date.now() + (expiryThresholdHours * 60 * 60 * 1000));
173
-
174
- if (expiryTime > thresholdTime) {
175
- return 'Token not near expiry';
176
- }
177
-
178
- return null;
179
- }
180
-
181
- /**
182
- * Perform the actual token refresh
183
- * @private
184
- */
185
- async _performTokenRefresh(integration) {
186
- const expiresAt = integration.config?.credentials?.expires_at;
187
-
188
- try {
189
- const instance = await this.context.instantiate(integration.id);
190
-
191
- if (!instance.primary?.api?.refreshAccessToken) {
192
- return this._createResult(integration.id, 'skipped', 'API does not support token refresh');
193
- }
194
-
195
- await instance.primary.api.refreshAccessToken();
196
- this.context.log('info', `Refreshed token for integration ${integration.id}`);
197
-
198
- return {
199
- integrationId: integration.id,
200
- action: 'refreshed',
201
- previousExpiry: expiresAt
202
- };
203
- } catch (error) {
204
- this.context.log('error', `Failed to refresh token for ${integration.id}`, {
205
- error: error.message
206
- });
207
- return this._createResult(integration.id, 'failed', error.message);
208
- }
209
- }
210
-
211
- /**
212
- * Create a result object
213
- * @private
214
- */
215
- _createResult(integrationId, action, reason) {
216
- return { integrationId, action, reason };
217
- }
218
- }
219
-
220
- module.exports = { OAuthTokenRefreshScript };