Skip to content

Commit

Permalink
Merge pull request #2 from jdvp/feature/cached_appscript_data
Browse files Browse the repository at this point in the history
Allow caching of comment data
  • Loading branch information
jdvp authored May 27, 2022
2 parents 5cd4234 + 724845e commit 6ad8039
Show file tree
Hide file tree
Showing 2 changed files with 113 additions and 62 deletions.
171 changes: 111 additions & 60 deletions appscript/Code.gs
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,18 @@
* @OnlyCurrentDoc
*/

var RECAPTCHA_SECRET_KEY = ""; //TODO add this if using RECAPTCHA
var RECAPTCHA_SECRET_KEY = null; //TODO add this if using RECAPTCHA
var RECAPTCHA_THRESHOLD = .4;
var CACHING_ENABLED = true;

/* Consts used so that typos don't occur between usages */
const SHEET_CACHE_KEY = "sheet_comment_data_cache_key";
const SHEET_CAHCE_HEADERS_KEY = "sheet_headers_data_cache_key";
const COLUMN_TIMESTAMP = "columnTimestamp";
const COLUMN_URL ="columnUrl";
const COLUMN_NAME = "columnName";
const COLUMN_COMMENT = "columnComment";
const COLUMN_IS_AUTHOR = "columnIsAuthor";

