<commit_before>package main

import (
	"encoding/json"
	"errors"
	"github.com/franela/goreq"
	"github.com/gin-gonic/gin"
	"sync/atomic"
	"time"
)

var errTaskNotFresh = errors.New("This task has been running too long to request a token.")
var errAlreadyGivenKey = errors.New("This task has already been given a token.")
var usedTaskIds = NewTtlSet()

func createToken(token string, opts interface{}) (string, error) {
	r, err := goreq.Request{
		Uri:    vaultPath("/v1/auth/token/create-orphan", ""),
		Method: "POST",
		Body:   opts,
	}.WithHeader("X-Vault-Token", token).Do()
	if err == nil {
		defer r.Body.Close()
		switch r.StatusCode {
		case 200:
			var t vaultTokenResp
			if err := r.Body.FromJsonTo(&t); err == nil {
				return t.Auth.ClientToken, nil
			} else {
				return "", err
			}
		default:
			var e vaultError
			e.Code = r.StatusCode
			if err := r.Body.FromJsonTo(&e); err == nil {
				return "", e
			} else {
				e.Errors = []string{"communication error."}
				return "", e
			}
		}
	} else {
		return "", err
	}
}

func createTokenPair(token string, p *policy) (string, error) {
	tempTokenOpts := struct {
		Ttl     string `json:"ttl"`
		NumUses int    `json:"num_uses"`
	}{"10m", 2}
	permTokenOpts := struct {
		Ttl      string            `json:"ttl,omitempty"`
		Policies []string          `json:"policies"`
		Meta     map[string]string `json:"meta,omitempty"`
		NumUses  int               `json:"num_uses"`
	}{time.Duration(time.Duration(p.Ttl) * time.Second).String(), p.Policies, p.Meta, p.NumUses}

	if tempToken, err := createToken(token, tempTokenOpts); err == nil {
		if permToken, err := createToken(token, permTokenOpts); err == nil {
			r, err := goreq.Request{
				Uri:    vaultPath("/v1/cubbyhole/vault-token", ""),
				Method: "POST",
				Body: struct {
					Token string `json:"token"`
				}{permToken},
			}.WithHeader("X-Vault-Token", tempToken).Do()
			if err == nil {
				defer r.Body.Close()
				switch r.StatusCode {
				case 204:
					return tempToken, nil
				default:
					var e vaultError
					e.Code = r.StatusCode
					if err := r.Body.FromJsonTo(&e); err == nil {
						return "", e
					} else {
						e.Errors = []string{"communication error."}
						return "", e
					}
				}
			} else {
				return "", err
			}
		} else {
			return "", err
		}
	} else {
		return "", err
	}
}

func Provide(c *gin.Context) {
	state.RLock()
	status := state.Status
	token := state.Token
	state.RUnlock()

	atomic.AddInt32(&state.Stats.Requests, 1)

	if status == StatusSealed {
		atomic.AddInt32(&state.Stats.Denied, 1)
		c.JSON(503, struct {
			Status string `json:"status"`
			Ok     bool   `json:"ok"`
			Error  string `json:"error"`
		}{string(state.Status), false, "Gatekeeper is sealed."})
		return
	}

	var reqParams struct {
		TaskId string `json:"task_id"`
	}
	decoder := json.NewDecoder(c.Request.Body)
	if err := decoder.Decode(&reqParams); err == nil {
		if usedTaskIds.Has(reqParams.TaskId) {
			atomic.AddInt32(&state.Stats.Denied, 1)
			c.JSON(403, struct {
				Status string `json:"status"`
				Ok     bool   `json:"ok"`
				Error  string `json:"error"`
			}{string(state.Status), false, errAlreadyGivenKey.Error()})
			return
		}
		if task, err := getMesosTask(reqParams.TaskId); err == nil {
			startTime := time.Unix(0, int64(task.Statuses[len(task.Statuses)-1].Timestamp*1000000000))
			if time.Now().Sub(startTime) > config.MaxTaskLife {
				atomic.AddInt32(&state.Stats.Denied, 1)
				c.JSON(403, struct {
					Status string `json:"status"`
					Ok     bool   `json:"ok"`
					Error  string `json:"error"`
				}{string(state.Status), false, errTaskNotFresh.Error()})
				return
			}
			state.RLock()
			policy := activePolicies.Get(task.Name)
			state.RUnlock()
			if tempToken, err := createTokenPair(token, policy); err == nil {
				atomic.AddInt32(&state.Stats.Successful, 1)
				usedTaskIds.Put(reqParams.TaskId, config.MaxTaskLife+1*time.Minute)
				c.JSON(200, struct {
					Status string `json:"status"`
					Ok     bool   `json:"ok"`
					Token  string `json:"token"`
				}{string(state.Status), true, tempToken})
			} else {
				atomic.AddInt32(&state.Stats.Denied, 1)
				c.JSON(500, struct {
					Status string `json:"status"`
					Ok     bool   `json:"ok"`
					Error  string `json:"error"`
				}{string(state.Status), false, err.Error()})
			}
		} else if err == errNoSuchTask {
			atomic.AddInt32(&state.Stats.Denied, 1)
			c.JSON(403, struct {
				Status string `json:"status"`
				Ok     bool   `json:"ok"`
				Error  string `json:"error"`
			}{string(state.Status), false, err.Error()})
		} else {
			atomic.AddInt32(&state.Stats.Denied, 1)
			c.JSON(500, struct {
				Status string `json:"status"`
				Ok     bool   `json:"ok"`
				Error  string `json:"error"`
			}{string(state.Status), false, err.Error()})
		}
	} else {
		atomic.AddInt32(&state.Stats.Denied, 1)
		c.JSON(400, struct {
			Status string `json:"status"`
			Ok     bool   `json:"ok"`
			Error  string `json:"error"`
		}{string(state.Status), false, err.Error()})
	}
}
<commit_msg>If task has no status, deny the request.<commit_after>package main

import (
	"encoding/json"
	"errors"
	"github.com/franela/goreq"
	"github.com/gin-gonic/gin"
	"sync/atomic"
	"time"
)

var errTaskNotFresh = errors.New("This task has been running too long to request a token.")
var errAlreadyGivenKey = errors.New("This task has already been given a token.")
var usedTaskIds = NewTtlSet()

func createToken(token string, opts interface{}) (string, error) {
	r, err := goreq.Request{
		Uri:    vaultPath("/v1/auth/token/create-orphan", ""),
		Method: "POST",
		Body:   opts,
	}.WithHeader("X-Vault-Token", token).Do()
	if err == nil {
		defer r.Body.Close()
		switch r.StatusCode {
		case 200:
			var t vaultTokenResp
			if err := r.Body.FromJsonTo(&t); err == nil {
				return t.Auth.ClientToken, nil
			} else {
				return "", err
			}
		default:
			var e vaultError
			e.Code = r.StatusCode
			if err := r.Body.FromJsonTo(&e); err == nil {
				return "", e
			} else {
				e.Errors = []string{"communication error."}
				return "", e
			}
		}
	} else {
		return "", err
	}
}

func createTokenPair(token string, p *policy) (string, error) {
	tempTokenOpts := struct {
		Ttl     string `json:"ttl"`
		NumUses int    `json:"num_uses"`
	}{"10m", 2}
	permTokenOpts := struct {
		Ttl      string            `json:"ttl,omitempty"`
		Policies []string          `json:"policies"`
		Meta     map[string]string `json:"meta,omitempty"`
		NumUses  int               `json:"num_uses"`
	}{time.Duration(time.Duration(p.Ttl) * time.Second).String(), p.Policies, p.Meta, p.NumUses}

	if tempToken, err := createToken(token, tempTokenOpts); err == nil {
		if permToken, err := createToken(token, permTokenOpts); err == nil {
			r, err := goreq.Request{
				Uri:    vaultPath("/v1/cubbyhole/vault-token", ""),
				Method: "POST",
				Body: struct {
					Token string `json:"token"`
				}{permToken},
			}.WithHeader("X-Vault-Token", tempToken).Do()
			if err == nil {
				defer r.Body.Close()
				switch r.StatusCode {
				case 204:
					return tempToken, nil
				default:
					var e vaultError
					e.Code = r.StatusCode
					if err := r.Body.FromJsonTo(&e); err == nil {
						return "", e
					} else {
						e.Errors = []string{"communication error."}
						return "", e
					}
				}
			} else {
				return "", err
			}
		} else {
			return "", err
		}
	} else {
		return "", err
	}
}

func Provide(c *gin.Context) {
	state.RLock()
	status := state.Status
	token := state.Token
	state.RUnlock()

	atomic.AddInt32(&state.Stats.Requests, 1)

	if status == StatusSealed {
		atomic.AddInt32(&state.Stats.Denied, 1)
		c.JSON(503, struct {
			Status string `json:"status"`
			Ok     bool   `json:"ok"`
			Error  string `json:"error"`
		}{string(state.Status), false, "Gatekeeper is sealed."})
		return
	}

	var reqParams struct {
		TaskId string `json:"task_id"`
	}
	decoder := json.NewDecoder(c.Request.Body)
	if err := decoder.Decode(&reqParams); err == nil {
		if usedTaskIds.Has(reqParams.TaskId) {
			atomic.AddInt32(&state.Stats.Denied, 1)
			c.JSON(403, struct {
				Status string `json:"status"`
				Ok     bool   `json:"ok"`
				Error  string `json:"error"`
			}{string(state.Status), false, errAlreadyGivenKey.Error()})
			return
		}
		if task, err := getMesosTask(reqParams.TaskId); err == nil {
			if len(task.Statuses) == 0 {
				atomic.AddInt32(&state.Stats.Denied, 1)
				c.JSON(403, struct {
					Status string `json:"status"`
					Ok     bool   `json:"ok"`
					Error  string `json:"error"`
				}{string(state.Status), false, errTaskNotFresh.Error()})
				return
			}
			startTime := time.Unix(0, int64(task.Statuses[len(task.Statuses)-1].Timestamp*1000000000))
			if time.Now().Sub(startTime) > config.MaxTaskLife {
				atomic.AddInt32(&state.Stats.Denied, 1)
				c.JSON(403, struct {
					Status string `json:"status"`
					Ok     bool   `json:"ok"`
					Error  string `json:"error"`
				}{string(state.Status), false, errTaskNotFresh.Error()})
				return
			}
			state.RLock()
			policy := activePolicies.Get(task.Name)
			state.RUnlock()
			if tempToken, err := createTokenPair(token, policy); err == nil {
				atomic.AddInt32(&state.Stats.Successful, 1)
				usedTaskIds.Put(reqParams.TaskId, config.MaxTaskLife+1*time.Minute)
				c.JSON(200, struct {
					Status string `json:"status"`
					Ok     bool   `json:"ok"`
					Token  string `json:"token"`
				}{string(state.Status), true, tempToken})
			} else {
				atomic.AddInt32(&state.Stats.Denied, 1)
				c.JSON(500, struct {
					Status string `json:"status"`
					Ok     bool   `json:"ok"`
					Error  string `json:"error"`
				}{string(state.Status), false, err.Error()})
			}
		} else if err == errNoSuchTask {
			atomic.AddInt32(&state.Stats.Denied, 1)
			c.JSON(403, struct {
				Status string `json:"status"`
				Ok     bool   `json:"ok"`
				Error  string `json:"error"`
			}{string(state.Status), false, err.Error()})
		} else {
			atomic.AddInt32(&state.Stats.Denied, 1)
			c.JSON(500, struct {
				Status string `json:"status"`
				Ok     bool   `json:"ok"`
				Error  string `json:"error"`
			}{string(state.Status), false, err.Error()})
		}
	} else {
		atomic.AddInt32(&state.Stats.Denied, 1)
		c.JSON(400, struct {
			Status string `json:"status"`
			Ok     bool   `json:"ok"`
			Error  string `json:"error"`
		}{string(state.Status), false, err.Error()})
	}
}
<|endoftext|>


<commit_before>package GOsu

import (
	"encoding/json"
	"errors"
	"io/ioutil"
	"net/http"
)

// Game Types which can be accessed via GOsu.<GAMETYPE>.
const (
	OSU   = "0"
	TAIKO = "1"
	CTB   = "2"
	MANIA = "3"
)

// Beatmap Identifier Types for use with GetBeatmaps.
const (
	BEATMAPSET = "s"
	BEATMAPID  = "b"
	USERID     = "u"
)

// Osu Mods which are accessed by the GetMod function.
// iota moves the 'binary 1' to the left.
// e.g. NoFail = 1, Easy = 10 in binary which is 2.
// NoVideo = 100 in binary which is 4.
// This is done in order for any combination of mods to be enabled at once.
const (
	None   = 0
	NoFail = 1 << (iota - 1)
	Easy
	NoVideo
	Hidden
	HardRock
	SuddenDeath
	DoubleTime
	Relax
	HalfTime
	Nightcore
	Flashlight
	Autoplay
	SpunOut
	Relax2
	Perfect
	Key4
	Key5
	Key6
	Key7
	Key8
	FadeIn
	Random
	LastMod
)

