diff --git a/lambda/alexa/smarthome/index.js b/lambda/alexa/smarthome/index.js index c4eb4ca5..f187a464 100644 --- a/lambda/alexa/smarthome/index.js +++ b/lambda/alexa/smarthome/index.js @@ -12,7 +12,6 @@ */ import { AxiosError } from 'axios'; -import config from '#root/config.js'; import log from '#root/log.js'; import OpenHAB from '#openhab/index.js'; import AlexaDirective from './directive.js'; @@ -22,13 +21,14 @@ import { AlexaError, InvalidDirectiveError } from './errors.js'; /** * Handles alexa smart home skill request * @param {Object} request + * @param {Object} context * @return {Promise} */ -export const handleRequest = async (request) => { +export const handleRequest = async (request, context) => { // Initialize directive object const directive = new AlexaDirective(request.directive); // Initialize openhab object - const openhab = new OpenHAB(config.openhab, directive.auth.token, AlexaResponse.TIMEOUT); + const openhab = new OpenHAB(context.awsRequestId, directive.auth.token, AlexaResponse.TIMEOUT); let response; diff --git a/lambda/index.js b/lambda/index.js index fec51303..a5e96c8e 100644 --- a/lambda/index.js +++ b/lambda/index.js @@ -17,13 +17,14 @@ import { handleRequest } from './alexa/smarthome/index.js'; /** * Defines skill event handler * @param {Object} event + * @param {Object} context * @return {Promise} */ -export const handler = async (event) => { +export const handler = async (event, context) => { log.info('Received event:', event); if (event.directive?.header.payloadVersion === '3') { - return handleRequest(event); + return handleRequest(event, context); } log.warn('Unsupported event:', event); diff --git a/lambda/openhab/index.js b/lambda/openhab/index.js index 0e7a1cd2..5b519ccf 100644 --- a/lambda/openhab/index.js +++ b/lambda/openhab/index.js @@ -15,8 +15,11 @@ import fs from 'node:fs'; import axios from 'axios'; import { HttpsAgent } from 'agentkeepalive'; import { validate as uuidValidate } from 'uuid'; +import config from '#root/config.js'; import { ItemType, ItemValue, UnitSymbol } from './constants.js'; +const packageInfo = JSON.parse(fs.readFileSync('./package.json')); + /** * Defines openHAB class */ @@ -30,12 +33,12 @@ export default class OpenHAB { /** * Constructor - * @param {Object} config + * @param {String} requestId * @param {String} token * @param {Number} timeout */ - constructor(config, token, timeout) { - this._client = OpenHAB.createClient(config, token, timeout); + constructor(requestId, token, timeout) { + this._client = OpenHAB.createClient(config.openhab, requestId, token, timeout); } /** @@ -205,16 +208,19 @@ export default class OpenHAB { /** * Returns request client * @param {Object} config + * @param {String} requestId * @param {String} token * @param {Number} timeout * @return {Object} */ - static createClient(config, token, timeout) { + static createClient(config, requestId, token, timeout) { const client = axios.create({ baseURL: config.baseURL, headers: { common: { - 'Cache-Control': 'no-cache' + 'Cache-Control': 'no-cache', + 'User-Agent': `${packageInfo.name}/${packageInfo.version}`, + 'X-Amzn-RequestId': requestId } }, httpsAgent: new HttpsAgent({ diff --git a/lambda/test/alexa/smarthome.test.js b/lambda/test/alexa/smarthome.test.js index 53a6b21d..cadeecf3 100644 --- a/lambda/test/alexa/smarthome.test.js +++ b/lambda/test/alexa/smarthome.test.js @@ -29,6 +29,9 @@ use(chaiCustom); /* eslint-disable mocha/no-setup-in-describe */ describe('Alexa Smart Home Tests', function () { + // set default environment + const context = { awsRequestId: 'request-id' }; + let commandStub, updateStub; beforeEach(function () { @@ -56,7 +59,7 @@ describe('Alexa Smart Home Tests', function () { sinon.stub(OpenHAB.prototype, 'getAllItems').resolves(items); sinon.stub(OpenHAB.prototype, 'getServerSettings').resolves(settings); // run test - const response = await handleRequest({ directive }); + const response = await handleRequest({ directive }, context); expect(commandStub.called).to.be.false; expect(updateStub.called).to.be.false; expect(response) @@ -86,7 +89,7 @@ describe('Alexa Smart Home Tests', function () { sinon.stub(log, 'error'); } // run test - const response = await handleRequest({ directive }); + const response = await handleRequest({ directive }, context); expect(commandStub.callCount).to.equal(expected.openhab.commands.length); expect(commandStub.args.map(([name, value]) => ({ name, value }))).to.deep.equal(expected.openhab.commands); expect(updateStub.callCount).to.equal(expected.openhab.updates.length); diff --git a/lambda/test/openhab.test.js b/lambda/test/openhab.test.js index 07cd62bb..64fc743c 100644 --- a/lambda/test/openhab.test.js +++ b/lambda/test/openhab.test.js @@ -17,19 +17,25 @@ import nock from 'nock'; import fs from 'node:fs'; import { AxiosError } from 'axios'; import { v4 as uuidv4 } from 'uuid'; +import config from '#root/config.js'; import OpenHAB from '#openhab/index.js'; +const packageInfo = JSON.parse(fs.readFileSync('./package.json')); + describe('OpenHAB Tests', function () { // set default environment const baseURL = 'https://foobar'; + const requestId = 'request-id'; const token = 'token'; const timeout = 42; let openhab; beforeEach(function () { + // set stub environment + sinon.stub(config.openhab, 'baseURL').value(baseURL); // create new openhab instance - openhab = new OpenHAB({ baseURL }, token, timeout); + openhab = new OpenHAB(requestId, token, timeout); }); afterEach(function () { @@ -43,9 +49,19 @@ describe('OpenHAB Tests', function () { it('https oauth2 token', async function () { // set environment sinon.stub(fs, 'existsSync').returns(false); - nock(baseURL).get('/').matchHeader('Authorization', `Bearer ${token}`).reply(200); + nock(baseURL) + .get('/') + .matchHeader('Authorization', `Bearer ${token}`) + .reply(200) + .on('request', ({ headers }) => { + expect(headers).to.include({ + authorization: `Bearer ${token}`, + 'user-agent': `${packageInfo.name}/${packageInfo.version}`, + 'x-amzn-requestid': requestId + }); + }); // run test - await OpenHAB.createClient({ baseURL }, token, timeout).get('/'); + await OpenHAB.createClient({ baseURL }, requestId, token, timeout).get('/'); expect(nock.isDone()).to.be.true; }); @@ -53,10 +69,17 @@ describe('OpenHAB Tests', function () { // set environment const user = 'username'; const pass = 'password'; + const token = Buffer.from(`${user}:${pass}`).toString('base64'); sinon.stub(fs, 'existsSync').returns(false); - nock(baseURL).get('/').basicAuth({ user, pass }).reply(200); + nock(baseURL) + .get('/') + .basicAuth({ user, pass }) + .reply(200) + .on('request', ({ headers }) => { + expect(headers).to.include({ authorization: `Basic ${token}` }); + }); // run test - await OpenHAB.createClient({ baseURL, user, pass }, token, timeout).get('/'); + await OpenHAB.createClient({ baseURL, user, pass }, requestId, token, timeout).get('/'); expect(nock.isDone()).to.be.true; }); @@ -76,7 +99,7 @@ describe('OpenHAB Tests', function () { expect(socket).to.include({ timeout }); }); // run test - await OpenHAB.createClient({ baseURL, certFile, certPass }, token, timeout).get('/'); + await OpenHAB.createClient({ baseURL, certFile, certPass }, requestId, token, timeout).get('/'); expect(nock.isDone()).to.be.true; }); diff --git a/lambda/test/skill.test.js b/lambda/test/skill.test.js index 2ce50016..332bd603 100644 --- a/lambda/test/skill.test.js +++ b/lambda/test/skill.test.js @@ -17,6 +17,9 @@ import esmock from 'esmock'; import log from '#root/log.js'; describe('Skill Event Tests', function () { + // set default environment + const context = { awsRequestId: 'request-id' }; + let smarthomeStub, skill; beforeEach(async function () { @@ -47,9 +50,9 @@ describe('Skill Event Tests', function () { } }; // run test - await skill.handler(event); + await skill.handler(event, context); expect(smarthomeStub.called).to.be.true; - expect(smarthomeStub.firstCall.args).to.deep.equal([event]); + expect(smarthomeStub.firstCall.args).to.deep.equal([event, context]); }); it('payload version 2', async function () { @@ -63,7 +66,7 @@ describe('Skill Event Tests', function () { }; const logWarn = sinon.stub(log, 'warn'); // run test - await skill.handler(event); + await skill.handler(event, context); expect(smarthomeStub.called).to.be.false; expect(logWarn.called).to.be.true; expect(logWarn.firstCall.args).to.deep.equal(['Unsupported event:', event]);