<commit_before>package video

import (
	"github.com/valerio/go-jeebie/jeebie/addr"
	"github.com/valerio/go-jeebie/jeebie/bit"
	"github.com/valerio/go-jeebie/jeebie/memory"
)

type GpuMode int

const (
	oamRead GpuMode = iota
	vramRead
	hblank
	vblank
)

const (
	hblankCycles       = 204
	oamScanlineCycles  = 80
	vramScanlineCycles = 172
	scanlineCycles     = oamScanlineCycles + vramScanlineCycles + hblankCycles
)

type GPU struct {
	memory      *memory.MMU
	screen      *Screen
	framebuffer *FrameBuffer

	line       uint8
	vblankLine int
	mode       GpuMode
	cycles     int
}

func NewGpu(screen *Screen, memory *memory.MMU) *GPU {
	fb := NewFrameBuffer()
	return &GPU{
		framebuffer: fb,
		screen:      screen,
		memory:      memory,
		mode:        oamRead,
		line:        0,
		cycles:      0,
	}
}

// Tick simulates gpu behaviour for a certain amount of clock cycles.
func (g *GPU) Tick(cycles int) {
	g.cycles += cycles

	switch g.mode {
	case oamRead:
		if g.cycles >= oamScanlineCycles {
			g.cycles %= oamScanlineCycles
			g.mode = vramRead
		}
		break
	case vramRead:
		if g.cycles >= vramScanlineCycles {
			g.cycles %= vramScanlineCycles
			g.mode = hblank
		}
		break
	case hblank:
		if g.cycles >= hblankCycles {
			g.line++
			g.memory.Write(addr.LY, g.line)
			// TODO: g.compareLYtoLYC()

			g.cycles %= hblankCycles
			g.mode = oamRead

			if g.line == 144 {
				g.mode = vblank
				g.vblankLine = 0

				// set vblank interrupt (bit 0)
				g.memory.RequestInterrupt(0)

				// g.drawFrame()
			}
		}
		break
	case vblank:
		if g.cycles >= scanlineCycles {
			g.line++
			g.cycles %= scanlineCycles

			if g.line == 154 {
				g.framebuffer.DrawNoise()
				// g.drawScanline()
				g.screen.Draw(g.framebuffer.ToSlice())
				g.line = 0
				g.mode = oamRead
			}
		}
		break
	}
}

func (g *GPU) drawScanline() {
	if g.readLCDCVariable(lcdDisplayEnable) == 0 {
		// display is disabled
		return
	}

	if g.readLCDCVariable(bgDisplay) == 0 {
		// drawing the background is disabled
		return
	}

	g.drawTiles()
}

func (g *GPU) drawTiles() {
	for y := 0; y < 32; y++ {
		for x := 0; x < 32; x++ {
			g.drawTile(x, y)
		}
	}
}

func (g *GPU) drawTile(tileX, tileY int) {
	useTileSetZero := g.readLCDCVariable(bgWindowTileDataSelect) == 0
	useTileMapZero := g.readLCDCVariable(bgTileMapDisplaySelect) == 0

	// select the correct starting address based on which tileMap/tileSet is set
	tileSetAddress := uint16(0x8000)
	if useTileSetZero {
		tileSetAddress = 0x8800
	}

	tileMapAddress := 0x9C00
	if useTileMapZero {
		tileMapAddress = 0x9800
	}

	// 32 tiles per scanline
	tileIndex := tileY*32 + tileX
	tileNumberAddress := tileMapAddress + tileIndex
	// grab the tile number
	tileNumber := g.memory.Read(uint16(tileNumberAddress))
	// offset is tile number times tile size (16 bytes per tile)
	tileOffset := uint16(tileNumber) * 16

	tileAddress := tileSetAddress + tileOffset
	tile := newTile(uint16(tileAddress), g.memory)

	// x and y inside a tile are offset by 8 in terms of address
	fbX := uint(8 * tileX)
	fbY := uint(8 * tileY)

	for y := uint(0); y < 8; y++ {
		for x := uint(0); x < 8; x++ {
			color := tile.getPixel(x, y)
			g.framebuffer.SetPixel(fbX+x, fbY+y, color)
		}
	}
}

// LCDC (LCD Control) Register bit values
// Bit 7 - LCD Display Enable (0=Off, 1=On)
// Bit 6 - Window Tile Map Display Select (0=9800-9BFF, 1=9C00-9FFF)
// Bit 5 - Window Display Enable (0=Off, 1=On)
// Bit 4 - BG & Window Tile Data Select (0=8800-97FF, 1=8000-8FFF)
// Bit 3 - BG Tile Map Display Select (0=9800-9BFF, 1=9C00-9FFF)
// Bit 2 - OBJ (Sprite) Size (0=8x8, 1=8x16)
// Bit 1 - OBJ (Sprite) Display Enable (0=Off, 1=On)
// Bit 0 - BG Display (0=Off, 1=On)

type lcdcFlag uint8

const (
	lcdDisplayEnable       lcdcFlag = 7
	windowTileMapSelect             = 6
	windowDisplayEnable             = 5
	bgWindowTileDataSelect          = 4
	bgTileMapDisplaySelect          = 3
	spriteSize                      = 2
	spriteDisplayEnable             = 1
	bgDisplay                       = 0
)

func (g *GPU) readLCDCVariable(flag lcdcFlag) byte {
	if bit.IsSet(uint8(flag), g.memory.Read(addr.LCDC)) {
		return 1
	}

	return 0
}

func (g *GPU) setLCDCVariable(flag lcdcFlag, shouldSet bool) {
	lcdcRegister := g.memory.Read(addr.LCDC)

	if shouldSet {
		lcdcRegister = bit.Set(uint8(flag), lcdcRegister)
	} else {
		lcdcRegister = bit.Clear(uint8(flag), lcdcRegister)
	}
}
<commit_msg>reimplement tick and BG/window rendering<commit_after>package video

import (
	"github.com/valerio/go-jeebie/jeebie/addr"
	"github.com/valerio/go-jeebie/jeebie/bit"
	"github.com/valerio/go-jeebie/jeebie/memory"
)

type GpuMode int

const (
	hblankMode GpuMode = iota
	vblankMode
	oamReadMode
	vramReadMode
)

const (
	hblankCycles       = 204
	oamScanlineCycles  = 80
	vramScanlineCycles = 172
	scanlineCycles     = oamScanlineCycles + vramScanlineCycles + hblankCycles
)

type GPU struct {
	memory      *memory.MMU
	screen      *Screen
	framebuffer *FrameBuffer

	mode                 GpuMode
	line                 int
	cycles               int
	modeCounterAux       int
	vBlankLine           int
	pixelCounter         int
	tileCycleCounter     int
	isScanLineTransfered bool
	windowLine           int
	irqSignal            uint8
}

func NewGpu(screen *Screen, memory *memory.MMU) *GPU {
	fb := NewFrameBuffer()
	return &GPU{
		framebuffer: fb,
		screen:      screen,
		memory:      memory,
		mode:        vblankMode,

		line: 144,
	}
}

// Tick simulates gpu behaviour for a certain amount of clock cycles.
func (g *GPU) Tick(cycles int) {
	g.cycles += cycles

	switch g.mode {
	case hblankMode:
		if g.cycles < hblankCycles {
			break
		}

		g.cycles %= hblankCycles
		g.mode = oamReadMode

		g.line++
		g.memory.Write(addr.LY, byte(g.line))
		g.compareLYToLYC()

		if g.line == 144 {
			g.mode = vblankMode
			g.vBlankLine = 0
			g.modeCounterAux = g.cycles

			g.memory.RequestInterrupt(addr.VBlankInterrupt)

			// We're switching to VBlank Mode
			// if enabled on STAT, trigger the LCDStat interrupt
			g.irqSignal &= 0x9
			if g.memory.ReadBit(statVblankIrq, addr.STAT) {
				if bit.IsSet(0, g.irqSignal) && !bit.IsSet(3, g.irqSignal) {
					g.memory.RequestInterrupt(addr.LCDSTATInterrupt)
				}
				g.irqSignal = bit.Set(1, g.irqSignal)
			}
			g.irqSignal &= 0xE

			g.windowLine = 0
		} else {
			// We're switching to OAM Read Mode
			// if enabled on STAT, trigger the LCDStat interrupt
			g.irqSignal &= 0x9
			if g.memory.ReadBit(statOamIrq, addr.STAT) {
				if bit.IsSet(0, g.irqSignal) && !bit.IsSet(3, g.irqSignal) {
					g.memory.RequestInterrupt(addr.LCDSTATInterrupt)
				}
				g.irqSignal = bit.Set(2, g.irqSignal)
			}
			g.irqSignal &= 0xE

			g.updateMode()
		}

		break
	case vblankMode:
		g.modeCounterAux += cycles

		if g.cycles >= scanlineCycles {
			g.cycles %= scanlineCycles
			g.vBlankLine++

			if g.vBlankLine <= 9 {
				g.line++
				g.memory.Write(addr.LY, byte(g.line))
				g.compareLYToLYC()
			}
		}

		if g.cycles >= 4104 && g.modeCounterAux >= 4 && g.line == 153 {
			g.line = 0
			g.memory.Write(addr.LY, byte(g.line))
			g.compareLYToLYC()
		}

		if g.cycles >= 4560 {
			g.cycles %= 4560
			g.mode = oamReadMode
			g.updateMode()

			// We're switching to OAM Read Mode
			// if enabled on STAT, trigger the LCDStat interrupt
			g.irqSignal &= 0x7
			g.irqSignal &= 0xA
			if g.memory.ReadBit(statOamIrq, addr.STAT) {
				if bit.IsSet(0, g.irqSignal) && !bit.IsSet(3, g.irqSignal) {
					g.memory.RequestInterrupt(addr.LCDSTATInterrupt)
				}
				g.irqSignal = bit.Set(2, g.irqSignal)
			}
			g.irqSignal &= 0xD
		}

		break
	case oamReadMode:
		if g.cycles >= oamScanlineCycles {
			g.cycles %= oamScanlineCycles
			g.mode = vramReadMode
			g.updateMode()
			g.isScanLineTransfered = false
			g.irqSignal &= 0x8
		}
		break
	case vramReadMode:
		if g.pixelCounter < 160 {
			g.tileCycleCounter += cycles

			if g.readLCDCVariable(lcdDisplayEnable) == 1 {
				for g.tileCycleCounter >= 3 {
					g.drawBackground(g.line, g.pixelCounter, 4)

					g.pixelCounter += 4
					g.tileCycleCounter -= 3

					if g.pixelCounter >= 160 {
						break
					}
				}

			}
		}

		if g.cycles >= 160 && g.isScanLineTransfered {
			g.drawScanline(g.line)
			g.isScanLineTransfered = true
		}

		if g.cycles >= vramScanlineCycles {
			g.pixelCounter = 0
			g.cycles %= vramScanlineCycles
			g.mode = hblankMode
			g.tileCycleCounter = 0
			g.updateMode()

			// We're switching to HBlank Mode
			// if enabled on STAT, trigger the LCDStat interrupt
			g.irqSignal &= 0x8
			if g.memory.ReadBit(statHblankIrq, addr.STAT) {
				if !bit.IsSet(3, g.irqSignal) {
					g.memory.RequestInterrupt(addr.LCDSTATInterrupt)
				}
				g.irqSignal = bit.Set(0, g.irqSignal)
			}
		}
		break
	}
}

func (g *GPU) drawScanline(line int) {
	lcdEnabled := g.readLCDCVariable(lcdDisplayEnable) == 1
	if lcdEnabled {
		g.drawWindow(line)
		g.drawSprites(line)
		return
	}

	g.framebuffer.Clear()
}

func (g *GPU) drawBackground(line, pixel, count int) {
	// g.line, g.pixelCounter, 4
	startXOffset := pixel % 8
	endXOffset := startXOffset + count
	screenTile := pixel / 8
	lineWidth := line * width

	backgroundEnabled := g.readLCDCVariable(bgDisplay) == 1
	if !backgroundEnabled {
		// TODO: clear current line
		return
	}

	useTileSetZero := g.readLCDCVariable(bgWindowTileDataSelect) == 0
	useTileMapZero := g.readLCDCVariable(bgTileMapDisplaySelect) == 0

	// select the correct starting address based on which tileMap/tileSet is set
	tilesAddr := uint16(0x8000)
	if useTileSetZero {
		tilesAddr = 0x8800
	}

	tileMapAddr := 0x9C00
	if useTileMapZero {
		tileMapAddr = 0x9800
	}

	scrollX := g.memory.Read(addr.SCX)
	scrollY := g.memory.Read(addr.SCY)
	lineScrolled := line + int(scrollY)
	lineScrolled32 := (lineScrolled / 8) * 32
	tilePixelY := lineScrolled % 8
	tilePixelY2 := tilePixelY * 2

	for xOffset := startXOffset; xOffset < endXOffset; xOffset++ {
		screenPixelX := (screenTile * 8) + xOffset
		mapPixelX := screenPixelX + int(scrollX)
		mapTileX := mapPixelX / 8
		mapTileXOffset := mapPixelX % 8
		mapTileAddr := uint16(tileMapAddr + lineScrolled32 + mapTileX)

		mapTile := 0

		if useTileSetZero {
			offset := int8(g.memory.Read(mapTileAddr))
			mapTile = int(offset) + 128
		} else {
			mapTile = int(g.memory.Read(mapTileAddr))
		}

		mapTile16 := mapTile * 16
		tileAddr := tilesAddr + uint16(mapTile16) + uint16(tilePixelY2)

		low := g.memory.Read(tileAddr)
		high := g.memory.Read(tileAddr + 1)

		pixelIndex := uint8(7 - mapTileXOffset)
		// the pixel is the bitwise OR of the low/high bit at
		// the current X index (from 7 to 0)
		pixel := 0
		if bit.IsSet(pixelIndex, low) {
			pixel |= 1
		}
		if bit.IsSet(pixelIndex, high) {
			pixel |= 2
		}

		pixelPosition := lineWidth + screenPixelX

		palette := g.memory.Read(addr.BGP)
		color := (palette >> (pixel * 2)) & 0x03
		g.framebuffer.buffer[pixelPosition] = uint32(ByteToColor(color))
	}
}