var (
	API_URL           string = "https://osu.ppy.sh/api/"
	API_RECENT_PLAYS  string = "get_user_recent"
	API_GET_BEATMAPS  string = "get_beatmaps"
	API_GET_USER      string = "get_user"
	API_GET_SCORES    string = "get_scores"
	API_GET_USER_BEST string = "get_user_best"
	API_GET_MATCH     string = "get_match"
)

type Database struct {
	API_KEY string
}

type Beatmap struct {
	Beatmapset_ID     string
	Beatmap_ID        string
	Approved          string
	Approved_Date     string
	Last_Update       string
	Total_Length      string
	Hit_Length        string
	Version           string
	Artist            string
	Title             string
	Creator           string
	Bpm               string
	Source            string
	Difficulty_Rating string
	Diff_Size         string
	Diff_Overall      string
	Diff_Approach     string
	Diff_Drain        string
	Mode              string
}

type Song struct {
	Beatmap_ID   string
	Score        string
	MaxCombo     string
	Count50      string
	Count100     string
	Count300     string
	CountMiss    string
	CountKatu    string
	CountGeki    string
	Perfect      string
	Enabled_Mods string
	User_ID      string
	Date         string
	Rank         string
}

type User struct {
	User_ID       string
	Username      string
	Count300      string
	Count100      string
	Count50       string
	PlayCount     string
	Ranked_Score  string
	Total_Score   string
	PP_Rank       string
	Level         string
	PP_Raw        string
	Accuracy      string
	Count_Rank_SS string
	Count_Rank_S  string
	Count_Rank_A  string
	Country       string
	Events        []Event
}

type Event struct {
	Display_HTML  string
	Beatmap_ID    string
	Beatmapset_ID string
	Date          string
	EpicFactor    string
}

type Score struct {
	Score        string
	Username     string
	MaxCombo     string
	Count50      string
	Count100     string
	Count300     string
	CountMiss    string
	CountKatu    string
	CountGeki    string
	Perfect      string
	Enabled_Mods string
	User_ID      string
	Date         string
	Rank         string
	PP           string
}

type PPSong struct {
	Beatmap_ID   string
	Score        string
	MaxCombo     string
	Count50      string
	Count100     string
	Count300     string
	CountMiss    string
	CountKatu    string
	CountGeki    string
	Perfect      string
	Enabled_Mods string
	User_ID      string
	Date         string
	Rank         string
	PP           string
}

type Game struct {
	Match MPMatch
	Games []MPGame
}

type MPMatch struct {
	Match_ID   string
	Name       string
	Start_Time string
	End_Time   string
}

type MPGame struct {
	Game_ID      string
	Start_Time   string
	End_Time     string
	Beatmap_ID   string
	Play_Mode    string
	Match_Type   string
	Scoring_Type string
	Team_Type    string
	Mods         string
	Scores       []MPScore
}

type MPScore struct {
	Slot      string
	Team      string
	User_ID   string
	Score     string
	MaxCombo  string
	Rank      string
	Count50   string
	Count100  string
	Count300  string
	CountMiss string
	CountGeki string
	CountKatu string
	Perfect   string
	Pass      string
}

func (d *Database) SetAPIKey(API_KEY string) error {
	var err error
	if len(API_KEY) <= 1 {
		err = errors.New("API Key: invalid/non-existant API key.")
		return err
	} else {
		d.API_KEY = API_KEY
	}

	return err
}

func (d Database) BuildRecentURL(USER_ID string, GAME_TYPE string) string {
	return API_URL + API_RECENT_PLAYS + "?k=" + d.API_KEY + "&u=" + USER_ID + "&m=" + GAME_TYPE
}

func (d Database) BuildBeatmapURL(ID string, TYPE string) string {
	return API_URL + API_GET_BEATMAPS + "?k=" + d.API_KEY + "&" + TYPE + "=" + ID
}

func (d Database) BuildUserURL(USER_ID string, GAME_TYPE string, DAYS string) string {
	return API_URL + API_GET_USER + "?k=" + d.API_KEY + "&u=" + USER_ID + "&m=" + GAME_TYPE + "&event_days=" + DAYS
}

func (d Database) BuildUserBestURL(USER_ID string, GAME_TYPE string) string {
	return API_URL + API_GET_USER_BEST + "?k=" + d.API_KEY + "&u=" + USER_ID + "&m=" + GAME_TYPE
}

func (d Database) BuildScoreURL(BEATMAP_ID string, USER_ID string, GAME_TYPE string) string {
	return API_URL + API_GET_SCORES + "?k=" + d.API_KEY + "&b=" + BEATMAP_ID + "&m=" + GAME_TYPE + "&u=" + USER_ID
}

func (d Database) BuildMatchURL(MATCH_ID string) string {
	return API_URL + API_GET_MATCH + "?k=" + d.API_KEY + "&mp=" + MATCH_ID
}

func RetrieveHTML(URL string) ([]byte, error) {
	res, err := http.Get(URL)

	if err != nil {
		return nil, errors.New("HTTP: Could not open a connection to the Osu! API server.")
	}

	defer res.Body.Close()

	html, err := ioutil.ReadAll(res.Body)

	if err != nil {
		return nil, errors.New("HTML: Could not read the HTML page grabbed.")
	}

	return html, err
}

func (d Database) GetUser(USER_ID string, GAME_TYPE string, DAYS string) ([]User, error) {
	var user []User
	url := d.BuildUserURL(USER_ID, GAME_TYPE, DAYS)
	html, err := RetrieveHTML(url)

	if err != nil {
		return user, err
	}

	err = json.Unmarshal(html, &user)

	if err != nil {
		return user, errors.New("JSON: Couldn't process the HTML into JSON. Possible wrong page/wrong API key/offline server.")
	}

	return user, err
}

func (d Database) GetBeatmaps(ID string, BM_TYPE string) ([]Beatmap, error) {
	var beatmaps []Beatmap
	url := d.BuildBeatmapURL(ID, BM_TYPE)
	html, err := RetrieveHTML(url)

	if err != nil {
		return nil, err
	}

	err = json.Unmarshal(html, &beatmaps)

	if err != nil {
		return nil, errors.New("JSON: Couldn't process the HTML into JSON. Possible wrong page/wrong API key/offline server.")
	}

	return beatmaps, err
}

func (d Database) GetRecentPlays(USER_ID string, GAME_TYPE string) ([]Song, error) {
	var songs []Song
	url := d.BuildRecentURL(USER_ID, GAME_TYPE)
	html, err := RetrieveHTML(url)

	if err != nil {
		return nil, err
	}

	err = json.Unmarshal(html, &songs)

	if err != nil {
		return nil, errors.New("JSON: Couldn't process the HTML into JSON. Possible wrong page/wrong API key/offline server.")
	}

	return songs, err
}

func (d Database) GetScores(BEATMAP_ID string, USER_ID string, GAME_TYPE string) ([]Score, error) {
	var scores []Score
	url := d.BuildScoreURL(BEATMAP_ID, USER_ID, GAME_TYPE)
	html, err := RetrieveHTML(url)

	if err != nil {
		return nil, err
	}

	err = json.Unmarshal(html, &scores)

	if err != nil {
		return nil, errors.New("JSON: Couldn't process the HTML into JSON. Possible wrong page/wrong API key/offline server.")
	}

	return scores, err
}

func (d Database) GetUserBest(USER_ID string, GAME_TYPE string) ([]PPSong, error) {
	var songs []PPSong
	url := d.BuildUserBestURL(USER_ID, GAME_TYPE)
	html, err := RetrieveHTML(url)

	if err != nil {
		return nil, err
	}

	err = json.Unmarshal(html, &songs)

	if err != nil {
		return nil, errors.New("JSON: Couldn't process the HTML into JSON. Possible wrong page/wrong API key/offline server.")
	}

	return songs, err
}

func (d Database) GetMatch(MATCH_ID string) (Game, error) {
	var game Game
	url := d.BuildMatchURL(MATCH_ID)
	html, err := RetrieveHTML(url)

	if err != nil {
		return game, err
	}

	err = json.Unmarshal(html, &game)

	if err != nil {
		return game, errors.New("JSON: Couldn't process the HTML into JSON. Possible wrong page/wrong API key/offline server.")
	}

	return game, err
}

// ONLY A TEMPORARY FUNCTION.
// Use this function if you are behind a proxy/corporate network and want to work off a local file.
// It will serve as a local HTML file for you to test the website.
func GetLocalPlays(path string) ([]Song, error) {
	var songs []Song

	html, err := ioutil.ReadFile(path)

	if err != nil {
		return nil, errors.New("HTML: Could not read the local HTML page properly.")
	}

	err = json.Unmarshal(html, &songs)

	if err != nil {
		return nil, errors.New("JSON: Couldn't process the local HTML page, most likely due to not being in the right format.")
	}

	return songs, err
}

func GetMods(mods int) []string {
	var resultantMods []string

	if mods == 0 {
		resultantMods = append(resultantMods, "None")
		return resultantMods
	}

	AddMod(&resultantMods, &mods, LastMod, "Last Mod")
	AddMod(&resultantMods, &mods, Random, "Random")
	AddMod(&resultantMods, &mods, FadeIn, "Fade In")
	AddMod(&resultantMods, &mods, Key8, "Key 8")
	AddMod(&resultantMods, &mods, Key7, "Key 7")
	AddMod(&resultantMods, &mods, Key6, "Key 6")
	AddMod(&resultantMods, &mods, Key5, "Key 5")
	AddMod(&resultantMods, &mods, Key4, "Key 4")
	AddMod(&resultantMods, &mods, Perfect, "Perfect")
	AddMod(&resultantMods, &mods, Relax2, "Relax 2")
	AddMod(&resultantMods, &mods, SpunOut, "Spun Out")
	AddMod(&resultantMods, &mods, Autoplay, "Autoplay")
	AddMod(&resultantMods, &mods, Flashlight, "Flashlight")
	AddMod(&resultantMods, &mods, Nightcore, "Nightcore")
	AddMod(&resultantMods, &mods, HalfTime, "Half Time")
	AddMod(&resultantMods, &mods, Relax, "Relax")
	AddMod(&resultantMods, &mods, DoubleTime, "Double Time")
	AddMod(&resultantMods, &mods, SuddenDeath, "Sudden Death")
	AddMod(&resultantMods, &mods, HardRock, "Hard Rock")
	AddMod(&resultantMods, &mods, Hidden, "Hidden")
	AddMod(&resultantMods, &mods, NoVideo, "No Video")
	AddMod(&resultantMods, &mods, Easy, "Easy")
	AddMod(&resultantMods, &mods, NoFail, "No Fail")

	return resultantMods
}

func AddMod(array *[]string, remainingMods *int, mod int, modName string) {
	if mod <= *remainingMods {
		*remainingMods -= mod
		*array = append(*array, modName)
	}
}
<commit_msg>Change request to grab 50 recent songs, not 10.<commit_after>package GOsu

import (
	"encoding/json"
	"errors"
	"io/ioutil"
	"net/http"
)

// Game Types which can be accessed via GOsu.<GAMETYPE>.
const (
	OSU   = "0"
	TAIKO = "1"
	CTB   = "2"
	MANIA = "3"
)

// Beatmap Identifier Types for use with GetBeatmaps.
const (
	BEATMAPSET = "s"
	BEATMAPID  = "b"
	USERID     = "u"
)

// Osu Mods which are accessed by the GetMod function.
// iota moves the 'binary 1' to the left.
// e.g. NoFail = 1, Easy = 10 in binary which is 2.
// NoVideo = 100 in binary which is 4.
// This is done in order for any combination of mods to be enabled at once.
const (
	None   = 0
	NoFail = 1 << (iota - 1)
	Easy
	NoVideo
	Hidden
	HardRock
	SuddenDeath
	DoubleTime
	Relax
	HalfTime
	Nightcore
	Flashlight
	Autoplay
	SpunOut
	Relax2
	Perfect
	Key4
	Key5
	Key6
	Key7
	Key8
	FadeIn
	Random
	LastMod
)

var (
	API_URL           string = "https://osu.ppy.sh/api/"
	API_RECENT_PLAYS  string = "get_user_recent"
	API_GET_BEATMAPS  string = "get_beatmaps"
	API_GET_USER      string = "get_user"
	API_GET_SCORES    string = "get_scores"
	API_GET_USER_BEST string = "get_user_best"
	API_GET_MATCH     string = "get_match"
)

type Database struct {
	API_KEY string
}

type Beatmap struct {
	Beatmapset_ID     string
	Beatmap_ID        string
	Approved          string
	Approved_Date     string
	Last_Update       string
	Total_Length      string
	Hit_Length        string
	Version           string
	Artist            string
	Title             string
	Creator           string
	Bpm               string
	Source            string
	Difficulty_Rating string
	Diff_Size         string
	Diff_Overall      string
	Diff_Approach     string
	Diff_Drain        string
	Mode              string
}

type Song struct {
	Beatmap_ID   string
	Score        string
	MaxCombo     string
	Count50      string
	Count100     string
	Count300     string
	CountMiss    string
	CountKatu    string
	CountGeki    string
	Perfect      string
	Enabled_Mods string
	User_ID      string
	Date         string
	Rank         string
}

