<commit_before>package main

import (
	"socialapi/models"
	"socialapi/rest"
	"socialapi/workers/common/tests"
	"strings"
	"testing"
	"time"

	"github.com/koding/bongo"
	"github.com/koding/runner"

	. "github.com/smartystreets/goconvey/convey"
)

func TestChannelParticipantOperations(t *testing.T) {
	tests.WithRunner(t, func(r *runner.Runner) {
		Convey("while testing channel participants", t, func() {
			Convey("First Create Users and initiate conversation", func() {
				ownerAccount, groupChannel, groupName := models.CreateRandomGroupDataWithChecks()

				ownerSes, err := models.FetchOrCreateSession(ownerAccount.Nick, groupName)
				So(err, ShouldBeNil)
				So(ownerSes, ShouldNotBeNil)

				secondAccount, err := models.CreateAccountInBothDbs()
				tests.ResultedWithNoErrorCheck(secondAccount, err)
				_, err = groupChannel.AddParticipant(secondAccount.Id)
				So(err, ShouldBeNil)

				thirdAccount, err := models.CreateAccountInBothDbs()
				tests.ResultedWithNoErrorCheck(thirdAccount, err)
				_, err = groupChannel.AddParticipant(thirdAccount.Id)
				So(err, ShouldBeNil)

				forthAccount, err := models.CreateAccountInBothDbs()
				tests.ResultedWithNoErrorCheck(forthAccount, err)
				_, err = groupChannel.AddParticipant(forthAccount.Id)
				So(err, ShouldBeNil)

				devrim, err := models.CreateAccountInBothDbsWithNick("devrim")
				tests.ResultedWithNoErrorCheck(devrim, err)
				_, err = groupChannel.AddParticipant(devrim.Id)
				So(err, ShouldBeNil)

				ses, err := models.FetchOrCreateSession(ownerAccount.Nick, groupName)
				tests.ResultedWithNoErrorCheck(ses, err)

				pmr := models.ChannelRequest{}

				pmr.AccountId = ownerAccount.Id

				pmr.Body = "new conversation"
				pmr.GroupName = groupName
				pmr.Recipients = []string{"devrim"}

				channelContainer, err := rest.SendPrivateChannelRequest(pmr, ownerSes.ClientId)
				So(err, ShouldBeNil)
				So(channelContainer, ShouldNotBeNil)

				Convey("First user should be able to add second and third users to conversation", func() {
					_, err = rest.AddChannelParticipant(channelContainer.Channel.Id, ownerAccount.Id, secondAccount.Id, thirdAccount.Id)
					So(err, ShouldBeNil)
					participants, err := rest.ListChannelParticipants(channelContainer.Channel.Id, ownerAccount.Id)
					So(err, ShouldBeNil)
					So(participants, ShouldNotBeNil)
					// it is four because first user is "devrim" here
					So(len(participants), ShouldEqual, 4)

					Convey("First user should not be able to re-add second participant", func() {
						_, err = rest.AddChannelParticipant(channelContainer.Channel.Id, ownerAccount.Id, secondAccount.Id)
						So(err, ShouldBeNil)

						participants, err := rest.ListChannelParticipants(channelContainer.Channel.Id, ownerAccount.Id)
						So(err, ShouldBeNil)
						So(participants, ShouldNotBeNil)
						So(len(participants), ShouldEqual, 4)
					})

					Convey("Second user should be able to leave conversation", func() {
						_, err = rest.DeleteChannelParticipant(channelContainer.Channel.Id, secondAccount.Id, secondAccount.Id)
						So(err, ShouldBeNil)

						participants, err := rest.ListChannelParticipants(channelContainer.Channel.Id, ownerAccount.Id)
						So(err, ShouldBeNil)
						So(participants, ShouldNotBeNil)
						So(len(participants), ShouldEqual, 3)

						Convey("A user who is not participant of a conversation should not be able to add another user to the conversation", func() {
							_, err = rest.AddChannelParticipant(channelContainer.Channel.Id, secondAccount.Id, forthAccount.Id)
							So(err, ShouldNotBeNil)

							participants, err := rest.ListChannelParticipants(channelContainer.Channel.Id, ownerAccount.Id)
							So(err, ShouldBeNil)
							So(participants, ShouldNotBeNil)
							So(len(participants), ShouldEqual, 3)
						})
					})

					Convey("Channel owner should be able to kick another conversation participant", func() {
						_, err = rest.DeleteChannelParticipant(channelContainer.Channel.Id, ownerAccount.Id, secondAccount.Id)
						So(err, ShouldBeNil)

						participants, err := rest.ListChannelParticipants(channelContainer.Channel.Id, ownerAccount.Id)
						So(err, ShouldBeNil)
						So(participants, ShouldNotBeNil)
						So(len(participants), ShouldEqual, 3)
					})

					Convey("when a user is blocked", func() {
						_, err = rest.BlockChannelParticipant(channelContainer.Channel.Id, ownerAccount.Id, secondAccount.Id)
						So(err, ShouldBeNil)

						Convey("it should not be in channel participant list", func() {
							participants, err := rest.ListChannelParticipants(channelContainer.Channel.Id, ownerAccount.Id)
							So(err, ShouldBeNil)
							So(participants, ShouldNotBeNil)
							So(len(participants), ShouldEqual, 3)
						})

						Convey("should not be able to add it back", func() {
							_, err = rest.AddChannelParticipant(channelContainer.Channel.Id, ownerAccount.Id, secondAccount.Id)
							So(err, ShouldNotBeNil)
						})

						Convey("should be able to unblock", func() {
							_, err = rest.UnblockChannelParticipant(channelContainer.Channel.Id, ownerAccount.Id, secondAccount.Id)
							So(err, ShouldBeNil)

							Convey("it should not be in channel participant list still", func() {
								participants, err := rest.ListChannelParticipants(channelContainer.Channel.Id, ownerAccount.Id)
								So(err, ShouldBeNil)
								So(participants, ShouldNotBeNil)
								So(len(participants), ShouldEqual, 3)
							})

							Convey("when we add the same user as participant", func() {
								_, err = rest.AddChannelParticipant(channelContainer.Channel.Id, ownerAccount.Id, secondAccount.Id, thirdAccount.Id)
								So(err, ShouldBeNil)

								Convey("it should be in channel participant list", func() {
									participants, err := rest.ListChannelParticipants(channelContainer.Channel.Id, ownerAccount.Id)
									So(err, ShouldBeNil)
									So(participants, ShouldNotBeNil)
									So(len(participants), ShouldEqual, 4)
								})
							})
						})
					})

					Convey("Second user should not be able to kick another conversation participant", func() {
						_, err = rest.DeleteChannelParticipant(channelContainer.Channel.Id, secondAccount.Id, thirdAccount.Id)
						So(err, ShouldNotBeNil)
					})

				})
				Convey("First user should be able to invite second user", func() {
					_, err = rest.InviteChannelParticipant(channelContainer.Channel.Id, ownerAccount.Id, secondAccount.Id)
					So(err, ShouldBeNil)
					participants, err := rest.ListChannelParticipants(channelContainer.Channel.Id, ownerAccount.Id)
					So(err, ShouldBeNil)
					So(participants, ShouldNotBeNil)
					// it is four because first user is "devrim" here
					So(len(participants), ShouldEqual, 2)

					Convey("Second user should be able to reject invitation", func() {
						ses, err := models.FetchOrCreateSession(secondAccount.Nick, groupName)
						So(err, ShouldBeNil)
						So(ses, ShouldNotBeNil)

						err = rest.RejectInvitation(channelContainer.Channel.Id, ses.ClientId)
						So(err, ShouldBeNil)

						participants, err := rest.ListChannelParticipants(channelContainer.Channel.Id, ownerAccount.Id)
						So(err, ShouldBeNil)
						So(participants, ShouldNotBeNil)
						So(len(participants), ShouldEqual, 2)
					})

					Convey("Second user should be able to accept invitation", func() {
						ses, err := models.FetchOrCreateSession(secondAccount.Nick, groupName)
						So(err, ShouldBeNil)
						So(ses, ShouldNotBeNil)

						err = rest.AcceptInvitation(channelContainer.Channel.Id, ses.ClientId)
						So(err, ShouldBeNil)

						participants, err := rest.ListChannelParticipants(channelContainer.Channel.Id, ownerAccount.Id)
						So(err, ShouldBeNil)
						So(participants, ShouldNotBeNil)
						So(len(participants), ShouldEqual, 3)
					})
				})

				// TODO Until we find a better way for handling async stuff, this test is skipped. Instead of sleep, we should use some
				// timeouts for testing these kind of stuff.
				SkipConvey("All private messages must be deleted when all participant users leave the channel", func() {
					account := models.NewAccount()
					err = account.ByNick("devrim")
					So(err, ShouldBeNil)

					_, err = rest.DeleteChannelParticipant(channelContainer.Channel.Id, account.Id, account.Id)
					So(err, ShouldBeNil)

					_, err = rest.DeleteChannelParticipant(channelContainer.Channel.Id, ownerAccount.Id, ownerAccount.Id)
					So(err, ShouldBeNil)

					time.Sleep(1 * time.Second)

					testChannel := models.NewChannel()
					err := testChannel.ById(channelContainer.Channel.Id)
					So(err, ShouldEqual, bongo.RecordNotFound)

					testChannelList := models.NewChannelMessageList()
					err = bongo.B.Unscoped().Where("channel_id = ?", channelContainer.Channel.Id).Find(testChannelList).Error
					So(err, ShouldEqual, bongo.RecordNotFound)

					testMessage := models.NewChannelMessage()
					err = bongo.B.Unscoped().Where("initial_channel_id = ?", channelContainer.Channel.Id).Find(testMessage).Error
					So(err, ShouldEqual, bongo.RecordNotFound)
				})
				Convey("Users should not be able to add/remove users to/from bot channels", func() {
					ownerAccount, _, groupName := models.CreateRandomGroupDataWithChecks()

					participant := models.NewAccount()
					participant.OldId = AccountOldId.Hex()
					participant, err = rest.CreateAccount(participant)
					So(err, ShouldBeNil)
					So(participant, ShouldNotBeNil)

					ses, err := models.FetchOrCreateSession(ownerAccount.Nick, groupName)
					So(err, ShouldBeNil)

					ch, err := rest.CreateChannelByGroupNameAndType(ownerAccount.Id, groupName, models.Channel_TYPE_BOT, ses.ClientId)
					So(err, ShouldBeNil)
					So(ch, ShouldNotBeNil)

					_, err = rest.AddChannelParticipant(ch.Id, ownerAccount.Id, participant.Id)
					So(strings.Contains(err.Error(), "can not add participants for bot channel"), ShouldBeTrue)
				})

				Convey("Users should be able to add/remove users to/from topic channels", func() {
					ownerAccount, _, groupName := models.CreateRandomGroupDataWithChecks()

					participant := models.NewAccount()
					participant.OldId = AccountOldId.Hex()
					participant, err = rest.CreateAccount(participant)
					So(err, ShouldBeNil)
					So(participant, ShouldNotBeNil)

					ses, err := models.FetchOrCreateSession(ownerAccount.Nick, groupName)
					So(err, ShouldBeNil)

					ch, err := rest.CreateChannelByGroupNameAndType(ownerAccount.Id, groupName, models.Channel_TYPE_TOPIC, ses.ClientId)
					So(err, ShouldBeNil)
					So(ch, ShouldNotBeNil)

					_, err = rest.AddChannelParticipant(ch.Id, ownerAccount.Id, participant.Id)
					So(err, ShouldBeNil)

					Convey("adding same user again should success", func() {
						_, err = rest.AddChannelParticipant(ch.Id, ownerAccount.Id, participant.Id)
						So(err, ShouldBeNil)
					})

					_, err = rest.DeleteChannelParticipant(ch.Id, ownerAccount.Id, participant.Id)
					So(err, ShouldBeNil)

					Convey("removing same user again should success", func() {
						_, err = rest.DeleteChannelParticipant(ch.Id, ownerAccount.Id, participant.Id)
						So(err, ShouldBeNil)
					})
				})
			})
		})
	})
}
<commit_msg>tests: add token for channel participants<commit_after>package main

import (
	"socialapi/models"
	"socialapi/rest"
	"socialapi/workers/common/tests"
	"strings"
	"testing"
	"time"

	"github.com/koding/bongo"
	"github.com/koding/runner"

	. "github.com/smartystreets/goconvey/convey"
)