func (g *GPU) drawWindow(line int) {
	if g.windowLine > 143 {
		return
	}

	windowEnabled := g.readLCDCVariable(windowDisplayEnable) == 1
	if !windowEnabled {
		return
	}

	wx := g.memory.Read(addr.WX) - 7
	wy := g.memory.Read(addr.WY)

	if wx > 159 {
		return
	}

	if wy > 143 || int(wy) > line {
		return
	}

	useTileSetZero := g.readLCDCVariable(bgWindowTileDataSelect) == 0
	useTileMapZero := g.readLCDCVariable(windowTileMapSelect) == 0

	// select the correct starting address based on which tileMap/tileSet is set
	tilesAddr := uint16(0x8000)
	if useTileSetZero {
		tilesAddr = 0x8800
	}

	tileMapAddr := 0x9C00
	if useTileMapZero {
		tileMapAddr = 0x9800
	}

	lineAdj := g.windowLine

	y32 := (lineAdj / 8) * 32
	pixelY := lineAdj & 8
	pixelY2 := pixelY * 2
	lineWidth := line * width

	for x := 0; x < 32; x++ {
		tileIndexAddr := uint16(tileMapAddr + y32 + x)
		tile := 0

		if useTileSetZero {
			offset := int8(g.memory.Read(tileIndexAddr))
			tile = int(offset) + 128
		} else {
			tile = int(g.memory.Read(tileIndexAddr))
		}

		xOffset := x * 8
		tile16 := tile * 16

		tileAddr := tilesAddr + uint16(tile16) + uint16(pixelY2)

		low := g.memory.Read(tileAddr)
		high := g.memory.Read(tileAddr + 1)

		for pixelX := 0; pixelX < 8; pixelX++ {
			bufferX := xOffset + pixelX + int(wx)

			if bufferX < 0 || bufferX > width {
				continue
			}

			// the pixel is the bitwise OR of the low/high bit at
			// the current X index (from 7 to 0)
			pixel := 0
			if bit.IsSet(7-uint8(pixelX), low) {
				pixel |= 1
			}
			if bit.IsSet(7-uint8(pixelX), high) {
				pixel |= 2
			}

			position := lineWidth + bufferX

			palette := g.memory.Read(addr.BGP)
			color := (palette >> (pixel * 2)) & 0x03
			g.framebuffer.buffer[position] = uint32(ByteToColor(color))
		}
	}
	g.windowLine++
}

func (g *GPU) drawSprites(line int) {
	// TODO: implement this
}

// LCD Stat (Status) Register bit values
// Bit 7 - unused
// Bit 6 - Interrupt based on LYC to LY comparison (based on bit 2)
// Bit 5 - Interrupt when Mode 10 (oamReadMode)
// Bit 4 - Interrupt when Mode 01 (vblankMode)
// Bit 3 - Interrupt when Mode 00 (hblankMode)
// Bit 2 - condition for triggering LYC/LY (0=LYC != LY, 1=LYC == LY)
// Bit 1,0 - represents the current GPU mode
//         - 00 -> hblankMode
//         - 01 -> vblankMode
//         - 10 -> oamReadMode
//         - 11 -> vramReadMode

type statFlag uint8

const (
	statLycIrq       statFlag = 6
	statOamIrq                = 5
	statVblankIrq             = 4
	statHblankIrq             = 3
	statLycCondition          = 2
	statModeHigh              = 1
	statModeLow               = 0
)

// LCDC (LCD Control) Register bit values
// Bit 7 - LCD Display Enable (0=Off, 1=On)
// Bit 6 - Window Tile Map Display Select (0=9800-9BFF, 1=9C00-9FFF)
// Bit 5 - Window Display Enable (0=Off, 1=On)
// Bit 4 - BG & Window Tile Data Select (0=8800-97FF, 1=8000-8FFF)
// Bit 3 - BG Tile Map Display Select (0=9800-9BFF, 1=9C00-9FFF)
// Bit 2 - OBJ (Sprite) Size (0=8x8, 1=8x16)
// Bit 1 - OBJ (Sprite) Display Enable (0=Off, 1=On)
// Bit 0 - BG Display (0=Off, 1=On)

type lcdcFlag uint8

const (
	lcdDisplayEnable       lcdcFlag = 7
	windowTileMapSelect             = 6
	windowDisplayEnable             = 5
	bgWindowTileDataSelect          = 4
	bgTileMapDisplaySelect          = 3
	spriteSize                      = 2
	spriteDisplayEnable             = 1
	bgDisplay                       = 0
)

func (g *GPU) readLCDCVariable(flag lcdcFlag) byte {
	if bit.IsSet(uint8(flag), g.memory.Read(addr.LCDC)) {
		return 1
	}

	return 0
}

func (g *GPU) setLCDCVariable(flag lcdcFlag, shouldSet bool) {
	lcdcRegister := g.memory.Read(addr.LCDC)

	if shouldSet {
		lcdcRegister = bit.Set(uint8(flag), lcdcRegister)
	} else {
		lcdcRegister = bit.Clear(uint8(flag), lcdcRegister)
	}
}

func (g *GPU) compareLYToLYC() {
	ly := g.memory.Read(addr.LY)
	lyc := g.memory.Read(addr.LYC)
	stat := g.memory.Read(addr.STAT)

	if ly == lyc {
		stat = bit.Set(statLycCondition, stat)
		if bit.IsSet(uint8(statLycIrq), stat) {
			if g.irqSignal == 0 {
				g.memory.RequestInterrupt(addr.LCDSTATInterrupt)
			}
			g.irqSignal = bit.Set(3, g.irqSignal)
		}
	} else {
		stat = bit.Reset(statLycCondition, stat)
		g.irqSignal = bit.Reset(3, g.irqSignal)
	}

	g.memory.Write(addr.STAT, stat)
}

// updateMode sets the two bits (1,0) in the STAT register
// according to the current GPU mode.
func (g *GPU) updateMode() {
	stat := g.memory.Read(addr.STAT)
	stat = stat&0xFC | byte(g.mode)
	g.memory.Write(addr.STAT, stat)
}
<|endoftext|>


<commit_before>package parser

import (
	"fmt"
	"net/http"

	"github.com/go-playground/validator"
	"github.com/labstack/echo"
	"github.com/leebenson/conform"
	"github.com/microcosm-cc/bluemonday"
)

type (
	// Request holds the mapped fields from the request's JSON body
	Request struct {
		Query         string                 `json:"query" validate:"required,gte=10,lte=5000" conform:"trim"`
		OperationName string                 `json:"operationName" validate:"required,gte=3,lte=15,alphanum,excludesall= " conform:"trim,lower"`
		Variables     map[string]interface{} `json:"variables" validate:"required,gte=1,lte=15,dive,required"`
	}
)

// Parse parses, scrubs and escapes a request's JSON body and maps it to a struct
func Parse(context echo.Context) (*Request, error) {
	request := new(Request)

	conform.Strings(request)
	escape(request)

	if err := bind(request, context); err != nil {
		return request, err
	}

	if err := validate(request, context); err != nil {
		return request, err
	}

	return request, nil
}

func bind(request *Request, context echo.Context) error {
	if err := context.Bind(request); err != nil {
		errorDescription := err.Error()
		errorMessage := fmt.Sprintf("Error parsing request: %s", errorDescription)
		errorCode := http.StatusBadRequest

		if httpError, ok := err.(*echo.HTTPError); ok {
			if value, isString := httpError.Message.(string); isString {
				errorMessage = value
				errorCode = httpError.Code
			}
		}

		context.Logger().Error("Error binding request: ", errorDescription)

		return echo.NewHTTPError(errorCode, []string{errorMessage})
	}

	return nil
}

func validate(request *Request, context echo.Context) error {
	if errs := context.Validate(request); errs != nil {
		var errorList []string
		var errorMessage = "Error validating request: "

		if _, ok := errs.(*validator.InvalidValidationError); ok {
			context.Logger().Error(errorMessage, errs.Error())

			return echo.NewHTTPError(http.StatusUnprocessableEntity, []string{errs.Error()})
		}

		for _, err := range errs.(validator.ValidationErrors) {
			errorDescription := err.(error).Error()
			errorList = append(errorList, errorDescription)

			context.Logger().Error(errorMessage, errorDescription)
		}

		return echo.NewHTTPError(http.StatusUnprocessableEntity, errorList)
	}

	return nil
}

func escape(request *Request) {
	sanitizer := bluemonday.StrictPolicy()

	request.Query = sanitizer.Sanitize(request.Query)
	request.OperationName = sanitizer.Sanitize(request.OperationName)

	for key, value := range request.Variables {
		switch actualValue := value.(type) {
		case int:
			break
		case float64:
			break
		case string:
			request.Variables[key] = sanitizer.Sanitize(actualValue)
		case bool:
			break
		default:
			break
		}
	}
}
<commit_msg>Added TODO coment<commit_after>package parser

import (
	"fmt"
	"net/http"

	"github.com/go-playground/validator"
	"github.com/labstack/echo"
	"github.com/leebenson/conform"
	"github.com/microcosm-cc/bluemonday"
)

type (
	// Request holds the mapped fields from the request's JSON body
	Request struct {
		Query         string                 `json:"query" validate:"required,gte=10,lte=5000" conform:"trim"`
		OperationName string                 `json:"operationName" validate:"required,gte=3,lte=15,alphanum,excludesall= " conform:"trim,lower"`
		Variables     map[string]interface{} `json:"variables" validate:"required,gte=1,lte=15,dive,required"`
	}
)

// TODO: Consider extracting the common parts into its own package

// Parse parses, scrubs and escapes a request's JSON body and maps it to a struct
func Parse(context echo.Context) (*Request, error) {
	request := new(Request)

	conform.Strings(request)
	escape(request)

	if err := bind(request, context); err != nil {
		return request, err
	}

	if err := validate(request, context); err != nil {
		return request, err
	}

	return request, nil
}

func bind(request *Request, context echo.Context) error {
	if err := context.Bind(request); err != nil {
		errorDescription := err.Error()
		errorMessage := fmt.Sprintf("Error parsing request: %s", errorDescription)
		errorCode := http.StatusBadRequest

		if httpError, ok := err.(*echo.HTTPError); ok {
			if value, isString := httpError.Message.(string); isString {
				errorMessage = value
				errorCode = httpError.Code
			}
		}

		context.Logger().Error("Error binding request: ", errorDescription)

		return echo.NewHTTPError(errorCode, []string{errorMessage})
	}

	return nil
}

func validate(request *Request, context echo.Context) error {
	if errs := context.Validate(request); errs != nil {
		var errorList []string
		var errorMessage = "Error validating request: "

		if _, ok := errs.(*validator.InvalidValidationError); ok {
			context.Logger().Error(errorMessage, errs.Error())

			return echo.NewHTTPError(http.StatusUnprocessableEntity, []string{errs.Error()})
		}

		for _, err := range errs.(validator.ValidationErrors) {
			errorDescription := err.(error).Error()
			errorList = append(errorList, errorDescription)

			context.Logger().Error(errorMessage, errorDescription)
		}

		return echo.NewHTTPError(http.StatusUnprocessableEntity, errorList)
	}

	return nil
}

func escape(request *Request) {
	sanitizer := bluemonday.StrictPolicy()

	request.Query = sanitizer.Sanitize(request.Query)
	request.OperationName = sanitizer.Sanitize(request.OperationName)

	for key, value := range request.Variables {
		switch actualValue := value.(type) {
		case int:
			break
		case float64:
			break
		case string:
			request.Variables[key] = sanitizer.Sanitize(actualValue)
		case bool:
			break
		default:
			break
		}
	}
}
<|endoftext|>


<commit_before>package config

import (
	"os"
	"path/filepath"

	"github.com/admpub/godotenv"
	"github.com/admpub/log"
	"github.com/admpub/nging/v3/application/library/common"
	"github.com/webx-top/com"
	"github.com/webx-top/echo"
)

func findEnvFile() []string {
	var envFiles []string
	envFile := filepath.Join(echo.Wd(), `.env`)
	if fi, err := os.Stat(envFile); err == nil && !fi.IsDir() {
		envFiles = append(envFiles, envFile)
	}
	return envFiles
}

func (c *CLIConfig) InitEnviron(needFindEnvFile ...bool) (err error) {
	if len(needFindEnvFile) > 0 && needFindEnvFile[0] {
		c.envFiles = findEnvFile()
	}
	if c.envVars != nil {
		for k := range c.envVars {
			os.Unsetenv(k)
		}
	}
	if len(c.envFiles) > 0 {
		log.Infof(`Loading env file: %#v`, c.envFiles)
		c.envVars, err = godotenv.Read(c.envFiles...)
		if err != nil {
			return
		}
		for k, v := range c.envVars {
			log.Infof(`Set env var: %s`, k)
			os.Setenv(k, v)
		}
	}
	return
}

func (c *CLIConfig) WatchEnvConfig() {
	if c.envMonitor != nil {
		c.envMonitor.Close()
		c.envMonitor = nil
	}
	if len(c.envFiles) == 0 {
		return
	}
	c.envMonitor = &com.MonitorEvent{
		Modify: func(file string) {
			log.Info(`Start reloading env file: ` + file)
			err := c.InitEnviron()
			if err == nil {
				log.Info(`Succcessfully reload the env file: ` + file)
				return
			}
			if err == common.ErrIgnoreConfigChange {
				log.Info(`No need to reload the env file: ` + file)
				return
			}
			log.Error(err)
		},
	}
	for _, envFile := range c.envFiles {
		err := c.envMonitor.AddFile(envFile)
		if err != nil {
			log.Error(err)
		}
	}
	c.envMonitor.Watch()
}
<commit_msg>improved<commit_after>package config

import (
	"os"
	"path/filepath"

	"github.com/admpub/godotenv"
	"github.com/admpub/log"
	"github.com/admpub/nging/v3/application/library/common"
	"github.com/webx-top/com"
	"github.com/webx-top/echo"
)

func findEnvFile() []string {
	var envFiles []string
	envFile := filepath.Join(echo.Wd(), `.env`)
	if fi, err := os.Stat(envFile); err == nil && !fi.IsDir() {
		envFiles = append(envFiles, envFile)
	}
	return envFiles
}

