<commit_before>// Copyright 2013 Hui Chen
// Copyright 2016 ego 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.

/*

Package riot full text search engine
*/
package riot

import (
	// _ "github.com/cznic/kv"
	_ "github.com/dgraph-io/badger"
	_ "github.com/go-ego/gse"
	_ "github.com/go-ego/murmur"
	_ "github.com/syndtr/goleveldb/leveldb"
	_ "go.etcd.io/bbolt"
)
<commit_msg>remove pkg mod init<commit_after>// Copyright 2013 Hui Chen
// Copyright 2016 ego 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.

/*

Package riot full text search engine
*/
package riot

// import (
// 	// _ "github.com/cznic/kv"
// 	_ "github.com/dgraph-io/badger"
// 	_ "github.com/go-ego/gse"
// 	_ "github.com/go-ego/murmur"
// 	_ "github.com/syndtr/goleveldb/leveldb"
// 	_ "go.etcd.io/bbolt"
// )
<|endoftext|>


<commit_before>package rpc

import (
	"bytes"
	crand "crypto/rand"
	"fmt"
	"math/rand"
	"net/http"
	"os/exec"
	"testing"
	"time"

	"github.com/stretchr/testify/assert"
	"github.com/stretchr/testify/require"
	client "github.com/tendermint/go-rpc/client"
	server "github.com/tendermint/go-rpc/server"
	types "github.com/tendermint/go-rpc/types"
	wire "github.com/tendermint/go-wire"
)

// Client and Server should work over tcp or unix sockets
const (
	tcpAddr = "tcp://0.0.0.0:46657"

	unixSocket = "/tmp/go-rpc.sock"
	unixAddr   = "unix:///tmp/go-rpc.sock"

	websocketEndpoint = "/websocket/endpoint"
)

// Define a type for results and register concrete versions
type Result interface{}

type ResultStatus struct {
	Value string
}

type ResultBytes struct {
	Value []byte
}

var _ = wire.RegisterInterface(
	struct{ Result }{},
	wire.ConcreteType{&ResultStatus{}, 0x1},
	wire.ConcreteType{&ResultBytes{}, 0x2},
)

// Define some routes
var Routes = map[string]*server.RPCFunc{
	"status":    server.NewRPCFunc(StatusResult, "arg"),
	"status_ws": server.NewWSRPCFunc(StatusWSResult, "arg"),
	"bytes":     server.NewRPCFunc(BytesResult, "arg"),
}

// an rpc function
func StatusResult(v string) (Result, error) {
	return &ResultStatus{v}, nil
}

func StatusWSResult(wsCtx types.WSRPCContext, v string) (Result, error) {
	return &ResultStatus{v}, nil
}

func BytesResult(v []byte) (Result, error) {
	return &ResultBytes{v}, nil
}

// launch unix and tcp servers
func init() {
	cmd := exec.Command("rm", "-f", unixSocket)
	err := cmd.Start()
	if err != nil {
		panic(err)
	}
	err = cmd.Wait()

	mux := http.NewServeMux()
	server.RegisterRPCFuncs(mux, Routes)
	wm := server.NewWebsocketManager(Routes, nil)
	mux.HandleFunc(websocketEndpoint, wm.WebsocketHandler)
	go func() {
		_, err := server.StartHTTPServer(tcpAddr, mux)
		if err != nil {
			panic(err)
		}
	}()

	mux2 := http.NewServeMux()
	server.RegisterRPCFuncs(mux2, Routes)
	wm = server.NewWebsocketManager(Routes, nil)
	mux2.HandleFunc(websocketEndpoint, wm.WebsocketHandler)
	go func() {
		_, err := server.StartHTTPServer(unixAddr, mux2)
		if err != nil {
			panic(err)
		}
	}()

	// wait for servers to start
	time.Sleep(time.Second * 2)

}

func status(cl client.HTTPClient, val string) (string, error) {
	params := map[string]interface{}{
		"arg": val,
	}
	var result Result
	if _, err := cl.Call("status", params, &result); err != nil {
		return "", err
	}
	return result.(*ResultStatus).Value, nil
}

func testWithHTTPClient(t *testing.T, cl client.HTTPClient) {
	val := "acbd"
	got, err := status(cl, val)
	require.Nil(t, err)
	assert.Equal(t, got, val)
}

func testWithWSClient(t *testing.T, cl *client.WSClient) {
	val := "acbd"
	params := map[string]interface{}{
		"arg": val,
	}
	err := cl.WriteJSON(types.RPCRequest{
		JSONRPC: "2.0",
		ID:      "",
		Method:  "status",
		Params:  params,
	})
	require.Nil(t, err)

	select {
	case msg := <-cl.ResultsCh:
		result := new(Result)
		wire.ReadJSONPtr(result, msg, &err)
		require.Nil(t, err)
		got := (*result).(*ResultStatus).Value
		assert.Equal(t, got, val)
	case err := <-cl.ErrorsCh:
		t.Fatal(err)
	}
}

//-------------

func TestServersAndClientsBasic(t *testing.T) {
	serverAddrs := [...]string{tcpAddr, unixAddr}
	for _, addr := range serverAddrs {
		cl1 := client.NewURIClient(addr)
		fmt.Printf("=== testing server on %s using %v client", addr, cl1)
		testWithHTTPClient(t, cl1)

		cl2 := client.NewJSONRPCClient(tcpAddr)
		fmt.Printf("=== testing server on %s using %v client", addr, cl2)
		testWithHTTPClient(t, cl2)

		cl3 := client.NewWSClient(tcpAddr, websocketEndpoint)
		_, err := cl3.Start()
		require.Nil(t, err)
		fmt.Printf("=== testing server on %s using %v client", addr, cl3)
		testWithWSClient(t, cl3)
		cl3.Stop()
	}
}

func TestHexStringArg(t *testing.T) {
	cl := client.NewURIClient(tcpAddr)
	// should NOT be handled as hex
	val := "0xabc"
	got, err := status(cl, val)
	require.Nil(t, err)
	assert.Equal(t, got, val)
}

func TestQuotedStringArg(t *testing.T) {
	cl := client.NewURIClient(tcpAddr)
	// should NOT be unquoted
	val := "\"abc\""
	got, err := status(cl, val)
	require.Nil(t, err)
	assert.Equal(t, got, val)
}

func randBytes(t *testing.T) []byte {
	n := rand.Intn(10) + 2
	buf := make([]byte, n)
	_, err := crand.Read(buf)
	require.Nil(t, err)
	return bytes.Replace(buf, []byte("="), []byte{100}, -1)
}

func TestByteSliceViaJSONRPC(t *testing.T) {
	cl := client.NewJSONRPCClient(unixAddr)

	val := randBytes(t)
	params := map[string]interface{}{
		"arg": val,
	}
	var result Result
	_, err := cl.Call("bytes", params, &result)
	require.Nil(t, err)
	got := result.(*ResultBytes).Value
	assert.Equal(t, got, val)
}

func TestWSNewWSRPCFunc(t *testing.T) {
	cl := client.NewWSClient(unixAddr, websocketEndpoint)
	_, err := cl.Start()
	require.Nil(t, err)
	defer cl.Stop()

	val := "acbd"
	params := map[string]interface{}{
		"arg": val,
	}
	err = cl.WriteJSON(types.RPCRequest{
		JSONRPC: "2.0",
		ID:      "",
		Method:  "status_ws",
		Params:  params,
	})
	require.Nil(t, err)

	select {
	case msg := <-cl.ResultsCh:
		result := new(Result)
		wire.ReadJSONPtr(result, msg, &err)
		require.Nil(t, err)
		got := (*result).(*ResultStatus).Value
		assert.Equal(t, got, val)
	case err := <-cl.ErrorsCh:
		t.Fatal(err)
	}
}
<commit_msg>rename rpc function status to echo<commit_after>package rpc

import (
	"bytes"
	crand "crypto/rand"
	"fmt"
	"math/rand"
	"net/http"
	"os/exec"
	"testing"
	"time"

	"github.com/stretchr/testify/assert"
	"github.com/stretchr/testify/require"
	client "github.com/tendermint/go-rpc/client"
	server "github.com/tendermint/go-rpc/server"
	types "github.com/tendermint/go-rpc/types"
	wire "github.com/tendermint/go-wire"
)

// Client and Server should work over tcp or unix sockets
const (
	tcpAddr = "tcp://0.0.0.0:46657"

	unixSocket = "/tmp/go-rpc.sock"
	unixAddr   = "unix:///tmp/go-rpc.sock"

	websocketEndpoint = "/websocket/endpoint"
)

// Define a type for results and register concrete versions
type Result interface{}

type ResultEcho struct {
	Value string
}

type ResultEchoBytes struct {
	Value []byte
}

var _ = wire.RegisterInterface(
	struct{ Result }{},
	wire.ConcreteType{&ResultEcho{}, 0x1},
	wire.ConcreteType{&ResultEchoBytes{}, 0x2},
)

// Define some routes
var Routes = map[string]*server.RPCFunc{
	"echo":       server.NewRPCFunc(EchoResult, "arg"),
	"echo_ws":    server.NewWSRPCFunc(EchoWSResult, "arg"),
	"echo_bytes": server.NewRPCFunc(EchoBytesResult, "arg"),
}

func EchoResult(v string) (Result, error) {
	return &ResultEcho{v}, nil
}

func EchoWSResult(wsCtx types.WSRPCContext, v string) (Result, error) {
	return &ResultEcho{v}, nil
}

func EchoBytesResult(v []byte) (Result, error) {
	return &ResultEchoBytes{v}, nil
}

// launch unix and tcp servers
func init() {
	cmd := exec.Command("rm", "-f", unixSocket)
	err := cmd.Start()
	if err != nil {
		panic(err)
	}
	err = cmd.Wait()

	mux := http.NewServeMux()
	server.RegisterRPCFuncs(mux, Routes)
	wm := server.NewWebsocketManager(Routes, nil)
	mux.HandleFunc(websocketEndpoint, wm.WebsocketHandler)
	go func() {
		_, err := server.StartHTTPServer(tcpAddr, mux)
		if err != nil {
			panic(err)
		}
	}()

	mux2 := http.NewServeMux()
	server.RegisterRPCFuncs(mux2, Routes)
	wm = server.NewWebsocketManager(Routes, nil)
	mux2.HandleFunc(websocketEndpoint, wm.WebsocketHandler)
	go func() {
		_, err := server.StartHTTPServer(unixAddr, mux2)
		if err != nil {
			panic(err)
		}
	}()

	// wait for servers to start
	time.Sleep(time.Second * 2)

}

func echo(cl client.HTTPClient, val string) (string, error) {
	params := map[string]interface{}{
		"arg": val,
	}
	var result Result
	if _, err := cl.Call("echo", params, &result); err != nil {
		return "", err
	}
	return result.(*ResultEcho).Value, nil
}

func testWithHTTPClient(t *testing.T, cl client.HTTPClient) {
	val := "acbd"
	got, err := echo(cl, val)
	require.Nil(t, err)
	assert.Equal(t, got, val)
}

func testWithWSClient(t *testing.T, cl *client.WSClient) {
	val := "acbd"
	params := map[string]interface{}{
		"arg": val,
	}
	err := cl.WriteJSON(types.RPCRequest{
		JSONRPC: "2.0",
		ID:      "",
		Method:  "echo",
		Params:  params,
	})
	require.Nil(t, err)

	select {
	case msg := <-cl.ResultsCh:
		result := new(Result)
		wire.ReadJSONPtr(result, msg, &err)
		require.Nil(t, err)
		got := (*result).(*ResultEcho).Value
		assert.Equal(t, got, val)
	case err := <-cl.ErrorsCh:
		t.Fatal(err)
	}
}

//-------------

func TestServersAndClientsBasic(t *testing.T) {
	serverAddrs := [...]string{tcpAddr, unixAddr}
	for _, addr := range serverAddrs {
		cl1 := client.NewURIClient(addr)
		fmt.Printf("=== testing server on %s using %v client", addr, cl1)
		testWithHTTPClient(t, cl1)

		cl2 := client.NewJSONRPCClient(tcpAddr)
		fmt.Printf("=== testing server on %s using %v client", addr, cl2)
		testWithHTTPClient(t, cl2)

		cl3 := client.NewWSClient(tcpAddr, websocketEndpoint)
		_, err := cl3.Start()
		require.Nil(t, err)
		fmt.Printf("=== testing server on %s using %v client", addr, cl3)
		testWithWSClient(t, cl3)
		cl3.Stop()
	}
}

func TestHexStringArg(t *testing.T) {
	cl := client.NewURIClient(tcpAddr)
	// should NOT be handled as hex
	val := "0xabc"
	got, err := echo(cl, val)
	require.Nil(t, err)
	assert.Equal(t, got, val)
}

func TestQuotedStringArg(t *testing.T) {
	cl := client.NewURIClient(tcpAddr)
	// should NOT be unquoted
	val := "\"abc\""
	got, err := echo(cl, val)
	require.Nil(t, err)
	assert.Equal(t, got, val)
}

