<commit_before>package progress

import (
	"os"
	"sync"
	"sync/atomic"
	"time"

	"github.com/restic/restic/internal/debug"
)

// A Func is a callback for a Counter.
//
// The final argument is true if Counter.Done has been called,
// which means that the current call will be the last.
type Func func(value uint64, runtime time.Duration, final bool)

// A Counter tracks a running count and controls a goroutine that passes its
// value periodically to a Func.
//
// The Func is also called when SIGUSR1 (or SIGINFO, on BSD) is received.
type Counter struct {
	report  Func
	start   time.Time
	stopped chan struct{} // Closed by run.
	stop    chan struct{} // Close to stop run.
	tick    *time.Ticker
	value   uint64
}

// New starts a new Counter.
func New(interval time.Duration, report Func) *Counter {
	signals.Once.Do(func() {
		signals.ch = make(chan os.Signal, 1)
		setupSignals()
	})

	c := &Counter{
		report:  report,
		start:   time.Now(),
		stopped: make(chan struct{}),
		stop:    make(chan struct{}),
		tick:    time.NewTicker(interval),
	}

	go c.run()
	return c
}

// Add v to the Counter. This method is concurrency-safe.
func (c *Counter) Add(v uint64) {
	if c == nil {
		return
	}
	atomic.AddUint64(&c.value, v)
}

// Done tells a Counter to stop and waits for it to report its final value.
func (c *Counter) Done() {
	if c == nil {
		return
	}
	c.tick.Stop()
	close(c.stop)
	<-c.stopped    // Wait for last progress report.
	*c = Counter{} // Prevent reuse.
}

func (c *Counter) get() uint64 { return atomic.LoadUint64(&c.value) }

func (c *Counter) run() {
	defer close(c.stopped)
	defer func() {
		// Must be a func so that time.Since isn't called at defer time.
		c.report(c.get(), time.Since(c.start), true)
	}()

	for {
		var now time.Time

		select {
		case now = <-c.tick.C:
		case sig := <-signals.ch:
			debug.Log("Signal received: %v\n", sig)
			now = time.Now()
		case <-c.stop:
			return
		}

		c.report(c.get(), now.Sub(c.start), false)
	}
}

// XXX The fact that signals is a single global variable means that only one
// Counter receives each incoming signal.
var signals struct {
	ch chan os.Signal
	sync.Once
}
<commit_msg>ui/progress: Use mutex instead of atomic<commit_after>package progress

import (
	"os"
	"sync"
	"time"

	"github.com/restic/restic/internal/debug"
)

// A Func is a callback for a Counter.
//
// The final argument is true if Counter.Done has been called,
// which means that the current call will be the last.
type Func func(value uint64, runtime time.Duration, final bool)

// A Counter tracks a running count and controls a goroutine that passes its
// value periodically to a Func.
//
// The Func is also called when SIGUSR1 (or SIGINFO, on BSD) is received.
type Counter struct {
	report  Func
	start   time.Time
	stopped chan struct{} // Closed by run.
	stop    chan struct{} // Close to stop run.
	tick    *time.Ticker

	valueMutex sync.Mutex
	value      uint64
}

// New starts a new Counter.
func New(interval time.Duration, report Func) *Counter {
	signals.Once.Do(func() {
		signals.ch = make(chan os.Signal, 1)
		setupSignals()
	})

	c := &Counter{
		report:  report,
		start:   time.Now(),
		stopped: make(chan struct{}),
		stop:    make(chan struct{}),
		tick:    time.NewTicker(interval),
	}

	go c.run()
	return c
}

// Add v to the Counter. This method is concurrency-safe.
func (c *Counter) Add(v uint64) {
	if c == nil {
		return
	}

	c.valueMutex.Lock()
	c.value += v
	c.valueMutex.Unlock()
}

// Done tells a Counter to stop and waits for it to report its final value.
func (c *Counter) Done() {
	if c == nil {
		return
	}
	c.tick.Stop()
	close(c.stop)
	<-c.stopped    // Wait for last progress report.
	*c = Counter{} // Prevent reuse.
}

func (c *Counter) get() uint64 {
	c.valueMutex.Lock()
	v := c.value
	c.valueMutex.Unlock()

	return v
}

func (c *Counter) run() {
	defer close(c.stopped)
	defer func() {
		// Must be a func so that time.Since isn't called at defer time.
		c.report(c.get(), time.Since(c.start), true)
	}()

	for {
		var now time.Time

		select {
		case now = <-c.tick.C:
		case sig := <-signals.ch:
			debug.Log("Signal received: %v\n", sig)
			now = time.Now()
		case <-c.stop:
			return
		}

		c.report(c.get(), now.Sub(c.start), false)
	}
}

// XXX The fact that signals is a single global variable means that only one
// Counter receives each incoming signal.
var signals struct {
	ch chan os.Signal
	sync.Once
}
<|endoftext|>


<commit_before>// Copyright 2019 Google LLC
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

// Package oauth implements functions to diagnose the supported OAuth2 flows
// (web and installed app flows) in a Google Ads API client library client
// environment.
package oauth

import (
	"bufio"
	"bytes"
	"encoding/json"
	"fmt"
	"log"
	"net/http"
	"net/http/httputil"
	"os"
	"strings"

	"github.com/googleads/google-ads-doctor/oauthdoctor/diag"

	"golang.org/x/oauth2"
	"golang.org/x/oauth2/google"
)

// This is a list of error codes (not comprehensive) returned by Google OAuth2
// endpoint based on Google Ads API scope.
const (
	AccessNotPermittedForManagerAccount = iota
	GoogleAdsAPIDisabled
	InvalidClientInfo
	InvalidRefreshToken
	InvalidCustomerID
	MissingDevToken
	Unauthenticated
	Unauthorized
	UnknownError

	GoogleAdsApiScope = "https://www.googleapis.com/auth/adwords"
)

// Config is a required configuration for diagnosing the OAuth2 flow based on
// the client library configuration.
type Config struct {
	ConfigFile diag.ConfigFile
	CustomerID string
	OAuthType  string
	Verbose    bool
}

// ConfigWriter allows replacement of key by a given value in a configuration.
type ConfigWriter interface {
	ReplaceConfig(k, v string) string
}

var (
	appVersion string

	stdinSanitizer = strings.NewReplacer("\n", "")

	readStdin = func() string {
		reader := bufio.NewReader(os.Stdin)
		str, err := reader.ReadString('\n')
		if err != nil {
			log.Printf("Error reading input (%s) from command line: %s", str, err)
		}

		return strings.TrimSpace(stdinSanitizer.Replace(str))
	}
)

// SimulateOAuthFlow simulates the OAuth2 flows supported by the Google Ads API
// client libraries.
func (c *Config) SimulateOAuthFlow() {
	switch c.OAuthType {
	case diag.Web:
		c.simulateWebFlow()
	case diag.InstalledApp:
		c.simulateAppFlow()
	case diag.ServiceAccount:
		c.simulateServiceAccFlow()
	}
}

// decodeError checks the JSON response in the error and determines the error
// code.
func (c *Config) decodeError(err error) int32 {
	errstr := err.Error()

	if strings.Contains(errstr, "invalid_client") {
		// Client ID and/or secret is invalid
		return InvalidClientInfo
	}
	if strings.Contains(errstr, "unauthorized_client") {
		// The given refresh token may not be generated with the given client ID
		// and secret
		return Unauthorized
	}
	if strings.Contains(errstr, "invalid_grant") {
		// Refresh token is not valid for any users
		return InvalidRefreshToken
	}
	if strings.Contains(errstr, "refresh token is not set") {
		return InvalidRefreshToken
	}
	if strings.Contains(errstr, "USER_PERMISSION_DENIED") {
		// User doesn't have permission to access Google Ads account
		return InvalidRefreshToken
	}
	if strings.Contains(errstr, "\"PERMISSION_DENIED\"") {
		return GoogleAdsAPIDisabled
	}
	if strings.Contains(errstr, "UNAUTHENTICATED") {
		return Unauthenticated
	}
	if strings.Contains(errstr, "CANNOT_BE_EXECUTED_BY_MANAGER_ACCOUNT") {
		// Request cannot be executed by a manager account
		return AccessNotPermittedForManagerAccount
	}
	if strings.Contains(errstr, "DEVELOPER_TOKEN_PARAMETER_MISSING") {
		return MissingDevToken
	}
	if strings.Contains(errstr, "INVALID_CUSTOMER_ID") {
		return InvalidCustomerID
	}
	return UnknownError
}

// diagnose handles the error by guiding the user to take appropriate
// actions to fix the OAuth2 error based on the error code.
func (c *Config) diagnose(err error) {
	// Print the given message from JSON response if there's any
	var parsedMsg map[string]interface{}
	if err := json.Unmarshal([]byte(err.Error()), &parsedMsg); err == nil {
		errMsg := parsedMsg["error"].(map[string]interface{})["message"]
		log.Print("JSON response error: " + errMsg.(string))
	}

	switch c.decodeError(err) {
	case AccessNotPermittedForManagerAccount:
		log.Print("ERROR: Your credentials are not permitted to access to a manager account." +
			"\nPlease create your credentials with a Google Ads account with manager access.")
	case GoogleAdsAPIDisabled:
		log.Print("Press <Enter> to continue after you enable Google Ads API")
		readStdin()
	case InvalidClientInfo:
		log.Print("ERROR: Your client ID and/or client secret may be invalid.")
		replaceCloudCredentials(&c.ConfigFile)
	case InvalidRefreshToken, Unauthorized:
		log.Print("ERROR: Your refresh token may be invalid.")
	case MissingDevToken:
		log.Print("ERROR: Your developer token is missing in the configuration file")
		replaceDevToken(&c.ConfigFile)
	case Unauthenticated:
		log.Print("ERROR: The login email may not have access to the given account.")
	case InvalidCustomerID:
		log.Print("ERROR: You customer ID is invalid.")
	default:
		var helperText string
		switch c.ConfigFile.OAuthType {
		case diag.ServiceAccount:
			helperText = "Please verify the path of JSON key file and impersonate email (or delegated email)."
		case diag.Web:
			helperText = "Please verify your developer token, client ID and client secret."
		case diag.InstalledApp:
			helperText = "Please verify your developer token, client ID, client secret and refresh token."
		}
		log.Print("ERROR: Your credentials are invalid but we cannot determine the exact error. " + helperText)
	}
}

var (
	getClientID = func() string {
		fmt.Print("New Client ID >> ")
		return readStdin()
	}

	getClientSecret = func() string {
		fmt.Print("New Client Secret >> ")
		return readStdin()
	}
)

// replaceCloudCredentials prompts the user to create a new client ID and
// secret and to then enter them at the prompt. The values entered will
// replace the existing values in the client library configuration file.
func replaceCloudCredentials(c ConfigWriter) {
	log.Print("Follow this guide to setup your OAuth2 client ID and client secret: " +
		"https://developers.google.com/adwords/api/docs/guides/first-api-call#set_up_oauth2_authentication")

	clientID := getClientID()
	clientSecret := getClientSecret()

	c.ReplaceConfig(diag.ClientID, clientID)
	c.ReplaceConfig(diag.ClientSecret, clientSecret)
}

// replaceDevToken guides the user to retrieve their developer token and
// enter it at the prompt. The entered value will replace the existing
// developer token in the client library configuration file.
var replaceDevToken = func(c ConfigWriter) {
	log.Print("Please follow this guide to retrieve your developer token: " +
		"https://developers.google.com/adwords/api/docs/guides/signup#step-2")
	log.Print("Pleae enter a new Developer Token here and it will replace " +
		"the one in your client library configuration file")

	fmt.Print("New Developer Token >> ")
	devToken := readStdin()

	c.ReplaceConfig(diag.DevToken, devToken)
}

// replaceRefreshToken asks the user if they want to replace the refresh
// token in the configuration file with the newly generated value.
func replaceRefreshToken(c ConfigWriter, refreshToken string) {
	log.Print("Would you like to replace your refresh token in the " +
		"client library config file with the new one generated?")

	fmt.Print("Enter Y for Yes [Anything else is No] >> ")
	answer := readStdin()

	if answer == "Y" {
		c.ReplaceConfig(diag.RefreshToken, refreshToken)
	} else {
		log.Print("Refresh token is NOT replaced")
	}
}

var oauthEndpoint = google.Endpoint

// oauth2Conf creates a corresponding OAuth2 config struct based on the
// given configuration details. This is only applicable when a refresh token
// is not given.
func (c *Config) oauth2Conf(redirectURL string) *oauth2.Config {
	return &oauth2.Config{
		ClientID:     c.ConfigFile.ConfigKeys.ClientID,
		ClientSecret: c.ConfigFile.ClientSecret,
		RedirectURL:  redirectURL,
		Scopes:       []string{GoogleAdsApiScope},
		Endpoint:     oauthEndpoint,
	}
}

// Given the auth code returned after the authentication and authorization
// step, oauth2Client creates a HTTP client with an authorized access token.
func (c *Config) oauth2Client(code string) (*http.Client, string) {
	conf := c.oauth2Conf(InstalledAppRedirectURL)
	// Handle the exchange code to initiate a transport.
	token, err := conf.Exchange(oauth2.NoContext, code)
	if err != nil {
		log.Fatal(err)
	}
	return conf.Client(oauth2.NoContext, token), token.RefreshToken
}

