Skip to content

Commit ee84605

Browse files
authored
Merge pull request #27 from Alex-At-Home/content-trigger
[self] Split content_change into content_change and control_change
2 parents 4a591f3 + 8f9b6cb commit ee84605

File tree

16 files changed

+543
-55
lines changed

16 files changed

+543
-55
lines changed

.gitignore

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,2 +1 @@
1-
elastic-sheets-project/*
2-
elastic-sheets-share/*
1+
elastic-sheets-*/*

src/frontend/data-models/DataExplorerTemplate.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -117,7 +117,7 @@ var DataExplorerTemplate =
117117
}
118118
}
119119
},
120-
"trigger": "content_change",
120+
"trigger": "control_change",
121121
"sql_table": {
122122
"enabled": false,
123123
"query": "--SHOW TABLES\n--DESCRIBE $$index\n--SELECT * FROM $$index WHERE $$query $$pagination"

src/frontend/managers/elasticsearchManager.js

Lines changed: 45 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,21 @@ var ElasticsearchManager = (function(){
5656
}).getElasticsearchMetadata()
5757
}
5858

59+
/** Util method to check if ES is configured to make requests */
60+
function getEsReadiness(esMeta, onReadyCallback, onNotReadyCallback) {
61+
var esEnabled = esMeta.hasOwnProperty("enabled") ? esMeta.enabled : true
62+
esMeta.enabled = esEnabled //(ensure always present)
63+
var esConfigured = esMeta.url ? true : false
64+
var esUnauthorized = esEnabled &&
65+
(("password" == esMeta.auth_type) && ("" == esMeta.password))
66+
67+
if (esEnabled && esConfigured && !esUnauthorized) {
68+
onReadyCallback(esMeta)
69+
} else {
70+
onNotReadyCallback(esMeta)
71+
}
72+
}
73+
5974
////////////////////////////////////////////////////////
6075

