@friggframework/devtools 2.0.0-next.41 → 2.0.0-next.42

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 (32) hide show
  1. package/frigg-cli/__tests__/unit/commands/build.test.js +173 -405
  2. package/frigg-cli/__tests__/unit/commands/db-setup.test.js +548 -0
  3. package/frigg-cli/__tests__/unit/commands/install.test.js +359 -377
  4. package/frigg-cli/__tests__/unit/commands/ui.test.js +266 -512
  5. package/frigg-cli/__tests__/unit/utils/database-validator.test.js +366 -0
  6. package/frigg-cli/__tests__/unit/utils/error-messages.test.js +304 -0
  7. package/frigg-cli/__tests__/unit/utils/prisma-runner.test.js +486 -0
  8. package/frigg-cli/__tests__/utils/prisma-mock.js +194 -0
  9. package/frigg-cli/__tests__/utils/test-setup.js +22 -21
  10. package/frigg-cli/db-setup-command/index.js +186 -0
  11. package/frigg-cli/generate-command/__tests__/generate-command.test.js +151 -162
  12. package/frigg-cli/generate-iam-command.js +7 -4
  13. package/frigg-cli/index.js +9 -1
  14. package/frigg-cli/install-command/index.js +1 -1
  15. package/frigg-cli/jest.config.js +124 -0
  16. package/frigg-cli/package.json +4 -1
  17. package/frigg-cli/start-command/index.js +95 -2
  18. package/frigg-cli/start-command/start-command.test.js +161 -19
  19. package/frigg-cli/utils/database-validator.js +158 -0
  20. package/frigg-cli/utils/error-messages.js +257 -0
  21. package/frigg-cli/utils/prisma-runner.js +280 -0
  22. package/infrastructure/CLAUDE.md +481 -0
  23. package/infrastructure/IAM-POLICY-TEMPLATES.md +30 -12
  24. package/infrastructure/create-frigg-infrastructure.js +0 -2
  25. package/infrastructure/iam-generator.js +18 -38
  26. package/infrastructure/iam-generator.test.js +40 -8
  27. package/package.json +6 -6
  28. package/test/index.js +2 -4
  29. package/test/mock-integration.js +4 -14
  30. package/frigg-cli/__tests__/jest.config.js +0 -102
  31. package/frigg-cli/__tests__/utils/command-tester.js +0 -170
  32. package/test/auther-definition-tester.js +0 -125
