@eagleeye-solutions/integration-events-common
Version:
Eagle Eye CDP connector common functionality
210 lines • 8.5 kB
JavaScript
;
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.handleZodError = exports.formatZodError = exports.getConnectorConfig = void 0;
exports.getConnectorConfigInternal = getConnectorConfigInternal;
exports.handleEeAirOutboundRequest = handleEeAirOutboundRequest;
exports.handleCdpOutboundRequest = handleCdpOutboundRequest;
exports.handleInternalMessage = handleInternalMessage;
exports.handleStatus = handleStatus;
const http_errors_1 = __importDefault(require("http-errors"));
const memoizee_1 = __importDefault(require("memoizee"));
const env_1 = require("./env");
const types_1 = require("./types");
const exceptions_1 = require("./exceptions");
const platform_1 = require("./platform");
const zod_1 = require("zod");
const connector_config_1 = require("./types/connector-config");
function redact(input) {
return input[0] + '*'.repeat(input.length - 2) + input.slice(-1);
}
async function getConnectorConfigInternal({ appConfig, connectorId, externalKey, direction, logger, }) {
logger.debug(`getConnectorConfigInternal: ${JSON.stringify({ connectorId, direction })}`);
if (direction !== null &&
appConfig.configOverride?.[direction] &&
connectorId === appConfig.configOverride[direction].connectorId &&
externalKey === appConfig.configOverride[direction].externalKey) {
logger.debug(`configOverride found for direction=${direction} connectorId=${connectorId} externalKey=${redact(externalKey)}`);
return appConfig.configOverride[direction].connectorConfig;
}
const url = `${appConfig.configUrl}/connectors/${connectorId}/config?platform=${appConfig.configPlatform}&key=${externalKey}`;
const redactedUrl = `${appConfig.configUrl}/connectors/${connectorId}/config?platform=${appConfig.configPlatform}&key=${redact(externalKey)}`;
try {
const t0 = performance.now();
const response = await fetch(url);
const t1 = performance.now();
logger.info(`GET ${redactedUrl}: ${response.status} ${response.statusText} (${t1 - t0}ms)`);
if (response.ok) {
const connectorConfig = connector_config_1.BaseConnectorConfigSchema.parse(await response.json());
return connectorConfig;
}
else {
logger.error(`GET ${redactedUrl}: ${response.status} ${response.statusText}`);
return null;
}
}
catch (err) {
logger.error(err, `GET ${redactedUrl} failed`);
return null;
}
}
exports.getConnectorConfig = (0, memoizee_1.default)(getConnectorConfigInternal, {
maxAge: env_1.env.CONFIG_API_RESPONSE_TTL_MS,
normalizer: function (args) {
const { connectorId, externalKey, direction } = args[0];
return `${connectorId}-${externalKey}-${direction}`;
},
});
async function handleEeAirOutboundRequest(req, res) {
const appConfig = res.app.get('appConfig');
const connectorId = req.params.connectorId;
const externalKey = req.get('X-Auth-Token');
req.log.info({
'ee-air-outbound-request': req.body,
connectorId,
}, `ee-air-outbound-request: method=${req.method}, url=${req.originalUrl} connectorId=${connectorId}`);
try {
if (!externalKey) {
throw http_errors_1.default.Unauthorized();
}
const direction = 'out';
const eventType = 'ee-air-outbound-event';
const attributes = {};
const requestBody = req.body;
attributes[appConfig.traceIdName] = `${req.id}`;
const connectorConfig = await (0, exports.getConnectorConfig)({
appConfig,
connectorId,
externalKey,
direction,
logger: req.log,
});
if (!connectorConfig) {
throw http_errors_1.default.Forbidden();
}
await (0, platform_1.sendInternalMessage)(appConfig, {
type: eventType,
connectorConfig,
payload: requestBody,
}, attributes, req.log);
res.status(200).send({ status: 'OK' });
}
catch (error) {
req.log.error(error, 'Error in handleEeAirOutboundRequest');
throw error;
}
}
async function handleCdpOutboundRequest(req, res) {
const appConfig = res.app.get('appConfig');
const connectorId = req.params.connectorId;
const externalKey = req.get('X-Auth-Token');
req.log.info({
'cdp-outbound-request': req.body,
connectorId,
}, `cdp-outbound-request: method=${req.method}, url=${req.originalUrl}, connectorId=${connectorId}`);
try {
if (!externalKey) {
throw http_errors_1.default.Unauthorized();
}
// direction is relative to AIR
const direction = 'in';
const attributes = {};
const requestBody = types_1.CdpOutboundRequestBodySchema.parse(req.body);
attributes[appConfig.traceIdName] = `${req.id}`;
const connectorConfig = await (0, exports.getConnectorConfig)({
appConfig,
connectorId,
externalKey,
direction,
logger: req.log,
});
if (!connectorConfig) {
throw http_errors_1.default.Forbidden();
}
const cdpOutboundEvent = {
type: 'cdp-outbound-event',
connectorConfig,
payload: requestBody,
};
await (0, platform_1.sendInternalMessage)(appConfig, cdpOutboundEvent, attributes, req.log);
res.status(200).send({ status: 'OK' });
}
catch (error) {
if (error instanceof zod_1.ZodError) {
req.log.error(error, 'Invalid request from CDP');
const formattedError = (0, exports.handleZodError)(error, req.log);
if (formattedError) {
res.status(400).send(formattedError);
return;
}
}
req.log.error(error, 'Error in handleCdpOutboundRequest');
throw error;
}
}
async function handleInternalMessage(req, res) {
const appConfig = res.app.get('appConfig');
const { message, attributes } = appConfig.routes.internal.getInternalMessageFromRequest(appConfig, req);
const handler = appConfig.routes.internal.handlers[message.type];
const connectorId = new URL(message.connectorConfig.connection_url).pathname
.split('/')
.at(-1);
req.log.info({
'internal-message': {
type: message.type,
payload: message.payload,
},
connectorId,
}, `internal-message: ${message.type}, connectorId: ${connectorId}`);
try {
await handler(res.app.get('appConfig'), message.connectorConfig, message.payload, attributes, req.log);
res.status(200).send({ status: 'OK' });
}
catch (error) {
req.log.error(error);
if (error instanceof exceptions_1.TemporaryDeliveryFailure) {
throw http_errors_1.default.InternalServerError();
}
else {
const formattedError = await appConfig.handlePermanentMessageDeliveryFailure(res.app.get('appConfig'), message.connectorConfig, message, attributes, req.log, error);
if (formattedError) {
res.status(400).send(formattedError);
}
else {
// In the case of permanent delivery failure, we do not
// want the caller to retry, so we respond with a 200 OK.
res.status(200).send({ status: 200 });
}
}
}
}
async function handleStatus(_req, res) {
const appConfig = res.app.get('appConfig');
res.status(200).send({
api: appConfig.appMetadata.name,
version: appConfig.appMetadata.version,
tagline: appConfig.appMetadata.tagline,
});
}
const formatZodError = (error, logger) => {
const formattedError = {
message: 'Validation error',
errors: error.errors.map(err => ({
path: err.path.join('.'),
message: err.message,
})),
};
logger.error(formattedError);
return formattedError;
};
exports.formatZodError = formatZodError;
const handleZodError = (error, logger) => {
if (error instanceof zod_1.ZodError) {
const formatted = (0, exports.formatZodError)(error, logger);
return formatted;
}
};
exports.handleZodError = handleZodError;
//# sourceMappingURL=handlers.js.map