OOO DynamoDB


CheckOutOfOfficeHours

index.mjs

/* Amplify Params - DO NOT EDIT
ENV
REGION
STORAGE_JHOOODDB_ARN
STORAGE_JHOOODDB_NAME
STORAGE_JHOOODDB_STREAMARN
Amplify Params - DO NOT EDIT */

import { DynamoDBClient } from '@aws-sdk/client-dynamodb';
import { DynamoDBDocumentClient, QueryCommand } from '@aws-sdk/lib-dynamodb';

const client = new DynamoDBClient({ region: process.env.REGION });
const dynamodb = DynamoDBDocumentClient.from(client);

const partitionKeyName = 'extension';

async function getOOOHours(agentExtension) {
    console.log(`getOOOHours(${agentExtension})`);

    const queryParams = {
        TableName: process.env.STORAGE_JHOOODDB_NAME,
        KeyConditionExpression: '#ext = :ext',
        ExpressionAttributeNames: {
            '#ext': partitionKeyName
        },
        ExpressionAttributeValues: {
            ':ext': agentExtension
        }
    };

    console.log(queryParams);

    let isOOO = false;
    let transferPhoneNumber = '';
    let hasCallForward = false;
    let oooMessage = '';

    try {
        const data = await dynamodb.send(new QueryCommand(queryParams));
        console.log(data);

        if (!data.Items || data.Items.length < 1) {
            console.log('No OOO entry found for ' + agentExtension);
        } else {
            const record = data.Items[0];
            console.log(record);

            const now = new Date().setTimeToNow();
            const startDate = new Date(record.startDate);
            const endDate = new Date(record.endDate);

            if (now > startDate && now < endDate) {
                console.log(`currently (${now.toISOString()}) within OOO period of ${startDate.toISOString()} - ${endDate.toISOString()}`);
                isOOO = true;
                transferPhoneNumber = record.transferPhoneNumber.replace(/\s/g, '');
                hasCallForward = record.hasCallForward;
                oooMessage = `${record.oooMessage}`;
            } else {
                console.log(`not currently (${now.toISOString()}) within OOO period of ${startDate.toISOString()} - ${endDate.toISOString()}`);
            }
        }
    } catch (error) {
        const errorMsg = 'Could not load items: ' + error;
        console.error(errorMsg);
        throw new Error(errorMsg);
    }

    return {
        isOOO,
        transferPhoneNumber,
        hasCallForward,
        oooMessage
    };
}

export const handler = async (event) => {
    return await getOOOHours(event.Details.Parameters.agentExtension);
};

OOOAPI

app.mjs

import { DynamoDBClient } from '@aws-sdk/client-dynamodb';
import {
  DynamoDBDocumentClient,
  ScanCommand,
  QueryCommand,
  GetCommand,
  PutCommand,
  DeleteCommand
} from '@aws-sdk/lib-dynamodb';
import serverlessExpress from '@vendia/serverless-express';
import bodyParser from 'body-parser';
import express from 'express';

const dynamoClient = new DynamoDBClient({ region: process.env.TABLE_REGION });
const dynamodb = DynamoDBDocumentClient.from(dynamoClient);

const usersLookupTable = process.env.USERS_LOOKUP_TABLE;

let tableName = 'oooTable';
if (process.env.ENV && process.env.ENV !== 'NONE') {
  tableName = tableName + '-' + process.env.ENV;
}

const userIdPresent = false; // TODO: update in case is required to use that definition
const partitionKeyName = 'extension';
const partitionKeyType = 'S';
const sortKeyName = '';
const sortKeyType = '';
const hasSortKey = sortKeyName !== '';
const path = '/items';
const UNAUTH = 'UNAUTH';
const hashKeyPath = '/:' + partitionKeyName;
const sortKeyPath = hasSortKey ? '/:' + sortKeyName : '';

// declare a new express app
const app = express();
app.use(bodyParser.json());

// Enable CORS for all methods
app.use(function (req, res, next) {
  res.header('Access-Control-Allow-Origin', '*');
  res.header('Access-Control-Allow-Headers', '*');
  next();
});

// convert url string param to expected Type
const convertUrlType = (param, type) => {
  switch (type) {
    case 'N':
      return Number.parseInt(param);
    default:
      return param;
  }
};

/********************************
 * HTTP Get method for list objects *
 ********************************/

app.get(path, async function (req, res) {
  console.log('hello from normal get');
  const params = {
    TableName: tableName,
    Select: 'ALL_ATTRIBUTES',
  };

  try {
    const data = await dynamodb.send(new ScanCommand(params));
    res.json({ data: data.Items });
  } catch (err) {
    res.json({ error: 'Could not load items: ' + err.message });
  }
});