func TestChannelParticipantOperations(t *testing.T) {
	tests.WithRunner(t, func(r *runner.Runner) {
		Convey("while testing channel participants", t, func() {
			Convey("First Create Users and initiate conversation", func() {
				ownerAccount, groupChannel, groupName := models.CreateRandomGroupDataWithChecks()

				ownerSes, err := models.FetchOrCreateSession(ownerAccount.Nick, groupName)
				So(err, ShouldBeNil)
				So(ownerSes, ShouldNotBeNil)

				secondAccount, err := models.CreateAccountInBothDbs()
				tests.ResultedWithNoErrorCheck(secondAccount, err)
				_, err = groupChannel.AddParticipant(secondAccount.Id)
				So(err, ShouldBeNil)

				thirdAccount, err := models.CreateAccountInBothDbs()
				tests.ResultedWithNoErrorCheck(thirdAccount, err)
				_, err = groupChannel.AddParticipant(thirdAccount.Id)
				So(err, ShouldBeNil)

				forthAccount, err := models.CreateAccountInBothDbs()
				tests.ResultedWithNoErrorCheck(forthAccount, err)
				_, err = groupChannel.AddParticipant(forthAccount.Id)
				So(err, ShouldBeNil)

				devrim, err := models.CreateAccountInBothDbsWithNick("devrim")
				tests.ResultedWithNoErrorCheck(devrim, err)
				_, err = groupChannel.AddParticipant(devrim.Id)
				So(err, ShouldBeNil)

				ses, err := models.FetchOrCreateSession(ownerAccount.Nick, groupName)
				tests.ResultedWithNoErrorCheck(ses, err)

				secondSes, err := models.FetchOrCreateSession(secondAccount.Nick, groupName)
				tests.ResultedWithNoErrorCheck(secondSes, err)

				pmr := models.ChannelRequest{}

				pmr.AccountId = ownerAccount.Id

				pmr.Body = "new conversation"
				pmr.GroupName = groupName
				pmr.Recipients = []string{"devrim"}

				channelContainer, err := rest.SendPrivateChannelRequest(pmr, ownerSes.ClientId)
				So(err, ShouldBeNil)
				So(channelContainer, ShouldNotBeNil)

				Convey("First user should be able to add second and third users to conversation", func() {
					_, err = rest.AddChannelParticipant(channelContainer.Channel.Id, ownerAccount.Id, ownerSes.ClientId, secondAccount.Id, thirdAccount.Id)
					So(err, ShouldBeNil)
					participants, err := rest.ListChannelParticipants(channelContainer.Channel.Id, ownerAccount.Id, ownerSes.ClientId)
					So(err, ShouldBeNil)
					So(participants, ShouldNotBeNil)
					// it is four because first user is "devrim" here
					So(len(participants), ShouldEqual, 4)

					Convey("First user should not be able to re-add second participant", func() {
						_, err = rest.AddChannelParticipant(channelContainer.Channel.Id, ownerAccount.Id, ownerSes.ClientId, secondAccount.Id)
						So(err, ShouldBeNil)

						participants, err := rest.ListChannelParticipants(channelContainer.Channel.Id, ownerAccount.Id, ownerSes.ClientId)
						So(err, ShouldBeNil)
						So(participants, ShouldNotBeNil)
						So(len(participants), ShouldEqual, 4)
					})

					Convey("Second user should be able to leave conversation", func() {
						_, err = rest.DeleteChannelParticipant(channelContainer.Channel.Id, secondAccount.Id, ses.ClientId, secondAccount.Id)
						So(err, ShouldBeNil)

						participants, err := rest.ListChannelParticipants(channelContainer.Channel.Id, ownerAccount.Id, ownerSes.ClientId)
						So(err, ShouldBeNil)
						So(participants, ShouldNotBeNil)
						So(len(participants), ShouldEqual, 3)

						Convey("A user who is not participant of a conversation should not be able to add another user to the conversation", func() {
							_, err = rest.AddChannelParticipant(channelContainer.Channel.Id, secondAccount.Id, secondSes.ClientId, forthAccount.Id)
							So(err, ShouldNotBeNil)

							participants, err := rest.ListChannelParticipants(channelContainer.Channel.Id, ownerAccount.Id, ownerSes.ClientId)
							So(err, ShouldBeNil)
							So(participants, ShouldNotBeNil)
							So(len(participants), ShouldEqual, 3)
						})
					})

					Convey("Channel owner should be able to kick another conversation participant", func() {
						_, err = rest.DeleteChannelParticipant(channelContainer.Channel.Id, ownerAccount.Id, ownerSes.ClientId, secondAccount.Id)
						So(err, ShouldBeNil)

						participants, err := rest.ListChannelParticipants(channelContainer.Channel.Id, ownerAccount.Id, ownerSes.ClientId)
						So(err, ShouldBeNil)
						So(participants, ShouldNotBeNil)
						So(len(participants), ShouldEqual, 3)
					})

					Convey("when a user is blocked", func() {
						_, err = rest.BlockChannelParticipant(channelContainer.Channel.Id, ownerAccount.Id, ownerSes.ClientId, secondAccount.Id)
						So(err, ShouldBeNil)

						Convey("it should not be in channel participant list", func() {
							participants, err := rest.ListChannelParticipants(channelContainer.Channel.Id, ownerAccount.Id, ownerSes.ClientId)
							So(err, ShouldBeNil)
							So(participants, ShouldNotBeNil)
							So(len(participants), ShouldEqual, 3)
						})

						Convey("should not be able to add it back", func() {
							_, err = rest.AddChannelParticipant(channelContainer.Channel.Id, ownerAccount.Id, ownerSes.ClientId, secondAccount.Id)
							So(err, ShouldNotBeNil)
						})

						Convey("should be able to unblock", func() {
							_, err = rest.UnblockChannelParticipant(channelContainer.Channel.Id, ownerAccount.Id, ownerSes.ClientId, secondAccount.Id)
							So(err, ShouldBeNil)

							Convey("it should not be in channel participant list still", func() {
								participants, err := rest.ListChannelParticipants(channelContainer.Channel.Id, ownerAccount.Id, ownerSes.ClientId)
								So(err, ShouldBeNil)
								So(participants, ShouldNotBeNil)
								So(len(participants), ShouldEqual, 3)
							})

							Convey("when we add the same user as participant", func() {
								_, err = rest.AddChannelParticipant(channelContainer.Channel.Id, ownerAccount.Id, ownerSes.ClientId, secondAccount.Id, thirdAccount.Id)
								So(err, ShouldBeNil)

								Convey("it should be in channel participant list", func() {
									participants, err := rest.ListChannelParticipants(channelContainer.Channel.Id, ownerAccount.Id, ownerSes.ClientId)
									So(err, ShouldBeNil)
									So(participants, ShouldNotBeNil)
									So(len(participants), ShouldEqual, 4)
								})
							})
						})
					})

					Convey("Second user should not be able to kick another conversation participant", func() {
						_, err = rest.DeleteChannelParticipant(channelContainer.Channel.Id, secondAccount.Id, secondSes.ClientId, thirdAccount.Id)
						So(err, ShouldNotBeNil)
					})

				})
				Convey("First user should be able to invite second user", func() {
					_, err = rest.InviteChannelParticipant(channelContainer.Channel.Id, ownerAccount.Id, ownerSes.ClientId, secondAccount.Id)
					So(err, ShouldBeNil)
					participants, err := rest.ListChannelParticipants(channelContainer.Channel.Id, ownerAccount.Id, ownerSes.ClientId)
					So(err, ShouldBeNil)
					So(participants, ShouldNotBeNil)
					// it is four because first user is "devrim" here
					So(len(participants), ShouldEqual, 2)

					Convey("Second user should be able to reject invitation", func() {
						ses, err := models.FetchOrCreateSession(secondAccount.Nick, groupName)
						So(err, ShouldBeNil)
						So(ses, ShouldNotBeNil)

						err = rest.RejectInvitation(channelContainer.Channel.Id, ses.ClientId)
						So(err, ShouldBeNil)

						participants, err := rest.ListChannelParticipants(channelContainer.Channel.Id, ownerAccount.Id, ownerSes.ClientId)
						So(err, ShouldBeNil)
						So(participants, ShouldNotBeNil)
						So(len(participants), ShouldEqual, 2)
					})

					Convey("Second user should be able to accept invitation", func() {
						ses, err := models.FetchOrCreateSession(secondAccount.Nick, groupName)
						So(err, ShouldBeNil)
						So(ses, ShouldNotBeNil)

						err = rest.AcceptInvitation(channelContainer.Channel.Id, ses.ClientId)
						So(err, ShouldBeNil)

						participants, err := rest.ListChannelParticipants(channelContainer.Channel.Id, ownerAccount.Id, ownerSes.ClientId)
						So(err, ShouldBeNil)
						So(participants, ShouldNotBeNil)
						So(len(participants), ShouldEqual, 3)
					})
				})

				// TODO Until we find a better way for handling async stuff, this test is skipped. Instead of sleep, we should use some
				// timeouts for testing these kind of stuff.
				SkipConvey("All private messages must be deleted when all participant users leave the channel", func() {
					account := models.NewAccount()
					err = account.ByNick("devrim")
					So(err, ShouldBeNil)

					_, err = rest.DeleteChannelParticipant(channelContainer.Channel.Id, account.Id, ses.ClientId, account.Id)
					So(err, ShouldBeNil)

					_, err = rest.DeleteChannelParticipant(channelContainer.Channel.Id, ownerAccount.Id, ownerSes.ClientId, ownerAccount.Id)
					So(err, ShouldBeNil)

					time.Sleep(1 * time.Second)

					testChannel := models.NewChannel()
					err := testChannel.ById(channelContainer.Channel.Id)
					So(err, ShouldEqual, bongo.RecordNotFound)

					testChannelList := models.NewChannelMessageList()
					err = bongo.B.Unscoped().Where("channel_id = ?", channelContainer.Channel.Id).Find(testChannelList).Error
					So(err, ShouldEqual, bongo.RecordNotFound)

					testMessage := models.NewChannelMessage()
					err = bongo.B.Unscoped().Where("initial_channel_id = ?", channelContainer.Channel.Id).Find(testMessage).Error
					So(err, ShouldEqual, bongo.RecordNotFound)
				})
				Convey("Users should not be able to add/remove users to/from bot channels", func() {
					ownerAccount, _, groupName := models.CreateRandomGroupDataWithChecks()

					participant := models.NewAccount()
					participant.OldId = AccountOldId.Hex()
					participant, err = rest.CreateAccount(participant)
					So(err, ShouldBeNil)
					So(participant, ShouldNotBeNil)

					ses, err := models.FetchOrCreateSession(ownerAccount.Nick, groupName)
					So(err, ShouldBeNil)

					ch, err := rest.CreateChannelByGroupNameAndType(ownerAccount.Id, groupName, models.Channel_TYPE_BOT, ses.ClientId)
					So(err, ShouldBeNil)
					So(ch, ShouldNotBeNil)

					_, err = rest.AddChannelParticipant(ch.Id, ownerAccount.Id, ses.ClientId, participant.Id)
					So(strings.Contains(err.Error(), "can not add participants for bot channel"), ShouldBeTrue)
				})

				Convey("Users should be able to add/remove users to/from topic channels", func() {
					ownerAccount, _, groupName := models.CreateRandomGroupDataWithChecks()

					participant := models.NewAccount()
					participant.OldId = AccountOldId.Hex()
					participant, err = rest.CreateAccount(participant)
					So(err, ShouldBeNil)
					So(participant, ShouldNotBeNil)

					ses, err := models.FetchOrCreateSession(ownerAccount.Nick, groupName)
					So(err, ShouldBeNil)

					ch, err := rest.CreateChannelByGroupNameAndType(ownerAccount.Id, groupName, models.Channel_TYPE_TOPIC, ses.ClientId)
					So(err, ShouldBeNil)
					So(ch, ShouldNotBeNil)

					_, err = rest.AddChannelParticipant(ch.Id, ownerAccount.Id, ses.ClientId, participant.Id)
					So(err, ShouldBeNil)

					Convey("adding same user again should success", func() {
						_, err = rest.AddChannelParticipant(ch.Id, ownerAccount.Id, ses.ClientId, participant.Id)
						So(err, ShouldBeNil)
					})

					_, err = rest.DeleteChannelParticipant(ch.Id, ownerAccount.Id, ses.ClientId, participant.Id)
					So(err, ShouldBeNil)

					Convey("removing same user again should success", func() {
						_, err = rest.DeleteChannelParticipant(ch.Id, ownerAccount.Id, ses.ClientId, participant.Id)
						So(err, ShouldBeNil)
					})
				})
			})
		})
	})
}
<|endoftext|>


<commit_before>package main

import (
	"socialapi/models"
	"socialapi/rest"
	"socialapi/workers/common/tests"
	"strings"
	"testing"
	"time"

	"github.com/koding/bongo"
	"github.com/koding/runner"

	. "github.com/smartystreets/goconvey/convey"
)