function doGet(req) {
var url = req?.parameter?.url || null;
Expand All @@ -12,47 +22,62 @@ function doGet(req) {
return getError("Required URL Parameter is null");
}

var sh = SpreadsheetApp.getActiveSheet();
var cache = CacheService.getScriptCache();
var cachedSheet = cache.get(SHEET_CACHE_KEY);
var cachedHeaders = cache.get(SHEET_CAHCE_HEADERS_KEY);
var sheetData;
var headers;
if (CACHING_ENABLED && cachedSheet != null && cachedHeaders != null ) {
sheetData = JSON.parse(cachedSheet);
headers = JSON.parse(cachedHeaders);
} else {
var sh = SpreadsheetApp.getActiveSheet();

var headers = sh.getSheetValues(1, 1, 1, sh.getLastColumn())[0];
var columnTimestamp = headers.findIndex(element => "timestamp".toUpperCase() === element.toUpperCase());
var columnUrl = headers.findIndex(element => element.toUpperCase().includes("url".toUpperCase()));
var columnName = headers.findIndex(element => "name".toUpperCase() === element.toUpperCase());
var columnComment = headers.findIndex(element => "comment".toUpperCase() === element.toUpperCase());
var columnIsAuthor = headers.findIndex(element => "isAuthor".toUpperCase() === element.toUpperCase());

/* Ensure required rows are there, technically isAuthor is not required, so we don't check for it at this point */
if (columnTimestamp < 0) {
return getError("Can't find 'timestamp' column in Google Sheet");
}
if (columnUrl < 0) {
return getError("Can't find 'article url' column in Google Sheet");
}
if (columnName < 0) {
return getError("Can't find 'name' column in Google Sheet");
}
if (columnComment < 0) {
return getError("Can't find 'comment' column in Google Sheet");
}
var sheetHeaders = sh.getSheetValues(1, 1, 1, sh.getLastColumn())[0];
headers = {};
headers[COLUMN_TIMESTAMP] = sheetHeaders.findIndex(element => "timestamp".toUpperCase() === element.toUpperCase());
headers[COLUMN_URL] = sheetHeaders.findIndex(element => element.toUpperCase().includes("url".toUpperCase()));
headers[COLUMN_NAME] = sheetHeaders.findIndex(element => "name".toUpperCase() === element.toUpperCase());
headers[COLUMN_COMMENT] = sheetHeaders.findIndex(element => "comment".toUpperCase() === element.toUpperCase());
headers[COLUMN_IS_AUTHOR] = sheetHeaders.findIndex(element => "isAuthor".toUpperCase() === element.toUpperCase());

/* Ensure required rows are there, technically isAuthor is not required, so we don't check for it at this point */
if (headers[COLUMN_TIMESTAMP] == null || headers[COLUMN_TIMESTAMP] < 0) {
return getError("Can't find 'timestamp' column in Google Sheet");
}
if (headers[COLUMN_URL] == null || headers[COLUMN_URL] < 0) {
return getError("Can't find 'article url' column in Google Sheet");
}
if (headers[COLUMN_NAME] == null || headers[COLUMN_NAME] < 0) {
return getError("Can't find 'name' column in Google Sheet");
}
if (headers[COLUMN_COMMENT] == null || headers[COLUMN_COMMENT] < 0) {
return getError("Can't find 'comment' column in Google Sheet");
}

var numRows = sh.getLastRow() - 1;
if (numRows == 0) {
return ContentService
.createTextOutput(JSON.stringify([]))
.setMimeType(ContentService.MimeType.JSON);
var numRows = sh.getLastRow() - 1;
if (numRows == 0) {
return ContentService
.createTextOutput(JSON.stringify([]))
.setMimeType(ContentService.MimeType.JSON);
}
sheetData = sh.getSheetValues(2, 1, sh.getLastRow() - 1, sh.getLastColumn())
/* Cache data for 1 hour OR until an error occurs OR a new comment is added */
cache.put(SHEET_CACHE_KEY, JSON.stringify(sheetData), 3600);
cache.put(SHEET_CAHCE_HEADERS_KEY, JSON.stringify(headers), 3600);
}

var values = sh.getSheetValues(2, 1, sh.getLastRow() - 1, sh.getLastColumn())
.sort((a, b) => a[columnTimestamp] - b[columnTimestamp])
.filter(element => (element[columnTimestamp] || 0) != 0)
.filter(element => element[columnUrl] == url);
var values = sheetData
.sort((a, b) => a[headers[COLUMN_TIMESTAMP]] - b[headers[COLUMN_TIMESTAMP]])
.filter(element => (element[headers[COLUMN_TIMESTAMP]] || 0) != 0)
.filter(element => element[headers[COLUMN_URL]] == url);

var data = values.map(element => {
return {
"timestamp" : element[columnTimestamp] || 0,
"name" : element[columnName] || "",
"comment" : element[columnComment] || "",
"isAuthor" : element[columnIsAuthor] || false
"timestamp" : element[headers[COLUMN_TIMESTAMP]] || 0,
"name" : element[headers[COLUMN_NAME]] || "",
"comment" : element[headers[COLUMN_COMMENT]] || "",
"isAuthor" : element[headers[COLUMN_IS_AUTHOR]] || false
}
})

Expand All @@ -62,6 +87,7 @@ function doGet(req) {
}

function getError(errorText) {
clearCachedData();
return ContentService
.createTextOutput(JSON.stringify({
"error": errorText
Expand All @@ -72,35 +98,37 @@ function getError(errorText) {
function doPost(req) {
var bodyData = JSON.parse(req?.postData?.contents || "{}")

/* TODO uncomment this if using recaptcha and want server-side validation
if (RECAPTCHA_SECRET_KEY != null) {
var recaptchaToken = bodyData?.recaptchaToken || null;
if (recaptchaToken == null) {
return getError("POST body missing required 'recaptchaToken' paramter (which must also be non-empty)");
}

var recaptchaToken = bodyData?.recaptchaToken || null;
if (recaptchaToken == null) {
return getError("POST body missing required 'recaptchaToken' paramter (which must also be non-empty)");
}
var formData = {
"secret": RECAPTCHA_SECRET_KEY,
"response": recaptchaToken
};
var options = {
"method" : "post",
"payload" : formData
};

var formData = {
"secret": RECAPTCHA_SECRET_KEY,
"response": recaptchaToken
};
var options = {
"method" : "post",
"payload" : formData
};
try {
var recaptchaResponse = JSON.parse(UrlFetchApp.fetch("https://www.google.com/recaptcha/api/siteverify", options).getContentText());
var sucess = recaptchaResponse.success || false;
if (!sucess) {
return getError("reCAPTCHA is required but appears to be misconfigured on the site")
}
var score = recaptchaResponse.score || 0;
if (score < RECAPTCHA_THRESHOLD) {
return getError("reCAPTCHA suspects bot behavior, please try again in a bit")
try {
var sucess = recaptchaResponse.success || false;
if (!sucess) {
return getError("reCAPTCHA is required but appears to be misconfigured on the site")
}
var score = recaptchaResponse.score || 0;
if (score < RECAPTCHA_THRESHOLD) {
return getError("reCAPTCHA suspects bot behavior, please try again in a bit")
}
} catch {
return getError("reCAPTCHA could not be verified");
}
} catch {
return getError("reCAPTCHA could not be verified");
}
*/

clearCachedData();

var url = bodyData?.url || null;
var name = bodyData?.name || null;
Expand Down Expand Up @@ -172,9 +200,12 @@ function doPost(req) {
* can change the url parameter below
*/
function testGetComments() {
var start = Date.now()
var results = doGet({ parameter : { url : "test-url"}}).getContent();
Logger.log("doGet results:")
Logger.log(results);
var end = Date.now()
Logger.log("Run took " + (end - start) + " millis")
}

/**
Expand All @@ -187,12 +218,32 @@ function testPostComment() {
postData : {
contents : JSON.stringify({
url : "test-url",
recaptchaToken: "abc",
name: "test-name",
comment: "test-comment"
})
}
}
var results = doPost(testResponse).getContent();
Logger.log("doPost results:")
Logger.log("doPost results:");
Logger.log(results);
}

/**
* Function that can be used to clear the cached comment data
*/
function clearCachedData() {
var cache = CacheService.getScriptCache();
cache.removeAll([SHEET_CACHE_KEY, SHEET_CAHCE_HEADERS_KEY]);
}

/**
* Triggered when the spreadsheet is updated.
* This is so that the comment cache is cleared when the spreadsheet
* is manually edited so that developer changes to the spreadsheet
* are reflected immediately and not hindered by the cache system.
*/
function onEdit(e) {
clearCachedData();
Logger.log("Spreadsheet was edited, so cache was cleared");
}
4 changes: 2 additions & 2 deletions comment-section.html
Original file line number Diff line number Diff line change
Expand Up @@ -124,7 +124,7 @@
disableButtonWithLoader("load-comment-button");
{%- unless site.google_forms_comments.google_app_script -%}
{%- comment -%}
/* Load JQuery, only needed in order to use JQuery CSV */
/* Load JQuery, only needed in order to use JQuery CSV */
{%- endcomment -%}
var loadJquery = document.createElement("script");
loadJquery.type = "text/javascript";
Expand Down Expand Up @@ -391,7 +391,7 @@
}

var jsonElement = JSON.parse(element);
var comment = getCommentInfo(jsonElement.comment);
var comment = getCommentInfo(jsonElement.comment?.toString());
{%- comment -%}
/* If the author was the one who commented back, add this 'verified' logic */
{%- endcomment -%}
Expand Down

0 comments on commit 6ad8039

Please sign in to comment.