Knowledge Base
  • Introduction
  • Events and Actions
  • Action Panel
  • sending email
  • calling a SQL action from a client side js action
  • Accessing to translations form a server
  • Executing SQL statements from within an action
  • How to invoke a generic SQL statement defined through a SQL action
  • How to show a message dialog
  • checking for "undefined" values
  • How to add spaces to the right of a text
  • How to create a docx report and show it on the web browser Enterprise Edition only
  • How to get or set a value from the graphics control
  • How to invoke a generic SQL query defined through a business component
  • How to remove spaces to the left and right of a text
  • How to support multiple themes in a single application, accoding to a rule
  • How to set content to a Google Map linked to a grid or form
  • How to replace all occurences of a pattern from a text
  • Utility methods
  • Link auto login
  • Creation of a link for the first access of a new user without give the user a password and forcing
  • Forgot password
  • setting up default values from values coming from a filter panel
  • identifing the modified record after the alteration
  • enabling/disabling checkboxes in a grid
  • Filtering a Lookup
  • formatting a column
  • using checkboxes to select rows in grid
  • showing a summary row in grid
  • Disabling a toolbar button
  • Configuring grid exports
  • Adding filter conditions to a grid
  • Filtering the grid content from a tree
  • Filtering the tree content, starting from a filter panel linked to a grid
  • collapsing a panel
  • validating a lookup
  • accessing the authorizations set for a specific grid
  • How to design a web service
  • How to remotelly invoke an action or business component or perform a write operation through a Restf
  • how to feed a grid from a JS business component
  • converting a JS object to a JSON string
  • executing a query
  • passing parameters to a server side JS action
  • return value
  • scheduling and frequency
  • finding the right filter panel
  • checking out if a component has been defined
  • Deploying an application
  • Enquiring a table belonging to the Platform repository
  • Adding a where clause to a business component linked to grid
  • Integrating Mailchimp lists
  • Formatting a number as a currency value to use it inside an email template
  • sending email from a template
  • How to send an email
  • Error 'smtpHost' is empty
  • Linking two windows
  • How to open manually a window from another window
  • How to open manually a popup window
  • How to hide a panel in a window dinamically
  • How to manage folder panels
  • How to manage card panels
  • Predefined variables supported by Platform
  • Accessing the application parameters
  • Application Log
  • How to design a web service
  • How to import java classes in server
  • How to import java classes in server
  • How to dynamically set a value on a combo
  • 4WS.Platform
  • How to listen to events in a mobile HTML panel
  • Issues with HTTPS requests
  • How to manage row totals in grid
  • How to send to the UI a notification to execute code automatically
  • How to filter a chart by date interval
  • How to filter a grid by date interval
  • How to read a text or csv file and save data on the database
  • How to write text or csv files
  • Reading an xls file stored in the specified path
  • How to create a report with Jasper Report
  • How to customize the alert message content
  • Setting up a cluster
  • Uploading and downloading files
  • How to listen to user definition changes
  • How to auto-show a window from login
  • How to manage encrypted fields
  • How to change CSS settings for a grid row
  • Customizing a Tree Panel
  • How to execute complex queries on Google Datastore
  • Theme customization
  • Retrieve and send the log of a mobile app
  • Import Roles and Users
  • How to synchronize multiple Form panels in the same window
  • Anchor buttons
  • Properties of subpanels
  • Bulk import
  • How to display the data not found message in a grid
  • How to setup an LDAP based authentication
  • How to synchronize data from Datastore to BigQuery
  • How to synchronize data from Datastore to Google Spanner
  • How to synchronize data from Datastore to CloudSQL
  • Scrollable form list
  • How to setup SAML authentication
  • How to export data from BigQuery in streaming
  • Update Google Spreadsheet
  • How to setup OAuth2 authentication
Powered by GitBook
On this page
  • Example of the final result
  • Retrieve and send the log
  • Client side action
  • Server side action
  • Define an incoming webhook

Was this helpful?

Retrieve and send the log of a mobile app

(6.0.2 version)

PreviousTheme customizationNextImport Roles and Users

Last updated 3 years ago

Was this helpful?

Example of the final result

Retrieve and send the log

The following code allows you to:

  • retrieve the log of your application

  • retrieve the local database

  • upload everything to a bucket on GCP

  • send a message to a chat room with links to download everything

Client side action

This action can be invoked for example when the user presses a "send report" button.

Replace XXX_SERVER_ACTION_ID with your server action and logDir whit the bucket folder id.

in this action there are 4 important functions that you can adapt to your purpose:

  • saveUnsentCrash

  • saveDb

  • saveScreenshot

  • saveLog

  • sendChatNotification