func randBytes(t *testing.T) []byte {
	n := rand.Intn(10) + 2
	buf := make([]byte, n)
	_, err := crand.Read(buf)
	require.Nil(t, err)
	return bytes.Replace(buf, []byte("="), []byte{100}, -1)
}

func TestByteSliceViaJSONRPC(t *testing.T) {
	cl := client.NewJSONRPCClient(unixAddr)

	val := randBytes(t)
	params := map[string]interface{}{
		"arg": val,
	}
	var result Result
	_, err := cl.Call("echo_bytes", params, &result)
	require.Nil(t, err)
	got := result.(*ResultEchoBytes).Value
	assert.Equal(t, got, val)
}

func TestWSNewWSRPCFunc(t *testing.T) {
	cl := client.NewWSClient(unixAddr, websocketEndpoint)
	_, err := cl.Start()
	require.Nil(t, err)
	defer cl.Stop()

	val := "acbd"
	params := map[string]interface{}{
		"arg": val,
	}
	err = cl.WriteJSON(types.RPCRequest{
		JSONRPC: "2.0",
		ID:      "",
		Method:  "echo_ws",
		Params:  params,
	})
	require.Nil(t, err)

	select {
	case msg := <-cl.ResultsCh:
		result := new(Result)
		wire.ReadJSONPtr(result, msg, &err)
		require.Nil(t, err)
		got := (*result).(*ResultEcho).Value
		assert.Equal(t, got, val)
	case err := <-cl.ErrorsCh:
		t.Fatal(err)
	}
}
<|endoftext|>


<commit_before>package data

import (
	"sort"
	"time"

	"github.com/UHERO/rest-api/models"
)

func (r *SeriesRepository) GetSeriesBySearchText(searchText string) (seriesList []models.DataPortalSeries, err error) {
	rows, err := r.DB.Query(`SELECT DISTINCT
	series.id, series.name, series.description, frequency, series.seasonally_adjusted, series.seasonal_adjustment,
	COALESCE(NULLIF(series.unitsLabel, ''), NULLIF(measurements.units_label, '')),
	COALESCE(NULLIF(series.unitsLabelShort, ''), NULLIF(measurements.units_label_short, '')),
	COALESCE(NULLIF(series.dataPortalName, ''), measurements.data_portal_name), measurements.percent, measurements.real,
	COALESCE(NULLIF(sources.description, ''), NULLIF(measurement_sources.description, '')),
	COALESCE(NULLIF(series.source_link, ''), NULLIF(measurements.source_link, ''), NULLIF(sources.link, ''), NULLIF(measurement_sources.link, '')),
	COALESCE(NULLIF(source_details.description, ''), NULLIF(measurement_source_details.description, '')),
	measurements.table_prefix, measurements.table_postfix,
	measurements.id,
	NULL, series.base_year, series.decimals,
	fips, SUBSTRING_INDEX(SUBSTR(series.name, LOCATE('@', series.name) + 1), '.', 1) as shandle, display_name_short
	FROM series
	LEFT JOIN geographies ON series.name LIKE CONCAT('%@', geographies.handle, '.%')
	LEFT JOIN measurement_series ON measurement_series.series_id = series.id
	LEFT JOIN measurements ON measurements.id = measurement_series.measurement_id
	LEFT JOIN sources ON sources.id = series.source_id
	LEFT JOIN sources AS measurement_sources ON measurement_sources.id = measurements.source_id
	LEFT JOIN source_details ON source_details.id = series.source_detail_id
	LEFT JOIN source_details AS measurement_source_details ON measurement_source_details.id = measurements.source_detail_id
	LEFT JOIN feature_toggles ON feature_toggles.name = 'filter_by_quarantine'
	WHERE NOT series.restricted
	AND (feature_toggles.status IS NULL OR NOT feature_toggles.status OR NOT series.quarantined)
	AND ((MATCH(series.name, series.description, series.dataPortalName) AGAINST(? IN NATURAL LANGUAGE MODE))
	  OR LOWER(CONCAT(series.name, series.description, series.dataPortalName)) LIKE CONCAT('%', LOWER(?), '%'))
	LIMIT 50;`, searchText, searchText)
	if err != nil {
		return
	}
	for rows.Next() {
		dataPortalSeries, scanErr := getNextSeriesFromRows(rows)
		if scanErr != nil {
			return seriesList, scanErr
		}
		geoFreqs, freqGeos, err := getFreqGeoCombinations(r, dataPortalSeries.Id)
		if err != nil {
			return seriesList, err
		}
		dataPortalSeries.GeographyFrequencies = &geoFreqs
		dataPortalSeries.FrequencyGeographies = &freqGeos
		dataPortalSeries.GeoFreqsDeprecated = &geoFreqs
		dataPortalSeries.FreqGeosDeprecated = &freqGeos
		seriesList = append(seriesList, dataPortalSeries)
	}
	return
}

func (r *SeriesRepository) GetSearchSummary(searchText string) (searchSummary models.SearchSummary, err error) {
	searchSummary.SearchText = searchText

	var observationStart, observationEnd models.NullTime
	err = r.DB.QueryRow(`SELECT MIN(public_data_points.date) AS start_date, MAX(public_data_points.date) AS end_date
	FROM series
 	LEFT JOIN public_data_points ON public_data_points.series_id = series.id
 	LEFT JOIN feature_toggles ON feature_toggles.name = 'filter_by_quarantine'
	WHERE ((MATCH(series.name, series.description, series.dataPortalName) AGAINST(? IN NATURAL LANGUAGE MODE))
	  OR LOWER(CONCAT(series.name, series.description, series.dataPortalName)) LIKE CONCAT('%', LOWER(?), '%'))
	AND NOT series.restricted
	AND (feature_toggles.status IS NULL OR NOT feature_toggles.status OR NOT series.quarantined)`, searchText, searchText).Scan(
		&observationStart,
		&observationEnd,
	)
	if err != nil {
		return
	}
	if observationStart.Valid && observationStart.Time.After(time.Time{}) {
		searchSummary.ObservationStart = &observationStart.Time
	}
	if observationEnd.Valid && observationEnd.Time.After(time.Time{}) {
		searchSummary.ObservationEnd = &observationEnd.Time
	}

	rows, err := r.DB.Query(`SELECT geographies.fips, geographies.display_name_short, geofreq.geo, geofreq.freq
FROM (SELECT MAX(SUBSTRING_INDEX(SUBSTR(s.name, LOCATE('@', s.name) + 1), '.', 1)) as geo, MAX(RIGHT(s.name, 1)) as freq
      FROM (SELECT series.name FROM series
			  LEFT JOIN feature_toggles ON feature_toggles.name = 'filter_by_quarantine'
			WHERE NOT restricted
					AND (feature_toggles.status IS NULL OR NOT feature_toggles.status OR NOT series.quarantined)
					AND (MATCH(series.name, series.description, dataPortalName) AGAINST(? IN NATURAL LANGUAGE MODE))
                                 OR LOWER(CONCAT(series.name, series.description, dataPortalName)) LIKE CONCAT('%', LOWER(?), '%')) AS s
GROUP BY SUBSTR(s.name, LOCATE('@', s.name) + 1) ORDER BY COUNT(*) DESC) as geofreq
LEFT JOIN geographies ON geographies.handle = geofreq.geo;`, searchText, searchText)
	if err != nil {
		return
	}
	geoFreqs := map[string][]models.FrequencyResult{}
	geoByHandle := map[string]models.DataPortalGeography{}
	freqGeos := map[string][]models.DataPortalGeography{}
	freqByHandle := map[string]models.FrequencyResult{}

	for rows.Next() {
		scangeo := models.Geography{}
		frequency := models.FrequencyResult{}
		err = rows.Scan(
			&scangeo.FIPS,
			&scangeo.Name,
			&scangeo.Handle,
			&frequency.Freq,
		)
		geography := models.DataPortalGeography{Handle: scangeo.Handle}
		if scangeo.FIPS.Valid {
			geography.FIPS = scangeo.FIPS.String
		}
		if scangeo.Name.Valid {
			geography.Name = scangeo.Name.String
		}
		frequency.Label = freqLabel[frequency.Freq]
		// set the default as the first in the sorted list
		if searchSummary.DefaultGeoFreq == nil {
			searchSummary.DefaultGeoFreq = &models.GeographyFrequency{
				Geography: geography,
				Frequency: frequency,
			}
		}
		// update the freq and geo maps
		geoByHandle[geography.Handle] = geography
		freqByHandle[frequency.Freq] = frequency
		// add to the geoFreqs and freqGeos maps
		geoFreqs[geography.Handle] = append(geoFreqs[geography.Handle], frequency)
		freqGeos[frequency.Freq] = append(freqGeos[frequency.Freq], geography)
	}

	geoFreqsResult := []models.GeographyFrequencies{}
	for geo, freqs := range geoFreqs {
		sort.Sort(models.ByFrequency(freqs))
		geoFreqsResult = append(geoFreqsResult, models.GeographyFrequencies{
			DataPortalGeography: geoByHandle[geo],
			Frequencies:         freqs,
		})
	}

	freqGeosResult := []models.FrequencyGeographies{}
	for _, freq := range models.FreqOrder {
		if val, ok := freqByHandle[freq]; ok {
			freqGeosResult = append(freqGeosResult, models.FrequencyGeographies{
				FrequencyResult: val,
				Geographies:     freqGeos[freq],
			})
		}
	}

	searchSummary.GeographyFrequencies = &geoFreqsResult
	searchSummary.FrequencyGeographies = &freqGeosResult
	searchSummary.GeoFreqsDeprecated = &geoFreqsResult
	searchSummary.FreqGeosDeprecated = &freqGeosResult
	return
}

func (r *SeriesRepository) GetSearchResultsByGeoAndFreq(searchText string, geo string, freq string) (seriesList []models.DataPortalSeries, err error) {
	rows, err := r.DB.Query(`SELECT DISTINCT
	series.id, series.name, series.description, frequency, series.seasonally_adjusted, series.seasonal_adjustment,
	COALESCE(NULLIF(series.unitsLabel, ''), NULLIF(measurements.units_label, '')),
	COALESCE(NULLIF(series.unitsLabelShort, ''), NULLIF(measurements.units_label_short, '')),
	COALESCE(NULLIF(series.dataPortalName, ''), measurements.data_portal_name), measurements.percent, measurements.real,
	COALESCE(NULLIF(sources.description, ''), NULLIF(measurement_sources.description, '')),
	COALESCE(NULLIF(series.source_link, ''), NULLIF(measurements.source_link, ''), NULLIF(sources.link, ''), NULLIF(measurement_sources.link, '')),
	COALESCE(NULLIF(source_details.description, ''), NULLIF(measurement_source_details.description, '')),
	measurements.table_prefix, measurements.table_postfix,
	measurements.id,
	NULL, series.base_year, series.decimals,
	fips, ?, display_name_short
	FROM series
	LEFT JOIN geographies ON geographies.handle LIKE ?
	LEFT JOIN measurement_series ON measurement_series.series_id = series.id
	LEFT JOIN measurements ON measurements.id = measurement_series.measurement_id
	LEFT JOIN sources ON sources.id = series.source_id
	LEFT JOIN sources AS measurement_sources ON measurement_sources.id = measurements.source_id
	LEFT JOIN source_details ON source_details.id = series.source_detail_id
	LEFT JOIN source_details AS measurement_source_details ON measurement_source_details.id = measurements.source_detail_id
	LEFT JOIN feature_toggles ON feature_toggles.name = 'filter_by_quarantine'
	WHERE NOT series.restricted
	AND (feature_toggles.status IS NULL OR NOT feature_toggles.status OR NOT series.quarantined)
	AND ((MATCH(series.name, series.description, series.dataPortalName) AGAINST(? IN NATURAL LANGUAGE MODE))
	  OR LOWER(CONCAT(series.name, series.description, series.dataPortalName)) LIKE CONCAT('%', LOWER(?), '%'))
	AND LOWER(series.name) LIKE CONCAT('%@', LOWER(?), '.', LOWER(?)) LIMIT 50;`,
		geo,
		geo,
		searchText,
		searchText,
		geo,
		freq,
	)
	if err != nil {
		return
	}
	for rows.Next() {
		dataPortalSeries, scanErr := getNextSeriesFromRows(rows)
		if scanErr != nil {
			return seriesList, scanErr
		}
		geoFreqs, freqGeos, err := getFreqGeoCombinations(r, dataPortalSeries.Id)
		if err != nil {
			return seriesList, err
		}
		dataPortalSeries.GeographyFrequencies = &geoFreqs
		dataPortalSeries.FrequencyGeographies = &freqGeos
		dataPortalSeries.GeoFreqsDeprecated = &geoFreqs
		dataPortalSeries.FreqGeosDeprecated = &freqGeos
		seriesList = append(seriesList, dataPortalSeries)
	}
	return
}

