documents/dev/snippets/aws/Lambda Router.md

Lambda Router

index.js

const Router = require('./Router');
const orgs = require('./orgs');

exports.handler = async (event, context, callback)=> {
  const router = Router(event, context, callback);
  router.attach(orgs);

  const out = await router.handle();
  if(out) return out;

  callback(null, {
    statusCode: 200,
    body: JSON.stringify(event)
  });
};

users.js

const DB = require('./DB');

const routes = {
  'GET:/users': async (evt, callback)=> {
    const list = await DB.getUserList();
    callback(null, { statusCode: 200, body: list  });
  },

  'GET:/users/{id}': async (evt, callback)=> {
    const {id} = evt.pathParameters;

    if(id === 'test') {
      const user = await DB.getUser('dominip');
      return { statusCode: 200, body: user };
    }

    const item = await DB.getUser(id);
    if(!item) {
      callback(null, { statusCode: 404, body: null});
    } else {
      callback(null, { statusCode: 200, body: item });
    }
  },

  'POST:/users': async (evt, callback)=> {
    const {id, name, pwd, email, duration, org} = JSON.parse(evt.body);
    let item = await DB.createUser({id, name, pwd, email, duration, org});
    if(item) {
      callback(null, { statusCode: 200, body: item });
    } else {
      callback(null, { statusCode: 409, body: {error: "something went wrong"} });
    }
  },

  'DELETE:/users/{id}': async (evt, callback)=> {
    const {id} = evt.pathParameters;
    const item = await DB.deleteUser(id);
    if(item) {
      callback(null, { statusCode: 200, body: item });
    } else {
      callback("error", { statusCode: 500 });
    }
  },

  'PUT:/users/{id}': async (evt, callback)=> {
  },
};

module.exports = routes;

db.js

const AWS = require('aws-sdk');
const bcrypt = require('./bcrypt')
const TABLE_USER = 'partner_users';

const ddb = new AWS.DynamoDB.DocumentClient({
  apiVersion: '2012-08-10',
  region: process.env.AWS_REGION,
  sslEnabled: false,
  paramValidation: false,
  convertResponseTypes: false
});


const GetAllItems = async (TableName)=> {
  try {
    const data = await ddb.scan({ TableName }).promise();
    return data.Items;
  } catch (e) {
    return [];
  }
};

const CreateItem = async (TableName, Item)=> {
  try {
    await ddb.put({ TableName, Item }).promise();
  } catch(err) {
    return null;
  }
  return Item;
};

const GetItemById = async (TableName, id)=> {
  try {
    const data = await ddb.get({
      TableName,
      Key: { id }
    }).promise();
    return data.Item;
  } catch (e) {
    return null;
  }
};

const DeleteItemById = async (TableName, id)=> {
  try {
    await ddb.delete({
      TableName,
      Key: { id }
    }).promise();

    return {deletedItem: {id}};
  } catch (e) {
    return {deletedItem: null};
  }
};

const getHash = (pwd, rounds=10)=> {
  const salt = bcrypt.genSaltSync(rounds);
  return bcrypt.hashSync(pwd, salt);
};
const getUserList = async ()=> {
  return await GetAllItems(TABLE_USER);
};
const createUser = async ({id, name, pwd, email, duration, org})=> {
  pwd = getHash(pwd);
  duration = parseInt(duration) || 365;
  const exp = Date.now() + duration*24*60*60*1000; // duration is in days
  return await CreateItem(TABLE_USER, {id, name, pwd, email, exp, org});
};
const getUser = async (id)=> {
  return await GetItemById(TABLE_USER, id);
};
const deleteUser = async (id)=> {
  return await DeleteItemById(TABLE_USER, id);
};

module.exports = {
  getUserList,
  createUser,
  getUser,
  deleteUser,
};

Router.js

module.exports = (event, context, cb) => {
  const myRoutes = {};

  const sanitizeResponse = (res)=> {
    // Enable CORS
    res.headers = Object.assign({}, res.headers, {
      "Access-Control-Allow-Headers": 'Content-Type,X-Amz-Date,Authorization,X-Api-Key,X-Amz-Security-Token,Set-Cookie',
      "Access-Control-Allow-Origin": "*",
      "Access-Control-Allow-Methods": "*"
    });
    if(res.body && typeof res.body !== 'string') res.body = JSON.stringify(res.body);
    return res;
  };

  const sanitizedCallback = (err, res)=> {
    sanitizeResponse(res);
    cb(err, res);
  };

  const attach = (routes)=> {
    Object.assign(myRoutes, routes);
  };

  const handle = async ()=> {
    const routeKey = event.httpMethod + ':' + event.resource;
    if(myRoutes[routeKey]) {
      return sanitizeResponse(await myRoutes[routeKey](event, sanitizedCallback));
    }
    return null;
  };

  return {attach, handle};
};