@@ -51,7 +51,11 @@ describe('IAM Generator', () => {
51
51
  websockets: { enable: false }
52
52
  };
53
53
 
54
- const yaml = generateIAMCloudFormation(appDefinition);
54
+ const summary = getFeatureSummary(appDefinition);
55
+ const yaml = generateIAMCloudFormation({
56
+ appName: summary.appName,
57
+ features: summary.features
58
+ });
55
59
 
56
60
  expect(yaml).toContain('AWSTemplateFormatVersion');
57
61
  expect(yaml).toContain('FriggDeploymentUser');
@@ -66,7 +70,11 @@ describe('IAM Generator', () => {
66
70
  vpc: { enable: true }
67
71
  };
68
72
 
69
- const yaml = generateIAMCloudFormation(appDefinition);
73
+ const summary = getFeatureSummary(appDefinition);
74
+ const yaml = generateIAMCloudFormation({
75
+ appName: summary.appName,
76
+ features: summary.features
77
+ });
70
78
 
71
79
  expect(yaml).toContain('FriggVPCPolicy');
72
80
  expect(yaml).toContain('CreateVPCPermissions');
@@ -81,7 +89,11 @@ describe('IAM Generator', () => {
81
89
  encryption: { fieldLevelEncryptionMethod: 'kms' }
82
90
  };
83
91
 
84
- const yaml = generateIAMCloudFormation(appDefinition);
92
+ const summary = getFeatureSummary(appDefinition);
93
+ const yaml = generateIAMCloudFormation({
94
+ appName: summary.appName,
95
+ features: summary.features
96
+ });
85
97
 
86
98
  expect(yaml).toContain('FriggKMSPolicy');
87
99
  expect(yaml).toContain('CreateKMSPermissions');
@@ -97,7 +109,11 @@ describe('IAM Generator', () => {
97
109
  ssm: { enable: true }
98
110
  };
99
111
 
100
- const yaml = generateIAMCloudFormation(appDefinition);
112
+ const summary = getFeatureSummary(appDefinition);
113
+ const yaml = generateIAMCloudFormation({
114
+ appName: summary.appName,
115
+ features: summary.features
116
+ });
101
117
 
102
118
  expect(yaml).toContain('FriggSSMPolicy');
103
119
  expect(yaml).toContain('CreateSSMPermissions');
@@ -113,7 +129,11 @@ describe('IAM Generator', () => {
113
129
  ssm: { enable: true }
114
130
  };
115
131
 
116
- const yaml = generateIAMCloudFormation(appDefinition);
132
+ const summary = getFeatureSummary(appDefinition);
133
+ const yaml = generateIAMCloudFormation({
134
+ appName: summary.appName,
135
+ features: summary.features
136
+ });
117
137
 
118
138
  // Check parameter defaults match the enabled features
119
139
  expect(yaml).toContain("Default: 'true'"); // VPC enabled
@@ -127,7 +147,11 @@ describe('IAM Generator', () => {
127
147
  integrations: []
128
148
  };
129
149
 
130
- const yaml = generateIAMCloudFormation(appDefinition);
150
+ const summary = getFeatureSummary(appDefinition);
151
+ const yaml = generateIAMCloudFormation({
152
+ appName: summary.appName,
153
+ features: summary.features
154
+ });
131
155
 
132
156
  // Check for core permissions
133
157
  expect(yaml).toContain('cloudformation:CreateStack');
@@ -149,7 +173,11 @@ describe('IAM Generator', () => {
149
173
  integrations: []
150
174
  };
151
175
 
152
- const yaml = generateIAMCloudFormation(appDefinition);
176
+ const summary = getFeatureSummary(appDefinition);
177
+ const yaml = generateIAMCloudFormation({
178
+ appName: summary.appName,
179
+ features: summary.features
180
+ });
153
181
 
154
182
  expect(yaml).toContain('internal-error-queue-*');
155
183
  });
@@ -160,7 +188,11 @@ describe('IAM Generator', () => {
160
188
  integrations: []
161
189
  };
162
190
 
163
- const yaml = generateIAMCloudFormation(appDefinition);
191
+ const summary = getFeatureSummary(appDefinition);
192
+ const yaml = generateIAMCloudFormation({
193
+ appName: summary.appName,
194
+ features: summary.features
195
+ });
164
196
 
165
197
  expect(yaml).toContain('Outputs:');
166
198
  expect(yaml).toContain('DeploymentUserArn:');
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@friggframework/devtools",
3
3
  "prettier": "@friggframework/prettier-config",
4
- "version": "2.0.0-next.41",
4
+ "version": "2.0.0-next.42",
5
5
  "dependencies": {
6
6
  "@aws-sdk/client-ec2": "^3.835.0",
7
7
  "@aws-sdk/client-kms": "^3.835.0",
@@ -9,8 +9,8 @@
9
9
  "@babel/eslint-parser": "^7.18.9",
10
10
  "@babel/parser": "^7.25.3",
11
11
  "@babel/traverse": "^7.25.3",
12
- "@friggframework/schemas": "2.0.0-next.41",
13
- "@friggframework/test": "2.0.0-next.41",
12
+ "@friggframework/schemas": "2.0.0-next.42",
13
+ "@friggframework/test": "2.0.0-next.42",
14
14
  "@hapi/boom": "^10.0.1",
15
15
  "@inquirer/prompts": "^5.3.8",
16
16
  "axios": "^1.7.2",
@@ -32,8 +32,8 @@
32
32
  "serverless-http": "^2.7.0"
33
33
  },
34
34
  "devDependencies": {
35
- "@friggframework/eslint-config": "2.0.0-next.41",
36
- "@friggframework/prettier-config": "2.0.0-next.41",
35
+ "@friggframework/eslint-config": "2.0.0-next.42",
36
+ "@friggframework/prettier-config": "2.0.0-next.42",
37
37
  "aws-sdk-client-mock": "^4.1.0",
38
38
  "aws-sdk-client-mock-jest": "^4.1.0",
39
39
  "jest": "^30.1.3",
@@ -68,5 +68,5 @@
68
68
  "publishConfig": {
69
69
  "access": "public"
70
70
  },
71
- "gitHead": "a5c413164c1b492a228689851d037706f7f869af"
71
+ "gitHead": "9b956c72ec74f70bb5efd336782cf269c79c3d9b"
72
72
  }
package/test/index.js CHANGED
@@ -1,11 +1,9 @@
1
- const {testDefinitionRequiredAuthMethods} = require('./auther-definition-method-tester');
2
- const {createMockIntegration, createMockApiObject} = require('./mock-integration');
3
- const { testAutherDefinition } = require('./auther-definition-tester');
1
+ const { testDefinitionRequiredAuthMethods } = require('./auther-definition-method-tester');
2
+ const { createMockIntegration, createMockApiObject } = require('./mock-integration');
4
3
 
5
4
 
6
5
  module.exports = {
7
6
  createMockIntegration,
8
7
  createMockApiObject,
9
8
  testDefinitionRequiredAuthMethods,
10
- testAutherDefinition,
11
9
  };
@@ -1,8 +1,4 @@
1
1
  const {
2
- Auther,
3
- Credential,
4
- Entity,
5
- IntegrationFactory,
6
2
  createObjectId,
7
3
  } = require('@friggframework/core');
8
4
 
@@ -11,7 +7,6 @@ async function createMockIntegration(
11
7
  userId = null,
12
8
  config = { type: IntegrationClass.Definition.name }
13
9
  ) {
14
- const integrationFactory = new IntegrationFactory([IntegrationClass]);
15
10
  userId = userId || createObjectId();
16
11
 
17
12
  const insertOptions = {
@@ -24,10 +19,8 @@ async function createMockIntegration(
24
19
  const entities = [];
25
20
  for (const moduleName in IntegrationClass.modules) {
26
21
  const ModuleDef = IntegrationClass.Definition.modules[moduleName];
27
- const module = await Auther.getInstance({
28
- definition: ModuleDef,
29
- userId: userId,
30
- });
22
+ // todo: create module using the new architecture
23
+ const module = {}
31
24
  const credential = await module.CredentialModel.findOneAndUpdate(
32
25
  user,
33
26
  { $set: user },
@@ -51,11 +44,8 @@ async function createMockIntegration(
51
44
  );
52
45
  }
53
46
 
54
- const integration = await integrationFactory.createIntegration(
55
- entities,
56
- userId,
57
- config
58
- );
47
+ // todo: create integration using the new architecture
48
+ const integration = {}
59
49
 
60
50
  integration.id = integration.record._id;
61
51
 
@@ -1,102 +0,0 @@
1
- module.exports = {
2
- displayName: 'Frigg CLI Tests',
3
- testMatch: [
4
- '<rootDir>/__tests__/**/*.test.js',
5
- '<rootDir>/__tests__/**/*.spec.js'
6
- ],
7
- testEnvironment: 'node',
8
- collectCoverageFrom: [
9
- '../**/*.js',
10
- '!../**/*.test.js',
11
- '!../**/*.spec.js',
12
- '!../node_modules/**',
13
- '!../__tests__/**',
14
- '!../coverage/**'
15
- ],
16
- coverageDirectory: 'coverage',
17
- coverageReporters: [
18
- 'text',
19
- 'text-summary',
20
- 'html',
21
- 'lcov',
22
- 'json'
23
- ],
24
- coverageThreshold: {
25
- global: {
26
- branches: 85,
27
- functions: 85,
28
- lines: 85,
29
- statements: 85
30
- },
31
- '../install-command/index.js': {
32
- branches: 90,
33
- functions: 90,
34
- lines: 90,
35
- statements: 90
36
- },
37
- '../build-command/index.js': {
38
- branches: 90,
39
- functions: 90,
40
- lines: 90,
41
- statements: 90
42
- },
43
- '../deploy-command/index.js': {
44
- branches: 90,
45
- functions: 90,
46
- lines: 90,
47
- statements: 90
48
- },
49
- '../ui-command/index.js': {
50
- branches: 90,
51
- functions: 90,
52
- lines: 90,
53
- statements: 90
54
- },
55
- '../generate-command/index.js': {
56
- branches: 90,
57
- functions: 90,
58
- lines: 90,
59
- statements: 90
60
- }
61
- },
62
- setupFilesAfterEnv: [
63
- '<rootDir>/utils/test-setup.js'
64
- ],
65
- testTimeout: 10000,
66
- maxWorkers: '50%',
67
- verbose: true,
68
- collectCoverage: true,
69
- coveragePathIgnorePatterns: [
70
- '/node_modules/',
71
- '/__tests__/',
72
- '/coverage/',
73
- '.test.js',
74
- '.spec.js'
75
- ],
76
- moduleFileExtensions: [
77
- 'js',
78
- 'json',
79
- 'node'
80
- ],
81
- transform: {},
82
- testResultsProcessor: 'jest-sonar-reporter',
83
- reporters: [
84
- 'default',
85
- [
86
- 'jest-junit',
87
- {
88
- outputDirectory: 'coverage',
89
- outputName: 'junit.xml',
90
- ancestorSeparator: ' › ',
91
- uniqueOutputName: 'false',
92
- suiteNameTemplate: '{filepath}',
93
- classNameTemplate: '{classname}',
94
- titleTemplate: '{title}'
95
- }
96
- ]
97
- ],
98
- watchman: false,
99
- forceExit: true,
100
- detectOpenHandles: true,
101
- errorOnDeprecated: true
102
- };
@@ -1,170 +0,0 @@
1
- const { Command } = require('commander');
2
-
3
- /**
4
- * CommandTester - Utility class for testing CLI commands
5
- * Provides a fluent interface for setting up mocks and executing commands
6
- */
7
- class CommandTester {
8
- constructor(commandDefinition) {
9
- this.commandDefinition = commandDefinition;
10
- this.mocks = new Map();
11
- this.originalEnv = process.env;
12
- this.capturedLogs = {
13
- info: [],
14
- error: [],
15
- debug: [],
16
- warn: []
17
- };
18
- }
19
-
20
- /**
21
- * Set up a mock for a module
22
- * @param {string} modulePath - Path to the module to mock
23
- * @param {object} implementation - Mock implementation
24
- * @returns {CommandTester} - Fluent interface
25
- */
26
- mock(modulePath, implementation) {
27
- this.mocks.set(modulePath, implementation);
28
- return this;
29
- }
30
-
31
- /**
32
- * Set environment variables for the test
33
- * @param {object} env - Environment variables to set
34
- * @returns {CommandTester} - Fluent interface
35
- */
36
- withEnv(env) {
37
- process.env = { ...process.env, ...env };
38
- return this;
39
- }
40
-
41
- /**
42
- * Capture console output during test execution
43
- * @returns {CommandTester} - Fluent interface
44
- */
45
- captureOutput() {
46
- const originalConsole = { ...console };
47
-
48
- console.log = (...args) => {
49
- this.capturedLogs.info.push(args.join(' '));
50
- originalConsole.log(...args);
51
- };
52
-
53
- console.error = (...args) => {
54
- this.capturedLogs.error.push(args.join(' '));
55
- originalConsole.error(...args);
56
- };
57
-
58
- console.warn = (...args) => {
59
- this.capturedLogs.warn.push(args.join(' '));
60
- originalConsole.warn(...args);
61
- };
62
-
63
- console.debug = (...args) => {
64
- this.capturedLogs.debug.push(args.join(' '));
65
- originalConsole.debug(...args);
66
- };
67
-
68
- return this;
69
- }
70
-
71
- /**
72
- * Execute the command with given arguments
73
- * @param {string[]} args - Command arguments
74
- * @param {object} options - Command options
75
- * @returns {Promise<object>} - Execution result
76
- */
77
- async execute(args = [], options = {}) {
78
- // Set up mocks
79
- for (const [path, impl] of this.mocks) {
80
- jest.mock(path, () => impl, { virtual: true });
81
- }
82
-
83
- try {
84
- const program = new Command();
85
-
86
- // Set up the command
87
- const cmd = program
88
- .command(this.commandDefinition.name)
89
- .description(this.commandDefinition.description);
90
-
91
- // Add options if defined
92
- if (this.commandDefinition.options) {
93
- this.commandDefinition.options.forEach(option => {
94
- cmd.option(option.flags, option.description, option.defaultValue);
95
- });
96
- }
97
-
98
- // Add action
99
- cmd.action(this.commandDefinition.action);
100
-
101
- // Mock process.exit to prevent actual exit
102
- const originalExit = process.exit;
103
- let exitCode = 0;
104
- process.exit = (code) => {
105
- exitCode = code;
106
- throw new Error(`Process exited with code ${code}`);
107
- };
108
-
109
- try {
110
- await program.parseAsync(['node', 'cli', ...args]);
111
-
112
- return {
113
- success: true,
114
- exitCode: 0,
115
- logs: this.capturedLogs,
116
- args,
117
- options
118
- };
119
- } catch (error) {
120
- if (error.message.includes('Process exited with code')) {
121
- return {
122
- success: false,
123
- exitCode,
124
- error: error.message,
125
- logs: this.capturedLogs,
126
- args,
127
- options
128
- };
129
- }
130
- throw error;
131
- } finally {
132
- process.exit = originalExit;
133
- }
134
- } finally {
135
- // Clean up mocks
136
- for (const [path] of this.mocks) {
137
- jest.unmock(path);
138
- }
139
-
140
- // Restore environment
141
- process.env = this.originalEnv;
142
- }
143
- }
144
-
145
- /**
146
- * Get captured logs
147
- * @returns {object} - Captured logs by type
148
- */
149
- getLogs() {
150
- return this.capturedLogs;
151
- }
152
-
153
- /**
154
- * Reset the tester state
155
- * @returns {CommandTester} - Fluent interface
156
- */
157
- reset() {
158
- this.mocks.clear();
159
- this.capturedLogs = {
160
- info: [],
161
- error: [],
162
- debug: [],
163
- warn: []
164
- };
165
- process.env = this.originalEnv;
166
- return this;
167
- }
168
- }
169
-
170
- module.exports = { CommandTester };
@@ -1,125 +0,0 @@
1
- const {
2
- Auther,
3
- ModuleConstants,
4
- createObjectId,
5
- connectToDatabase,
6
- disconnectFromDatabase,
7
- } = require('@friggframework/core');
8
- const { createMockApiObject } = require("./mock-integration");
9
-
10
-
11
- function testAutherDefinition(definition, mocks) {
12
- const getModule = async (params) => {
13
- const module = await Auther.getInstance({
14
- definition,
15
- userId: createObjectId(),
16
- ...params,
17
- });
18
- if (mocks.tokenResponse) {
19
- mocks.getTokenFrom = async function(code) {
20
- await this.setTokens(mocks.tokenResponse);
21
- return mocks.tokenResponse
22
- }
23
- mocks.getTokenFromCode = mocks.getTokenFromCode || mocks.getTokenFrom
24
- mocks.getTokenFromCodeBasicAuthHeader = mocks.getTokenFromCodeBasicAuthHeader || mocks.getTokenFrom
25
- mocks.getTokenFromClientCredentials = mocks.getTokenFromClientCredentials || mocks.getTokenFrom
26
- mocks.getTokenFromUsernamePassword = mocks.getTokenFromUsernamePassword || mocks.getTokenFrom
27
- }
28
- if (mocks.refreshResponse) {
29
- mocks.refreshAccessToken = async function(code) {
30
- await this.setTokens(mocks.refreshResponse);
31
- return mocks.refreshResponse
32
- }
33
- }
34
- module.api = createMockApiObject(jest, module.api, mocks);
35
- return module
36
- }
37
-
38
-
39
- describe(`${definition.moduleName} Module Tests`, () => {
40
- let module, authUrl;
41
- beforeAll(async () => {
42
- await connectToDatabase();
43
- module = await getModule();
44
- });
45
-
46
- afterAll(async () => {
47
- await disconnectFromDatabase();
48
- });
49
-
50
- let requirements, authCallbackParams;
51
- if (definition.API.requesterType === ModuleConstants.authType.oauth2) {
52
- authCallbackParams = mocks.authorizeResponse || mocks.authorizeParams;
53
- describe('getAuthorizationRequirements() test', () => {
54
- it('should return auth requirements', async () => {
55
- requirements = await module.getAuthorizationRequirements();
56
- expect(requirements).toBeDefined();
57
- expect(requirements.type).toEqual(ModuleConstants.authType.oauth2);
58
- expect(requirements.url).toBeDefined();
59
- authUrl = requirements.url;
60
- });
61
- });
62
- } else if (definition.API.requesterType === ModuleConstants.authType.basic) {
63
- // could also confirm authCallbackParams against the auth requirements
64
- authCallbackParams = mocks.authorizeParams
65
- describe('getAuthorizationRequirements() test', () => {
66
- it('should return auth requirements', async () => {
67
- requirements = module.getAuthorizationRequirements();
68
- expect(requirements).toBeDefined();
69
- expect(requirements.type).toEqual(ModuleConstants.authType.basic);
70
- });
71
- });
72
- } else if (definition.API.requesterType === ModuleConstants.authType.apiKey) {
73
- // could also confirm authCallbackParams against the auth requirements
74
- authCallbackParams = mocks.authorizeParams
75
- describe('getAuthorizationRequirements() test', () => {
76
- it('should return auth requirements', async () => {
77
- requirements = module.getAuthorizationRequirements();
78
- expect(requirements).toBeDefined();
79
- expect(requirements.type).toEqual(ModuleConstants.authType.apiKey);
80
- });
81
- });
82
- }
83
-
84
- describe('Authorization requests', () => {
85
- let firstRes;
86
- it('processAuthorizationCallback()', async () => {
87
- firstRes = await module.processAuthorizationCallback(authCallbackParams);
88
- expect(firstRes).toBeDefined();
89
- expect(firstRes.entity_id).toBeDefined();
90
- expect(firstRes.credential_id).toBeDefined();
91
- });
92
- it('retrieves existing entity on subsequent calls', async () => {
93
- const res = await module.processAuthorizationCallback(authCallbackParams);
94
- expect(res).toEqual(firstRes);
95
- });
96
- });
97
-
98
- describe('Test credential retrieval and module instantiation', () => {
99
- it('retrieve by entity id', async () => {
100
- const newModule = await getModule({
101
- userId: module.userId,
102
- entityId: module.entity.id
103
- });
104
- expect(newModule).toBeDefined();
105
- expect(newModule.entity).toBeDefined();
106
- expect(newModule.credential).toBeDefined();
107
- expect(await newModule.testAuth()).toBeTruthy();
108
-
109
- });
110
-
111
- it('retrieve by credential id', async () => {
112
- const newModule = await getModule({
113
- userId: module.userId,
114
- credentialId: module.credential.id
115
- });
116
- expect(newModule).toBeDefined();
117
- expect(newModule.credential).toBeDefined();
118
- expect(await newModule.testAuth()).toBeTruthy();
119
- });
120
- });
121
- });
122
- }
123
-
124
- module.exports = { testAutherDefinition }
125
-