type User struct {
	User_ID       string
	Username      string
	Count300      string
	Count100      string
	Count50       string
	PlayCount     string
	Ranked_Score  string
	Total_Score   string
	PP_Rank       string
	Level         string
	PP_Raw        string
	Accuracy      string
	Count_Rank_SS string
	Count_Rank_S  string
	Count_Rank_A  string
	Country       string
	Events        []Event
}

type Event struct {
	Display_HTML  string
	Beatmap_ID    string
	Beatmapset_ID string
	Date          string
	EpicFactor    string
}

type Score struct {
	Score        string
	Username     string
	MaxCombo     string
	Count50      string
	Count100     string
	Count300     string
	CountMiss    string
	CountKatu    string
	CountGeki    string
	Perfect      string
	Enabled_Mods string
	User_ID      string
	Date         string
	Rank         string
	PP           string
}

type PPSong struct {
	Beatmap_ID   string
	Score        string
	MaxCombo     string
	Count50      string
	Count100     string
	Count300     string
	CountMiss    string
	CountKatu    string
	CountGeki    string
	Perfect      string
	Enabled_Mods string
	User_ID      string
	Date         string
	Rank         string
	PP           string
}

type Game struct {
	Match MPMatch
	Games []MPGame
}

type MPMatch struct {
	Match_ID   string
	Name       string
	Start_Time string
	End_Time   string
}

type MPGame struct {
	Game_ID      string
	Start_Time   string
	End_Time     string
	Beatmap_ID   string
	Play_Mode    string
	Match_Type   string
	Scoring_Type string
	Team_Type    string
	Mods         string
	Scores       []MPScore
}

type MPScore struct {
	Slot      string
	Team      string
	User_ID   string
	Score     string
	MaxCombo  string
	Rank      string
	Count50   string
	Count100  string
	Count300  string
	CountMiss string
	CountGeki string
	CountKatu string
	Perfect   string
	Pass      string
}

func (d *Database) SetAPIKey(API_KEY string) error {
	var err error
	if len(API_KEY) <= 1 {
		err = errors.New("API Key: invalid/non-existant API key.")
		return err
	} else {
		d.API_KEY = API_KEY
	}

	return err
}

func (d Database) BuildRecentURL(USER_ID string, GAME_TYPE string) string {
	return API_URL + API_RECENT_PLAYS + "?k=" + d.API_KEY + "&u=" + USER_ID + "&m=" + GAME_TYPE + "&limit=50"
}

func (d Database) BuildBeatmapURL(ID string, TYPE string) string {
	return API_URL + API_GET_BEATMAPS + "?k=" + d.API_KEY + "&" + TYPE + "=" + ID
}

func (d Database) BuildUserURL(USER_ID string, GAME_TYPE string, DAYS string) string {
	return API_URL + API_GET_USER + "?k=" + d.API_KEY + "&u=" + USER_ID + "&m=" + GAME_TYPE + "&event_days=" + DAYS
}

func (d Database) BuildUserBestURL(USER_ID string, GAME_TYPE string) string {
	return API_URL + API_GET_USER_BEST + "?k=" + d.API_KEY + "&u=" + USER_ID + "&m=" + GAME_TYPE
}

func (d Database) BuildScoreURL(BEATMAP_ID string, USER_ID string, GAME_TYPE string) string {
	return API_URL + API_GET_SCORES + "?k=" + d.API_KEY + "&b=" + BEATMAP_ID + "&m=" + GAME_TYPE + "&u=" + USER_ID
}

func (d Database) BuildMatchURL(MATCH_ID string) string {
	return API_URL + API_GET_MATCH + "?k=" + d.API_KEY + "&mp=" + MATCH_ID
}

func RetrieveHTML(URL string) ([]byte, error) {
	res, err := http.Get(URL)

	if err != nil {
		return nil, errors.New("HTTP: Could not open a connection to the Osu! API server.")
	}

	defer res.Body.Close()

	html, err := ioutil.ReadAll(res.Body)

	if err != nil {
		return nil, errors.New("HTML: Could not read the HTML page grabbed.")
	}

	return html, err
}

func (d Database) GetUser(USER_ID string, GAME_TYPE string, DAYS string) ([]User, error) {
	var user []User
	url := d.BuildUserURL(USER_ID, GAME_TYPE, DAYS)
	html, err := RetrieveHTML(url)

	if err != nil {
		return user, err
	}

	err = json.Unmarshal(html, &user)

	if err != nil {
		return user, errors.New("JSON: Couldn't process the HTML into JSON. Possible wrong page/wrong API key/offline server.")
	}

	return user, err
}

func (d Database) GetBeatmaps(ID string, BM_TYPE string) ([]Beatmap, error) {
	var beatmaps []Beatmap
	url := d.BuildBeatmapURL(ID, BM_TYPE)
	html, err := RetrieveHTML(url)

	if err != nil {
		return nil, err
	}

	err = json.Unmarshal(html, &beatmaps)

	if err != nil {
		return nil, errors.New("JSON: Couldn't process the HTML into JSON. Possible wrong page/wrong API key/offline server.")
	}

	return beatmaps, err
}

func (d Database) GetRecentPlays(USER_ID string, GAME_TYPE string) ([]Song, error) {
	var songs []Song
	url := d.BuildRecentURL(USER_ID, GAME_TYPE)
	html, err := RetrieveHTML(url)

	if err != nil {
		return nil, err
	}

	err = json.Unmarshal(html, &songs)

	if err != nil {
		return nil, errors.New("JSON: Couldn't process the HTML into JSON. Possible wrong page/wrong API key/offline server.")
	}

	return songs, err
}

func (d Database) GetScores(BEATMAP_ID string, USER_ID string, GAME_TYPE string) ([]Score, error) {
	var scores []Score
	url := d.BuildScoreURL(BEATMAP_ID, USER_ID, GAME_TYPE)
	html, err := RetrieveHTML(url)

	if err != nil {
		return nil, err
	}

	err = json.Unmarshal(html, &scores)

	if err != nil {
		return nil, errors.New("JSON: Couldn't process the HTML into JSON. Possible wrong page/wrong API key/offline server.")
	}

	return scores, err
}

func (d Database) GetUserBest(USER_ID string, GAME_TYPE string) ([]PPSong, error) {
	var songs []PPSong
	url := d.BuildUserBestURL(USER_ID, GAME_TYPE)
	html, err := RetrieveHTML(url)

	if err != nil {
		return nil, err
	}

	err = json.Unmarshal(html, &songs)

	if err != nil {
		return nil, errors.New("JSON: Couldn't process the HTML into JSON. Possible wrong page/wrong API key/offline server.")
	}

	return songs, err
}

func (d Database) GetMatch(MATCH_ID string) (Game, error) {
	var game Game
	url := d.BuildMatchURL(MATCH_ID)
	html, err := RetrieveHTML(url)

	if err != nil {
		return game, err
	}

	err = json.Unmarshal(html, &game)

	if err != nil {
		return game, errors.New("JSON: Couldn't process the HTML into JSON. Possible wrong page/wrong API key/offline server.")
	}

	return game, err
}

// ONLY A TEMPORARY FUNCTION.
// Use this function if you are behind a proxy/corporate network and want to work off a local file.
// It will serve as a local HTML file for you to test the website.
func GetLocalPlays(path string) ([]Song, error) {
	var songs []Song

	html, err := ioutil.ReadFile(path)

	if err != nil {
		return nil, errors.New("HTML: Could not read the local HTML page properly.")
	}

	err = json.Unmarshal(html, &songs)

	if err != nil {
		return nil, errors.New("JSON: Couldn't process the local HTML page, most likely due to not being in the right format.")
	}

	return songs, err
}

func GetMods(mods int) []string {
	var resultantMods []string

	if mods == 0 {
		resultantMods = append(resultantMods, "None")
		return resultantMods
	}

	AddMod(&resultantMods, &mods, LastMod, "Last Mod")
	AddMod(&resultantMods, &mods, Random, "Random")
	AddMod(&resultantMods, &mods, FadeIn, "Fade In")
	AddMod(&resultantMods, &mods, Key8, "Key 8")
	AddMod(&resultantMods, &mods, Key7, "Key 7")
	AddMod(&resultantMods, &mods, Key6, "Key 6")
	AddMod(&resultantMods, &mods, Key5, "Key 5")
	AddMod(&resultantMods, &mods, Key4, "Key 4")
	AddMod(&resultantMods, &mods, Perfect, "Perfect")
	AddMod(&resultantMods, &mods, Relax2, "Relax 2")
	AddMod(&resultantMods, &mods, SpunOut, "Spun Out")
	AddMod(&resultantMods, &mods, Autoplay, "Autoplay")
	AddMod(&resultantMods, &mods, Flashlight, "Flashlight")
	AddMod(&resultantMods, &mods, Nightcore, "Nightcore")
	AddMod(&resultantMods, &mods, HalfTime, "Half Time")
	AddMod(&resultantMods, &mods, Relax, "Relax")
	AddMod(&resultantMods, &mods, DoubleTime, "Double Time")
	AddMod(&resultantMods, &mods, SuddenDeath, "Sudden Death")
	AddMod(&resultantMods, &mods, HardRock, "Hard Rock")
	AddMod(&resultantMods, &mods, Hidden, "Hidden")
	AddMod(&resultantMods, &mods, NoVideo, "No Video")
	AddMod(&resultantMods, &mods, Easy, "Easy")
	AddMod(&resultantMods, &mods, NoFail, "No Fail")

	return resultantMods
}

func AddMod(array *[]string, remainingMods *int, mod int, modName string) {
	if mod <= *remainingMods {
		*remainingMods -= mod
		*array = append(*array, modName)
	}
}
<|endoftext|>


<commit_before>package sso

import (
	"github.com/skygeario/skygear-server/pkg/auth/dependency/hook"
	"github.com/skygeario/skygear-server/pkg/auth/dependency/principal"
	"github.com/skygeario/skygear-server/pkg/auth/dependency/principal/oauth"
	"github.com/skygeario/skygear-server/pkg/auth/dependency/principal/password"
	"github.com/skygeario/skygear-server/pkg/auth/dependency/sso"
	"github.com/skygeario/skygear-server/pkg/auth/dependency/userprofile"
	"github.com/skygeario/skygear-server/pkg/auth/event"
	signUpHandler "github.com/skygeario/skygear-server/pkg/auth/handler"
	"github.com/skygeario/skygear-server/pkg/auth/model"
	"github.com/skygeario/skygear-server/pkg/auth/task"
	"github.com/skygeario/skygear-server/pkg/core/async"
	"github.com/skygeario/skygear-server/pkg/core/auth/authinfo"
	"github.com/skygeario/skygear-server/pkg/core/auth/authtoken"
	"github.com/skygeario/skygear-server/pkg/core/skydb"
	"github.com/skygeario/skygear-server/pkg/core/skyerr"
)

// nolint: deadcode
/*
	@ID SSOProviderID
	@Parameter provider_id path
		ID of SSO provider
		@JSONSchema
			{ "type": "string" }
*/
type ssoProviderParameter string

type respHandler struct {
	TokenStore          authtoken.Store
	AuthInfoStore       authinfo.Store
	OAuthAuthProvider   oauth.Provider
	IdentityProvider    principal.IdentityProvider
	UserProfileStore    userprofile.Store
	HookProvider        hook.Provider
	TaskQueue           async.Queue
	WelcomeEmailEnabled bool
}

func (h respHandler) loginActionResp(oauthAuthInfo sso.AuthInfo, loginState sso.LoginState) (resp interface{}, err error) {
	// action => login
	var info authinfo.AuthInfo
	createNewUser, principal, err := h.handleLogin(oauthAuthInfo, &info, loginState)
	if err != nil {
		return
	}

	// Create empty user profile or get the existing one
	var userProfile userprofile.UserProfile
	emptyProfile := map[string]interface{}{}
	if createNewUser {
		userProfile, err = h.UserProfileStore.CreateUserProfile(info.ID, emptyProfile)
	} else {
		userProfile, err = h.UserProfileStore.GetUserProfile(info.ID)
	}
	if err != nil {
		// TODO:
		// return proper error
		err = skyerr.NewError(skyerr.UnexpectedError, "Unable to save user profile")
		return
	}

	user := model.NewUser(info, userProfile)
	identity := model.NewIdentity(h.IdentityProvider, principal)

	if createNewUser {
		err = h.HookProvider.DispatchEvent(
			event.UserCreateEvent{
				User:       user,
				Identities: []model.Identity{identity},
			},
			&user,
		)
		if err != nil {
			return
		}
	}

	// Create auth token
	var token authtoken.Token
	token, err = h.TokenStore.NewToken(info.ID, principal.ID)
	if err != nil {
		panic(err)
	}
	if err = h.TokenStore.Put(&token); err != nil {
		panic(err)
	}

	var sessionCreateReason event.SessionCreateReason
	if createNewUser {
		sessionCreateReason = event.SessionCreateReasonSignup
	} else {
		sessionCreateReason = event.SessionCreateReasonLogin
	}
	err = h.HookProvider.DispatchEvent(
		event.SessionCreateEvent{
			Reason:   sessionCreateReason,
			User:     user,
			Identity: identity,
		},
		&user,
	)
	if err != nil {
		return
	}

	// Reload auth info, in case before hook handler mutated it
	if err = h.AuthInfoStore.GetAuth(principal.UserID, &info); err != nil {
		return
	}

	// Update the activity time of user (return old activity time for usefulness)
	now := timeNow()
	info.LastLoginAt = &now
	info.LastSeenAt = &now
	if err = h.AuthInfoStore.UpdateAuth(&info); err != nil {
		err = skyerr.MakeError(err)
		return
	}

	resp = model.NewAuthResponse(user, identity, token.AccessToken)

	if createNewUser &&
		h.WelcomeEmailEnabled &&
		oauthAuthInfo.ProviderUserInfo.Email != "" &&
		h.TaskQueue != nil {
		h.TaskQueue.Enqueue(task.WelcomeEmailSendTaskName, task.WelcomeEmailSendTaskParam{
			Email: oauthAuthInfo.ProviderUserInfo.Email,
			User:  user,
		}, nil)
	}

	return
}