/**
 * Send a notification to Chat
 */
function sendChatNotification() {

    var notifParam = {
        fileLogPath: destFileNameLog,
        fileDbPath: destFileNameDb,
        fileCrashPath: destFileNameCrash,
        fileScreenPath: destFileNameScreen
    };
    
    var notifNote = "";
    
    if (!notifParam.fileLogPath || notifParam.fileLogPath == "") {
        notifNote += "Nessun LOG inviato\n";
    }
    if (!notifParam.fileDbPath || notifParam.fileDbPath == "") {
        notifNote += "Nessun DB inviato\n";
    }
    if (!notifParam.fileScreenPath || notifParam.fileScreenPath == "") {
        notifNote += "Nessuno SCREENSHOT inviato\n";
    }
    if (!notifParam.fileCrashPath || notifParam.fileCrashPath == "") {
        notifNote += "Nessun CRASH inviato\n";
    }
    
    if (notifNote && notifNote != "") {
        notifNote += "\n";
    }
    
    if (note && note != "") {
        notifNote += note;
    }
    
    notifParam.note = notifNote;
    
    executeServerAction(XXX_SERVER_ACTION_ID, notifParam);
}


/**
 * Upload log error
 */
function showError(){
    //invio le notifiche, anche se parziali
    asyncFunction("sendChatNotification", []);
    
    //....update your UI
}

/**
* Callback after file send
**/
function sendCallback(responseFromWebService) {
    
    if(!responseFromWebService){
        showError();
    }else{
        var respObj = JSON.parse(responseFromWebService);
        if(respObj.success){
            asyncFunction("sendChatNotification", []);
            
            //....update your UI
        }else{
            showError();    
        }
    }
    
}

/**
 * Save on bucket crashes not sent to crashlytics
 */
function saveUnsentCrash(connectionData){
    log("Connection data: " + connectionData);
    try{
        //recupero il file di log
        var crashPath = getCrashlyticsUnsentReports();
        if(crashPath){
                
            //invio il file al server che lo carica nel bucket
            var logDir = 49;
            //platform in autmatico mette già COMPANY_ID/SITE_ID
            //il file avrà un percorso tipo 210_1001/210_1001_20191126_123000.zip
            destFileNameCrash = destFilePrefix + "_CRASH.zip";
            var url = getBaseURL() + "/uploadfiles/uploadfilegrid?appId=MOBILE_SHOP&applicationId=MOBILE_SHOP&dirId=" + logDir + "&unzip=false&restfulToken="+getToken();
            sendFile(url, crashPath, destFileNameCrash, "saveDb");
        }else{
             log("[3629] saveUnsentCrash SKIPPED NO PATH");
            saveDb();
        }
        
        try{
            //invio i report mancanti a crashlytics
            sendCrashlyticsUnsentReports();
        }catch(ec){
            log("[3629] sendCrashlyticsUnsentReports error: " + ec);
        }
    
    }catch(e){
        log("[3629] saveUnsentCrash ERROR: " + e);
        saveDb();
    }
}

/**
 * Save on bucket the app db
 */
function saveDb() {
    
    try {
        var dbPath = getDbPath("DBNOSYNC"); //DBNOSYNC | DBCON | DBREADONLY
        
        if (dbPath) {
            var zipPathDb = zipFile(dbPath,"dbnosync.zip");
            //invio il file al server che lo carica nel bucket
            var logDirDb = 49;
            var filePathDb = zipPathDb;
            //platform in autmatico mette già COMPANY_ID/SITE_ID
            //il file avrà un percorso tipo 210_1001/210_1001_20191126_123000.zip
            destFileNameDb = destFilePrefix + "_DB.zip";
            var urlDb = getBaseURL() + "/uploadfiles/uploadfilegrid?appId=MOBILE_SHOP&applicationId=MOBILE_SHOP&dirId=" + logDirDb + "&unzip=false&restfulToken="+getToken();
            sendFile(urlDb, filePathDb, destFileNameDb, "saveScreenshot");
        }else {
            log("[3629] saveDb SKIPPED NO PATH");
            saveScreenshot();
        }
    }catch(e) {
        log("[3629] saveDb ERROR: " + e);
        saveScreenshot();
    }
}

/**
 * Save on bucket an eventualy user screenshots passed in the window params
 */
