Action Link

La funzionalità Action Link, introdotta nella release 2021.06+, ha lo scopo di consentire la definizione di link che consentono, quando attivati da un utente o da un applicativo esterno, l’esecuzione di azioni in risposta che possono essere:

  • la visualizzazione di una pagina di risposta;
  • la restituzione di dati in formato json;
  • la ridirezione ad un’altra pagina.

Il tutto governato da uno script definito dal programmatore, che può valutare i parametri passati nel link stesso, arbitrariamente definiti dal programmatore stesso, eventualmente aggiornare il database e compiere l’azione richiesta fra quelle sopra indicate.

 

La scheda di configurazione si presenta come nella figura seguente:

L’Action Link ID è un valore univoco che viene generato dal sistema, ma può essere modificato, e che identificata in maniera univoca il link. Deve essere sempre passato nel parametro ACTIONID del link.
Il flag Restituisci flusso JSON, se attivato, definisce che la risposta al link sarà di tipo “application/json” anzichè una pagina HTML. Lo script dovrà quindi comportarsi di conseguenza, come specificato più di seguito.
Analogamente, il flag Restituisci flusso iCalendar, se attivato, definisce che la risposta al link sarà di tipo “text/calendar” secondo la specifica iCalendar, consentendo di utilizzare l’action link per configurare un “calendario internet” su Microsoft Outlook.
Lo Script consente di definire l’azione che verrà eseguita in risposta all’esecuzione del link. Può essere scritto in VB.NET, C# o Python. Al suo interno si possono utilizzare le variabili “DB”, oggetto di tipo QWDatabase che rappresenta un riferimento al database di QualiWare, e “Params”, che contiene, sotto forma di array associativo (AssocArray). Lo script deve ritornare, tramite l’istruzione “Return”, una stringa che può essere:

  • nel caso non sia spuntato il flag Restituisci flusso JSON,  un testo in formato HTML che verrà visualizzato in una pagina di risposta standard;
  • nel caso sia spuntato il flag Restituisci flusso JSON,  un testo in formato JSON che verrà utilizzato da un altro applicativo per acquisire informazioni.

Il secondo caso è particolarmente interessante in quanto dà la possibilità di creare Web Service REST che possono essere utilizzati da altri applicativi per integrarsi con QualiWare.

NOTA: nel primo caso, la pagina di risposta viene visualizzata in modalità non autenticata, senza, cioè, un utente corrente, a meno che il link non venga richiamato all’interno di una sessione di QualiWare, nel qual caso ne eredita la sessione.

Le chiamate ad Action Link vengono registrate sulla tabella LOGDOC con IDDOC=’**’ e CODDOC=’ACTIONLINK’. Nel campo “NOTE” vengono registrati l’indirizzo IP del chiamante, i parametri della chiamata (ad esclusione dell’eventuale payload) e la risposta restituita al chiamante.

E’ inoltre possibile aggiungere, alla chiamata, i parametri OTLP_TraceId e OTLP_SpanID, che vengono registrati su LOGDOC e possono essere utilizzati per effettuare una traccia globale di un flusso. Si veda l’apposita pagina di approfondimento.

Di seguito riportiamo alcuni esempi di script riferiti alle varie possibilità.

Visualizzazione di un messaggio

Il seguente script restituisce, nella pagina di risposta, tutti i parametri del link. Può essere utilizzato come punto di partenza per utilizzare i parametri stessi per effettuare altri tipi di azione, come ad esempio l’aggiornamento di informazioni sul database, e restituirne l’esito nella pagina di risposta.

dim k as string=PARAMS.firstkey()

dim html as string=""
while not empty(k)
   html+=k+"="+PARAMS(k)+"<BR>"
   k=PARAMS.nextkey(k)
end while

return html

Restituzione di un flusso JSON 

Il seguente script restituisce un flusso json che rappresenta l’oggetto “obj”, che può essere definito dinamicamente in base alle esigenze. Fra i valori restituiti vi è anche quello del parametro “PARAMETRO1” assegnato nel link.

dim obj=New With {.valore1 = "campo1", .valore2 = 100.24, .valore3 = true, .valore4=now(), .parametro1=Params("PARAMETRO1")}

return newtonsoft.Json.jsonconvert.SerializeObject(obj)

Restituzione di un flusso JSON a partire da un flusso JSON nel payload

