3.4 Il codice - Lato server
3.4.1 dbManager.js
Si è installata attraverso npm la libreria mysql tra le dependencies del package.json. [22] La libreria si comporta da driver nei confronti di un ser-ver MySQL e permette di andare a lavorare sullo schema fota creato in precedenza.
1 c o n s t c o n f i g = r e q u i r e (’ ./ c o n f i g . js ’) ; 2 c o n s t m y s q l = r e q u i r e (’ m y s q l ’) ;
3 c o n s t f o r m a t D a t e = r e q u i r e (’ ./ u t i l s / d a t e . js ’) ; 4 // C r e a t e the p o o l
5 c o n s t p o o l = m y s q l . c r e a t e P o o l ({
6 h o s t : c o n f i g . P O O L _ C O N F I G . host , 7 p o r t : c o n f i g . P O O L _ C O N F I G . port , 8 u s e r : c o n f i g . P O O L _ C O N F I G . user ,
9 d a t a b a s e : c o n f i g . P O O L _ C O N F I G . d a t a b a s e ,
10 c o n n e c t i o n L i m i t : c o n f i g . P O O L _ C O N F I G . c o n n e c t i o n L i m i t , 11 m u l t i p l e S t a t e m e n t s : c o n f i g . P O O L _ C O N F I G . m u l t i p l e S t a t e m e n t s
3.4 – Il codice - Lato server
12 }) ; 13 14
15 p o o l . on (’ r e l e a s e ’, f u n c t i o n ( c o n n e c t i o n ) {
16 c o n s o l e . log (’ C o n n e c t i o n % d r e l e a s e d ’, c o n n e c t i o n . t h r e a d I d ) ; 17 }) ;
18
19 p o o l . on (’ a c q u i r e ’, f u n c t i o n ( c o n n e c t i o n ) {
20 c o n s o l e . log (’ C o n n e c t i o n % d a c q u i r e d ’, c o n n e c t i o n . t h r e a d I d ) ; 21 }) ;
22
23 p o o l . on (’ c o n n e c t i o n ’, f u n c t i o n ( c o n n e c t i o n ) {
24 c o n s o l e . log (’ C o n n e c t i o n % d c r e a t e d ’, c o n n e c t i o n . t h r e a d I d ) ; 25 }) ;
26
27 p o o l . on (’ e n q u e u e ’, f u n c t i o n () {
28 c o n s o l e . log (’ W a i t i n g for a v a i l a b l e c o n n e c t i o n s l o t ’) ; 29 }) ;
30
31 // E x e c u t e the q u e r y p a s s e d in i n p u t 32 f u n c t i o n e x e c u t e Q u e r y ( q u e r y ) {
33 r e t u r n new P r o m i s e (f u n c t i o n( resolve , r e j e c t ) { 34 p o o l . g e t C o n n e c t i o n (f u n c t i o n( err , c o n n e c t i o n ) {
35 if ( err ) {
36 // not c o n n e c t e d !
37 c o n s o l e . log (’ E r r o r d u r i n g the o p e n i n g of the c o n n e c t i o n w i t h d a t a b a s e : ’ + err ) ;
38 r e j e c t ( err ) ;
39 }
40 // Use the c o n n e c t i o n
41 c o n n e c t i o n . q u e r y ( query , f u n c t i o n ( error , results , f i e l d s ) {
42 // W h e n d o n e w i t h the c o n n e c t i o n , r e l e a s e it . 43 c o n s o l e . log ( q u e r y + ’ e x e c u t e d ’) ;
44 c o n n e c t i o n . r e l e a s e () ;
45 // H a n d l e e r r o r a f t e r the r e l e a s e .
46 if ( e r r o r ) {
47 c o n s o l e . log (’ E r r o r d u r i n g the q u e r y : ’ + e r r o r ) ; 48 r e j e c t ( e r r o r ) ;
49 } e l s e {
50 r e s o l v e ( r e s u l t s ) ;
51 }
52 }) ;
53 }) ;
54 }) ;
55 }
56
57 // C h e c k the d e v i c e t a b l e if t h e r e is an e a r l i e r v e r s i o n of the f i r m w a r e
58 a s y n c f u n c t i o n c h e c k V e r s i o n s ( c l a s s D e v i c e , v e r s i o n I n s t a l l e d ) {
59 try {
60 let q u e r y = " S E L E C T * F R O M f o t a . f i r m w a r e W H E R E
c l a s s D e v i c e = ’ "+ c l a s s D e v i c e + " ’ AND v e r s i o n > ’ "+
v e r s i o n I n s t a l l e d +" ’ O R D E R BY v e r s i o n D E S C L I M I T 1; "; 61 r e t u r n e x e c u t e Q u e r y ( q u e r y )
62 . t h e n (f u n c t i o n( r e s u l t s ) { 63 if( r e s u l t s . l e n g t h != 0) {
64 var l a t e s t F i r m w a r e = r e s u l t s [ 0 ] . v e r s i o n ; 65 var i d F i l e = r e s u l t s [ 0 ] . f i l e N a m e I D ;
66 var url = r e s u l t s [ 0 ] . u r l F i r m w a r e ;
67 var u p d a t e = {
68 l a t e s t F i r m w a r e ,
69 idFile ,
70 url
71 }
72 r e t u r n u p d a t e ;
73 }
74 e l s e{
75 c o n s o l e . log (’ D e v i c e is a l r e a d y u p d a t e d ’) ;
76 r e t u r n n u l l;
77 }
78 })
79 .c a t c h(f u n c t i o n( e r r o r ) {
80 c o n s o l e . log (’ An e r r o r o c c u r e d in the c h e c k i n g of f i r m w a r e v e r s i o n s : ’ + e r r o r . t o S t r i n g () ) ;
81 })
82 }c a t c h( e r r o r s ) {
83 c o n s o l e . log ( e r r o r s ) ;
84 }
85 } 86
87 // W h e n a new f i r m w a r e is a v a i l a b l e 88 // R e p o r t the new f i r m w a r e in the t a b l e
89 a s y n c f u n c t i o n n e w F i r m w a r e ( p o s t R e q u e s t , url ) {
90 try {
91 let q u e r y 1 = ’ C R E A T E T A B L E IF NOT E X I S T S f o t a . f i r m w a r e ( ‘ f i l e N a m e I D ‘ V A R C H A R ( 5 0 0 ) NOT NULL , ‘ u r l F i r m w a r e ‘
V A R C H A R ( 5 0 0 ) NULL , ‘ r e a l e a s e D a t e ‘ d a t e t i m e NULL , ‘
u p l o a d D a t e ‘ d a t e t i m e NULL , ‘ version ‘ V A R C H A R ( 4 5 ) NULL ,
‘ c l a s s D e v i c e ‘ V A R C H A R ( 3 0 0 ) NULL , P R I M A R Y KEY ( ‘ s e r i a l N u m b e r ‘) ) ; ’
92 e x e c u t e Q u e r y ( q u e r y 1 )
3.4 – Il codice - Lato server
93 . t h e n (f u n c t i o n( r e s u l t s ) { 94 c o n s o l e . log ( r e s u l t s ) ;
95 var t o d a y = new D a t e () ;
96 let q u e r y 2 = " I N S E R T I N T O f o t a . f i r m w a r e ( f i l e N a m e I D , u r l F i r m w a r e , r e l e a s e D a t e , u p l o a d D a t e , version ,
c l a s s D e v i c e ) V A L U E S ( ’ " + p o s t R e q u e s t . f i l e N a m e I D +
" ’ , ’ " + url + " ’ , ’ " + f o r m a t D a t e (new D a t e ((
p o s t R e q u e s t . r e l e a s e D a t e ) ) ) + " ’ , ’ " + f o r m a t D a t e ( t o d a y . t o U T C S t r i n g () ) + " ’ , ’ " + p o s t R e q u e s t . v e r s i o n
+ " ’ , ’ " + p o s t R e q u e s t . c l a s s D e v i c e + " ’) ; "; 97 e x e c u t e Q u e r y ( q u e r y 2 )
98 . t h e n (f u n c t i o n( r e s u l t s ) { 99 c o n s o l e . log ( r e s u l t s ) ;
100 })
101 .c a t c h(f u n c t i o n( e r r o r ) {
102 c o n s o l e . log (’ An e r r o r o c c u r e d d u r i n g the i n s e r t i o n of new r e c o r d s i n s i d e f i r m w a r e t a b l e : ’ + e r r o r ) ;
103 })
104 })
105 .c a t c h(f u n c t i o n( e r r o r ) {
106 c o n s o l e . log (’ E r r o r d u r i n g the c r e a t i o n of t a b l e f i r m w a r e ’ + e r r o r )
107 })
108 }c a t c h( e r r o r ) {
109 c o n s o l e . log (’ E r r o r in the t a b l e f i r m w a r e : ’ + e r r o r ) ;
110 }
111 } 112
113 // C r e a t e a new t a b l e for the d e v i c e s p r o v i d e d f r o m a p r o d u c t o r
114 a s y n c f u n c t i o n i n i t i a l i z e T a b l e D e v i c e s ( d a t a ) {
115 try {
116 let q u e r y 1 = ’ C R E A T E T A B L E IF NOT E X I S T S f o t a .
p r o d u c t o r d e v i c e s ( ‘ s e r i a l N u m b e r ‘ V A R C H A R ( 5 0 0 ) NOT NULL , ‘ c l a s s D e v i c e ‘ V A R C H A R ( 3 0 0 ) NULL , ‘ f w V e r s i o n ‘ V A R C H A R ( 4 5 ) NULL , P R I M A R Y KEY ( ‘ s e r i a l N u m b e r ‘) ) ; ’
117 e x e c u t e Q u e r y ( q u e r y 1 ) 118 . t h e n (f u n c t i o n( r e s u l t s ) { 119 c o n s o l e . log ( r e s u l t s ) ;
120 let q u e r y 2 = ’ I N S E R T I N T O f o t a . p r o d u c t o r d e v i c e s ( s e r i a l N u m b e r , c l a s s D e v i c e , f w V e r s i o n ) V A L U E S (" ’+ d a t a . s e r i a l N u m b e r + ’ " , " ’+ d a t a . c l a s s D e v i c e + ’ " , "
’+ d a t a . f w V e r s i o n + ’ ") ’;
121 e x e c u t e Q u e r y ( q u e r y 2 ) 122 . t h e n (f u n c t i o n( r e s u l t s ) { 123 c o n s o l e . log ( r e s u l t s ) ;
124 })
125 .c a t c h(f u n c t i o n( e r r o r ) {
126 c o n s o l e . log (’ An e r r o r o c c u r r e d d u r i n g the i n s e r t i o n of new r e c o r d s i n s i d e t a b l e p r o d u c t o r d e v i c e s : ’ + e r r o r ) ;
127 })
128 })
129 .c a t c h(f u n c t i o n( e r r o r ) {
130 c o n s o l e . log (’ E r r o r d u r i n g the c r e a t i o n of t a b l e p r o d u c t o r d e v i c e s ’ + e r r o r ) ;
131 })
132 }c a t c h( e r r o r ) {
133 c o n s o l e . log (’ E r r o r in the t a b l e p r o d u c t o r d e v i c e s ’ + e r r o r ) ;
134 }
135 } 136
137 e x p o r t s . e x e c u t e Q u e r y = e x e c u t e Q u e r y ; 138 e x p o r t s . c h e c k V e r s i o n s = c h e c k V e r s i o n s ; 139 e x p o r t s . n e w F i r m w a r e = n e w F i r m w a r e ;
140 e x p o r t s . i n i t i a l i z e T a b l e D e v i c e s = i n i t i a l i z e T a b l e D e v i c e s ; Listing 3.1. Codice Javascript per il file dbManager.js
Nelle righe 1-3 si vanno ad importare il file di configurazione in cui sono contenuti i parametri della connessione col server MySQL, la libreria mysql ed un modulo date.js scritto appositamente per la formattazione dei dati in un formato compatibile con il tipo SQL datetime di cui si parlerà in fondo al paragrafo.
Il primo metodo richiamato dalla libreria mysql è alla riga 5 ed è create-Pool: il pooling della connessione è una tecnica che viene utilizzata per non ristabilire ogni volta una nuova connessione col database, ma mantenere i dati salvati nella cache del server, permettendo un considerevole aumento della velocità di esecuzione dei comandi ricevuti dal client e riducendo dra-sticamente la latenza. Al metodo viene passata una configurazione JSON, andando ad attingere dal file config.js, che predispone il nome dell’host del database, la porta utilizzata per la comunicazione, l’username e la password per l’autenticazione al server, il nome dello schema a cui si vuole accedere (fota), il numero di connessioni contemporanee ammesse per questo pool per non doverne creare uno nuovo e infine un parametro multipleStatements che indica la possibilità di eseguire più query contemporaneamente sul database.
Quando si verifica uno dei quattro eventi nelle righe 15-29, viene stampato a video un messaggio indicando l’azione che il client ha appena compiuto nei confronti del server:
3.4 – Il codice - Lato server
• release indica che una richiesta è terminata e la rispettiva connessione è di nuovo libera per essere utilizzata da un nuovo processo.
• acquire indica che i dati per la connessione sono stati correttamente acquisiti.
• connection indica che la connessione del pool al server MySQL è avve-nuta correttamente.
• enqueue indica che una richiesta è stata accodata perchè tutte le con-nessioni del pool sono occupate.
La funzione executeQuery al rigo 32 esegue la query SQL passata ritornando come valore una promise che avrà uno stato fullfilled in caso di esecuzione corretta sul database, rejected in caso di errore. La funzione richiama il me-todo getConnection per connettere il pool al server MySQL ritornando un errore in caso di mancata apertura della connessione. Se la connessione è stata aperta correttamente, al rigo 41 la funzione query prende il parametro omonimo passato alla funzione e lo applica al database: in caso di successo la query eseguita verrà stampata a video e la connessione verrà liberata e reintrodotta nel pool e la funzione ritornerà i risultati della query; in caso contrario, la query non verrà eseguita e verrà stampato a schermo il relativo errore.
Al rigo 58 viene introdotta una nuova funzione chiamata checkVersions che andrà ad interrogare la tabella firmware per verificare se esista un aggior-namento più recente per la classe del dispositivo su cui si vuole intervenire: la funzione riceve in ingresso la versione del firmware comunicata dal device e la sua classe. I due parametri citati verranno introdotti all’interno della que-ry SQL dichiarata al rigo 60: si noti come il comando ORDER BY version DESC LIMIT 1 intervenga sui risultati della query riportando soltanto il valore più grande della versione , corrispondente all’ultima release. A questo punto viene richiamata la executeQuery che eseguirà la query e ritornerà una promise: se lo stato della promise è fullfilled, la funzione controlla se la query ha prodotto un risultato che sarà unico per la formulazione della query ivi sopra e se ne acquisiranno i valori relativi alla versione dell’ultimo update di-sponibile, al nome del file d’aggiornamento e all’URL in cui si trova la risorsa nel blob storage. Se invece la query è andata a buon fine ma non ha prodotto risultati significa che il device è già aggiornato all’ultima versione. In coda alla funzione sono presenti i catch per la gestione degli errori generici.
Al rigo 89 viene dichiarata la funzione newFirmware la quale ha il compito di aggiornare il database con le specifiche sul nuovo firmware: la funzione ac-cetta in ingresso un parametro corrispondente al body della richiesta inviata dal firmwarista e l’URL nel quale viene salvata la risorsa. Vengono quindi inserite due promise in cascata, ognuna con il compito di eseguire una que-ry: la query al rigo 91, se non ne esiste già una, crea una tabella firmware identica a quella discussa nel paragrafo 3.4, mentre la seconda al rigo 96 prende i parametri in ingresso alla funzione e li inserisce nella tabella. Va da sé che qui la executeQuery viene richiamata due volte. Anche in questo caso i risultati delle operazioni vengono stampati a schermo in casso di fullfilled delle promise mentre vengono elaborati gli errori in caso di rejection.
Al rigo 114 si instanzia una funzione simile a newFirmware che andrà però ad occuparsi della tabella productordevices; anche in questo caso si co-struisce una query che vada a creare, nel caso in cui non esista, una tabella productordevices identica a quella esposta al paragrafo xxx. Se non ci so-no problemi e la prima query viene applicata, la prima promise ritorna un fullfilled permettendo di entrare nella seconda promise che costruisce una nuova query che inserisce i dati inviati dal costruttore nella tabella. Anche qui i catch in coda alla funzione vengono utilizzati per la gestione dei relativi errori.
Le righe 137-140 vengono utilizzate per esportare le funzioni appena discusse.
date.js
Il file trasforma la stringa passata come input in una stringa salvabile in formato varchar, andando a valutare se i campi anno, mese, giorno, ora, mi-nuto e secondo esistono nella stringa passata in ingresso; nel caso in cui siano presenti, si formattano rispetto ad uno 0 di origine. Il parametro ritornato sarà la data formattata costituita dall’unione delle singole stringhe.
1 /* *
2 * Get a d a t e as s t r i n g in f o r m a t YYYY - MM - D D T h h : mm : ssZ
3 */
4 f u n c t i o n g e t F o r m a t t e d D a t e ( d a t e ) { 5 d a t e = new D a t e ( d a t e ) ;
6 var y e a r = d a t e . g e t F u l l Y e a r () ; 7
8 var m o n t h = (1 + d a t e . g e t M o n t h () ) . t o S t r i n g () ; 9 m o n t h = m o n t h . l e n g t h > 1 ? m o n t h : ’ 0 ’ + m o n t h ; 10
11 var day = d a t e . g e t D a t e () . t o S t r i n g () ;
3.4 – Il codice - Lato server
12 day = day . l e n g t h > 1 ? day : ’ 0 ’ + day ;
13
14 var h o u r s = d a t e . g e t H o u r s () . t o S t r i n g () ;
15 h o u r s = h o u r s . l e n g t h > 1 ? h o u r s : ’ 0 ’ + h o u r s ; 16
17 var m i n s = d a t e . g e t M i n u t e s () . t o S t r i n g () ; 18 m i n s = m i n s . l e n g t h > 1 ? m i n s : ’ 0 ’ + m i n s ; 19
20 var s e c o n d s = d a t e . g e t S e c o n d s () . t o S t r i n g () ; 21 s e c o n d s = s e c o n d s . l e n g t h > 1 ? s e c o n d s : ’ 0 ’ +
s e c o n d s ; 22
23 var f o r m a t t e d D a t e = y e a r + ’ - ’ + m o n t h + ’ - ’ + day +
’ T ’ + h o u r s + ’ : ’ + m i n s + ’ : ’ + s e c o n d s ; 24 r e t u r n f o r m a t t e d D a t e ;
25 }
26
27 m o d u l e . e x p o r t s = g e t F o r m a t t e d D a t e ;
Listing 3.2. Codice Javascript per il file date.js
Si passerà adesso a discutere il file atto all’interlocuzione col blob storage.