<commit_before>package main

import (
	"encoding/json"
	"fmt"
	"log"
	"os"
	"strings"
	"time"
)

type ReadableBirthday struct {
	time.Time
}

// TODO: support short date format in addition to long, e.g. "Jan 2"
const readableBirthdayLayout = "January 2"

func (ct *ReadableBirthday) UnmarshalJSON(b []byte) (err error) {
	s := strings.Trim(string(b), "\"")
	if s == "null" {
		ct.Time = time.Time{}
		return
	}
	ct.Time, err = time.Parse(readableBirthdayLayout, s)
	return
}

func main() {

	type Config struct {
		Name     string
		Birthday ReadableBirthday
		Twitter  string
		Location string
	}

	if len(os.Args) == 1 {
		fmt.Println("I need a .json file to read from, please add this to your go run birthday.go command.")
		os.Exit(1)
	}

	if len(os.Args) > 2 {
		fmt.Println("You gave me too many .json files to read from, please only use one .json in your go run birthday.go command.")
		os.Exit(1)
	}

	var filepath string
	filepath = os.Args[1]

	file, err := os.Open(filepath)

	if err != nil {
		log.Fatal(err)
	}
	decoder := json.NewDecoder(file)
	configs := []Config{}
	err = decoder.Decode(&configs)
	if err != nil {
		fmt.Println("error:", err)
		os.Exit(1)
	}

	// TODO: display birthdays this week

	// TODO: look into `range`
	for i := 0; i < (len(configs)); i++ {
		fmt.Println(configs[i].Name, configs[i].Birthday.Format(readableBirthdayLayout), configs[i].Twitter, configs[i].Location) // output
	}

}
<commit_msg>use range instead for loop<commit_after>package main

import (
	"encoding/json"
	"fmt"
	"log"
	"os"
	"strings"
	"time"
)

type ReadableBirthday struct {
	time.Time
}

// TODO: support short date format in addition to long, e.g. "Jan 2"
const readableBirthdayLayout = "January 2"

func (ct *ReadableBirthday) UnmarshalJSON(b []byte) (err error) {
	s := strings.Trim(string(b), "\"")
	if s == "null" {
		ct.Time = time.Time{}
		return
	}
	ct.Time, err = time.Parse(readableBirthdayLayout, s)
	return
}

func main() {

	type Config struct {
		Name     string
		Birthday ReadableBirthday
		Twitter  string
		Location string
	}

	if len(os.Args) == 1 {
		fmt.Println("I need a .json file to read from, please add this to your go run birthday.go command.")
		os.Exit(1)
	}

	if len(os.Args) > 2 {
		fmt.Println("You gave me too many .json files to read from, please only use one .json in your go run birthday.go command.")
		os.Exit(1)
	}

	var filepath string
	filepath = os.Args[1]

	file, err := os.Open(filepath)

	if err != nil {
		log.Fatal(err)
	}
	decoder := json.NewDecoder(file)
	configs := []Config{}
	err = decoder.Decode(&configs)
	if err != nil {
		fmt.Println("error:", err)
		os.Exit(1)
	}

	// TODO: display birthdays this week

	// TODO: look into `range`
	for i := range configs {
		fmt.Println(configs[i].Name, configs[i].Birthday.Format(readableBirthdayLayout), configs[i].Twitter, configs[i].Location) // output
	}

}
<|endoftext|>


<commit_before>package transcription

import (
	"net/smtp"
	"strings"
)

// SendEmail connects to an email server at host:port, switches to TLS,
// authenticates on TLS connections using the username and password, and sends
// an email from address from, to address to, with subject line subject with message body.
func SendEmail(username string, password string, host string, port int, to []string, subject string, body string) error {
	from := username
	auth := smtp.PlainAuth("", username, password, host)

	// The msg parameter should be an RFC 822-style email with headers first,
	// a blank line, and then the message body. The lines of msg should be CRLF terminated.
	msg := []byte(msgHeaders(from, to, subject) + "\r\n" + body + "\r\n")
	addr := host + ":" + string(port)
	if err := smtp.SendMail(addr, auth, from, to, msg); err != nil {
		return err
	}
	return nil
}

func msgHeaders(from string, to []string, subject string) string {
	fromHeader := "From: " + from
	toHeader := "To: " + strings.Join(to, ", ")
	subjectHeader := "Subject: " + subject
	msgHeaders := []string{fromHeader, toHeader, subjectHeader}
	return strings.Join(msgHeaders, "\r\n")
}
<commit_msg>DownloadFileFromURL takes a url string and creates a file that copies the data from the url<commit_after>package transcription

import (
	"fmt"
	"io"
	"net/http"
	"net/smtp"
	"os"
	"strings"
)

// SendEmail connects to an email server at host:port, switches to TLS,
// authenticates on TLS connections using the username and password, and sends
// an email from address from, to address to, with subject line subject with message body.
func SendEmail(username string, password string, host string, port int, to []string, subject string, body string) error {
	from := username
	auth := smtp.PlainAuth("", username, password, host)

	// The msg parameter should be an RFC 822-style email with headers first,
	// a blank line, and then the message body. The lines of msg should be CRLF terminated.
	msg := []byte(msgHeaders(from, to, subject) + "\r\n" + body + "\r\n")
	addr := host + ":" + string(port)
	if err := smtp.SendMail(addr, auth, from, to, msg); err != nil {
		return err
	}
	return nil
}

func msgHeaders(from string, to []string, subject string) string {
	fromHeader := "From: " + from
	toHeader := "To: " + strings.Join(to, ", ")
	subjectHeader := "Subject: " + subject
	msgHeaders := []string{fromHeader, toHeader, subjectHeader}
	return strings.Join(msgHeaders, "\r\n")
}

// DownloadFileFromURL downloads an mp3 file locally from a url.
func DownloadFileFromURL(url string) error {
	// https://github.com/thbar/golang-playground/blob/master/download-files.go
	tokens := strings.Split(url, "/")
	fileName := tokens[len(tokens)-1]
	fmt.Println("Downloading", url, "to", fileName)

	// TODO: check file existence first with io.IsExist
	// Create the file
	output, err := os.Create(fileName)
	if err != nil {
		return err
	}
	defer output.Close()

	// Get the data
	response, err := http.Get(url)
	if err != nil {
		return err
	}
	defer response.Body.Close()

	// Write the body to file
	_, err = io.Copy(output, response.Body)
	if err != nil {
		return err
	}

	return nil
}
<|endoftext|>


<commit_before>package cloudformation

var AWSTemplateFormatVersion = "2010-09-09"

func NewTemplate(desc string) *Template {
	return &Template{
		AWSTemplateFormatVersion: AWSTemplateFormatVersion,
		Description:              desc,
	}
}

type Template struct {
	AWSTemplateFormatVersion string `json:"AWSTemplateFormatVersion,omitempty"`
	Description              string `json:"Description,omitempty"`

	Parameters map[string]*Parameter `json:"Parameters,omitempty"`
	Resources  Resources             `json:"Resources,omitempty"`
}

type Resources map[string]interface{}

type Resource struct {
	Type         string      `json:"Type,omitempty"`
	Properties   interface{} `json:"Properties,omitempty"`
	UpdatePolicy interface{} `json:"UpdatePolicy,omitempty"`
}

func NewResource(theType string, properties interface{}) *Resource {
	return &Resource{
		Type: theType, Properties: properties,
	}
}

type Properties struct {
	Type       string      `json:"Type,omitempty"`
	Properties *Properties `json:"Properties,omitempty"`
	MinValue   int         `json:"MinValue,omitempty"`
}

// AWS::ElasticLoadBalancing::LoadBalancer

type LoadBalancer struct {
	LoadBalancerName interface{}  `json:"LoadBalancerName,omitempty"`
	CrossZone        interface{}  `json:"CrossZone,omitempty"`
	HealthCheck      *HealthCheck `json:"HealthCheck,omitempty"`
	Listeners        []*Listener  `json:"Listeners,omitempty"`
	SecurityGroups   interface{}  `json:"SecurityGroups,omitempty"`
	Subnets          interface{}  `json:"Subnets,omitempty"`
}

type HealthCheck struct {
	HealthyThreshold   interface{} `json:"HealthyThreshold,omitempty"`
	Interval           interface{} `json:"Interval,omitempty"`
	Target             interface{} `json:"Target,omitempty"`
	Timeout            interface{} `json:"Timeout,omitempty"`
	UnhealthyThreshold interface{} `json:"UnhealthyThreshold,omitempty"`
}

type Listener struct {
	InstancePort     interface{}   `json:"InstancePort,omitempty"`
	LoadBalancerPort interface{}   `json:"LoadBalancerPort,omitempty"`
	Protocol         interface{}   `json:"Protocol,omitempty"`
	InstanceProtocol interface{}   `json:"InstanceProtocol,omitempty"`
	SSLCertificateId interface{}   `json:"SSLCertificateId,omitempty"`
	PolicyNames      []interface{} `json:"PolicyNames,omitempty"`
}

type Policy struct {
	PolicyName interface{}        `json:"PolicyName,omitempty"`
	PolicyType interface{}        `json:"PolicyType,omitempty"`
	Attributes []*PolicyAttribute `json:"Attributes,omitempty"`
}

// AWS::AutoScaling::LaunchConfiguration
type LaunchConfiguration struct {
	InstanceType        interface{}           `json:"InstanceType,omitempty"`
	ImageId             interface{}           `json:"ImageId,omitempty"`
	KeyName             interface{}           `json:"KeyName,omitempty"`
	SecurityGroups      []interface{}         `json:"SecurityGroups,omitempty"`
	UserData            interface{}           `json:"UserData,omitempty"`
	BlockDeviceMappings []*BlockDeviceMapping `json:"BlockDeviceMappings,omitempty"`
}

// AWS::AutoScaling::AutoScalingGroup",
type AutoScalingGroup struct {
	AvailabilityZones       []interface{} `json:"AvailabilityZones,omitempty"`
	Cooldown                interface{}   `json:"Cooldown,omitempty"`
	DesiredCapacity         interface{}   `json:"DesiredCapacity,omitempty"`
	HealthCheckGracePeriod  interface{}   `json:"HealthCheckGracePeriod,omitempty"`
	HealthCheckType         interface{}   `json:"HealthCheckType,omitempty"`
	LaunchConfigurationName interface{}   `json:"LaunchConfigurationName,omitempty"`
	LoadBalancerNames       []interface{} `json:"LoadBalancerNames,omitempty"`
	MaxSize                 interface{}   `json:"MaxSize,omitempty"`
	MinSize                 interface{}   `json:"MinSize,omitempty"`
}

type NotificationConfiguration struct {
	TopicARN          interface{}   `json:"TopicARN,omitempty"`
	NotificationTypes []interface{} `json:"NotificationTypes,omitempty"`
	Tags              []*Tag        `json:"Tags,omitempty"`
}

type Tag struct {
	Key               interface{} `json:"Key,omitempty"`
	Value             interface{} `json:"Value,omitempty"`
	PropagateAtLaunch interface{} `json:"PropagateAtLaunch,omitempty"`
	VPCZoneIdentifier interface{} `json:"VPCZoneIdentifier,omitempty"`
}

// AWS::CloudWatch::Alarm
type Alarm struct {
	ActionsEnabled     interface{}   `json:"ActionsEnabled,omitempty"`
	ComparisonOperator interface{}   `json:"ComparisonOperator,omitempty"`
	EvaluationPeriods  interface{}   `json:"EvaluationPeriods,omitempty"`
	MetricName         interface{}   `json:"MetricName,omitempty"`
	Namespace          interface{}   `json:"Namespace,omitempty"`
	Period             interface{}   `json:"Period,omitempty"`
	Statistic          interface{}   `json:"Statistic,omitempty"`
	Threshold          interface{}   `json:"Threshold,omitempty"`
	AlarmActions       []interface{} `json:"AlarmActions,omitempty"`
}

type BlockDeviceMapping struct {
	DeviceName interface{} `json:"DeviceName,omitempty"`
	Ebs        *Ebs        `json:"Ebs,omitempty"`
}

type Ebs struct {
	VolumeSize interface{} `json:"VolumeSize,omitempty"`
}

type PolicyAttribute struct {
	Name           interface{}   `json:"Name,omitempty"`
	Value          interface{}   `json:"Value,omitempty"`
	SecurityGroups []interface{} `json:"SecurityGroups,omitempty"`
	Subnets        interface{}   `json:"Subnets,omitempty"`
}

// AWS::EC2::SecurityGroup
type SecurityGroupProperties struct {
	GroupDescription     interface{}     `json:"GroupDescription,omitempty"`
	VpcId                interface{}     `json:"VpcId,omitempty"`
	SecurityGroupIngress []SecurityGroup `json:"SecurityGroupIngress,omitempty"`
	SecurityGroupEgress  []SecurityGroup `json:"SecurityGroupEgress,omitempty"`
}

type SecurityGroup struct {
	VpcId                interface{}          `json:"VpcId,omitempty"`
	GroupDescription     interface{}          `json:"GroupDescription,omitempty"`
	SecurityGroupEgress  []*SecurityGroupRule `json:"SecurityGroupEgress,omitempty"`
	SecurityGroupIngress []*SecurityGroupRule `json:"SecurityGroupIngress,omitempty"`
	Tags                 []*Tag               `json:"Tags,omitempty"`
}

type SecurityGroupRule struct {
	IpProtocol                 interface{} `json:"IpProtocol,omitempty"`
	FromPort                   interface{} `json:"FromPort,omitempty"`
	ToPort                     interface{} `json:"ToPort,omitempty"`
	CidrIp                     interface{} `json:"CidrIp,omitempty"`
	SourceSecurityGroupId      interface{} `json:"SourceSecurityGroupId,omitempty"`
	SourceSecurityGroupOwnerId interface{} `json:"SourceSecurityGroupOwnerId,omitempty"`
}

