Skip to content

Commit

Permalink
Merge pull request #57 from Pythagora-io/feat/js-code-processing
Browse files Browse the repository at this point in the history
Integrate js-code-processing module to project
  • Loading branch information
LeonOstrez authored Aug 31, 2023
2 parents 1686bad + ba2cc43 commit 092aafb
Show file tree
Hide file tree
Showing 27 changed files with 130 additions and 1,361 deletions.
4 changes: 1 addition & 3 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -27,10 +27,8 @@
"node": ">=14.x"
},
"dependencies": {
"@babel/generator": "^7.22.3",
"@babel/parser": "^7.22.3",
"@babel/traverse": "^7.22.1",
"@mswjs/interceptors": "^0.19.2",
"@pythagora.io/js-code-processing": "^0.0.4",
"axios": "^1.2.2",
"blessed": "^0.1.81",
"body-parser": "^1.20.1",
Expand Down
6 changes: 2 additions & 4 deletions src/Pythagora.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,10 +9,8 @@ const {
PYTHAGORA_TESTS_DIR,
PYTHAGORA_METADATA_DIR,
METADATA_FILENAME,
PYTHAGORA_DELIMITER,
EXPORTED_TESTS_DIR,
EXPORTED_TESTS_DATA_DIR
} = require('./const/common.js');
PYTHAGORA_DELIMITER
} = require("@pythagora.io/js-code-processing").common;

let { BatchInterceptor } = require('@mswjs/interceptors');
let nodeInterceptors = require('@mswjs/interceptors/lib/presets/node.js');
Expand Down
2 changes: 1 addition & 1 deletion src/RunPythagoraTests.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
const { logTestsFinished, logTestStarting, logTestsStarting } = require('./utils/cmdPrint.js');
const { makeTestRequest } = require('./helpers/testing.js');
const { getCircularReplacer } = require('./utils/common.js')
const { PYTHAGORA_METADATA_DIR, REVIEW_DATA_FILENAME, PYTHAGORA_DELIMITER, PYTHAGORA_TESTS_DIR } = require('./const/common.js');
const { PYTHAGORA_METADATA_DIR, REVIEW_DATA_FILENAME, PYTHAGORA_DELIMITER, PYTHAGORA_TESTS_DIR } = require("@pythagora.io/js-code-processing").common;

const fs = require('fs');
const path = require('path');
Expand Down
2 changes: 1 addition & 1 deletion src/bin/postinstall.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ const fs = require('fs');
const crypto = require('crypto');
const path = require('path');
const { v4: uuidv4 } = require('uuid');
const {PYTHAGORA_METADATA_DIR, CONFIG_FILENAME, PYTHAGORA_API_SERVER} = require("../const/common");
const {PYTHAGORA_METADATA_DIR, CONFIG_FILENAME, PYTHAGORA_API_SERVER} = require("@pythagora.io/js-code-processing").common;
const packageJson = require('../../package.json');

