<commit_before>package main

import (
	"io/ioutil"
	"log"
	"os"
	"path"
	"sort"
	"strconv"
	"strings"
	"testing"
	"time"
)

const testContent string = `# This is some content
And some more content

## It was the best of times.

### It was the worst of times.
Charles Dickens Charles Dickens`

var testHTML []string = []string{
	"
This is some content
",
	"
And some more content
",
	"
It was the best of times.
",
	"
It was the worst of times.
",
	"
Charles Dickens Charles Dickens
",
}

func testOnePost(t *testing.T, title, date, tags string, includePostHeaderLine bool,
	content string) {

	t.Logf("Testing title: %s, date: %s, tags: \"%s\", postHeaderLine: %s",
		title, date, tags, strconv.FormatBool(includePostHeaderLine))

	var expectedTime time.Time
	expectedTags := make(map[string]bool)
	var err error
	validPost := true

	f, err := ioutil.TempFile("", "testPost")
	if err != nil {
		t.Fatal("Creating test post:", err)
	}

	if title != "MISSING" {
		f.WriteString(title + "\n")
	} else {
		validPost = false
	}

	if date != "MISSING" {
		f.WriteString(date + "\n")
		expectedTime, err = time.Parse("1/2/06 3:04PM MST", strings.TrimSpace(date))
		if err != nil {
			validPost = false
		}
	} else {
		validPost = false
	}

	if tags != "MISSING" {
		f.WriteString(tags + "\n")

		tagsList := strings.Split(tags, ",")
		for _, tag := range tagsList {
			tag = strings.Title(strings.TrimSpace(tag))
			expectedTags[tag] = true
		}
	} else {
		validPost = false
	}

	if includePostHeaderLine {
		f.WriteString("\n")
	} else {
		validPost = false
	}

	if content != "MISSING" {
		f.WriteString(content)
	} else {
		// Missing content is actually ok...
		content = ""
	}

	filename := f.Name()
	f.Close()

	defer os.Remove("filename")

	postWithContent, err := NewPost(filename, true)
	if validPost && err != nil {
		t.Error("Expected valid post but saw error", err)
		return
	} else if !validPost {
		if err == nil && postWithContent != nil {
			t.Error("Expected invalid post but parsing succeeded")
		}
		return
	}

	if postWithContent.SourcePath != filename {
		t.Errorf("Expected SourcePath %s, saw %s", filename, postWithContent.SourcePath)
	}

	expectedTitle := strings.TrimSpace(title)
	if postWithContent.Title != expectedTitle {
		t.Errorf("Expected title \"%s\", saw \"%s\"", expectedTitle, postWithContent.Title)
	}

	if !postWithContent.Timestamp.Equal(expectedTime) {
		t.Errorf("Expected time \"%s\", saw \"%s\"", expectedTime, postWithContent.Timestamp)
	}

	for _, tag := range postWithContent.Tags {
		_, ok := expectedTags[tag]
		if !ok {
			t.Error("Unexpected tag", tag)
		}
		delete(expectedTags, tag)
	}

	for tag := range expectedTags {
		t.Error("Did not see expected tag", tag)
	}

	if string(postWithContent.Content) != content {
		t.Errorf("Expected content: %s\nSaw content: %s", content)
	}

	htmlContent := string(postWithContent.HTMLContent())

	if len(content) != 0 {
		for _, expectedHTML := range testHTML {
			if !strings.Contains(htmlContent, expectedHTML) {
				t.Errorf("Expected HTML to contain: %s\nSaw HTML: %s", testHTML, htmlContent)
			}
		}
	}

	postWithoutContent, err := NewPost(filename, false)
	if err != nil {
		t.Error("Error loading post without content:", err)
	}

	if postWithoutContent.SourcePath != filename {
		t.Errorf("Post without content expected SourcePath %s, saw %s",
			filename, postWithoutContent.SourcePath)
	}

	if postWithoutContent.Title != expectedTitle {
		t.Errorf("Post without content expected title %s, saw %s",
			expectedTitle, postWithoutContent.Title)
	}

	if !postWithoutContent.Timestamp.Equal(expectedTime) {
		t.Errorf("Post without content expected time %s, saw %s",
			expectedTime, postWithoutContent.Timestamp)
	}

	for i := range postWithContent.Tags {
		if i >= len(postWithoutContent.Tags) {
			t.Errorf("Post without content is missing tag %d", i)
		} else if postWithContent.Tags[i] != postWithoutContent.Tags[i] {
			t.Errorf("Post without content expected tag #%d %s, has %s",
				i, postWithContent.Tags[i], postWithoutContent.Tags[i])
		}
	}

	if postWithoutContent.Content != nil {
		t.Error("Post without content had content", postWithoutContent.Content)
	}
}

func TestNewPost(t *testing.T) {
	titles := []string{"Valid Title", " Extra spaces ", "", "MISSING"}
	dates := []string{"10/12/14 4:15PM MST", "6/4/12 3:57AM CDT  ",
		"", "MISSING"}
	tags := []string{"onetag", "tag1,tag 2", "a long tag", "tag 1, tag2, tag 3", "", "MISSING"}
	contents := []string{testContent, "", "MISSING"}
	includePostHeaderLine := []bool{true, false}

	for _, title := range titles {
		for _, date := range dates {
			for _, tag := range tags {
				for _, include := range includePostHeaderLine {
					for _, content := range contents {
						testOnePost(t, title, date, tag, include, content)
						// if t.Failed() {
						// 	// Quit early if we fail to ease debugging.
						// 	t.FailNow()
						// }
					}
				}
			}
		}
	}

	f, err := ioutil.TempFile("", "temppost")
	if err != nil {
		t.Fatal("Failed to create temp file")
	}
	filename := f.Name()
	// Make the file unreadable.
	f.Chmod(0200)
	f.Close()
	defer os.Remove(filename)

	_, err = NewPost(filename, true)
	if err == nil {
		t.Error("No error returned when reading unreadable post")
	}

}

var testPosts []*Post

func createTestPosts(t *testing.T) {
	// Can't call time.Parse in a static initializer, so we make this here.
	testPosts = make([]*Post, 3)

	postTime, err := time.Parse(PostTimeFormat, "1/2/14 4:15PM MST")
	if err != nil {
		t.Fatal("Invalid time in createTestPosts #0")
	}
	testPosts[0] = &Post{"2014/02/test-post1",
		"TestPost1",
		postTime,
		[]string{"tag1", "tag2"},
		[]byte("content")}

	postTime, err = time.Parse(PostTimeFormat, "1/3/14 4:15PM MST")
	if err != nil {
		t.Fatal("Invalid time in createTestPosts #0")
	}
	testPosts[1] = &Post{"2014/02/test-post2",
		"TestPost2",
		postTime,
		[]string{"tag1"},
		[]byte("content")}

	postTime, err = time.Parse(PostTimeFormat, "1/2/12 4:15PM MST")
	if err != nil {
		t.Fatal("Invalid time in createTestPosts #0")
	}
	testPosts[2] = &Post{"2012/02/test-post3",
		"TestPost3",
		postTime,
		[]string{"tag2"},
		[]byte("content")}
}

func writePost(t *testing.T, postPath string, post *Post) {
	dir, _ := path.Split(post.SourcePath)
	dir = path.Join(postPath, dir)
	err := os.MkdirAll(dir, 0755)
	if err != nil {
		t.Fatalf("Error making directory %s: %s", dir, err)
	}

	fullPath := path.Join(postPath, post.SourcePath)
	f, err := os.Create(fullPath)
	if err != nil {
		t.Fatalf("Error creating %s: %s", fullPath, err)
	}
	defer f.Close()

	f.WriteString(post.Title + "\n")
	postTime := post.Timestamp.Format("1/2/06 3:04PM MST")
	f.WriteString(postTime + "\n")
	f.WriteString(strings.Join(post.Tags, ",") + "\n\n")
	f.Write(post.Content)
}

func createPostTree(t *testing.T) (dirPath string) {
	createTestPosts(t)
	dirPath, err := ioutil.TempDir("", "testposts")
	if err != nil {
		t.Fatal("Could not create temporary directory:", err)
	}

	for _, post := range testPosts {
		writePost(t, dirPath, post)
	}

	return dirPath
}

type WriteCapturer struct {
	data [][]byte
}

func (w *WriteCapturer) Write(data []byte) (n int, err error) {
	if w.data == nil {
		w.data = make([][]byte, 0)
	}
	w.data = append(w.data, data)
	return len(data), nil
}

func TestLoadPostsFromPath(t *testing.T) {
	writeCapturer := &WriteCapturer{}
	logger = log.New(writeCapturer, "testlog", log.LstdFlags)
	dir := createPostTree(t)
	defer os.RemoveAll(dir)

	checkPostList := func(postList PostList, expected []string) {
		if len(postList) != len(expected) {
			t.Errorf("Expected %d posts but found %d",
				len(expected), len(postList))
		}

		for i, exp := range expected {
			if postList[i].SourcePath != exp {
				t.Errorf("Expected post 0 to be %s, saw %s",
					exp, postList[i].SourcePath)
			}
		}

	}

	t.Log("Loading 3 valid posts")
	expectedSorted := []string{"2012/02/test-post3",
		"2014/02/test-post1",
		"2014/02/test-post2"}
	for i, val := range expectedSorted {
		expectedSorted[i] = path.Join(dir, val)
	}

	postList, err := LoadPostsFromPath(dir, true)
	sort.Sort(postList)

	checkPostList(postList, expectedSorted)

	t.Log("Testing with 3 valid posts and 1 invalid post")
	ioutil.WriteFile(path.Join(dir, "2012/02/invalidpost"), []byte("Invalid post"), 0666)
	postList, err = LoadPostsFromPath(dir, true)
	if err == nil {
		t.Error("LoadPostsFromPath did not propagate error from NewPost")
	}
	if postList == nil || len(postList) != 3 {
		t.Error("LoadPostsFromPath did not load valid posts because of one invalid post.")
	}
	sort.Sort(postList)
	checkPostList(postList, expectedSorted)
	if len(writeCapturer.data) != 1 {
		t.Errorf("Expected 1 log message, saw %d", len(writeCapturer.data))
	}

	t.Log("Testing load from invalid path")
	_, err = LoadPostsFromPath("/jklsdfjklfds hjlksdfj", true)
	if err == nil {
		t.Error("No error returned on loading from nonexistent path.")
	}
}

func TestArchiveSpecList(t *testing.T) {
	checkSpecList := func(expected, actual ArchiveSpecList) {
		if len(expected) != len(actual) {
			t.Errorf("Expected %d specs but saw %d",
				len(expected), len(actual))
			return
		}

		for i, spec := range expected {
			if !time.Time(spec).Equal(time.Time(actual[i])) {
				t.Errorf("Expected spec #%d %s, saw %s",
					i, spec, actual[i])
			}
		}
	}

	writeCapturer := &WriteCapturer{}
	logger = log.New(writeCapturer, "testlog", log.LstdFlags)

	dir := createPostTree(t)
	defer os.RemoveAll(dir)

	expectedSpecList := ArchiveSpecList{
		ArchiveSpec(time.Date(2014, time.Month(02), 1, 1, 1, 1, 1, time.UTC)),
		ArchiveSpec(time.Date(2012, time.Month(02), 1, 1, 1, 1, 1, time.UTC)),
	}

	t.Log("Test normal spec list creation")
	specList, err := NewArchiveSpecList(dir)
	if err != nil {
		t.Error("Error creating spec list")
	}
	sort.Sort(specList)
	checkSpecList(expectedSpecList, specList)

	href := specList[0].Href()
	if href != "/2014/02" {
		t.Errorf("Expected href %s, saw %s", "/2014/02", href)
	}

	text := specList[0].String()
	if text != "Feb 2014" {
		t.Errorf("Expected text %s, saw %s", "Feb 2014", "text")
	}

	t.Log("Test with some non-numeric directories")
	os.Mkdir(path.Join(dir, "abc"), 0755)
	os.Mkdir(path.Join(dir, "2012", "abc"), 0755)
	specList, err = NewArchiveSpecList(dir)
	sort.Sort(specList)
	checkSpecList(expectedSpecList, specList)

	_, err = NewArchiveSpecList("/jklsdfnkjlse kslef")
	if err == nil {
		t.Error("NewArchiveSpecList did not fail with invalid directory")
	}
}
<commit_msg>Use PostTimeFormat instead of hardcoding<commit_after>package main

import (
	"io/ioutil"
	"log"
	"os"
	"path"
	"sort"
	"strconv"
	"strings"
	"testing"
	"time"
)

const testContent string = `# This is some content
And some more content

## It was the best of times.

### It was the worst of times.
Charles Dickens Charles Dickens`

var testHTML []string = []string{
	"
This is some content
",
	"
And some more content
",
	"
It was the best of times.
",
	"
It was the worst of times.
",
	"
Charles Dickens Charles Dickens
",
}