function saveScreenshot() {
    log("[3629] SAVE SCREEN");
    try {
        //uso il path fisso!!
        var screenPath = getWindowParameter('screenshotPath');
        log("[3629] screenPath: " + screenPath);
        if (screenPath) {
            var zipPathScreen = zipFile(screenPath,"screenshot.zip");
            //invio il file al server che lo carica nel bucket
            var logDirDb = 49;
            var filePathScreen = zipPathScreen;
            //platform in autmatico mette già COMPANY_ID/SITE_ID
            //il file avrà un percorso tipo 210_1001/210_1001_20191126_123000.zip
            destFileNameScreen = destFilePrefix + "_SCREEN.zip";
            var urlDb = getBaseURL() + "/uploadfiles/uploadfilegrid?appId=MOBILE_SHOP&applicationId=MOBILE_SHOP&dirId=" + logDirDb + "&unzip=false&restfulToken="+getToken();
            sendFile(urlDb, filePathScreen, destFileNameScreen, "saveLog");
        }else {
            log("[3629] saveScreenshot SKIPPED NO PATH");
            saveLog();
        }
    }catch(e) {
        log("[3629] saveScreenshot ERROR: " + e);
        saveLog();
    }
}

/**
 * Save on bucket the log file
 */
function saveLog(){
    log("[3629] NOTE SEGNALAZIONE: " + note);
    //recupero il file di log
    var logPath = getLogFilePath();
    if(logPath){
        
        //faccio uno zip del log
        var zipPath = zipFile(logPath, "log.zip");
        if(zipPath){
            
            //invio il file al server che lo carica nel bucket
            var logDir = 49;
            var filePath = zipPath;
            //platform in autmatico mette già COMPANY_ID/SITE_ID
            //il file avrà un percorso tipo 210_1001/210_1001_20191126_123000.zip
            destFileNameLog = destFilePrefix + "_LOG.zip";
            var url = getBaseURL() + "/uploadfiles/uploadfilegrid?appId=MOBILE_SHOP&applicationId=MOBILE_SHOP&dirId=" + logDir + "&unzip=false&restfulToken="+getToken();
            sendFile(url, zipPath, destFileNameLog, "sendCallback");
     
            //condivide lo zip con le app presenti nel device
            //shareDocument(zipPath,log.zip);
        }else{
            log("[3629] no zipPath");
            showError();
        }
    }else{
        log("[3629] no logPath");
        showError();
    }
}

function inviaSegnalazione() {
    //invia crahs, log e db dell'app
    saveUnsentCrash("");
}


var lastSyncDate = getVariable("LAST_SYNC_DATE");

var destFilePrefix = userInfo.username + "/" + userInfo.username + "_" + userInfo.deviceId + "_" + stringifyDate(new Date().getTime(), "yyyyMMdd_HHmmss", true);
var destFileNameLog = "";
var destFileNameDb = "";
var destFileNameCrash = "";
var destFileNameScreen = "";
var note = ""//TODO get user not from your UI

asyncFunction("inviaSegnalazione", [],400);

Server side action

Replace WEB_HOOK_URL with the id of your web hook.

function appendWidget(topLabel,content,contentMultiline,iconUrl) {
    var tmpWdgt = {
        "keyValue": {
            "topLabel": topLabel,
            "content": content,
            "contentMultiline":contentMultiline,
            "iconUrl":iconUrl
        }
    };
    widgets.push(tmpWdgt);
}

//Invia la notifica a google Chat se viene caricata una segnalazione nel bucket

//WEBHOOK DI GOOGLE CHAT
var googleChatURL = "WEB_HOOK&threadKey="+utils.getUUID();
utils.log("[8479] vo: " + JSON.stringify(vo,jsonReplacer),"INFO");

var response = {succcess:true};
var BUCKET_DIR_ID = 49;//bucket segnalazioni