func TestChannelParticipantOperations(t *testing.T) {
	tests.WithRunner(t, func(r *runner.Runner) {
		Convey("while testing channel participants", t, func() {
			Convey("First Create Users and initiate conversation", func() {
				ownerAccount, groupChannel, groupName := models.CreateRandomGroupDataWithChecks()

				secondAccount, err := models.CreateAccountInBothDbs()
				tests.ResultedWithNoErrorCheck(secondAccount, err)
				_, err = groupChannel.AddParticipant(secondAccount.Id)
				So(err, ShouldBeNil)

				thirdAccount, err := models.CreateAccountInBothDbs()
				tests.ResultedWithNoErrorCheck(thirdAccount, err)
				_, err = groupChannel.AddParticipant(thirdAccount.Id)
				So(err, ShouldBeNil)

				forthAccount, err := models.CreateAccountInBothDbs()
				tests.ResultedWithNoErrorCheck(forthAccount, err)
				_, err = groupChannel.AddParticipant(forthAccount.Id)
				So(err, ShouldBeNil)

				devrim, err := models.CreateAccountInBothDbsWithNick("devrim")
				tests.ResultedWithNoErrorCheck(devrim, err)
				_, err = groupChannel.AddParticipant(devrim.Id)
				So(err, ShouldBeNil)

				ses, err := models.FetchOrCreateSession(ownerAccount.Nick, groupName)
				tests.ResultedWithNoErrorCheck(ses, err)

				pmr := models.PrivateChannelRequest{}

				pmr.AccountId = ownerAccount.Id

				pmr.Body = "new conversation"
				pmr.GroupName = groupName
				pmr.Recipients = []string{"devrim"}

				channelContainer, err := rest.SendPrivateChannelRequest(pmr)
				So(err, ShouldBeNil)
				So(channelContainer, ShouldNotBeNil)

				Convey("First user should be able to add second and third users to conversation", func() {
					_, err = rest.AddChannelParticipant(channelContainer.Channel.Id, ownerAccount.Id, secondAccount.Id, thirdAccount.Id)
					So(err, ShouldBeNil)
					participants, err := rest.ListChannelParticipants(channelContainer.Channel.Id, ownerAccount.Id)
					So(err, ShouldBeNil)
					So(participants, ShouldNotBeNil)
					// it is four because first user is "devrim" here
					So(len(participants), ShouldEqual, 4)

					Convey("First user should not be able to re-add second participant", func() {
						_, err = rest.AddChannelParticipant(channelContainer.Channel.Id, ownerAccount.Id, secondAccount.Id)
						So(err, ShouldBeNil)

						participants, err := rest.ListChannelParticipants(channelContainer.Channel.Id, ownerAccount.Id)
						So(err, ShouldBeNil)
						So(participants, ShouldNotBeNil)
						So(len(participants), ShouldEqual, 4)
					})

					Convey("Second user should be able to leave conversation", func() {
						_, err = rest.DeleteChannelParticipant(channelContainer.Channel.Id, secondAccount.Id, secondAccount.Id)
						So(err, ShouldBeNil)

						participants, err := rest.ListChannelParticipants(channelContainer.Channel.Id, ownerAccount.Id)
						So(err, ShouldBeNil)
						So(participants, ShouldNotBeNil)
						So(len(participants), ShouldEqual, 3)

						Convey("A user who is not participant of a conversation should not be able to add another user to the conversation", func() {
							_, err = rest.AddChannelParticipant(channelContainer.Channel.Id, secondAccount.Id, forthAccount.Id)
							So(err, ShouldNotBeNil)

							participants, err := rest.ListChannelParticipants(channelContainer.Channel.Id, ownerAccount.Id)
							So(err, ShouldBeNil)
							So(participants, ShouldNotBeNil)
							So(len(participants), ShouldEqual, 3)
						})
					})

					Convey("Channel owner should be able to kick another conversation participant", func() {
						_, err = rest.DeleteChannelParticipant(channelContainer.Channel.Id, ownerAccount.Id, secondAccount.Id)
						So(err, ShouldBeNil)

						participants, err := rest.ListChannelParticipants(channelContainer.Channel.Id, ownerAccount.Id)
						So(err, ShouldBeNil)
						So(participants, ShouldNotBeNil)
						So(len(participants), ShouldEqual, 3)
					})

					Convey("when a user is blocked", func() {
						_, err = rest.BlockChannelParticipant(channelContainer.Channel.Id, ownerAccount.Id, secondAccount.Id)
						So(err, ShouldBeNil)

						Convey("it should not be in channel participant list", func() {
							participants, err := rest.ListChannelParticipants(channelContainer.Channel.Id, ownerAccount.Id)
							So(err, ShouldBeNil)
							So(participants, ShouldNotBeNil)
							So(len(participants), ShouldEqual, 3)
						})

						Convey("should not be able to add it back", func() {
							_, err = rest.AddChannelParticipant(channelContainer.Channel.Id, ownerAccount.Id, secondAccount.Id)
							So(err, ShouldNotBeNil)
						})

						Convey("should be able to unblock", func() {
							_, err = rest.UnblockChannelParticipant(channelContainer.Channel.Id, ownerAccount.Id, secondAccount.Id)
							So(err, ShouldBeNil)

							Convey("it should not be in channel participant list still", func() {
								participants, err := rest.ListChannelParticipants(channelContainer.Channel.Id, ownerAccount.Id)
								So(err, ShouldBeNil)
								So(participants, ShouldNotBeNil)
								So(len(participants), ShouldEqual, 3)
							})

							Convey("when we add the same user as participant", func() {
								_, err = rest.AddChannelParticipant(channelContainer.Channel.Id, ownerAccount.Id, secondAccount.Id, thirdAccount.Id)
								So(err, ShouldBeNil)

								Convey("it should be in channel participant list", func() {
									participants, err := rest.ListChannelParticipants(channelContainer.Channel.Id, ownerAccount.Id)
									So(err, ShouldBeNil)
									So(participants, ShouldNotBeNil)
									So(len(participants), ShouldEqual, 4)
								})
							})
						})
					})

					Convey("Second user should not be able to kick another conversation participant", func() {
						_, err = rest.DeleteChannelParticipant(channelContainer.Channel.Id, secondAccount.Id, thirdAccount.Id)
						So(err, ShouldNotBeNil)
					})

				})
				Convey("First user should be able to invite second user", func() {
					_, err = rest.InviteChannelParticipant(channelContainer.Channel.Id, ownerAccount.Id, secondAccount.Id)
					So(err, ShouldBeNil)
					participants, err := rest.ListChannelParticipants(channelContainer.Channel.Id, ownerAccount.Id)
					So(err, ShouldBeNil)
					So(participants, ShouldNotBeNil)
					// it is four because first user is "devrim" here
					So(len(participants), ShouldEqual, 2)

					Convey("Second user should be able to reject invitation", func() {
						ses, err := models.FetchOrCreateSession(secondAccount.Nick, groupName)
						So(err, ShouldBeNil)
						So(ses, ShouldNotBeNil)

						err = rest.RejectInvitation(channelContainer.Channel.Id, ses.ClientId)
						So(err, ShouldBeNil)

						participants, err := rest.ListChannelParticipants(channelContainer.Channel.Id, ownerAccount.Id)
						So(err, ShouldBeNil)
						So(participants, ShouldNotBeNil)
						So(len(participants), ShouldEqual, 2)
					})

					Convey("Second user should be able to accept invitation", func() {
						ses, err := models.FetchOrCreateSession(secondAccount.Nick, groupName)
						So(err, ShouldBeNil)
						So(ses, ShouldNotBeNil)

						err = rest.AcceptInvitation(channelContainer.Channel.Id, ses.ClientId)
						So(err, ShouldBeNil)

						participants, err := rest.ListChannelParticipants(channelContainer.Channel.Id, ownerAccount.Id)
						So(err, ShouldBeNil)
						So(participants, ShouldNotBeNil)
						So(len(participants), ShouldEqual, 3)
					})
				})

				// TODO Until we find a better way for handling async stuff, this test is skipped. Instead of sleep, we should use some
				// timeouts for testing these kind of stuff.
				SkipConvey("All private messages must be deleted when all participant users leave the channel", func() {
					account := models.NewAccount()
					err = account.ByNick("devrim")
					So(err, ShouldBeNil)

					_, err = rest.DeleteChannelParticipant(channelContainer.Channel.Id, account.Id, account.Id)
					So(err, ShouldBeNil)

					_, err = rest.DeleteChannelParticipant(channelContainer.Channel.Id, ownerAccount.Id, ownerAccount.Id)
					So(err, ShouldBeNil)

					time.Sleep(1 * time.Second)

					testChannel := models.NewChannel()
					err := testChannel.ById(channelContainer.Channel.Id)
					So(err, ShouldEqual, bongo.RecordNotFound)

					testChannelList := models.NewChannelMessageList()
					err = bongo.B.Unscoped().Where("channel_id = ?", channelContainer.Channel.Id).Find(testChannelList).Error
					So(err, ShouldEqual, bongo.RecordNotFound)

					testMessage := models.NewChannelMessage()
					err = bongo.B.Unscoped().Where("initial_channel_id = ?", channelContainer.Channel.Id).Find(testMessage).Error
					So(err, ShouldEqual, bongo.RecordNotFound)
				})
				Convey("Users should not be able to add/remove users to/from bot channels", func() {
					ownerAccount, _, groupName := models.CreateRandomGroupDataWithChecks()

					participant := models.NewAccount()
					participant.OldId = AccountOldId.Hex()
					participant, err = rest.CreateAccount(participant)
					So(err, ShouldBeNil)
					So(participant, ShouldNotBeNil)

					ses, err := models.FetchOrCreateSession(ownerAccount.Nick, groupName)
					So(err, ShouldBeNil)

					ch, err := rest.CreateChannelByGroupNameAndType(ownerAccount.Id, groupName, models.Channel_TYPE_BOT, ses.ClientId)
					So(err, ShouldBeNil)
					So(ch, ShouldNotBeNil)

					_, err = rest.AddChannelParticipant(ch.Id, ownerAccount.Id, participant.Id)
					So(strings.Contains(err.Error(), "can not add participants for bot channel"), ShouldBeTrue)
				})

				Convey("Users should be able to add/remove users to/from topic channels", func() {
					ownerAccount, _, groupName := models.CreateRandomGroupDataWithChecks()

					participant := models.NewAccount()
					participant.OldId = AccountOldId.Hex()
					participant, err = rest.CreateAccount(participant)
					So(err, ShouldBeNil)
					So(participant, ShouldNotBeNil)

					ses, err := models.FetchOrCreateSession(ownerAccount.Nick, groupName)
					So(err, ShouldBeNil)

					ch, err := rest.CreateChannelByGroupNameAndType(ownerAccount.Id, groupName, models.Channel_TYPE_TOPIC, ses.ClientId)
					So(err, ShouldBeNil)
					So(ch, ShouldNotBeNil)

					_, err = rest.AddChannelParticipant(ch.Id, ownerAccount.Id, participant.Id)
					So(err, ShouldBeNil)
				})
			})
		})
	})
}
<commit_msg>Socialapi: make sure endpoints are idempotent<commit_after>package main

import (
	"socialapi/models"
	"socialapi/rest"
	"socialapi/workers/common/tests"
	"strings"
	"testing"
	"time"

	"github.com/koding/bongo"
	"github.com/koding/runner"

	. "github.com/smartystreets/goconvey/convey"
)