func (r *SeriesRepository) GetInflatedSearchResultsByGeoAndFreq(
	searchText string,
	geo string,
	freq string,
) (seriesList []models.InflatedSeries, err error) {
	rows, err := r.DB.Query(`SELECT DISTINCT
	series.id, series.name, series.description, frequency, series.seasonally_adjusted, series.seasonal_adjustment,
	COALESCE(NULLIF(series.unitsLabel, ''), NULLIF(measurements.units_label, '')),
	COALESCE(NULLIF(series.unitsLabelShort, ''), NULLIF(measurements.units_label_short, '')),
	COALESCE(NULLIF(series.dataPortalName, ''), measurements.data_portal_name), measurements.percent, measurements.real,
	COALESCE(NULLIF(sources.description, ''), NULLIF(measurement_sources.description, '')),
	COALESCE(NULLIF(series.source_link, ''), NULLIF(measurements.source_link, ''), NULLIF(sources.link, ''), NULLIF(measurement_sources.link, '')),
	COALESCE(NULLIF(source_details.description, ''), NULLIF(measurement_source_details.description, '')),
	measurements.table_prefix, measurements.table_postfix,
	measurements.id,
	NULL, series.base_year, series.decimals,
	fips, ?, display_name_short
	FROM series
	LEFT JOIN geographies ON geographies.handle LIKE ?
	LEFT JOIN measurement_series ON measurement_series.series_id = series.id
	LEFT JOIN measurements ON measurements.id = measurement_series.measurement_id
	LEFT JOIN sources ON sources.id = series.source_id
	LEFT JOIN sources AS measurement_sources ON measurement_sources.id = measurements.source_id
	LEFT JOIN source_details ON source_details.id = series.source_detail_id
	LEFT JOIN source_details AS measurement_source_details ON measurement_source_details.id = measurements.source_detail_id
	LEFT JOIN feature_toggles ON feature_toggles.name = 'filter_by_quarantine'
	WHERE NOT series.restricted
	AND (feature_toggles.status IS NULL OR NOT feature_toggles.status OR NOT series.quarantined)
	AND ((MATCH(series.name, series.description, series.dataPortalName) AGAINST(? IN NATURAL LANGUAGE MODE))
	  OR LOWER(CONCAT(series.name, series.description, series.dataPortalName)) LIKE CONCAT('%', LOWER(?), '%'))
	AND LOWER(series.name) LIKE CONCAT('%@', LOWER(?), '.', LOWER(?)) LIMIT 50;`,
		geo,
		geo,
		searchText,
		searchText,
		geo,
		freq,
	)
	if err != nil {
		return
	}
	for rows.Next() {
		dataPortalSeries, scanErr := getNextSeriesFromRows(rows)
		if scanErr != nil {
			return seriesList, scanErr
		}
		geoFreqs, freqGeos, err := getFreqGeoCombinations(r, dataPortalSeries.Id)
		if err != nil {
			return seriesList, err
		}
		dataPortalSeries.GeographyFrequencies = &geoFreqs
		dataPortalSeries.FrequencyGeographies = &freqGeos
		dataPortalSeries.GeoFreqsDeprecated = &geoFreqs
		dataPortalSeries.FreqGeosDeprecated = &freqGeos
		seriesObservations, scanErr := r.GetSeriesObservations(dataPortalSeries.Id)
		if scanErr != nil {
			return seriesList, scanErr
		}
		inflatedSeries := models.InflatedSeries{dataPortalSeries, seriesObservations}
		seriesList = append(seriesList, inflatedSeries)
	}
	return
}
<commit_msg>Eliminate DBEDT series from results (UA-601)<commit_after>package data

import (
	"sort"
	"time"

	"github.com/UHERO/rest-api/models"
)

func (r *SeriesRepository) GetSeriesBySearchText(searchText string) (seriesList []models.DataPortalSeries, err error) {
	rows, err := r.DB.Query(`SELECT DISTINCT
	series.id, series.name, series.description, frequency, series.seasonally_adjusted, series.seasonal_adjustment,
	COALESCE(NULLIF(series.unitsLabel, ''), NULLIF(measurements.units_label, '')),
	COALESCE(NULLIF(series.unitsLabelShort, ''), NULLIF(measurements.units_label_short, '')),
	COALESCE(NULLIF(series.dataPortalName, ''), measurements.data_portal_name), measurements.percent, measurements.real,
	COALESCE(NULLIF(sources.description, ''), NULLIF(measurement_sources.description, '')),
	COALESCE(NULLIF(series.source_link, ''), NULLIF(measurements.source_link, ''), NULLIF(sources.link, ''), NULLIF(measurement_sources.link, '')),
	COALESCE(NULLIF(source_details.description, ''), NULLIF(measurement_source_details.description, '')),
	measurements.table_prefix, measurements.table_postfix,
	measurements.id,
	NULL, series.base_year, series.decimals,
	fips, SUBSTRING_INDEX(SUBSTR(series.name, LOCATE('@', series.name) + 1), '.', 1) as shandle, display_name_short
	FROM series
	LEFT JOIN geographies ON series.name LIKE CONCAT('%@', geographies.handle, '.%')
	LEFT JOIN measurement_series ON measurement_series.series_id = series.id
	LEFT JOIN measurements ON measurements.id = measurement_series.measurement_id
	LEFT JOIN sources ON sources.id = series.source_id
	LEFT JOIN sources AS measurement_sources ON measurement_sources.id = measurements.source_id
	LEFT JOIN source_details ON source_details.id = series.source_detail_id
	LEFT JOIN source_details AS measurement_source_details ON measurement_source_details.id = measurements.source_detail_id
	LEFT JOIN feature_toggles ON feature_toggles.name = 'filter_by_quarantine'
	WHERE NOT series.restricted
	AND series.name NOT LIKE 'DBEDT%'
	AND (feature_toggles.status IS NULL OR NOT feature_toggles.status OR NOT series.quarantined)
	AND ((MATCH(series.name, series.description, series.dataPortalName) AGAINST(? IN NATURAL LANGUAGE MODE))
	  OR LOWER(CONCAT(series.name, series.description, series.dataPortalName)) LIKE CONCAT('%', LOWER(?), '%'))
	LIMIT 50;`, searchText, searchText)
	if err != nil {
		return
	}
	for rows.Next() {
		dataPortalSeries, scanErr := getNextSeriesFromRows(rows)
		if scanErr != nil {
			return seriesList, scanErr
		}
		geoFreqs, freqGeos, err := getFreqGeoCombinations(r, dataPortalSeries.Id)
		if err != nil {
			return seriesList, err
		}
		dataPortalSeries.GeographyFrequencies = &geoFreqs
		dataPortalSeries.FrequencyGeographies = &freqGeos
		dataPortalSeries.GeoFreqsDeprecated = &geoFreqs
		dataPortalSeries.FreqGeosDeprecated = &freqGeos
		seriesList = append(seriesList, dataPortalSeries)
	}
	return
}

func (r *SeriesRepository) GetSearchSummary(searchText string) (searchSummary models.SearchSummary, err error) {
	searchSummary.SearchText = searchText

	var observationStart, observationEnd models.NullTime
	err = r.DB.QueryRow(`SELECT MIN(public_data_points.date) AS start_date, MAX(public_data_points.date) AS end_date
	FROM series
 	LEFT JOIN public_data_points ON public_data_points.series_id = series.id
 	LEFT JOIN feature_toggles ON feature_toggles.name = 'filter_by_quarantine'
	WHERE series.name NOT LIKE 'DBEDT%'
	AND ((MATCH(series.name, series.description, series.dataPortalName) AGAINST(? IN NATURAL LANGUAGE MODE))
	  OR LOWER(CONCAT(series.name, series.description, series.dataPortalName)) LIKE CONCAT('%', LOWER(?), '%'))
	AND NOT series.restricted
	AND (feature_toggles.status IS NULL OR NOT feature_toggles.status OR NOT series.quarantined)`, searchText, searchText).Scan(
		&observationStart,
		&observationEnd,
	)
	if err != nil {
		return
	}
	if observationStart.Valid && observationStart.Time.After(time.Time{}) {
		searchSummary.ObservationStart = &observationStart.Time
	}
	if observationEnd.Valid && observationEnd.Time.After(time.Time{}) {
		searchSummary.ObservationEnd = &observationEnd.Time
	}

	rows, err := r.DB.Query(`SELECT geographies.fips, geographies.display_name_short, geofreq.geo, geofreq.freq
FROM (SELECT MAX(SUBSTRING_INDEX(SUBSTR(s.name, LOCATE('@', s.name) + 1), '.', 1)) as geo, MAX(RIGHT(s.name, 1)) as freq
      FROM (SELECT series.name FROM series
			  LEFT JOIN feature_toggles ON feature_toggles.name = 'filter_by_quarantine'
			WHERE NOT restricted
				AND series.name NOT LIKE 'DBEDT%'
					AND (feature_toggles.status IS NULL OR NOT feature_toggles.status OR NOT series.quarantined)
					AND (MATCH(series.name, series.description, dataPortalName) AGAINST(? IN NATURAL LANGUAGE MODE))
                                 OR LOWER(CONCAT(series.name, series.description, dataPortalName)) LIKE CONCAT('%', LOWER(?), '%')) AS s
GROUP BY SUBSTR(s.name, LOCATE('@', s.name) + 1) ORDER BY COUNT(*) DESC) as geofreq
LEFT JOIN geographies ON geographies.handle = geofreq.geo;`, searchText, searchText)
	if err != nil {
		return
	}
	geoFreqs := map[string][]models.FrequencyResult{}
	geoByHandle := map[string]models.DataPortalGeography{}
	freqGeos := map[string][]models.DataPortalGeography{}
	freqByHandle := map[string]models.FrequencyResult{}

	for rows.Next() {
		scangeo := models.Geography{}
		frequency := models.FrequencyResult{}
		err = rows.Scan(
			&scangeo.FIPS,
			&scangeo.Name,
			&scangeo.Handle,
			&frequency.Freq,
		)
		geography := models.DataPortalGeography{Handle: scangeo.Handle}
		if scangeo.FIPS.Valid {
			geography.FIPS = scangeo.FIPS.String
		}
		if scangeo.Name.Valid {
			geography.Name = scangeo.Name.String
		}
		frequency.Label = freqLabel[frequency.Freq]
		// set the default as the first in the sorted list
		if searchSummary.DefaultGeoFreq == nil {
			searchSummary.DefaultGeoFreq = &models.GeographyFrequency{
				Geography: geography,
				Frequency: frequency,
			}
		}
		// update the freq and geo maps
		geoByHandle[geography.Handle] = geography
		freqByHandle[frequency.Freq] = frequency
		// add to the geoFreqs and freqGeos maps
		geoFreqs[geography.Handle] = append(geoFreqs[geography.Handle], frequency)
		freqGeos[frequency.Freq] = append(freqGeos[frequency.Freq], geography)
	}

	geoFreqsResult := []models.GeographyFrequencies{}
	for geo, freqs := range geoFreqs {
		sort.Sort(models.ByFrequency(freqs))
		geoFreqsResult = append(geoFreqsResult, models.GeographyFrequencies{
			DataPortalGeography: geoByHandle[geo],
			Frequencies:         freqs,
		})
	}

	freqGeosResult := []models.FrequencyGeographies{}
	for _, freq := range models.FreqOrder {
		if val, ok := freqByHandle[freq]; ok {
			freqGeosResult = append(freqGeosResult, models.FrequencyGeographies{
				FrequencyResult: val,
				Geographies:     freqGeos[freq],
			})
		}
	}

	searchSummary.GeographyFrequencies = &geoFreqsResult
	searchSummary.FrequencyGeographies = &freqGeosResult
	searchSummary.GeoFreqsDeprecated = &geoFreqsResult
	searchSummary.FreqGeosDeprecated = &freqGeosResult
	return
}

