Skip to content

Commit

Permalink
Merge 82ed770 into c7486cd
Browse files Browse the repository at this point in the history
  • Loading branch information
tawAsh1 committed Feb 16, 2019
2 parents c7486cd + 82ed770 commit bee4bb6
Show file tree
Hide file tree
Showing 4 changed files with 247 additions and 194 deletions.
196 changes: 10 additions & 186 deletions src/commands.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,12 +16,11 @@ import {
drive,
getLocalScript,
loadAPICredentials,
logger,
script,
serviceUsage,
} from './auth';
import { DOT, DOTFILE, ProjectSettings } from './dotfile';
import { fetchProject, hasProject, pushFiles, writeProjectFiles } from './files';
import { ProjectSettings } from './dotfile';
import { fetchProject, hasProject, writeProjectFiles } from './files';
import {
addScopeToManifest,
isValidManifest,
Expand Down Expand Up @@ -183,7 +182,7 @@ export const login = async (options: { localhost?: boolean; creds?: string }) =>
});
console.log('');
console.log(`NOTE: The full list of scopes you're project may need` +
` can be found at script.google.com under:`);
` can be found at script.google.com under:`);
console.log(`File > Project Properties > Scopes`);
console.log('');

Expand Down Expand Up @@ -218,181 +217,6 @@ export const login = async (options: { localhost?: boolean; creds?: string }) =>
process.exit(0); // gracefully exit after successful login
};

/**
* Prints StackDriver logs from this Apps Script project.
* @param cmd.json {boolean} If true, the command will output logs as json.
* @param cmd.open {boolean} If true, the command will open the StackDriver logs website.
* @param cmd.setup {boolean} If true, the command will help you setup logs.
* @param cmd.watch {boolean} If true, the command will watch for logs and print them. Exit with ^C.
*/
export const logs = async (cmd: { json: boolean; open: boolean; setup: boolean; watch: boolean }) => {
await checkIfOnline();
/**
* This object holds all log IDs that have been printed to the user.
* This prevents log entries from being printed multiple times.
* StackDriver isn't super reliable, so it's easier to get generous chunk of logs and filter them
* rather than filter server-side.
* @see logs.data.entries[0].insertId
*/
const logEntryCache: { [key: string]: boolean } = {};

/**
* Prints log entries
* @param entries {any[]} StackDriver log entries.
*/
function printLogs(entries: any[] = []) {
entries = entries.reverse(); // print in syslog ascending order
for (let i = 0; i < 50 && entries ? i < entries.length : i < 0; ++i) {
const { severity, timestamp, resource, textPayload, protoPayload, jsonPayload, insertId } = entries[i];
let functionName = resource.labels.function_name;
functionName = functionName ? padEnd(functionName, 15) : ERROR.NO_FUNCTION_NAME;
let payloadData: any = '';
if (cmd.json) {
payloadData = JSON.stringify(entries[i], null, 2);
} else {
const data: any = {
textPayload,
// chokes on unmatched json payloads
// jsonPayload: jsonPayload ? jsonPayload.fields.message.stringValue : '',
jsonPayload: jsonPayload ? JSON.stringify(jsonPayload).substr(0, 255) : '',
protoPayload,
};
payloadData = data.textPayload || data.jsonPayload || data.protoPayload || ERROR.PAYLOAD_UNKNOWN;
if (payloadData && payloadData['@type'] === 'type.googleapis.com/google.cloud.audit.AuditLog') {
payloadData = LOG.STACKDRIVER_SETUP;
functionName = padEnd(protoPayload.methodName, 15);
}
if (payloadData && typeof payloadData === 'string') {
payloadData = padEnd(payloadData, 20);
}
}
const coloredStringMap: any = {
ERROR: chalk.red(severity),
INFO: chalk.cyan(severity),
DEBUG: chalk.green(severity), // includes timeEnd
NOTICE: chalk.magenta(severity),
WARNING: chalk.yellow(severity),
};
let coloredSeverity: string = coloredStringMap[severity] || severity;
coloredSeverity = padEnd(String(coloredSeverity), 20);
// If we haven't logged this entry before, log it and mark the cache.
if (!logEntryCache[insertId]) {
console.log(`${coloredSeverity} ${timestamp} ${functionName} ${payloadData}`);
logEntryCache[insertId] = true;
}
}
}
async function setupLogs(projectId?: string): Promise<string> {
const promise = new Promise<string>((resolve, reject) => {
getProjectSettings().then(projectSettings => {
console.log(`Open this link: ${LOG.SCRIPT_LINK(projectSettings.scriptId)}\n`);
console.log(`Go to *Resource > Cloud Platform Project...* and copy your projectId
(including "project-id-")\n`);
prompt([
{
type: 'input',
name: 'projectId',
message: 'What is your GCP projectId?',
},
])
.then((answers: any) => {
projectId = answers.projectId;
const dotfile = DOTFILE.PROJECT();
if (!dotfile) return reject(logError(null, ERROR.SETTINGS_DNE));
dotfile
.read()
.then((settings: ProjectSettings) => {
if (!settings.scriptId) logError(ERROR.SCRIPT_ID_DNE);
dotfile.write(Object.assign(settings, { projectId }));
resolve(projectId);
})
.catch((err: object) => {
reject(logError(err));
});
})
.catch((err: any) => {
reject(console.log(err));
});
});
});
promise.catch(err => {
logError(err);
spinner.stop(true);
});
return promise;
}
// Get project settings.
let { projectId } = await getProjectSettings();
projectId = cmd.setup ? await setupLogs() : projectId;
if (!projectId) {
console.log(LOG.NO_GCLOUD_PROJECT);
projectId = await setupLogs();
console.log(LOG.LOGS_SETUP);
}
// If we're opening the logs, get the URL, open, then quit.
if (cmd.open) {
const url = URL.LOGS(projectId);
console.log(`Opening logs: ${url}`);
return open(url, { wait: false });
}

/**
* Fetches the logs and prints the to the user.
* @param startDate {Date?} Get logs from this date to now.
*/
async function fetchAndPrintLogs(startDate?: Date) {
spinner.setSpinnerTitle(`${oauthSettings.isLocalCreds ? LOG.LOCAL_CREDS : ''}${LOG.GRAB_LOGS}`).start();
// Create a time filter (timestamp >= "2016-11-29T23:00:00Z")
// https://cloud.google.com/logging/docs/view/advanced-filters#search-by-time
let filter = '';
if (startDate) {
filter = `timestamp >= "${startDate.toISOString()}"`;
}
const logs = await logger.entries.list({
requestBody: {
resourceNames: [`projects/${projectId}`],
filter,
orderBy: 'timestamp desc',
},
});

// We have an API response. Now, check the API response status.
spinner.stop(true);
// Only print filter if provided.
if (filter.length) {
console.log(filter);
}
if (logs.status !== 200) {
switch (logs.status) {
case 401:
logError(null, oauthSettings.isLocalCreds ? ERROR.UNAUTHENTICATED_LOCAL : ERROR.UNAUTHENTICATED);
case 403:
logError(
null,
oauthSettings.isLocalCreds ? ERROR.PERMISSION_DENIED_LOCAL : ERROR.PERMISSION_DENIED,
);
default:
logError(null, `(${logs.status}) Error: ${logs.statusText}`);
}
} else {
printLogs(logs.data.entries);
}
}

// Otherwise, if not opening StackDriver, load StackDriver logs.
const oauthSettings = await loadAPICredentials();
if (cmd.watch) {
const POLL_INTERVAL = 6000; // 6s
setInterval(() => {
const startDate = new Date();
startDate.setSeconds(startDate.getSeconds() - (10 * POLL_INTERVAL) / 1000);
fetchAndPrintLogs(startDate);
}, POLL_INTERVAL);
} else {
fetchAndPrintLogs();
}
};