func (h respHandler) linkActionResp(oauthAuthInfo sso.AuthInfo, linkState sso.LinkState) (resp interface{}, err error) {
	// action => link
	// We only need to check if we can find such principal.
	// If such principal exists, it does not matter whether the principal
	// is associated with the user.
	// We do not allow the same provider user to be associated with an user
	// more than once.
	_, err = h.OAuthAuthProvider.GetPrincipalByProvider(oauth.GetByProviderOptions{
		ProviderType:   string(oauthAuthInfo.ProviderConfig.Type),
		ProviderKeys:   oauth.ProviderKeysFromProviderConfig(oauthAuthInfo.ProviderConfig),
		ProviderUserID: oauthAuthInfo.ProviderUserInfo.ID,
	})
	if err == nil {
		err = skyerr.NewError(skyerr.InvalidArgument, "the provider user is already linked")
		return resp, err
	}

	if err != skydb.ErrUserNotFound {
		// some other error
		return resp, err
	}

	var info authinfo.AuthInfo
	if err = h.AuthInfoStore.GetAuth(linkState.UserID, &info); err != nil {
		err = skyerr.NewError(skyerr.ResourceNotFound, "user not found")
		return resp, err
	}

	var principal *oauth.Principal
	principal, err = h.createPrincipalByOAuthInfo(info.ID, oauthAuthInfo)
	if err != nil {
		return resp, err
	}

	var userProfile userprofile.UserProfile
	userProfile, err = h.UserProfileStore.GetUserProfile(info.ID)
	if err != nil {
		return
	}

	user := model.NewUser(info, userProfile)
	identity := model.NewIdentity(h.IdentityProvider, principal)
	err = h.HookProvider.DispatchEvent(
		event.IdentityCreateEvent{
			User:     user,
			Identity: identity,
		},
		&user,
	)
	if err != nil {
		return
	}

	resp = map[string]string{}
	return
}

func (h respHandler) handleLogin(
	oauthAuthInfo sso.AuthInfo,
	info *authinfo.AuthInfo,
	loginState sso.LoginState,
) (createNewUser bool, oauthPrincipal *oauth.Principal, err error) {
	oauthPrincipal, err = h.findExistingOAuthPrincipal(oauthAuthInfo)
	if err != nil {
		return
	}

	now := timeNow()

	// Two func that closes over the arguments and the return value
	// and need to be reused.

	// populateInfo sets the argument info to non-nil value
	populateInfo := func(userID string) {
		if e := h.AuthInfoStore.GetAuth(userID, info); e != nil {
			if e == skydb.ErrUserNotFound {
				err = skyerr.NewError(skyerr.ResourceNotFound, "User not found")
				return
			}
			err = skyerr.MakeError(e)
			return
		}
	}

	// createFunc creates a new user.
	createFunc := func() {
		createNewUser = true
		// if there is no existed user
		// signup a new user
		*info = authinfo.NewAuthInfo()
		info.LastLoginAt = &now

		// Create AuthInfo
		if e := h.AuthInfoStore.CreateAuth(info); e != nil {
			if e == skydb.ErrUserDuplicated {
				err = signUpHandler.ErrUserDuplicated
				return
			}
			// TODO:
			// return proper error
			err = skyerr.NewError(skyerr.UnexpectedError, "Unable to save auth info")
			return
		}

		oauthPrincipal, err = h.createPrincipalByOAuthInfo(info.ID, oauthAuthInfo)
		if err != nil {
			return
		}
	}

	// Case: OAuth principal was found
	// => Simple update case
	// We do not need to consider other principals
	if oauthPrincipal != nil {
		oauthPrincipal.AccessTokenResp = oauthAuthInfo.ProviderAccessTokenResp
		oauthPrincipal.SetRawProfile(oauthAuthInfo.ProviderRawProfile)
		oauthPrincipal.UpdatedAt = &now
		if err = h.OAuthAuthProvider.UpdatePrincipal(oauthPrincipal); err != nil {
			err = skyerr.MakeError(err)
			return
		}
		populateInfo(oauthPrincipal.UserID)
		// Always return here because we are done with this case.
		return
	}

	// Case: OAuth principal was not found
	// We need to consider all principals
	principals, err := h.findExistingPrincipals(oauthAuthInfo, loginState.MergeRealm)
	if err != nil {
		return
	}
	userIDs := h.principalsToUserIDs(principals)

	// Case: OAuth principal was not found and no other principals were not found
	// => Simple create case
	if len(userIDs) <= 0 {
		createFunc()
		return
	}

	// Case: OAuth principal was not found and some principals were found
	// => Complex case
	switch loginState.OnUserDuplicate {
	case model.OnUserDuplicateAbort:
		err = skyerr.NewError(skyerr.Duplicated, "Aborted due to duplicate user")
	case model.OnUserDuplicateCreate:
		createFunc()
	case model.OnUserDuplicateMerge:
		// Case: The same email is shared by multiple users
		if len(userIDs) > 1 {
			err = skyerr.NewError(skyerr.Duplicated, "Email shared by multiple users")
			return
		}
		// Associate the provider to the existing user
		userID := userIDs[0]
		oauthPrincipal, err = h.createPrincipalByOAuthInfo(
			userID,
			oauthAuthInfo,
		)
		if err != nil {
			return
		}
		populateInfo(userID)
	}

	return
}

func (h respHandler) findExistingOAuthPrincipal(oauthAuthInfo sso.AuthInfo) (*oauth.Principal, error) {
	// Find oauth principal from by (provider_id, provider_user_id)
	principal, err := h.OAuthAuthProvider.GetPrincipalByProvider(oauth.GetByProviderOptions{
		ProviderType:   string(oauthAuthInfo.ProviderConfig.Type),
		ProviderKeys:   oauth.ProviderKeysFromProviderConfig(oauthAuthInfo.ProviderConfig),
		ProviderUserID: oauthAuthInfo.ProviderUserInfo.ID,
	})
	if err == skydb.ErrUserNotFound {
		return nil, nil
	}
	if err != nil {
		return nil, err
	}
	return principal, nil
}

func (h respHandler) findExistingPrincipals(oauthAuthInfo sso.AuthInfo, mergeRealm string) ([]principal.Principal, error) {
	email := oauthAuthInfo.ProviderUserInfo.Email
	if email == "" {
		return nil, nil
	}
	principals, err := h.IdentityProvider.ListPrincipalsByClaim("email", oauthAuthInfo.ProviderUserInfo.Email)
	if err != nil {
		return nil, err
	}
	var filteredPrincipals []principal.Principal
	for _, p := range principals {
		if passwordPrincipal, ok := p.(*password.Principal); ok {
			if passwordPrincipal.Realm == mergeRealm {
				filteredPrincipals = append(filteredPrincipals, p)
			}
		} else {
			filteredPrincipals = append(filteredPrincipals, p)
		}
	}
	return filteredPrincipals, nil
}

func (h respHandler) principalsToUserIDs(principals []principal.Principal) []string {
	seen := map[string]struct{}{}
	var userIDs []string
	for _, p := range principals {
		userID := p.PrincipalUserID()
		_, ok := seen[userID]
		if !ok {
			seen[userID] = struct{}{}
			userIDs = append(userIDs, userID)
		}
	}
	return userIDs
}

func (h respHandler) createPrincipalByOAuthInfo(userID string, oauthAuthInfo sso.AuthInfo) (*oauth.Principal, error) {
	now := timeNow()
	providerKeys := oauth.ProviderKeysFromProviderConfig(oauthAuthInfo.ProviderConfig)
	principal := oauth.NewPrincipal(providerKeys)
	principal.UserID = userID
	principal.ProviderType = string(oauthAuthInfo.ProviderConfig.Type)
	principal.ProviderUserID = oauthAuthInfo.ProviderUserInfo.ID
	principal.AccessTokenResp = oauthAuthInfo.ProviderAccessTokenResp
	principal.SetRawProfile(oauthAuthInfo.ProviderRawProfile)
	principal.CreatedAt = &now
	principal.UpdatedAt = &now
	err := h.OAuthAuthProvider.CreatePrincipal(principal)
	return principal, err
}
<commit_msg>Simplify loop body<commit_after>package sso

import (
	"github.com/skygeario/skygear-server/pkg/auth/dependency/hook"
	"github.com/skygeario/skygear-server/pkg/auth/dependency/principal"
	"github.com/skygeario/skygear-server/pkg/auth/dependency/principal/oauth"
	"github.com/skygeario/skygear-server/pkg/auth/dependency/principal/password"
	"github.com/skygeario/skygear-server/pkg/auth/dependency/sso"
	"github.com/skygeario/skygear-server/pkg/auth/dependency/userprofile"
	"github.com/skygeario/skygear-server/pkg/auth/event"
	signUpHandler "github.com/skygeario/skygear-server/pkg/auth/handler"
	"github.com/skygeario/skygear-server/pkg/auth/model"
	"github.com/skygeario/skygear-server/pkg/auth/task"
	"github.com/skygeario/skygear-server/pkg/core/async"
	"github.com/skygeario/skygear-server/pkg/core/auth/authinfo"
	"github.com/skygeario/skygear-server/pkg/core/auth/authtoken"
	"github.com/skygeario/skygear-server/pkg/core/skydb"
	"github.com/skygeario/skygear-server/pkg/core/skyerr"
)

// nolint: deadcode
/*
	@ID SSOProviderID
	@Parameter provider_id path
		ID of SSO provider
		@JSONSchema
			{ "type": "string" }
*/
type ssoProviderParameter string

type respHandler struct {
	TokenStore          authtoken.Store
	AuthInfoStore       authinfo.Store
	OAuthAuthProvider   oauth.Provider
	IdentityProvider    principal.IdentityProvider
	UserProfileStore    userprofile.Store
	HookProvider        hook.Provider
	TaskQueue           async.Queue
	WelcomeEmailEnabled bool
}

func (h respHandler) loginActionResp(oauthAuthInfo sso.AuthInfo, loginState sso.LoginState) (resp interface{}, err error) {
	// action => login
	var info authinfo.AuthInfo
	createNewUser, principal, err := h.handleLogin(oauthAuthInfo, &info, loginState)
	if err != nil {
		return
	}

	// Create empty user profile or get the existing one
	var userProfile userprofile.UserProfile
	emptyProfile := map[string]interface{}{}
	if createNewUser {
		userProfile, err = h.UserProfileStore.CreateUserProfile(info.ID, emptyProfile)
	} else {
		userProfile, err = h.UserProfileStore.GetUserProfile(info.ID)
	}
	if err != nil {
		// TODO:
		// return proper error
		err = skyerr.NewError(skyerr.UnexpectedError, "Unable to save user profile")
		return
	}

	user := model.NewUser(info, userProfile)
	identity := model.NewIdentity(h.IdentityProvider, principal)

	if createNewUser {
		err = h.HookProvider.DispatchEvent(
			event.UserCreateEvent{
				User:       user,
				Identities: []model.Identity{identity},
			},
			&user,
		)
		if err != nil {
			return
		}
	}

	// Create auth token
	var token authtoken.Token
	token, err = h.TokenStore.NewToken(info.ID, principal.ID)
	if err != nil {
		panic(err)
	}
	if err = h.TokenStore.Put(&token); err != nil {
		panic(err)
	}

	var sessionCreateReason event.SessionCreateReason
	if createNewUser {
		sessionCreateReason = event.SessionCreateReasonSignup
	} else {
		sessionCreateReason = event.SessionCreateReasonLogin
	}
	err = h.HookProvider.DispatchEvent(
		event.SessionCreateEvent{
			Reason:   sessionCreateReason,
			User:     user,
			Identity: identity,
		},
		&user,
	)
	if err != nil {
		return
	}

	// Reload auth info, in case before hook handler mutated it
	if err = h.AuthInfoStore.GetAuth(principal.UserID, &info); err != nil {
		return
	}

	// Update the activity time of user (return old activity time for usefulness)
	now := timeNow()
	info.LastLoginAt = &now
	info.LastSeenAt = &now
	if err = h.AuthInfoStore.UpdateAuth(&info); err != nil {
		err = skyerr.MakeError(err)
		return
	}

	resp = model.NewAuthResponse(user, identity, token.AccessToken)

	if createNewUser &&
		h.WelcomeEmailEnabled &&
		oauthAuthInfo.ProviderUserInfo.Email != "" &&
		h.TaskQueue != nil {
		h.TaskQueue.Enqueue(task.WelcomeEmailSendTaskName, task.WelcomeEmailSendTaskParam{
			Email: oauthAuthInfo.ProviderUserInfo.Email,
			User:  user,
		}, nil)
	}

	return
}

