1- var VERSION = '0.3.1' ;
2- var util = require ( 'util' ) ;
1+ var VERSION = '0.4.0' ;
32var assert = require ( 'assert' ) ;
43var aws = require ( 'aws-sdk' ) ;
54var s3 = new aws . S3 ( { apiVersion : '2006-03-01' } ) ;
@@ -9,41 +8,35 @@ var crypto = require('crypto');
98
109// replace below with your credentials in the form key:secret
1110// to create your key go to https://scanii.com/account/settings/keys
12- var SCANII_CREDS = 'CHANGE' ;
11+ var SCANII_CREDS = 'CHANGE:ME ' ;
1312// replace with the URL of your callback lambda function
14- var CALLBACK_URL = 'ME' ;
15-
16-
17- onFindings = function ( bucket , key , result , context ) {
18- console . log ( 'RESULT:' , internalId ( bucket , key ) , 'has findings' , result . findings ) ;
19- var pending = true ;
20-
21- // sample code that delete objects with findings from S3:
22- var req = s3 . deleteObject ( { Bucket : bucket , Key : key } ) ;
23- req . on ( 'success' , function ( response ) {
24- console . log ( 'file' , internalId ( bucket , key ) , 'deleted' ) ;
25- context . succeed ( util . format ( 'callback for file %s completed successful' , result . id ) ) ;
26- pending = false ;
27-
28- } ) . on ( 'error' , function ( response ) {
29- console . log ( 'error while deleting' , internalId ( bucket , key ) ) ;
30- context . fail ( response ) ;
31- pending = false ;
13+ var CALLBACK_URL = 'CHANGE_ME' ;
14+
15+ var onFindings = function ( bucket , key , result ) {
16+ return new Promise ( ( resolve , reject ) => {
17+ "use strict" ;
18+ console . log ( 'RESULT:' , internalId ( bucket , key ) , 'has findings' , result . findings ) ;
19+
20+ // sample code that delete objects with findings from S3:
21+ s3 . deleteObject ( { Bucket : bucket , Key : key } , ( error , data ) => {
22+ if ( error ) {
23+ console . error ( error , error . stack ) ; // an error occurred
24+ reject ( `error while deleting: ${ internalId ( bucket , key ) } please see logs for details` ) ;
25+ } else {
26+ console . log ( 'file' , internalId ( bucket , key ) , 'deleted' ) ;
27+ resolve ( true ) ;
28+ }
29+ } ) ;
3230 } ) ;
33- req . send ( ) ;
34- var waitWhilePending = function ( ) {
35- if ( pending ) {
36- console . log ( 'waiting' ) ;
37- setTimeout ( waitWhilePending , 100 ) ;
38- }
39- } ;
40- setTimeout ( waitWhilePending , 100 ) ;
41-
4231} ;
4332
44- onNoFindings = function ( bucket , key , result , context ) {
45- console . log ( 'RESULT:' , internalId ( bucket , key ) , 'has no findings' ) ;
46- context . succeed ( util . format ( 'callback for file %s completed successful' , result . id ) ) ;
33+ var onNoFindings = function ( bucket , key , result ) {
34+ return new Promise ( ( resolve , reject ) => {
35+ "use strict" ;
36+ console . log ( 'RESULT:' , internalId ( bucket , key ) , 'has no findings' ) ;
37+ console . log ( `callback for file ${ result . id } completed successful` ) ;
38+ resolve ( true ) ;
39+ } ) ;
4740
4841} ;
4942
@@ -57,29 +50,36 @@ if (process.env.SCANII_CREDS !== undefined) {
5750}
5851
5952const API_ENDPOINT = '/v2.1/files/' ;
60-
61- console . log ( 'loading function v' , VERSION ) ;
62-
63- var KEY = SCANII_CREDS . split ( ':' ) [ 0 ] ;
64- var SECRET = SCANII_CREDS . split ( ':' ) [ 1 ] ;
65- console . log ( 'using key' , KEY ) ;
53+ const KEY = SCANII_CREDS . split ( ':' ) [ 0 ] ;
54+ const SECRET = SCANII_CREDS . split ( ':' ) [ 1 ] ;
6655
6756var internalId = function ( bucket , key ) {
68- return util . format ( ' s3://%s/%s' , bucket , key ) ;
57+ return ` s3://${ bucket } / ${ key } ` ;
6958} ;
7059
60+ /**
61+ * Generates a HMAC-SHA1 digital signature for a bucket/key combination using the SECRET as the key
62+ * @param bucket the bucket name
63+ * @param key the key name
64+ * @returns {string } the digitally signed bucket+key combination
65+ */
7166var generateSignature = function ( bucket , key ) {
7267 return crypto . createHmac ( 'sha1' , SECRET ) . update ( internalId ( bucket , key ) ) . digest ( 'hex' ) ;
7368} ;
7469
7570/**
7671 * Handles HTTP result callback
72+ * @param {Object } event the API gateway event to be processed
73+ * @param callback the lambda flow control callback
7774 */
78- var handleCallback = function ( event , context ) {
75+ var handleApiGatewayEvent = ( event , callback ) => {
7976 console . log ( 'handling callback event' ) ;
8077
8178 var r = event ;
82- //console.log('response:', JSON.stringify(r, null, 2));
79+ if ( event . body !== undefined ) {
80+ // if this is proxy request we yank the actual body from the payload
81+ r = JSON . parse ( event . body ) ;
82+ }
8383 console . log ( "metadata:" , r . metadata ) ;
8484
8585 // callback sanity checks
@@ -93,28 +93,56 @@ var handleCallback = function (event, context) {
9393 assert . ok ( r . metadata . signature == generateSignature ( r . metadata . bucket , r . metadata . key ) , "invalid signature" ) ;
9494 console . log ( 'signature check passed for signature' , r . metadata . signature ) ;
9595
96- if ( r . findings !== undefined ) {
97- if ( r . findings . length > 0 ) {
98- onFindings ( r . metadata . bucket , r . metadata . key , r , context ) ;
96+ var handler ;
97+
98+ if ( r . findings !== undefined && r . findings . length > 0 ) {
99+ console . log ( "processing callback with findings " , r . findings ) ;
100+
101+ // if in test mode we really don't dispatch this call
102+ if ( process . env . MOCK_EXTERNAL_SERVICES !== undefined ) {
103+ handler = Promise . resolve ( true ) ;
99104 } else {
100- onNoFindings ( r . metadata . bucket , r . metadata . key , r , context ) ;
105+ handler = onFindings ( r . metadata . bucket , r . metadata . key , r ) ;
101106 }
107+ } else {
108+ handler = onNoFindings ( r . metadata . bucket , r . metadata . key , r ) ;
102109 }
103110
104- } ;
111+ handler . then ( ( result ) => {
112+ "use strict" ;
113+ if ( result === true ) {
114+ // returns a API gateway lambda proxy confirming response:
115+ callback ( null , {
116+ "statusCode" : 200 ,
117+ "headers" : {
118+ "Content-Type" : "application/json"
119+ } ,
120+ "body" : JSON . stringify ( { status : "OK" } , null , 2 )
121+ } ) ;
122+ }
123+ } ) . catch ( ( error ) => {
124+ "use strict" ;
125+ console . error ( error ) ;
126+ callback ( error ) ;
127+ } ) ;
105128
129+ } ;
106130
107131/**
108- * Handles S3 event and submits content for processing
132+ * Handles events from S3 and submits object for processing
133+ * @param event {Object} S3 event to be processed
134+ * @param callback the lambda flow control callback
109135 */
110- var handleS3Event = function ( event , context ) {
111- console . log ( 'handling S3 event' ) ;
112-
136+ var handleS3Event = ( event , callback ) => {
113137 // Get the object from the event and show its content type
114138 var bucket = event . Records [ 0 ] . s3 . bucket . name ;
115139 // see https://forums.aws.amazon.com/thread.jspa?threadID=215813
116140 var key = Object . keys ( qs . decode ( event . Records [ 0 ] . s3 . object . key ) ) [ 0 ] ;
117141
142+ // sanity checks
143+ assert ( bucket !== undefined , "bucket not present in s3 event" ) ;
144+ assert ( key !== undefined , "key not present in s3 event" ) ;
145+
118146 console . log ( 'processing ' + internalId ( bucket , key ) ) ;
119147
120148 // creating signed url for processing
@@ -148,40 +176,66 @@ var handleS3Event = function (event, context) {
148176 headers : {
149177 'Content-Type' : 'application/x-www-form-urlencoded' ,
150178 'Content-Length' : payload . length ,
151- 'User-Agent' : 'scanii-lambda/ v' + VERSION
179+ 'User-Agent' : 'scanii-lambda- v' + VERSION
152180 }
153181 } ;
154- console . log ( payload ) ;
155- // calling scanii API v2.1 (http://docs.scanii.com/v2.1/resources.html):
156- var req = http . request ( options , function ( res ) {
157- console . log ( "headers: " , res . headers ) ;
158-
159- res . on ( 'data' , function ( data ) {
160- var serviceResponse = JSON . parse ( data ) ;
161- if ( res . statusCode == 202 ) {
162- console . log ( 'file id:' , serviceResponse . id ) ;
163- context . succeed ( serviceResponse . id ) ;
164- } else {
165- console . log ( serviceResponse ) ;
166- context . fail ( util . format ( "Error: invalid response from server, message [%s] and http code: %d" , serviceResponse . body , res . statusCode ) ) ;
167- }
168- } ) ;
169- res . on ( 'error' , function ( error ) {
170- context . fail ( error ) ;
182+
183+ var processResponse = ( res , data ) => {
184+ "use strict" ;
185+ var serviceResponse = JSON . parse ( data ) ;
186+ if ( res . statusCode == 202 ) {
187+ console . log ( 'file id:' , serviceResponse . id ) ;
188+ callback ( null , serviceResponse . id ) ;
189+ } else {
190+ console . log ( serviceResponse ) ;
191+ callback ( `Error: invalid response from server, message [${ serviceResponse . body } ] and http code: ${ res . statusCode } ` ) ;
192+ }
193+ } ;
194+
195+ // check whether we should return a canned response (used during testing)
196+ if ( process . env . MOCK_EXTERNAL_SERVICES !== undefined ) {
197+ processResponse ( event . _mock . response , event . _mock . data ) ;
198+ } else {
199+ // calling scanii API v2.1 (http://docs.scanii.com/v2.1/resources.html):
200+ var req = http . request ( options , function ( res ) {
201+ console . log ( "headers: " , res . headers ) ;
202+ res . on ( 'data' , function ( data ) {
203+ processResponse ( res , data ) ;
204+ } ) ;
205+ res . on ( 'error' , function ( error ) {
206+ callback ( error ) ;
207+ } ) ;
171208 } ) ;
172- } ) ;
173209
174- req . write ( payload ) ;
175- req . end ( ) ;
210+ req . write ( payload ) ;
211+ req . end ( ) ;
176212
213+ }
177214} ;
178215
179- exports . handler = function ( event , context ) {
180- console . log ( 'received event:' , JSON . stringify ( event , null , 2 ) ) ;
181- if ( event . Records !== undefined ) {
182- handleS3Event ( event , context ) ;
183- } else {
184- handleCallback ( event , context ) ;
216+ /**
217+ * lambda entry point function
218+ * @param event event to be processed
219+ * @param context lambda environmental context object
220+ * @param callback the lambda flow control callback
221+ */
222+ exports . handler = ( event , context , callback ) => {
223+ console . log ( ">> starting" ) ;
224+ console . log ( `>> using key [${ KEY } ] and function scanii-lambda/v${ VERSION } ` ) ;
225+ if ( process . env . MOCK_EXTERNAL_SERVICES !== undefined ) {
226+ console . log ( ">> mocking external calls!" ) ;
227+ }
228+ console . log ( '>> received event:' , JSON . stringify ( event , null , 2 ) ) ;
229+
230+ try {
231+ if ( event . Records !== undefined ) {
232+ handleS3Event ( event , callback ) ;
233+ } else {
234+ handleApiGatewayEvent ( event , callback ) ;
235+ }
236+ } catch ( error ) {
237+ console . error ( error , error . stack ) ; // an error occurred
238+ callback ( error ) ;
185239 }
186240} ;
187241
0 commit comments