@friggframework/devtools 1.1.6 → 1.1.7--canary.359.1946bb6.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.
- package/CHANGELOG.md +55 -0
- package/README.md +90 -0
- package/frigg-cli/backendJs.js +33 -0
- package/frigg-cli/backendPath.js +26 -0
- package/frigg-cli/commitChanges.js +16 -0
- package/frigg-cli/environmentVariables.js +134 -0
- package/frigg-cli/environmentVariables.test.js +86 -0
- package/frigg-cli/index.js +14 -0
- package/frigg-cli/index.test.js +109 -0
- package/frigg-cli/installCommand.js +57 -0
- package/frigg-cli/installPackage.js +13 -0
- package/frigg-cli/integrationFile.js +30 -0
- package/frigg-cli/logger.js +12 -0
- package/frigg-cli/template.js +90 -0
- package/frigg-cli/validatePackage.js +79 -0
- package/package.json +17 -7
- package/test/auther-definition-tester.js +1 -1
- package/test/mock-integration.js +10 -9
package/CHANGELOG.md
CHANGED
|
@@ -1,3 +1,58 @@
|
|
|
1
|
+
# v1.2.0 (Tue Aug 06 2024)
|
|
2
|
+
|
|
3
|
+
#### 🚀 Enhancement
|
|
4
|
+
|
|
5
|
+
- CLI for Frigg - Install command for now [#322](https://github.com/friggframework/frigg/pull/322) ([@seanspeaks](https://github.com/seanspeaks))
|
|
6
|
+
|
|
7
|
+
#### 🐛 Bug Fix
|
|
8
|
+
|
|
9
|
+
- Add READMEs that will need updating, but for version releasing [#324](https://github.com/friggframework/frigg/pull/324) ([@seanspeaks](https://github.com/seanspeaks))
|
|
10
|
+
- Add READMEs that will need updating, but for version releasing ([@seanspeaks](https://github.com/seanspeaks))
|
|
11
|
+
- small update to integration testing / tooling [#304](https://github.com/friggframework/frigg/pull/304) ([@MichaelRyanWebber](https://github.com/MichaelRyanWebber))
|
|
12
|
+
- Added missing dependencies ([@seanspeaks](https://github.com/seanspeaks))
|
|
13
|
+
- Added a missing dependency ([@seanspeaks](https://github.com/seanspeaks))
|
|
14
|
+
- Updated to handle envs properly, also further refactoring, and better templating. ([@seanspeaks](https://github.com/seanspeaks))
|
|
15
|
+
- WIP with help from Tabnine AI chat. ([@seanspeaks](https://github.com/seanspeaks))
|
|
16
|
+
- Bump version to: v1.1.8 \[skip ci\] ([@seanspeaks](https://github.com/seanspeaks))
|
|
17
|
+
- use the factory methods for creating the mock integration so that everything is set up (mostly events and userActions) ([@MichaelRyanWebber](https://github.com/MichaelRyanWebber))
|
|
18
|
+
- Bump version to: v1.1.5 \[skip ci\] ([@seanspeaks](https://github.com/seanspeaks))
|
|
19
|
+
|
|
20
|
+
#### Authors: 2
|
|
21
|
+
|
|
22
|
+
- [@MichaelRyanWebber](https://github.com/MichaelRyanWebber)
|
|
23
|
+
- Sean Matthews ([@seanspeaks](https://github.com/seanspeaks))
|
|
24
|
+
|
|
25
|
+
---
|
|
26
|
+
|
|
27
|
+
# v1.1.8 (Thu Jul 18 2024)
|
|
28
|
+
|
|
29
|
+
#### 🐛 Bug Fix
|
|
30
|
+
|
|
31
|
+
- Revert open to support commonjs [#319](https://github.com/friggframework/frigg/pull/319) ([@MichaelRyanWebber](https://github.com/MichaelRyanWebber))
|
|
32
|
+
- Bump version to: v1.1.6 \[skip ci\] ([@seanspeaks](https://github.com/seanspeaks))
|
|
33
|
+
|
|
34
|
+
#### Authors: 2
|
|
35
|
+
|
|
36
|
+
- [@MichaelRyanWebber](https://github.com/MichaelRyanWebber)
|
|
37
|
+
- Sean Matthews ([@seanspeaks](https://github.com/seanspeaks))
|
|
38
|
+
|
|
39
|
+
---
|
|
40
|
+
|
|
41
|
+
# v1.1.7 (Mon Jul 15 2024)
|
|
42
|
+
|
|
43
|
+
#### 🐛 Bug Fix
|
|
44
|
+
|
|
45
|
+
- getAuthorizationRequirements() async [#318](https://github.com/friggframework/frigg/pull/318) ([@MichaelRyanWebber](https://github.com/MichaelRyanWebber))
|
|
46
|
+
- await getAuthorizeRequirements() ([@MichaelRyanWebber](https://github.com/MichaelRyanWebber))
|
|
47
|
+
- Bump version to: v1.1.6 \[skip ci\] ([@seanspeaks](https://github.com/seanspeaks))
|
|
48
|
+
|
|
49
|
+
#### Authors: 2
|
|
50
|
+
|
|
51
|
+
- [@MichaelRyanWebber](https://github.com/MichaelRyanWebber)
|
|
52
|
+
- Sean Matthews ([@seanspeaks](https://github.com/seanspeaks))
|
|
53
|
+
|
|
54
|
+
---
|
|
55
|
+
|
|
1
56
|
# v1.1.6 (Fri Apr 26 2024)
|
|
2
57
|
|
|
3
58
|
#### 🐛 Bug Fix
|
package/README.md
ADDED
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
# Frigg Framework Devtools
|
|
2
|
+
|
|
3
|
+
This package contains development tools and utilities for the Frigg Framework, an open-source serverless framework designed to simplify the development of integrations at scale.
|
|
4
|
+
|
|
5
|
+
## Contents
|
|
6
|
+
|
|
7
|
+
The devtools package includes the following main components:
|
|
8
|
+
|
|
9
|
+
1. Frigg CLI
|
|
10
|
+
2. Migrations
|
|
11
|
+
3. Test Utilities
|
|
12
|
+
4. Local runner and deploy tooling
|
|
13
|
+
5. Infrastructure
|
|
14
|
+
|
|
15
|
+
## Frigg CLI
|
|
16
|
+
|
|
17
|
+
The Frigg CLI is a command-line interface tool that helps developers manage and install API modules in their Frigg projects.
|
|
18
|
+
|
|
19
|
+
### Key Features
|
|
20
|
+
|
|
21
|
+
- Install API modules
|
|
22
|
+
- Search for available API modules
|
|
23
|
+
- Automatically update project files
|
|
24
|
+
- Handle environment variables
|
|
25
|
+
- Validate package existence and backend paths
|
|
26
|
+
- Run your Frigg instance locally
|
|
27
|
+
- Deploy your Frigg application to your configured provider
|
|
28
|
+
|
|
29
|
+
### Usage
|
|
30
|
+
|
|
31
|
+
To use the Frigg CLI, run the following command:
|
|
32
|
+
```sh
|
|
33
|
+
frigg install <api-module-name>
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
This command will search for the specified API module, install it, and update your project accordingly.
|
|
37
|
+
|
|
38
|
+
```sh
|
|
39
|
+
frigg start
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
This command will look for the closest infrastructure.js file and run a start command, programmatically generating the serverless yml needed to run locally.
|
|
43
|
+
|
|
44
|
+
## Migrations
|
|
45
|
+
|
|
46
|
+
(Add information about migrations here if available)
|
|
47
|
+
|
|
48
|
+
## Test Utilities
|
|
49
|
+
|
|
50
|
+
The test directory contains utilities to assist with testing in the Frigg Framework.
|
|
51
|
+
|
|
52
|
+
### Key Features
|
|
53
|
+
|
|
54
|
+
- Integration validator (TODO: implementation details to be added)
|
|
55
|
+
- Mock API functionality
|
|
56
|
+
|
|
57
|
+
### Mock API
|
|
58
|
+
|
|
59
|
+
The `mock-api.js` file provides functionality to mock API responses for testing purposes. It uses `nock` for HTTP request interception and includes features like:
|
|
60
|
+
|
|
61
|
+
- Caching of authentication tokens
|
|
62
|
+
- Recording and replaying of HTTP requests
|
|
63
|
+
- Jest test state management
|
|
64
|
+
|
|
65
|
+
Usage example:
|
|
66
|
+
|
|
67
|
+
```javascript
|
|
68
|
+
const { mockApi } = require('@friggframework/devtools/test/mock-api');
|
|
69
|
+
|
|
70
|
+
// Use mockApi in your tests to simulate API responses
|
|
71
|
+
```
|
|
72
|
+
## Installation
|
|
73
|
+
|
|
74
|
+
To install the devtools package as a dev dependency, run:
|
|
75
|
+
|
|
76
|
+
```
|
|
77
|
+
npm install --save-dev @friggframework/devtools
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
## Contributing
|
|
81
|
+
|
|
82
|
+
Please read [CONTRIBUTING.md](../../CONTRIBUTING.md) for details on our code of conduct, and the process for submitting pull requests to us.
|
|
83
|
+
|
|
84
|
+
## License
|
|
85
|
+
|
|
86
|
+
This project is licensed under the MIT License - see the [LICENSE.md](../../LICENSE.md) file for details
|
|
87
|
+
|
|
88
|
+
## Support
|
|
89
|
+
|
|
90
|
+
For support, please open an issue in the main Frigg Framework repository or contact the maintainers directly.
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
const fs = require('fs-extra');
|
|
2
|
+
const path = require('path');
|
|
3
|
+
const { logInfo } = require('./logger');
|
|
4
|
+
const INTEGRATIONS_DIR = 'src/integrations';
|
|
5
|
+
const BACKEND_JS = 'backend.js';
|
|
6
|
+
|
|
7
|
+
function updateBackendJsFile(backendPath, apiModuleName) {
|
|
8
|
+
const backendJsPath = path.join(path.dirname(backendPath), BACKEND_JS);
|
|
9
|
+
logInfo(`Updating backend.js: ${backendJsPath}`);
|
|
10
|
+
updateBackendJs(backendJsPath, apiModuleName);
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
function updateBackendJs(backendJsPath, apiModuleName) {
|
|
14
|
+
const backendJsContent = fs.readFileSync(backendJsPath, 'utf-8');
|
|
15
|
+
const importStatement = `const ${apiModuleName}Integration = require('./${INTEGRATIONS_DIR}/${apiModuleName}Integration');\n`;
|
|
16
|
+
|
|
17
|
+
if (!backendJsContent.includes(importStatement)) {
|
|
18
|
+
const updatedContent = backendJsContent.replace(
|
|
19
|
+
/(integrations\s*:\s*\[)([\s\S]*?)(\])/,
|
|
20
|
+
`$1\n ${apiModuleName}Integration,$2$3`
|
|
21
|
+
);
|
|
22
|
+
fs.writeFileSync(backendJsPath, importStatement + updatedContent);
|
|
23
|
+
} else {
|
|
24
|
+
logInfo(
|
|
25
|
+
`Import statement for ${apiModuleName}Integration already exists in backend.js`
|
|
26
|
+
);
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
module.exports = {
|
|
31
|
+
updateBackendJsFile,
|
|
32
|
+
updateBackendJs,
|
|
33
|
+
};
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
const fs = require('fs-extra');
|
|
2
|
+
const path = require('path');
|
|
3
|
+
const PACKAGE_JSON = 'package.json';
|
|
4
|
+
|
|
5
|
+
function findNearestBackendPackageJson() {
|
|
6
|
+
let currentDir = process.cwd();
|
|
7
|
+
while (currentDir !== path.parse(currentDir).root) {
|
|
8
|
+
const packageJsonPath = path.join(currentDir, 'backend', PACKAGE_JSON);
|
|
9
|
+
if (fs.existsSync(packageJsonPath)) {
|
|
10
|
+
return packageJsonPath;
|
|
11
|
+
}
|
|
12
|
+
currentDir = path.dirname(currentDir);
|
|
13
|
+
}
|
|
14
|
+
return null;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
function validateBackendPath(backendPath) {
|
|
18
|
+
if (!backendPath) {
|
|
19
|
+
throw new Error('Could not find a backend package.json file.');
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
module.exports = {
|
|
24
|
+
findNearestBackendPackageJson,
|
|
25
|
+
validateBackendPath,
|
|
26
|
+
};
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
const { execSync } = require('child_process');
|
|
2
|
+
const path = require('path');
|
|
3
|
+
|
|
4
|
+
function commitChanges(backendPath, apiModuleName) {
|
|
5
|
+
const apiModulePath = path.join(path.dirname(backendPath), 'src', 'integrations', `${apiModuleName}Integration.js`);
|
|
6
|
+
try {
|
|
7
|
+
execSync(`git add ${apiModulePath}`);
|
|
8
|
+
execSync(`git commit -m "Add ${apiModuleName}Integration to ${apiModuleName}Integration.js"`);
|
|
9
|
+
} catch (error) {
|
|
10
|
+
throw new Error('Failed to commit changes:', error);
|
|
11
|
+
}
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
module.exports = {
|
|
15
|
+
commitChanges,
|
|
16
|
+
};
|
|
@@ -0,0 +1,134 @@
|
|
|
1
|
+
const fs = require('fs');
|
|
2
|
+
const dotenv = require('dotenv');
|
|
3
|
+
const { readFileSync, writeFileSync, existsSync } = require('fs');
|
|
4
|
+
const { logInfo } = require('./logger');
|
|
5
|
+
const { resolve } = require('node:path');
|
|
6
|
+
const inquirer = require('inquirer');
|
|
7
|
+
|
|
8
|
+
const { parse } = require('@babel/parser');
|
|
9
|
+
const traverse = require('@babel/traverse').default;
|
|
10
|
+
|
|
11
|
+
const extractRawEnvVariables = (modulePath) => {
|
|
12
|
+
const filePath = resolve(modulePath, 'definition.js');
|
|
13
|
+
|
|
14
|
+
const fileContent = fs.readFileSync(filePath, 'utf-8');
|
|
15
|
+
const ast = parse(fileContent, {
|
|
16
|
+
sourceType: 'module',
|
|
17
|
+
plugins: ['jsx', 'typescript'], // Add more plugins if needed
|
|
18
|
+
});
|
|
19
|
+
|
|
20
|
+
const envVariables = {};
|
|
21
|
+
|
|
22
|
+
traverse(ast, {
|
|
23
|
+
ObjectProperty(path) {
|
|
24
|
+
if (path.node.key.name === 'env') {
|
|
25
|
+
path.node.value.properties.forEach((prop) => {
|
|
26
|
+
const key = prop.key.name;
|
|
27
|
+
if (prop.value.type === 'MemberExpression') {
|
|
28
|
+
const property = prop.value.property.name;
|
|
29
|
+
envVariables[key] = `${property}`;
|
|
30
|
+
} else if (prop.value.type === 'TemplateLiteral') {
|
|
31
|
+
// Handle template literals
|
|
32
|
+
const expressions = prop.value.expressions.map((exp) =>
|
|
33
|
+
exp.type === 'MemberExpression'
|
|
34
|
+
? `${exp.property.name}`
|
|
35
|
+
: exp.name
|
|
36
|
+
);
|
|
37
|
+
envVariables[key] = expressions.join('');
|
|
38
|
+
}
|
|
39
|
+
});
|
|
40
|
+
}
|
|
41
|
+
},
|
|
42
|
+
});
|
|
43
|
+
|
|
44
|
+
return envVariables;
|
|
45
|
+
};
|
|
46
|
+
const handleEnvVariables = async (backendPath, modulePath) => {
|
|
47
|
+
logInfo('Searching for missing environment variables...');
|
|
48
|
+
const Definition = { env: extractRawEnvVariables(modulePath) };
|
|
49
|
+
if (Definition && Definition.env) {
|
|
50
|
+
console.log('Here is Definition.env:', Definition.env);
|
|
51
|
+
const envVars = Object.values(Definition.env);
|
|
52
|
+
|
|
53
|
+
console.log(
|
|
54
|
+
'Found the following environment variables in the API module:',
|
|
55
|
+
envVars
|
|
56
|
+
);
|
|
57
|
+
|
|
58
|
+
const localEnvPath = resolve(backendPath, '../.env');
|
|
59
|
+
const localDevConfigPath = resolve(
|
|
60
|
+
backendPath,
|
|
61
|
+
'../src/configs/dev.json'
|
|
62
|
+
);
|
|
63
|
+
|
|
64
|
+
// Load local .env variables
|
|
65
|
+
let localEnvVars = {};
|
|
66
|
+
if (existsSync(localEnvPath)) {
|
|
67
|
+
localEnvVars = dotenv.parse(readFileSync(localEnvPath, 'utf8'));
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
// Load local dev.json variables
|
|
71
|
+
let localDevConfig = {};
|
|
72
|
+
if (existsSync(localDevConfigPath)) {
|
|
73
|
+
localDevConfig = JSON.parse(
|
|
74
|
+
readFileSync(localDevConfigPath, 'utf8')
|
|
75
|
+
);
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
const missingEnvVars = envVars.filter(
|
|
79
|
+
(envVar) => !localEnvVars[envVar] && !localDevConfig[envVar]
|
|
80
|
+
);
|
|
81
|
+
|
|
82
|
+
logInfo(`Missing environment variables: ${missingEnvVars.join(', ')}`);
|
|
83
|
+
|
|
84
|
+
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
|
+
]);
|
|
94
|
+
|
|
95
|
+
if (addEnvVars) {
|
|
96
|
+
const envValues = {};
|
|
97
|
+
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
|
+
]);
|
|
105
|
+
envValues[envVar] = value;
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
// Add the envValues to the local .env file if it exists
|
|
109
|
+
if (existsSync(localEnvPath)) {
|
|
110
|
+
const envContent = Object.entries(envValues)
|
|
111
|
+
.map(([key, value]) => `${key}=${value}`)
|
|
112
|
+
.join('\n');
|
|
113
|
+
fs.appendFileSync(localEnvPath, `\n${envContent}`);
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
// Add the envValues to the local dev.json file if it exists
|
|
117
|
+
if (existsSync(localDevConfigPath)) {
|
|
118
|
+
const updatedDevConfig = {
|
|
119
|
+
...localDevConfig,
|
|
120
|
+
...envValues,
|
|
121
|
+
};
|
|
122
|
+
writeFileSync(
|
|
123
|
+
localDevConfigPath,
|
|
124
|
+
JSON.stringify(updatedDevConfig, null, 2)
|
|
125
|
+
);
|
|
126
|
+
}
|
|
127
|
+
} else {
|
|
128
|
+
logInfo("Edit whenever you're able, safe travels friend!");
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
};
|
|
133
|
+
|
|
134
|
+
module.exports = { handleEnvVariables };
|
|
@@ -0,0 +1,86 @@
|
|
|
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
|
+
});
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
const { Command } = require('commander');
|
|
4
|
+
const { installCommand } = require('./installCommand');
|
|
5
|
+
|
|
6
|
+
const program = new Command();
|
|
7
|
+
program
|
|
8
|
+
.command('install [apiModuleName]')
|
|
9
|
+
.description('Install an API module')
|
|
10
|
+
.action(installCommand);
|
|
11
|
+
|
|
12
|
+
program.parse(process.argv);
|
|
13
|
+
|
|
14
|
+
module.exports = { installCommand };
|
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
const { Command } = require('commander');
|
|
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');
|
|
10
|
+
|
|
11
|
+
describe('CLI Command Tests', () => {
|
|
12
|
+
it('should successfully install an API module when all steps complete without errors', async () => {
|
|
13
|
+
const mockApiModuleName = 'testModule';
|
|
14
|
+
const mockPackageName = `@friggframework/api-module-${mockApiModuleName}`;
|
|
15
|
+
const mockBackendPath = '/mock/backend/path';
|
|
16
|
+
|
|
17
|
+
jest.mock('./validatePackage', () => ({
|
|
18
|
+
validatePackageExists: jest.fn().mockResolvedValue(true),
|
|
19
|
+
}));
|
|
20
|
+
jest.mock('./backendPath', () => ({
|
|
21
|
+
findNearestBackendPackageJson: jest.fn().mockReturnValue(mockBackendPath),
|
|
22
|
+
validateBackendPath: jest.fn().mockReturnValue(true),
|
|
23
|
+
}));
|
|
24
|
+
jest.mock('./installPackage', () => ({
|
|
25
|
+
installPackage: jest.fn().mockReturnValue(true),
|
|
26
|
+
}));
|
|
27
|
+
jest.mock('./integrationFile', () => ({
|
|
28
|
+
createIntegrationFile: jest.fn().mockReturnValue(true),
|
|
29
|
+
}));
|
|
30
|
+
jest.mock('./backendJs', () => ({
|
|
31
|
+
updateBackendJsFile: jest.fn().mockReturnValue(true),
|
|
32
|
+
}));
|
|
33
|
+
jest.mock('./commitChanges', () => ({
|
|
34
|
+
commitChanges: jest.fn().mockReturnValue(true),
|
|
35
|
+
}));
|
|
36
|
+
jest.mock('./logger', () => ({
|
|
37
|
+
logInfo: jest.fn(),
|
|
38
|
+
logError: jest.fn(),
|
|
39
|
+
}));
|
|
40
|
+
|
|
41
|
+
const program = new Command();
|
|
42
|
+
program
|
|
43
|
+
.command('install <apiModuleName>')
|
|
44
|
+
.description('Install an API module')
|
|
45
|
+
.action(installCommand);
|
|
46
|
+
|
|
47
|
+
await program.parseAsync(['node', 'install', mockApiModuleName]);
|
|
48
|
+
|
|
49
|
+
expect(validatePackageExists).toHaveBeenCalledWith(mockPackageName);
|
|
50
|
+
expect(findNearestBackendPackageJson).toHaveBeenCalled();
|
|
51
|
+
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
|
+
});
|
|
58
|
+
|
|
59
|
+
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
|
+
});
|
|
65
|
+
|
|
66
|
+
const program = new Command();
|
|
67
|
+
program
|
|
68
|
+
.command('install <apiModuleName>')
|
|
69
|
+
.description('Install an API module')
|
|
70
|
+
.action(installCommand);
|
|
71
|
+
|
|
72
|
+
await program.parseAsync(['node', 'install', 'nonexistent-package']);
|
|
73
|
+
|
|
74
|
+
expect(mockValidatePackageExists).toHaveBeenCalledWith('@friggframework/api-module-nonexistent-package');
|
|
75
|
+
expect(mockLogError).toHaveBeenCalledWith('An error occurred:', expect.any(Error));
|
|
76
|
+
expect(mockExit).toHaveBeenCalledWith(1);
|
|
77
|
+
|
|
78
|
+
mockExit.mockRestore();
|
|
79
|
+
mockLogError.mockRestore();
|
|
80
|
+
mockValidatePackageExists.mockRestore();
|
|
81
|
+
});
|
|
82
|
+
|
|
83
|
+
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
|
+
});
|
|
91
|
+
|
|
92
|
+
const program = new Command();
|
|
93
|
+
program
|
|
94
|
+
.command('install <apiModuleName>')
|
|
95
|
+
.description('Install an API module')
|
|
96
|
+
.action(installCommand);
|
|
97
|
+
|
|
98
|
+
await program.parseAsync(['node', 'install', 'test-module']);
|
|
99
|
+
|
|
100
|
+
expect(mockLogError).toHaveBeenCalledWith('An error occurred:', expect.any(Error));
|
|
101
|
+
expect(mockProcessExit).toHaveBeenCalledWith(1);
|
|
102
|
+
|
|
103
|
+
mockLogError.mockRestore();
|
|
104
|
+
mockProcessExit.mockRestore();
|
|
105
|
+
mockValidatePackageExists.mockRestore();
|
|
106
|
+
mockFindNearestBackendPackageJson.mockRestore();
|
|
107
|
+
mockValidateBackendPath.mockRestore();
|
|
108
|
+
});
|
|
109
|
+
});
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
const { installPackage } = require('./installPackage');
|
|
2
|
+
const { createIntegrationFile } = require('./integrationFile');
|
|
3
|
+
const { resolve } = require('node:path');
|
|
4
|
+
const { updateBackendJsFile } = require('./backendJs');
|
|
5
|
+
const { logInfo, logError } = require('./logger');
|
|
6
|
+
const { commitChanges } = require('./commitChanges');
|
|
7
|
+
const {
|
|
8
|
+
findNearestBackendPackageJson,
|
|
9
|
+
validateBackendPath,
|
|
10
|
+
} = require('./backendPath');
|
|
11
|
+
const { handleEnvVariables } = require('./environmentVariables');
|
|
12
|
+
const {
|
|
13
|
+
validatePackageExists,
|
|
14
|
+
searchAndSelectPackage,
|
|
15
|
+
} = require('./validatePackage');
|
|
16
|
+
|
|
17
|
+
const installCommand = async (apiModuleName) => {
|
|
18
|
+
try {
|
|
19
|
+
const packageNames = await searchAndSelectPackage(apiModuleName);
|
|
20
|
+
if (!packageNames || packageNames.length === 0) return;
|
|
21
|
+
|
|
22
|
+
const backendPath = findNearestBackendPackageJson();
|
|
23
|
+
validateBackendPath(backendPath);
|
|
24
|
+
|
|
25
|
+
for (const packageName of packageNames) {
|
|
26
|
+
await validatePackageExists(packageName);
|
|
27
|
+
installPackage(backendPath, packageName);
|
|
28
|
+
|
|
29
|
+
const modulePath = resolve(
|
|
30
|
+
backendPath,
|
|
31
|
+
`../../node_modules/${packageName}`
|
|
32
|
+
);
|
|
33
|
+
const {
|
|
34
|
+
Config: { label },
|
|
35
|
+
Api: ApiClass,
|
|
36
|
+
} = require(modulePath);
|
|
37
|
+
|
|
38
|
+
const sanitizedLabel = label.replace(
|
|
39
|
+
/[<>:"/\\|?*\x00-\x1F\s]/g,
|
|
40
|
+
''
|
|
41
|
+
); // Remove invalid characters and spaces console.log('Installing integration for:', sanitizedLabel);
|
|
42
|
+
createIntegrationFile(backendPath, sanitizedLabel, ApiClass);
|
|
43
|
+
updateBackendJsFile(backendPath, sanitizedLabel);
|
|
44
|
+
commitChanges(backendPath, sanitizedLabel);
|
|
45
|
+
logInfo(
|
|
46
|
+
`Successfully installed ${packageName} and updated the project.`
|
|
47
|
+
);
|
|
48
|
+
|
|
49
|
+
await handleEnvVariables(backendPath, modulePath);
|
|
50
|
+
}
|
|
51
|
+
} catch (error) {
|
|
52
|
+
logError('An error occurred:', error);
|
|
53
|
+
process.exit(1);
|
|
54
|
+
}
|
|
55
|
+
};
|
|
56
|
+
|
|
57
|
+
module.exports = { installCommand };
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
const { execSync } = require('child_process');
|
|
2
|
+
const path = require('path');
|
|
3
|
+
|
|
4
|
+
function installPackage(backendPath, packageName) {
|
|
5
|
+
execSync(`npm install ${packageName}`, {
|
|
6
|
+
cwd: path.dirname(backendPath),
|
|
7
|
+
stdio: 'inherit',
|
|
8
|
+
});
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
module.exports = {
|
|
12
|
+
installPackage,
|
|
13
|
+
};
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
const fs = require('fs-extra');
|
|
2
|
+
const path = require('path');
|
|
3
|
+
const { logInfo } = require('./logger');
|
|
4
|
+
const { getIntegrationTemplate } = require('./template');
|
|
5
|
+
const INTEGRATIONS_DIR = 'src/integrations';
|
|
6
|
+
|
|
7
|
+
function createIntegrationFile(backendPath, apiModuleName, ApiClass) {
|
|
8
|
+
const integrationDir = path.join(
|
|
9
|
+
path.dirname(backendPath),
|
|
10
|
+
INTEGRATIONS_DIR
|
|
11
|
+
);
|
|
12
|
+
logInfo(`Ensuring directory exists: ${integrationDir}`);
|
|
13
|
+
fs.ensureDirSync(integrationDir);
|
|
14
|
+
|
|
15
|
+
const integrationFilePath = path.join(
|
|
16
|
+
integrationDir,
|
|
17
|
+
`${apiModuleName}Integration.js`
|
|
18
|
+
);
|
|
19
|
+
logInfo(`Writing integration file: ${integrationFilePath}`);
|
|
20
|
+
const integrationTemplate = getIntegrationTemplate(
|
|
21
|
+
apiModuleName,
|
|
22
|
+
backendPath,
|
|
23
|
+
ApiClass
|
|
24
|
+
);
|
|
25
|
+
fs.writeFileSync(integrationFilePath, integrationTemplate);
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
module.exports = {
|
|
29
|
+
createIntegrationFile,
|
|
30
|
+
};
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
const path = require('path');
|
|
2
|
+
|
|
3
|
+
function getIntegrationTemplate(apiModuleName, backendPath, ApiClass) {
|
|
4
|
+
// Find the sample data method
|
|
5
|
+
const apiMethods = Object.getOwnPropertyNames(ApiClass.prototype);
|
|
6
|
+
const sampleDataMethod =
|
|
7
|
+
apiMethods.find(
|
|
8
|
+
(method) =>
|
|
9
|
+
method.toLowerCase().includes('search') ||
|
|
10
|
+
method.toLowerCase().includes('list') ||
|
|
11
|
+
method.toLowerCase().includes('get')
|
|
12
|
+
) || 'searchDeals';
|
|
13
|
+
|
|
14
|
+
return `const { get, IntegrationBase, Options } = require('@friggframework/core');
|
|
15
|
+
const { Definition: ${apiModuleName}Module, Config: defaultConfig } = require('@friggframework/api-module-${apiModuleName.toLowerCase()}');
|
|
16
|
+
|
|
17
|
+
class ${apiModuleName}Integration extends IntegrationBase {
|
|
18
|
+
static Config = {
|
|
19
|
+
name: defaultConfig.name || '${apiModuleName.toLowerCase()}',
|
|
20
|
+
version: '1.0.0',
|
|
21
|
+
supportedVersions: ['1.0.0'],
|
|
22
|
+
events: ['SEARCH_DEALS'],
|
|
23
|
+
};
|
|
24
|
+
|
|
25
|
+
static Options =
|
|
26
|
+
new Options({
|
|
27
|
+
module: ${apiModuleName}Module,
|
|
28
|
+
integrations: [${apiModuleName}Module],
|
|
29
|
+
display: {
|
|
30
|
+
name: defaultConfig.displayName || '${apiModuleName}',
|
|
31
|
+
description: defaultConfig.description || 'Sales & CRM, Marketing',
|
|
32
|
+
category: defaultConfig.category || 'Sales & CRM, Marketing',
|
|
33
|
+
detailsUrl: defaultConfig.detailsUrl || 'https://www.${apiModuleName.toLowerCase()}.com',
|
|
34
|
+
icon: defaultConfig.icon || 'https://friggframework.org/assets/img/${apiModuleName.toLowerCase()}.jpeg',
|
|
35
|
+
},
|
|
36
|
+
hasUserConfig: true,
|
|
37
|
+
});
|
|
38
|
+
|
|
39
|
+
static modules = {
|
|
40
|
+
${apiModuleName.toLowerCase()}: ${apiModuleName}Module,
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
/**
|
|
44
|
+
* HANDLE EVENTS
|
|
45
|
+
*/
|
|
46
|
+
async receiveNotification(notifier, event, object = null) {
|
|
47
|
+
if (event === 'SEARCH_DEALS') {
|
|
48
|
+
return this.target.api.searchDeals(object);
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
/**
|
|
53
|
+
* ALL CUSTOM/OPTIONAL METHODS FOR AN INTEGRATION MANAGER
|
|
54
|
+
*/
|
|
55
|
+
async getSampleData() {
|
|
56
|
+
const res = await this.target.api.${sampleDataMethod}();
|
|
57
|
+
return { data: res };
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
/**
|
|
61
|
+
* ALL REQUIRED METHODS FOR AN INTEGRATION MANAGER
|
|
62
|
+
*/
|
|
63
|
+
async onCreate(params) {
|
|
64
|
+
// Validate that we have all of the data we need
|
|
65
|
+
// Set integration status as makes sense. Default ENABLED
|
|
66
|
+
// TODO turn this into a validateConfig method/function
|
|
67
|
+
this.record.status = 'ENABLED';
|
|
68
|
+
await this.record.save();
|
|
69
|
+
return this.record;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
async onUpdate(params) {
|
|
73
|
+
const newConfig = get(params, 'config');
|
|
74
|
+
const oldConfig = this.record.config;
|
|
75
|
+
// Just save whatever
|
|
76
|
+
this.record.markModified('config');
|
|
77
|
+
await this.record.save();
|
|
78
|
+
return this.validateConfig();
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
async getConfigOptions() {
|
|
82
|
+
const options = {}
|
|
83
|
+
return options;
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
module.exports = ${apiModuleName}Integration;`;
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
module.exports = { getIntegrationTemplate };
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
const { execSync } = require('child_process');
|
|
2
|
+
const axios = require('axios');
|
|
3
|
+
const { logError } = require('./logger');
|
|
4
|
+
const inquirer = require('inquirer');
|
|
5
|
+
|
|
6
|
+
async function searchPackages(apiModuleName) {
|
|
7
|
+
const searchCommand = `npm search @friggframework/api-module-${apiModuleName} --json`;
|
|
8
|
+
const result = execSync(searchCommand, { encoding: 'utf8' });
|
|
9
|
+
return JSON.parse(result);
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
async function checkPackageExists(packageName) {
|
|
13
|
+
try {
|
|
14
|
+
const response = await axios.get(
|
|
15
|
+
`https://registry.npmjs.org/${packageName}`
|
|
16
|
+
);
|
|
17
|
+
return response.status === 200;
|
|
18
|
+
} catch (error) {
|
|
19
|
+
return false;
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
async function validatePackageExists(packageName) {
|
|
24
|
+
const packageExists = await checkPackageExists(packageName);
|
|
25
|
+
if (!packageExists) {
|
|
26
|
+
throw new Error(`Package ${packageName} does not exist on npm.`);
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
const searchAndSelectPackage = async (apiModuleName) => {
|
|
31
|
+
const searchResults = await searchPackages(apiModuleName || '');
|
|
32
|
+
|
|
33
|
+
if (searchResults.length === 0) {
|
|
34
|
+
logError(`No packages found matching ${apiModuleName}`);
|
|
35
|
+
process.exit(1);
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
const filteredResults = searchResults.filter((pkg) => {
|
|
39
|
+
const version = pkg.version
|
|
40
|
+
? pkg.version.split('.').map(Number)
|
|
41
|
+
: [];
|
|
42
|
+
return version[0] >= 1;
|
|
43
|
+
});
|
|
44
|
+
|
|
45
|
+
if (filteredResults.length === 0) {
|
|
46
|
+
const earlierVersions = searchResults
|
|
47
|
+
.map((pkg) => `${pkg.name} (${pkg.version})`)
|
|
48
|
+
.join(', ');
|
|
49
|
+
logError(
|
|
50
|
+
`No packages found with version 1.0.0 or above for ${apiModuleName}. Found earlier versions: ${earlierVersions}`
|
|
51
|
+
);
|
|
52
|
+
process.exit(1);
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
const choices = filteredResults.map((pkg) => {
|
|
56
|
+
return {
|
|
57
|
+
name: `${pkg.name} (${pkg.version})`,
|
|
58
|
+
checked: filteredResults.length === 1, // Automatically select if only one result
|
|
59
|
+
};
|
|
60
|
+
});
|
|
61
|
+
|
|
62
|
+
const { selectedPackages } = await inquirer.prompt([
|
|
63
|
+
{
|
|
64
|
+
type: 'checkbox',
|
|
65
|
+
name: 'selectedPackages',
|
|
66
|
+
message: 'Select the packages to install:',
|
|
67
|
+
choices,
|
|
68
|
+
},
|
|
69
|
+
]);
|
|
70
|
+
|
|
71
|
+
return selectedPackages.map(choice => choice.split(' ')[0]);
|
|
72
|
+
};
|
|
73
|
+
|
|
74
|
+
module.exports = {
|
|
75
|
+
validatePackageExists,
|
|
76
|
+
checkPackageExists,
|
|
77
|
+
searchPackages,
|
|
78
|
+
searchAndSelectPackage,
|
|
79
|
+
};
|
package/package.json
CHANGED
|
@@ -1,26 +1,36 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@friggframework/devtools",
|
|
3
3
|
"prettier": "@friggframework/prettier-config",
|
|
4
|
-
"version": "1.1.
|
|
4
|
+
"version": "1.1.7--canary.359.1946bb6.0",
|
|
5
5
|
"dependencies": {
|
|
6
6
|
"@babel/eslint-parser": "^7.18.9",
|
|
7
|
-
"@
|
|
8
|
-
"@
|
|
7
|
+
"@babel/parser": "^7.25.3",
|
|
8
|
+
"@babel/traverse": "^7.25.3",
|
|
9
|
+
"@friggframework/core": "1.1.7--canary.359.1946bb6.0",
|
|
10
|
+
"@friggframework/test": "1.1.7--canary.359.1946bb6.0",
|
|
11
|
+
"axios": "^1.7.2",
|
|
12
|
+
"commander": "^12.1.0",
|
|
13
|
+
"dotenv": "^16.4.5",
|
|
9
14
|
"eslint": "^8.22.0",
|
|
10
15
|
"eslint-config-prettier": "^8.5.0",
|
|
11
16
|
"eslint-plugin-json": "^3.1.0",
|
|
12
17
|
"eslint-plugin-markdown": "^3.0.0",
|
|
13
18
|
"eslint-plugin-no-only-tests": "^3.0.0",
|
|
14
|
-
"eslint-plugin-yaml": "^0.5.0"
|
|
19
|
+
"eslint-plugin-yaml": "^0.5.0",
|
|
20
|
+
"fs-extra": "^11.2.0",
|
|
21
|
+
"inquirer": "^10.1.6"
|
|
15
22
|
},
|
|
16
23
|
"devDependencies": {
|
|
17
|
-
"@friggframework/eslint-config": "
|
|
18
|
-
"@friggframework/prettier-config": "
|
|
24
|
+
"@friggframework/eslint-config": "1.1.7--canary.359.1946bb6.0",
|
|
25
|
+
"@friggframework/prettier-config": "1.1.7--canary.359.1946bb6.0"
|
|
19
26
|
},
|
|
20
27
|
"scripts": {
|
|
21
28
|
"lint:fix": "prettier --write --loglevel error . && eslint . --fix",
|
|
22
29
|
"test": "jest --passWithNoTests # TODO"
|
|
23
30
|
},
|
|
31
|
+
"bin": {
|
|
32
|
+
"frigg": "./frigg-cli/index.js"
|
|
33
|
+
},
|
|
24
34
|
"author": "",
|
|
25
35
|
"license": "MIT",
|
|
26
36
|
"main": "index.js",
|
|
@@ -36,5 +46,5 @@
|
|
|
36
46
|
"publishConfig": {
|
|
37
47
|
"access": "public"
|
|
38
48
|
},
|
|
39
|
-
"gitHead": "
|
|
49
|
+
"gitHead": "1946bb6a964605c8b3482683bb53eed8d6ee2da6"
|
|
40
50
|
}
|
|
@@ -52,7 +52,7 @@ function testAutherDefinition(definition, mocks) {
|
|
|
52
52
|
authCallbackParams = mocks.authorizeResponse || mocks.authorizeParams;
|
|
53
53
|
describe('getAuthorizationRequirements() test', () => {
|
|
54
54
|
it('should return auth requirements', async () => {
|
|
55
|
-
requirements = module.getAuthorizationRequirements();
|
|
55
|
+
requirements = await module.getAuthorizationRequirements();
|
|
56
56
|
expect(requirements).toBeDefined();
|
|
57
57
|
expect(requirements.type).toEqual(ModuleConstants.authType.oauth2);
|
|
58
58
|
expect(requirements.url).toBeDefined();
|
package/test/mock-integration.js
CHANGED
|
@@ -1,10 +1,9 @@
|
|
|
1
|
-
const { Auther, Credential, Entity,
|
|
1
|
+
const { Auther, Credential, Entity, IntegrationFactory, createObjectId } = require('@friggframework/core');
|
|
2
2
|
|
|
3
3
|
|
|
4
4
|
async function createMockIntegration(IntegrationClassDef, userId = null, config = {},) {
|
|
5
|
-
const
|
|
6
|
-
userId = userId ||
|
|
7
|
-
integration.delegateTypes.push(...IntegrationClassDef.Config.events)
|
|
5
|
+
const integrationFactory = new IntegrationFactory([IntegrationClassDef]);
|
|
6
|
+
userId = userId || createObjectId();
|
|
8
7
|
|
|
9
8
|
const insertOptions = {
|
|
10
9
|
new: true,
|
|
@@ -42,11 +41,13 @@ async function createMockIntegration(IntegrationClassDef, userId = null, config
|
|
|
42
41
|
);
|
|
43
42
|
|
|
44
43
|
const entities = [entity1, entity2]
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
44
|
+
|
|
45
|
+
const integration =
|
|
46
|
+
await integrationFactory.createIntegration(
|
|
47
|
+
entities,
|
|
48
|
+
userId,
|
|
49
|
+
config,
|
|
50
|
+
);
|
|
50
51
|
|
|
51
52
|
integration.id = integration.record._id
|
|
52
53
|
|