Looks now like the lambda function is the blocker, it currently is only configured to send JSON content and not binary. We have made some amendments to the function but currently it isn't working at it is a very tricky beast to debug directly in lambda.Â
The code is below but ideally if there is anyway to test this directly in lamba to see where it is faulty but we haven't worked out what we need to pass to test.
Any suggestions are welcome, thanks
console.log('Loading function');
var https = require('https');
var querystring = require('querystring');
var host = 'Âxx].fmecloud.com';
var path = '/fmedatastreaming/APIrepository/';
var dataZIPObj = false; Â
/*
* Handles the HTTP requests to FME Cloud.
*/
var makeHttpRequest = function (httpMethod, event, callback) {
    var dataJsonObj = '';
    var qs = querystring.stringify(event.params.querystring)
    var paths = querystring.stringify(event.params.path);
var statusCode;
var parsed;
   Â
    var options = {
      hostname: host,
      port: 443,
      path: path + event.workspace + '?' + qs + '&' + paths,
      method: httpMethod
    };
    var req = https.request(options, function(response){
        //Trigger an error response if the ajax request fails
        response.on('error', function(d) {
            console.log("error");
            if (callback) {
                callback({
                    statusCode: response.statusCode
                });
            }
        });
       Â
        //Populate the JSON object as data comes in.
        response.on('data', function(d) {
            if (response.headersÂ'content-type'] === 'application/json') {
                dataJsonObj += d;
            }
            if (response.headers 'content-type'] === 'application/zip') {
                dataZIPObj = true;
            }
        });
       Â
        //Populate the callback object with the json response and status code.
        response.on('end', function(d) {
            //Check to see if there is any data returned from the request
            if(dataJsonObj === "" && dataZIPObj === false){
                if (callback) {
                    callback({
                        body: { "status": response.statusCode, "message": response.statusMessage},
                        statusCode: response.statusCode,
                        statusMessage: response.statusMessage
                    });
                }
            }else if(dataZIPObj){
                parsed = d;
if(parsed.status){
                    //Use status code defined in JSON
                    statusCode = parsed.status;
}
}
else {
               Â
                //JSON was returned in the request, process.
                parsed = JSON.parse(dataJsonObj);
               Â
                //FME can return a 200 HTTP status but send a warning error back, this
                //overrides the status code on the request object with the status code
                //in the JSON from FME.
             Â
                if(parsed.status){
                    //Use status code defined in JSON
                    statusCode = parsed.status;
                }else{
                    //Default to HTTP status code returned by request   Â
                    statusCode = response.statusCode;
                }
               Â
                if (callback) {
                    callback({
                        body: parsed,
                        statusCode: statusCode,
                        statusMessage: response.statusMessage
                    });
                }
            }
        });
      return response;
    });
    req.end();
};
/*
* Handler that gets called by the API gateway.
*/
exports.handler = function(event, context) {
    try {
        var httpMethod = event.context "http-method"];
        makeHttpRequest(httpMethod, event, function (response) {
if (!dataZIPObj)
            return context.fail(JSON.stringify(response.body));
elseÂ
return response.body;
        });
    } catch (e) {
        context.fail(e);
    }
};
Looks now like the lambda function is the blocker, it currently is only configured to send JSON content and not binary. We have made some amendments to the function but currently it isn't working at it is a very tricky beast to debug directly in lambda.Â
The code is below but ideally if there is anyway to test this directly in lamba to see where it is faulty but we haven't worked out what we need to pass to test.
Any suggestions are welcome, thanks
console.log('Loading function');
var https = require('https');
var querystring = require('querystring');
var host = 'Âxx].fmecloud.com';
var path = '/fmedatastreaming/APIrepository/';
var dataZIPObj = false; Â
/*
* Handles the HTTP requests to FME Cloud.
*/
var makeHttpRequest = function (httpMethod, event, callback) {
    var dataJsonObj = '';
    var qs = querystring.stringify(event.params.querystring)
    var paths = querystring.stringify(event.params.path);
var statusCode;
var parsed;
   Â
    var options = {
      hostname: host,
      port: 443,
      path: path + event.workspace + '?' + qs + '&' + paths,
      method: httpMethod
    };
    var req = https.request(options, function(response){
        //Trigger an error response if the ajax request fails
        response.on('error', function(d) {
            console.log("error");
            if (callback) {
                callback({
                    statusCode: response.statusCode
                });
            }
        });
       Â
        //Populate the JSON object as data comes in.
        response.on('data', function(d) {
            if (response.headersÂ'content-type'] === 'application/json') {
                dataJsonObj += d;
            }
            if (response.headers 'content-type'] === 'application/zip') {
                dataZIPObj = true;
            }
        });
       Â
        //Populate the callback object with the json response and status code.
        response.on('end', function(d) {
            //Check to see if there is any data returned from the request
            if(dataJsonObj === "" && dataZIPObj === false){
                if (callback) {
                    callback({
                        body: { "status": response.statusCode, "message": response.statusMessage},
                        statusCode: response.statusCode,
                        statusMessage: response.statusMessage
                    });
                }
            }else if(dataZIPObj){
                parsed = d;
if(parsed.status){
                    //Use status code defined in JSON
                    statusCode = parsed.status;
}
}
else {
               Â
                //JSON was returned in the request, process.
                parsed = JSON.parse(dataJsonObj);
               Â
                //FME can return a 200 HTTP status but send a warning error back, this
                //overrides the status code on the request object with the status code
                //in the JSON from FME.
             Â
                if(parsed.status){
                    //Use status code defined in JSON
                    statusCode = parsed.status;
                }else{
                    //Default to HTTP status code returned by request   Â
                    statusCode = response.statusCode;
                }
               Â
                if (callback) {
                    callback({
                        body: parsed,
                        statusCode: statusCode,
                        statusMessage: response.statusMessage
                    });
                }
            }
        });
      return response;
    });
    req.end();
};
/*
* Handler that gets called by the API gateway.
*/
exports.handler = function(event, context) {
    try {
        var httpMethod = event.context "http-method"];
        makeHttpRequest(httpMethod, event, function (response) {
if (!dataZIPObj)
            return context.fail(JSON.stringify(response.body));
elseÂ
return response.body;
        });
    } catch (e) {
        context.fail(e);
    }
};
Maybe start with an image rather than a zip and get that working. As definedÂ
here. Content-type is determined in FME by the first writer added to a workspace. For example, if a Google KML writer is added to a workspace, followed by an Adobe 3D PDF writer, the Data Streaming service sends content-type application/vnd.google-earth.kmz. If a PDF writer is added first, followed by an OGCKML writer, the content-type is application/pdf.Â
Â
Â
So maybe you just create a really simple workspace that streams a png then you know the content type will be image/png?
Â
For others in a similar situation - you would like to return a zip file / image anything other than JSON really here is what you need to do.
Firstly lambda wont let you stream directly from fme because it tries to chunk the data and then the API gateway can't put it back properly such then when it encodes to binary it fails (most of the time). So the work around I found was basically to save whatever file it is to S3, then pick it up in lambda and sent it to API Gateway.Â
The lambda code needs to change to something like:
console.log("Lambda Function working");
var AWS = require("aws-sdk");
var https = require('https');
var querystring = require('querystring');
var host = 'xxx';
var path = '/fmedatastreaming/APIrepository/';
var S3Key = 'xx';
var S3SecretKey = 'xx';
var isJSON = false;
var isZIP = false;
var makeRequestToFME = function (httpMethod, event, callback) {
    var qs = querystring.stringify(event.params.querystring);
    var token = querystring.stringify(event,"stage-variables"]);
    var statusCode;
    var parsed;
    var fmeResponse = '';
    var parsedResponse;
    var options = {
        hostname: host,
        port: 443,
        path: path + event.workspace + '?' + qs + '&' + token ,
        timeout: 20000,Â
        method: httpMethod
    };
    console.log("Options", options);
    var req = https.request(options, function (response) {
        response.on('error', function (d) {
            console.log("FME Error", d);
            if (callback) {
                callback({
                    statusCode: response.statusCode
                });
            }
        });
        response.on('data', function (returnData) {
            console.log("Headers", response.headers);
            if (response.headers 'content-type'] === 'application/json') {
                isJSON = true;
                fmeResponse += returnData;              Â
            }
            if (response.headers 'content-type'] === 'application/zip') {
                isZIP = true;
                fmeResponse += returnData;              Â
            }
        });
        response.on('end', function (d) {
            console.log('FME Response end', fmeResponse); Â
            parsed = JSON.parse(fmeResponse);
           Â
            if ( parsed  != null && (parsed.status !== undefined && parsed.filename !== undefined)){
                isZIP = true;
            }
           Â
            if (isZIP){
                if (fmeResponse==='') {
                    if (callback) {
                        callback({
                            body: { "status": response.statusCode, "message": response.statusMessage },
                            statusCode: response.statusCode,
                            statusMessage: response.statusMessage
                        });
                    }
                }
                else {
                    console.log('FME Response end', fmeResponse);
                    parsed = JSON.parse(fmeResponse);
                   Â
                    if (callback) {
                        callback({
                            body: parsed.filename,
                            statusCode: parsed.status,
                            statusMessage: 'Reply from FME '+parsed.status
                        });
                    }
                }
            }
            else if (isJSON){
                parsed = JSON.parse(fmeResponse);
                if (parsed.status) {
                    //Use status code defined in JSON
                    statusCode = parsed.status;
                } else {
                    //Default to HTTP status code returned by request   Â
                    statusCode = response.statusCode;
                }
                if (callback) {
                    callback({
                        body: parsed,
                        statusCode: statusCode,
                        statusMessage: response.statusMessage
                    });
                }
            }
        });
        console.log("End makeFMERequest", response);
        return response;
    });    Â
    console.log("Request gone");
    req.end();
};
exports.handler = (event, context, callback) => {          Â
    try {      Â
        var httpMethod = event.contextÂ"http-method"];
        makeRequestToFME(httpMethod, event, function (response) {            Â
            console.log('makeFMERequest done', response);
            if (response.body != null && response.statusCode === 'ok') {
                var s3 = new AWS.S3({ region: "us-east-1", accessKeyId: S3Key, secretAccessKey: S3SecretKey });
                s3.getObject({
                    Bucket: 'xxxxx',
                    Key: 'DOWNLOAD/' + response.body
                }, function (err, data) {
                    if (err) {
                        console.log("S3", err);
                        callback(err);
                    }
                    else {
                        console.log("ZIP", data.Body);
                        callback(null, data.Body.toString('base64'));
                    }
                });
            }
            else {
                console.log('context failed', response);
                return context.fail(JSON.stringify(response.body));
            }
        });Â
    }
    catch (e) {
        console.log('try catch', e);
        context.fail(e);
    }  Â
};
 And in the API Gateway, under settings and Binary Media Types add the MIME type e.g. application/zip
