wiki-interface/index.js (381 lines of code) (raw):
/**
* Copyright 2024 Google LLC
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
const morgan = require('morgan');
const express = require('express');
const process = require('node:process');
const bodyParser = require('body-parser');
const session = require("express-session")
const crypto = require("crypto");
const oauthHelper = require('./lib/gitlab/oauth2');
const redisSession = require('./lib/redis/session');
const configEnv = require('./lib/config/env');
const configFile = require('./lib/config/file');
const ctxHelper = require('./lib/config/ctx');
const obfuscatorMid = require('./lib/security/obfuscator');
const sessionMid = require('./lib/security/session');
const gitlabOperator = require('./lib/gitlab/wiki');
const metricOperator = require('./lib/metrificator/operator');
const uiFunctions = require('./lib/html/ui');
const gitUtils = require('./lib/git/utils');
const fileUtils = require('./lib/file/file');
const gcsUtils = require('./lib/gcs/gcs');
const apiv1 = require("./lib/api/v1");
/* Server Listening Port */
const port = process.env.PORT || 8080;
/* Checking our Config File */
if (!configFile.checkConfigFile()) {
process.exit(1);
}
/* Checking our Contexts */
if (!ctxHelper.checkContext()) {
process.exit(1);
}
/* Checking our Environment Variables */
if (!configEnv.checkEnvironment()) {
process.exit(1);
}
/* Create our Express APP */
const app = express();
/* Setup Logging */
app.use(morgan(configFile.getLogFormat()));
/* Middleware Setup */
app.use(obfuscatorMid);
/********************
* SESSION SETUP
********************/
let ourPassport = oauthHelper.getPassport();
app.use(
session({
store: redisSession.getRedisStore(),
resave: true,
saveUninitialized: false,
cookie: { secure: false },
secret: configEnv.getSessionSecret(),
}),
)
app.use(ourPassport.initialize());
app.use(ourPassport.session());
app.use('/api', sessionMid);
app.use('/ui', sessionMid);
/********************
* GITLAB Oauth
********************/
app.get(`/gitlab`, (req, res, next) => {
const state = "/ui/";
const authenticator = ourPassport.authenticate('gitlab', { scope: ["read_user"], state })
authenticator(req, res, next)
})
app.get("/gitlab/callback",
ourPassport.authenticate("gitlab", { failureRedirect: "/failure" }), async (req, res) => {
if (req.isAuthenticated()) {
let redirectTo = req.query["state"];
if (typeof redirectTo === 'string' && redirectTo.startsWith('/')) {
res.redirect(redirectTo)
} else {
res.redirect(configFile.getGitlabUrl());
}
}
}
);
app.get('/logout', (req, res) => {
req.logout(function (err) {
if (err) {
return res.send("Error logging out.");
}
req.session.destroy(function (err) {
if (err) {
return res.send("Error destroying session.");
}
res.redirect(configFile.getGitlabUrl());
});
});
});
/********************
* STATIC CONTENT
********************/
app.use("/images", express.static("./ui/images"));
app.use("/css", express.static("./ui/css"));
app.use("/js", express.static("./ui/js"));
app.use("/static", express.static("./ui/static"));
/********************
* UI SECTION
********************/
/* About/Dashboard Screen */
app.get('/ui/about', async (req, res) => {
let myresponse = uiFunctions.renderAbout();
res.header("Content-Type", "text/html");
res.send(myresponse);
});
/* User Story Screen */
app.get('/ui/userstory', async (req, res) => {
let myresponse = uiFunctions.renderUserStory();
res.header("Content-Type", "text/html");
res.send(myresponse);
});
/* Test Case Screen */
app.get('/ui/testcase', async (req, res) => {
let myresponse = uiFunctions.renderTestCase();
res.header("Content-Type", "text/html");
res.send(myresponse);
});
/* Test Script Screen */
app.get('/ui/testscript', async (req, res) => {
let myresponse = uiFunctions.renderTestScript();
res.header("Content-Type", "text/html");
res.send(myresponse);
});
/* Test Data Screen */
app.get('/ui/testdata', async (req, res) => {
let myresponse = uiFunctions.renderTestData();
res.header("Content-Type", "text/html");
res.send(myresponse);
});
/* Document Chatbot Screen */
app.get('/ui/docchatbot', async (req, res) => {
/* Generate a Chat Session */
let chatSession = crypto.randomUUID();
req.session.chatSession = chatSession;
/* Getting Repo URL */
let projectId = req.session.projectId;
let repoId = await gitlabOperator.getProjectUrl(projectId);
repoId = repoId.replaceAll(".git", ".wiki.git");
if (repoId != "") {
/* Clone Repo */
await gitUtils.cloneGitRepo(repoId, configEnv.getGitToken(), chatSession);
/* Build Chat Page */
let myresponse = uiFunctions.renderDocChatbot();
res.header("Content-Type", "text/html");
res.send(myresponse);
/* Preparing context for AI */
let dirPath = configFile.getWorkDir() + "/" + chatSession;
await fileUtils.getAllFilesContentsForChat(dirPath, chatSession);
await gcsUtils.uploadToGCS(chatSession);
await gcsUtils.createAndUploadEmptyJSON(chatSession);
gitUtils.removeLocalSource(chatSession);
} else {
response.status = "FAILED"
res.redirect('/ui/');
}
});
/* Code Chatbot Screen */
app.get('/ui/codechatbot', async (req, res) => {
/* Generate a Chat Session */
let chatSession = crypto.randomUUID();
req.session.chatSession = chatSession;
/* Getting Repo URL */
let projectId = req.session.projectId;
let repoId = await gitlabOperator.getProjectUrl(projectId);
if (repoId != "") {
/* Clone Repo */
await gitUtils.cloneGitRepo(repoId, configEnv.getGitToken(), chatSession);
/* Build Chat Page */
let myresponse = uiFunctions.renderCodeChatbot();
res.header("Content-Type", "text/html");
res.send(myresponse);
/* Preparing context for AI */
let dirPath = configFile.getWorkDir() + "/" + chatSession;
await fileUtils.getAllFilesContentsForChat(dirPath, chatSession);
await gcsUtils.uploadToGCS(chatSession);
await gcsUtils.createAndUploadEmptyJSON(chatSession);
gitUtils.removeLocalSource(chatSession);
} else {
response.status = "FAILED"
res.redirect('/ui/');
}
});
/* Our Search Screen */
app.get('/ui/codesearch', async (req, res) => {
let myresponse = uiFunctions.renderCodeSearch();
res.header("Content-Type", "text/html");
res.send(myresponse);
});
/* Solution Overview Screen */
app.get('/ui/solutionoverview', async (req, res) => {
let myresponse = uiFunctions.renderSolutionOverview();
res.header("Content-Type", "text/html");
res.send(myresponse);
});
/* Solution Overview Screen */
app.get('/ui/solutiondatabase', async (req, res) => {
let myresponse = uiFunctions.renderSolutionDatabase();
res.header("Content-Type", "text/html");
res.send(myresponse);
});
/* Solution API Screen */
app.get('/ui/solutionapi', async (req, res) => {
let myresponse = uiFunctions.renderSolutionAPI();
res.header("Content-Type", "text/html");
res.send(myresponse);
});
/* Solution Dependency Screen */
app.get('/ui/solutiondep', async (req, res) => {
let myresponse = uiFunctions.renderSolutionDep();
res.header("Content-Type", "text/html");
res.send(myresponse);
});
/* Solution Integration Screen */
app.get('/ui/solutionintegration', async (req, res) => {
let myresponse = uiFunctions.renderSolutionIntegration();
res.header("Content-Type", "text/html");
res.send(myresponse);
});
/* Solution Security Screen */
app.get('/ui/solutionsecurity', async (req, res) => {
let myresponse = uiFunctions.renderSolutionSecurity();
res.header("Content-Type", "text/html");
res.send(myresponse);
});
/* Index */
app.get('/ui', async (req, res) => {
let projectName = req.session.projectName;
let username = req.session.passport.user.displayName;
let myresponse = uiFunctions.renderIndex(username, projectName);
res.header("Content-Type", "text/html");
res.send(myresponse);
});
/* Select Project Screen */
app.get('/ui/selectproject', async (req, res) => {
let projectName = req.session.projectName;
let username = req.session.passport.user.displayName;
if (!req.headers["referer"] || !req.headers["referer"].includes("/ui/")) {
let myresponse = uiFunctions.renderSelectProjectFull(username, projectName);
res.header("Content-Type", "text/html");
res.send(myresponse);
} else {
let myresponse = uiFunctions.renderSelectProject();
res.header("Content-Type", "text/html");
res.send(myresponse);
}
});
/* Select Project Action */
app.get('/ui/setproject/:projectId', async (req, res) => {
let projectId = req.params.projectId;
let projectName = await apiv1.getProjectName(projectId);
req.session.projectId = projectId;
req.session.projectName = projectName;
res.redirect('/ui/');
});
/********************
* API SECTION
********************/
/* Check if Chatbot Doc session is present on Cloud Storage */
app.get('/api/v1/checkdocsession', async function (req, res, next) {
let chatSession = req.session.chatSession;
response = await apiv1.checkChatSession(chatSession);
res.json(response);
});
/* Check if Chatbot Code session is present on Cloud Storage */
app.get('/api/v1/checkcodesession', async function (req, res, next) {
let chatSession = req.session.chatSession;
response = await apiv1.checkChatSession(chatSession);
res.json(response);
});
/* send message to Document Chatbot */
app.post('/api/v1/docmessage', bodyParser.json(), async function (req, res, next) {
let chatSession = req.session.chatSession;
let inputMessage = req.body.message;
let response = await apiv1.sendDocChatMessage(chatSession, inputMessage);
res.json(response);
});
/* send message to Code Chatbot */
app.post('/api/v1/codemessage', bodyParser.json(), async function (req, res, next) {
let chatSession = req.session.chatSession;
let inputMessage = req.body.message;
let response = await apiv1.sendCodeChatMessage(chatSession, inputMessage);
res.json(response);
});
/* Load Project List */
app.get('/api/v1/projects', async function (req, res, next) {
let response = await apiv1.getProjects()
res.json(response);
});
/* Load User Story List for Evaluation*/
app.get('/api/v1/userstoryoptions', async function (req, res, next) {
let projectId = req.session.projectId;
let response = await apiv1.getUsOptions(projectId);
res.json(response);
});
/* Load User Story List for Test Case Creation */
app.get('/api/v1/testcaseoptions', async function (req, res, next) {
let projectId = req.session.projectId;
let response = await apiv1.getTcOptions(projectId);
res.json(response);
});
/* Load Test Case List for Script Creation */
app.get('/api/v1/testscriptoptions', async function (req, res, next) {
let projectId = req.session.projectId;
let response = await apiv1.getTsOptions(projectId);
res.json(response);
});
/* Evaluate User Story */
app.post('/api/v1/userstory', bodyParser.json(), async function (req, res, next) {
let projectId = req.session.projectId;
let inputDoc = req.body.documentId;
let gitlabUser = req.session.passport.user.displayName;
let response = await apiv1.processUserStory(projectId, inputDoc);
await metricOperator.insertMetric(response.transactionId, gitlabUser, response.projectId, response.documentId, response.pagePath, "userstory-evaluator");
res.json(response);
});
/* Create Test Case */
app.post('/api/v1/testcase', bodyParser.json(), async function (req, res, next) {
let projectId = req.session.projectId;
let inputDoc = req.body.documentId;
let gitlabUser = req.session.passport.user.displayName;
let response = await apiv1.processTestCase(projectId, inputDoc);
await metricOperator.insertMetric(response.transactionId, gitlabUser, response.projectId, response.documentId, response.pagePath, "testcase-generator");
res.json(response);
});
/* Create Cypress Script */
app.post('/api/v1/cypress', bodyParser.json(), async function (req, res, next) {
let projectId = req.session.projectId;
let inputDoc = req.body.documentId;
let gitlabUser = req.session.passport.user.displayName;
let response = await apiv1.processCypress(projectId, inputDoc);
await metricOperator.insertMetric(response.transactionId, gitlabUser, response.projectId, response.documentId, response.pagePath, "script-cypress");
res.json(response);
});
/* Create Playwright Script */
app.post('/api/v1/playwright', bodyParser.json(), async function (req, res, next) {
let projectId = req.session.projectId;
let inputDoc = req.body.documentId;
let gitlabUser = req.session.passport.user.displayName;
let response = await apiv1.processPlaywright(projectId, inputDoc);
await metricOperator.insertMetric(response.transactionId, gitlabUser, response.projectId, response.documentId, response.pagePath, "script-playwright");
res.json(response);
});
/* Create Selenium Script */
app.post('/api/v1/selenium', bodyParser.json(), async function (req, res, next) {
let projectId = req.session.projectId;
let inputDoc = req.body.documentId;
let gitlabUser = req.session.passport.user.displayName;
let response = await apiv1.processSelenium(projectId, inputDoc);
await metricOperator.insertMetric(response.transactionId, gitlabUser, response.projectId, response.documentId, response.pagePath, "script-selenium");
res.json(response);
});
/* Perform Codesearch */
app.post('/api/v1/codesearch', bodyParser.json(), async function (req, res, next) {
let projectId = req.session.projectId;
let userQuery = req.body.userQuery;
let repoId = await gitlabOperator.getProjectUrl(projectId);
let gitlabUser = req.session.passport.user.displayName;
let response = await apiv1.processCodeSearch(projectId, repoId, userQuery);
await metricOperator.insertMetric(response.transactionId, gitlabUser, response.projectId, "code-search", "code-search", "code-search");
res.json(response);
});
/* Code Overview */
app.get('/api/v1/solutionoverview', bodyParser.json(), async function (req, res, next) {
let projectId = req.session.projectId;
let repoId = await gitlabOperator.getProjectUrl(projectId);
let gitlabUser = req.session.passport.user.displayName;
let response = await apiv1.processSolutionOverview(projectId, repoId);
await metricOperator.insertMetric(response.transactionId, gitlabUser, response.projectId, "solution-overview", "solution-overview", "solution-overview");
res.json(response);
});
/* Database Overview */
app.get('/api/v1/solutiondatabase', bodyParser.json(), async function (req, res, next) {
let projectId = req.session.projectId;
let repoId = await gitlabOperator.getProjectUrl(projectId);
let gitlabUser = req.session.passport.user.displayName;
let response = await apiv1.processSolutionDatabase(projectId, repoId);
await metricOperator.insertMetric(response.transactionId, gitlabUser, response.projectId, "solution-database", "solution-database", "solution-database");
res.json(response);
});
/* API Overview */
app.get('/api/v1/solutionapi', bodyParser.json(), async function (req, res, next) {
let projectId = req.session.projectId;
let repoId = await gitlabOperator.getProjectUrl(projectId);
let gitlabUser = req.session.passport.user.displayName;
let response = await apiv1.processSolutionAPI(projectId, repoId);
await metricOperator.insertMetric(response.transactionId, gitlabUser, response.projectId, "solution-api", "solution-api", "solution-api");
res.json(response);
});
/* Dependency Overview */
app.get('/api/v1/solutiondep', bodyParser.json(), async function (req, res, next) {
let projectId = req.session.projectId;
let repoId = await gitlabOperator.getProjectUrl(projectId);
let gitlabUser = req.session.passport.user.displayName;
let response = await apiv1.processSolutionDep(projectId, repoId);
await metricOperator.insertMetric(response.transactionId, gitlabUser, response.projectId, "solution-dependency", "solution-dependency", "solution-dependency");
res.json(response);
});
/* Integration Overview */
app.get('/api/v1/solutionintegration', bodyParser.json(), async function (req, res, next) {
let projectId = req.session.projectId;
let repoId = await gitlabOperator.getProjectUrl(projectId);
let gitlabUser = req.session.passport.user.displayName;
let response = await apiv1.processSolutionIntegration(projectId, repoId);
await metricOperator.insertMetric(response.transactionId, gitlabUser, response.projectId, "solution-integration", "solution-integration", "solution-integration");
res.json(response);
});
/* Security Overview */
app.get('/api/v1/solutionsecurity', bodyParser.json(), async function (req, res, next) {
let projectId = req.session.projectId;
let repoId = await gitlabOperator.getProjectUrl(projectId);
let gitlabUser = req.session.passport.user.displayName;
let response = await apiv1.processSolutionSecurity(projectId, repoId);
await metricOperator.insertMetric(response.transactionId, gitlabUser, response.projectId, "solution-security", "solution-security", "solution-security");
res.json(response);
});
/* Check Document Exists */
app.post('/api/v1/checkdocument', bodyParser.json(), async function (req, res, next) {
let projectId = req.session.projectId;
let inputDoc = req.body.documentId;
let modelId = req.body.modelId;
let response = await apiv1.checkDocumentExists(projectId, inputDoc, modelId);
res.json(response);
});
/* Create Test Data */
app.post('/api/v1/testdata', bodyParser.json(), async function (req, res, next) {
let projectId = req.session.projectId;
let gitlabUser = req.session.passport.user.displayName;
let inputDoc = req.body.documentId;
let sampleQty = req.body.sampleQty;
let modelId = req.body.modelId;
let response = await apiv1.processTestData(projectId, modelId, inputDoc, sampleQty);
await metricOperator.insertMetric(response.transactionId, gitlabUser, response.projectId, "test-data", "test-data", modelId);
res.json(response);
});
/* Rating Entry */
app.post('/api/v1/rating', bodyParser.json(), async function (req, res, next) {
let response = "";
let projectId = req.session.projectId;
let documentId = req.body.documentId;
let documentContent = req.body.documentContent;
let documentPath = req.body.documentPath;
let transactionId = req.body.transactionId;
let ratingValue = req.body.ratingValue;
await metricOperator.insertRating(transactionId, parseInt(ratingValue));
if (documentId != "do-not-save") {
response = await apiv1.createDocument(projectId, documentPath, documentContent);
}
res.json(response);
});
/* Starting Application */
app.listen(port, () => {
console.log('Listening on port', port);
});