func testOnePost(t *testing.T, title, date, tags string, includePostHeaderLine bool,
	content string) {

	t.Logf("Testing title: %s, date: %s, tags: \"%s\", postHeaderLine: %s",
		title, date, tags, strconv.FormatBool(includePostHeaderLine))

	var expectedTime time.Time
	expectedTags := make(map[string]bool)
	var err error
	validPost := true

	f, err := ioutil.TempFile("", "testPost")
	if err != nil {
		t.Fatal("Creating test post:", err)
	}

	if title != "MISSING" {
		f.WriteString(title + "\n")
	} else {
		validPost = false
	}

	if date != "MISSING" {
		f.WriteString(date + "\n")
		expectedTime, err = time.Parse(PostTimeFormat, strings.TrimSpace(date))
		if err != nil {
			validPost = false
		}
	} else {
		validPost = false
	}

	if tags != "MISSING" {
		f.WriteString(tags + "\n")

		tagsList := strings.Split(tags, ",")
		for _, tag := range tagsList {
			tag = strings.Title(strings.TrimSpace(tag))
			expectedTags[tag] = true
		}
	} else {
		validPost = false
	}

	if includePostHeaderLine {
		f.WriteString("\n")
	} else {
		validPost = false
	}

	if content != "MISSING" {
		f.WriteString(content)
	} else {
		// Missing content is actually ok...
		content = ""
	}

	filename := f.Name()
	f.Close()

	defer os.Remove("filename")

	postWithContent, err := NewPost(filename, true)
	if validPost && err != nil {
		t.Error("Expected valid post but saw error", err)
		return
	} else if !validPost {
		if err == nil && postWithContent != nil {
			t.Error("Expected invalid post but parsing succeeded")
		}
		return
	}

	if postWithContent.SourcePath != filename {
		t.Errorf("Expected SourcePath %s, saw %s", filename, postWithContent.SourcePath)
	}

	expectedTitle := strings.TrimSpace(title)
	if postWithContent.Title != expectedTitle {
		t.Errorf("Expected title \"%s\", saw \"%s\"", expectedTitle, postWithContent.Title)
	}

	if !postWithContent.Timestamp.Equal(expectedTime) {
		t.Errorf("Expected time \"%s\", saw \"%s\"", expectedTime, postWithContent.Timestamp)
	}

	for _, tag := range postWithContent.Tags {
		_, ok := expectedTags[tag]
		if !ok {
			t.Error("Unexpected tag", tag)
		}
		delete(expectedTags, tag)
	}

	for tag := range expectedTags {
		t.Error("Did not see expected tag", tag)
	}

	if string(postWithContent.Content) != content {
		t.Errorf("Expected content: %s\nSaw content: %s", content)
	}

	htmlContent := string(postWithContent.HTMLContent())

	if len(content) != 0 {
		for _, expectedHTML := range testHTML {
			if !strings.Contains(htmlContent, expectedHTML) {
				t.Errorf("Expected HTML to contain: %s\nSaw HTML: %s", testHTML, htmlContent)
			}
		}
	}

	postWithoutContent, err := NewPost(filename, false)
	if err != nil {
		t.Error("Error loading post without content:", err)
	}

	if postWithoutContent.SourcePath != filename {
		t.Errorf("Post without content expected SourcePath %s, saw %s",
			filename, postWithoutContent.SourcePath)
	}

	if postWithoutContent.Title != expectedTitle {
		t.Errorf("Post without content expected title %s, saw %s",
			expectedTitle, postWithoutContent.Title)
	}

	if !postWithoutContent.Timestamp.Equal(expectedTime) {
		t.Errorf("Post without content expected time %s, saw %s",
			expectedTime, postWithoutContent.Timestamp)
	}

	for i := range postWithContent.Tags {
		if i >= len(postWithoutContent.Tags) {
			t.Errorf("Post without content is missing tag %d", i)
		} else if postWithContent.Tags[i] != postWithoutContent.Tags[i] {
			t.Errorf("Post without content expected tag #%d %s, has %s",
				i, postWithContent.Tags[i], postWithoutContent.Tags[i])
		}
	}

	if postWithoutContent.Content != nil {
		t.Error("Post without content had content", postWithoutContent.Content)
	}
}

func TestNewPost(t *testing.T) {
	titles := []string{"Valid Title", " Extra spaces ", "", "MISSING"}
	dates := []string{"10/12/14 4:15PM MST", "6/4/12 3:57AM CDT  ",
		"", "MISSING"}
	tags := []string{"onetag", "tag1,tag 2", "a long tag", "tag 1, tag2, tag 3", "", "MISSING"}
	contents := []string{testContent, "", "MISSING"}
	includePostHeaderLine := []bool{true, false}

	for _, title := range titles {
		for _, date := range dates {
			for _, tag := range tags {
				for _, include := range includePostHeaderLine {
					for _, content := range contents {
						testOnePost(t, title, date, tag, include, content)
						// if t.Failed() {
						// 	// Quit early if we fail to ease debugging.
						// 	t.FailNow()
						// }
					}
				}
			}
		}
	}

	f, err := ioutil.TempFile("", "temppost")
	if err != nil {
		t.Fatal("Failed to create temp file")
	}
	filename := f.Name()
	// Make the file unreadable.
	f.Chmod(0200)
	f.Close()
	defer os.Remove(filename)

	_, err = NewPost(filename, true)
	if err == nil {
		t.Error("No error returned when reading unreadable post")
	}

}

var testPosts []*Post

func createTestPosts(t *testing.T) {
	// Can't call time.Parse in a static initializer, so we make this here.
	testPosts = make([]*Post, 3)

	postTime, err := time.Parse(PostTimeFormat, "1/2/14 4:15PM MST")
	if err != nil {
		t.Fatal("Invalid time in createTestPosts #0")
	}
	testPosts[0] = &Post{"2014/02/test-post1",
		"TestPost1",
		postTime,
		[]string{"tag1", "tag2"},
		[]byte("content")}

	postTime, err = time.Parse(PostTimeFormat, "1/3/14 4:15PM MST")
	if err != nil {
		t.Fatal("Invalid time in createTestPosts #0")
	}
	testPosts[1] = &Post{"2014/02/test-post2",
		"TestPost2",
		postTime,
		[]string{"tag1"},
		[]byte("content")}

	postTime, err = time.Parse(PostTimeFormat, "1/2/12 4:15PM MST")
	if err != nil {
		t.Fatal("Invalid time in createTestPosts #0")
	}
	testPosts[2] = &Post{"2012/02/test-post3",
		"TestPost3",
		postTime,
		[]string{"tag2"},
		[]byte("content")}
}

func writePost(t *testing.T, postPath string, post *Post) {
	dir, _ := path.Split(post.SourcePath)
	dir = path.Join(postPath, dir)
	err := os.MkdirAll(dir, 0755)
	if err != nil {
		t.Fatalf("Error making directory %s: %s", dir, err)
	}

	fullPath := path.Join(postPath, post.SourcePath)
	f, err := os.Create(fullPath)
	if err != nil {
		t.Fatalf("Error creating %s: %s", fullPath, err)
	}
	defer f.Close()

	f.WriteString(post.Title + "\n")
	postTime := post.Timestamp.Format("1/2/06 3:04PM MST")
	f.WriteString(postTime + "\n")
	f.WriteString(strings.Join(post.Tags, ",") + "\n\n")
	f.Write(post.Content)
}

func createPostTree(t *testing.T) (dirPath string) {
	createTestPosts(t)
	dirPath, err := ioutil.TempDir("", "testposts")
	if err != nil {
		t.Fatal("Could not create temporary directory:", err)
	}

	for _, post := range testPosts {
		writePost(t, dirPath, post)
	}

	return dirPath
}

type WriteCapturer struct {
	data [][]byte
}

func (w *WriteCapturer) Write(data []byte) (n int, err error) {
	if w.data == nil {
		w.data = make([][]byte, 0)
	}
	w.data = append(w.data, data)
	return len(data), nil
}

func TestLoadPostsFromPath(t *testing.T) {
	writeCapturer := &WriteCapturer{}
	logger = log.New(writeCapturer, "testlog", log.LstdFlags)
	dir := createPostTree(t)
	defer os.RemoveAll(dir)

	checkPostList := func(postList PostList, expected []string) {
		if len(postList) != len(expected) {
			t.Errorf("Expected %d posts but found %d",
				len(expected), len(postList))
		}

		for i, exp := range expected {
			if postList[i].SourcePath != exp {
				t.Errorf("Expected post 0 to be %s, saw %s",
					exp, postList[i].SourcePath)
			}
		}

	}

	t.Log("Loading 3 valid posts")
	expectedSorted := []string{"2012/02/test-post3",
		"2014/02/test-post1",
		"2014/02/test-post2"}
	for i, val := range expectedSorted {
		expectedSorted[i] = path.Join(dir, val)
	}

	postList, err := LoadPostsFromPath(dir, true)
	sort.Sort(postList)

	checkPostList(postList, expectedSorted)

	t.Log("Testing with 3 valid posts and 1 invalid post")
	ioutil.WriteFile(path.Join(dir, "2012/02/invalidpost"), []byte("Invalid post"), 0666)
	postList, err = LoadPostsFromPath(dir, true)
	if err == nil {
		t.Error("LoadPostsFromPath did not propagate error from NewPost")
	}
	if postList == nil || len(postList) != 3 {
		t.Error("LoadPostsFromPath did not load valid posts because of one invalid post.")
	}
	sort.Sort(postList)
	checkPostList(postList, expectedSorted)
	if len(writeCapturer.data) != 1 {
		t.Errorf("Expected 1 log message, saw %d", len(writeCapturer.data))
	}

	t.Log("Testing load from invalid path")
	_, err = LoadPostsFromPath("/jklsdfjklfds hjlksdfj", true)
	if err == nil {
		t.Error("No error returned on loading from nonexistent path.")
	}
}

func TestArchiveSpecList(t *testing.T) {
	checkSpecList := func(expected, actual ArchiveSpecList) {
		if len(expected) != len(actual) {
			t.Errorf("Expected %d specs but saw %d",
				len(expected), len(actual))
			return
		}

		for i, spec := range expected {
			if !time.Time(spec).Equal(time.Time(actual[i])) {
				t.Errorf("Expected spec #%d %s, saw %s",
					i, spec, actual[i])
			}
		}
	}

	writeCapturer := &WriteCapturer{}
	logger = log.New(writeCapturer, "testlog", log.LstdFlags)

	dir := createPostTree(t)
	defer os.RemoveAll(dir)

	expectedSpecList := ArchiveSpecList{
		ArchiveSpec(time.Date(2014, time.Month(02), 1, 1, 1, 1, 1, time.UTC)),
		ArchiveSpec(time.Date(2012, time.Month(02), 1, 1, 1, 1, 1, time.UTC)),
	}

	t.Log("Test normal spec list creation")
	specList, err := NewArchiveSpecList(dir)
	if err != nil {
		t.Error("Error creating spec list")
	}
	sort.Sort(specList)
	checkSpecList(expectedSpecList, specList)

	href := specList[0].Href()
	if href != "/2014/02" {
		t.Errorf("Expected href %s, saw %s", "/2014/02", href)
	}

	text := specList[0].String()
	if text != "Feb 2014" {
		t.Errorf("Expected text %s, saw %s", "Feb 2014", "text")
	}

	t.Log("Test with some non-numeric directories")
	os.Mkdir(path.Join(dir, "abc"), 0755)
	os.Mkdir(path.Join(dir, "2012", "abc"), 0755)
	specList, err = NewArchiveSpecList(dir)
	sort.Sort(specList)
	checkSpecList(expectedSpecList, specList)

	_, err = NewArchiveSpecList("/jklsdfnkjlse kslef")
	if err == nil {
		t.Error("NewArchiveSpecList did not fail with invalid directory")
	}
}
<|endoftext|>


<commit_before>// Praelatus is an Open Source bug tracking and ticketing system. The
// backend API is written in Go and the frontend is a React.js
// app. You are viewing the Godoc for the API if you would like
// information about how to use the API as a client or how to start
// working on the backend visit https://docs.praelatus.io
package main

import (
	"os"

	"github.com/praelatus/praelatus/cli"
)

func main() {
	cli.Run(os.Args)
}
<commit_msg>fix bad import<commit_after>// Praelatus is an Open Source bug tracking and ticketing system. The
// backend API is written in Go and the frontend is a React.js
// app. You are viewing the Godoc for the API if you would like
// information about how to use the API as a client or how to start
// working on the backend visit https://docs.praelatus.io
package main

import (
	"os"

	"github.com/praelatus/backend/cli"
)

func main() {
	cli.Run(os.Args)
}
<|endoftext|>


<commit_before>package git

import (
	"context"
	"fmt"
	"io"
	"io/ioutil"

	. "github.com/polydawn/go-errcat"
	"gopkg.in/src-d/go-git.v4/plumbing/filemode"
	"gopkg.in/src-d/go-git.v4/plumbing/object"

	"go.polydawn.net/go-timeless-api"
	"go.polydawn.net/go-timeless-api/rio"
	"go.polydawn.net/go-timeless-api/util"
	"go.polydawn.net/rio/config"
	"go.polydawn.net/rio/fs"
	"go.polydawn.net/rio/fs/osfs"
	"go.polydawn.net/rio/fsOp"
	"go.polydawn.net/rio/transmat/mixins/cache"
	"go.polydawn.net/rio/transmat/mixins/filters"
	"go.polydawn.net/rio/transmat/mixins/fshash"
	gitWarehouse "go.polydawn.net/rio/warehouse/impl/git"
)

var (
	_ rio.UnpackFunc = Unpack
)

