diff --git a/src/cogs/handler.js b/src/cogs/handler.js new file mode 100644 index 00000000..537fb2c5 --- /dev/null +++ b/src/cogs/handler.js @@ -0,0 +1,42 @@ +/* + * Copyright 2024 Adobe. All rights reserved. + * This file is licensed to you 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 REPRESENTATIONS + * OF ANY KIND, either express or implied. See the License for the specific language + * governing permissions and limitations under the License. + */ +import { badRequest, internalServerError, noContent } from '@adobe/spacecat-shared-http-utils'; +import { fetch } from '../support/utils.js'; + +export default async function cogsHandler(message, context) { + const { log } = context; + const { monthYear, usageCost } = message; + const { COGS_EXCEL_POST_URL: cogsExcelUrl } = context.env; + if (!monthYear || monthYear === '') { + return badRequest('MonthYear is missing'); + } + if (!usageCost || Object.keys(usageCost).length === 0) { + return badRequest('UsageCost is missing'); + } + try { + const response = await fetch(cogsExcelUrl, { + method: 'POST', + headers: { + 'Content-Type': 'application/json', + }, + body: JSON.stringify({ data: { MONTH: monthYear, ...usageCost } }), + }); + if (response.status !== 201) { + return badRequest(`Failed to post data to ${cogsExcelUrl}. Status: ${response.status}`); + } + log.info(`Successfully posted data to ${cogsExcelUrl}`); + return noContent(); + } catch (e) { + log.error(e); + return internalServerError('Failed to post data to COGS Excel'); + } +} diff --git a/src/index.js b/src/index.js index 1b19157f..5c76c559 100644 --- a/src/index.js +++ b/src/index.js @@ -21,6 +21,7 @@ import notFoundHandler from './notfound/handler.js'; import backlinks from './backlinks/handler.js'; import notFoundInternalDigestHandler from './notfound/handler-internal.js'; import notFoundExternalDigestHandler from './notfound/handler-external.js'; +import cogs from './cogs/handler.js'; export const HANDLERS = { apex, @@ -29,6 +30,7 @@ export const HANDLERS = { '404-external': notFoundExternalDigestHandler, '404-internal': notFoundInternalDigestHandler, 'broken-backlinks': backlinks, + cogs, }; function guardEnvironmentVariables(fn) { diff --git a/test/cogs/handler.test.js b/test/cogs/handler.test.js new file mode 100644 index 00000000..3fe21b4b --- /dev/null +++ b/test/cogs/handler.test.js @@ -0,0 +1,97 @@ +/* + * Copyright 2024 Adobe. All rights reserved. + * This file is licensed to you 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 REPRESENTATIONS + * OF ANY KIND, either express or implied. See the License for the specific language + * governing permissions and limitations under the License. + */ + +import sinon from 'sinon'; +import chai from 'chai'; +import sinonChai from 'sinon-chai'; +import chaiAsPromised from 'chai-as-promised'; +import nock from 'nock'; +import cogsHandler from '../../src/cogs/handler.js'; + +chai.use(sinonChai); +chai.use(chaiAsPromised); +const { expect } = chai; + +const sandbox = sinon.createSandbox(); +const COGS_HELIX_HOST = 'https://main--sharepoint--sudo-buddy.hlx.page'; +const COGS_SPREADSHEET_PATH = '/spacecatpoc'; +// eslint-disable-next-line no-undef +describe('cogs handler', () => { + let message; + let context; + // eslint-disable-next-line no-undef + beforeEach('setup', () => { + message = { + monthYear: 'Dec-23', + usageCost: { + LAMBDA: '14.67', + 'SECRETS MANAGER': '0.09', + DYNAMODB: '0.10', + SQS: '0.17', + S3: '0.00', + CLOUDWATCH: '0.05', + }, + }; + context = { + log: console, + env: { + COGS_EXCEL_POST_URL: `${COGS_HELIX_HOST}${COGS_SPREADSHEET_PATH}`, + }, + }; + }); + // eslint-disable-next-line no-undef + afterEach('clean', () => { + sandbox.restore(); + nock.cleanAll(); + }); + + // eslint-disable-next-line no-undef + it('reject when MonthYear is missing', async () => { + delete message.monthYear; + const result = await cogsHandler(message, context); + expect(result.status).equal(400); + }); + // eslint-disable-next-line no-undef + it('reject when UsageCost is missing', async () => { + delete message.usageCost; + const result = await cogsHandler(message, context); + expect(result.status).equal(400); + }); + // eslint-disable-next-line no-undef + it('reject when helix returns 400', async () => { + nock(COGS_HELIX_HOST) + .post(COGS_SPREADSHEET_PATH) + .reply(400); + const result = await cogsHandler(message, context); + expect(result.status) + .equal(400); + }); + // eslint-disable-next-line no-undef + it('reject when COGS_EXCEL_POST_URL is missing', async () => { + delete context.env.COGS_EXCEL_POST_URL; + nock(COGS_HELIX_HOST) + .post(COGS_SPREADSHEET_PATH) + .reply(500); + const result = await cogsHandler(message, context); + expect(result.status) + .equal(500); + }); + // eslint-disable-next-line no-undef + it('should post data to COGS Excel', async () => { + nock(COGS_HELIX_HOST) + .post(COGS_SPREADSHEET_PATH, { data: { MONTH: 'Dec-23', ...message.usageCost } }) + .reply(201); + const result = await cogsHandler(message, context); + expect(result.status) + .equal(204); + }); +});