Then in the method response add content-type
and in the integration response pull it into binary and update the header mappings as below
Then when making the curl request - in say Postman be sure to request the data in the header as content-type / application-zip.
Â
I hope this helps someone, AWS Gateway and lambda integration is quite a tricky piece, FME is the easiest bit.
Cheers
Oliver
For others in a similar situation - you would like to return a zip file / image anything other than JSON really here is what you need to do.
Firstly lambda wont let you stream directly from fme because it tries to chunk the data and then the API gateway can't put it back properly such then when it encodes to binary it fails (most of the time). So the work around I found was basically to save whatever file it is to S3, then pick it up in lambda and sent it to API Gateway.Â
The lambda code needs to change to something like:
console.log("Lambda Function working");
var AWS = require("aws-sdk");
var https = require('https');
var querystring = require('querystring');
var host = 'xxx';
var path = '/fmedatastreaming/APIrepository/';
var S3Key = 'xx';
var S3SecretKey = 'xx';
var isJSON = false;
var isZIP = false;
var makeRequestToFME = function (httpMethod, event, callback) {
    var qs = querystring.stringify(event.params.querystring);
    var token = querystring.stringify(event,"stage-variables"]);
    var statusCode;
    var parsed;
    var fmeResponse = '';
    var parsedResponse;
    var options = {
        hostname: host,
        port: 443,
        path: path + event.workspace + '?' + qs + '&' + token ,
        timeout: 20000,Â
        method: httpMethod
    };
    console.log("Options", options);
    var req = https.request(options, function (response) {
        response.on('error', function (d) {
            console.log("FME Error", d);
            if (callback) {
                callback({
                    statusCode: response.statusCode
                });
            }
        });
        response.on('data', function (returnData) {
            console.log("Headers", response.headers);
            if (response.headers 'content-type'] === 'application/json') {
                isJSON = true;
                fmeResponse += returnData;              Â
            }
            if (response.headers 'content-type'] === 'application/zip') {
                isZIP = true;
                fmeResponse += returnData;              Â
            }
        });
        response.on('end', function (d) {
            console.log('FME Response end', fmeResponse); Â
            parsed = JSON.parse(fmeResponse);
           Â
            if ( parsed  != null && (parsed.status !== undefined && parsed.filename !== undefined)){
                isZIP = true;
            }
           Â
            if (isZIP){
                if (fmeResponse==='') {
                    if (callback) {
                        callback({
                            body: { "status": response.statusCode, "message": response.statusMessage },
                            statusCode: response.statusCode,
                            statusMessage: response.statusMessage
                        });
                    }
                }
                else {
                    console.log('FME Response end', fmeResponse);
                    parsed = JSON.parse(fmeResponse);
                   Â
                    if (callback) {
                        callback({
                            body: parsed.filename,
                            statusCode: parsed.status,
                            statusMessage: 'Reply from FME '+parsed.status
                        });
                    }
                }
            }
            else if (isJSON){
                parsed = JSON.parse(fmeResponse);
                if (parsed.status) {
                    //Use status code defined in JSON
                    statusCode = parsed.status;
                } else {
                    //Default to HTTP status code returned by request   Â
                    statusCode = response.statusCode;
                }
                if (callback) {
                    callback({
                        body: parsed,
                        statusCode: statusCode,
                        statusMessage: response.statusMessage
                    });
                }
            }
        });
        console.log("End makeFMERequest", response);
        return response;
    });    Â
    console.log("Request gone");
    req.end();
};
exports.handler = (event, context, callback) => {          Â
    try {      Â
        var httpMethod = event.contextÂ"http-method"];
        makeRequestToFME(httpMethod, event, function (response) {            Â
            console.log('makeFMERequest done', response);
            if (response.body != null && response.statusCode === 'ok') {
                var s3 = new AWS.S3({ region: "us-east-1", accessKeyId: S3Key, secretAccessKey: S3SecretKey });
                s3.getObject({
                    Bucket: 'xxxxx',
                    Key: 'DOWNLOAD/' + response.body
                }, function (err, data) {
                    if (err) {
                        console.log("S3", err);
                        callback(err);
                    }
                    else {
                        console.log("ZIP", data.Body);
                        callback(null, data.Body.toString('base64'));
                    }
                });
            }
            else {
                console.log('context failed', response);
                return context.fail(JSON.stringify(response.body));
            }
        });Â
    }
    catch (e) {
        console.log('try catch', e);
        context.fail(e);
    }  Â
};
 And in the API Gateway, under settings and Binary Media Types add the MIME type e.g. application/zip
Then in the method response add content-type
and in the integration response pull it into binary and update the header mappings as below
Then when making the curl request - in say Postman be sure to request the data in the header as content-type / application-zip.
Â
I hope this helps someone, AWS Gateway and lambda integration is quite a tricky piece, FME is the easiest bit.
Cheers
Oliver
Godo job figuring that out! Thanks for posting the answer.
Â
Â