// AWS::RDS::DBSubnetGroup
type DBSubnetGroup struct {
	DBSubnetGroupDescription interface{} `json:"DBSubnetGroupDescription,omitempty"`
	SubnetIds                interface{} `json:"SubnetIds,omitempty"`
}

// AWS::SNS::Topic
type Topic struct {
	DisplayName string `json:"DisplayName,omitempty"`
}

// AWS::ElastiCache::CacheCluster
type CacheClusterProperties struct {
	AutoMinorVersionUpgrade    interface{}   `json:"AutoMinorVersionUpgrade,omitempty"`
	CacheNodeType              interface{}   `json:"CacheNodeType,omitempty"`
	ClusterName                interface{}   `json:"ClusterName,omitempty"`
	Engine                     interface{}   `json:"Engine,omitempty"`
	EngineVersion              interface{}   `json:"EngineVersion,omitempty"`
	NotificationTopicArn       interface{}   `json:"NotificationTopicArn,omitempty"`
	NumCacheNodes              interface{}   `json:"NumCacheNodes,omitempty"`
	PreferredAvailabilityZone  interface{}   `json:"PreferredAvailabilityZone,omitempty"`
	PreferredMaintenanceWindow interface{}   `json:"PreferredMaintenanceWindow,omitempty"`
	VpcSecurityGroupIds        []interface{} `json:"VpcSecurityGroupIds,omitempty"`
}

// AWS::RDS::DBParameterGroup
type DBParameterGroup struct {
	Description interface{}       `json:"Description,omitempty"`
	Family      interface{}       `json:"Family,omitempty"`
	Parameters  map[string]string `json:"Parameters,omitempty"`
}

// AWS::RDS::DBInstance
type DBInstance struct {
	AllocatedStorage           interface{}   `json:"	AllocatedStorage,omitempty"`
	AutoMinorVersionUpgrade    interface{}   `json:"	AutoMinorVersionUpgrade,omitempty"`
	BackupRetentionPeriod      interface{}   `json:"	BackupRetentionPeriod,omitempty"`
	DBSubnetGroupName          interface{}   `json:"	DBSubnetGroupName,omitempty"`
	DBInstanceClass            interface{}   `json:"	DBInstanceClass,omitempty"`
	DBInstanceIdentifier       interface{}   `json:"	DBInstanceIdentifier,omitempty"`
	DBName                     interface{}   `json:"	DBName,omitempty"`
	Engine                     interface{}   `json:"	Engine,omitempty"`
	EngineVersion              interface{}   `json:"	EngineVersion,omitempty"`
	LicenseModel               interface{}   `json:"	LicenseModel,omitempty"`
	MasterUsername             interface{}   `json:"	MasterUsername,omitempty"`
	MasterUserPassword         interface{}   `json:"	MasterUserPassword,omitempty"`
	MultiAZ                    interface{}   `json:"	MultiAZ,omitempty"`
	Port                       interface{}   `json:"	Port,omitempty"`
	PreferredBackupWindow      interface{}   `json:"	PreferredBackupWindow,omitempty"`
	PreferredMaintenanceWindow interface{}   `json:"	PreferredMaintenanceWindow,omitempty"`
	Tags                       []interface{} `json:"	Tags,omitempty"`
	VPCSecurityGroups          []interface{} `json:"	VPCSecurityGroups,omitempty"`
}

type Property struct {
	Type string `json:"Type,omitempty"`
}

type Parameter struct {
	Type    string `json:"Type,omitempty"`
	Default string `json:"Default,omitempty"`
}
<commit_msg>allow interface{} for Parameters<commit_after>package cloudformation

var AWSTemplateFormatVersion = "2010-09-09"

func NewTemplate(desc string) *Template {
	return &Template{
		AWSTemplateFormatVersion: AWSTemplateFormatVersion,
		Description:              desc,
	}
}

type Template struct {
	AWSTemplateFormatVersion string `json:"AWSTemplateFormatVersion,omitempty"`
	Description              string `json:"Description,omitempty"`

	Parameters map[string]*Parameter `json:"Parameters,omitempty"`
	Resources  Resources             `json:"Resources,omitempty"`
}

type Resources map[string]interface{}

type Resource struct {
	Type         string      `json:"Type,omitempty"`
	Properties   interface{} `json:"Properties,omitempty"`
	UpdatePolicy interface{} `json:"UpdatePolicy,omitempty"`
}

func NewResource(theType string, properties interface{}) *Resource {
	return &Resource{
		Type: theType, Properties: properties,
	}
}

type Properties struct {
	Type       string      `json:"Type,omitempty"`
	Properties *Properties `json:"Properties,omitempty"`
	MinValue   int         `json:"MinValue,omitempty"`
}

// AWS::ElasticLoadBalancing::LoadBalancer

type LoadBalancer struct {
	LoadBalancerName interface{}  `json:"LoadBalancerName,omitempty"`
	CrossZone        interface{}  `json:"CrossZone,omitempty"`
	HealthCheck      *HealthCheck `json:"HealthCheck,omitempty"`
	Listeners        []*Listener  `json:"Listeners,omitempty"`
	SecurityGroups   interface{}  `json:"SecurityGroups,omitempty"`
	Subnets          interface{}  `json:"Subnets,omitempty"`
}

type HealthCheck struct {
	HealthyThreshold   interface{} `json:"HealthyThreshold,omitempty"`
	Interval           interface{} `json:"Interval,omitempty"`
	Target             interface{} `json:"Target,omitempty"`
	Timeout            interface{} `json:"Timeout,omitempty"`
	UnhealthyThreshold interface{} `json:"UnhealthyThreshold,omitempty"`
}

type Listener struct {
	InstancePort     interface{}   `json:"InstancePort,omitempty"`
	LoadBalancerPort interface{}   `json:"LoadBalancerPort,omitempty"`
	Protocol         interface{}   `json:"Protocol,omitempty"`
	InstanceProtocol interface{}   `json:"InstanceProtocol,omitempty"`
	SSLCertificateId interface{}   `json:"SSLCertificateId,omitempty"`
	PolicyNames      []interface{} `json:"PolicyNames,omitempty"`
}

type Policy struct {
	PolicyName interface{}        `json:"PolicyName,omitempty"`
	PolicyType interface{}        `json:"PolicyType,omitempty"`
	Attributes []*PolicyAttribute `json:"Attributes,omitempty"`
}

// AWS::AutoScaling::LaunchConfiguration
type LaunchConfiguration struct {
	InstanceType        interface{}           `json:"InstanceType,omitempty"`
	ImageId             interface{}           `json:"ImageId,omitempty"`
	KeyName             interface{}           `json:"KeyName,omitempty"`
	SecurityGroups      []interface{}         `json:"SecurityGroups,omitempty"`
	UserData            interface{}           `json:"UserData,omitempty"`
	BlockDeviceMappings []*BlockDeviceMapping `json:"BlockDeviceMappings,omitempty"`
}

// AWS::AutoScaling::AutoScalingGroup",
type AutoScalingGroup struct {
	AvailabilityZones       []interface{} `json:"AvailabilityZones,omitempty"`
	Cooldown                interface{}   `json:"Cooldown,omitempty"`
	DesiredCapacity         interface{}   `json:"DesiredCapacity,omitempty"`
	HealthCheckGracePeriod  interface{}   `json:"HealthCheckGracePeriod,omitempty"`
	HealthCheckType         interface{}   `json:"HealthCheckType,omitempty"`
	LaunchConfigurationName interface{}   `json:"LaunchConfigurationName,omitempty"`
	LoadBalancerNames       []interface{} `json:"LoadBalancerNames,omitempty"`
	MaxSize                 interface{}   `json:"MaxSize,omitempty"`
	MinSize                 interface{}   `json:"MinSize,omitempty"`
}

type NotificationConfiguration struct {
	TopicARN          interface{}   `json:"TopicARN,omitempty"`
	NotificationTypes []interface{} `json:"NotificationTypes,omitempty"`
	Tags              []*Tag        `json:"Tags,omitempty"`
}

type Tag struct {
	Key               interface{} `json:"Key,omitempty"`
	Value             interface{} `json:"Value,omitempty"`
	PropagateAtLaunch interface{} `json:"PropagateAtLaunch,omitempty"`
	VPCZoneIdentifier interface{} `json:"VPCZoneIdentifier,omitempty"`
}

// AWS::CloudWatch::Alarm
type Alarm struct {
	ActionsEnabled     interface{}   `json:"ActionsEnabled,omitempty"`
	ComparisonOperator interface{}   `json:"ComparisonOperator,omitempty"`
	EvaluationPeriods  interface{}   `json:"EvaluationPeriods,omitempty"`
	MetricName         interface{}   `json:"MetricName,omitempty"`
	Namespace          interface{}   `json:"Namespace,omitempty"`
	Period             interface{}   `json:"Period,omitempty"`
	Statistic          interface{}   `json:"Statistic,omitempty"`
	Threshold          interface{}   `json:"Threshold,omitempty"`
	AlarmActions       []interface{} `json:"AlarmActions,omitempty"`
}

type BlockDeviceMapping struct {
	DeviceName interface{} `json:"DeviceName,omitempty"`
	Ebs        *Ebs        `json:"Ebs,omitempty"`
}

type Ebs struct {
	VolumeSize interface{} `json:"VolumeSize,omitempty"`
}

type PolicyAttribute struct {
	Name           interface{}   `json:"Name,omitempty"`
	Value          interface{}   `json:"Value,omitempty"`
	SecurityGroups []interface{} `json:"SecurityGroups,omitempty"`
	Subnets        interface{}   `json:"Subnets,omitempty"`
}

// AWS::EC2::SecurityGroup
type SecurityGroupProperties struct {
	GroupDescription     interface{}     `json:"GroupDescription,omitempty"`
	VpcId                interface{}     `json:"VpcId,omitempty"`
	SecurityGroupIngress []SecurityGroup `json:"SecurityGroupIngress,omitempty"`
	SecurityGroupEgress  []SecurityGroup `json:"SecurityGroupEgress,omitempty"`
}

type SecurityGroup struct {
	VpcId                interface{}          `json:"VpcId,omitempty"`
	GroupDescription     interface{}          `json:"GroupDescription,omitempty"`
	SecurityGroupEgress  []*SecurityGroupRule `json:"SecurityGroupEgress,omitempty"`
	SecurityGroupIngress []*SecurityGroupRule `json:"SecurityGroupIngress,omitempty"`
	Tags                 []*Tag               `json:"Tags,omitempty"`
}

type SecurityGroupRule struct {
	IpProtocol                 interface{} `json:"IpProtocol,omitempty"`
	FromPort                   interface{} `json:"FromPort,omitempty"`
	ToPort                     interface{} `json:"ToPort,omitempty"`
	CidrIp                     interface{} `json:"CidrIp,omitempty"`
	SourceSecurityGroupId      interface{} `json:"SourceSecurityGroupId,omitempty"`
	SourceSecurityGroupOwnerId interface{} `json:"SourceSecurityGroupOwnerId,omitempty"`
}

// AWS::RDS::DBSubnetGroup
type DBSubnetGroup struct {
	DBSubnetGroupDescription interface{} `json:"DBSubnetGroupDescription,omitempty"`
	SubnetIds                interface{} `json:"SubnetIds,omitempty"`
}

// AWS::SNS::Topic
type Topic struct {
	DisplayName string `json:"DisplayName,omitempty"`
}

// AWS::ElastiCache::CacheCluster
type CacheClusterProperties struct {
	AutoMinorVersionUpgrade    interface{}   `json:"AutoMinorVersionUpgrade,omitempty"`
	CacheNodeType              interface{}   `json:"CacheNodeType,omitempty"`
	ClusterName                interface{}   `json:"ClusterName,omitempty"`
	Engine                     interface{}   `json:"Engine,omitempty"`
	EngineVersion              interface{}   `json:"EngineVersion,omitempty"`
	NotificationTopicArn       interface{}   `json:"NotificationTopicArn,omitempty"`
	NumCacheNodes              interface{}   `json:"NumCacheNodes,omitempty"`
	PreferredAvailabilityZone  interface{}   `json:"PreferredAvailabilityZone,omitempty"`
	PreferredMaintenanceWindow interface{}   `json:"PreferredMaintenanceWindow,omitempty"`
	VpcSecurityGroupIds        []interface{} `json:"VpcSecurityGroupIds,omitempty"`
}

// AWS::RDS::DBParameterGroup
type DBParameterGroup struct {
	Description interface{} `json:"Description,omitempty"`
	Family      interface{} `json:"Family,omitempty"`
	Parameters  interface{} `json:"Parameters,omitempty"`
}

// AWS::RDS::DBInstance
type DBInstance struct {
	AllocatedStorage           interface{}   `json:"	AllocatedStorage,omitempty"`
	AutoMinorVersionUpgrade    interface{}   `json:"	AutoMinorVersionUpgrade,omitempty"`
	BackupRetentionPeriod      interface{}   `json:"	BackupRetentionPeriod,omitempty"`
	DBSubnetGroupName          interface{}   `json:"	DBSubnetGroupName,omitempty"`
	DBInstanceClass            interface{}   `json:"	DBInstanceClass,omitempty"`
	DBInstanceIdentifier       interface{}   `json:"	DBInstanceIdentifier,omitempty"`
	DBName                     interface{}   `json:"	DBName,omitempty"`
	Engine                     interface{}   `json:"	Engine,omitempty"`
	EngineVersion              interface{}   `json:"	EngineVersion,omitempty"`
	LicenseModel               interface{}   `json:"	LicenseModel,omitempty"`
	MasterUsername             interface{}   `json:"	MasterUsername,omitempty"`
	MasterUserPassword         interface{}   `json:"	MasterUserPassword,omitempty"`
	MultiAZ                    interface{}   `json:"	MultiAZ,omitempty"`
	Port                       interface{}   `json:"	Port,omitempty"`
	PreferredBackupWindow      interface{}   `json:"	PreferredBackupWindow,omitempty"`
	PreferredMaintenanceWindow interface{}   `json:"	PreferredMaintenanceWindow,omitempty"`
	Tags                       []interface{} `json:"	Tags,omitempty"`
	VPCSecurityGroups          []interface{} `json:"	VPCSecurityGroups,omitempty"`
}