var apiURL = "https://googleads.googleapis.com/v1/customers/"

// getAccount makes a HTTP request to Google Ads API customer account
// endpoint and parses the JSON response.
func (c *Config) getAccount(client *http.Client) (*bytes.Buffer, error) {
	req, err := http.NewRequest("GET", apiURL+c.CustomerID, nil)
	if err != nil {
		return nil, err
	}

	req.Header.Set("user-agent", userAgent())
	req.Header.Set("developer-token", c.ConfigFile.DevToken)
	if c.ConfigFile.LoginCustomerID != "" {
		req.Header.Set("login-customer-id", c.ConfigFile.LoginCustomerID)
	}

	if c.Verbose {
		dump, err := httputil.DumpRequestOut(req, true)
		if err != nil {
			log.Printf("Error printing HTTP request: %s", err)
		}
		log.Printf("Making a HTTP Request to Google Ads API:\n%v\n", c.sanitizeOutput(string(dump)))
	}

	resp, err := client.Do(req)
	if err != nil {
		return nil, err
	}
	defer resp.Body.Close()

	buf := new(bytes.Buffer)
	buf.ReadFrom(resp.Body)

	var jsonBody map[string]interface{}
	json.Unmarshal(buf.Bytes(), &jsonBody)

	if resp.StatusCode != http.StatusOK {
		return nil, fmt.Errorf("A HTTP Status (%s) is returned while calling %s", resp.Status, apiURL+c.CustomerID)
	}

	if jsonBody["error"] != nil {
		return nil, fmt.Errorf(jsonBody["error"].(string))
	}

	return buf, nil
}

func userAgent() string {
	ua := "google-ads-doctor/"
	if appVersion != "" {
		ua += appVersion
	} else {
		ua += "source"
	}
	return ua
}

func (c *Config) sanitizeOutput(s string) string {
	return strings.ReplaceAll(s, c.ConfigFile.DevToken, "REDACTED")
}

// ReadCustomerID retrieves the CID from stdin.
func ReadCustomerID() string {
	for {
		log.Print("Please enter a Google Ads account ID:")
		customerID := readStdin()

		if customerID != "" {
			return strings.ReplaceAll(customerID, "-", "")
		}
	}
}
<commit_msg>Added documentation for userAgent()<commit_after>// Copyright 2019 Google LLC
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

// Package oauth implements functions to diagnose the supported OAuth2 flows
// (web and installed app flows) in a Google Ads API client library client
// environment.
package oauth

import (
	"bufio"
	"bytes"
	"encoding/json"
	"fmt"
	"log"
	"net/http"
	"net/http/httputil"
	"os"
	"strings"

	"github.com/googleads/google-ads-doctor/oauthdoctor/diag"

	"golang.org/x/oauth2"
	"golang.org/x/oauth2/google"
)

// This is a list of error codes (not comprehensive) returned by Google OAuth2
// endpoint based on Google Ads API scope.
const (
	AccessNotPermittedForManagerAccount = iota
	GoogleAdsAPIDisabled
	InvalidClientInfo
	InvalidRefreshToken
	InvalidCustomerID
	MissingDevToken
	Unauthenticated
	Unauthorized
	UnknownError

	GoogleAdsApiScope = "https://www.googleapis.com/auth/adwords"
)

// Config is a required configuration for diagnosing the OAuth2 flow based on
// the client library configuration.
type Config struct {
	ConfigFile diag.ConfigFile
	CustomerID string
	OAuthType  string
	Verbose    bool
}

// ConfigWriter allows replacement of key by a given value in a configuration.
type ConfigWriter interface {
	ReplaceConfig(k, v string) string
}

var (
	appVersion string

	stdinSanitizer = strings.NewReplacer("\n", "")

	readStdin = func() string {
		reader := bufio.NewReader(os.Stdin)
		str, err := reader.ReadString('\n')
		if err != nil {
			log.Printf("Error reading input (%s) from command line: %s", str, err)
		}

		return strings.TrimSpace(stdinSanitizer.Replace(str))
	}
)

// SimulateOAuthFlow simulates the OAuth2 flows supported by the Google Ads API
// client libraries.
func (c *Config) SimulateOAuthFlow() {
	switch c.OAuthType {
	case diag.Web:
		c.simulateWebFlow()
	case diag.InstalledApp:
		c.simulateAppFlow()
	case diag.ServiceAccount:
		c.simulateServiceAccFlow()
	}
}

// decodeError checks the JSON response in the error and determines the error
// code.
func (c *Config) decodeError(err error) int32 {
	errstr := err.Error()

	if strings.Contains(errstr, "invalid_client") {
		// Client ID and/or secret is invalid
		return InvalidClientInfo
	}
	if strings.Contains(errstr, "unauthorized_client") {
		// The given refresh token may not be generated with the given client ID
		// and secret
		return Unauthorized
	}
	if strings.Contains(errstr, "invalid_grant") {
		// Refresh token is not valid for any users
		return InvalidRefreshToken
	}
	if strings.Contains(errstr, "refresh token is not set") {
		return InvalidRefreshToken
	}
	if strings.Contains(errstr, "USER_PERMISSION_DENIED") {
		// User doesn't have permission to access Google Ads account
		return InvalidRefreshToken
	}
	if strings.Contains(errstr, "\"PERMISSION_DENIED\"") {
		return GoogleAdsAPIDisabled
	}
	if strings.Contains(errstr, "UNAUTHENTICATED") {
		return Unauthenticated
	}
	if strings.Contains(errstr, "CANNOT_BE_EXECUTED_BY_MANAGER_ACCOUNT") {
		// Request cannot be executed by a manager account
		return AccessNotPermittedForManagerAccount
	}
	if strings.Contains(errstr, "DEVELOPER_TOKEN_PARAMETER_MISSING") {
		return MissingDevToken
	}
	if strings.Contains(errstr, "INVALID_CUSTOMER_ID") {
		return InvalidCustomerID
	}
	return UnknownError
}

// diagnose handles the error by guiding the user to take appropriate
// actions to fix the OAuth2 error based on the error code.
func (c *Config) diagnose(err error) {
	// Print the given message from JSON response if there's any
	var parsedMsg map[string]interface{}
	if err := json.Unmarshal([]byte(err.Error()), &parsedMsg); err == nil {
		errMsg := parsedMsg["error"].(map[string]interface{})["message"]
		log.Print("JSON response error: " + errMsg.(string))
	}

	switch c.decodeError(err) {
	case AccessNotPermittedForManagerAccount:
		log.Print("ERROR: Your credentials are not permitted to access to a manager account." +
			"\nPlease create your credentials with a Google Ads account with manager access.")
	case GoogleAdsAPIDisabled:
		log.Print("Press <Enter> to continue after you enable Google Ads API")
		readStdin()
	case InvalidClientInfo:
		log.Print("ERROR: Your client ID and/or client secret may be invalid.")
		replaceCloudCredentials(&c.ConfigFile)
	case InvalidRefreshToken, Unauthorized:
		log.Print("ERROR: Your refresh token may be invalid.")
	case MissingDevToken:
		log.Print("ERROR: Your developer token is missing in the configuration file")
		replaceDevToken(&c.ConfigFile)
	case Unauthenticated:
		log.Print("ERROR: The login email may not have access to the given account.")
	case InvalidCustomerID:
		log.Print("ERROR: You customer ID is invalid.")
	default:
		var helperText string
		switch c.ConfigFile.OAuthType {
		case diag.ServiceAccount:
			helperText = "Please verify the path of JSON key file and impersonate email (or delegated email)."
		case diag.Web:
			helperText = "Please verify your developer token, client ID and client secret."
		case diag.InstalledApp:
			helperText = "Please verify your developer token, client ID, client secret and refresh token."
		}
		log.Print("ERROR: Your credentials are invalid but we cannot determine the exact error. " + helperText)
	}
}

var (
	getClientID = func() string {
		fmt.Print("New Client ID >> ")
		return readStdin()
	}

	getClientSecret = func() string {
		fmt.Print("New Client Secret >> ")
		return readStdin()
	}
)

// replaceCloudCredentials prompts the user to create a new client ID and
// secret and to then enter them at the prompt. The values entered will
// replace the existing values in the client library configuration file.
func replaceCloudCredentials(c ConfigWriter) {
	log.Print("Follow this guide to setup your OAuth2 client ID and client secret: " +
		"https://developers.google.com/adwords/api/docs/guides/first-api-call#set_up_oauth2_authentication")

	clientID := getClientID()
	clientSecret := getClientSecret()

	c.ReplaceConfig(diag.ClientID, clientID)
	c.ReplaceConfig(diag.ClientSecret, clientSecret)
}

// replaceDevToken guides the user to retrieve their developer token and
// enter it at the prompt. The entered value will replace the existing
// developer token in the client library configuration file.
var replaceDevToken = func(c ConfigWriter) {
	log.Print("Please follow this guide to retrieve your developer token: " +
		"https://developers.google.com/adwords/api/docs/guides/signup#step-2")
	log.Print("Pleae enter a new Developer Token here and it will replace " +
		"the one in your client library configuration file")

	fmt.Print("New Developer Token >> ")
	devToken := readStdin()

	c.ReplaceConfig(diag.DevToken, devToken)
}

// replaceRefreshToken asks the user if they want to replace the refresh
// token in the configuration file with the newly generated value.
func replaceRefreshToken(c ConfigWriter, refreshToken string) {
	log.Print("Would you like to replace your refresh token in the " +
		"client library config file with the new one generated?")

	fmt.Print("Enter Y for Yes [Anything else is No] >> ")
	answer := readStdin()

	if answer == "Y" {
		c.ReplaceConfig(diag.RefreshToken, refreshToken)
	} else {
		log.Print("Refresh token is NOT replaced")
	}
}

var oauthEndpoint = google.Endpoint

// oauth2Conf creates a corresponding OAuth2 config struct based on the
// given configuration details. This is only applicable when a refresh token
// is not given.
func (c *Config) oauth2Conf(redirectURL string) *oauth2.Config {
	return &oauth2.Config{
		ClientID:     c.ConfigFile.ConfigKeys.ClientID,
		ClientSecret: c.ConfigFile.ClientSecret,
		RedirectURL:  redirectURL,
		Scopes:       []string{GoogleAdsApiScope},
		Endpoint:     oauthEndpoint,
	}
}

// Given the auth code returned after the authentication and authorization
// step, oauth2Client creates a HTTP client with an authorized access token.
func (c *Config) oauth2Client(code string) (*http.Client, string) {
	conf := c.oauth2Conf(InstalledAppRedirectURL)
	// Handle the exchange code to initiate a transport.
	token, err := conf.Exchange(oauth2.NoContext, code)
	if err != nil {
		log.Fatal(err)
	}
	return conf.Client(oauth2.NoContext, token), token.RefreshToken
}

var apiURL = "https://googleads.googleapis.com/v1/customers/"

// getAccount makes a HTTP request to Google Ads API customer account
// endpoint and parses the JSON response.
func (c *Config) getAccount(client *http.Client) (*bytes.Buffer, error) {
	req, err := http.NewRequest("GET", apiURL+c.CustomerID, nil)
	if err != nil {
		return nil, err
	}

	req.Header.Set("user-agent", userAgent())
	req.Header.Set("developer-token", c.ConfigFile.DevToken)
	if c.ConfigFile.LoginCustomerID != "" {
		req.Header.Set("login-customer-id", c.ConfigFile.LoginCustomerID)
	}

	if c.Verbose {
		dump, err := httputil.DumpRequestOut(req, true)
		if err != nil {
			log.Printf("Error printing HTTP request: %s", err)
		}
		log.Printf("Making a HTTP Request to Google Ads API:\n%v\n", c.sanitizeOutput(string(dump)))
	}

	resp, err := client.Do(req)
	if err != nil {
		return nil, err
	}
	defer resp.Body.Close()

	buf := new(bytes.Buffer)
	buf.ReadFrom(resp.Body)

	var jsonBody map[string]interface{}
	json.Unmarshal(buf.Bytes(), &jsonBody)

	if resp.StatusCode != http.StatusOK {
		return nil, fmt.Errorf("A HTTP Status (%s) is returned while calling %s", resp.Status, apiURL+c.CustomerID)
	}

	if jsonBody["error"] != nil {
		return nil, fmt.Errorf(jsonBody["error"].(string))
	}

	return buf, nil
}

// userAgent returns a User-Agent HTTP header for this tool.
func userAgent() string {
	ua := "google-ads-doctor/"
	if appVersion != "" {
		ua += appVersion
	} else {
		ua += "source"
	}
	return ua
}

func (c *Config) sanitizeOutput(s string) string {
	return strings.ReplaceAll(s, c.ConfigFile.DevToken, "REDACTED")
}

// ReadCustomerID retrieves the CID from stdin.
func ReadCustomerID() string {
	for {
		log.Print("Please enter a Google Ads account ID:")
		customerID := readStdin()

		if customerID != "" {
			return strings.ReplaceAll(customerID, "-", "")
		}
	}
}
<|endoftext|>