func (h respHandler) linkActionResp(oauthAuthInfo sso.AuthInfo, linkState sso.LinkState) (resp interface{}, err error) {
	// action => link
	// We only need to check if we can find such principal.
	// If such principal exists, it does not matter whether the principal
	// is associated with the user.
	// We do not allow the same provider user to be associated with an user
	// more than once.
	_, err = h.OAuthAuthProvider.GetPrincipalByProvider(oauth.GetByProviderOptions{
		ProviderType:   string(oauthAuthInfo.ProviderConfig.Type),
		ProviderKeys:   oauth.ProviderKeysFromProviderConfig(oauthAuthInfo.ProviderConfig),
		ProviderUserID: oauthAuthInfo.ProviderUserInfo.ID,
	})
	if err == nil {
		err = skyerr.NewError(skyerr.InvalidArgument, "the provider user is already linked")
		return resp, err
	}

	if err != skydb.ErrUserNotFound {
		// some other error
		return resp, err
	}

	var info authinfo.AuthInfo
	if err = h.AuthInfoStore.GetAuth(linkState.UserID, &info); err != nil {
		err = skyerr.NewError(skyerr.ResourceNotFound, "user not found")
		return resp, err
	}

	var principal *oauth.Principal
	principal, err = h.createPrincipalByOAuthInfo(info.ID, oauthAuthInfo)
	if err != nil {
		return resp, err
	}

	var userProfile userprofile.UserProfile
	userProfile, err = h.UserProfileStore.GetUserProfile(info.ID)
	if err != nil {
		return
	}

	user := model.NewUser(info, userProfile)
	identity := model.NewIdentity(h.IdentityProvider, principal)
	err = h.HookProvider.DispatchEvent(
		event.IdentityCreateEvent{
			User:     user,
			Identity: identity,
		},
		&user,
	)
	if err != nil {
		return
	}

	resp = map[string]string{}
	return
}

func (h respHandler) handleLogin(
	oauthAuthInfo sso.AuthInfo,
	info *authinfo.AuthInfo,
	loginState sso.LoginState,
) (createNewUser bool, oauthPrincipal *oauth.Principal, err error) {
	oauthPrincipal, err = h.findExistingOAuthPrincipal(oauthAuthInfo)
	if err != nil {
		return
	}

	now := timeNow()

	// Two func that closes over the arguments and the return value
	// and need to be reused.

	// populateInfo sets the argument info to non-nil value
	populateInfo := func(userID string) {
		if e := h.AuthInfoStore.GetAuth(userID, info); e != nil {
			if e == skydb.ErrUserNotFound {
				err = skyerr.NewError(skyerr.ResourceNotFound, "User not found")
				return
			}
			err = skyerr.MakeError(e)
			return
		}
	}

	// createFunc creates a new user.
	createFunc := func() {
		createNewUser = true
		// if there is no existed user
		// signup a new user
		*info = authinfo.NewAuthInfo()
		info.LastLoginAt = &now

		// Create AuthInfo
		if e := h.AuthInfoStore.CreateAuth(info); e != nil {
			if e == skydb.ErrUserDuplicated {
				err = signUpHandler.ErrUserDuplicated
				return
			}
			// TODO:
			// return proper error
			err = skyerr.NewError(skyerr.UnexpectedError, "Unable to save auth info")
			return
		}

		oauthPrincipal, err = h.createPrincipalByOAuthInfo(info.ID, oauthAuthInfo)
		if err != nil {
			return
		}
	}

	// Case: OAuth principal was found
	// => Simple update case
	// We do not need to consider other principals
	if oauthPrincipal != nil {
		oauthPrincipal.AccessTokenResp = oauthAuthInfo.ProviderAccessTokenResp
		oauthPrincipal.SetRawProfile(oauthAuthInfo.ProviderRawProfile)
		oauthPrincipal.UpdatedAt = &now
		if err = h.OAuthAuthProvider.UpdatePrincipal(oauthPrincipal); err != nil {
			err = skyerr.MakeError(err)
			return
		}
		populateInfo(oauthPrincipal.UserID)
		// Always return here because we are done with this case.
		return
	}

	// Case: OAuth principal was not found
	// We need to consider all principals
	principals, err := h.findExistingPrincipals(oauthAuthInfo, loginState.MergeRealm)
	if err != nil {
		return
	}
	userIDs := h.principalsToUserIDs(principals)

	// Case: OAuth principal was not found and no other principals were not found
	// => Simple create case
	if len(userIDs) <= 0 {
		createFunc()
		return
	}

	// Case: OAuth principal was not found and some principals were found
	// => Complex case
	switch loginState.OnUserDuplicate {
	case model.OnUserDuplicateAbort:
		err = skyerr.NewError(skyerr.Duplicated, "Aborted due to duplicate user")
	case model.OnUserDuplicateCreate:
		createFunc()
	case model.OnUserDuplicateMerge:
		// Case: The same email is shared by multiple users
		if len(userIDs) > 1 {
			err = skyerr.NewError(skyerr.Duplicated, "Email shared by multiple users")
			return
		}
		// Associate the provider to the existing user
		userID := userIDs[0]
		oauthPrincipal, err = h.createPrincipalByOAuthInfo(
			userID,
			oauthAuthInfo,
		)
		if err != nil {
			return
		}
		populateInfo(userID)
	}

	return
}

func (h respHandler) findExistingOAuthPrincipal(oauthAuthInfo sso.AuthInfo) (*oauth.Principal, error) {
	// Find oauth principal from by (provider_id, provider_user_id)
	principal, err := h.OAuthAuthProvider.GetPrincipalByProvider(oauth.GetByProviderOptions{
		ProviderType:   string(oauthAuthInfo.ProviderConfig.Type),
		ProviderKeys:   oauth.ProviderKeysFromProviderConfig(oauthAuthInfo.ProviderConfig),
		ProviderUserID: oauthAuthInfo.ProviderUserInfo.ID,
	})
	if err == skydb.ErrUserNotFound {
		return nil, nil
	}
	if err != nil {
		return nil, err
	}
	return principal, nil
}

func (h respHandler) findExistingPrincipals(oauthAuthInfo sso.AuthInfo, mergeRealm string) ([]principal.Principal, error) {
	email := oauthAuthInfo.ProviderUserInfo.Email
	if email == "" {
		return nil, nil
	}
	principals, err := h.IdentityProvider.ListPrincipalsByClaim("email", oauthAuthInfo.ProviderUserInfo.Email)
	if err != nil {
		return nil, err
	}
	var filteredPrincipals []principal.Principal
	for _, p := range principals {
		if passwordPrincipal, ok := p.(*password.Principal); ok && passwordPrincipal.Realm != mergeRealm {
			continue
		}
		filteredPrincipals = append(filteredPrincipals, p)
	}
	return filteredPrincipals, nil
}

func (h respHandler) principalsToUserIDs(principals []principal.Principal) []string {
	seen := map[string]struct{}{}
	var userIDs []string
	for _, p := range principals {
		userID := p.PrincipalUserID()
		_, ok := seen[userID]
		if !ok {
			seen[userID] = struct{}{}
			userIDs = append(userIDs, userID)
		}
	}
	return userIDs
}

func (h respHandler) createPrincipalByOAuthInfo(userID string, oauthAuthInfo sso.AuthInfo) (*oauth.Principal, error) {
	now := timeNow()
	providerKeys := oauth.ProviderKeysFromProviderConfig(oauthAuthInfo.ProviderConfig)
	principal := oauth.NewPrincipal(providerKeys)
	principal.UserID = userID
	principal.ProviderType = string(oauthAuthInfo.ProviderConfig.Type)
	principal.ProviderUserID = oauthAuthInfo.ProviderUserInfo.ID
	principal.AccessTokenResp = oauthAuthInfo.ProviderAccessTokenResp
	principal.SetRawProfile(oauthAuthInfo.ProviderRawProfile)
	principal.CreatedAt = &now
	principal.UpdatedAt = &now
	err := h.OAuthAuthProvider.CreatePrincipal(principal)
	return principal, err
}
<|endoftext|>


<commit_before>package dsdk

import (
	"context"
	_path "path"

	greq "github.com/levigross/grequests"
)

const (
	ProviderAWS    = "AWS S3"
	ProviderGoogle = "Google Cloud"
	ProviderS3     = "S3 Object Store"
)

type RemoteProvider struct {
	Path              string                   `json:"path,omitempty" mapstructure:"path"`
	Uuid              string                   `json:"uuid,omitempty" mapstructure:"uuid"`
	AccountId         string                   `json:"account_id,omitempty" mapstructure:"account_id"`
	RemoteType        string                   `json:"remote_type,omitempty" mapstructure:"remote_type"`
	LastSeenTimestamp string                   `json:"last_seen_timestamp,omitempty" mapstructure:"last_seen_timestamp"`
	Operations        []map[string]interface{} `json:"operations,omitempty" mapstructure:"operations"`
	Snapshots         []*Snapshot              `json:"snapshots,omitempty" mapstructure:"snapshots"`
	Label             string                   `json:"label,omitempty" mapstructure:"label"`
	Status            string                   `json:"status,omitempty" mapstructure:"status"`
	Host              string                   `json:"host,omitempty" mapstructure:"host"`
	Port              string                   `json:"port,omitempty" mapstructure:"port"`
	OperationsEp      string
	SnapshotsEp       *Snapshots
}

func RegisterRemoteProviderEndpoints(rp *RemoteProvider) {
	//a.OperationsEp = newOperations(a.Path)
	rp.SnapshotsEp = newSnapshots(rp.Path)
}

type RemoteProviders struct {
	Path string
}

type RemoteProvidersCreateRequest struct {
	Ctxt        context.Context `json:"-"`
	ProjectName string          `json:"project_name,omitempty" mapstructure:"project_name"`
	AccountId   string          `json:"account_id,omitempty" mapstructure:"account_id"`
	RemoteType  string          `json:"remote_type,omitempty" mapstructure:"remote_type"`
	PrivateKey  string          `json:"private_key,omitempty" mapstructure:"private_key"`
	Label       string          `json:"label,omitempty" mapstructure:"label"`
	Host        string          `json:"host,omitempty" mapstructure:"host"`
	Port        int             `json:"port,omitempty" mapstructure:"port"`
	AccessKey   string          `json:"access_key,omitempty" mapstructure:"access_key"`
	SecretKey   string          `json:"secret_key,omitempty" mapstructure:"secret_key"`
}

func newRemoteProviders(path string) *RemoteProviders {
	return &RemoteProviders{
		Path: _path.Join(path, "remote_providers"),
	}
}

func (e *RemoteProviders) Create(ro *RemoteProvidersCreateRequest) (*RemoteProvider, *ApiErrorResponse, error) {
	gro := &greq.RequestOptions{JSON: ro}
	rs, apierr, err := GetConn(ro.Ctxt).Post(ro.Ctxt, e.Path, gro)
	if apierr != nil {
		return nil, apierr, err
	}
	if err != nil {
		return nil, nil, err
	}
	resp := &RemoteProvider{}
	if err = FillStruct(rs.Data, resp); err != nil {
		return nil, nil, err
	}
	RegisterRemoteProviderEndpoints(resp)
	return resp, nil, nil
}

type RemoteProvidersListRequest struct {
	Ctxt   context.Context `json:"-"`
	Params ListParams      `json:"params,omitempty"`
}

func (e *RemoteProviders) List(ro *RemoteProvidersListRequest) ([]*RemoteProvider, *ApiErrorResponse, error) {
	gro := &greq.RequestOptions{
		JSON:   ro,
		Params: ro.Params.ToMap()}
	rs, apierr, err := GetConn(ro.Ctxt).GetList(ro.Ctxt, e.Path, gro)
	if apierr != nil {
		return nil, apierr, err
	}
	if err != nil {
		return nil, nil, err
	}
	resp := []*RemoteProvider{}
	for _, data := range rs.Data {
		elem := &RemoteProvider{}
		adata := data.(map[string]interface{})
		if err = FillStruct(adata, elem); err != nil {
			return nil, nil, err
		}
		RegisterRemoteProviderEndpoints(elem)
		resp = append(resp, elem)
	}
	return resp, nil, nil
}

type RemoteProvidersGetRequest struct {
	Ctxt context.Context `json:"-"`
	Id   string          `json:"-"`
}

func (e *RemoteProviders) Get(ro *RemoteProvidersGetRequest) (*RemoteProvider, *ApiErrorResponse, error) {
	gro := &greq.RequestOptions{JSON: ro}
	rs, apierr, err := GetConn(ro.Ctxt).Get(ro.Ctxt, _path.Join(e.Path, ro.Id), gro)
	if apierr != nil {
		return nil, apierr, err
	}
	if err != nil {
		return nil, nil, err
	}
	resp := &RemoteProvider{}
	if err = FillStruct(rs.Data, resp); err != nil {
		return nil, nil, err
	}
	RegisterRemoteProviderEndpoints(resp)
	return resp, nil, nil
}