app.get(path + hashKeyPath, async function (req, res) {
  console.log('hello from lambda get');
  console.log('path: ', path, 'hashKeyPath: ', hashKeyPath);

  let keyValue;
  if (userIdPresent && req.apiGateway) {
    keyValue = req.apiGateway.event.requestContext.identity.cognitoIdentityId || UNAUTH;
  } else {
    try {
      keyValue = convertUrlType(req.params[partitionKeyName], partitionKeyType);
    } catch (err) {
      res.statusCode = 500;
      return res.json({ error: 'Wrong column type ' + err });
    }
  }

  // comment

  const queryParams = {
    TableName: tableName,
    KeyConditionExpression: '#pk = :pkval',
    ExpressionAttributeNames: {
      '#pk': partitionKeyName
    },
    ExpressionAttributeValues: {
      ':pkval': keyValue
    }
  };

  try {
    const data = await dynamodb.send(new QueryCommand(queryParams));
    console.log(data.Items);
    res.json(data.Items);
  } catch (err) {
    res.statusCode = 500;
    res.json({ error: 'Could not load items: ' + err.message });
  }
});

/*****************************************
 * HTTP Get method for get single object *
 *****************************************/

app.get(path + '/object' + hashKeyPath + sortKeyPath, async function (req, res) {
  console.log('hello from lambda get object');
  var params = {};
  if (userIdPresent && req.apiGateway) {
    params[partitionKeyName] = req.apiGateway.event.requestContext.identity.cognitoIdentityId || UNAUTH;
  } else {
    try {
      params[partitionKeyName] = convertUrlType(req.params[partitionKeyName], partitionKeyType);
    } catch (err) {
      res.statusCode = 500;
      return res.json({ error: 'Wrong column type ' + err });
    }
  }
  if (hasSortKey) {
    try {
      params[sortKeyName] = convertUrlType(req.params[sortKeyName], sortKeyType);
    } catch (err) {
      res.statusCode = 500;
      return res.json({ error: 'Wrong column type ' + err });
    }
  }

  const getItemParams = {
    TableName: tableName,
    Key: params
  };

  try {
    const data = await dynamodb.send(new GetCommand(getItemParams));
    if (data.Item) {
      res.json(data.Item);
    } else {
      res.json(data);
    }
  } catch (err) {
    res.statusCode = 500;
    res.json({ error: 'Could not load items: ' + err.message });
  }
});

/******************************************
 * HTTP Get method for users extension *
 ******************************************/

// comment

app.get(path + '/username' + hashKeyPath + sortKeyPath, async function (req, res) {
  console.log('hello from lambda get object extension');
  console.log('path: ', path, 'hashKeyPath: ', hashKeyPath, 'sortKeyPath', sortKeyPath);

  var params = {};
  if (userIdPresent && req.apiGateway) {
    params['username'] = req.apiGateway.event.requestContext.identity.cognitoIdentityId || UNAUTH;
  } else {
    try {
      params['username'] = convertUrlType(req.params['extension'].toUpperCase(), partitionKeyType);
    } catch (err) {
      res.statusCode = 500;
      return res.json({ error: 'Wrong column type ' + err });
    }
  }
  if (hasSortKey) {
    try {
      params[sortKeyName] = convertUrlType(req.params[sortKeyName], sortKeyType);
    } catch (err) {
      res.statusCode = 500;
      return res.json({ error: 'Wrong column type ' + err });
    }
  }

  const email = `${params.username}`;

  const getItemParams = {
    TableName: usersLookupTable,
    IndexName: 'username-extension-index',
    KeyConditionExpression: 'username = :hkey',
    ExpressionAttributeValues: {
      ':hkey': email.toUpperCase(),
    }
  };

  console.log(getItemParams);

  try {
    const data = await dynamodb.send(new QueryCommand(getItemParams));
    if (data.Item) {
      res.json(data.Item);
    } else {
      res.json(data);
    }
  } catch (err) {
    console.log(err);
    res.statusCode = 500;
    res.json({ error: 'Could not load items: ' + err.message });
  }
});

/************************************
 * HTTP put method for insert object *
 ************************************/