<commit_before>/*
Copyright (c) 2014 VMware, Inc. All Rights Reserved.

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

    http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

package govmomi

import (
	"github.com/vmware/govmomi/vim25/methods"
	"github.com/vmware/govmomi/vim25/types"
)

type GuestFileManager struct {
	types.ManagedObjectReference

	c *Client
}

func (m GuestFileManager) Reference() types.ManagedObjectReference {
	return m.ManagedObjectReference
}

func (m GuestFileManager) ChangeFileAttributesInGuest(vm *VirtualMachine, auth types.BaseGuestAuthentication, guestFilePath string, fileAttributes types.BaseGuestFileAttributes) error {
	req := types.ChangeFileAttributesInGuest{
		This:           m.Reference(),
		Vm:             vm.Reference(),
		Auth:           auth,
		GuestFilePath:  guestFilePath,
		FileAttributes: fileAttributes,
	}

	_, err := methods.ChangeFileAttributesInGuest(m.c, &req)
	return err
}

func (m GuestFileManager) CreateTemporaryDirectoryInGuest(vm *VirtualMachine, auth types.BaseGuestAuthentication, prefix, suffix string) (string, error) {
	req := types.CreateTemporaryDirectoryInGuest{
		This:   m.Reference(),
		Vm:     vm.Reference(),
		Auth:   auth,
		Prefix: prefix,
		Suffix: suffix,
	}

	res, err := methods.CreateTemporaryDirectoryInGuest(m.c, &req)
	if err != nil {
		return "", err
	}

	return res.Returnval, nil
}

func (m GuestFileManager) CreateTemporaryFileInGuest(vm *VirtualMachine, auth types.BaseGuestAuthentication, prefix, suffix string) (string, error) {
	req := types.CreateTemporaryFileInGuest{
		This:   m.Reference(),
		Vm:     vm.Reference(),
		Auth:   auth,
		Prefix: prefix,
		Suffix: suffix,
	}

	res, err := methods.CreateTemporaryFileInGuest(m.c, &req)
	if err != nil {
		return "", err
	}

	return res.Returnval, nil
}

func (m GuestFileManager) DeleteDirectoryInGuest(vm *VirtualMachine, auth types.BaseGuestAuthentication, directoryPath string, recursive bool) error {
	req := types.DeleteDirectoryInGuest{
		This:          m.Reference(),
		Vm:            vm.Reference(),
		Auth:          auth,
		DirectoryPath: directoryPath,
		Recursive:     recursive,
	}

	_, err := methods.DeleteDirectoryInGuest(m.c, &req)
	return err
}

func (m GuestFileManager) DeleteFileInGuest(vm *VirtualMachine, auth types.BaseGuestAuthentication, filePath string, recursive bool) error {
	req := types.DeleteFileInGuest{
		This:     m.Reference(),
		Vm:       vm.Reference(),
		Auth:     auth,
		FilePath: filePath,
	}

	_, err := methods.DeleteFileInGuest(m.c, &req)
	return err
}

func (m GuestFileManager) InitiateFileTransferFromGuest(vm *VirtualMachine, auth types.BaseGuestAuthentication, guestFilePath string) (*types.FileTransferInformation, error) {
	req := types.InitiateFileTransferFromGuest{
		This:          m.Reference(),
		Vm:            vm.Reference(),
		Auth:          auth,
		GuestFilePath: guestFilePath,
	}

	res, err := methods.InitiateFileTransferFromGuest(m.c, &req)
	if err != nil {
		return nil, err
	}

	return &res.Returnval, nil
}

func (m GuestFileManager) InitiateFileTransferToGuest(vm *VirtualMachine, auth types.BaseGuestAuthentication, guestFilePath string, fileAttributes types.BaseGuestFileAttributes, fileSize int64, overwrite bool) (string, error) {
	req := types.InitiateFileTransferToGuest{
		This:           m.Reference(),
		Vm:             vm.Reference(),
		Auth:           auth,
		GuestFilePath:  guestFilePath,
		FileAttributes: fileAttributes,
		FileSize:       fileSize,
		Overwrite:      overwrite,
	}

	res, err := methods.InitiateFileTransferToGuest(m.c, &req)
	if err != nil {
		return "", err
	}

	return res.Returnval, nil
}
<commit_msg>More GuestFileManager wrappers<commit_after>/*
Copyright (c) 2014 VMware, Inc. All Rights Reserved.

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

    http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

package govmomi

import (
	"github.com/vmware/govmomi/vim25/methods"
	"github.com/vmware/govmomi/vim25/types"
)

type GuestFileManager struct {
	types.ManagedObjectReference

	c *Client
}

func (m GuestFileManager) Reference() types.ManagedObjectReference {
	return m.ManagedObjectReference
}

func (m GuestFileManager) ChangeFileAttributesInGuest(vm *VirtualMachine, auth types.BaseGuestAuthentication, guestFilePath string, fileAttributes types.BaseGuestFileAttributes) error {
	req := types.ChangeFileAttributesInGuest{
		This:           m.Reference(),
		Vm:             vm.Reference(),
		Auth:           auth,
		GuestFilePath:  guestFilePath,
		FileAttributes: fileAttributes,
	}

	_, err := methods.ChangeFileAttributesInGuest(m.c, &req)
	return err
}

func (m GuestFileManager) CreateTemporaryDirectoryInGuest(vm *VirtualMachine, auth types.BaseGuestAuthentication, prefix, suffix string) (string, error) {
	req := types.CreateTemporaryDirectoryInGuest{
		This:   m.Reference(),
		Vm:     vm.Reference(),
		Auth:   auth,
		Prefix: prefix,
		Suffix: suffix,
	}

	res, err := methods.CreateTemporaryDirectoryInGuest(m.c, &req)
	if err != nil {
		return "", err
	}

	return res.Returnval, nil
}

func (m GuestFileManager) CreateTemporaryFileInGuest(vm *VirtualMachine, auth types.BaseGuestAuthentication, prefix, suffix string) (string, error) {
	req := types.CreateTemporaryFileInGuest{
		This:   m.Reference(),
		Vm:     vm.Reference(),
		Auth:   auth,
		Prefix: prefix,
		Suffix: suffix,
	}

	res, err := methods.CreateTemporaryFileInGuest(m.c, &req)
	if err != nil {
		return "", err
	}

	return res.Returnval, nil
}

func (m GuestFileManager) DeleteDirectoryInGuest(vm *VirtualMachine, auth types.BaseGuestAuthentication, directoryPath string, recursive bool) error {
	req := types.DeleteDirectoryInGuest{
		This:          m.Reference(),
		Vm:            vm.Reference(),
		Auth:          auth,
		DirectoryPath: directoryPath,
		Recursive:     recursive,
	}

	_, err := methods.DeleteDirectoryInGuest(m.c, &req)
	return err
}

func (m GuestFileManager) DeleteFileInGuest(vm *VirtualMachine, auth types.BaseGuestAuthentication, filePath string, recursive bool) error {
	req := types.DeleteFileInGuest{
		This:     m.Reference(),
		Vm:       vm.Reference(),
		Auth:     auth,
		FilePath: filePath,
	}

	_, err := methods.DeleteFileInGuest(m.c, &req)
	return err
}

func (m GuestFileManager) InitiateFileTransferFromGuest(vm *VirtualMachine, auth types.BaseGuestAuthentication, guestFilePath string) (*types.FileTransferInformation, error) {
	req := types.InitiateFileTransferFromGuest{
		This:          m.Reference(),
		Vm:            vm.Reference(),
		Auth:          auth,
		GuestFilePath: guestFilePath,
	}

	res, err := methods.InitiateFileTransferFromGuest(m.c, &req)
	if err != nil {
		return nil, err
	}

	return &res.Returnval, nil
}

func (m GuestFileManager) InitiateFileTransferToGuest(vm *VirtualMachine, auth types.BaseGuestAuthentication, guestFilePath string, fileAttributes types.BaseGuestFileAttributes, fileSize int64, overwrite bool) (string, error) {
	req := types.InitiateFileTransferToGuest{
		This:           m.Reference(),
		Vm:             vm.Reference(),
		Auth:           auth,
		GuestFilePath:  guestFilePath,
		FileAttributes: fileAttributes,
		FileSize:       fileSize,
		Overwrite:      overwrite,
	}

	res, err := methods.InitiateFileTransferToGuest(m.c, &req)
	if err != nil {
		return "", err
	}

	return res.Returnval, nil
}

func (m GuestFileManager) ListFilesInGuest(vm *VirtualMachine, auth types.BaseGuestAuthentication, filePath string, index int, maxResults int, matchPattern string) (*types.GuestListFileInfo, error) {
	req := types.ListFilesInGuest{
		This:         m.Reference(),
		Vm:           vm.Reference(),
		Auth:         auth,
		FilePath:     filePath,
		Index:        index,
		MaxResults:   maxResults,
		MatchPattern: matchPattern,
	}

	res, err := methods.ListFilesInGuest(m.c, &req)
	if err != nil {
		return nil, err
	}

	return &res.Returnval, nil
}

func (m GuestFileManager) MakeDirectoryInGuest(vm *VirtualMachine, auth types.BaseGuestAuthentication, directoryPath string, createParentDirectories bool) error {
	req := types.MakeDirectoryInGuest{
		This:                    m.Reference(),
		Vm:                      vm.Reference(),
		Auth:                    auth,
		DirectoryPath:           directoryPath,
		CreateParentDirectories: createParentDirectories,
	}

	_, err := methods.MakeDirectoryInGuest(m.c, &req)
	return err
}

func (m GuestFileManager) MoveDirectoryInGuest(vm *VirtualMachine, auth types.BaseGuestAuthentication, srcDirectoryPath string, dstDirectoryPath string) error {
	req := types.MoveDirectoryInGuest{
		This:             m.Reference(),
		Vm:               vm.Reference(),
		Auth:             auth,
		SrcDirectoryPath: srcDirectoryPath,
		DstDirectoryPath: dstDirectoryPath,
	}

	_, err := methods.MoveDirectoryInGuest(m.c, &req)
	return err
}

func (m GuestFileManager) MoveFileInGuest(vm *VirtualMachine, auth types.BaseGuestAuthentication, srcFilePath string, dstFilePath string, overwrite bool) error {
	req := types.MoveFileInGuest{
		This:        m.Reference(),
		Vm:          vm.Reference(),
		Auth:        auth,
		SrcFilePath: srcFilePath,
		DstFilePath: dstFilePath,
		Overwrite:   overwrite,
	}

	_, err := methods.MoveFileInGuest(m.c, &req)
	return err
}
<|endoftext|>


<commit_before>// Copyright 2019 The Ebiten Authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

//go:build darwin && !ios
// +build darwin,!ios

package metal

// #cgo CFLAGS: -x objective-c
// #cgo LDFLAGS: -framework Foundation
//
// #import <Foundation/Foundation.h>
//
// static int getMacOSMajorVersion() {
//   NSOperatingSystemVersion version = [[NSProcessInfo processInfo] operatingSystemVersion];
//   return (int)version.majorVersion;
// }
import "C"

import (
	"github.com/hajimehoshi/ebiten/v2/internal/graphicsdriver/metal/mtl"
	"github.com/hajimehoshi/ebiten/v2/internal/graphicsdriver/metal/ns"
)

var macOSMajorVersion = int(C.getMacOSMajorVersion())

func (v *view) setWindow(window uintptr) {
	// NSView can be updated e.g., fullscreen-state is switched.
	v.window = window
	v.windowChanged = true
}

func (v *view) setUIView(uiview uintptr) {
	panic("metal: setUIView is not available on macOS")
}

func (v *view) update() {
	if !v.windowChanged {
		return
	}

	cocoaWindow := ns.NewWindow(v.window)
	cocoaWindow.ContentView().SetLayer(v.ml)
	cocoaWindow.ContentView().SetWantsLayer(true)
	v.windowChanged = false
}

func (v *view) usePresentsWithTransaction() bool {
	// On macOS 12 (or later), do not use presentsWithTransaction, or vsync doesn't work (#1885).
	// This works only for Metal. Unfortunately, there is not a good solution for OpenGL.
	if macOSMajorVersion >= 12 {
		return false
	}

	// Disable presentsWithTransaction on the fullscreen mode (#1745).
	return !v.vsyncDisabled
}

func (v *view) maximumDrawableCount() int {
	// When presentsWithTransaction is YES and triple buffering is enabled, nextDrawing returns immediately once every two times.
	// This makes FPS doubled. To avoid this, disable the triple buffering.
	if v.usePresentsWithTransaction() {
		return 2
	}
	return 3
}

const (
	storageMode         = mtl.StorageModeManaged
	resourceStorageMode = mtl.ResourceStorageModeManaged
)
<commit_msg>Revert "internal/graphicsdriver/metal: Bug fix: Vsync didn't work on macOS"<commit_after>// Copyright 2019 The Ebiten Authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

//go:build darwin && !ios
// +build darwin,!ios

package metal

import (
	"github.com/hajimehoshi/ebiten/v2/internal/graphicsdriver/metal/mtl"
	"github.com/hajimehoshi/ebiten/v2/internal/graphicsdriver/metal/ns"
)

func (v *view) setWindow(window uintptr) {
	// NSView can be updated e.g., fullscreen-state is switched.
	v.window = window
	v.windowChanged = true
}

func (v *view) setUIView(uiview uintptr) {
	panic("metal: setUIView is not available on macOS")
}

func (v *view) update() {
	if !v.windowChanged {
		return
	}

	cocoaWindow := ns.NewWindow(v.window)
	cocoaWindow.ContentView().SetLayer(v.ml)
	cocoaWindow.ContentView().SetWantsLayer(true)
	v.windowChanged = false
}

func (v *view) usePresentsWithTransaction() bool {
	// Disable presentsWithTransaction on the fullscreen mode (#1745).
	return !v.vsyncDisabled
}

func (v *view) maximumDrawableCount() int {
	// When presentsWithTransaction is YES and triple buffering is enabled, nextDrawing returns immediately once every two times.
	// This makes FPS doubled. To avoid this, disable the triple buffering.
	if v.usePresentsWithTransaction() {
		return 2
	}
	return 3
}

const (
	storageMode         = mtl.StorageModeManaged
	resourceStorageMode = mtl.ResourceStorageModeManaged
)
<|endoftext|>


<commit_before>package provider

import (
	"context"
	"fmt"
	"regexp"
	"testing"

	"github.com/google/go-cmp/cmp"
	"github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource"
	"github.com/hashicorp/terraform-plugin-sdk/v2/terraform"
)

func TestAccResourcePasswordBasic(t *testing.T) {
	resource.UnitTest(t, resource.TestCase{
		PreCheck:          func() { testAccPreCheck(t) },
		ProviderFactories: testAccProviders,
		Steps: []resource.TestStep{
			{
				Config: `resource "random_password" "basic" {
							length = 12
						}`,
				Check: resource.ComposeTestCheckFunc(
					testAccResourceStringCheck("random_password.basic", &customLens{
						customLen: 12,
					}),
				),
			},
			{
				ResourceName: "random_password.basic",
				// Usage of ImportStateIdFunc is required as the value passed to the `terraform import` command needs
				// to be the password itself, as the password resource sets ID to "none" and "result" to the password
				// supplied during import.
				ImportStateIdFunc: func(s *terraform.State) (string, error) {
					id := "random_password.basic"
					rs, ok := s.RootModule().Resources[id]
					if !ok {
						return "", fmt.Errorf("not found: %s", id)
					}
					if rs.Primary.ID == "" {
						return "", fmt.Errorf("no ID is set")
					}

					return rs.Primary.Attributes["result"], nil
				},
				ImportState:             true,
				ImportStateVerify:       true,
				ImportStateVerifyIgnore: []string{"bcrypt_hash", "length", "lower", "number", "special", "upper", "min_lower", "min_numeric", "min_special", "min_upper", "override_special"},
			},
		},
	})
}

func TestAccResourcePasswordOverride(t *testing.T) {
	resource.UnitTest(t, resource.TestCase{
		PreCheck:          func() { testAccPreCheck(t) },
		ProviderFactories: testAccProviders,
		Steps: []resource.TestStep{
			{
				Config: `resource "random_password" "override" {
							length = 4
							override_special = "!"
							lower = false
							upper = false
							number = false
						}`,
				Check: resource.ComposeTestCheckFunc(
					testAccResourceStringCheck("random_password.override", &customLens{
						customLen: 4,
					}),
					patternMatch("random_password.override", "!!!!"),
				),
			},
		},
	})
}

func TestAccResourcePasswordMin(t *testing.T) {
	resource.UnitTest(t, resource.TestCase{
		PreCheck:          func() { testAccPreCheck(t) },
		ProviderFactories: testAccProviders,
		Steps: []resource.TestStep{
			{
				Config: `resource "random_password" "min" {
							length = 12
							override_special = "!#@"
							min_lower = 2
							min_upper = 3
							min_special = 1
							min_numeric = 4
						}`,
				Check: resource.ComposeTestCheckFunc(
					testAccResourceStringCheck("random_password.min", &customLens{
						customLen: 12,
					}),
					regexMatch("random_password.min", regexp.MustCompile(`([a-z])`), 2),
					regexMatch("random_password.min", regexp.MustCompile(`([A-Z])`), 3),
					regexMatch("random_password.min", regexp.MustCompile(`([0-9])`), 4),
					regexMatch("random_password.min", regexp.MustCompile(`([!#@])`), 1),
				),
			},
		},
	})
}

func TestResourcePasswordStateUpgradeV0(t *testing.T) {
	cases := []struct {
		name            string
		stateV0         map[string]interface{}
		shouldError     bool
		errMsg          string
		expectedStateV1 map[string]interface{}
	}{
		{
			name:        "result is not string",
			stateV0:     map[string]interface{}{"result": 0},
			shouldError: true,
			errMsg:      "resource password state upgrade failed, result could not be asserted as string: int",
		},
		{
			name:            "success",
			stateV0:         map[string]interface{}{"result": "abc123"},
			shouldError:     false,
			expectedStateV1: map[string]interface{}{"result": "abc123", "bcrypt_hash": "123"},
		},
	}

	for _, c := range cases {
		t.Run(c.name, func(t *testing.T) {
			actualStateV1, err := resourcePasswordStateUpgradeV0(context.Background(), c.stateV0, nil)

			if c.shouldError {
				if !cmp.Equal(c.errMsg, err.Error()) {
					t.Errorf("expected: %q, got: %q", c.errMsg, err)
				}
				if !cmp.Equal(c.expectedStateV1, actualStateV1) {
					t.Errorf("expected: %+v, got: %+v", c.expectedStateV1, err)
				}
			} else {
				if err != nil {
					t.Errorf("err should be nil, actual: %v", err)
				}

				for k := range c.expectedStateV1 {
					_, ok := actualStateV1[k]
					if !ok {
						t.Errorf("expected key: %s is missing from state", k)
					}
				}
			}
		})
	}
}

func TestResourcePasswordStateUpgradeV1(t *testing.T) {
	cases := []struct {
		name            string
		stateV1         map[string]interface{}
		shouldError     bool
		errMsg          string
		expectedStateV2 map[string]interface{}
	}{
		{
			name:        "number is not bool",
			stateV1:     map[string]interface{}{"number": 0},
			shouldError: true,
			errMsg:      "resource password state upgrade failed, number could not be asserted as bool: int",
		},
		{
			name:            "success",
			stateV1:         map[string]interface{}{"number": true},
			shouldError:     false,
			expectedStateV2: map[string]interface{}{"number": true, "numeric": true},
		},
	}

	for _, c := range cases {
		t.Run(c.name, func(t *testing.T) {
			actualStateV2, err := resourcePasswordStateUpgradeV1(context.Background(), c.stateV1, nil)

			if c.shouldError {
				if !cmp.Equal(c.errMsg, err.Error()) {
					t.Errorf("expected: %q, got: %q", c.errMsg, err)
				}
				if !cmp.Equal(c.expectedStateV2, actualStateV2) {
					t.Errorf("expected: %+v, got: %+v", c.expectedStateV2, err)
				}
			} else {
				if err != nil {
					t.Errorf("err should be nil, actual: %v", err)
				}

				for k := range c.expectedStateV2 {
					_, ok := actualStateV2[k]
					if !ok {
						t.Errorf("expected key: %s is missing from state", k)
					}
				}
			}
		})
	}
}
<commit_msg>Adding test for updating number and numeric<commit_after>package provider

import (
	"context"
	"fmt"
	"regexp"
	"testing"

	"github.com/google/go-cmp/cmp"
	"github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource"
	"github.com/hashicorp/terraform-plugin-sdk/v2/terraform"
)

func TestAccResourcePasswordBasic(t *testing.T) {
	resource.UnitTest(t, resource.TestCase{
		PreCheck:          func() { testAccPreCheck(t) },
		ProviderFactories: testAccProviders,
		Steps: []resource.TestStep{
			{
				Config: `resource "random_password" "basic" {
							length = 12
						}`,
				Check: resource.ComposeTestCheckFunc(
					testAccResourceStringCheck("random_password.basic", &customLens{
						customLen: 12,
					}),
				),
			},
			{
				ResourceName: "random_password.basic",
				// Usage of ImportStateIdFunc is required as the value passed to the `terraform import` command needs
				// to be the password itself, as the password resource sets ID to "none" and "result" to the password
				// supplied during import.
				ImportStateIdFunc: func(s *terraform.State) (string, error) {
					id := "random_password.basic"
					rs, ok := s.RootModule().Resources[id]
					if !ok {
						return "", fmt.Errorf("not found: %s", id)
					}
					if rs.Primary.ID == "" {
						return "", fmt.Errorf("no ID is set")
					}

					return rs.Primary.Attributes["result"], nil
				},
				ImportState:             true,
				ImportStateVerify:       true,
				ImportStateVerifyIgnore: []string{"bcrypt_hash", "length", "lower", "number", "special", "upper", "min_lower", "min_numeric", "min_special", "min_upper", "override_special"},
			},
		},
	})
}

func TestAccResourcePasswordOverride(t *testing.T) {
	resource.UnitTest(t, resource.TestCase{
		PreCheck:          func() { testAccPreCheck(t) },
		ProviderFactories: testAccProviders,
		Steps: []resource.TestStep{
			{
				Config: `resource "random_password" "override" {
							length = 4
							override_special = "!"
							lower = false
							upper = false
							number = false
						}`,
				Check: resource.ComposeTestCheckFunc(
					testAccResourceStringCheck("random_password.override", &customLens{
						customLen: 4,
					}),
					patternMatch("random_password.override", "!!!!"),
				),
			},
		},
	})
}

func TestAccResourcePasswordMin(t *testing.T) {
	resource.UnitTest(t, resource.TestCase{
		PreCheck:          func() { testAccPreCheck(t) },
		ProviderFactories: testAccProviders,
		Steps: []resource.TestStep{
			{
				Config: `resource "random_password" "min" {
							length = 12
							override_special = "!#@"
							min_lower = 2
							min_upper = 3
							min_special = 1
							min_numeric = 4
						}`,
				Check: resource.ComposeTestCheckFunc(
					testAccResourceStringCheck("random_password.min", &customLens{
						customLen: 12,
					}),
					regexMatch("random_password.min", regexp.MustCompile(`([a-z])`), 2),
					regexMatch("random_password.min", regexp.MustCompile(`([A-Z])`), 3),
					regexMatch("random_password.min", regexp.MustCompile(`([0-9])`), 4),
					regexMatch("random_password.min", regexp.MustCompile(`([!#@])`), 1),
				),
			},
		},
	})
}

func TestAccResourcePassword_UpdateNumberAndNumeric(t *testing.T) {
	t.Parallel()
	resource.UnitTest(t, resource.TestCase{
		PreCheck:          func() { testAccPreCheck(t) },
		ProviderFactories: testAccProviders,
		Steps: []resource.TestStep{
			{
				Config: `resource "random_password" "default" {
							length = 12
						}`,
				Check: resource.ComposeTestCheckFunc(
					resource.TestCheckResourceAttr("random_password.default", "number", "true"),
					resource.TestCheckResourceAttr("random_password.default", "numeric", "true"),
				),
			},
			{
				Config: `resource "random_password" "default" {
							length = 12
							number = false
						}`,
				Check: resource.ComposeTestCheckFunc(
					resource.TestCheckResourceAttr("random_password.default", "number", "false"),
					resource.TestCheckResourceAttr("random_password.default", "numeric", "false"),
				),
			},
			{
				Config: `resource "random_password" "default" {
							length = 12
							numeric = true
						}`,
				Check: resource.ComposeTestCheckFunc(
					resource.TestCheckResourceAttr("random_password.default", "number", "true"),
					resource.TestCheckResourceAttr("random_password.default", "numeric", "true"),
				),
			},
			{
				Config: `resource "random_password" "default" {
							length = 12
							numeric = false
						}`,
				Check: resource.ComposeTestCheckFunc(
					resource.TestCheckResourceAttr("random_password.default", "number", "false"),
					resource.TestCheckResourceAttr("random_password.default", "numeric", "false"),
				),
			},
			{
				Config: `resource "random_password" "default" {
							length = 12
						}`,
				Check: resource.ComposeTestCheckFunc(
					resource.TestCheckResourceAttr("random_password.default", "number", "true"),
					resource.TestCheckResourceAttr("random_password.default", "numeric", "true"),
				),
			},
		},
	})
}

func TestResourcePasswordStateUpgradeV0(t *testing.T) {
	cases := []struct {
		name            string
		stateV0         map[string]interface{}
		shouldError     bool
		errMsg          string
		expectedStateV1 map[string]interface{}
	}{
		{
			name:        "result is not string",
			stateV0:     map[string]interface{}{"result": 0},
			shouldError: true,
			errMsg:      "resource password state upgrade failed, result could not be asserted as string: int",
		},
		{
			name:            "success",
			stateV0:         map[string]interface{}{"result": "abc123"},
			shouldError:     false,
			expectedStateV1: map[string]interface{}{"result": "abc123", "bcrypt_hash": "123"},
		},
	}

	for _, c := range cases {
		t.Run(c.name, func(t *testing.T) {
			actualStateV1, err := resourcePasswordStateUpgradeV0(context.Background(), c.stateV0, nil)

			if c.shouldError {
				if !cmp.Equal(c.errMsg, err.Error()) {
					t.Errorf("expected: %q, got: %q", c.errMsg, err)
				}
				if !cmp.Equal(c.expectedStateV1, actualStateV1) {
					t.Errorf("expected: %+v, got: %+v", c.expectedStateV1, err)
				}
			} else {
				if err != nil {
					t.Errorf("err should be nil, actual: %v", err)
				}

				for k := range c.expectedStateV1 {
					_, ok := actualStateV1[k]
					if !ok {
						t.Errorf("expected key: %s is missing from state", k)
					}
				}
			}
		})
	}
}

func TestResourcePasswordStateUpgradeV1(t *testing.T) {
	cases := []struct {
		name            string
		stateV1         map[string]interface{}
		shouldError     bool
		errMsg          string
		expectedStateV2 map[string]interface{}
	}{
		{
			name:        "number is not bool",
			stateV1:     map[string]interface{}{"number": 0},
			shouldError: true,
			errMsg:      "resource password state upgrade failed, number could not be asserted as bool: int",
		},
		{
			name:            "success",
			stateV1:         map[string]interface{}{"number": true},
			shouldError:     false,
			expectedStateV2: map[string]interface{}{"number": true, "numeric": true},
		},
	}

	for _, c := range cases {
		t.Run(c.name, func(t *testing.T) {
			actualStateV2, err := resourcePasswordStateUpgradeV1(context.Background(), c.stateV1, nil)

			if c.shouldError {
				if !cmp.Equal(c.errMsg, err.Error()) {
					t.Errorf("expected: %q, got: %q", c.errMsg, err)
				}
				if !cmp.Equal(c.expectedStateV2, actualStateV2) {
					t.Errorf("expected: %+v, got: %+v", c.expectedStateV2, err)
				}
			} else {
				if err != nil {
					t.Errorf("err should be nil, actual: %v", err)
				}

				for k := range c.expectedStateV2 {
					_, ok := actualStateV2[k]
					if !ok {
						t.Errorf("expected key: %s is missing from state", k)
					}
				}
			}
		})
	}
}
<|endoftext|>


<commit_before>package restclient

import (
	"bytes"
	"fmt"
	"net/http"
	"strings"

	log "github.com/Sirupsen/logrus"
)

type gubleSender struct {
	Endpoint   string
	httpClient *http.Client
}

// New returns a new Sender.
func New(endpoint string) Sender {
	return &gubleSender{
		Endpoint:   endpoint,
		httpClient: &http.Client{},
	}
}

func (gs gubleSender) Check() bool {
	request, err := http.NewRequest(http.MethodHead, gs.Endpoint, nil)
	if err != nil {
		logger.WithError(err).Error("error creating request url")
		return false
	}
	response, err := gs.httpClient.Do(request)
	if err != nil {
		logger.WithError(err).Error("error reaching guble server endpoint")
		return false
	}
	defer response.Body.Close()
	return response.StatusCode == http.StatusOK
}

func (gs gubleSender) Send(topic string, body []byte, userID string) error {
	url := fmt.Sprintf("%s%s?userId=%s",
		strings.TrimPrefix(gs.Endpoint, "/"), topic, userID)
	request, err := http.NewRequest(http.MethodPost, url, bytes.NewReader(body))
	if err != nil {
		return err
	}
	response, err := gs.httpClient.Do(request)
	if err != nil {
		return err
	}
	defer response.Body.Close()

	if response.StatusCode != http.StatusOK {
		logger.WithFields(log.Fields{
			"header": response.Header,
			"code":   response.StatusCode,
			"status": response.Status,
		}).Error("Guble response error")
		return fmt.Errorf("Error code returned from guble: %d", response.StatusCode)
	}
	return nil
}
<commit_msg>updating rest client to to insert / and use filter<commit_after>package restclient

import (
	"bytes"
	"fmt"
	"net/http"
	"strings"

	log "github.com/Sirupsen/logrus"
)

type gubleSender struct {
	Endpoint   string
	httpClient *http.Client
}

// New returns a new Sender.
func New(endpoint string) Sender {
	return &gubleSender{
		Endpoint:   endpoint,
		httpClient: &http.Client{},
	}
}

func (gs gubleSender) Check() bool {
	request, err := http.NewRequest(http.MethodHead, gs.Endpoint, nil)
	if err != nil {
		logger.WithError(err).Error("error creating request url")
		return false
	}
	response, err := gs.httpClient.Do(request)
	if err != nil {
		logger.WithError(err).Error("error reaching guble server endpoint")
		return false
	}
	defer response.Body.Close()
	return response.StatusCode == http.StatusOK
}

func (gs gubleSender) Send(topic string, body []byte, userID string) error {
	url := fmt.Sprintf("%s/%s?userId=%s&filterUserID",
		strings.TrimPrefix(gs.Endpoint, "/"), topic, userID)
	request, err := http.NewRequest(http.MethodPost, url, bytes.NewReader(body))
	if err != nil {
		return err
	}
	response, err := gs.httpClient.Do(request)
	if err != nil {
		return err
	}
	defer response.Body.Close()

	if response.StatusCode != http.StatusOK {
		logger.WithFields(log.Fields{
			"header": response.Header,
			"code":   response.StatusCode,
			"status": response.Status,
		}).Error("Guble response error")
		return fmt.Errorf("Error code returned from guble: %d", response.StatusCode)
	}
	return nil
}
<|endoftext|>


<commit_before>package restclient

import (
	"bytes"
	"fmt"
	"net/http"
	"strings"

	log "github.com/Sirupsen/logrus"
)

type gubleSender struct {
	Endpoint   string
	httpClient *http.Client
}

// New returns a new Sender.
func New(endpoint string) Sender {
	return &gubleSender{
		Endpoint:   endpoint,
		httpClient: &http.Client{},
	}
}

func (gs gubleSender) Check() bool {
	request, err := http.NewRequest(http.MethodHead, gs.Endpoint, nil)
	if err != nil {
		logger.WithError(err).Error("error creating request url")
		return false
	}
	response, err := gs.httpClient.Do(request)
	if err != nil {
		logger.WithError(err).Error("error reaching guble server endpoint")
		return false
	}
	return response.StatusCode == http.StatusOK
}

func (gs gubleSender) Send(topic string, body []byte, userID string) error {
	url := fmt.Sprintf("%s%s?userId=%s",
		strings.TrimPrefix(gs.Endpoint, "/"), topic, userID)
	request, err := http.NewRequest(http.MethodPost, url, bytes.NewReader(body))
	if err != nil {
		return err
	}
	response, err := gs.httpClient.Do(request)
	if err != nil {
		return err
	}
	defer response.Body.Close()

	if response.StatusCode != http.StatusOK {
		logger.WithFields(log.Fields{
			"header": response.Header,
			"code":   response.StatusCode,
			"status": response.Status,
		}).Error("Guble response error")
		return fmt.Errorf("Error code returned from guble: %d", response.StatusCode)
	}
	return nil
}
<commit_msg>adding Close() in restclient Check()<commit_after>package restclient

import (
	"bytes"
	"fmt"
	"net/http"
	"strings"

	log "github.com/Sirupsen/logrus"
)

type gubleSender struct {
	Endpoint   string
	httpClient *http.Client
}

// New returns a new Sender.
func New(endpoint string) Sender {
	return &gubleSender{
		Endpoint:   endpoint,
		httpClient: &http.Client{},
	}
}

func (gs gubleSender) Check() bool {
	request, err := http.NewRequest(http.MethodHead, gs.Endpoint, nil)
	if err != nil {
		logger.WithError(err).Error("error creating request url")
		return false
	}
	response, err := gs.httpClient.Do(request)
	if err != nil {
		logger.WithError(err).Error("error reaching guble server endpoint")
		return false
	}
	defer response.Body.Close()
	return response.StatusCode == http.StatusOK
}

func (gs gubleSender) Send(topic string, body []byte, userID string) error {
	url := fmt.Sprintf("%s%s?userId=%s",
		strings.TrimPrefix(gs.Endpoint, "/"), topic, userID)
	request, err := http.NewRequest(http.MethodPost, url, bytes.NewReader(body))
	if err != nil {
		return err
	}
	response, err := gs.httpClient.Do(request)
	if err != nil {
		return err
	}
	defer response.Body.Close()

	if response.StatusCode != http.StatusOK {
		logger.WithFields(log.Fields{
			"header": response.Header,
			"code":   response.StatusCode,
			"status": response.Status,
		}).Error("Guble response error")
		return fmt.Errorf("Error code returned from guble: %d", response.StatusCode)
	}
	return nil
}
<|endoftext|>


<commit_before>// Copyright 2013 Google Inc.  All rights reserved.
// Copyright 2016 the gousb Authors.  All rights reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

// rawread attempts to read from the specified USB device.
package main

import (
	"flag"
	"fmt"
	"log"

	"github.com/kylelemons/gousb/usb"
	"github.com/kylelemons/gousb/usbid"
)

var (
	device   = flag.String("device", "vend:prod", "Device to which to connect")
	config   = flag.Int("config", 1, "Endpoint to which to connect")
	iface    = flag.Int("interface", 0, "Endpoint to which to connect")
	setup    = flag.Int("setup", 0, "Endpoint to which to connect")
	endpoint = flag.Int("endpoint", 1, "Endpoint to which to connect")
	debug    = flag.Int("debug", 3, "Debug level for libusb")
)

func main() {
	flag.Parse()

	// Only one context should be needed for an application.  It should always be closed.
	ctx := usb.NewContext()
	defer ctx.Close()

	ctx.Debug(*debug)

	log.Printf("Scanning for device %q...", *device)

	// ListDevices is used to find the devices to open.
	devs, err := ctx.ListDevices(func(desc *usb.Descriptor) bool {
		if fmt.Sprintf("%s:%s", desc.Vendor, desc.Product) != *device {
			return false
		}

		// The usbid package can be used to print out human readable information.
		fmt.Printf("  Protocol: %s\n", usbid.Classify(desc))

		// The configurations can be examined from the Descriptor, though they can only
		// be set once the device is opened.  All configuration references must be closed,
		// to free up the memory in libusb.
		for _, cfg := range desc.Configs {
			// This loop just uses more of the built-in and usbid pretty printing to list
			// the USB devices.
			fmt.Printf("  %s:\n", cfg)
			for _, alt := range cfg.Interfaces {
				fmt.Printf("    --------------\n")
				for _, iface := range alt.Setups {
					fmt.Printf("    %s\n", iface)
					fmt.Printf("      %s\n", usbid.Classify(iface))
					for _, end := range iface.Endpoints {
						fmt.Printf("      %s\n", end)
					}
				}
			}
			fmt.Printf("    --------------\n")
		}

		return true
	})

	// All Devices returned from ListDevices must be closed.
	defer func() {
		for _, d := range devs {
			d.Close()
		}
	}()

	// ListDevices can occaionally fail, so be sure to check its return value.
	if err != nil {
		log.Fatalf("list: %s", err)
	}

	if len(devs) == 0 {
		log.Fatalf("no devices found")
	}

	dev := devs[0]

	log.Printf("Connecting to endpoint...")
	log.Printf("- %#v", dev.Descriptor)
	ep, err := dev.OpenEndpoint(uint8(*config), uint8(*iface), uint8(*setup), uint8(*endpoint)|uint8(usb.ENDPOINT_DIR_IN))
	if err != nil {
		log.Fatalf("open: %s", err)
	}
	_ = ep
}
<commit_msg>Change how device is addressed, add alternative --bus/--addr flags. Print all diagnostics to the log (stderr). Capture the data from the specified endpoint and print it to stdout.<commit_after>// Copyright 2013 Google Inc.  All rights reserved.
// Copyright 2016 the gousb Authors.  All rights reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

// rawread attempts to read from the specified USB device.
package main

import (
	"flag"
	"fmt"
	"log"
	"os"

	"github.com/kylelemons/gousb/usb"
	"github.com/kylelemons/gousb/usbid"
)

var (
	vid      = flag.Uint("vid", 0, "VID of the device to which to connect. Exclusive with bus/addr flags.")
	pid      = flag.Uint("pid", 0, "PID of the device to which to connect. Exclusive with bus/addr flags.")
	bus      = flag.Uint("bus", 0, "Bus number for the device to which to connect. Exclusive with vid/pid flags.")
	addr     = flag.Uint("addr", 0, "Address of the device to which to connect. Exclusive with vid/pid flags.")
	config   = flag.Uint("config", 1, "Endpoint to which to connect")
	iface    = flag.Uint("interface", 0, "Endpoint to which to connect")
	setup    = flag.Uint("setup", 0, "Endpoint to which to connect")
	endpoint = flag.Uint("endpoint", 1, "Endpoint to which to connect")
	debug    = flag.Int("debug", 3, "Debug level for libusb")
	size     = flag.Uint("read_size", 1024, "Maximum number of bytes of data to read. Collected will be printed to STDOUT.")
)

func main() {
	flag.Parse()

	// Only one context should be needed for an application.  It should always be closed.
	ctx := usb.NewContext()
	defer ctx.Close()

	ctx.Debug(*debug)

	var devName string
	switch {
	case *vid == 0 && *pid == 0 && *bus == 0 && *addr == 0:
		log.Fatal("You need to specify the device, either through --vid/--pid flags or through --bus/--addr flags.")
	case (*vid > 0 || *pid > 0) && (*bus > 0 || *addr > 0):
		log.Fatal("You can't use --vid/--pid flags at the same time as --bus/--addr.")
	case *vid > 0 || *pid > 0:
		devName = fmt.Sprintf("VID:PID %04x:%04x", *vid, *pid)
	default:
		devName = fmt.Sprintf("bus:addr %d:%d", *bus, *addr)
	}

	log.Printf("Scanning for device %q...", devName)
	// ListDevices is used to find the devices to open.
	devs, err := ctx.ListDevices(func(desc *usb.Descriptor) bool {
		switch {
		case usb.ID(*vid) == desc.Vendor && usb.ID(*pid) == desc.Product:
			return true
		case uint8(*bus) == desc.Bus && uint8(*addr) == desc.Address:
			return true
		}
		return false
	})
	// All Devices returned from ListDevices must be closed.
	defer func() {
		for _, d := range devs {
			d.Close()
		}
	}()

	// ListDevices can occasionally fail, so be sure to check its return value.
	if err != nil {
		log.Printf("Warning: ListDevices: %s.", err)
	}
	switch {
	case len(devs) == 0:
		log.Fatal("No matching devices found.")
	case len(devs) > 1:
		log.Printf("Warning: multiple devices found. Using bus %d, addr %d.", devs[0].Bus, devs[0].Address)
		for _, d := range devs[1:] {
			d.Close()
		}
		devs = devs[:1]
	}
	dev := devs[0]

	// The usbid package can be used to print out human readable information.
	log.Printf("  Protocol: %s\n", usbid.Classify(dev.Descriptor))

	// The configurations can be examined from the Descriptor, though they can only
	// be set once the device is opened.  All configuration references must be closed,
	// to free up the memory in libusb.
	for _, cfg := range dev.Configs {
		// This loop just uses more of the built-in and usbid pretty printing to list
		// the USB devices.
		log.Printf("  %s:\n", cfg)
		for _, alt := range cfg.Interfaces {
			log.Printf("    --------------\n")
			for _, iface := range alt.Setups {
				log.Printf("    %s\n", iface)
				log.Printf("      %s\n", usbid.Classify(iface))
				for _, end := range iface.Endpoints {
					log.Printf("      %s\n", end)
				}
			}
		}
		log.Printf("    --------------\n")
	}

	log.Printf("Connecting to endpoint...")
	ep, err := dev.OpenEndpoint(uint8(*config), uint8(*iface), uint8(*setup), uint8(*endpoint)|uint8(usb.ENDPOINT_DIR_IN))
	if err != nil {
		log.Fatalf("open: %s", err)
	}

	buf := make([]byte, *size)
	num, err := ep.Read(buf)
	if err != nil {
		log.Fatalf("Reading from device failed: %v", err)
	}
	log.Printf("Read %d bytes of data", num)
	os.Stdout.Write(buf[:num])
}
<|endoftext|>


<commit_before>package kite

import (
	"flag"
	"fmt"
	"github.com/op/go-logging"
	"koding/newkite/dnode/rpc"
	"koding/newkite/protocol"
	"koding/newkite/utils"
	"log"
	"net"
	"net/http"
	"os"
	"os/signal"
	"runtime"
	"syscall"
	"time"
)

// Debugging helper.
func init() {
	// Print stacktrace on SIGUSR1.
	c := make(chan os.Signal)
	signal.Notify(c, syscall.SIGUSR1)
	go func() {
		for {
			s := <-c
			fmt.Println("Got signal:", s)
			buf := make([]byte, 1<<16)
			runtime.Stack(buf, true)
			fmt.Println(string(buf))
			fmt.Println("Number of goroutines:", runtime.NumGoroutine())
		}
	}()
}

// Kite defines a single process that enables distributed service messaging
// amongst the peers it is connected. A Kite process acts as a Client and as a
// Server. That means it can receive request, process them, but it also can
// make request to other kites. A Kite can be anything. It can be simple Image
// processing kite (which would process data), it could be a Chat kite that
// enables peer-to-peer chat. For examples we have FileSystem kite that expose
// the file system to a client, which in order build the filetree.
type Kite struct {
	protocol.Kite

	// KodingKey is used for authenticate to Kontrol.
	KodingKey string

	// Is this Kite Public or Private? Default is Private.
	Visibility protocol.Visibility

	// Points to the Kontrol instance if enabled
	Kontrol *Kontrol

	// Wheter we want to connect to Kontrol on startup, true by default.
	KontrolEnabled bool

	// Wheter we want to register our Kite to Kontrol, true by default.
	RegisterToKontrol bool

	// method map for exported methods
	handlers map[string]HandlerFunc

	// Dnode rpc server
	server *rpc.Server

	// Handlers to call when a Kite opens a connection to this Kite.
	onConnectHandlers []func(*RemoteKite)

	// Handlers to call when a client has disconnected.
	onDisconnectHandlers []func(*RemoteKite)

	// Contains different functions for authenticating user from request.
	// Keys are the authentication types (options.authentication.type).
	Authenticators map[string]func(*Request) error

	// Used to signal if the kite is ready to start and make calls to
	// other kites.
	ready chan bool

	// Prints logging messages to stderr and syslog.
	Log *logging.Logger
}

// New creates, initialize and then returns a new Kite instance. It accepts
// a single options argument that is a config struct that needs to be filled
// with several informations like Name, Port, IP and so on.
func New(options *Options) *Kite {
	var err error
	if options == nil {
		options, err = ReadKiteOptions("manifest.json")
		if err != nil {
			log.Fatal("error: could not read config file", err)
		}
	}

	options.validate() // exits if validating fails

	hostname, _ := os.Hostname()
	kiteID := utils.GenerateUUID()
	kodingKey, err := utils.GetKodingKey()
	if err != nil {
		log.Fatal("Couldn't find koding.key. Please run 'kd register'.")
	}

	k := &Kite{
		Kite: protocol.Kite{
			Name:        options.Kitename,
			Username:    options.Username,
			ID:          kiteID,
			Version:     options.Version,
			Hostname:    hostname,
			Port:        options.Port,
			Environment: options.Environment,
			Region:      options.Region,
			Visibility:  options.Visibility,

			// PublicIP will be set by Kontrol after registering if it is not set.
			PublicIP: options.PublicIP,
		},
		KodingKey:         kodingKey,
		server:            rpc.NewServer(),
		KontrolEnabled:    true,
		RegisterToKontrol: true,
		Authenticators:    make(map[string]func(*Request) error),
		handlers:          make(map[string]HandlerFunc),
		ready:             make(chan bool),
	}

	k.Log = newLogger(k.Name, k.hasDebugFlag())
	k.Kontrol = k.NewKontrol(options.KontrolAddr)

	// Call registered handlers when a client has disconnected.
	k.server.OnDisconnect(func(c *rpc.Client) {
		if r, ok := c.Properties()["remoteKite"]; ok {
			// Run OnDisconnect handlers.
			k.notifyRemoteKiteDisconnected(r.(*RemoteKite))
		}
	})

	// Every kite should be able to authenticate the user from token.
	k.Authenticators["token"] = k.AuthenticateFromToken
	// A kite accepts requests from Kontrol.
	k.Authenticators["kodingKey"] = k.AuthenticateFromKodingKey

	// Register our internal methods
	k.HandleFunc("systemInfo", new(Status).Info)
	k.HandleFunc("heartbeat", k.handleHeartbeat)
	k.HandleFunc("log", k.handleLog)

	return k
}

// Run is a blocking method. It runs the kite server and then accepts requests
// asynchronously.
func (k *Kite) Run() {
	k.Start()
	select {}
}

// Start is like Run(), but does not wait for it to complete. It's nonblocking.
func (k *Kite) Start() {
	k.parseVersionFlag()

	go func() {
		err := k.listenAndServe()
		if err != nil {
			k.Log.Fatal(err)
		}
	}()

	<-k.ready // wait until we are ready
}

func (k *Kite) handleHeartbeat(r *Request) (interface{}, error) {
	args := r.Args.MustSliceOfLength(2)
	seconds := args[0].MustFloat64()
	ping := args[1].MustFunction()

	go func() {
		for {
			time.Sleep(time.Duration(seconds) * time.Second)
			if ping() != nil {
				return
			}
		}
	}()

	return nil, nil
}

// handleLog prints a log message to stdout.
func (k *Kite) handleLog(r *Request) (interface{}, error) {
	msg := r.Args.MustString()
	k.Log.Info(fmt.Sprintf("%s: %s", r.RemoteKite.Name, msg))
	return nil, nil
}

func init() {
	// These logging related stuff needs to be called once because stupid
	// logging library uses global variables and resets the backends every time.
	logging.SetFormatter(logging.MustStringFormatter("%{level:-8s} ▶ %{message}"))
	stderrBackend := logging.NewLogBackend(os.Stderr, "", log.LstdFlags)
	stderrBackend.Color = true
	syslogBackend, _ := logging.NewSyslogBackend("")
	logging.SetBackend(stderrBackend, syslogBackend)
}

// newLogger returns a new logger object for desired name and level.
func newLogger(name string, debug bool) *logging.Logger {
	logger := logging.MustGetLogger(name)

	level := logging.INFO
	if debug {
		level = logging.DEBUG
	}

	logging.SetLevel(level, name)
	return logger
}

// If the user wants to call flag.Parse() the flag must be defined in advance.
var _ = flag.Bool("version", false, "show version")
var _ = flag.Bool("debug", false, "print debug logs")

// parseVersionFlag prints the version number of the kite and exits with 0
// if "-version" flag is enabled.
// We did not use the "flag" package because it causes trouble if the user
// also calls "flag.Parse()" in his code. flag.Parse() can be called only once.
func (k *Kite) parseVersionFlag() {
	for _, flag := range os.Args {
		if flag == "-version" {
			fmt.Println(k.Version)
			os.Exit(0)
		}
	}
}

// hasDebugFlag returns true if -debug flag is present in os.Args.
func (k *Kite) hasDebugFlag() bool {
	for _, flag := range os.Args {
		if flag == "-debug" {
			return true
		}
	}

	// We can't use flags when running "go test" command.
	// This is another way to print debug logs.
	if os.Getenv("DEBUG") != "" {
		return true
	}

	return false
}

// listenAndServe starts our rpc server with the given addr.
func (k *Kite) listenAndServe() error {
	listener, err := net.Listen("tcp4", ":"+k.Port)
	if err != nil {
		return err
	}

	k.Log.Info("Listening: %s", listener.Addr().String())

	// Port is known here if "0" is used as port number
	_, k.Port, _ = net.SplitHostPort(listener.Addr().String())

	// We must connect to Kontrol after starting to listen on port
	if k.KontrolEnabled {
		if k.RegisterToKontrol {
			k.Kontrol.OnConnect(k.registerToKontrol)
		}

		k.Kontrol.DialForever()
	}

	k.ready <- true // listener is ready, means we are ready too
	return http.Serve(listener, k.server)
}

func (k *Kite) registerToKontrol() {
	err := k.Kontrol.Register()
	if err != nil {
		k.Log.Fatalf("Cannot register to Kontrol: %s", err)
	}
}

// OnConnect registers a function to run when a Kite connects to this Kite.
func (k *Kite) OnConnect(handler func(*RemoteKite)) {
	k.onConnectHandlers = append(k.onConnectHandlers, handler)
}

// OnDisconnect registers a function to run when a connected Kite is disconnected.
func (k *Kite) OnDisconnect(handler func(*RemoteKite)) {
	k.onDisconnectHandlers = append(k.onDisconnectHandlers, handler)
}

// notifyRemoteKiteConnected runs the registered handlers with OnConnect().
func (k *Kite) notifyRemoteKiteConnected(r *RemoteKite) {
	k.Log.Info("Client is connected to us: [%s %s]", r.Name, r.Addr())

	for _, handler := range k.onConnectHandlers {
		go handler(r)
	}
}

func (k *Kite) notifyRemoteKiteDisconnected(r *RemoteKite) {
	k.Log.Info("Client has disconnected: [%s %s]", r.Name, r.Addr())

	for _, handler := range k.onDisconnectHandlers {
		go handler(r)
	}
}
<commit_msg>kite: use all available cpus<commit_after>package kite

import (
	"flag"
	"fmt"
	"github.com/op/go-logging"
	"koding/newkite/dnode/rpc"
	"koding/newkite/protocol"
	"koding/newkite/utils"
	"log"
	"net"
	"net/http"
	"os"
	"os/signal"
	"runtime"
	"syscall"
	"time"
)

func init() {
	// Use all available CPUS.
	runtime.GOMAXPROCS(runtime.NumCPU())

	// Debugging helper: Prints stacktrace on SIGUSR1.
	c := make(chan os.Signal)
	signal.Notify(c, syscall.SIGUSR1)
	go func() {
		for {
			s := <-c
			fmt.Println("Got signal:", s)
			buf := make([]byte, 1<<16)
			runtime.Stack(buf, true)
			fmt.Println(string(buf))
			fmt.Println("Number of goroutines:", runtime.NumGoroutine())
		}
	}()
}

// Kite defines a single process that enables distributed service messaging
// amongst the peers it is connected. A Kite process acts as a Client and as a
// Server. That means it can receive request, process them, but it also can
// make request to other kites. A Kite can be anything. It can be simple Image
// processing kite (which would process data), it could be a Chat kite that
// enables peer-to-peer chat. For examples we have FileSystem kite that expose
// the file system to a client, which in order build the filetree.
type Kite struct {
	protocol.Kite

	// KodingKey is used for authenticate to Kontrol.
	KodingKey string

	// Is this Kite Public or Private? Default is Private.
	Visibility protocol.Visibility

	// Points to the Kontrol instance if enabled
	Kontrol *Kontrol

	// Wheter we want to connect to Kontrol on startup, true by default.
	KontrolEnabled bool

	// Wheter we want to register our Kite to Kontrol, true by default.
	RegisterToKontrol bool

	// method map for exported methods
	handlers map[string]HandlerFunc

	// Dnode rpc server
	server *rpc.Server

	// Handlers to call when a Kite opens a connection to this Kite.
	onConnectHandlers []func(*RemoteKite)

	// Handlers to call when a client has disconnected.
	onDisconnectHandlers []func(*RemoteKite)

	// Contains different functions for authenticating user from request.
	// Keys are the authentication types (options.authentication.type).
	Authenticators map[string]func(*Request) error

	// Used to signal if the kite is ready to start and make calls to
	// other kites.
	ready chan bool

	// Prints logging messages to stderr and syslog.
	Log *logging.Logger
}

// New creates, initialize and then returns a new Kite instance. It accepts
// a single options argument that is a config struct that needs to be filled
// with several informations like Name, Port, IP and so on.
func New(options *Options) *Kite {
	var err error
	if options == nil {
		options, err = ReadKiteOptions("manifest.json")
		if err != nil {
			log.Fatal("error: could not read config file", err)
		}
	}

	options.validate() // exits if validating fails

	hostname, _ := os.Hostname()
	kiteID := utils.GenerateUUID()
	kodingKey, err := utils.GetKodingKey()
	if err != nil {
		log.Fatal("Couldn't find koding.key. Please run 'kd register'.")
	}

	k := &Kite{
		Kite: protocol.Kite{
			Name:        options.Kitename,
			Username:    options.Username,
			ID:          kiteID,
			Version:     options.Version,
			Hostname:    hostname,
			Port:        options.Port,
			Environment: options.Environment,
			Region:      options.Region,
			Visibility:  options.Visibility,

			// PublicIP will be set by Kontrol after registering if it is not set.
			PublicIP: options.PublicIP,
		},
		KodingKey:         kodingKey,
		server:            rpc.NewServer(),
		KontrolEnabled:    true,
		RegisterToKontrol: true,
		Authenticators:    make(map[string]func(*Request) error),
		handlers:          make(map[string]HandlerFunc),
		ready:             make(chan bool),
	}

	k.Log = newLogger(k.Name, k.hasDebugFlag())
	k.Kontrol = k.NewKontrol(options.KontrolAddr)

	// Call registered handlers when a client has disconnected.
	k.server.OnDisconnect(func(c *rpc.Client) {
		if r, ok := c.Properties()["remoteKite"]; ok {
			// Run OnDisconnect handlers.
			k.notifyRemoteKiteDisconnected(r.(*RemoteKite))
		}
	})

	// Every kite should be able to authenticate the user from token.
	k.Authenticators["token"] = k.AuthenticateFromToken
	// A kite accepts requests from Kontrol.
	k.Authenticators["kodingKey"] = k.AuthenticateFromKodingKey

	// Register our internal methods
	k.HandleFunc("systemInfo", new(Status).Info)
	k.HandleFunc("heartbeat", k.handleHeartbeat)
	k.HandleFunc("log", k.handleLog)

	return k
}

// Run is a blocking method. It runs the kite server and then accepts requests
// asynchronously.
func (k *Kite) Run() {
	k.Start()
	select {}
}

// Start is like Run(), but does not wait for it to complete. It's nonblocking.
func (k *Kite) Start() {
	k.parseVersionFlag()

	go func() {
		err := k.listenAndServe()
		if err != nil {
			k.Log.Fatal(err)
		}
	}()

	<-k.ready // wait until we are ready
}

func (k *Kite) handleHeartbeat(r *Request) (interface{}, error) {
	args := r.Args.MustSliceOfLength(2)
	seconds := args[0].MustFloat64()
	ping := args[1].MustFunction()

	go func() {
		for {
			time.Sleep(time.Duration(seconds) * time.Second)
			if ping() != nil {
				return
			}
		}
	}()

	return nil, nil
}

// handleLog prints a log message to stdout.
func (k *Kite) handleLog(r *Request) (interface{}, error) {
	msg := r.Args.MustString()
	k.Log.Info(fmt.Sprintf("%s: %s", r.RemoteKite.Name, msg))
	return nil, nil
}

func init() {
	// These logging related stuff needs to be called once because stupid
	// logging library uses global variables and resets the backends every time.
	logging.SetFormatter(logging.MustStringFormatter("%{level:-8s} ▶ %{message}"))
	stderrBackend := logging.NewLogBackend(os.Stderr, "", log.LstdFlags)
	stderrBackend.Color = true
	syslogBackend, _ := logging.NewSyslogBackend("")
	logging.SetBackend(stderrBackend, syslogBackend)
}

// newLogger returns a new logger object for desired name and level.
func newLogger(name string, debug bool) *logging.Logger {
	logger := logging.MustGetLogger(name)

	level := logging.INFO
	if debug {
		level = logging.DEBUG
	}

	logging.SetLevel(level, name)
	return logger
}

// If the user wants to call flag.Parse() the flag must be defined in advance.
var _ = flag.Bool("version", false, "show version")
var _ = flag.Bool("debug", false, "print debug logs")

// parseVersionFlag prints the version number of the kite and exits with 0
// if "-version" flag is enabled.
// We did not use the "flag" package because it causes trouble if the user
// also calls "flag.Parse()" in his code. flag.Parse() can be called only once.
func (k *Kite) parseVersionFlag() {
	for _, flag := range os.Args {
		if flag == "-version" {
			fmt.Println(k.Version)
			os.Exit(0)
		}
	}
}

// hasDebugFlag returns true if -debug flag is present in os.Args.
func (k *Kite) hasDebugFlag() bool {
	for _, flag := range os.Args {
		if flag == "-debug" {
			return true
		}
	}

	// We can't use flags when running "go test" command.
	// This is another way to print debug logs.
	if os.Getenv("DEBUG") != "" {
		return true
	}

	return false
}

// listenAndServe starts our rpc server with the given addr.
func (k *Kite) listenAndServe() error {
	listener, err := net.Listen("tcp4", ":"+k.Port)
	if err != nil {
		return err
	}

	k.Log.Info("Listening: %s", listener.Addr().String())

	// Port is known here if "0" is used as port number
	_, k.Port, _ = net.SplitHostPort(listener.Addr().String())

	// We must connect to Kontrol after starting to listen on port
	if k.KontrolEnabled {
		if k.RegisterToKontrol {
			k.Kontrol.OnConnect(k.registerToKontrol)
		}

		k.Kontrol.DialForever()
	}

	k.ready <- true // listener is ready, means we are ready too
	return http.Serve(listener, k.server)
}

func (k *Kite) registerToKontrol() {
	err := k.Kontrol.Register()
	if err != nil {
		k.Log.Fatalf("Cannot register to Kontrol: %s", err)
	}
}

// OnConnect registers a function to run when a Kite connects to this Kite.
func (k *Kite) OnConnect(handler func(*RemoteKite)) {
	k.onConnectHandlers = append(k.onConnectHandlers, handler)
}

// OnDisconnect registers a function to run when a connected Kite is disconnected.
func (k *Kite) OnDisconnect(handler func(*RemoteKite)) {
	k.onDisconnectHandlers = append(k.onDisconnectHandlers, handler)
}

// notifyRemoteKiteConnected runs the registered handlers with OnConnect().
func (k *Kite) notifyRemoteKiteConnected(r *RemoteKite) {
	k.Log.Info("Client is connected to us: [%s %s]", r.Name, r.Addr())

	for _, handler := range k.onConnectHandlers {
		go handler(r)
	}
}

func (k *Kite) notifyRemoteKiteDisconnected(r *RemoteKite) {
	k.Log.Info("Client has disconnected: [%s %s]", r.Name, r.Addr())

	for _, handler := range k.onDisconnectHandlers {
		go handler(r)
	}
}
<|endoftext|>


<commit_before>package models

import (
	"errors"
	"fmt"
	"strings"
	"time"

	"github.com/jinzhu/gorm"
	"github.com/koding/bongo"
)

type Channel struct {
	// unique identifier of the channel
	Id int64 `json:"id"`

	// Name of the channel
	Name string `json:"name"                         sql:"NOT NULL;TYPE:VARCHAR(200);"`

	// Creator of the channel
	CreatorId int64 `json:"creatorId"                sql:"NOT NULL"`

	// Name of the group which channel is belong to
	GroupName string `json:"groupName"               sql:"NOT NULL;TYPE:VARCHAR(200);"`

	// Purpose of the channel
	Purpose string `json:"purpose"`

	// Secret key of the channel for event propagation purposes
	// we can put this key into another table?
	SecretKey string `json:"secretKey"`

	// Type of the channel
	TypeConstant string `json:"typeConstant"         sql:"NOT NULL;TYPE:VARCHAR(100);"`

	// Privacy constant of the channel
	PrivacyConstant string `json:"privacyConstant"   sql:"NOT NULL;TYPE:VARCHAR(100);"`

	// Creation date of the channel
	CreatedAt time.Time `json:"createdAt"            sql:"NOT NULL"`

	// Modification date of the channel
	UpdatedAt time.Time `json:"updatedAt"            sql:"NOT NULL"`
}

// to-do check for allowed channels
const (
	// TYPES
	Channel_TYPE_GROUP           = "group"
	Channel_TYPE_TOPIC           = "topic"
	Channel_TYPE_FOLLOWINGFEED   = "followingfeed"
	Channel_TYPE_FOLLOWERS       = "followers"
	Channel_TYPE_CHAT            = "chat"
	Channel_TYPE_PINNED_ACTIVITY = "pinnedActivity"
	// Privacy
	Channel_TYPE_PUBLIC  = "public"
	Channel_TYPE_PRIVATE = "private"
	// Koding Group Name
	Channel_KODING_NAME = "koding"
)

func NewChannel() *Channel {
	return &Channel{
		Name:            "koding",
		CreatorId:       123,
		GroupName:       Channel_KODING_NAME,
		Purpose:         "string",
		SecretKey:       "string",
		TypeConstant:    "default",
		PrivacyConstant: Channel_TYPE_PRIVATE,
	}
}

func (c *Channel) BeforeCreate() {
	c.CreatedAt = time.Now()
	c.UpdatedAt = time.Now()
}

func (c *Channel) BeforeUpdate() {
	c.UpdatedAt = time.Now()
}

func (c *Channel) GetId() int64 {
	return c.Id
}

func (c Channel) TableName() string {
	return "api.channel"
}

func (c *Channel) Fetch() error {
	return bongo.B.Fetch(c)
}

func (c *Channel) AfterCreate() {
	bongo.B.AfterCreate(c)
}

func (c *Channel) AfterUpdate() {
	bongo.B.AfterUpdate(c)
}

func (c *Channel) AfterDelete() {
	bongo.B.AfterDelete(c)
}

func (c *Channel) Update() error {
	if c.Name == "" || c.GroupName == "" {
		return fmt.Errorf("Validation failed %s - %s", c.Name, c.GroupName)
	}

	return bongo.B.Update(c)
}

func (c *Channel) Create() error {
	if c.Name == "" || c.GroupName == "" || c.TypeConstant == "" {
		return fmt.Errorf("Validation failed %s - %s -%s", c.Name, c.GroupName, c.TypeConstant)
	}

	// golang returns -1 if item not in the string
	if strings.Index(c.Name, " ") > -1 {
		return fmt.Errorf("Channel name %q has empty space in it", c.Name)
	}

	if c.TypeConstant == Channel_TYPE_GROUP /* we can add more types here */ {
		selector := map[string]interface{}{
			"group_name":    c.GroupName,
			"type_constant": c.TypeConstant,
		}

		// if err is nil
		// it means we already have that channel
		err := c.One(bongo.NewQS(selector))
		if err == nil {
			return nil
			// return fmt.Errorf("%s typed channel is already created before for %s group", c.TypeConstant, c.GroupName)
		}

		if err != gorm.RecordNotFound {
			return err
		}

	}

	return bongo.B.Create(c)
}