type RemoteProvidersRefreshRequest struct {
	Ctxt context.Context `json:"-"`
	Uuid string          `json:"-"`
}

type RemoteProvidersRefreshResponse struct {
	Uuid string `json:"uuid,omitempty" mapstructure:"uuid"`
}

func (e *RemoteProviders) Refresh(ro *RemoteProvidersRefreshRequest) (*RemoteProvidersRefreshResponse, *ApiErrorResponse, error) {
	gro := &greq.RequestOptions{JSON: ro}
	rs, apierr, err := GetConn(ro.Ctxt).Put(ro.Ctxt, _path.Join(e.Path, ro.Uuid, "refresh"), gro)

	if apierr != nil {
		return nil, apierr, err
	}
	if err != nil {
		return nil, nil, err
	}

	resp := &RemoteProvidersRefreshResponse{}
	if err = FillStruct(rs.Data, resp); err != nil {
		return nil, nil, err
	}

	return resp, nil, nil
}

type RemoteProviderSetRequest struct {
	Ctxt        context.Context `json:"-"`
	ProjectName string          `json:"project_name,omitempty" mapstructure:"project_name"`
	AccountId   string          `json:"account_id,omitempty" mapstructure:"account_id"`
	PrivateKey  string          `json:"private_key,omitempty" mapstructure:"private_key"`
	Label       string          `json:"label,omitempty" mapstructure:"label"`
	Host        string          `json:"host,omitempty" mapstructure:"host"`
	Port        int             `json:"port,omitempty" mapstructure:"port"`
	AccessKey   string          `json:"access_key,omitempty" mapstructure:"access_key"`
	SecretKey   string          `json:"secret_key,omitempty" mapstructure:"secret_key"`
}

func (e *RemoteProvider) Set(ro *RemoteProviderSetRequest) (*RemoteProvider, *ApiErrorResponse, error) {
	gro := &greq.RequestOptions{JSON: ro}
	rs, apierr, err := GetConn(ro.Ctxt).Put(ro.Ctxt, e.Path, gro)
	if apierr != nil {
		return nil, apierr, err
	}
	if err != nil {
		return nil, nil, err
	}
	resp := &RemoteProvider{}
	if err = FillStruct(rs.Data, resp); err != nil {
		return nil, nil, err
	}
	RegisterRemoteProviderEndpoints(resp)
	return resp, nil, nil
}

type RemoteProviderDeleteRequest struct {
	Ctxt  context.Context `json:"-"`
	Force bool            `json:"force,omitempty" mapstructure:"force"`
}

func (e *RemoteProvider) Delete(ro *RemoteProviderDeleteRequest) (*RemoteProvider, *ApiErrorResponse, error) {
	rs, apierr, err := GetConn(ro.Ctxt).Delete(ro.Ctxt, e.Path, nil)
	if apierr != nil {
		return nil, apierr, err
	}
	if err != nil {
		return nil, nil, err
	}
	resp := &RemoteProvider{}
	if err = FillStruct(rs.Data, resp); err != nil {
		return nil, nil, err
	}
	RegisterRemoteProviderEndpoints(resp)
	return resp, nil, nil
}

type RemoteProviderAppTemplate struct {
	Path           string `json:"path,omitempty" mapstructure:"path"`
	ResolvedPath   string `json:"resolved_path,omitempty" mapstructure:"resolved_path"`
	ResolvedTenant string `json:"resolved_tenant,omitempty" mapstructure:"resolved_tenant"`
}

type RemoteProviderReloadRequest struct {
	Ctxt context.Context `json:"-"`
}

func (e *RemoteProvider) Reload(ro *RemoteProviderReloadRequest) (*RemoteProvider, *ApiErrorResponse, error) {
	gro := &greq.RequestOptions{JSON: ro}
	rs, apierr, err := GetConn(ro.Ctxt).Get(ro.Ctxt, e.Path, gro)
	if apierr != nil {
		return nil, apierr, err
	}
	if err != nil {
		return nil, nil, err
	}
	resp := &RemoteProvider{}
	if err = FillStruct(rs.Data, resp); err != nil {
		return nil, nil, err
	}
	RegisterRemoteProviderEndpoints(resp)
	return resp, nil, nil
}

type RemoteProviderOperationsUpdateRequest struct {
	Ctxt        context.Context `json:"-"`
	OperationId string          `json:"omit"`
	Action      string          `json:"action"` //available options are 'clear' and 'abort'
}

func (e *RemoteProvider) CreateOperation(ao *RemoteProviderOperationsUpdateRequest) (*RemoteProvider, *ApiErrorResponse, error) {

	gro := &greq.RequestOptions{JSON: ao}
	rs, apierr, err := GetConn(ao.Ctxt).Put(ao.Ctxt, _path.Join(e.Path, "operations", ao.OperationId), gro)
	if apierr != nil {
		return nil, apierr, err
	}
	if err != nil {
		return nil, nil, err
	}
	resp := &RemoteProvider{}
	if err = FillStruct(rs.Data, resp); err != nil {
		return nil, nil, err
	}
	RegisterRemoteProviderEndpoints(resp)
	return resp, nil, nil
}
<commit_msg>Fix JSON serialization for UpdateRequest<commit_after>package dsdk

import (
	"context"
	_path "path"

	greq "github.com/levigross/grequests"
)

const (
	ProviderAWS    = "AWS S3"
	ProviderGoogle = "Google Cloud"
	ProviderS3     = "S3 Object Store"
)

type RemoteProvider struct {
	Path              string                   `json:"path,omitempty" mapstructure:"path"`
	Uuid              string                   `json:"uuid,omitempty" mapstructure:"uuid"`
	AccountId         string                   `json:"account_id,omitempty" mapstructure:"account_id"`
	RemoteType        string                   `json:"remote_type,omitempty" mapstructure:"remote_type"`
	LastSeenTimestamp string                   `json:"last_seen_timestamp,omitempty" mapstructure:"last_seen_timestamp"`
	Operations        []map[string]interface{} `json:"operations,omitempty" mapstructure:"operations"`
	Snapshots         []*Snapshot              `json:"snapshots,omitempty" mapstructure:"snapshots"`
	Label             string                   `json:"label,omitempty" mapstructure:"label"`
	Status            string                   `json:"status,omitempty" mapstructure:"status"`
	Host              string                   `json:"host,omitempty" mapstructure:"host"`
	Port              string                   `json:"port,omitempty" mapstructure:"port"`
	OperationsEp      string
	SnapshotsEp       *Snapshots
}

func RegisterRemoteProviderEndpoints(rp *RemoteProvider) {
	//a.OperationsEp = newOperations(a.Path)
	rp.SnapshotsEp = newSnapshots(rp.Path)
}

type RemoteProviders struct {
	Path string
}

type RemoteProvidersCreateRequest struct {
	Ctxt        context.Context `json:"-"`
	ProjectName string          `json:"project_name,omitempty" mapstructure:"project_name"`
	AccountId   string          `json:"account_id,omitempty" mapstructure:"account_id"`
	RemoteType  string          `json:"remote_type,omitempty" mapstructure:"remote_type"`
	PrivateKey  string          `json:"private_key,omitempty" mapstructure:"private_key"`
	Label       string          `json:"label,omitempty" mapstructure:"label"`
	Host        string          `json:"host,omitempty" mapstructure:"host"`
	Port        int             `json:"port,omitempty" mapstructure:"port"`
	AccessKey   string          `json:"access_key,omitempty" mapstructure:"access_key"`
	SecretKey   string          `json:"secret_key,omitempty" mapstructure:"secret_key"`
}

func newRemoteProviders(path string) *RemoteProviders {
	return &RemoteProviders{
		Path: _path.Join(path, "remote_providers"),
	}
}

func (e *RemoteProviders) Create(ro *RemoteProvidersCreateRequest) (*RemoteProvider, *ApiErrorResponse, error) {
	gro := &greq.RequestOptions{JSON: ro}
	rs, apierr, err := GetConn(ro.Ctxt).Post(ro.Ctxt, e.Path, gro)
	if apierr != nil {
		return nil, apierr, err
	}
	if err != nil {
		return nil, nil, err
	}
	resp := &RemoteProvider{}
	if err = FillStruct(rs.Data, resp); err != nil {
		return nil, nil, err
	}
	RegisterRemoteProviderEndpoints(resp)
	return resp, nil, nil
}

type RemoteProvidersListRequest struct {
	Ctxt   context.Context `json:"-"`
	Params ListParams      `json:"params,omitempty"`
}

func (e *RemoteProviders) List(ro *RemoteProvidersListRequest) ([]*RemoteProvider, *ApiErrorResponse, error) {
	gro := &greq.RequestOptions{
		JSON:   ro,
		Params: ro.Params.ToMap()}
	rs, apierr, err := GetConn(ro.Ctxt).GetList(ro.Ctxt, e.Path, gro)
	if apierr != nil {
		return nil, apierr, err
	}
	if err != nil {
		return nil, nil, err
	}
	resp := []*RemoteProvider{}
	for _, data := range rs.Data {
		elem := &RemoteProvider{}
		adata := data.(map[string]interface{})
		if err = FillStruct(adata, elem); err != nil {
			return nil, nil, err
		}
		RegisterRemoteProviderEndpoints(elem)
		resp = append(resp, elem)
	}
	return resp, nil, nil
}

type RemoteProvidersGetRequest struct {
	Ctxt context.Context `json:"-"`
	Id   string          `json:"-"`
}

func (e *RemoteProviders) Get(ro *RemoteProvidersGetRequest) (*RemoteProvider, *ApiErrorResponse, error) {
	gro := &greq.RequestOptions{JSON: ro}
	rs, apierr, err := GetConn(ro.Ctxt).Get(ro.Ctxt, _path.Join(e.Path, ro.Id), gro)
	if apierr != nil {
		return nil, apierr, err
	}
	if err != nil {
		return nil, nil, err
	}
	resp := &RemoteProvider{}
	if err = FillStruct(rs.Data, resp); err != nil {
		return nil, nil, err
	}
	RegisterRemoteProviderEndpoints(resp)
	return resp, nil, nil
}

type RemoteProvidersRefreshRequest struct {
	Ctxt context.Context `json:"-"`
	Uuid string          `json:"-"`
}

type RemoteProvidersRefreshResponse struct {
	Uuid string `json:"uuid,omitempty" mapstructure:"uuid"`
}

func (e *RemoteProviders) Refresh(ro *RemoteProvidersRefreshRequest) (*RemoteProvidersRefreshResponse, *ApiErrorResponse, error) {
	gro := &greq.RequestOptions{JSON: ro}
	rs, apierr, err := GetConn(ro.Ctxt).Put(ro.Ctxt, _path.Join(e.Path, ro.Uuid, "refresh"), gro)

	if apierr != nil {
		return nil, apierr, err
	}
	if err != nil {
		return nil, nil, err
	}

	resp := &RemoteProvidersRefreshResponse{}
	if err = FillStruct(rs.Data, resp); err != nil {
		return nil, nil, err
	}

	return resp, nil, nil
}

type RemoteProviderSetRequest struct {
	Ctxt        context.Context `json:"-"`
	ProjectName string          `json:"project_name,omitempty" mapstructure:"project_name"`
	AccountId   string          `json:"account_id,omitempty" mapstructure:"account_id"`
	PrivateKey  string          `json:"private_key,omitempty" mapstructure:"private_key"`
	Label       string          `json:"label,omitempty" mapstructure:"label"`
	Host        string          `json:"host,omitempty" mapstructure:"host"`
	Port        int             `json:"port,omitempty" mapstructure:"port"`
	AccessKey   string          `json:"access_key,omitempty" mapstructure:"access_key"`
	SecretKey   string          `json:"secret_key,omitempty" mapstructure:"secret_key"`
}

func (e *RemoteProvider) Set(ro *RemoteProviderSetRequest) (*RemoteProvider, *ApiErrorResponse, error) {
	gro := &greq.RequestOptions{JSON: ro}
	rs, apierr, err := GetConn(ro.Ctxt).Put(ro.Ctxt, e.Path, gro)
	if apierr != nil {
		return nil, apierr, err
	}
	if err != nil {
		return nil, nil, err
	}
	resp := &RemoteProvider{}
	if err = FillStruct(rs.Data, resp); err != nil {
		return nil, nil, err
	}
	RegisterRemoteProviderEndpoints(resp)
	return resp, nil, nil
}

type RemoteProviderDeleteRequest struct {
	Ctxt  context.Context `json:"-"`
	Force bool            `json:"force,omitempty" mapstructure:"force"`
}

func (e *RemoteProvider) Delete(ro *RemoteProviderDeleteRequest) (*RemoteProvider, *ApiErrorResponse, error) {
	rs, apierr, err := GetConn(ro.Ctxt).Delete(ro.Ctxt, e.Path, nil)
	if apierr != nil {
		return nil, apierr, err
	}
	if err != nil {
		return nil, nil, err
	}
	resp := &RemoteProvider{}
	if err = FillStruct(rs.Data, resp); err != nil {
		return nil, nil, err
	}
	RegisterRemoteProviderEndpoints(resp)
	return resp, nil, nil
}

