Avete un foglio Google Sheet che volete interrogare da codice ? Si, si può fare, associando al foglio un opportuno script e pubblicandolo come una web app da interrogare via API. In questo articolo vedremo come fare passo passo (usando Flutter).
Prepariamo quanto serve
Se non lo abbiamo già disponibile, creiamo un nuovo foglio di lavoro nel nostro Google Drive, con qualche riga e qualche colonna (come questo, ad esempio):
e dal menu
Estensioni, avviamo la console
App Script.
Affinché lo script possa essere pubblicato come una web app, occorre che rispetti questi requisiti:
- definisca una funzione doGet(request) oppure doPost(request) o entrambe
- le funzioni definite restituiscano un oggetto HtmlService o ContentService (spoiler, noi useremo questo)
Ovviamente, già si intuisce che il codice della funzione doGet() sarà eseguito in caso di interrogazione HTTP di tipo Get, mentre doPost() in caso di una richiesta Post e il parametro request conterrà i dettagli della richiesta (qui maggiori dettagli)
Lo script
Per lo scopo di questo articolo, definiamo la funzione doPost() in questo modo:
function doPost(request) {
var sheetID = request.parameter.sheetID;
var action = request.parameter.action;
if (action == "read") {
return readData(sheetID, request);
} else{
return ContentService
.createTextOutput(JSON.stringify({"status": "FAILED", "message": "action is NOT defined"}))
.setMimeType(ContentService.MimeType.JSON)
}
}
Questa funzione recupera dalla richiesta due parametri: l'id del foglio e una stringa con l'azione desiderata (arbitrariamente definita da noi). Lo script gestirà la richiesta solo in caso di azione di tipo "read". Negli altri casi restituirà un messaggio di errore.
Definiamo ora la funzione readData(sheetID, request):
function readData(sheedID, request) {
var result = {"status": "NA", "data" : []};
var sheet = SpreadsheetApp.openById(sheedID);
var sheetData = sheet.getDataRange().getValues()
var columns = sheetData.shift();
result['total_rows'] = sheetData.length;
result['data'] = sheetData;
result['columns'] = columns;
result['status'] = "SUCCESS";
return ContentService
.createTextOutput(JSON.stringify(result))
.setMimeType(ContentService.MimeType.JSON)
}
Questa funzione apre il foglio richiesto e recupera alcune informazioni:
- l'intero contenuto (sheetData)
- il numero di righe presenti
- le colonne presenti
e restituisce il tutto, come ContentService in formato Json.
Pubblichiamo lo script
Salviamo il codice scritto (e se vogliamo damogli un nome) e procediamo alla pubblicazione utilizzando la funzione Nuovo deployment nel menu Esegui il deployment (se necessario, abilitiamo il tipo "Applicazione web" dalla rotellina di configurazione).
Associamo una descrizione, confermiamo la casella "Esegui come" e indichiamo che chiunque possa eseguire l'app:
Confermando l'esecuzione, vi verrà richiesto di autorizzare l'accesso associando il vostro account Google. Confermata l'autorizzazione, otterrete due stringhe (da copiare e conservare):
- l'ID di implementazione (un token da indicare nelle richieste)
- l'URL da usare per le chiamate HTTP
Alle due stringe ottenute, aggiungiamone una terza, lo sheetId da recuperare dall'url del foglio (è la stringa alfanumerica abbastanza lunga, ben riconoscibile).
Ogni volta che modificate lo script, il deploy va ripetuto, ottenendo un nuovo ID di implementazione.
Usiamo le API da codice
Per semplicità, anziché creare un nuovo progetto Flutter, useremo
DartPad per scrivere e testare il codice.
Come prima cosa cancelliamo il contenuto della funzione
main() predefinita e creiamo le costanti con i dati ottenuti nella fase di deploy dell'app:
String deploymentID = "AKfycbxNsl10n...............HPqYT8uKCwfewK_jTo0wKk";
String scriptUrl = "https://script.google.com/macros/s/$deploymentID/exec";
String sheetID = "19w77cZyG9IuB.......1HsokUP9Nw";
Creiamo quindi una funzione che effettui la chiamata POST alla web app:
Future<Map> triggerWebAPP({required String url, required Map body}) async {
Map dataDict = {};
Uri urlPost = Uri.parse(url);
try {
await http.post(urlPost, body: body).then((response) async {
if (response.statusCode == 200) {
dataDict = jsonDecode(response.body);
} else {
print("StatusCode: ${response.statusCode}");
}
});
} catch (e) {
print("Error: $e");
}
return dataDict;
}
Questa funzione, utilizzando la libreria http, effettua una chiamata POST, con i parametri che vedremo dopo e se lo statusCode è 200 (OK), restituisce una mappa con i dati Json decodificati .
Aggiungiamo le necessarie dipendenze allo script:
import 'package:http/http.dart' as http;
import 'dart:convert';
import 'dart:async';
La funzione per chiamare lo script non deve far altro che passare i dati della nostra richiesta:
void main() async {
Map body = {"sheetID": sheetID, "action": "read"};
Map dataDict = await triggerWebAPP(url: scriptUrl, body: body);
int total_rows = dataDict["total_rows"];
print(total_rows);
List columns = dataDict["columns"];
print(columns);
List data = dataDict["data"];
print(data);
}
e stampare i risultati che ci aspettavamo:
Righe: 3
Colonne: [ID Prodotto, Nome Prodotto, Descrizione, Quantità, Prezzo Unitario]
Data:
[[1, Maglietta, Maglietta di cotone, taglia M, colore blu, 50, 10], [2, ... ]]
Un caso particolare
Potrebbe capitare che lo statusCode restituito sia 302 (ovvero Redirect). Per gestire questo caso particolare, la funzione triggerWebAPP() va modifica in questo modo:
Future<Map> triggerWebAPP({required String url, required Map body}) async {
Map dataDict = {};
Uri urlPost = Uri.parse(url);
try {
await http.post(urlPost, body: body).then((response) async {
if (response.statusCode == 200) {
dataDict = jsonDecode(response.body);
} else if (response.statusCode == 302) {
String redirectedUrl = response.headers['location'] ?? "";
if (redirectedUrl.isNotEmpty) {
Uri url = Uri.parse(redirectedUrl);
await http.get(url).then((response) {
if (response.statusCode == 200) {
dataDict = jsonDecode(response.body);
}
});
}
} else {
print("StatusCode: ${response.statusCode}");
}
});
} catch (e) {
print("Error: $e");
}
return dataDict;
}
Conclusioni
Sarebbe interessante provare realizzare un'intera applicazione CRUD su un foglio Google.
Come per gli articoli precedenti potete trovare il codice completo tra le mie repo GitHub:
https://github.com/luigimicco/webapp_test.
Commenti
Posta un commento