func (c *Channel) Delete() error {
	return bongo.B.Delete(c)
}

func (c *Channel) One(q *bongo.Query) error {
	return bongo.B.One(c, c, q)
}

func (c *Channel) Some(data interface{}, q *bongo.Query) error {
	return bongo.B.Some(c, data, q)
}

func (c *Channel) FetchByIds(ids []int64) ([]Channel, error) {
	var channels []Channel

	if len(ids) == 0 {
		return channels, nil
	}

	if err := bongo.B.FetchByIds(c, &channels, ids); err != nil {
		return nil, err
	}
	return channels, nil
}

func (c *Channel) AddParticipant(participantId int64) (*ChannelParticipant, error) {
	if c.Id == 0 {
		return nil, errors.New("Channel Id is not set")
	}

	cp := NewChannelParticipant()
	cp.ChannelId = c.Id
	cp.AccountId = participantId

	err := cp.FetchParticipant()
	if err != nil && err != gorm.RecordNotFound {
		return nil, err
	}

	// if we have this record in DB
	if cp.Id != 0 {
		// if status is not active
		if cp.StatusConstant == ChannelParticipant_STATUS_ACTIVE {
			return nil, errors.New(fmt.Sprintf("Account %d is already a participant of channel %d", cp.AccountId, cp.ChannelId))
		}
		cp.StatusConstant = ChannelParticipant_STATUS_ACTIVE
		if err := cp.Update(); err != nil {
			return nil, err
		}
		return cp, nil
	}

	cp.StatusConstant = ChannelParticipant_STATUS_ACTIVE

	if err := cp.Create(); err != nil {
		return nil, err
	}

	return cp, nil
}