func (r *SeriesRepository) GetSearchResultsByGeoAndFreq(searchText string, geo string, freq string) (seriesList []models.DataPortalSeries, err error) {
	rows, err := r.DB.Query(`SELECT DISTINCT
	series.id, series.name, series.description, frequency, series.seasonally_adjusted, series.seasonal_adjustment,
	COALESCE(NULLIF(series.unitsLabel, ''), NULLIF(measurements.units_label, '')),
	COALESCE(NULLIF(series.unitsLabelShort, ''), NULLIF(measurements.units_label_short, '')),
	COALESCE(NULLIF(series.dataPortalName, ''), measurements.data_portal_name), measurements.percent, measurements.real,
	COALESCE(NULLIF(sources.description, ''), NULLIF(measurement_sources.description, '')),
	COALESCE(NULLIF(series.source_link, ''), NULLIF(measurements.source_link, ''), NULLIF(sources.link, ''), NULLIF(measurement_sources.link, '')),
	COALESCE(NULLIF(source_details.description, ''), NULLIF(measurement_source_details.description, '')),
	measurements.table_prefix, measurements.table_postfix,
	measurements.id,
	NULL, series.base_year, series.decimals,
	fips, ?, display_name_short
	FROM series
	LEFT JOIN geographies ON geographies.handle LIKE ?
	LEFT JOIN measurement_series ON measurement_series.series_id = series.id
	LEFT JOIN measurements ON measurements.id = measurement_series.measurement_id
	LEFT JOIN sources ON sources.id = series.source_id
	LEFT JOIN sources AS measurement_sources ON measurement_sources.id = measurements.source_id
	LEFT JOIN source_details ON source_details.id = series.source_detail_id
	LEFT JOIN source_details AS measurement_source_details ON measurement_source_details.id = measurements.source_detail_id
	LEFT JOIN feature_toggles ON feature_toggles.name = 'filter_by_quarantine'
	WHERE NOT series.restricted
	AND series.name NOT LIKE 'DBEDT%'
	AND (feature_toggles.status IS NULL OR NOT feature_toggles.status OR NOT series.quarantined)
	AND ((MATCH(series.name, series.description, series.dataPortalName) AGAINST(? IN NATURAL LANGUAGE MODE))
	  OR LOWER(CONCAT(series.name, series.description, series.dataPortalName)) LIKE CONCAT('%', LOWER(?), '%'))
	AND LOWER(series.name) LIKE CONCAT('%@', LOWER(?), '.', LOWER(?)) LIMIT 50;`,
		geo,
		geo,
		searchText,
		searchText,
		geo,
		freq,
	)
	if err != nil {
		return
	}
	for rows.Next() {
		dataPortalSeries, scanErr := getNextSeriesFromRows(rows)
		if scanErr != nil {
			return seriesList, scanErr
		}
		geoFreqs, freqGeos, err := getFreqGeoCombinations(r, dataPortalSeries.Id)
		if err != nil {
			return seriesList, err
		}
		dataPortalSeries.GeographyFrequencies = &geoFreqs
		dataPortalSeries.FrequencyGeographies = &freqGeos
		dataPortalSeries.GeoFreqsDeprecated = &geoFreqs
		dataPortalSeries.FreqGeosDeprecated = &freqGeos
		seriesList = append(seriesList, dataPortalSeries)
	}
	return
}

func (r *SeriesRepository) GetInflatedSearchResultsByGeoAndFreq(
	searchText string,
	geo string,
	freq string,
) (seriesList []models.InflatedSeries, err error) {
	rows, err := r.DB.Query(`SELECT DISTINCT
	series.id, series.name, series.description, frequency, series.seasonally_adjusted, series.seasonal_adjustment,
	COALESCE(NULLIF(series.unitsLabel, ''), NULLIF(measurements.units_label, '')),
	COALESCE(NULLIF(series.unitsLabelShort, ''), NULLIF(measurements.units_label_short, '')),
	COALESCE(NULLIF(series.dataPortalName, ''), measurements.data_portal_name), measurements.percent, measurements.real,
	COALESCE(NULLIF(sources.description, ''), NULLIF(measurement_sources.description, '')),
	COALESCE(NULLIF(series.source_link, ''), NULLIF(measurements.source_link, ''), NULLIF(sources.link, ''), NULLIF(measurement_sources.link, '')),
	COALESCE(NULLIF(source_details.description, ''), NULLIF(measurement_source_details.description, '')),
	measurements.table_prefix, measurements.table_postfix,
	measurements.id,
	NULL, series.base_year, series.decimals,
	fips, ?, display_name_short
	FROM series
	LEFT JOIN geographies ON geographies.handle LIKE ?
	LEFT JOIN measurement_series ON measurement_series.series_id = series.id
	LEFT JOIN measurements ON measurements.id = measurement_series.measurement_id
	LEFT JOIN sources ON sources.id = series.source_id
	LEFT JOIN sources AS measurement_sources ON measurement_sources.id = measurements.source_id
	LEFT JOIN source_details ON source_details.id = series.source_detail_id
	LEFT JOIN source_details AS measurement_source_details ON measurement_source_details.id = measurements.source_detail_id
	LEFT JOIN feature_toggles ON feature_toggles.name = 'filter_by_quarantine'
	WHERE NOT series.restricted
	AND series.name NOT LIKE 'DBEDT%'
	AND (feature_toggles.status IS NULL OR NOT feature_toggles.status OR NOT series.quarantined)
	AND ((MATCH(series.name, series.description, series.dataPortalName) AGAINST(? IN NATURAL LANGUAGE MODE))
	  OR LOWER(CONCAT(series.name, series.description, series.dataPortalName)) LIKE CONCAT('%', LOWER(?), '%'))
	AND LOWER(series.name) LIKE CONCAT('%@', LOWER(?), '.', LOWER(?)) LIMIT 50;`,
		geo,
		geo,
		searchText,
		searchText,
		geo,
		freq,
	)
	if err != nil {
		return
	}
	for rows.Next() {
		dataPortalSeries, scanErr := getNextSeriesFromRows(rows)
		if scanErr != nil {
			return seriesList, scanErr
		}
		geoFreqs, freqGeos, err := getFreqGeoCombinations(r, dataPortalSeries.Id)
		if err != nil {
			return seriesList, err
		}
		dataPortalSeries.GeographyFrequencies = &geoFreqs
		dataPortalSeries.FrequencyGeographies = &freqGeos
		dataPortalSeries.GeoFreqsDeprecated = &geoFreqs
		dataPortalSeries.FreqGeosDeprecated = &freqGeos
		seriesObservations, scanErr := r.GetSeriesObservations(dataPortalSeries.Id)
		if scanErr != nil {
			return seriesList, scanErr
		}
		inflatedSeries := models.InflatedSeries{dataPortalSeries, seriesObservations}
		seriesList = append(seriesList, inflatedSeries)
	}
	return
}
<|endoftext|>


<commit_before>package ws

import (
	"bufio"
	"bytes"
	"crypto/rand"
	"encoding/base64"
	"fmt"
	"net/http"
	"reflect"
	"strings"
	"testing"
)

func TestRequestReset(t *testing.T) {
	for i, test := range []struct {
		url        string
		expHost    string
		expPath    string
		protocols  []string
		extensions []string
		headers    http.Header
		err        bool
	}{
		{
			url:        "wss://websocket.com/chat",
			expHost:    "websocket.com",
			expPath:    "/chat",
			protocols:  []string{"subproto", "hello"},
			extensions: []string{"foo; bar=1", "baz"},
			headers: http.Header{
				"Origin": []string{"https://websocket.com"},
			},
		},
		{
			url: "websocket.com/chat",
			err: true,
		},
	} {
		commonHeaders := map[string]string{
			headerConnection: "Upgrade",
			headerUpgrade:    "websocket",
			headerSecVersion: "13",
		}

		t.Run(fmt.Sprintf("#%d", i), func(t *testing.T) {
			req := getRequest()
			err := req.Reset(test.url, test.headers, test.protocols, test.extensions)
			if test.err && err == nil {
				t.Errorf("expected error; got nil")
			}
			if !test.err && err != nil {
				t.Errorf("unexpected error: %s", err)
			}
			if test.err {
				return
			}

			buf := &bytes.Buffer{}
			if err = req.Write(buf); err != nil {
				t.Errorf("dumping request error: %s", err)
				return
			}

			r, err := http.ReadRequest(bufio.NewReader(buf))
			if err != nil {
				t.Errorf("read request error: %s", err)
				return
			}

			if r.Method != "GET" {
				t.Errorf("http method is %s; want GET", r.Method)
			}
			if r.ProtoMinor != 1 {
				t.Errorf("http proto minor is %d; want 1", r.ProtoMinor)
			}
			if r.ProtoMajor != 1 {
				t.Errorf("http proto major is %d; want 1", r.ProtoMajor)
			}
			if r.URL.Path != test.expPath {
				t.Errorf("http path is %s; want %s", r.URL.Path, test.expPath)
			}

			key := r.Header.Get(headerSecKey)
			bts, err := base64.StdEncoding.DecodeString(key)
			if err != nil {
				t.Errorf("bad %q header: %s", headerSecKey, err)
			}
			if n := len(bts); n != 16 {
				t.Errorf("nonce len is %d; want 16", n)
			}
			r.Header.Del(headerSecKey)

			sub := r.Header.Get(headerSecProtocol)
			protocols := strings.Split(sub, ",")
			for i, p := range protocols {
				protocols[i] = strings.TrimSpace(p)
			}
			if !reflect.DeepEqual(protocols, test.protocols) {
				t.Errorf("%q headers is %v; want %s", headerSecProtocol, protocols, test.protocols)
			}
			r.Header.Del(headerSecProtocol)

			ext := r.Header.Get(headerSecExtensions)
			extensions := strings.Split(ext, ",")
			for i, e := range extensions {
				extensions[i] = strings.TrimSpace(e)
			}
			if !reflect.DeepEqual(extensions, test.extensions) {
				t.Errorf("%q headers is %v; want %s", headerSecExtensions, extensions, test.extensions)
			}
			r.Header.Del(headerSecExtensions)

			for key, exp := range commonHeaders {
				if act := r.Header.Get(key); act != exp {
					t.Errorf("http %q header is %q; want %q", key, act, exp)
				}
				r.Header.Del(key)
			}
			for key, exp := range test.headers {
				if act := r.Header.Get(key); act != exp[0] {
					t.Errorf("http %q custom header is %q; want %q", key, act, exp[0])
				}
				r.Header.Del(key)
			}
			if len(r.Header) != 0 {
				t.Errorf("http request has extra headers:\n\t%v", r.Header)
			}
		})
	}
}

func BenchmarkMakeAccept(b *testing.B) {
	nonce := make([]byte, nonceSize)
	_, err := rand.Read(nonce)
	if err != nil {
		b.Fatal(err)
	}
	b.StartTimer()
	for i := 0; i < b.N; i++ {
		_ = makeAccept(nonce)
	}
}
<commit_msg>add check nonce benchmark<commit_after>package ws

import (
	"bufio"
	"bytes"
	"crypto/rand"
	"encoding/base64"
	"fmt"
	"net/http"
	"reflect"
	"strings"
	"testing"
)

func TestRequestReset(t *testing.T) {
	for i, test := range []struct {
		url        string
		expHost    string
		expPath    string
		protocols  []string
		extensions []string
		headers    http.Header
		err        bool
	}{
		{
			url:        "wss://websocket.com/chat",
			expHost:    "websocket.com",
			expPath:    "/chat",
			protocols:  []string{"subproto", "hello"},
			extensions: []string{"foo; bar=1", "baz"},
			headers: http.Header{
				"Origin": []string{"https://websocket.com"},
			},
		},
		{
			url: "websocket.com/chat",
			err: true,
		},
	} {
		commonHeaders := map[string]string{
			headerConnection: "Upgrade",
			headerUpgrade:    "websocket",
			headerSecVersion: "13",
		}

		t.Run(fmt.Sprintf("#%d", i), func(t *testing.T) {
			req := getRequest()
			err := req.Reset(test.url, test.headers, test.protocols, test.extensions)
			if test.err && err == nil {
				t.Errorf("expected error; got nil")
			}
			if !test.err && err != nil {
				t.Errorf("unexpected error: %s", err)
			}
			if test.err {
				return
			}

			buf := &bytes.Buffer{}
			if err = req.Write(buf); err != nil {
				t.Errorf("dumping request error: %s", err)
				return
			}

			r, err := http.ReadRequest(bufio.NewReader(buf))
			if err != nil {
				t.Errorf("read request error: %s", err)
				return
			}

			if r.Method != "GET" {
				t.Errorf("http method is %s; want GET", r.Method)
			}
			if r.ProtoMinor != 1 {
				t.Errorf("http proto minor is %d; want 1", r.ProtoMinor)
			}
			if r.ProtoMajor != 1 {
				t.Errorf("http proto major is %d; want 1", r.ProtoMajor)
			}
			if r.URL.Path != test.expPath {
				t.Errorf("http path is %s; want %s", r.URL.Path, test.expPath)
			}

			key := r.Header.Get(headerSecKey)
			bts, err := base64.StdEncoding.DecodeString(key)
			if err != nil {
				t.Errorf("bad %q header: %s", headerSecKey, err)
			}
			if n := len(bts); n != 16 {
				t.Errorf("nonce len is %d; want 16", n)
			}
			r.Header.Del(headerSecKey)

			sub := r.Header.Get(headerSecProtocol)
			protocols := strings.Split(sub, ",")
			for i, p := range protocols {
				protocols[i] = strings.TrimSpace(p)
			}
			if !reflect.DeepEqual(protocols, test.protocols) {
				t.Errorf("%q headers is %v; want %s", headerSecProtocol, protocols, test.protocols)
			}
			r.Header.Del(headerSecProtocol)

			ext := r.Header.Get(headerSecExtensions)
			extensions := strings.Split(ext, ",")
			for i, e := range extensions {
				extensions[i] = strings.TrimSpace(e)
			}
			if !reflect.DeepEqual(extensions, test.extensions) {
				t.Errorf("%q headers is %v; want %s", headerSecExtensions, extensions, test.extensions)
			}
			r.Header.Del(headerSecExtensions)

			for key, exp := range commonHeaders {
				if act := r.Header.Get(key); act != exp {
					t.Errorf("http %q header is %q; want %q", key, act, exp)
				}
				r.Header.Del(key)
			}
			for key, exp := range test.headers {
				if act := r.Header.Get(key); act != exp[0] {
					t.Errorf("http %q custom header is %q; want %q", key, act, exp[0])
				}
				r.Header.Del(key)
			}
			if len(r.Header) != 0 {
				t.Errorf("http request has extra headers:\n\t%v", r.Header)
			}
		})
	}
}