const pythagoraVersion = packageJson.version;
Expand Down
1 change: 1 addition & 0 deletions src/bin/run.js
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@ exec(bashCommand, (error, stdout, stderr) => {
const pythagoraRoot = process.argv[1].split('node_modules')[0];
const args = process.argv.slice(2);


// Run the bash script and forward all arguments
const child = spawn(bashPath, [bashScript, ...['--pythagora-dir', pythagoraDir, '--pythagora-root', pythagoraRoot, ...args]], { stdio: 'inherit' });

Expand Down
14 changes: 7 additions & 7 deletions src/commands/export.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,18 +4,16 @@ const {
EXPORTED_TESTS_DIR,
PYTHAGORA_METADATA_DIR,
EXPORT_METADATA_FILENAME,
} = require('../const/common');
} = require("@pythagora.io/js-code-processing").common;
const { getAllGeneratedTests } = require("../utils/common");
const { convertOldTestForGPT } = require("../utils/legacy");
const { setUpPythagoraDirs } = require("../helpers/starting");
const {
logAndExit,
testEligibleForExportLog,
} = require("../utils/cmdPrint");
const {
isEligibleForExport,
checkForAPIKey
} = require("../helpers/api");
const {getApiConfig} = require("../helpers/api");
const {API} = require("@pythagora.io/js-code-processing");
const args = require('../utils/getArgs.js');
const {
createDefaultFiles,
Expand All @@ -25,7 +23,6 @@ const {
} = require('../helpers/exports');

async function runExport() {
checkForAPIKey();
setUpPythagoraDirs();
cleanupDataFolder();
let exportsMetadata = JSON.parse(fs.readFileSync(path.resolve(args.pythagora_root, PYTHAGORA_METADATA_DIR, EXPORT_METADATA_FILENAME)));
Expand All @@ -42,6 +39,9 @@ async function runExport() {
await exportTest(originalTest, exportsMetadata);
}
else {
const { apiUrl, apiKey, apiKeyType } = getApiConfig();
const Api = new API(apiUrl, apiKey, apiKeyType);

for (let originalTest of generatedTests) {
if (originalTest.method === 'OPTIONS') continue;
if (testExists(exportsMetadata, originalTest.id)) {
Expand All @@ -50,7 +50,7 @@ async function runExport() {
}

let test = convertOldTestForGPT(originalTest);
const isEligible = await isEligibleForExport(test);
const isEligible = await Api.isEligibleForExport(test);

if (isEligible) {
await exportTest(originalTest, exportsMetadata);
Expand Down
2 changes: 1 addition & 1 deletion src/commands/jest.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
const jest = require('jest');
const {EXPORTED_TESTS_DIR, EXPORTED_TESTS_DATA_DIR, SRC_TO_ROOT} = require("../const/common");
const {EXPORTED_TESTS_DIR, EXPORTED_TESTS_DATA_DIR, SRC_TO_ROOT} = require("@pythagora.io/js-code-processing").common;
let fs = require('fs');
const pythagoraJestMethods = require("../helpers/jestMethods");
const {primeJestLog} = require("../utils/cmdPrint");
Expand Down
18 changes: 0 additions & 18 deletions src/const/common.js

This file was deleted.

176 changes: 7 additions & 169 deletions src/helpers/api.js
Original file line number Diff line number Diff line change
@@ -1,174 +1,12 @@
const _ = require('lodash');
const axios = require('axios');
const { jestAuthFileGenerationLog } = require('../utils/cmdPrint');
const { bold, reset, red, blue } = require('../utils/cmdPrint').colors;
const args = require('../utils/getArgs.js');
const {PYTHAGORA_UNIT_TESTS_VERSION,PYTHAGORA_API_SERVER} = require('../const/common');
const API_SERVER = args.pythagora_api_server || PYTHAGORA_API_SERVER;
const {PYTHAGORA_API_SERVER} = require("@pythagora.io/js-code-processing").common;

function extractGPTMessageFromStreamData(input) {
const regex = /data: (.*?)\n/g;
const substrings = [];
let match;

while ((match = regex.exec(input)) !== null) {
substrings.push(match[1]);
function getApiConfig() {
return {
apiUrl: args.pythagora_api_server || PYTHAGORA_API_SERVER,
apiKey: args.openai_api_key || args.pythagora_api_key,
apiKeyType: args.openai_api_key ? 'openai' : 'pythagora'
}

return substrings.map(s => JSON.parse(s));
}

function setOptions({path, method, headers}) {
let apiKey = args.openai_api_key || args.pythagora_api_key;
const parsedUrl = new URL(API_SERVER);
if (!apiKey) throw new Error('No API key provided. Please add --openai-api-key or --pythagora-api-key')
let options = {
protocol: parsedUrl.protocol.replace(':', ''),
hostname: parsedUrl.hostname,
port: parsedUrl.port,
path: path || '/',
method: method || 'POST',
headers: headers || {
'Content-Type': 'application/json',
'apikey': apiKey,
'apikeytype': args.openai_api_key ? 'openai' : 'pythagora'
},
};

if (!options.port) delete options.port;
return options
}

async function makeRequest(data, options, customLogFunction) {
let gptResponse = '';
let httpModule = options.protocol === 'http' ? require('http') : require('https');
let timeout;

return new Promise((resolve, reject) => {
const req = httpModule.request(_.omit(options, ['protocol']), function(res){
res.on('data', (chunk) => {
try {
clearTimeout(timeout);
timeout = setTimeout(() => {
reject(new Error("Request timeout"));
}, 30000);

let stringified = chunk.toString();
try {
let json = JSON.parse(stringified);
if (json.error || json.message) {
gptResponse = json;
return;
}
} catch (e) {}

gptResponse += stringified;
if (gptResponse.indexOf('pythagora_end:') > -1) return;
if (customLogFunction) customLogFunction(gptResponse);
else process.stdout.write(stringified);
} catch (e) {}
});
res.on('end', async function () {
clearTimeout(timeout);

process.stdout.write('\n');
if (res.statusCode >= 400) return reject(new Error(`Response status code: ${res.statusCode}. Error message: ${gptResponse}`));
if (gptResponse.error) return reject(new Error(`Error: ${gptResponse.error.message}. Code: ${gptResponse.error.code}`));
if (gptResponse.message) return reject(new Error(`Error: ${gptResponse.message}. Code: ${gptResponse.code}`));
gptResponse = gptResponse.split('pythagora_end:').pop();
return resolve(gptResponse);
});
});

req.on('error', (e) => {
clearTimeout(timeout);
console.error("problem with request:"+e.message);
reject(e);
});

req.write(data);

req.end();
});
}

async function getUnitTests(data, customLogFunction) {
let options = setOptions({path: '/api/generate-unit-tests'});
let tests, error;
try {
tests = await makeRequest(JSON.stringify(data), options, customLogFunction);
} catch (e) {
error = e;
} finally {
return {tests, error};
}
}

async function expandUnitTests(data, customLogFunction) {
let options = setOptions({path: '/api/expand-unit-tests'});
let tests, error;
try {
tests = await makeRequest(JSON.stringify(data), options, customLogFunction);
} catch (e) {
error = e;
} finally {
return {tests, error};
}
}

async function getJestAuthFunction(loginMongoQueriesArray, loginRequestBody, loginEndpointPath) {
jestAuthFileGenerationLog();

let options = setOptions({path: '/api/generate-jest-auth'});
return await makeRequest(JSON.stringify({loginMongoQueriesArray, loginRequestBody, loginEndpointPath}), options);
}


async function getJestTest(test) {
let options = setOptions({path: '/api/generate-jest-test'});
return await makeRequest(JSON.stringify(test), options);
}

async function getJestTestName(test, usedNames) {
let options = setOptions({path:'/api/generate-jest-test-name'});
return await makeRequest(JSON.stringify({ test }), options);
}

async function isEligibleForExport(test) {
try {
let options = setOptions({ path: '/api/check-if-eligible' });

const response = await axios.post(
`${options.protocol}://${options.hostname}${options.port ? ':' + options.port : ''}${options.path}`,
JSON.stringify({ test }),
{ headers: options.headers }
);

return response.data;
} catch (error) {
console.log(error);
return false;
}
}

function checkForAPIKey() {
if (!args.pythagora_api_key && !args.openai_api_key) {
console.log(`${bold+red}No API key found!${reset}`);
console.log('Please run:')
console.log(`${bold+blue}npx pythagora --config --pythagora-api-key <YOUR_PYTHAGORA_API_KEY>${reset}`);
console.log('or')
console.log(`${bold+blue}npx pythagora --config --openai-api-key <YOUR_OPENAI_API_KEY>${reset}`);
console.log(`You can get Pythagora API key here: https://mailchi.mp/f4f4d7270a7a/api-waitlist`);
process.exit(0);
}
}

module.exports = {
getJestAuthFunction,
getJestTest,
getJestTestName,
isEligibleForExport,
getUnitTests,
expandUnitTests,
checkForAPIKey
}
module.exports = {getApiConfig};
22 changes: 12 additions & 10 deletions src/helpers/exports.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ const {
METADATA_FILENAME,
EXPORT_METADATA_FILENAME,
SRC_TO_ROOT
} = require('../const/common');
} = require("@pythagora.io/js-code-processing").common;
const { updateMetadata } = require("../utils/common");
const { convertOldTestForGPT } = require("../utils/legacy");
const {
Expand All @@ -16,14 +16,10 @@ const {
enterLoginRouteLog,
testExportStartedLog
} = require("../utils/cmdPrint");
const {
getJestAuthFunction,
getJestTest,
getJestTestName,
cleanupGPTResponse,
} = require("./api");
const {getApiConfig} = require("./api");
const _ = require('lodash');
const args = require('../utils/getArgs.js');
const {API} = require("@pythagora.io/js-code-processing");

async function createDefaultFiles(generatedTests) {
if (!fs.existsSync('jest.config.js')) {
Expand All @@ -40,6 +36,9 @@ async function createDefaultFiles(generatedTests) {
}

async function configureAuthFile(generatedTests) {
const { apiUrl, apiKey, apiKeyType } = getApiConfig();
const Api = new API(apiUrl, apiKey, apiKeyType);

// TODO make require path better
let pythagoraMetadata = require(`../${SRC_TO_ROOT}.pythagora/${METADATA_FILENAME}`);
let loginPath = _.get(pythagoraMetadata, 'exportRequirements.login.endpointPath');
Expand All @@ -64,7 +63,7 @@ async function configureAuthFile(generatedTests) {
}

let loginData = pythagoraMetadata.exportRequirements.login;
let code = await getJestAuthFunction(loginData.mongoQueriesArray, loginData.requestBody, loginData.endpointPath);
let code = await Api.getJestAuthFunction(loginData.mongoQueriesArray, loginData.requestBody, loginData.endpointPath);

fs.writeFileSync(path.resolve(args.pythagora_root, EXPORTED_TESTS_DIR, 'auth.js'), code);
}
Expand Down Expand Up @@ -97,10 +96,13 @@ function cleanupDataFolder() {
}

async function exportTest(originalTest, exportsMetadata) {
const { apiUrl, apiKey, apiKeyType } = getApiConfig();
const Api = new API(apiUrl, apiKey, apiKeyType);

testExportStartedLog();
let test = convertOldTestForGPT(originalTest);
let jestTest = await getJestTest(test);
let testName = await getJestTestName(jestTest, Object.values(exportsMetadata).map(obj => obj.testName));
let jestTest = await Api.getJestTest(test);
let testName = await Api.getJestTestName(jestTest, Object.values(exportsMetadata).map(obj => obj.testName));
if (!jestTest && !testName) return console.error('There was issue with getting GPT response. Make sure you have access to GPT4 with your API key.');

fs.writeFileSync(`./${EXPORTED_TESTS_DATA_DIR}/${testName.replace('.test.js', '.json')}`, JSON.stringify(test.mongoQueries, null, 2));
Expand Down
2 changes: 1 addition & 1 deletion src/helpers/jestMethods.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
const { cleanupDb, jsonObjToMongo } = require("./mongodb");
const {PYTHAGORA_JEST_DB} = require("../const/mongodb");
const {EXPORTED_TESTS_DATA_DIR, SRC_TO_ROOT} = require("../const/common");
const {EXPORTED_TESTS_DATA_DIR, SRC_TO_ROOT} = require("@pythagora.io/js-code-processing").common;
const _ = require('lodash');

// TODO can we merge this with the other prepareDB?
Expand Down
2 changes: 1 addition & 1 deletion src/helpers/middlewares.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ const { getCircularReplacer, compareResponse, compareJson, updateMetadata, compa
const pythagoraErrors = require("../const/errors");
const { logEndpointNotCaptured, logEndpointCaptured, logWithStoreId } = require("../utils/cmdPrint.js");
const { prepareDB } = require("./mongodb.js");
const { PYTHAGORA_TESTS_DIR, PYTHAGORA_DELIMITER } = require("../const/common.js");
const { PYTHAGORA_TESTS_DIR, PYTHAGORA_DELIMITER } = require("@pythagora.io/js-code-processing").common;

const bodyParser = require("body-parser");
const {v4} = require("uuid");
Expand Down
2 changes: 1 addition & 1 deletion src/helpers/mongodb.js
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ const {
const {v4} = require("uuid");
const _ = require("lodash");
const { MONGO_METHODS, PYTHAGORA_DB } = require("../const/mongodb");
const { PYTHAGORA_ASYNC_STORE } = require('../const/common');
const { PYTHAGORA_ASYNC_STORE } = require("@pythagora.io/js-code-processing").common;
let unsupportedMethods = ['aggregate'];

// todo remove this methods?
Expand Down
Loading

0 comments on commit 092aafb

Please sign in to comment.