func Unpack(
	ctx context.Context, // Long-running call.  Cancellable.
	wareID api.WareID, // What wareID to fetch for unpacking.
	path string, // Where to unpack the fileset (absolute path).
	filt api.FilesetFilters, // Optionally: filters we should apply while unpacking.
	placementMode rio.PlacementMode, // Optionally: a placement mode (default is "copy").
	warehouses []api.WarehouseAddr, // Warehouses we can try to fetch from.
	mon rio.Monitor, // Optionally: callbacks for progress monitoring.
) (_ api.WareID, err error) {
	if mon.Chan != nil {
		defer close(mon.Chan)
	}
	defer RequireErrorHasCategory(&err, rio.ErrorCategory(""))

	// Sanitize arguments.
	if wareID.Type != PackType {
		return api.WareID{}, Errorf(rio.ErrUsage, "this transmat implementation only supports packtype %q (not %q)", PackType, wareID.Type)
	}
	if placementMode == "" {
		placementMode = rio.Placement_Copy
	}
	// Wrap the direct unpack func with cache behavior; call that.
	return cache.Lrn2Cache(
		osfs.New(config.GetCacheBasePath()),
		unpack,
	)(ctx, wareID, path, filt, placementMode, warehouses, mon)
}

func unpack(
	ctx context.Context,
	wareID api.WareID,
	path string,
	filt api.FilesetFilters,
	placementMode rio.PlacementMode,
	warehouses []api.WarehouseAddr,
	mon rio.Monitor,
) (_ api.WareID, err error) {
	defer RequireErrorHasCategory(&err, rio.ErrorCategory(""))

	// Sanitize arguments.
	path2 := fs.MustAbsolutePath(path)
	filt2, err := apiutil.ProcessFilters(filt, apiutil.FilterPurposeUnpack)
	if err != nil {
		return api.WareID{}, Errorf(rio.ErrUsage, "invalid filter specification: %s", err)
	}

	// Pick a warehouse and get a reader.
	//  This is a *very* expensive operation for git.  It's less
	//  of "pick a warehouse" and more "download the whole thing and hope we
	//  get what we wanted" (which is very ironic for a system that has
	//  a CAS system on its inside, yes).
	whCtrl, err := pick(ctx,
		wareID,
		warehouses,
		osfs.New(config.GetCacheBasePath().Join(fs.MustRelPath("git/objs"))),
		mon,
	)
	if err != nil {
		return api.WareID{}, err
	}

	// Get submodule config.  Fetch them all.
	submodules, err := whCtrl.Submodules(wareID.Hash)
	if err != nil {
		return api.WareID{}, err
	}
	// We'll organize them by path now; only thing that's useful.
	submoduleCtrls := map[string]*gitWarehouse.Controller{}
	for _, submCfg := range submodules {
		// TODO it would be dreamy to parallelize this.
		whCtrl, err := pick(ctx,
			api.WareID{"git", submCfg.Hash},
			[]api.WarehouseAddr{api.WarehouseAddr(submCfg.URL)},
			osfs.New(config.GetCacheBasePath().Join(fs.MustRelPath("git/objs"))),
			mon,
		)
		if err != nil {
			return api.WareID{}, err
		}
		submoduleCtrls[submCfg.Path] = whCtrl
	}

	// Open a tree to walk in the main repo.
	//  We'll do submodule checkouts somewhere deep in the middle of this.
	tr, err := whCtrl.GetTree(wareID.Hash)
	if err != nil {
		panic(err)
	}

	// Construct filesystem wrapper to use for all our ops.
	afs := osfs.New(path2)

	// Walk.
	if err := unpackOneRepo(ctx, tr, afs, true, filt2, submoduleCtrls, mon); err != nil {
		return api.WareID{}, err
	}

	// That's it.  Checkout should have already checked the hash, so we just return it.
	return wareID, nil
}

func unpackOneRepo(
	ctx context.Context,
	tr *object.Tree,
	afs fs.FS,
	isRoot bool, // if true, will recurse for submodules (with this set to false).
	filt apiutil.FilesetFilters,
	submoduleCtrls map[string]*gitWarehouse.Controller,
	mon rio.Monitor,
) (err error) {
	tw := object.NewTreeWalker(tr, true, nil)

	// Make the root dir.  Git doesn't have metadata for the tree root.
	conjuredFmeta := fshash.DefaultDirMetadata()
	filters.Apply(filt, &conjuredFmeta)
	if err := fsOp.PlaceFile(afs, conjuredFmeta, nil, filt.SkipChown); err != nil {
		return Errorf(rio.ErrInoperablePath, "error while unpacking: %s", err)
	}

	// Extract.
	// Iterate over each entry, mutating filesystem as we go.
	dirs := make([]fs.RelPath, 1, 200) // Keep for dir time repair at end.
	dirs[0] = fs.RelPath{}
	for {
		fmeta := fs.Metadata{}
		name, te, err := tw.Next()

		// Check for done.
		if err == io.EOF {
			break // sucess!  end of archive.
		}
		if err != nil {
			return Errorf(rio.ErrWareCorrupt, "corrupt git tree: %s", err)
		}
		if ctx.Err() != nil {
			return Errorf(rio.ErrCancelled, "cancelled")
		}
		//fmt.Fprintf(os.Stderr, "walking git tree %s -- %#v\n", name, te)

		// Reshuffle metainfo to our default format.
		fmeta.Name = fs.MustRelPath(name)
		switch te.Mode {
		case filemode.Dir:
			fmeta.Type = fs.Type_Dir
			fmeta.Perms = 0755
			dirs = append(dirs, fmeta.Name)
		case filemode.Regular:
			fmeta.Type = fs.Type_File
			fmeta.Perms = 0644
		case filemode.Executable:
			fmeta.Type = fs.Type_File
			fmeta.Perms = 0755
		case filemode.Symlink:
			fmeta.Type = fs.Type_Symlink
			fmeta.Perms = 0644
			// Hang on, extracting a symlink is actually rough.
			tf, err := tr.TreeEntryFile(&te)
			if err != nil {
				return Errorf(rio.ErrWareCorrupt, "corrupt git tree: %s", err)
			}
			reader, err := tf.Blob.Reader()
			if err != nil {
				return Errorf(rio.ErrWareCorrupt, "corrupt git tree: %s", err)
			}
			blob, err := ioutil.ReadAll(reader)
			if err != nil {
				return Errorf(rio.ErrWareCorrupt, "corrupt git tree: %s", err)
			}
			fmeta.Linkname = string(blob)
		case filemode.Submodule:
			// Ooowee!  Recurse time!
			submCtrl, ok := submoduleCtrls[name]
			if !ok {
				return Errorf(rio.ErrWareCorrupt, "gitlink found at path %q but no matching config in .gitmodules", name)
			}
			submTr, err := submCtrl.GetTree(te.Hash.String())
			if err != nil {
				panic(err)
			}
			submFs := osfs.New(afs.BasePath().Join(fmeta.Name))
			if err := unpackOneRepo(ctx, submTr, submFs, false, filt, nil, mon); err != nil {
				return err
			}
			continue
		case filemode.Empty:
			fallthrough
		case filemode.Deprecated:
			fallthrough
		default:
			panic(fmt.Errorf("unknown git filemode %#v", te.Mode))
		}
		fmeta.Mtime = apiutil.DefaultMtime // git doesn't have time info

		// Apply filters.
		filters.Apply(filt, &fmeta)

		// Place the file.
		switch fmeta.Type {
		case fs.Type_File:
			tf, err := tr.TreeEntryFile(&te)
			if err != nil {
				return Errorf(rio.ErrWareCorrupt, "corrupt git tree: %s", err)
			}
			reader, err := tf.Blob.Reader()
			if err != nil {
				return Errorf(rio.ErrWareCorrupt, "corrupt git tree: %s", err)
			}
			if err := fsOp.PlaceFile(afs, fmeta, reader, filt.SkipChown); err != nil {
				return Errorf(rio.ErrInoperablePath, "error while unpacking: %s", err)
			}
			reader.Close()
		default:
			if err := fsOp.PlaceFile(afs, fmeta, nil, filt.SkipChown); err != nil {
				return Errorf(rio.ErrInoperablePath, "error while unpacking: %s", err)
			}
		}
	}

	// Cleanup dir times with a post-order traversal over the bucket.
	//  Files and dirs placed inside dirs cause the parent's mtime to update, so we have to re-pave them.
	for i := len(dirs) - 1; i >= 0; i-- {
		if err := afs.SetTimesNano(dirs[i], fs.DefaultAtime, fs.DefaultAtime); err != nil {
			return Errorf(rio.ErrInoperablePath, "error while unpacking: %s", err)
		}
	}

	return nil
}
<commit_msg>git: default uid/gid settings<commit_after>package git

import (
	"context"
	"fmt"
	"io"
	"io/ioutil"

	. "github.com/polydawn/go-errcat"
	"gopkg.in/src-d/go-git.v4/plumbing/filemode"
	"gopkg.in/src-d/go-git.v4/plumbing/object"

	"go.polydawn.net/go-timeless-api"
	"go.polydawn.net/go-timeless-api/rio"
	"go.polydawn.net/go-timeless-api/util"
	"go.polydawn.net/rio/config"
	"go.polydawn.net/rio/fs"
	"go.polydawn.net/rio/fs/osfs"
	"go.polydawn.net/rio/fsOp"
	"go.polydawn.net/rio/transmat/mixins/cache"
	"go.polydawn.net/rio/transmat/mixins/filters"
	"go.polydawn.net/rio/transmat/mixins/fshash"
	gitWarehouse "go.polydawn.net/rio/warehouse/impl/git"
)

var (
	_ rio.UnpackFunc = Unpack
)

func Unpack(
	ctx context.Context, // Long-running call.  Cancellable.
	wareID api.WareID, // What wareID to fetch for unpacking.
	path string, // Where to unpack the fileset (absolute path).
	filt api.FilesetFilters, // Optionally: filters we should apply while unpacking.
	placementMode rio.PlacementMode, // Optionally: a placement mode (default is "copy").
	warehouses []api.WarehouseAddr, // Warehouses we can try to fetch from.
	mon rio.Monitor, // Optionally: callbacks for progress monitoring.
) (_ api.WareID, err error) {
	if mon.Chan != nil {
		defer close(mon.Chan)
	}
	defer RequireErrorHasCategory(&err, rio.ErrorCategory(""))

	// Sanitize arguments.
	if wareID.Type != PackType {
		return api.WareID{}, Errorf(rio.ErrUsage, "this transmat implementation only supports packtype %q (not %q)", PackType, wareID.Type)
	}
	if placementMode == "" {
		placementMode = rio.Placement_Copy
	}
	// Wrap the direct unpack func with cache behavior; call that.
	return cache.Lrn2Cache(
		osfs.New(config.GetCacheBasePath()),
		unpack,
	)(ctx, wareID, path, filt, placementMode, warehouses, mon)
}

func unpack(
	ctx context.Context,
	wareID api.WareID,
	path string,
	filt api.FilesetFilters,
	placementMode rio.PlacementMode,
	warehouses []api.WarehouseAddr,
	mon rio.Monitor,
) (_ api.WareID, err error) {
	defer RequireErrorHasCategory(&err, rio.ErrorCategory(""))

	// Sanitize arguments.
	path2 := fs.MustAbsolutePath(path)
	filt2, err := apiutil.ProcessFilters(filt, apiutil.FilterPurposeUnpack)
	if err != nil {
		return api.WareID{}, Errorf(rio.ErrUsage, "invalid filter specification: %s", err)
	}

	// Pick a warehouse and get a reader.
	//  This is a *very* expensive operation for git.  It's less
	//  of "pick a warehouse" and more "download the whole thing and hope we
	//  get what we wanted" (which is very ironic for a system that has
	//  a CAS system on its inside, yes).
	whCtrl, err := pick(ctx,
		wareID,
		warehouses,
		osfs.New(config.GetCacheBasePath().Join(fs.MustRelPath("git/objs"))),
		mon,
	)
	if err != nil {
		return api.WareID{}, err
	}

	// Get submodule config.  Fetch them all.
	submodules, err := whCtrl.Submodules(wareID.Hash)
	if err != nil {
		return api.WareID{}, err
	}
	// We'll organize them by path now; only thing that's useful.
	submoduleCtrls := map[string]*gitWarehouse.Controller{}
	for _, submCfg := range submodules {
		// TODO it would be dreamy to parallelize this.
		whCtrl, err := pick(ctx,
			api.WareID{"git", submCfg.Hash},
			[]api.WarehouseAddr{api.WarehouseAddr(submCfg.URL)},
			osfs.New(config.GetCacheBasePath().Join(fs.MustRelPath("git/objs"))),
			mon,
		)
		if err != nil {
			return api.WareID{}, err
		}
		submoduleCtrls[submCfg.Path] = whCtrl
	}

	// Open a tree to walk in the main repo.
	//  We'll do submodule checkouts somewhere deep in the middle of this.
	tr, err := whCtrl.GetTree(wareID.Hash)
	if err != nil {
		panic(err)
	}

	// Construct filesystem wrapper to use for all our ops.
	afs := osfs.New(path2)

	// Walk.
	if err := unpackOneRepo(ctx, tr, afs, true, filt2, submoduleCtrls, mon); err != nil {
		return api.WareID{}, err
	}

	// That's it.  Checkout should have already checked the hash, so we just return it.
	return wareID, nil
}