type Property struct {
	Type string `json:"Type,omitempty"`
}

type Parameter struct {
	Type    string `json:"Type,omitempty"`
	Default string `json:"Default,omitempty"`
}
<|endoftext|>


<commit_before>package aws

import (
	"fmt"
	"log"
	"time"

	"github.com/aws/aws-sdk-go/aws"
	"github.com/aws/aws-sdk-go/service/guardduty"
	"github.com/hashicorp/terraform/helper/schema"
)

func resourceAwsGuardDutyFilter() *schema.Resource {
	return &schema.Resource{
		Create: resourceAwsGuardDutyFilterCreate,
		Read:   resourceAwsGuardDutyFilterRead,
		// Update: resourceAwsGuardDutyFilterUpdate,
		Delete: resourceAwsGuardDutyFilterDelete,

		// Importer: &schema.ResourceImporter{
		// 	State: schema.ImportStatePassthrough,
		// },
		Schema: map[string]*schema.Schema{
			// "account_id": { // idk, do we need it
			// 	Type:         schema.TypeString,
			// 	Required:     true,
			// 	ForceNew:     true,
			// 	ValidateFunc: validateAwsAccountId,
			// },
			"detector_id": {
				Type:     schema.TypeString,
				Required: true,
				ForceNew: true,
			},
			"name": {
				Type:     schema.TypeString,
				Required: true,
				ForceNew: true, // perhaps remove here and below, when Update is back
			},
			"description": {
				Type:     schema.TypeString,
				Optional: true,
				ForceNew: true, // perhaps remove here and below, when Update is back
			},
			// "tags": { // Must be added back
			// 	Type:     schema.TypeTags, // probably wrong type
			// 	Optional: true,
			// },
			// "findingCriteria": {
			// 	Type:     schema.TypeString, // need to implement a new type
			// 	Optional: true,              // change to required
			// 	ForceNew: true,              // perhaps remove here and below, when Update is back
			// },
			"action": {
				Type:     schema.TypeString, // should have a new type or a validation for NOOP/ARCHIVE
				Optional: true,
				ForceNew: true, // perhaps remove here and below, when Update is back
			},
			"rank": {
				Type:     schema.TypeInt,
				Optional: true,
				ForceNew: true, // perhaps remove here and below, when Update is back
			},
		},
		Timeouts: &schema.ResourceTimeout{
			Create: schema.DefaultTimeout(60 * time.Second),
			Update: schema.DefaultTimeout(60 * time.Second),
		},
	}
}

func resourceAwsGuardDutyFilterCreate(d *schema.ResourceData, meta interface{}) error {
	conn := meta.(*AWSClient).guarddutyconn

	input := guardduty.CreateFilterInput{
		Name:        aws.String(d.Get("name").(string)),
		Description: aws.String(d.Get("description").(string)),
		Action:      aws.String(d.Get("action").(string)),
		Rank:        aws.Int64(int64(d.Get("rank").(int))),
	}

	log.Printf("[DEBUG] Creating GuardDuty Filter: %s", input)
	output, err := conn.CreateFilter(&input)
	if err != nil {
		return fmt.Errorf("Creating GuardDuty Filter failed: %s", err.Error())
	}
	d.SetId(*output.Name)

	return resourceAwsGuardDutyFilterRead(d, meta)
}

func resourceAwsGuardDutyFilterRead(d *schema.ResourceData, meta interface{}) error {
	conn := meta.(*AWSClient).guarddutyconn
	detectorId := d.Get("detectorId").(string)
	filterName := d.Get("filterName").(string)

	input := guardduty.GetFilterInput{
		DetectorId: aws.String(detectorId),
		FilterName: aws.String(filterName),
	}

	log.Printf("[DEBUG] Reading GuardDuty Filter: %s", input)
	filter, err := conn.GetFilter(&input)

	if err != nil {
		if isAWSErr(err, guardduty.ErrCodeBadRequestException, "The request is rejected because the input detectorId is not owned by the current account.") {
			log.Printf("[WARN] GuardDuty detector %q not found, removing from state", d.Id())
			d.SetId("")
			return nil
		}
		return fmt.Errorf("Reading GuardDuty Filter '%s' failed: %s", filterName, err.Error())
	}

	d.Set("account_id", filter.Action)
	d.Set("account_id", filter.Description)
	d.Set("account_id", filter.Name)
	d.Set("account_id", filter.Rank)
	d.Set("detector_id", d.Id())

	// need to find a way how to fill it interface{}
	// d.Set("account_id", filter.FindingCriteria)

	// FindingCriteria.Criterion
	// Eq
	// Gt
	// Gte
	// Lt
	// Lte
	// Neq

	return nil
}

// func resourceAwsGuardDutyFilterUpdate(d *schema.ResourceData, meta interface{}) error {
// 	conn := meta.(*AWSClient).guarddutyconn
//
// 	accountID, detectorID, err := decodeGuardDutyMemberID(d.Id())
// 	if err != nil {
// 		return err
// 	}
//
// 	if d.HasChange("invite") {
// 		if d.Get("invite").(bool) {
// 			input := &guardduty.InviteMembersInput{
// 				DetectorId:               aws.String(detectorID),
// 				AccountIds:               []*string{aws.String(accountID)},
// 				DisableEmailNotification: aws.Bool(d.Get("disable_email_notification").(bool)),
// 				Message:                  aws.String(d.Get("invitation_message").(string)),
// 			}
//
// 			log.Printf("[INFO] Inviting GuardDuty Member: %s", input)
// 			output, err := conn.InviteMembers(input)
// 			if err != nil {
// 				return fmt.Errorf("error inviting GuardDuty Member %q: %s", d.Id(), err)
// 			}
//
// 			// {"unprocessedAccounts":[{"result":"The request is rejected because the current account has already invited or is already the GuardDuty master of the given member account ID.","accountId":"067819342479"}]}
// 			if len(output.UnprocessedAccounts) > 0 {
// 				return fmt.Errorf("error inviting GuardDuty Member %q: %s", d.Id(), aws.StringValue(output.UnprocessedAccounts[0].Result))
// 			}
// 		} else {
// 			input := &guardduty.DisassociateMembersInput{
// 				AccountIds: []*string{aws.String(accountID)},
// 				DetectorId: aws.String(detectorID),
// 			}
// 			log.Printf("[INFO] Disassociating GuardDuty Member: %s", input)
// 			_, err := conn.DisassociateMembers(input)
// 			if err != nil {
// 				return fmt.Errorf("error disassociating GuardDuty Member %q: %s", d.Id(), err)
// 			}
// 		}
// 	}
//
// 	return resourceAwsGuardDutyFilterRead(d, meta)
// }

func resourceAwsGuardDutyFilterDelete(d *schema.ResourceData, meta interface{}) error {
	conn := meta.(*AWSClient).guarddutyconn

	accountID, detectorID, err := decodeGuardDutyMemberID(d.Id())
	if err != nil {
		return err
	}

	input := guardduty.DeleteMembersInput{
		AccountIds: []*string{aws.String(accountID)},
		DetectorId: aws.String(detectorID),
	}

	log.Printf("[DEBUG] Delete GuardDuty Member: %s", input)
	_, err = conn.DeleteMembers(&input)
	if err != nil {
		return fmt.Errorf("Deleting GuardDuty Member '%s' failed: %s", d.Id(), err.Error())
	}
	return nil
}
<commit_msg>Made Create action to make its way to the API request<commit_after>package aws

import (
	"fmt"
	"log"
	"time"

	"github.com/aws/aws-sdk-go/aws"
	"github.com/aws/aws-sdk-go/service/guardduty"
	"github.com/hashicorp/terraform/helper/schema"
)

func resourceAwsGuardDutyFilter() *schema.Resource {
	return &schema.Resource{
		Create: resourceAwsGuardDutyFilterCreate,
		Read:   resourceAwsGuardDutyFilterRead,
		// Update: resourceAwsGuardDutyFilterUpdate,
		Delete: resourceAwsGuardDutyFilterDelete,

		// Importer: &schema.ResourceImporter{
		// 	State: schema.ImportStatePassthrough,
		// },
		Schema: map[string]*schema.Schema{ // TODO: add validations
			// "account_id": { // idk, do we need it
			// 	Type:         schema.TypeString,
			// 	Required:     true,
			// 	ForceNew:     true,
			// 	ValidateFunc: validateAwsAccountId,
			// },
			"detector_id": {
				Type:     schema.TypeString,
				Required: true,
				ForceNew: true,
			},
			"name": {
				Type:     schema.TypeString,
				Required: true,
				ForceNew: true, // perhaps remove here and below, when Update is back
			},
			"description": {
				Type:     schema.TypeString,
				Optional: true,
				ForceNew: true, // perhaps remove here and below, when Update is back
			},
			// "tags": { // Must be added back
			// 	Type:     schema.TypeTags, // probably wrong type
			// 	Optional: true,
			// },
			"finding_criteria": {
				Type:     schema.TypeList, // Probably need to use FindingCriteria type
				MaxItems: 1,
				Required: true, // change to required
				ForceNew: true, // perhaps remove here and below, when Update is back
				Elem: &schema.Resource{
					Schema: map[string]*schema.Schema{
						"criterion": &schema.Schema{
							Type:     schema.TypeSet,
							Optional: true,
							Elem: &schema.Resource{
								Schema: map[string]*schema.Schema{
									"condition": &schema.Schema{
										Type:     schema.TypeSet,
										Optional: true,
										Elem: &schema.Resource{
											Schema: map[string]*schema.Schema{
												"equals": {
													Type:     schema.TypeList,
													Optional: true,
													Elem:     &schema.Schema{Type: schema.TypeString},
												},
												"greater_than": {
													Type:     schema.TypeInt,
													Optional: true,
												},
												"greater_than_or_equal": {
													Type:     schema.TypeInt,
													Optional: true,
												},
												"less_than": {
													Type:     schema.TypeInt,
													Optional: true,
												},
												"less_than_or_equal": {
													Type:     schema.TypeInt,
													Optional: true,
												},
												"not_equals": {
													Type:     schema.TypeList,
													Optional: true,
													Elem:     &schema.Schema{Type: schema.TypeString},
												},
											},
										},
									},
								},
							},
						},
					},
				},
			},
			"action": {
				Type:     schema.TypeString, // should have a new type or a validation for NOOP/ARCHIVE
				Optional: true,
				ForceNew: true, // perhaps remove here and below, when Update is back
			},
			"rank": {
				Type:     schema.TypeInt,
				Optional: true,
				ForceNew: true, // perhaps remove here and below, when Update is back
			},
		},
		Timeouts: &schema.ResourceTimeout{
			Create: schema.DefaultTimeout(60 * time.Second),
			Update: schema.DefaultTimeout(60 * time.Second),
		},
	}
}

func resourceAwsGuardDutyFilterCreate(d *schema.ResourceData, meta interface{}) error {
	conn := meta.(*AWSClient).guarddutyconn

	input := guardduty.CreateFilterInput{
		DetectorId:  aws.String(d.Get("detector_id").(string)),
		Name:        aws.String(d.Get("name").(string)),
		Description: aws.String(d.Get("description").(string)),
		Rank:        aws.Int64(int64(d.Get("rank").(int))),
	}

	// building FindingCriteria
	findingCriteria := d.Get("finding_criteria").([]interface{})[0].(map[string]interface{})
	criterion := findingCriteria["criterion"].(*schema.Set).List()[0].(map[string]interface{})
	condition := criterion["condition"].(*schema.Set).List()[0].(map[string]interface{})

	interfaceForEquals := condition["equals"].([]interface{})

	equals := make([]string, len(interfaceForEquals))
	for i, v := range interfaceForEquals {
		new[i] = string(v.(string))
	}

	interfaceForNotEquals := condition["equals"].([]interface{})

	notEquals := make([]string, len(interfaceForNotEquals))
	for i, v := range interfaceForNotEquals {
		new[i] = string(v.(string))
	}

	input.FindingCriteria = &guardduty.FindingCriteria{
		Criterion: map[string]*guardduty.Condition{ // with star or without, that't the question!
			"condition": &guardduty.Condition{
				Equals:             aws.StringSlice(equals),
				GreaterThan:        aws.Int64(int64(condition["greater_than"].(int))),
				GreaterThanOrEqual: aws.Int64(int64(condition["greater_than_or_equal"].(int))),
				LessThan:           aws.Int64(int64(condition["less_than"].(int))),
				LessThanOrEqual:    aws.Int64(int64(condition["less_than_or_equal"].(int))),
				NotEquals:          aws.StringSlice(notEquals), //aws.StringSlice([]string(condition["equals"].([]interface{}))),
			},
		},
	}
	log.Printf("[DEBUG] Creating FindingCriteria map: %#v", findingCriteria)

	// Setting the default value for `action`
	action := "NOOP"

	if len(d.Get("action").(string)) > 0 {
		action = d.Get("action").(string)
	}

	input.Action = aws.String(action)

	log.Printf("[DEBUG] Creating GuardDuty Filter: %s", input)
	output, err := conn.CreateFilter(&input)
	if err != nil {
		return fmt.Errorf("Creating GuardDuty Filter failed: %s", err.Error())
	}
	d.SetId(*output.Name)

	return resourceAwsGuardDutyFilterRead(d, meta)
}