func (c *CLIConfig) InitEnviron(needFindEnvFile ...bool) (err error) {
	if len(needFindEnvFile) > 0 && needFindEnvFile[0] {
		c.envFiles = findEnvFile()
	}
	var newEnvVars map[string]string
	if len(c.envFiles) > 0 {
		log.Infof(`Loading env file: %#v`, c.envFiles)
		newEnvVars, err = godotenv.Read(c.envFiles...)
		if err != nil {
			return
		}
	}
	if c.envVars != nil {
		for k := range c.envVars {
			os.Unsetenv(k)
		}
	}
	c.envVars = newEnvVars
	if c.envVars != nil {
		for k, v := range c.envVars {
			log.Infof(`Set env var: %s`, k)
			os.Setenv(k, v)
		}
	}
	return
}

func (c *CLIConfig) WatchEnvConfig() {
	if c.envMonitor != nil {
		c.envMonitor.Close()
		c.envMonitor = nil
	}
	if len(c.envFiles) == 0 {
		return
	}
	c.envMonitor = &com.MonitorEvent{
		Modify: func(file string) {
			log.Info(`Start reloading env file: ` + file)
			err := c.InitEnviron()
			if err == nil {
				log.Info(`Succcessfully reload the env file: ` + file)
				return
			}
			if err == common.ErrIgnoreConfigChange {
				log.Info(`No need to reload the env file: ` + file)
				return
			}
			log.Error(err)
		},
	}
	for _, envFile := range c.envFiles {
		err := c.envMonitor.AddFile(envFile)
		if err != nil {
			log.Error(err)
		}
	}
	c.envMonitor.Watch()
}
<|endoftext|>


<commit_before>package twitter

import (
	"context"
	"encoding/json"
	"net/url"
	"strconv"
)

// MentionsTimelineParams represents the query parameters for a
// /statuses/mentions_timeline.json request.
type MentionsTimelineParams struct {
	Count              int    `json:"count"`
	SinceID            string `json:"since_id"`
	MaxID              string `json:"max_id"`
	TrimUser           bool   `json:"trim_user"`
	ContributorDetails bool   `json:"contributor_details"`
	ExcludeEntities    bool   `json:"exclude_entities"`
}

// MentionsTimeline calls the Twitter /statuses/mentions_timeline.json endpoint.
func (c *Client) MentionsTimeline(ctx context.Context, params MentionsTimelineParams) (*TweetsResponse, error) {
	values := mentionsTimelineToQuery(params)
	resp, err := c.do(ctx, "GET", "https://api.twitter.com/1.1/statuses/mentions_timeline.json", values)
	if err != nil {
		return nil, err
	}
	defer resp.Body.Close()
	var tweets []Tweet
	err = json.NewDecoder(resp.Body).Decode(resp.Body)
	if err != nil {
		return nil, err
	}
	return &TweetsResponse{
		Tweets:    tweets,
		RateLimit: getRateLimit(resp.Header),
	}, nil
}

func mentionsTimelineToQuery(params MentionsTimelineParams) url.Values {
	values := url.Values{}
	if params.Count > 0 {
		values.Set("count", strconv.Itoa(params.Count))
	}
	if params.SinceID != "" {
		values.Set("since_id", params.SinceID)
	}
	if params.MaxID != "" {
		values.Set("max_id", params.MaxID)
	}
	if params.TrimUser {
		values.Set("trim_user", "true")
	}
	if params.ContributorDetails {
		values.Set("contributor_details", "true")
	}
	if params.ExcludeEntities {
		values.Set("include_entities", "false")
	}
	return values
}

// UserTimelineParams represents the query parameters for a
// /statuses/user_timeline.json request.
type UserTimelineParams struct {
	UserID             string `json:"user_id"`
	ScreenName         string `json:"screen_name"`
	SinceID            string `json:"since_id"`
	Count              int    `json:"count"`
	MaxID              string `json:"max_id"`
	TrimUser           bool   `json:"trim_user"`
	ExcludeReplies     bool   `json:"exclude_replies"`
	ContributorDetails bool   `json:"contributor_details"`
	ExcludeRTS         bool   `json:"exclude_rts"`
}

// UserTimeline calls the Twitter /statuses/user_timeline.json endpoint.
func (c *Client) UserTimeline(ctx context.Context, params UserTimelineParams) (*TweetsResponse, error) {
	values := userTimelineToQuery(params)
	resp, err := c.do(ctx, "GET", "https://api.twitter.com/1.1/statuses/user_timeline.json", values)
	if err != nil {
		return nil, err
	}
	defer resp.Body.Close()
	var tweets []Tweet
	err = json.NewDecoder(resp.Body).Decode(resp.Body)
	if err != nil {
		return nil, err
	}
	return &TweetsResponse{
		Tweets:    tweets,
		RateLimit: getRateLimit(resp.Header),
	}, nil
}

func userTimelineToQuery(params UserTimelineParams) url.Values {
	values := url.Values{}
	if params.UserID != "" {
		values.Set("user_id", params.UserID)
	}
	if params.ScreenName != "" {
		values.Set("screen_name", params.ScreenName)
	}
	if params.SinceID != "" {
		values.Set("since_id", params.SinceID)
	}
	if params.Count > 0 {
		values.Set("count", strconv.Itoa(params.Count))
	}
	if params.MaxID != "" {
		values.Set("max_id", params.MaxID)
	}
	if params.TrimUser {
		values.Set("trim_user", "true")
	}
	if params.ExcludeReplies {
		values.Set("exclude_replies", "true")
	}
	if params.ContributorDetails {
		values.Set("contributor_details", "true")
	}
	if params.ExcludeRTS {
		values.Set("include_rts", "false")
	}
	return values
}

// HomeTimelineParams represents the query parameters for a
// /statuses/home_timeline.json request.
type HomeTimelineParams struct {
	Count              int    `json:"count"`
	SinceID            string `json:"since_id"`
	MaxID              string `json:"max_id"`
	TrimUser           bool   `json:"trim_user"`
	ExcludeReplies     bool   `json:"exclude_replies"`
	ContributorDetails bool   `json:"contributor_details"`
	ExcludeEntities    bool   `json:"exclude_entities"`
}

// HomeTimeline calls the Twitter /statuses/home_timeline.json endpoint.
func (c *Client) HomeTimeline(ctx context.Context, params HomeTimelineParams) (*TweetsResponse, error) {
	values := homeTimelineToQuery(params)
	resp, err := c.do(ctx, "GET", "https://api.twitter.com/1.1/statuses/home_timeline.json", values)
	if err != nil {
		return nil, err
	}
	defer resp.Body.Close()
	var tweets []Tweet
	err = json.NewDecoder(resp.Body).Decode(resp.Body)
	if err != nil {
		return nil, err
	}
	return &TweetsResponse{
		Tweets:    tweets,
		RateLimit: getRateLimit(resp.Header),
	}, nil
}

func homeTimelineToQuery(params HomeTimelineParams) url.Values {
	values := url.Values{}
	if params.Count > 0 {
		values.Set("count", strconv.Itoa(params.Count))
	}
	if params.SinceID != "" {
		values.Set("since_id", params.SinceID)
	}
	if params.MaxID != "" {
		values.Set("max_id", params.MaxID)
	}
	if params.TrimUser {
		values.Set("trim_user", "true")
	}
	if params.ExcludeReplies {
		values.Set("exclude_replies", "true")
	}
	if params.ContributorDetails {
		values.Set("contributor_details", "true")
	}
	if params.ExcludeEntities {
		values.Set("include_entities", "false")
	}
	return values
}

// RetweetsOfMeParams represents the query parameters for a
// /statuses/retweets_of_me.json request.
type RetweetsOfMeParams struct {
	Count               int    `json:"count"`
	SinceID             string `json:"since_id"`
	MaxID               string `json:"max_id"`
	TrimUser            bool   `json:"trim_user"`
	ExcludeEntities     bool   `json:"exclude_entities"`
	ExcludeUserEntities bool   `json:"exclude_user_entities"`
}

// RetweetsOfMe calls the Twitter /statuses/retweets_of_me.json endpoint.
func (c *Client) RetweetsOfMe(ctx context.Context, params RetweetsOfMeParams) (*TweetsResponse, error) {
	values := retweetsOfMeToQuery(params)
	resp, err := c.do(ctx, "GET", "https://api.twitter.com/1.1/statuses/retweets_of_me.json", values)
	if err != nil {
		return nil, err
	}
	defer resp.Body.Close()
	var tweets []Tweet
	err = json.NewDecoder(resp.Body).Decode(resp.Body)
	if err != nil {
		return nil, err
	}
	return &TweetsResponse{
		Tweets:    tweets,
		RateLimit: getRateLimit(resp.Header),
	}, nil
}

func retweetsOfMeToQuery(params RetweetsOfMeParams) url.Values {
	values := url.Values{}
	if params.Count > 0 {
		values.Set("count", strconv.Itoa(params.Count))
	}
	if params.SinceID != "" {
		values.Set("since_id", params.SinceID)
	}
	if params.MaxID != "" {
		values.Set("max_id", params.MaxID)
	}
	if params.TrimUser {
		values.Set("trim_user", "true")
	}
	if params.ExcludeEntities {
		values.Set("include_entities", "false")
	}
	if params.ExcludeUserEntities {
		values.Set("include_user_entities", "false")
	}
	return values
}

// RetweetsOfTweetParams represents the query parameters for a
// /statuses/retweets/:id.json request.
type RetweetsOfTweetParams struct {
	ID       string `json:"id"`
	Count    int    `json:"count"`
	TrimUser bool   `json:"trim_user"`
}

// RetweetsOfTweet calls the Twitter /statuses/retweets/:id.json endpoint.
func (c *Client) RetweetsOfTweet(ctx context.Context, params RetweetsOfTweetParams) (*TweetsResponse, error) {
	values := retweetsOfTweetToQuery(params)
	urlStr := "https://api.twitter.com/1.1/statuses/retweets/" + params.ID + ".json"
	resp, err := c.do(ctx, "GET", urlStr, values)
	if err != nil {
		return nil, err
	}
	defer resp.Body.Close()
	var tweets []Tweet
	err = json.NewDecoder(resp.Body).Decode(resp.Body)
	if err != nil {
		return nil, err
	}
	return &TweetsResponse{
		Tweets:    tweets,
		RateLimit: getRateLimit(resp.Header),
	}, nil
}

func retweetsOfTweetToQuery(params RetweetsOfTweetParams) url.Values {
	values := url.Values{}
	if params.Count > 0 {
		values.Set("count", strconv.Itoa(params.Count))
	}
	if params.TrimUser {
		values.Set("trim_user", "true")
	}
	return values
}
<commit_msg>Add show tweet endpoint<commit_after>package twitter

import (
	"context"
	"encoding/json"
	"net/url"
	"strconv"
)

// MentionsTimelineParams represents the query parameters for a
// /statuses/mentions_timeline.json request.
type MentionsTimelineParams struct {
	Count              int    `json:"count"`
	SinceID            string `json:"since_id"`
	MaxID              string `json:"max_id"`
	TrimUser           bool   `json:"trim_user"`
	ContributorDetails bool   `json:"contributor_details"`
	ExcludeEntities    bool   `json:"exclude_entities"`
}

// MentionsTimeline calls the Twitter /statuses/mentions_timeline.json endpoint.
func (c *Client) MentionsTimeline(ctx context.Context, params MentionsTimelineParams) (*TweetsResponse, error) {
	values := mentionsTimelineToQuery(params)
	resp, err := c.do(ctx, "GET", "https://api.twitter.com/1.1/statuses/mentions_timeline.json", values)
	if err != nil {
		return nil, err
	}
	defer resp.Body.Close()
	var tweets []Tweet
	err = json.NewDecoder(resp.Body).Decode(resp.Body)
	if err != nil {
		return nil, err
	}
	return &TweetsResponse{
		Tweets:    tweets,
		RateLimit: getRateLimit(resp.Header),
	}, nil
}

func mentionsTimelineToQuery(params MentionsTimelineParams) url.Values {
	values := url.Values{}
	if params.Count > 0 {
		values.Set("count", strconv.Itoa(params.Count))
	}
	if params.SinceID != "" {
		values.Set("since_id", params.SinceID)
	}
	if params.MaxID != "" {
		values.Set("max_id", params.MaxID)
	}
	if params.TrimUser {
		values.Set("trim_user", "true")
	}
	if params.ContributorDetails {
		values.Set("contributor_details", "true")
	}
	if params.ExcludeEntities {
		values.Set("include_entities", "false")
	}
	return values
}

// UserTimelineParams represents the query parameters for a
// /statuses/user_timeline.json request.
type UserTimelineParams struct {
	UserID             string `json:"user_id"`
	ScreenName         string `json:"screen_name"`
	SinceID            string `json:"since_id"`
	Count              int    `json:"count"`
	MaxID              string `json:"max_id"`
	TrimUser           bool   `json:"trim_user"`
	ExcludeReplies     bool   `json:"exclude_replies"`
	ContributorDetails bool   `json:"contributor_details"`
	ExcludeRTS         bool   `json:"exclude_rts"`
}

// UserTimeline calls the Twitter /statuses/user_timeline.json endpoint.
func (c *Client) UserTimeline(ctx context.Context, params UserTimelineParams) (*TweetsResponse, error) {
	values := userTimelineToQuery(params)
	resp, err := c.do(ctx, "GET", "https://api.twitter.com/1.1/statuses/user_timeline.json", values)
	if err != nil {
		return nil, err
	}
	defer resp.Body.Close()
	var tweets []Tweet
	err = json.NewDecoder(resp.Body).Decode(resp.Body)
	if err != nil {
		return nil, err
	}
	return &TweetsResponse{
		Tweets:    tweets,
		RateLimit: getRateLimit(resp.Header),
	}, nil
}