app.put(path, async function (req, res) {
  console.log('hello from lambda put');

  if (userIdPresent) {
    req.body['userId'] = req.apiGateway.event.requestContext.identity.cognitoIdentityId || UNAUTH;
  }

  const putItemParams = {
    TableName: tableName,
    Item: req.body
  };

  try {
    const data = await dynamodb.send(new PutCommand(putItemParams));
    res.json({ success: 'put call succeed!', url: req.url, data: data });
  } catch (err) {
    res.statusCode = 500;
    res.json({ error: err.message, url: req.url, body: req.body });
  }
});

/************************************
 * HTTP post method for insert object *
 ************************************/

app.post(path, async function (req, res) {
  console.log('hello from lambda post');

  if (userIdPresent) {
    req.body['userId'] = req.apiGateway.event.requestContext.identity.cognitoIdentityId || UNAUTH;
  }

  const putItemParams = {
    TableName: tableName,
    Item: req.body
  };

  try {
    const data = await dynamodb.send(new PutCommand(putItemParams));
    res.json({ success: 'post call succeed!', url: req.url, data: data });
  } catch (err) {
    res.statusCode = 500;
    res.json({ error: err.message, url: req.url, body: req.body });
  }
});

/**************************************
 * HTTP remove method to delete object *
 **************************************/

app.delete(path + '/object' + hashKeyPath + sortKeyPath, async function (req, res) {
  console.log('hello from lambda delete');
  var params = {};
  if (userIdPresent && req.apiGateway) {
    params[partitionKeyName] = req.apiGateway.event.requestContext.identity.cognitoIdentityId || UNAUTH;
  } else {
    try {
      params[partitionKeyName] = convertUrlType(req.params[partitionKeyName], partitionKeyType);
    } catch (err) {
      res.statusCode = 500;
      return res.json({ error: 'Wrong column type ' + err });
    }
  }

  if (hasSortKey) {
    try {
      params[sortKeyName] = convertUrlType(req.params[sortKeyName], sortKeyType);
    } catch (err) {
      res.statusCode = 500;
      return res.json({ error: 'Wrong column type ' + err });
    }
  }

  const removeItemParams = {
    TableName: tableName,
    Key: params
  };

  try {
    const data = await dynamodb.send(new DeleteCommand(removeItemParams));
    res.json({ url: req.url, data: data });
  } catch (err) {
    res.statusCode = 500;
    res.json({ error: err.message, url: req.url });
  }
});

app.listen(3000, function () {
  console.log('App started');
});

// Export handler for AWS Lambda via @vendia/serverless-express
export const handler = serverlessExpress({ app });


updateTTLStream


/* Amplify Params - DO NOT EDIT
ENV
REGION
STORAGE_JHOOODDB_ARN
STORAGE_JHOOODDB_NAME
STORAGE_JHOOODDB_STREAMARN
Amplify Params - DO NOT EDIT */

import { DynamoDBClient } from '@aws-sdk/client-dynamodb';
import { DynamoDBDocumentClient, UpdateCommand } from '@aws-sdk/lib-dynamodb';
import { unmarshall } from '@aws-sdk/util-dynamodb';

const client = new DynamoDBClient({ region: process.env.REGION });
const dynamodb = DynamoDBDocumentClient.from(client);

export const handler = async (event) => {
    console.log(JSON.stringify(event, null, 2));

    for (const record of event.Records) {
        console.log(JSON.stringify(record, null, 2));

        if (record.eventName === 'REMOVE') {
            console.log('Skipping deleted record.');
            continue;
        }

        const newImage = unmarshall(record.dynamodb.NewImage);
        console.log(newImage);

        const parsedEndDate = new Date(newImage.endDate);
        if (isNaN(parsedEndDate.getTime())) {
            console.log('endDate is missing or invalid - skipping.');
            continue;
        }

        // Add 1 week (in milliseconds) to endDate, then convert to Unix epoch seconds for TTL
        const oneWeekMs = 7 * 24 * 60 * 60 * 1000;
        const ttl = Math.floor((parsedEndDate.getTime() + oneWeekMs) / 1000);
        console.log(`new ttl: ${ttl}`);

        try {
            console.log('Saving updated record.');
            const result = await dynamodb.send(new UpdateCommand({
                TableName: process.env.STORAGE_JHOOODDB_NAME,
                Key: { extension: newImage.extension },
                UpdateExpression: 'SET #ttl = :val',
                ExpressionAttributeNames: { '#ttl': 'ttl' },
                ExpressionAttributeValues: { ':val': ttl },
                ReturnValues: 'ALL_NEW'
            }));
            console.log(result);
        } catch (error) {
            console.error(error);
        }
    }

    return 'Successfully processed DynamoDB record';
};