func unpackOneRepo(
	ctx context.Context,
	tr *object.Tree,
	afs fs.FS,
	isRoot bool, // if true, will recurse for submodules (with this set to false).
	filt apiutil.FilesetFilters,
	submoduleCtrls map[string]*gitWarehouse.Controller,
	mon rio.Monitor,
) (err error) {
	tw := object.NewTreeWalker(tr, true, nil)

	// Make the root dir.  Git doesn't have metadata for the tree root.
	conjuredFmeta := fshash.DefaultDirMetadata()
	filters.Apply(filt, &conjuredFmeta)
	if err := fsOp.PlaceFile(afs, conjuredFmeta, nil, filt.SkipChown); err != nil {
		return Errorf(rio.ErrInoperablePath, "error while unpacking: %s", err)
	}

	// Extract.
	// Iterate over each entry, mutating filesystem as we go.
	dirs := make([]fs.RelPath, 1, 200) // Keep for dir time repair at end.
	dirs[0] = fs.RelPath{}
	for {
		fmeta := fs.Metadata{}
		name, te, err := tw.Next()

		// Check for done.
		if err == io.EOF {
			break // sucess!  end of archive.
		}
		if err != nil {
			return Errorf(rio.ErrWareCorrupt, "corrupt git tree: %s", err)
		}
		if ctx.Err() != nil {
			return Errorf(rio.ErrCancelled, "cancelled")
		}
		//fmt.Fprintf(os.Stderr, "walking git tree %s -- %#v\n", name, te)

		// Reshuffle metainfo to our default format.
		fmeta.Name = fs.MustRelPath(name)
		fmeta.Uid = 1000
		fmeta.Gid = 1000
		switch te.Mode {
		case filemode.Dir:
			fmeta.Type = fs.Type_Dir
			fmeta.Perms = 0755
			dirs = append(dirs, fmeta.Name)
		case filemode.Regular:
			fmeta.Type = fs.Type_File
			fmeta.Perms = 0644
		case filemode.Executable:
			fmeta.Type = fs.Type_File
			fmeta.Perms = 0755
		case filemode.Symlink:
			fmeta.Type = fs.Type_Symlink
			fmeta.Perms = 0644
			// Hang on, extracting a symlink is actually rough.
			tf, err := tr.TreeEntryFile(&te)
			if err != nil {
				return Errorf(rio.ErrWareCorrupt, "corrupt git tree: %s", err)
			}
			reader, err := tf.Blob.Reader()
			if err != nil {
				return Errorf(rio.ErrWareCorrupt, "corrupt git tree: %s", err)
			}
			blob, err := ioutil.ReadAll(reader)
			if err != nil {
				return Errorf(rio.ErrWareCorrupt, "corrupt git tree: %s", err)
			}
			fmeta.Linkname = string(blob)
		case filemode.Submodule:
			// Ooowee!  Recurse time!
			submCtrl, ok := submoduleCtrls[name]
			if !ok {
				return Errorf(rio.ErrWareCorrupt, "gitlink found at path %q but no matching config in .gitmodules", name)
			}
			submTr, err := submCtrl.GetTree(te.Hash.String())
			if err != nil {
				panic(err)
			}
			submFs := osfs.New(afs.BasePath().Join(fmeta.Name))
			if err := unpackOneRepo(ctx, submTr, submFs, false, filt, nil, mon); err != nil {
				return err
			}
			continue
		case filemode.Empty:
			fallthrough
		case filemode.Deprecated:
			fallthrough
		default:
			panic(fmt.Errorf("unknown git filemode %#v", te.Mode))
		}
		fmeta.Mtime = apiutil.DefaultMtime // git doesn't have time info

		// Apply filters.
		filters.Apply(filt, &fmeta)

		// Place the file.
		switch fmeta.Type {
		case fs.Type_File:
			tf, err := tr.TreeEntryFile(&te)
			if err != nil {
				return Errorf(rio.ErrWareCorrupt, "corrupt git tree: %s", err)
			}
			reader, err := tf.Blob.Reader()
			if err != nil {
				return Errorf(rio.ErrWareCorrupt, "corrupt git tree: %s", err)
			}
			if err := fsOp.PlaceFile(afs, fmeta, reader, filt.SkipChown); err != nil {
				return Errorf(rio.ErrInoperablePath, "error while unpacking: %s", err)
			}
			reader.Close()
		default:
			if err := fsOp.PlaceFile(afs, fmeta, nil, filt.SkipChown); err != nil {
				return Errorf(rio.ErrInoperablePath, "error while unpacking: %s", err)
			}
		}
	}

	// Cleanup dir times with a post-order traversal over the bucket.
	//  Files and dirs placed inside dirs cause the parent's mtime to update, so we have to re-pave them.
	for i := len(dirs) - 1; i >= 0; i-- {
		if err := afs.SetTimesNano(dirs[i], fs.DefaultAtime, fs.DefaultAtime); err != nil {
			return Errorf(rio.ErrInoperablePath, "error while unpacking: %s", err)
		}
	}

	return nil
}
<|endoftext|>


<commit_before>package main

import(
	"code.google.com/p/go.net/websocket"
	"net/http"
	"text/template"
	"encoding/json"
	"io/ioutil"
	"fmt"
	"strings"
	"regexp"
	//"strconv"
)

const addr = ":8080"

type User struct{
	Name string
}

type CommandType int

const(
	REGISTER = iota //0
	MESSAGE			//1
	BLOGIN
	BLOGOUT
)

type Command struct{
	Type int
	Value []byte
}

type Connection struct{
	//User who sends
	Usr *User
	//websocket-connection
	Conn *websocket.Conn
	// Buffered channel of outbound messages.
	send chan *Message
}

type Message struct{
	Usr *User
	Msg []byte
}

type Server struct{
	history []*Message
	broadcast chan *Message
	register chan *Connection
	unregister chan *Connection
	registeredConnections map[*Connection]bool
}

func (s *Server)run(){
	for {
		select {
		case c := <-server.register:
			server.registeredConnections[c] = true
			c.send<-&Message{&User{"From server"}, []byte("with love")}

			jsonU, _ := json.Marshal(s.history)
			c.send<-&Message{&User{"All user"}, jsonU}
			//send the history to a new user
			/*for _, message := range s.history {
				c.send<- message
			}*/
		case c := <-server.unregister:
			delete(server.registeredConnections, c)
			close(c.send)
		case m := <-server.broadcast:
			//append to history
			s.history = append(s.history, m)
			for c := range s.registeredConnections {
				select {
				case c.send <- m:
				default:
					delete(s.registeredConnections, c)
					close(c.send)
					go c.Conn.Close()
				}
			}
		}
	}
}

func (c *Connection)Read(){
	for {
		var message string
		err := websocket.Message.Receive(c.Conn, &message)
		if err != nil {
			break
		}

		fmt.Println(message)

		var cmd *Command
		var user_name string
		errm := json.Unmarshal([]byte(message), &cmd)
		if(errm != nil){

		}else{
			if cmd.Type == REGISTER {
				for d := range server.registeredConnections {
					if d != nil && d.Usr.Name == string(cmd.Value) {
						c.send <-&Message{&User{"From Server"}, "Sorry name already used"}
						server.unregister<-c
						c.Conn.Close()
					}
				}
				c.Usr = &User{Name:string(cmd.Value)}
			}

			if cmd.Type == MESSAGE {
				r,_ := regexp.MatchString("@", string(cmd.Value))
				if r {
					var found bool = false
					user_name = strings.Split(string(cmd.Value), " ")[0]
					user_name = strings.Split(user_name, "@")[1]
					fmt.Println(user_name)
					for d := range server.registeredConnections {
						if d.Usr.Name == user_name {
							d.send <-&Message{c.Usr, cmd.Value}
							found = true
						}
					}
					if(found) {
						c.send <-&Message{c.Usr, cmd.Value}
					} else {
						c.send <-&Message{&User{"From server"},[]byte("user not found")}
					}
				} else {
					server.broadcast<-&Message{c.Usr, cmd.Value}
				}
			}

			if cmd.Type == BLOGIN {
				server.broadcast<-&Message{c.Usr, []byte("Logged In")}
			}

			if cmd.Type == BLOGOUT {
				fmt.Println("Somebody wants to logout")
			}

			//c.Usr = user
		}

	}
	c.Conn.Close()
}

func (c *Connection)Write(){
	for message := range c.send {
		jsonM, _ := json.Marshal(message)
		fmt.Println(string(jsonM))
		err := websocket.Message.Send(c.Conn, string(jsonM))
		if err != nil {
			break
		}
	}
}

var server = Server{
	history: make([]*Message, 0),
	broadcast: make(chan *Message),
	register: make(chan *Connection),
	unregister: make(chan *Connection),
	registeredConnections: make(map[*Connection]bool),
}

func wsHandler(ws *websocket.Conn) {
	c := &Connection{Usr: nil,send: make(chan *Message), Conn: ws}
	server.register <- c
	defer func() {server.unregister <- c}()
	go c.Write()
	c.Read()
}

//var indexTemplate = template.Must(template.ParseFiles("template/index.html"))

func mainHandler(w http.ResponseWriter, r *http.Request) {

}

func chatHandler(w http.ResponseWriter, r *http.Request){
	indexTemplate, _ := template.ParseFiles("template/chat.html")
	indexTemplate.Execute(w, r.Host)
}

func doLogin(w http.ResponseWriter, r *http.Request){
	http.Redirect(w, r, "/chat.html", http.StatusFound)
}

func loginHandler(w http.ResponseWriter, r *http.Request){
	html, err := ioutil.ReadFile("template/index.html")
	if err != nil{
		w.Write([]byte(err.Error()))
		return
	}

	w.Write(html)
}

func main(){
	go server.run()
	http.HandleFunc("/", loginHandler)
	http.HandleFunc("/chat.html", chatHandler)
	http.HandleFunc("/doLogin", doLogin)
	http.Handle("/ws", websocket.Handler(wsHandler))
	err := http.ListenAndServe(addr, nil)
	    if err != nil {
	        panic("ListenAndServe: " + err.Error())
	    }
}
<commit_msg>closes #5<commit_after>package main

import(
	"code.google.com/p/go.net/websocket"
	"net/http"
	"text/template"
	"encoding/json"
	"io/ioutil"
	"fmt"
	"strings"
	"regexp"
	//"strconv"
)

const addr = ":8080"

type User struct{
	Name string
}

type CommandType int

const(
	REGISTER = iota //0
	MESSAGE			//1
	BLOGIN
	BLOGOUT
)

type Command struct{
	Type int
	Value []byte
}

type Connection struct{
	//User who sends
	Usr *User
	//websocket-connection
	Conn *websocket.Conn
	// Buffered channel of outbound messages.
	send chan *Message
}

type Message struct{
	Usr *User
	Msg []byte
}

type Server struct{
	history []*Message
	broadcast chan *Message
	register chan *Connection
	unregister chan *Connection
	registeredConnections map[*Connection]bool
}

func (s *Server)run(){
	for {
		select {
		case c := <-server.register:
			server.registeredConnections[c] = true
			c.send<-&Message{&User{"From server"}, []byte("with love")}

			jsonU, _ := json.Marshal(s.history)
			c.send<-&Message{&User{"All user"}, jsonU}
			//send the history to a new user
			/*for _, message := range s.history {
				c.send<- message
			}*/
		case c := <-server.unregister:
			delete(server.registeredConnections, c)
			close(c.send)
		case m := <-server.broadcast:
			//append to history
			s.history = append(s.history, m)
			for c := range s.registeredConnections {
				select {
				case c.send <- m:
				default:
					delete(s.registeredConnections, c)
					close(c.send)
					go c.Conn.Close()
				}
			}
		}
	}
}

func (c *Connection)Read(){
	for {
		var message string
		err := websocket.Message.Receive(c.Conn, &message)
		if err != nil {
			break
		}

		fmt.Println(message)

		var cmd *Command
		var user_name string
		var found bool
		errm := json.Unmarshal([]byte(message), &cmd)
		if(errm != nil){

		}else{
			if cmd.Type == REGISTER {
				//check if user name already is taken.
				for d := range server.registeredConnections {
					if d.Usr != nil && d.Usr.Name == string(cmd.Value) {
						found = true
					}
				}
				if found {
					break
				} else {
					c.Usr = &User{Name:string(cmd.Value)}
				}
			}

			if cmd.Type == MESSAGE {
				r,_ := regexp.MatchString("@", string(cmd.Value))
				if r {
					found = false
					user_name = strings.Split(string(cmd.Value), " ")[0]
					user_name = strings.Split(user_name, "@")[1]
					for d := range server.registeredConnections {
						if d.Usr.Name == user_name {
							d.send <-&Message{c.Usr, cmd.Value}
							found = true
						}
					}
					if(found) {
						c.send <-&Message{c.Usr, cmd.Value}
					} else {
						c.send <-&Message{&User{"From server"},[]byte("user not found")}
					}
				} else {
					server.broadcast<-&Message{c.Usr, cmd.Value}
				}
			}

			if cmd.Type == BLOGIN {
				server.broadcast<-&Message{c.Usr, []byte("Logged In")}
			}

			if cmd.Type == BLOGOUT {
				fmt.Println("Somebody wants to logout")
			}

		}

	}
	c.Conn.Close()
}

func (c *Connection)Write(){
	for message := range c.send {
		jsonM, _ := json.Marshal(message)
		fmt.Println(string(jsonM))
		err := websocket.Message.Send(c.Conn, string(jsonM))
		if err != nil {
			break
		}
	}
}

var server = Server{
	history: make([]*Message, 0),
	broadcast: make(chan *Message),
	register: make(chan *Connection),
	unregister: make(chan *Connection),
	registeredConnections: make(map[*Connection]bool),
}

func wsHandler(ws *websocket.Conn) {
	c := &Connection{Usr: nil,send: make(chan *Message), Conn: ws}
	server.register <- c
	defer func() {server.unregister <- c}()
	go c.Write()
	c.Read()
}

//var indexTemplate = template.Must(template.ParseFiles("template/index.html"))

func mainHandler(w http.ResponseWriter, r *http.Request) {

}

func chatHandler(w http.ResponseWriter, r *http.Request){
	indexTemplate, _ := template.ParseFiles("template/chat.html")
	indexTemplate.Execute(w, r.Host)
}