func resourceAwsGuardDutyFilterRead(d *schema.ResourceData, meta interface{}) error {
	conn := meta.(*AWSClient).guarddutyconn
	detectorId := d.Get("detectorId").(string)
	filterName := d.Get("filterName").(string)

	input := guardduty.GetFilterInput{
		DetectorId: aws.String(detectorId),
		FilterName: aws.String(filterName),
	}

	log.Printf("[DEBUG] Reading GuardDuty Filter: %s", input)
	filter, err := conn.GetFilter(&input)

	if err != nil {
		if isAWSErr(err, guardduty.ErrCodeBadRequestException, "The request is rejected because the input detectorId is not owned by the current account.") {
			log.Printf("[WARN] GuardDuty detector %q not found, removing from state", d.Id())
			d.SetId("")
			return nil
		}
		return fmt.Errorf("Reading GuardDuty Filter '%s' failed: %s", filterName, err.Error())
	}

	d.Set("action", filter.Action)
	d.Set("description", filter.Description)
	d.Set("rank", filter.Rank)
	d.Set("name", d.Id())

	// need to find a way how to fill it interface{}
	// d.Set("account_id", filter.FindingCriteria)

	// FindingCriteria.Criterion
	// Eq
	// Gt
	// Gte
	// Lt
	// Lte
	// Neq

	return nil
}

// func resourceAwsGuardDutyFilterUpdate(d *schema.ResourceData, meta interface{}) error {
// 	conn := meta.(*AWSClient).guarddutyconn
//
// 	accountID, detectorID, err := decodeGuardDutyMemberID(d.Id())
// 	if err != nil {
// 		return err
// 	}
//
// 	if d.HasChange("invite") {
// 		if d.Get("invite").(bool) {
// 			input := &guardduty.InviteMembersInput{
// 				DetectorId:               aws.String(detectorID),
// 				AccountIds:               []*string{aws.String(accountID)},
// 				DisableEmailNotification: aws.Bool(d.Get("disable_email_notification").(bool)),
// 				Message:                  aws.String(d.Get("invitation_message").(string)),
// 			}
//
// 			log.Printf("[INFO] Inviting GuardDuty Member: %s", input)
// 			output, err := conn.InviteMembers(input)
// 			if err != nil {
// 				return fmt.Errorf("error inviting GuardDuty Member %q: %s", d.Id(), err)
// 			}
//
// 			// {"unprocessedAccounts":[{"result":"The request is rejected because the current account has already invited or is already the GuardDuty master of the given member account ID.","accountId":"067819342479"}]}
// 			if len(output.UnprocessedAccounts) > 0 {
// 				return fmt.Errorf("error inviting GuardDuty Member %q: %s", d.Id(), aws.StringValue(output.UnprocessedAccounts[0].Result))
// 			}
// 		} else {
// 			input := &guardduty.DisassociateMembersInput{
// 				AccountIds: []*string{aws.String(accountID)},
// 				DetectorId: aws.String(detectorID),
// 			}
// 			log.Printf("[INFO] Disassociating GuardDuty Member: %s", input)
// 			_, err := conn.DisassociateMembers(input)
// 			if err != nil {
// 				return fmt.Errorf("error disassociating GuardDuty Member %q: %s", d.Id(), err)
// 			}
// 		}
// 	}
//
// 	return resourceAwsGuardDutyFilterRead(d, meta)
// }

func resourceAwsGuardDutyFilterDelete(d *schema.ResourceData, meta interface{}) error {
	conn := meta.(*AWSClient).guarddutyconn

	accountID, detectorID, err := decodeGuardDutyMemberID(d.Id())
	if err != nil {
		return err
	}

	input := guardduty.DeleteMembersInput{
		AccountIds: []*string{aws.String(accountID)},
		DetectorId: aws.String(detectorID),
	}

	log.Printf("[DEBUG] Delete GuardDuty Member: %s", input)
	_, err = conn.DeleteMembers(&input)
	if err != nil {
		return fmt.Errorf("Deleting GuardDuty Member '%s' failed: %s", d.Id(), err.Error())
	}
	return nil
}
<|endoftext|>


<commit_before>package http

import (
	"context"
	"fmt"
	"net/mail"
	"time"

	"github.com/jinzhu/gorm"
	"github.com/twitchtv/twirp"
	"go.uber.org/zap"

	"github.com/oinume/lekcije/backend/context_data"
	model2 "github.com/oinume/lekcije/backend/model2c"
	"github.com/oinume/lekcije/backend/usecase"
	api_v1 "github.com/oinume/lekcije/proto-gen/go/proto/api/v1"
)

type UserService struct {
	appLogger                   *zap.Logger
	db                          *gorm.DB
	gaMeasurementUsecase        *usecase.GAMeasurement
	notificationTimeSpanUsecase *usecase.NotificationTimeSpan
	userUsecase                 *usecase.User
}

func NewUserService(
	db *gorm.DB, // TODO: Remove
	appLogger *zap.Logger,
	gaMeasurementUsecase *usecase.GAMeasurement,
	notificationTimeSpanUsecase *usecase.NotificationTimeSpan,
	userUsecase *usecase.User,
) api_v1.User {
	return &UserService{
		appLogger:                   appLogger,
		db:                          db,
		gaMeasurementUsecase:        gaMeasurementUsecase,
		notificationTimeSpanUsecase: notificationTimeSpanUsecase,
		userUsecase:                 userUsecase,
	}
}

func (s *UserService) Ping(
	_ context.Context,
	_ *api_v1.PingRequest,
) (*api_v1.PingResponse, error) {
	return &api_v1.PingResponse{}, nil
}

func (s *UserService) GetMe(
	ctx context.Context,
	_ *api_v1.GetMeRequest,
) (*api_v1.GetMeResponse, error) {
	time.Sleep(3 * time.Second)
	user, err := authenticateFromContext(ctx, s.db)
	if err != nil {
		return nil, err
	}

	timeSpans, err := s.notificationTimeSpanUsecase.FindByUserID(ctx, uint(user.ID))
	if err != nil {
		return nil, err
	}
	timeSpansProto, err := NotificationTimeSpansProto(timeSpans)
	if err != nil {
		return nil, err
	}

	return &api_v1.GetMeResponse{
		UserId:                int32(user.ID),
		Email:                 user.Email,
		NotificationTimeSpans: timeSpansProto,
		MPlan:                 nil, // TODO: Remove
	}, nil
}

func (s *UserService) GetMeEmail(
	ctx context.Context,
	_ *api_v1.GetMeEmailRequest,
) (*api_v1.GetMeEmailResponse, error) {
	user, err := authenticateFromContext(ctx, s.db)
	if err != nil {
		return nil, err
	}
	return &api_v1.GetMeEmailResponse{Email: user.Email}, nil
}

func (s *UserService) UpdateMeEmail(
	ctx context.Context,
	request *api_v1.UpdateMeEmailRequest,
) (*api_v1.UpdateMeEmailResponse, error) {
	user, err := authenticateFromContext(ctx, s.db)
	if err != nil {
		return nil, err
	}

	email := request.Email
	if email == "" || !validateEmail(email) {
		return nil, twirp.InvalidArgumentError("email", "invalid email")
	}
	duplicate, err := s.userUsecase.IsDuplicateEmail(ctx, email)
	if err != nil {
		return nil, err
	}
	if duplicate {
		return nil, twirp.InvalidArgumentError("email", "email exists")
	}

	if err := s.userUsecase.UpdateEmail(ctx, uint(user.ID), email); err != nil {
		return nil, err
	}

	go func() {
		if err := s.gaMeasurementUsecase.SendEvent(
			ctx,
			context_data.MustGAMeasurementEvent(ctx),
			model2.GAMeasurementEventCategoryUser,
			"update",
			fmt.Sprint(user.ID),
			0,
			user.ID,
		); err != nil {
			panic(err) // TODO: Better error handling
		}
	}()

	return &api_v1.UpdateMeEmailResponse{}, nil
}

func (s *UserService) UpdateMeNotificationTimeSpan(
	ctx context.Context,
	request *api_v1.UpdateMeNotificationTimeSpanRequest,
) (*api_v1.UpdateMeNotificationTimeSpanResponse, error) {
	user, err := authenticateFromContext(ctx, s.db)
	if err != nil {
		return nil, err
	}

	// TODO: validation
	userID := uint(user.ID)
	timeSpans := NotificationTimeSpansModel(request.NotificationTimeSpans, userID)
	if err := s.notificationTimeSpanUsecase.UpdateAll(ctx, userID, timeSpans); err != nil {
		return nil, err
	}

	go func() {
		if err := s.gaMeasurementUsecase.SendEvent(
			ctx,
			context_data.MustGAMeasurementEvent(ctx),
			model2.GAMeasurementEventCategoryUser,
			"updateNotificationTimeSpan",
			fmt.Sprint(user.ID),
			0,
			user.ID,
		); err != nil {
			panic(err) // TODO: Better error handling
		}
	}()

	return &api_v1.UpdateMeNotificationTimeSpanResponse{}, nil
}

func validateEmail(email string) bool {
	_, err := mail.ParseAddress(email)
	return err == nil
}
<commit_msg>Remove sleep<commit_after>package http

import (
	"context"
	"fmt"
	"net/mail"

	"github.com/jinzhu/gorm"
	"github.com/twitchtv/twirp"
	"go.uber.org/zap"

	"github.com/oinume/lekcije/backend/context_data"
	model2 "github.com/oinume/lekcije/backend/model2c"
	"github.com/oinume/lekcije/backend/usecase"
	api_v1 "github.com/oinume/lekcije/proto-gen/go/proto/api/v1"
)

type UserService struct {
	appLogger                   *zap.Logger
	db                          *gorm.DB
	gaMeasurementUsecase        *usecase.GAMeasurement
	notificationTimeSpanUsecase *usecase.NotificationTimeSpan
	userUsecase                 *usecase.User
}

func NewUserService(
	db *gorm.DB, // TODO: Remove
	appLogger *zap.Logger,
	gaMeasurementUsecase *usecase.GAMeasurement,
	notificationTimeSpanUsecase *usecase.NotificationTimeSpan,
	userUsecase *usecase.User,
) api_v1.User {
	return &UserService{
		appLogger:                   appLogger,
		db:                          db,
		gaMeasurementUsecase:        gaMeasurementUsecase,
		notificationTimeSpanUsecase: notificationTimeSpanUsecase,
		userUsecase:                 userUsecase,
	}
}

func (s *UserService) Ping(
	_ context.Context,
	_ *api_v1.PingRequest,
) (*api_v1.PingResponse, error) {
	return &api_v1.PingResponse{}, nil
}

func (s *UserService) GetMe(
	ctx context.Context,
	_ *api_v1.GetMeRequest,
) (*api_v1.GetMeResponse, error) {
	user, err := authenticateFromContext(ctx, s.db)
	if err != nil {
		return nil, err
	}

	timeSpans, err := s.notificationTimeSpanUsecase.FindByUserID(ctx, uint(user.ID))
	if err != nil {
		return nil, err
	}
	timeSpansProto, err := NotificationTimeSpansProto(timeSpans)
	if err != nil {
		return nil, err
	}

	return &api_v1.GetMeResponse{
		UserId:                int32(user.ID),
		Email:                 user.Email,
		NotificationTimeSpans: timeSpansProto,
		MPlan:                 nil, // TODO: Remove
	}, nil
}

func (s *UserService) GetMeEmail(
	ctx context.Context,
	_ *api_v1.GetMeEmailRequest,
) (*api_v1.GetMeEmailResponse, error) {
	user, err := authenticateFromContext(ctx, s.db)
	if err != nil {
		return nil, err
	}
	return &api_v1.GetMeEmailResponse{Email: user.Email}, nil
}

func (s *UserService) UpdateMeEmail(
	ctx context.Context,
	request *api_v1.UpdateMeEmailRequest,
) (*api_v1.UpdateMeEmailResponse, error) {
	user, err := authenticateFromContext(ctx, s.db)
	if err != nil {
		return nil, err
	}

	email := request.Email
	if email == "" || !validateEmail(email) {
		return nil, twirp.InvalidArgumentError("email", "invalid email")
	}
	duplicate, err := s.userUsecase.IsDuplicateEmail(ctx, email)
	if err != nil {
		return nil, err
	}
	if duplicate {
		return nil, twirp.InvalidArgumentError("email", "email exists")
	}

	if err := s.userUsecase.UpdateEmail(ctx, uint(user.ID), email); err != nil {
		return nil, err
	}

	go func() {
		if err := s.gaMeasurementUsecase.SendEvent(
			ctx,
			context_data.MustGAMeasurementEvent(ctx),
			model2.GAMeasurementEventCategoryUser,
			"update",
			fmt.Sprint(user.ID),
			0,
			user.ID,
		); err != nil {
			panic(err) // TODO: Better error handling
		}
	}()

	return &api_v1.UpdateMeEmailResponse{}, nil
}

func (s *UserService) UpdateMeNotificationTimeSpan(
	ctx context.Context,
	request *api_v1.UpdateMeNotificationTimeSpanRequest,
) (*api_v1.UpdateMeNotificationTimeSpanResponse, error) {
	user, err := authenticateFromContext(ctx, s.db)
	if err != nil {
		return nil, err
	}

	// TODO: validation
	userID := uint(user.ID)
	timeSpans := NotificationTimeSpansModel(request.NotificationTimeSpans, userID)
	if err := s.notificationTimeSpanUsecase.UpdateAll(ctx, userID, timeSpans); err != nil {
		return nil, err
	}

	go func() {
		if err := s.gaMeasurementUsecase.SendEvent(
			ctx,
			context_data.MustGAMeasurementEvent(ctx),
			model2.GAMeasurementEventCategoryUser,
			"updateNotificationTimeSpan",
			fmt.Sprint(user.ID),
			0,
			user.ID,
		); err != nil {
			panic(err) // TODO: Better error handling
		}
	}()

	return &api_v1.UpdateMeNotificationTimeSpanResponse{}, nil
}