type RemoteProviderAppTemplate struct {
	Path           string `json:"path,omitempty" mapstructure:"path"`
	ResolvedPath   string `json:"resolved_path,omitempty" mapstructure:"resolved_path"`
	ResolvedTenant string `json:"resolved_tenant,omitempty" mapstructure:"resolved_tenant"`
}

type RemoteProviderReloadRequest struct {
	Ctxt context.Context `json:"-"`
}

func (e *RemoteProvider) Reload(ro *RemoteProviderReloadRequest) (*RemoteProvider, *ApiErrorResponse, error) {
	gro := &greq.RequestOptions{JSON: ro}
	rs, apierr, err := GetConn(ro.Ctxt).Get(ro.Ctxt, e.Path, gro)
	if apierr != nil {
		return nil, apierr, err
	}
	if err != nil {
		return nil, nil, err
	}
	resp := &RemoteProvider{}
	if err = FillStruct(rs.Data, resp); err != nil {
		return nil, nil, err
	}
	RegisterRemoteProviderEndpoints(resp)
	return resp, nil, nil
}

type RemoteProviderOperationsUpdateRequest struct {
	Ctxt        context.Context `json:"-"`
	OperationId string          `json:"-"`
	Action      string          `json:"action"` //available options are 'clear' and 'abort'
}

func (e *RemoteProvider) CreateOperation(ao *RemoteProviderOperationsUpdateRequest) (*RemoteProvider, *ApiErrorResponse, error) {

	gro := &greq.RequestOptions{JSON: ao}
	rs, apierr, err := GetConn(ao.Ctxt).Put(ao.Ctxt, _path.Join(e.Path, "operations", ao.OperationId), gro)
	if apierr != nil {
		return nil, apierr, err
	}
	if err != nil {
		return nil, nil, err
	}
	resp := &RemoteProvider{}
	if err = FillStruct(rs.Data, resp); err != nil {
		return nil, nil, err
	}
	RegisterRemoteProviderEndpoints(resp)
	return resp, nil, nil
}
<|endoftext|>