6176
// Internals
@@ -100,15 +115,32 @@ var ElasticsearchManager = (function(){
100115

101116
/** Launches an ES client operation */
102117
function performGenericOperation(tableName, tableConfig, operationLogicFn, trigger, testMode) {
118+
var onReady = function(obj) {
119+
operationLogicFn(tableName, tableConfig, obj, ClientState_.getOrBuildClient(obj.es_meta), testMode)
120+
}
103121
google.script.run.withSuccessHandler(function(obj) {
104-
if (obj && (obj.es_meta.enabled || true)) { //(null used to return error which server has already handled)
105-
//TODO: all sorts of works still to be done on auth
106-
if (("password" == obj.es_meta.auth_type) && ("" == obj.es_meta.password)) {
107-
google.script.run.launchElasticsearchConfig()
122+
if (obj) getEsReadiness(obj.es_meta || {},
123+
function(esMeta) { // ready
124+
onReady(obj)
125+
},
126+
function(esMeta) { // not ready
127+
if (testMode) { // this is fine, carry on
128+
onReady(obj)
108129
} else {
109-
operationLogicFn(tableName, tableConfig, obj, ClientState_.getOrBuildClient(obj.es_meta), testMode)
130+
if ("manual" == trigger) {
131+
if (esMeta.enabled) { //(password or URL missing)
132+
google.script.run.launchElasticsearchConfig()
133+
} else {
134+
Util.showStatus("This table's ES connection is disabled")
135+
}
136+
} else {
137+
delete esMeta.password //(eg if URL removed but password present)
138+
//(will only happen if user unsets authentication after launching table builder)
139+
console.log("Authorization not configured for ES: [" + JSON.stringify(esMeta) + "]")
140+
}
110141
}
111-
}
142+
}
143+
) //(if obj is null, there's been a server-side error which has already been reported)
112144
}).withFailureHandler(function(obj) {
113145
if ("manual" == trigger) { //TODO: move this into Util.showStatus
114146
Util.showStatus("Failed to retrieve ES metadata: [" + JSON.stringify(obj) + "]")
@@ -385,12 +417,14 @@ var ElasticsearchManager = (function(){
385417
/** Checks whether a requested re-populate is desired */
386418
function isTriggerEnabled_(tableConfig, trigger) {
387419
//(copy paste from ElasticsearchService.handleContentUpdates.isTriggerEnabled)
388-
var tableTrigger = tableConfig.trigger || "content_change"
420+
var tableTrigger = tableConfig.trigger || "control_change"
389421
switch(trigger) {
390422
case "manual":
391423
return (tableTrigger != "disabled")
392424
case "config_change":
393425
return (tableTrigger != "disabled") && (tableTrigger != "manual")
426+
case "control_change":
427+
return (tableTrigger == "control_change") || (tableTrigger == "content_change")
394428
case "content_change":
395429
return (tableTrigger == "content_change")
396430
default:
@@ -404,8 +438,11 @@ var ElasticsearchManager = (function(){
404438
populateTable: populateTable,
405439
retrieveIndexPatternFields: retrieveIndexPatternFields,
406440

441+
getEsReadiness: getEsReadiness,
442+
407443
TESTONLY: {
408-
convertFieldFilterToSource_: convertFieldFilterToSource_
444+
convertFieldFilterToSource_: convertFieldFilterToSource_,
445+
isTriggerEnabled_: isTriggerEnabled_
409446
}
410447
}
411448

src/frontend/managers/tableListManager.js

Lines changed: 49 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -147,6 +147,12 @@ var TableListManager = (function() {
147147
/** Timer interval */
148148
var timerInterval_ = 5000 //(ms)
149149

150+
/** Latched when ES is seen to have been configured */
151+
var esAuthConfigured_ = false
152+
153+
/** Latched first time we log that ES isn't configured */
154+
var esAuthConfiguredLog_ = false
155+
150156
/** At desired interval, checks if any tables need refreshing */
151157
function onTableRefresh_() {
152158

@@ -157,23 +163,50 @@ var TableListManager = (function() {
157163
}
158164

159165
// Get tables in need of refresh:
160-
google.script.run.withSuccessHandler(function(obj) {
161-
try {
162-
Object.entries(obj).map(function(kv) {
163-
var tableConfig = State_.getEntryByName(kv[0])
164-
if (tableConfig) {
165-
tableConfig = tableConfig.temp ? tableConfig.temp : tableConfig
166-
ElasticsearchManager.populateTable(kv[0], tableConfig, kv[1], /*testMode*/false)
167-
}
168-
})
169-
} catch (err) {
170-
throw err
171-
} finally {
166+
var lookForTriggeredTables = function() {
167+
google.script.run.withSuccessHandler(function(obj) {
168+
try {
169+
Object.entries(obj).map(function(kv) {
170+
var tableConfig = State_.getEntryByName(kv[0])
171+
if (tableConfig) {
172+
tableConfig = tableConfig.temp ? tableConfig.temp : tableConfig
173+
ElasticsearchManager.populateTable(kv[0], tableConfig, kv[1], /*testMode*/false)
174+
}
175+
})
176+
} catch (err) {
177+
throw err
178+
} finally {
179+
scheduleNextRefresh()
180+
}
181+
}).withFailureHandler(function(err) {
172182
scheduleNextRefresh()
173-
}
174-
}).withFailureHandler(function(err) {
175-
scheduleNextRefresh()
176-
}).listTriggeredTables()
183+
}).listTriggeredTables()
184+
}
185+
186+
if (esAuthConfigured_) {
187+
lookForTriggeredTables()
188+
} else {
189+
// Very simple logic to stop unconfigured ES from stealing triggers
190+
//(of course it's still very easy to steal triggers, just not quite as inadvertently)
191+
google.script.run.withSuccessHandler(function(obj) {
192+
if (obj) ElasticsearchManager.getEsReadiness(obj.es_meta || {},
193+
function(esMeta) { // ready
194+
esAuthConfigured_ = true //(no longer check until table builder reloaded)
195+
lookForTriggeredTables()
196+
},
197+
function(esMeta) { // not ready
198+
if (!esAuthConfiguredLog_) {
199+
delete esMeta.password //(avoid leaking to logs if URL is not configured)
200+
console.log(`ES not configured [${JSON.stringify(esMeta)}], will keep checking until it is`)
201+
esAuthConfiguredLog_ = true
202+
}
203+
scheduleNextRefresh()
204+
}
205+
)
206+
}).withFailureHandler(function(obj) {
207+
console.log("Failed to retrieve ES metadata: [" + JSON.stringify(obj) + "]")
208+
}).getElasticsearchMetadata()
209+
}
177210
}
178211

179212
var State_ = (function() {

src/frontend/view-models/sidebarAppGeneralEditor.js

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@ var GeneralEditor = (function(){
3232
<option value='disabled'>Disabled</option>
3333
<option value='manual'>Manual</option>
3434
<option value='config_change'>Config Change</option>
35+
<option value='control_change'>Control Change</option>
3536
<option value='content_change'>Content Change</option>
3637
</select>
3738
</div>
@@ -100,7 +101,7 @@ var GeneralEditor = (function(){
100101
function populate(index, name, json, tableType) {
101102

102103
// Trigger:
103-
var trigger = json.trigger || "content_change"
104+
var trigger = json.trigger || "control_change"
104105
$(`#trigger_${tableType}_${index}`).val(trigger)
105106

106107
// Query bar

src/server/Code.gs

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ function onInstall() { return UiService_.onOpen() }
1717

1818
/** Edit trigger */
1919
function onEdit(e) {
20-
return ElasticsearchService_.handleContentUpdates(e.range, /*triggerOverride*/null)
20+
return ElasticsearchService_.handleContentUpdates(e, /*triggerOverride*/null)
2121
}
2222

2323
/** Allows for UI to launch a full screen dialog showing the query that would be launched */
@@ -184,5 +184,6 @@ function buildEsSubTable(subTableCell, configOverride) {
184184

185185
/** Triggers a refresh of the table that is currently "under the cursor" */
186186
function refreshSelectedTable() {
187-
return ElasticsearchService_.handleContentUpdates(SpreadsheetApp.getActiveRange(), "manual")
187+
var event = { range: SpreadsheetApp.getActiveRange() }
188+
return ElasticsearchService_.handleContentUpdates(event, "manual")
188189
}

src/server/models/ElasticsearchMetaModel.gs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,6 @@ var esMetaModel_ = {
99
"header_json": {}, //key, value map
1010
"client_options_json": {}, //(passed directly to ES client)
1111
"enabled": true,
12-
"query_trigger": "timed_content", //"none", "timed_config", "timed_content"
12+
"query_trigger": "timed_content", //"none", "timed_config", "timed_control", "timed_content"
1313
"query_trigger_interval_s": 5
1414
}

src/server/models/TableModel.gs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
/** The default table config - also using to sort-of-document the model */
22
var defaultTableConfig_ = {
3-
"trigger": "content_change", //OR "disabled", "manual", "config_change", content_change" - later "timed"
3+
"trigger": "control_change", //OR "disabled", "manual", "config_change", control_change", "content_change" (timed will be separate param)
44
"common": {
55
// "refresh": {
66
// "on_query_change": true,

src/server/services/ElasticsearchService.gs

Lines changed: 73 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ var ElasticsearchService_ = (function() {
2020
// 2] Pre-request logic
2121

2222
/** Update the status field of tables that have changed */
23-
function markTableAsPending(tableName) {
23+
function markTableAsPending(tableName, message) {
2424
try {
2525
var savedObjects = ManagementService_.listSavedObjects(/*discardRange*/false)
2626
var tableConfig = savedObjects[tableName]
@@ -32,8 +32,11 @@ var ElasticsearchService_ = (function() {
3232
var tableRange = TableRangeUtils_.findTableRange(ss, tableName)
3333
if (tableRange) {
3434
var range = tableRange.getRange()
35-
var statusInfo = "AWAITING REFRESH [" + TableRangeUtils_.formatDate() + "]"
36-
var tableMeta = ElasticsearchRequestUtils_.buildTableOutline(tableName, tableConfig, range, statusInfo, /*testMode*/false)
35+
var statusMessage = message || "AWAITING REFRESH"
36+
var statusInfo = statusMessage + " [" + TableRangeUtils_.formatDate() + "]"
37+
var tableMeta = ElasticsearchRequestUtils_.buildTableOutline(
38+
tableName, tableConfig, range, statusInfo, /*testMode*/false
39+
)
3740
}
3841
}
3942
} catch (err) {} //(fire and forget, just for display)
@@ -50,6 +53,11 @@ var ElasticsearchService_ = (function() {
5053
return { "es_meta": esInfo }
5154
}
5255

56+
// If ES is enavled we're not going to do anything to the table:
57+
if ((false == esInfo.enabled) || !esInfo.url) {
58+
testMode = true
59+
}
60+
5361
// Table metadata/validation
5462

5563
// Revalidate range:
@@ -262,37 +270,88 @@ var ElasticsearchService_ = (function() {
262270
}
263271

264272
/** Trigger for edit */
265-
function handleContentUpdates(range, triggerOverride) {
273+
function handleContentUpdates(event, triggerOverride) {
266274
//(copy paste from ElasticsearchManager.isTriggerEnabled_)
267275
var isTriggerEnabled = function(tableConfig, trigger) {
268-
var tableTrigger = tableConfig.trigger || "content_change"
276+
var tableTrigger = tableConfig.trigger || "control_change"
269277
switch(trigger) {
270278
case "manual":
271279
return (tableTrigger != "disabled")
272280
case "config_change":
273281
return (tableTrigger != "disabled") && (tableTrigger != "manual")
282+
case "control_change":
283+
return (tableTrigger == "control_change") || (tableTrigger == "content_change")
274284
case "content_change":
275285
return (tableTrigger == "content_change")
276286
default:
277287
return true
278288
}
279289
}
280-
var trigger = triggerOverride || "content_change"
281290
var triggerPolicy = ManagementService_.getEsTriggerPolicy()
282-
if (!triggerOverride && ("timed_content" != triggerPolicy)) {
283-
return
291+
var canBeTriggeredByContentChange =
292+
("timed_control" == triggerPolicy) || ("timed_content" == triggerPolicy)
293+
if (!triggerOverride && !canBeTriggeredByContentChange) {
294+
return -1
284295
}
285-
var matchingTables = TableService_.findTablesIntersectingRange(range)
296+
var updatedTables = 0
297+
var matchingTables = TableService_.findTablesIntersectingRange(event.range, /*addRange*/true)
286298
Object.keys(matchingTables).forEach(function(matchingTableName) {
299+
//TODO: also need to handle 2-way sync regardless of table trigger
287300
var tableConfig = matchingTables[matchingTableName]
288-
tableConfig = tableConfig.temp ? tableConfig.temp : tableConfig
289-
if (isTriggerEnabled(tableConfig, trigger)) {
301+
var activeRange = tableConfig.activeRange
302+
delete tableConfig.activeRange //(remove extra non-standard field)
303+
tableConfig = tableConfig.temp ? tableConfig.temp : tableConfig //(use current version, not saved)
304+
305+
// Logic to determine if a table edit hits the control cells (query/page)
306+
var isControlEvent = function() {
307+
// Check metadata to see if it's a control or content change
308+
var retVal = ElasticsearchRequestUtils_.buildTableOutline(
309+
matchingTableName, tableConfig, activeRange, "", /*testMode*/true
310+
)
311+
var offsets = [ "query_offset", "page_info_offset" ]
312+
var modifiedOffsets = offsets
313+
.filter(function(offset) {
314+
return retVal.hasOwnProperty(offset)
315+
})
316+
.filter(function(offset) {
317+
var newRange = activeRange.offset(
318+
retVal[offset].row - 1, retVal[offset].col - 1, 1, 1
319+
)
320+
return TableRangeUtils_.doRangesIntersect(event.range, newRange)
321+
})
322+
323+
if (modifiedOffsets.length > 0) {
324+
if (modifiedOffsets.indexOf("query_offset") >= 0) { //query has changed...
325+
//...if the page is hand specified, reset to 1
326+
if (retVal.page_info_offset) {
327+
var pageRange = activeRange.offset(
328+
retVal.page_info_offset.row - 1, retVal.page_info_offset.col - 1, 1, 1
329+
)
330+
if (!pageRange.getFormulaR1C1()) {
331+
pageRange.setValue(1)
332+
}
333+
}
334+
}
335+
return true
336+
} else {
337+
return false
338+
}
339+
}
340+
var triggerToUse = triggerOverride ?
341+
triggerOverride :
342+
(isControlEvent() ? "control_change" : "content_change")
343+
344+
if (isTriggerEnabled(tableConfig, triggerToUse)) {
290345
markTableAsPending(matchingTableName)
291346
ManagementService_.setSavedObjectTrigger(
292-
matchingTableName, trigger
347+
matchingTableName, triggerToUse
293348
)
294-
}
295-
})
349+
updatedTables++
350+
} else if ("disabled" != tableConfig.trigger) { // Just note the table has been changed
351+
markTableAsPending(matchingTableName, "HAND EDITED")
352+
}//(if the table is disabled, do nothing)
353+
})//(end loop over intersecting tables)
354+
return updatedTables
296355
}
297356

298357
////////////////////////////////////////////////////////

src/server/services/ManagementService.gs

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -164,7 +164,12 @@ var ManagementService_ = (function(){
164164
case "manual":
165165
trigger = "manual" //nothing can overwrite manual (except "")
166166
break
167-
case "config_change": //(everything except data_change overwrites)
167+
case "config_change": //(everything except control/content_change overwrites)
168+
trigger =
169+
(("control_change" == trigger) || ("content_change" == trigger))
170+
? curr : trigger
171+
break
172+
case "control_change": //(everything except content_change overwrites)
168173
trigger = ("content_change" == trigger) ? curr : trigger
169174
break
170175
default: //("" or "content_change" - always update)

0 commit comments

Comments
 (0)