func validateEmail(email string) bool {
	_, err := mail.ParseAddress(email)
	return err == nil
}
<|endoftext|>


<commit_before>package facebook

import (
	"encoding/json"
	"fmt"
	"github.com/chaosphere2112/go-jsonUtil"
	"net/http"
	"strconv"
	"strings"
)

type AccessToken struct {
	Token  string
	Expiry int64
}

func readHttpBody(response *http.Response) string {

	fmt.Println("Reading body")

	bodyBuffer := make([]byte, 1000)
	var str string

	count, err := response.Body.Read(bodyBuffer)

	for ; count > 0; count, err = response.Body.Read(bodyBuffer) {

		if err != nil {

		}

		str += string(bodyBuffer[:count])
	}

	return str

}

//Converts a code to an Auth_Token
func GetAccessToken(client_id string, code string, secret string, callbackUri string) AccessToken {
	fmt.Println("GetAccessToken")
	//https://graph.facebook.com/oauth/access_token?client_id=YOUR_APP_ID&redirect_uri=YOUR_REDIRECT_URI&client_secret=YOUR_APP_SECRET&code=CODE_GENERATED_BY_FACEBOOK
	response, err := http.Get("https://graph.facebook.com/oauth/access_token?client_id=" +
		client_id + "&redirect_uri=" + callbackUri +
		"&client_secret=" + secret + "&code=" + code)

	if err == nil {

		auth := readHttpBody(response)

		var token AccessToken

		tokenArr := strings.Split(auth, "&")

		token.Token = strings.Split(tokenArr[0], "=")[1]
		expireInt, err := strconv.Atoi(strings.Split(tokenArr[1], "=")[1])

		if err == nil {
			token.Expiry = int64(expireInt)
		}

		return token
	}

	var token AccessToken

	return token
}

func GetMe(token AccessToken) string {
	fmt.Println("Getting me")
	response, err := GetUncachedResponse("https://graph.facebook.com/me?access_token=" + token.Token)

	if err == nil {

		var jsonBlob interface{}

		responseBody := readHttpBody(response)

		if responseBody != "" {
			err = json.Unmarshal([]byte(responseBody), &jsonBlob)

			if err == nil {
				jsonObj := jsonBlob.(map[string]interface{})
				return jsonObj["id"].(string)
			}
		}
		return err.Error()
	}

	return err.Error()
}

func GetUncachedResponse(uri string) (*http.Response, error) {
	fmt.Println("Uncached response GET")
	request, err := http.NewRequest("GET", uri, nil)

	if err == nil {
		request.Header.Add("Cache-Control", "no-cache")

		client := new(http.Client)

		return client.Do(request)
	}

	if err != nil {
	}
	return nil, err

}

type GroupEvent struct {
	Data []struct {
		Name      string
		StartTime string `json:"start_time"`
		Timezone  string
		Location  string
		Id        string
	}
	Paging struct {
		Cursors struct {
			After  string
			Before string
		}
	}
}

func GetGroupEvents(token *AccessToken, groupId string) GroupEvent {

	group_events := GroupEvent{}
	response, err := GetUncachedResponse("https://graph.facebook.com/" + groupId + "/events?access_token=" + token.Token)

	if err == nil && response != nil {

		body := readHttpBody(response)

		if body != "" {
			b := []byte(body)
			json.Unmarshal(b, &group_events)
			return group_events
		}

	}

	return GroupEvent{}
}

func GetGroupEventIds(group_events GroupEvent) []string {

	event_ids := make([]string, len(group_events.Data))
	for i := 0; i < len(group_events.Data); i++ {
		event_ids[i] = group_events.Data[i].Id
	}

	return event_ids
}

type Event struct {
	Id    string
	Owner struct {
		Name    string
		Id      string
	}
	Name            string
	Description     string
	StartTime		string
	TimeZone		string
	IsDateOnly		bool
	Location		string
	Venue struct {
		Latitude	float64
		Longitude	float64
		City		string
		Country	    string
		Id			string
		Street		string
		Zip		    string
	}
	UpdatedTime	    string
}

func GetEvent(token *AccessToken, eventId string) Event {

	event := Event{}
	response, err := GetUncachedResponse("https://graph.facebook.com/" + eventId + "?access_token=" + token.Token)

	if err == nil && response != nil {

		body := readHttpBody(response)

		if body != "" {
			b := []byte(body)
			json.Unmarshal(b, &event)
			return event
		}

	}

	return Event{}
}

func getPhotoSource(token *AccessToken, photoId string) string {
	fmt.Println("Getting photo source")
	response, err := GetUncachedResponse("https://graph.facebook.com/" + photoId + "?access_token=" + token.Token + "&fields=source")

	if err == nil && response != nil {

		body := readHttpBody(response)

		if body != "" {

			object, err := jsonUtil.JsonFromString(body)

			if err == nil {

				source, err := object.String("source")

				if err == nil {

					return source

				}

			}

		}

	}

	return ""

}

func GetAlbumPhotos(token *AccessToken, albumId string) []string {
	response, err := GetUncachedResponse("https://graph.facebook.com/" + albumId + "/photos?access_token=" + token.Token + "&fields=images&limit=1000")
	fmt.Println("Getting album photos")
	if err == nil && response != nil {

		body := readHttpBody(response)

		if body != "" {

			object, err := jsonUtil.JsonFromString(body)
			if err == nil {
				//Get the "data" array
				var data jsonUtil.JsonArray
				returned := make([]string, 0, 1)

				data, err = object.Array("data")

				if err == nil {
					fmt.Println(len(data))
					for dataIndex := 0; dataIndex < len(data); dataIndex++ {
						var anonObj jsonUtil.JsonObject
						anonObj, err = data.Object(dataIndex)
						if err == nil {

							//For each object in data, iterate over the "images" array
							var imagesArray jsonUtil.JsonArray

							imagesArray, err = anonObj.Array("images")
							if err == nil {
								//Get the source for each image
								var image jsonUtil.JsonObject
								image, err = imagesArray.Object(1)

								if err == nil {
									var photoSource string
									photoSource, err = image.String("source")

									if err == nil {
										returned = append(returned, photoSource)
									}
								}
							}

						}

					}

				}
				return returned
			}
		}

	}

	if err != nil {
		returned := make([]string, 1)
		returned[0] = err.Error()
		return returned
	}

	return make([]string, 0)
}

func GetPhotos(token *AccessToken) []string {
	fmt.Println("Getting photos")
	response, err := GetUncachedResponse("https://graph.facebook.com/me/albums?access_token=" + token.Token + "&fields=id")

	if err == nil && response != nil {
		var jsonBlob interface{}

		responseBody := readHttpBody(response)

		if responseBody != "" {
			err = json.Unmarshal([]byte(responseBody), &jsonBlob)

			if err == nil {
				jsonObj := jsonBlob.(map[string]interface{})

				dataArray := jsonObj["data"].([]interface{})

				first := dataArray[0].(map[string]interface{})

				firstAlbumId := first["id"].(string)

				//Feed albumId into GetAlbumPhotos
				return GetAlbumPhotos(token, firstAlbumId)

			}
		}
	}
	fmt.Println("Failed to GetPhotos because " + err.Error())
	return nil
}
<commit_msg>Handle StartTime, EndTime and UpdatedTime<commit_after>package facebook

import (
	"encoding/json"
	"fmt"
	"github.com/chaosphere2112/go-jsonUtil"
	"net/http"
	"strconv"
	"strings"
)

type AccessToken struct {
	Token  string
	Expiry int64
}

func readHttpBody(response *http.Response) string {

	fmt.Println("Reading body")

	bodyBuffer := make([]byte, 1000)
	var str string

	count, err := response.Body.Read(bodyBuffer)

	for ; count > 0; count, err = response.Body.Read(bodyBuffer) {

		if err != nil {

		}

		str += string(bodyBuffer[:count])
	}

	return str

}

//Converts a code to an Auth_Token
func GetAccessToken(client_id string, code string, secret string, callbackUri string) AccessToken {
	fmt.Println("GetAccessToken")
	//https://graph.facebook.com/oauth/access_token?client_id=YOUR_APP_ID&redirect_uri=YOUR_REDIRECT_URI&client_secret=YOUR_APP_SECRET&code=CODE_GENERATED_BY_FACEBOOK
	response, err := http.Get("https://graph.facebook.com/oauth/access_token?client_id=" +
		client_id + "&redirect_uri=" + callbackUri +
		"&client_secret=" + secret + "&code=" + code)

	if err == nil {

		auth := readHttpBody(response)

		var token AccessToken

		tokenArr := strings.Split(auth, "&")

		token.Token = strings.Split(tokenArr[0], "=")[1]
		expireInt, err := strconv.Atoi(strings.Split(tokenArr[1], "=")[1])

		if err == nil {
			token.Expiry = int64(expireInt)
		}

		return token
	}

	var token AccessToken

	return token
}

func GetMe(token AccessToken) string {
	fmt.Println("Getting me")
	response, err := GetUncachedResponse("https://graph.facebook.com/me?access_token=" + token.Token)

	if err == nil {

		var jsonBlob interface{}

		responseBody := readHttpBody(response)

		if responseBody != "" {
			err = json.Unmarshal([]byte(responseBody), &jsonBlob)

			if err == nil {
				jsonObj := jsonBlob.(map[string]interface{})
				return jsonObj["id"].(string)
			}
		}
		return err.Error()
	}

	return err.Error()
}

func GetUncachedResponse(uri string) (*http.Response, error) {
	fmt.Println("Uncached response GET")
	request, err := http.NewRequest("GET", uri, nil)

	if err == nil {
		request.Header.Add("Cache-Control", "no-cache")

		client := new(http.Client)

		return client.Do(request)
	}

	if err != nil {
	}
	return nil, err

}

type GroupEvent struct {
	Data []struct {
		Name      string
		StartTime string `json:"start_time"`
		Timezone  string
		Location  string
		Id        string
	}
	Paging struct {
		Cursors struct {
			After  string
			Before string
		}
	}
}

func GetGroupEvents(token *AccessToken, groupId string) GroupEvent {

	group_events := GroupEvent{}
	response, err := GetUncachedResponse("https://graph.facebook.com/" + groupId + "/events?access_token=" + token.Token)

	if err == nil && response != nil {

		body := readHttpBody(response)

		if body != "" {
			b := []byte(body)
			json.Unmarshal(b, &group_events)
			return group_events
		}

	}

	return GroupEvent{}
}

func GetGroupEventIds(group_events GroupEvent) []string {

	event_ids := make([]string, len(group_events.Data))
	for i := 0; i < len(group_events.Data); i++ {
		event_ids[i] = group_events.Data[i].Id
	}

	return event_ids
}

type Event struct {
	Id    string
	Owner struct {
		Name    string
		Id      string
	}
	Name            string
	Description     string
	StartTime		string    `bson:"start_time" json:"start_time"`
	EndTime         string    `bson:"end_time" json:"end_time"`
	TimeZone		string
	IsDateOnly		bool
	Location		string
	Venue struct {
		Latitude	float64
		Longitude	float64
		City		string
		Country	    string
		Id			string
		Street		string
		Zip		    string
	}
	UpdatedTime	    string     `bson:"updated_time" json:"updated_time"`
}

func GetEvent(token *AccessToken, eventId string) Event {

	event := Event{}
	response, err := GetUncachedResponse("https://graph.facebook.com/" + eventId + "?access_token=" + token.Token)

	if err == nil && response != nil {

		body := readHttpBody(response)
		fmt.Println(111111, body)

		if body != "" {
			b := []byte(body)
			json.Unmarshal(b, &event)
			fmt.Println(222222, event)
			return event
		}

	}

	return Event{}
}

func getPhotoSource(token *AccessToken, photoId string) string {
	fmt.Println("Getting photo source")
	response, err := GetUncachedResponse("https://graph.facebook.com/" + photoId + "?access_token=" + token.Token + "&fields=source")

	if err == nil && response != nil {

		body := readHttpBody(response)

		if body != "" {

			object, err := jsonUtil.JsonFromString(body)

			if err == nil {

				source, err := object.String("source")

				if err == nil {

					return source

				}

			}

		}

	}

	return ""

}

func GetAlbumPhotos(token *AccessToken, albumId string) []string {
	response, err := GetUncachedResponse("https://graph.facebook.com/" + albumId + "/photos?access_token=" + token.Token + "&fields=images&limit=1000")
	fmt.Println("Getting album photos")
	if err == nil && response != nil {

		body := readHttpBody(response)

		if body != "" {

			object, err := jsonUtil.JsonFromString(body)
			if err == nil {
				//Get the "data" array
				var data jsonUtil.JsonArray
				returned := make([]string, 0, 1)

				data, err = object.Array("data")

				if err == nil {
					fmt.Println(len(data))
					for dataIndex := 0; dataIndex < len(data); dataIndex++ {
						var anonObj jsonUtil.JsonObject
						anonObj, err = data.Object(dataIndex)
						if err == nil {

							//For each object in data, iterate over the "images" array
							var imagesArray jsonUtil.JsonArray

							imagesArray, err = anonObj.Array("images")
							if err == nil {
								//Get the source for each image
								var image jsonUtil.JsonObject
								image, err = imagesArray.Object(1)

								if err == nil {
									var photoSource string
									photoSource, err = image.String("source")

									if err == nil {
										returned = append(returned, photoSource)
									}
								}
							}

						}

					}

				}
				return returned
			}
		}

	}

	if err != nil {
		returned := make([]string, 1)
		returned[0] = err.Error()
		return returned
	}

	return make([]string, 0)
}