<commit_before>/*
Copyright 2014 The Kubernetes Authors All rights reserved.

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

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

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

package cmd

import (
	"bytes"
	"fmt"
	"io"
	"io/ioutil"
	"os"
	"reflect"
	"testing"

	"github.com/GoogleCloudPlatform/kubernetes/pkg/api"
	"github.com/GoogleCloudPlatform/kubernetes/pkg/api/latest"
	"github.com/GoogleCloudPlatform/kubernetes/pkg/api/meta"
	"github.com/GoogleCloudPlatform/kubernetes/pkg/api/testapi"
	"github.com/GoogleCloudPlatform/kubernetes/pkg/api/validation"
	"github.com/GoogleCloudPlatform/kubernetes/pkg/client"
	"github.com/GoogleCloudPlatform/kubernetes/pkg/kubectl"
	cmdutil "github.com/GoogleCloudPlatform/kubernetes/pkg/kubectl/cmd/util"
	"github.com/GoogleCloudPlatform/kubernetes/pkg/kubectl/resource"
	"github.com/GoogleCloudPlatform/kubernetes/pkg/runtime"
)

type internalType struct {
	Kind       string
	APIVersion string

	Name string
}

type externalType struct {
	Kind       string `json:"kind"`
	APIVersion string `json:"apiVersion"`

	Name string `json:"name"`
}

type ExternalType2 struct {
	Kind       string `json:"kind"`
	APIVersion string `json:"apiVersion"`

	Name string `json:"name"`
}

func (*internalType) IsAnAPIObject()  {}
func (*externalType) IsAnAPIObject()  {}
func (*ExternalType2) IsAnAPIObject() {}

func newExternalScheme() (*runtime.Scheme, meta.RESTMapper, runtime.Codec) {
	scheme := runtime.NewScheme()
	scheme.AddKnownTypeWithName("", "Type", &internalType{})
	scheme.AddKnownTypeWithName("unlikelyversion", "Type", &externalType{})
	//This tests that kubectl will not confuse the external scheme with the internal scheme, even when they accidentally have versions of the same name.
	scheme.AddKnownTypeWithName(testapi.Version(), "Type", &ExternalType2{})

	codec := runtime.CodecFor(scheme, "unlikelyversion")
	validVersion := testapi.Version()
	mapper := meta.NewDefaultRESTMapper([]string{"unlikelyversion", validVersion}, func(version string) (*meta.VersionInterfaces, bool) {
		return &meta.VersionInterfaces{
			Codec:            runtime.CodecFor(scheme, version),
			ObjectConvertor:  scheme,
			MetadataAccessor: meta.NewAccessor(),
		}, (version == validVersion || version == "unlikelyversion")
	})
	for _, version := range []string{"unlikelyversion", validVersion} {
		for kind := range scheme.KnownTypes(version) {
			mixedCase := false
			scope := meta.RESTScopeNamespace
			mapper.Add(scope, kind, version, mixedCase)
		}
	}

	return scheme, mapper, codec
}

type testPrinter struct {
	Objects []runtime.Object
	Err     error
}

func (t *testPrinter) PrintObj(obj runtime.Object, out io.Writer) error {
	t.Objects = append(t.Objects, obj)
	fmt.Fprintf(out, "%#v", obj)
	return t.Err
}

type testDescriber struct {
	Name, Namespace string
	Output          string
	Err             error
}

func (t *testDescriber) Describe(namespace, name string) (output string, err error) {
	t.Namespace, t.Name = namespace, name
	return t.Output, t.Err
}

type testFactory struct {
	Mapper       meta.RESTMapper
	Typer        runtime.ObjectTyper
	Client       kubectl.RESTClient
	Describer    kubectl.Describer
	Printer      kubectl.ResourcePrinter
	Validator    validation.Schema
	Namespace    string
	ClientConfig *client.Config
	Err          error
}

func NewTestFactory() (*cmdutil.Factory, *testFactory, runtime.Codec) {
	scheme, mapper, codec := newExternalScheme()
	t := &testFactory{
		Validator: validation.NullSchema{},
		Mapper:    mapper,
		Typer:     scheme,
	}
	return &cmdutil.Factory{
		Object: func() (meta.RESTMapper, runtime.ObjectTyper) {
			return t.Mapper, t.Typer
		},
		RESTClient: func(*meta.RESTMapping) (resource.RESTClient, error) {
			return t.Client, t.Err
		},
		Describer: func(*meta.RESTMapping) (kubectl.Describer, error) {
			return t.Describer, t.Err
		},
		Printer: func(mapping *meta.RESTMapping, noHeaders, withNamespace bool, wide bool, columnLabels []string) (kubectl.ResourcePrinter, error) {
			return t.Printer, t.Err
		},
		Validator: func() (validation.Schema, error) {
			return t.Validator, t.Err
		},
		DefaultNamespace: func() (string, bool, error) {
			return t.Namespace, false, t.Err
		},
		ClientConfig: func() (*client.Config, error) {
			return t.ClientConfig, t.Err
		},
	}, t, codec
}

func NewMixedFactory(apiClient resource.RESTClient) (*cmdutil.Factory, *testFactory, runtime.Codec) {
	f, t, c := NewTestFactory()
	f.Object = func() (meta.RESTMapper, runtime.ObjectTyper) {
		return meta.MultiRESTMapper{t.Mapper, latest.RESTMapper}, runtime.MultiObjectTyper{t.Typer, api.Scheme}
	}
	f.RESTClient = func(m *meta.RESTMapping) (resource.RESTClient, error) {
		if m.ObjectConvertor == api.Scheme {
			return apiClient, t.Err
		}
		return t.Client, t.Err
	}
	return f, t, c
}

func NewAPIFactory() (*cmdutil.Factory, *testFactory, runtime.Codec) {
	t := &testFactory{
		Validator: validation.NullSchema{},
	}
	generators := map[string]kubectl.Generator{
		"run/v1":     kubectl.BasicReplicationController{},
		"service/v1": kubectl.ServiceGenerator{},
	}
	return &cmdutil.Factory{
		Object: func() (meta.RESTMapper, runtime.ObjectTyper) {
			return latest.RESTMapper, api.Scheme
		},
		Client: func() (*client.Client, error) {
			// Swap out the HTTP client out of the client with the fake's version.
			fakeClient := t.Client.(*client.FakeRESTClient)
			c := client.NewOrDie(t.ClientConfig)
			c.Client = fakeClient.Client
			return c, t.Err
		},
		RESTClient: func(*meta.RESTMapping) (resource.RESTClient, error) {
			return t.Client, t.Err
		},
		Describer: func(*meta.RESTMapping) (kubectl.Describer, error) {
			return t.Describer, t.Err
		},
		Printer: func(mapping *meta.RESTMapping, noHeaders, withNamespace bool, wide bool, columnLabels []string) (kubectl.ResourcePrinter, error) {
			return t.Printer, t.Err
		},
		Validator: func() (validation.Schema, error) {
			return t.Validator, t.Err
		},
		DefaultNamespace: func() (string, bool, error) {
			return t.Namespace, false, t.Err
		},
		ClientConfig: func() (*client.Config, error) {
			return t.ClientConfig, t.Err
		},
		Generator: func(name string) (kubectl.Generator, bool) {
			generator, ok := generators[name]
			return generator, ok
		},
	}, t, testapi.Codec()
}

func objBody(codec runtime.Codec, obj runtime.Object) io.ReadCloser {
	return ioutil.NopCloser(bytes.NewReader([]byte(runtime.EncodeOrDie(codec, obj))))
}

func stringBody(body string) io.ReadCloser {
	return ioutil.NopCloser(bytes.NewReader([]byte(body)))
}

// TODO(jlowdermilk): refactor the Factory so we can test client versions properly,
// with different client/server version skew scenarios.
// Verify that resource.RESTClients constructed from a factory respect mapping.APIVersion
//func TestClientVersions(t *testing.T) {
//	f := cmdutil.NewFactory(nil)
//
//	version := testapi.Version()
//	mapping := &meta.RESTMapping{
//		APIVersion: version,
//	}
//	c, err := f.RESTClient(mapping)
//	if err != nil {
//		t.Errorf("unexpected error: %v", err)
//	}
//	client := c.(*client.RESTClient)
//	if client.APIVersion() != version {
//		t.Errorf("unexpected Client APIVersion: %s %v", client.APIVersion, client)
//	}
//}

func ExamplePrintReplicationControllerWithNamespace() {
	f, tf, codec := NewAPIFactory()
	tf.Printer = kubectl.NewHumanReadablePrinter(false, true, false, []string{})
	tf.Client = &client.FakeRESTClient{
		Codec:  codec,
		Client: nil,
	}
	cmd := NewCmdRun(f, os.Stdout)
	ctrl := &api.ReplicationController{
		ObjectMeta: api.ObjectMeta{
			Name:      "foo",
			Namespace: "beep",
			Labels:    map[string]string{"foo": "bar"},
		},
		Spec: api.ReplicationControllerSpec{
			Replicas: 1,
			Selector: map[string]string{"foo": "bar"},
			Template: &api.PodTemplateSpec{
				ObjectMeta: api.ObjectMeta{
					Labels: map[string]string{"foo": "bar"},
				},
				Spec: api.PodSpec{
					Containers: []api.Container{
						{
							Name:  "foo",
							Image: "someimage",
						},
					},
				},
			},
		},
	}
	err := f.PrintObject(cmd, ctrl, os.Stdout)
	if err != nil {
		fmt.Printf("Unexpected error: %v", err)
	}
	// Output:
	// NAMESPACE   CONTROLLER   CONTAINER(S)   IMAGE(S)    SELECTOR   REPLICAS
	// beep        foo          foo            someimage   foo=bar    1
}

func ExamplePrintPodWithWideFormat() {
	f, tf, codec := NewAPIFactory()
	tf.Printer = kubectl.NewHumanReadablePrinter(false, false, true, []string{})
	tf.Client = &client.FakeRESTClient{
		Codec:  codec,
		Client: nil,
	}
	nodeName := "kubernetes-minion-abcd"
	cmd := NewCmdRun(f, os.Stdout)
	ctrl := &api.Pod{
		ObjectMeta: api.ObjectMeta{Name: "test1"},
		Spec: api.PodSpec{
			Containers: make([]api.Container, 2),
			NodeName:   nodeName,
		},
		Status: api.PodStatus{
			Phase: "podPhase",
			ContainerStatuses: []api.ContainerStatus{
				{Ready: true, RestartCount: 3, State: api.ContainerState{Running: &api.ContainerStateRunning{}}},
				{RestartCount: 3},
			},
		},
	}
	err := f.PrintObject(cmd, ctrl, os.Stdout)
	if err != nil {
		fmt.Printf("Unexpected error: %v", err)
	}
	// Output:
	// NAME      READY     STATUS     RESTARTS   AGE       NODE
	// test1     1/2       podPhase   6          292y      kubernetes-minion-abcd
}

func TestNormalizationFuncGlobalExistance(t *testing.T) {
	// This test can be safely deleted when we will not support multiple flag formats
	root := NewKubectlCommand(cmdutil.NewFactory(nil), os.Stdin, os.Stdout, os.Stderr)

	if root.Parent() != nil {
		t.Fatal("We expect the root command to be returned")
	}
	if root.GlobalNormalizationFunc() == nil {
		t.Fatal("We expect that root command has a global normalization function")
	}

	if reflect.ValueOf(root.GlobalNormalizationFunc()).Pointer() != reflect.ValueOf(root.Flags().GetNormalizeFunc()).Pointer() {
		t.Fatal("root command seems to have a wrong normalization function")
	}

	sub := root
	for sub.HasSubCommands() {
		sub = sub.Commands()[0]
	}

	// In case of failure of this test check this PR: spf13/cobra#110
	if reflect.ValueOf(sub.Flags().GetNormalizeFunc()).Pointer() != reflect.ValueOf(root.Flags().GetNormalizeFunc()).Pointer() {
		t.Fatal("child and root commands should have the same normalization functions")
	}
}
<commit_msg>Make "age" of pod print example static<commit_after>/*
Copyright 2014 The Kubernetes Authors All rights reserved.

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

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

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

package cmd

import (
	"bytes"
	"fmt"
	"io"
	"io/ioutil"
	"os"
	"reflect"
	"testing"
	"time"

	"github.com/GoogleCloudPlatform/kubernetes/pkg/api"
	"github.com/GoogleCloudPlatform/kubernetes/pkg/api/latest"
	"github.com/GoogleCloudPlatform/kubernetes/pkg/api/meta"
	"github.com/GoogleCloudPlatform/kubernetes/pkg/api/testapi"
	"github.com/GoogleCloudPlatform/kubernetes/pkg/api/validation"
	"github.com/GoogleCloudPlatform/kubernetes/pkg/client"
	"github.com/GoogleCloudPlatform/kubernetes/pkg/kubectl"
	cmdutil "github.com/GoogleCloudPlatform/kubernetes/pkg/kubectl/cmd/util"
	"github.com/GoogleCloudPlatform/kubernetes/pkg/kubectl/resource"
	"github.com/GoogleCloudPlatform/kubernetes/pkg/runtime"
	"github.com/GoogleCloudPlatform/kubernetes/pkg/util"
)

type internalType struct {
	Kind       string
	APIVersion string

	Name string
}

type externalType struct {
	Kind       string `json:"kind"`
	APIVersion string `json:"apiVersion"`

	Name string `json:"name"`
}

type ExternalType2 struct {
	Kind       string `json:"kind"`
	APIVersion string `json:"apiVersion"`

	Name string `json:"name"`
}

func (*internalType) IsAnAPIObject()  {}
func (*externalType) IsAnAPIObject()  {}
func (*ExternalType2) IsAnAPIObject() {}

func newExternalScheme() (*runtime.Scheme, meta.RESTMapper, runtime.Codec) {
	scheme := runtime.NewScheme()
	scheme.AddKnownTypeWithName("", "Type", &internalType{})
	scheme.AddKnownTypeWithName("unlikelyversion", "Type", &externalType{})
	//This tests that kubectl will not confuse the external scheme with the internal scheme, even when they accidentally have versions of the same name.
	scheme.AddKnownTypeWithName(testapi.Version(), "Type", &ExternalType2{})

	codec := runtime.CodecFor(scheme, "unlikelyversion")
	validVersion := testapi.Version()
	mapper := meta.NewDefaultRESTMapper([]string{"unlikelyversion", validVersion}, func(version string) (*meta.VersionInterfaces, bool) {
		return &meta.VersionInterfaces{
			Codec:            runtime.CodecFor(scheme, version),
			ObjectConvertor:  scheme,
			MetadataAccessor: meta.NewAccessor(),
		}, (version == validVersion || version == "unlikelyversion")
	})
	for _, version := range []string{"unlikelyversion", validVersion} {
		for kind := range scheme.KnownTypes(version) {
			mixedCase := false
			scope := meta.RESTScopeNamespace
			mapper.Add(scope, kind, version, mixedCase)
		}
	}

	return scheme, mapper, codec
}

type testPrinter struct {
	Objects []runtime.Object
	Err     error
}

func (t *testPrinter) PrintObj(obj runtime.Object, out io.Writer) error {
	t.Objects = append(t.Objects, obj)
	fmt.Fprintf(out, "%#v", obj)
	return t.Err
}

type testDescriber struct {
	Name, Namespace string
	Output          string
	Err             error
}

func (t *testDescriber) Describe(namespace, name string) (output string, err error) {
	t.Namespace, t.Name = namespace, name
	return t.Output, t.Err
}

type testFactory struct {
	Mapper       meta.RESTMapper
	Typer        runtime.ObjectTyper
	Client       kubectl.RESTClient
	Describer    kubectl.Describer
	Printer      kubectl.ResourcePrinter
	Validator    validation.Schema
	Namespace    string
	ClientConfig *client.Config
	Err          error
}

func NewTestFactory() (*cmdutil.Factory, *testFactory, runtime.Codec) {
	scheme, mapper, codec := newExternalScheme()
	t := &testFactory{
		Validator: validation.NullSchema{},
		Mapper:    mapper,
		Typer:     scheme,
	}
	return &cmdutil.Factory{
		Object: func() (meta.RESTMapper, runtime.ObjectTyper) {
			return t.Mapper, t.Typer
		},
		RESTClient: func(*meta.RESTMapping) (resource.RESTClient, error) {
			return t.Client, t.Err
		},
		Describer: func(*meta.RESTMapping) (kubectl.Describer, error) {
			return t.Describer, t.Err
		},
		Printer: func(mapping *meta.RESTMapping, noHeaders, withNamespace bool, wide bool, columnLabels []string) (kubectl.ResourcePrinter, error) {
			return t.Printer, t.Err
		},
		Validator: func() (validation.Schema, error) {
			return t.Validator, t.Err
		},
		DefaultNamespace: func() (string, bool, error) {
			return t.Namespace, false, t.Err
		},
		ClientConfig: func() (*client.Config, error) {
			return t.ClientConfig, t.Err
		},
	}, t, codec
}

func NewMixedFactory(apiClient resource.RESTClient) (*cmdutil.Factory, *testFactory, runtime.Codec) {
	f, t, c := NewTestFactory()
	f.Object = func() (meta.RESTMapper, runtime.ObjectTyper) {
		return meta.MultiRESTMapper{t.Mapper, latest.RESTMapper}, runtime.MultiObjectTyper{t.Typer, api.Scheme}
	}
	f.RESTClient = func(m *meta.RESTMapping) (resource.RESTClient, error) {
		if m.ObjectConvertor == api.Scheme {
			return apiClient, t.Err
		}
		return t.Client, t.Err
	}
	return f, t, c
}

func NewAPIFactory() (*cmdutil.Factory, *testFactory, runtime.Codec) {
	t := &testFactory{
		Validator: validation.NullSchema{},
	}
	generators := map[string]kubectl.Generator{
		"run/v1":     kubectl.BasicReplicationController{},
		"service/v1": kubectl.ServiceGenerator{},
	}
	return &cmdutil.Factory{
		Object: func() (meta.RESTMapper, runtime.ObjectTyper) {
			return latest.RESTMapper, api.Scheme
		},
		Client: func() (*client.Client, error) {
			// Swap out the HTTP client out of the client with the fake's version.
			fakeClient := t.Client.(*client.FakeRESTClient)
			c := client.NewOrDie(t.ClientConfig)
			c.Client = fakeClient.Client
			return c, t.Err
		},
		RESTClient: func(*meta.RESTMapping) (resource.RESTClient, error) {
			return t.Client, t.Err
		},
		Describer: func(*meta.RESTMapping) (kubectl.Describer, error) {
			return t.Describer, t.Err
		},
		Printer: func(mapping *meta.RESTMapping, noHeaders, withNamespace bool, wide bool, columnLabels []string) (kubectl.ResourcePrinter, error) {
			return t.Printer, t.Err
		},
		Validator: func() (validation.Schema, error) {
			return t.Validator, t.Err
		},
		DefaultNamespace: func() (string, bool, error) {
			return t.Namespace, false, t.Err
		},
		ClientConfig: func() (*client.Config, error) {
			return t.ClientConfig, t.Err
		},
		Generator: func(name string) (kubectl.Generator, bool) {
			generator, ok := generators[name]
			return generator, ok
		},
	}, t, testapi.Codec()
}

func objBody(codec runtime.Codec, obj runtime.Object) io.ReadCloser {
	return ioutil.NopCloser(bytes.NewReader([]byte(runtime.EncodeOrDie(codec, obj))))
}

func stringBody(body string) io.ReadCloser {
	return ioutil.NopCloser(bytes.NewReader([]byte(body)))
}

// TODO(jlowdermilk): refactor the Factory so we can test client versions properly,
// with different client/server version skew scenarios.
// Verify that resource.RESTClients constructed from a factory respect mapping.APIVersion
//func TestClientVersions(t *testing.T) {
//	f := cmdutil.NewFactory(nil)
//
//	version := testapi.Version()
//	mapping := &meta.RESTMapping{
//		APIVersion: version,
//	}
//	c, err := f.RESTClient(mapping)
//	if err != nil {
//		t.Errorf("unexpected error: %v", err)
//	}
//	client := c.(*client.RESTClient)
//	if client.APIVersion() != version {
//		t.Errorf("unexpected Client APIVersion: %s %v", client.APIVersion, client)
//	}
//}

func ExamplePrintReplicationControllerWithNamespace() {
	f, tf, codec := NewAPIFactory()
	tf.Printer = kubectl.NewHumanReadablePrinter(false, true, false, []string{})
	tf.Client = &client.FakeRESTClient{
		Codec:  codec,
		Client: nil,
	}
	cmd := NewCmdRun(f, os.Stdout)
	ctrl := &api.ReplicationController{
		ObjectMeta: api.ObjectMeta{
			Name:      "foo",
			Namespace: "beep",
			Labels:    map[string]string{"foo": "bar"},
		},
		Spec: api.ReplicationControllerSpec{
			Replicas: 1,
			Selector: map[string]string{"foo": "bar"},
			Template: &api.PodTemplateSpec{
				ObjectMeta: api.ObjectMeta{
					Labels: map[string]string{"foo": "bar"},
				},
				Spec: api.PodSpec{
					Containers: []api.Container{
						{
							Name:  "foo",
							Image: "someimage",
						},
					},
				},
			},
		},
	}
	err := f.PrintObject(cmd, ctrl, os.Stdout)
	if err != nil {
		fmt.Printf("Unexpected error: %v", err)
	}
	// Output:
	// NAMESPACE   CONTROLLER   CONTAINER(S)   IMAGE(S)    SELECTOR   REPLICAS
	// beep        foo          foo            someimage   foo=bar    1
}

func ExamplePrintPodWithWideFormat() {
	f, tf, codec := NewAPIFactory()
	tf.Printer = kubectl.NewHumanReadablePrinter(false, false, true, []string{})
	tf.Client = &client.FakeRESTClient{
		Codec:  codec,
		Client: nil,
	}
	nodeName := "kubernetes-minion-abcd"
	cmd := NewCmdRun(f, os.Stdout)
	pod := &api.Pod{
		ObjectMeta: api.ObjectMeta{
			Name:              "test1",
			CreationTimestamp: util.Time{time.Now().AddDate(-10, 0, 0)},
		},
		Spec: api.PodSpec{
			Containers: make([]api.Container, 2),
			NodeName:   nodeName,
		},
		Status: api.PodStatus{
			Phase: "podPhase",
			ContainerStatuses: []api.ContainerStatus{
				{Ready: true, RestartCount: 3, State: api.ContainerState{Running: &api.ContainerStateRunning{}}},
				{RestartCount: 3},
			},
		},
	}
	err := f.PrintObject(cmd, pod, os.Stdout)
	if err != nil {
		fmt.Printf("Unexpected error: %v", err)
	}
	// Output:
	// NAME      READY     STATUS     RESTARTS   AGE       NODE
	// test1     1/2       podPhase   6          10y       kubernetes-minion-abcd
}

func TestNormalizationFuncGlobalExistance(t *testing.T) {
	// This test can be safely deleted when we will not support multiple flag formats
	root := NewKubectlCommand(cmdutil.NewFactory(nil), os.Stdin, os.Stdout, os.Stderr)

	if root.Parent() != nil {
		t.Fatal("We expect the root command to be returned")
	}
	if root.GlobalNormalizationFunc() == nil {
		t.Fatal("We expect that root command has a global normalization function")
	}

	if reflect.ValueOf(root.GlobalNormalizationFunc()).Pointer() != reflect.ValueOf(root.Flags().GetNormalizeFunc()).Pointer() {
		t.Fatal("root command seems to have a wrong normalization function")
	}

	sub := root
	for sub.HasSubCommands() {
		sub = sub.Commands()[0]
	}

	// In case of failure of this test check this PR: spf13/cobra#110
	if reflect.ValueOf(sub.Flags().GetNormalizeFunc()).Pointer() != reflect.ValueOf(root.Flags().GetNormalizeFunc()).Pointer() {
		t.Fatal("child and root commands should have the same normalization functions")
	}
}
<|endoftext|>