func TestChannelParticipantOperations(t *testing.T) {
	tests.WithRunner(t, func(r *runner.Runner) {
		Convey("while testing channel participants", t, func() {
			Convey("First Create Users and initiate conversation", func() {
				ownerAccount, groupChannel, groupName := models.CreateRandomGroupDataWithChecks()

				secondAccount, err := models.CreateAccountInBothDbs()
				tests.ResultedWithNoErrorCheck(secondAccount, err)
				_, err = groupChannel.AddParticipant(secondAccount.Id)
				So(err, ShouldBeNil)

				thirdAccount, err := models.CreateAccountInBothDbs()
				tests.ResultedWithNoErrorCheck(thirdAccount, err)
				_, err = groupChannel.AddParticipant(thirdAccount.Id)
				So(err, ShouldBeNil)

				forthAccount, err := models.CreateAccountInBothDbs()
				tests.ResultedWithNoErrorCheck(forthAccount, err)
				_, err = groupChannel.AddParticipant(forthAccount.Id)
				So(err, ShouldBeNil)

				devrim, err := models.CreateAccountInBothDbsWithNick("devrim")
				tests.ResultedWithNoErrorCheck(devrim, err)
				_, err = groupChannel.AddParticipant(devrim.Id)
				So(err, ShouldBeNil)

				ses, err := models.FetchOrCreateSession(ownerAccount.Nick, groupName)
				tests.ResultedWithNoErrorCheck(ses, err)

				pmr := models.PrivateChannelRequest{}

				pmr.AccountId = ownerAccount.Id

				pmr.Body = "new conversation"
				pmr.GroupName = groupName
				pmr.Recipients = []string{"devrim"}

				channelContainer, err := rest.SendPrivateChannelRequest(pmr)
				So(err, ShouldBeNil)
				So(channelContainer, ShouldNotBeNil)

				Convey("First user should be able to add second and third users to conversation", func() {
					_, err = rest.AddChannelParticipant(channelContainer.Channel.Id, ownerAccount.Id, secondAccount.Id, thirdAccount.Id)
					So(err, ShouldBeNil)
					participants, err := rest.ListChannelParticipants(channelContainer.Channel.Id, ownerAccount.Id)
					So(err, ShouldBeNil)
					So(participants, ShouldNotBeNil)
					// it is four because first user is "devrim" here
					So(len(participants), ShouldEqual, 4)

					Convey("First user should not be able to re-add second participant", func() {
						_, err = rest.AddChannelParticipant(channelContainer.Channel.Id, ownerAccount.Id, secondAccount.Id)
						So(err, ShouldBeNil)

						participants, err := rest.ListChannelParticipants(channelContainer.Channel.Id, ownerAccount.Id)
						So(err, ShouldBeNil)
						So(participants, ShouldNotBeNil)
						So(len(participants), ShouldEqual, 4)
					})

					Convey("Second user should be able to leave conversation", func() {
						_, err = rest.DeleteChannelParticipant(channelContainer.Channel.Id, secondAccount.Id, secondAccount.Id)
						So(err, ShouldBeNil)

						participants, err := rest.ListChannelParticipants(channelContainer.Channel.Id, ownerAccount.Id)
						So(err, ShouldBeNil)
						So(participants, ShouldNotBeNil)
						So(len(participants), ShouldEqual, 3)

						Convey("A user who is not participant of a conversation should not be able to add another user to the conversation", func() {
							_, err = rest.AddChannelParticipant(channelContainer.Channel.Id, secondAccount.Id, forthAccount.Id)
							So(err, ShouldNotBeNil)

							participants, err := rest.ListChannelParticipants(channelContainer.Channel.Id, ownerAccount.Id)
							So(err, ShouldBeNil)
							So(participants, ShouldNotBeNil)
							So(len(participants), ShouldEqual, 3)
						})
					})

					Convey("Channel owner should be able to kick another conversation participant", func() {
						_, err = rest.DeleteChannelParticipant(channelContainer.Channel.Id, ownerAccount.Id, secondAccount.Id)
						So(err, ShouldBeNil)

						participants, err := rest.ListChannelParticipants(channelContainer.Channel.Id, ownerAccount.Id)
						So(err, ShouldBeNil)
						So(participants, ShouldNotBeNil)
						So(len(participants), ShouldEqual, 3)
					})

					Convey("when a user is blocked", func() {
						_, err = rest.BlockChannelParticipant(channelContainer.Channel.Id, ownerAccount.Id, secondAccount.Id)
						So(err, ShouldBeNil)

						Convey("it should not be in channel participant list", func() {
							participants, err := rest.ListChannelParticipants(channelContainer.Channel.Id, ownerAccount.Id)
							So(err, ShouldBeNil)
							So(participants, ShouldNotBeNil)
							So(len(participants), ShouldEqual, 3)
						})

						Convey("should not be able to add it back", func() {
							_, err = rest.AddChannelParticipant(channelContainer.Channel.Id, ownerAccount.Id, secondAccount.Id)
							So(err, ShouldNotBeNil)
						})

						Convey("should be able to unblock", func() {
							_, err = rest.UnblockChannelParticipant(channelContainer.Channel.Id, ownerAccount.Id, secondAccount.Id)
							So(err, ShouldBeNil)

							Convey("it should not be in channel participant list still", func() {
								participants, err := rest.ListChannelParticipants(channelContainer.Channel.Id, ownerAccount.Id)
								So(err, ShouldBeNil)
								So(participants, ShouldNotBeNil)
								So(len(participants), ShouldEqual, 3)
							})

							Convey("when we add the same user as participant", func() {
								_, err = rest.AddChannelParticipant(channelContainer.Channel.Id, ownerAccount.Id, secondAccount.Id, thirdAccount.Id)
								So(err, ShouldBeNil)

								Convey("it should be in channel participant list", func() {
									participants, err := rest.ListChannelParticipants(channelContainer.Channel.Id, ownerAccount.Id)
									So(err, ShouldBeNil)
									So(participants, ShouldNotBeNil)
									So(len(participants), ShouldEqual, 4)
								})
							})
						})
					})

					Convey("Second user should not be able to kick another conversation participant", func() {
						_, err = rest.DeleteChannelParticipant(channelContainer.Channel.Id, secondAccount.Id, thirdAccount.Id)
						So(err, ShouldNotBeNil)
					})

				})
				Convey("First user should be able to invite second user", func() {
					_, err = rest.InviteChannelParticipant(channelContainer.Channel.Id, ownerAccount.Id, secondAccount.Id)
					So(err, ShouldBeNil)
					participants, err := rest.ListChannelParticipants(channelContainer.Channel.Id, ownerAccount.Id)
					So(err, ShouldBeNil)
					So(participants, ShouldNotBeNil)
					// it is four because first user is "devrim" here
					So(len(participants), ShouldEqual, 2)

					Convey("Second user should be able to reject invitation", func() {
						ses, err := models.FetchOrCreateSession(secondAccount.Nick, groupName)
						So(err, ShouldBeNil)
						So(ses, ShouldNotBeNil)

						err = rest.RejectInvitation(channelContainer.Channel.Id, ses.ClientId)
						So(err, ShouldBeNil)

						participants, err := rest.ListChannelParticipants(channelContainer.Channel.Id, ownerAccount.Id)
						So(err, ShouldBeNil)
						So(participants, ShouldNotBeNil)
						So(len(participants), ShouldEqual, 2)
					})

					Convey("Second user should be able to accept invitation", func() {
						ses, err := models.FetchOrCreateSession(secondAccount.Nick, groupName)
						So(err, ShouldBeNil)
						So(ses, ShouldNotBeNil)

						err = rest.AcceptInvitation(channelContainer.Channel.Id, ses.ClientId)
						So(err, ShouldBeNil)

						participants, err := rest.ListChannelParticipants(channelContainer.Channel.Id, ownerAccount.Id)
						So(err, ShouldBeNil)
						So(participants, ShouldNotBeNil)
						So(len(participants), ShouldEqual, 3)
					})
				})

				// TODO Until we find a better way for handling async stuff, this test is skipped. Instead of sleep, we should use some
				// timeouts for testing these kind of stuff.
				SkipConvey("All private messages must be deleted when all participant users leave the channel", func() {
					account := models.NewAccount()
					err = account.ByNick("devrim")
					So(err, ShouldBeNil)

					_, err = rest.DeleteChannelParticipant(channelContainer.Channel.Id, account.Id, account.Id)
					So(err, ShouldBeNil)

					_, err = rest.DeleteChannelParticipant(channelContainer.Channel.Id, ownerAccount.Id, ownerAccount.Id)
					So(err, ShouldBeNil)

					time.Sleep(1 * time.Second)

					testChannel := models.NewChannel()
					err := testChannel.ById(channelContainer.Channel.Id)
					So(err, ShouldEqual, bongo.RecordNotFound)

					testChannelList := models.NewChannelMessageList()
					err = bongo.B.Unscoped().Where("channel_id = ?", channelContainer.Channel.Id).Find(testChannelList).Error
					So(err, ShouldEqual, bongo.RecordNotFound)

					testMessage := models.NewChannelMessage()
					err = bongo.B.Unscoped().Where("initial_channel_id = ?", channelContainer.Channel.Id).Find(testMessage).Error
					So(err, ShouldEqual, bongo.RecordNotFound)
				})
				Convey("Users should not be able to add/remove users to/from bot channels", func() {
					ownerAccount, _, groupName := models.CreateRandomGroupDataWithChecks()

					participant := models.NewAccount()
					participant.OldId = AccountOldId.Hex()
					participant, err = rest.CreateAccount(participant)
					So(err, ShouldBeNil)
					So(participant, ShouldNotBeNil)

					ses, err := models.FetchOrCreateSession(ownerAccount.Nick, groupName)
					So(err, ShouldBeNil)

					ch, err := rest.CreateChannelByGroupNameAndType(ownerAccount.Id, groupName, models.Channel_TYPE_BOT, ses.ClientId)
					So(err, ShouldBeNil)
					So(ch, ShouldNotBeNil)

					_, err = rest.AddChannelParticipant(ch.Id, ownerAccount.Id, participant.Id)
					So(strings.Contains(err.Error(), "can not add participants for bot channel"), ShouldBeTrue)
				})

				Convey("Users should be able to add/remove users to/from topic channels", func() {
					ownerAccount, _, groupName := models.CreateRandomGroupDataWithChecks()

					participant := models.NewAccount()
					participant.OldId = AccountOldId.Hex()
					participant, err = rest.CreateAccount(participant)
					So(err, ShouldBeNil)
					So(participant, ShouldNotBeNil)

					ses, err := models.FetchOrCreateSession(ownerAccount.Nick, groupName)
					So(err, ShouldBeNil)

					ch, err := rest.CreateChannelByGroupNameAndType(ownerAccount.Id, groupName, models.Channel_TYPE_TOPIC, ses.ClientId)
					So(err, ShouldBeNil)
					So(ch, ShouldNotBeNil)

					_, err = rest.AddChannelParticipant(ch.Id, ownerAccount.Id, participant.Id)
					So(err, ShouldBeNil)

					Convey("adding same user again should success", func() {
						_, err = rest.AddChannelParticipant(ch.Id, ownerAccount.Id, participant.Id)
						So(err, ShouldBeNil)
					})

					_, err = rest.DeleteChannelParticipant(ch.Id, ownerAccount.Id, participant.Id)
					So(err, ShouldBeNil)

					Convey("removing same user again should success", func() {
						_, err = rest.DeleteChannelParticipant(ch.Id, ownerAccount.Id, participant.Id)
						So(err, ShouldBeNil)
					})
				})
			})
		})
	})
}
<|endoftext|>


<commit_before>package handler

import (
	"bytes"
	"encoding/json"
	"fmt"
	"io"
	"net"
	"net/http"
	"time"

	"koding/tools/utils"
	"socialapi/config"
	"socialapi/models"
	"socialapi/workers/common/metrics"
	"socialapi/workers/common/response"
	"strconv"
	"strings"

	"github.com/juju/ratelimit"
	kmetrics "github.com/koding/metrics"

	tigertonic "github.com/rcrowley/go-tigertonic"

	gometrics "github.com/rcrowley/go-metrics"
)

const (
	PostRequest   = "POST"
	GetRequest    = "GET"
	DeleteRequest = "DELETE"
)

var (
	// TODO allowed origins must be configurable
	cors     = tigertonic.NewCORSBuilder().AddAllowedOrigins("*")
	conf     *config.Config
	trackers *metrics.Trackers
)

type Request struct {
	Handler        interface{}
	Endpoint       string
	Type           string
	Name           string
	CollectMetrics bool
	Metrics        *kmetrics.Metrics
	// used for external requests
	Params    map[string]string
	Cookie    string
	Cookies   []*http.Cookie
	Body      interface{}
	Headers   map[string]string
	Ratelimit func(*http.Request) *ratelimit.Bucket
}

// todo add prooper logging
func getAccount(r *http.Request) *models.Account {
	cookie, err := r.Cookie("clientId")
	if err != nil {
		return models.NewAccount()
	}

	// if cookie doenst exists return empty account
	if cookie.Value == "" {
		return models.NewAccount()
	}

	session, err := models.Cache.Session.ById(cookie.Value)
	if err != nil {
		return models.NewAccount()
	}

	// if session doesnt have username, return empty account
	if session.Username == "" {
		return models.NewAccount()
	}

	acc, err := models.Cache.Account.ByNick(session.Username)
	if err != nil {
		return models.NewAccount()
	}

	return acc
}

const timedOutMsg = `{"description":"request timed out","error":"koding.RequestTimedoutError"}`
const timeoutDuration = time.Second * 30

func Wrapper(r Request) http.Handler {
	handler := r.Handler

	var hHandler http.Handler

	// count the statuses of the requests
	hHandler = buildHandlerWithStatusCount(handler, r)

	hHandler = buildHandlerWithTimeTracking(hHandler, r)

	// every request should return under 30 secs, if not return 503 service
	// unavailable
	hHandler = http.TimeoutHandler(hHandler, timeoutDuration, timedOutMsg)

	// set rate limiting if the handler has one
	if r.Ratelimit != nil {
		hHandler = BuildHandlerWithRateLimit(hHandler, r.Ratelimit)
	}
	// create the final handler
	return cors.Build(hHandler)
}

// count the statuses of the requests
func buildHandlerWithStatusCount(handler interface{}, r Request) http.Handler {
	return CountedByStatus(
		tigertonic.Marshaled(handler), r.Name, r.CollectMetrics,
	)
}

// add request time tracking
func buildHandlerWithTimeTracking(handler http.Handler, r Request) http.Handler {
	var registry gometrics.Registry

	if r.Metrics != nil {
		registry = r.Metrics.Registry
	}

	return tigertonic.Timed(
		handler,
		r.Name,
		registry,
	)
}

func BuildHandlerWithContext(handler http.Handler) http.Handler {
	// add context
	return tigertonic.If(
		func(r *http.Request) (http.Header, error) {
			// this is an example
			// set group name to context
			//

			context := &models.Context{
				GroupName: "koding",
				Client: &models.Client{
					Account: getAccount(r),
					IP:      net.ParseIP(utils.GetIpAddress(r)),
				},
			}

			*(tigertonic.Context(r).(*models.Context)) = *context
			return nil, nil
		},
		handler,
	)
}

func BuildHandlerWithRateLimit(handler http.Handler, limiter func(*http.Request) *ratelimit.Bucket) http.Handler {
	// add context
	return tigertonic.If(
		func(r *http.Request) (http.Header, error) {
			// get the limiter
			bucket := limiter(r)

			h := http.Header{}
			h.Add("X-RateLimit-Limit", strconv.FormatInt(int64(bucket.Rate()*60), 10))

			// check if any 		throttling is enabled and then check token's available.
			// Tokens are filled per frequency of the initial bucket, so every request
			// is going to take one token from the bucket. If many requests come in (in
			// span time larger than the bucket's frequency), there will be no token's
			// available more so it will return a zero.
			timeToWait := bucket.Take(1)
			if timeToWait > time.Duration(0) {
				h.Add("Retry-After", strconv.FormatInt(int64(timeToWait.Seconds()), 10))
				return h, &response.LimitRateExceededError{}
			}

			return h, nil
		},
		handler,
	)
}

//----------------------------------------------------------
// CounterByStatus
//----------------------------------------------------------

type CounterByStatus struct {
	name           string
	handler        http.Handler
	collectMetrics bool
}

func (c *CounterByStatus) ServeHTTP(w0 http.ResponseWriter, r *http.Request) {
	w := tigertonic.NewTeeHeaderResponseWriter(w0)
	c.handler.ServeHTTP(w, r)

	if w.StatusCode <= 300 {
		c.track()
	}
}

func (c *CounterByStatus) track() {
	// don't log if analytics are disabled for that endpoint
	if !c.collectMetrics {
		return
	}

	// set `conf` and `trackers` if either is not set
	if conf == nil || trackers == nil {
		conf = config.MustGet()

		// don't log if analytics are disabled globally
		if !conf.Mixpanel.Enabled {
			return
		}

		trackers = metrics.InitTrackers(
			metrics.NewMixpanelTracker(conf.Mixpanel.Token),
		)
	}

	trackers.Track(c.name)
}

func CountedByStatus(handler http.Handler, name string, collectMetrics bool) *CounterByStatus {
	return &CounterByStatus{
		name:           name,
		handler:        handler,
		collectMetrics: collectMetrics,
	}
}

//
func MakeRequest(request *Request) (*http.Response, error) {
	if request.Cookie != "" {
		request.Cookies = parseCookiesToArray(request.Cookie)
	}

	request.Endpoint = prepareQueryString(request.Endpoint, request.Params)

	client := new(http.Client)
	hostname := config.MustGet().CustomDomain.Local
	endpoint := fmt.Sprintf("%s/%s", hostname, request.Endpoint)

	var byteData io.Reader
	if request.Body != nil {
		body, err := json.Marshal(request.Body)
		if err != nil {
			return nil, err
		}
		byteData = bytes.NewReader(body)
	}

	req, err := http.NewRequest(request.Type, endpoint, byteData)
	req = prepareHeaders(req, request.Headers)

	if err != nil {
		return nil, err
	}

	// Add cookies
	for _, cookie := range request.Cookies {
		req.AddCookie(cookie)
	}

	return client.Do(req)
}

func prepareQueryString(endpoint string, params map[string]string) string {
	if len(params) == 0 {
		return endpoint
	}

	fullPath := fmt.Sprintf("%s?", endpoint)

	for key, value := range params {
		fullPath = fmt.Sprintf("%s%s=%s&", fullPath, key, value)
	}

	return fullPath[0 : len(fullPath)-1]
}