func GetPhotos(token *AccessToken) []string {
	fmt.Println("Getting photos")
	response, err := GetUncachedResponse("https://graph.facebook.com/me/albums?access_token=" + token.Token + "&fields=id")

	if err == nil && response != nil {
		var jsonBlob interface{}

		responseBody := readHttpBody(response)

		if responseBody != "" {
			err = json.Unmarshal([]byte(responseBody), &jsonBlob)

			if err == nil {
				jsonObj := jsonBlob.(map[string]interface{})

				dataArray := jsonObj["data"].([]interface{})

				first := dataArray[0].(map[string]interface{})

				firstAlbumId := first["id"].(string)

				//Feed albumId into GetAlbumPhotos
				return GetAlbumPhotos(token, firstAlbumId)

			}
		}
	}
	fmt.Println("Failed to GetPhotos because " + err.Error())
	return nil
}
<|endoftext|>


<commit_before>package authorizer

import (
	"context"

	"github.com/influxdata/influxdb"
)

var _ influxdb.ScraperTargetStoreService = (*ScraperTargetStoreService)(nil)

// ScraperTargetStoreService wraps a influxdb.ScraperTargetStoreService and authorizes actions
// against it appropriately.
type ScraperTargetStoreService struct {
	s influxdb.ScraperTargetStoreService
}

// NewScraperTargetStoreService constructs an instance of an authorizing scraper target store serivce.
func NewScraperTargetStoreService(s influxdb.ScraperTargetStoreService) *ScraperTargetStoreService {
	return &ScraperTargetStoreService{
		s: s,
	}
}

func newScraperPermission(a influxdb.Action, orgID, id influxdb.ID) (*influxdb.Permission, error) {
	return influxdb.NewPermissionAtID(id, a, influxdb.ScraperResourceType, orgID)
}

func authorizeReadScraper(ctx context.Context, orgID, id influxdb.ID) error {
	p, err := newScraperPermission(influxdb.ReadAction, orgID, id)
	if err != nil {
		return err
	}

	if err := IsAllowed(ctx, *p); err != nil {
		return err
	}

	return nil
}

func authorizeWriteScraper(ctx context.Context, orgID, id influxdb.ID) error {
	p, err := newScraperPermission(influxdb.WriteAction, orgID, id)
	if err != nil {
		return err
	}

	if err := IsAllowed(ctx, *p); err != nil {
		return err
	}

	return nil
}

// GetTargetByID checks to see if the authorizer on context has read access to the id provided.
func (s *ScraperTargetStoreService) GetTargetByID(ctx context.Context, id influxdb.ID) (*influxdb.ScraperTarget, error) {
	st, err := s.s.GetTargetByID(ctx, id)
	if err != nil {
		return nil, err
	}

	if err := authorizeReadScraper(ctx, st.OrgID, id); err != nil {
		return nil, err
	}

	return st, nil
}

// ListTargets retrieves all scraper targets that match the provided filter and then filters the list down to only the resources that are authorized.
func (s *ScraperTargetStoreService) ListTargets(ctx context.Context) ([]influxdb.ScraperTarget, error) {
	// TODO: we'll likely want to push this operation into the database eventually since fetching the whole list of data
	// will likely be expensive.
	ss, err := s.s.ListTargets(ctx)
	if err != nil {
		return nil, err
	}

	// This filters without allocating
	// https://github.com/golang/go/wiki/SliceTricks#filtering-without-allocating
	scrapers := ss[:0]
	for _, st := range ss {
		err := authorizeReadScraper(ctx, st.OrgID, st.ID)
		if err != nil && influxdb.ErrorCode(err) != influxdb.EUnauthorized {
			return nil, err
		}

		if influxdb.ErrorCode(err) == influxdb.EUnauthorized {
			continue
		}

		scrapers = append(scrapers, st)
	}

	return scrapers, nil
}

// AddTarget checks to see if the authorizer on context has write access to the global scraper target resource.
func (s *ScraperTargetStoreService) AddTarget(ctx context.Context, st *influxdb.ScraperTarget) error {
	p, err := influxdb.NewPermission(influxdb.WriteAction, influxdb.ScraperResourceType, st.OrgID)
	if err != nil {
		return err
	}

	if err := IsAllowed(ctx, *p); err != nil {
		return err
	}

	return s.s.AddTarget(ctx, st)
}

// UpdateTarget checks to see if the authorizer on context has write access to the scraper target provided.
func (s *ScraperTargetStoreService) UpdateTarget(ctx context.Context, upd *influxdb.ScraperTarget) (*influxdb.ScraperTarget, error) {
	st, err := s.GetTargetByID(ctx, upd.ID)
	if err != nil {
		return nil, err
	}

	if err := authorizeWriteScraper(ctx, st.OrgID, upd.ID); err != nil {
		return nil, err
	}

	return s.s.UpdateTarget(ctx, upd)
}

// RemoveTarget checks to see if the authorizer on context has write access to the scraper target provided.
func (s *ScraperTargetStoreService) RemoveTarget(ctx context.Context, id influxdb.ID) error {
	st, err := s.GetTargetByID(ctx, id)
	if err != nil {
		return err
	}

	if err := authorizeWriteScraper(ctx, st.OrgID, id); err != nil {
		return err
	}

	return s.s.RemoveTarget(ctx, id)
}
<commit_msg>fix(authorizer): do not nead read action while writing to scrapers<commit_after>package authorizer

import (
	"context"

	"github.com/influxdata/influxdb"
)

var _ influxdb.ScraperTargetStoreService = (*ScraperTargetStoreService)(nil)

// ScraperTargetStoreService wraps a influxdb.ScraperTargetStoreService and authorizes actions
// against it appropriately.
type ScraperTargetStoreService struct {
	s influxdb.ScraperTargetStoreService
}

// NewScraperTargetStoreService constructs an instance of an authorizing scraper target store serivce.
func NewScraperTargetStoreService(s influxdb.ScraperTargetStoreService) *ScraperTargetStoreService {
	return &ScraperTargetStoreService{
		s: s,
	}
}

func newScraperPermission(a influxdb.Action, orgID, id influxdb.ID) (*influxdb.Permission, error) {
	return influxdb.NewPermissionAtID(id, a, influxdb.ScraperResourceType, orgID)
}

func authorizeReadScraper(ctx context.Context, orgID, id influxdb.ID) error {
	p, err := newScraperPermission(influxdb.ReadAction, orgID, id)
	if err != nil {
		return err
	}

	if err := IsAllowed(ctx, *p); err != nil {
		return err
	}

	return nil
}

func authorizeWriteScraper(ctx context.Context, orgID, id influxdb.ID) error {
	p, err := newScraperPermission(influxdb.WriteAction, orgID, id)
	if err != nil {
		return err
	}

	if err := IsAllowed(ctx, *p); err != nil {
		return err
	}

	return nil
}

// GetTargetByID checks to see if the authorizer on context has read access to the id provided.
func (s *ScraperTargetStoreService) GetTargetByID(ctx context.Context, id influxdb.ID) (*influxdb.ScraperTarget, error) {
	st, err := s.s.GetTargetByID(ctx, id)
	if err != nil {
		return nil, err
	}

	if err := authorizeReadScraper(ctx, st.OrgID, id); err != nil {
		return nil, err
	}

	return st, nil
}

// ListTargets retrieves all scraper targets that match the provided filter and then filters the list down to only the resources that are authorized.
func (s *ScraperTargetStoreService) ListTargets(ctx context.Context) ([]influxdb.ScraperTarget, error) {
	// TODO: we'll likely want to push this operation into the database eventually since fetching the whole list of data
	// will likely be expensive.
	ss, err := s.s.ListTargets(ctx)
	if err != nil {
		return nil, err
	}

	// This filters without allocating
	// https://github.com/golang/go/wiki/SliceTricks#filtering-without-allocating
	scrapers := ss[:0]
	for _, st := range ss {
		err := authorizeReadScraper(ctx, st.OrgID, st.ID)
		if err != nil && influxdb.ErrorCode(err) != influxdb.EUnauthorized {
			return nil, err
		}

		if influxdb.ErrorCode(err) == influxdb.EUnauthorized {
			continue
		}

		scrapers = append(scrapers, st)
	}

	return scrapers, nil
}

// AddTarget checks to see if the authorizer on context has write access to the global scraper target resource.
func (s *ScraperTargetStoreService) AddTarget(ctx context.Context, st *influxdb.ScraperTarget) error {
	p, err := influxdb.NewPermission(influxdb.WriteAction, influxdb.ScraperResourceType, st.OrgID)
	if err != nil {
		return err
	}

	if err := IsAllowed(ctx, *p); err != nil {
		return err
	}

	return s.s.AddTarget(ctx, st)
}

// UpdateTarget checks to see if the authorizer on context has write access to the scraper target provided.
func (s *ScraperTargetStoreService) UpdateTarget(ctx context.Context, upd *influxdb.ScraperTarget) (*influxdb.ScraperTarget, error) {
	st, err := s.s.GetTargetByID(ctx, upd.ID)
	if err != nil {
		return nil, err
	}

	if err := authorizeWriteScraper(ctx, st.OrgID, upd.ID); err != nil {
		return nil, err
	}

	return s.s.UpdateTarget(ctx, upd)
}

// RemoveTarget checks to see if the authorizer on context has write access to the scraper target provided.
func (s *ScraperTargetStoreService) RemoveTarget(ctx context.Context, id influxdb.ID) error {
	st, err := s.s.GetTargetByID(ctx, id)
	if err != nil {
		return err
	}

	if err := authorizeWriteScraper(ctx, st.OrgID, id); err != nil {
		return err
	}

	return s.s.RemoveTarget(ctx, id)
}
<|endoftext|>


<commit_before>package file

import (
	"os"
	"path"
	"runtime"
	"strings"
)

type Context map[string]string

func (c Context) Source() string {
	source := "/source/"
	if v, ok := c["DAPPER_SOURCE"]; ok && v != "" {
		source = v
	}

	if !strings.HasSuffix(source, "/") {
		source += "/"
	}

	return source
}

func (c Context) Cp() string {
	if v, ok := c["DAPPER_CP"]; ok && v != "" {
		return v
	}
	return "."
}

func (c Context) Socket() bool {
	if v, ok := c["DAPPER_DOCKER_SOCKET"]; ok && v != "" {
		return "true" == v
	}
	return false
}

func (c Context) Mode(mode string) string {
	if mode == "cp" {
		return "cp"
	} else if mode == "bind" {
		return "bind"
	}

	host := os.Getenv("DOCKER_HOST")
	if strings.HasPrefix(host, "unix://") || (host == "" && runtime.GOOS != "window") {
		return "bind"
	}

	return "cp"
}

func (c Context) Env() []string {
	val := []string{}
	if v, ok := c["DAPPER_ENV"]; ok && v != "" {
		val = strings.Split(v, " ")
	}

	ret := []string{}

	for _, i := range val {
		i = strings.TrimSpace(i)
		if i != "" {
			ret = append(ret, i)
		}
	}

	return ret
}

func (c Context) Shell() string {
	shell := os.Getenv("SHELL")

	if v, ok := c["SHELL"]; ok && v != "" {
		return v
	}

	if shell == "" {
		return "/bin/bash"
	}

	return shell
}

func (c Context) Output() []string {
	source := c.Source()
	if v, ok := c["DAPPER_OUTPUT"]; ok {
		ret := []string{}
		for _, i := range strings.Split(v, " ") {
			i = strings.TrimSpace(i)
			if i != "" {
				if strings.HasPrefix(i, "/") {
					ret = append(ret, i)
				} else {
					ret = append(ret, path.Join(source, i))
				}
			}
		}
		return ret
	}
	return []string{}
}

func (c Context) RunArgs() []string {
	if v, ok := c["DAPPER_RUN_ARGS"]; ok {
		ret := []string{}
		for _, i := range strings.Split(v, " ") {
			i = strings.TrimSpace(i)
			if i != "" {
				ret = append(ret, i)
			}
		}
		return ret
	}
	return []string{}
}
<commit_msg>Use cp mode by default<commit_after>package file

import (
	"os"
	"path"
	"strings"
)

type Context map[string]string

func (c Context) Source() string {
	source := "/source/"
	if v, ok := c["DAPPER_SOURCE"]; ok && v != "" {
		source = v
	}

	if !strings.HasSuffix(source, "/") {
		source += "/"
	}

	return source
}

func (c Context) Cp() string {
	if v, ok := c["DAPPER_CP"]; ok && v != "" {
		return v
	}
	return "."
}

func (c Context) Socket() bool {
	if v, ok := c["DAPPER_DOCKER_SOCKET"]; ok && v != "" {
		return "true" == v
	}
	return false
}

func (c Context) Mode(mode string) string {
	switch mode {
	case "cp", "bind":
		return mode
	}
	return "cp"
}

func (c Context) Env() []string {
	val := []string{}
	if v, ok := c["DAPPER_ENV"]; ok && v != "" {
		val = strings.Split(v, " ")
	}

	ret := []string{}

	for _, i := range val {
		i = strings.TrimSpace(i)
		if i != "" {
			ret = append(ret, i)
		}
	}

	return ret
}

func (c Context) Shell() string {
	shell := os.Getenv("SHELL")

	if v, ok := c["SHELL"]; ok && v != "" {
		return v
	}

	if shell == "" {
		return "/bin/bash"
	}

	return shell
}

func (c Context) Output() []string {
	source := c.Source()
	if v, ok := c["DAPPER_OUTPUT"]; ok {
		ret := []string{}
		for _, i := range strings.Split(v, " ") {
			i = strings.TrimSpace(i)
			if i != "" {
				if strings.HasPrefix(i, "/") {
					ret = append(ret, i)
				} else {
					ret = append(ret, path.Join(source, i))
				}
			}
		}
		return ret
	}
	return []string{}
}