func userTimelineToQuery(params UserTimelineParams) url.Values {
	values := url.Values{}
	if params.UserID != "" {
		values.Set("user_id", params.UserID)
	}
	if params.ScreenName != "" {
		values.Set("screen_name", params.ScreenName)
	}
	if params.SinceID != "" {
		values.Set("since_id", params.SinceID)
	}
	if params.Count > 0 {
		values.Set("count", strconv.Itoa(params.Count))
	}
	if params.MaxID != "" {
		values.Set("max_id", params.MaxID)
	}
	if params.TrimUser {
		values.Set("trim_user", "true")
	}
	if params.ExcludeReplies {
		values.Set("exclude_replies", "true")
	}
	if params.ContributorDetails {
		values.Set("contributor_details", "true")
	}
	if params.ExcludeRTS {
		values.Set("include_rts", "false")
	}
	return values
}

// HomeTimelineParams represents the query parameters for a
// /statuses/home_timeline.json request.
type HomeTimelineParams struct {
	Count              int    `json:"count"`
	SinceID            string `json:"since_id"`
	MaxID              string `json:"max_id"`
	TrimUser           bool   `json:"trim_user"`
	ExcludeReplies     bool   `json:"exclude_replies"`
	ContributorDetails bool   `json:"contributor_details"`
	ExcludeEntities    bool   `json:"exclude_entities"`
}

// HomeTimeline calls the Twitter /statuses/home_timeline.json endpoint.
func (c *Client) HomeTimeline(ctx context.Context, params HomeTimelineParams) (*TweetsResponse, error) {
	values := homeTimelineToQuery(params)
	resp, err := c.do(ctx, "GET", "https://api.twitter.com/1.1/statuses/home_timeline.json", values)
	if err != nil {
		return nil, err
	}
	defer resp.Body.Close()
	var tweets []Tweet
	err = json.NewDecoder(resp.Body).Decode(resp.Body)
	if err != nil {
		return nil, err
	}
	return &TweetsResponse{
		Tweets:    tweets,
		RateLimit: getRateLimit(resp.Header),
	}, nil
}

func homeTimelineToQuery(params HomeTimelineParams) url.Values {
	values := url.Values{}
	if params.Count > 0 {
		values.Set("count", strconv.Itoa(params.Count))
	}
	if params.SinceID != "" {
		values.Set("since_id", params.SinceID)
	}
	if params.MaxID != "" {
		values.Set("max_id", params.MaxID)
	}
	if params.TrimUser {
		values.Set("trim_user", "true")
	}
	if params.ExcludeReplies {
		values.Set("exclude_replies", "true")
	}
	if params.ContributorDetails {
		values.Set("contributor_details", "true")
	}
	if params.ExcludeEntities {
		values.Set("include_entities", "false")
	}
	return values
}

// RetweetsOfMeParams represents the query parameters for a
// /statuses/retweets_of_me.json request.
type RetweetsOfMeParams struct {
	Count               int    `json:"count"`
	SinceID             string `json:"since_id"`
	MaxID               string `json:"max_id"`
	TrimUser            bool   `json:"trim_user"`
	ExcludeEntities     bool   `json:"exclude_entities"`
	ExcludeUserEntities bool   `json:"exclude_user_entities"`
}

// RetweetsOfMe calls the Twitter /statuses/retweets_of_me.json endpoint.
func (c *Client) RetweetsOfMe(ctx context.Context, params RetweetsOfMeParams) (*TweetsResponse, error) {
	values := retweetsOfMeToQuery(params)
	resp, err := c.do(ctx, "GET", "https://api.twitter.com/1.1/statuses/retweets_of_me.json", values)
	if err != nil {
		return nil, err
	}
	defer resp.Body.Close()
	var tweets []Tweet
	err = json.NewDecoder(resp.Body).Decode(resp.Body)
	if err != nil {
		return nil, err
	}
	return &TweetsResponse{
		Tweets:    tweets,
		RateLimit: getRateLimit(resp.Header),
	}, nil
}

func retweetsOfMeToQuery(params RetweetsOfMeParams) url.Values {
	values := url.Values{}
	if params.Count > 0 {
		values.Set("count", strconv.Itoa(params.Count))
	}
	if params.SinceID != "" {
		values.Set("since_id", params.SinceID)
	}
	if params.MaxID != "" {
		values.Set("max_id", params.MaxID)
	}
	if params.TrimUser {
		values.Set("trim_user", "true")
	}
	if params.ExcludeEntities {
		values.Set("include_entities", "false")
	}
	if params.ExcludeUserEntities {
		values.Set("include_user_entities", "false")
	}
	return values
}

// RetweetsOfTweetParams represents the query parameters for a
// /statuses/retweets/:id.json request.
type RetweetsOfTweetParams struct {
	ID       string `json:"id"`
	Count    int    `json:"count"`
	TrimUser bool   `json:"trim_user"`
}

// RetweetsOfTweet calls the Twitter /statuses/retweets/:id.json endpoint.
func (c *Client) RetweetsOfTweet(ctx context.Context, params RetweetsOfTweetParams) (*TweetsResponse, error) {
	values := retweetsOfTweetToQuery(params)
	urlStr := "https://api.twitter.com/1.1/statuses/retweets/" + params.ID + ".json"
	resp, err := c.do(ctx, "GET", urlStr, values)
	if err != nil {
		return nil, err
	}
	defer resp.Body.Close()
	var tweets []Tweet
	err = json.NewDecoder(resp.Body).Decode(resp.Body)
	if err != nil {
		return nil, err
	}
	return &TweetsResponse{
		Tweets:    tweets,
		RateLimit: getRateLimit(resp.Header),
	}, nil
}

func retweetsOfTweetToQuery(params RetweetsOfTweetParams) url.Values {
	values := url.Values{}
	if params.Count > 0 {
		values.Set("count", strconv.Itoa(params.Count))
	}
	if params.TrimUser {
		values.Set("trim_user", "true")
	}
	return values
}

// GetTweetParams represents the query parameters for a
// /statuses/show/:id.json request.
type GetTweetParams struct {
	ID               string `json:"id"`
	TrimUser         bool   `json:"trim_user"`
	IncludeMyRetweet bool   `json:"include_my_retweet"`
	ExcludeEntities  bool   `json:"exclude_entities"`
}

// GetTweet calls the Twitter /statuses/show/:id.json endpoint.
func (c *Client) GetTweet(ctx context.Context, params GetTweetParams) (*TweetsResponse, error) {
	values := getTweetToQuery(params)
	urlStr := "https://api.twitter.com/1.1/statuses/show.json"
	resp, err := c.do(ctx, "GET", urlStr, values)
	if err != nil {
		return nil, err
	}
	defer resp.Body.Close()
	var tweets []Tweet
	err = json.NewDecoder(resp.Body).Decode(resp.Body)
	if err != nil {
		return nil, err
	}
	return &TweetsResponse{
		Tweets:    tweets,
		RateLimit: getRateLimit(resp.Header),
	}, nil
}

func getTweetToQuery(params GetTweetParams) url.Values {
	values := url.Values{}
	values.Set("id", params.ID)
	if params.TrimUser {
		values.Set("trim_user", "true")
	}
	if params.IncludeMyRetweet {
		values.Set("include_my_retweet", "true")
	}
	if params.ExcludeEntities {
		values.Set("include_entities", "false")
	}
	return values
}
<|endoftext|>


<commit_before>package datastore

import (
	"log"
	"strconv"
	"time"

	"github.com/swagchat/chat-api/models"
	"github.com/swagchat/chat-api/utils"
)

func RdbCreateUserStore() {
	master := RdbStoreInstance().master()
	tableMap := master.AddTableWithName(models.User{}, TABLE_NAME_USER)
	tableMap.SetKeys(true, "id")
	for _, columnMap := range tableMap.Columns {
		if columnMap.ColumnName == "user_id" {
			columnMap.SetUnique(true)
		}
	}
	if err := master.CreateTablesIfNotExists(); err != nil {
		log.Println(err)
	}
}

func RdbInsertUser(user *models.User) StoreResult {
	master := RdbStoreInstance().master()
	result := StoreResult{}
	trans, err := master.Begin()
	user.AccessToken = utils.GenerateToken(utils.TokenLength)
	if err = trans.Insert(user); err != nil {
		result.ProblemDetail = createProblemDetail("An error occurred while creating user item.", err)
		if err := trans.Rollback(); err != nil {
			result.ProblemDetail = createProblemDetail("An error occurred while rollback creating user item.", err)
		}
		return result
	}

	if result.ProblemDetail == nil && user.Devices != nil {
		for _, device := range user.Devices {
			if err := trans.Insert(device); err != nil {
				result.ProblemDetail = createProblemDetail("An error occurred while creating device item.", err)
				if err := trans.Rollback(); err != nil {
					result.ProblemDetail = createProblemDetail("An error occurred while rollback creating user item.", err)
				}
				return result
			}
		}
	}

	if result.ProblemDetail == nil {
		if err := trans.Commit(); err != nil {
			result.ProblemDetail = createProblemDetail("An error occurred while commit creating user item.", err)
		}
	}
	result.Data = user
	return result
}

func RdbSelectUser(userId string, isWithRooms, isWithDevices, isWithBlocks bool) StoreResult {
	slave := RdbStoreInstance().replica()
	result := StoreResult{}
	var users []*models.User
	query := utils.AppendStrings("SELECT * FROM ", TABLE_NAME_USER, " WHERE user_id=:userId AND deleted=0;")
	params := map[string]interface{}{"userId": userId}
	if _, err := slave.Select(&users, query, params); err != nil {
		result.ProblemDetail = createProblemDetail("An error occurred while getting user item.", err)
		return result
	}
	var user *models.User
	if len(users) == 1 {
		user = users[0]
		if isWithRooms {
			var rooms []*models.RoomForUser
			query := utils.AppendStrings("SELECT ",
				"r.room_id, ",
				"r.user_id, ",
				"r.name, ",
				"r.picture_url, ",
				"r.information_url, ",
				"r.meta_data, ",
				"r.type, ",
				"r.last_message, ",
				"r.last_message_updated, ",
				"r.is_can_left, ",
				"r.created, ",
				"r.modified, ",
				"ru.unread_count AS ru_unread_count, ",
				"ru.meta_data AS ru_meta_data, ",
				"ru.created AS ru_created, ",
				"ru.modified AS ru_modified ",
				"FROM ", TABLE_NAME_ROOM_USER, " AS ru ",
				"LEFT JOIN ", TABLE_NAME_ROOM, " AS r ON ru.room_id=r.room_id ",
				"WHERE ru.user_id=:userId AND r.deleted=0 ",
				"ORDER BY r.last_message_updated DESC;")
			params := map[string]interface{}{"userId": userId}
			_, err := slave.Select(&rooms, query, params)
			if err != nil {
				result.ProblemDetail = createProblemDetail("An error occurred while getting user's rooms.", err)
				return result
			}

			var userMinis []*models.UserMini
			query = utils.AppendStrings("SELECT ",
				"r.room_id, ",
				"u.user_id, ",
				"u.name, ",
				"u.picture_url, ",
				"u.is_show_users ",
				"FROM ", TABLE_NAME_ROOM_USER, " AS ru ",
				"LEFT JOIN ", TABLE_NAME_ROOM, " AS r ON ru.room_id=r.room_id ",
				"LEFT JOIN ", TABLE_NAME_USER, " AS u ON ru.user_id=u.user_id ",
				"WHERE r.room_id IN ( ",
				"SELECT room_id ",
				"FROM ", TABLE_NAME_ROOM_USER, " ",
				"WHERE user_id=:userId ",
				") ",
				"ORDER BY ru.room_id",
			)
			params = map[string]interface{}{"userId": userId}
			_, err = slave.Select(&userMinis, query, params)
			if err != nil {
				result.ProblemDetail = createProblemDetail("An error occurred while getting user's rooms.", err)
				return result
			}

			for _, room := range rooms {
				room.Users = make([]*models.UserMini, 0)
				for _, userMini := range userMinis {
					if room.RoomId == userMini.RoomId {
						room.Users = append(room.Users, userMini)
					}
				}
			}
			user.Rooms = rooms
		}

		if isWithDevices {
			var devices []*models.Device
			query := utils.AppendStrings("SELECT user_id, platform, token, notification_device_id from ", TABLE_NAME_DEVICE, " WHERE user_id=:userId")
			params := map[string]interface{}{"userId": userId}
			_, err := slave.Select(&devices, query, params)
			if err != nil {
				result.ProblemDetail = createProblemDetail("An error occurred while getting device items.", err)
				return result
			}
			user.Devices = devices
		}

		if isWithBlocks {
			dRes := RdbSelectBlockUsersByUserId(userId)
			user.Blocks = dRes.Data.([]string)
		}

		result.Data = user
	}
	return result
}

func RdbSelectUserByUserIdAndAccessToken(userId, accessToken string) StoreResult {
	slave := RdbStoreInstance().replica()
	result := StoreResult{}
	var users []*models.User
	query := utils.AppendStrings("SELECT id FROM ", TABLE_NAME_USER, " WHERE user_id=:userId AND access_token=:accessToken AND deleted=0;")
	params := map[string]interface{}{
		"userId":      userId,
		"accessToken": accessToken,
	}
	if _, err := slave.Select(&users, query, params); err != nil {
		result.ProblemDetail = createProblemDetail("An error occurred while getting user item.", err)
	}
	if len(users) == 1 {
		result.Data = users[0]
	}
	return result
}

func RdbSelectUsers() StoreResult {
	slave := RdbStoreInstance().replica()
	result := StoreResult{}
	var users []*models.User
	query := utils.AppendStrings("SELECT user_id, name, picture_url, information_url, unread_count, meta_data, is_public, created, modified FROM ", TABLE_NAME_USER, " WHERE deleted = 0 ORDER BY unread_count DESC;")
	_, err := slave.Select(&users, query)
	if err != nil {
		result.ProblemDetail = createProblemDetail("An error occurred while getting user items.", err)
	}
	result.Data = users
	return result
}

