<commit_before>// Package function implements function-level operations.
package function

import (
	"bytes"
	"compress/flate"
	"encoding/base64"
	"encoding/json"
	"errors"
	"fmt"
	"io"
	"io/ioutil"
	"os"
	"path/filepath"
	"strings"
	"time"

	"github.com/apex/log"
	"github.com/aws/aws-sdk-go/aws"
	"github.com/aws/aws-sdk-go/aws/awserr"
	"github.com/aws/aws-sdk-go/service/lambda"
	"github.com/aws/aws-sdk-go/service/lambda/lambdaiface"
	"github.com/dustin/go-humanize"
	"github.com/jpillora/archive"
	"gopkg.in/validator.v2"

	"github.com/apex/apex/hooks"
	"github.com/apex/apex/utils"
	"github.com/apex/apex/vpc"
)

// timelessInfo is used to zero mtime which causes function checksums
// to change regardless of the contents actually being altered, specifically
// when using tools such as browserify or webpack.
type timelessInfo struct {
	os.FileInfo
}

func (t timelessInfo) ModTime() time.Time {
	return time.Unix(0, 0)
}

// defaultPlugins are the default plugins which are required by Apex. Note that
// the order here is important for some plugins such as inference before the
// runtimes.
var defaultPlugins = []string{
	"inference",
	"golang",
	"python",
	"nodejs",
	"java",
	"hooks",
	"env",
	"shim",
}

// InvocationType determines how an invocation request is made.
type InvocationType string

// Invocation types.
const (
	RequestResponse InvocationType = "RequestResponse"
	Event                          = "Event"
	DryRun                         = "DryRun"
)

// CurrentAlias name.
const CurrentAlias = "current"

// InvokeError records an error from an invocation.
type InvokeError struct {
	Message string   `json:"errorMessage"`
	Type    string   `json:"errorType"`
	Stack   []string `json:"stackTrace"`
	Handled bool
}

// Error message.
func (e *InvokeError) Error() string {
	return e.Message
}

// Config for a Lambda function.
type Config struct {
	Description      string            `json:"description"`
	Runtime          string            `json:"runtime" validate:"nonzero"`
	Memory           int64             `json:"memory" validate:"nonzero"`
	Timeout          int64             `json:"timeout" validate:"nonzero"`
	Role             string            `json:"role" validate:"nonzero"`
	Handler          string            `json:"handler" validate:"nonzero"`
	Shim             bool              `json:"shim"`
	Environment      map[string]string `json:"environment"`
	Hooks            hooks.Hooks       `json:"hooks"`
	RetainedVersions int               `json:"retainedVersions"`
	VPC              vpc.VPC           `json:"vpc"`
}

// Function represents a Lambda function, with configuration loaded
// from the "function.json" file on disk.
type Function struct {
	Config
	Name         string
	FunctionName string
	Path         string
	Service      lambdaiface.LambdaAPI
	Log          log.Interface
	IgnoreFile   []byte
	Plugins      []string
	Alias        string
}

// Open the function.json file and prime the config.
func (f *Function) Open() error {
	f.defaults()
	f.Log = f.Log.WithField("function", f.Name)

	p, err := os.Open(filepath.Join(f.Path, "function.json"))
	if err == nil {
		if err := json.NewDecoder(p).Decode(&f.Config); err != nil {
			return err
		}
	}

	if err := f.hookOpen(); err != nil {
		return err
	}

	if err := validator.Validate(&f.Config); err != nil {
		return fmt.Errorf("error opening function %s: %s", f.Name, err.Error())
	}

	ignoreFile, err := utils.ReadIgnoreFile(f.Path)
	if err != nil {
		return err
	}
	f.IgnoreFile = append(f.IgnoreFile, []byte("\n")...)
	f.IgnoreFile = append(f.IgnoreFile, ignoreFile...)

	return nil
}

// defaults applies configuration defaults.
func (f *Function) defaults() {
	if f.Alias == "" {
		f.Alias = CurrentAlias
	}

	if f.Plugins == nil {
		f.Plugins = defaultPlugins
	}

	if f.Environment == nil {
		f.Environment = make(map[string]string)
	}

	if f.VPC.Subnets == nil {
		f.VPC.Subnets = []string{}
	}

	if f.VPC.SecurityGroups == nil {
		f.VPC.SecurityGroups = []string{}
	}
}

// Setenv sets environment variable `name` to `value`.
func (f *Function) Setenv(name, value string) {
	f.Environment[name] = value
}

