UNPKG

@pythagora.io/js-code-processing

Version:

This repository hosts the 'code-processing' npm package, which contains a set of code processing methods for generating tests.

210 lines (186 loc) 6.18 kB
/* eslint-disable no-unsafe-finally */ const _ = require("lodash"); const axios = require("axios"); const { blue, red, reset, bold } = require("../const/colors"); /** * @class Api * @description A class to interact with the remote API. * @throws Will throw an error if the class is instantiated directly. */ class Api { #apiUrl; #apiKey; #apiKeyType; /** * @param {string} apiUrl - The API base url. * @param {string} apiKey - The API key. * @param {string} apiKeyType - The type of the API key. It should be 'openai' or 'pythagora'. * @throws Will throw an error if called directly. */ constructor(apiUrl, apiKey, apiKeyType) { if (!apiKey) { 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); } if (!apiUrl || !apiKey) { throw new Error("Please, pass API url!"); } if (apiKeyType !== "openai" && apiKeyType !== "pythagora") { throw new Error("API key type value must be openai or pythagora!"); } this.#apiUrl = apiUrl; this.#apiKey = apiKey; this.#apiKeyType = apiKeyType; } /** * Prepare and set the options for the API request */ setOptions({ path, method, headers }) { const parsedUrl = new URL(this.#apiUrl); const options = { protocol: parsedUrl.protocol.replace(":", ""), hostname: parsedUrl.hostname, port: parsedUrl.port, path: path || "/", method: method || "POST", headers: headers || { "Content-Type": "application/json", apikey: this.#apiKey, apikeytype: this.#apiKeyType } }; if (!options.port) delete options.port; return options; } /** * Make API request */ async makeRequest(data, options, customLogFunction) { let gptResponse = ""; let timeout; const httpModule = options.protocol === "http" ? require("http") : require("https"); 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); const stringified = chunk.toString(); try { const json = JSON.parse(stringified); if (json.error || json.message) { gptResponse = json; return; } } catch (e) { } gptResponse += stringified; 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 checkApiKey() { try { await axios.get(this.#apiUrl + "/api/check-api-key", { headers: { apikey: this.#apiKey, apikeytype: this.#apiKeyType } }); return true; } catch { return false; } } async getUnitTests(data, customLogFunction) { const options = this.setOptions({ path: "/api/generate-unit-tests" }); let tests, error; try { tests = await this.makeRequest(JSON.stringify(data), options, customLogFunction); } catch (e) { error = e; } finally { return { tests, error }; } } async expandUnitTests(data, customLogFunction) { const options = this.setOptions({ path: "/api/expand-unit-tests" }); let tests, error; try { tests = await this.makeRequest(JSON.stringify(data), options, customLogFunction); } catch (e) { error = e; } finally { return { tests, error }; } } async getJestAuthFunction(loginMongoQueriesArray, loginRequestBody, loginEndpointPath) { const options = this.setOptions({ path: "/api/generate-jest-auth" }); return this.makeRequest(JSON.stringify({ loginMongoQueriesArray, loginRequestBody, loginEndpointPath }), options); } /** * Generate jest test */ async getJestTest(test) { const options = this.setOptions({ path: "/api/generate-jest-test" }); return this.makeRequest(JSON.stringify(test), options); } /** * Generate jest test name */ async getJestTestName(test, usedNames) { const options = this.setOptions({ path: "/api/generate-jest-test-name" }); return this.makeRequest(JSON.stringify({ test }), options); } /** * Check if the test is eligible for export */ async isEligibleForExport(test) { try { const options = this.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; } } } module.exports = Api;