func RdbSelectUserIdsByUserIds(userIds []string) StoreResult {
	slave := RdbStoreInstance().replica()
	result := StoreResult{}
	var users []*models.User
	userIdsQuery, params := utils.MakePrepareForInExpression(userIds)
	query := utils.AppendStrings("SELECT * ",
		"FROM ", TABLE_NAME_USER,
		" WHERE user_id in (", userIdsQuery, ") AND deleted = 0;")
	_, err := slave.Select(&users, query, params)
	if err != nil {
		result.ProblemDetail = createProblemDetail("An error occurred while getting userIds.", err)
	}

	resultUuserIds := make([]string, 0)
	for _, user := range users {
		resultUuserIds = append(resultUuserIds, user.UserId)
	}
	result.Data = resultUuserIds
	return result
}

func RdbUpdateUser(user *models.User) StoreResult {
	master := RdbStoreInstance().master()
	trans, err := master.Begin()
	result := StoreResult{}
	_, err = trans.Update(user)
	if err != nil {
		result.ProblemDetail = createProblemDetail("An error occurred while updating user item.", err)
		if err := trans.Rollback(); err != nil {
			result.ProblemDetail = createProblemDetail("An error occurred while rollback updating user item.", err)
		}
		return result
	}

	if *user.UnreadCount == 0 {
		query := utils.AppendStrings("UPDATE ", TABLE_NAME_ROOM_USER, " SET unread_count=0 WHERE user_id=:userId;")
		params := map[string]interface{}{
			"userId": user.UserId,
		}
		_, err := trans.Exec(query, params)
		if err != nil {
			result.ProblemDetail = createProblemDetail("An error occurred while mark all as read.", err)
			if err := trans.Rollback(); err != nil {
				result.ProblemDetail = createProblemDetail("An error occurred while rollback updating user item.", err)
			}
			return result
		}
	}

	if result.ProblemDetail == nil {
		if err := trans.Commit(); err != nil {
			result.ProblemDetail = createProblemDetail("An error occurred while commit updating user item.", err)
		}
	}
	result.Data = user
	return result
}

func RdbUpdateUserDeleted(userId string) StoreResult {
	master := RdbStoreInstance().master()
	trans, err := master.Begin()
	result := StoreResult{}
	query := utils.AppendStrings("DELETE FROM ", TABLE_NAME_ROOM_USER, " WHERE user_id=:userId;")
	params := map[string]interface{}{
		"userId": userId,
	}
	_, err = trans.Exec(query, params)
	if err != nil {
		result.ProblemDetail = createProblemDetail("An error occurred while deleting room's user items.", err)
		if err := trans.Rollback(); err != nil {
			result.ProblemDetail = createProblemDetail("An error occurred while rollback updating user item.", err)
		}
		return result
	}

	query = utils.AppendStrings("DELETE FROM ", TABLE_NAME_DEVICE, " WHERE user_id=:userId;")
	params = map[string]interface{}{
		"userId": userId,
	}
	_, err = trans.Exec(query, params)
	if err != nil {
		result.ProblemDetail = createProblemDetail("An error occurred while deleting device items.", err)
		if err := trans.Rollback(); err != nil {
			result.ProblemDetail = createProblemDetail("An error occurred while rollback updating user item.", err)
		}
		return result
	}

	query = utils.AppendStrings("UPDATE ", TABLE_NAME_SUBSCRIPTION, " SET deleted=:deleted WHERE user_id=:userId;")
	params = map[string]interface{}{
		"userId":  userId,
		"deleted": time.Now().Unix(),
	}
	_, err = trans.Exec(query, params)
	if err != nil {
		result.ProblemDetail = createProblemDetail("An error occurred while updating subscription items.", err)
		if err := trans.Rollback(); err != nil {
			result.ProblemDetail = createProblemDetail("An error occurred while rollback updating user item.", err)
		}
		return result
	}

	query = utils.AppendStrings("UPDATE ", TABLE_NAME_USER, " SET deleted=:deleted WHERE user_id=:userId;")
	params = map[string]interface{}{
		"userId":  userId,
		"deleted": time.Now().Unix(),
	}
	_, err = trans.Exec(query, params)
	if err != nil {
		result.ProblemDetail = createProblemDetail("An error occurred while updating user item.", err)
		if err := trans.Rollback(); err != nil {
			result.ProblemDetail = createProblemDetail("An error occurred while rollback updating user item.", err)
		}
		return result
	}

	if result.ProblemDetail == nil {
		if err := trans.Commit(); err != nil {
			result.ProblemDetail = createProblemDetail("An error occurred while commit updating user item.", err)
		}
	}
	return result
}

func RdbSelectContacts(userId string) StoreResult {
	slave := RdbStoreInstance().replica()
	result := StoreResult{}
	var users []*models.User
	query := utils.AppendStrings("SELECT ",
		"u.user_id, ",
		"u.name, ",
		"u.picture_url, ",
		"u.information_url, ",
		"u.unread_count, ",
		"u.meta_data, ",
		"u.is_public, ",
		"u.created, ",
		"u.modified ",
		"FROM ", TABLE_NAME_USER, " as u ",
		"WHERE (u.is_public=1 AND u.deleted=0) OR (",
		"u.user_id IN (",
		"SELECT ru.user_id FROM ", TABLE_NAME_ROOM_USER, " as ru WHERE ru.user_id!=:userId AND ru.room_id IN (",
		"SELECT ru.room_id FROM ", TABLE_NAME_ROOM_USER, " as ru ",
		"LEFT JOIN ", TABLE_NAME_ROOM, " as r ON ru.room_id = r.room_id ",
		"WHERE ru.user_id=:userId AND r.type!=", strconv.Itoa(int(models.NOTICE_ROOM)),
		")) ",
		"AND u.is_show_users=1 ",
		"AND u.deleted=0)",
		"GROUP BY u.user_id ORDER BY u.modified DESC")
	params := map[string]interface{}{
		"userId": userId,
	}
	_, err := slave.Select(&users, query, params)
	if err != nil {
		result.ProblemDetail = createProblemDetail("An error occurred while getting contact items.", err)
	}
	result.Data = users
	return result
}
<commit_msg>Add properties to get users<commit_after>package datastore

import (
	"log"
	"strconv"
	"time"

	"github.com/swagchat/chat-api/models"
	"github.com/swagchat/chat-api/utils"
)

func RdbCreateUserStore() {
	master := RdbStoreInstance().master()
	tableMap := master.AddTableWithName(models.User{}, TABLE_NAME_USER)
	tableMap.SetKeys(true, "id")
	for _, columnMap := range tableMap.Columns {
		if columnMap.ColumnName == "user_id" {
			columnMap.SetUnique(true)
		}
	}
	if err := master.CreateTablesIfNotExists(); err != nil {
		log.Println(err)
	}
}

func RdbInsertUser(user *models.User) StoreResult {
	master := RdbStoreInstance().master()
	result := StoreResult{}
	trans, err := master.Begin()
	user.AccessToken = utils.GenerateToken(utils.TokenLength)
	if err = trans.Insert(user); err != nil {
		result.ProblemDetail = createProblemDetail("An error occurred while creating user item.", err)
		if err := trans.Rollback(); err != nil {
			result.ProblemDetail = createProblemDetail("An error occurred while rollback creating user item.", err)
		}
		return result
	}

	if result.ProblemDetail == nil && user.Devices != nil {
		for _, device := range user.Devices {
			if err := trans.Insert(device); err != nil {
				result.ProblemDetail = createProblemDetail("An error occurred while creating device item.", err)
				if err := trans.Rollback(); err != nil {
					result.ProblemDetail = createProblemDetail("An error occurred while rollback creating user item.", err)
				}
				return result
			}
		}
	}

	if result.ProblemDetail == nil {
		if err := trans.Commit(); err != nil {
			result.ProblemDetail = createProblemDetail("An error occurred while commit creating user item.", err)
		}
	}
	result.Data = user
	return result
}

func RdbSelectUser(userId string, isWithRooms, isWithDevices, isWithBlocks bool) StoreResult {
	slave := RdbStoreInstance().replica()
	result := StoreResult{}
	var users []*models.User
	query := utils.AppendStrings("SELECT * FROM ", TABLE_NAME_USER, " WHERE user_id=:userId AND deleted=0;")
	params := map[string]interface{}{"userId": userId}
	if _, err := slave.Select(&users, query, params); err != nil {
		result.ProblemDetail = createProblemDetail("An error occurred while getting user item.", err)
		return result
	}
	var user *models.User
	if len(users) == 1 {
		user = users[0]
		if isWithRooms {
			var rooms []*models.RoomForUser
			query := utils.AppendStrings("SELECT ",
				"r.room_id, ",
				"r.user_id, ",
				"r.name, ",
				"r.picture_url, ",
				"r.information_url, ",
				"r.meta_data, ",
				"r.type, ",
				"r.last_message, ",
				"r.last_message_updated, ",
				"r.is_can_left, ",
				"r.created, ",
				"r.modified, ",
				"ru.unread_count AS ru_unread_count, ",
				"ru.meta_data AS ru_meta_data, ",
				"ru.created AS ru_created, ",
				"ru.modified AS ru_modified ",
				"FROM ", TABLE_NAME_ROOM_USER, " AS ru ",
				"LEFT JOIN ", TABLE_NAME_ROOM, " AS r ON ru.room_id=r.room_id ",
				"WHERE ru.user_id=:userId AND r.deleted=0 ",
				"ORDER BY r.last_message_updated DESC;")
			params := map[string]interface{}{"userId": userId}
			_, err := slave.Select(&rooms, query, params)
			if err != nil {
				result.ProblemDetail = createProblemDetail("An error occurred while getting user's rooms.", err)
				return result
			}

			var userMinis []*models.UserMini
			query = utils.AppendStrings("SELECT ",
				"r.room_id, ",
				"u.user_id, ",
				"u.name, ",
				"u.picture_url, ",
				"u.is_show_users ",
				"FROM ", TABLE_NAME_ROOM_USER, " AS ru ",
				"LEFT JOIN ", TABLE_NAME_ROOM, " AS r ON ru.room_id=r.room_id ",
				"LEFT JOIN ", TABLE_NAME_USER, " AS u ON ru.user_id=u.user_id ",
				"WHERE r.room_id IN ( ",
				"SELECT room_id ",
				"FROM ", TABLE_NAME_ROOM_USER, " ",
				"WHERE user_id=:userId ",
				") ",
				"ORDER BY ru.room_id",
			)
			params = map[string]interface{}{"userId": userId}
			_, err = slave.Select(&userMinis, query, params)
			if err != nil {
				result.ProblemDetail = createProblemDetail("An error occurred while getting user's rooms.", err)
				return result
			}

			for _, room := range rooms {
				room.Users = make([]*models.UserMini, 0)
				for _, userMini := range userMinis {
					if room.RoomId == userMini.RoomId {
						room.Users = append(room.Users, userMini)
					}
				}
			}
			user.Rooms = rooms
		}

		if isWithDevices {
			var devices []*models.Device
			query := utils.AppendStrings("SELECT user_id, platform, token, notification_device_id from ", TABLE_NAME_DEVICE, " WHERE user_id=:userId")
			params := map[string]interface{}{"userId": userId}
			_, err := slave.Select(&devices, query, params)
			if err != nil {
				result.ProblemDetail = createProblemDetail("An error occurred while getting device items.", err)
				return result
			}
			user.Devices = devices
		}

		if isWithBlocks {
			dRes := RdbSelectBlockUsersByUserId(userId)
			user.Blocks = dRes.Data.([]string)
		}

		result.Data = user
	}
	return result
}

func RdbSelectUserByUserIdAndAccessToken(userId, accessToken string) StoreResult {
	slave := RdbStoreInstance().replica()
	result := StoreResult{}
	var users []*models.User
	query := utils.AppendStrings("SELECT id FROM ", TABLE_NAME_USER, " WHERE user_id=:userId AND access_token=:accessToken AND deleted=0;")
	params := map[string]interface{}{
		"userId":      userId,
		"accessToken": accessToken,
	}
	if _, err := slave.Select(&users, query, params); err != nil {
		result.ProblemDetail = createProblemDetail("An error occurred while getting user item.", err)
	}
	if len(users) == 1 {
		result.Data = users[0]
	}
	return result
}

func RdbSelectUsers() StoreResult {
	slave := RdbStoreInstance().replica()
	result := StoreResult{}
	var users []*models.User
	query := utils.AppendStrings("SELECT user_id, name, picture_url, information_url, unread_count, meta_data, is_bot, is_public, is_can_block, is_show_users, created, modified FROM ", TABLE_NAME_USER, " WHERE deleted = 0 ORDER BY unread_count DESC;")
	_, err := slave.Select(&users, query)
	if err != nil {
		result.ProblemDetail = createProblemDetail("An error occurred while getting user items.", err)
	}
	result.Data = users
	return result
}

func RdbSelectUserIdsByUserIds(userIds []string) StoreResult {
	slave := RdbStoreInstance().replica()
	result := StoreResult{}
	var users []*models.User
	userIdsQuery, params := utils.MakePrepareForInExpression(userIds)
	query := utils.AppendStrings("SELECT * ",
		"FROM ", TABLE_NAME_USER,
		" WHERE user_id in (", userIdsQuery, ") AND deleted = 0;")
	_, err := slave.Select(&users, query, params)
	if err != nil {
		result.ProblemDetail = createProblemDetail("An error occurred while getting userIds.", err)
	}

	resultUuserIds := make([]string, 0)
	for _, user := range users {
		resultUuserIds = append(resultUuserIds, user.UserId)
	}
	result.Data = resultUuserIds
	return result
}

func RdbUpdateUser(user *models.User) StoreResult {
	master := RdbStoreInstance().master()
	trans, err := master.Begin()
	result := StoreResult{}
	_, err = trans.Update(user)
	if err != nil {
		result.ProblemDetail = createProblemDetail("An error occurred while updating user item.", err)
		if err := trans.Rollback(); err != nil {
			result.ProblemDetail = createProblemDetail("An error occurred while rollback updating user item.", err)
		}
		return result
	}

	if *user.UnreadCount == 0 {
		query := utils.AppendStrings("UPDATE ", TABLE_NAME_ROOM_USER, " SET unread_count=0 WHERE user_id=:userId;")
		params := map[string]interface{}{
			"userId": user.UserId,
		}
		_, err := trans.Exec(query, params)
		if err != nil {
			result.ProblemDetail = createProblemDetail("An error occurred while mark all as read.", err)
			if err := trans.Rollback(); err != nil {
				result.ProblemDetail = createProblemDetail("An error occurred while rollback updating user item.", err)
			}
			return result
		}
	}

	if result.ProblemDetail == nil {
		if err := trans.Commit(); err != nil {
			result.ProblemDetail = createProblemDetail("An error occurred while commit updating user item.", err)
		}
	}
	result.Data = user
	return result
}