// Deploy generates a zip and creates or deploy the function.
// If the configuration hasn't been changed it will deploy only code,
// otherwise it will deploy both configuration and code.
func (f *Function) Deploy() error {
	f.Log.Debug("deploying")

	zip, err := f.BuildBytes()
	if err != nil {
		return err
	}

	if err := f.hookDeploy(); err != nil {
		return err
	}

	config, err := f.GetConfig()
	if e, ok := err.(awserr.Error); ok {
		if e.Code() == "ResourceNotFoundException" {
			return f.Create(zip)
		}
	}
	if err != nil {
		return err<commit_msg>add implicit APEX_FUNCTION_NAME and LAMBDA_FUNCTION_NAME env vars<commit_after>// Package function implements function-level operations.
package function

import (
	"bytes"
	"compress/flate"
	"encoding/base64"
	"encoding/json"
	"errors"
	"fmt"
	"io"
	"io/ioutil"
	"os"
	"path/filepath"
	"strings"
	"time"

	"github.com/apex/log"
	"github.com/aws/aws-sdk-go/aws"
	"github.com/aws/aws-sdk-go/aws/awserr"
	"github.com/aws/aws-sdk-go/service/lambda"
	"github.com/aws/aws-sdk-go/service/lambda/lambdaiface"
	"github.com/dustin/go-humanize"
	"github.com/jpillora/archive"
	"gopkg.in/validator.v2"

	"github.com/apex/apex/hooks"
	"github.com/apex/apex/utils"
	"github.com/apex/apex/vpc"
)

// timelessInfo is used to zero mtime which causes function checksums
// to change regardless of the contents actually being altered, specifically
// when using tools such as browserify or webpack.
type timelessInfo struct {
	os.FileInfo
}

func (t timelessInfo) ModTime() time.Time {
	return time.Unix(0, 0)
}

// defaultPlugins are the default plugins which are required by Apex. Note that
// the order here is important for some plugins such as inference before the
// runtimes.
var defaultPlugins = []string{
	"inference",
	"golang",
	"python",
	"nodejs",
	"java",
	"hooks",
	"env",
	"shim",
}

// InvocationType determines how an invocation request is made.
type InvocationType string

// Invocation types.
const (
	RequestResponse InvocationType = "RequestResponse"
	Event                          = "Event"
	DryRun                         = "DryRun"
)

// CurrentAlias name.
const CurrentAlias = "current"

// InvokeError records an error from an invocation.
type InvokeError struct {
	Message string   `json:"errorMessage"`
	Type    string   `json:"errorType"`
	Stack   []string `json:"stackTrace"`
	Handled bool
}

// Error message.
func (e *InvokeError) Error() string {
	return e.Message
}

// Config for a Lambda function.
type Config struct {
	Description      string            `json:"description"`
	Runtime          string            `json:"runtime" validate:"nonzero"`
	Memory           int64             `json:"memory" validate:"nonzero"`
	Timeout          int64             `json:"timeout" validate:"nonzero"`
	Role             string            `json:"role" validate:"nonzero"`
	Handler          string            `json:"handler" validate:"nonzero"`
	Shim             bool              `json:"shim"`
	Environment      map[string]string `json:"environment"`
	Hooks            hooks.Hooks       `json:"hooks"`
	RetainedVersions int               `json:"retainedVersions"`
	VPC              vpc.VPC           `json:"vpc"`
}

// Function represents a Lambda function, with configuration loaded
// from the "function.json" file on disk.
type Function struct {
	Config
	Name         string
	FunctionName string
	Path         string
	Service      lambdaiface.LambdaAPI
	Log          log.Interface
	IgnoreFile   []byte
	Plugins      []string
	Alias        string
}

// Open the function.json file and prime the config.
func (f *Function) Open() error {
	f.defaults()
	f.Log = f.Log.WithField("function", f.Name)

	p, err := os.Open(filepath.Join(f.Path, "function.json"))
	if err == nil {
		if err := json.NewDecoder(p).Decode(&f.Config); err != nil {
			return err
		}
	}

	if err := f.hookOpen(); err != nil {
		return err
	}

	if err := validator.Validate(&f.Config); err != nil {
		return fmt.Errorf("error opening function %s: %s", f.Name, err.Error())
	}

	ignoreFile, err := utils.ReadIgnoreFile(f.Path)
	if err != nil {
		return err
	}
	f.IgnoreFile = append(f.IgnoreFile, []byte("\n")...)
	f.IgnoreFile = append(f.IgnoreFile, ignoreFile...)

	return nil
}

// defaults applies configuration defaults.
func (f *Function) defaults() {
	if f.Alias == "" {
		f.Alias = CurrentAlias
	}

	if f.Plugins == nil {
		f.Plugins = defaultPlugins
	}

	if f.Environment == nil {
		f.Environment = make(map[string]string)
	}

	if f.VPC.Subnets == nil {
		f.VPC.Subnets = []string{}
	}

	if f.VPC.SecurityGroups == nil {
		f.VPC.SecurityGroups = []string{}
	}

	f.Setenv("APEX_FUNCTION_NAME", f.Name)
	f.Setenv("LAMBDA_FUNCTION_NAME", f.FunctionName)
}

// Setenv sets environment variable `name` to `value`.
func (f *Function) Setenv(name, value string) {
	f.Environment[name] = value
}

// Deploy generates a zip and creates or deploy the function.
// If the configuration hasn't been changed it will deploy only code,
// otherwise it will deploy both configuration and code.
func (f *Function) Deploy() error {
	f.Log.Debug("deploying")

	zip, err := f.BuildBytes()
	if err != nil {
		return err
	}

	if err := f.hookDeploy(); err != nil {
		return err
	}

	config, err := f.GetConfig()
	if e, ok := err.(awserr.Error); ok {
		if e.Code() == "ResourceNotFoundException" {
			return f.Create(zip)
		}
	}
	if err != nil {
		return err
{'fraction_non_alphanumeric': 0.12946005683612252, 'fraction_numerical': 0.001894537417113988, 'mean_word_length': 2.9657762938230383, 'pattern_counts': {'":': 0, '<': 3, '<?xml version=': 0, '>': 3, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '768490', 'n_tokens_mistral': 3207, 'n_tokens_neox': 3118, 'n_words': 863}