func doLogin(w http.ResponseWriter, r *http.Request){
	http.Redirect(w, r, "/chat.html", http.StatusFound)
}

func loginHandler(w http.ResponseWriter, r *http.Request){
	html, err := ioutil.ReadFile("template/index.html")
	if err != nil{
		w.Write([]byte(err.Error()))
		return
	}

	w.Write(html)
}

func main(){
	go server.run()
	http.HandleFunc("/", loginHandler)
	http.HandleFunc("/chat.html", chatHandler)
	http.HandleFunc("/doLogin", doLogin)
	http.Handle("/ws", websocket.Handler(wsHandler))
	err := http.ListenAndServe(addr, nil)
	    if err != nil {
	        panic("ListenAndServe: " + err.Error())
	    }
}
<|endoftext|>


<commit_before>package filter

import (
	"testing"

	"github.com/tyler-sommer/stick"
	"time"
)

func TestFilters(t *testing.T) {
	newBatchFunc := func(in stick.Value, args ...stick.Value) func() stick.Value {
		return func() stick.Value {
			batched := filterBatch(nil, in, args...)
			res := ""
			stick.Iterate(batched, func(k, v stick.Value, l stick.Loop) (bool, error) {
				stick.Iterate(v, func(k, v stick.Value, l stick.Loop) (bool, error) {
					res += stick.CoerceString(v) + "."
					return false, nil
				})
				res += "."
				return false, nil
			})
			return res
		}
	}

	tz, err := time.LoadLocation("Australia/Perth")
	if nil != err {
		t.Error(err)
	}
	testDate := time.Date(1980, 5, 31, 22, 01, 0, 0, tz)
	testDate2 := time.Date(2018, 2, 3, 2, 1, 44, 123456000, tz)

	tests := []struct {
		name     string
		actual   func() stick.Value
		expected stick.Value
	}{
		{"default nil", func() stick.Value { return filterDefault(nil, nil, "person") }, "person"},
		{"default empty string", func() stick.Value { return filterDefault(nil, "", "person") }, "person"},
		{"default not empty", func() stick.Value { return filterDefault(nil, "user", "person") }, "user"},
		{"abs positive", func() stick.Value { return filterAbs(nil, 5.1) }, 5.1},
		{"abs negative", func() stick.Value { return filterAbs(nil, -42) }, 42.0 /* note: coerced to float */},
		{"abs invalid", func() stick.Value { return filterAbs(nil, "invalid") }, 0.0},
		{"len string", func() stick.Value { return filterLength(nil, "hello") }, 5},
		{"len nil", func() stick.Value { return filterLength(nil, nil) }, 0},
		{"len slice", func() stick.Value { return filterLength(nil, []string{"h", "e"}) }, 2},
		{"capitalize", func() stick.Value { return filterCapitalize(nil, "word") }, "Word"},
		{"lower", func() stick.Value { return filterLower(nil, "HELLO, WORLD!") }, "hello, world!"},
		{"title", func() stick.Value { return filterTitle(nil, "hello, world!") }, "Hello, World!"},
		{"trim", func() stick.Value { return filterTrim(nil, " Hello   ") }, "Hello"},
		{"upper", func() stick.Value { return filterUpper(nil, "hello, world!") }, "HELLO, WORLD!"},
		{"batch underfull with fill", newBatchFunc([]int{1, 2, 3, 4, 5, 6, 7, 8}, 3, "No Item"), "1.2.3..4.5.6..7.8.No Item.."},
		{"batch underfull without fill", newBatchFunc([]int{1, 2, 3, 4, 5}, 3), "1.2.3..4.5.."},
		{"batch full", newBatchFunc([]int{1, 2, 3, 4}, 2), "1.2..3.4.."},
		{"batch empty", newBatchFunc([]int{}, 10), ""},
		{"batch nil", newBatchFunc(nil, 10), ""},
		{"first array", func() stick.Value { return filterFirst(nil, []string{"1","2","3","4"}) }, "1"},
		{"first string", func() stick.Value { return filterFirst(nil, "1234") }, "1"},
		{"date c", func() stick.Value { return filterDate(nil, testDate, "c") }, "1980-05-31T22:01:00+08:00"},
		{"date r", func() stick.Value { return filterDate(nil, testDate, "r") }, "Sat, 31 May 1980 22:01:00 +0800"},
		{"date test", func() stick.Value { return filterDate(nil, testDate2, "d D j l F m M n Y y a A g G h H i s O P T")}, "03 Sat 3 Saturday February 02 Feb 2 2018 18 am AM 2 02 02 02 01 44 +0800 +08:00 AWST"},
		{"date u", func() stick.Value { return filterDate(nil, testDate2, "s.u") }, "44.123456"},
	}
	for _, test := range tests {
		res := test.actual()
		if res != test.expected {
			t.Errorf("%s:\n\texpected: %v\n\tgot: %v", test.name, test.expected, res)
		}
	}
}
<commit_msg>Adding unit test.<commit_after>package filter

import (
	"testing"

	"github.com/tyler-sommer/stick"
	"time"
)

func TestFilters(t *testing.T) {
	newBatchFunc := func(in stick.Value, args ...stick.Value) func() stick.Value {
		return func() stick.Value {
			batched := filterBatch(nil, in, args...)
			res := ""
			stick.Iterate(batched, func(k, v stick.Value, l stick.Loop) (bool, error) {
				stick.Iterate(v, func(k, v stick.Value, l stick.Loop) (bool, error) {
					res += stick.CoerceString(v) + "."
					return false, nil
				})
				res += "."
				return false, nil
			})
			return res
		}
	}

	tz, err := time.LoadLocation("Australia/Perth")
	if nil != err {
		t.Error(err)
	}
	testDate := time.Date(1980, 5, 31, 22, 01, 0, 0, tz)
	testDate2 := time.Date(2018, 2, 3, 2, 1, 44, 123456000, tz)

	tests := []struct {
		name     string
		actual   func() stick.Value
		expected stick.Value
	}{
		{"default nil", func() stick.Value { return filterDefault(nil, nil, "person") }, "person"},
		{"default empty string", func() stick.Value { return filterDefault(nil, "", "person") }, "person"},
		{"default not empty", func() stick.Value { return filterDefault(nil, "user", "person") }, "user"},
		{"abs positive", func() stick.Value { return filterAbs(nil, 5.1) }, 5.1},
		{"abs negative", func() stick.Value { return filterAbs(nil, -42) }, 42.0 /* note: coerced to float */},
		{"abs invalid", func() stick.Value { return filterAbs(nil, "invalid") }, 0.0},
		{"len string", func() stick.Value { return filterLength(nil, "hello") }, 5},
		{"len nil", func() stick.Value { return filterLength(nil, nil) }, 0},
		{"len slice", func() stick.Value { return filterLength(nil, []string{"h", "e"}) }, 2},
		{"capitalize", func() stick.Value { return filterCapitalize(nil, "word") }, "Word"},
		{"lower", func() stick.Value { return filterLower(nil, "HELLO, WORLD!") }, "hello, world!"},
		{"title", func() stick.Value { return filterTitle(nil, "hello, world!") }, "Hello, World!"},
		{"trim", func() stick.Value { return filterTrim(nil, " Hello   ") }, "Hello"},
		{"upper", func() stick.Value { return filterUpper(nil, "hello, world!") }, "HELLO, WORLD!"},
		{"batch underfull with fill", newBatchFunc([]int{1, 2, 3, 4, 5, 6, 7, 8}, 3, "No Item"), "1.2.3..4.5.6..7.8.No Item.."},
		{"batch underfull without fill", newBatchFunc([]int{1, 2, 3, 4, 5}, 3), "1.2.3..4.5.."},
		{"batch full", newBatchFunc([]int{1, 2, 3, 4}, 2), "1.2..3.4.."},
		{"batch empty", newBatchFunc([]int{}, 10), ""},
		{"batch nil", newBatchFunc(nil, 10), ""},
		{"first array", func() stick.Value { return filterFirst(nil, []string{"1","2","3","4"}) }, "1"},
		{"first string", func() stick.Value { return filterFirst(nil, "1234") }, "1"},
		{"date c", func() stick.Value { return filterDate(nil, testDate, "c") }, "1980-05-31T22:01:00+08:00"},
		{"date r", func() stick.Value { return filterDate(nil, testDate, "r") }, "Sat, 31 May 1980 22:01:00 +0800"},
		{"date test", func() stick.Value { return filterDate(nil, testDate2, "d D j l F m M n Y y a A g G h H i s O P T")}, "03 Sat 3 Saturday February 02 Feb 2 2018 18 am AM 2 02 02 02 01 44 +0800 +08:00 AWST"},
		{"date u", func() stick.Value { return filterDate(nil, testDate2, "s.u") }, "44.123456"},
		{"join", func() stick.Value { return filterJoin(nil, []string{"a","b","c"}, "-") }, "a-b-c"},
	}
	for _, test := range tests {
		res := test.actual()
		if res != test.expected {
			t.Errorf("%s:\n\texpected: %v\n\tgot: %v", test.name, test.expected, res)
		}
	}
}
<|endoftext|>


<commit_before>package config

import (
	"fmt"
	"io"
	"strings"
)

// An Encoder writes config files to an output stream.
type Encoder struct {
	w io.Writer
}

// NewEncoder returns a new encoder that writes to w.
func NewEncoder(w io.Writer) *Encoder {
	return &Encoder{w}
}

// Encode writes the config in git config format to the stream of the encoder.
func (e *Encoder) Encode(cfg *Config) error {
	for _, s := range cfg.Sections {
		if err := e.encodeSection(s); err != nil {
			return err
		}
	}

	return nil
}

func (e *Encoder) encodeSection(s *Section) error {
	if len(s.Options) > 0 {
		if err := e.printf("[%s]\n", s.Name); err != nil {
			return err
		}

		if err := e.encodeOptions(s.Options); err != nil {
			return err
		}
	}

	for _, ss := range s.Subsections {
		if err := e.encodeSubsection(s.Name, ss); err != nil {
			return err
		}
	}

	return nil
}

func (e *Encoder) encodeSubsection(sectionName string, s *Subsection) error {
	//TODO: escape
	if err := e.printf("[%s \"%s\"]\n", sectionName, s.Name); err != nil {
		return err
	}

	return e.encodeOptions(s.Options)
}

func (e *Encoder) encodeOptions(opts Options) error {
	for _, o := range opts {
		pattern := "\t%s = %s\n"
		if strings.ContainsAny(o.Value, `"#\"`) || strings.HasPrefix(o.Value, " ") || strings.HasSuffix(o.Value, " ") {
			pattern = "\t%s = %q\n"
		}

		if err := e.printf(pattern, o.Key, o.Value); err != nil {
			return err
		}
	}

	return nil
}

func (e *Encoder) printf(msg string, args ...interface{}) error {
	_, err := fmt.Fprintf(e.w, msg, args...)
	return err
}
<commit_msg>plumbing: config, remove duplicated character in set<commit_after>package config

import (
	"fmt"
	"io"
	"strings"
)

// An Encoder writes config files to an output stream.
type Encoder struct {
	w io.Writer
}

// NewEncoder returns a new encoder that writes to w.
func NewEncoder(w io.Writer) *Encoder {
	return &Encoder{w}
}

// Encode writes the config in git config format to the stream of the encoder.
func (e *Encoder) Encode(cfg *Config) error {
	for _, s := range cfg.Sections {
		if err := e.encodeSection(s); err != nil {
			return err
		}
	}

	return nil
}

func (e *Encoder) encodeSection(s *Section) error {
	if len(s.Options) > 0 {
		if err := e.printf("[%s]\n", s.Name); err != nil {
			return err
		}

		if err := e.encodeOptions(s.Options); err != nil {
			return err
		}
	}

	for _, ss := range s.Subsections {
		if err := e.encodeSubsection(s.Name, ss); err != nil {
			return err
		}
	}

	return nil
}

func (e *Encoder) encodeSubsection(sectionName string, s *Subsection) error {
	//TODO: escape
	if err := e.printf("[%s \"%s\"]\n", sectionName, s.Name); err != nil {
		return err
	}

	return e.encodeOptions(s.Options)
}