func RdbUpdateUserDeleted(userId string) StoreResult {
	master := RdbStoreInstance().master()
	trans, err := master.Begin()
	result := StoreResult{}
	query := utils.AppendStrings("DELETE FROM ", TABLE_NAME_ROOM_USER, " WHERE user_id=:userId;")
	params := map[string]interface{}{
		"userId": userId,
	}
	_, err = trans.Exec(query, params)
	if err != nil {
		result.ProblemDetail = createProblemDetail("An error occurred while deleting room's user items.", err)
		if err := trans.Rollback(); err != nil {
			result.ProblemDetail = createProblemDetail("An error occurred while rollback updating user item.", err)
		}
		return result
	}

	query = utils.AppendStrings("DELETE FROM ", TABLE_NAME_DEVICE, " WHERE user_id=:userId;")
	params = map[string]interface{}{
		"userId": userId,
	}
	_, err = trans.Exec(query, params)
	if err != nil {
		result.ProblemDetail = createProblemDetail("An error occurred while deleting device items.", err)
		if err := trans.Rollback(); err != nil {
			result.ProblemDetail = createProblemDetail("An error occurred while rollback updating user item.", err)
		}
		return result
	}

	query = utils.AppendStrings("UPDATE ", TABLE_NAME_SUBSCRIPTION, " SET deleted=:deleted WHERE user_id=:userId;")
	params = map[string]interface{}{
		"userId":  userId,
		"deleted": time.Now().Unix(),
	}
	_, err = trans.Exec(query, params)
	if err != nil {
		result.ProblemDetail = createProblemDetail("An error occurred while updating subscription items.", err)
		if err := trans.Rollback(); err != nil {
			result.ProblemDetail = createProblemDetail("An error occurred while rollback updating user item.", err)
		}
		return result
	}

	query = utils.AppendStrings("UPDATE ", TABLE_NAME_USER, " SET deleted=:deleted WHERE user_id=:userId;")
	params = map[string]interface{}{
		"userId":  userId,
		"deleted": time.Now().Unix(),
	}
	_, err = trans.Exec(query, params)
	if err != nil {
		result.ProblemDetail = createProblemDetail("An error occurred while updating user item.", err)
		if err := trans.Rollback(); err != nil {
			result.ProblemDetail = createProblemDetail("An error occurred while rollback updating user item.", err)
		}
		return result
	}

	if result.ProblemDetail == nil {
		if err := trans.Commit(); err != nil {
			result.ProblemDetail = createProblemDetail("An error occurred while commit updating user item.", err)
		}
	}
	return result
}

func RdbSelectContacts(userId string) StoreResult {
	slave := RdbStoreInstance().replica()
	result := StoreResult{}
	var users []*models.User
	query := utils.AppendStrings("SELECT ",
		"u.user_id, ",
		"u.name, ",
		"u.picture_url, ",
		"u.information_url, ",
		"u.unread_count, ",
		"u.meta_data, ",
		"u.is_public, ",
		"u.created, ",
		"u.modified ",
		"FROM ", TABLE_NAME_USER, " as u ",
		"WHERE (u.is_public=1 AND u.deleted=0) OR (",
		"u.user_id IN (",
		"SELECT ru.user_id FROM ", TABLE_NAME_ROOM_USER, " as ru WHERE ru.user_id!=:userId AND ru.room_id IN (",
		"SELECT ru.room_id FROM ", TABLE_NAME_ROOM_USER, " as ru ",
		"LEFT JOIN ", TABLE_NAME_ROOM, " as r ON ru.room_id = r.room_id ",
		"WHERE ru.user_id=:userId AND r.type!=", strconv.Itoa(int(models.NOTICE_ROOM)),
		")) ",
		"AND u.is_show_users=1 ",
		"AND u.deleted=0)",
		"GROUP BY u.user_id ORDER BY u.modified DESC")
	params := map[string]interface{}{
		"userId": userId,
	}
	_, err := slave.Select(&users, query, params)
	if err != nil {
		result.ProblemDetail = createProblemDetail("An error occurred while getting contact items.", err)
	}
	result.Data = users
	return result
}
<|endoftext|>


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

// Package rootsgetter periodically queries a Log for its set of acceptable root certificates and stores them.
package rootsgetter

import (
	"context"
	"fmt"
	"time"

	"github.com/golang/glog"
	ct "github.com/google/certificate-transparency-go"
	"github.com/google/certificate-transparency-go/schedule"
	"github.com/google/certificate-transparency-go/x509"
	"github.com/google/monologue/apicall"
	"github.com/google/monologue/client"
	"github.com/google/monologue/ctlog"
	"github.com/google/monologue/storage"
)

const logStr = "Roots Getter"

// Storage interface required by Roots Getter.
type Storage interface {
	storage.APICallWriter
	storage.RootsWriter
}

// Run starts an STH Getter, which periodically queries a Log for its set of acceptable root certificates and stores them.
func Run(ctx context.Context, lc *client.LogClient, st Storage, l *ctlog.Log, period time.Duration) {
	glog.Infof("%s: %s: started with period %v", l.URL, logStr, period)

	schedule.Every(ctx, period, func(ctx context.Context) {
		roots, receivedAt, err := getRoots(ctx, lc, st, l)
		if err != nil {
			glog.Errorf("%s: %s: %s", l.URL, logStr, err)
		}
		if err := st.WriteRoots(ctx, l, roots, receivedAt); err != nil {
			glog.Errorf("%s: %s: %s", l.URL, logStr, err)
		}
	})

	glog.Infof("%s: %s: stopped", l.URL, logStr)
}

func getRoots(ctx context.Context, lc *client.LogClient, st storage.APICallWriter, l *ctlog.Log) ([]*x509.Certificate, time.Time, error) {
	glog.Infof("%s: %s: getting roots...", l.URL, logStr)
	roots, httpData, getErr := lc.GetRoots()

	// Store get-roots API call.
	apiCall := apicall.New(ct.GetRootsStr, httpData, getErr)
	glog.Infof("%s: %s: writing API Call...", l.URL, logStr)
	if err := st.WriteAPICall(ctx, l, apiCall); err != nil {
		return nil, httpData.Timing.End, fmt.Errorf("error writing API Call %s: %s", apiCall, err)
	}

	if getErr != nil {
		return nil, httpData.Timing.End, fmt.Errorf("error getting roots: %s", getErr)
	}

	glog.Infof("%s: %s: response: %d certificates", l.URL, logStr, len(roots))
	return roots, httpData.Timing.End, nil
}
<commit_msg>rootsgetter: if getRoots() fails, don't call storage.WriteRoots()<commit_after>// Copyright 2018 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

// Package rootsgetter periodically queries a Log for its set of acceptable root certificates and stores them.
package rootsgetter

import (
	"context"
	"fmt"
	"time"

	"github.com/golang/glog"
	ct "github.com/google/certificate-transparency-go"
	"github.com/google/certificate-transparency-go/schedule"
	"github.com/google/certificate-transparency-go/x509"
	"github.com/google/monologue/apicall"
	"github.com/google/monologue/client"
	"github.com/google/monologue/ctlog"
	"github.com/google/monologue/storage"
)

const logStr = "Roots Getter"

// Storage interface required by Roots Getter.
type Storage interface {
	storage.APICallWriter
	storage.RootsWriter
}

// Run starts an STH Getter, which periodically queries a Log for its set of acceptable root certificates and stores them.
func Run(ctx context.Context, lc *client.LogClient, st Storage, l *ctlog.Log, period time.Duration) {
	glog.Infof("%s: %s: started with period %v", l.URL, logStr, period)

	schedule.Every(ctx, period, func(ctx context.Context) {
		roots, receivedAt, err := getRoots(ctx, lc, st, l)
		if err != nil {
			glog.Errorf("%s: %s: %s", l.URL, logStr, err)
			return
		}
		if err := st.WriteRoots(ctx, l, roots, receivedAt); err != nil {
			glog.Errorf("%s: %s: %s", l.URL, logStr, err)
		}
	})

	glog.Infof("%s: %s: stopped", l.URL, logStr)
}

func getRoots(ctx context.Context, lc *client.LogClient, st storage.APICallWriter, l *ctlog.Log) ([]*x509.Certificate, time.Time, error) {
	glog.Infof("%s: %s: getting roots...", l.URL, logStr)
	roots, httpData, getErr := lc.GetRoots()

	// Store get-roots API call.
	apiCall := apicall.New(ct.GetRootsStr, httpData, getErr)
	glog.Infof("%s: %s: writing API Call...", l.URL, logStr)
	if err := st.WriteAPICall(ctx, l, apiCall); err != nil {
		return nil, httpData.Timing.End, fmt.Errorf("error writing API Call %s: %s", apiCall, err)
	}

	if getErr != nil {
		return nil, httpData.Timing.End, fmt.Errorf("error getting roots: %s", getErr)
	}

	glog.Infof("%s: %s: response: %d certificates", l.URL, logStr, len(roots))
	return roots, httpData.Timing.End, nil
}
<|endoftext|>


<commit_before>package speed

import "testing"

func BenchmarkGetHash(b *testing.B) {
	strings := []string{
		"a",
		"abcdefghijklmnopqrstuvwxyz",
		"aaaaaaaaaaaaaaaaaaaaaaaaaa",
		"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
		"abcdefghijklmnopqrstuvwxyabcdefghijklmnopqrstuvwxyabcdefghijklmnopqrstuvwxyabcdefghijklmnopqrstuvwxy",
	}

	l := len(strings)
	for i := 0; i < b.N; i++ {
		_ = getHash(strings[i%l], 0)
	}
}
<commit_msg>speed: fix test<commit_after>package speed

import "testing"

func BenchmarkGetHash(b *testing.B) {
	strings := []string{
		"a",
		"abcdefghijklmnopqrstuvwxyz",
		"aaaaaaaaaaaaaaaaaaaaaaaaaa",
		"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
		"abcdefghijklmnopqrstuvwxyabcdefghijklmnopqrstuvwxyabcdefghijklmnopqrstuvwxyabcdefghijklmnopqrstuvwxy",
	}

	l := len(strings)
	for i := 0; i < b.N; i++ {
		_ = hash(strings[i%l], 0)
	}
}
<|endoftext|>


<commit_before>package main

import (
	"flag"
	"fmt"
	"github.com/couchbase/gomemcached"
	"github.com/couchbase/gomemcached/client"
	"github.com/couchbaselabs/go-couchbase"
	"log"
	"math/rand"
	"net/http"
	_ "net/http/pprof"
	"net/url"
	"os"
	"runtime/pprof"
	"time"
)

var vbcount = 64
var cpuprofile = flag.String("cpuprofile", "", "write cpu profile to file")
var memprofile = flag.String("memprofile", "", "write memory profile to this file")

func mf(err error, msg string) {
	if err != nil {
		log.Fatalf("%v: %v", msg, err)
	}
}

// Flush the bucket before trying this program
func main() {

	//runtime.GOMAXPROCS(4)

	go func() {
		log.Println(http.ListenAndServe("localhost:6060", nil))
	}()

	bname := flag.String("bucket", "",
		"bucket to connect to (defaults to username)")

	flag.Usage = func() {
		fmt.Fprintf(os.Stderr,
			"%v [flags] http://user:pass@host:8091/\n\nFlags:\n",
			os.Args[0])
		flag.PrintDefaults()
		os.Exit(64)
	}

	flag.Parse()

	if flag.NArg() < 1 {
		flag.Usage()
	}

	if *cpuprofile != "" {
		f, err := os.Create(*cpuprofile)
		if err != nil {
			log.Fatal(err)
		}
		pprof.StartCPUProfile(f)
		defer pprof.StopCPUProfile()
	}

	if *memprofile != "" {
		f, err := os.Create(*memprofile)
		if err != nil {
			log.Fatal(err)
		}
		defer pprof.WriteHeapProfile(f)
		defer f.Close()
	}

	u, err := url.Parse(flag.Arg(0))
	mf(err, "parse")

	if *bname == "" && u.User != nil {
		*bname = u.User.Username()
	}

	c, err := couchbase.Connect(u.String())
	mf(err, "connect - "+u.String())

	p, err := c.GetPool("default")
	mf(err, "pool")

	bucket, err := p.GetBucket(*bname)
	mf(err, "bucket")

	// start upr feed
	name := fmt.Sprintf("%v", time.Now().UnixNano())
	feed, err := bucket.StartUprFeed(name, 0)
	if err != nil {
		log.Print(" Failed to start stream ", err)
		return
	}

	// request stream for all vbuckets
	for i := 0; i < vbcount; i++ {
		err := feed.UprRequestStream(
			uint16(i) /*vbno*/, uint16(0) /*opaque*/, 0 /*flag*/, 0, /*vbuuid*/
			0 /*seqStart*/, 0xFFFFFFFFFFFFFFFF /*seqEnd*/, 0 /*snaps*/, 0)
		if err != nil {
			fmt.Printf("%s", err.Error())
		}
	}

	// observe the mutations from the channel.
	var event *memcached.UprEvent
	var mutations = 0
	//var callOnce bool
loop:
	for {
		select {
		case e, ok := <-feed.C:
			if !ok {
				break loop
			} else {
				event = e
			}
		case <-time.After(time.Second):
			break loop
		}
		if event.Opcode == gomemcached.UPR_MUTATION {
			//log.Printf(" got mutation %s", e.Value)
			mutations += 1
		}

		if event.Opcode == gomemcached.UPR_STREAMEND {
			log.Printf(" Received Stream end for vbucket %d", event.VBucket)
		}

		if mutations%1000000 == 0 {
			log.Printf(" received %d mutations ", mutations)
		}

		//e.Release()
	}

	feed.Close()
	log.Printf("Mutation count %d", mutations)

}