func BenchmarkMakeAccept(b *testing.B) {
	nonce := make([]byte, nonceSize)
	_, err := rand.Read(nonce)
	if err != nil {
		b.Fatal(err)
	}
	b.StartTimer()
	for i := 0; i < b.N; i++ {
		_ = makeAccept(nonce)
	}
}

func BenchmarkCheckNonce(b *testing.B) {
	var nonce [nonceSize]byte
	_, err := rand.Read(nonce[:])
	if err != nil {
		b.Fatal(err)
	}

	accept := makeAccept(nonce[:])

	b.StartTimer()
	for i := 0; i < b.N; i++ {
		_ = checkNonce(accept, nonce)
	}
}
<|endoftext|>


<commit_before>/*
Copyright 2019 The Kubernetes 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.
*/

package noderesources

import (
	"context"
	"fmt"
	"strings"

	v1 "k8s.io/api/core/v1"
	metav1validation "k8s.io/apimachinery/pkg/apis/meta/v1/validation"
	"k8s.io/apimachinery/pkg/runtime"
	"k8s.io/apimachinery/pkg/util/sets"
	"k8s.io/apimachinery/pkg/util/validation/field"
	utilfeature "k8s.io/apiserver/pkg/util/feature"
	v1helper "k8s.io/kubernetes/pkg/apis/core/v1/helper"
	"k8s.io/kubernetes/pkg/features"
	"k8s.io/kubernetes/pkg/scheduler/apis/config"
	"k8s.io/kubernetes/pkg/scheduler/framework"
)

var _ framework.PreFilterPlugin = &Fit{}
var _ framework.FilterPlugin = &Fit{}

const (
	// FitName is the name of the plugin used in the plugin registry and configurations.
	FitName = "NodeResourcesFit"

	// preFilterStateKey is the key in CycleState to NodeResourcesFit pre-computed data.
	// Using the name of the plugin will likely help us avoid collisions with other plugins.
	preFilterStateKey = "PreFilter" + FitName
)

// Fit is a plugin that checks if a node has sufficient resources.
type Fit struct {
	ignoredResources      sets.String
	ignoredResourceGroups sets.String
}

// preFilterState computed at PreFilter and used at Filter.
type preFilterState struct {
	framework.Resource
}

// Clone the prefilter state.
func (s *preFilterState) Clone() framework.StateData {
	return s
}

// Name returns name of the plugin. It is used in logs, etc.
func (f *Fit) Name() string {
	return FitName
}

func validateFitArgs(args config.NodeResourcesFitArgs) error {
	var allErrs field.ErrorList
	resPath := field.NewPath("ignoredResources")
	for i, res := range args.IgnoredResources {
		path := resPath.Index(i)
		if errs := metav1validation.ValidateLabelName(res, path); len(errs) != 0 {
			allErrs = append(allErrs, errs...)
		}
	}

	groupPath := field.NewPath("ignoredResourceGroups")
	for i, group := range args.IgnoredResourceGroups {
		path := groupPath.Index(i)
		if strings.Contains(group, "/") {
			allErrs = append(allErrs, field.Invalid(path, group, "resource group name can't contain '/'"))
		}
		if errs := metav1validation.ValidateLabelName(group, path); len(errs) != 0 {
			allErrs = append(allErrs, errs...)
		}
	}

	if len(allErrs) == 0 {
		return nil
	}
	return allErrs.ToAggregate()
}

// NewFit initializes a new plugin and returns it.
func NewFit(plArgs runtime.Object, _ framework.Handle) (framework.Plugin, error) {
	args, err := getFitArgs(plArgs)
	if err != nil {
		return nil, err
	}

	if err := validateFitArgs(args); err != nil {
		return nil, err
	}

	return &Fit{
		ignoredResources:      sets.NewString(args.IgnoredResources...),
		ignoredResourceGroups: sets.NewString(args.IgnoredResourceGroups...),
	}, nil
}

func getFitArgs(obj runtime.Object) (config.NodeResourcesFitArgs, error) {
	ptr, ok := obj.(*config.NodeResourcesFitArgs)
	if !ok {
		return config.NodeResourcesFitArgs{}, fmt.Errorf("want args to be of type NodeResourcesFitArgs, got %T", obj)
	}
	return *ptr, nil
}

// computePodResourceRequest returns a framework.Resource that covers the largest
// width in each resource dimension. Because init-containers run sequentially, we collect
// the max in each dimension iteratively. In contrast, we sum the resource vectors for
// regular containers since they run simultaneously.
//
// If Pod Overhead is specified and the feature gate is set, the resources defined for Overhead
// are added to the calculated Resource request sum
//
// Example:
//
// Pod:
//   InitContainers
//     IC1:
//       CPU: 2
//       Memory: 1G
//     IC2:
//       CPU: 2
//       Memory: 3G
//   Containers
//     C1:
//       CPU: 2
//       Memory: 1G
//     C2:
//       CPU: 1
//       Memory: 1G
//
// Result: CPU: 3, Memory: 3G
func computePodResourceRequest(pod *v1.Pod) *preFilterState {
	result := &preFilterState{}
	for _, container := range pod.Spec.Containers {
		result.Add(container.Resources.Requests)
	}

	// take max_resource(sum_pod, any_init_container)
	for _, container := range pod.Spec.InitContainers {
		result.SetMaxResource(container.Resources.Requests)
	}

	// If Overhead is being utilized, add to the total requests for the pod
	if pod.Spec.Overhead != nil && utilfeature.DefaultFeatureGate.Enabled(features.PodOverhead) {
		result.Add(pod.Spec.Overhead)
	}

	return result
}

// PreFilter invoked at the prefilter extension point.
func (f *Fit) PreFilter(ctx context.Context, cycleState *framework.CycleState, pod *v1.Pod) *framework.Status {
	cycleState.Write(preFilterStateKey, computePodResourceRequest(pod))
	return nil
}

// PreFilterExtensions returns prefilter extensions, pod add and remove.
func (f *Fit) PreFilterExtensions() framework.PreFilterExtensions {
	return nil
}

func getPreFilterState(cycleState *framework.CycleState) (*preFilterState, error) {
	c, err := cycleState.Read(preFilterStateKey)
	if err != nil {
		// preFilterState doesn't exist, likely PreFilter wasn't invoked.
		return nil, fmt.Errorf("error reading %q from cycleState: %v", preFilterStateKey, err)
	}

	s, ok := c.(*preFilterState)
	if !ok {
		return nil, fmt.Errorf("%+v  convert to NodeResourcesFit.preFilterState error", c)
	}
	return s, nil
}

// Filter invoked at the filter extension point.
// Checks if a node has sufficient resources, such as cpu, memory, gpu, opaque int resources etc to run a pod.
// It returns a list of insufficient resources, if empty, then the node has all the resources requested by the pod.
func (f *Fit) Filter(ctx context.Context, cycleState *framework.CycleState, pod *v1.Pod, nodeInfo *framework.NodeInfo) *framework.Status {
	s, err := getPreFilterState(cycleState)
	if err != nil {
		return framework.NewStatus(framework.Error, err.Error())
	}

	insufficientResources := fitsRequest(s, nodeInfo, f.ignoredResources, f.ignoredResourceGroups)

	if len(insufficientResources) != 0 {
		// We will keep all failure reasons.
		failureReasons := make([]string, 0, len(insufficientResources))
		for _, r := range insufficientResources {
			failureReasons = append(failureReasons, r.Reason)
		}
		return framework.NewStatus(framework.Unschedulable, failureReasons...)
	}
	return nil
}

// InsufficientResource describes what kind of resource limit is hit and caused the pod to not fit the node.
type InsufficientResource struct {
	ResourceName v1.ResourceName
	// We explicitly have a parameter for reason to avoid formatting a message on the fly
	// for common resources, which is expensive for cluster autoscaler simulations.
	Reason    string
	Requested int64
	Used      int64
	Capacity  int64
}

// Fits checks if node have enough resources to host the pod.
func Fits(pod *v1.Pod, nodeInfo *framework.NodeInfo) []InsufficientResource {
	return fitsRequest(computePodResourceRequest(pod), nodeInfo, nil, nil)
}

func fitsRequest(podRequest *preFilterState, nodeInfo *framework.NodeInfo, ignoredExtendedResources, ignoredResourceGroups sets.String) []InsufficientResource {
	insufficientResources := make([]InsufficientResource, 0, 4)

	allowedPodNumber := nodeInfo.Allocatable.AllowedPodNumber
	if len(nodeInfo.Pods)+1 > allowedPodNumber {
		insufficientResources = append(insufficientResources, InsufficientResource{
			v1.ResourcePods,
			"Too many pods",
			1,
			int64(len(nodeInfo.Pods)),
			int64(allowedPodNumber),
		})
	}

	if podRequest.MilliCPU == 0 &&
		podRequest.Memory == 0 &&
		podRequest.EphemeralStorage == 0 &&
		len(podRequest.ScalarResources) == 0 {
		return insufficientResources
	}

	if nodeInfo.Allocatable.MilliCPU < podRequest.MilliCPU+nodeInfo.Requested.MilliCPU {
		insufficientResources = append(insufficientResources, InsufficientResource{
			v1.ResourceCPU,
			"Insufficient cpu",
			podRequest.MilliCPU,
			nodeInfo.Requested.MilliCPU,
			nodeInfo.Allocatable.MilliCPU,
		})
	}
	if nodeInfo.Allocatable.Memory < podRequest.Memory+nodeInfo.Requested.Memory {
		insufficientResources = append(insufficientResources, InsufficientResource{
			v1.ResourceMemory,
			"Insufficient memory",
			podRequest.Memory,
			nodeInfo.Requested.Memory,
			nodeInfo.Allocatable.Memory,
		})
	}
	if nodeInfo.Allocatable.EphemeralStorage < podRequest.EphemeralStorage+nodeInfo.Requested.EphemeralStorage {
		insufficientResources = append(insufficientResources, InsufficientResource{
			v1.ResourceEphemeralStorage,
			"Insufficient ephemeral-storage",
			podRequest.EphemeralStorage,
			nodeInfo.Requested.EphemeralStorage,
			nodeInfo.Allocatable.EphemeralStorage,
		})
	}

	for rName, rQuant := range podRequest.ScalarResources {
		if v1helper.IsExtendedResourceName(rName) {
			// If this resource is one of the extended resources that should be ignored, we will skip checking it.
			// rName is guaranteed to have a slash due to API validation.
			var rNamePrefix string
			if ignoredResourceGroups.Len() > 0 {
				rNamePrefix = strings.Split(string(rName), "/")[0]
			}
			if ignoredExtendedResources.Has(string(rName)) || ignoredResourceGroups.Has(rNamePrefix) {
				continue
			}
		}
		if nodeInfo.Allocatable.ScalarResources[rName] < rQuant+nodeInfo.Requested.ScalarResources[rName] {
			insufficientResources = append(insufficientResources, InsufficientResource{
				rName,
				fmt.Sprintf("Insufficient %v", rName),
				podRequest.ScalarResources[rName],
				nodeInfo.Requested.ScalarResources[rName],
				nodeInfo.Allocatable.ScalarResources[rName],
			})
		}
	}

	return insufficientResources
}
<commit_msg>optimize the logic of noderesources comparision<commit_after>/*
Copyright 2019 The Kubernetes 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.
*/

package noderesources

import (
	"context"
	"fmt"
	"strings"

	v1 "k8s.io/api/core/v1"
	metav1validation "k8s.io/apimachinery/pkg/apis/meta/v1/validation"
	"k8s.io/apimachinery/pkg/runtime"
	"k8s.io/apimachinery/pkg/util/sets"
	"k8s.io/apimachinery/pkg/util/validation/field"
	utilfeature "k8s.io/apiserver/pkg/util/feature"
	v1helper "k8s.io/kubernetes/pkg/apis/core/v1/helper"
	"k8s.io/kubernetes/pkg/features"
	"k8s.io/kubernetes/pkg/scheduler/apis/config"
	"k8s.io/kubernetes/pkg/scheduler/framework"
)

var _ framework.PreFilterPlugin = &Fit{}
var _ framework.FilterPlugin = &Fit{}