func (c *Channel) RemoveParticipant(participantId int64) error {
	if c.Id == 0 {
		return errors.New("Channel Id is not set")
	}

	cp := NewChannelParticipant()
	cp.ChannelId = c.Id
	cp.AccountId = participantId

	err := cp.FetchParticipant()
	// if user is not in this channel, do nothing
	if err == gorm.RecordNotFound {
		return nil
	}

	if err != nil {
		return err
	}

	if cp.StatusConstant == ChannelParticipant_STATUS_LEFT {
		return nil
	}

	cp.StatusConstant = ChannelParticipant_STATUS_LEFT
	if err := cp.Update(); err != nil {
		return err
	}

	return nil
}

func (c *Channel) FetchParticipantIds() ([]int64, error) {
	var participantIds []int64

	if c.Id == 0 {
		return participantIds, errors.New("Channel Id is not set")
	}

	query := &bongo.Query{
		Selector: map[string]interface{}{
			"channel_id":      c.Id,
			"status_constant": ChannelParticipant_STATUS_ACTIVE,
		},
		Pluck: "account_id",
	}

	cp := NewChannelParticipant()
	err := cp.Some(&participantIds, query)
	if err != nil {
		return nil, err
	}

	return participantIds, nil
}

func (c *Channel) AddMessage(messageId int64) (*ChannelMessageList, error) {
	if c.Id == 0 {
		return nil, errors.New("Channel Id is not set")
	}

	cml := NewChannelMessageList()

	selector := map[string]interface{}{
		"channel_id": c.Id,
		"message_id": messageId,
	}
	err := cml.One(bongo.NewQS(selector))
	if err == nil {
		return nil, errors.New("Message is already in the channel")
	}

	if err != gorm.RecordNotFound {
		return nil, err
	}
	// silence record not found err

	cml.ChannelId = c.Id
	cml.MessageId = messageId

	if err := cml.Create(); err != nil {
		return nil, err
	}

	return cml, nil
}