func prepareHeaders(req *http.Request, headers map[string]string) *http.Request {
	newReq := new(http.Request)
	*newReq = *req
	for k, v := range headers {
		newReq.Header.Set(k, v)
	}

	return newReq
}

func parseCookiesToArray(cookie string) []*http.Cookie {
	pairs := strings.Split(cookie, "; ")
	cookies := make([]*http.Cookie, 0)

	if len(pairs) == 0 {
		return cookies
	}

	for _, val := range pairs {
		cp := strings.Split(val, "=")
		if len(cp) != 2 {
			continue
		}

		c := new(http.Cookie)
		c.Name = cp[0]
		c.Value = cp[1]

		cookies = append(cookies, c)
	}

	return cookies
}

func ParseCookiesToMap(cookie string) map[string]*http.Cookie {
	pairs := strings.Split(cookie, "; ")
	cookies := make(map[string]*http.Cookie, 0)

	if len(pairs) == 0 {
		return cookies
	}

	for _, val := range pairs {
		cp := strings.Split(val, "=")
		if len(cp) != 2 {
			continue
		}

		c := new(http.Cookie)
		c.Name = cp[0]
		c.Value = cp[1]

		cookies[cp[0]] = c
	}

	return cookies
}
<commit_msg>geoip: getting user with nick is added to handler<commit_after>package handler

import (
	"bytes"
	"encoding/json"
	"fmt"
	"io"
	"net"
	"net/http"
	"time"

	"koding/tools/utils"
	"socialapi/config"
	"socialapi/models"
	"socialapi/workers/common/metrics"
	"socialapi/workers/common/response"
	"strconv"
	"strings"

	"github.com/juju/ratelimit"
	kmetrics "github.com/koding/metrics"

	tigertonic "github.com/rcrowley/go-tigertonic"

	gometrics "github.com/rcrowley/go-metrics"
)

const (
	PostRequest   = "POST"
	GetRequest    = "GET"
	DeleteRequest = "DELETE"
)

var (
	// TODO allowed origins must be configurable
	cors     = tigertonic.NewCORSBuilder().AddAllowedOrigins("*")
	conf     *config.Config
	trackers *metrics.Trackers
)

type Request struct {
	Handler        interface{}
	Endpoint       string
	Type           string
	Name           string
	CollectMetrics bool
	Metrics        *kmetrics.Metrics
	// used for external requests
	Params    map[string]string
	Cookie    string
	Cookies   []*http.Cookie
	Body      interface{}
	Headers   map[string]string
	Ratelimit func(*http.Request) *ratelimit.Bucket
}

// todo add prooper logging
func getAccount(r *http.Request) *models.Account {
	cookie, err := r.Cookie("clientId")
	if err != nil {
		return models.NewAccount()
	}

	// if cookie doenst exists return empty account
	if cookie.Value == "" {
		return models.NewAccount()
	}

	session, err := models.Cache.Session.ById(cookie.Value)
	if err != nil {
		return models.NewAccount()
	}

	// if session doesnt have username, return empty account
	if session.Username == "" {
		return models.NewAccount()
	}

    acc := models.NewAccount()
    // err is ignored intentionally
    acc.ByNick(session.Username)

	return acc
}

const timedOutMsg = `{"description":"request timed out","error":"koding.RequestTimedoutError"}`
const timeoutDuration = time.Second * 30

func Wrapper(r Request) http.Handler {
	handler := r.Handler

	var hHandler http.Handler

	// count the statuses of the requests
	hHandler = buildHandlerWithStatusCount(handler, r)

	hHandler = buildHandlerWithTimeTracking(hHandler, r)

	// every request should return under 30 secs, if not return 503 service
	// unavailable
	hHandler = http.TimeoutHandler(hHandler, timeoutDuration, timedOutMsg)

	// set rate limiting if the handler has one
	if r.Ratelimit != nil {
		hHandler = BuildHandlerWithRateLimit(hHandler, r.Ratelimit)
	}
	// create the final handler
	return cors.Build(hHandler)
}

// count the statuses of the requests
func buildHandlerWithStatusCount(handler interface{}, r Request) http.Handler {
	return CountedByStatus(
		tigertonic.Marshaled(handler), r.Name, r.CollectMetrics,
	)
}

// add request time tracking
func buildHandlerWithTimeTracking(handler http.Handler, r Request) http.Handler {
	var registry gometrics.Registry

	if r.Metrics != nil {
		registry = r.Metrics.Registry
	}

	return tigertonic.Timed(
		handler,
		r.Name,
		registry,
	)
}

func BuildHandlerWithContext(handler http.Handler) http.Handler {
	// add context
	return tigertonic.If(
		func(r *http.Request) (http.Header, error) {
			// this is an example
			// set group name to context
			//

			context := &models.Context{
				GroupName: "koding",
				Client: &models.Client{
					Account: getAccount(r),
					IP:      net.ParseIP(utils.GetIpAddress(r)),
				},
			}

			*(tigertonic.Context(r).(*models.Context)) = *context
			return nil, nil
		},
		handler,
	)
}

func BuildHandlerWithRateLimit(handler http.Handler, limiter func(*http.Request) *ratelimit.Bucket) http.Handler {
	// add context
	return tigertonic.If(
		func(r *http.Request) (http.Header, error) {
			// get the limiter
			bucket := limiter(r)

			h := http.Header{}
			h.Add("X-RateLimit-Limit", strconv.FormatInt(int64(bucket.Rate()*60), 10))

			// check if any 		throttling is enabled and then check token's available.
			// Tokens are filled per frequency of the initial bucket, so every request
			// is going to take one token from the bucket. If many requests come in (in
			// span time larger than the bucket's frequency), there will be no token's
			// available more so it will return a zero.
			timeToWait := bucket.Take(1)
			if timeToWait > time.Duration(0) {
				h.Add("Retry-After", strconv.FormatInt(int64(timeToWait.Seconds()), 10))
				return h, &response.LimitRateExceededError{}
			}

			return h, nil
		},
		handler,
	)
}

//----------------------------------------------------------
// CounterByStatus
//----------------------------------------------------------

type CounterByStatus struct {
	name           string
	handler        http.Handler
	collectMetrics bool
}

func (c *CounterByStatus) ServeHTTP(w0 http.ResponseWriter, r *http.Request) {
	w := tigertonic.NewTeeHeaderResponseWriter(w0)
	c.handler.ServeHTTP(w, r)

	if w.StatusCode <= 300 {
		c.track()
	}
}

func (c *CounterByStatus) track() {
	// don't log if analytics are disabled for that endpoint
	if !c.collectMetrics {
		return
	}

	// set `conf` and `trackers` if either is not set
	if conf == nil || trackers == nil {
		conf = config.MustGet()

		// don't log if analytics are disabled globally
		if !conf.Mixpanel.Enabled {
			return
		}

		trackers = metrics.InitTrackers(
			metrics.NewMixpanelTracker(conf.Mixpanel.Token),
		)
	}

	trackers.Track(c.name)
}

func CountedByStatus(handler http.Handler, name string, collectMetrics bool) *CounterByStatus {
	return &CounterByStatus{
		name:           name,
		handler:        handler,
		collectMetrics: collectMetrics,
	}
}

//
func MakeRequest(request *Request) (*http.Response, error) {
	if request.Cookie != "" {
		request.Cookies = parseCookiesToArray(request.Cookie)
	}

	request.Endpoint = prepareQueryString(request.Endpoint, request.Params)

	client := new(http.Client)
	hostname := config.MustGet().CustomDomain.Local
	endpoint := fmt.Sprintf("%s/%s", hostname, request.Endpoint)

	var byteData io.Reader
	if request.Body != nil {
		body, err := json.Marshal(request.Body)
		if err != nil {
			return nil, err
		}
		byteData = bytes.NewReader(body)
	}

	req, err := http.NewRequest(request.Type, endpoint, byteData)
	req = prepareHeaders(req, request.Headers)

	if err != nil {
		return nil, err
	}

	// Add cookies
	for _, cookie := range request.Cookies {
		req.AddCookie(cookie)
	}

	return client.Do(req)
}

func prepareQueryString(endpoint string, params map[string]string) string {
	if len(params) == 0 {
		return endpoint
	}

	fullPath := fmt.Sprintf("%s?", endpoint)

	for key, value := range params {
		fullPath = fmt.Sprintf("%s%s=%s&", fullPath, key, value)
	}

	return fullPath[0 : len(fullPath)-1]
}

func prepareHeaders(req *http.Request, headers map[string]string) *http.Request {
	newReq := new(http.Request)
	*newReq = *req
	for k, v := range headers {
		newReq.Header.Set(k, v)
	}

	return newReq
}

func parseCookiesToArray(cookie string) []*http.Cookie {
	pairs := strings.Split(cookie, "; ")
	cookies := make([]*http.Cookie, 0)

	if len(pairs) == 0 {
		return cookies
	}

	for _, val := range pairs {
		cp := strings.Split(val, "=")
		if len(cp) != 2 {
			continue
		}

		c := new(http.Cookie)
		c.Name = cp[0]
		c.Value = cp[1]

		cookies = append(cookies, c)
	}

	return cookies
}

func ParseCookiesToMap(cookie string) map[string]*http.Cookie {
	pairs := strings.Split(cookie, "; ")
	cookies := make(map[string]*http.Cookie, 0)

	if len(pairs) == 0 {
		return cookies
	}

	for _, val := range pairs {
		cp := strings.Split(val, "=")
		if len(cp) != 2 {
			continue
		}

		c := new(http.Cookie)
		c.Name = cp[0]
		c.Value = cp[1]

		cookies[cp[0]] = c
	}

	return cookies
}
<|endoftext|>


<commit_before>package main

func messageCheck(msg string) string {
	var result = ""
	if msg == "#help" {
		result = "ควย"
	} else {
		result = getReplyMessageFromUser(msg)
	}

	return result
}
<commit_msg>v.3.7.0<commit_after>package main

func messageCheck(msg string) string {
	var result = ""
	if msg == "#help" {
		result = "ควย"
	} else {
		//result = getReplyMessageFromUser(msg)
		result = msg
	}

	return result
}
<|endoftext|>


<commit_before>package chatroom

import (
    "container/list"
    "time"
)

type Event struct {
    Type        string
    User        string
    Timestamp   int
    Text        string
}

type Subscription struct {
    Archive     []Event
    New         <-chan Event
}

func (s Subscription) Cancel() {
    unsubscribe <- s.New
    drain(s.New)
}

func (typ, usr, msg String) Event {
    return Event{typ, usr, int(time.Now().Unix()), msg}
}

func Subscribe() Subscription {
    resp := make(chan Subscription)
    subscribe <- resp //this means push response onto subscribe
    return <-resp //this means wait on response, then return whatever value comes out of resp
}

func Join(user string) {
    publish <- newEvent("join", user, "")
}

func Say(user, message string) {
    publish <- newEvent("message", user, message)
}

func Leave(user) {
    publish <- newEvent("leave", user, "")
}

const archiveSize = 10

var (
    subscribe = make(chan (chan<- Subscription), 10)
    unsubscribe = make(chan (<-chan Event), 10)
    publish = make(chan Event, 10)
)

func chatroom() {
    archive := list.New()
    subscribers := list.New()

    for {
        select {
        case ch := <-subscribe:
            var events []Event
            for e := archive.Front(); e != nil; e = e.Next() {
                events = append(events, e.Value.(Event))
            }
            subscriber := make(chan Event, 10)
            subscribers.PushBack(subscriber)
            ch <- Subscription(events, subscriber)

        case event := <-publish:
            for ch := subscribers.Front(); ch != nil; ch = ch.Next() {
                ch.Value.(chan Event) <- event
            }
            if archive.Len() >= archiveSize {
                archive.Remove(archive.Front())
            }
            archive.PushBack(event)

        case unsub := <-unsubscribe:
            for ch := subscribers.Front(); ch != nil; ch = ch.Next() {
                if ch.Value.(chan Event) == unsub {
                    subscribers.Remove(ch)
                    break
                }
            }
        }
    }
}

func init() {
    go chatroom()
}

//empties out channel
func drain(ch <-chan Event) {
    for {
        select {
        case _, ok := <-ch:
            if !ok {
                return
            }
        default:
            return
        }
    }
}
<commit_msg>format chatroom.go<commit_after>package chatroom

import (
	"container/list"
	"time"
)

type Event struct {
	Type      string
	User      string
	Timestamp int
	Text      string
}

type Subscription struct {
	Archive []Event
	New     <-chan Event
}

func (s Subscription) Cancel() {
	unsubscribe <- s.New
	drain(s.New)
}

func newEvent(typ, usr, msg String) Event {
	return Event{typ, usr, int(time.Now().Unix()), msg}
}

func Subscribe(room ChatRoom) Subscription {
	resp := make(chan Subscription)
	room.subscribe <- resp //this means push response onto subscribe
	return <-resp     //this means wait on response, then return whatever value comes out of resp
}

func JoinRoom(room ChatRoom, user string) {
	room.publish <- newEvent("join", user, "")
}

func SayRoom(room ChatRoom, user, message string) {
	room.publish <- newEvent("message", user, message)
}

func LeaveRoom(room ChatRoom, user string) {
	room.publish <- newEvent("leave", user, "")
}

func CommandRoom(room ChatRoom, user, command string) {
    room.publish <- newEvent("command", user, command)
}

type ChatRoom struct {
	subscribe   chan (chan<- Subscription)
	unsubscribe chan (<-chan Event)
	publish     chan Event
}

func NewChatRoom() ChatRoom {
	return ChatRoom{
		make(chan (chan<- Subscription), 10),
		make(chan (<-chan Event), 10),
		make(chan Event, 10),
	}
}

var Rooms map[string]ChatRoom

func GetOrCreateRoom(key string) ChatRoom {
    r = Rooms[key]
    if r.subscribe == nil && r.unsubscribe == nil && r.publish == nil {
        //Room does not exist and we need to initialize it
        Rooms[key] = NewChatRoom()
        go runChatRoom(Rooms[key])
    }
    return r[key]
}