const (
	// FitName is the name of the plugin used in the plugin registry and configurations.
	FitName = "NodeResourcesFit"

	// preFilterStateKey is the key in CycleState to NodeResourcesFit pre-computed data.
	// Using the name of the plugin will likely help us avoid collisions with other plugins.
	preFilterStateKey = "PreFilter" + FitName
)

// Fit is a plugin that checks if a node has sufficient resources.
type Fit struct {
	ignoredResources      sets.String
	ignoredResourceGroups sets.String
}

// preFilterState computed at PreFilter and used at Filter.
type preFilterState struct {
	framework.Resource
}

// Clone the prefilter state.
func (s *preFilterState) Clone() framework.StateData {
	return s
}

// Name returns name of the plugin. It is used in logs, etc.
func (f *Fit) Name() string {
	return FitName
}

func validateFitArgs(args config.NodeResourcesFitArgs) error {
	var allErrs field.ErrorList
	resPath := field.NewPath("ignoredResources")
	for i, res := range args.IgnoredResources {
		path := resPath.Index(i)
		if errs := metav1validation.ValidateLabelName(res, path); len(errs) != 0 {
			allErrs = append(allErrs, errs...)
		}
	}

	groupPath := field.NewPath("ignoredResourceGroups")
	for i, group := range args.IgnoredResourceGroups {
		path := groupPath.Index(i)
		if strings.Contains(group, "/") {
			allErrs = append(allErrs, field.Invalid(path, group, "resource group name can't contain '/'"))
		}
		if errs := metav1validation.ValidateLabelName(group, path); len(errs) != 0 {
			allErrs = append(allErrs, errs...)
		}
	}

	if len(allErrs) == 0 {
		return nil
	}
	return allErrs.ToAggregate()
}

// NewFit initializes a new plugin and returns it.
func NewFit(plArgs runtime.Object, _ framework.Handle) (framework.Plugin, error) {
	args, err := getFitArgs(plArgs)
	if err != nil {
		return nil, err
	}

	if err := validateFitArgs(args); err != nil {
		return nil, err
	}

	return &Fit{
		ignoredResources:      sets.NewString(args.IgnoredResources...),
		ignoredResourceGroups: sets.NewString(args.IgnoredResourceGroups...),
	}, nil
}

func getFitArgs(obj runtime.Object) (config.NodeResourcesFitArgs, error) {
	ptr, ok := obj.(*config.NodeResourcesFitArgs)
	if !ok {
		return config.NodeResourcesFitArgs{}, fmt.Errorf("want args to be of type NodeResourcesFitArgs, got %T", obj)
	}
	return *ptr, nil
}

// computePodResourceRequest returns a framework.Resource that covers the largest
// width in each resource dimension. Because init-containers run sequentially, we collect
// the max in each dimension iteratively. In contrast, we sum the resource vectors for
// regular containers since they run simultaneously.
//
// If Pod Overhead is specified and the feature gate is set, the resources defined for Overhead
// are added to the calculated Resource request sum
//
// Example:
//
// Pod:
//   InitContainers
//     IC1:
//       CPU: 2
//       Memory: 1G
//     IC2:
//       CPU: 2
//       Memory: 3G
//   Containers
//     C1:
//       CPU: 2
//       Memory: 1G
//     C2:
//       CPU: 1
//       Memory: 1G
//
// Result: CPU: 3, Memory: 3G
func computePodResourceRequest(pod *v1.Pod) *preFilterState {
	result := &preFilterState{}
	for _, container := range pod.Spec.Containers {
		result.Add(container.Resources.Requests)
	}

	// take max_resource(sum_pod, any_init_container)
	for _, container := range pod.Spec.InitContainers {
		result.SetMaxResource(container.Resources.Requests)
	}

	// If Overhead is being utilized, add to the total requests for the pod
	if pod.Spec.Overhead != nil && utilfeature.DefaultFeatureGate.Enabled(features.PodOverhead) {
		result.Add(pod.Spec.Overhead)
	}

	return result
}

// PreFilter invoked at the prefilter extension point.
func (f *Fit) PreFilter(ctx context.Context, cycleState *framework.CycleState, pod *v1.Pod) *framework.Status {
	cycleState.Write(preFilterStateKey, computePodResourceRequest(pod))
	return nil
}

// PreFilterExtensions returns prefilter extensions, pod add and remove.
func (f *Fit) PreFilterExtensions() framework.PreFilterExtensions {
	return nil
}

func getPreFilterState(cycleState *framework.CycleState) (*preFilterState, error) {
	c, err := cycleState.Read(preFilterStateKey)
	if err != nil {
		// preFilterState doesn't exist, likely PreFilter wasn't invoked.
		return nil, fmt.Errorf("error reading %q from cycleState: %v", preFilterStateKey, err)
	}

	s, ok := c.(*preFilterState)
	if !ok {
		return nil, fmt.Errorf("%+v  convert to NodeResourcesFit.preFilterState error", c)
	}
	return s, nil
}

// Filter invoked at the filter extension point.
// Checks if a node has sufficient resources, such as cpu, memory, gpu, opaque int resources etc to run a pod.
// It returns a list of insufficient resources, if empty, then the node has all the resources requested by the pod.
func (f *Fit) Filter(ctx context.Context, cycleState *framework.CycleState, pod *v1.Pod, nodeInfo *framework.NodeInfo) *framework.Status {
	s, err := getPreFilterState(cycleState)
	if err != nil {
		return framework.NewStatus(framework.Error, err.Error())
	}

	insufficientResources := fitsRequest(s, nodeInfo, f.ignoredResources, f.ignoredResourceGroups)

	if len(insufficientResources) != 0 {
		// We will keep all failure reasons.
		failureReasons := make([]string, 0, len(insufficientResources))
		for _, r := range insufficientResources {
			failureReasons = append(failureReasons, r.Reason)
		}
		return framework.NewStatus(framework.Unschedulable, failureReasons...)
	}
	return nil
}

// InsufficientResource describes what kind of resource limit is hit and caused the pod to not fit the node.
type InsufficientResource struct {
	ResourceName v1.ResourceName
	// We explicitly have a parameter for reason to avoid formatting a message on the fly
	// for common resources, which is expensive for cluster autoscaler simulations.
	Reason    string
	Requested int64
	Used      int64
	Capacity  int64
}

// Fits checks if node have enough resources to host the pod.
func Fits(pod *v1.Pod, nodeInfo *framework.NodeInfo) []InsufficientResource {
	return fitsRequest(computePodResourceRequest(pod), nodeInfo, nil, nil)
}

func fitsRequest(podRequest *preFilterState, nodeInfo *framework.NodeInfo, ignoredExtendedResources, ignoredResourceGroups sets.String) []InsufficientResource {
	insufficientResources := make([]InsufficientResource, 0, 4)

	allowedPodNumber := nodeInfo.Allocatable.AllowedPodNumber
	if len(nodeInfo.Pods)+1 > allowedPodNumber {
		insufficientResources = append(insufficientResources, InsufficientResource{
			v1.ResourcePods,
			"Too many pods",
			1,
			int64(len(nodeInfo.Pods)),
			int64(allowedPodNumber),
		})
	}

	if podRequest.MilliCPU == 0 &&
		podRequest.Memory == 0 &&
		podRequest.EphemeralStorage == 0 &&
		len(podRequest.ScalarResources) == 0 {
		return insufficientResources
	}

	if podRequest.MilliCPU > (nodeInfo.Allocatable.MilliCPU - nodeInfo.Requested.MilliCPU) {
		insufficientResources = append(insufficientResources, InsufficientResource{
			v1.ResourceCPU,
			"Insufficient cpu",
			podRequest.MilliCPU,
			nodeInfo.Requested.MilliCPU,
			nodeInfo.Allocatable.MilliCPU,
		})
	}
	if podRequest.Memory > (nodeInfo.Allocatable.Memory - nodeInfo.Requested.Memory) {
		insufficientResources = append(insufficientResources, InsufficientResource{
			v1.ResourceMemory,
			"Insufficient memory",
			podRequest.Memory,
			nodeInfo.Requested.Memory,
			nodeInfo.Allocatable.Memory,
		})
	}
	if podRequest.EphemeralStorage > (nodeInfo.Allocatable.EphemeralStorage - nodeInfo.Requested.EphemeralStorage) {
		insufficientResources = append(insufficientResources, InsufficientResource{
			v1.ResourceEphemeralStorage,
			"Insufficient ephemeral-storage",
			podRequest.EphemeralStorage,
			nodeInfo.Requested.EphemeralStorage,
			nodeInfo.Allocatable.EphemeralStorage,
		})
	}

	for rName, rQuant := range podRequest.ScalarResources {
		if v1helper.IsExtendedResourceName(rName) {
			// If this resource is one of the extended resources that should be ignored, we will skip checking it.
			// rName is guaranteed to have a slash due to API validation.
			var rNamePrefix string
			if ignoredResourceGroups.Len() > 0 {
				rNamePrefix = strings.Split(string(rName), "/")[0]
			}
			if ignoredExtendedResources.Has(string(rName)) || ignoredResourceGroups.Has(rNamePrefix) {
				continue
			}
		}
		if rQuant > (nodeInfo.Allocatable.ScalarResources[rName] - nodeInfo.Requested.ScalarResources[rName]) {
			insufficientResources = append(insufficientResources, InsufficientResource{
				rName,
				fmt.Sprintf("Insufficient %v", rName),
				podRequest.ScalarResources[rName],
				nodeInfo.Requested.ScalarResources[rName],
				nodeInfo.Allocatable.ScalarResources[rName],
			})
		}
	}

	return insufficientResources
}
<|endoftext|>


<commit_before>package main

import (
	"fmt"
	"github.com/Sirupsen/logrus"
	"github.com/rancher/external-dns/providers"
	"strings"
	"sync"
)

func UpdateProviderDnsRecords(metadataRecs map[string]providers.DnsRecord) error {
	providerRecs, err := getProviderDnsRecords()
	if err != nil {
		return fmt.Errorf("Provider error reading dns entries: %v", err)
	}
	logrus.Debugf("DNS records from provider: %v", providerRecs)
	if err = addMissingRecords(metadataRecs, providerRecs); err != nil {
		return fmt.Errorf("Failed to add missing records: %v", err)
	}

	if err = removeExtraRecords(metadataRecs, providerRecs); err != nil {
		return fmt.Errorf("Failed to remove extra records: %v", err)
	}

	if err = updateExistingRecords(metadataRecs, providerRecs); err != nil {
		return fmt.Errorf("Failed to update existing records records: %v", err)
	}
	return nil
}

func addMissingRecords(metadataRecs map[string]providers.DnsRecord, providerRecs map[string]providers.DnsRecord) error {
	var toAdd []providers.DnsRecord
	for key := range metadataRecs {
		if _, ok := providerRecs[key]; !ok {
			toAdd = append(toAdd, metadataRecs[key])
		}
	}
	if len(toAdd) == 0 {
		logrus.Debug("No DNS records to add")
		return nil
	} else {
		logrus.Infof("DNS records to add: %v", toAdd)
	}
	return updateRecords(toAdd, &Add)
}

func updateRecords(toChange []providers.DnsRecord, op *Op) error {
	values := make(chan providers.DnsRecord)
	var wg sync.WaitGroup
	wg.Add(len(toChange))

	for _, value := range toChange {
		go func(value providers.DnsRecord) {
			defer wg.Done()
			values <- value
		}(value)
	}

	go func() error {
		for value := range values {
			updateCattle := false
			switch *op {
			case Add:
				logrus.Infof("Adding dns record: %v", value)
				if err := provider.AddRecord(value); err != nil {
					return fmt.Errorf("Failed to add DNS record to provider %v: %v", value, err)
				}
				updateCattle = true
			case Remove:
				logrus.Infof("Removing dns record: %v", value)
				if err := provider.RemoveRecord(value); err != nil {
					return fmt.Errorf("Failed to remove DNS record from provider %v: %v", value, err)
				}
			case Update:
				logrus.Infof("Updating dns record: %v", value)
				if err := provider.UpdateRecord(value); err != nil {
					return fmt.Errorf("Failed to update DNS record to provider %v: %v", value, err)
				}
				updateCattle = true
			}
			if updateCattle {
				serviceDnsRecord := convertToServiceDnsRecord(value)
				c.UpdateServiceDomainName(serviceDnsRecord)
			}
		}
		return nil
	}()
	wg.Wait()
	return nil
}

