1
1
/* jshint esversion:8, node:true, strict:true */
2
2
/**
3
3
* Node-RED node transforming a JSON observation from whichever format to another format using a specified JSONata URL.
4
- * Schemas are automatically downloaded and cached the first time they are needed.
4
+ * Schemas are automatically downloaded and cached on disk the first time they are needed.
5
5
* JSONata expressions are cached in memory.
6
6
*/
7
7
8
+ //JSONata: A declarative open-source query and transformation language for JSON data.
8
9
const jsonata = require ( 'jsonata' ) ;
9
10
const util = require ( 'util' ) ;
10
11
@@ -14,105 +15,80 @@ module.exports = RED => {
14
15
function JsonMultiSchemaTransformerNode ( config ) {
15
16
RED . nodes . createNode ( this , config ) ;
16
17
const node = this ;
17
- const transformsUrl = config . transformsUrl ;
18
18
19
19
let lastStatusError = true ;
20
20
node . status ( { fill :'grey' , shape :'ring' , text :'Uninitialized' , } ) ;
21
21
22
22
const jsonCache = require ( './json-cache.js' ) ( node ) ;
23
23
24
- /**
25
- * Find the URL to the JSONata expression to use for the given payload.
26
- */
27
- async function resolveAsync ( payload ) {
28
- const transforms = await jsonCache . loadAsync ( transformsUrl ) ;
29
- let transformUrl = '' ;
30
- for ( const mapping of transforms ) {
31
- if ( mapping . query && mapping . cases ) {
32
- const expression = jsonata ( mapping . query ) ;
33
- let match = expression . evaluate ( payload ) ;
34
- if ( match ) {
35
- if ( match === true ) {
36
- //Special case for boolean
37
- match = "true" ;
38
- }
39
- const result = mapping . cases [ match ] ;
40
- if ( result ) {
41
- transformUrl = result ;
42
- break ;
43
- }
44
- }
45
- }
46
- }
47
- return transformUrl ;
48
- }
49
-
50
24
//Cache of JSONata expressions
51
25
const jsonatas = { } ;
52
26
53
27
/**
54
28
* Transform the given payload with the JSONata expression given in URL.
55
29
*/
56
30
async function transformAsync ( payload , transformUrl ) {
57
- if ( transformUrl ) {
58
- let jsonataExpression ;
59
- let jsonataCache = jsonatas [ transformUrl ] ;
60
- if ( jsonataCache ) {
61
- if ( jsonataCache . expression === null ) {
62
- //Wait for another task to be done building the same JSONata, so that we can use its cache
63
- await new Promise ( ( resolve , reject ) => jsonataCache . mutexQueue . push ( resolve ) ) ;
64
- }
65
- jsonataExpression = jsonataCache . expression ;
66
- } else {
67
- //Build JSONata expression for the given transformation URL
68
- jsonataCache = { expression : null , mutexQueue : [ ] } ;
69
- jsonatas [ transformUrl ] = jsonataCache ;
70
- const transform = await jsonCache . loadAsync ( transformUrl , false ) ;
71
- node . debug ( 'Build JSONata expression for: ' + transformUrl ) ;
72
- jsonataExpression = jsonata ( transform ) ;
73
- jsonataCache . expression = jsonataExpression ;
31
+ if ( ! transformUrl ) {
32
+ return 'Error: Invalid JSONata URL' ;
33
+ }
74
34
75
- //Resume tasks waiting for the same JSONata expression
76
- let next ;
77
- while ( ( next = jsonataCache . mutexQueue . shift ( ) ) != undefined ) {
78
- next ( ) ; //Resolve promise
79
- }
35
+ let jsonataExpression ;
36
+ let jsonataCache = jsonatas [ transformUrl ] ;
37
+ if ( jsonataCache ) {
38
+ if ( jsonataCache . expression === null ) {
39
+ //Wait for another task to be done building the same JSONata, so that we can use its cache
40
+ await new Promise ( ( resolve , reject ) => jsonataCache . mutexQueue . push ( resolve ) ) ;
80
41
}
42
+ jsonataExpression = jsonataCache . expression ;
43
+ } else {
44
+ //Build JSONata expression for the given transformation URL
45
+ jsonataCache = { expression : null , mutexQueue : [ ] } ;
46
+ jsonatas [ transformUrl ] = jsonataCache ;
47
+ const transform = await jsonCache . loadAsync ( transformUrl , false ) ;
48
+ node . debug ( 'Build JSONata expression for: ' + transformUrl ) ;
49
+ jsonataExpression = jsonata ( transform ) ;
50
+ jsonataCache . expression = jsonataExpression ;
81
51
82
- if ( jsonataExpression ) {
83
- //Perform transformation
84
- return jsonataExpression . evaluate ( payload ) ;
52
+ //Resume tasks waiting for the same JSONata expression
53
+ let next ;
54
+ while ( ( next = jsonataCache . mutexQueue . shift ( ) ) != undefined ) {
55
+ next ( ) ; //Resolve promise
85
56
}
86
57
}
58
+
59
+ if ( jsonataExpression ) {
60
+ //Perform transformation
61
+ return jsonataExpression . evaluate ( payload ) ;
62
+ }
63
+
87
64
return false ;
88
65
}
89
66
90
67
node . on ( 'input' , async msg => {
91
- delete msg . transformUrl ;
92
68
msg . error = msg . error ? msg . error + ' ; ' : '' ;
93
- try {
94
- const transformUrl = await resolveAsync ( msg . payload ) ;
95
- if ( transformUrl != '' ) {
96
- msg . transformUrl = transformUrl ;
97
- const result = await transformAsync ( msg . payload , msg . transformUrl ) ;
69
+ if ( msg . schemaUrl == '' ) {
70
+ msg . error += 'Unknown schema!' ;
71
+ } else {
72
+ msg . transformUrl = msg . schemaUrl ;
73
+ try {
74
+ const result = await transformAsync ( msg . payload , msg . schemaUrl ) ;
98
75
if ( result ) {
99
76
msg . payload = result ;
100
77
msg . error = msg . error != '' ;
101
78
} else {
102
- lastStatusError = true ;
103
- node . status ( { fill :'red' , shape :'ring' , text :'Error' , } ) ;
104
- msg . error += util . format ( 'Failed tranforming using "%s"' , transformsUrl ) ;
79
+ msg . error += util . format ( 'Failed tranforming using "%s"' , msg . schemaUrl ) ;
105
80
}
106
81
if ( lastStatusError ) {
107
82
node . status ( { fill :'green' , shape :'dot' , text :'OK' , } ) ;
108
83
lastStatusError = false ;
109
84
}
85
+ } catch ( ex ) {
86
+ lastStatusError = true ;
87
+ node . status ( { fill :'red' , shape :'ring' , text :'Error' , } ) ;
88
+ msg . error += util . format ( 'Error tranforming using "%s": %s' , msg . schemaUrl , ex ) ;
110
89
}
111
- } catch ( ex ) {
112
- lastStatusError = true ;
113
- node . status ( { fill :'red' , shape :'ring' , text :'Error' , } ) ;
114
- msg . error += util . format ( 'Error tranforming using "%s": %s : %s' , transformsUrl , ex , JSON . stringify ( msg . payload ) ) ;
115
90
}
91
+ delete msg . schemaUrl ;
116
92
node . send ( msg ) ;
117
93
} ) ;
118
94
}
0 commit comments