const archiveSize = 10

func runChatRoom(r ChatRoom) {
    //Runs a chat room.
	var r = GetOrCreateRoom(key)
	subscribe, publish, unsubscribe := r
	archive := list.New()
	subscribers := list.New()
	for {
		select {
		case ch := <-subscribe:
			var events []Event
			for e := archive.Front(); e != nil; e = e.Next() {
				events = append(events, e.Value.(Event))
			}
			subscriber := make(chan Event, 10)
			subscribers.PushBack(subscriber)
			ch <- Subscription(events, subscriber)

		case event := <-publish:
			for ch := subscribers.Front(); ch != nil; ch = ch.Next() {
				ch.Value.(chan Event) <- event
			}
			if archive.Len() >= archiveSize {
				archive.Remove(archive.Front())
			}
			archive.PushBack(event)

		case unsub := <-unsubscribe:
			for ch := subscribers.Front(); ch != nil; ch = ch.Next() {
				if ch.Value.(chan Event) == unsub {
					subscribers.Remove(ch)
					break
				}
			}
		}
	}
}

//empties out event channel
func drain(ch <-chan Event) {
	for {
		select {
		case _, ok := <-ch:
			if !ok {
				return
			}
		default:
			return
		}
	}
}
<|endoftext|>


<commit_before>package corbel

import (
	"encoding/json"
	"fmt"
	"net/http"
)

// Search is the struct used to query every searcheable api in the platform
type Search struct {
	client   *Client
	Query    *apiquery
	Sort     *sort
	PageSize int
	endpoint string
	baseURL  string
}

// aggregationCount is the json representation of Count responses
type aggregationCount struct {
	Count int `json:"count"`
}

// aggregationAvg is the json representation of Average responses
type aggregationAvg struct {
	Average float64 `json:"average"`
}

// aggregationSum is the json representation of Sum responses
type aggregationSum struct {
	Sum float64 `json:"sum"`
}

// Page fills the struct array passed as parameter as paged search by pageNumber
func (s *Search) Page(pageNumber int, result interface{}) error {
	var (
		req *http.Request
		err error
	)
	opts := &SearchListOptions{
		APIQuery:    s.Query.string(),
		APISort:     s.Sort.string(),
		APIPage:     pageNumber,
		APIPageSize: s.PageSize,
	}
	req, err = s.client.NewRequest("GET", s.endpoint, s.queryString(opts), nil)
	_, err = returnErrorHTTPInterface(s.client, req, err, result, 200)
	return err
}

// Count returns the aggregated count of an especific field in the search
func (s *Search) Count(field string) (int, error) {
	var (
		req       *http.Request
		err       error
		aggrCount aggregationCount
	)
	opts := &SearchListOptions{
		APIQuery:       s.Query.string(),
		APISort:        s.Sort.string(),
		APIAggregation: fmt.Sprintf("{\"$count\":\"%s\"}", field),
	}

	req, err = s.client.NewRequest("GET", s.endpoint, s.queryString(opts), nil)
	_, err = returnErrorHTTPInterface(s.client, req, err, &aggrCount, 200)
	if err != nil {
		return 0, err
	}

	return aggrCount.Count, nil
}

// CountAll returns the aggregated count of all items in the search.
// It's an alias of Count("*")
func (s *Search) CountAll() (int, error) {
	return s.Count("*")
}

// Average returns the average of an especific field in the search
func (s *Search) Average(field string) (float64, error) {
	var (
		req     *http.Request
		err     error
		aggrAvg aggregationAvg
	)

	opts := &SearchListOptions{
		APIQuery:       s.Query.string(),
		APISort:        s.Sort.string(),
		APIAggregation: fmt.Sprintf("{\"$avg\":\"%s\"}", field),
	}
	req, err = s.client.NewRequest("GET", s.endpoint, s.queryString(opts), nil)
	_, err = returnErrorHTTPInterface(s.client, req, err, &aggrAvg, 200)
	if err != nil {
		return 0, err
	}

	return aggrAvg.Average, nil
}

// // Sum returns the average of an especific field in the search as integer
// func (s *Search) Sum(field string) (int, error) {
// 	sum, err := s.SumFloat(field)
// 	return int(sum), err
// }

// Sum returns the average of an especific field in the search as float
func (s *Search) Sum(field string) (float64, error) {
	var (
		req     *http.Request
		err     error
		aggrSum aggregationSum
	)

	opts := &SearchListOptions{
		APIQuery:       s.Query.string(),
		APISort:        s.Sort.string(),
		APIAggregation: fmt.Sprintf("{\"$sum\":\"%s\"}", field),
	}

	req, err = s.client.NewRequest("GET", s.endpoint, s.queryString(opts), nil)
	_, err = returnErrorHTTPInterface(s.client, req, err, &aggrSum, 200)
	if err != nil {
		return 0, err
	}

	return aggrSum.Sum, nil
}

// SearchListOptions specifies the optional parameters for searches supporting
// paging and aggregation
type SearchListOptions struct {
	APIQuery       string `url:"api:query,omitempty"`
	APISort        string `url:"api:sort,omitempty"`
	APIPageSize    int    `url:"api:pageSize,omitempty"`
	APIPage        int    `url:"api:page,omitempty"`
	APIAggregation string `url:"api:aggregation,omitempty"`
}

func (s *Search) queryString(options *SearchListOptions) string {
	path, _ := addOptions(s.baseURL, options)
	return path
}

// NewSearch returns a Search struct that allows to select especific search requirements
func NewSearch(client *Client, endpoint, baseURL string) *Search {
	return &Search{
		client:   client,
		Query:    newQuery(),
		Sort:     newSort(),
		PageSize: 10,
		endpoint: endpoint,
		baseURL:  baseURL,
	}
}

// Query is the struct that contains the especification of a search
type apiquery struct {
	Eq   map[string]string   `json:"$eq,omitempty"`
	Gt   map[string]int      `json:"$gt,omitempty"`
	Gte  map[string]int      `json:"$gte,omitempty"`
	Lt   map[string]int      `json:"$lt,omitempty"`
	Lte  map[string]int      `json:"$lte,omitempty"`
	Ne   map[string]string   `json:"$ne,omitempty"`
	In   map[string][]string `json:"$in,omitempty"`
	All  map[string][]string `json:"$all,omitempty"`
	Like map[string]string   `json:"$like,omitempty"`
}

// QueryString returns the query string to append to the url we are searching for.
// api:query must be enclosed by []
func (q *apiquery) string() string {
	apiQueryString, _ := json.Marshal(q)
	if string(apiQueryString) == "{}" {
		return ""
	}
	return fmt.Sprintf("[%s]", string(apiQueryString))
}

// NewQuery returns a New search struct
func newQuery() *apiquery {
	return &apiquery{
		Eq:   make(map[string]string),
		Gt:   make(map[string]int),
		Gte:  make(map[string]int),
		Lt:   make(map[string]int),
		Lte:  make(map[string]int),
		Ne:   make(map[string]string),
		In:   make(map[string][]string),
		All:  make(map[string][]string),
		Like: make(map[string]string),
	}
}

// Sort is the struct that contains the sort to apply to a search
type sort struct {
	Asc  []string
	Desc []string
}

// NewSort returns a Sort struct
func newSort() *sort {
	return &sort{}
}

// QueryString returns the query string to append to the url we are sorting
func (s *sort) string() string {
	var (
		apiSortString []byte
		apiSortMap    = make(map[string]string)
		field         string
	)

	for _, field = range s.Asc {
		apiSortMap[field] = "asc"
	}

	for _, field = range s.Desc {
		apiSortMap[field] = "desc"
	}

	apiSortString, _ = json.Marshal(apiSortMap)
	if string(apiSortString) == "{}" {
		return ""
	}
	return string(apiSortString)
}
<commit_msg>Added exits in search<commit_after>package corbel

import (
	"encoding/json"
	"fmt"
	"net/http"
)

// Search is the struct used to query every searcheable api in the platform
type Search struct {
	client   *Client
	Query    *apiquery
	Sort     *sort
	PageSize int
	endpoint string
	baseURL  string
}

// aggregationCount is the json representation of Count responses
type aggregationCount struct {
	Count int `json:"count"`
}

// aggregationAvg is the json representation of Average responses
type aggregationAvg struct {
	Average float64 `json:"average"`
}

// aggregationSum is the json representation of Sum responses
type aggregationSum struct {
	Sum float64 `json:"sum"`
}

// Page fills the struct array passed as parameter as paged search by pageNumber
func (s *Search) Page(pageNumber int, result interface{}) error {
	var (
		req *http.Request
		err error
	)
	opts := &SearchListOptions{
		APIQuery:    s.Query.string(),
		APISort:     s.Sort.string(),
		APIPage:     pageNumber,
		APIPageSize: s.PageSize,
	}
	req, err = s.client.NewRequest("GET", s.endpoint, s.queryString(opts), nil)
	_, err = returnErrorHTTPInterface(s.client, req, err, result, 200)
	return err
}

// Count returns the aggregated count of an especific field in the search
func (s *Search) Count(field string) (int, error) {
	var (
		req       *http.Request
		err       error
		aggrCount aggregationCount
	)
	opts := &SearchListOptions{
		APIQuery:       s.Query.string(),
		APISort:        s.Sort.string(),
		APIAggregation: fmt.Sprintf("{\"$count\":\"%s\"}", field),
	}

	req, err = s.client.NewRequest("GET", s.endpoint, s.queryString(opts), nil)
	_, err = returnErrorHTTPInterface(s.client, req, err, &aggrCount, 200)
	if err != nil {
		return 0, err
	}

	return aggrCount.Count, nil
}

// CountAll returns the aggregated count of all items in the search.
// It's an alias of Count("*")
func (s *Search) CountAll() (int, error) {
	return s.Count("*")
}

// Average returns the average of an especific field in the search
func (s *Search) Average(field string) (float64, error) {
	var (
		req     *http.Request
		err     error
		aggrAvg aggregationAvg
	)

	opts := &SearchListOptions{
		APIQuery:       s.Query.string(),
		APISort:        s.Sort.string(),
		APIAggregation: fmt.Sprintf("{\"$avg\":\"%s\"}", field),
	}
	req, err = s.client.NewRequest("GET", s.endpoint, s.queryString(opts), nil)
	_, err = returnErrorHTTPInterface(s.client, req, err, &aggrAvg, 200)
	if err != nil {
		return 0, err
	}

	return aggrAvg.Average, nil
}

// // Sum returns the average of an especific field in the search as integer
// func (s *Search) Sum(field string) (int, error) {
// 	sum, err := s.SumFloat(field)
// 	return int(sum), err
// }

// Sum returns the average of an especific field in the search as float
func (s *Search) Sum(field string) (float64, error) {
	var (
		req     *http.Request
		err     error
		aggrSum aggregationSum
	)

	opts := &SearchListOptions{
		APIQuery:       s.Query.string(),
		APISort:        s.Sort.string(),
		APIAggregation: fmt.Sprintf("{\"$sum\":\"%s\"}", field),
	}

	req, err = s.client.NewRequest("GET", s.endpoint, s.queryString(opts), nil)
	_, err = returnErrorHTTPInterface(s.client, req, err, &aggrSum, 200)
	if err != nil {
		return 0, err
	}

	return aggrSum.Sum, nil
}

// SearchListOptions specifies the optional parameters for searches supporting
// paging and aggregation
type SearchListOptions struct {
	APIQuery       string `url:"api:query,omitempty"`
	APISort        string `url:"api:sort,omitempty"`
	APIPageSize    int    `url:"api:pageSize,omitempty"`
	APIPage        int    `url:"api:page,omitempty"`
	APIAggregation string `url:"api:aggregation,omitempty"`
}

func (s *Search) queryString(options *SearchListOptions) string {
	path, _ := addOptions(s.baseURL, options)
	return path
}

// NewSearch returns a Search struct that allows to select especific search requirements
func NewSearch(client *Client, endpoint, baseURL string) *Search {
	return &Search{
		client:   client,
		Query:    newQuery(),
		Sort:     newSort(),
		PageSize: 10,
		endpoint: endpoint,
		baseURL:  baseURL,
	}
}

// Query is the struct that contains the especification of a search
type apiquery struct {
	Eq     map[string]string   `json:"$eq,omitempty"`
	Gt     map[string]int      `json:"$gt,omitempty"`
	Gte    map[string]int      `json:"$gte,omitempty"`
	Lt     map[string]int      `json:"$lt,omitempty"`
	Lte    map[string]int      `json:"$lte,omitempty"`
	Ne     map[string]string   `json:"$ne,omitempty"`
	In     map[string][]string `json:"$in,omitempty"`
	All    map[string][]string `json:"$all,omitempty"`
	Like   map[string]string   `json:"$like,omitempty"`
	Exists map[string]bool     `json:"$exists,omitempty"`
}

// QueryString returns the query string to append to the url we are searching for.
// api:query must be enclosed by []
func (q *apiquery) string() string {
	apiQueryString, _ := json.Marshal(q)
	if string(apiQueryString) == "{}" {
		return ""
	}
	return fmt.Sprintf("[%s]", string(apiQueryString))
}

// NewQuery returns a New search struct
func newQuery() *apiquery {
	return &apiquery{
		Eq:     make(map[string]string),
		Gt:     make(map[string]int),
		Gte:    make(map[string]int),
		Lt:     make(map[string]int),
		Lte:    make(map[string]int),
		Ne:     make(map[string]string),
		In:     make(map[string][]string),
		All:    make(map[string][]string),
		Like:   make(map[string]string),
		Exists: make(map[string]bool),
	}
}

// Sort is the struct that contains the sort to apply to a search
type sort struct {
	Asc  []string
	Desc []string
}

// NewSort returns a Sort struct
func newSort() *sort {
	return &sort{}
}