func addKVset(b *couchbase.Bucket, count int) {
	for i := 0; i < count; i++ {
		key := fmt.Sprintf("key%v", i+1000000)
		val_len := rand.Intn(10*1024) + rand.Intn(10*1024)
		value := fmt.Sprintf("This is a test key %d", val_len)
		err := b.Set(key, 0, value)
		if err != nil {
			panic(err)
		}

		if i%1000000 == 0 {
			fmt.Printf("\n Added %d keys", i)
		}
	}
}
<commit_msg>Avoid writing to output feed if it has been closed<commit_after>package main

import (
	"flag"
	"fmt"
	"github.com/couchbase/gomemcached"
	"github.com/couchbase/gomemcached/client"
	"github.com/couchbaselabs/go-couchbase"
	"log"
	"math/rand"
	"net/http"
	_ "net/http/pprof"
	"net/url"
	"os"
	"runtime/pprof"
	"time"
)

var vbcount = 64
var cpuprofile = flag.String("cpuprofile", "", "write cpu profile to file")
var memprofile = flag.String("memprofile", "", "write memory profile to this file")

func mf(err error, msg string) {
	if err != nil {
		log.Fatalf("%v: %v", msg, err)
	}
}

// Flush the bucket before trying this program
func main() {

	//runtime.GOMAXPROCS(4)

	go func() {
		log.Println(http.ListenAndServe("localhost:6060", nil))
	}()

	bname := flag.String("bucket", "",
		"bucket to connect to (defaults to username)")

	flag.Usage = func() {
		fmt.Fprintf(os.Stderr,
			"%v [flags] http://user:pass@host:8091/\n\nFlags:\n",
			os.Args[0])
		flag.PrintDefaults()
		os.Exit(64)
	}

	flag.Parse()

	if flag.NArg() < 1 {
		flag.Usage()
	}

	if *cpuprofile != "" {
		f, err := os.Create(*cpuprofile)
		if err != nil {
			log.Fatal(err)
		}
		pprof.StartCPUProfile(f)
		defer pprof.StopCPUProfile()
	}

	if *memprofile != "" {
		f, err := os.Create(*memprofile)
		if err != nil {
			log.Fatal(err)
		}
		defer pprof.WriteHeapProfile(f)
		defer f.Close()
	}

	u, err := url.Parse(flag.Arg(0))
	mf(err, "parse")

	if *bname == "" && u.User != nil {
		*bname = u.User.Username()
	}

	c, err := couchbase.Connect(u.String())
	mf(err, "connect - "+u.String())

	p, err := c.GetPool("default")
	mf(err, "pool")

	bucket, err := p.GetBucket(*bname)
	mf(err, "bucket")

	addKVset(bucket, 10000)

	// start upr feed
	name := fmt.Sprintf("%v", time.Now().UnixNano())
	feed, err := bucket.StartUprFeed(name, 0)
	if err != nil {
		log.Print(" Failed to start stream ", err)
		return
	}

	// request stream for all vbuckets
	for i := 0; i < vbcount; i++ {
		err := feed.UprRequestStream(
			uint16(i) /*vbno*/, uint16(0) /*opaque*/, 0 /*flag*/, 0, /*vbuuid*/
			0 /*seqStart*/, 0xFFFFFFFFFFFFFFFF /*seqEnd*/, 0 /*snaps*/, 0)
		if err != nil {
			fmt.Printf("%s", err.Error())
		}
	}

	// observe the mutations from the channel.
	var event *memcached.UprEvent
	var mutations = 0
	//var callOnce bool
loop:
	for {
		select {
		case e, ok := <-feed.C:
			if !ok {
				break loop
			} else {
				event = e
			}
		case <-time.After(time.Second):
			break loop
		}
		if event.Opcode == gomemcached.UPR_MUTATION {
			//log.Printf(" got mutation %s", e.Value)
			mutations += 1
		}

		if event.Opcode == gomemcached.UPR_STREAMEND {
			log.Printf(" Received Stream end for vbucket %d", event.VBucket)
		}

		if mutations%1000000 == 0 {
			log.Printf(" received %d mutations ", mutations)
		}

		//e.Release()
	}

	feed.Close()
	log.Printf("Mutation count %d", mutations)

}

func addKVset(b *couchbase.Bucket, count int) {
	for i := 0; i < count; i++ {
		key := fmt.Sprintf("key%v", i+1000000)
		val_len := rand.Intn(10*1024) + rand.Intn(10*1024)
		value := fmt.Sprintf("This is a test key %d", val_len)
		err := b.Set(key, 0, value)
		if err != nil {
			panic(err)
		}

		if i%1000000 == 0 {
			fmt.Printf("\n Added %d keys", i)
		}
	}
}
<|endoftext|>


<commit_before>package sample

import (
	"fmt"

	"github.com/aliyun/aliyun-oss-go-sdk/oss"
)

// BucketLifecycleSample shows how to set, get and delete bucket's lifecycle.
func BucketLifecycleSample() {
	// New client
	client, err := oss.New(endpoint, accessID, accessKey)
	if err != nil {
		HandleError(err)
	}

	// Create the bucket with default parameters
	err = client.CreateBucket(bucketName)
	if err != nil {
		HandleError(err)
	}

	// Case 1: Set the lifecycle. The rule ID is id1 and the applied objects' prefix is one and expired time is 11/11/2015
	var rule1 = oss.BuildLifecycleRuleByDate("id1", "one", true, 2015, 11, 11)
	var rules = []oss.LifecycleRule{rule1}
	err = client.SetBucketLifecycle(bucketName, rules)
	if err != nil {
		HandleError(err)
	}

	// Case 2: Set the lifecycle, The rule ID is id2 and the applied objects' prefix is two and the expired time is three days after the object created.
	var rule2 = oss.BuildLifecycleRuleByDays("id2", "two", true, 3)
	rules = []oss.LifecycleRule{rule2}
	err = client.SetBucketLifecycle(bucketName, rules)
	if err != nil {
		HandleError(err)
	}

	// Case 3: Create two rules in the bucket for different objects. The rule with the same ID will be overwritten.
	var rule3 = oss.BuildLifecycleRuleByDays("id1", "two", true, 365)
	var rule4 = oss.BuildLifecycleRuleByDate("id2", "one", true, 2016, 11, 11)
	rules = []oss.LifecycleRule{rule3, rule4}
	err = client.SetBucketLifecycle(bucketName, rules)
	if err != nil {
		HandleError(err)
	}

	// Get the bucket's lifecycle
	gbl, err := client.GetBucketLifecycle(bucketName)
	if err != nil {
		HandleError(err)
	}
	fmt.Println("Bucket Lifecycle:", gbl.Rules)

	// Delete bucket's Lifecycle
	err = client.DeleteBucketLifecycle(bucketName)
	if err != nil {
		HandleError(err)
	}

	// Delete bucket
	err = client.DeleteBucket(bucketName)
	if err != nil {
		HandleError(err)
	}

	fmt.Println("BucketLifecycleSample completed")
}
<commit_msg>use new methods to create lifecycle rule<commit_after>package sample

import (
	"fmt"

	"github.com/aliyun/aliyun-oss-go-sdk/oss"
)

// BucketLifecycleSample shows how to set, get and delete bucket's lifecycle.
func BucketLifecycleSample() {
	// New client
	client, err := oss.New(endpoint, accessID, accessKey)
	if err != nil {
		HandleError(err)
	}

	// Create the bucket with default parameters
	err = client.CreateBucket(bucketName)
	if err != nil {
		HandleError(err)
	}

	// Case 1: Set the lifecycle. The rule ID is id1 and the applied objects' prefix is one and expired time is 11/11/2015
	//var rule1 = oss.BuildLifecycleRuleByDate("id1", "one", true, 2015, 11, 11)
	rule1, err := oss.NewLifecycleRuleByCreateBeforeDate("id1", "one", true, 2015, 11, 11, oss.LRTExpriration)
	if err != nil {
		HandleError(err)
	}
	var rules = []oss.LifecycleRule{*rule1}
	err = client.SetBucketLifecycle(bucketName, rules)
	if err != nil {
		HandleError(err)
	}

	// Case 2: Set the lifecycle, The rule ID is id2 and the applied objects' prefix is two and the expired time is three days after the object created.
	//var rule2 = oss.BuildLifecycleRuleByDays("id2", "two", true, 3)
	rule2, err := oss.NewLifecleRuleByDays("id2", "two", true, 3, oss.LRTTransition, oss.StorageIA)
	if err != nil {
		HandleError(err)
	}
	rules = []oss.LifecycleRule{*rule2}
	err = client.SetBucketLifecycle(bucketName, rules)
	if err != nil {
		HandleError(err)
	}

	// Get the bucket's lifecycle
	lc, err := client.GetBucketLifecycle(bucketName)
	if err != nil {
		HandleError(err)
	}
	fmt.Println("Bucket Lifecycle:", lc.Rules)

	rule3, err := oss.NewLifecleRuleByDays("id3", "three", true, 3, oss.LRTAbortMultiPartUpload)
	if err != nil {
		HandleError(err)
	}
	rules = append(lc.Rules, *rule3)
	err = client.SetBucketLifecycle(bucketName, rules)
	if err != nil {
		HandleError(err)
	}

	// Get the bucket's lifecycle
	lc, err = client.GetBucketLifecycle(bucketName)
	if err != nil {
		HandleError(err)
	}
	fmt.Println("Bucket Lifecycle:", lc.Rules)

	// Delete bucket's Lifecycle
	err = client.DeleteBucketLifecycle(bucketName)
	if err != nil {
		HandleError(err)
	}

	// Delete bucket
	err = client.DeleteBucket(bucketName)
	if err != nil {
		HandleError(err)
	}

	fmt.Println("BucketLifecycleSample completed")
}
<|endoftext|>


<commit_before>package main

import (
	"github.com/gin-gonic/gin"
	elastic "gopkg.in/olivere/elastic.v5"
	"fmt"
)

type Status struct {
	Name        string `json:"name"`
	Version     string `json:"version"`
	Build       string `json:"build"`
	Description string `json:"description"`
	Documents   string `json:"documents"`
}

func main() {

	client, err := elastic.NewClient()
	if err != nil {
		// Handle error
		panic(err)
	}

	q := elastic.NewTermQuery("termid", "dna repair")
	searchResult, err := client.Search().
		Index("terms").   // search in index "twitter"
		Query(q).   // specify the query
		Sort("name", true). // sort by "user" field, ascending
		From(0).Size(10).   // take documents 0-9
		Pretty(true).       // pretty print request and response JSON
		Do()               // execute
	if err != nil {
		// Handle error
		panic(err)
	}

	// searchResult is of type SearchResult and returns hits, suggestions,
	// and all kinds of other information from Elasticsearch.
	fmt.Printf("Query took %d milliseconds\n", searchResult.TookInMillis)

	r := gin.Default()

	// Ping test
	r.GET("/status", func(c *gin.Context) {

		serviceStatus := Status{
			Name:"Annotation search service",
			Version:"v1",
			Build:"12-01-2016",
			Description:"Converts CX format into Cytoscape.js compatible JSON.",
			Documents: "https://github.com/cyService/service-annotation",
		}

		c.JSON(200, serviceStatus)
	})

	r.Run(":8080")
}
<commit_msg>Replaced with echo<commit_after>package main

import (
	//"github.com/gin-gonic/gin"
	//elastic "gopkg.in/olivere/elastic.v5"
	//"fmt"
	"net/http"
	"github.com/labstack/echo"
)

type Status struct {
	Name        string `json:"name"`
	Version     string `json:"version"`
	Build       string `json:"build"`
	Description string `json:"description"`
	Documents   string `json:"documents"`
}

func main() {

	//client, err := elastic.NewClient()
	//if err != nil {
	//	// Handle error
	//	panic(err)
	//}

	//q := elastic.NewTermQuery("termid", "dna repair")
	//searchResult, err := client.Search().
	//	Index("terms").   // search in index "twitter"
	//	Query(q).   // specify the query
	//	Sort("name", true). // sort by "user" field, ascending
	//	From(0).Size(10).   // take documents 0-9
	//	Pretty(true).       // pretty print request and response JSON
	//	Do()               // execute
	//if err != nil {
	//	// Handle error
	//	panic(err)
	//}
	//
	//// searchResult is of type SearchResult and returns hits, suggestions,
	//// and all kinds of other information from Elasticsearch.
	//fmt.Printf("Query took %d milliseconds\n", searchResult.TookInMillis)

	e := echo.New()
	e.GET("/", func(c echo.Context) error {
		serviceStatus := Status{
			Name:"Annotation search service",
			Version:"v1",
			Build:"12-20-2016",
			Description:"Property server.",
			Documents: "https://github.com/cyService/service-annotation",
		}
		return c.JSON(http.StatusOK, serviceStatus)
	})
	e.Logger.Fatal(e.Start(":1323"))

	//r := gin.Default()
	//
	//// Ping test
	//r.GET("/status", func(c *gin.Context) {
	//
	//	serviceStatus := Status{
	//		Name:"Annotation search service",
	//		Version:"v1",
	//		Build:"12-01-2016",
	//		Description:"Converts CX format into Cytoscape.js compatible JSON.",
	//		Documents: "https://github.com/cyService/service-annotation",
	//	}
	//
	//	c.JSON(200, serviceStatus)
	//})
	//
	//r.Run(":8080")
}
<|endoftext|>


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

// Handler for /validate content.
// Validates cadvisor dependencies - kernel, os, docker setup.

package validate

import (
	"fmt"
	"github.com/google/cadvisor/manager"
	"io/ioutil"
	"log"
	"net/http"
	"strings"

	"github.com/docker/libcontainer/cgroups"
	dclient "github.com/fsouza/go-dockerclient"
	"github.com/google/cadvisor/container/docker"
	"github.com/google/cadvisor/utils"
)

const ValidatePage = "/validate/"
const Supported = "[Supported, but not recommended]"
const Unsupported = "[Unsupported]"
const Recommended = "[Supported and recommended]"
const Unknown = "[Unknown]"
const VersionFormat = "%d.%d.%s"
const OutputFormat = "%s: %s\n\t%s\n\n"

