"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
const cloudwatch = require("@aws-cdk/aws-cloudwatch");
const s3n = require("@aws-cdk/aws-s3-notifications");
const cdk = require("@aws-cdk/cdk");
const lambda_generated_1 = require("./lambda.generated");
class FunctionRef extends cdk.Construct {
    constructor() {
        super(...arguments);
        /**
         * Indicates if the policy that allows CloudWatch logs to publish to this lambda has been added.
         */
        this.logSubscriptionDestinationPolicyAddedFor = [];
    }
    /**
     * Creates a Lambda function object which represents a function not defined
     * within this stack.
     *
     *      Lambda.import(this, 'MyImportedFunction', { lambdaArn: new LambdaArn('arn:aws:...') });
     *
     * @param parent The parent construct
     * @param name The name of the lambda construct
     * @param ref A reference to a Lambda function. Can be created manually (see
     * example above) or obtained through a call to `lambda.export()`.
     */
    static import(parent, name, ref) {
        return new LambdaRefImport(parent, name, ref);
    }
    /**
     * Return the given named metric for this Lambda
     */
    static metricAll(metricName, props) {
        return new cloudwatch.Metric(Object.assign({ namespace: 'AWS/Lambda', metricName }, props));
    }
    /**
     * Metric for the number of Errors executing all Lambdas
     *
     * @default sum over 5 minutes
     */
    static metricAllErrors(props) {
        return FunctionRef.metricAll('Errors', Object.assign({ statistic: 'sum' }, props));
    }
    /**
     * Metric for the Duration executing all Lambdas
     *
     * @default average over 5 minutes
     */
    static metricAllDuration(props) {
        return FunctionRef.metricAll('Duration', props);
    }
    /**
     * Metric for the number of invocations of all Lambdas
     *
     * @default sum over 5 minutes
     */
    static metricAllInvocations(props) {
        return FunctionRef.metricAll('Invocations', Object.assign({ statistic: 'sum' }, props));
    }
    /**
     * Metric for the number of throttled invocations of all Lambdas
     *
     * @default sum over 5 minutes
     */
    static metricAllThrottles(props) {
        return FunctionRef.metricAll('Throttles', Object.assign({ statistic: 'sum' }, props));
    }
    /**
     * Metric for the number of concurrent executions across all Lambdas
     *
     * @default max over 5 minutes
     */
    static metricAllConcurrentExecutions(props) {
        // Mini-FAQ: why max? This metric is a gauge that is emitted every
        // minute, so either max or avg or a percentile make sense (but sum
        // doesn't). Max is more sensitive to spiky load changes which is
        // probably what you're interested in if you're looking at this metric
        // (Load spikes may lead to concurrent execution errors that would
        // otherwise not be visible in the avg)
        return FunctionRef.metricAll('ConcurrentExecutions', Object.assign({ statistic: 'max' }, props));
    }
    /**
     * Metric for the number of unreserved concurrent executions across all Lambdas
     *
     * @default max over 5 minutes
     */
    static metricAllUnreservedConcurrentExecutions(props) {
        return FunctionRef.metricAll('UnreservedConcurrentExecutions', Object.assign({ statistic: 'max' }, props));
    }
    /**
     * Adds a permission to the Lambda resource policy.
     * @param id The id ƒor the permission construct
     */
    addPermission(id, permission) {
        if (!this.canCreatePermissions) {
            // FIXME: Report metadata
            return;
        }
        const principal = this.parsePermissionPrincipal(permission.principal);
        const action = permission.action || 'lambda:InvokeFunction';
        new lambda_generated_1.cloudformation.PermissionResource(this, id, {
            action,
            principal,
            functionName: this.functionName,
            eventSourceToken: permission.eventSourceToken,
            sourceAccount: permission.sourceAccount,
            sourceArn: permission.sourceArn,
        });
    }
    addToRolePolicy(statement) {
        if (!this.role) {
            return;
        }
        this.role.addToPolicy(statement);
    }
    /**
     * Returns a RuleTarget that can be used to trigger this Lambda as a
     * result from a CloudWatch event.
     */
    asEventRuleTarget(ruleArn, ruleId) {
        const permissionId = `AllowEventRule${ruleId}`;
        if (!this.tryFindChild(permissionId)) {
            this.addPermission(permissionId, {
                action: 'lambda:InvokeFunction',
                principal: new cdk.ServicePrincipal('events.amazonaws.com'),
                sourceArn: ruleArn
            });
        }
        return {
            id: this.id,
            arn: this.functionArn,
        };
    }
    /**
     * Return the given named metric for this Lambda
     */
    metric(metricName, props) {
        return new cloudwatch.Metric(Object.assign({ namespace: 'AWS/Lambda', metricName, dimensions: { FunctionName: this.functionName } }, props));
    }
    /**
     * Metric for the Errors executing this Lambda
     *
     * @default sum over 5 minutes
     */
    metricErrors(props) {
        return this.metric('Errors', Object.assign({ statistic: 'sum' }, props));
    }
    /**
     * Metric for the Duration of this Lambda
     *
     * @default average over 5 minutes
     */
    metricDuration(props) {
        return this.metric('Duration', props);
    }
    /**
     * Metric for the number of invocations of this Lambda
     *
     * @default sum over 5 minutes
     */
    metricInvocations(props) {
        return this.metric('Invocations', Object.assign({ statistic: 'sum' }, props));
    }
    /**
     * Metric for the number of throttled invocations of this Lambda
     *
     * @default sum over 5 minutes
     */
    metricThrottles(props) {
        return this.metric('Throttles', Object.assign({ statistic: 'sum' }, props));
    }
    logSubscriptionDestination(sourceLogGroup) {
        const arn = sourceLogGroup.logGroupArn;
        if (this.logSubscriptionDestinationPolicyAddedFor.indexOf(arn) === -1) {
            // NOTE: the use of {AWS::Region} limits this to the same region, which shouldn't really be an issue,
            // since the Lambda must be in the same region as the SubscriptionFilter anyway.
            //
            // (Wildcards in principals are unfortunately not supported.
            this.addPermission('InvokedByCloudWatchLogs', {
                principal: new cdk.ServicePrincipal(new cdk.FnConcat('logs.', new cdk.AwsRegion(), '.amazonaws.com')),
                sourceArn: arn
            });
            this.logSubscriptionDestinationPolicyAddedFor.push(arn);
        }
        return { arn: this.functionArn };
    }
    /**
     * Export this Function (without the role)
     */
    export() {
        return {
            functionArn: new cdk.Output(this, 'FunctionArn', { value: this.functionArn }).makeImportValue(),
        };
    }
    /**
     * Allows this Lambda to be used as a destination for bucket notifications.
     * Use `bucket.onEvent(lambda)` to subscribe.
     */
    asBucketNotificationDestination(bucketArn, bucketId) {
        const permissionId = `AllowBucketNotificationsFrom${bucketId}`;
        if (!this.tryFindChild(permissionId)) {
            this.addPermission(permissionId, {
                sourceAccount: new cdk.AwsAccountId(),
                principal: new cdk.ServicePrincipal('s3.amazonaws.com'),
                sourceArn: bucketArn,
            });
        }
        return {
            type: s3n.BucketNotificationDestinationType.Lambda,
            arn: this.functionArn
        };
    }
    parsePermissionPrincipal(principal) {
        if (!principal) {
            return undefined;
        }
        // use duck-typing, not instance of
        if ('accountId' in principal) {
            return principal.accountId;
        }
        if (`service` in principal) {
            return principal.service;
        }
        throw new Error(`Invalid principal type for Lambda permission statement: ${JSON.stringify(cdk.resolve(principal))}. ` +
            'Supported: AccountPrincipal, ServicePrincipal');
    }
}
exports.FunctionRef = FunctionRef;
class LambdaRefImport extends FunctionRef {
    constructor(parent, name, props) {
        super(parent, name);
        this.canCreatePermissions = false;
        this.functionArn = props.functionArn;
        this.functionName = this.extractNameFromArn(props.functionArn);
        this.role = props.role;
    }
    /**
     * Given an opaque (token) ARN, returns a CloudFormation expression that extracts the function
     * name from the ARN.
     *
     * Function ARNs look like this:
     *
     *     arn:aws:lambda:region:account-id:function:function-name
     *
     * ..which means that in order to extract the `function-name` component from the ARN, we can
     * split the ARN using ":" and select the component in index 6.
     *
     * @returns `FnSelect(6, FnSplit(':', arn))`
     */
    extractNameFromArn(arn) {
        return new cdk.FnSelect(6, new cdk.FnSplit(':', arn));
    }
}
class FunctionName extends cdk.Token {
}
exports.FunctionName = FunctionName;
//# sourceMappingURL=data:application/json;base64, 