@friggframework/devtools 2.0.0-next.0 → 2.0.0-next.1

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.
@@ -1,7 +1,8 @@
1
1
  #!/usr/bin/env node
2
2
 
3
3
  const { Command } = require('commander');
4
- const { installCommand } = require('./installCommand');
4
+ const { installCommand } = require('./install-command');
5
+ const { startCommand } = require('./start-command'); // Assuming you have a startCommand module
5
6
 
6
7
  const program = new Command();
7
8
  program
@@ -9,6 +10,11 @@ program
9
10
  .description('Install an API module')
10
11
  .action(installCommand);
11
12
 
13
+ program
14
+ .command('start')
15
+ .description('Run the backend and optional frontend')
16
+ .action(startCommand);
17
+
12
18
  program.parse(process.argv);
13
19
 
14
- module.exports = { installCommand };
20
+ module.exports = { installCommand, startCommand };
@@ -1,12 +1,15 @@
1
1
  const { Command } = require('commander');
2
2
  const { installCommand } = require('./index');
3
- const { validatePackageExists } = require('./validatePackage');
4
- const { findNearestBackendPackageJson, validateBackendPath } = require('./backendPath');
5
- const { installPackage } = require('./installPackage');
6
- const { createIntegrationFile } = require('./integrationFile');
7
- const { updateBackendJsFile } = require('./backendJs');
8
- const { commitChanges } = require('./commitChanges');
9
- const { logInfo, logError } = require('./logger');
3
+ const { validatePackageExists } = require('./install-command/validate-package');
4
+ const {
5
+ findNearestBackendPackageJson,
6
+ validateBackendPath,
7
+ } = require('./utils/backend-path');
8
+ const { installPackage } = require('./install-command/install-package');
9
+ const { createIntegrationFile } = require('./install-command/integration-file');
10
+ const { updateBackendJsFile } = require('./install-command/backend-js');
11
+ const { commitChanges } = require('./install-command/commit-changes');
12
+ const { logInfo, logError } = require('./install-command/logger');
10
13
 