Il seguente script restituisce un flusso json a partire da un flusso json inviato nel payload della richiesta. Questa modalità è tipica dei Web Service Rest.

Dim obj As Newtonsoft.Json.linq.JObject
Dim jsonr as string

Page.Request.InputStream.Seek(0,System.IO.SeekOrigin.Begin)
Using receiveStream As System.IO.Stream = Page.Request.InputStream
   Using readStream As System.IO.StreamReader = New System.IO.StreamReader(receiveStream, System.Text.Encoding.UTF8)
      jsonr = readStream.ReadToEnd()
   End Using
End Using

obj=Newtonsoft.Json.linq.JObject.Parse(jsonr)  

dim ret_obj=New With {.output1 = ctype(obj("input1"),Newtonsoft.Json.Linq.JValue).Value, .output2 = ctype(obj("input2"),Newtonsoft.Json.Linq.JValue).Value   }

return newtonsoft.Json.jsonconvert.SerializeObject(ret_obj)

Richiamando il link e specificando il seguente payload:

{
    "input1": "valore1",
    "input2": "valore2"
}

verrà restituito il seguente flusso.

{
    "output1": "valore1",
    "output2": "valore2"
}

Ridirezione ad un’altra pagina

Il seguente script rimanda ad un’altra pagina.

Page.Response.Redirect("GEMANOCO2.ASPX")

Restituzione di un calendario iCalendar

Il seguente script mostra come sia possibile restituire un calendario internet, configurabile in Microsoft Outlook, con la possibilità di definire tutte le proprietà degli eventi in esso inclusi, compreso il colore. Si faccia riferimento ai commenti nel codice per le opportune spiegazioni.
Il link dell’Action Link può essere inserito in Outlook per configurare un calendario che si aggiorna automaticamente in base ai dati contenuti in QualiWare e restituiti dall’Action Link stesso. La frequenza di aggiornamento può essere stabilita nello script assegnando la proprietà “X-PUBLISHED-TTL”.

' Creazione di un calendario utilizzando l'oggetto DDay.Ical.Calendar, che consente di assegnare le proprietà degli eventi come proprietà
' di oggetti contenuti in una collection
' N.B. non tutte le proprietà contenute nello standard sono disponibili come proprietà dell'oggetto, ma è possibile ugualmente assegnarle
' utilizzando il metodo AddProperty dell'evento
' Tutte le proprietà disponibili sono visibili qui: https://icalendar.org/

dim resp as string

Dim iCal As DDay.iCal.iCalendar = New DDay.iCal.iCalendar()

iCal.Method = "PUBLISH"
iCal.Version = "2.0"
iCal.AddProperty("X-WR-CALNAME", "Calendario di prova Action link")
iCal.AddProperty("PRODID", "QualiWare")
iCal.AddProperty("X-PUBLISHED-TTL", "PT1H")       ' Tempo di refresh di Outlook: 1H=1 ora
iCal.AddProperty("X-WR-TIMEZONE", TimeZone.CurrentTimeZone.StandardName)

Dim evt As DDay.iCal.Components.Event 

' **** Evento 1
evt = iCal.Create(Of DDay.iCal.Components.Event)()
evt.Summary = "Evento di prova 1"
evt.Start = New DDay.iCal.DataTypes.iCalDateTime(new date(2022,01,30,12,00,00).ToUniversalTime)
evt.Start.HasTime = True
evt.Start.IsUniversalTime = True
evt.End = New DDay.iCal.DataTypes.iCalDateTime(new date(2022,01,30,15,00,00).ToUniversalTime)
evt.End.HasTime = True
evt.End.IsUniversalTime = True
evt.Duration=TimeSpan.FromHours(3) ' durata in ore
evt.Description = "Descrizione dell'evento 1 (corpo)"
evt.Location = "Locazione dell'evento"
evt.IsAllDay = false ' mettere true se l'evento occupa tutto il giorno
evt.UID = "001" ' Codice univoco dell'evento
evt.Organizer = New DDay.iCal.DataTypes.Cal_Address("pguidotti@qualiware.it") ' Mail dell'organizzatore
evt.Status = DDay.iCal.DataTypes.EventStatus.Confirmed

