Initial commit
This commit is contained in:
commit
787d6bc85e
21
LICENSE.md
Normal file
21
LICENSE.md
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
MIT License
|
||||||
|
|
||||||
|
Copyright (c) 2022 marco@sdf.org
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
of this software and associated documentation files (the "Software"), to deal
|
||||||
|
in the Software without restriction, including without limitation the rights
|
||||||
|
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
copies of the Software, and to permit persons to whom the Software is
|
||||||
|
furnished to do so, subject to the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be included in all
|
||||||
|
copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
SOFTWARE.
|
51
README.md
Normal file
51
README.md
Normal file
@ -0,0 +1,51 @@
|
|||||||
|
# Apps Script Showcase
|
||||||
|
|
||||||
|
![screenshot showing ](./screenshot.png)
|
||||||
|
|
||||||
|
---
|
||||||
|
This is an example how one could build a frontend based on Google Sheets and Apps Script to generate a JSON document which serves as a configuration input for an SLA monitoring service.
|
||||||
|
|
||||||
|
The SLA monitoring service expects a JSON document in the following structure (array of objects containing configuration parameters).
|
||||||
|
|
||||||
|
```json
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"defg-5678__used_payment_methods" : {
|
||||||
|
"schedule" : "* * * * 1",
|
||||||
|
"account_identifier" : "",
|
||||||
|
"included_payment_methods" : "",
|
||||||
|
"check_name" : "used_payment_methods",
|
||||||
|
"additional_configuration" : "",
|
||||||
|
"organisation_identifier" : "defg-5678",
|
||||||
|
"excluded_payment_methods" : ""
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"hijk-1234__used_payment_methods" : {
|
||||||
|
"schedule" : "* * * * 2-5,7",
|
||||||
|
"account_identifier" : "",
|
||||||
|
"included_payment_methods" : "",
|
||||||
|
"organisation_identifier" : "hijk-1234",
|
||||||
|
"additional_configuration" : "",
|
||||||
|
"check_name" : "used_payment_methods",
|
||||||
|
"excluded_payment_methods" : "ECA"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"abcd-1234__used_payment_methods" : {
|
||||||
|
"additional_configuration" : "VIS:3,ECA:5",
|
||||||
|
"organisation_identifier" : "abcd-1234",
|
||||||
|
"check_name" : "used_payment_methods",
|
||||||
|
"excluded_payment_methods" : "",
|
||||||
|
"schedule" : "* * * * 1-5",
|
||||||
|
"included_payment_methods" : "VIS,ECA,FOO",
|
||||||
|
"account_identifier" : "efgh-567"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
```
|
||||||
|
|
||||||
|
The idea is that a support-agent can enter the configuration parameters (per customer/organisation and account) into a Google Sheet and then "deploy" them.
|
||||||
|
Valid configurations will then be available via an API (see `doGet()` in the [apps-script/web](https://developers.google.com/apps-script/guides/web) documentation) for the SLA monitoring service to poll.
|
||||||
|
|
||||||
|
See [src/Script.js](./src/Script.js) for how the deployment and validation works and [src/SidebarTemplate.html](./src/SidebarTemplate.html) for how the sidebar is built.
|
BIN
screenshot.png
Normal file
BIN
screenshot.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 324 KiB |
116
src/Script.js
Normal file
116
src/Script.js
Normal file
@ -0,0 +1,116 @@
|
|||||||
|
let scriptProperties = PropertiesService.getScriptProperties();
|
||||||
|
let supportedChecks = [
|
||||||
|
'used_payment_methods',
|
||||||
|
];
|
||||||
|
|
||||||
|
function onOpen() {
|
||||||
|
SpreadsheetApp.getUi()
|
||||||
|
.createMenu('Monitoring 👀')
|
||||||
|
.addItem('Deploy configurations', 'deploy')
|
||||||
|
.addToUi();
|
||||||
|
}
|
||||||
|
|
||||||
|
function doGet() {
|
||||||
|
return ContentService.createTextOutput(JSON.stringify(getStoredChecks())).setMimeType(ContentService.MimeType.JSON);
|
||||||
|
}
|
||||||
|
|
||||||
|
function storeChecksById(checksById) {
|
||||||
|
scriptProperties.setProperties(checksById);
|
||||||
|
}
|
||||||
|
|
||||||
|
function getStoredChecks() {
|
||||||
|
arr = [];
|
||||||
|
Object.entries(scriptProperties.getProperties()).forEach(([key, value]) => {
|
||||||
|
obj = {};
|
||||||
|
obj[key] = JSON.parse(value);
|
||||||
|
arr.push(obj);
|
||||||
|
});
|
||||||
|
return arr;
|
||||||
|
}
|
||||||
|
|
||||||
|
function getStoredCheckIds() {
|
||||||
|
return Object.keys(getStoredChecks());
|
||||||
|
}
|
||||||
|
|
||||||
|
function getCheckId(check) {
|
||||||
|
return check['organisation_identifier'] + '__' + check['check_name'];
|
||||||
|
}
|
||||||
|
|
||||||
|
function validateCheck(check) {
|
||||||
|
let errors = [];
|
||||||
|
if (getCheckId(check).split('__').includes('')) {
|
||||||
|
errors.push('Check is missing `organisation_identifier` or `check_name`');
|
||||||
|
}
|
||||||
|
if (!supportedChecks.includes(check['check_name']) && check['check_name'] !== '') {
|
||||||
|
errors.push('Check is not supported: `' + check['check_name'] + '`');
|
||||||
|
}
|
||||||
|
if (check['schedule'] && !/^[1-7]+((,[1-7])|(-[1-7]))*$/.test(check['schedule'])) {
|
||||||
|
errors.push('Please specify the schedule as a comma separated list and/or range of weekday numbers between 1 and 7, like for example: `1-5`, `1,3,5,7` or `1-3,5`');
|
||||||
|
} else {
|
||||||
|
check['schedule'] = '* * * * ' + check['schedule'];
|
||||||
|
}
|
||||||
|
if ('' !== check['included_payment_methods'] !== check['excluded_payment_methods']) {
|
||||||
|
re = /^([A-Z]+,)*[A-Z]+$|^$/;
|
||||||
|
if (!re.test(check['included_payment_methods']) || !re.test(check['excluded_payment_methods'])) {
|
||||||
|
errors.push('Please specify payment methods as a comma separated list, like for example: `VIS,ECA` ');
|
||||||
|
}
|
||||||
|
if (!check['included_payment_methods'] === !check['excluded_payment_methods']) {
|
||||||
|
errors.push('Please specify only either `included_payment_methods` or `excluded_payment_methods`, or leave both fields empty to check all payment methods');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return errors;
|
||||||
|
}
|
||||||
|
|
||||||
|
function getValidatedChecksById(checks) {
|
||||||
|
let arr = [];
|
||||||
|
checks.forEach(function(check) {
|
||||||
|
let obj = {};
|
||||||
|
obj[getCheckId(check)] = {}
|
||||||
|
obj[getCheckId(check)]['check'] = check;
|
||||||
|
obj[getCheckId(check)]['errors'] = validateCheck(check);
|
||||||
|
arr.push(obj)
|
||||||
|
});
|
||||||
|
return arr;
|
||||||
|
}
|
||||||
|
|
||||||
|
function validateAndStoreChecks(checks) {
|
||||||
|
let validatedChecksById = getValidatedChecksById(checks);
|
||||||
|
let validChecks = validatedChecksById.filter(check => {return check[Object.keys(check)[0]]['errors'].length === 0});
|
||||||
|
|
||||||
|
let checksToStore = {};
|
||||||
|
validChecks.forEach(function (check) {
|
||||||
|
let key = Object.keys(check)[0];
|
||||||
|
let value = JSON.stringify(check[key]['check']);
|
||||||
|
checksToStore[key] = value;
|
||||||
|
});
|
||||||
|
|
||||||
|
storeChecksById(checksToStore);
|
||||||
|
|
||||||
|
let storedCheckIdsToRemove = getStoredCheckIds().map(c => c).filter(check => !validChecks.map(c => Object.keys(c)[0]).includes(check));
|
||||||
|
storedCheckIdsToRemove.forEach(function (check_name) {
|
||||||
|
scriptProperties.deleteProperty(check_name);
|
||||||
|
});
|
||||||
|
|
||||||
|
return validatedChecksById;
|
||||||
|
}
|
||||||
|
|
||||||
|
function deploy() {
|
||||||
|
let html = HtmlService.createTemplateFromFile('DeploymentsSidebarTemplate');
|
||||||
|
let data = SpreadsheetApp.getActive().getSheetByName("Payments Monitoring Configurations").getDataRange().getValues();
|
||||||
|
|
||||||
|
scriptProperties.deleteAllProperties();
|
||||||
|
|
||||||
|
let checks = [];
|
||||||
|
for (let i = 0; i < data.length; i++) {
|
||||||
|
check = {};
|
||||||
|
for (let j = 0; j < data[0].length; j++) {
|
||||||
|
check[data[0][j]] = data[i][j];
|
||||||
|
}
|
||||||
|
checks.push(check);
|
||||||
|
}
|
||||||
|
checks.shift();
|
||||||
|
|
||||||
|
html.deployments = validateAndStoreChecks(checks);
|
||||||
|
|
||||||
|
SpreadsheetApp.getUi().showSidebar(html.evaluate());
|
||||||
|
}
|
42
src/SidebarTemplate.html
Normal file
42
src/SidebarTemplate.html
Normal file
@ -0,0 +1,42 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<base target="_top">
|
||||||
|
<style>
|
||||||
|
body {
|
||||||
|
font-family: Arial, Helvetica, sans-serif;
|
||||||
|
}
|
||||||
|
dl {
|
||||||
|
font-size: 14px;
|
||||||
|
}
|
||||||
|
dd {
|
||||||
|
display: list-item;
|
||||||
|
list-style-type: disc;
|
||||||
|
}
|
||||||
|
hr {
|
||||||
|
border: 1px solid black;
|
||||||
|
}
|
||||||
|
table, th, td {
|
||||||
|
border: 1px solid red;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
</head>
|
||||||
|
|
||||||
|
<body>
|
||||||
|
<h1>🚀 Deployments</h1>
|
||||||
|
<hr />
|
||||||
|
<? for (var i = 0; i < deployments.length; i++) {
|
||||||
|
var errors = deployments[i][Object.keys(deployments[i])[0]]['errors'];
|
||||||
|
var sig = errors.length === 0 ? '✅' : '🛑';
|
||||||
|
?>
|
||||||
|
<dl>
|
||||||
|
<dt><?= sig ?> <?= Object.keys(deployments[i]) ?>
|
||||||
|
(row <?= (i + 2) ?>)
|
||||||
|
</dt>
|
||||||
|
<? for (var j = 0; j < errors.length; j++) { ?>
|
||||||
|
<dd><?= errors[j] ?></dd>
|
||||||
|
<? } ?>
|
||||||
|
</dl>
|
||||||
|
<? } ?>
|
||||||
|
</body>
|
||||||
|
</html>
|
Loading…
Reference in New Issue
Block a user