11
14
  describe('CLI Command Tests', () => {
12
15
  it('should successfully install an API module when all steps complete without errors', async () => {
@@ -14,26 +17,28 @@ describe('CLI Command Tests', () => {
14
17
  const mockPackageName = `@friggframework/api-module-${mockApiModuleName}`;
15
18
  const mockBackendPath = '/mock/backend/path';
16
19
 
17
- jest.mock('./validatePackage', () => ({
20
+ jest.mock('./install-command/validate-package', () => ({
18
21
  validatePackageExists: jest.fn().mockResolvedValue(true),
19
22
  }));
20
- jest.mock('./backendPath', () => ({
21
- findNearestBackendPackageJson: jest.fn().mockReturnValue(mockBackendPath),
23
+ jest.mock('./utils/backend-path', () => ({
24
+ findNearestBackendPackageJson: jest
25
+ .fn()
26
+ .mockReturnValue(mockBackendPath),
22
27
  validateBackendPath: jest.fn().mockReturnValue(true),
23
28
  }));
24
- jest.mock('./installPackage', () => ({
29
+ jest.mock('./install-command/install-package', () => ({
25
30
  installPackage: jest.fn().mockReturnValue(true),
26
31
  }));
27
- jest.mock('./integrationFile', () => ({
32
+ jest.mock('./install-command/integration-file', () => ({
28
33
  createIntegrationFile: jest.fn().mockReturnValue(true),
29
34
  }));
30
- jest.mock('./backendJs', () => ({
35
+ jest.mock('./install-command/backend-js', () => ({
31
36
  updateBackendJsFile: jest.fn().mockReturnValue(true),
32
37
  }));
33
- jest.mock('./commitChanges', () => ({
38
+ jest.mock('./install-command/commit-changes', () => ({
34
39
  commitChanges: jest.fn().mockReturnValue(true),
35
40
  }));
36
- jest.mock('./logger', () => ({
41
+ jest.mock('./install-command/logger', () => ({
37
42
  logInfo: jest.fn(),
38
43
  logError: jest.fn(),
39
44
  }));
@@ -49,19 +54,42 @@ describe('CLI Command Tests', () => {
49
54
  expect(validatePackageExists).toHaveBeenCalledWith(mockPackageName);
50
55
  expect(findNearestBackendPackageJson).toHaveBeenCalled();
51
56
  expect(validateBackendPath).toHaveBeenCalledWith(mockBackendPath);
52
- expect(installPackage).toHaveBeenCalledWith(mockBackendPath, mockPackageName);
53
- expect(createIntegrationFile).toHaveBeenCalledWith(mockBackendPath, mockApiModuleName);
54
- expect(updateBackendJsFile).toHaveBeenCalledWith(mockBackendPath, mockApiModuleName);
55
- expect(commitChanges).toHaveBeenCalledWith(mockBackendPath, mockApiModuleName);
56
- expect(logInfo).toHaveBeenCalledWith(`Successfully installed ${mockPackageName} and updated the project.`);
57
+ expect(installPackage).toHaveBeenCalledWith(
58
+ mockBackendPath,
59
+ mockPackageName
60
+ );
61
+ expect(createIntegrationFile).toHaveBeenCalledWith(
62
+ mockBackendPath,
63
+ mockApiModuleName
64
+ );
65
+ expect(updateBackendJsFile).toHaveBeenCalledWith(
66
+ mockBackendPath,
67
+ mockApiModuleName
68
+ );
69
+ expect(commitChanges).toHaveBeenCalledWith(
70
+ mockBackendPath,
71
+ mockApiModuleName
72
+ );
73
+ expect(logInfo).toHaveBeenCalledWith(
74
+ `Successfully installed ${mockPackageName} and updated the project.`
75
+ );
57
76
  });
58
77
 
59
78
  it('should log an error and exit with code 1 if the package does not exist', async () => {
60
- const mockExit = jest.spyOn(process, 'exit').mockImplementation(() => {});
61
- const mockLogError = jest.spyOn(require('./logger'), 'logError').mockImplementation(() => {});
62
- const mockValidatePackageExists = jest.spyOn(require('./validatePackage'), 'validatePackageExists').mockImplementation(() => {
63
- throw new Error('Package not found');
64
- });
79
+ const mockExit = jest
80
+ .spyOn(process, 'exit')
81
+ .mockImplementation(() => {});
82
+ const mockLogError = jest
83
+ .spyOn(require('./install-command/logger'), 'logError')
84
+ .mockImplementation(() => {});
85
+ const mockValidatePackageExists = jest
86
+ .spyOn(
87
+ require('./install-command/validate-package'),
88
+ 'validatePackageExists'
89
+ )
90
+ .mockImplementation(() => {
91
+ throw new Error('Package not found');
92
+ });
65
93
 
66
94
  const program = new Command();
67
95
  program
@@ -71,8 +99,13 @@ describe('CLI Command Tests', () => {
71
99
 
72
100
  await program.parseAsync(['node', 'install', 'nonexistent-package']);
73
101
 
74
- expect(mockValidatePackageExists).toHaveBeenCalledWith('@friggframework/api-module-nonexistent-package');
75
- expect(mockLogError).toHaveBeenCalledWith('An error occurred:', expect.any(Error));
102
+ expect(mockValidatePackageExists).toHaveBeenCalledWith(
103
+ '@friggframework/api-module-nonexistent-package'
104
+ );
105
+ expect(mockLogError).toHaveBeenCalledWith(
106
+ 'An error occurred:',
107
+ expect.any(Error)
108
+ );
76
109
  expect(mockExit).toHaveBeenCalledWith(1);
77
110
 
78
111
  mockExit.mockRestore();
@@ -81,13 +114,29 @@ describe('CLI Command Tests', () => {
81
114
  });
82
115
 
83
116
  it('should log an error and exit with code 1 if the backend path is invalid', async () => {
84
- const mockLogError = jest.spyOn(require('./logger'), 'logError').mockImplementation(() => {});
85
- const mockProcessExit = jest.spyOn(process, 'exit').mockImplementation(() => {});
86
- const mockValidatePackageExists = jest.spyOn(require('./validatePackage'), 'validatePackageExists').mockResolvedValue(true);
87
- const mockFindNearestBackendPackageJson = jest.spyOn(require('./backendPath'), 'findNearestBackendPackageJson').mockReturnValue('/invalid/path');
88
- const mockValidateBackendPath = jest.spyOn(require('./backendPath'), 'validateBackendPath').mockImplementation(() => {
89
- throw new Error('Invalid backend path');
90
- });
117
+ const mockLogError = jest
118
+ .spyOn(require('./install-command/logger'), 'logError')
119
+ .mockImplementation(() => {});
120
+ const mockProcessExit = jest
121
+ .spyOn(process, 'exit')
122
+ .mockImplementation(() => {});
123
+ const mockValidatePackageExists = jest
124
+ .spyOn(
125
+ require('./install-command/validate-package'),
126
+ 'validatePackageExists'
127
+ )
128
+ .mockResolvedValue(true);
129
+ const mockFindNearestBackendPackageJson = jest
130
+ .spyOn(
131
+ require('./utils/backend-path'),
132
+ 'findNearestBackendPackageJson'
133
+ )
134
+ .mockReturnValue('/invalid/path');
135
+ const mockValidateBackendPath = jest
136
+ .spyOn(require('./utils/backend-path'), 'validateBackendPath')
137
+ .mockImplementation(() => {
138
+ throw new Error('Invalid backend path');
139
+ });
91
140
 
92
141
  const program = new Command();
93
142
  program
@@ -97,7 +146,10 @@ describe('CLI Command Tests', () => {
97
146
 
98
147
  await program.parseAsync(['node', 'install', 'test-module']);
99
148
 
100
- expect(mockLogError).toHaveBeenCalledWith('An error occurred:', expect.any(Error));
149
+ expect(mockLogError).toHaveBeenCalledWith(
150
+ 'An error occurred:',
151
+ expect.any(Error)
152
+ );
101
153
  expect(mockProcessExit).toHaveBeenCalledWith(1);
102
154
 
103
155
  mockLogError.mockRestore();
@@ -3,8 +3,7 @@ const dotenv = require('dotenv');
3
3
  const { readFileSync, writeFileSync, existsSync } = require('fs');
4
4
  const { logInfo } = require('./logger');
5
5
  const { resolve } = require('node:path');
6
- const inquirer = require('inquirer');
7
-
6
+ const { confirm, input } = require('@inquirer/prompts');
8
7
  const { parse } = require('@babel/parser');
9
8
  const traverse = require('@babel/traverse').default;
10
9
 
@@ -82,26 +81,20 @@ const handleEnvVariables = async (backendPath, modulePath) => {
82
81
  logInfo(`Missing environment variables: ${missingEnvVars.join(', ')}`);
83
82
 
84
83
  if (missingEnvVars.length > 0) {
85
- const { addEnvVars } = await inquirer.prompt([
86
- {
87
- type: 'confirm',
88
- name: 'addEnvVars',
89
- message: `The following environment variables are required: ${missingEnvVars.join(
90
- ', '
91
- )}. Do you want to add them now?`,
92
- },
93
- ]);
84
+ const addEnvVars = await confirm({
85
+ message: `The following environment variables are required: ${missingEnvVars.join(
86
+ ', '
87
+ )}. Do you want to add them now?`,
88
+ });
94
89
 
95
90
  if (addEnvVars) {
96
91
  const envValues = {};
97
92
  for (const envVar of missingEnvVars) {
98
- const { value } = await inquirer.prompt([
99
- {
100
- type: 'input',
101
- name: 'value',
102
- message: `Enter value for ${envVar}:`,
103
- },
104
- ]);
93
+ const value = await input({
94
+ type: 'input',
95
+ name: 'value',
96
+ message: `Enter value for ${envVar}:`,
97
+ });
105
98
  envValues[envVar] = value;
106
99
  }
107
100
 
@@ -0,0 +1,136 @@
1
+ const { handleEnvVariables } = require('./environment-variables');
2
+ const { logInfo } = require('./logger');
3
+ const inquirer = require('inquirer');
4
+ const fs = require('fs');
5
+ const dotenv = require('dotenv');
6
+ const { resolve } = require('node:path');
7
+ const { parse } = require('@babel/parser');
8
+ const traverse = require('@babel/traverse');
9
+
10
+ jest.mock('inquirer');
11
+ jest.mock('fs');
12
+ jest.mock('dotenv');
13
+ jest.mock('./logger');
14
+ jest.mock('@babel/parser');
15
+ jest.mock('@babel/traverse');
16
+
17
+ describe('handleEnvVariables', () => {
18
+ const backendPath = '/mock/backend/path';
19
+ const modulePath = '/mock/module/path';
20
+
21
+ beforeEach(() => {
22
+ jest.clearAllMocks();
23
+ fs.readFileSync.mockReturnValue(`
24
+ const Definition = {
25
+ env: {
26
+ client_id: process.env.GOOGLE_CALENDAR_CLIENT_ID,
27
+ client_secret: process.env.GOOGLE_CALENDAR_CLIENT_SECRET,
28
+ redirect_uri: \`\${process.env.REDIRECT_URI}/google-calendar\`,
29
+ scope: process.env.GOOGLE_CALENDAR_SCOPE,
30
+ }
31
+ };
32
+ `);
33
+ parse.mockReturnValue({});
34
+ traverse.default.mockImplementation((ast, visitor) => {
35
+ visitor.ObjectProperty({
36
+ node: {
37
+ key: { name: 'env' },
38
+ value: {
39
+ properties: [
40
+ {
41
+ key: { name: 'client_id' },
42
+ value: {
43
+ type: 'MemberExpression',
44
+ object: { name: 'process' },
45
+ property: {
46
+ name: 'GOOGLE_CALENDAR_CLIENT_ID',
47
+ },
48
+ },
49
+ },
50
+ {
51
+ key: { name: 'client_secret' },
52
+ value: {
53
+ type: 'MemberExpression',
54
+ object: { name: 'process' },
55
+ property: {
56
+ name: 'GOOGLE_CALENDAR_CLIENT_SECRET',
57
+ },
58
+ },
59
+ },
60
+ {
61
+ key: { name: 'redirect_uri' },
62
+ value: {
63
+ type: 'MemberExpression',
64
+ object: { name: 'process' },
65
+ property: { name: 'REDIRECT_URI' },
66
+ },
67
+ },
68
+ {
69
+ key: { name: 'scope' },
70
+ value: {
71
+ type: 'MemberExpression',
72
+ object: { name: 'process' },
73
+ property: { name: 'GOOGLE_CALENDAR_SCOPE' },
74
+ },
75
+ },
76
+ ],
77
+ },
78
+ },
79
+ });
80
+ });
81
+ });
82
+
83
+ xit('should identify and handle missing environment variables', async () => {
84
+ const localEnvPath = resolve(backendPath, '../.env');
85
+ const localDevConfigPath = resolve(
86
+ backendPath,
87
+ '../src/configs/dev.json'
88
+ );
89
+
90
+ fs.existsSync.mockImplementation(
91
+ (path) => path === localEnvPath || path === localDevConfigPath
92
+ );
93
+ dotenv.parse.mockReturnValue({});
94
+ fs.readFileSync.mockImplementation((path) => {
95
+ if (path === resolve(modulePath, 'index.js'))
96
+ return 'mock module content';
97
+ if (path === localEnvPath) return '';
98
+ if (path === localDevConfigPath) return '{}';
99
+ return '';
100
+ });
101
+
102
+ inquirer.prompt
103
+ .mockResolvedValueOnce({ addEnvVars: true })
104
+ .mockResolvedValueOnce({ value: 'client_id_value' })
105
+ .mockResolvedValueOnce({ value: 'client_secret_value' })
106
+ .mockResolvedValueOnce({ value: 'redirect_uri_value' })
107
+ .mockResolvedValueOnce({ value: 'scope_value' });
108
+
109
+ await handleEnvVariables(backendPath, modulePath);
110
+
111
+ expect(logInfo).toHaveBeenCalledWith(
112
+ 'Searching for missing environment variables...'
113
+ );
114
+ expect(logInfo).toHaveBeenCalledWith(
115
+ 'Missing environment variables: GOOGLE_CALENDAR_CLIENT_ID, GOOGLE_CALENDAR_CLIENT_SECRET, REDIRECT_URI, GOOGLE_CALENDAR_SCOPE'
116
+ );
117
+ expect(inquirer.prompt).toHaveBeenCalledTimes(5);
118
+ expect(fs.appendFileSync).toHaveBeenCalledWith(
119
+ localEnvPath,
120
+ '\nGOOGLE_CALENDAR_CLIENT_ID=client_id_value\nGOOGLE_CALENDAR_CLIENT_SECRET=client_secret_value\nREDIRECT_URI=redirect_uri_value\nGOOGLE_CALENDAR_SCOPE=scope_value'
121
+ );
122
+ expect(fs.writeFileSync).toHaveBeenCalledWith(
123
+ localDevConfigPath,
124
+ JSON.stringify(
125
+ {
126
+ GOOGLE_CALENDAR_CLIENT_ID: 'client_id_value',
127
+ GOOGLE_CALENDAR_CLIENT_SECRET: 'client_secret_value',
128
+ REDIRECT_URI: 'redirect_uri_value',
129
+ GOOGLE_CALENDAR_SCOPE: 'scope_value',
130
+ },
131
+ null,
132
+ 2
133
+ )
134
+ );
135
+ });
136
+ });
@@ -1,18 +1,18 @@
1
- const { installPackage } = require('./installPackage');
2
- const { createIntegrationFile } = require('./integrationFile');
1
+ const { installPackage } = require('./install-package');
2
+ const { createIntegrationFile } = require('./integration-file');
3
3
  const { resolve } = require('node:path');
4
- const { updateBackendJsFile } = require('./backendJs');
4
+ const { updateBackendJsFile } = require('./backend-js');
5
5
  const { logInfo, logError } = require('./logger');
6
- const { commitChanges } = require('./commitChanges');
6
+ const { commitChanges } = require('./commit-changes');
7
7
  const {
8
8
  findNearestBackendPackageJson,
9
9
  validateBackendPath,
10
- } = require('./backendPath');
11
- const { handleEnvVariables } = require('./environmentVariables');
10
+ } = require('../utils/backend-path');
11
+ const { handleEnvVariables } = require('./environment-variables');
12
12
  const {
13
13
  validatePackageExists,
14
14
  searchAndSelectPackage,
15
- } = require('./validatePackage');
15
+ } = require('./validate-package');
16
16
 
17
17
  const installCommand = async (apiModuleName) => {
18
18
  try {
@@ -1,7 +1,7 @@
1
1
  const { execSync } = require('child_process');
2
2
  const axios = require('axios');
3
3
  const { logError } = require('./logger');
4
- const inquirer = require('inquirer');
4
+ const { checkbox } = require('@inquirer/prompts');
5
5
 
6
6
  async function searchPackages(apiModuleName) {
7
7
  const searchCommand = `npm search @friggframework/api-module-${apiModuleName} --json`;
@@ -36,9 +36,7 @@ const searchAndSelectPackage = async (apiModuleName) => {
36
36
  }
37
37
 
38
38
  const filteredResults = searchResults.filter((pkg) => {
39
- const version = pkg.version
40
- ? pkg.version.split('.').map(Number)
41
- : [];
39
+ const version = pkg.version ? pkg.version.split('.').map(Number) : [];
42
40
  return version[0] >= 1;
43
41
  });
44
42
 
@@ -55,20 +53,18 @@ const searchAndSelectPackage = async (apiModuleName) => {
55
53
  const choices = filteredResults.map((pkg) => {
56
54
  return {
57
55
  name: `${pkg.name} (${pkg.version})`,
56
+ value: pkg.name,
58
57
  checked: filteredResults.length === 1, // Automatically select if only one result
59
58
  };
60
59
  });
61
60
 
62
- const { selectedPackages } = await inquirer.prompt([
63
- {
64
- type: 'checkbox',
65
- name: 'selectedPackages',
66
- message: 'Select the packages to install:',
67
- choices,
68
- },
69
- ]);
61
+ const selectedPackages = await checkbox({
62
+ message: 'Select the packages to install:',
63
+ choices,
64
+ });
65
+ console.log('Selected packages:', selectedPackages);
70
66
 
71
- return selectedPackages.map(choice => choice.split(' ')[0]);
67
+ return selectedPackages.map((choice) => choice.split(' ')[0]);
72
68
  };
73
69
 
74
70
  module.exports = {
@@ -0,0 +1,30 @@
1
+ const { spawn } = require('child_process');
2
+ const path = require('path');
3
+
4
+ function startCommand() {
5
+ console.log('Starting backend and optional frontend...');
6
+ // Suppress AWS SDK warning message about maintenance mode
7
+ process.env.AWS_SDK_JS_SUPPRESS_MAINTENANCE_MODE_MESSAGE = 1;
8
+ const backendPath = path.resolve(process.cwd());
9
+ console.log(`Starting backend in ${backendPath}...`);
10
+ const infrastructurePath = 'infrastructure.js';
11
+ const command = 'serverless';
12
+ const args = ['offline', '--config', infrastructurePath, '--stage=dev'];
13
+
14
+ const childProcess = spawn(command, args, {
15
+ cwd: backendPath,
16
+ stdio: 'inherit',
17
+ });
18
+
19
+ childProcess.on('error', (error) => {
20
+ console.error(`Error executing command: ${error.message}`);
21
+ });
22
+
23
+ childProcess.on('close', (code) => {
24
+ if (code !== 0) {
25
+ console.log(`Child process exited with code ${code}`);
26
+ }
27
+ });
28
+ }
29
+
30
+ module.exports = { startCommand };
package/package.json CHANGED
@@ -1,13 +1,13 @@
1
1
  {
2
2
  "name": "@friggframework/devtools",
3
3
  "prettier": "@friggframework/prettier-config",
4
- "version": "2.0.0-next.0",
4
+ "version": "2.0.0-next.1",
5
5
  "dependencies": {
6
6
  "@babel/eslint-parser": "^7.18.9",
7
7
  "@babel/parser": "^7.25.3",
8
8
  "@babel/traverse": "^7.25.3",
9
- "@friggframework/core": "^2.0.0-next.0",
10
- "@friggframework/test": "^2.0.0-next.0",
9
+ "@friggframework/core": "2.0.0-next.1",
10
+ "@friggframework/test": "2.0.0-next.1",
11
11
  "axios": "^1.7.2",
12
12
  "commander": "^12.1.0",
13
13
  "dotenv": "^16.4.5",
@@ -21,8 +21,8 @@
21
21
  "inquirer": "^10.1.6"
22
22
  },
23
23
  "devDependencies": {
24
- "@friggframework/eslint-config": "^2.0.0-next.0",
25
- "@friggframework/prettier-config": "^2.0.0-next.0"
24
+ "@friggframework/eslint-config": "2.0.0-next.1",
25
+ "@friggframework/prettier-config": "2.0.0-next.1"
26
26
  },
27
27
  "scripts": {
28
28
  "lint:fix": "prettier --write --loglevel error . && eslint . --fix",
@@ -46,5 +46,5 @@
46
46
  "publishConfig": {
47
47
  "access": "public"
48
48
  },
49
- "gitHead": "e37f577d3c14f8eed6584517830c6c61815cf759"
49
+ "gitHead": "44b619e288b23d879673518076cf9e4acacb3c71"
50
50
  }
@@ -1,65 +1,65 @@
1
- const { Auther, Credential, Entity, IntegrationFactory, createObjectId } = require('@friggframework/core');
1
+ const {
2
+ Auther,
3
+ Credential,
4
+ Entity,
5
+ IntegrationFactory,
6
+ createObjectId,
7
+ } = require('@friggframework/core');
2
8
 
3
-
4
- async function createMockIntegration(IntegrationClassDef, userId = null, config = {},) {
5
- const integrationFactory = new IntegrationFactory([IntegrationClassDef]);
9
+ async function createMockIntegration(
10
+ IntegrationClass,
11
+ userId = null,
12
+ config = { type: IntegrationClass.Definition.name }
13
+ ) {
14
+ const integrationFactory = new IntegrationFactory([IntegrationClass]);
6
15
  userId = userId || createObjectId();
7
16
 
8
17
  const insertOptions = {
9
18
  new: true,
10
19
  upsert: true,
11
20
  setDefaultsOnInsert: true,
12
- }
13
- const user = {user: userId}
14
-
15
- const credential = await Credential.findOneAndUpdate(
16
- user,
17
- { $set: user },
18
- insertOptions
19
- );
20
- const entity1 = await Entity.findOneAndUpdate(
21
- user,
22
- {
23
- $set: {
24
- credential: credential.id,
25
- user: userId,
26
- name: 'Test user',
27
- externalId: '1234567890123456',
28
- },
29
- },
30
- insertOptions
31
- );
32
- const entity2 = await Entity.findOneAndUpdate(
33
- user,
34
- {
35
- $set: {
36
- credential: credential.id,
37
- user: userId,
38
- },
39
- },
40
- insertOptions
41
- );
21
+ };
22
+ const user = { user: userId };
42
23
 
43
- const entities = [entity1, entity2]
44
-
45
- const integration =
46
- await integrationFactory.createIntegration(
47
- entities,
48
- userId,
49
- config,
24
+ const entities = [];
25
+ for (const moduleName in IntegrationClass.modules) {
26
+ const ModuleDef = IntegrationClass.Definition.modules[moduleName];
27
+ const module = await Auther.getInstance({
28
+ definition: ModuleDef,
29
+ userId: userId,
30
+ });
31
+ const credential = await module.CredentialModel.findOneAndUpdate(
32
+ user,
33
+ { $set: user },
34
+ insertOptions
35
+ );
36
+ entities.push(
37
+ (
38
+ await module.EntityModel.findOneAndUpdate(
39
+ user,
40
+ {
41
+ $set: {
42
+ credential,
43
+ user: userId,
44
+ name: `Test ${moduleName}`,
45
+ externalId: `1234567890123456_${moduleName}`,
46
+ },
47
+ },
48
+ insertOptions
49
+ )
50
+ ).id
50
51
  );
52
+ }
51
53
 
52
- integration.id = integration.record._id
54
+ const integration = await integrationFactory.createIntegration(
55
+ entities,
56
+ userId,
57
+ config
58
+ );
53
59
 
54
- for (const i in entities){
55
- if (Object.entries(IntegrationClassDef.modules).length <= i) break
56
- const [moduleName, ModuleDef] = Object.entries(IntegrationClassDef.modules)[i];
57
- const module = await Auther.getInstance({definition: ModuleDef, userId: userId})
58
- module.entity = entities[i];
59
- integration[moduleName] = module;
60
- }
60
+ integration.id = integration.record._id;
61
61
 
62
- return integration
62
+ return integration;
63
63
  }
64
64
 
65
65
  function createMockApiObject(jest, api = {}, mockMethodMap) {
@@ -67,17 +67,22 @@ function createMockApiObject(jest, api = {}, mockMethodMap) {
67
67
  // and values which are the mock response (or implementation)
68
68
  const clone = (data) => JSON.parse(JSON.stringify(data));
69
69
 
70
- for (const [methodName, mockDataOrImplementation] of Object.entries(mockMethodMap)) {
70
+ for (const [methodName, mockDataOrImplementation] of Object.entries(
71
+ mockMethodMap
72
+ )) {
71
73
  if (mockDataOrImplementation instanceof Function) {
72
74
  api[methodName] = jest.fn(mockDataOrImplementation);
73
- }
74
- else if (api[methodName]?.constructor?.name === "AsyncFunction") {
75
- api[methodName] = jest.fn().mockResolvedValue(clone(mockDataOrImplementation));
75
+ } else if (api[methodName]?.constructor?.name === 'AsyncFunction') {
76
+ api[methodName] = jest
77
+ .fn()
78
+ .mockResolvedValue(clone(mockDataOrImplementation));
76
79
  } else {
77
- api[methodName] = jest.fn().mockReturnValue(clone(mockDataOrImplementation));
80
+ api[methodName] = jest
81
+ .fn()
82
+ .mockReturnValue(clone(mockDataOrImplementation));
78
83
  }
79
84
  }
80
85
  return api;
81
86
  }
82
87
 
83
- module.exports = {createMockIntegration, createMockApiObject};
88
+ module.exports = { createMockIntegration, createMockApiObject };
@@ -1,86 +0,0 @@
1
- const { handleEnvVariables } = require('./environmentVariables');
2
- const { logInfo } = require('./logger');
3
- const inquirer = require('inquirer');
4
- const fs = require('fs');
5
- const dotenv = require('dotenv');
6
- const { resolve } = require('node:path');
7
- const { parse } = require('@babel/parser');
8
- const traverse = require('@babel/traverse');
9
-
10
- jest.mock('inquirer');
11
- jest.mock('fs');
12
- jest.mock('dotenv');
13
- jest.mock('./logger');
14
- jest.mock('@babel/parser');
15
- jest.mock('@babel/traverse');
16
-
17
- describe('handleEnvVariables', () => {
18
- const backendPath = '/mock/backend/path';
19
- const modulePath = '/mock/module/path';
20
-
21
- beforeEach(() => {
22
- jest.clearAllMocks();
23
- fs.readFileSync.mockReturnValue(`
24
- const Definition = {
25
- env: {
26
- client_id: process.env.GOOGLE_CALENDAR_CLIENT_ID,
27
- client_secret: process.env.GOOGLE_CALENDAR_CLIENT_SECRET,
28
- redirect_uri: \`\${process.env.REDIRECT_URI}/google-calendar\`,
29
- scope: process.env.GOOGLE_CALENDAR_SCOPE,
30
- }
31
- };
32
- `);
33
- parse.mockReturnValue({});
34
- traverse.default.mockImplementation((ast, visitor) => {
35
- visitor.ObjectProperty({
36
- node: {
37
- key: { name: 'env' },
38
- value: {
39
- properties: [
40
- { key: { name: 'client_id' }, value: { type: 'MemberExpression', object: { name: 'process' }, property: { name: 'GOOGLE_CALENDAR_CLIENT_ID' } } },
41
- { key: { name: 'client_secret' }, value: { type: 'MemberExpression', object: { name: 'process' }, property: { name: 'GOOGLE_CALENDAR_CLIENT_SECRET' } } },
42
- { key: { name: 'redirect_uri' }, value: { type: 'MemberExpression', object: { name: 'process' }, property: { name: 'REDIRECT_URI' } } },
43
- { key: { name: 'scope' }, value: { type: 'MemberExpression', object: { name: 'process' }, property: { name: 'GOOGLE_CALENDAR_SCOPE' } } },
44
- ]
45
- }
46
- }
47
- });
48
- });
49
- });
50
-
51
- xit('should identify and handle missing environment variables', async () => {
52
- const localEnvPath = resolve(backendPath, '../.env');
53
- const localDevConfigPath = resolve(backendPath, '../src/configs/dev.json');
54
-
55
- fs.existsSync.mockImplementation((path) => path === localEnvPath || path === localDevConfigPath);
56
- dotenv.parse.mockReturnValue({});
57
- fs.readFileSync.mockImplementation((path) => {
58
- if (path === resolve(modulePath, 'index.js')) return 'mock module content';
59
- if (path === localEnvPath) return '';
60
- if (path === localDevConfigPath) return '{}';
61
- return '';
62
- });
63
-
64
- inquirer.prompt.mockResolvedValueOnce({ addEnvVars: true })
65
- .mockResolvedValueOnce({ value: 'client_id_value' })
66
- .mockResolvedValueOnce({ value: 'client_secret_value' })
67
- .mockResolvedValueOnce({ value: 'redirect_uri_value' })
68
- .mockResolvedValueOnce({ value: 'scope_value' });
69
-
70
- await handleEnvVariables(backendPath, modulePath);
71
-
72
- expect(logInfo).toHaveBeenCalledWith('Searching for missing environment variables...');
73
- expect(logInfo).toHaveBeenCalledWith('Missing environment variables: GOOGLE_CALENDAR_CLIENT_ID, GOOGLE_CALENDAR_CLIENT_SECRET, REDIRECT_URI, GOOGLE_CALENDAR_SCOPE');
74
- expect(inquirer.prompt).toHaveBeenCalledTimes(5);
75
- expect(fs.appendFileSync).toHaveBeenCalledWith(localEnvPath, '\nGOOGLE_CALENDAR_CLIENT_ID=client_id_value\nGOOGLE_CALENDAR_CLIENT_SECRET=client_secret_value\nREDIRECT_URI=redirect_uri_value\nGOOGLE_CALENDAR_SCOPE=scope_value');
76
- expect(fs.writeFileSync).toHaveBeenCalledWith(
77
- localDevConfigPath,
78
- JSON.stringify({
79
- GOOGLE_CALENDAR_CLIENT_ID: 'client_id_value',
80
- GOOGLE_CALENDAR_CLIENT_SECRET: 'client_secret_value',
81
- REDIRECT_URI: 'redirect_uri_value',
82
- GOOGLE_CALENDAR_SCOPE: 'scope_value'
83
- }, null, 2)
84
- );
85
- });
86
- });