@codebynithin/time-entry
Version:
Nithin time entry cli
315 lines (279 loc) • 7.53 kB
JavaScript
const axios = require('axios');
const os = require('os');
const path = require('path');
const readline = require('readline');
const fs = require('fs');
const userHomeDir = `${os.homedir()}/.time-entry`;
const filePath = path.join(userHomeDir, '.zoho-config');
const keyMap = {
p: 'project',
project: 'project',
s: 'sprint',
sprint: 'sprint',
t: 'task',
task: 'task',
dt: 'date',
date: 'date',
w: 'work',
work: 'work',
du: 'duration',
duration: 'duration',
r: 'remarks',
remarks: 'remarks',
id: 'id',
i: 'id',
};
const contentTableHeading =
'| id | project | sprint | date | task | work | duration | remarks | synced |';
const contentTableSeparator = '| -- | -- | -- | -- | -- | -- | -- | -- | -- |';
const accessFileSync = (path) => {
try {
fs.accessSync(path, fs.constants.R_OK | fs.constants.W_OK);
return true;
} catch {
return false;
}
};
const appendFileSync = (path, data) => {
try {
fs.appendFileSync(path, data);
} catch (error) {
console.log(error);
}
};
const convertToTaskData = async (values) => {
const [{ project }, sprints] = await Promise.all([
userConfig(),
getSprints({ params: { type: '2' } }),
]);
const data = values.reduce((acc, item) => {
let [key, ...itemValues] = item.split(' ');
let itemValue = itemValues.join(' ');
if (key.charAt(0) === '-') {
key = key.substring(1);
}
switch (key) {
case 's':
case 'sprint': {
if (!itemValue.includes('sprint')) {
itemValue = `sprint ${itemValue}`;
}
acc[keyMap[key]] = itemValue;
break;
}
case 'dt':
case 'date': {
acc[keyMap[key]] = new Date(itemValue).toISOString().split('T')[0];
break;
}
case 'du':
case 'duration': {
acc[keyMap[key]] = +itemValue;
break;
}
default: {
acc[keyMap[key]] = itemValue;
break;
}
}
return acc;
}, {});
if (!data.project) {
data.project = project.default.label;
}
if (!data.sprint) {
if (sprints.length > 1) {
await new Promise((resolve) => {
console.log('Multiple active sprints are available, select your task sprint.');
for (const [index, project] of Object.entries(sprints)) {
console.log(`${+index + 1}. ${project.label}`);
}
rl.question('Enter the sprint index: ', (userInput) => {
data.sprint = sprints[+userInput - 1].label;
rl.close();
resolve(data.sprint);
});
});
} else {
data.sprint = sprints[0].label;
}
}
return data;
};
const existsSync = (path) => {
return fs.existsSync(path);
};
const getGitLabAuth = async (config) => {
if (!config) {
config = await userConfig();
}
return config.gitlab;
};
const getHeaders = async (config) => {
if (!config) {
config = await userConfig();
}
return {
authority: 'externalusers.zohosprints.com',
accept: '*/*',
'accept-language': 'en-GB,en-US;q=0.9,en;q=0.8,ml;q=0.7',
'cache-control': 'no-cache',
cookie: config.zoho.cookie,
pragma: 'no-cache',
referer: 'https://externalusers.zohosprints.com/workspace/4medica/client/wmoku',
'sec-ch-ua': '"Not A(Brand";v="99", "Google Chrome";v="121", "Chromium";v="121"',
'sec-ch-ua-mobile': '?0',
'sec-ch-ua-platform': '"macOS"',
'sec-fetch-dest': 'empty',
'sec-fetch-mode': 'cors',
'sec-fetch-site': 'same-origin',
'user-agent':
'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/121.0.0.0 Safari/537.36',
'x-requested-with': 'XMLHttpRequest',
'x-za-clientportalid': config.zoho.portalId,
'x-za-reqsize': 'large',
'x-za-sessionid': config.zoho.sessionId,
'x-za-source': config.zoho.source,
'x-za-ui-version': 'v2',
'x-zcsrf-token': config.zoho.token,
};
};
const getProjects = async (userConfig) => {
const headers = await getHeaders(userConfig);
const p = {
action: 'recentprojects',
team: '803166918',
};
const parentUrl = `https://externalusers.zohosprints.com/zsapi/team/${p.team}/projects/?action=${p.action}`;
try {
const response = await axios.get(parentUrl, { headers });
return Object.entries(response.data.projectJObj)
.map(([key, value]) => ({ value: key, label: value[0] }))
.sort((a, b) => (a.label < b.label ? 1 : a.label > b.label ? -1 : 0));
} catch (error) {
console.error(error.message);
}
};
const getSprints = async ({ params }) => {
const config = await userConfig();
const headers = await getHeaders();
const p = {
index: params.index || 1,
range: params.range || 150,
project: params.project || config.project.default.value,
action: 'data',
team: '803166918',
};
if (params?.type) {
if (typeof params.type === 'string') {
p.type = params.type.split(',');
} else {
p.type = params.type;
}
} else {
p.type = ['2', '3'];
}
const parentUrl = `https://externalusers.zohosprints.com/zsapi/team/${p.team}/projects/${
p.project
}/sprints/?action=${p.action}&range=${p.range}&type=${encodeURIComponent(
JSON.stringify(p.type),
)}&index=${p.index}`;
try {
const response = await axios.get(parentUrl, { headers });
return Object.entries(response.data.sprintJObj)
.map(([key, value]) => ({ value: key, label: value[0] }))
.sort((a, b) => (a.label < b.label ? 1 : a.label > b.label ? -1 : 0));
} catch (error) {
console.error(error.message);
}
};
const groupBy = (arr, key) => {
return arr.reduce((acc, obj) => {
const group = obj[key];
if (!acc[group]) {
acc[group] = [];
}
acc[group].push(obj);
return acc;
}, {});
};
const mkdirSync = () => {
try {
return fs.mkdirSync(userHomeDir);
} catch (error) {
console.log(error);
}
};
const readFileSync = (path) => {
try {
return fs.readFileSync(path, 'utf8');
} catch (error) {
// console.log(error);
}
};
const removeEmpty = (obj) => {
for (let [key, val] of Object.entries(obj)) {
if (val && typeof val === 'object') {
this.removeEmpty(val);
if (!(Object.keys(val).length || val instanceof Date)) {
delete obj[key];
}
} else {
if (typeof val === 'string') {
val = val.trim();
}
if (val === null || val === undefined || val === '') {
delete obj[key];
} else {
obj[key] = val;
}
}
}
return obj;
};
const rl = readline.createInterface({ input: process.stdin, output: process.stdout });
const userConfig = async (jsonData) => {
return new Promise((resolve, reject) => {
try {
if (!jsonData) {
const data = readFileSync(filePath);
jsonData = JSON.parse(data);
}
if (!Object.values(jsonData).length) {
throw new Error('No configurations found, try `zoho init` as first step.');
}
resolve(jsonData);
} catch (parseError) {
reject(parseError);
}
});
};
const writeFileSync = (path, data) => {
try {
fs.writeFileSync(path, data);
} catch (error) {
console.log(error);
}
};
module.exports = {
accessFileSync,
appendFileSync,
contentTableHeading,
contentTableSeparator,
convertToTaskData,
existsSync,
filePath,
getGitLabAuth,
getHeaders,
getProjects,
getSprints,
groupBy,
mkdirSync,
path,
readFileSync,
removeEmpty,
rl,
userConfig,
userHomeDir,
writeFileSync,
};