Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions docker/.env
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,9 @@ CPX_DB_PASSWORD=cx
CPX_DB_URL=jdbc:postgresql://capxmldb:5432/cx
CPX_DB_USERNAME=cx
CPX_DB_CONNECTION_STRING=postgres://${CPX_DB_USERNAME}:${CPX_DB_PASSWORD}@capxmldb/${CPX_DB_NAME}
CPX_REDIS_HOST=cap-xml-redis
CPX_REDIS_PORT=6379
CPX_REDIS_TLS=false
PGADMIN_DEFAULT_PASSWORD=pgadmin
POSTGRES_PASSWORD=postgres
LIQUIBASE_COMMAND_CHANGELOG_FILE=./changelog/db.changelog-master.xml
Expand Down
15 changes: 15 additions & 0 deletions docker/infrastructure.yml
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,21 @@ services:
interval: 10s
timeout: 5s
retries: 5
cap-xml-redis:
container_name: cap-xml-redis
image: redis:8.4-alpine
ports:
- "6379:6379"
deploy:
restart_policy:
condition: on-failure
networks:
ls:
healthcheck:
test: ["CMD", "redis-cli", "ping"]
interval: 5s
timeout: 5s
retries: 10
volumes:
capxmlpgdata:
external: true
Expand Down
5 changes: 4 additions & 1 deletion docker/scripts/register-lambda-functions.sh
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,10 @@ cpx_db_password=$(echo CPX_DB_PASSWORD=$CPX_DB_PASSWORD)
cpx_db_name=$(echo CPX_DB_NAME=$CPX_DB_NAME)
cpx_db_host=$(echo CPX_DB_HOST=$CPX_DB_HOST)
cpx_agw_url=$(echo CPX_AGW_URL=$deployed_cpx_agw_url)
set -- $cpx_db_username $cpx_db_password $cpx_db_name $cpx_db_host $cpx_agw_url
cpx_redis_host=$(echo CPX_REDIS_HOST=$CPX_REDIS_HOST)
cpx_redis_port=$(echo CPX_REDIS_PORT=$CPX_REDIS_PORT)
cpx_redis_tls=$(echo CPX_REDIS_TLS=$CPX_REDIS_TLS)
set -- $cpx_db_username $cpx_db_password $cpx_db_name $cpx_db_host $cpx_agw_url $cpx_redis_host $cpx_redis_port $cpx_redis_tls
custom_environment_variables=$(printf '%s,' "$@" | sed 's/,*$//g')

# Iterate over each file in lambda_functions_dir
Expand Down
6 changes: 4 additions & 2 deletions lib/functions/processMessage.js
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ const Message = require('../models/message')
const EA_WHO = '2.49.0.0.826.1'
const CODE = 'MCP:v2.0'
const severityV2Mapping = require('../models/v2MessageMapping')
const redis = require('../helpers/redis')

module.exports.processMessage = async (event) => {
try {
Expand Down Expand Up @@ -56,8 +57,9 @@ module.exports.processMessage = async (event) => {
throw new Error(JSON.stringify(errors))
}

// store the message in database
await service.putMessage(message.putQuery(message, messageV2))
const { message: redisMessage, query: dbQuery } = message.putQuery(message, messageV2)
// store the message in database and redis/elasticache
await Promise.all([service.putMessage(dbQuery), redis.set(redisMessage.identifier, redisMessage)])
console.log(`Finished processing CAP message: ${message.identifier} for ${message.fwisCode}`)

return {
Expand Down
25 changes: 18 additions & 7 deletions lib/helpers/message.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ const { validateXML } = require('xmllint-wasm')
const fs = require('node:fs')
const path = require('node:path')
const xsdSchema = fs.readFileSync(path.join(__dirname, '..', 'schemas', 'CAP-v1.2.xsd'), 'utf8')
const redis = require('../helpers/redis')

module.exports.getMessage = async (event, v2) => {
const { error } = eventSchema.validate(event)
Expand All @@ -14,15 +15,25 @@ module.exports.getMessage = async (event, v2) => {
throw error
}

const ret = await service.getMessage(event.pathParameters.id)

if (!ret?.rows || !Array.isArray(ret.rows) || ret.rows.length < 1 || !ret.rows[0].getmessage) {
console.log('No message found for ' + event.pathParameters.id)
throw new Error('No message found')
// Fetch message from redis, else get from postgres
let body
const key = event.pathParameters.id
const cachedMessage = await redis.get(key)

if (cachedMessage) {
body = v2 ? cachedMessage.alert_v2 : cachedMessage.alert
} else {
const ret = await service.getMessage(key)
if (!ret?.rows || !Array.isArray(ret.rows) || ret.rows.length < 1 || !ret.rows[0].getmessage) {
console.log('No message found for ' + key)
throw new Error('No message found')
}
const message = ret.rows[0].getmessage
body = v2 ? message.alert_v2 : message.alert
// Cache the message in redis
await redis.set(key, message)
}

const body = v2 ? ret.rows[0].getmessage.alert_v2 : ret.rows[0].getmessage.alert

const validationResult = await validateXML({
xml: [{
fileName: 'message.xml',
Expand Down
46 changes: 46 additions & 0 deletions lib/helpers/redis.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
'use strict'

const Redis = require('ioredis')
let client

const getClient = () => {
if (!client || client.status === 'end' || client.status === 'close') {
client = new Redis({
host: process.env.CPX_REDIS_HOST,
port: process.env.CPX_REDIS_PORT,
tls: process.env.CPX_REDIS_TLS === 'true' ? { checkServerIdentity: () => undefined } : undefined,
maxRetriesPerRequest: 3,
connectTimeout: 10000
})

client.on('error', (error) => {
console.error('Redis connection error:', error)
})

client.on('connect', () => {
console.log('Redis connected successfully')
})
}
return client
}

module.exports = {
get: async (key) => {
const redisClient = getClient()
const value = await redisClient.get(key)
if (value === null) {
return null
}
try {
return JSON.parse(value)
} catch (error) {
console.error(`Failed to parse Redis value for key ${key}:`, error)
return value
}
},
set: async (key, value) => {
const redisClient = getClient()
const serializedValue = typeof value === 'object' ? JSON.stringify(value) : value
return redisClient.set(key, serializedValue)
}
}
2 changes: 1 addition & 1 deletion lib/models/message.js
Original file line number Diff line number Diff line change
Expand Up @@ -195,7 +195,7 @@ class Message {
references_v2: messageV2.references,
alert_v2: messageV2.toString()
}
return messages.insert(message).toQuery()
return { message, query: messages.insert(message).toQuery() }
}
}

Expand Down
92 changes: 89 additions & 3 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
"@aws-sdk/client-sns": "3.932.0",
"@xmldom/xmldom": "0.8.11",
"feed": "5.1.0",
"ioredis": "5.9.2",
"joi": "18.0.1",
"moment": "2.30.1",
"pg": "8.16.3",
Expand Down
3 changes: 3 additions & 0 deletions readme.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,9 @@ This project provides CAP XML services through the use of AWS Lambda.
| CPX_DB_NAME | Database name | yes | | |
| CPX_DB_HOST | Database host | yes | | |
| CPX_AGW_URL | API Gateway URL | yes | | |
| CPX_REDIS_HOST | Redis/Elasticache host| yes | | |
| CPX_REDIS_PORT | Redis/Elasticache port| yes | | |
| CPX_REDIS_TLS | Redis/Elasticache tls | yes | | |

## Prerequisites

Expand Down
Loading