' aggiunge l'allarme esplicitamente
evt.AddProperty("BEGIN", "VALARM")
evt.AddProperty("ACTION", "DISPLAY")
evt.AddProperty("DESCRIPTION", "Evento di prova 1")
evt.AddProperty("TRIGGER", "-PT15M")
evt.AddProperty("END", "VALARM")

' aggiunge il colore
evt.AddProperty("CATEGORIES","Categoria Blu") ' N.B. la descrizione della categoria deve essere quella presente nell'Outlook del destinatario

' **** Evento 2
evt = iCal.Create(Of DDay.iCal.Components.Event)()
evt.Summary = "Evento di prova 2"
evt.Start = New DDay.iCal.DataTypes.iCalDateTime(DateAdd(DateInterval.Day,1,now()).ToUniversalTime) ' Piazza l'evento ad un giorno da oggi
evt.Start.HasTime = True
evt.Start.IsUniversalTime = True
evt.End = New DDay.iCal.DataTypes.iCalDateTime(DateAdd(DateInterval.Hour,1,DateAdd(DateInterval.Day,1,now())).ToUniversalTime)
evt.End.HasTime = True
evt.End.IsUniversalTime = True
evt.Duration=TimeSpan.FromHours(1) ' durata in ore
evt.Description = "Descrizione dell'evento 2 (corpo)"
evt.Location = "Locazione dell'evento"
evt.IsAllDay = false ' mettere true se l'evento occupa tutto il giorno
evt.UID = "002" ' Codice univoco dell'evento
evt.Organizer = New DDay.iCal.DataTypes.Cal_Address("pguidotti@qualiware.it") ' Mail dell'organizzatore
evt.Status = DDay.iCal.DataTypes.EventStatus.Confirmed

' aggiunge l'allarme esplicitamente
evt.AddProperty("BEGIN", "VALARM")
evt.AddProperty("ACTION", "DISPLAY")
evt.AddProperty("DESCRIPTION", "Evento di prova 2")
evt.AddProperty("TRIGGER", "-PT15M")
evt.AddProperty("END", "VALARM")

' aggiunge il colore
evt.AddProperty("CATEGORIES","Categoria Verde") ' N.B. la descrizione della categoria deve essere quella presente nell'Outlook del destinatario

Dim serializer As DDay.iCal.Serialization.iCalendarSerializer = New DDay.iCal.Serialization.iCalendarSerializer(iCal)
return serializer.SerializeToString()  ' ritorno del calendario serializzato

Protezione di un Action link tramite token

E’ possibile proteggere tramite token un action link che restituisce dati in formato JSon. Per farlo, si può utilizzare il seguente codice:

dim ok as boolean=true
dim msg as string=""
dim emmsg as string=""
dim errcode as integer = 0

try	
        ' Verifica il Token, che è nel payload della richiesta, proprietà "Token"
  Dim jsonr as string
  Page.Request.InputStream.Seek(0,System.IO.SeekOrigin.Begin)

  Using receiveStream As System.IO.Stream = Page.Request.InputStream
    Using readStream As System.IO.StreamReader = New System.IO.StreamReader(receiveStream, System.Text.Encoding.UTF8)
      jsonr = readStream.ReadToEnd()
    End Using
  End Using

  objtmp = Newtonsoft.Json.Linq.JObject.Parse(jsonr)
  
  dim token as string
  token = ctype(objtmp("Token"), Newtonsoft.Json.Linq.JValue).value
  
  dim valid_token as boolean = QWLib.Webutils.CheckToken2(token)
  
  if valid_token 
    ' Inserire qui l'elaborazioen dell'Action Link
  else
    errcode = 1
    msg += " Token non valido!"
  end if
catch e as exception
   errcode= -1
   writelog("ERRORE ACTION LINK : " & e.message+vbcr+e.stacktrace.tostring())
   msg= "Errore nell'elaborazione della richiesta."
   emmsg = e.message+vbcr+e.stacktrace.tostring() 
End try

dim ret_obj=New With {.ResultCode = errcode, .Result = ..... , .ResultMessage = msg, .ErrorMsg = emmsg  }
return newtonsoft.Json.jsonconvert.SerializeObject(ret_obj)

Il token deve essere preventivamente richiesto dal chiamante utilizzando il Web Service Rest “GetUserToken” dell’SDK di QualiWare, e passato all’action link nel payload nella proprietà “Token”.