func updateExistingRecords(metadataRecs map[string]providers.DnsRecord, providerRecs map[string]providers.DnsRecord) error {
	var toUpdate []providers.DnsRecord
	for key := range metadataRecs {
		if _, ok := providerRecs[key]; ok {
			metadataR := make(map[string]struct{}, len(metadataRecs[key].Records))
			for _, s := range metadataRecs[key].Records {
				metadataR[s] = struct{}{}
			}

			providerR := make(map[string]struct{}, len(providerRecs[key].Records))
			for _, s := range providerRecs[key].Records {
				providerR[s] = struct{}{}
			}
			var update bool
			if len(metadataR) != len(providerR) {
				update = true
			} else {
				for key := range metadataR {
					if _, ok := providerR[key]; !ok {
						update = true
					}
				}
				for key := range providerR {
					if _, ok := metadataR[key]; !ok {
						update = true
					}
				}
			}
			if update {
				toUpdate = append(toUpdate, metadataRecs[key])
			}
		}
	}

	if len(toUpdate) == 0 {
		logrus.Debug("No DNS records to update")
		return nil
	} else {
		logrus.Infof("DNS records to update: %v", toUpdate)
	}

	return updateRecords(toUpdate, &Update)
}

func removeExtraRecords(metadataRecs map[string]providers.DnsRecord, providerRecs map[string]providers.DnsRecord) error {
	var toRemove []providers.DnsRecord
	for key := range providerRecs {
		if _, ok := metadataRecs[key]; !ok {
			toRemove = append(toRemove, providerRecs[key])
		}
	}

	if len(toRemove) == 0 {
		logrus.Debug("No DNS records to remove")
		return nil
	} else {
		logrus.Infof("DNS records to remove: %v", toRemove)
	}
	return updateRecords(toRemove, &Remove)
}

func getProviderDnsRecords() (map[string]providers.DnsRecord, error) {
	allRecords, err := provider.GetRecords()
	if err != nil {
		return nil, err
	}
	ourRecords := make(map[string]providers.DnsRecord, len(allRecords))
	joins := []string{EnvironmentName, providers.RootDomainName}
	suffix := strings.ToLower(strings.Join(joins, "."))
	for _, value := range allRecords {
		if strings.HasSuffix(value.DomainName, suffix) {
			ourRecords[value.DomainName] = value
		}
	}
	return ourRecords, nil
}

func addToDnsEntries(dnsEntry providers.DnsRecord, dnsEntries map[string]providers.DnsRecord) {
	var records []string
	if _, ok := dnsEntries[dnsEntry.DomainName]; ok {
		records = dnsEntry.Records
	} else {
		records = dnsEntries[dnsEntry.DomainName].Records
		records = append(records, dnsEntry.Records...)
	}
	dnsEntry = providers.DnsRecord{dnsEntry.DomainName, records, "A", 300}
	dnsEntries[dnsEntry.DomainName] = dnsEntry
}
<commit_msg>Fixed the bug in multi ips records set<commit_after>package main

import (
	"fmt"
	"github.com/Sirupsen/logrus"
	"github.com/rancher/external-dns/providers"
	"strings"
	"sync"
)

func UpdateProviderDnsRecords(metadataRecs map[string]providers.DnsRecord) error {
	providerRecs, err := getProviderDnsRecords()
	if err != nil {
		return fmt.Errorf("Provider error reading dns entries: %v", err)
	}
	logrus.Debugf("DNS records from provider: %v", providerRecs)
	if err = addMissingRecords(metadataRecs, providerRecs); err != nil {
		return fmt.Errorf("Failed to add missing records: %v", err)
	}

	if err = removeExtraRecords(metadataRecs, providerRecs); err != nil {
		return fmt.Errorf("Failed to remove extra records: %v", err)
	}

	if err = updateExistingRecords(metadataRecs, providerRecs); err != nil {
		return fmt.Errorf("Failed to update existing records records: %v", err)
	}
	return nil
}

func addMissingRecords(metadataRecs map[string]providers.DnsRecord, providerRecs map[string]providers.DnsRecord) error {
	var toAdd []providers.DnsRecord
	for key := range metadataRecs {
		if _, ok := providerRecs[key]; !ok {
			toAdd = append(toAdd, metadataRecs[key])
		}
	}
	if len(toAdd) == 0 {
		logrus.Debug("No DNS records to add")
		return nil
	} else {
		logrus.Infof("DNS records to add: %v", toAdd)
	}
	return updateRecords(toAdd, &Add)
}

func updateRecords(toChange []providers.DnsRecord, op *Op) error {
	values := make(chan providers.DnsRecord)
	var wg sync.WaitGroup
	wg.Add(len(toChange))

	for _, value := range toChange {
		go func(value providers.DnsRecord) {
			defer wg.Done()
			values <- value
		}(value)
	}

	go func() error {
		for value := range values {
			updateCattle := false
			switch *op {
			case Add:
				logrus.Infof("Adding dns record: %v", value)
				if err := provider.AddRecord(value); err != nil {
					return fmt.Errorf("Failed to add DNS record to provider %v: %v", value, err)
				}
				updateCattle = true
			case Remove:
				logrus.Infof("Removing dns record: %v", value)
				if err := provider.RemoveRecord(value); err != nil {
					return fmt.Errorf("Failed to remove DNS record from provider %v: %v", value, err)
				}
			case Update:
				logrus.Infof("Updating dns record: %v", value)
				if err := provider.UpdateRecord(value); err != nil {
					return fmt.Errorf("Failed to update DNS record to provider %v: %v", value, err)
				}
				updateCattle = true
			}
			if updateCattle {
				serviceDnsRecord := convertToServiceDnsRecord(value)
				c.UpdateServiceDomainName(serviceDnsRecord)
			}
		}
		return nil
	}()
	wg.Wait()
	return nil
}

func updateExistingRecords(metadataRecs map[string]providers.DnsRecord, providerRecs map[string]providers.DnsRecord) error {
	var toUpdate []providers.DnsRecord
	for key := range metadataRecs {
		if _, ok := providerRecs[key]; ok {
			metadataR := make(map[string]struct{}, len(metadataRecs[key].Records))
			for _, s := range metadataRecs[key].Records {
				metadataR[s] = struct{}{}
			}

			providerR := make(map[string]struct{}, len(providerRecs[key].Records))
			for _, s := range providerRecs[key].Records {
				providerR[s] = struct{}{}
			}
			var update bool
			if len(metadataR) != len(providerR) {
				update = true
			} else {
				for key := range metadataR {
					if _, ok := providerR[key]; !ok {
						update = true
					}
				}
				for key := range providerR {
					if _, ok := metadataR[key]; !ok {
						update = true
					}
				}
			}
			if update {
				toUpdate = append(toUpdate, metadataRecs[key])
			}
		}
	}

	if len(toUpdate) == 0 {
		logrus.Debug("No DNS records to update")
		return nil
	} else {
		logrus.Infof("DNS records to update: %v", toUpdate)
	}

	return updateRecords(toUpdate, &Update)
}

func removeExtraRecords(metadataRecs map[string]providers.DnsRecord, providerRecs map[string]providers.DnsRecord) error {
	var toRemove []providers.DnsRecord
	for key := range providerRecs {
		if _, ok := metadataRecs[key]; !ok {
			toRemove = append(toRemove, providerRecs[key])
		}
	}

	if len(toRemove) == 0 {
		logrus.Debug("No DNS records to remove")
		return nil
	} else {
		logrus.Infof("DNS records to remove: %v", toRemove)
	}
	return updateRecords(toRemove, &Remove)
}

func getProviderDnsRecords() (map[string]providers.DnsRecord, error) {
	allRecords, err := provider.GetRecords()
	if err != nil {
		return nil, err
	}
	ourRecords := make(map[string]providers.DnsRecord, len(allRecords))
	joins := []string{EnvironmentName, providers.RootDomainName}
	suffix := strings.ToLower(strings.Join(joins, "."))
	for _, value := range allRecords {
		if strings.HasSuffix(value.DomainName, suffix) {
			ourRecords[value.DomainName] = value
		}
	}
	return ourRecords, nil
}

func addToDnsEntries(dnsEntry providers.DnsRecord, dnsEntries map[string]providers.DnsRecord) {
	var records []string
	if _, ok := dnsEntries[dnsEntry.DomainName]; !ok {
		records = dnsEntry.Records
	} else {
		records = dnsEntries[dnsEntry.DomainName].Records
		records = append(records, dnsEntry.Records...)
	}
	dnsEntry = providers.DnsRecord{dnsEntry.DomainName, records, "A", 300}
	dnsEntries[dnsEntry.DomainName] = dnsEntry
}
<|endoftext|>


<commit_before>package gumble

import (
	"code.google.com/p/goprotobuf/proto"
	"github.com/layeh/gopus"
	"github.com/layeh/gumble/gumble/MumbleProto"
)

// User represents a user that is currently connected to the server.
type User struct {
	client  *Client
	decoder *gopus.Decoder

	session, userID                          uint32
	name                                     string
	channel                                  *Channel
	mute, deaf, suppress, selfMute, selfDeaf bool
	comment                                  string
	commentHash                              []byte
	hash                                     string
	texture, textureHash                     []byte
	prioritySpeaker                          bool
	recording                                bool

	statsFetched bool
	stats        UserStats
}

// Session returns the user's session ID.
func (u *User) Session() uint {
	return uint(u.session)
}

// UserID returns the user's ID. Returns an invalid value if the user is not
// registered.
func (u *User) UserID() uint {
	return uint(u.userID)
}

// Name returns the user's name.
func (u *User) Name() string {
	return u.name
}

// Channel returns a pointer to the channel that the user is currently in.
func (u *User) Channel() *Channel {
	return u.channel
}

// IsMuted returns true if the user has been muted.
func (u *User) IsMuted() bool {
	return u.mute
}

// IsDeafened returns true if the user has been deafened.
func (u *User) IsDeafened() bool {
	return u.deaf
}

// IsSuppressed returns true if the user has been suppressed.
func (u *User) IsSuppressed() bool {
	return u.suppress
}

// IsSelfMuted returns true if the user has been muted by him/herself.
func (u *User) IsSelfMuted() bool {
	return u.selfMute
}

// IsSelfDeafened returns true if the user has been deafened by him/herself.
func (u *User) IsSelfDeafened() bool {
	return u.selfDeaf
}

// Comment returns the user's comment.
func (u *User) Comment() string {
	return u.comment
}

// CommentHash returns the user's comment hash. This function can return nil.
func (u *User) CommentHash() []byte {
	return u.commentHash
}

// Hash returns a string representation of the user's certificate hash.
func (u *User) Hash() string {
	return u.hash
}

// Texture returns the user's texture (avatar). This function can return nil.
func (u *User) Texture() []byte {
	return u.texture
}

// SetTexture sets the user's texture.
func (u *User) SetTexture(texture []byte) {
	packet := MumbleProto.UserState{
		Session: &u.session,
		Texture: texture,
	}
	u.client.Send(protoMessage{&packet})
}

// TextureHash returns the user's texture hash. This  can return nil.
func (u *User) TextureHash() []byte {
	return u.textureHash
}

// IsPrioritySpeaker returns true if the user is the priority speaker in the
// channel.
func (u *User) IsPrioritySpeaker() bool {
	return u.prioritySpeaker
}

// SetPrioritySpeaker sets if the user is the priority speaker in the channel.
func (u *User) SetPrioritySpeaker(prioritySpeaker bool) {
	packet := MumbleProto.UserState{
		Session:         &u.session,
		PrioritySpeaker: &prioritySpeaker,
	}
	u.client.Send(protoMessage{&packet})
}

// IsRecording returns true if the user is recording audio.
func (u *User) IsRecording() bool {
	return u.recording
}

// IsRegistered returns true if the user's certificate has been registered with
// the server. A registered user will have a valid user ID.
func (u *User) IsRegistered() bool {
	return u.userID > 0
}

// Register will register the user with the server. If the client has
// permission to do so, the user will shortly be given a UserID.
func (u *User) Register() {
	packet := MumbleProto.UserState{
		Session: &u.session,
		UserId:  proto.Uint32(0),
	}
	u.client.Send(protoMessage{&packet})
}

// SetComment will set the user's comment to the given string. The user's
// comment will be erased if the comment is set to the empty string.
func (u *User) SetComment(comment string) {
	packet := MumbleProto.UserState{
		Session: &u.session,
		Comment: &comment,
	}
	u.client.Send(protoMessage{&packet})
}

// Move will move the user to the given channel.
func (u *User) Move(channel *Channel) {
	packet := MumbleProto.UserState{
		Session:   &u.session,
		ChannelId: &channel.id,
	}
	u.client.Send(protoMessage{&packet})
}

// Kick will kick the user from the server.
func (u *User) Kick(reason string) {
	packet := MumbleProto.UserRemove{
		Session: &u.session,
		Reason:  &reason,
	}
	u.client.Send(protoMessage{&packet})
}

// Ban will ban the user from the server.
func (u *User) Ban(reason string) {
	packet := MumbleProto.UserRemove{
		Session: &u.session,
		Reason:  &reason,
		Ban:     proto.Bool(true),
	}
	u.client.Send(protoMessage{&packet})
}

// SetMuted sets whether the user can transmit audio or not.
func (u *User) SetMuted(muted bool) {
	packet := MumbleProto.UserState{
		Session: &u.session,
		Mute:    proto.Bool(muted),
	}
	u.client.Send(protoMessage{&packet})
}