// QueryString returns the query string to append to the url we are sorting
func (s *sort) string() string {
	var (
		apiSortString []byte
		apiSortMap    = make(map[string]string)
		field         string
	)

	for _, field = range s.Asc {
		apiSortMap[field] = "asc"
	}

	for _, field = range s.Desc {
		apiSortMap[field] = "desc"
	}

	apiSortString, _ = json.Marshal(apiSortMap)
	if string(apiSortString) == "{}" {
		return ""
	}
	return string(apiSortString)
}
<|endoftext|>


<commit_before>package zfs

// #include <stdlib.h>
// #include <libzfs.h>
// #include "common.h"
// #include "zpool.h"
// #include "zfs.h"
// #include <memory.h>
// #include <string.h>
import "C"
import (
	"fmt"
	"io/ioutil"
	"os"
	"path"
	"regexp"
	"strconv"
	"strings"
	"time"
	"unsafe"
)

type SendFlags struct {
	Verbose    bool // -v
	Replicate  bool // -R
	DoAll      bool // -I
	FromOrigin bool // -o
	Dedup      bool // -D
	Props      bool // -p
	DryRun     bool // -n
	Parsable   bool // -P
	LargeBlock bool // -L
	EmbedData  bool // -e
	Compress   bool // -c
	Progress   bool
}

type RecvFlags struct {
	Verbose     bool // -v
	IsPrefix    bool // -d
	IsTail      bool // -e
	DryRun      bool // -n
	Force       bool // -r
	Resumable   bool // -s
	NoMount     bool // -u
	CanmountOff bool
	ByteSwap    bool
}

func to_boolean_t(a bool) C.boolean_t {
	if a {
		return 1
	}
	return 0
}

func to_sendflags_t(flags *SendFlags) (cflags *C.sendflags_t) {
	cflags = C.alloc_sendflags()
	// cflags.verbose = to_boolean_t(flags.Verbose)
	cflags.replicate = to_boolean_t(flags.Replicate)
	cflags.doall = to_boolean_t(flags.DoAll)
	cflags.fromorigin = to_boolean_t(flags.FromOrigin)
	cflags.dedup = to_boolean_t(flags.Dedup)
	cflags.props = to_boolean_t(flags.Props)
	cflags.dryrun = to_boolean_t(flags.DryRun)
	cflags.parsable = to_boolean_t(flags.Parsable)
	cflags.progress = to_boolean_t(flags.Progress)
	cflags.largeblock = to_boolean_t(flags.LargeBlock)
	cflags.embed_data = to_boolean_t(flags.EmbedData)
	cflags.compress = to_boolean_t(flags.Compress)
	return
}

func to_recvflags_t(flags *RecvFlags) (cflags *C.recvflags_t) {
	cflags = C.alloc_recvflags()
	cflags.verbose = to_boolean_t(flags.Verbose)
	cflags.isprefix = to_boolean_t(flags.IsPrefix)
	cflags.istail = to_boolean_t(flags.IsTail)
	cflags.dryrun = to_boolean_t(flags.DryRun)
	cflags.force = to_boolean_t(flags.Force)
	cflags.canmountoff = to_boolean_t(flags.CanmountOff)
	// cflags.resumable = to_boolean_t(flags.Resumable)
	cflags.byteswap = to_boolean_t(flags.ByteSwap)
	cflags.nomount = to_boolean_t(flags.NoMount)
	return
}

func (d *Dataset) send(FromName string, outf *os.File, flags *SendFlags) (err error) {
	var cfromname, ctoname *C.char
	var dpath string
	var pd Dataset

	if d.Type != DatasetTypeSnapshot || (len(FromName) > 0 && strings.Contains(FromName, "#")) {
		err = fmt.Errorf(
			"Unsupported method on filesystem or bookmark. Use func SendOne() for that purpose.")
		return
	}

	cflags := to_sendflags_t(flags)
	defer C.free(unsafe.Pointer(cflags))
	if dpath, err = d.Path(); err != nil {
		return
	}
	sendparams := strings.Split(dpath, "@")
	parent := sendparams[0]
	if len(FromName) > 0 {
		if FromName[0] == '@' {
			FromName = FromName[1:]
		} else if strings.Contains(FromName, "/") {
			from := strings.Split(FromName, "@")
			if len(from) > 0 {
				FromName = from[1]
			}
		}
		cfromname = C.CString(FromName)
		defer C.free(unsafe.Pointer(cfromname))
	}
	ctoname = C.CString(sendparams[1])
	defer C.free(unsafe.Pointer(ctoname))
	if pd, err = DatasetOpen(parent); err != nil {
		return
	}
	defer pd.Close()
	cerr := C.zfs_send(pd.list.zh, cfromname, ctoname, cflags, C.int(outf.Fd()), nil, nil, nil)
	if cerr != 0 {
		err = LastError()
	}
	return
}

func (d *Dataset) SendOne(FromName string, outf *os.File, flags *SendFlags) (err error) {
	var cfromname, ctoname *C.char
	var dpath string
	var lzc_send_flags C.struct_sendflags

	if d.Type == DatasetTypeSnapshot || (len(FromName) > 0 && !strings.Contains(FromName, "#")) {
		err = fmt.Errorf(
			"Unsupported with snapshot. Use func Send() for that purpose.")
		return
	}
	if flags.Replicate || flags.DoAll || flags.Props || flags.Dedup || flags.DryRun {
		err = fmt.Errorf("Unsupported flag with filesystem or bookmark.")
		return
	}

	if flags.LargeBlock {
		lzc_send_flags.largeblock = booleanT(true)
	}
	if flags.EmbedData {
		lzc_send_flags.embed_data = booleanT(true)
	}
	// if (flags.Compress)
	// 	lzc_send_flags |= LZC_SEND_FLAG_COMPRESS;
	if dpath, err = d.Path(); err != nil {
		return
	}
	if len(FromName) > 0 {
		if FromName[0] == '#' || FromName[0] == '@' {
			FromName = dpath + FromName
		}
		cfromname = C.CString(FromName)
		defer C.free(unsafe.Pointer(cfromname))
	}
	ctoname = C.CString(path.Base(dpath))
	defer C.free(unsafe.Pointer(ctoname))
	cerr := C.zfs_send_one(d.list.zh, cfromname, C.int(outf.Fd()), &lzc_send_flags, nil)
	if cerr != 0 {
		err = LastError()
	}
	return
}

func (d *Dataset) SendResume(outf *os.File, flags *SendFlags, receiveResumeToken string) (err error) {
	if d.Type != DatasetTypeSnapshot {
		err = fmt.Errorf("Unsupported method on filesystem or bookmark. Use func SendOne() for that purpose.")
		return
	}

	var dpath string
	var pd Dataset

	cflags := to_sendflags_t(flags)
	defer C.free(unsafe.Pointer(cflags))
	if dpath, err = d.Path(); err != nil {
		return
	}
	sendparams := strings.Split(dpath, "@")
	parent := sendparams[0]

	if pd, err = DatasetOpen(parent); err != nil {
		return
	}
	defer pd.Close()

	cReceiveResumeToken := C.CString(receiveResumeToken)
	defer C.free(unsafe.Pointer(cReceiveResumeToken))

	clerr := C.zfs_send_resume(C.libzfsHandle, cflags, C.int(outf.Fd()), cReceiveResumeToken)
	if clerr != 0 {
		err = LastError()
	}

	return
}

func (d *Dataset) Send(outf *os.File, flags SendFlags) (err error) {
	if flags.Replicate {
		flags.DoAll = true
	}
	err = d.send("", outf, &flags)
	return
}

func (d *Dataset) SendFrom(FromName string, outf *os.File, flags SendFlags) (err error) {
	var porigin Property
	var from, dest []string
	if err = d.ReloadProperties(); err != nil {
		return
	}
	porigin, _ = d.GetProperty(DatasetPropOrigin)
	if len(porigin.Value) > 0 && porigin.Value == FromName {
		FromName = ""
		flags.FromOrigin = true
	} else {
		var dpath string
		if dpath, err = d.Path(); err != nil {
			return
		}
		dest = strings.Split(dpath, "@")
		from = strings.Split(FromName, "@")

		if len(from[0]) > 0 && from[0] != dest[0] {
			err = fmt.Errorf("Incremental source must be in same filesystem.")
			return
		}
		if len(from) < 2 || strings.Contains(from[1], "@") || strings.Contains(from[1], "/") {
			err = fmt.Errorf("Invalid incremental source.")
			return
		}
	}
	err = d.send("@"+from[1], outf, &flags)
	return
}

// SendSize - estimate snapshot size to transfer
func (d *Dataset) SendSize(FromName string, flags SendFlags) (size int64, err error) {
	var r, w *os.File
	errch := make(chan error)
	defer func() {
		select {
		case <-errch:
		default:
		}
		close(errch)
	}()
	flags.DryRun = true
	flags.Verbose = true
	flags.Progress = true
	flags.Parsable = true
	if r, w, err = os.Pipe(); err != nil {
		return
	}
	defer r.Close()
	go func() {
		var tmpe error
		saveOut := C.redirect_libzfs_stdout(C.int(w.Fd()))
		if saveOut < 0 {
			tmpe = fmt.Errorf("Redirection of zfslib stdout failed %d", saveOut)
		} else {
			tmpe = d.send(FromName, w, &flags)
			C.restore_libzfs_stdout(saveOut)
		}
		w.Close()
		errch <- tmpe
	}()

	r.SetReadDeadline(time.Now().Add(60 * time.Second))
	var data []byte
	if data, err = ioutil.ReadAll(r); err != nil {
		return
	}
	// parse size
	var sizeRe *regexp.Regexp
	if sizeRe, err = regexp.Compile("size[ \t]*([0-9]+)"); err != nil {
		return
	}
	matches := sizeRe.FindAllSubmatch(data, 3)
	if len(matches) > 0 && len(matches[0]) > 1 {
		if size, err = strconv.ParseInt(
			string(matches[0][1]), 10, 64); err != nil {
			return
		}
	}
	err = <-errch
	return
}

// Receive - receive snapshot stream
func (d *Dataset) Receive(inf *os.File, flags RecvFlags) (err error) {
	var dpath string
	if dpath, err = d.Path(); err != nil {
		return
	}
	props := C.new_property_nvlist()
	if props == nil {
		err = fmt.Errorf("Out of memory func (d *Dataset) Recv()")
		return
	}
	defer C.nvlist_free(props)
	cflags := to_recvflags_t(&flags)
	defer C.free(unsafe.Pointer(cflags))
	dest := C.CString(dpath)
	defer C.free(unsafe.Pointer(dest))
	ec := C.zfs_receive(C.libzfsHandle, dest, nil, cflags, C.int(inf.Fd()), nil)
	if ec != 0 {
		err = fmt.Errorf("ZFS receive of %s failed. %s", C.GoString(dest), LastError().Error())
	}
	return
}
<commit_msg>Remove SendOne since it does not make sense in context of this lib<commit_after>package zfs

// #include <stdlib.h>
// #include <libzfs.h>
// #include "common.h"
// #include "zpool.h"
// #include "zfs.h"
// #include <memory.h>
// #include <string.h>
import "C"
import (
	"fmt"
	"io/ioutil"
	"os"
	"regexp"
	"strconv"
	"strings"
	"time"
	"unsafe"
)

type SendFlags struct {
	Verbose    bool // -v
	Replicate  bool // -R
	DoAll      bool // -I
	FromOrigin bool // -o
	Dedup      bool // -D
	Props      bool // -p
	DryRun     bool // -n
	Parsable   bool // -P
	LargeBlock bool // -L
	EmbedData  bool // -e
	Compress   bool // -c
	Progress   bool
}

type RecvFlags struct {
	Verbose     bool // -v
	IsPrefix    bool // -d
	IsTail      bool // -e
	DryRun      bool // -n
	Force       bool // -r
	Resumable   bool // -s
	NoMount     bool // -u
	CanmountOff bool
	ByteSwap    bool
}

func to_boolean_t(a bool) C.boolean_t {
	if a {
		return 1
	}
	return 0
}

func to_sendflags_t(flags *SendFlags) (cflags *C.sendflags_t) {
	cflags = C.alloc_sendflags()
	// cflags.verbose = to_boolean_t(flags.Verbose)
	cflags.replicate = to_boolean_t(flags.Replicate)
	cflags.doall = to_boolean_t(flags.DoAll)
	cflags.fromorigin = to_boolean_t(flags.FromOrigin)
	cflags.dedup = to_boolean_t(flags.Dedup)
	cflags.props = to_boolean_t(flags.Props)
	cflags.dryrun = to_boolean_t(flags.DryRun)
	cflags.parsable = to_boolean_t(flags.Parsable)
	cflags.progress = to_boolean_t(flags.Progress)
	cflags.largeblock = to_boolean_t(flags.LargeBlock)
	cflags.embed_data = to_boolean_t(flags.EmbedData)
	cflags.compress = to_boolean_t(flags.Compress)
	return
}

func to_recvflags_t(flags *RecvFlags) (cflags *C.recvflags_t) {
	cflags = C.alloc_recvflags()
	cflags.verbose = to_boolean_t(flags.Verbose)
	cflags.isprefix = to_boolean_t(flags.IsPrefix)
	cflags.istail = to_boolean_t(flags.IsTail)
	cflags.dryrun = to_boolean_t(flags.DryRun)
	cflags.force = to_boolean_t(flags.Force)
	cflags.canmountoff = to_boolean_t(flags.CanmountOff)
	// cflags.resumable = to_boolean_t(flags.Resumable)
	cflags.byteswap = to_boolean_t(flags.ByteSwap)
	cflags.nomount = to_boolean_t(flags.NoMount)
	return
}

