Skip to content

Commit

Permalink
Merge pull request ibm-messaging#77 from chughts/lambda
Browse files Browse the repository at this point in the history
Add AWS Lambda function sample
  • Loading branch information
chughts authored May 10, 2022
2 parents ef5b80f + d829a88 commit ddcbc9e
Show file tree
Hide file tree
Showing 6 changed files with 499 additions and 0 deletions.
52 changes: 52 additions & 0 deletions serverless/aws-lambda-rest/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
# IBM MQ Serverless Samples for AWS Lambda
These REST samples are written in Node.js and have been tested with
a SAM AWS Lambda runtime of `nodejs14.x`.

The application consists of a single Lambda function exposed as an API Gateway API, defined in the `template.yaml`.

## MQ Server Configuration
The application configuration for MQ server is in the `template.yaml` file in the `Environment` `Variables` section. Set `HOSTNAME`, `QM_NAME` etc in for your MQ server.


## Testing locally
These samples can be tested offline locally using the [AWS Serverless Application Model Command Line Interface](https://docs.aws.amazon.com/serverless-application-model/latest/developerguide/serverless-sam-cli-install.html). Follow the SAM install instructions to install SAM and its pre-requisites.

You will not need to sign in to AWS to build and test this application. You will need to log into AWS if you want to deploy the application to AWS. Remember to delete any deployments when you no longer need them.

### Building the application
The application is built by running
`sam build`
from a command line in the directory that contains the `template.yaml` file.
This will create a `.aws-sam` directory that contains the application dependencies and build artifacts in the correct format for subsequent testing, packaging and deployment.

Check the `.aws-sam` directory. It should contain a `build/PutMessageFunction` directory containing the application and dependencies.

### Running the application locally
To start the application run
`sam local start-api`
from a command line in the directory that contains the `template.yaml` file.

You should see output similar to:
````
Mounting PutMessageFunction at http://127.0.0.1:3000/putmessage [GET]
You can now browse to the above endpoints to invoke your functions. You do not need to restart/reload SAM CLI while working on your functions, changes will be reflected instantly/automatically. You only need to restart SAM CLI if you update your AWS SAM template
2022-05-05 16:26:29 * Running on http://127.0.0.1:3000/ (Press CTRL+C to quit)
````

The application is exposed as a `GET` on http://127.0.0.1:3000/putmessage. You
can specify the message to post to MQ as a query string eg. http://127.0.0.1:3000/putmessage?msg=Message+from+MQ+Lambda+function


## Deploying the application
Before deploying the application, [configure](https://docs.aws.amazon.com/lambda/latest/dg/configuration-vpc.html) your AWS Lambda VPC to be able to connect to your MQ Server.

Deploy the application by running.
`sam deploy --guided`


## Undeploying the application
Delete the application from AWS when you no longer need it by running.
`sam delete`

Verify that the application and related stack is removed by checking on the AWS console.
94 changes: 94 additions & 0 deletions serverless/aws-lambda-rest/mq-lambda/app.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
/**
* Copyright 2022 IBM Corp.
*
* Licensed under the Apache License, Version 2.0 (the 'License');
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
**/

const MQRest = require('./lib/mqrestapi.js');
const MQParamBuilder = require('./lib/parambuilder.js');

let response;

const PATH = 'path';
const QUERYSTRING = 'queryStringParameters';
const MESSAGE = 'msg';

let mqrest = new MQRest();
let mqparam = new MQParamBuilder();

// Set up debug logging options
var debug_info = require('debug')('mqaws:info');
var debug_warn = require('debug')('mqaws:warn');

checkPath = (apipath) => {
if (apipath) {
let pathvalue = apipath.replace(/^\//, '');
debug_info("cleansed path is ", pathvalue);
} else {
debug_warn("path not set on invocation");
}
}

/**
*
* Event doc: https://docs.aws.amazon.com/apigateway/latest/developerguide/set-up-lambda-proxy-integrations.html#api-gateway-simple-proxy-for-lambda-input-format
* @param {Object} event - API Gateway Lambda Proxy Input Format
*
* Context doc: https://docs.aws.amazon.com/lambda/latest/dg/nodejs-prog-model-context.html
* @param {Object} context
*
* Return doc: https://docs.aws.amazon.com/apigateway/latest/developerguide/set-up-lambda-proxy-integrations.html
* @returns {Object} object - API Gateway Lambda Proxy Output Format
*
*/
exports.lambdaPutMessageHandler = async (event, context) => {
return new Promise(function(resolve, reject) {
let msg = '';

if (event) {
checkPath(event[PATH]);

if (event[QUERYSTRING] && event[QUERYSTRING][MESSAGE]) {
debug_info("found query string set to ", event[QUERYSTRING]);
msg = event[QUERYSTRING][MESSAGE];
}
}

mqparam.buildParamsForAWS(msg)
.then((params) => {
debug_info('Call params set to : ');
debug_info(params);
return mqrest.postMessage(params);
})
.then((messageId) => {
response = {
'statusCode': 200,
'headers': {'Content-Type': 'application/json'},
'body': JSON.stringify({
'message': 'Message put was successful',
'messageId' : messageId
})
};
resolve(response);
})
.catch((err) => {
debug_warn(err);
response = {
'statusCode': 400,
'headers': { 'Content-Type': 'application/json' },
'body': JSON.stringify({'Error:': err, 'statusCode' : 400})
};
resolve(response);
});
});
}
134 changes: 134 additions & 0 deletions serverless/aws-lambda-rest/mq-lambda/lib/mqrestapi.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,134 @@
/**
* Copyright 2021 IBM Corp.
*
* Licensed under the Apache License, Version 2.0 (the 'License');
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
**/

const axios = require('axios');

// This class acts as the REST CAll manager. All MQ Rest calls are invoked
// as methods on this class.
class MQRestAPI {
constructor() {}

browseForMsg(options) {
return new Promise(function resolver(resolve, reject) {
//console.log('Configuration looks like ', config);
//console.log('Connection information looks like ', connection);
console.log('https://' + options.host + options.path);
let uri = 'https://' + options.host + options.path;
axios({
method: 'GET',
url: uri,
headers: options.headers,
})
.then(function(response) {
console.log('Status code is ', response.status);
console.log('Message Id:');
console.log(response.headers['ibm-mq-md-messageid']);
// console.log('Headers are:');
// console.log(response.headers);
// console.log("Full response is:")
// console.log(response);
switch (response.status) {
case 200:
case 201:
resolve(response.headers['ibm-mq-md-messageid']);
break;
case 204:
reject('Queue is empty');
break;
default:
reject('Error Invoking API ' + response.statusCode);
break;
}
}).catch(function(err) {
console.log("REST call error : ", err);
reject(err);
});
});
}

deleteMessage (options) {
return new Promise((resolve, reject) => {
console.log("Retrieving message from MQ Queue");
let uri = 'https://' + options.host + options.path;
axios({
method: 'DELETE',
url: uri,
params: options.qs,
headers: options.headers,
})
.then(function(response) {
console.log('Status code is ', response.status);
console.log('Message Id:');
console.log(response.headers['ibm-mq-md-messageid']);
console.log("Data found : ", response.data);
//console.log("Dumping full response");
//console.log(response);
switch (response.status) {
case 200:
case 201:
let msg = {
'messageId' : response.headers['ibm-mq-md-messageid'],
'message' : response.data
};
resolve(msg);
break;
case 204:
reject('Message not found on queue');
break;
default:
reject('Error Invoking API ' + response.status);
break;
}
}).catch(function(err) {
console.log("REST call error : ", err);
reject(err);
});
});
}

postMessage (options) {
return new Promise((resolve, reject) => {
console.log("Posting message to MQ Queue");
let uri = 'https://' + options.host + options.path;
axios({
method: 'POST',
url: uri,
data: options.message,
headers: options.headers,
})
.then(function(response) {
console.log('Status code is ', response.status);
console.log('Message Id:');
console.log(response.headers['ibm-mq-md-messageid']);
switch (response.status) {
case 200:
case 201:
resolve(response.headers['ibm-mq-md-messageid']);
break;
default:
reject('Error Invoking API ' + response.status);
break;
}
}).catch(function(err) {
console.log("REST call error : ", err);
reject(err);
});
});
}

}

module.exports = MQRestAPI;
Loading

0 comments on commit ddcbc9e

Please sign in to comment.