// SetDeafened sets whether the user can receive audio or not.
func (u *User) SetDeafened(muted bool) {
	packet := MumbleProto.UserState{
		Session: &u.session,
		Deaf:    proto.Bool(muted),
	}
	u.client.Send(protoMessage{&packet})
}

// SetSelfMuted sets whether the user can transmit audio or not.
//
// This method should only be called on Client.Self().
func (u *User) SetSelfMuted(muted bool) {
	packet := MumbleProto.UserState{
		Session:  &u.session,
		SelfMute: proto.Bool(muted),
	}
	u.client.Send(protoMessage{&packet})
}

// SetSelfDeafened sets whether the user can receive audio or not.
//
// This method should only be called on Client.Self().
func (u *User) SetSelfDeafened(muted bool) {
	packet := MumbleProto.UserState{
		Session:  &u.session,
		SelfDeaf: proto.Bool(muted),
	}
	u.client.Send(protoMessage{&packet})
}

// Stats returns the user's stats, and a boolean value specifying if the stats
// are valid or not.
func (u *User) Stats() (UserStats, bool) {
	return u.stats, u.statsFetched
}

// Request requests user information that has not yet been sent to the client.
// The supported request types are: RequestStats, RequestTexture, and
// RequestComment.
func (u *User) Request(request Request) {
	if (request & RequestStats) != 0 {
		packet := MumbleProto.UserStats{
			Session: &u.session,
		}
		u.client.Send(protoMessage{&packet})
	}

	packet := MumbleProto.RequestBlob{}
	if (request & RequestTexture) != 0 {
		packet.SessionTexture = []uint32{u.session}
	}
	if (request & RequestComment) != 0 {
		packet.SessionComment = []uint32{u.session}
	}
	if packet.SessionTexture != nil || packet.SessionComment != nil {
		u.client.Send(protoMessage{&packet})
	}
}

// Send will send a text message to the user.
func (u *User) Send(message string) {
	textMessage := TextMessage{
		Users:   []*User{u},
		Message: message,
	}
	u.client.Send(&textMessage)
}
<commit_msg>add User.SetRecording<commit_after>package gumble

import (
	"code.google.com/p/goprotobuf/proto"
	"github.com/layeh/gopus"
	"github.com/layeh/gumble/gumble/MumbleProto"
)

// User represents a user that is currently connected to the server.
type User struct {
	client  *Client
	decoder *gopus.Decoder

	session, userID                          uint32
	name                                     string
	channel                                  *Channel
	mute, deaf, suppress, selfMute, selfDeaf bool
	comment                                  string
	commentHash                              []byte
	hash                                     string
	texture, textureHash                     []byte
	prioritySpeaker                          bool
	recording                                bool

	statsFetched bool
	stats        UserStats
}

// Session returns the user's session ID.
func (u *User) Session() uint {
	return uint(u.session)
}

// UserID returns the user's ID. Returns an invalid value if the user is not
// registered.
func (u *User) UserID() uint {
	return uint(u.userID)
}

// Name returns the user's name.
func (u *User) Name() string {
	return u.name
}

// Channel returns a pointer to the channel that the user is currently in.
func (u *User) Channel() *Channel {
	return u.channel
}

// IsMuted returns true if the user has been muted.
func (u *User) IsMuted() bool {
	return u.mute
}

// IsDeafened returns true if the user has been deafened.
func (u *User) IsDeafened() bool {
	return u.deaf
}

// IsSuppressed returns true if the user has been suppressed.
func (u *User) IsSuppressed() bool {
	return u.suppress
}

// IsSelfMuted returns true if the user has been muted by him/herself.
func (u *User) IsSelfMuted() bool {
	return u.selfMute
}

// IsSelfDeafened returns true if the user has been deafened by him/herself.
func (u *User) IsSelfDeafened() bool {
	return u.selfDeaf
}

// Comment returns the user's comment.
func (u *User) Comment() string {
	return u.comment
}

// CommentHash returns the user's comment hash. This function can return nil.
func (u *User) CommentHash() []byte {
	return u.commentHash
}

// Hash returns a string representation of the user's certificate hash.
func (u *User) Hash() string {
	return u.hash
}

// Texture returns the user's texture (avatar). This function can return nil.
func (u *User) Texture() []byte {
	return u.texture
}

// SetTexture sets the user's texture.
func (u *User) SetTexture(texture []byte) {
	packet := MumbleProto.UserState{
		Session: &u.session,
		Texture: texture,
	}
	u.client.Send(protoMessage{&packet})
}

// TextureHash returns the user's texture hash. This  can return nil.
func (u *User) TextureHash() []byte {
	return u.textureHash
}

// IsPrioritySpeaker returns true if the user is the priority speaker in the
// channel.
func (u *User) IsPrioritySpeaker() bool {
	return u.prioritySpeaker
}

// SetPrioritySpeaker sets if the user is the priority speaker in the channel.
func (u *User) SetPrioritySpeaker(prioritySpeaker bool) {
	packet := MumbleProto.UserState{
		Session:         &u.session,
		PrioritySpeaker: &prioritySpeaker,
	}
	u.client.Send(protoMessage{&packet})
}

// IsRecording returns true if the user is recording audio.
func (u *User) IsRecording() bool {
	return u.recording
}

// SetRecording sets if the user is recording audio.
func (u *User) SetRecording(recording bool) {
	packet := MumbleProto.UserState{
		Session:   &u.session,
		Recording: &recording,
	}
	u.client.Send(protoMessage{&packet})
}

// IsRegistered returns true if the user's certificate has been registered with
// the server. A registered user will have a valid user ID.
func (u *User) IsRegistered() bool {
	return u.userID > 0
}

// Register will register the user with the server. If the client has
// permission to do so, the user will shortly be given a UserID.
func (u *User) Register() {
	packet := MumbleProto.UserState{
		Session: &u.session,
		UserId:  proto.Uint32(0),
	}
	u.client.Send(protoMessage{&packet})
}

// SetComment will set the user's comment to the given string. The user's
// comment will be erased if the comment is set to the empty string.
func (u *User) SetComment(comment string) {
	packet := MumbleProto.UserState{
		Session: &u.session,
		Comment: &comment,
	}
	u.client.Send(protoMessage{&packet})
}

// Move will move the user to the given channel.
func (u *User) Move(channel *Channel) {
	packet := MumbleProto.UserState{
		Session:   &u.session,
		ChannelId: &channel.id,
	}
	u.client.Send(protoMessage{&packet})
}

// Kick will kick the user from the server.
func (u *User) Kick(reason string) {
	packet := MumbleProto.UserRemove{
		Session: &u.session,
		Reason:  &reason,
	}
	u.client.Send(protoMessage{&packet})
}

// Ban will ban the user from the server.
func (u *User) Ban(reason string) {
	packet := MumbleProto.UserRemove{
		Session: &u.session,
		Reason:  &reason,
		Ban:     proto.Bool(true),
	}
	u.client.Send(protoMessage{&packet})
}

// SetMuted sets whether the user can transmit audio or not.
func (u *User) SetMuted(muted bool) {
	packet := MumbleProto.UserState{
		Session: &u.session,
		Mute:    proto.Bool(muted),
	}
	u.client.Send(protoMessage{&packet})
}

// SetDeafened sets whether the user can receive audio or not.
func (u *User) SetDeafened(muted bool) {
	packet := MumbleProto.UserState{
		Session: &u.session,
		Deaf:    proto.Bool(muted),
	}
	u.client.Send(protoMessage{&packet})
}

// SetSelfMuted sets whether the user can transmit audio or not.
//
// This method should only be called on Client.Self().
func (u *User) SetSelfMuted(muted bool) {
	packet := MumbleProto.UserState{
		Session:  &u.session,
		SelfMute: proto.Bool(muted),
	}
	u.client.Send(protoMessage{&packet})
}

// SetSelfDeafened sets whether the user can receive audio or not.
//
// This method should only be called on Client.Self().
func (u *User) SetSelfDeafened(muted bool) {
	packet := MumbleProto.UserState{
		Session:  &u.session,
		SelfDeaf: proto.Bool(muted),
	}
	u.client.Send(protoMessage{&packet})
}

// Stats returns the user's stats, and a boolean value specifying if the stats
// are valid or not.
func (u *User) Stats() (UserStats, bool) {
	return u.stats, u.statsFetched
}

// Request requests user information that has not yet been sent to the client.
// The supported request types are: RequestStats, RequestTexture, and
// RequestComment.
func (u *User) Request(request Request) {
	if (request & RequestStats) != 0 {
		packet := MumbleProto.UserStats{
			Session: &u.session,
		}
		u.client.Send(protoMessage{&packet})
	}

	packet := MumbleProto.RequestBlob{}
	if (request & RequestTexture) != 0 {
		packet.SessionTexture = []uint32{u.session}
	}
	if (request & RequestComment) != 0 {
		packet.SessionComment = []uint32{u.session}
	}
	if packet.SessionTexture != nil || packet.SessionComment != nil {
		u.client.Send(protoMessage{&packet})
	}
}

// Send will send a text message to the user.
func (u *User) Send(message string) {
	textMessage := TextMessage{
		Users:   []*User{u},
		Message: message,
	}
	u.client.Send(&textMessage)
}
<|endoftext|>


<commit_before>package object

import (
	"github.com/qiniu/go-sdk/v7/storage"
	"github.com/qiniu/qshell/v2/iqshell/common/account"
	"github.com/qiniu/qshell/v2/iqshell/common/alert"
	"github.com/qiniu/qshell/v2/iqshell/common/data"
	"github.com/qiniu/qshell/v2/iqshell/common/workspace"
)

func PreFopStatus(persistentId string) (storage.PrefopRet, *data.CodeError) {
	if len(persistentId) == 0 {
		return storage.PrefopRet{}, alert.CannotEmptyError("persistent id", "")
	}

	mac, err := account.GetMac()
	if err != nil {
		return storage.PrefopRet{}, err
	}

	cfg := workspace.GetStorageConfig()
	opManager := storage.NewOperationManager(mac, cfg)
	ret, e := opManager.Prefop(persistentId)
	return ret, data.ConvertError(e)
}

type PreFopApiInfo struct {
	Bucket      string
	Key         string
	Fops        string
	Pipeline    string
	NotifyURL   string
	NotifyForce bool
}

func PreFop(info PreFopApiInfo) (string, *data.CodeError) {
	if len(info.Bucket) == 0 {
		return "", alert.CannotEmptyError("bucket", "")
	}

	if len(info.Key) == 0 {
		return "", alert.CannotEmptyError("key", "")
	}

	if len(info.Fops) == 0 {
		return "", alert.CannotEmptyError("fops", "")
	}

	mac, err := account.GetMac()
	if err != nil {
		return "", err
	}
	cfg := workspace.GetStorageConfig()
	opManager := storage.NewOperationManager(mac, cfg)
	persistentId, e := opManager.Pfop(info.Bucket, info.Key, info.Fops, info.Pipeline, info.NotifyURL, info.NotifyForce)
	return persistentId, data.ConvertError(e)
}
<commit_msg>optimize fop<commit_after>package object

import (
	"github.com/qiniu/go-sdk/v7/storage"
	"github.com/qiniu/qshell/v2/iqshell/common/account"
	"github.com/qiniu/qshell/v2/iqshell/common/alert"
	"github.com/qiniu/qshell/v2/iqshell/common/data"
	"github.com/qiniu/qshell/v2/iqshell/common/workspace"
)

func PreFopStatus(persistentId string) (storage.PrefopRet, *data.CodeError) {
	if len(persistentId) == 0 {
		return storage.PrefopRet{}, alert.CannotEmptyError("persistent id", "")
	}

	opManager, err := getOperationManager()
	if err != nil {
		return storage.PrefopRet{}, err
	}
	ret, e := opManager.Prefop(persistentId)
	return ret, data.ConvertError(e)
}

type PreFopApiInfo struct {
	Bucket      string
	Key         string
	Fops        string
	Pipeline    string
	NotifyURL   string
	NotifyForce bool
}

func PreFop(info PreFopApiInfo) (string, *data.CodeError) {
	if len(info.Bucket) == 0 {
		return "", alert.CannotEmptyError("bucket", "")
	}

	if len(info.Key) == 0 {
		return "", alert.CannotEmptyError("key", "")
	}

	if len(info.Fops) == 0 {
		return "", alert.CannotEmptyError("fops", "")
	}

	opManager, err := getOperationManager()
	if err != nil {
		return "", err
	}
	persistentId, e := opManager.Pfop(info.Bucket, info.Key, info.Fops, info.Pipeline, info.NotifyURL, info.NotifyForce)
	return persistentId, data.ConvertError(e)
}

func getOperationManager() (*storage.OperationManager, *data.CodeError) {
	mac, err := account.GetMac()
	if err != nil {
		return nil, err
	}
	cfg := workspace.GetStorageConfig()
	opManager := storage.NewOperationManager(mac, cfg)
	return opManager, nil
}
<|endoftext|>