func (d *Dataset) send(FromName string, outf *os.File, flags *SendFlags) (err error) {
	var cfromname, ctoname *C.char
	var dpath string
	var pd Dataset

	if d.Type != DatasetTypeSnapshot || (len(FromName) > 0 && strings.Contains(FromName, "#")) {
		err = fmt.Errorf(
			"Unsupported method on filesystem or bookmark. Use func SendOne() for that purpose.")
		return
	}

	cflags := to_sendflags_t(flags)
	defer C.free(unsafe.Pointer(cflags))
	if dpath, err = d.Path(); err != nil {
		return
	}
	sendparams := strings.Split(dpath, "@")
	parent := sendparams[0]
	if len(FromName) > 0 {
		if FromName[0] == '@' {
			FromName = FromName[1:]
		} else if strings.Contains(FromName, "/") {
			from := strings.Split(FromName, "@")
			if len(from) > 0 {
				FromName = from[1]
			}
		}
		cfromname = C.CString(FromName)
		defer C.free(unsafe.Pointer(cfromname))
	}
	ctoname = C.CString(sendparams[1])
	defer C.free(unsafe.Pointer(ctoname))
	if pd, err = DatasetOpen(parent); err != nil {
		return
	}
	defer pd.Close()
	cerr := C.zfs_send(pd.list.zh, cfromname, ctoname, cflags, C.int(outf.Fd()), nil, nil, nil)
	if cerr != 0 {
		err = LastError()
	}
	return
}

func (d *Dataset) SendResume(outf *os.File, flags *SendFlags, receiveResumeToken string) (err error) {
	if d.Type != DatasetTypeSnapshot {
		err = fmt.Errorf("Unsupported method on filesystem or bookmark. Use func SendOne() for that purpose.")
		return
	}

	var dpath string
	var pd Dataset

	cflags := to_sendflags_t(flags)
	defer C.free(unsafe.Pointer(cflags))
	if dpath, err = d.Path(); err != nil {
		return
	}
	sendparams := strings.Split(dpath, "@")
	parent := sendparams[0]

	if pd, err = DatasetOpen(parent); err != nil {
		return
	}
	defer pd.Close()

	cReceiveResumeToken := C.CString(receiveResumeToken)
	defer C.free(unsafe.Pointer(cReceiveResumeToken))

	clerr := C.zfs_send_resume(C.libzfsHandle, cflags, C.int(outf.Fd()), cReceiveResumeToken)
	if clerr != 0 {
		err = LastError()
	}

	return
}

func (d *Dataset) Send(outf *os.File, flags SendFlags) (err error) {
	if flags.Replicate {
		flags.DoAll = true
	}
	err = d.send("", outf, &flags)
	return
}

func (d *Dataset) SendFrom(FromName string, outf *os.File, flags SendFlags) (err error) {
	var porigin Property
	var from, dest []string
	if err = d.ReloadProperties(); err != nil {
		return
	}
	porigin, _ = d.GetProperty(DatasetPropOrigin)
	if len(porigin.Value) > 0 && porigin.Value == FromName {
		FromName = ""
		flags.FromOrigin = true
	} else {
		var dpath string
		if dpath, err = d.Path(); err != nil {
			return
		}
		dest = strings.Split(dpath, "@")
		from = strings.Split(FromName, "@")

		if len(from[0]) > 0 && from[0] != dest[0] {
			err = fmt.Errorf("Incremental source must be in same filesystem.")
			return
		}
		if len(from) < 2 || strings.Contains(from[1], "@") || strings.Contains(from[1], "/") {
			err = fmt.Errorf("Invalid incremental source.")
			return
		}
	}
	err = d.send("@"+from[1], outf, &flags)
	return
}

// SendSize - estimate snapshot size to transfer
func (d *Dataset) SendSize(FromName string, flags SendFlags) (size int64, err error) {
	var r, w *os.File
	errch := make(chan error)
	defer func() {
		select {
		case <-errch:
		default:
		}
		close(errch)
	}()
	flags.DryRun = true
	flags.Verbose = true
	flags.Progress = true
	flags.Parsable = true
	if r, w, err = os.Pipe(); err != nil {
		return
	}
	defer r.Close()
	go func() {
		var tmpe error
		saveOut := C.redirect_libzfs_stdout(C.int(w.Fd()))
		if saveOut < 0 {
			tmpe = fmt.Errorf("Redirection of zfslib stdout failed %d", saveOut)
		} else {
			tmpe = d.send(FromName, w, &flags)
			C.restore_libzfs_stdout(saveOut)
		}
		w.Close()
		errch <- tmpe
	}()

	r.SetReadDeadline(time.Now().Add(60 * time.Second))
	var data []byte
	if data, err = ioutil.ReadAll(r); err != nil {
		return
	}
	// parse size
	var sizeRe *regexp.Regexp
	if sizeRe, err = regexp.Compile("size[ \t]*([0-9]+)"); err != nil {
		return
	}
	matches := sizeRe.FindAllSubmatch(data, 3)
	if len(matches) > 0 && len(matches[0]) > 1 {
		if size, err = strconv.ParseInt(
			string(matches[0][1]), 10, 64); err != nil {
			return
		}
	}
	err = <-errch
	return
}

// Receive - receive snapshot stream
func (d *Dataset) Receive(inf *os.File, flags RecvFlags) (err error) {
	var dpath string
	if dpath, err = d.Path(); err != nil {
		return
	}
	props := C.new_property_nvlist()
	if props == nil {
		err = fmt.Errorf("Out of memory func (d *Dataset) Recv()")
		return
	}
	defer C.nvlist_free(props)
	cflags := to_recvflags_t(&flags)
	defer C.free(unsafe.Pointer(cflags))
	dest := C.CString(dpath)
	defer C.free(unsafe.Pointer(dest))
	ec := C.zfs_receive(C.libzfsHandle, dest, nil, cflags, C.int(inf.Fd()), nil)
	if ec != 0 {
		err = fmt.Errorf("ZFS receive of %s failed. %s", C.GoString(dest), LastError().Error())
	}
	return
}
<|endoftext|>


<commit_before>package main

import (
	"flag"
	"fmt"
	"net"
	"os"
	"os/signal"
	"syscall"

	"github.com/weaveworks/docker-plugin/plugin/driver"
	"github.com/weaveworks/docker-plugin/plugin/skel"
	. "github.com/weaveworks/weave/common"
)

var version = "(unreleased version)"

func main() {
	var (
		justVersion bool
		address     string
		nameserver  string
		debug       bool
	)

	flag.BoolVar(&justVersion, "version", false, "print version and exit")
	flag.BoolVar(&debug, "debug", false, "output debugging info to stderr")
	flag.StringVar(&address, "socket", "/run/docker/plugins/weave.sock", "socket on which to listen")
	flag.StringVar(&nameserver, "nameserver", "", "nameserver to provide to containers")

	flag.Parse()

	if justVersion {
		fmt.Printf("weave plugin %s\n", version)
		os.Exit(0)
	}

	if debug {
		SetLogLevel("debug")
	}

	var d skel.Driver
	d, err := driver.New(version, nameserver)
	if err != nil {
		Log.Fatalf("unable to create driver: %s", err)
	}

	var listener net.Listener

	listener, err = net.Listen("unix", address)
	if err != nil {
		Log.Fatal(err)
	}
	defer listener.Close()

	sigChan := make(chan os.Signal, 1)
	signal.Notify(sigChan, os.Interrupt, os.Kill, syscall.SIGTERM)

	endChan := make(chan error, 1)
	go func() {
		endChan <- skel.Listen(listener, d)
	}()

	select {
	case sig := <-sigChan:
		Log.Debugf("Caught signal %s; shutting down", sig)
	case err := <-endChan:
		if err != nil {
			Log.Errorf("Error from listener: ", err)
			listener.Close()
			os.Exit(1)
		}
	}
}
<commit_msg>Remove unix domain socket before listening on it<commit_after>package main

import (
	"flag"
	"fmt"
	"net"
	"os"
	"os/signal"
	"syscall"

	"github.com/weaveworks/docker-plugin/plugin/driver"
	"github.com/weaveworks/docker-plugin/plugin/skel"
	. "github.com/weaveworks/weave/common"
)

var version = "(unreleased version)"

func main() {
	var (
		justVersion bool
		address     string
		nameserver  string
		debug       bool
	)

	flag.BoolVar(&justVersion, "version", false, "print version and exit")
	flag.BoolVar(&debug, "debug", false, "output debugging info to stderr")
	flag.StringVar(&address, "socket", "/run/docker/plugins/weave.sock", "socket on which to listen")
	flag.StringVar(&nameserver, "nameserver", "", "nameserver to provide to containers")

	flag.Parse()

	if justVersion {
		fmt.Printf("weave plugin %s\n", version)
		os.Exit(0)
	}

	if debug {
		SetLogLevel("debug")
	}

	var d skel.Driver
	d, err := driver.New(version, nameserver)
	if err != nil {
		Log.Fatalf("unable to create driver: %s", err)
	}

	var listener net.Listener

	// remove socket from last invocation
	if err := os.Remove(address); err != nil && !os.IsNotExist(err) {
		Log.Fatal(err)
	}
	listener, err = net.Listen("unix", address)
	if err != nil {
		Log.Fatal(err)
	}
	defer listener.Close()

	sigChan := make(chan os.Signal, 1)
	signal.Notify(sigChan, os.Interrupt, os.Kill, syscall.SIGTERM)

	endChan := make(chan error, 1)
	go func() {
		endChan <- skel.Listen(listener, d)
	}()

	select {
	case sig := <-sigChan:
		Log.Debugf("Caught signal %s; shutting down", sig)
	case err := <-endChan:
		if err != nil {
			Log.Errorf("Error from listener: ", err)
			listener.Close()
			os.Exit(1)
		}
	}
}
<|endoftext|>


<commit_before>package pluggo

import "testing"

func TestRegister1(t *testing.T) {
	err := Register("ep1", func() interface{} {
		return "1"
	})
	if err != nil {
		t.Fatal(err)
	}
}

func TestRegister2(t *testing.T) {
	err := Register("ep2", func() interface{} {
		return "2"
	})
	if err != nil {
		t.Fatal(err)
	}
}

func TestRegisterDuplicated(t *testing.T) {
	err := Register("ep1", func() interface{} {
		return "3"
	})
	if err == nil {
		t.Fatal("expected to fail duplicated registration")
	}
}

func TestGet(t *testing.T) {
	ep1 := Get("ep1").(string)
	if ep1 != "1" {
		t.Fatal("plugin returned unexpected instance")
	}
}
<commit_msg>Add test case for unknown extension point<commit_after>package pluggo

import "testing"

func TestRegister1(t *testing.T) {
	err := Register("ep1", func() interface{} {
		return "1"
	})
	if err != nil {
		t.Fatal(err)
	}
}

func TestRegister2(t *testing.T) {
	err := Register("ep2", func() interface{} {
		return "2"
	})
	if err != nil {
		t.Fatal(err)
	}
}

func TestRegisterDuplicated(t *testing.T) {
	err := Register("ep1", func() interface{} {
		return "3"
	})
	if err == nil {
		t.Fatal("expected to fail duplicated registration")
	}
}

func TestGet(t *testing.T) {
	ep1 := Get("ep1").(string)
	if ep1 != "1" {
		t.Fatal("plugin returned unexpected instance")
	}
}

func TestGetUnknown(t *testing.T) {
	ep := Get("ep-unknown")
	if ep != nil {
		t.Fatal("unexpected plugin instance returned for unknown extension point")
	}
}
<|endoftext|>


<commit_before>package lib

import (
	"bufio"
	"bytes"
	"os"
)

var (
	maxDataSize = 4096
)

// Reads a whole fortune from the fortune base file
func ReadData(inputFile *os.File, pos int64) (string, error) {
	buffer := make([]byte, maxDataSize)
	_, err := inputFile.ReadAt(buffer, pos)
	if err != nil {
		return "", err
	}

	scanner := bufio.NewScanner(bytes.NewReader(buffer))
	scanner.Split(advanceAwareSplitter)
	scanner.Scan()

	return string(RemoveCRLF(scanner.Bytes())), nil
}

// advanceAwareSplitter splits byte array in lines returning also how many bytes were read.
// It differs from the default line splitter by the ability to return how many bytes were read.
func advanceAwareSplitter(data []byte, atEOF bool) (advance int, token []byte, err error) {
	if atEOF && len(data) == 0 {
		return 0, nil, nil
	}
	if i := bytes.Index(data, []byte("\n%\n")); i >= 0 {
		// We have a full newline-terminated line.
		return i + 1, data[0 : i+1], nil
	}
	// If we're at EOF, we have a final, non-terminated line. Return it.
	if atEOF {
		return len(data), data, nil
	}
	// Request more data.
	return 0, nil, nil
}
<commit_msg>Fixes #8 : empty cookies shown when cookie close to the end of the file<commit_after>package lib

import (
	"bufio"
	"bytes"
	"os"
	"io"
)

var (
	maxDataSize = 4096
)

// Reads a whole fortune from the fortune base file
func ReadData(inputFile *os.File, pos int64) (string, error) {
	buffer := make([]byte, maxDataSize)
	_, err := inputFile.ReadAt(buffer, pos)
	if err != nil && err != io.EOF {
		return "", err
	}

	scanner := bufio.NewScanner(bytes.NewReader(buffer))
	scanner.Split(advanceAwareSplitter)
	scanner.Scan()

	return string(RemoveCRLF(scanner.Bytes())), nil
}

// advanceAwareSplitter splits byte array in lines returning also how many bytes were read.
// It differs from the default line splitter by the ability to return how many bytes were read.
func advanceAwareSplitter(data []byte, atEOF bool) (advance int, token []byte, err error) {
	if atEOF && len(data) == 0 {
		return 0, nil, nil
	}
	if i := bytes.Index(data, []byte("\n%\n")); i >= 0 {
		// We have a full newline-terminated line.
		return i + 1, data[0 : i+1], nil
	}
	// If we're at EOF, we have a final, non-terminated line. Return it.
	if atEOF {
		return len(data), data, nil
	}
	// Request more data.
	return 0, nil, nil
}
<|endoftext|>