func (c Context) RunArgs() []string {
	if v, ok := c["DAPPER_RUN_ARGS"]; ok {
		ret := []string{}
		for _, i := range strings.Split(v, " ") {
			i = strings.TrimSpace(i)
			if i != "" {
				ret = append(ret, i)
			}
		}
		return ret
	}
	return []string{}
}
<|endoftext|>


<commit_before>package etcd

import (
	"context"
	"crypto/md5"
	"encoding/json"
	"fmt"
	"sync"
	"time"

	etcdv3 "github.com/coreos/etcd/clientv3"
	etcdv3sync "github.com/coreos/etcd/clientv3/concurrency"
	"github.com/hashicorp/go-multierror"
	"github.com/hashicorp/terraform/state"
	"github.com/hashicorp/terraform/state/remote"
)

const (
	lockAcquireTimeout = 2 * time.Second
	lockInfoSuffix     = ".lockinfo"
	lockSuffix         = ".lock"
)

// RemoteClient is a remote client that will store data in etcd.
type RemoteClient struct {
	Client *etcdv3.Client
	DoLock bool
	Key    string

	etcdMutex   *etcdv3sync.Mutex
	etcdSession *etcdv3sync.Session
	info        *state.LockInfo
	mu          sync.Mutex
	modRevision int64
}

func (c *RemoteClient) Get() (*remote.Payload, error) {
	c.mu.Lock()
	defer c.mu.Unlock()

	res, err := c.Client.KV.Get(context.TODO(), c.Key)
	if err != nil {
		return nil, err
	}
	if res.Count == 0 {
		return nil, nil
	}
	if res.Count >= 2 {
		return nil, fmt.Errorf("Expected a single result but got %d.", res.Count)
	}

	c.modRevision = res.Kvs[0].ModRevision

	payload := res.Kvs[0].Value
	md5 := md5.Sum(payload)

	return &remote.Payload{
		Data: payload,
		MD5:  md5[:],
	}, nil
}

func (c *RemoteClient) Put(data []byte) error {
	c.mu.Lock()
	defer c.mu.Unlock()

	res, err := etcdv3.NewKV(c.Client).Txn(context.TODO()).If(
		etcdv3.Compare(etcdv3.ModRevision(c.Key), "=", c.modRevision),
	).Then(
		etcdv3.OpPut(c.Key, string(data)),
		etcdv3.OpGet(c.Key),
	).Commit()

	if err != nil {
		return err
	}
	if !res.Succeeded {
		return fmt.Errorf("The transaction did not succeed.")
	}
	if len(res.Responses) != 2 {
		return fmt.Errorf("Expected two responses but got %d.", len(res.Responses))
	}

	c.modRevision = res.Responses[1].GetResponseRange().Kvs[0].ModRevision
	return nil
}

func (c *RemoteClient) Delete() error {
	c.mu.Lock()
	defer c.mu.Unlock()

	_, err := c.Client.KV.Delete(context.TODO(), c.Key)
	return err
}

func (c *RemoteClient) Lock(info *state.LockInfo) (string, error) {
	c.mu.Lock()
	defer c.mu.Unlock()

	if !c.DoLock {
		return "", nil
	}

	c.info = info
	return c.lock()
}

func (c *RemoteClient) Unlock(id string) error {
	c.mu.Lock()
	defer c.mu.Unlock()

	if !c.DoLock {
		return nil
	}

	return c.unlock(id)
}

func (c *RemoteClient) deleteLockInfo(info *state.LockInfo) error {
	res, err := c.Client.KV.Delete(context.TODO(), c.Key+lockInfoSuffix)
	if err != nil {
		return err
	}
	if res.Deleted == 0 {
		return fmt.Errorf("No keys deleted for %s when deleting lock info.", c.Key+lockInfoSuffix)
	}
	return nil
}

func (c *RemoteClient) getLockInfo() (*state.LockInfo, error) {
	res, err := c.Client.KV.Get(context.TODO(), c.Key+lockInfoSuffix)
	if err != nil {
		return nil, err
	}
	if res.Count == 0 {
		return nil, nil
	}

	li := &state.LockInfo{}
	err = json.Unmarshal(res.Kvs[0].Value, li)
	if err != nil {
		return nil, fmt.Errorf("Error unmarshaling lock info: %s.", err)
	}

	return li, nil
}

func (c *RemoteClient) putLockInfo(info *state.LockInfo) error {
	c.info.Path = c.Key
	c.info.Created = time.Now().UTC()

	_, err := c.Client.KV.Put(context.TODO(), c.Key+lockInfoSuffix, string(c.info.Marshal()))
	return err
}

func (c *RemoteClient) lock() (string, error) {
	session, err := etcdv3sync.NewSession(c.Client)
	if err != nil {
		return "", nil
	}

	ctx, cancel := context.WithTimeout(context.TODO(), lockAcquireTimeout)
	defer cancel()

	mutex := etcdv3sync.NewMutex(session, c.Key+lockSuffix)
	if err1 := mutex.Lock(ctx); err1 != nil {
		lockInfo, err2 := c.getLockInfo()
		if err2 != nil {
			return "", &state.LockError{Err: err2}
		}
		return "", &state.LockError{Info: lockInfo, Err: err1}
	}

	c.etcdMutex = mutex
	c.etcdSession = session

	err = c.putLockInfo(c.info)
	if err != nil {
		if unlockErr := c.unlock(c.info.ID); unlockErr != nil {
			err = multierror.Append(err, unlockErr)
		}
		return "", err
	}

	return c.info.ID, nil
}

func (c *RemoteClient) unlock(id string) error {
	if c.etcdMutex == nil {
		return nil
	}

	var errs error

	if err := c.deleteLockInfo(c.info); err != nil {
		errs = multierror.Append(errs, err)
	}
	if err := c.etcdMutex.Unlock(context.TODO()); err != nil {
		errs = multierror.Append(errs, err)
	}
	if err := c.etcdSession.Close(); err != nil {
		errs = multierror.Append(errs, err)
	}

	c.etcdMutex = nil
	c.etcdSession = nil

	return errs
}
<commit_msg>Make sure we don't relock.<commit_after>package etcd

import (
	"context"
	"crypto/md5"
	"encoding/json"
	"fmt"
	"sync"
	"time"

	etcdv3 "github.com/coreos/etcd/clientv3"
	etcdv3sync "github.com/coreos/etcd/clientv3/concurrency"
	"github.com/hashicorp/go-multierror"
	"github.com/hashicorp/terraform/state"
	"github.com/hashicorp/terraform/state/remote"
)

const (
	lockAcquireTimeout = 2 * time.Second
	lockInfoSuffix     = ".lockinfo"
	lockSuffix         = ".lock"
)

// RemoteClient is a remote client that will store data in etcd.
type RemoteClient struct {
	Client *etcdv3.Client
	DoLock bool
	Key    string

	etcdMutex   *etcdv3sync.Mutex
	etcdSession *etcdv3sync.Session
	info        *state.LockInfo
	mu          sync.Mutex
	modRevision int64
}

func (c *RemoteClient) Get() (*remote.Payload, error) {
	c.mu.Lock()
	defer c.mu.Unlock()

	res, err := c.Client.KV.Get(context.TODO(), c.Key)
	if err != nil {
		return nil, err
	}
	if res.Count == 0 {
		return nil, nil
	}
	if res.Count >= 2 {
		return nil, fmt.Errorf("Expected a single result but got %d.", res.Count)
	}

	c.modRevision = res.Kvs[0].ModRevision

	payload := res.Kvs[0].Value
	md5 := md5.Sum(payload)

	return &remote.Payload{
		Data: payload,
		MD5:  md5[:],
	}, nil
}

func (c *RemoteClient) Put(data []byte) error {
	c.mu.Lock()
	defer c.mu.Unlock()

	res, err := etcdv3.NewKV(c.Client).Txn(context.TODO()).If(
		etcdv3.Compare(etcdv3.ModRevision(c.Key), "=", c.modRevision),
	).Then(
		etcdv3.OpPut(c.Key, string(data)),
		etcdv3.OpGet(c.Key),
	).Commit()

	if err != nil {
		return err
	}
	if !res.Succeeded {
		return fmt.Errorf("The transaction did not succeed.")
	}
	if len(res.Responses) != 2 {
		return fmt.Errorf("Expected two responses but got %d.", len(res.Responses))
	}

	c.modRevision = res.Responses[1].GetResponseRange().Kvs[0].ModRevision
	return nil
}

func (c *RemoteClient) Delete() error {
	c.mu.Lock()
	defer c.mu.Unlock()

	_, err := c.Client.KV.Delete(context.TODO(), c.Key)
	return err
}

func (c *RemoteClient) Lock(info *state.LockInfo) (string, error) {
	c.mu.Lock()
	defer c.mu.Unlock()

	if !c.DoLock {
		return "", nil
	}
	if c.etcdSession != nil {
		return "", fmt.Errorf("state %q already locked", c.Key)
	}

	c.info = info
	return c.lock()
}

func (c *RemoteClient) Unlock(id string) error {
	c.mu.Lock()
	defer c.mu.Unlock()

	if !c.DoLock {
		return nil
	}

	return c.unlock(id)
}

func (c *RemoteClient) deleteLockInfo(info *state.LockInfo) error {
	res, err := c.Client.KV.Delete(context.TODO(), c.Key+lockInfoSuffix)
	if err != nil {
		return err
	}
	if res.Deleted == 0 {
		return fmt.Errorf("No keys deleted for %s when deleting lock info.", c.Key+lockInfoSuffix)
	}
	return nil
}

func (c *RemoteClient) getLockInfo() (*state.LockInfo, error) {
	res, err := c.Client.KV.Get(context.TODO(), c.Key+lockInfoSuffix)
	if err != nil {
		return nil, err
	}
	if res.Count == 0 {
		return nil, nil
	}

	li := &state.LockInfo{}
	err = json.Unmarshal(res.Kvs[0].Value, li)
	if err != nil {
		return nil, fmt.Errorf("Error unmarshaling lock info: %s.", err)
	}

	return li, nil
}

func (c *RemoteClient) putLockInfo(info *state.LockInfo) error {
	c.info.Path = c.Key
	c.info.Created = time.Now().UTC()

	_, err := c.Client.KV.Put(context.TODO(), c.Key+lockInfoSuffix, string(c.info.Marshal()))
	return err
}

func (c *RemoteClient) lock() (string, error) {
	session, err := etcdv3sync.NewSession(c.Client)
	if err != nil {
		return "", nil
	}

	ctx, cancel := context.WithTimeout(context.TODO(), lockAcquireTimeout)
	defer cancel()

	mutex := etcdv3sync.NewMutex(session, c.Key+lockSuffix)
	if err1 := mutex.Lock(ctx); err1 != nil {
		lockInfo, err2 := c.getLockInfo()
		if err2 != nil {
			return "", &state.LockError{Err: err2}
		}
		return "", &state.LockError{Info: lockInfo, Err: err1}
	}

	c.etcdMutex = mutex
	c.etcdSession = session

	err = c.putLockInfo(c.info)
	if err != nil {
		if unlockErr := c.unlock(c.info.ID); unlockErr != nil {
			err = multierror.Append(err, unlockErr)
		}
		return "", err
	}

	return c.info.ID, nil
}

func (c *RemoteClient) unlock(id string) error {
	if c.etcdMutex == nil {
		return nil
	}

	var errs error

	if err := c.deleteLockInfo(c.info); err != nil {
		errs = multierror.Append(errs, err)
	}
	if err := c.etcdMutex.Unlock(context.TODO()); err != nil {
		errs = multierror.Append(errs, err)
	}
	if err := c.etcdSession.Close(); err != nil {
		errs = multierror.Append(errs, err)
	}

	c.etcdMutex = nil
	c.etcdSession = nil

	return errs
}
<|endoftext|>


<commit_before>package transactions

import (
	"errors"
	"reflect"
	"testing"

	"github.com/luistm/go-bank-cli/elib/testkit"
	"github.com/luistm/go-bank-cli/lib/categories"

	"github.com/luistm/go-bank-cli/lib"

	"github.com/luistm/go-bank-cli/lib/customerrors"
	"github.com/luistm/go-bank-cli/lib/sellers"
	"github.com/stretchr/testify/mock"
)

type testMock struct {
	mock.Mock
}

func (m *testMock) GetAll() ([]*Transaction, error) {
	args := m.Called()
	return args.Get(0).([]*Transaction), args.Error(1)
}

func (m *testMock) Create(s string) (lib.Entity, error) {
	args := m.Called(s)
	return args.Get(0).(lib.Entity), args.Error(1)
}

func TestUnitInteractorTransactionsLoadDataFromRecords(t *testing.T) {

	testCasesRepository := []struct {
		name       string
		output     error
		withMock   bool
		mockOutput []interface{}
	}{
		{
			name:       "Returns error if repository is not defined",
			output:     customerrors.ErrRepositoryUndefined,
			withMock:   false,
			mockOutput: nil,
		},
		{
			name:       "Returns error on repository error",
			output:     &customerrors.ErrRepository{Msg: "Test Error"},
			withMock:   true,
			mockOutput: []interface{}{[]*Transaction{}, errors.New("Test Error")},
		},
	}

	for _, tc := range testCasesRepository {
		t.Log(tc.name)
		i := Interactor{}
		var m *testMock
		if tc.withMock {
			m = new(testMock)
			i.transactionsRepository = m
			m.On("GetAll").Return(tc.mockOutput...)
		}

		err := i.LoadDataFromRecords()

		if tc.withMock {
			m.AssertExpectations(t)
		}
		if !reflect.DeepEqual(tc.output, err) {
			t.Errorf("Expected '%v', got '%v'", tc.output, err)
		}
	}

	c := categories.New("Test Category")
	t1 := &Transaction{s: sellers.New("d1", "d1"), c: c}
	t2 := &Transaction{s: sellers.New("d2", "d2"), c: c}
	i := Interactor{}
	tm := new(testMock)
	i.transactionsRepository = tm
	tm.On("GetAll").Return([]*Transaction{t1, t2}, nil)

	testCasesEntityRepositorySave := []struct {
		name       string
		output     error
		withMock   bool
		mockInput  lib.Entity
		mockOutput error
	}{
		{
			name:       "Returns error if entity repository is not defined",
			output:     customerrors.ErrInteractorUndefined,
			withMock:   false,
			mockInput:  nil,
			mockOutput: nil,
		},
		{
			name:       "Returns error if entity save method returns fail",
			output:     &customerrors.ErrInteractor{Msg: "Test Error"},
			withMock:   true,
			mockInput:  t1.s,
			mockOutput: errors.New("Test Error"),
		},
	}

	for _, tc := range testCasesEntityRepositorySave {
		t.Log(tc.name)
		var im *lib.RepositoryMock
		if tc.withMock {
			im = new(lib.RepositoryMock)
			im.On("Save", tc.mockInput).Return(tc.mockOutput)
			i.sellersRepository = im
		}

		err := i.LoadDataFromRecords()

		if tc.withMock {
			im.AssertExpectations(t)
		}
		if !reflect.DeepEqual(tc.output, err) {
			t.Errorf("Expected '%v', got '%v'", tc.output, err)
		}
	}

}