try{
    var bucketName = utils.getDirectoryPath(BUCKET_DIR_ID);
    var expiration = new Date().getTime() + (60 * 60 * 1000); //il link ha durata di 60 minuti
    var filePathBase = userInfo.companyId + "/" + userInfo.siteId + "/";
    
    var buttonsArr = [];
    //LOG DELL'APP
    if (vo.fileLogPath) {
        var fileLogPath = filePathBase + vo.fileLogPath;
        var bucketLogUrl = utils.getGoogleCloudStorageSignedURL("GET", expiration, bucketName, fileLogPath, null) + "";
        //aggiungo il bottone per scaricare il db dell'app
        var logButton = {
            "textButton": {
                "text": "CONTROLLA SEGNALAZIONE (60min Link)",
                "onClick": {
                    "openLink": {
                        "url": bucketLogUrl
                    }
                }
            }
        };
        buttonsArr.push(logButton);
    }
    //DB DELL'APP
    if (vo.fileDbPath) {
        var fileDbPath = filePathBase + vo.fileDbPath;
        var bucketDbUrl = utils.getGoogleCloudStorageSignedURL("GET", expiration, bucketName, fileDbPath, null) + "";
        //aggiungo il bottone per scaricare il db dell'app
        var dbButton = {
            "textButton": {
                "text": "SCARICA DATI APPLICATIVI (60min Link)",
                "onClick": {
                    "openLink": {
                        "url": bucketDbUrl
                    }
                }
            }
        };
        buttonsArr.push(dbButton);
    }
    //CRASH DELL'APP
    if (vo.fileCrashPath) {
        var fileCrashPath = filePathBase + vo.fileCrashPath;
        var bucketCrashUrl = utils.getGoogleCloudStorageSignedURL("GET", expiration, bucketName, fileCrashPath, null) + "";
        //aggiungo il bottone per scaricare il db dell'app
        var crashButton = {
            "textButton": {
                "text": "SCARICA CRASH NON INVIATI (60min Link)",
                "onClick": {
                    "openLink": {
                        "url": bucketCrashUrl
                    }
                }
            }
        };
        buttonsArr.push(crashButton);
    }
    //SCREENSHOT DELL'APP
    if (vo.fileScreenPath) {
        var fileScreenPath = filePathBase + vo.fileScreenPath;
        var bucketScreenUrl = utils.getGoogleCloudStorageSignedURL("GET", expiration, bucketName, fileScreenPath, null) + "";
        //aggiungo il bottone per scaricare il db dell'app
        var screenButton = {
            "textButton": {
                "text": "SCARICA SCREENSHOT (60min Link)",
                "onClick": {
                    "openLink": {
                        "url": bucketScreenUrl
                    }
                }
            }
        };
        buttonsArr.push(screenButton);
    }
    
    var widgets = [];
    //Widget Company - Site
    appendWidget(
        "Company - Site",
        userInfo.companyId + " - " + userInfo.siteId,
        "true",
        "https://github.com/google/material-design-icons/blob/master/png/social/group/materialicons/24dp/1x/baseline_group_black_24dp.png?raw=true"
    );
    //Widget Username
    appendWidget(
        "Utente",
        userInfo.username,
        "true",
        "https://github.com/google/material-design-icons/blob/master/png/social/person/materialicons/24dp/1x/baseline_person_black_24dp.png?raw=true"
    );
    //Widget Data e Ora segnalazione
    appendWidget(
        "Data e Ora Segnalazione",
        utils.convertDateToString(utils.getCurrentDateAndTime(), "yyyy-MM-dd HH:mm:ss"),
        "true",
        "https://github.com/google/material-design-icons/blob/master/png/social/person/materialicons/24dp/1x/baseline_person_black_24dp.png?raw=true"
    );
    //widget Nome del bucket
    appendWidget(
        "Nome del bucket",
        bucketName,
        "true",
        "https://github.com/google/material-design-icons/blob/master/png/file/folder/materialicons/24dp/1x/baseline_folder_black_24dp.png?raw=true"
    );
    
    //Widget note
    if (vo.note && vo.note !== "") {
        appendWidget(
            "Note",
            vo.note,
            "true",
            "https://github.com/google/material-design-icons/blob/master/png/notification/sms/materialicons/24dp/1x/baseline_sms_black_24dp.png?raw=true"
        );  
    }
    
    //aggiungo i pulsanti
    var buttons = {
        "buttons": buttonsArr
    };
    
    widgets.push(buttons);
    
    var requestBody = {
                "cards": [
                    {
                        "header": {
                            "title": "<b>Nuova segnalazione ricevuta</b>",
                            "imageUrl": "https://w7.pngwing.com/pngs/169/167/png-transparent-package-delivery-mail-box-parcel-box-thumbnail.png",
                            "imageStyle": "AVATAR"
                        },
                        "sections": [
                            {
                                "widgets": widgets
                            }
                        ]
                    }
                ]
            };
            
    utils.getWebContent(googleChatURL, false, "POST", "application/json", JSON.stringify(requestBody,jsonReplacer), null, null);
    
}catch(e) {
    utils.log("[8479] Invio notifica fallito: " + e.toString(),"INFO");
}
utils.setReturnValue(JSON.stringify(response,jsonReplacer));

Define an incoming webhook

From the chat room menu:

  1. Select Configure Webhooks

    A dialog appears that lists any incoming webhooks already defined for the room.

  2. Click on ADD WEBHOOK.

  3. Fill in the name field and optionally the avatar URL field.

  4. Click SAVE.

  5. Copy and save the webhook URL

Once you're strong enough, save the world.

Official documentation:

https://developers.google.com/hangouts/chat/how-tos/webhooks
example of notification made by an app user
example of automatic report on error