func getMajorMinor(version string) (int, int, error) {
	var major, minor int
	var ign string
	n, err := fmt.Sscanf(version, VersionFormat, &major, &minor, &ign)
	if n != 3 || err != nil {
		log.Printf("Failed to parse version for %s", version)
		return -1, -1, err
	}
	return major, minor, nil
}

func validateKernelVersion(version string) (string, string) {
	desc := fmt.Sprintf("Kernel version is %s. Versions >= 2.6 are supported. 3.0+ are recommended.\n", version)
	major, minor, err := getMajorMinor(version)
	if err != nil {
		desc = fmt.Sprintf("Could not parse kernel version. %s", desc)
		return Unknown, desc
	}

	if major < 2 {
		return Unsupported, desc
	}

	if major == 2 && minor < 6 {
		return Unsupported, desc
	}

	if major >= 3 {
		return Recommended, desc
	}

	return Supported, desc
}

func validateDockerVersion(version string) (string, string) {
	desc := fmt.Sprintf("Docker version is %s. Versions >= 1.0 are supported. 1.2+ are recommended.\n", version)
	major, minor, err := getMajorMinor(version)
	if err != nil {
		desc = fmt.Sprintf("Could not parse docker version. %s\n\t", desc)
		return Unknown, desc
	}
	if major < 1 {
		return Unsupported, desc
	}

	if major == 1 && minor < 2 {
		return Supported, desc
	}

	return Recommended, desc
}

func getEnabledCgroups() (map[string]int, error) {
	out, err := ioutil.ReadFile("/proc/cgroups")
	if err != nil {
		return nil, err
	}
	cgroups := make(map[string]int)
	for i, line := range strings.Split(string(out), "\n") {
		var cgroup string
		var ign, enabled int
		if i == 0 || line == "" {
			continue
		}
		n, err := fmt.Sscanf(line, "%s %d %d %d", &cgroup, &ign, &ign, &enabled)
		if n != 4 || err != nil {
			if err == nil {
				err = fmt.Errorf("failed to parse /proc/cgroup entry %s", line)
			}
			return nil, err
		}
		cgroups[cgroup] = enabled
	}
	return cgroups, nil
}

func areCgroupsPresent(available map[string]int, desired []string) (bool, string) {
	for _, cgroup := range desired {
		enabled, ok := available[cgroup]
		if !ok {
			reason := fmt.Sprintf("Missing cgroup %s. Available cgroups: %v\n", cgroup, available)
			return false, reason
		}
		if enabled != 1 {
			reason := fmt.Sprintf("Cgroup %s not enabled. Available cgroups: %v\n", cgroup, available)
			return false, reason
		}
	}
	return true, ""
}

func validateCgroups() (string, string) {
	required_cgroups := []string{"cpu", "cpuacct"}
	recommended_cgroups := []string{"memory", "blkio", "cpuset", "devices", "freezer"}
	available_cgroups, err := getEnabledCgroups()
	desc := fmt.Sprintf("\tFollowing cgroups are required: %v\n\tFollowing other cgroups are recommended: %v\n", required_cgroups, recommended_cgroups)
	if err != nil {
		desc = fmt.Sprintf("Could not parse /proc/cgroups.\n%s", desc)
		return Unknown, desc
	}
	ok, out := areCgroupsPresent(available_cgroups, required_cgroups)
	if !ok {
		out += desc
		return Unsupported, out
	}
	ok, out = areCgroupsPresent(available_cgroups, recommended_cgroups)
	if !ok {
		// supported, but not recommended.
		out += desc
		return Supported, out
	}
	out = fmt.Sprintf("Available cgroups: %v\n", available_cgroups)
	out += desc
	return Recommended, out
}

func validateDockerInfo() (string, string) {
	client, err := dclient.NewClient(*docker.ArgDockerEndpoint)
	if err == nil {
		info, err := client.Info()
		if err == nil {
			execDriver := info.Get("ExecutionDriver")
			storageDriver := info.Get("Driver")
			desc := fmt.Sprintf("Docker exec driver is %s. Storage driver is %s.\n", execDriver, storageDriver)
			if docker.UseSystemd() {
				desc += "\tsystemd is being used to create cgroups.\n"
			} else {
				desc += "\tCgroups are being created through cgroup filesystem.\n"
			}
			if strings.Contains(execDriver, "native") {
				return Recommended, desc
			} else if strings.Contains(execDriver, "lxc") {
				return Supported, desc
			}
			return Unknown, desc
		}
	}
	return Unknown, "Docker remote API not reachable\n\t"
}

func validateCgroupMounts() (string, string) {
	const recommendedMount = "/sys/fs/cgroup"
	desc := fmt.Sprintf("\tAny cgroup mount point that is detectible and accessible is supported. %s is recommended as a standard location.\n", recommendedMount)
	mnt, err := cgroups.FindCgroupMountpoint("cpu")
	if err != nil {
		out := "Could not locate cgroup mount point.\n"
		out += desc
		return Unknown, out
	}
	mnt = strings.TrimSuffix(mnt, "/cpu")
	if !utils.FileExists(mnt) {
		out := fmt.Sprintf("Cgroup mount directory %s inaccessible.\n", mnt)
		out += desc
		return Unsupported, out
	}
	out := fmt.Sprintf("Cgroups are mounted at %s.\n", mnt)
	out += desc
	if mnt == recommendedMount {
		return Recommended, out
	}
	return Supported, out
}

func HandleRequest(w http.ResponseWriter, containerManager manager.Manager) error {
	// Get cAdvisor version Info.
	versionInfo, err := containerManager.GetVersionInfo()
	if err != nil {
		return err
	}

	out := fmt.Sprintf("cAdvisor version: %s\n\n", versionInfo.CadvisorVersion)

	// No OS is preferred or unsupported as of now.
	out += fmt.Sprintf("OS version: %s\n\n", versionInfo.ContainerOsVersion)

	kernelValidation, desc := validateKernelVersion(versionInfo.KernelVersion)
	out += fmt.Sprintf(OutputFormat, "Kernel version", kernelValidation, desc)

	cgroupValidation, desc := validateCgroups()
	out += fmt.Sprintf(OutputFormat, "Cgroup setup", cgroupValidation, desc)

	mountsValidation, desc := validateCgroupMounts()
	out += fmt.Sprintf(OutputFormat, "Cgroup mount setup", mountsValidation, desc)

	dockerValidation, desc := validateDockerVersion(versionInfo.DockerVersion)
	out += fmt.Sprintf(OutputFormat, "Docker version", dockerValidation, desc)

	dockerInfoValidation, desc := validateDockerInfo()
	out += fmt.Sprintf(OutputFormat, "Docker driver setup", dockerInfoValidation, desc)

	_, err = w.Write([]byte(out))
	return err
}
<commit_msg>Relax the kernel version string format.<commit_after>// Copyright 2014 Google Inc. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

// Handler for /validate content.
// Validates cadvisor dependencies - kernel, os, docker setup.

package validate

import (
	"fmt"
	"github.com/google/cadvisor/manager"
	"io/ioutil"
	"log"
	"net/http"
	"strings"

	"github.com/docker/libcontainer/cgroups"
	dclient "github.com/fsouza/go-dockerclient"
	"github.com/google/cadvisor/container/docker"
	"github.com/google/cadvisor/utils"
)

const ValidatePage = "/validate/"
const Supported = "[Supported, but not recommended]"
const Unsupported = "[Unsupported]"
const Recommended = "[Supported and recommended]"
const Unknown = "[Unknown]"
const VersionFormat = "%d.%d%s"
const OutputFormat = "%s: %s\n\t%s\n\n"

func getMajorMinor(version string) (int, int, error) {
	var major, minor int
	var ign string
	n, err := fmt.Sscanf(version, VersionFormat, &major, &minor, &ign)
	if n != 3 || err != nil {
		log.Printf("Failed to parse version for %s", version)
		return -1, -1, err
	}
	return major, minor, nil
}

func validateKernelVersion(version string) (string, string) {
	desc := fmt.Sprintf("Kernel version is %s. Versions >= 2.6 are supported. 3.0+ are recommended.\n", version)
	major, minor, err := getMajorMinor(version)
	if err != nil {
		desc = fmt.Sprintf("Could not parse kernel version. %s", desc)
		return Unknown, desc
	}

	if major < 2 {
		return Unsupported, desc
	}

	if major == 2 && minor < 6 {
		return Unsupported, desc
	}

	if major >= 3 {
		return Recommended, desc
	}

	return Supported, desc
}

func validateDockerVersion(version string) (string, string) {
	desc := fmt.Sprintf("Docker version is %s. Versions >= 1.0 are supported. 1.2+ are recommended.\n", version)
	major, minor, err := getMajorMinor(version)
	if err != nil {
		desc = fmt.Sprintf("Could not parse docker version. %s\n\t", desc)
		return Unknown, desc
	}
	if major < 1 {
		return Unsupported, desc
	}

	if major == 1 && minor < 2 {
		return Supported, desc
	}

	return Recommended, desc
}

func getEnabledCgroups() (map[string]int, error) {
	out, err := ioutil.ReadFile("/proc/cgroups")
	if err != nil {
		return nil, err
	}
	cgroups := make(map[string]int)
	for i, line := range strings.Split(string(out), "\n") {
		var cgroup string
		var ign, enabled int
		if i == 0 || line == "" {
			continue
		}
		n, err := fmt.Sscanf(line, "%s %d %d %d", &cgroup, &ign, &ign, &enabled)
		if n != 4 || err != nil {
			if err == nil {
				err = fmt.Errorf("failed to parse /proc/cgroup entry %s", line)
			}
			return nil, err
		}
		cgroups[cgroup] = enabled
	}
	return cgroups, nil
}

func areCgroupsPresent(available map[string]int, desired []string) (bool, string) {
	for _, cgroup := range desired {
		enabled, ok := available[cgroup]
		if !ok {
			reason := fmt.Sprintf("Missing cgroup %s. Available cgroups: %v\n", cgroup, available)
			return false, reason
		}
		if enabled != 1 {
			reason := fmt.Sprintf("Cgroup %s not enabled. Available cgroups: %v\n", cgroup, available)
			return false, reason
		}
	}
	return true, ""
}

func validateCgroups() (string, string) {
	required_cgroups := []string{"cpu", "cpuacct"}
	recommended_cgroups := []string{"memory", "blkio", "cpuset", "devices", "freezer"}
	available_cgroups, err := getEnabledCgroups()
	desc := fmt.Sprintf("\tFollowing cgroups are required: %v\n\tFollowing other cgroups are recommended: %v\n", required_cgroups, recommended_cgroups)
	if err != nil {
		desc = fmt.Sprintf("Could not parse /proc/cgroups.\n%s", desc)
		return Unknown, desc
	}
	ok, out := areCgroupsPresent(available_cgroups, required_cgroups)
	if !ok {
		out += desc
		return Unsupported, out
	}
	ok, out = areCgroupsPresent(available_cgroups, recommended_cgroups)
	if !ok {
		// supported, but not recommended.
		out += desc
		return Supported, out
	}
	out = fmt.Sprintf("Available cgroups: %v\n", available_cgroups)
	out += desc
	return Recommended, out
}

func validateDockerInfo() (string, string) {
	client, err := dclient.NewClient(*docker.ArgDockerEndpoint)
	if err == nil {
		info, err := client.Info()
		if err == nil {
			execDriver := info.Get("ExecutionDriver")
			storageDriver := info.Get("Driver")
			desc := fmt.Sprintf("Docker exec driver is %s. Storage driver is %s.\n", execDriver, storageDriver)
			if docker.UseSystemd() {
				desc += "\tsystemd is being used to create cgroups.\n"
			} else {
				desc += "\tCgroups are being created through cgroup filesystem.\n"
			}
			if strings.Contains(execDriver, "native") {
				return Recommended, desc
			} else if strings.Contains(execDriver, "lxc") {
				return Supported, desc
			}
			return Unknown, desc
		}
	}
	return Unknown, "Docker remote API not reachable\n\t"
}

func validateCgroupMounts() (string, string) {
	const recommendedMount = "/sys/fs/cgroup"
	desc := fmt.Sprintf("\tAny cgroup mount point that is detectible and accessible is supported. %s is recommended as a standard location.\n", recommendedMount)
	mnt, err := cgroups.FindCgroupMountpoint("cpu")
	if err != nil {
		out := "Could not locate cgroup mount point.\n"
		out += desc
		return Unknown, out
	}
	mnt = strings.TrimSuffix(mnt, "/cpu")
	if !utils.FileExists(mnt) {
		out := fmt.Sprintf("Cgroup mount directory %s inaccessible.\n", mnt)
		out += desc
		return Unsupported, out
	}
	out := fmt.Sprintf("Cgroups are mounted at %s.\n", mnt)
	out += desc
	if mnt == recommendedMount {
		return Recommended, out
	}
	return Supported, out
}

func HandleRequest(w http.ResponseWriter, containerManager manager.Manager) error {
	// Get cAdvisor version Info.
	versionInfo, err := containerManager.GetVersionInfo()
	if err != nil {
		return err
	}

	out := fmt.Sprintf("cAdvisor version: %s\n\n", versionInfo.CadvisorVersion)

	// No OS is preferred or unsupported as of now.
	out += fmt.Sprintf("OS version: %s\n\n", versionInfo.ContainerOsVersion)

	kernelValidation, desc := validateKernelVersion(versionInfo.KernelVersion)
	out += fmt.Sprintf(OutputFormat, "Kernel version", kernelValidation, desc)

	cgroupValidation, desc := validateCgroups()
	out += fmt.Sprintf(OutputFormat, "Cgroup setup", cgroupValidation, desc)

	mountsValidation, desc := validateCgroupMounts()
	out += fmt.Sprintf(OutputFormat, "Cgroup mount setup", mountsValidation, desc)

	dockerValidation, desc := validateDockerVersion(versionInfo.DockerVersion)
	out += fmt.Sprintf(OutputFormat, "Docker version", dockerValidation, desc)

	dockerInfoValidation, desc := validateDockerInfo()
	out += fmt.Sprintf(OutputFormat, "Docker driver setup", dockerInfoValidation, desc)

	_, err = w.Write([]byte(out))
	return err
}
<|endoftext|>