func (c *Channel) RemoveMessage(messageId int64) (*ChannelMessageList, error) {
	if c.Id == 0 {
		return nil, errors.New("Channel Id is not set")
	}

	cml := NewChannelMessageList()
	selector := map[string]interface{}{
		"channel_id": c.Id,
		"message_id": messageId,
	}
	err := cml.One(bongo.NewQS(selector))
	// one returns error when record not found case
	// but we dont care if it is not there tho
	if err != nil {
		return nil, err
	}

	if err := cml.Delete(); err != nil {
		return nil, err
	}

	return cml, nil
}

func (c *Channel) List(q *Query) ([]Channel, error) {

	if q.GroupName == "" {
		return nil, fmt.Errorf("Query doesnt have any Group info %+v", q)
	}

	var channels []Channel

	query := &bongo.Query{
		Selector: map[string]interface{}{
			"group_name": q.GroupName,
		},
	}

	if q.Type != "" {
		query.Selector["type_constant"] = q.Type
	}

	err := bongo.B.Some(c, &channels, query)
	if err != nil {
		return nil, err
	}

	return channels, nil
}
<commit_msg>Social: use channel some function<commit_after>package models

import (
	"errors"
	"fmt"
	"strings"
	"time"

	"github.com/jinzhu/gorm"
	"github.com/koding/bongo"
)