func (e *Encoder) encodeOptions(opts Options) error {
	for _, o := range opts {
		pattern := "\t%s = %s\n"
		if strings.ContainsAny(o.Value, `"#\`) || strings.HasPrefix(o.Value, " ") || strings.HasSuffix(o.Value, " ") {
			pattern = "\t%s = %q\n"
		}

		if err := e.printf(pattern, o.Key, o.Value); err != nil {
			return err
		}
	}

	return nil
}

func (e *Encoder) printf(msg string, args ...interface{}) error {
	_, err := fmt.Fprintf(e.w, msg, args...)
	return err
}
<|endoftext|>


<commit_before>package main

import (
	"fmt"
	"log"
	"net/http"

	"github.com/micro/go-micro/client"
	"github.com/micro/go-web"
	hello "github.com/micro/micro/examples/greeter/server/proto/hello"

	"golang.org/x/net/context"
)

func main() {
	service := web.NewService(
		web.Name("go.micro.web.helloworld"),
	)

	service.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
		if r.Method == "POST" {
			r.ParseForm()

			name := r.Form.Get("name")
			if len(name) == 0 {
				name = "World"
			}

			cl := hello.NewSayClient("go.micro.srv.greeter", client.DefaultClient)
			rsp, err := cl.Hello(context.Background(), &hello.Request{
				Name: name,
			})

			if err != nil {
				http.Error(w, err.Error(), 500)
				return
			}

			w.Write([]byte(`<html><body><h1>` + rsp.Msg + `</h1></body></html>`))
			return
		}

		fmt.Fprint(w, `<html><body><h1>Enter Name<h1><form method=post><input name=name type=text /></form></body></html>`)
	})

	if err := service.Init(); err != nil {
		log.Fatal(err)
	}

	if err := service.Run(); err != nil {
		log.Fatal(err)
	}
}
<commit_msg>That should be greeter<commit_after>package main

import (
	"fmt"
	"log"
	"net/http"

	"github.com/micro/go-micro/client"
	"github.com/micro/go-web"
	hello "github.com/micro/micro/examples/greeter/server/proto/hello"

	"golang.org/x/net/context"
)

func main() {
	service := web.NewService(
		web.Name("go.micro.web.greeter"),
	)

	service.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
		if r.Method == "POST" {
			r.ParseForm()

			name := r.Form.Get("name")
			if len(name) == 0 {
				name = "World"
			}

			cl := hello.NewSayClient("go.micro.srv.greeter", client.DefaultClient)
			rsp, err := cl.Hello(context.Background(), &hello.Request{
				Name: name,
			})

			if err != nil {
				http.Error(w, err.Error(), 500)
				return
			}

			w.Write([]byte(`<html><body><h1>` + rsp.Msg + `</h1></body></html>`))
			return
		}

		fmt.Fprint(w, `<html><body><h1>Enter Name<h1><form method=post><input name=name type=text /></form></body></html>`)
	})

	if err := service.Init(); err != nil {
		log.Fatal(err)
	}

	if err := service.Run(); err != nil {
		log.Fatal(err)
	}
}
<|endoftext|>


<commit_before>package main

import (
	"context"
	"log"
	"os"

	"github.com/gofn/gofn"
	"github.com/gofn/gofn/iaas/digitalocean"
	"github.com/gofn/gofn/provision"
)

func main() {
	key := os.Getenv("DIGITALOCEAN_API_KEY")
	if key == "" {
		log.Println("You must provide an api key for digital ocean")
	}
	iaas, err := digitalocean.New(key)
	if err != nil {
		log.Println(err)
	}

	ctx := context.Background()
	client, _, err := gofn.ProvideMachine(ctx, iaas)
	if err != nil {
		log.Fatal(err)
	}
	defer iaas.DeleteMachine()
	container, err := gofn.PrepareContainer(ctx, client, &provision.BuildOptions{
		ContextDir: "testDocker",
		Dockerfile: "Dockerfile",
		ImageName:  "python",
	}, nil)
	if err != nil {
		log.Println(err)
		return
	}
	errors, err := gofn.RunWait(ctx, client, container)
	if err != nil {
		log.Println(err)
		return
	}
	_, err = gofn.Attach(ctx, client, container, nil, os.Stdout, os.Stderr)
	if err != nil {
		log.Println(err)
		return
	}
	err = <-errors
	if err != nil {
		log.Println(err)
	}
}
<commit_msg>add log.fatal when digital ocean api key is missing<commit_after>package main

import (
	"context"
	"log"
	"os"

	"github.com/gofn/gofn"
	"github.com/gofn/gofn/iaas/digitalocean"
	"github.com/gofn/gofn/provision"
)

func main() {
	key := os.Getenv("DIGITALOCEAN_API_KEY")
	if key == "" {
		log.Fatalln("You must provide an api key for digital ocean")
	}
	iaas, err := digitalocean.New(key)
	if err != nil {
		log.Println(err)
	}

	ctx := context.Background()
	client, _, err := gofn.ProvideMachine(ctx, iaas)
	if err != nil {
		log.Fatal(err)
	}
	defer iaas.DeleteMachine()
	container, err := gofn.PrepareContainer(ctx, client, &provision.BuildOptions{
		ContextDir: "testDocker",
		Dockerfile: "Dockerfile",
		ImageName:  "python",
	}, nil)
	if err != nil {
		log.Println(err)
		return
	}
	errors, err := gofn.RunWait(ctx, client, container)
	if err != nil {
		log.Println(err)
		return
	}
	_, err = gofn.Attach(ctx, client, container, nil, os.Stdout, os.Stderr)
	if err != nil {
		log.Println(err)
		return
	}
	err = <-errors
	if err != nil {
		log.Println(err)
	}
}
<|endoftext|>


<commit_before>package controllers

import (
	"github.com/robfig/revel"
	"net/http"
	"net/http/pprof"
)

type Pprof struct {
	*revel.Controller
}

const (
	index   = 0
	profile = 1
	symbol  = 2
	cmdline = 3
)

// The PprofHandler type makes it easy to call the net/http/pprof handler methods
// since they all have the same method signature
type PprofHandler func(http.ResponseWriter, *http.Request)

func (r PprofHandler) Apply(req *revel.Request, resp *revel.Response) {
	r(resp.Out, req.Request)
}

func (c Pprof) Profile() revel.Result {
	return PprofHandler(pprof.Profile)
}

func (c Pprof) Symbol() revel.Result {
	return PprofHandler(pprof.Symbol)
}

func (c Pprof) Cmdline() revel.Result {
	return PprofHandler(pprof.Cmdline)
}

func (c Pprof) Index() revel.Result {
	return PprofHandler(pprof.Index)
}
<commit_msg>Removed pprof constants<commit_after>package controllers

import (
	"github.com/robfig/revel"
	"net/http"
	"net/http/pprof"
)

type Pprof struct {
	*revel.Controller
}

// The PprofHandler type makes it easy to call the net/http/pprof handler methods
// since they all have the same method signature
type PprofHandler func(http.ResponseWriter, *http.Request)

func (r PprofHandler) Apply(req *revel.Request, resp *revel.Response) {
	r(resp.Out, req.Request)
}

func (c Pprof) Profile() revel.Result {
	return PprofHandler(pprof.Profile)
}

func (c Pprof) Symbol() revel.Result {
	return PprofHandler(pprof.Symbol)
}

func (c Pprof) Cmdline() revel.Result {
	return PprofHandler(pprof.Cmdline)
}

func (c Pprof) Index() revel.Result {
	return PprofHandler(pprof.Index)
}
<|endoftext|>


<commit_before>// Copyright (c) 2017 Hardik Bagdi <hbagdi1@binghamton.edu>
//
// MIT License
//
// Permission is hereby granted, free of charge, to any person obtaining
// a copy of this software and associated documentation files (the
// "Software"), to deal in the Software without restriction, including
// without limitation the rights to use, copy, modify, merge, publish,
// distribute, sublicense, and/or sell copies of the Software, and to
// permit persons to whom the Software is furnished to do so, subject to
// the following conditions:
//
// The above copyright notice and this permission notice shall be
// included in all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

package unsplash

import (
	"bytes"
	"encoding/json"
	"fmt"
	"strconv"
)

// PhotosService interacts with /photos endpoint
type PhotosService service

// PhotoOpt denotes properties of any Image
type PhotoOpt struct {
	Height int `json:"h" url:"h"`
	Width  int `json:"w" url:"w"`
	CropX  int
	CropY  int
	Crop   bool
}

// Valid validates a PhotoOpt
func (p *PhotoOpt) Valid() bool {
	if p.Height <= 0 || p.Width <= 0 || p.CropX < 0 || p.CropY < 0 {
		return false
	}
	return true
}

type rect struct {
	Rect string `url:"rect"`
}

func processPhotoOpt(photoOpt *PhotoOpt) interface{} {
	if !photoOpt.Crop {
		return photoOpt
	}
	var buf bytes.Buffer
	var r rect
	buf.WriteString(strconv.Itoa(photoOpt.CropX) + "," +
		strconv.Itoa(photoOpt.CropY) + "," +
		strconv.Itoa(photoOpt.Width) + "," +
		strconv.Itoa(photoOpt.Height))
	r.Rect = buf.String()
	return r
}

// Photo return a photo with id
func (ps *PhotosService) Photo(id string, photoOpt *PhotoOpt) (*Photo, *Response, error) {
	if "" == id {
		return nil, nil, &IllegalArgumentError{ErrString: "Photo ID cannot be null"}
	}

	// Validation and conversion if necessary of photoOpt
	var opt interface{}
	opt = nil
	if photoOpt != nil {
		if !photoOpt.Valid() {
			return nil, nil, &InvalidPhotoOptError{ErrString: " photoOpt has zero or non-negative values"}
		}
		opt = processPhotoOpt(photoOpt)
	}
	endpoint := fmt.Sprintf("%v/%v", getEndpoint(photos), id)
	req, err := newRequest(GET, endpoint, opt, nil)
	if err != nil {
		return nil, nil, err
	}
	resp, err := ps.client.do(req)
	if err != nil {
		return nil, nil, err
	}
	var photo Photo
	err = json.Unmarshal(*resp.body, &photo)
	if err != nil {
		return nil, nil, err
	}
	return &photo, resp, nil
}

// Stats return a stats about a photo with id.
func (ps *PhotosService) Stats(id string) (*PhotoStats, *Response, error) {
	if "" == id {
		return nil, nil, &IllegalArgumentError{ErrString: "Photo ID cannot be null"}
	}
	endpoint := fmt.Sprintf("%v/%v/stats", getEndpoint(photos), id)
	req, err := newRequest(GET, endpoint, nil, nil)
	if err != nil {
		return nil, nil, err
	}
	resp, err := ps.client.do(req)
	if err != nil {
		return nil, nil, err
	}
	var stats PhotoStats
	err = json.Unmarshal(*resp.body, &stats)
	if err != nil {
		return nil, nil, err
	}
	return &stats, resp, nil
}

// Statistics return a stats about a photo with id.
func (ps *PhotosService) Statistics(id string, opt *StatsOpt) (*PhotoStatistics, *Response, error) {
	if "" == id {
		return nil, nil, &IllegalArgumentError{ErrString: "Photo ID cannot be null"}
	}
	if opt == nil {
		opt = defaultStatsOpt
	}
	if !opt.Valid() {
		return nil, nil, &InvalidStatsOptError{ErrString: "opt provided is not valid."}
	}
	endpoint := fmt.Sprintf("%v/%v/statistics", getEndpoint(photos), id)
	req, err := newRequest(GET, endpoint, opt, nil)
	if err != nil {
		return nil, nil, err
	}
	resp, err := ps.client.do(req)
	if err != nil {
		return nil, nil, err
	}
	var stats PhotoStatistics
	err = json.Unmarshal(*resp.body, &stats)
	if err != nil {
		return nil, nil, err
	}
	return &stats, resp, nil
}

// DownloadLink return the download URL for a photo.
func (ps *PhotosService) DownloadLink(id string) (*URL, *Response, error) {
	if "" == id {
		return nil, nil, &IllegalArgumentError{ErrString: "Photo ID cannot be null"}
	}
	endpoint := fmt.Sprintf("%v/%v/download", getEndpoint(photos), id)
	req, err := newRequest(GET, endpoint, nil, nil)
	if err != nil {
		return nil, nil, err
	}
	resp, err := ps.client.do(req)
	if err != nil {
		return nil, nil, err
	}
	var url urlWrapper
	err = json.Unmarshal(*resp.body, &url)
	if err != nil {
		return nil, nil, err
	}
	return url.URL, resp, nil
}

// All returns a list of all photos on unsplash.
// Note that some fields in photo structs from this result will be missing.
// Use Photo() method to get all details of the  Photo.
func (ps *PhotosService) All(listOpt *ListOpt) (*[]Photo, *Response, error) {
	s := (service)(*ps)
	return s.getPhotos(listOpt, getEndpoint(photos))
}

// Curated return a list of all curated photos.
func (ps *PhotosService) Curated(listOpt *ListOpt) (*[]Photo, *Response, error) {
	s := (service)(*ps)
	return s.getPhotos(listOpt, getEndpoint(photos)+"/curated")
}

// RandomPhotoOpt optional parameters for a random photo search
type RandomPhotoOpt struct {
	Height        int         `url:"h,omitempty"`
	Width         int         `url:"w,omitempty"`
	Featured      bool        `url:"featured,omitempty"`
	Username      string      `url:"username,omitempty"`
	SearchQuery   string      `url:"query,omitempty"`
	Count         int         `url:"count,omitempty"`
	Orientation   orientation `url:"orientation,omitempty"`
	CollectionIDs []int       `url:"collections,comma"`
}

//Valid validates a RandomPhotoOpt
func (opt *RandomPhotoOpt) Valid() bool {
	//negative values
	if opt.Count < 0 || opt.Height < 0 || opt.Width < 0 {
		return false
	}
	//collections and query can't be used at the same time acc to API documentation
	if len(opt.CollectionIDs) != 0 && opt.SearchQuery != "" {
		return false
	}
	if opt.Count == 0 {
		opt.Count = 1
	}
	if opt.Orientation != "" {
		if opt.Orientation != Landscape && opt.Orientation != Portrait && opt.Orientation != Squarish {
			return false
		}
	}

	return true
}

// Orientation is orientation of a photo
type orientation string

// These constants show possible Orientation types
const (
	Landscape orientation = "landscape"
	Portrait  orientation = "portrait"
	Squarish  orientation = "squarish"
)

var defaultRandomPhotoOpt = &RandomPhotoOpt{Count: 1}

// Random returns random photo(s).
// If opt is nil, then a single random photo is returned by default
func (ps *PhotosService) Random(opt *RandomPhotoOpt) (*[]Photo, *Response, error) {
	if opt == nil {
		opt = defaultRandomPhotoOpt
	}
	if !opt.Valid() {
		return nil, nil, &InvalidListOptError{ErrString: "opt provided is not valid."}
	}
	req, err := newRequest(GET, getEndpoint(photos)+"/random", opt, nil)
	if err != nil {
		return nil, nil, err
	}
	resp, err := ps.client.do(req)
	if err != nil {
		return nil, nil, err
	}
	photos := make([]Photo, 0)
	err = json.Unmarshal(*resp.body, &photos)
	if err != nil {
		return nil, nil, err
	}
	return &photos, resp, nil

}

// Like likes a photo on the currently authenticated user's behalf
func (ps *PhotosService) Like(photoID string) (*Photo, *Response, error) {
	if photoID == "" {
		return nil, nil, &IllegalArgumentError{ErrString: "PhotoID cannot be null"}
	}
	endpoint := fmt.Sprintf("%v/%v/like", getEndpoint(photos), photoID)
	req, err := newRequest(POST, endpoint, nil, nil)
	if err != nil {
		return nil, nil, err
	}
	resp, err := ps.client.do(req)
	if err != nil {
		return nil, nil, err
	}
	var photo Photo
	err = json.Unmarshal(*resp.body, &photo)
	if err != nil {
		return nil, nil, err
	}
	return &photo, resp, nil
}

// Unlike likes a photo on the currently authenticated user's behalf
func (ps *PhotosService) Unlike(photoID string) (*Photo, *Response, error) {
	if photoID == "" {
		return nil, nil, &IllegalArgumentError{ErrString: "PhotoID cannot be null"}
	}
	endpoint := fmt.Sprintf("%v/%v/like", getEndpoint(photos), photoID)
	req, err := newRequest(DELETE, endpoint, nil, nil)
	if err != nil {
		return nil, nil, err
	}
	resp, err := ps.client.do(req)
	if err != nil {
		return nil, nil, err
	}
	var photo Photo
	err = json.Unmarshal(*resp.body, &photo)
	if err != nil {
		return nil, nil, err
	}
	return &photo, resp, nil
}
<commit_msg>Add topic field to RandomPhotoOpt (#49)<commit_after>// Copyright (c) 2017 Hardik Bagdi <hbagdi1@binghamton.edu>
//
// MIT License
//
// Permission is hereby granted, free of charge, to any person obtaining
// a copy of this software and associated documentation files (the
// "Software"), to deal in the Software without restriction, including
// without limitation the rights to use, copy, modify, merge, publish,
// distribute, sublicense, and/or sell copies of the Software, and to
// permit persons to whom the Software is furnished to do so, subject to
// the following conditions:
//
// The above copyright notice and this permission notice shall be
// included in all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

package unsplash

import (
	"bytes"
	"encoding/json"
	"fmt"
	"strconv"
)

// PhotosService interacts with /photos endpoint
type PhotosService service

// PhotoOpt denotes properties of any Image
type PhotoOpt struct {
	Height int `json:"h" url:"h"`
	Width  int `json:"w" url:"w"`
	CropX  int
	CropY  int
	Crop   bool
}

// Valid validates a PhotoOpt
func (p *PhotoOpt) Valid() bool {
	if p.Height <= 0 || p.Width <= 0 || p.CropX < 0 || p.CropY < 0 {
		return false
	}
	return true
}

type rect struct {
	Rect string `url:"rect"`
}

func processPhotoOpt(photoOpt *PhotoOpt) interface{} {
	if !photoOpt.Crop {
		return photoOpt
	}
	var buf bytes.Buffer
	var r rect
	buf.WriteString(strconv.Itoa(photoOpt.CropX) + "," +
		strconv.Itoa(photoOpt.CropY) + "," +
		strconv.Itoa(photoOpt.Width) + "," +
		strconv.Itoa(photoOpt.Height))
	r.Rect = buf.String()
	return r
}

// Photo return a photo with id
func (ps *PhotosService) Photo(id string, photoOpt *PhotoOpt) (*Photo, *Response, error) {
	if "" == id {
		return nil, nil, &IllegalArgumentError{ErrString: "Photo ID cannot be null"}
	}

	// Validation and conversion if necessary of photoOpt
	var opt interface{}
	opt = nil
	if photoOpt != nil {
		if !photoOpt.Valid() {
			return nil, nil, &InvalidPhotoOptError{ErrString: " photoOpt has zero or non-negative values"}
		}
		opt = processPhotoOpt(photoOpt)
	}
	endpoint := fmt.Sprintf("%v/%v", getEndpoint(photos), id)
	req, err := newRequest(GET, endpoint, opt, nil)
	if err != nil {
		return nil, nil, err
	}
	resp, err := ps.client.do(req)
	if err != nil {
		return nil, nil, err
	}
	var photo Photo
	err = json.Unmarshal(*resp.body, &photo)
	if err != nil {
		return nil, nil, err
	}
	return &photo, resp, nil
}

// Stats return a stats about a photo with id.
func (ps *PhotosService) Stats(id string) (*PhotoStats, *Response, error) {
	if "" == id {
		return nil, nil, &IllegalArgumentError{ErrString: "Photo ID cannot be null"}
	}
	endpoint := fmt.Sprintf("%v/%v/stats", getEndpoint(photos), id)
	req, err := newRequest(GET, endpoint, nil, nil)
	if err != nil {
		return nil, nil, err
	}
	resp, err := ps.client.do(req)
	if err != nil {
		return nil, nil, err
	}
	var stats PhotoStats
	err = json.Unmarshal(*resp.body, &stats)
	if err != nil {
		return nil, nil, err
	}
	return &stats, resp, nil
}

// Statistics return a stats about a photo with id.
func (ps *PhotosService) Statistics(id string, opt *StatsOpt) (*PhotoStatistics, *Response, error) {
	if "" == id {
		return nil, nil, &IllegalArgumentError{ErrString: "Photo ID cannot be null"}
	}
	if opt == nil {
		opt = defaultStatsOpt
	}
	if !opt.Valid() {
		return nil, nil, &InvalidStatsOptError{ErrString: "opt provided is not valid."}
	}
	endpoint := fmt.Sprintf("%v/%v/statistics", getEndpoint(photos), id)
	req, err := newRequest(GET, endpoint, opt, nil)
	if err != nil {
		return nil, nil, err
	}
	resp, err := ps.client.do(req)
	if err != nil {
		return nil, nil, err
	}
	var stats PhotoStatistics
	err = json.Unmarshal(*resp.body, &stats)
	if err != nil {
		return nil, nil, err
	}
	return &stats, resp, nil
}

// DownloadLink return the download URL for a photo.
func (ps *PhotosService) DownloadLink(id string) (*URL, *Response, error) {
	if "" == id {
		return nil, nil, &IllegalArgumentError{ErrString: "Photo ID cannot be null"}
	}
	endpoint := fmt.Sprintf("%v/%v/download", getEndpoint(photos), id)
	req, err := newRequest(GET, endpoint, nil, nil)
	if err != nil {
		return nil, nil, err
	}
	resp, err := ps.client.do(req)
	if err != nil {
		return nil, nil, err
	}
	var url urlWrapper
	err = json.Unmarshal(*resp.body, &url)
	if err != nil {
		return nil, nil, err
	}
	return url.URL, resp, nil
}

// All returns a list of all photos on unsplash.
// Note that some fields in photo structs from this result will be missing.
// Use Photo() method to get all details of the  Photo.
func (ps *PhotosService) All(listOpt *ListOpt) (*[]Photo, *Response, error) {
	s := (service)(*ps)
	return s.getPhotos(listOpt, getEndpoint(photos))
}

// Curated return a list of all curated photos.
func (ps *PhotosService) Curated(listOpt *ListOpt) (*[]Photo, *Response, error) {
	s := (service)(*ps)
	return s.getPhotos(listOpt, getEndpoint(photos)+"/curated")
}

// RandomPhotoOpt optional parameters for a random photo search
type RandomPhotoOpt struct {
	Height        int         `url:"h,omitempty"`
	Width         int         `url:"w,omitempty"`
	Featured      bool        `url:"featured,omitempty"`
	Username      string      `url:"username,omitempty"`
	SearchQuery   string      `url:"query,omitempty"`
	Count         int         `url:"count,omitempty"`
	Orientation   orientation `url:"orientation,omitempty"`
	CollectionIDs []int       `url:"collections,comma"`
	TopicIDs      []string    `url:"topics,comma"`
}

// Valid validates a RandomPhotoOpt
func (opt *RandomPhotoOpt) Valid() bool {
	//negative values
	if opt.Count < 0 || opt.Height < 0 || opt.Width < 0 {
		return false
	}
	//collections/topics and query can't be used at the same time acc to API documentation
	if (len(opt.CollectionIDs) != 0 || len(opt.TopicIDs) != 0) && opt.SearchQuery != "" {
		return false
	}
	if opt.Count == 0 {
		opt.Count = 1
	}
	if opt.Orientation != "" {
		if opt.Orientation != Landscape && opt.Orientation != Portrait && opt.Orientation != Squarish {
			return false
		}
	}

	return true
}

// Orientation is orientation of a photo
type orientation string

// These constants show possible Orientation types
const (
	Landscape orientation = "landscape"
	Portrait  orientation = "portrait"
	Squarish  orientation = "squarish"
)

var defaultRandomPhotoOpt = &RandomPhotoOpt{Count: 1}

// Random returns random photo(s).
// If opt is nil, then a single random photo is returned by default
func (ps *PhotosService) Random(opt *RandomPhotoOpt) (*[]Photo, *Response, error) {
	if opt == nil {
		opt = defaultRandomPhotoOpt
	}
	if !opt.Valid() {
		return nil, nil, &InvalidListOptError{ErrString: "opt provided is not valid."}
	}
	req, err := newRequest(GET, getEndpoint(photos)+"/random", opt, nil)
	if err != nil {
		return nil, nil, err
	}
	resp, err := ps.client.do(req)
	if err != nil {
		return nil, nil, err
	}
	photos := make([]Photo, 0)
	err = json.Unmarshal(*resp.body, &photos)
	if err != nil {
		return nil, nil, err
	}
	return &photos, resp, nil

}

// Like likes a photo on the currently authenticated user's behalf
func (ps *PhotosService) Like(photoID string) (*Photo, *Response, error) {
	if photoID == "" {
		return nil, nil, &IllegalArgumentError{ErrString: "PhotoID cannot be null"}
	}
	endpoint := fmt.Sprintf("%v/%v/like", getEndpoint(photos), photoID)
	req, err := newRequest(POST, endpoint, nil, nil)
	if err != nil {
		return nil, nil, err
	}
	resp, err := ps.client.do(req)
	if err != nil {
		return nil, nil, err
	}
	var photo Photo
	err = json.Unmarshal(*resp.body, &photo)
	if err != nil {
		return nil, nil, err
	}
	return &photo, resp, nil
}

// Unlike likes a photo on the currently authenticated user's behalf
func (ps *PhotosService) Unlike(photoID string) (*Photo, *Response, error) {
	if photoID == "" {
		return nil, nil, &IllegalArgumentError{ErrString: "PhotoID cannot be null"}
	}
	endpoint := fmt.Sprintf("%v/%v/like", getEndpoint(photos), photoID)
	req, err := newRequest(DELETE, endpoint, nil, nil)
	if err != nil {
		return nil, nil, err
	}
	resp, err := ps.client.do(req)
	if err != nil {
		return nil, nil, err
	}
	var photo Photo
	err = json.Unmarshal(*resp.body, &photo)
	if err != nil {
		return nil, nil, err
	}
	return &photo, resp, nil
}
<|endoftext|>


<commit_before>package parser_test

import (
	"github.com/stretchr/testify/assert"
	"github.com/stretchr/testify/suite"
	"github.com/yvasiyarov/swagger/parser"
	"testing"
)

type OperationSuite struct {
	suite.Suite
	parser *parser.Parser
}

func (suite *OperationSuite) SetupSuite() {
	suite.parser = parser.NewParser()
}

func (suite *OperationSuite) TestNewApi() {
	assert.NotNil(suite.T(), parser.NewOperation(suite.parser, "test"), "Can no create new operation instance")
}

func TestOperationSuite(t *testing.T) {
	suite.Run(t, &OperationSuite{})
}
<commit_msg>Add tests for operation<commit_after>package parser_test

import (
	"github.com/stretchr/testify/assert"
	"github.com/stretchr/testify/suite"
	"github.com/yvasiyarov/swagger/parser"
	"testing"
)

type OperationSuite struct {
	suite.Suite
	parser *parser.Parser
}

func (suite *OperationSuite) SetupSuite() {
	suite.parser = parser.NewParser()
}

func (suite *OperationSuite) TestNewApi() {
	assert.NotNil(suite.T(), parser.NewOperation(suite.parser, "test"), "Can no create new operation instance")
}

func (suite *OperationSuite) TestSetItemsType() {
	op := parser.NewOperation(suite.parser, "test")
	op.SetItemsType("int")
	assert.Equal(suite.T(), op.Items.Type, "int", "Can no set item type to simple type")

	op2 := parser.NewOperation(suite.parser, "test")
	op2.SetItemsType("SomeType")
	assert.Equal(suite.T(), op2.Items.Ref, "SomeType", "Can no set item type to custom type")
}

func (suite *OperationSuite) TestParseAcceptComment() {
	op := parser.NewOperation(suite.parser, "test")
	err := op.ParseAcceptComment("@Accept json")
	assert.Nil(suite.T(), err, "can not parse accept comment")
	assert.Equal(suite.T(), op.Consumes, []string{parser.ContentTypeJson}, "Can no parse accept comment")
	assert.Equal(suite.T(), op.Produces, []string{parser.ContentTypeJson}, "Can no parse accept comment")

	op2 := parser.NewOperation(suite.parser, "test")
	err2 := op2.ParseAcceptComment("@Accept json,html,plain,xml")
	assert.Nil(suite.T(), err2, "Can not parse accept comment with multiple types")

	expected := []string{parser.ContentTypeJson, parser.ContentTypeHtml, parser.ContentTypePlain, parser.ContentTypeXml}
	assert.Equal(suite.T(), op2.Consumes, expected, "Can not parse accept comment with multiple types")
	assert.Equal(suite.T(), op2.Produces, expected, "Can not parse accept comment with multiple types")
}

func (suite *OperationSuite) TestParseRouterComment() {
	op := parser.NewOperation(suite.parser, "test")
	err := op.ParseRouterComment("@router /customer/get-wishlist/ [get]")
	assert.Nil(suite.T(), err, "Can not parse router comment")
	assert.Equal(suite.T(), op.Path, "/customer/get-wishlist/", "Can not parse router comment")
	assert.Equal(suite.T(), op.HttpMethod, "GET", "Can not parse router comment")

	op2 := parser.NewOperation(suite.parser, "test")
	err2 := op2.ParseRouterComment("@router /customer/get-wishlist/{id} [PoSt]")
	assert.Nil(suite.T(), err2, "Can not parse router comment")
	assert.Equal(suite.T(), op2.Path, "/customer/get-wishlist/{id}", "Can not parse router comment")
	assert.Equal(suite.T(), op2.HttpMethod, "POST", "Can not parse router comment")
}

func (suite *OperationSuite) TestParseParamComment() {
	op := parser.NewOperation(suite.parser, "test")
	err := op.ParseParamComment("@Param   order_nr     path    string  true	\"Order number\"")
	assert.Nil(suite.T(), err, "Can not parse param comment")
	assert.Len(suite.T(), op.Parameters, 1, "Can not parse param comment")

	assert.Equal(suite.T(), op.Parameters[0].Name, "order_nr", "Can not parse param comment")
	assert.Equal(suite.T(), op.Parameters[0].ParamType, "path", "Can not parse param comment")
	assert.Equal(suite.T(), op.Parameters[0].Type, "string", "Can not parse param comment")
	assert.Equal(suite.T(), op.Parameters[0].DataType, "string", "Can not parse param comment")
	assert.Equal(suite.T(), op.Parameters[0].Required, true, "Can not parse param comment")
	assert.Equal(suite.T(), op.Parameters[0].Description, "Order number", "Can not parse param comment")

	//assert.Equal(suite.T(), op.Path, "/customer/get-wishlist/", "Can not parse router comment")
	//assert.Equal(suite.T(), op.HttpMethod, "GET", "Can not parse router comment")
}

func TestOperationSuite(t *testing.T) {
	suite.Run(t, &OperationSuite{})
}
<|endoftext|>


<commit_before>package aws

import (
	"fmt"
	"log"

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

func resourceAwsCodeBuildWebhook() *schema.Resource {
	return &schema.Resource{
		Create: resourceAwsCodeBuildWebhookCreate,
		Read:   resourceAwsCodeBuildWebhookRead,
		Delete: resourceAwsCodeBuildWebhookDelete,
		Update: resourceAwsCodeBuildWebhookUpdate,

		Importer: &schema.ResourceImporter{
			State: schema.ImportStatePassthrough,
		},

		Schema: map[string]*schema.Schema{
			"project_name": {
				Type:     schema.TypeString,
				Required: true,
				ForceNew: true,
			},
			"branch_filter": {
				Type:     schema.TypeString,
				Optional: true,
			},
			"filter_group": {
				Type:     schema.TypeSet,
				Optional: true,
				Computed: true,
				Elem: &schema.Resource{
					Schema: map[string]*schema.Schema{
						"type": {
							Type:     schema.TypeString,
							Required: true,
							ValidateFunc: validation.StringInSlice([]string{
								codebuild.WebhookFilterTypeEvent,
								codebuild.WebhookFilterTypeActorAccountId,
								codebuild.WebhookFilterTypeBaseRef,
								codebuild.WebhookFilterTypeFilePath,
								codebuild.WebhookFilterTypeHeadRef,
							}, false),
						},
						"exclude_matched_pattern": {
							Type:     schema.TypeBool,
							Required: true,
						},
						"pattern": {
							Type:     schema.TypeString,
							Optional: true,
						},
					},
				},
			},
			"payload_url": {
				Type:     schema.TypeString,
				Computed: true,
			},
			"secret": {
				Type:      schema.TypeString,
				Computed:  true,
				Sensitive: true,
			},
			"url": {
				Type:     schema.TypeString,
				Computed: true,
			},
		},
	}
}

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

	webhookFilterGroups := expandWebhookFilterGroup(d)

	input := &codebuild.CreateWebhookInput{
		ProjectName:  aws.String(d.Get("project_name").(string)),
		FilterGroups: webhookFilterGroups,
	}

	// The CodeBuild API requires this to be non-empty if defined
	if v, ok := d.GetOk("branch_filter"); ok {
		input.BranchFilter = aws.String(v.(string))
	}

	log.Printf("[DEBUG] Creating CodeBuild Webhook: %s", input)
	resp, err := conn.CreateWebhook(input)

	if err != nil {
		return fmt.Errorf("error creating CodeBuild Webhook: %s", err)
	}

	// Secret is only returned on create, so capture it at the start
	d.Set("secret", resp.Webhook.Secret)
	d.SetId(d.Get("project_name").(string))

	return resourceAwsCodeBuildWebhookRead(d, meta)
}

func expandWebhookFilterGroup(d *schema.ResourceData) [][]*codebuild.WebhookFilter {
	webhookFilters := make([]*codebuild.WebhookFilter, 0)

	configsList := d.Get("filter_group").(*schema.Set).List()

	if len(configsList) == 0 {
		return nil
	}

	for _, config := range configsList {
		filter := expandWebhookFilterData(config.(map[string]interface{}))
		webhookFilters = append(webhookFilters, &filter)
	}

	return [][]*codebuild.WebhookFilter{webhookFilters}
}

func expandWebhookFilterData(data map[string]interface{}) codebuild.WebhookFilter {
	filter := codebuild.WebhookFilter{
		Type:                  aws.String(data["type"].(string)),
		ExcludeMatchedPattern: aws.Bool(data["exclude_matched_pattern"].(bool)),
	}

	if v := data["pattern"]; v != nil {
		filter.Pattern = aws.String(v.(string))
	}

	return filter
}

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

	resp, err := conn.BatchGetProjects(&codebuild.BatchGetProjectsInput{
		Names: []*string{
			aws.String(d.Id()),
		},
	})

	if err != nil {
		return err
	}

	if len(resp.Projects) == 0 {
		log.Printf("[WARN] CodeBuild Project %q not found, removing from state", d.Id())
		d.SetId("")
		return nil
	}

	project := resp.Projects[0]

	if project.Webhook == nil {
		log.Printf("[WARN] CodeBuild Project %q webhook not found, removing from state", d.Id())
		d.SetId("")
		return nil
	}

	d.Set("branch_filter", project.Webhook.BranchFilter)
	d.Set("filter_group", project.Webhook.FilterGroups)
	d.Set("payload_url", project.Webhook.PayloadUrl)
	d.Set("project_name", project.Name)
	d.Set("url", project.Webhook.Url)
	// The secret is never returned after creation, so don't set it here

	return nil
}

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

	_, err := conn.UpdateWebhook(&codebuild.UpdateWebhookInput{
		ProjectName:  aws.String(d.Id()),
		BranchFilter: aws.String(d.Get("branch_filter").(string)),
		FilterGroups: expandWebhookFilterGroup(d),
		RotateSecret: aws.Bool(false),
	})

	if err != nil {
		return err
	}

	return resourceAwsCodeBuildWebhookRead(d, meta)
}

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

	_, err := conn.DeleteWebhook(&codebuild.DeleteWebhookInput{
		ProjectName: aws.String(d.Id()),
	})

	if err != nil {
		if isAWSErr(err, codebuild.ErrCodeResourceNotFoundException, "") {
			return nil
		}
		return err
	}

	return nil
}
<commit_msg>Update filter_group properties<commit_after>package aws

import (
	"fmt"
	"log"

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

func resourceAwsCodeBuildWebhook() *schema.Resource {
	return &schema.Resource{
		Create: resourceAwsCodeBuildWebhookCreate,
		Read:   resourceAwsCodeBuildWebhookRead,
		Delete: resourceAwsCodeBuildWebhookDelete,
		Update: resourceAwsCodeBuildWebhookUpdate,

		Importer: &schema.ResourceImporter{
			State: schema.ImportStatePassthrough,
		},

		Schema: map[string]*schema.Schema{
			"project_name": {
				Type:     schema.TypeString,
				Required: true,
				ForceNew: true,
			},
			"branch_filter": {
				Type:     schema.TypeString,
				Optional: true,
			},
			"filter_group": {
				Type:     schema.TypeSet,
				Optional: true,
				Elem: &schema.Resource{
					Schema: map[string]*schema.Schema{
						"type": {
							Type:     schema.TypeString,
							Required: true,
							ValidateFunc: validation.StringInSlice([]string{
								codebuild.WebhookFilterTypeEvent,
								codebuild.WebhookFilterTypeActorAccountId,
								codebuild.WebhookFilterTypeBaseRef,
								codebuild.WebhookFilterTypeFilePath,
								codebuild.WebhookFilterTypeHeadRef,
							}, false),
						},
						"exclude_matched_pattern": {
							Type:     schema.TypeBool,
							Optional: true,
							Default:  false,
						},
						"pattern": {
							Type:     schema.TypeString,
							Required: true,
						},
					},
				},
			},
			"payload_url": {
				Type:     schema.TypeString,
				Computed: true,
			},
			"secret": {
				Type:      schema.TypeString,
				Computed:  true,
				Sensitive: true,
			},
			"url": {
				Type:     schema.TypeString,
				Computed: true,
			},
		},
	}
}

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

	webhookFilterGroups := expandWebhookFilterGroup(d)

	input := &codebuild.CreateWebhookInput{
		ProjectName:  aws.String(d.Get("project_name").(string)),
		FilterGroups: webhookFilterGroups,
	}

	// The CodeBuild API requires this to be non-empty if defined
	if v, ok := d.GetOk("branch_filter"); ok {
		input.BranchFilter = aws.String(v.(string))
	}

	log.Printf("[DEBUG] Creating CodeBuild Webhook: %s", input)
	resp, err := conn.CreateWebhook(input)

	if err != nil {
		return fmt.Errorf("error creating CodeBuild Webhook: %s", err)
	}

	// Secret is only returned on create, so capture it at the start
	d.Set("secret", resp.Webhook.Secret)
	d.SetId(d.Get("project_name").(string))

	return resourceAwsCodeBuildWebhookRead(d, meta)
}

func expandWebhookFilterGroup(d *schema.ResourceData) [][]*codebuild.WebhookFilter {
	webhookFilters := make([]*codebuild.WebhookFilter, 0)

	configsList := d.Get("filter_group").(*schema.Set).List()

	if len(configsList) == 0 {
		return nil
	}

	for _, config := range configsList {
		filter := expandWebhookFilterData(config.(map[string]interface{}))
		webhookFilters = append(webhookFilters, &filter)
	}

	return [][]*codebuild.WebhookFilter{webhookFilters}
}

func expandWebhookFilterData(data map[string]interface{}) codebuild.WebhookFilter {
	filter := codebuild.WebhookFilter{
		Type:                  aws.String(data["type"].(string)),
		ExcludeMatchedPattern: aws.Bool(data["exclude_matched_pattern"].(bool)),
	}

	if v := data["pattern"]; v != nil {
		filter.Pattern = aws.String(v.(string))
	}

	return filter
}

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

	resp, err := conn.BatchGetProjects(&codebuild.BatchGetProjectsInput{
		Names: []*string{
			aws.String(d.Id()),
		},
	})

	if err != nil {
		return err
	}

	if len(resp.Projects) == 0 {
		log.Printf("[WARN] CodeBuild Project %q not found, removing from state", d.Id())
		d.SetId("")
		return nil
	}

	project := resp.Projects[0]

	if project.Webhook == nil {
		log.Printf("[WARN] CodeBuild Project %q webhook not found, removing from state", d.Id())
		d.SetId("")
		return nil
	}

	d.Set("branch_filter", project.Webhook.BranchFilter)
	d.Set("filter_group", project.Webhook.FilterGroups)
	d.Set("payload_url", project.Webhook.PayloadUrl)
	d.Set("project_name", project.Name)
	d.Set("url", project.Webhook.Url)
	// The secret is never returned after creation, so don't set it here

	return nil
}

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

	_, err := conn.UpdateWebhook(&codebuild.UpdateWebhookInput{
		ProjectName:  aws.String(d.Id()),
		BranchFilter: aws.String(d.Get("branch_filter").(string)),
		FilterGroups: expandWebhookFilterGroup(d),
		RotateSecret: aws.Bool(false),
	})

	if err != nil {
		return err
	}

	return resourceAwsCodeBuildWebhookRead(d, meta)
}

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

	_, err := conn.DeleteWebhook(&codebuild.DeleteWebhookInput{
		ProjectName: aws.String(d.Id()),
	})

	if err != nil {
		if isAWSErr(err, codebuild.ErrCodeResourceNotFoundException, "") {
			return nil
		}
		return err
	}

	return nil
}
<|endoftext|>