/**
* Executes an Apps Script function. Requires clasp login --creds.
* @param functionName {string} The function name within the Apps Script project.
Expand Down Expand Up @@ -503,11 +327,11 @@ https://www.googleapis.com/auth/presentations
// https://www.googleapis.com/auth/presentations
// https://www.googleapis.com/auth/spreadsheets
const readline = require('readline');
const scopes:string[] = [];
const scopes: string[] = [];
const rl = readline.createInterface({
input: process.stdin,
output: process.stdout,
prompt: '',
input: process.stdin,
output: process.stdout,
prompt: '',
});
rl.prompt();
rl.on('line', (cmd: string) => {
Expand Down Expand Up @@ -617,7 +441,7 @@ export const undeploy = async (deploymentId: string, cmd: { all: boolean }) => {
await loadAPICredentials();
const { scriptId } = await getProjectSettings();
if (!scriptId) return;
if (cmd.all){
if (cmd.all) {
const deploymentsList = await script.projects.deployments.list({
scriptId,
});
Expand All @@ -629,7 +453,7 @@ export const undeploy = async (deploymentId: string, cmd: { all: boolean }) => {
logError(null, ERROR.SCRIPT_ID_INCORRECT(scriptId));
}
deployments.shift(); // @HEAD (Read-only deployments) may not be deleted.
for(const deployment of deployments){
for (const deployment of deployments) {
const deploymentId = deployment.deploymentId || '';
spinner.setSpinnerTitle(LOG.UNDEPLOYMENT_START(deploymentId)).start();
const result = await script.projects.deployments.delete({
Expand Down Expand Up @@ -942,7 +766,7 @@ export const setting = async (settingKey?: keyof ProjectSettings, settingValue?:
try {
const currentSettings = await getProjectSettings();
const currentValue = settingKey in currentSettings ? currentSettings[settingKey] : '';
switch(settingKey) {
switch (settingKey) {
case 'scriptId':
currentSettings.scriptId = settingValue;
break;
Expand Down
Loading

0 comments on commit bee4bb6

Please sign in to comment.