type repositoryMock struct {
	mock.Mock
}

func (m *repositoryMock) GetAll() ([]*Transaction, error) {
	args := m.Called()
	return args.Get(0).([]*Transaction), args.Error(1)
}

func TestUnitReport(t *testing.T) {

	testCases := []struct {
		name       string
		output     []interface{}
		withMock   bool
		mockOutput []interface{}
	}{
		{
			name:   "Returns error if repository is indefined",
			output: []interface{}{&Report{}, customerrors.ErrRepositoryUndefined},
		},
		{
			name:       "Returns error if repository returns error",
			output:     []interface{}{&Report{}, &customerrors.ErrRepository{Msg: "Test Error"}},
			withMock:   true,
			mockOutput: []interface{}{[]*Transaction{}, errors.New("Test Error")},
		},
		{
			name: "Report has transactions",
			output: []interface{}{
				&Report{transactions: []*Transaction{&Transaction{}}},
				nil,
			},
			withMock:   true,
			mockOutput: []interface{}{[]*Transaction{&Transaction{}}, nil},
		},
	}

	for _, tc := range testCases {
		t.Log(tc.name)
		i := Interactor{}
		var m *repositoryMock
		if tc.withMock {
			m = new(repositoryMock)
			m.On("GetAll").Return(tc.mockOutput...)
			i.transactionsRepository = m
		}

		r, err := i.ReportFromRecords()

		if tc.withMock {
			m.AssertExpectations(t)
		}
		testkit.AssertEqual(t, tc.output, []interface{}{r, err})
	}
}
<commit_msg>Transactions unit tests no longer crashes<commit_after>package transactions

import (
	"errors"
	"reflect"
	"testing"

	"github.com/luistm/go-bank-cli/elib/testkit"
	"github.com/luistm/go-bank-cli/lib/categories"

	"github.com/luistm/go-bank-cli/lib"

	"github.com/luistm/go-bank-cli/lib/customerrors"
	"github.com/luistm/go-bank-cli/lib/sellers"
	"github.com/stretchr/testify/mock"
)

type testMock struct {
	mock.Mock
}

func (m *testMock) GetAll() ([]*Transaction, error) {
	args := m.Called()
	return args.Get(0).([]*Transaction), args.Error(1)
}

func (m *testMock) Create(s string) (lib.Entity, error) {
	args := m.Called(s)
	return args.Get(0).(lib.Entity), args.Error(1)
}

func TestUnitInteractorTransactionsLoadDataFromRecords(t *testing.T) {

	testCasesRepository := []struct {
		name       string
		output     error
		withMock   bool
		mockOutput []interface{}
	}{
		{
			name:       "Returns error if repository is not defined",
			output:     customerrors.ErrRepositoryUndefined,
			withMock:   false,
			mockOutput: nil,
		},
		{
			name:       "Returns error on repository error",
			output:     &customerrors.ErrRepository{Msg: "Test Error"},
			withMock:   true,
			mockOutput: []interface{}{[]*Transaction{}, errors.New("Test Error")},
		},
	}

	for _, tc := range testCasesRepository {
		t.Log(tc.name)
		i := Interactor{}
		var m *testMock
		if tc.withMock {
			m = new(testMock)
			i.transactionsRepository = m
			m.On("GetAll").Return(tc.mockOutput...)
		}

		err := i.LoadDataFromRecords()

		if tc.withMock {
			m.AssertExpectations(t)
		}
		if !reflect.DeepEqual(tc.output, err) {
			t.Errorf("Expected '%v', got '%v'", tc.output, err)
		}
	}

	c := categories.New("Test Category")
	t1 := &Transaction{s: sellers.New("d1", "d1"), c: c}
	t2 := &Transaction{s: sellers.New("d2", "d2"), c: c}
	i := Interactor{}
	tm := new(testMock)
	i.transactionsRepository = tm
	tm.On("GetAll").Return([]*Transaction{t1, t2}, nil)

	testCasesEntityRepositorySave := []struct {
		name       string
		output     error
		withMock   bool
		mockInput  lib.Entity
		mockOutput error
	}{
		{
			name:       "Returns error if entity repository is not defined",
			output:     customerrors.ErrInteractorUndefined,
			withMock:   false,
			mockInput:  nil,
			mockOutput: nil,
		},
		{
			name:       "Returns error if entity save method returns fail",
			output:     &customerrors.ErrInteractor{Msg: "Test Error"},
			withMock:   true,
			mockInput:  t1.s,
			mockOutput: errors.New("Test Error"),
		},
	}

	for _, tc := range testCasesEntityRepositorySave {
		t.Log(tc.name)
		var im *lib.RepositoryMock
		if tc.withMock {
			im = new(lib.RepositoryMock)
			im.On("Save", tc.mockInput).Return(tc.mockOutput)
			i.sellersRepository = im
		}

		err := i.LoadDataFromRecords()

		if tc.withMock {
			im.AssertExpectations(t)
		}
		if !reflect.DeepEqual(tc.output, err) {
			t.Errorf("Expected '%v', got '%v'", tc.output, err)
		}
	}

}

type repositoryMock struct {
	mock.Mock
}

func (m *repositoryMock) GetAll() ([]*Transaction, error) {
	args := m.Called()
	return args.Get(0).([]*Transaction), args.Error(1)
}

func TestUnitReport(t *testing.T) {

	testCases := []struct {
		name       string
		output     []interface{}
		withMock   bool
		mockOutput []interface{}
	}{
		{
			name:   "Returns error if repository is indefined",
			output: []interface{}{&Report{}, customerrors.ErrRepositoryUndefined},
		},
		{
			name:       "Returns error if repository returns error",
			output:     []interface{}{&Report{}, &customerrors.ErrRepository{Msg: "Test Error"}},
			withMock:   true,
			mockOutput: []interface{}{[]*Transaction{}, errors.New("Test Error")},
		},
		{
			name: "Report has transactions",
			output: []interface{}{
				&Report{transactions: []*Transaction{&Transaction{}}},
				nil,
			},
			withMock:   true,
			mockOutput: []interface{}{[]*Transaction{&Transaction{}}, nil},
		},
	}

	for _, tc := range testCases {
		t.Log(tc.name)
		i := Interactor{}
		var m *repositoryMock
		if tc.withMock {
			m = new(repositoryMock)
			m.On("GetAll").Return(tc.mockOutput...)
			i.transactionsRepository = m

			sellersMock := new(lib.RepositoryMock)
			sellersMock.On("GetAll").Return([]lib.Entity{}, nil)
			i.sellersRepository = sellersMock
		}

		r, err := i.ReportFromRecords()

		if tc.withMock {
			m.AssertExpectations(t)
		}
		testkit.AssertEqual(t, tc.output, []interface{}{r, err})
	}
}
<|endoftext|>


<commit_before>/*
 *    Copyright (C) 2014 Christian Muehlhaeuser
 *
 *    This program is free software: you can redistribute it and/or modify
 *    it under the terms of the GNU Affero General Public License as published
 *    by the Free Software Foundation, either version 3 of the License, or
 *    (at your option) any later version.
 *
 *    This program is distributed in the hope that it will be useful,
 *    but WITHOUT ANY WARRANTY; without even the implied warranty of
 *    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *    GNU Affero General Public License for more details.
 *
 *    You should have received a copy of the GNU Affero General Public License
 *    along with this program.  If not, see <http://www.gnu.org/licenses/>.
 *
 *    Authors:
 *      Christian Muehlhaeuser <muesli@gmail.com>
 */

// beehive's Html Extraction module.
package htmlextractbee

import (
	"github.com/advancedlogic/GoOse"
	"github.com/muesli/beehive/bees"
	"strings"
)

type HtmlExtractBee struct {
	bees.Bee

	url string

	evchan chan bees.Event
}

func (mod *HtmlExtractBee) Action(action bees.Action) []bees.Placeholder {
	outs := []bees.Placeholder{}

	switch action.Name {
	case "extract":
		var url string
		for _, opt := range action.Options {
			if opt.Name == "url" {
				url = opt.Value.(string)
				if start := strings.Index(url, "http"); start >= 0 {
					url = url[start:]
					if end := strings.Index(url, " "); end >= 0 {
						url = url[:end]
					}
				}
			}
		}

		g := goose.New()
		article := g.ExtractFromUrl(url)
		article.Title = strings.TrimSpace(strings.Replace(article.Title, "\n", " ", -1))
		if strings.HasPrefix(article.TopImage, "http://data:image") {
			article.TopImage = ""
		}
		if len(article.Title) > 0 {
			ev := bees.Event{
				Bee:  mod.Name(),
				Name: "info_extracted",
				Options: []bees.Placeholder{
					bees.Placeholder{
						Name:  "title",
						Type:  "string",
						Value: article.Title,
					},
					bees.Placeholder{
						Name:  "domain",
						Type:  "string",
						Value: article.Domain,
					},
					bees.Placeholder{
						Name:  "topimage",
						Type:  "url",
						Value: article.TopImage,
					},
					bees.Placeholder{
						Name:  "finalurl",
						Type:  "url",
						Value: article.FinalUrl,
					},
					bees.Placeholder{
						Name:  "meta_description",
						Type:  "string",
						Value: article.MetaDescription,
					},
					bees.Placeholder{
						Name:  "meta_keywords",
						Type:  "string",
						Value: article.MetaKeywords,
					},
				},
			}
			mod.evchan <- ev
		}

	default:
		panic("Unknown action triggered in " + mod.Name() + ": " + action.Name)
	}

	return outs
}

func (mod *HtmlExtractBee) Run(eventChan chan bees.Event) {
	mod.evchan = eventChan
}
<commit_msg>Fixed upstream API changes.<commit_after>/*
 *    Copyright (C) 2014 Christian Muehlhaeuser
 *
 *    This program is free software: you can redistribute it and/or modify
 *    it under the terms of the GNU Affero General Public License as published
 *    by the Free Software Foundation, either version 3 of the License, or
 *    (at your option) any later version.
 *
 *    This program is distributed in the hope that it will be useful,
 *    but WITHOUT ANY WARRANTY; without even the implied warranty of
 *    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *    GNU Affero General Public License for more details.
 *
 *    You should have received a copy of the GNU Affero General Public License
 *    along with this program.  If not, see <http://www.gnu.org/licenses/>.
 *
 *    Authors:
 *      Christian Muehlhaeuser <muesli@gmail.com>
 */

// beehive's Html Extraction module.
package htmlextractbee

import (
	"github.com/advancedlogic/GoOse"
	"github.com/muesli/beehive/bees"
	"strings"
)

type HtmlExtractBee struct {
	bees.Bee

	url string

	evchan chan bees.Event
}

func (mod *HtmlExtractBee) Action(action bees.Action) []bees.Placeholder {
	outs := []bees.Placeholder{}

	switch action.Name {
	case "extract":
		var url string
		for _, opt := range action.Options {
			if opt.Name == "url" {
				url = opt.Value.(string)
				if start := strings.Index(url, "http"); start >= 0 {
					url = url[start:]
					if end := strings.Index(url, " "); end >= 0 {
						url = url[:end]
					}
				}
			}
		}

		g := goose.New()
		article, _ := g.ExtractFromURL(url)
		article.Title = strings.TrimSpace(strings.Replace(article.Title, "\n", " ", -1))
		if strings.HasPrefix(article.TopImage, "http://data:image") {
			article.TopImage = ""
		}
		if len(article.Title) > 0 {
			ev := bees.Event{
				Bee:  mod.Name(),
				Name: "info_extracted",
				Options: []bees.Placeholder{
					bees.Placeholder{
						Name:  "title",
						Type:  "string",
						Value: article.Title,
					},
					bees.Placeholder{
						Name:  "domain",
						Type:  "string",
						Value: article.Domain,
					},
					bees.Placeholder{
						Name:  "topimage",
						Type:  "url",
						Value: article.TopImage,
					},
					bees.Placeholder{
						Name:  "finalurl",
						Type:  "url",
						Value: article.FinalURL,
					},
					bees.Placeholder{
						Name:  "meta_description",
						Type:  "string",
						Value: article.MetaDescription,
					},
					bees.Placeholder{
						Name:  "meta_keywords",
						Type:  "string",
						Value: article.MetaKeywords,
					},
				},
			}
			mod.evchan <- ev
		}

	default:
		panic("Unknown action triggered in " + mod.Name() + ": " + action.Name)
	}

	return outs
}

func (mod *HtmlExtractBee) Run(eventChan chan bees.Event) {
	mod.evchan = eventChan
}
<|endoftext|>