Skip to content

Commit

Permalink
Merge pull request #70 from Alex-At-Home/add-global-triggers
Browse files Browse the repository at this point in the history
[#65] Add global triggers
  • Loading branch information
Alex-At-Home authored Sep 28, 2019
2 parents 51bacfe + 5109e44 commit caba14d
Show file tree
Hide file tree
Showing 10 changed files with 251 additions and 54 deletions.
14 changes: 11 additions & 3 deletions src/frontend/view-models/sidebarAppGeneralEditor.js
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ var GeneralEditor = (function(){
<option value='none'>Disabled</option>
<option value='top'>Top</option>
<option value='bottom'>Bottom</option>
<option value='global'>Cell Reference (use Advanced)</option>
</select>
</div>
`
Expand Down Expand Up @@ -61,6 +62,7 @@ var GeneralEditor = (function(){
<option value='top'>Top</option>
<option value='bottom'>Bottom</option>
<option value='none'>Disabled</option>
<option value='global'>Cell Reference (use Advanced)</option>
</select>
<div class="checkbox">
<label><input type="checkbox" id="status_merge_${tableType}_${index}">Merge with query/pagination rows</label>
Expand Down Expand Up @@ -110,6 +112,8 @@ var GeneralEditor = (function(){
var querySource = Util.getJson(json, ["common", "query", "source"]) || "none"
if (querySource == "none") {
$(`#querybar_${tableType}_${index}`).val("none")
} else if (querySource == "global") {
$(`#querybar_${tableType}_${index}`).val("global")
} else {
var localQueryPos = Util.getJson(json, ["common", "query", "local", "position"]) || "none"
$(`#querybar_${tableType}_${index}`).val(localQueryPos)
Expand Down Expand Up @@ -162,9 +166,13 @@ var GeneralEditor = (function(){
var thisValue = this.value
Util.updateRawJsonNow(globalEditor, function(currJson) {
var query = Util.getOrPutJsonObj(currJson, [ "common", "query" ])
query.source = (thisValue == "none") ? "none" : "local" // (only supported option currently)
var localQuery = Util.getOrPutJsonObj(currJson, [ "common", "query", "local" ])
localQuery.position = thisValue
if (("none" == thisValue) || ("global" == thisValue)) {
query.source = thisValue
} else {
query.source = "local"
var localQuery = Util.getOrPutJsonObj(currJson, [ "common", "query", "local" ])
localQuery.position = thisValue
}
})
})
}
Expand Down
1 change: 0 additions & 1 deletion src/frontend/view-models/sidebarAppTableForm.js
Original file line number Diff line number Diff line change
Expand Up @@ -103,7 +103,6 @@ var TableForm = (function() {
`
}//(endif standaloneEdit)

var panelHeader = ''
var panelHeader = ''
var panelInOrOut = standaloneEdit ? 'in' : 'out'
if (!standaloneEdit) {
Expand Down
39 changes: 20 additions & 19 deletions src/server/models/TableModel.gs
Original file line number Diff line number Diff line change
Expand Up @@ -7,48 +7,49 @@ var defaultTableConfig_ = {
// "timed": false,
// "refresh_s": 60
// },
"global_content_triggers": [], //array of ranges ("sheet!range") to include when deciding whether to mark the table as edited
"global_control_triggers": [], //array of ranges ("sheet!range") to include when deciding whether to refresh the table
"query": {
// "index_pattern": "tbd",
"source": "none",
//, //points to field to use ("global", "local", "fixed") .. NOT_SUPPORTED: "global", "fixed"
// "global": {
// "range_name": "tbd"
// },
//, //^points to field to use ("global", "local")
"global": {
"range_name": "[sheet!]range"
},
"local": {
"position": "top" //(or "bottom" ... NOT_SUPPORTED: "bottom")
"position": "top" //(or "bottom")
}
//,
// "fixed": {
// "string": "{} or SQL or lucene"
// }
},
"pagination": {
"source": "none",
//, //points to field to use ("global", "local", "fixed") .. NOT_SUPPORTED: "global", "fixed"
//, //points to field to use ("global", "local") .. NOT_SUPPORTED: "global"
// "global": {
// "enabled": false,
// "range_name": "tbd"
// },
"local": {
"position": "bottom" //(or "top") .. NOT_SUPPORTED: "top"
"position": "bottom" //(or "top")
}
},
"status": {
"position": "top", //(or "bottom", "none")
"merge": false //(if false will be its own separate line, else will merge with query/pagination if they exist)
"position": "top", //(or "bottom", "none", "global")
"merge": false, //(if false will be its own separate line, else will merge with query/pagination if they exist)
"global": {
"range_name": "[sheet!]range"
}
},
"headers": {
"position": "top", //(or "bottom", "top_bottom", "none") .. NOT_SUPPORTED: "bottom", "top_bottom"
"field_filters": [
"# eg -x.*.y / +x.y (start with -s where possible)",
"# pre-built groups: $$<name>",
"#(note fields are laid out in match order)"
], //(# to ignore an entry, [+-] to be +ve/-ve selection, // for regex else */** for single/multi path wildcard)
], //(# to ignore an entry, [+-] to be +ve/-ve selection, // for regex else * for full wildcard)
"exclude_filtered_fields_from_autocomplete": true,
"autocomplete_filters": [ //(only affects autocomplete - eg for aggregations)
"# eg x, -x.*.y, +x.y (start with -s if possible)",
"# pre-built groups: $$<name>"
], //(# to ignore an entry, [+-] to be +ve/-ve selection, // for regex else */** for single/multi path wildcard)
], //(# to ignore an entry, [+-] to be +ve/-ve selection, // for regex else * for full wildcard)
"field_aliases": [
"#field.path=Alias To Use",
"#(note fields are laid out in order within filter matches)"
Expand All @@ -58,9 +59,9 @@ var defaultTableConfig_ = {
"include_note": true, //(adds a note to the top left of each table with the name)
"theme": "minimal" //(or "none", in the future: "default", etc)
},
"skip": {
"rows": "", //comma-separated list of offsets
"cols": "", //comma-separated list of offsets
"skip": { //NOT_SUPPORTED
// "rows": "", //comma-separated list of offsets
// "cols": "", //comma-separated list of offsets
}
//,
// "rotated": false, //(left-to-right instead of top-to-bottom)
Expand All @@ -86,7 +87,7 @@ var defaultTableConfig_ = {
"size": "$$pagination_size",
"_source": "$$field_filters"
},
"script_fields": []
"script_fields": []
// format is {
// name: "string", // the name used in the output column
// source: "string", // the script itself
Expand Down
14 changes: 14 additions & 0 deletions src/server/services/ElasticsearchService.gs
Original file line number Diff line number Diff line change
Expand Up @@ -287,6 +287,7 @@ var ElasticsearchService_ = (function() {

/** Trigger for edit */
function handleContentUpdates(event, triggerOverride) {
var ss = SpreadsheetApp.getActive()
//(copy paste from ElasticsearchManager.isTriggerEnabled_)
var isTriggerEnabled = function(tableConfig, trigger) {
var tableTrigger = tableConfig.trigger || "control_change"
Expand Down Expand Up @@ -316,6 +317,8 @@ var ElasticsearchService_ = (function() {
var tableConfig = matchingTables[matchingTableName]
var activeRange = tableConfig.activeRange
delete tableConfig.activeRange //(remove extra non-standard field)
delete tableConfig.temp //(this is a copy of the meta so doesn't do anything)
//TODO: ^ really we should use the last table config that was _tested_?
tableConfig = tableConfig.temp ? tableConfig.temp : tableConfig //(use current version, not saved)

// Logic to determine if a table edit hits the control cells (query/page)
Expand All @@ -336,6 +339,17 @@ var ElasticsearchService_ = (function() {
return TableRangeUtils_.doRangesIntersect(event.range, newRange)
})

// Also handle any global triggers (including queries):
var globalTriggerRanges =
TableRangeUtils_.getExternalTableRanges(ss, tableConfig, /*controlOnly*/true)
modifiedOffsets = modifiedOffsets.concat(
globalTriggerRanges.filter(function(triggerRange) {
return TableRangeUtils_.doRangesIntersect(event.range, triggerRange)
}).map(function(rangeNotation) {
return "query_offset" //(so that will be treated like a control change)
})
)

if (modifiedOffsets.length > 0) {
if (modifiedOffsets.indexOf("query_offset") >= 0) { //query has changed...
//...if the page is hand specified, reset to 1
Expand Down
16 changes: 12 additions & 4 deletions src/server/services/TableService.gs
Original file line number Diff line number Diff line change
Expand Up @@ -173,11 +173,19 @@ var TableService_ = (function(){
var namedRangeMap = TableRangeUtils_.listTableRanges(ss, Object.keys(tableMap))
var retVal = {}
Object.keys(namedRangeMap).forEach(function(tableName) {
var tableConfig = tableMap[tableName]
var globalTriggerRanges =
TableRangeUtils_.getExternalTableRanges(ss, tableConfig, /*controlOnly*/false)
var namedRange = namedRangeMap[tableName]
if (TableRangeUtils_.doRangesIntersect(range, namedRange.getRange())) {
retVal[tableName] = TableRangeUtils_.shallowCopy(tableMap[tableName])
if (addRange) {
retVal[tableName].activeRange = namedRange.getRange()
var allRangesToTest = globalTriggerRanges.concat([ namedRange.getRange() ])
for (rangeToTestIndex in allRangesToTest) {
var rangeToTest = allRangesToTest[rangeToTestIndex]
if (TableRangeUtils_.doRangesIntersect(range, rangeToTest)) {
retVal[tableName] = TableRangeUtils_.shallowCopy(tableConfig)
if (addRange) {
retVal[tableName].activeRange = namedRange.getRange()
}
break //(no more checks needed, match gets qualified in calling method)
}
}
})
Expand Down
17 changes: 16 additions & 1 deletion src/server/utils/ElasticsearchRequestUtils.gs
Original file line number Diff line number Diff line change
Expand Up @@ -122,6 +122,18 @@ var ElasticsearchRequestUtils_ = (function() {
break
}
retVal.query_offset = { row: queryRow, col: 2 }
} else if ( //Global query, get from its external location:
"global" == TableRangeUtils_.getJson(tableConfig, [ "common", "query", "source" ])
) {
var ss = SpreadsheetApp.getActive()
var globalQueryRef = TableRangeUtils_.getJson(
tableConfig, [ "common", "query", "global", "range_name" ]
)
var globalQueryRange = globalQueryRef ?
TableRangeUtils_.getRangeFromName(ss, globalQueryRef) : null
if (globalQueryRange) {
retVal.query = globalQueryRange.getCell(1, 1).getValue()
}
}

// Status (if not merged)
Expand Down Expand Up @@ -157,7 +169,10 @@ var ElasticsearchRequestUtils_ = (function() {
statusCells.merge()
break
}
}
} else if (!testMode) { // Check for global status
var ss = SpreadsheetApp.getActive()
TableRangeUtils_.handleGlobalStatusInfo(ss, statusInfo, tableConfig)
}

// Headers

Expand Down
33 changes: 19 additions & 14 deletions src/server/utils/ElasticsearchResponseUtils.gs
Original file line number Diff line number Diff line change
Expand Up @@ -195,7 +195,7 @@ var ElasticsearchResponseUtils_ = (function() {
if (debugMode && debugModeSwallowException) debug.push("ERROR: [" + arrayFieldChain + "] vs [" + mutableState.bottom_path + "], "); else
throw new Error(
"By policy, only allowed a single chain of nested aggregations - [" + arrayFieldChain + "] vs [" + mutableState.bottom_path + "], " +
"if you need intermediate buckets you can filter them out by setting 'filter_field' to '-' or '-**'"
"if you need intermediate buckets you can filter them out by setting 'filter_field' to '-' or '-*'"
)
} else if (!mutableState.bottom_path) {
mutableState.bottom_path = arrayFieldChain
Expand Down Expand Up @@ -325,14 +325,16 @@ var ElasticsearchResponseUtils_ = (function() {
json, rows, fullCols,
supportsSize, numHits, numHitsOperator
) {
var ss = SpreadsheetApp.getActive()
var tableRange = TableRangeUtils_.findTableRange(ss, tableName)
var range = null
if (null == tableRange) { //(use current selection, test mode
var ss = SpreadsheetApp.getActive()
var tableRange = TableRangeUtils_.findTableRange(ss, tableName)
var range = null
if (null == tableRange) { //(use current selection, test mode
range = ss.getActiveRange()
} else {
range = tableRange.getRange()
}
} else {
range = tableRange.getRange()
}
var globalStatus =
"global" == TableRangeUtils_.getJson(tableConfig, [ "common", "status", "position" ])

if (null != json.response) {
/** Apply the global filters to the cols and re-order as desired */
Expand Down Expand Up @@ -490,13 +492,13 @@ var ElasticsearchResponseUtils_ = (function() {
}

// Write warnings to status (never to toaster)
if (context.table_meta.status_offset) {
if (context.table_meta.status_offset || globalStatus) {
var warningText = ""
if (warnings.length > 0) {
warningText = ": (WARNINGS = " + warnings.map(function(x) { return "[" + x + "]" }).join(", ") + ")"
}
setQueryResponseInStatus_(range, context.table_meta.status_offset,
"SUCCESS [" + TableRangeUtils_.formatDate() + "]" + warningText)
"SUCCESS [" + TableRangeUtils_.formatDate() + "]" + warningText, tableConfig)
}
} else if (null != json.error_message) {
// Write errors to status or toaster
Expand All @@ -505,8 +507,8 @@ var ElasticsearchResponseUtils_ = (function() {
:
"ERROR [" + TableRangeUtils_.formatDate() + "]" + ": status = [" + json.status + "], msg = [" + json.error_message + "], query = [" + json.query_string + "]"

if (context.table_meta.status_offset) {
setQueryResponseInStatus_(range, context.table_meta.status_offset, requestError)
if (context.table_meta.status_offset || globalStatus) {
setQueryResponseInStatus_(range, context.table_meta.status_offset, requestError, tableConfig)
} else { // pop up toaster
showStatus("[" + tableName + "]: " + requestError, "Query Error")
}
Expand Down Expand Up @@ -685,8 +687,11 @@ var ElasticsearchResponseUtils_ = (function() {
}

/** Adds the error info the status, if necessary */
function setQueryResponseInStatus_(range, statusLocation, errorString) {
range.getCell(statusLocation.row, statusLocation.col).setValue(errorString)
function setQueryResponseInStatus_(range, statusLocation, errorString, tableConfig) {
var ss = SpreadsheetApp.getActive()
if (!TableRangeUtils_.handleGlobalStatusInfo(ss, errorString, tableConfig)) {
range.getCell(statusLocation.row, statusLocation.col).setValue(errorString)
}
}

////////////////////////////////////////////////////////
Expand Down
59 changes: 59 additions & 0 deletions src/server/utils/TableRangeUtils.gs
Original file line number Diff line number Diff line change
Expand Up @@ -354,6 +354,62 @@
return retVal
}

/** For a given table, returns a list of ranges that the table monitors
* and treats like control changes
*/
function getExternalTableRanges(ss, tableConfig, controlOnly) {
var globalControlTriggers =
(TableRangeUtils_.getJson(tableConfig, [ "common", "global_control_triggers" ]) || [])
var globalContentTriggers = !controlOnly ?
(TableRangeUtils_.getJson(tableConfig, [ "common", "global_content_triggers" ]) || []) : []
var globalTriggers = globalControlTriggers.concat(globalContentTriggers)
var isGlobalQuery = "global" == TableRangeUtils_.getJson(tableConfig, [ "common", "query", "source" ])
var globalQuery = TableRangeUtils_.getJson(tableConfig, [ "common", "query", "global", "range_name" ])
if (isGlobalQuery) {
globalTriggers = globalTriggers.concat([ globalQuery ])
}
return globalTriggers.map(function(rangeOrNotation) {
return getRangeFromName(ss, rangeOrNotation)
}).filter(function(range) {
return range != null
})
}

/** Returns the range from a range notation
* TODO: needs to be able to handle named ranges (see getJsonLookup)
*/
function getRangeFromName(ss, rangeOrNotation) {
var isNotation =
rangeOrNotation.indexOf(":") >= 0 || /^[A-Z]+[0-9]+$/.exec(rangeOrNotation)
var isFullNotation = rangeOrNotation.indexOf("!") >= 0

return isNotation ?
(isFullNotation ?
ss.getRange(rangeOrNotation)
:
ss.getActiveSheet().getRange(rangeOrNotation)
)
: null //(see above: also support named ranges, cf getJsonLookup)
}

/** Utility function to write global status info out - returns true iff status _is_ global */
function handleGlobalStatusInfo(ss, statusInfo, tableConfig) {
if ( //Global query, get from its external location:
"global" == TableRangeUtils_.getJson(tableConfig, [ "common", "status", "position" ])
) {
var globalSourceRef = TableRangeUtils_.getJson(
tableConfig, [ "common", "status", "global", "range_name" ]
)
var globalSourceRange = globalSourceRef ?
TableRangeUtils_.getRangeFromName(ss, globalSourceRef) : null
if (globalSourceRange) {
globalSourceRange.getCell(1, 1).setValue(statusInfo)
return true
}
}
return false
}

////////////////////////////////////////////////////////

// 3] Internal utils
Expand Down Expand Up @@ -402,6 +458,9 @@
formatDate: formatDate,
fixSelectAllRanges: fixSelectAllRanges,
doRangesIntersect: doRangesIntersect,
getExternalTableRanges: getExternalTableRanges,
getRangeFromName: getRangeFromName,
handleGlobalStatusInfo: handleGlobalStatusInfo,

TESTONLY: {

Expand Down
Loading

0 comments on commit caba14d

Please sign in to comment.