type Channel struct {
	// unique identifier of the channel
	Id int64 `json:"id"`

	// Name of the channel
	Name string `json:"name"                         sql:"NOT NULL;TYPE:VARCHAR(200);"`

	// Creator of the channel
	CreatorId int64 `json:"creatorId"                sql:"NOT NULL"`

	// Name of the group which channel is belong to
	GroupName string `json:"groupName"               sql:"NOT NULL;TYPE:VARCHAR(200);"`

	// Purpose of the channel
	Purpose string `json:"purpose"`

	// Secret key of the channel for event propagation purposes
	// we can put this key into another table?
	SecretKey string `json:"secretKey"`

	// Type of the channel
	TypeConstant string `json:"typeConstant"         sql:"NOT NULL;TYPE:VARCHAR(100);"`

	// Privacy constant of the channel
	PrivacyConstant string `json:"privacyConstant"   sql:"NOT NULL;TYPE:VARCHAR(100);"`

	// Creation date of the channel
	CreatedAt time.Time `json:"createdAt"            sql:"NOT NULL"`

	// Modification date of the channel
	UpdatedAt time.Time `json:"updatedAt"            sql:"NOT NULL"`
}

// to-do check for allowed channels
const (
	// TYPES
	Channel_TYPE_GROUP           = "group"
	Channel_TYPE_TOPIC           = "topic"
	Channel_TYPE_FOLLOWINGFEED   = "followingfeed"
	Channel_TYPE_FOLLOWERS       = "followers"
	Channel_TYPE_CHAT            = "chat"
	Channel_TYPE_PINNED_ACTIVITY = "pinnedActivity"
	// Privacy
	Channel_TYPE_PUBLIC  = "public"
	Channel_TYPE_PRIVATE = "private"
	// Koding Group Name
	Channel_KODING_NAME = "koding"
)

