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
|
||||
|
||||

|
||||
|
||||
---
|
||||
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…
x
Reference in New Issue
Block a user