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
31 changes: 17 additions & 14 deletions src/env.ts
Original file line number Diff line number Diff line change
Expand Up @@ -84,7 +84,7 @@ export interface Configuration {
// When set, this plugin will not try to redirect the handlers of these specified functions;
exclude: string[];
// When set, this plugin will configure the specified monitors for the function
monitors?: { [id: string]: { [key: string]: any } }[];
monitors?: Record<string, Record<string, unknown>>[];

// When set, this plugin will fail a deployment if monitors can't be created
failOnError: boolean;
Expand Down Expand Up @@ -217,7 +217,7 @@ export const defaultConfiguration: Configuration = {
export function setEnvConfiguration(config: Configuration, handlers: FunctionInfo[]): void {
handlers.forEach(({ handler, type }) => {
handler.environment ??= {};
const environment = handler.environment as any;
const environment = handler.environment as Record<string, unknown>;
const functionName = handler.name ?? "";
if (
process.env.DATADOG_API_KEY !== undefined &&
Expand Down Expand Up @@ -397,10 +397,7 @@ function throwEnvVariableError(variable: string, value: string, functionName: st
}

export function getConfig(service: Service): Configuration {
let custom = service.custom as any;
if (custom === undefined) {
custom = {};
}
const custom = service.custom ?? {};

let datadog = custom.datadog as Partial<Configuration> | undefined;
if (datadog === undefined) {
Expand All @@ -425,7 +422,11 @@ export function getConfig(service: Service): Configuration {
}

export function forceExcludeDepsFromWebpack(service: Service): void {
const includeModules = getPropertyFromPath(service, ["custom", "webpack", "includeModules"]);
const includeModules = getPropertyFromPath(service as unknown as Record<string, unknown>, [
"custom",
"webpack",
"includeModules",
]);
if (includeModules === undefined) {
return;
}
Expand All @@ -442,23 +443,25 @@ export function forceExcludeDepsFromWebpack(service: Service): void {
}
}

function getPropertyFromPath(obj: any, path: string[]): any {
function getPropertyFromPath(obj: Record<string, unknown>, path: string[]): Record<string, unknown> | undefined {
let current: Record<string, unknown> = obj;
for (const part of path) {
let prop = obj[part];
let prop = current[part];
if (prop === undefined || prop === true) {
prop = {};
obj[part] = prop;
current[part] = prop;
}
if (prop === false) {
return;
}
obj = prop;
current = prop as Record<string, unknown>;
}
return obj;
return current;
}

export function hasWebpackPlugin(service: Service): boolean {
const plugins: string[] | undefined = (service as any).plugins;
const serviceWithPlugins = service as unknown as { plugins?: string[] | { modules?: string[] } };
const plugins = serviceWithPlugins.plugins;
if (plugins === undefined) {
return false;
}
Expand All @@ -467,7 +470,7 @@ export function hasWebpackPlugin(service: Service): boolean {
return plugins.find((plugin) => plugin === webpackPluginName) !== undefined;
}
// We have an enhanced plugins object
const modules: string[] | undefined = (service as any).plugins.modules;
const modules: string[] | undefined = plugins.modules;
if (modules === undefined) {
return false;
}
Expand Down
114 changes: 85 additions & 29 deletions src/forwarder.ts
Original file line number Diff line number Diff line change
Expand Up @@ -63,14 +63,49 @@ const REST_EXECUTION_SUBSCRIPTION_KEY = "RestExecutionLogGroupSubscription";
const WEBSOCKETS_EXECUTION_LOG_GROUP_KEY = "WebsocketsExecutionLogGroup";
const WEBSOCKETS_EXECUTION_SUBSCRIPTION_KEY = "WebsocketsExecutionLogGroupSubscription";

/** A single CloudFormation resource */
interface CfnResource {
Type: string;
Properties?: Record<string, unknown>;
[key: string]: unknown;
}

/** The Resources block of a compiled CloudFormation template */
export type CfnResourceMap = Record<string, CfnResource>;

/** A compiled AWS::StepFunctions::StateMachine CFN resource */
export interface StateMachineCfnResource {
Properties?: {
Tags?: { Key: string; Value: string }[];
[key: string]: unknown;
};
[key: string]: unknown;
}

/** Logging config for a step function (from serverless-step-functions plugin) */
interface StepFunctionLoggingConfig {
level: string;
includeExecutionData: boolean;
destinations: unknown[];
}

/** A step function entry from stepFunctions.stateMachines */
export interface StepFunctionConfig {
name: string;
loggingConfig?: StepFunctionLoggingConfig;
}

/** A CloudFormation intrinsic function value */
type CfnIntrinsicValue = Record<string, unknown>;

// When users define ARN with CloudFormation functions, the ARN takes this type instead of a string.
export interface CloudFormationObjectArn {
"Fn::Sub"?: string;
"arn:aws"?: string;
}

function isLogGroup(value: any): value is LogGroupResource {
return value.Type === logGroupKey;
function isLogGroup(value: unknown): value is LogGroupResource {
return (value as LogGroupResource).Type === logGroupKey;
}

/**
Expand All @@ -91,7 +126,7 @@ export async function addExecutionLogGroupsAndSubscriptions(
aws: Aws,
functionArn: CloudFormationObjectArn | string,
): Promise<void> {
const extendedProvider = (service.provider as any)?.logs;
const extendedProvider = (service.provider as unknown as { logs?: LogsConfig })?.logs;

if (!isLogsConfig(extendedProvider)) {
return;
Expand Down Expand Up @@ -119,7 +154,11 @@ export async function addExecutionLogGroupsAndSubscriptions(
}
}

export async function addStepFunctionLogGroup(aws: Aws, resources: any, stepFunction: any): Promise<void> {
export async function addStepFunctionLogGroup(
aws: Aws,
resources: CfnResourceMap,
stepFunction: StepFunctionConfig,
): Promise<void> {
const stepFunctionName = stepFunction.name;
const logGroupName = `/aws/vendedlogs/states/${stepFunctionName}-Logs-${aws.getStage()}`;
const logGroupResourceName = `${normalizeResourceName(stepFunctionName)}LogGroup`;
Expand All @@ -142,14 +181,17 @@ export async function addStepFunctionLogGroup(aws: Aws, resources: any, stepFunc
};
}

export function addDdSlsPluginTag(stateMachineObj: any): void {
export function addDdSlsPluginTag(stateMachineObj: StateMachineCfnResource): void {
stateMachineObj.Properties?.Tags?.push({
Key: "dd_sls_plugin",
Value: `v${version}`,
});
}

export function addDdTraceEnabledTag(stateMachineObj: any, enableStepFunctionsTracing: undefined | boolean): void {
export function addDdTraceEnabledTag(
stateMachineObj: StateMachineCfnResource,
enableStepFunctionsTracing: undefined | boolean,
): void {
if (!enableStepFunctionsTracing) {
return;
}
Expand All @@ -160,8 +202,8 @@ export function addDdTraceEnabledTag(stateMachineObj: any, enableStepFunctionsTr
}

export async function addStepFunctionLogGroupSubscription(
resources: any,
stepFunction: any,
resources: CfnResourceMap,
stepFunction: StepFunctionConfig,
functionArn: CloudFormationObjectArn | string,
): Promise<void> {
const logGroupSubscriptionResourceName = `${normalizeResourceName(stepFunction.name)}LogGroupSubscription`;
Expand All @@ -176,7 +218,7 @@ export async function addStepFunctionLogGroupSubscription(
"Fn::Select": [
6,
{
"Fn::Split": [":", stepFunction.loggingConfig.destinations[0]],
"Fn::Split": [":", stepFunction.loggingConfig!.destinations[0]],
},
],
},
Expand Down Expand Up @@ -266,21 +308,33 @@ export async function describeSubscriptionFilters(aws: Aws, logGroupName: string
}

// Helper functions to validate we have a particular log group and if we should subscribe to it
function validateRestApiSubscription(resource: any, subscribe: boolean, extendedProvider: any): boolean {
function validateRestApiSubscription(
resource: LogGroupResource,
subscribe: boolean,
extendedProvider: LogsConfig,
): boolean {
return (
restAccessLoggingIsEnabled(extendedProvider) &&
resource.Properties.LogGroupName.startsWith("/aws/api-gateway/") &&
subscribe
);
}
function validateHttpApiSubscription(resource: any, subscribe: boolean, extendedProvider: any): boolean {
function validateHttpApiSubscription(
resource: LogGroupResource,
subscribe: boolean,
extendedProvider: LogsConfig,
): boolean {
return (
httpAccessLoggingIsEnabled(extendedProvider) &&
resource.Properties.LogGroupName.startsWith("/aws/http-api/") &&
subscribe
);
}
function validateWebsocketSubscription(resource: any, subscribe: boolean, extendedProvider: any): boolean {
function validateWebsocketSubscription(
resource: LogGroupResource,
subscribe: boolean,
extendedProvider: LogsConfig,
): boolean {
return (
websocketAccessLoggingIsEnabled(extendedProvider) &&
resource.Properties.LogGroupName.startsWith("/aws/websocket/") &&
Expand All @@ -290,12 +344,12 @@ function validateWebsocketSubscription(resource: any, subscribe: boolean, extend

function shouldSubscribe(
resourceName: string,
resource: any,
resource: unknown,
forwarderConfigs: ForwarderConfigs,
handlers: FunctionInfo[],
service: Service,
): boolean {
const extendedProvider = (service.provider as any)?.logs;
const extendedProvider = (service.provider as unknown as { logs?: LogsConfig })?.logs;
if (!isLogGroup(resource)) {
return false;
}
Expand Down Expand Up @@ -374,7 +428,7 @@ async function createWebsocketExecutionLogGroupName(aws: Aws) {
};
}

function addExecutionLogGroup(logGroupName: any) {
function addExecutionLogGroup(logGroupName: string | CfnIntrinsicValue) {
// Create the Execution log group for API Gateway REST logging manually
const executionLogGroup = {
Type: "AWS::Logs::LogGroup",
Expand All @@ -397,45 +451,47 @@ function subscribeToExecutionLogGroup(functionArn: string | CloudFormationObject
return executionSubscription;
}

export function isLogsConfig(obj: any): obj is LogsConfig {
if (typeof obj !== "object") {
export function isLogsConfig(obj: unknown): obj is LogsConfig {
if (typeof obj !== "object" || obj === null) {
return false;
}
const record = obj as Record<string, unknown>;

if (obj.hasOwnProperty("restApi")) {
if (!isSubLogsConfig(obj.restApi)) {
if (record.hasOwnProperty("restApi")) {
if (!isSubLogsConfig(record.restApi)) {
return false;
}
}

if (obj.hasOwnProperty("httpApi")) {
if (!isSubLogsConfig(obj.httpApi)) {
if (record.hasOwnProperty("httpApi")) {
if (!isSubLogsConfig(record.httpApi)) {
return false;
}
}

if (obj.hasOwnProperty("websocket")) {
if (!isSubLogsConfig(obj.websocket)) {
if (record.hasOwnProperty("websocket")) {
if (!isSubLogsConfig(record.websocket)) {
return false;
}
}
return true;
}

function isSubLogsConfig(obj: any): obj is SubLogsConfig {
function isSubLogsConfig(obj: unknown): obj is SubLogsConfig {
if (typeof obj === "boolean") {
return true;
}
if (typeof obj !== "object") {
if (typeof obj !== "object" || obj === null) {
return false;
}
if (obj.hasOwnProperty("accessLogging")) {
if (typeof obj.accessLogging !== "boolean" && typeof obj.accessLogging !== undefined) {
const record = obj as Record<string, unknown>;
if (record.hasOwnProperty("accessLogging")) {
if (typeof record.accessLogging !== "boolean" && typeof record.accessLogging !== undefined) {
return false;
}
}
if (obj.hasOwnProperty("executionLogging")) {
if (typeof obj.executionLogging !== "boolean" && typeof obj.executionLogging !== undefined) {
if (record.hasOwnProperty("executionLogging")) {
if (typeof record.executionLogging !== "boolean" && typeof record.executionLogging !== undefined) {
return false;
}
}
Expand Down
Loading
Loading