func NewChannel() *Channel {
	return &Channel{
		Name:            "koding",
		CreatorId:       123,
		GroupName:       Channel_KODING_NAME,
		Purpose:         "string",
		SecretKey:       "string",
		TypeConstant:    "default",
		PrivacyConstant: Channel_TYPE_PRIVATE,
	}
}

func (c *Channel) BeforeCreate() {
	c.CreatedAt = time.Now()
	c.UpdatedAt = time.Now()
}

func (c *Channel) BeforeUpdate() {
	c.UpdatedAt = time.Now()
}

func (c *Channel) GetId() int64 {
	return c.Id
}

func (c Channel) TableName() string {
	return "api.channel"
}

func (c *Channel) Fetch() error {
	return bongo.B.Fetch(c)
}

func (c *Channel) AfterCreate() {
	bongo.B.AfterCreate(c)
}

func (c *Channel) AfterUpdate() {
	bongo.B.AfterUpdate(c)
}

func (c *Channel) AfterDelete() {
	bongo.B.AfterDelete(c)
}

func (c *Channel) Update() error {
	if c.Name == "" || c.GroupName == "" {
		return fmt.Errorf("Validation failed %s - %s", c.Name, c.GroupName)
	}

	return bongo.B.Update(c)
}

func (c *Channel) Create() error {
	if c.Name == "" || c.GroupName == "" || c.TypeConstant == "" {
		return fmt.Errorf("Validation failed %s - %s -%s", c.Name, c.GroupName, c.TypeConstant)
	}

	// golang returns -1 if item not in the string
	if strings.Index(c.Name, " ") > -1 {
		return fmt.Errorf("Channel name %q has empty space in it", c.Name)
	}

	if c.TypeConstant == Channel_TYPE_GROUP /* we can add more types here */ {
		selector := map[string]interface{}{
			"group_name":    c.GroupName,
			"type_constant": c.TypeConstant,
		}

		// if err is nil
		// it means we already have that channel
		err := c.One(bongo.NewQS(selector))
		if err == nil {
			return nil
			// return fmt.Errorf("%s typed channel is already created before for %s group", c.TypeConstant, c.GroupName)
		}

		if err != gorm.RecordNotFound {
			return err
		}

	}

	return bongo.B.Create(c)
}

func (c *Channel) Delete() error {
	return bongo.B.Delete(c)
}

func (c *Channel) One(q *bongo.Query) error {
	return bongo.B.One(c, c, q)
}

func (c *Channel) Some(data interface{}, q *bongo.Query) error {
	return bongo.B.Some(c, data, q)
}

func (c *Channel) FetchByIds(ids []int64) ([]Channel, error) {
	var channels []Channel

	if len(ids) == 0 {
		return channels, nil
	}

	if err := bongo.B.FetchByIds(c, &channels, ids); err != nil {
		return nil, err
	}
	return channels, nil
}

func (c *Channel) AddParticipant(participantId int64) (*ChannelParticipant, error) {
	if c.Id == 0 {
		return nil, errors.New("Channel Id is not set")
	}

	cp := NewChannelParticipant()
	cp.ChannelId = c.Id
	cp.AccountId = participantId

	err := cp.FetchParticipant()
	if err != nil && err != gorm.RecordNotFound {
		return nil, err
	}

	// if we have this record in DB
	if cp.Id != 0 {
		// if status is not active
		if cp.StatusConstant == ChannelParticipant_STATUS_ACTIVE {
			return nil, errors.New(fmt.Sprintf("Account %d is already a participant of channel %d", cp.AccountId, cp.ChannelId))
		}
		cp.StatusConstant = ChannelParticipant_STATUS_ACTIVE
		if err := cp.Update(); err != nil {
			return nil, err
		}
		return cp, nil
	}

	cp.StatusConstant = ChannelParticipant_STATUS_ACTIVE

	if err := cp.Create(); err != nil {
		return nil, err
	}

	return cp, nil
}

func (c *Channel) RemoveParticipant(participantId int64) error {
	if c.Id == 0 {
		return errors.New("Channel Id is not set")
	}

	cp := NewChannelParticipant()
	cp.ChannelId = c.Id
	cp.AccountId = participantId

	err := cp.FetchParticipant()
	// if user is not in this channel, do nothing
	if err == gorm.RecordNotFound {
		return nil
	}

	if err != nil {
		return err
	}

	if cp.StatusConstant == ChannelParticipant_STATUS_LEFT {
		return nil
	}

	cp.StatusConstant = ChannelParticipant_STATUS_LEFT
	if err := cp.Update(); err != nil {
		return err
	}

	return nil
}

func (c *Channel) FetchParticipantIds() ([]int64, error) {
	var participantIds []int64

	if c.Id == 0 {
		return participantIds, errors.New("Channel Id is not set")
	}

	query := &bongo.Query{
		Selector: map[string]interface{}{
			"channel_id":      c.Id,
			"status_constant": ChannelParticipant_STATUS_ACTIVE,
		},
		Pluck: "account_id",
	}

	cp := NewChannelParticipant()
	err := cp.Some(&participantIds, query)
	if err != nil {
		return nil, err
	}

	return participantIds, nil
}

func (c *Channel) AddMessage(messageId int64) (*ChannelMessageList, error) {
	if c.Id == 0 {
		return nil, errors.New("Channel Id is not set")
	}

	cml := NewChannelMessageList()

	selector := map[string]interface{}{
		"channel_id": c.Id,
		"message_id": messageId,
	}
	err := cml.One(bongo.NewQS(selector))
	if err == nil {
		return nil, errors.New("Message is already in the channel")
	}

	if err != gorm.RecordNotFound {
		return nil, err
	}
	// silence record not found err

	cml.ChannelId = c.Id
	cml.MessageId = messageId

	if err := cml.Create(); err != nil {
		return nil, err
	}

	return cml, nil
}

func (c *Channel) RemoveMessage(messageId int64) (*ChannelMessageList, error) {
	if c.Id == 0 {
		return nil, errors.New("Channel Id is not set")
	}

	cml := NewChannelMessageList()
	selector := map[string]interface{}{
		"channel_id": c.Id,
		"message_id": messageId,
	}
	err := cml.One(bongo.NewQS(selector))
	// one returns error when record not found case
	// but we dont care if it is not there tho
	if err != nil {
		return nil, err
	}

	if err := cml.Delete(); err != nil {
		return nil, err
	}

	return cml, nil
}

func (c *Channel) List(q *Query) ([]Channel, error) {

	if q.GroupName == "" {
		return nil, fmt.Errorf("Query doesnt have any Group info %+v", q)
	}

	var channels []Channel

	query := &bongo.Query{
		Selector: map[string]interface{}{
			"group_name": q.GroupName,
		},
	}

	if q.Type != "" {
		query.Selector["type_constant"] = q.Type
	}

	err := c.Some(&channels, query)
	if err != nil {
		return nil, err
	}

	return channels, nil
}
<|endoftext|>