diff --git a/api4/bot.go b/api4/bot.go
index 2ccf17b6ee..9be54a1b4d 100644
--- a/api4/bot.go
+++ b/api4/bot.go
@@ -10,6 +10,7 @@ import (
"net/http"
"strconv"
+ "github.com/mattermost/mattermost-server/v5/audit"
"github.com/mattermost/mattermost-server/v5/model"
)
@@ -39,6 +40,9 @@ func createBot(c *Context, w http.ResponseWriter, r *http.Request) {
}
bot.Patch(botPatch)
+ auditRec := c.MakeAuditRecord("createBot", audit.Fail)
+ defer c.LogAuditRec(auditRec)
+
if !c.App.SessionHasPermissionTo(*c.App.Session(), model.PERMISSION_CREATE_BOT) {
c.SetPermissionError(model.PERMISSION_CREATE_BOT)
return
@@ -62,6 +66,11 @@ func createBot(c *Context, w http.ResponseWriter, r *http.Request) {
return
}
+ auditRec.Success()
+ // Note that the primary key of a bot is the UserId, and matches the primary key of the
+ // corresponding user.
+ auditRec.AddMeta("bot_id", createdBot.UserId)
+
w.WriteHeader(http.StatusCreated)
w.Write(createdBot.ToJson())
}
@@ -79,6 +88,10 @@ func patchBot(c *Context, w http.ResponseWriter, r *http.Request) {
return
}
+ auditRec := c.MakeAuditRecord("patchBot", audit.Fail)
+ defer c.LogAuditRec(auditRec)
+ auditRec.AddMeta("bot_id", botUserId)
+
if err := c.App.SessionHasPermissionToManageBot(*c.App.Session(), botUserId); err != nil {
c.Err = err
return
@@ -90,6 +103,8 @@ func patchBot(c *Context, w http.ResponseWriter, r *http.Request) {
return
}
+ auditRec.Success()
+
w.Write(updatedBot.ToJson())
}
@@ -182,6 +197,11 @@ func updateBotActive(c *Context, w http.ResponseWriter, r *http.Request, active
}
botUserId := c.Params.BotUserId
+ auditRec := c.MakeAuditRecord("updateBotActive", audit.Fail)
+ defer c.LogAuditRec(auditRec)
+ auditRec.AddMeta("bot_id", botUserId)
+ auditRec.AddMeta("enable", active)
+
if err := c.App.SessionHasPermissionToManageBot(*c.App.Session(), botUserId); err != nil {
c.Err = err
return
@@ -193,6 +213,8 @@ func updateBotActive(c *Context, w http.ResponseWriter, r *http.Request, active
return
}
+ auditRec.Success()
+
w.Write(bot.ToJson())
}
@@ -205,6 +227,11 @@ func assignBot(c *Context, w http.ResponseWriter, r *http.Request) {
botUserId := c.Params.BotUserId
userId := c.Params.UserId
+ auditRec := c.MakeAuditRecord("assignBot", audit.Fail)
+ defer c.LogAuditRec(auditRec)
+ auditRec.AddMeta("bot_id", botUserId)
+ auditRec.AddMeta("assign_user_id", userId)
+
if err := c.App.SessionHasPermissionToManageBot(*c.App.Session(), botUserId); err != nil {
c.Err = err
return
@@ -223,6 +250,8 @@ func assignBot(c *Context, w http.ResponseWriter, r *http.Request) {
return
}
+ auditRec.Success()
+
w.Write(bot.ToJson())
}
@@ -276,6 +305,10 @@ func setBotIconImage(c *Context, w http.ResponseWriter, r *http.Request) {
}
botUserId := c.Params.BotUserId
+ auditRec := c.MakeAuditRecord("setBotIconImage", audit.Fail)
+ defer c.LogAuditRec(auditRec)
+ auditRec.AddMeta("bot_id", botUserId)
+
if err := c.App.SessionHasPermissionToManageBot(*c.App.Session(), botUserId); err != nil {
c.Err = err
return
@@ -309,7 +342,9 @@ func setBotIconImage(c *Context, w http.ResponseWriter, r *http.Request) {
return
}
+ auditRec.Success()
c.LogAudit("")
+
ReturnStatusOK(w)
}
@@ -322,6 +357,10 @@ func deleteBotIconImage(c *Context, w http.ResponseWriter, r *http.Request) {
}
botUserId := c.Params.BotUserId
+ auditRec := c.MakeAuditRecord("deleteBotIconImage", audit.Fail)
+ defer c.LogAuditRec(auditRec)
+ auditRec.AddMeta("bot_id", botUserId)
+
if err := c.App.SessionHasPermissionToManageBot(*c.App.Session(), botUserId); err != nil {
c.Err = err
return
@@ -332,6 +371,8 @@ func deleteBotIconImage(c *Context, w http.ResponseWriter, r *http.Request) {
return
}
+ auditRec.Success()
c.LogAudit("")
+
ReturnStatusOK(w)
}
diff --git a/api4/brand.go b/api4/brand.go
index f636d6a273..78b3fae3dd 100644
--- a/api4/brand.go
+++ b/api4/brand.go
@@ -8,6 +8,7 @@ import (
"io/ioutil"
"net/http"
+ "github.com/mattermost/mattermost-server/v5/audit"
"github.com/mattermost/mattermost-server/v5/model"
)
@@ -57,6 +58,9 @@ func uploadBrandImage(c *Context, w http.ResponseWriter, r *http.Request) {
return
}
+ auditRec := c.MakeAuditRecord("uploadBrandImage", audit.Fail)
+ defer c.LogAuditRec(auditRec)
+
if !c.App.SessionHasPermissionTo(*c.App.Session(), model.PERMISSION_MANAGE_SYSTEM) {
c.SetPermissionError(model.PERMISSION_MANAGE_SYSTEM)
return
@@ -67,6 +71,7 @@ func uploadBrandImage(c *Context, w http.ResponseWriter, r *http.Request) {
return
}
+ auditRec.Success()
c.LogAudit("")
w.WriteHeader(http.StatusCreated)
@@ -74,6 +79,9 @@ func uploadBrandImage(c *Context, w http.ResponseWriter, r *http.Request) {
}
func deleteBrandImage(c *Context, w http.ResponseWriter, r *http.Request) {
+ auditRec := c.MakeAuditRecord("deleteBrandImage", audit.Fail)
+ defer c.LogAuditRec(auditRec)
+
if !c.App.SessionHasPermissionTo(*c.App.Session(), model.PERMISSION_MANAGE_SYSTEM) {
c.SetPermissionError(model.PERMISSION_MANAGE_SYSTEM)
return
@@ -84,5 +92,7 @@ func deleteBrandImage(c *Context, w http.ResponseWriter, r *http.Request) {
return
}
+ auditRec.Success()
+
ReturnStatusOK(w)
}
diff --git a/api4/channel.go b/api4/channel.go
index f261b00bfb..e048fbbb30 100644
--- a/api4/channel.go
+++ b/api4/channel.go
@@ -8,6 +8,7 @@ import (
"net/http"
"strings"
+ "github.com/mattermost/mattermost-server/v5/audit"
"github.com/mattermost/mattermost-server/v5/mlog"
"github.com/mattermost/mattermost-server/v5/model"
"github.com/mattermost/mattermost-server/v5/store"
@@ -70,6 +71,10 @@ func createChannel(c *Context, w http.ResponseWriter, r *http.Request) {
return
}
+ auditRec := c.MakeAuditRecord("createChannel", audit.Fail)
+ defer c.LogAuditRec(auditRec)
+ auditRec.AddMeta("channel_name", channel.Name)
+
if channel.Type == model.CHANNEL_OPEN && !c.App.SessionHasPermissionToTeam(*c.App.Session(), channel.TeamId, model.PERMISSION_CREATE_PUBLIC_CHANNEL) {
c.SetPermissionError(model.PERMISSION_CREATE_PUBLIC_CHANNEL)
return
@@ -86,7 +91,10 @@ func createChannel(c *Context, w http.ResponseWriter, r *http.Request) {
return
}
+ auditRec.Success()
+ auditRec.AddMeta("channel_id", sc.Id)
c.LogAudit("name=" + channel.Name)
+
w.WriteHeader(http.StatusCreated)
w.Write([]byte(sc.ToJson()))
}
@@ -110,6 +118,10 @@ func updateChannel(c *Context, w http.ResponseWriter, r *http.Request) {
return
}
+ auditRec := c.MakeAuditRecord("updateChannel", audit.Fail)
+ defer c.LogAuditRec(auditRec)
+ auditRec.AddMeta("channel_id", channel.Id)
+
originalOldChannel, err := c.App.GetChannel(channel.Id)
if err != nil {
c.Err = err
@@ -117,6 +129,8 @@ func updateChannel(c *Context, w http.ResponseWriter, r *http.Request) {
}
oldChannel := originalOldChannel.DeepCopy()
+ auditRec.AddMeta("channel_name", oldChannel.Name)
+
switch oldChannel.Type {
case model.CHANNEL_OPEN:
if !c.App.SessionHasPermissionToChannel(*c.App.Session(), c.Params.ChannelId, model.PERMISSION_MANAGE_PUBLIC_CHANNEL_PROPERTIES) {
@@ -170,6 +184,7 @@ func updateChannel(c *Context, w http.ResponseWriter, r *http.Request) {
if len(channel.Name) > 0 {
oldChannel.Name = channel.Name
+ auditRec.AddMeta("new_channel_name", oldChannel.Name)
}
if channel.GroupConstrained != nil {
@@ -187,7 +202,9 @@ func updateChannel(c *Context, w http.ResponseWriter, r *http.Request) {
}
}
+ auditRec.Success()
c.LogAudit("name=" + channel.Name)
+
w.Write([]byte(oldChannel.ToJson()))
}
@@ -203,6 +220,11 @@ func convertChannelToPrivate(c *Context, w http.ResponseWriter, r *http.Request)
return
}
+ auditRec := c.MakeAuditRecord("convertChannelToPrivate", audit.Fail)
+ defer c.LogAuditRec(auditRec)
+ auditRec.AddMeta("channel_id", oldPublicChannel.Id)
+ auditRec.AddMeta("channel_name", oldPublicChannel.Name)
+
if !c.App.SessionHasPermissionToTeam(*c.App.Session(), oldPublicChannel.TeamId, model.PERMISSION_MANAGE_TEAM) {
c.SetPermissionError(model.PERMISSION_MANAGE_TEAM)
return
@@ -232,7 +254,9 @@ func convertChannelToPrivate(c *Context, w http.ResponseWriter, r *http.Request)
return
}
+ auditRec.Success()
c.LogAudit("name=" + rchannel.Name)
+
w.Write([]byte(rchannel.ToJson()))
}
@@ -255,6 +279,13 @@ func updateChannelPrivacy(c *Context, w http.ResponseWriter, r *http.Request) {
return
}
+ auditRec := c.MakeAuditRecord("updateChannelPrivacy", audit.Fail)
+ defer c.LogAuditRec(auditRec)
+ auditRec.AddMeta("channel_id", channel.Id)
+ auditRec.AddMeta("channel_name", channel.Name)
+ auditRec.AddMeta("channel_type", channel.Type)
+ auditRec.AddMeta("new_channel_type", privacy)
+
if !c.App.SessionHasPermissionToTeam(*c.App.Session(), channel.TeamId, model.PERMISSION_MANAGE_TEAM) {
c.SetPermissionError(model.PERMISSION_MANAGE_TEAM)
return
@@ -279,6 +310,7 @@ func updateChannelPrivacy(c *Context, w http.ResponseWriter, r *http.Request) {
return
}
+ auditRec.Success()
c.LogAudit("name=" + updatedChannel.Name)
w.Write([]byte(updatedChannel.ToJson()))
@@ -303,6 +335,11 @@ func patchChannel(c *Context, w http.ResponseWriter, r *http.Request) {
}
oldChannel := originalOldChannel.DeepCopy()
+ auditRec := c.MakeAuditRecord("patchChannel", audit.Fail)
+ defer c.LogAuditRec(auditRec)
+ auditRec.AddMeta("channel_id", oldChannel.Id)
+ auditRec.AddMeta("channel_name", oldChannel.Name)
+
switch oldChannel.Type {
case model.CHANNEL_OPEN:
if !c.App.SessionHasPermissionToChannel(*c.App.Session(), c.Params.ChannelId, model.PERMISSION_MANAGE_PUBLIC_CHANNEL_PROPERTIES) {
@@ -340,7 +377,9 @@ func patchChannel(c *Context, w http.ResponseWriter, r *http.Request) {
return
}
+ auditRec.Success()
c.LogAudit("")
+
w.Write([]byte(rchannel.ToJson()))
}
@@ -357,6 +396,11 @@ func restoreChannel(c *Context, w http.ResponseWriter, r *http.Request) {
}
teamId := channel.TeamId
+ auditRec := c.MakeAuditRecord("restoreChannel", audit.Fail)
+ defer c.LogAuditRec(auditRec)
+ auditRec.AddMeta("channel_id", channel.Id)
+ auditRec.AddMeta("channel_name", channel.Name)
+
if !c.App.SessionHasPermissionToTeam(*c.App.Session(), teamId, model.PERMISSION_MANAGE_TEAM) {
c.SetPermissionError(model.PERMISSION_MANAGE_TEAM)
return
@@ -368,9 +412,10 @@ func restoreChannel(c *Context, w http.ResponseWriter, r *http.Request) {
return
}
+ auditRec.Success()
c.LogAudit("name=" + channel.Name)
- w.Write([]byte(channel.ToJson()))
+ w.Write([]byte(channel.ToJson()))
}
func createDirectChannel(c *Context, w http.ResponseWriter, r *http.Request) {
@@ -392,6 +437,9 @@ func createDirectChannel(c *Context, w http.ResponseWriter, r *http.Request) {
}
}
+ auditRec := c.MakeAuditRecord("createDirectChannel", audit.Fail)
+ defer c.LogAuditRec(auditRec)
+
if !c.App.SessionHasPermissionTo(*c.App.Session(), model.PERMISSION_CREATE_DIRECT_CHANNEL) {
c.SetPermissionError(model.PERMISSION_CREATE_DIRECT_CHANNEL)
return
@@ -407,6 +455,8 @@ func createDirectChannel(c *Context, w http.ResponseWriter, r *http.Request) {
otherUserId = userIds[1]
}
+ auditRec.AddMeta("other_user_id", otherUserId)
+
canSee, err := c.App.UserCanSeeOtherUser(c.App.Session().UserId, otherUserId)
if err != nil {
c.Err = err
@@ -424,6 +474,10 @@ func createDirectChannel(c *Context, w http.ResponseWriter, r *http.Request) {
return
}
+ auditRec.Success()
+ auditRec.AddMeta("channel_id", sc.Id)
+ auditRec.AddMeta("channel_name", sc.Name)
+
w.WriteHeader(http.StatusCreated)
w.Write([]byte(sc.ToJson()))
}
@@ -467,6 +521,9 @@ func createGroupChannel(c *Context, w http.ResponseWriter, r *http.Request) {
userIds = append(userIds, c.App.Session().UserId)
}
+ auditRec := c.MakeAuditRecord("createGroupChannel", audit.Fail)
+ defer c.LogAuditRec(auditRec)
+
if !c.App.SessionHasPermissionTo(*c.App.Session(), model.PERMISSION_CREATE_GROUP_CHANNEL) {
c.SetPermissionError(model.PERMISSION_CREATE_GROUP_CHANNEL)
return
@@ -497,6 +554,10 @@ func createGroupChannel(c *Context, w http.ResponseWriter, r *http.Request) {
return
}
+ auditRec.Success()
+ auditRec.AddMeta("channel_id", groupChannel.Id)
+ auditRec.AddMeta("channel_name", groupChannel.Name)
+
w.WriteHeader(http.StatusCreated)
w.Write([]byte(groupChannel.ToJson()))
}
@@ -949,6 +1010,11 @@ func deleteChannel(c *Context, w http.ResponseWriter, r *http.Request) {
return
}
+ auditRec := c.MakeAuditRecord("deleteChannel", audit.Fail)
+ defer c.LogAuditRec(auditRec)
+ auditRec.AddMeta("channel_id", channel.Id)
+ auditRec.AddMeta("channel_name", channel.Name)
+
if channel.Type == model.CHANNEL_DIRECT || channel.Type == model.CHANNEL_GROUP {
c.Err = model.NewAppError("deleteChannel", "api.channel.delete_channel.type.invalid", nil, "", http.StatusBadRequest)
return
@@ -970,6 +1036,7 @@ func deleteChannel(c *Context, w http.ResponseWriter, r *http.Request) {
return
}
+ auditRec.Success()
c.LogAudit("name=" + channel.Name)
ReturnStatusOK(w)
@@ -1208,6 +1275,11 @@ func updateChannelMemberRoles(c *Context, w http.ResponseWriter, r *http.Request
return
}
+ auditRec := c.MakeAuditRecord("updateChannelMemberRoles", audit.Fail)
+ defer c.LogAuditRec(auditRec)
+ auditRec.AddMeta("channel_id", c.Params.ChannelId)
+ auditRec.AddMeta("roles", newRoles)
+
if !c.App.SessionHasPermissionToChannel(*c.App.Session(), c.Params.ChannelId, model.PERMISSION_MANAGE_CHANNEL_ROLES) {
c.SetPermissionError(model.PERMISSION_MANAGE_CHANNEL_ROLES)
return
@@ -1218,6 +1290,8 @@ func updateChannelMemberRoles(c *Context, w http.ResponseWriter, r *http.Request
return
}
+ auditRec.Success()
+
ReturnStatusOK(w)
}
@@ -1233,6 +1307,11 @@ func updateChannelMemberSchemeRoles(c *Context, w http.ResponseWriter, r *http.R
return
}
+ auditRec := c.MakeAuditRecord("updateChannelMemberSchemeRoles", audit.Fail)
+ defer c.LogAuditRec(auditRec)
+ auditRec.AddMeta("channel_id", c.Params.ChannelId)
+ auditRec.AddMeta("roles", schemeRoles)
+
if !c.App.SessionHasPermissionToChannel(*c.App.Session(), c.Params.ChannelId, model.PERMISSION_MANAGE_CHANNEL_ROLES) {
c.SetPermissionError(model.PERMISSION_MANAGE_CHANNEL_ROLES)
return
@@ -1243,6 +1322,8 @@ func updateChannelMemberSchemeRoles(c *Context, w http.ResponseWriter, r *http.R
return
}
+ auditRec.Success()
+
ReturnStatusOK(w)
}
@@ -1258,6 +1339,11 @@ func updateChannelMemberNotifyProps(c *Context, w http.ResponseWriter, r *http.R
return
}
+ auditRec := c.MakeAuditRecord("updateChannelMemberNotifyProps", audit.Fail)
+ defer c.LogAuditRec(auditRec)
+ auditRec.AddMeta("channel_id", c.Params.ChannelId)
+ auditRec.AddMeta("props", props)
+
if !c.App.SessionHasPermissionToUser(*c.App.Session(), c.Params.UserId) {
c.SetPermissionError(model.PERMISSION_EDIT_OTHER_USERS)
return
@@ -1269,6 +1355,8 @@ func updateChannelMemberNotifyProps(c *Context, w http.ResponseWriter, r *http.R
return
}
+ auditRec.Success()
+
ReturnStatusOK(w)
}
@@ -1314,6 +1402,11 @@ func addChannelMember(c *Context, w http.ResponseWriter, r *http.Request) {
return
}
+ auditRec := c.MakeAuditRecord("addChannelMember", audit.Fail)
+ defer c.LogAuditRec(auditRec)
+ auditRec.AddMeta("channel_id", channel.Id)
+ auditRec.AddMeta("channel_name", channel.Name)
+
if channel.Type == model.CHANNEL_DIRECT || channel.Type == model.CHANNEL_GROUP {
c.Err = model.NewAppError("addUserToChannel", "api.channel.add_user_to_channel.type.app_error", nil, "", http.StatusBadRequest)
return
@@ -1385,7 +1478,10 @@ func addChannelMember(c *Context, w http.ResponseWriter, r *http.Request) {
return
}
+ auditRec.Success()
+ auditRec.AddMeta("add_user_id", cm.UserId)
c.LogAudit("name=" + channel.Name + " user_id=" + cm.UserId)
+
w.WriteHeader(http.StatusCreated)
w.Write([]byte(cm.ToJson()))
}
@@ -1408,6 +1504,12 @@ func removeChannelMember(c *Context, w http.ResponseWriter, r *http.Request) {
return
}
+ auditRec := c.MakeAuditRecord("removeChannelMember", audit.Fail)
+ defer c.LogAuditRec(auditRec)
+ auditRec.AddMeta("channel_id", channel.Id)
+ auditRec.AddMeta("channel_name", channel.Name)
+ auditRec.AddMeta("remove_user_id", user.Id)
+
if !(channel.Type == model.CHANNEL_OPEN || channel.Type == model.CHANNEL_PRIVATE) {
c.Err = model.NewAppError("removeChannelMember", "api.channel.remove_channel_member.type.app_error", nil, "", http.StatusBadRequest)
return
@@ -1435,6 +1537,7 @@ func removeChannelMember(c *Context, w http.ResponseWriter, r *http.Request) {
return
}
+ auditRec.Success()
c.LogAudit("name=" + channel.Name + " user_id=" + c.Params.UserId)
ReturnStatusOK(w)
@@ -1452,6 +1555,10 @@ func updateChannelScheme(c *Context, w http.ResponseWriter, r *http.Request) {
return
}
+ auditRec := c.MakeAuditRecord("updateChannelScheme", audit.Fail)
+ defer c.LogAuditRec(auditRec)
+ auditRec.AddMeta("new_scheme_id", schemeID)
+
if c.App.License() == nil {
c.Err = model.NewAppError("Api4.UpdateChannelScheme", "api.channel.update_channel_scheme.license.error", nil, "", http.StatusNotImplemented)
return
@@ -1479,6 +1586,10 @@ func updateChannelScheme(c *Context, w http.ResponseWriter, r *http.Request) {
return
}
+ auditRec.AddMeta("channel_id", channel.Id)
+ auditRec.AddMeta("channel_name", channel.Name)
+ auditRec.AddMeta("old_scheme_id", channel.SchemeId)
+
channel.SchemeId = &scheme.Id
_, err = c.App.UpdateChannelScheme(channel)
@@ -1487,6 +1598,8 @@ func updateChannelScheme(c *Context, w http.ResponseWriter, r *http.Request) {
return
}
+ auditRec.Success()
+
ReturnStatusOK(w)
}
diff --git a/api4/command.go b/api4/command.go
index c85e48bf18..6e9f5a19ea 100644
--- a/api4/command.go
+++ b/api4/command.go
@@ -8,6 +8,7 @@ import (
"strconv"
"strings"
+ "github.com/mattermost/mattermost-server/v5/audit"
"github.com/mattermost/mattermost-server/v5/model"
)
@@ -32,6 +33,8 @@ func createCommand(c *Context, w http.ResponseWriter, r *http.Request) {
return
}
+ auditRec := c.MakeAuditRecord("createCommand", audit.Fail)
+ defer c.LogAuditRec(auditRec)
c.LogAudit("attempt")
if !c.App.SessionHasPermissionToTeam(*c.App.Session(), cmd.TeamId, model.PERMISSION_MANAGE_SLASH_COMMANDS) {
@@ -47,7 +50,10 @@ func createCommand(c *Context, w http.ResponseWriter, r *http.Request) {
return
}
+ auditRec.Success()
+ auditRec.AddMeta("command_id", rcmd.Id)
c.LogAudit("success")
+
w.WriteHeader(http.StatusCreated)
w.Write([]byte(rcmd.ToJson()))
}
@@ -64,6 +70,9 @@ func updateCommand(c *Context, w http.ResponseWriter, r *http.Request) {
return
}
+ auditRec := c.MakeAuditRecord("updateCommand", audit.Fail)
+ auditRec.AddMeta("command_id", c.Params.CommandId)
+ defer c.LogAuditRec(auditRec)
c.LogAudit("attempt")
oldCmd, err := c.App.GetCommand(c.Params.CommandId)
@@ -97,6 +106,7 @@ func updateCommand(c *Context, w http.ResponseWriter, r *http.Request) {
return
}
+ auditRec.Success()
c.LogAudit("success")
w.Write([]byte(rcmd.ToJson()))
@@ -108,14 +118,18 @@ func moveCommand(c *Context, w http.ResponseWriter, r *http.Request) {
return
}
- c.LogAudit("attempt")
-
cmr, err := model.CommandMoveRequestFromJson(r.Body)
if err != nil {
c.SetInvalidParam("team_id")
return
}
+ auditRec := c.MakeAuditRecord("moveCommand", audit.Fail)
+ auditRec.AddMeta("command_id", c.Params.CommandId)
+ auditRec.AddMeta("to_team_id", cmr.TeamId)
+ defer c.LogAuditRec(auditRec)
+ c.LogAudit("attempt")
+
newTeam, appErr := c.App.GetTeam(cmr.TeamId)
if appErr != nil {
c.Err = appErr
@@ -133,6 +147,7 @@ func moveCommand(c *Context, w http.ResponseWriter, r *http.Request) {
c.SetCommandNotFoundError()
return
}
+ auditRec.AddMeta("from_team_id", cmd.TeamId)
if !c.App.SessionHasPermissionToTeam(*c.App.Session(), cmd.TeamId, model.PERMISSION_MANAGE_SLASH_COMMANDS) {
c.LogAudit("fail - inappropriate permissions")
@@ -147,7 +162,9 @@ func moveCommand(c *Context, w http.ResponseWriter, r *http.Request) {
return
}
+ auditRec.Success()
c.LogAudit("success")
+
ReturnStatusOK(w)
}
@@ -157,6 +174,9 @@ func deleteCommand(c *Context, w http.ResponseWriter, r *http.Request) {
return
}
+ auditRec := c.MakeAuditRecord("deleteCommand", audit.Fail)
+ auditRec.AddMeta("command_id", c.Params.CommandId)
+ defer c.LogAuditRec(auditRec)
c.LogAudit("attempt")
cmd, err := c.App.GetCommand(c.Params.CommandId)
@@ -185,6 +205,7 @@ func deleteCommand(c *Context, w http.ResponseWriter, r *http.Request) {
return
}
+ auditRec.Success()
c.LogAudit("success")
ReturnStatusOK(w)
@@ -347,7 +368,11 @@ func regenCommandToken(c *Context, w http.ResponseWriter, r *http.Request) {
return
}
+ auditRec := c.MakeAuditRecord("regenCommandToken", audit.Fail)
+ auditRec.AddMeta("command_id", c.Params.CommandId)
+ defer c.LogAuditRec(auditRec)
c.LogAudit("attempt")
+
cmd, err := c.App.GetCommand(c.Params.CommandId)
if err != nil {
c.SetCommandNotFoundError()
@@ -374,6 +399,9 @@ func regenCommandToken(c *Context, w http.ResponseWriter, r *http.Request) {
return
}
+ auditRec.Success()
+ c.LogAudit("success")
+
resp := make(map[string]string)
resp["token"] = rcmd.Token
diff --git a/api4/compliance.go b/api4/compliance.go
index b94dc9bfdc..38f2efe7f4 100644
--- a/api4/compliance.go
+++ b/api4/compliance.go
@@ -8,6 +8,7 @@ import (
"strconv"
"github.com/avct/uasurfer"
+ "github.com/mattermost/mattermost-server/v5/audit"
"github.com/mattermost/mattermost-server/v5/model"
)
@@ -25,6 +26,9 @@ func createComplianceReport(c *Context, w http.ResponseWriter, r *http.Request)
return
}
+ auditRec := c.MakeAuditRecord("createComplianceReport", audit.Fail)
+ defer c.LogAuditRec(auditRec)
+
if !c.App.SessionHasPermissionTo(*c.App.Session(), model.PERMISSION_MANAGE_SYSTEM) {
c.SetPermissionError(model.PERMISSION_MANAGE_SYSTEM)
return
@@ -38,7 +42,11 @@ func createComplianceReport(c *Context, w http.ResponseWriter, r *http.Request)
return
}
+ auditRec.Success()
+ auditRec.AddMeta("compliance_id", rjob.Id)
+ auditRec.AddMeta("compliance_desc", rjob.Desc)
c.LogAudit("")
+
w.WriteHeader(http.StatusCreated)
w.Write([]byte(rjob.ToJson()))
}
@@ -84,6 +92,10 @@ func downloadComplianceReport(c *Context, w http.ResponseWriter, r *http.Request
return
}
+ auditRec := c.MakeAuditRecord("downloadComplianceReport", audit.Fail)
+ defer c.LogAuditRec(auditRec)
+ auditRec.AddMeta("compliance_id", c.Params.ReportId)
+
if !c.App.SessionHasPermissionTo(*c.App.Session(), model.PERMISSION_MANAGE_SYSTEM) {
c.SetPermissionError(model.PERMISSION_MANAGE_SYSTEM)
return
@@ -101,6 +113,7 @@ func downloadComplianceReport(c *Context, w http.ResponseWriter, r *http.Request
return
}
+ auditRec.AddMeta("compliance_desc", job.Desc)
c.LogAudit("downloaded " + job.Desc)
w.Header().Set("Cache-Control", "max-age=2592000, public")
@@ -117,5 +130,7 @@ func downloadComplianceReport(c *Context, w http.ResponseWriter, r *http.Request
w.Header().Set("Content-Type", "application/octet-stream")
}
+ auditRec.Success()
+
w.Write(reportBytes)
}
diff --git a/api4/config.go b/api4/config.go
index 1914ea4b19..95fdfc2b6c 100644
--- a/api4/config.go
+++ b/api4/config.go
@@ -7,6 +7,7 @@ import (
"net/http"
"reflect"
+ "github.com/mattermost/mattermost-server/v5/audit"
"github.com/mattermost/mattermost-server/v5/config"
"github.com/mattermost/mattermost-server/v5/model"
"github.com/mattermost/mattermost-server/v5/utils"
@@ -34,6 +35,9 @@ func getConfig(c *Context, w http.ResponseWriter, r *http.Request) {
}
func configReload(c *Context, w http.ResponseWriter, r *http.Request) {
+ auditRec := c.MakeAuditRecord("configReload", audit.Fail)
+ defer c.LogAuditRec(auditRec)
+
if !c.App.SessionHasPermissionTo(*c.App.Session(), model.PERMISSION_MANAGE_SYSTEM) {
c.SetPermissionError(model.PERMISSION_MANAGE_SYSTEM)
return
@@ -46,6 +50,8 @@ func configReload(c *Context, w http.ResponseWriter, r *http.Request) {
c.App.ReloadConfig()
+ auditRec.Success()
+
w.Header().Set("Cache-Control", "no-cache, no-store, must-revalidate")
ReturnStatusOK(w)
}
@@ -57,6 +63,9 @@ func updateConfig(c *Context, w http.ResponseWriter, r *http.Request) {
return
}
+ auditRec := c.MakeAuditRecord("updateConfig", audit.Fail)
+ defer c.LogAuditRec(auditRec)
+
cfg.SetDefaults()
if !c.App.SessionHasPermissionTo(*c.App.Session(), model.PERMISSION_MANAGE_SYSTEM) {
@@ -101,10 +110,11 @@ func updateConfig(c *Context, w http.ResponseWriter, r *http.Request) {
return
}
- c.LogAudit("updateConfig")
-
cfg = c.App.GetSanitizedConfig()
+ auditRec.Success()
+ c.LogAudit("updateConfig")
+
w.Header().Set("Cache-Control", "no-cache, no-store, must-revalidate")
w.Write([]byte(cfg.ToJson()))
}
@@ -151,6 +161,9 @@ func patchConfig(c *Context, w http.ResponseWriter, r *http.Request) {
return
}
+ auditRec := c.MakeAuditRecord("patchConfig", audit.Fail)
+ defer c.LogAuditRec(auditRec)
+
if !c.App.SessionHasPermissionTo(*c.App.Session(), model.PERMISSION_MANAGE_SYSTEM) {
c.SetPermissionError(model.PERMISSION_MANAGE_SYSTEM)
return
@@ -196,6 +209,8 @@ func patchConfig(c *Context, w http.ResponseWriter, r *http.Request) {
return
}
+ auditRec.Success()
+
w.Header().Set("Cache-Control", "no-cache, no-store, must-revalidate")
w.Write([]byte(c.App.GetSanitizedConfig().ToJson()))
}
diff --git a/api4/elasticsearch.go b/api4/elasticsearch.go
index 94fed473e1..1f2a159531 100644
--- a/api4/elasticsearch.go
+++ b/api4/elasticsearch.go
@@ -6,6 +6,7 @@ package api4
import (
"net/http"
+ "github.com/mattermost/mattermost-server/v5/audit"
"github.com/mattermost/mattermost-server/v5/model"
)
@@ -39,6 +40,9 @@ func testElasticsearch(c *Context, w http.ResponseWriter, r *http.Request) {
}
func purgeElasticsearchIndexes(c *Context, w http.ResponseWriter, r *http.Request) {
+ auditRec := c.MakeAuditRecord("purgeElasticsearchIndexes", audit.Fail)
+ defer c.LogAuditRec(auditRec)
+
if !c.App.SessionHasPermissionTo(*c.App.Session(), model.PERMISSION_MANAGE_SYSTEM) {
c.SetPermissionError(model.PERMISSION_MANAGE_SYSTEM)
return
@@ -54,5 +58,7 @@ func purgeElasticsearchIndexes(c *Context, w http.ResponseWriter, r *http.Reques
return
}
+ auditRec.Success()
+
ReturnStatusOK(w)
}
diff --git a/api4/emoji.go b/api4/emoji.go
index 99bc2495a3..294c29e22f 100644
--- a/api4/emoji.go
+++ b/api4/emoji.go
@@ -10,6 +10,7 @@ import (
"strings"
"github.com/mattermost/mattermost-server/v5/app"
+ "github.com/mattermost/mattermost-server/v5/audit"
"github.com/mattermost/mattermost-server/v5/model"
"github.com/mattermost/mattermost-server/v5/web"
)
@@ -47,6 +48,9 @@ func createEmoji(c *Context, w http.ResponseWriter, r *http.Request) {
return
}
+ auditRec := c.MakeAuditRecord("createEmoji", audit.Fail)
+ defer c.LogAuditRec(auditRec)
+
// Allow any user with CREATE_EMOJIS permission at Team level to create emojis at system level
memberships, err := c.App.GetTeamMembersForUser(c.App.Session().UserId)
@@ -83,12 +87,16 @@ func createEmoji(c *Context, w http.ResponseWriter, r *http.Request) {
return
}
+ auditRec.AddMeta("emoji_id", emoji.Id)
+ auditRec.AddMeta("emoji_name", emoji.Name)
+
newEmoji, err := c.App.CreateEmoji(c.App.Session().UserId, emoji, m)
if err != nil {
c.Err = err
return
}
+ auditRec.Success()
w.Write([]byte(newEmoji.ToJson()))
}
@@ -119,11 +127,16 @@ func deleteEmoji(c *Context, w http.ResponseWriter, r *http.Request) {
return
}
+ auditRec := c.MakeAuditRecord("deleteEmoji", audit.Fail)
+ defer c.LogAuditRec(auditRec)
+ auditRec.AddMeta("emoji_id", c.Params.EmojiId)
+
emoji, err := c.App.GetEmoji(c.Params.EmojiId)
if err != nil {
c.Err = err
return
}
+ auditRec.AddMeta("emoji_name", emoji.Name)
// Allow any user with DELETE_EMOJIS permission at Team level to delete emojis at system level
memberships, err := c.App.GetTeamMembersForUser(c.App.Session().UserId)
@@ -170,6 +183,8 @@ func deleteEmoji(c *Context, w http.ResponseWriter, r *http.Request) {
return
}
+ auditRec.Success()
+
ReturnStatusOK(w)
}
diff --git a/api4/file.go b/api4/file.go
index 0ecd8b0f31..212ada2f2e 100644
--- a/api4/file.go
+++ b/api4/file.go
@@ -17,6 +17,7 @@ import (
"time"
"github.com/mattermost/mattermost-server/v5/app"
+ "github.com/mattermost/mattermost-server/v5/audit"
"github.com/mattermost/mattermost-server/v5/model"
"github.com/mattermost/mattermost-server/v5/utils"
)
@@ -153,12 +154,20 @@ func uploadFileSimple(c *Context, r *http.Request, timestamp time.Time) *model.F
return nil
}
+ auditRec := c.MakeAuditRecord("uploadFileSimple", audit.Fail)
+ defer c.LogAuditRec(auditRec)
+ auditRec.AddMeta("channel_id", c.Params.ChannelId)
+ auditRec.AddMeta("filename", c.Params.Filename)
+
if !c.App.SessionHasPermissionToChannel(*c.App.Session(), c.Params.ChannelId, model.PERMISSION_UPLOAD_FILE) {
c.SetPermissionError(model.PERMISSION_UPLOAD_FILE)
return nil
}
clientId := r.Form.Get("client_id")
+ auditRec.AddMeta("client_id", clientId)
+ auditRec.AddMeta("content_length", r.ContentLength)
+
info, appErr := c.App.UploadFileX(c.Params.ChannelId, c.Params.Filename, r.Body,
app.UploadFileSetTeamId(FILE_TEAM_ID),
app.UploadFileSetUserId(c.App.Session().UserId),
@@ -176,6 +185,7 @@ func uploadFileSimple(c *Context, r *http.Request, timestamp time.Time) *model.F
if clientId != "" {
fileUploadResponse.ClientIds = []string{clientId}
}
+ auditRec.Success()
return fileUploadResponse
}
@@ -316,6 +326,11 @@ NEXT_PART:
clientId = clientIds[nFiles]
}
+ auditRec := c.MakeAuditRecord("uploadFileMultipart", audit.Fail)
+ auditRec.AddMeta("channel_id", c.Params.ChannelId)
+ auditRec.AddMeta("filename", filename)
+ auditRec.AddMeta("client_id", clientId)
+
info, appErr := c.App.UploadFileX(c.Params.ChannelId, filename, part,
app.UploadFileSetTeamId(FILE_TEAM_ID),
app.UploadFileSetUserId(c.App.Session().UserId),
@@ -324,8 +339,11 @@ NEXT_PART:
app.UploadFileSetClientId(clientId))
if appErr != nil {
c.Err = appErr
+ c.LogAuditRec(auditRec)
return nil
}
+ auditRec.Success()
+ c.LogAuditRec(auditRec)
// add to the response
resp.FileInfos = append(resp.FileInfos, info)
@@ -409,6 +427,12 @@ func uploadFileMultipartLegacy(c *Context, mr *multipart.Reader,
clientId = clientIds[i]
}
+ auditRec := c.MakeAuditRecord("uploadFileMultipartLegacy", audit.Fail)
+ defer c.LogAuditRec(auditRec)
+ auditRec.AddMeta("channel_id", channelId)
+ auditRec.AddMeta("filename", fileHeader.Filename)
+ auditRec.AddMeta("client_id", clientId)
+
info, appErr := c.App.UploadFileX(c.Params.ChannelId, fileHeader.Filename, f,
app.UploadFileSetTeamId(FILE_TEAM_ID),
app.UploadFileSetUserId(c.App.Session().UserId),
@@ -418,9 +442,13 @@ func uploadFileMultipartLegacy(c *Context, mr *multipart.Reader,
f.Close()
if appErr != nil {
c.Err = appErr
+ c.LogAuditRec(auditRec)
return nil
}
+ auditRec.Success()
+ c.LogAuditRec(auditRec)
+
resp.FileInfos = append(resp.FileInfos, info)
if clientId != "" {
resp.ClientIds = append(resp.ClientIds, clientId)
diff --git a/api4/group.go b/api4/group.go
index 555e93b200..b87b4b4609 100644
--- a/api4/group.go
+++ b/api4/group.go
@@ -9,6 +9,7 @@ import (
"io/ioutil"
"net/http"
+ "github.com/mattermost/mattermost-server/v5/audit"
"github.com/mattermost/mattermost-server/v5/model"
)
@@ -105,6 +106,10 @@ func patchGroup(c *Context, w http.ResponseWriter, r *http.Request) {
return
}
+ auditRec := c.MakeAuditRecord("patchGroup", audit.Fail)
+ defer c.LogAuditRec(auditRec)
+ auditRec.AddMeta("group_id", c.Params.GroupId)
+
if c.App.License() == nil || !*c.App.License().Features.LDAPGroups {
c.Err = model.NewAppError("Api4.patchGroup", "api.ldap_groups.license_error", nil, "", http.StatusNotImplemented)
return
@@ -120,6 +125,9 @@ func patchGroup(c *Context, w http.ResponseWriter, r *http.Request) {
c.Err = err
return
}
+ auditRec.AddMeta("old_group_name", group.Name)
+ auditRec.AddMeta("old_group_display", group.DisplayName)
+ auditRec.AddMeta("old_group_desc", group.Description)
group.Patch(groupPatch)
@@ -129,12 +137,18 @@ func patchGroup(c *Context, w http.ResponseWriter, r *http.Request) {
return
}
+ auditRec.AddMeta("new_group_name", group.Name)
+ auditRec.AddMeta("new_group_display", group.DisplayName)
+ auditRec.AddMeta("new_group_desc", group.Description)
+
b, marshalErr := json.Marshal(group)
if marshalErr != nil {
c.Err = model.NewAppError("Api4.patchGroup", "api.marshal_error", nil, marshalErr.Error(), http.StatusInternalServerError)
return
}
+ auditRec.Success()
+
w.Write(b)
}
@@ -162,6 +176,12 @@ func linkGroupSyncable(c *Context, w http.ResponseWriter, r *http.Request) {
return
}
+ auditRec := c.MakeAuditRecord("linkGroupSyncable", audit.Fail)
+ defer c.LogAuditRec(auditRec)
+ auditRec.AddMeta("group_id", c.Params.GroupId)
+ auditRec.AddMeta("syncable_id", syncableID)
+ auditRec.AddMeta("syncable_type", syncableType)
+
var patch *model.GroupSyncablePatch
err = json.Unmarshal(body, &patch)
if err != nil || patch == nil {
@@ -203,7 +223,7 @@ func linkGroupSyncable(c *Context, w http.ResponseWriter, r *http.Request) {
c.Err = model.NewAppError("Api4.createGroupSyncable", "api.marshal_error", nil, marshalErr.Error(), http.StatusInternalServerError)
return
}
-
+ auditRec.Success()
w.Write(b)
}
@@ -311,6 +331,12 @@ func patchGroupSyncable(c *Context, w http.ResponseWriter, r *http.Request) {
return
}
+ auditRec := c.MakeAuditRecord("patchGroupSyncable", audit.Fail)
+ defer c.LogAuditRec(auditRec)
+ auditRec.AddMeta("group_id", c.Params.GroupId)
+ auditRec.AddMeta("old_syncable_id", syncableID)
+ auditRec.AddMeta("old_syncable_type", syncableType)
+
var patch *model.GroupSyncablePatch
err = json.Unmarshal(body, &patch)
if err != nil || patch == nil {
@@ -344,6 +370,9 @@ func patchGroupSyncable(c *Context, w http.ResponseWriter, r *http.Request) {
return
}
+ auditRec.AddMeta("new_syncable_id", groupSyncable.SyncableId)
+ auditRec.AddMeta("new_syncable_type", groupSyncable.Type)
+
// Not awaiting completion because the group sync job executes the same procedure—but for all syncables—and
// persists the execution status to the jobs table.
go c.App.SyncRolesAndMembership(syncableID, syncableType)
@@ -353,7 +382,7 @@ func patchGroupSyncable(c *Context, w http.ResponseWriter, r *http.Request) {
c.Err = model.NewAppError("Api4.patchGroupSyncable", "api.marshal_error", nil, marshalErr.Error(), http.StatusInternalServerError)
return
}
-
+ auditRec.Success()
w.Write(b)
}
@@ -375,6 +404,12 @@ func unlinkGroupSyncable(c *Context, w http.ResponseWriter, r *http.Request) {
}
syncableType := c.Params.SyncableType
+ auditRec := c.MakeAuditRecord("unlinkGroupSyncable", audit.Fail)
+ defer c.LogAuditRec(auditRec)
+ auditRec.AddMeta("group_id", c.Params.GroupId)
+ auditRec.AddMeta("syncable_id", syncableID)
+ auditRec.AddMeta("syncable_type", syncableType)
+
if c.App.License() == nil || !*c.App.License().Features.LDAPGroups {
c.Err = model.NewAppError("Api4.unlinkGroupSyncable", "api.ldap_groups.license_error", nil, "", http.StatusNotImplemented)
return
@@ -396,6 +431,8 @@ func unlinkGroupSyncable(c *Context, w http.ResponseWriter, r *http.Request) {
// persists the execution status to the jobs table.
go c.App.SyncRolesAndMembership(syncableID, syncableType)
+ auditRec.Success()
+
ReturnStatusOK(w)
}
diff --git a/api4/job.go b/api4/job.go
index 7ed365a518..b82043a89c 100644
--- a/api4/job.go
+++ b/api4/job.go
@@ -6,6 +6,7 @@ package api4
import (
"net/http"
+ "github.com/mattermost/mattermost-server/v5/audit"
"github.com/mattermost/mattermost-server/v5/model"
)
@@ -44,6 +45,10 @@ func createJob(c *Context, w http.ResponseWriter, r *http.Request) {
return
}
+ auditRec := c.MakeAuditRecord("createJob", audit.Fail)
+ defer c.LogAuditRec(auditRec)
+ auditRec.AddMeta("job_type", job.Type)
+
if !c.App.SessionHasPermissionTo(*c.App.Session(), model.PERMISSION_MANAGE_JOBS) {
c.SetPermissionError(model.PERMISSION_MANAGE_JOBS)
return
@@ -55,6 +60,9 @@ func createJob(c *Context, w http.ResponseWriter, r *http.Request) {
return
}
+ auditRec.Success()
+ auditRec.AddMeta("job_id", job.Id)
+
w.WriteHeader(http.StatusCreated)
w.Write([]byte(job.ToJson()))
}
@@ -104,6 +112,10 @@ func cancelJob(c *Context, w http.ResponseWriter, r *http.Request) {
return
}
+ auditRec := c.MakeAuditRecord("cancelJob", audit.Fail)
+ defer c.LogAuditRec(auditRec)
+ auditRec.AddMeta("job_id", c.Params.JobId)
+
if !c.App.SessionHasPermissionTo(*c.App.Session(), model.PERMISSION_MANAGE_JOBS) {
c.SetPermissionError(model.PERMISSION_MANAGE_JOBS)
return
@@ -114,5 +126,7 @@ func cancelJob(c *Context, w http.ResponseWriter, r *http.Request) {
return
}
+ auditRec.Success()
+
ReturnStatusOK(w)
}
diff --git a/api4/ldap.go b/api4/ldap.go
index e5fd4b83fb..5ebbf89d18 100644
--- a/api4/ldap.go
+++ b/api4/ldap.go
@@ -8,6 +8,7 @@ import (
"encoding/json"
"net/http"
+ "github.com/mattermost/mattermost-server/v5/audit"
"github.com/mattermost/mattermost-server/v5/model"
)
@@ -38,6 +39,9 @@ func syncLdap(c *Context, w http.ResponseWriter, r *http.Request) {
return
}
+ auditRec := c.MakeAuditRecord("syncLdap", audit.Fail)
+ defer c.LogAuditRec(auditRec)
+
if !c.App.SessionHasPermissionTo(*c.App.Session(), model.PERMISSION_MANAGE_SYSTEM) {
c.SetPermissionError(model.PERMISSION_MANAGE_SYSTEM)
return
@@ -45,6 +49,7 @@ func syncLdap(c *Context, w http.ResponseWriter, r *http.Request) {
c.App.SyncLdap()
+ auditRec.Success()
ReturnStatusOK(w)
}
@@ -130,6 +135,10 @@ func linkLdapGroup(c *Context, w http.ResponseWriter, r *http.Request) {
return
}
+ auditRec := c.MakeAuditRecord("linkLdapGroup", audit.Fail)
+ defer c.LogAuditRec(auditRec)
+ auditRec.AddMeta("remote_id", c.Params.RemoteId)
+
if c.App.License() == nil || !*c.App.License().Features.LDAPGroups {
c.Err = model.NewAppError("Api4.linkLdapGroup", "api.ldap_groups.license_error", nil, "", http.StatusNotImplemented)
return
@@ -140,6 +149,8 @@ func linkLdapGroup(c *Context, w http.ResponseWriter, r *http.Request) {
c.Err = err
return
}
+ auditRec.AddMeta("ldap_group_id", ldapGroup.Id)
+ auditRec.AddMeta("ldap_group_desc", ldapGroup.Description)
if ldapGroup == nil {
c.Err = model.NewAppError("Api4.linkLdapGroup", "api.ldap_group.not_found", nil, "", http.StatusNotFound)
@@ -151,6 +162,10 @@ func linkLdapGroup(c *Context, w http.ResponseWriter, r *http.Request) {
c.Err = err
return
}
+ if group != nil {
+ auditRec.AddMeta("group_id", group.Id)
+ auditRec.AddMeta("group_name", group.Name)
+ }
var status int
var newOrUpdatedGroup *model.Group
@@ -162,6 +177,7 @@ func linkLdapGroup(c *Context, w http.ResponseWriter, r *http.Request) {
} else {
displayName = ldapGroup.DisplayName
}
+ auditRec.AddMeta("ldap_group_display", displayName)
// Group has been previously linked
if group != nil {
@@ -204,6 +220,8 @@ func linkLdapGroup(c *Context, w http.ResponseWriter, r *http.Request) {
return
}
+ auditRec.Success()
+
w.WriteHeader(status)
w.Write(b)
}
@@ -214,6 +232,10 @@ func unlinkLdapGroup(c *Context, w http.ResponseWriter, r *http.Request) {
return
}
+ auditRec := c.MakeAuditRecord("unlinkLdapGroup", audit.Fail)
+ defer c.LogAuditRec(auditRec)
+ auditRec.AddMeta("remote_id", c.Params.RemoteId)
+
if !c.App.SessionHasPermissionTo(*c.App.Session(), model.PERMISSION_MANAGE_SYSTEM) {
c.SetPermissionError(model.PERMISSION_MANAGE_SYSTEM)
return
@@ -229,6 +251,8 @@ func unlinkLdapGroup(c *Context, w http.ResponseWriter, r *http.Request) {
c.Err = err
return
}
+ auditRec.AddMeta("group_id", group.Id)
+ auditRec.AddMeta("group_name", group.Name)
if group.DeleteAt == 0 {
_, err = c.App.DeleteGroup(group.Id)
@@ -238,5 +262,6 @@ func unlinkLdapGroup(c *Context, w http.ResponseWriter, r *http.Request) {
}
}
+ auditRec.Success()
ReturnStatusOK(w)
}
diff --git a/api4/license.go b/api4/license.go
index a038023bb0..ab63883502 100644
--- a/api4/license.go
+++ b/api4/license.go
@@ -8,6 +8,7 @@ import (
"io"
"net/http"
+ "github.com/mattermost/mattermost-server/v5/audit"
"github.com/mattermost/mattermost-server/v5/model"
)
@@ -42,6 +43,8 @@ func getClientLicense(c *Context, w http.ResponseWriter, r *http.Request) {
}
func addLicense(c *Context, w http.ResponseWriter, r *http.Request) {
+ auditRec := c.MakeAuditRecord("addLicense", audit.Fail)
+ defer c.LogAuditRec(auditRec)
c.LogAudit("attempt")
if !c.App.SessionHasPermissionTo(*c.App.Session(), model.PERMISSION_MANAGE_SYSTEM) {
@@ -74,6 +77,7 @@ func addLicense(c *Context, w http.ResponseWriter, r *http.Request) {
}
fileData := fileArray[0]
+ auditRec.AddMeta("filename", fileData.Filename)
file, err := fileData.Open()
if err != nil {
@@ -103,11 +107,15 @@ func addLicense(c *Context, w http.ResponseWriter, r *http.Request) {
c.App.Srv().Jobs.StartWorkers()
}
+ auditRec.Success()
c.LogAudit("success")
+
w.Write([]byte(license.ToJson()))
}
func removeLicense(c *Context, w http.ResponseWriter, r *http.Request) {
+ auditRec := c.MakeAuditRecord("removeLicense", audit.Fail)
+ defer c.LogAuditRec(auditRec)
c.LogAudit("attempt")
if !c.App.SessionHasPermissionTo(*c.App.Session(), model.PERMISSION_MANAGE_SYSTEM) {
@@ -125,6 +133,8 @@ func removeLicense(c *Context, w http.ResponseWriter, r *http.Request) {
return
}
+ auditRec.Success()
c.LogAudit("success")
+
ReturnStatusOK(w)
}
diff --git a/api4/oauth.go b/api4/oauth.go
index 61a67b4d70..1fea587ce7 100644
--- a/api4/oauth.go
+++ b/api4/oauth.go
@@ -6,6 +6,7 @@ package api4
import (
"net/http"
+ "github.com/mattermost/mattermost-server/v5/audit"
"github.com/mattermost/mattermost-server/v5/model"
)
@@ -29,6 +30,11 @@ func createOAuthApp(c *Context, w http.ResponseWriter, r *http.Request) {
return
}
+ auditRec := c.MakeAuditRecord("createOAuthApp", audit.Fail)
+ defer c.LogAuditRec(auditRec)
+ auditRec.AddMeta("oauth_app_name", oauthApp.Name)
+ auditRec.AddMeta("oauth_app_desc", oauthApp.Description)
+
if !c.App.SessionHasPermissionTo(*c.App.Session(), model.PERMISSION_MANAGE_OAUTH) {
c.SetPermissionError(model.PERMISSION_MANAGE_OAUTH)
return
@@ -46,7 +52,11 @@ func createOAuthApp(c *Context, w http.ResponseWriter, r *http.Request) {
return
}
+ auditRec.Success()
+ auditRec.AddMeta("oauth_app_id", rapp.Id)
+ auditRec.AddMeta("client_id", rapp.Id)
c.LogAudit("client_id=" + rapp.Id)
+
w.WriteHeader(http.StatusCreated)
w.Write([]byte(rapp.ToJson()))
}
@@ -57,6 +67,11 @@ func updateOAuthApp(c *Context, w http.ResponseWriter, r *http.Request) {
return
}
+ auditRec := c.MakeAuditRecord("updateOAuthApp", audit.Fail)
+ defer c.LogAuditRec(auditRec)
+ auditRec.AddMeta("oauth_app_id", c.Params.AppId)
+ c.LogAudit("attempt")
+
if !c.App.SessionHasPermissionTo(*c.App.Session(), model.PERMISSION_MANAGE_OAUTH) {
c.SetPermissionError(model.PERMISSION_MANAGE_OAUTH)
return
@@ -67,6 +82,7 @@ func updateOAuthApp(c *Context, w http.ResponseWriter, r *http.Request) {
c.SetInvalidParam("oauth_app")
return
}
+ auditRec.AddMeta("oauth_app_name", oauthApp.Name)
// The app being updated in the payload must be the same one as indicated in the URL.
if oauthApp.Id != c.Params.AppId {
@@ -74,8 +90,6 @@ func updateOAuthApp(c *Context, w http.ResponseWriter, r *http.Request) {
return
}
- c.LogAudit("attempt")
-
oldOauthApp, err := c.App.GetOAuthApp(c.Params.AppId)
if err != nil {
c.Err = err
@@ -97,6 +111,7 @@ func updateOAuthApp(c *Context, w http.ResponseWriter, r *http.Request) {
return
}
+ auditRec.Success()
c.LogAudit("success")
w.Write([]byte(updatedOauthApp.ToJson()))
@@ -174,6 +189,9 @@ func deleteOAuthApp(c *Context, w http.ResponseWriter, r *http.Request) {
return
}
+ auditRec := c.MakeAuditRecord("deleteOAuthApp", audit.Fail)
+ defer c.LogAuditRec(auditRec)
+ auditRec.AddMeta("oauth_app_id", c.Params.AppId)
c.LogAudit("attempt")
if !c.App.SessionHasPermissionTo(*c.App.Session(), model.PERMISSION_MANAGE_OAUTH) {
@@ -186,6 +204,7 @@ func deleteOAuthApp(c *Context, w http.ResponseWriter, r *http.Request) {
c.Err = err
return
}
+ auditRec.AddMeta("oauth_app_name", oauthApp.Name)
if c.App.Session().UserId != oauthApp.CreatorId && !c.App.SessionHasPermissionTo(*c.App.Session(), model.PERMISSION_MANAGE_SYSTEM_WIDE_OAUTH) {
c.SetPermissionError(model.PERMISSION_MANAGE_SYSTEM_WIDE_OAUTH)
@@ -198,7 +217,9 @@ func deleteOAuthApp(c *Context, w http.ResponseWriter, r *http.Request) {
return
}
+ auditRec.Success()
c.LogAudit("success")
+
ReturnStatusOK(w)
}
@@ -208,6 +229,10 @@ func regenerateOAuthAppSecret(c *Context, w http.ResponseWriter, r *http.Request
return
}
+ auditRec := c.MakeAuditRecord("regenerateOAuthAppSecret", audit.Fail)
+ defer c.LogAuditRec(auditRec)
+ auditRec.AddMeta("oauth_app_id", c.Params.AppId)
+
if !c.App.SessionHasPermissionTo(*c.App.Session(), model.PERMISSION_MANAGE_OAUTH) {
c.SetPermissionError(model.PERMISSION_MANAGE_OAUTH)
return
@@ -218,6 +243,7 @@ func regenerateOAuthAppSecret(c *Context, w http.ResponseWriter, r *http.Request
c.Err = err
return
}
+ auditRec.AddMeta("oauth_app_name", oauthApp.Name)
if oauthApp.CreatorId != c.App.Session().UserId && !c.App.SessionHasPermissionTo(*c.App.Session(), model.PERMISSION_MANAGE_SYSTEM_WIDE_OAUTH) {
c.SetPermissionError(model.PERMISSION_MANAGE_SYSTEM_WIDE_OAUTH)
@@ -230,7 +256,9 @@ func regenerateOAuthAppSecret(c *Context, w http.ResponseWriter, r *http.Request
return
}
+ auditRec.Success()
c.LogAudit("success")
+
w.Write([]byte(oauthApp.ToJson()))
}
diff --git a/api4/plugin.go b/api4/plugin.go
index 46a4c46cb2..3e4bc08442 100644
--- a/api4/plugin.go
+++ b/api4/plugin.go
@@ -13,6 +13,7 @@ import (
"net/url"
"strconv"
+ "github.com/mattermost/mattermost-server/v5/audit"
"github.com/mattermost/mattermost-server/v5/mlog"
"github.com/mattermost/mattermost-server/v5/model"
)
@@ -46,6 +47,9 @@ func uploadPlugin(c *Context, w http.ResponseWriter, r *http.Request) {
return
}
+ auditRec := c.MakeAuditRecord("uploadPlugin", audit.Fail)
+ defer c.LogAuditRec(auditRec)
+
if !c.App.SessionHasPermissionTo(*c.App.Session(), model.PERMISSION_MANAGE_SYSTEM) {
c.SetPermissionError(model.PERMISSION_MANAGE_SYSTEM)
return
@@ -68,6 +72,7 @@ func uploadPlugin(c *Context, w http.ResponseWriter, r *http.Request) {
c.Err = model.NewAppError("uploadPlugin", "api.plugin.upload.array.app_error", nil, "", http.StatusBadRequest)
return
}
+ auditRec.AddMeta("filename", pluginArray[0].Filename)
file, err := pluginArray[0].Open()
if err != nil {
@@ -82,6 +87,7 @@ func uploadPlugin(c *Context, w http.ResponseWriter, r *http.Request) {
}
installPlugin(c, w, file, force)
+ auditRec.Success()
}
func installPluginFromUrl(c *Context, w http.ResponseWriter, r *http.Request) {
@@ -92,6 +98,9 @@ func installPluginFromUrl(c *Context, w http.ResponseWriter, r *http.Request) {
return
}
+ auditRec := c.MakeAuditRecord("installPluginFromUrl", audit.Fail)
+ defer c.LogAuditRec(auditRec)
+
if !c.App.SessionHasPermissionTo(*c.App.Session(), model.PERMISSION_MANAGE_SYSTEM) {
c.SetPermissionError(model.PERMISSION_MANAGE_SYSTEM)
return
@@ -99,6 +108,7 @@ func installPluginFromUrl(c *Context, w http.ResponseWriter, r *http.Request) {
force := r.URL.Query().Get("force") == "true"
downloadURL := r.URL.Query().Get("plugin_download_url")
+ auditRec.AddMeta("url", downloadURL)
pluginFileBytes, err := c.App.DownloadFromURL(downloadURL)
if err != nil {
@@ -107,6 +117,7 @@ func installPluginFromUrl(c *Context, w http.ResponseWriter, r *http.Request) {
}
installPlugin(c, w, bytes.NewReader(pluginFileBytes), force)
+ auditRec.Success()
}
func installMarketplacePlugin(c *Context, w http.ResponseWriter, r *http.Request) {
@@ -120,6 +131,9 @@ func installMarketplacePlugin(c *Context, w http.ResponseWriter, r *http.Request
return
}
+ auditRec := c.MakeAuditRecord("installMarketplacePlugin", audit.Fail)
+ defer c.LogAuditRec(auditRec)
+
if !c.App.SessionHasPermissionTo(*c.App.Session(), model.PERMISSION_MANAGE_SYSTEM) {
c.SetPermissionError(model.PERMISSION_MANAGE_SYSTEM)
return
@@ -130,6 +144,7 @@ func installMarketplacePlugin(c *Context, w http.ResponseWriter, r *http.Request
c.Err = model.NewAppError("installMarketplacePlugin", "app.plugin.marketplace_plugin_request.app_error", nil, err.Error(), http.StatusNotImplemented)
return
}
+ auditRec.AddMeta("plugin_id", pluginRequest.Id)
manifest, appErr := c.App.InstallMarketplacePlugin(pluginRequest)
if appErr != nil {
@@ -137,6 +152,10 @@ func installMarketplacePlugin(c *Context, w http.ResponseWriter, r *http.Request
return
}
+ auditRec.Success()
+ auditRec.AddMeta("plugin_name", manifest.Name)
+ auditRec.AddMeta("plugin_desc", manifest.Description)
+
w.WriteHeader(http.StatusCreated)
w.Write([]byte(manifest.ToJson()))
}
@@ -192,6 +211,10 @@ func removePlugin(c *Context, w http.ResponseWriter, r *http.Request) {
return
}
+ auditRec := c.MakeAuditRecord("removePlugin", audit.Fail)
+ defer c.LogAuditRec(auditRec)
+ auditRec.AddMeta("plugin_id", c.Params.PluginId)
+
if !c.App.SessionHasPermissionTo(*c.App.Session(), model.PERMISSION_MANAGE_SYSTEM) {
c.SetPermissionError(model.PERMISSION_MANAGE_SYSTEM)
return
@@ -203,6 +226,7 @@ func removePlugin(c *Context, w http.ResponseWriter, r *http.Request) {
return
}
+ auditRec.Success()
ReturnStatusOK(w)
}
@@ -280,6 +304,10 @@ func enablePlugin(c *Context, w http.ResponseWriter, r *http.Request) {
return
}
+ auditRec := c.MakeAuditRecord("enablePlugin", audit.Fail)
+ defer c.LogAuditRec(auditRec)
+ auditRec.AddMeta("plugin_id", c.Params.PluginId)
+
if !c.App.SessionHasPermissionTo(*c.App.Session(), model.PERMISSION_MANAGE_SYSTEM) {
c.SetPermissionError(model.PERMISSION_MANAGE_SYSTEM)
return
@@ -290,6 +318,7 @@ func enablePlugin(c *Context, w http.ResponseWriter, r *http.Request) {
return
}
+ auditRec.Success()
ReturnStatusOK(w)
}
@@ -304,6 +333,10 @@ func disablePlugin(c *Context, w http.ResponseWriter, r *http.Request) {
return
}
+ auditRec := c.MakeAuditRecord("disablePlugin", audit.Fail)
+ defer c.LogAuditRec(auditRec)
+ auditRec.AddMeta("plugin_id", c.Params.PluginId)
+
if !c.App.SessionHasPermissionTo(*c.App.Session(), model.PERMISSION_MANAGE_SYSTEM) {
c.SetPermissionError(model.PERMISSION_MANAGE_SYSTEM)
return
@@ -314,6 +347,7 @@ func disablePlugin(c *Context, w http.ResponseWriter, r *http.Request) {
return
}
+ auditRec.Success()
ReturnStatusOK(w)
}
diff --git a/api4/post.go b/api4/post.go
index e61b8852da..6bd465b759 100644
--- a/api4/post.go
+++ b/api4/post.go
@@ -10,6 +10,7 @@ import (
"time"
"github.com/mattermost/mattermost-server/v5/app"
+ "github.com/mattermost/mattermost-server/v5/audit"
"github.com/mattermost/mattermost-server/v5/mlog"
"github.com/mattermost/mattermost-server/v5/model"
)
@@ -43,6 +44,10 @@ func createPost(c *Context, w http.ResponseWriter, r *http.Request) {
post.UserId = c.App.Session().UserId
+ auditRec := c.MakeAuditRecord("createPost", audit.Fail)
+ defer c.LogAuditRecWithLevel(auditRec, app.RestContentLevel)
+ auditRec.AddMeta("channel_id", post.ChannelId)
+
hasPermission := false
if c.App.SessionHasPermissionToChannel(*c.App.Session(), post.ChannelId, model.PERMISSION_CREATE_POST) {
hasPermission = true
@@ -67,6 +72,8 @@ func createPost(c *Context, w http.ResponseWriter, r *http.Request) {
c.Err = err
return
}
+ auditRec.Success()
+ auditRec.AddMeta("post_id", rp.Id)
setOnline := r.URL.Query().Get("set_online")
setOnlineBool := true // By default, always set online.
@@ -362,11 +369,17 @@ func deletePost(c *Context, w http.ResponseWriter, r *http.Request) {
return
}
+ auditRec := c.MakeAuditRecord("deletePost", audit.Fail)
+ defer c.LogAuditRecWithLevel(auditRec, app.RestContentLevel)
+ auditRec.AddMeta("post_id", c.Params.PostId)
+
post, err := c.App.GetSinglePost(c.Params.PostId)
if err != nil {
c.SetPermissionError(model.PERMISSION_DELETE_POST)
return
}
+ auditRec.AddMeta("channel_id", post.ChannelId)
+ auditRec.AddMeta("creator_user_id", post.UserId)
if c.App.Session().UserId == post.UserId {
if !c.App.SessionHasPermissionToChannel(*c.App.Session(), post.ChannelId, model.PERMISSION_DELETE_POST) {
@@ -385,6 +398,7 @@ func deletePost(c *Context, w http.ResponseWriter, r *http.Request) {
return
}
+ auditRec.Success()
ReturnStatusOK(w)
}
@@ -516,6 +530,12 @@ func updatePost(c *Context, w http.ResponseWriter, r *http.Request) {
return
}
+ auditRec := c.MakeAuditRecord("updatePost", audit.Fail)
+ defer c.LogAuditRecWithLevel(auditRec, app.RestContentLevel)
+ auditRec.AddMeta("post_id", post.Id)
+ auditRec.AddMeta("channel_id", post.ChannelId)
+ auditRec.AddMeta("creator_user_id", post.UserId)
+
// The post being updated in the payload must be the same one as indicated in the URL.
if post.Id != c.Params.PostId {
c.SetInvalidParam("id")
@@ -551,6 +571,8 @@ func updatePost(c *Context, w http.ResponseWriter, r *http.Request) {
return
}
+ auditRec.Success()
+
w.Write([]byte(rpost.ToJson()))
}
@@ -567,6 +589,10 @@ func patchPost(c *Context, w http.ResponseWriter, r *http.Request) {
return
}
+ auditRec := c.MakeAuditRecord("patchPost", audit.Fail)
+ defer c.LogAuditRecWithLevel(auditRec, app.RestContentLevel)
+ auditRec.AddMeta("post_id", c.Params.PostId)
+
// Updating the file_ids of a post is not a supported operation and will be ignored
post.FileIds = nil
@@ -580,6 +606,8 @@ func patchPost(c *Context, w http.ResponseWriter, r *http.Request) {
c.SetPermissionError(model.PERMISSION_EDIT_POST)
return
}
+ auditRec.AddMeta("channel_id", originalPost.ChannelId)
+ auditRec.AddMeta("creator_user_id", originalPost.UserId)
if c.App.Session().UserId != originalPost.UserId {
if !c.App.SessionHasPermissionToChannelByPost(*c.App.Session(), c.Params.PostId, model.PERMISSION_EDIT_OTHERS_POSTS) {
@@ -594,6 +622,8 @@ func patchPost(c *Context, w http.ResponseWriter, r *http.Request) {
return
}
+ auditRec.Success()
+
w.Write([]byte(patchedPost.ToJson()))
}
@@ -625,6 +655,10 @@ func saveIsPinnedPost(c *Context, w http.ResponseWriter, r *http.Request, isPinn
return
}
+ auditRec := c.MakeAuditRecord("saveIsPinnedPost", audit.Fail)
+ defer c.LogAuditRecWithLevel(auditRec, app.RestContentLevel)
+ auditRec.AddMeta("post_id", c.Params.PostId)
+
if !c.App.SessionHasPermissionToChannelByPost(*c.App.Session(), c.Params.PostId, model.PERMISSION_READ_CHANNEL) {
c.SetPermissionError(model.PERMISSION_READ_CHANNEL)
return
@@ -642,6 +676,8 @@ func saveIsPinnedPost(c *Context, w http.ResponseWriter, r *http.Request, isPinn
c.Err = err
return
}
+ auditRec.AddMeta("channel_id", post.ChannelId)
+ auditRec.AddMeta("creator_user_id", post.UserId)
channel, err := c.App.GetChannel(post.ChannelId)
if err != nil {
@@ -666,6 +702,7 @@ func saveIsPinnedPost(c *Context, w http.ResponseWriter, r *http.Request, isPinn
return
}
+ auditRec.Success()
ReturnStatusOK(w)
}
diff --git a/api4/preference.go b/api4/preference.go
index 0623add030..a41742a5b5 100644
--- a/api4/preference.go
+++ b/api4/preference.go
@@ -6,6 +6,7 @@ package api4
import (
"net/http"
+ "github.com/mattermost/mattermost-server/v5/audit"
"github.com/mattermost/mattermost-server/v5/model"
)
@@ -83,6 +84,9 @@ func updatePreferences(c *Context, w http.ResponseWriter, r *http.Request) {
return
}
+ auditRec := c.MakeAuditRecord("updatePreferences", audit.Fail)
+ defer c.LogAuditRec(auditRec)
+
if !c.App.SessionHasPermissionToUser(*c.App.Session(), c.Params.UserId) {
c.SetPermissionError(model.PERMISSION_EDIT_OTHER_USERS)
return
@@ -118,6 +122,7 @@ func updatePreferences(c *Context, w http.ResponseWriter, r *http.Request) {
return
}
+ auditRec.Success()
ReturnStatusOK(w)
}
@@ -127,6 +132,9 @@ func deletePreferences(c *Context, w http.ResponseWriter, r *http.Request) {
return
}
+ auditRec := c.MakeAuditRecord("deletePreferences", audit.Fail)
+ defer c.LogAuditRec(auditRec)
+
if !c.App.SessionHasPermissionToUser(*c.App.Session(), c.Params.UserId) {
c.SetPermissionError(model.PERMISSION_EDIT_OTHER_USERS)
return
@@ -143,5 +151,6 @@ func deletePreferences(c *Context, w http.ResponseWriter, r *http.Request) {
return
}
+ auditRec.Success()
ReturnStatusOK(w)
}
diff --git a/api4/role.go b/api4/role.go
index 80421372ec..e8d888acfc 100644
--- a/api4/role.go
+++ b/api4/role.go
@@ -7,6 +7,7 @@ import (
"net/http"
"strings"
+ "github.com/mattermost/mattermost-server/v5/audit"
"github.com/mattermost/mattermost-server/v5/model"
)
@@ -90,11 +91,18 @@ func patchRole(c *Context, w http.ResponseWriter, r *http.Request) {
return
}
+ auditRec := c.MakeAuditRecord("patchRole", audit.Fail)
+ defer c.LogAuditRec(auditRec)
+ auditRec.AddMeta("role_id", c.Params.RoleId)
+
oldRole, err := c.App.GetRole(c.Params.RoleId)
if err != nil {
c.Err = err
return
}
+ auditRec.AddMeta("role_id", oldRole.Name)
+ auditRec.AddMeta("role_desc", oldRole.Description)
+ auditRec.AddMeta("role_display", oldRole.DisplayName)
if c.App.License() == nil && patch.Permissions != nil {
if oldRole.Name == "system_guest" || oldRole.Name == "team_guest" || oldRole.Name == "channel_guest" {
@@ -145,6 +153,8 @@ func patchRole(c *Context, w http.ResponseWriter, r *http.Request) {
return
}
+ auditRec.Success()
c.LogAudit("")
+
w.Write([]byte(role.ToJson()))
}
diff --git a/api4/saml.go b/api4/saml.go
index feed3e3fcb..6dc7a93585 100644
--- a/api4/saml.go
+++ b/api4/saml.go
@@ -9,6 +9,7 @@ import (
"mime/multipart"
"net/http"
+ "github.com/mattermost/mattermost-server/v5/audit"
"github.com/mattermost/mattermost-server/v5/model"
)
@@ -72,10 +73,15 @@ func addSamlPublicCertificate(c *Context, w http.ResponseWriter, r *http.Request
return
}
+ auditRec := c.MakeAuditRecord("addSamlPublicCertificate", audit.Fail)
+ defer c.LogAuditRec(auditRec)
+ auditRec.AddMeta("filename", fileData.Filename)
+
if err := c.App.AddSamlPublicCertificate(fileData); err != nil {
c.Err = err
return
}
+ auditRec.Success()
ReturnStatusOK(w)
}
@@ -91,10 +97,15 @@ func addSamlPrivateCertificate(c *Context, w http.ResponseWriter, r *http.Reques
return
}
+ auditRec := c.MakeAuditRecord("addSamlPrivateCertificate", audit.Fail)
+ defer c.LogAuditRec(auditRec)
+ auditRec.AddMeta("filename", fileData.Filename)
+
if err := c.App.AddSamlPrivateCertificate(fileData); err != nil {
c.Err = err
return
}
+ auditRec.Success()
ReturnStatusOK(w)
}
@@ -115,6 +126,10 @@ func addSamlIdpCertificate(c *Context, w http.ResponseWriter, r *http.Request) {
return
}
+ auditRec := c.MakeAuditRecord("addSamlIdpCertificate", audit.Fail)
+ defer c.LogAuditRec(auditRec)
+ auditRec.AddMeta("type", d)
+
if d == "application/x-pem-file" {
body, err := ioutil.ReadAll(r.Body)
if err != nil {
@@ -132,6 +147,7 @@ func addSamlIdpCertificate(c *Context, w http.ResponseWriter, r *http.Request) {
c.Err = err
return
}
+ auditRec.AddMeta("filename", fileData.Filename)
if err := c.App.AddSamlIdpCertificate(fileData); err != nil {
c.Err = err
@@ -142,6 +158,7 @@ func addSamlIdpCertificate(c *Context, w http.ResponseWriter, r *http.Request) {
return
}
+ auditRec.Success()
ReturnStatusOK(w)
}
@@ -151,11 +168,15 @@ func removeSamlPublicCertificate(c *Context, w http.ResponseWriter, r *http.Requ
return
}
+ auditRec := c.MakeAuditRecord("removeSamlPublicCertificate", audit.Fail)
+ defer c.LogAuditRec(auditRec)
+
if err := c.App.RemoveSamlPublicCertificate(); err != nil {
c.Err = err
return
}
+ auditRec.Success()
ReturnStatusOK(w)
}
@@ -165,11 +186,15 @@ func removeSamlPrivateCertificate(c *Context, w http.ResponseWriter, r *http.Req
return
}
+ auditRec := c.MakeAuditRecord("removeSamlPrivateCertificate", audit.Fail)
+ defer c.LogAuditRec(auditRec)
+
if err := c.App.RemoveSamlPrivateCertificate(); err != nil {
c.Err = err
return
}
+ auditRec.Success()
ReturnStatusOK(w)
}
@@ -179,11 +204,15 @@ func removeSamlIdpCertificate(c *Context, w http.ResponseWriter, r *http.Request
return
}
+ auditRec := c.MakeAuditRecord("removeSamlIdpCertificate", audit.Fail)
+ defer c.LogAuditRec(auditRec)
+
if err := c.App.RemoveSamlIdpCertificate(); err != nil {
c.Err = err
return
}
+ auditRec.Success()
ReturnStatusOK(w)
}
diff --git a/api4/scheme.go b/api4/scheme.go
index b3de3b7f4c..4df56a1744 100644
--- a/api4/scheme.go
+++ b/api4/scheme.go
@@ -6,6 +6,7 @@ package api4
import (
"net/http"
+ "github.com/mattermost/mattermost-server/v5/audit"
"github.com/mattermost/mattermost-server/v5/model"
)
@@ -26,6 +27,12 @@ func createScheme(c *Context, w http.ResponseWriter, r *http.Request) {
return
}
+ auditRec := c.MakeAuditRecord("createScheme", audit.Fail)
+ defer c.LogAuditRec(auditRec)
+ auditRec.AddMeta("scheme_name", scheme.Name)
+ auditRec.AddMeta("scheme_display", scheme.DisplayName)
+ auditRec.AddMeta("scheme_desc", scheme.Description)
+
if c.App.License() == nil || !*c.App.License().Features.CustomPermissionsSchemes {
c.Err = model.NewAppError("Api4.CreateScheme", "api.scheme.create_scheme.license.error", nil, "", http.StatusNotImplemented)
return
@@ -42,6 +49,9 @@ func createScheme(c *Context, w http.ResponseWriter, r *http.Request) {
return
}
+ auditRec.Success()
+ auditRec.AddMeta("scheme_id", scheme.Id)
+
w.WriteHeader(http.StatusCreated)
w.Write([]byte(scheme.ToJson()))
}
@@ -161,6 +171,13 @@ func patchScheme(c *Context, w http.ResponseWriter, r *http.Request) {
return
}
+ auditRec := c.MakeAuditRecord("patchScheme", audit.Fail)
+ defer c.LogAuditRec(auditRec)
+ auditRec.AddMeta("scheme_id", c.Params.SchemeId)
+ auditRec.AddMeta("new_scheme_name", patch.Name)
+ auditRec.AddMeta("new_scheme_display", patch.DisplayName)
+ auditRec.AddMeta("new_scheme_desc", patch.Description)
+
if c.App.License() == nil || !*c.App.License().Features.CustomPermissionsSchemes {
c.Err = model.NewAppError("Api4.PatchScheme", "api.scheme.patch_scheme.license.error", nil, "", http.StatusNotImplemented)
return
@@ -171,6 +188,9 @@ func patchScheme(c *Context, w http.ResponseWriter, r *http.Request) {
c.Err = err
return
}
+ auditRec.AddMeta("old_scheme_name", scheme.Name)
+ auditRec.AddMeta("old_scheme_display", scheme.DisplayName)
+ auditRec.AddMeta("old_scheme_desc", scheme.Description)
if !c.App.SessionHasPermissionTo(*c.App.Session(), model.PERMISSION_MANAGE_SYSTEM) {
c.SetPermissionError(model.PERMISSION_MANAGE_SYSTEM)
@@ -183,7 +203,9 @@ func patchScheme(c *Context, w http.ResponseWriter, r *http.Request) {
return
}
+ auditRec.Success()
c.LogAudit("")
+
w.Write([]byte(scheme.ToJson()))
}
@@ -193,6 +215,10 @@ func deleteScheme(c *Context, w http.ResponseWriter, r *http.Request) {
return
}
+ auditRec := c.MakeAuditRecord("deleteScheme", audit.Fail)
+ defer c.LogAuditRec(auditRec)
+ auditRec.AddMeta("scheme_id", c.Params.SchemeId)
+
if c.App.License() == nil || !*c.App.License().Features.CustomPermissionsSchemes {
c.Err = model.NewAppError("Api4.DeleteScheme", "api.scheme.delete_scheme.license.error", nil, "", http.StatusNotImplemented)
return
@@ -208,5 +234,6 @@ func deleteScheme(c *Context, w http.ResponseWriter, r *http.Request) {
return
}
+ auditRec.Success()
ReturnStatusOK(w)
}
diff --git a/api4/system.go b/api4/system.go
index e1dfa3c109..883e3ef7ae 100644
--- a/api4/system.go
+++ b/api4/system.go
@@ -11,6 +11,7 @@ import (
"strconv"
"time"
+ "github.com/mattermost/mattermost-server/v5/audit"
"github.com/mattermost/mattermost-server/v5/mlog"
"github.com/mattermost/mattermost-server/v5/model"
"github.com/mattermost/mattermost-server/v5/services/cache/lru"
@@ -203,6 +204,9 @@ func databaseRecycle(c *Context, w http.ResponseWriter, r *http.Request) {
return
}
+ auditRec := c.MakeAuditRecord("databaseRecycle", audit.Fail)
+ defer c.LogAuditRec(auditRec)
+
if *c.App.Config().ExperimentalSettings.RestrictSystemAdmin {
c.Err = model.NewAppError("databaseRecycle", "api.restricted_system_admin", nil, "", http.StatusForbidden)
return
@@ -210,6 +214,7 @@ func databaseRecycle(c *Context, w http.ResponseWriter, r *http.Request) {
c.App.RecycleDatabaseConnection()
+ auditRec.Success()
ReturnStatusOK(w)
}
@@ -219,6 +224,9 @@ func invalidateCaches(c *Context, w http.ResponseWriter, r *http.Request) {
return
}
+ auditRec := c.MakeAuditRecord("invalidateCaches", audit.Fail)
+ defer c.LogAuditRec(auditRec)
+
if *c.App.Config().ExperimentalSettings.RestrictSystemAdmin {
c.Err = model.NewAppError("invalidateCaches", "api.restricted_system_admin", nil, "", http.StatusForbidden)
return
@@ -230,6 +238,8 @@ func invalidateCaches(c *Context, w http.ResponseWriter, r *http.Request) {
return
}
+ auditRec.Success()
+
w.Header().Set("Cache-Control", "no-cache, no-store, must-revalidate")
ReturnStatusOK(w)
}
@@ -481,8 +491,14 @@ func setServerBusy(c *Context, w http.ResponseWriter, r *http.Request) {
return
}
+ auditRec := c.MakeAuditRecord("setServerBusy", audit.Fail)
+ defer c.LogAuditRec(auditRec)
+ auditRec.AddMeta("seconds", i)
+
c.App.Srv().Busy.Set(time.Second * time.Duration(i))
mlog.Warn("server busy state activated - non-critical services disabled", mlog.Int64("seconds", i))
+
+ auditRec.Success()
ReturnStatusOK(w)
}
@@ -491,8 +507,14 @@ func clearServerBusy(c *Context, w http.ResponseWriter, r *http.Request) {
c.SetPermissionError(model.PERMISSION_MANAGE_SYSTEM)
return
}
+
+ auditRec := c.MakeAuditRecord("clearServerBusy", audit.Fail)
+ defer c.LogAuditRec(auditRec)
+
c.App.Srv().Busy.Clear()
mlog.Info("server busy state cleared - non-critical services enabled")
+
+ auditRec.Success()
ReturnStatusOK(w)
}
diff --git a/api4/team.go b/api4/team.go
index d15cb25ea7..fab1b234cc 100644
--- a/api4/team.go
+++ b/api4/team.go
@@ -15,6 +15,7 @@ import (
"strconv"
"strings"
+ "github.com/mattermost/mattermost-server/v5/audit"
"github.com/mattermost/mattermost-server/v5/model"
)
@@ -81,6 +82,11 @@ func createTeam(c *Context, w http.ResponseWriter, r *http.Request) {
}
team.Email = strings.ToLower(team.Email)
+ auditRec := c.MakeAuditRecord("createTeam", audit.Fail)
+ defer c.LogAuditRec(auditRec)
+ auditRec.AddMeta("team_name", team.Name)
+ auditRec.AddMeta("team_display", team.DisplayName)
+
if !c.App.SessionHasPermissionTo(*c.App.Session(), model.PERMISSION_CREATE_TEAM) {
c.Err = model.NewAppError("createTeam", "api.team.is_team_creation_allowed.disabled.app_error", nil, "", http.StatusForbidden)
return
@@ -94,6 +100,9 @@ func createTeam(c *Context, w http.ResponseWriter, r *http.Request) {
// Don't sanitize the team here since the user will be a team admin and their session won't reflect that yet
+ auditRec.Success()
+ auditRec.AddMeta("team_id", rteam.Id)
+
w.WriteHeader(http.StatusCreated)
w.Write([]byte(rteam.ToJson()))
}
@@ -160,6 +169,12 @@ func updateTeam(c *Context, w http.ResponseWriter, r *http.Request) {
return
}
+ auditRec := c.MakeAuditRecord("updateTeam", audit.Fail)
+ defer c.LogAuditRec(auditRec)
+ auditRec.AddMeta("team_id", c.Params.TeamId)
+ auditRec.AddMeta("team_name", team.Name)
+ auditRec.AddMeta("team_display", team.DisplayName)
+
if !c.App.SessionHasPermissionToTeam(*c.App.Session(), c.Params.TeamId, model.PERMISSION_MANAGE_TEAM) {
c.SetPermissionError(model.PERMISSION_MANAGE_TEAM)
return
@@ -171,6 +186,8 @@ func updateTeam(c *Context, w http.ResponseWriter, r *http.Request) {
return
}
+ auditRec.Success()
+
c.App.SanitizeTeam(*c.App.Session(), updatedTeam)
w.Write([]byte(updatedTeam.ToJson()))
}
@@ -188,6 +205,10 @@ func patchTeam(c *Context, w http.ResponseWriter, r *http.Request) {
return
}
+ auditRec := c.MakeAuditRecord("patchTeam", audit.Fail)
+ defer c.LogAuditRec(auditRec)
+ auditRec.AddMeta("team_id", c.Params.TeamId)
+
if !c.App.SessionHasPermissionToTeam(*c.App.Session(), c.Params.TeamId, model.PERMISSION_MANAGE_TEAM) {
c.SetPermissionError(model.PERMISSION_MANAGE_TEAM)
return
@@ -202,7 +223,11 @@ func patchTeam(c *Context, w http.ResponseWriter, r *http.Request) {
c.App.SanitizeTeam(*c.App.Session(), patchedTeam)
+ auditRec.Success()
+ auditRec.AddMeta("team_name", patchedTeam.Name)
+ auditRec.AddMeta("team_display", patchedTeam.DisplayName)
c.LogAudit("")
+
w.Write([]byte(patchedTeam.ToJson()))
}
@@ -217,6 +242,10 @@ func regenerateTeamInviteId(c *Context, w http.ResponseWriter, r *http.Request)
return
}
+ auditRec := c.MakeAuditRecord("regenerateTeamInviteId", audit.Fail)
+ defer c.LogAuditRec(auditRec)
+ auditRec.AddMeta("team_id", c.Params.TeamId)
+
patchedTeam, err := c.App.RegenerateTeamInviteId(c.Params.TeamId)
if err != nil {
c.Err = err
@@ -225,7 +254,11 @@ func regenerateTeamInviteId(c *Context, w http.ResponseWriter, r *http.Request)
c.App.SanitizeTeam(*c.App.Session(), patchedTeam)
+ auditRec.Success()
+ auditRec.AddMeta("team_name", patchedTeam.Name)
+ auditRec.AddMeta("team_display", patchedTeam.DisplayName)
c.LogAudit("")
+
w.Write([]byte(patchedTeam.ToJson()))
}
@@ -240,6 +273,10 @@ func deleteTeam(c *Context, w http.ResponseWriter, r *http.Request) {
return
}
+ auditRec := c.MakeAuditRecord("deleteTeam", audit.Fail)
+ defer c.LogAuditRec(auditRec)
+ auditRec.AddMeta("team_id", c.Params.TeamId)
+
var err *model.AppError
if c.Params.Permanent && *c.App.Config().ServiceSettings.EnableAPITeamDeletion {
err = c.App.PermanentDeleteTeamId(c.Params.TeamId)
@@ -252,6 +289,7 @@ func deleteTeam(c *Context, w http.ResponseWriter, r *http.Request) {
return
}
+ auditRec.Success()
ReturnStatusOK(w)
}
@@ -438,6 +476,11 @@ func addTeamMember(c *Context, w http.ResponseWriter, r *http.Request) {
return
}
+ auditRec := c.MakeAuditRecord("addTeamMember", audit.Fail)
+ defer c.LogAuditRec(auditRec)
+ auditRec.AddMeta("team_id", c.Params.TeamId)
+ auditRec.AddMeta("add_user_id", member.UserId)
+
if member.UserId == c.App.Session().UserId {
var team *model.Team
team, err = c.App.GetTeam(member.TeamId)
@@ -466,6 +509,8 @@ func addTeamMember(c *Context, w http.ResponseWriter, r *http.Request) {
c.Err = err
return
}
+ auditRec.AddMeta("team_name", team.Name)
+ auditRec.AddMeta("team_display", team.DisplayName)
if team.IsGroupConstrained() {
nonMembers, err := c.App.FilterNonGroupTeamMembers([]string{member.UserId}, team)
@@ -490,6 +535,8 @@ func addTeamMember(c *Context, w http.ResponseWriter, r *http.Request) {
return
}
+ auditRec.Success()
+
w.WriteHeader(http.StatusCreated)
w.Write([]byte(member.ToJson()))
}
@@ -501,6 +548,10 @@ func addUserToTeamFromInvite(c *Context, w http.ResponseWriter, r *http.Request)
var member *model.TeamMember
var err *model.AppError
+ auditRec := c.MakeAuditRecord("addUserToTeamFromInvite", audit.Fail)
+ defer c.LogAuditRec(auditRec)
+ auditRec.AddMeta("invite_id", inviteId)
+
if len(tokenId) > 0 {
member, err = c.App.AddTeamMemberByToken(c.App.Session().UserId, tokenId)
} else if len(inviteId) > 0 {
@@ -519,6 +570,11 @@ func addUserToTeamFromInvite(c *Context, w http.ResponseWriter, r *http.Request)
return
}
+ auditRec.Success()
+ if member != nil {
+ auditRec.AddMeta("add_user_id", member.UserId)
+ }
+
w.WriteHeader(http.StatusCreated)
w.Write([]byte(member.ToJson()))
}
@@ -544,16 +600,24 @@ func addTeamMembers(c *Context, w http.ResponseWriter, r *http.Request) {
return
}
+ auditRec := c.MakeAuditRecord("addTeamMembers", audit.Fail)
+ defer c.LogAuditRec(auditRec)
+ auditRec.AddMeta("team_id", c.Params.TeamId)
+ auditRec.AddMeta("count", len(members))
+
var memberIDs []string
for _, member := range members {
memberIDs = append(memberIDs, member.UserId)
}
+ auditRec.AddMeta("user_ids", memberIDs)
team, err := c.App.GetTeam(c.Params.TeamId)
if err != nil {
c.Err = err
return
}
+ auditRec.AddMeta("team_name", team.Name)
+ auditRec.AddMeta("team_display", team.DisplayName)
if team.IsGroupConstrained() {
nonMembers, err := c.App.FilterNonGroupTeamMembers(memberIDs, team)
@@ -593,11 +657,22 @@ func addTeamMembers(c *Context, w http.ResponseWriter, r *http.Request) {
membersWithErrors, err := c.App.AddTeamMembers(c.Params.TeamId, userIds, c.App.Session().UserId, graceful)
+ if membersWithErrors != nil {
+ errList := make([]string, 0, len(membersWithErrors))
+ for _, m := range membersWithErrors {
+ if m.Error != nil {
+ errList = append(errList, model.TeamMemberWithErrorToString(m))
+ }
+ }
+ auditRec.AddMeta("errors", errList)
+ }
if err != nil {
c.Err = err
return
}
+ auditRec.Success()
+
w.WriteHeader(http.StatusCreated)
if graceful {
@@ -615,6 +690,10 @@ func removeTeamMember(c *Context, w http.ResponseWriter, r *http.Request) {
return
}
+ auditRec := c.MakeAuditRecord("removeTeamMember", audit.Fail)
+ defer c.LogAuditRec(auditRec)
+ auditRec.AddMeta("team_id", c.Params.TeamId)
+
if c.App.Session().UserId != c.Params.UserId {
if !c.App.SessionHasPermissionToTeam(*c.App.Session(), c.Params.TeamId, model.PERMISSION_REMOVE_USER_FROM_TEAM) {
c.SetPermissionError(model.PERMISSION_REMOVE_USER_FROM_TEAM)
@@ -627,12 +706,15 @@ func removeTeamMember(c *Context, w http.ResponseWriter, r *http.Request) {
c.Err = err
return
}
+ auditRec.AddMeta("team_name", team.Name)
+ auditRec.AddMeta("team_display", team.DisplayName)
user, err := c.App.GetUser(c.Params.UserId)
if err != nil {
c.Err = err
return
}
+ auditRec.AddMeta("remove_user_id", user.Id)
if team.IsGroupConstrained() && (c.Params.UserId != c.App.Session().UserId) && !user.IsBot {
c.Err = model.NewAppError("removeTeamMember", "api.team.remove_member.group_constrained.app_error", nil, "", http.StatusBadRequest)
@@ -644,6 +726,7 @@ func removeTeamMember(c *Context, w http.ResponseWriter, r *http.Request) {
return
}
+ auditRec.Success()
ReturnStatusOK(w)
}
@@ -712,6 +795,11 @@ func updateTeamMemberRoles(c *Context, w http.ResponseWriter, r *http.Request) {
return
}
+ auditRec := c.MakeAuditRecord("updateTeamMemberRoles", audit.Fail)
+ defer c.LogAuditRec(auditRec)
+ auditRec.AddMeta("team_id", c.Params.TeamId)
+ auditRec.AddMeta("update_user_id", c.Params.UserId)
+
if !c.App.SessionHasPermissionToTeam(*c.App.Session(), c.Params.TeamId, model.PERMISSION_MANAGE_TEAM_ROLES) {
c.SetPermissionError(model.PERMISSION_MANAGE_TEAM_ROLES)
return
@@ -722,6 +810,7 @@ func updateTeamMemberRoles(c *Context, w http.ResponseWriter, r *http.Request) {
return
}
+ auditRec.Success()
ReturnStatusOK(w)
}
@@ -737,6 +826,14 @@ func updateTeamMemberSchemeRoles(c *Context, w http.ResponseWriter, r *http.Requ
return
}
+ auditRec := c.MakeAuditRecord("updateTeamMemberSchemeRoles", audit.Fail)
+ defer c.LogAuditRec(auditRec)
+ auditRec.AddMeta("team_id", c.Params.TeamId)
+ auditRec.AddMeta("update_user_id", c.Params.UserId)
+ auditRec.AddMeta("new_scheme_admin", schemeRoles.SchemeAdmin)
+ auditRec.AddMeta("new_scheme_user", schemeRoles.SchemeUser)
+ auditRec.AddMeta("new_scheme_guest", schemeRoles.SchemeGuest)
+
if !c.App.SessionHasPermissionToTeam(*c.App.Session(), c.Params.TeamId, model.PERMISSION_MANAGE_TEAM_ROLES) {
c.SetPermissionError(model.PERMISSION_MANAGE_TEAM_ROLES)
return
@@ -747,6 +844,7 @@ func updateTeamMemberSchemeRoles(c *Context, w http.ResponseWriter, r *http.Requ
return
}
+ auditRec.Success()
ReturnStatusOK(w)
}
@@ -925,6 +1023,10 @@ func importTeam(c *Context, w http.ResponseWriter, r *http.Request) {
return
}
+ auditRec := c.MakeAuditRecord("importTeam", audit.Fail)
+ defer c.LogAuditRec(auditRec)
+ auditRec.AddMeta("team_id", c.Params.TeamId)
+
fileInfo := fileInfoArray[0]
fileData, err := fileInfo.Open()
@@ -933,6 +1035,9 @@ func importTeam(c *Context, w http.ResponseWriter, r *http.Request) {
return
}
defer fileData.Close()
+ auditRec.AddMeta("filename", fileInfo.Filename)
+ auditRec.AddMeta("filesize", fileSize)
+ auditRec.AddMeta("from", importFrom)
var log *bytes.Buffer
switch importFrom {
@@ -949,6 +1054,7 @@ func importTeam(c *Context, w http.ResponseWriter, r *http.Request) {
if c.Err != nil {
w.WriteHeader(c.Err.StatusCode)
}
+ auditRec.Success()
w.Write([]byte(model.MapToJson(data)))
}
@@ -980,8 +1086,23 @@ func inviteUsersToTeam(c *Context, w http.ResponseWriter, r *http.Request) {
return
}
+ auditRec := c.MakeAuditRecord("inviteUsersToTeam", audit.Fail)
+ defer c.LogAuditRec(auditRec)
+ auditRec.AddMeta("team_id", c.Params.TeamId)
+ auditRec.AddMeta("count", len(emailList))
+ auditRec.AddMeta("emails", emailList)
+
if graceful {
invitesWithError, err := c.App.InviteNewUsersToTeamGracefully(emailList, c.Params.TeamId, c.App.Session().UserId)
+ if invitesWithError != nil {
+ errList := make([]string, 0, len(invitesWithError))
+ for _, inv := range invitesWithError {
+ if inv.Error != nil {
+ errList = append(errList, model.EmailInviteWithErrorToString(inv))
+ }
+ }
+ auditRec.AddMeta("errors", errList)
+ }
if err != nil {
c.Err = err
return
@@ -996,6 +1117,7 @@ func inviteUsersToTeam(c *Context, w http.ResponseWriter, r *http.Request) {
}
ReturnStatusOK(w)
}
+ auditRec.Success()
}
func inviteGuestsToChannels(c *Context, w http.ResponseWriter, r *http.Request) {
@@ -1015,6 +1137,10 @@ func inviteGuestsToChannels(c *Context, w http.ResponseWriter, r *http.Request)
return
}
+ auditRec := c.MakeAuditRecord("inviteGuestsToChannels", audit.Fail)
+ defer c.LogAuditRec(auditRec)
+ auditRec.AddMeta("team_id", c.Params.TeamId)
+
if !c.App.SessionHasPermissionToTeam(*c.App.Session(), c.Params.TeamId, model.PERMISSION_INVITE_GUEST) {
c.SetPermissionError(model.PERMISSION_INVITE_GUEST)
return
@@ -1028,10 +1154,19 @@ func inviteGuestsToChannels(c *Context, w http.ResponseWriter, r *http.Request)
c.Err = err
return
}
+ auditRec.AddMeta("email_count", len(guestsInvite.Emails))
+ auditRec.AddMeta("emails", guestsInvite.Emails)
+ auditRec.AddMeta("channel_count", len(guestsInvite.Channels))
+ auditRec.AddMeta("channels", guestsInvite.Channels)
if graceful {
invitesWithError, err := c.App.InviteGuestsToChannelsGracefully(c.Params.TeamId, guestsInvite, c.App.Session().UserId)
if err != nil {
+ errList := make([]string, 0, len(invitesWithError))
+ for _, inv := range invitesWithError {
+ errList = append(errList, model.EmailInviteWithErrorToString(inv))
+ }
+ auditRec.AddMeta("errors", errList)
c.Err = err
return
}
@@ -1045,6 +1180,7 @@ func inviteGuestsToChannels(c *Context, w http.ResponseWriter, r *http.Request)
}
ReturnStatusOK(w)
}
+ auditRec.Success()
}
func getInviteInfo(c *Context, w http.ResponseWriter, r *http.Request) {
@@ -1078,11 +1214,15 @@ func invalidateAllEmailInvites(c *Context, w http.ResponseWriter, r *http.Reques
return
}
+ auditRec := c.MakeAuditRecord("invalidateAllEmailInvites", audit.Fail)
+ defer c.LogAuditRec(auditRec)
+
if err := c.App.InvalidateAllEmailInvites(); err != nil {
c.Err = err
return
}
+ auditRec.Success()
ReturnStatusOK(w)
}
@@ -1131,6 +1271,10 @@ func setTeamIcon(c *Context, w http.ResponseWriter, r *http.Request) {
return
}
+ auditRec := c.MakeAuditRecord("setTeamIcon", audit.Fail)
+ defer c.LogAuditRec(auditRec)
+ auditRec.AddMeta("team_id", c.Params.TeamId)
+
if !c.App.SessionHasPermissionToTeam(*c.App.Session(), c.Params.TeamId, model.PERMISSION_MANAGE_TEAM) {
c.SetPermissionError(model.PERMISSION_MANAGE_TEAM)
return
@@ -1166,7 +1310,9 @@ func setTeamIcon(c *Context, w http.ResponseWriter, r *http.Request) {
return
}
+ auditRec.Success()
c.LogAudit("")
+
ReturnStatusOK(w)
}
@@ -1176,6 +1322,10 @@ func removeTeamIcon(c *Context, w http.ResponseWriter, r *http.Request) {
return
}
+ auditRec := c.MakeAuditRecord("removeTeamIcon", audit.Fail)
+ defer c.LogAuditRec(auditRec)
+ auditRec.AddMeta("team_id", c.Params.TeamId)
+
if !c.App.SessionHasPermissionToTeam(*c.App.Session(), c.Params.TeamId, model.PERMISSION_MANAGE_TEAM) {
c.SetPermissionError(model.PERMISSION_MANAGE_TEAM)
return
@@ -1186,7 +1336,9 @@ func removeTeamIcon(c *Context, w http.ResponseWriter, r *http.Request) {
return
}
+ auditRec.Success()
c.LogAudit("")
+
ReturnStatusOK(w)
}
@@ -1202,6 +1354,10 @@ func updateTeamScheme(c *Context, w http.ResponseWriter, r *http.Request) {
return
}
+ auditRec := c.MakeAuditRecord("updateTeamScheme", audit.Fail)
+ defer c.LogAuditRec(auditRec)
+ auditRec.AddMeta("team_id", c.Params.TeamId)
+
if c.App.License() == nil {
c.Err = model.NewAppError("Api4.UpdateTeamScheme", "api.team.update_team_scheme.license.error", nil, "", http.StatusNotImplemented)
return
@@ -1218,6 +1374,9 @@ func updateTeamScheme(c *Context, w http.ResponseWriter, r *http.Request) {
c.Err = err
return
}
+ auditRec.AddMeta("scheme_id", scheme.Id)
+ auditRec.AddMeta("scheme_name", scheme.Name)
+ auditRec.AddMeta("scheme_display", scheme.DisplayName)
if scheme.Scope != model.SCHEME_SCOPE_TEAM {
c.Err = model.NewAppError("Api4.UpdateTeamScheme", "api.team.update_team_scheme.scheme_scope.error", nil, "", http.StatusBadRequest)
@@ -1230,6 +1389,8 @@ func updateTeamScheme(c *Context, w http.ResponseWriter, r *http.Request) {
c.Err = err
return
}
+ auditRec.AddMeta("team_name", team.Name)
+ auditRec.AddMeta("team_display", team.DisplayName)
team.SchemeId = schemeID
@@ -1239,6 +1400,7 @@ func updateTeamScheme(c *Context, w http.ResponseWriter, r *http.Request) {
return
}
+ auditRec.Success()
ReturnStatusOK(w)
}
diff --git a/api4/terms_of_service.go b/api4/terms_of_service.go
index f8a95239e7..0cd71e72da 100644
--- a/api4/terms_of_service.go
+++ b/api4/terms_of_service.go
@@ -7,6 +7,7 @@ import (
"net/http"
"github.com/mattermost/mattermost-server/v5/app"
+ "github.com/mattermost/mattermost-server/v5/audit"
"github.com/mattermost/mattermost-server/v5/model"
)
@@ -36,6 +37,9 @@ func createTermsOfService(c *Context, w http.ResponseWriter, r *http.Request) {
return
}
+ auditRec := c.MakeAuditRecord("createTermsOfService", audit.Fail)
+ defer c.LogAuditRec(auditRec)
+
props := model.MapFromJson(r.Body)
text := props["text"]
userId := c.App.Session().UserId
@@ -62,4 +66,5 @@ func createTermsOfService(c *Context, w http.ResponseWriter, r *http.Request) {
} else {
w.Write([]byte(oldTermsOfService.ToJson()))
}
+ auditRec.Success()
}
diff --git a/api4/user.go b/api4/user.go
index aa2a6c56bb..7af8a82cc0 100644
--- a/api4/user.go
+++ b/api4/user.go
@@ -14,6 +14,7 @@ import (
"time"
"github.com/mattermost/mattermost-server/v5/app"
+ "github.com/mattermost/mattermost-server/v5/audit"
"github.com/mattermost/mattermost-server/v5/mlog"
"github.com/mattermost/mattermost-server/v5/model"
"github.com/mattermost/mattermost-server/v5/store"
@@ -92,6 +93,11 @@ func createUser(c *Context, w http.ResponseWriter, r *http.Request) {
tokenId := r.URL.Query().Get("t")
inviteId := r.URL.Query().Get("iid")
+ auditRec := c.MakeAuditRecord("createUser", audit.Fail)
+ defer c.LogAuditRec(auditRec)
+ auditRec.AddMeta("invite_id", inviteId)
+ auditRec.AddMeta("create_username", user.Username)
+
// No permission check required
var ruser *model.User
@@ -103,6 +109,7 @@ func createUser(c *Context, w http.ResponseWriter, r *http.Request) {
c.Err = model.NewAppError("CreateUserWithToken", "api.user.create_user.signup_link_invalid.app_error", nil, err.Error(), http.StatusBadRequest)
return
}
+ auditRec.AddMeta("token_type", token.Type)
if token.Type == app.TOKEN_TYPE_GUEST_INVITATION {
if c.App.License() == nil {
@@ -119,6 +126,7 @@ func createUser(c *Context, w http.ResponseWriter, r *http.Request) {
ruser, err = c.App.CreateUserWithInviteId(user, inviteId)
} else if c.IsSystemAdmin() {
ruser, err = c.App.CreateUserAsAdmin(user)
+ auditRec.AddMeta("admin", true)
} else {
ruser, err = c.App.CreateUserFromSignup(user)
}
@@ -128,6 +136,9 @@ func createUser(c *Context, w http.ResponseWriter, r *http.Request) {
return
}
+ auditRec.Success()
+ auditRec.AddMeta("create_user_id", ruser.Id)
+
w.WriteHeader(http.StatusCreated)
w.Write([]byte(ruser.ToJson()))
}
@@ -412,13 +423,22 @@ func setProfileImage(c *Context, w http.ResponseWriter, r *http.Request) {
return
}
+ auditRec := c.MakeAuditRecord("setProfileImage", audit.Fail)
+ defer c.LogAuditRec(auditRec)
+ auditRec.AddMeta("set_user_id", c.Params.UserId)
+ if imageArray[0] != nil {
+ auditRec.AddMeta("filename", imageArray[0].Filename)
+ }
+
imageData := imageArray[0]
if err := c.App.SetProfileImage(c.Params.UserId, imageData); err != nil {
c.Err = err
return
}
+ auditRec.Success()
c.LogAudit("")
+
ReturnStatusOK(w)
}
@@ -438,18 +458,25 @@ func setDefaultProfileImage(c *Context, w http.ResponseWriter, r *http.Request)
return
}
+ auditRec := c.MakeAuditRecord("setDefaultProfileImage", audit.Fail)
+ defer c.LogAuditRec(auditRec)
+ auditRec.AddMeta("set_user_id", c.Params.UserId)
+
user, err := c.App.GetUser(c.Params.UserId)
if err != nil {
c.Err = err
return
}
+ auditRec.AddMeta("set_username", user.Username)
if err := c.App.SetDefaultProfileImage(user); err != nil {
c.Err = err
return
}
+ auditRec.Success()
c.LogAudit("")
+
ReturnStatusOK(w)
}
@@ -859,6 +886,10 @@ func updateUser(c *Context, w http.ResponseWriter, r *http.Request) {
return
}
+ auditRec := c.MakeAuditRecord("updateUser", audit.Fail)
+ defer c.LogAuditRec(auditRec)
+ auditRec.AddMeta("update_user_id", user.Id)
+
if !c.App.SessionHasPermissionToUser(*c.App.Session(), user.Id) {
c.SetPermissionError(model.PERMISSION_EDIT_OTHER_USERS)
return
@@ -893,7 +924,11 @@ func updateUser(c *Context, w http.ResponseWriter, r *http.Request) {
return
}
+ auditRec.Success()
+ auditRec.AddMeta("update_username", ruser.Username)
+ auditRec.AddMeta("update_email", ruser.Email)
c.LogAudit("")
+
w.Write([]byte(ruser.ToJson()))
}
@@ -909,6 +944,10 @@ func patchUser(c *Context, w http.ResponseWriter, r *http.Request) {
return
}
+ auditRec := c.MakeAuditRecord("patchUser", audit.Fail)
+ defer c.LogAuditRec(auditRec)
+ auditRec.AddMeta("patch_user_id", c.Params.UserId)
+
if !c.App.SessionHasPermissionToUser(*c.App.Session(), c.Params.UserId) {
c.SetPermissionError(model.PERMISSION_EDIT_OTHER_USERS)
return
@@ -948,7 +987,12 @@ func patchUser(c *Context, w http.ResponseWriter, r *http.Request) {
}
c.App.SetAutoResponderStatus(ruser, ouser.NotifyProps)
+
+ auditRec.Success()
+ auditRec.AddMeta("patch_username", ruser.Username)
+ auditRec.AddMeta("patch_email", ruser.Email)
c.LogAudit("")
+
w.Write([]byte(ruser.ToJson()))
}
@@ -960,6 +1004,10 @@ func deleteUser(c *Context, w http.ResponseWriter, r *http.Request) {
userId := c.Params.UserId
+ auditRec := c.MakeAuditRecord("deleteUser", audit.Fail)
+ defer c.LogAuditRec(auditRec)
+ auditRec.AddMeta("delete_user_id", c.Params.UserId)
+
if !c.App.SessionHasPermissionToUser(*c.App.Session(), userId) {
c.SetPermissionError(model.PERMISSION_EDIT_OTHER_USERS)
return
@@ -976,12 +1024,14 @@ func deleteUser(c *Context, w http.ResponseWriter, r *http.Request) {
c.Err = err
return
}
+ auditRec.AddMeta("delete_username", user.Username)
if _, err = c.App.UpdateActive(user, false); err != nil {
c.Err = err
return
}
+ auditRec.Success()
ReturnStatusOK(w)
}
@@ -999,6 +1049,11 @@ func updateUserRoles(c *Context, w http.ResponseWriter, r *http.Request) {
return
}
+ auditRec := c.MakeAuditRecord("updateUserRoles", audit.Fail)
+ defer c.LogAuditRec(auditRec)
+ auditRec.AddMeta("update_user_id", c.Params.UserId)
+ auditRec.AddMeta("new_roles", newRoles)
+
if !c.App.SessionHasPermissionTo(*c.App.Session(), model.PERMISSION_MANAGE_ROLES) {
c.SetPermissionError(model.PERMISSION_MANAGE_ROLES)
return
@@ -1009,7 +1064,9 @@ func updateUserRoles(c *Context, w http.ResponseWriter, r *http.Request) {
return
}
+ auditRec.Success()
c.LogAudit(fmt.Sprintf("user=%s roles=%s", c.Params.UserId, newRoles))
+
ReturnStatusOK(w)
}
@@ -1027,6 +1084,11 @@ func updateUserActive(c *Context, w http.ResponseWriter, r *http.Request) {
return
}
+ auditRec := c.MakeAuditRecord("updateUserActive", audit.Fail)
+ defer c.LogAuditRec(auditRec)
+ auditRec.AddMeta("update_user_id", c.Params.UserId)
+ auditRec.AddMeta("new_active", active)
+
// true when you're trying to de-activate yourself
isSelfDeactive := !active && c.Params.UserId == c.App.Session().UserId
@@ -1056,7 +1118,9 @@ func updateUserActive(c *Context, w http.ResponseWriter, r *http.Request) {
c.Err = err
}
+ auditRec.Success()
c.LogAudit(fmt.Sprintf("user_id=%s active=%v", user.Id, active))
+
if isSelfDeactive {
c.App.Srv().Go(func() {
if err = c.App.SendDeactivateAccountEmail(user.Email, user.Locale, c.App.GetSiteURL()); err != nil {
@@ -1078,6 +1142,10 @@ func updateUserAuth(c *Context, w http.ResponseWriter, r *http.Request) {
return
}
+ auditRec := c.MakeAuditRecord("updateUserAuth", audit.Fail)
+ defer c.LogAuditRec(auditRec)
+ auditRec.AddMeta("update_user_id", c.Params.UserId)
+
userAuth := model.UserAuthFromJson(r.Body)
if userAuth == nil {
c.SetInvalidParam("user")
@@ -1090,7 +1158,10 @@ func updateUserAuth(c *Context, w http.ResponseWriter, r *http.Request) {
return
}
+ auditRec.Success()
+ auditRec.AddMeta("auth_service", user.AuthService)
c.LogAudit(fmt.Sprintf("updated user %s auth to service=%v", c.Params.UserId, user.AuthService))
+
w.Write([]byte(user.ToJson()))
}
@@ -1134,6 +1205,10 @@ func updateUserMfa(c *Context, w http.ResponseWriter, r *http.Request) {
return
}
+ auditRec := c.MakeAuditRecord("updateUserMfa", audit.Fail)
+ defer c.LogAuditRec(auditRec)
+ auditRec.AddMeta("update_user_id", c.Params.UserId)
+
if c.App.Session().IsOAuth {
c.SetPermissionError(model.PERMISSION_EDIT_OTHER_USERS)
c.Err.DetailedError += ", attempted access by oauth app"
@@ -1168,7 +1243,10 @@ func updateUserMfa(c *Context, w http.ResponseWriter, r *http.Request) {
return
}
+ auditRec.Success()
+ auditRec.AddMeta("activate", activate)
c.LogAudit("success - mfa updated")
+
ReturnStatusOK(w)
}
@@ -1210,6 +1288,9 @@ func updatePassword(c *Context, w http.ResponseWriter, r *http.Request) {
props := model.MapFromJson(r.Body)
newPassword := props["new_password"]
+ auditRec := c.MakeAuditRecord("updatePassword", audit.Fail)
+ defer c.LogAuditRec(auditRec)
+ auditRec.AddMeta("update_user_id", c.Params.UserId)
c.LogAudit("attempted")
var err *model.AppError
@@ -1233,7 +1314,9 @@ func updatePassword(c *Context, w http.ResponseWriter, r *http.Request) {
return
}
+ auditRec.Success()
c.LogAudit("completed")
+
ReturnStatusOK(w)
}
@@ -1248,6 +1331,9 @@ func resetPassword(c *Context, w http.ResponseWriter, r *http.Request) {
newPassword := props["new_password"]
+ auditRec := c.MakeAuditRecord("resetPassword", audit.Fail)
+ defer c.LogAuditRec(auditRec)
+ auditRec.AddMeta("token", token)
c.LogAudit("attempt - token=" + token)
if err := c.App.ResetPasswordFromToken(token, newPassword); err != nil {
@@ -1256,6 +1342,7 @@ func resetPassword(c *Context, w http.ResponseWriter, r *http.Request) {
return
}
+ auditRec.Success()
c.LogAudit("success - token=" + token)
ReturnStatusOK(w)
@@ -1271,6 +1358,10 @@ func sendPasswordReset(c *Context, w http.ResponseWriter, r *http.Request) {
return
}
+ auditRec := c.MakeAuditRecord("sendPasswordReset", audit.Fail)
+ defer c.LogAuditRec(auditRec)
+ auditRec.AddMeta("email", email)
+
sent, err := c.App.SendPasswordReset(email, c.App.GetSiteURL())
if err != nil {
if *c.App.Config().ServiceSettings.ExperimentalEnableHardenedMode {
@@ -1282,9 +1373,9 @@ func sendPasswordReset(c *Context, w http.ResponseWriter, r *http.Request) {
}
if sent {
+ auditRec.Success()
c.LogAudit("sent=" + email)
}
-
ReturnStatusOK(w)
}
@@ -1374,14 +1465,20 @@ func login(c *Context, w http.ResponseWriter, r *http.Request) {
}
}
- c.LogAuditWithUserId(id, "attempt - login_id="+loginId)
- user, err := c.App.AuthenticateUserForLogin(id, loginId, password, mfaToken, ldapOnly)
+ auditRec := c.MakeAuditRecord("login", audit.Fail)
+ defer c.LogAuditRec(auditRec)
+ auditRec.AddMeta("login_id", loginId)
+ auditRec.AddMeta("device_id", deviceId)
+ c.LogAuditWithUserId(id, "attempt - login_id="+loginId)
+
+ user, err := c.App.AuthenticateUserForLogin(id, loginId, password, mfaToken, ldapOnly)
if err != nil {
c.LogAuditWithUserId(id, "failure - login_id="+loginId)
c.Err = err
return
}
+ auditRec.AddMeta(audit.KeyUserID, user.Id)
if user.IsGuest() {
if c.App.License() == nil {
@@ -1421,6 +1518,7 @@ func login(c *Context, w http.ResponseWriter, r *http.Request) {
user.Sanitize(map[string]bool{})
+ auditRec.Success()
w.Write([]byte(user.ToJson()))
}
@@ -1429,7 +1527,10 @@ func logout(c *Context, w http.ResponseWriter, r *http.Request) {
}
func Logout(c *Context, w http.ResponseWriter, r *http.Request) {
+ auditRec := c.MakeAuditRecord("Logout", audit.Fail)
+ defer c.LogAuditRec(auditRec)
c.LogAudit("")
+
c.RemoveSessionCookie(w, r)
if c.App.Session().Id != "" {
if err := c.App.RevokeSessionById(c.App.Session().Id); err != nil {
@@ -1438,6 +1539,7 @@ func Logout(c *Context, w http.ResponseWriter, r *http.Request) {
}
}
+ auditRec.Success()
ReturnStatusOK(w)
}
@@ -1471,6 +1573,10 @@ func revokeSession(c *Context, w http.ResponseWriter, r *http.Request) {
return
}
+ auditRec := c.MakeAuditRecord("revokeSession", audit.Fail)
+ defer c.LogAuditRec(auditRec)
+ auditRec.AddMeta("revoke_user_id", c.Params.UserId)
+
if !c.App.SessionHasPermissionToUser(*c.App.Session(), c.Params.UserId) {
c.SetPermissionError(model.PERMISSION_EDIT_OTHER_USERS)
return
@@ -1488,6 +1594,7 @@ func revokeSession(c *Context, w http.ResponseWriter, r *http.Request) {
c.Err = err
return
}
+ auditRec.AddMeta("device_id", session.DeviceId)
if session.UserId != c.Params.UserId {
c.SetInvalidUrlParam("user_id")
@@ -1499,6 +1606,9 @@ func revokeSession(c *Context, w http.ResponseWriter, r *http.Request) {
return
}
+ auditRec.Success()
+ c.LogAudit("")
+
ReturnStatusOK(w)
}
@@ -1508,6 +1618,10 @@ func revokeAllSessionsForUser(c *Context, w http.ResponseWriter, r *http.Request
return
}
+ auditRec := c.MakeAuditRecord("revokeAllSessionsForUser", audit.Fail)
+ defer c.LogAuditRec(auditRec)
+ auditRec.AddMeta("revoke_user_id", c.Params.UserId)
+
if !c.App.SessionHasPermissionToUser(*c.App.Session(), c.Params.UserId) {
c.SetPermissionError(model.PERMISSION_EDIT_OTHER_USERS)
return
@@ -1518,6 +1632,9 @@ func revokeAllSessionsForUser(c *Context, w http.ResponseWriter, r *http.Request
return
}
+ auditRec.Success()
+ c.LogAudit("")
+
ReturnStatusOK(w)
}
@@ -1527,11 +1644,17 @@ func revokeAllSessionsAllUsers(c *Context, w http.ResponseWriter, r *http.Reques
return
}
+ auditRec := c.MakeAuditRecord("revokeAllSessionsAllUsers", audit.Fail)
+ defer c.LogAuditRec(auditRec)
+
if err := c.App.RevokeSessionsFromAllUsers(); err != nil {
c.Err = err
return
}
+ auditRec.Success()
+ c.LogAudit("")
+
ReturnStatusOK(w)
}
@@ -1544,6 +1667,10 @@ func attachDeviceId(c *Context, w http.ResponseWriter, r *http.Request) {
return
}
+ auditRec := c.MakeAuditRecord("attachDeviceId", audit.Fail)
+ defer c.LogAuditRec(auditRec)
+ auditRec.AddMeta("device_id", deviceId)
+
// A special case where we logout of all other sessions with the same device id
if err := c.App.RevokeSessionsForDeviceId(c.App.Session().UserId, deviceId, c.App.Session().Id); err != nil {
c.Err = err
@@ -1581,7 +1708,9 @@ func attachDeviceId(c *Context, w http.ResponseWriter, r *http.Request) {
return
}
+ auditRec.Success()
c.LogAudit("")
+
ReturnStatusOK(w)
}
@@ -1614,12 +1743,17 @@ func verifyUserEmail(c *Context, w http.ResponseWriter, r *http.Request) {
return
}
+ auditRec := c.MakeAuditRecord("verifyUserEmail", audit.Fail)
+ defer c.LogAuditRec(auditRec)
+
if err := c.App.VerifyEmailFromToken(token); err != nil {
c.Err = model.NewAppError("verifyUserEmail", "api.user.verify_email.bad_link.app_error", nil, err.Error(), http.StatusBadRequest)
return
}
+ auditRec.Success()
c.LogAudit("Email Verified")
+
ReturnStatusOK(w)
}
@@ -1633,12 +1767,18 @@ func sendVerificationEmail(c *Context, w http.ResponseWriter, r *http.Request) {
return
}
+ auditRec := c.MakeAuditRecord("sendVerificationEmail", audit.Fail)
+ defer c.LogAuditRec(auditRec)
+ auditRec.AddMeta("email", email)
+
user, err := c.App.GetUserForLogin("", email)
if err != nil {
// Don't want to leak whether the email is valid or not
ReturnStatusOK(w)
return
}
+ auditRec.AddMeta("send_user_id", user.Id)
+ auditRec.AddMeta("send_username", user.Username)
if err = c.App.SendEmailVerification(user, user.Email); err != nil {
// Don't want to leak whether the email is valid or not
@@ -1647,6 +1787,7 @@ func sendVerificationEmail(c *Context, w http.ResponseWriter, r *http.Request) {
return
}
+ auditRec.Success()
ReturnStatusOK(w)
}
@@ -1657,6 +1798,12 @@ func switchAccountType(c *Context, w http.ResponseWriter, r *http.Request) {
return
}
+ auditRec := c.MakeAuditRecord("switchAccountType", audit.Fail)
+ defer c.LogAuditRec(auditRec)
+ auditRec.AddMeta("email", switchRequest.Email)
+ auditRec.AddMeta("new_service", switchRequest.NewService)
+ auditRec.AddMeta("old_service", switchRequest.CurrentService)
+
link := ""
var err *model.AppError
@@ -1683,7 +1830,9 @@ func switchAccountType(c *Context, w http.ResponseWriter, r *http.Request) {
return
}
+ auditRec.Success()
c.LogAudit("success")
+
w.Write([]byte(model.MapToJson(map[string]string{"follow_link": link})))
}
@@ -1693,6 +1842,10 @@ func createUserAccessToken(c *Context, w http.ResponseWriter, r *http.Request) {
return
}
+ auditRec := c.MakeAuditRecord("createUserAccessToken", audit.Fail)
+ defer c.LogAuditRec(auditRec)
+ auditRec.AddMeta("create_user_id", c.Params.UserId)
+
if c.App.Session().IsOAuth {
c.SetPermissionError(model.PERMISSION_CREATE_USER_ACCESS_TOKEN)
c.Err.DetailedError += ", attempted access by oauth app"
@@ -1731,7 +1884,10 @@ func createUserAccessToken(c *Context, w http.ResponseWriter, r *http.Request) {
return
}
+ auditRec.Success()
+ auditRec.AddMeta("token_id", accessToken.Id)
c.LogAudit("success - token_id=" + accessToken.Id)
+
w.Write([]byte(accessToken.ToJson()))
}
@@ -1833,6 +1989,9 @@ func revokeUserAccessToken(c *Context, w http.ResponseWriter, r *http.Request) {
c.SetInvalidParam("token_id")
}
+ auditRec := c.MakeAuditRecord("revokeUserAccessToken", audit.Fail)
+ defer c.LogAuditRec(auditRec)
+ auditRec.AddMeta("token_id", tokenId)
c.LogAudit("")
if !c.App.SessionHasPermissionTo(*c.App.Session(), model.PERMISSION_REVOKE_USER_ACCESS_TOKEN) {
@@ -1845,6 +2004,7 @@ func revokeUserAccessToken(c *Context, w http.ResponseWriter, r *http.Request) {
c.Err = err
return
}
+ auditRec.AddMeta("revoke_user_id", accessToken.UserId)
if !c.App.SessionHasPermissionToUserOrBot(*c.App.Session(), accessToken.UserId) {
c.SetPermissionError(model.PERMISSION_EDIT_OTHER_USERS)
@@ -1856,7 +2016,9 @@ func revokeUserAccessToken(c *Context, w http.ResponseWriter, r *http.Request) {
return
}
+ auditRec.Success()
c.LogAudit("success - token_id=" + accessToken.Id)
+
ReturnStatusOK(w)
}
@@ -1868,6 +2030,9 @@ func disableUserAccessToken(c *Context, w http.ResponseWriter, r *http.Request)
c.SetInvalidParam("token_id")
}
+ auditRec := c.MakeAuditRecord("disableUserAccessToken", audit.Fail)
+ defer c.LogAuditRec(auditRec)
+ auditRec.AddMeta("token_id", tokenId)
c.LogAudit("")
// No separate permission for this action for now
@@ -1881,6 +2046,7 @@ func disableUserAccessToken(c *Context, w http.ResponseWriter, r *http.Request)
c.Err = err
return
}
+ auditRec.AddMeta("disable_user_id", accessToken.UserId)
if !c.App.SessionHasPermissionToUserOrBot(*c.App.Session(), accessToken.UserId) {
c.SetPermissionError(model.PERMISSION_EDIT_OTHER_USERS)
@@ -1892,7 +2058,9 @@ func disableUserAccessToken(c *Context, w http.ResponseWriter, r *http.Request)
return
}
+ auditRec.Success()
c.LogAudit("success - token_id=" + accessToken.Id)
+
ReturnStatusOK(w)
}
@@ -1904,6 +2072,9 @@ func enableUserAccessToken(c *Context, w http.ResponseWriter, r *http.Request) {
c.SetInvalidParam("token_id")
}
+ auditRec := c.MakeAuditRecord("enableUserAccessToken", audit.Fail)
+ defer c.LogAuditRec(auditRec)
+ auditRec.AddMeta("token_id", tokenId)
c.LogAudit("")
// No separate permission for this action for now
@@ -1917,6 +2088,7 @@ func enableUserAccessToken(c *Context, w http.ResponseWriter, r *http.Request) {
c.Err = err
return
}
+ auditRec.AddMeta("enabled_user_id", accessToken.UserId)
if !c.App.SessionHasPermissionToUserOrBot(*c.App.Session(), accessToken.UserId) {
c.SetPermissionError(model.PERMISSION_EDIT_OTHER_USERS)
@@ -1928,7 +2100,9 @@ func enableUserAccessToken(c *Context, w http.ResponseWriter, r *http.Request) {
return
}
+ auditRec.Success()
c.LogAudit("success - token_id=" + accessToken.Id)
+
ReturnStatusOK(w)
}
@@ -1939,6 +2113,11 @@ func saveUserTermsOfService(c *Context, w http.ResponseWriter, r *http.Request)
termsOfServiceId := props["termsOfServiceId"].(string)
accepted := props["accepted"].(bool)
+ auditRec := c.MakeAuditRecord("saveUserTermsOfService", audit.Fail)
+ defer c.LogAuditRec(auditRec)
+ auditRec.AddMeta("terms_id", termsOfServiceId)
+ auditRec.AddMeta("accepted", accepted)
+
if _, err := c.App.GetTermsOfService(termsOfServiceId); err != nil {
c.Err = err
return
@@ -1949,7 +2128,9 @@ func saveUserTermsOfService(c *Context, w http.ResponseWriter, r *http.Request)
return
}
+ auditRec.Success()
c.LogAudit("TermsOfServiceId=" + termsOfServiceId + ", accepted=" + strconv.FormatBool(accepted))
+
ReturnStatusOK(w)
}
@@ -1969,6 +2150,10 @@ func promoteGuestToUser(c *Context, w http.ResponseWriter, r *http.Request) {
return
}
+ auditRec := c.MakeAuditRecord("promoteGuestToUser", audit.Fail)
+ defer c.LogAuditRec(auditRec)
+ auditRec.AddMeta("promote_user_id", c.Params.UserId)
+
if !c.App.SessionHasPermissionTo(*c.App.Session(), model.PERMISSION_PROMOTE_GUEST) {
c.SetPermissionError(model.PERMISSION_PROMOTE_GUEST)
return
@@ -1979,6 +2164,7 @@ func promoteGuestToUser(c *Context, w http.ResponseWriter, r *http.Request) {
c.Err = err
return
}
+ auditRec.AddMeta("promote_username", user.Username)
if !user.IsGuest() {
c.Err = model.NewAppError("Api4.promoteGuestToUser", "api.user.promote_guest_to_user.no_guest.app_error", nil, "", http.StatusNotImplemented)
@@ -1990,6 +2176,7 @@ func promoteGuestToUser(c *Context, w http.ResponseWriter, r *http.Request) {
return
}
+ auditRec.Success()
ReturnStatusOK(w)
}
@@ -2009,6 +2196,10 @@ func demoteUserToGuest(c *Context, w http.ResponseWriter, r *http.Request) {
return
}
+ auditRec := c.MakeAuditRecord("demoteUserToGuest", audit.Fail)
+ defer c.LogAuditRec(auditRec)
+ auditRec.AddMeta("demote_user_id", c.Params.UserId)
+
if !c.App.SessionHasPermissionTo(*c.App.Session(), model.PERMISSION_DEMOTE_TO_GUEST) {
c.SetPermissionError(model.PERMISSION_DEMOTE_TO_GUEST)
return
@@ -2019,6 +2210,7 @@ func demoteUserToGuest(c *Context, w http.ResponseWriter, r *http.Request) {
c.Err = err
return
}
+ auditRec.AddMeta("demote_username", user.Username)
if user.IsGuest() {
c.Err = model.NewAppError("Api4.demoteUserToGuest", "api.user.demote_user_to_guest.already_guest.app_error", nil, "", http.StatusNotImplemented)
@@ -2030,5 +2222,6 @@ func demoteUserToGuest(c *Context, w http.ResponseWriter, r *http.Request) {
return
}
+ auditRec.Success()
ReturnStatusOK(w)
}
diff --git a/api4/webhook.go b/api4/webhook.go
index 866810ccdb..c2697f9b01 100644
--- a/api4/webhook.go
+++ b/api4/webhook.go
@@ -6,6 +6,7 @@ package api4
import (
"net/http"
+ "github.com/mattermost/mattermost-server/v5/audit"
"github.com/mattermost/mattermost-server/v5/model"
)
@@ -37,6 +38,11 @@ func createIncomingHook(c *Context, w http.ResponseWriter, r *http.Request) {
return
}
+ auditRec := c.MakeAuditRecord("createIncomingHook", audit.Fail)
+ defer c.LogAuditRec(auditRec)
+ auditRec.AddMeta("channel_id", channel.Id)
+ auditRec.AddMeta("channel_name", channel.Name)
+ auditRec.AddMeta("team_id", channel.TeamId)
c.LogAudit("attempt")
if !c.App.SessionHasPermissionToTeam(*c.App.Session(), channel.TeamId, model.PERMISSION_MANAGE_INCOMING_WEBHOOKS) {
@@ -56,7 +62,11 @@ func createIncomingHook(c *Context, w http.ResponseWriter, r *http.Request) {
return
}
+ auditRec.Success()
+ auditRec.AddMeta("hook_id", incomingHook.Id)
+ auditRec.AddMeta("hook_display", incomingHook.DisplayName)
c.LogAudit("success")
+
w.WriteHeader(http.StatusCreated)
w.Write([]byte(incomingHook.ToJson()))
}
@@ -79,6 +89,9 @@ func updateIncomingHook(c *Context, w http.ResponseWriter, r *http.Request) {
return
}
+ auditRec := c.MakeAuditRecord("updateIncomingHook", audit.Fail)
+ defer c.LogAuditRec(auditRec)
+ auditRec.AddMeta("hook_id", c.Params.HookId)
c.LogAudit("attempt")
oldHook, err := c.App.GetIncomingWebhook(c.Params.HookId)
@@ -86,6 +99,7 @@ func updateIncomingHook(c *Context, w http.ResponseWriter, r *http.Request) {
c.Err = err
return
}
+ auditRec.AddMeta("team_id", oldHook.TeamId)
if updatedHook.TeamId == "" {
updatedHook.TeamId = oldHook.TeamId
@@ -101,6 +115,8 @@ func updateIncomingHook(c *Context, w http.ResponseWriter, r *http.Request) {
c.Err = err
return
}
+ auditRec.AddMeta("channel_id", channel.Id)
+ auditRec.AddMeta("channel_name", channel.Name)
if channel.TeamId != updatedHook.TeamId {
c.SetInvalidParam("channel_id")
@@ -130,7 +146,9 @@ func updateIncomingHook(c *Context, w http.ResponseWriter, r *http.Request) {
return
}
+ auditRec.Success()
c.LogAudit("success")
+
w.WriteHeader(http.StatusCreated)
w.Write([]byte(incomingHook.ToJson()))
}
@@ -194,6 +212,14 @@ func getIncomingHook(c *Context, w http.ResponseWriter, r *http.Request) {
return
}
+ auditRec := c.MakeAuditRecord("getIncomingHook", audit.Fail)
+ defer c.LogAuditRec(auditRec)
+ auditRec.AddMeta("hook_id", hook.Id)
+ auditRec.AddMeta("hook_display", hook.DisplayName)
+ auditRec.AddMeta("channel_id", hook.ChannelId)
+ auditRec.AddMeta("team_id", hook.TeamId)
+ c.LogAudit("attempt")
+
channel, err = c.App.GetChannel(hook.ChannelId)
if err != nil {
c.Err = err
@@ -213,6 +239,9 @@ func getIncomingHook(c *Context, w http.ResponseWriter, r *http.Request) {
return
}
+ auditRec.Success()
+ c.LogAudit("success")
+
w.Write([]byte(hook.ToJson()))
}
@@ -240,6 +269,14 @@ func deleteIncomingHook(c *Context, w http.ResponseWriter, r *http.Request) {
return
}
+ auditRec := c.MakeAuditRecord("deleteIncomingHook", audit.Fail)
+ defer c.LogAuditRec(auditRec)
+ auditRec.AddMeta("hook_id", hook.Id)
+ auditRec.AddMeta("hook_display", hook.DisplayName)
+ auditRec.AddMeta("channel_id", channel.Id)
+ auditRec.AddMeta("channel_name", channel.Name)
+ auditRec.AddMeta("team_id", hook.TeamId)
+
if !c.App.SessionHasPermissionToTeam(*c.App.Session(), hook.TeamId, model.PERMISSION_MANAGE_INCOMING_WEBHOOKS) ||
(channel.Type != model.CHANNEL_OPEN && !c.App.SessionHasPermissionToChannel(*c.App.Session(), hook.ChannelId, model.PERMISSION_READ_CHANNEL)) {
c.LogAudit("fail - bad permissions")
@@ -258,6 +295,7 @@ func deleteIncomingHook(c *Context, w http.ResponseWriter, r *http.Request) {
return
}
+ auditRec.Success()
ReturnStatusOK(w)
}
@@ -279,6 +317,12 @@ func updateOutgoingHook(c *Context, w http.ResponseWriter, r *http.Request) {
return
}
+ auditRec := c.MakeAuditRecord("updateOutgoingHook", audit.Fail)
+ defer c.LogAuditRec(auditRec)
+ auditRec.AddMeta("hook_id", updatedHook.Id)
+ auditRec.AddMeta("hook_display", updatedHook.DisplayName)
+ auditRec.AddMeta("channel_id", updatedHook.ChannelId)
+ auditRec.AddMeta("team_id", updatedHook.TeamId)
c.LogAudit("attempt")
oldHook, err := c.App.GetOutgoingWebhook(c.Params.HookId)
@@ -315,7 +359,9 @@ func updateOutgoingHook(c *Context, w http.ResponseWriter, r *http.Request) {
return
}
+ auditRec.Success()
c.LogAudit("success")
+
w.Write([]byte(rhook.ToJson()))
}
@@ -326,6 +372,9 @@ func createOutgoingHook(c *Context, w http.ResponseWriter, r *http.Request) {
return
}
+ auditRec := c.MakeAuditRecord("createOutgoingHook", audit.Fail)
+ defer c.LogAuditRec(auditRec)
+ auditRec.AddMeta("hook_id", hook.Id)
c.LogAudit("attempt")
hook.CreatorId = c.App.Session().UserId
@@ -342,7 +391,12 @@ func createOutgoingHook(c *Context, w http.ResponseWriter, r *http.Request) {
return
}
+ auditRec.Success()
+ auditRec.AddMeta("hook_display", rhook.DisplayName)
+ auditRec.AddMeta("channel_id", rhook.ChannelId)
+ auditRec.AddMeta("team_id", rhook.TeamId)
c.LogAudit("success")
+
w.WriteHeader(http.StatusCreated)
w.Write([]byte(rhook.ToJson()))
}
@@ -413,6 +467,12 @@ func getOutgoingHook(c *Context, w http.ResponseWriter, r *http.Request) {
return
}
+ auditRec := c.MakeAuditRecord("getOutgoingHook", audit.Fail)
+ defer c.LogAuditRec(auditRec)
+ auditRec.AddMeta("hook_id", hook.Id)
+ auditRec.AddMeta("hook_display", hook.DisplayName)
+ auditRec.AddMeta("channel_id", hook.ChannelId)
+ auditRec.AddMeta("team_id", hook.TeamId)
c.LogAudit("attempt")
if !c.App.SessionHasPermissionToTeam(*c.App.Session(), hook.TeamId, model.PERMISSION_MANAGE_OUTGOING_WEBHOOKS) {
@@ -426,7 +486,9 @@ func getOutgoingHook(c *Context, w http.ResponseWriter, r *http.Request) {
return
}
+ auditRec.Success()
c.LogAudit("success")
+
w.Write([]byte(hook.ToJson()))
}
@@ -442,6 +504,12 @@ func regenOutgoingHookToken(c *Context, w http.ResponseWriter, r *http.Request)
return
}
+ auditRec := c.MakeAuditRecord("regenOutgoingHookToken", audit.Fail)
+ defer c.LogAuditRec(auditRec)
+ auditRec.AddMeta("hook_id", hook.Id)
+ auditRec.AddMeta("hook_display", hook.DisplayName)
+ auditRec.AddMeta("channel_id", hook.ChannelId)
+ auditRec.AddMeta("team_id", hook.TeamId)
c.LogAudit("attempt")
if !c.App.SessionHasPermissionToTeam(*c.App.Session(), hook.TeamId, model.PERMISSION_MANAGE_OUTGOING_WEBHOOKS) {
@@ -461,6 +529,9 @@ func regenOutgoingHookToken(c *Context, w http.ResponseWriter, r *http.Request)
return
}
+ auditRec.Success()
+ c.LogAudit("success")
+
w.Write([]byte(rhook.ToJson()))
}
@@ -476,6 +547,12 @@ func deleteOutgoingHook(c *Context, w http.ResponseWriter, r *http.Request) {
return
}
+ auditRec := c.MakeAuditRecord("deleteOutgoingHook", audit.Fail)
+ defer c.LogAuditRec(auditRec)
+ auditRec.AddMeta("hook_id", hook.Id)
+ auditRec.AddMeta("hook_display", hook.DisplayName)
+ auditRec.AddMeta("channel_id", hook.ChannelId)
+ auditRec.AddMeta("team_id", hook.TeamId)
c.LogAudit("attempt")
if !c.App.SessionHasPermissionToTeam(*c.App.Session(), hook.TeamId, model.PERMISSION_MANAGE_OUTGOING_WEBHOOKS) {
@@ -495,6 +572,8 @@ func deleteOutgoingHook(c *Context, w http.ResponseWriter, r *http.Request) {
return
}
+ auditRec.Success()
c.LogAudit("success")
+
ReturnStatusOK(w)
}
diff --git a/app/audit.go b/app/audit.go
index 6e991cb8c1..f22eb3c0e3 100644
--- a/app/audit.go
+++ b/app/audit.go
@@ -4,9 +4,27 @@
package app
import (
+ "fmt"
+
+ "github.com/mattermost/mattermost-server/v5/audit"
+ "github.com/mattermost/mattermost-server/v5/mlog"
"github.com/mattermost/mattermost-server/v5/model"
)
+const (
+ RestLevelID = 240
+ RestContentLevelID = 241
+ RestPermsLevelID = 242
+ CLILevelID = 243
+)
+
+var (
+ RestLevel = audit.Level{ID: RestLevelID, Name: "audit-rest", Stacktrace: false}
+ RestContentLevel = audit.Level{ID: RestContentLevelID, Name: "audit-rest-content", Stacktrace: false}
+ RestPermsLevel = audit.Level{ID: RestPermsLevelID, Name: "audit-rest-perms", Stacktrace: false}
+ CLILevel = audit.Level{ID: CLILevelID, Name: "audit-cli", Stacktrace: false}
+)
+
func (a *App) GetAudits(userId string, limit int) (model.Audits, *model.AppError) {
return a.Srv().Store.Audit().Get(userId, 0, limit)
}
@@ -14,3 +32,51 @@ func (a *App) GetAudits(userId string, limit int) (model.Audits, *model.AppError
func (a *App) GetAuditsPage(userId string, page int, perPage int) (model.Audits, *model.AppError) {
return a.Srv().Store.Audit().Get(userId, page*perPage, perPage)
}
+
+func (s *Server) configureAudit(adt *audit.Audit) {
+ adt.OnQueueFull = s.onAuditTargetQueueFull
+ adt.OnError = s.onAuditError
+
+ // For now we only support sending audit records to Syslog via TLS.
+ // See https://www.rsyslog.com/doc/v8-stable/tutorials/tls_cert_summary.html
+ if *s.Config().ExperimentalAuditSettings.Enabled {
+ IP := *s.Config().ExperimentalAuditSettings.IP
+ if IP == "" {
+ IP = "localhost"
+ }
+ port := *s.Config().ExperimentalAuditSettings.Port
+ if port <= 0 {
+ port = 6514
+ }
+ raddr := fmt.Sprintf("%s:%d", IP, port)
+ maxQSize := *s.Config().ExperimentalAuditSettings.MaxQSize
+ if maxQSize <= 0 {
+ maxQSize = audit.DefMaxQueueSize
+ }
+
+ params := &audit.SyslogParams{
+ Raddr: raddr,
+ Cert: *s.Config().ExperimentalAuditSettings.Cert,
+ Tag: *s.Config().ExperimentalAuditSettings.Tag,
+ Insecure: *s.Config().ExperimentalAuditSettings.Insecure,
+ }
+
+ filter := adt.MakeFilter(RestLevel, RestContentLevel, RestPermsLevel, CLILevel)
+ formatter := adt.MakeJSONFormatter()
+ target, err := audit.NewSyslogTLSTarget(filter, formatter, params, maxQSize)
+ if err != nil {
+ mlog.Error("cannot configure SysLogTLS audit target", mlog.Err(err))
+ return
+ }
+ mlog.Debug("SysLogTLS audit target connected successfully", mlog.String("raddy", raddr))
+ adt.AddTarget(target)
+ }
+}
+
+func (s *Server) onAuditTargetQueueFull(qname string, maxQSize int) {
+ mlog.Warn("Audit Queue Full", mlog.String("qname", qname), mlog.Int("maxQSize", maxQSize))
+}
+
+func (s *Server) onAuditError(err error) {
+ mlog.Error("Audit Error", mlog.Err(err))
+}
diff --git a/app/server.go b/app/server.go
index 0cb6dd8975..e08ba2fdeb 100644
--- a/app/server.go
+++ b/app/server.go
@@ -24,6 +24,7 @@ import (
"github.com/throttled/throttled"
"golang.org/x/crypto/acme/autocert"
+ "github.com/mattermost/mattermost-server/v5/audit"
"github.com/mattermost/mattermost-server/v5/config"
"github.com/mattermost/mattermost-server/v5/einterfaces"
"github.com/mattermost/mattermost-server/v5/jobs"
@@ -117,6 +118,7 @@ type Server struct {
ImageProxy *imageproxy.ImageProxy
+ Audit *audit.Audit
Log *mlog.Logger
NotificationsLog *mlog.Logger
@@ -278,6 +280,12 @@ func NewServer(options ...Option) (*Server, error) {
s.ReloadConfig()
+ if s.Audit == nil {
+ s.Audit = &audit.Audit{}
+ s.Audit.Init(audit.DefMaxQueueSize)
+ s.configureAudit(s.Audit)
+ }
+
// Enable developer settings if this is a "dev" build
if model.BuildNumber == "dev" {
s.UpdateConfig(func(cfg *model.Config) { *cfg.ServiceSettings.EnableDeveloper = true })
@@ -406,6 +414,8 @@ func (s *Server) Shutdown() error {
s.RemoveConfigListener(s.configListenerId)
s.RemoveConfigListener(s.logListenerId)
+ s.Audit.Shutdown()
+
s.configStore.Close()
if s.Cluster != nil {
diff --git a/audit/audit.go b/audit/audit.go
new file mode 100644
index 0000000000..3d970c87e2
--- /dev/null
+++ b/audit/audit.go
@@ -0,0 +1,119 @@
+// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
+// See LICENSE.txt for license information.
+
+package audit
+
+import (
+ "fmt"
+
+ "github.com/wiggin77/logr"
+ "github.com/wiggin77/logr/format"
+)
+
+type Level logr.Level
+
+type Audit struct {
+ lgr *logr.Logr
+ logger logr.Logger
+
+ // OnQueueFull is called on an attempt to add an audit record to a full queue.
+ // On return the calling goroutine will block until the audit record can be added.
+ OnQueueFull func(qname string, maxQueueSize int)
+
+ // OnError is called when an error occurs while writing an audit record.
+ OnError func(err error)
+}
+
+func (a *Audit) Init(maxQueueSize int) {
+ a.lgr = &logr.Logr{MaxQueueSize: maxQueueSize}
+ a.logger = a.lgr.NewLogger()
+
+ a.lgr.OnQueueFull = a.onQueueFull
+ a.lgr.OnTargetQueueFull = a.onTargetQueueFull
+ a.lgr.OnLoggerError = a.onLoggerError
+}
+
+// MakeFilter creates a filter which only allows the specified audit levels to be output.
+func (a *Audit) MakeFilter(level ...Level) *logr.CustomFilter {
+ filter := &logr.CustomFilter{}
+ for _, l := range level {
+ filter.Add(logr.Level(l))
+ }
+ return filter
+}
+
+// MakeJSONFormatter creates a formatter that outputs JSON suitable for audit records.
+func (a *Audit) MakeJSONFormatter() *format.JSON {
+ f := &format.JSON{
+ DisableTimestamp: true,
+ DisableStacktrace: true,
+ DisableLevel: true,
+ }
+ return f
+}
+
+// LogRecord emits an audit record with complete info.
+func (a *Audit) LogRecord(level Level, rec Record) {
+ flds := logr.Fields{}
+ flds[KeyAPIPath] = rec.APIPath
+ flds[KeyEvent] = rec.Event
+ flds[KeyStatus] = rec.Status
+ flds[KeyUserID] = rec.UserID
+ flds[KeySessionID] = rec.SessionID
+ flds[KeyClient] = rec.Client
+ flds[KeyIPAddress] = rec.IPAddress
+
+ for k, v := range rec.Meta {
+ flds[k] = v
+ }
+
+ l := a.logger.WithFields(flds)
+ l.Log(logr.Level(level))
+}
+
+// Log emits an audit record based on minimum required info.
+func (a *Audit) Log(level Level, path string, evt string, status string, userID string, sessionID string, meta Meta) {
+ a.LogRecord(level, Record{
+ APIPath: path,
+ Event: evt,
+ Status: status,
+ UserID: userID,
+ SessionID: sessionID,
+ Meta: meta,
+ })
+}
+
+// AddTarget adds a Logr target to the list of targets each audit record will be output to.
+func (a *Audit) AddTarget(target logr.Target) {
+ a.lgr.AddTarget(target)
+}
+
+// Shutdown cleanly stops the audit engine after making best efforts to flush all targets.
+func (a *Audit) Shutdown() {
+ err := a.lgr.Shutdown()
+ if err != nil {
+ a.onLoggerError(err)
+ }
+}
+
+func (a *Audit) onQueueFull(rec *logr.LogRec, maxQueueSize int) bool {
+ if a.OnQueueFull != nil {
+ a.OnQueueFull("main", maxQueueSize)
+ }
+ // block until record can be added.
+ return false
+}
+
+func (a *Audit) onTargetQueueFull(target logr.Target, rec *logr.LogRec, maxQueueSize int) bool {
+ if a.OnQueueFull != nil {
+ a.OnQueueFull(fmt.Sprintf("%v", target), maxQueueSize)
+ }
+ // block until record can be added.
+ return false
+}
+
+func (a *Audit) onLoggerError(err error) {
+ if a.OnError != nil {
+ a.OnError(err)
+ }
+}
diff --git a/audit/const.go b/audit/const.go
new file mode 100644
index 0000000000..a092ef7783
--- /dev/null
+++ b/audit/const.go
@@ -0,0 +1,20 @@
+// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
+// See LICENSE.txt for license information.
+
+package audit
+
+const (
+ DefMaxQueueSize = 1000
+
+ KeyAPIPath = "api_path"
+ KeyEvent = "event"
+ KeyStatus = "status"
+ KeyUserID = "user_id"
+ KeySessionID = "session_id"
+ KeyClient = "client"
+ KeyIPAddress = "ip_address"
+
+ Success = "success"
+ Attempt = "attempt"
+ Fail = "fail"
+)
diff --git a/audit/record.go b/audit/record.go
new file mode 100644
index 0000000000..887ec7516c
--- /dev/null
+++ b/audit/record.go
@@ -0,0 +1,37 @@
+// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
+// See LICENSE.txt for license information.
+
+package audit
+
+// Meta represents metadata that can be added to a audit record as name/value pairs.
+type Meta map[string]interface{}
+
+// Record provides a consistent set of fields used for all audit logging.
+type Record struct {
+ APIPath string
+ Event string
+ Status string
+ UserID string
+ SessionID string
+ Client string
+ IPAddress string
+ Meta Meta
+}
+
+// Success marks the audit record status as successful.
+func (rec *Record) Success() {
+ rec.Status = Success
+}
+
+// Success marks the audit record status as failed.
+func (rec *Record) Fail() {
+ rec.Status = Fail
+}
+
+// AddMeta adds a single name/value pair to this audit record's metadata.
+func (rec *Record) AddMeta(name string, val interface{}) {
+ if rec.Meta == nil {
+ rec.Meta = Meta{}
+ }
+ rec.Meta[name] = val
+}
diff --git a/audit/syslogtls.go b/audit/syslogtls.go
new file mode 100644
index 0000000000..cc6f65d139
--- /dev/null
+++ b/audit/syslogtls.go
@@ -0,0 +1,110 @@
+// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
+// See LICENSE.txt for license information.
+
+package audit
+
+import (
+ "context"
+ "crypto/tls"
+ "crypto/x509"
+ "fmt"
+ "io/ioutil"
+
+ syslog "github.com/RackSec/srslog"
+ "github.com/wiggin77/logr"
+ "github.com/wiggin77/merror"
+)
+
+// Syslog outputs log records to local or remote syslog.
+type SyslogTLS struct {
+ logr.Basic
+ w *syslog.Writer
+}
+
+// SyslogParams provides parameters for dialing a syslogTLS daemon.
+type SyslogParams struct {
+ Raddr string
+ Cert string
+ Tag string
+ Insecure bool
+}
+
+// NewSyslogTLSTarget creates a target capable of outputting log records to remote or local syslog via TLS.
+func NewSyslogTLSTarget(filter logr.Filter, formatter logr.Formatter, params *SyslogParams, maxQueue int) (*SyslogTLS, error) {
+ config := tls.Config{InsecureSkipVerify: params.Insecure}
+
+ if params.Cert != "" {
+ serverCert, err := ioutil.ReadFile(params.Cert)
+ if err != nil {
+ return nil, err
+ }
+ pool := x509.NewCertPool()
+ pool.AppendCertsFromPEM(serverCert)
+ config.RootCAs = pool
+ }
+
+ writer, err := syslog.DialWithTLSConfig("tcp+tls", params.Raddr, syslog.LOG_INFO, params.Tag, &config)
+ if err != nil {
+ return nil, err
+ }
+
+ s := &SyslogTLS{w: writer}
+ s.Basic.Start(s, s, filter, formatter, maxQueue)
+
+ return s, nil
+}
+
+// Shutdown stops processing log records after making best
+// effort to flush queue.
+func (s *SyslogTLS) Shutdown(ctx context.Context) error {
+ errs := merror.New()
+
+ err := s.Basic.Shutdown(ctx)
+ errs.Append(err)
+
+ err = s.w.Close()
+ errs.Append(err)
+
+ return errs.ErrorOrNil()
+}
+
+// Write converts the log record to bytes, via the Formatter,
+// and outputs to syslog via TLS.
+func (s *SyslogTLS) Write(rec *logr.LogRec) error {
+ _, stacktrace := s.IsLevelEnabled(rec.Level())
+
+ buf := rec.Logger().Logr().BorrowBuffer()
+ defer rec.Logger().Logr().ReleaseBuffer(buf)
+
+ buf, err := s.Formatter().Format(rec, stacktrace, buf)
+ if err != nil {
+ return err
+ }
+ txt := buf.String()
+
+ switch rec.Level() {
+ case logr.Panic, logr.Fatal:
+ err = s.w.Crit(txt)
+ case logr.Error:
+ err = s.w.Err(txt)
+ case logr.Warn:
+ err = s.w.Warning(txt)
+ case logr.Debug, logr.Trace:
+ err = s.w.Debug(txt)
+ default:
+ // logr.Info plus all custom levels.
+ err = s.w.Info(txt)
+ }
+
+ if err != nil {
+ reporter := rec.Logger().Logr().ReportError
+ reporter(fmt.Errorf("syslog write fail: %w", err))
+ // syslogTLS writer will try to reconnect.
+ }
+ return err
+}
+
+// String returns a string representation of this target.
+func (s *SyslogTLS) String() string {
+ return "SyslogTLSTarget"
+}
diff --git a/go.mod b/go.mod
index 5020a14a3d..93bfafe31a 100644
--- a/go.mod
+++ b/go.mod
@@ -5,6 +5,7 @@ go 1.12
require (
github.com/Masterminds/squirrel v1.1.0
github.com/NYTimes/gziphandler v1.1.1
+ github.com/RackSec/srslog v0.0.0-20180709174129-a4725f04ec91
github.com/armon/go-metrics v0.3.0 // indirect
github.com/avct/uasurfer v0.0.0-20191028135549-26b5daa857f1
github.com/beevik/etree v1.1.0 // indirect
@@ -42,7 +43,7 @@ require (
github.com/icrowley/fake v0.0.0-20180203215853-4178557ae428
github.com/jaytaylor/html2text v0.0.0-20190408195923-01ec452cbe43
github.com/jmoiron/sqlx v1.2.0
- github.com/jonboulle/clockwork v0.1.0 // indirect
+ github.com/jonboulle/clockwork v0.1.0
github.com/konsorten/go-windows-terminal-sequences v1.0.2 // indirect
github.com/lib/pq v1.3.0
github.com/magiconair/properties v1.8.1 // indirect
@@ -68,6 +69,7 @@ require (
github.com/pelletier/go-toml v1.6.0 // indirect
github.com/pkg/errors v0.9.1
github.com/prometheus/client_golang v1.4.0
+ github.com/prometheus/client_model v0.2.0
github.com/rs/cors v1.7.0
github.com/russellhaering/goxmldsig v0.0.0-20180430223755-7acd5e4a6ef7
github.com/rwcarlsen/goexif v0.0.0-20190401172101-9e8deecbddbd
@@ -88,6 +90,8 @@ require (
github.com/tylerb/graceful v1.2.15
github.com/uber/jaeger-client-go v2.22.1+incompatible
github.com/uber/jaeger-lib v2.2.0+incompatible
+ github.com/wiggin77/logr v1.0.3
+ github.com/wiggin77/merror v1.0.2
github.com/xtgo/uuid v0.0.0-20140804021211-a0b114877d4c // indirect
github.com/ziutek/mymysql v1.5.4 // indirect
go.uber.org/atomic v1.5.1 // indirect
diff --git a/go.sum b/go.sum
index 138528d1ad..5d2fb80907 100644
--- a/go.sum
+++ b/go.sum
@@ -1,8 +1,13 @@
cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
cloud.google.com/go v0.31.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
+cloud.google.com/go v0.37.0/go.mod h1:TS1dMSSfndXH133OKGwekG838Om/cQT0BUHV3HcBgoo=
cloud.google.com/go v0.37.1/go.mod h1:SAbnLi6YTSPKSI0dTUEOVLCkyPfKXK8n4ibqiMoj4ok=
contrib.go.opencensus.io/exporter/ocagent v0.4.9/go.mod h1:ueLzZcP7LPhPulEBukGn4aLh7Mx9YJwpVJ9nL2FYltw=
+dmitri.shuralyov.com/app/changes v0.0.0-20180602232624-0a106ad413e3/go.mod h1:Yl+fi1br7+Rr3LqpNJf1/uxUdtRUV+Tnj0o93V2B9MU=
+dmitri.shuralyov.com/html/belt v0.0.0-20180602232347-f7d459c86be0/go.mod h1:JLBrvjyP0v+ecvNYvCpyZgu5/xkfAUhi6wJj28eUfSU=
+dmitri.shuralyov.com/service/change v0.0.0-20181023043359-a85b471d5412/go.mod h1:a1inKt/atXimZ4Mv927x+r7UpyzRUf4emIoiiSC2TN4=
+dmitri.shuralyov.com/state v0.0.0-20180228185332-28bcc343414c/go.mod h1:0PRwlb0D6DFvNNtx+9ybjezNCa8XF0xaYcETyp6rHWU=
git.apache.org/thrift.git v0.0.0-20180902110319-2566ecd5d999/go.mod h1:fPE2ZNJGynbRyZ4dJvy6G277gSllfV2HJqblrnkyeyg=
git.apache.org/thrift.git v0.12.0/go.mod h1:fPE2ZNJGynbRyZ4dJvy6G277gSllfV2HJqblrnkyeyg=
github.com/Azure/azure-sdk-for-go v26.5.0+incompatible/go.mod h1:9XXNKU+eRnpl9moKnB4QOLf1HestfXbmab5FXxiDBjc=
@@ -18,6 +23,8 @@ github.com/Masterminds/vcs v1.13.0/go.mod h1:N09YCmOQr6RLxC6UNHzuVwAdodYbbnycGHS
github.com/NYTimes/gziphandler v1.1.1 h1:ZUDjpQae29j0ryrS0u/B8HZfJBtBQHjqw2rQ2cqUQ3I=
github.com/NYTimes/gziphandler v1.1.1/go.mod h1:n/CVRwUEOgIxrgPvAQhUUr9oeUtvrhMomdKFjzJNB0c=
github.com/PaulARoy/azurestoragecache v0.0.0-20170906084534-3c249a3ba788/go.mod h1:lY1dZd8HBzJ10eqKERHn3CU59tfhzcAVb2c0ZhIWSOk=
+github.com/RackSec/srslog v0.0.0-20180709174129-a4725f04ec91 h1:vX+gnvBc56EbWYrmlhYbFYRaeikAke1GL84N4BEYOFE=
+github.com/RackSec/srslog v0.0.0-20180709174129-a4725f04ec91/go.mod h1:cDLGBht23g0XQdLjzn6xOGXDkLK182YfINAaZEQLCHQ=
github.com/Shopify/sarama v1.19.0/go.mod h1:FVkBWblsNy7DGZRfXLU0O9RCGt5g3g3yEuWXgklEdEo=
github.com/Shopify/toxiproxy v2.1.4+incompatible/go.mod h1:OXgGpZ6Cli1/URJOF1DMxUHB2q5Ap20/P/eIdh4G0pI=
github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc=
@@ -49,6 +56,7 @@ github.com/blang/semver v3.5.1+incompatible/go.mod h1:kRBLl5iJ+tD4TcOOxsy/0fnweb
github.com/bmizerany/assert v0.0.0-20160611221934-b7ed37b82869 h1:DDGfHa7BWjL4YnC6+E63dPcxHo2sUxDIu8g3QgEJdRY=
github.com/bmizerany/assert v0.0.0-20160611221934-b7ed37b82869/go.mod h1:Ekp36dRnpXw/yCqJaO+ZrUyxD+3VXMFFr56k5XYrpB4=
github.com/bradfitz/go-smtpd v0.0.0-20170404230938-deb6d6237625/go.mod h1:HYsPBTaaSFSlLx/70C2HPIMNZpVV8+vt/A+FMnYP11g=
+github.com/buger/jsonparser v0.0.0-20181115193947-bf1c66bbce23/go.mod h1:bbYlZJ7hK1yFx9hf58LP0zeX7UjIGs20ufpu3evjr+s=
github.com/census-instrumentation/opencensus-proto v0.2.0/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
github.com/cespare/xxhash/v2 v2.1.1 h1:6MnRN8NT7+YBpUIWxHtefFZOKTAPgGjpQSxqLNn0+qY=
@@ -95,6 +103,8 @@ github.com/fatih/color v1.9.0/go.mod h1:eQcE1qtQxscV5RaZvpXrrb8Drkc3/DdQ+uUYCNjL
github.com/flynn/go-shlex v0.0.0-20150515145356-3f9db97f8568/go.mod h1:xEzjJPgXI435gkrCt3MPfRiAkVrwSbHsst4LCFVfpJc=
github.com/fortytw2/leaktest v1.3.0 h1:u8491cBMTQ8ft8aeV+adlcytMZylmA5nnwwkRZjI8vw=
github.com/fortytw2/leaktest v1.3.0/go.mod h1:jDsjWgpAGjm2CA7WthBh/CdZYEPF31XHquHwclZch5g=
+github.com/francoispqt/gojay v1.2.13 h1:d2m3sFjloqoIUQU3TsHBgj6qg/BVGlTBeHDUmyJnXKk=
+github.com/francoispqt/gojay v1.2.13/go.mod h1:ehT5mTG4ua4581f1++1WLG0vPdaA9HaiDsoyrBGkyDY=
github.com/fsnotify/fsnotify v1.4.7 h1:IXs+QLmnXW2CcXuY+8Mzv/fWEsPGWxqefPtCP5CnV9I=
github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
github.com/garyburd/redigo v1.6.0/go.mod h1:NR3MbYisc3/PwhQ00EMzDiPmrwpPxAn5GI05/YaO1SY=
@@ -102,6 +112,7 @@ github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeME
github.com/gliderlabs/ssh v0.1.1/go.mod h1:U7qILu1NlMHj9FlMhZLlkCdDnU1DBEAqr0aevW3Awn0=
github.com/go-asn1-ber/asn1-ber v1.3.2-0.20191121212151-29be175fc3a3 h1:QW2p25fGTu/S0MvEftCo3wV7aEFHBt2m1DTg1HUwh+o=
github.com/go-asn1-ber/asn1-ber v1.3.2-0.20191121212151-29be175fc3a3/go.mod h1:hEBeB/ic+5LoWskz+yKT7vGhhPYkProFKoKdwZRWMe0=
+github.com/go-errors/errors v1.0.1/go.mod h1:f4zRHt4oKfwPJE5k8C9vpYG+aDHdBFUsgrm6/TyX73Q=
github.com/go-gorp/gorp v2.0.0+incompatible h1:dIQPsBtl6/H1MjVseWuWPXa7ET4p6Dve4j3Hg+UjqYw=
github.com/go-gorp/gorp v2.0.0+incompatible/go.mod h1:7IfkAQnO7jfT/9IQ3R9wL1dFhukN6aQxzKTHnkxzA/E=
github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as=
@@ -147,6 +158,7 @@ github.com/google/uuid v1.0.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+
github.com/google/uuid v1.1.1 h1:Gkbcsh/GbpXz7lPftLA3P6TYMwjCLYm83jiFQZF/3gY=
github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/googleapis/gax-go v2.0.0+incompatible/go.mod h1:SFVmujtThgffbyetf+mdk2eWhX2bMyUtNHzFKcPA9HY=
+github.com/googleapis/gax-go/v2 v2.0.3/go.mod h1:LLvjysVCY1JZeum8Z6l8qUty8fiNwE08qbEPm1M08qg=
github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg=
github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY=
github.com/gopherjs/gopherjs v0.0.0-20190430165422-3e4dfb77656c h1:7lF+Vz0LqiRidnzC1Oq86fpX1q/iEv2KJdrCtttYjT4=
@@ -253,10 +265,12 @@ github.com/lib/pq v1.0.0 h1:X5PMW56eZitiTeO7tKzZxFCSpbFZJtkMMooicw2us9A=
github.com/lib/pq v1.0.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo=
github.com/lib/pq v1.3.0 h1:/qkRGz8zljWiDcFvgpwUpwIAPu3r07TDvs3Rws+o/pU=
github.com/lib/pq v1.3.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo=
+github.com/lunixbochs/vtclean v1.0.0/go.mod h1:pHhQNgMf3btfWnGBVipUOjRYhoOsdGqdm/+2c2E2WMI=
github.com/magiconair/properties v1.8.0 h1:LLgXmsheXeRoUOBOjtwPQCWIYqM/LU1ayDtDePerRcY=
github.com/magiconair/properties v1.8.0/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ=
github.com/magiconair/properties v1.8.1 h1:ZC2Vc7/ZFkGmsVC9KvOjumD+G5lXy2RtTKyzRKO2BQ4=
github.com/magiconair/properties v1.8.1/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ=
+github.com/mailru/easyjson v0.0.0-20190312143242-1de009706dbe/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc=
github.com/mailru/easyjson v0.7.0 h1:aizVhC/NAAcKWb+5QsU1iNOZb4Yws5UO2I+aIprQITM=
github.com/mailru/easyjson v0.7.0/go.mod h1:KAzv3t3aY1NaHWoQz1+4F1ccyAH66Jk7yos7ldAVICs=
github.com/marstr/guid v0.0.0-20170427235115-8bdf7d1a087c/go.mod h1:74gB1z2wpxxInTG6yaqA7KrtM0NZ+RbrcqDvYHefzho=
@@ -268,7 +282,6 @@ github.com/mattermost/gosaml2 v0.3.2 h1:kq2dY5qUe6fPPHra171GVlgo+ycBsEog0gZMetxL
github.com/mattermost/gosaml2 v0.3.2/go.mod h1:Z429EIOiEi9kbq6yHoApfzlcXpa6dzRDc6pO+Vy2Ksk=
github.com/mattermost/ldap v0.0.0-20191128190019-9f62ba4b8d4d h1:2DV7VIlEv6J5R5o6tUcb3ZMKJYeeZuWZL7Rv1m23TgQ=
github.com/mattermost/ldap v0.0.0-20191128190019-9f62ba4b8d4d/go.mod h1:HLbgMEI5K131jpxGazJ97AxfPDt31osq36YS1oxFQPQ=
-github.com/mattermost/mattermost-server v5.11.1+incompatible h1:LPzKY0+2Tic/ik67qIg6VrydRCgxNXZQXOeaiJ2rMBY=
github.com/mattermost/rsc v0.0.0-20160330161541-bbaefb05eaa0 h1:G9tL6JXRBMzjuD1kkBtcnd42kUiT6QDwxfFYu7adM6o=
github.com/mattermost/rsc v0.0.0-20160330161541-bbaefb05eaa0/go.mod h1:nV5bfVpT//+B1RPD2JvRnxbkLmJEYXmRaaVl15fsXjs=
github.com/mattermost/viper v1.0.4 h1:cMYOz4PhguscGSPxrSokUtib5HrG4gCpiUh27wyA3d0=
@@ -291,6 +304,7 @@ github.com/mattn/go-sqlite3 v1.11.0 h1:LDdKkqtYlom37fkvqs8rMPFKAMe8+SgjbwZ6ex1/A
github.com/mattn/go-sqlite3 v1.11.0/go.mod h1:FPy6KqzDD04eiIsT53CuJW3U88zkxoIYsOqkbpncsNc=
github.com/matttproud/golang_protobuf_extensions v1.0.1 h1:4hp9jkHxhMHkqkrB3Ix0jegS5sx/RkqARlsWZ6pIwiU=
github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0=
+github.com/microcosm-cc/bluemonday v1.0.1/go.mod h1:hsXNsILzKxV+sX77C5b8FSuKF00vh2OMYv+xgHpAMF4=
github.com/miekg/dns v1.0.14/go.mod h1:W1PPwlIAgtquWBMBEV9nkV9Cazfe8ScdGz/Lj7v3Nrg=
github.com/miekg/dns v1.1.27 h1:aEH/kqUzUxGJ/UHcEKdJY+ugH6WEzsEBBSPa8zuy1aM=
github.com/miekg/dns v1.1.27/go.mod h1:KNUDUusw/aVsxyTYZM1oqvCicbwhgbNgztCETuNZ7xM=
@@ -316,9 +330,13 @@ github.com/muesli/smartcrop v0.2.1-0.20181030220600-548bbf0c0965/go.mod h1:i2fCI
github.com/muesli/smartcrop v0.3.0 h1:JTlSkmxWg/oQ1TcLDoypuirdE8Y/jzNirQeLkxpA6Oc=
github.com/muesli/smartcrop v0.3.0/go.mod h1:i2fCI/UorTfgEpPPLWiFBv4pye+YAG78RwcQLUkocpI=
github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U=
+github.com/neelance/astrewrite v0.0.0-20160511093645-99348263ae86/go.mod h1:kHJEU3ofeGjhHklVoIGuVj85JJwZ6kWPaJwCIxgnFmo=
+github.com/neelance/sourcemap v0.0.0-20151028013722-8c68805598ab/go.mod h1:Qr6/a/Q4r9LP1IltGz7tA7iOK1WonHEYhu1HRBA7ZiM=
github.com/nfnt/resize v0.0.0-20180221191011-83c6a9932646 h1:zYyBkD/k9seD2A7fsi6Oo2LfFZAehjjQMERAvZLEDnQ=
github.com/nfnt/resize v0.0.0-20180221191011-83c6a9932646/go.mod h1:jpp1/29i3P1S/RLdc7JQKbRpFeM1dOBd8T9ki5s+AY8=
github.com/ngdinhtoan/glide-cleanup v0.2.0/go.mod h1:UQzsmiDOb8YV3nOsCxK/c9zPpCZVNoHScRE3EO9pVMM=
+github.com/nsf/jsondiff v0.0.0-20190712045011-8443391ee9b6 h1:qsqscDgSJy+HqgMTR+3NwjYJBbp1+honwDsszLoS+pA=
+github.com/nsf/jsondiff v0.0.0-20190712045011-8443391ee9b6/go.mod h1:uFMI8w+ref4v2r9jz+c9i1IfIttS/OkmLfrk1jne5hs=
github.com/oklog/run v1.0.0 h1:Ru7dDtJNOyC66gQ5dQmaCa0qIsAUFY3sFpK1Xk8igrw=
github.com/oklog/run v1.0.0/go.mod h1:dlhp/R75TPv97u0XWUtDeV/lRKWPKSdTuV0TZvrmrQA=
github.com/oklog/run v1.1.0 h1:GEenZ1cK0+q0+wsJew9qUg/DyD8k3JzYsZAi5gYi2mA=
@@ -410,6 +428,29 @@ github.com/segmentio/analytics-go v3.1.0+incompatible h1:IyiOfUgQFVHvsykKKbdI7Zs
github.com/segmentio/analytics-go v3.1.0+incompatible/go.mod h1:C7CYBtQWk4vRk2RyLu0qOcbHJ18E3F1HV2C/8JvKN48=
github.com/segmentio/backo-go v0.0.0-20160424052352-204274ad699c h1:rsRTAcCR5CeNLkvgBVSjQoDGRRt6kggsE6XYBqCv2KQ=
github.com/segmentio/backo-go v0.0.0-20160424052352-204274ad699c/go.mod h1:kJ9mm9YmoWSkk+oQ+5Cj8DEoRCX2JT6As4kEtIIOp1M=
+github.com/sergi/go-diff v1.0.0/go.mod h1:0CfEIISq7TuYL3j771MWULgwwjU+GofnZX9QAmXWZgo=
+github.com/shurcooL/component v0.0.0-20170202220835-f88ec8f54cc4/go.mod h1:XhFIlyj5a1fBNx5aJTbKoIq0mNaPvOagO+HjB3EtxrY=
+github.com/shurcooL/events v0.0.0-20181021180414-410e4ca65f48/go.mod h1:5u70Mqkb5O5cxEA8nxTsgrgLehJeAw6Oc4Ab1c/P1HM=
+github.com/shurcooL/github_flavored_markdown v0.0.0-20181002035957-2122de532470/go.mod h1:2dOwnU2uBioM+SGy2aZoq1f/Sd1l9OkAeAUvjSyvgU0=
+github.com/shurcooL/go v0.0.0-20180423040247-9e1955d9fb6e/go.mod h1:TDJrrUr11Vxrven61rcy3hJMUqaf/CLWYhHNPmT14Lk=
+github.com/shurcooL/go-goon v0.0.0-20170922171312-37c2f522c041/go.mod h1:N5mDOmsrJOB+vfqUK+7DmDyjhSLIIBnXo9lvZJj3MWQ=
+github.com/shurcooL/gofontwoff v0.0.0-20180329035133-29b52fc0a18d/go.mod h1:05UtEgK5zq39gLST6uB0cf3NEHjETfB4Fgr3Gx5R9Vw=
+github.com/shurcooL/gopherjslib v0.0.0-20160914041154-feb6d3990c2c/go.mod h1:8d3azKNyqcHP1GaQE/c6dDgjkgSx2BZ4IoEi4F1reUI=
+github.com/shurcooL/highlight_diff v0.0.0-20170515013008-09bb4053de1b/go.mod h1:ZpfEhSmds4ytuByIcDnOLkTHGUI6KNqRNPDLHDk+mUU=
+github.com/shurcooL/highlight_go v0.0.0-20181028180052-98c3abbbae20/go.mod h1:UDKB5a1T23gOMUJrI+uSuH0VRDStOiUVSjBTRDVBVag=
+github.com/shurcooL/home v0.0.0-20181020052607-80b7ffcb30f9/go.mod h1:+rgNQw2P9ARFAs37qieuu7ohDNQ3gds9msbT2yn85sg=
+github.com/shurcooL/htmlg v0.0.0-20170918183704-d01228ac9e50/go.mod h1:zPn1wHpTIePGnXSHpsVPWEktKXHr6+SS6x/IKRb7cpw=
+github.com/shurcooL/httperror v0.0.0-20170206035902-86b7830d14cc/go.mod h1:aYMfkZ6DWSJPJ6c4Wwz3QtW22G7mf/PEgaB9k/ik5+Y=
+github.com/shurcooL/httpfs v0.0.0-20171119174359-809beceb2371/go.mod h1:ZY1cvUeJuFPAdZ/B6v7RHavJWZn2YPVFQ1OSXhCGOkg=
+github.com/shurcooL/httpgzip v0.0.0-20180522190206-b1c53ac65af9/go.mod h1:919LwcH0M7/W4fcZ0/jy0qGght1GIhqyS/EgWGH2j5Q=
+github.com/shurcooL/issues v0.0.0-20181008053335-6292fdc1e191/go.mod h1:e2qWDig5bLteJ4fwvDAc2NHzqFEthkqn7aOZAOpj+PQ=
+github.com/shurcooL/issuesapp v0.0.0-20180602232740-048589ce2241/go.mod h1:NPpHK2TI7iSaM0buivtFUc9offApnI0Alt/K8hcHy0I=
+github.com/shurcooL/notifications v0.0.0-20181007000457-627ab5aea122/go.mod h1:b5uSkrEVM1jQUspwbixRBhaIjIzL2xazXp6kntxYle0=
+github.com/shurcooL/octicon v0.0.0-20181028054416-fa4f57f9efb2/go.mod h1:eWdoE5JD4R5UVWDucdOPg1g2fqQRq78IQa9zlOV1vpQ=
+github.com/shurcooL/reactions v0.0.0-20181006231557-f2e0b4ca5b82/go.mod h1:TCR1lToEk4d2s07G3XGfz2QrgHXg4RJBvjrOozvoWfk=
+github.com/shurcooL/sanitized_anchor_name v0.0.0-20170918181015-86672fcb3f95/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc=
+github.com/shurcooL/users v0.0.0-20180125191416-49c67e49c537/go.mod h1:QJTqeLYEDaXHZDBsXlPCDqdhQuJkuw4NOtaxYe3xii4=
+github.com/shurcooL/webdavfs v0.0.0-20170829043945-18c3829fa133/go.mod h1:hKmq5kWdCj2z2KEozexVbfEZIWiTjhE0+UjmZgPqehw=
github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo=
github.com/sirupsen/logrus v1.4.2 h1:SPIRibHv4MatM3XXNO2BJeFLZwZ2LvZgfQ5+UNI2im4=
github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE=
@@ -419,6 +460,8 @@ github.com/smartystreets/assertions v1.0.0/go.mod h1:kHHU4qYBaI3q23Pp3VPrmWhuIUr
github.com/smartystreets/goconvey v0.0.0-20190330032615-68dc04aab96a/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA=
github.com/smartystreets/goconvey v0.0.0-20190710185942-9d28bd7c0945 h1:N8Bg45zpk/UcpNGnfJt2y/3lRWASHNTUET8owPYCgYI=
github.com/smartystreets/goconvey v0.0.0-20190710185942-9d28bd7c0945/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA=
+github.com/sourcegraph/annotate v0.0.0-20160123013949-f4cad6c6324d/go.mod h1:UdhH50NIW0fCiwBSr0co2m7BnFLdv4fQTgdqdJTHFeE=
+github.com/sourcegraph/syntaxhighlight v0.0.0-20170531221838-bd320f5d308e/go.mod h1:HuIsMU8RRBOtsCgI77wP899iHVBQpCmg4ErYMZB+2IA=
github.com/spf13/afero v1.1.2/go.mod h1:j4pytiNVoe2o6bmDsKpLACNPDBIoEAkihy7loJ1B0CQ=
github.com/spf13/afero v1.2.2 h1:5jhuqJyZCZf2JRofRvN/nIFgIWNzPa3/Vz8mYylgbWc=
github.com/spf13/afero v1.2.2/go.mod h1:9ZxEEn6pIJ8Rxe320qSDBk6AsU0r9pR7Q4OcevTdifk=
@@ -460,6 +503,14 @@ github.com/uber/jaeger-client-go v2.22.1+incompatible/go.mod h1:WVhlPFC8FDjOFMMW
github.com/uber/jaeger-lib v2.2.0+incompatible h1:MxZXOiR2JuoANZ3J6DE/U0kSFv/eJ/GfSYVCjK7dyaw=
github.com/uber/jaeger-lib v2.2.0+incompatible/go.mod h1:ComeNDZlWwrWnDv8aPp0Ba6+uUTzImX/AauajbLI56U=
github.com/ugorji/go/codec v0.0.0-20181204163529-d75b2dcb6bc8/go.mod h1:VFNgLljTbGfSG7qAOspJ7OScBnGdDN/yBr0sguwnwf0=
+github.com/viant/assertly v0.4.8/go.mod h1:aGifi++jvCrUaklKEKT0BU95igDNaqkvz+49uaYMPRU=
+github.com/viant/toolbox v0.24.0/go.mod h1:OxMCG57V0PXuIP2HNQrtJf2CjqdmbrOx5EkMILuUhzM=
+github.com/wiggin77/cfg v1.0.2 h1:NBUX+iJRr+RTncTqTNvajHwzduqbhCQjEqxLHr6Fk7A=
+github.com/wiggin77/cfg v1.0.2/go.mod h1:b3gotba2e5bXTqTW48DwIFoLc+4lWKP7WPi/CdvZ4aE=
+github.com/wiggin77/logr v1.0.3 h1:4Cj899GZJInB9vudlxsmLRDsBlsw9pE/nyJo9XZ/yzo=
+github.com/wiggin77/logr v1.0.3/go.mod h1:oIvnsSkyTQojUsr7QO0d4rE2afZbsTj/5WbiikGJu3E=
+github.com/wiggin77/merror v1.0.2 h1:V0nH9eFp64ASyaXC+pB5WpvBoCg7NUwvaCSKdzlcHqw=
+github.com/wiggin77/merror v1.0.2/go.mod h1:uQTcIU0Z6jRK4OwqganPYerzQxSFJ4GSHM3aurxxQpg=
github.com/xordataexchange/crypt v0.0.3-0.20170626215501-b2862e3d0a77/go.mod h1:aYKd//L2LvnjZzWKhF00oedf4jCCReLcmhLdhm1A27Q=
github.com/xtgo/uuid v0.0.0-20140804021211-a0b114877d4c h1:3lbZUMbMiGUW/LMkfsEABsc5zNT9+b1CvsJx47JzJ8g=
github.com/xtgo/uuid v0.0.0-20140804021211-a0b114877d4c/go.mod h1:UrdRz5enIKZ63MEE3IF9l2/ebyx59GyGgPi+tICQdmM=
@@ -479,6 +530,7 @@ go.uber.org/tools v0.0.0-20190618225709-2cfd321de3ee/go.mod h1:vJERXedbb3MVM5f9E
go.uber.org/zap v1.13.0 h1:nR6NoDBgAf67s68NhaXbsojM+2gxp3S1hWkHDl27pVU=
go.uber.org/zap v1.13.0/go.mod h1:zwrFLgMcdUuIBviXEYEH1YKNaOBnKXsx2IPda5bBwHM=
go4.org v0.0.0-20180809161055-417644f6feb5/go.mod h1:MkTOUMDaeVYJUOUsaDXIhWPZYa1yOyC1qaOBpL57BhE=
+golang.org/x/build v0.0.0-20190111050920-041ab4dc3f9d/go.mod h1:OWs+y06UdEOHN4y+MfF/py+xQ/tYqIWW03b70/CG9Rw=
golang.org/x/build v0.0.0-20190314133821-5284462c4bec/go.mod h1:atTaCNAy0f16Ah5aV1gMSwgiKVHwu/JncqDpuRr7lS4=
golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
golang.org/x/crypto v0.0.0-20181029021203-45a5f77698d3/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
@@ -486,6 +538,7 @@ golang.org/x/crypto v0.0.0-20181030102418-4d3f4d9ffa16/go.mod h1:6SG95UA2DQfeDnf
golang.org/x/crypto v0.0.0-20181203042331-505ab145d0a9/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2 h1:VklqNMn3ovrHsnt90PveolxSbWFaJdECFbxSq0Mqo2M=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
+golang.org/x/crypto v0.0.0-20190313024323-a1f597ede03a/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20190513172903-22d7a77e9e5f/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
@@ -526,6 +579,7 @@ golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73r
golang.org/x/net v0.0.0-20190125091013-d26f9f9a57f3/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
+golang.org/x/net v0.0.0-20190313220215-9f648a60d977/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190322120337-addf6b3196f6/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190522155817-f3200d17e092/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks=
@@ -565,6 +619,7 @@ golang.org/x/sys v0.0.0-20190129075346-302c3dd5f1cc/go.mod h1:STP8DvDyc/dI5b8T5h
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a h1:1BGLXjeY4akVXGgbC9HugT3Jv3hCI0z56oJR5vAMgBU=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
+golang.org/x/sys v0.0.0-20190316082340-a2f829d7f35f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190322080309-f49334f85ddc h1:4gbWbmmPFp4ySWICouJl6emP0MyS31yy9SrTlAGFT+g=
golang.org/x/sys v0.0.0-20190322080309-f49334f85ddc/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
@@ -586,6 +641,7 @@ golang.org/x/time v0.0.0-20180412165947-fbb02b2291d2/go.mod h1:tRJNPiyCQ0inRvYxb
golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/tools v0.0.0-20180828015842-6cd1fcedba52/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
+golang.org/x/tools v0.0.0-20181030000716-a0a13e073c7b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20181219222714-6e267b5cc78e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY=
@@ -608,6 +664,7 @@ golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8T
google.golang.org/api v0.0.0-20180910000450-7ca32eb868bf/go.mod h1:4mhQ8q/RsB7i+udVvVy5NUi08OU8ZlA0gRVgrF7VFY0=
google.golang.org/api v0.0.0-20181030000543-1d582fd0359e/go.mod h1:4mhQ8q/RsB7i+udVvVy5NUi08OU8ZlA0gRVgrF7VFY0=
google.golang.org/api v0.0.0-20181220000619-583d854617af/go.mod h1:4mhQ8q/RsB7i+udVvVy5NUi08OU8ZlA0gRVgrF7VFY0=
+google.golang.org/api v0.1.0/go.mod h1:UGEZY7KEX120AnNLIHFMKIo4obdJhkp2tPbaPlQx13Y=
google.golang.org/api v0.2.0/go.mod h1:IfRCZScioGtypHNTlz3gFk67J8uePVW7uDTBzXuIkhU=
google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM=
google.golang.org/appengine v1.2.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
@@ -619,7 +676,9 @@ google.golang.org/appengine v1.5.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7
google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
google.golang.org/genproto v0.0.0-20180831171423-11092d34479b/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
google.golang.org/genproto v0.0.0-20181029155118-b69ba1387ce2/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
+google.golang.org/genproto v0.0.0-20181202183823-bd91e49a0898/go.mod h1:7Ep/1NZk928CDR8SjdVbjWNpdIf6nzjE3BTgJDr2Atg=
google.golang.org/genproto v0.0.0-20181219182458-5a97ab628bfb/go.mod h1:7Ep/1NZk928CDR8SjdVbjWNpdIf6nzjE3BTgJDr2Atg=
+google.golang.org/genproto v0.0.0-20190306203927-b5d61aea6440/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
google.golang.org/genproto v0.0.0-20190307195333-5fe7a883aa19/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
google.golang.org/genproto v0.0.0-20190321212433-e79c0c59cdb5 h1:VchCZUJA1Lkjn3FxAtLPl4GotxoGt/E8ZIm9nVqbhQ8=
google.golang.org/genproto v0.0.0-20190321212433-e79c0c59cdb5/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
@@ -675,6 +734,8 @@ honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWh
honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
honnef.co/go/tools v0.0.1-2019.2.3 h1:3JgtbtFHMiCmsznwGVTUWbgGov+pVqnlf1dEJTNAXeM=
honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg=
+sourcegraph.com/sourcegraph/go-diff v0.5.0/go.mod h1:kuch7UrkMzY0X+p9CRK03kfuPQ2zzQcaEFbx8wA8rck=
+sourcegraph.com/sqs/pbtypes v0.0.0-20180604144634-d3ebe8f20ae4/go.mod h1:ketZ/q3QxT9HOBeFhu6RdvsftgpsbFHBF5Cas6cDKZ0=
willnorris.com/go/gifresize v1.0.0 h1:GKS68zjNhHMqkgNTv4iFAO/j/sNcVSOHQ7SqmDAIAmM=
willnorris.com/go/gifresize v1.0.0/go.mod h1:eBM8gogBGCcaH603vxSpnfjwXIpq6nmnj/jauBDKtAk=
willnorris.com/go/imageproxy v0.9.0 h1:pjhb8K4co5Xo0Q/uCeDBPwAtXxZzgvb/Ue0jgYqDapE=
diff --git a/model/config.go b/model/config.go
index f5b7b9a67a..ed319e2a9a 100644
--- a/model/config.go
+++ b/model/config.go
@@ -1055,6 +1055,46 @@ func (s *LogSettings) SetDefaults() {
}
}
+type ExperimentalAuditSettings struct {
+ Enabled *bool `restricted:"true"`
+ IP *string `restricted:"true"`
+ Port *int `restricted:"true"`
+ Tag *string `restricted:"true"`
+ Cert *string `restricted:"true"`
+ Insecure *bool `restricted:"true"`
+ MaxQSize *int `restricted:"true"`
+}
+
+func (s *ExperimentalAuditSettings) SetDefaults() {
+ if s.Enabled == nil {
+ s.Enabled = NewBool(false)
+ }
+
+ if s.IP == nil {
+ s.IP = NewString("localhost")
+ }
+
+ if s.Port == nil {
+ s.Port = NewInt(6514)
+ }
+
+ if s.Tag == nil {
+ s.Tag = NewString("")
+ }
+
+ if s.Cert == nil {
+ s.Cert = NewString("")
+ }
+
+ if s.Insecure == nil {
+ s.Insecure = NewBool(false)
+ }
+
+ if s.MaxQSize == nil {
+ s.MaxQSize = NewInt(1000)
+ }
+}
+
type NotificationLogSettings struct {
EnableConsole *bool `restricted:"true"`
ConsoleLevel *string `restricted:"true"`
@@ -2562,40 +2602,41 @@ func (s *ImageProxySettings) SetDefaults(ss ServiceSettings) {
type ConfigFunc func() *Config
type Config struct {
- ServiceSettings ServiceSettings
- TeamSettings TeamSettings
- ClientRequirements ClientRequirements
- SqlSettings SqlSettings
- LogSettings LogSettings
- NotificationLogSettings NotificationLogSettings
- PasswordSettings PasswordSettings
- FileSettings FileSettings
- EmailSettings EmailSettings
- RateLimitSettings RateLimitSettings
- PrivacySettings PrivacySettings
- SupportSettings SupportSettings
- AnnouncementSettings AnnouncementSettings
- ThemeSettings ThemeSettings
- GitLabSettings SSOSettings
- GoogleSettings SSOSettings
- Office365Settings Office365Settings
- LdapSettings LdapSettings
- ComplianceSettings ComplianceSettings
- LocalizationSettings LocalizationSettings
- SamlSettings SamlSettings
- NativeAppSettings NativeAppSettings
- ClusterSettings ClusterSettings
- MetricsSettings MetricsSettings
- ExperimentalSettings ExperimentalSettings
- AnalyticsSettings AnalyticsSettings
- ElasticsearchSettings ElasticsearchSettings
- DataRetentionSettings DataRetentionSettings
- MessageExportSettings MessageExportSettings
- JobSettings JobSettings
- PluginSettings PluginSettings
- DisplaySettings DisplaySettings
- GuestAccountsSettings GuestAccountsSettings
- ImageProxySettings ImageProxySettings
+ ServiceSettings ServiceSettings
+ TeamSettings TeamSettings
+ ClientRequirements ClientRequirements
+ SqlSettings SqlSettings
+ LogSettings LogSettings
+ ExperimentalAuditSettings ExperimentalAuditSettings
+ NotificationLogSettings NotificationLogSettings
+ PasswordSettings PasswordSettings
+ FileSettings FileSettings
+ EmailSettings EmailSettings
+ RateLimitSettings RateLimitSettings
+ PrivacySettings PrivacySettings
+ SupportSettings SupportSettings
+ AnnouncementSettings AnnouncementSettings
+ ThemeSettings ThemeSettings
+ GitLabSettings SSOSettings
+ GoogleSettings SSOSettings
+ Office365Settings Office365Settings
+ LdapSettings LdapSettings
+ ComplianceSettings ComplianceSettings
+ LocalizationSettings LocalizationSettings
+ SamlSettings SamlSettings
+ NativeAppSettings NativeAppSettings
+ ClusterSettings ClusterSettings
+ MetricsSettings MetricsSettings
+ ExperimentalSettings ExperimentalSettings
+ AnalyticsSettings AnalyticsSettings
+ ElasticsearchSettings ElasticsearchSettings
+ DataRetentionSettings DataRetentionSettings
+ MessageExportSettings MessageExportSettings
+ JobSettings JobSettings
+ PluginSettings PluginSettings
+ DisplaySettings DisplaySettings
+ GuestAccountsSettings GuestAccountsSettings
+ ImageProxySettings ImageProxySettings
}
func (o *Config) Clone() *Config {
@@ -2674,6 +2715,7 @@ func (o *Config) SetDefaults() {
o.DataRetentionSettings.SetDefaults()
o.RateLimitSettings.SetDefaults()
o.LogSettings.SetDefaults()
+ o.ExperimentalAuditSettings.SetDefaults()
o.NotificationLogSettings.SetDefaults()
o.JobSettings.SetDefaults()
o.MessageExportSettings.SetDefaults()
diff --git a/model/team_member.go b/model/team_member.go
index 0c0a3435da..c00b1a3b03 100644
--- a/model/team_member.go
+++ b/model/team_member.go
@@ -5,6 +5,7 @@ package model
import (
"encoding/json"
+ "fmt"
"io"
"net/http"
"strings"
@@ -89,6 +90,10 @@ func EmailInviteWithErrorToJson(o []*EmailInviteWithError) string {
}
}
+func EmailInviteWithErrorToString(o *EmailInviteWithError) string {
+ return fmt.Sprintf("%s:%s", o.Email, o.Error.Error())
+}
+
func TeamMembersWithErrorToTeamMembers(o []*TeamMemberWithError) []*TeamMember {
var ret []*TeamMember
for _, o := range o {
@@ -107,6 +112,10 @@ func TeamMembersWithErrorToJson(o []*TeamMemberWithError) string {
}
}
+func TeamMemberWithErrorToString(o *TeamMemberWithError) string {
+ return fmt.Sprintf("%s:%s", o.UserId, o.Error.Error())
+}
+
func TeamMembersWithErrorFromJson(data io.Reader) []*TeamMemberWithError {
var o []*TeamMemberWithError
json.NewDecoder(data).Decode(&o)
diff --git a/vendor/github.com/RackSec/srslog/.gitignore b/vendor/github.com/RackSec/srslog/.gitignore
new file mode 100644
index 0000000000..ebf0f2e4e3
--- /dev/null
+++ b/vendor/github.com/RackSec/srslog/.gitignore
@@ -0,0 +1 @@
+.cover
diff --git a/vendor/github.com/RackSec/srslog/.travis.yml b/vendor/github.com/RackSec/srslog/.travis.yml
new file mode 100644
index 0000000000..4e5c4f0753
--- /dev/null
+++ b/vendor/github.com/RackSec/srslog/.travis.yml
@@ -0,0 +1,18 @@
+sudo: required
+dist: trusty
+group: edge
+language: go
+go:
+- 1.5
+before_install:
+ - pip install --user codecov
+script:
+- |
+ go get ./...
+ go test -v -coverprofile=coverage.txt -covermode=atomic
+ go vet
+after_success:
+ - codecov
+notifications:
+ slack:
+ secure: dtDue9gP6CRR1jYjEf6raXXFak3QKGcCFvCf5mfvv5XScdpmc3udwgqc5TdyjC0goaC9OK/4jTcCD30dYZm/u6ux3E9mo3xwMl2xRLHx76p5r9rSQtloH19BDwA2+A+bpDfFQVz05k2YXuTiGSvNMMdwzx+Dr294Sl/z43RFB4+b9/R/6LlFpRW89IwftvpLAFnBy4K/ZcspQzKM+rQfQTL5Kk+iZ/KBsuR/VziDq6MoJ8t43i4ee8vwS06vFBKDbUiZ4FIZpLgc2RAL5qso5aWRKYXL6waXfoKHZWKPe0w4+9IY1rDJxG1jEb7YGgcbLaF9xzPRRs2b2yO/c87FKpkh6PDgYHfLjpgXotCoojZrL4p1x6MI1ldJr3NhARGPxS9r4liB9n6Y5nD+ErXi1IMf55fuUHcPY27Jc0ySeLFeM6cIWJ8OhFejCgGw6a5DnnmJo0PqopsaBDHhadpLejT1+K6bL2iGkT4SLcVNuRGLs+VyuNf1+5XpkWZvy32vquO7SZOngLLBv+GIem+t3fWm0Z9s/0i1uRCQei1iUutlYjoV/LBd35H2rhob4B5phIuJin9kb0zbHf6HnaoN0CtN8r0d8G5CZiInVlG5Xcid5Byb4dddf5U2EJTDuCMVyyiM7tcnfjqw9UbVYNxtYM9SzcqIq+uVqM8pYL9xSec=
diff --git a/vendor/github.com/RackSec/srslog/CODE_OF_CONDUCT.md b/vendor/github.com/RackSec/srslog/CODE_OF_CONDUCT.md
new file mode 100644
index 0000000000..18ac49fc75
--- /dev/null
+++ b/vendor/github.com/RackSec/srslog/CODE_OF_CONDUCT.md
@@ -0,0 +1,50 @@
+# Contributor Code of Conduct
+
+As contributors and maintainers of this project, and in the interest of
+fostering an open and welcoming community, we pledge to respect all people who
+contribute through reporting issues, posting feature requests, updating
+documentation, submitting pull requests or patches, and other activities.
+
+We are committed to making participation in this project a harassment-free
+experience for everyone, regardless of level of experience, gender, gender
+identity and expression, sexual orientation, disability, personal appearance,
+body size, race, ethnicity, age, religion, or nationality.
+
+Examples of unacceptable behavior by participants include:
+
+* The use of sexualized language or imagery
+* Personal attacks
+* Trolling or insulting/derogatory comments
+* Public or private harassment
+* Publishing other's private information, such as physical or electronic
+ addresses, without explicit permission
+* Other unethical or unprofessional conduct
+
+Project maintainers have the right and responsibility to remove, edit, or
+reject comments, commits, code, wiki edits, issues, and other contributions
+that are not aligned to this Code of Conduct, or to ban temporarily or
+permanently any contributor for other behaviors that they deem inappropriate,
+threatening, offensive, or harmful.
+
+By adopting this Code of Conduct, project maintainers commit themselves to
+fairly and consistently applying these principles to every aspect of managing
+this project. Project maintainers who do not follow or enforce the Code of
+Conduct may be permanently removed from the project team.
+
+This Code of Conduct applies both within project spaces and in public spaces
+when an individual is representing the project or its community.
+
+Instances of abusive, harassing, or otherwise unacceptable behavior may be
+reported by contacting a project maintainer at [sirsean@gmail.com]. All
+complaints will be reviewed and investigated and will result in a response that
+is deemed necessary and appropriate to the circumstances. Maintainers are
+obligated to maintain confidentiality with regard to the reporter of an
+incident.
+
+
+This Code of Conduct is adapted from the [Contributor Covenant][homepage],
+version 1.3.0, available at
+[http://contributor-covenant.org/version/1/3/0/][version]
+
+[homepage]: http://contributor-covenant.org
+[version]: http://contributor-covenant.org/version/1/3/0/
diff --git a/vendor/github.com/RackSec/srslog/LICENSE b/vendor/github.com/RackSec/srslog/LICENSE
new file mode 100644
index 0000000000..9269338fbb
--- /dev/null
+++ b/vendor/github.com/RackSec/srslog/LICENSE
@@ -0,0 +1,27 @@
+Copyright (c) 2015 Rackspace. All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are
+met:
+
+ * Redistributions of source code must retain the above copyright
+notice, this list of conditions and the following disclaimer.
+ * Redistributions in binary form must reproduce the above
+copyright notice, this list of conditions and the following disclaimer
+in the documentation and/or other materials provided with the
+distribution.
+ * Neither the name of Google Inc. nor the names of its
+contributors may be used to endorse or promote products derived from
+this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
diff --git a/vendor/github.com/RackSec/srslog/README.md b/vendor/github.com/RackSec/srslog/README.md
new file mode 100644
index 0000000000..dcacc34881
--- /dev/null
+++ b/vendor/github.com/RackSec/srslog/README.md
@@ -0,0 +1,147 @@
+[](https://travis-ci.org/RackSec/srslog)
+
+# srslog
+
+Go has a `syslog` package in the standard library, but it has the following
+shortcomings:
+
+1. It doesn't have TLS support
+2. [According to bradfitz on the Go team, it is no longer being maintained.](https://github.com/golang/go/issues/13449#issuecomment-161204716)
+
+I agree that it doesn't need to be in the standard library. So, I've
+followed Brad's suggestion and have made a separate project to handle syslog.
+
+This code was taken directly from the Go project as a base to start from.
+
+However, this _does_ have TLS support.
+
+# Usage
+
+Basic usage retains the same interface as the original `syslog` package. We
+only added to the interface where required to support new functionality.
+
+Switch from the standard library:
+
+```
+import(
+ //"log/syslog"
+ syslog "github.com/RackSec/srslog"
+)
+```
+
+You can still use it for local syslog:
+
+```
+w, err := syslog.Dial("", "", syslog.LOG_ERR, "testtag")
+```
+
+Or to unencrypted UDP:
+
+```
+w, err := syslog.Dial("udp", "192.168.0.50:514", syslog.LOG_ERR, "testtag")
+```
+
+Or to unencrypted TCP:
+
+```
+w, err := syslog.Dial("tcp", "192.168.0.51:514", syslog.LOG_ERR, "testtag")
+```
+
+But now you can also send messages via TLS-encrypted TCP:
+
+```
+w, err := syslog.DialWithTLSCertPath("tcp+tls", "192.168.0.52:514", syslog.LOG_ERR, "testtag", "/path/to/servercert.pem")
+```
+
+And if you need more control over your TLS configuration :
+
+```
+pool := x509.NewCertPool()
+serverCert, err := ioutil.ReadFile("/path/to/servercert.pem")
+if err != nil {
+ return nil, err
+}
+pool.AppendCertsFromPEM(serverCert)
+config := tls.Config{
+ RootCAs: pool,
+}
+
+w, err := DialWithTLSConfig(network, raddr, priority, tag, &config)
+```
+
+(Note that in both TLS cases, this uses a self-signed certificate, where the
+remote syslog server has the keypair and the client has only the public key.)
+
+And then to write log messages, continue like so:
+
+```
+if err != nil {
+ log.Fatal("failed to connect to syslog:", err)
+}
+defer w.Close()
+
+w.Alert("this is an alert")
+w.Crit("this is critical")
+w.Err("this is an error")
+w.Warning("this is a warning")
+w.Notice("this is a notice")
+w.Info("this is info")
+w.Debug("this is debug")
+w.Write([]byte("these are some bytes"))
+```
+
+If you need further control over connection attempts, you can use the DialWithCustomDialer
+function. To continue with the DialWithTLSConfig example:
+
+```
+netDialer := &net.Dialer{Timeout: time.Second*5} // easy timeouts
+realNetwork := "tcp" // real network, other vars your dail func can close over
+dial := func(network, addr string) (net.Conn, error) {
+ // cannot use "network" here as it'll simply be "custom" which will fail
+ return tls.DialWithDialer(netDialer, realNetwork, addr, &config)
+}
+
+w, err := DialWithCustomDialer("custom", "192.168.0.52:514", syslog.LOG_ERR, "testtag", dial)
+```
+
+Your custom dial func can set timeouts, proxy connections, and do whatever else it needs before returning a net.Conn.
+
+# Generating TLS Certificates
+
+We've provided a script that you can use to generate a self-signed keypair:
+
+```
+pip install cryptography
+python script/gen-certs.py
+```
+
+That outputs the public key and private key to standard out. Put those into
+`.pem` files. (And don't put them into any source control. The certificate in
+the `test` directory is used by the unit tests, and please do not actually use
+it anywhere else.)
+
+# Running Tests
+
+Run the tests as usual:
+
+```
+go test
+```
+
+But we've also provided a test coverage script that will show you which
+lines of code are not covered:
+
+```
+script/coverage --html
+```
+
+That will open a new browser tab showing coverage information.
+
+# License
+
+This project uses the New BSD License, the same as the Go project itself.
+
+# Code of Conduct
+
+Please note that this project is released with a Contributor Code of Conduct.
+By participating in this project you agree to abide by its terms.
diff --git a/vendor/github.com/RackSec/srslog/constants.go b/vendor/github.com/RackSec/srslog/constants.go
new file mode 100644
index 0000000000..600801ee84
--- /dev/null
+++ b/vendor/github.com/RackSec/srslog/constants.go
@@ -0,0 +1,68 @@
+package srslog
+
+import (
+ "errors"
+)
+
+// Priority is a combination of the syslog facility and
+// severity. For example, LOG_ALERT | LOG_FTP sends an alert severity
+// message from the FTP facility. The default severity is LOG_EMERG;
+// the default facility is LOG_KERN.
+type Priority int
+
+const severityMask = 0x07
+const facilityMask = 0xf8
+
+const (
+ // Severity.
+
+ // From /usr/include/sys/syslog.h.
+ // These are the same on Linux, BSD, and OS X.
+ LOG_EMERG Priority = iota
+ LOG_ALERT
+ LOG_CRIT
+ LOG_ERR
+ LOG_WARNING
+ LOG_NOTICE
+ LOG_INFO
+ LOG_DEBUG
+)
+
+const (
+ // Facility.
+
+ // From /usr/include/sys/syslog.h.
+ // These are the same up to LOG_FTP on Linux, BSD, and OS X.
+ LOG_KERN Priority = iota << 3
+ LOG_USER
+ LOG_MAIL
+ LOG_DAEMON
+ LOG_AUTH
+ LOG_SYSLOG
+ LOG_LPR
+ LOG_NEWS
+ LOG_UUCP
+ LOG_CRON
+ LOG_AUTHPRIV
+ LOG_FTP
+ _ // unused
+ _ // unused
+ _ // unused
+ _ // unused
+ LOG_LOCAL0
+ LOG_LOCAL1
+ LOG_LOCAL2
+ LOG_LOCAL3
+ LOG_LOCAL4
+ LOG_LOCAL5
+ LOG_LOCAL6
+ LOG_LOCAL7
+)
+
+func validatePriority(p Priority) error {
+ if p < 0 || p > LOG_LOCAL7|LOG_DEBUG {
+ return errors.New("log/syslog: invalid priority")
+ } else {
+ return nil
+ }
+}
diff --git a/vendor/github.com/RackSec/srslog/dialer.go b/vendor/github.com/RackSec/srslog/dialer.go
new file mode 100644
index 0000000000..fc7e53860f
--- /dev/null
+++ b/vendor/github.com/RackSec/srslog/dialer.go
@@ -0,0 +1,104 @@
+package srslog
+
+import (
+ "crypto/tls"
+ "net"
+)
+
+// dialerFunctionWrapper is a simple object that consists of a dialer function
+// and its name. This is primarily for testing, so we can make sure that the
+// getDialer method returns the correct dialer function. However, if you ever
+// find that you need to check which dialer function you have, this would also
+// be useful for you without having to use reflection.
+type dialerFunctionWrapper struct {
+ Name string
+ Dialer func() (serverConn, string, error)
+}
+
+// Call the wrapped dialer function and return its return values.
+func (df dialerFunctionWrapper) Call() (serverConn, string, error) {
+ return df.Dialer()
+}
+
+// getDialer returns a "dialer" function that can be called to connect to a
+// syslog server.
+//
+// Each dialer function is responsible for dialing the remote host and returns
+// a serverConn, the hostname (or a default if the Writer has not specified a
+// hostname), and an error in case dialing fails.
+//
+// The reason for separate dialers is that different network types may need
+// to dial their connection differently, yet still provide a net.Conn interface
+// that you can use once they have dialed. Rather than an increasingly long
+// conditional, we have a map of network -> dialer function (with a sane default
+// value), and adding a new network type is as easy as writing the dialer
+// function and adding it to the map.
+func (w *Writer) getDialer() dialerFunctionWrapper {
+ dialers := map[string]dialerFunctionWrapper{
+ "": dialerFunctionWrapper{"unixDialer", w.unixDialer},
+ "tcp+tls": dialerFunctionWrapper{"tlsDialer", w.tlsDialer},
+ "custom": dialerFunctionWrapper{"customDialer", w.customDialer},
+ }
+ dialer, ok := dialers[w.network]
+ if !ok {
+ dialer = dialerFunctionWrapper{"basicDialer", w.basicDialer}
+ }
+ return dialer
+}
+
+// unixDialer uses the unixSyslog method to open a connection to the syslog
+// daemon running on the local machine.
+func (w *Writer) unixDialer() (serverConn, string, error) {
+ sc, err := unixSyslog()
+ hostname := w.hostname
+ if hostname == "" {
+ hostname = "localhost"
+ }
+ return sc, hostname, err
+}
+
+// tlsDialer connects to TLS over TCP, and is used for the "tcp+tls" network
+// type.
+func (w *Writer) tlsDialer() (serverConn, string, error) {
+ c, err := tls.Dial("tcp", w.raddr, w.tlsConfig)
+ var sc serverConn
+ hostname := w.hostname
+ if err == nil {
+ sc = &netConn{conn: c}
+ if hostname == "" {
+ hostname = c.LocalAddr().String()
+ }
+ }
+ return sc, hostname, err
+}
+
+// basicDialer is the most common dialer for syslog, and supports both TCP and
+// UDP connections.
+func (w *Writer) basicDialer() (serverConn, string, error) {
+ c, err := net.Dial(w.network, w.raddr)
+ var sc serverConn
+ hostname := w.hostname
+ if err == nil {
+ sc = &netConn{conn: c}
+ if hostname == "" {
+ hostname = c.LocalAddr().String()
+ }
+ }
+ return sc, hostname, err
+}
+
+// customDialer uses the custom dialer when the Writer was created
+// giving developers total control over how connections are made and returned.
+// Note it does not check if cdialer is nil, as it should only be referenced from getDialer.
+func (w *Writer) customDialer() (serverConn, string, error) {
+ c, err := w.customDial(w.network, w.raddr)
+ var sc serverConn
+ hostname := w.hostname
+ if err == nil {
+ sc = &netConn{conn: c}
+ if hostname == "" {
+ hostname = c.LocalAddr().String()
+ }
+ }
+ return sc, hostname, err
+}
diff --git a/vendor/github.com/RackSec/srslog/formatter.go b/vendor/github.com/RackSec/srslog/formatter.go
new file mode 100644
index 0000000000..e306fd6713
--- /dev/null
+++ b/vendor/github.com/RackSec/srslog/formatter.go
@@ -0,0 +1,58 @@
+package srslog
+
+import (
+ "fmt"
+ "os"
+ "time"
+)
+
+const appNameMaxLength = 48 // limit to 48 chars as per RFC5424
+
+// Formatter is a type of function that takes the consituent parts of a
+// syslog message and returns a formatted string. A different Formatter is
+// defined for each different syslog protocol we support.
+type Formatter func(p Priority, hostname, tag, content string) string
+
+// DefaultFormatter is the original format supported by the Go syslog package,
+// and is a non-compliant amalgamation of 3164 and 5424 that is intended to
+// maximize compatibility.
+func DefaultFormatter(p Priority, hostname, tag, content string) string {
+ timestamp := time.Now().Format(time.RFC3339)
+ msg := fmt.Sprintf("<%d> %s %s %s[%d]: %s",
+ p, timestamp, hostname, tag, os.Getpid(), content)
+ return msg
+}
+
+// UnixFormatter omits the hostname, because it is only used locally.
+func UnixFormatter(p Priority, hostname, tag, content string) string {
+ timestamp := time.Now().Format(time.Stamp)
+ msg := fmt.Sprintf("<%d>%s %s[%d]: %s",
+ p, timestamp, tag, os.Getpid(), content)
+ return msg
+}
+
+// RFC3164Formatter provides an RFC 3164 compliant message.
+func RFC3164Formatter(p Priority, hostname, tag, content string) string {
+ timestamp := time.Now().Format(time.Stamp)
+ msg := fmt.Sprintf("<%d>%s %s %s[%d]: %s",
+ p, timestamp, hostname, tag, os.Getpid(), content)
+ return msg
+}
+
+// if string's length is greater than max, then use the last part
+func truncateStartStr(s string, max int) string {
+ if (len(s) > max) {
+ return s[len(s) - max:]
+ }
+ return s
+}
+
+// RFC5424Formatter provides an RFC 5424 compliant message.
+func RFC5424Formatter(p Priority, hostname, tag, content string) string {
+ timestamp := time.Now().Format(time.RFC3339)
+ pid := os.Getpid()
+ appName := truncateStartStr(os.Args[0], appNameMaxLength)
+ msg := fmt.Sprintf("<%d>%d %s %s %s %d %s - %s",
+ p, 1, timestamp, hostname, appName, pid, tag, content)
+ return msg
+}
diff --git a/vendor/github.com/RackSec/srslog/framer.go b/vendor/github.com/RackSec/srslog/framer.go
new file mode 100644
index 0000000000..ab46f0de74
--- /dev/null
+++ b/vendor/github.com/RackSec/srslog/framer.go
@@ -0,0 +1,24 @@
+package srslog
+
+import (
+ "fmt"
+)
+
+// Framer is a type of function that takes an input string (typically an
+// already-formatted syslog message) and applies "message framing" to it. We
+// have different framers because different versions of the syslog protocol
+// and its transport requirements define different framing behavior.
+type Framer func(in string) string
+
+// DefaultFramer does nothing, since there is no framing to apply. This is
+// the original behavior of the Go syslog package, and is also typically used
+// for UDP syslog.
+func DefaultFramer(in string) string {
+ return in
+}
+
+// RFC5425MessageLengthFramer prepends the message length to the front of the
+// provided message, as defined in RFC 5425.
+func RFC5425MessageLengthFramer(in string) string {
+ return fmt.Sprintf("%d %s", len(in), in)
+}
diff --git a/vendor/github.com/RackSec/srslog/net_conn.go b/vendor/github.com/RackSec/srslog/net_conn.go
new file mode 100644
index 0000000000..75e4c3ca1c
--- /dev/null
+++ b/vendor/github.com/RackSec/srslog/net_conn.go
@@ -0,0 +1,30 @@
+package srslog
+
+import (
+ "net"
+)
+
+// netConn has an internal net.Conn and adheres to the serverConn interface,
+// allowing us to send syslog messages over the network.
+type netConn struct {
+ conn net.Conn
+}
+
+// writeString formats syslog messages using time.RFC3339 and includes the
+// hostname, and sends the message to the connection.
+func (n *netConn) writeString(framer Framer, formatter Formatter, p Priority, hostname, tag, msg string) error {
+ if framer == nil {
+ framer = DefaultFramer
+ }
+ if formatter == nil {
+ formatter = DefaultFormatter
+ }
+ formattedMessage := framer(formatter(p, hostname, tag, msg))
+ _, err := n.conn.Write([]byte(formattedMessage))
+ return err
+}
+
+// close the network connection
+func (n *netConn) close() error {
+ return n.conn.Close()
+}
diff --git a/vendor/github.com/RackSec/srslog/srslog.go b/vendor/github.com/RackSec/srslog/srslog.go
new file mode 100644
index 0000000000..b47ad72df4
--- /dev/null
+++ b/vendor/github.com/RackSec/srslog/srslog.go
@@ -0,0 +1,125 @@
+package srslog
+
+import (
+ "crypto/tls"
+ "crypto/x509"
+ "errors"
+ "io/ioutil"
+ "log"
+ "net"
+ "os"
+)
+
+// This interface allows us to work with both local and network connections,
+// and enables Solaris support (see syslog_unix.go).
+type serverConn interface {
+ writeString(framer Framer, formatter Formatter, p Priority, hostname, tag, s string) error
+ close() error
+}
+
+// DialFunc is the function signature to be used for a custom dialer callback
+// with DialWithCustomDialer
+type DialFunc func(string, string) (net.Conn, error)
+
+// New establishes a new connection to the system log daemon. Each
+// write to the returned Writer sends a log message with the given
+// priority and prefix.
+func New(priority Priority, tag string) (w *Writer, err error) {
+ return Dial("", "", priority, tag)
+}
+
+// Dial establishes a connection to a log daemon by connecting to
+// address raddr on the specified network. Each write to the returned
+// Writer sends a log message with the given facility, severity and
+// tag.
+// If network is empty, Dial will connect to the local syslog server.
+func Dial(network, raddr string, priority Priority, tag string) (*Writer, error) {
+ return DialWithTLSConfig(network, raddr, priority, tag, nil)
+}
+
+// ErrNilDialFunc is returned from DialWithCustomDialer when a nil DialFunc is passed,
+// avoiding a nil pointer deference panic.
+var ErrNilDialFunc = errors.New("srslog: nil DialFunc passed to DialWithCustomDialer")
+
+// DialWithCustomDialer establishes a connection by calling customDial.
+// Each write to the returned Writer sends a log message with the given facility, severity and tag.
+// Network must be "custom" in order for this package to use customDial.
+// While network and raddr will be passed to customDial, it is allowed for customDial to ignore them.
+// If customDial is nil, this function returns ErrNilDialFunc.
+func DialWithCustomDialer(network, raddr string, priority Priority, tag string, customDial DialFunc) (*Writer, error) {
+ if customDial == nil {
+ return nil, ErrNilDialFunc
+ }
+ return dialAllParameters(network, raddr, priority, tag, nil, customDial)
+}
+
+// DialWithTLSCertPath establishes a secure connection to a log daemon by connecting to
+// address raddr on the specified network. It uses certPath to load TLS certificates and configure
+// the secure connection.
+func DialWithTLSCertPath(network, raddr string, priority Priority, tag, certPath string) (*Writer, error) {
+ serverCert, err := ioutil.ReadFile(certPath)
+ if err != nil {
+ return nil, err
+ }
+
+ return DialWithTLSCert(network, raddr, priority, tag, serverCert)
+}
+
+// DialWIthTLSCert establishes a secure connection to a log daemon by connecting to
+// address raddr on the specified network. It uses serverCert to load a TLS certificate
+// and configure the secure connection.
+func DialWithTLSCert(network, raddr string, priority Priority, tag string, serverCert []byte) (*Writer, error) {
+ pool := x509.NewCertPool()
+ pool.AppendCertsFromPEM(serverCert)
+ config := tls.Config{
+ RootCAs: pool,
+ }
+
+ return DialWithTLSConfig(network, raddr, priority, tag, &config)
+}
+
+// DialWithTLSConfig establishes a secure connection to a log daemon by connecting to
+// address raddr on the specified network. It uses tlsConfig to configure the secure connection.
+func DialWithTLSConfig(network, raddr string, priority Priority, tag string, tlsConfig *tls.Config) (*Writer, error) {
+ return dialAllParameters(network, raddr, priority, tag, tlsConfig, nil)
+}
+
+// implementation of the various functions above
+func dialAllParameters(network, raddr string, priority Priority, tag string, tlsConfig *tls.Config, customDial DialFunc) (*Writer, error) {
+ if err := validatePriority(priority); err != nil {
+ return nil, err
+ }
+
+ if tag == "" {
+ tag = os.Args[0]
+ }
+ hostname, _ := os.Hostname()
+
+ w := &Writer{
+ priority: priority,
+ tag: tag,
+ hostname: hostname,
+ network: network,
+ raddr: raddr,
+ tlsConfig: tlsConfig,
+ customDial: customDial,
+ }
+
+ _, err := w.connect()
+ if err != nil {
+ return nil, err
+ }
+ return w, err
+}
+
+// NewLogger creates a log.Logger whose output is written to
+// the system log service with the specified priority. The logFlag
+// argument is the flag set passed through to log.New to create
+// the Logger.
+func NewLogger(p Priority, logFlag int) (*log.Logger, error) {
+ s, err := New(p, "")
+ if err != nil {
+ return nil, err
+ }
+ return log.New(s, "", logFlag), nil
+}
diff --git a/vendor/github.com/RackSec/srslog/srslog_unix.go b/vendor/github.com/RackSec/srslog/srslog_unix.go
new file mode 100644
index 0000000000..a04d9396f6
--- /dev/null
+++ b/vendor/github.com/RackSec/srslog/srslog_unix.go
@@ -0,0 +1,54 @@
+package srslog
+
+import (
+ "errors"
+ "io"
+ "net"
+)
+
+// unixSyslog opens a connection to the syslog daemon running on the
+// local machine using a Unix domain socket. This function exists because of
+// Solaris support as implemented by gccgo. On Solaris you can not
+// simply open a TCP connection to the syslog daemon. The gccgo
+// sources have a syslog_solaris.go file that implements unixSyslog to
+// return a type that satisfies the serverConn interface and simply calls the C
+// library syslog function.
+func unixSyslog() (conn serverConn, err error) {
+ logTypes := []string{"unixgram", "unix"}
+ logPaths := []string{"/dev/log", "/var/run/syslog", "/var/run/log"}
+ for _, network := range logTypes {
+ for _, path := range logPaths {
+ conn, err := net.Dial(network, path)
+ if err != nil {
+ continue
+ } else {
+ return &localConn{conn: conn}, nil
+ }
+ }
+ }
+ return nil, errors.New("Unix syslog delivery error")
+}
+
+// localConn adheres to the serverConn interface, allowing us to send syslog
+// messages to the local syslog daemon over a Unix domain socket.
+type localConn struct {
+ conn io.WriteCloser
+}
+
+// writeString formats syslog messages using time.Stamp instead of time.RFC3339,
+// and omits the hostname (because it is expected to be used locally).
+func (n *localConn) writeString(framer Framer, formatter Formatter, p Priority, hostname, tag, msg string) error {
+ if framer == nil {
+ framer = DefaultFramer
+ }
+ if formatter == nil {
+ formatter = UnixFormatter
+ }
+ _, err := n.conn.Write([]byte(framer(formatter(p, hostname, tag, msg))))
+ return err
+}
+
+// close the (local) network connection
+func (n *localConn) close() error {
+ return n.conn.Close()
+}
diff --git a/vendor/github.com/RackSec/srslog/writer.go b/vendor/github.com/RackSec/srslog/writer.go
new file mode 100644
index 0000000000..86bccba157
--- /dev/null
+++ b/vendor/github.com/RackSec/srslog/writer.go
@@ -0,0 +1,201 @@
+package srslog
+
+import (
+ "crypto/tls"
+ "strings"
+ "sync"
+)
+
+// A Writer is a connection to a syslog server.
+type Writer struct {
+ priority Priority
+ tag string
+ hostname string
+ network string
+ raddr string
+ tlsConfig *tls.Config
+ framer Framer
+ formatter Formatter
+
+ //non-nil if custom dialer set, used in getDialer
+ customDial DialFunc
+
+ mu sync.RWMutex // guards conn
+ conn serverConn
+}
+
+// getConn provides access to the internal conn, protected by a mutex. The
+// conn is threadsafe, so it can be used while unlocked, but we want to avoid
+// race conditions on grabbing a reference to it.
+func (w *Writer) getConn() serverConn {
+ w.mu.RLock()
+ conn := w.conn
+ w.mu.RUnlock()
+ return conn
+}
+
+// setConn updates the internal conn, protected by a mutex.
+func (w *Writer) setConn(c serverConn) {
+ w.mu.Lock()
+ w.conn = c
+ w.mu.Unlock()
+}
+
+// connect makes a connection to the syslog server.
+func (w *Writer) connect() (serverConn, error) {
+ conn := w.getConn()
+ if conn != nil {
+ // ignore err from close, it makes sense to continue anyway
+ conn.close()
+ w.setConn(nil)
+ }
+
+ var hostname string
+ var err error
+ dialer := w.getDialer()
+ conn, hostname, err = dialer.Call()
+ if err == nil {
+ w.setConn(conn)
+ w.hostname = hostname
+
+ return conn, nil
+ } else {
+ return nil, err
+ }
+}
+
+// SetFormatter changes the formatter function for subsequent messages.
+func (w *Writer) SetFormatter(f Formatter) {
+ w.formatter = f
+}
+
+// SetFramer changes the framer function for subsequent messages.
+func (w *Writer) SetFramer(f Framer) {
+ w.framer = f
+}
+
+// SetHostname changes the hostname for syslog messages if needed.
+func (w *Writer) SetHostname(hostname string) {
+ w.hostname = hostname
+}
+
+// Write sends a log message to the syslog daemon using the default priority
+// passed into `srslog.New` or the `srslog.Dial*` functions.
+func (w *Writer) Write(b []byte) (int, error) {
+ return w.writeAndRetry(w.priority, string(b))
+}
+
+// WriteWithPriority sends a log message with a custom priority.
+func (w *Writer) WriteWithPriority(p Priority, b []byte) (int, error) {
+ return w.writeAndRetryWithPriority(p, string(b))
+}
+
+// Close closes a connection to the syslog daemon.
+func (w *Writer) Close() error {
+ conn := w.getConn()
+ if conn != nil {
+ err := conn.close()
+ w.setConn(nil)
+ return err
+ }
+ return nil
+}
+
+// Emerg logs a message with severity LOG_EMERG; this overrides the default
+// priority passed to `srslog.New` and the `srslog.Dial*` functions.
+func (w *Writer) Emerg(m string) (err error) {
+ _, err = w.writeAndRetry(LOG_EMERG, m)
+ return err
+}
+
+// Alert logs a message with severity LOG_ALERT; this overrides the default
+// priority passed to `srslog.New` and the `srslog.Dial*` functions.
+func (w *Writer) Alert(m string) (err error) {
+ _, err = w.writeAndRetry(LOG_ALERT, m)
+ return err
+}
+
+// Crit logs a message with severity LOG_CRIT; this overrides the default
+// priority passed to `srslog.New` and the `srslog.Dial*` functions.
+func (w *Writer) Crit(m string) (err error) {
+ _, err = w.writeAndRetry(LOG_CRIT, m)
+ return err
+}
+
+// Err logs a message with severity LOG_ERR; this overrides the default
+// priority passed to `srslog.New` and the `srslog.Dial*` functions.
+func (w *Writer) Err(m string) (err error) {
+ _, err = w.writeAndRetry(LOG_ERR, m)
+ return err
+}
+
+// Warning logs a message with severity LOG_WARNING; this overrides the default
+// priority passed to `srslog.New` and the `srslog.Dial*` functions.
+func (w *Writer) Warning(m string) (err error) {
+ _, err = w.writeAndRetry(LOG_WARNING, m)
+ return err
+}
+
+// Notice logs a message with severity LOG_NOTICE; this overrides the default
+// priority passed to `srslog.New` and the `srslog.Dial*` functions.
+func (w *Writer) Notice(m string) (err error) {
+ _, err = w.writeAndRetry(LOG_NOTICE, m)
+ return err
+}
+
+// Info logs a message with severity LOG_INFO; this overrides the default
+// priority passed to `srslog.New` and the `srslog.Dial*` functions.
+func (w *Writer) Info(m string) (err error) {
+ _, err = w.writeAndRetry(LOG_INFO, m)
+ return err
+}
+
+// Debug logs a message with severity LOG_DEBUG; this overrides the default
+// priority passed to `srslog.New` and the `srslog.Dial*` functions.
+func (w *Writer) Debug(m string) (err error) {
+ _, err = w.writeAndRetry(LOG_DEBUG, m)
+ return err
+}
+
+// writeAndRetry takes a severity and the string to write. Any facility passed to
+// it as part of the severity Priority will be ignored.
+func (w *Writer) writeAndRetry(severity Priority, s string) (int, error) {
+ pr := (w.priority & facilityMask) | (severity & severityMask)
+
+ return w.writeAndRetryWithPriority(pr, s)
+}
+
+// writeAndRetryWithPriority differs from writeAndRetry in that it allows setting
+// of both the facility and the severity.
+func (w *Writer) writeAndRetryWithPriority(p Priority, s string) (int, error) {
+ conn := w.getConn()
+ if conn != nil {
+ if n, err := w.write(conn, p, s); err == nil {
+ return n, err
+ }
+ }
+
+ var err error
+ if conn, err = w.connect(); err != nil {
+ return 0, err
+ }
+ return w.write(conn, p, s)
+}
+
+// write generates and writes a syslog formatted string. It formats the
+// message based on the current Formatter and Framer.
+func (w *Writer) write(conn serverConn, p Priority, msg string) (int, error) {
+ // ensure it ends in a \n
+ if !strings.HasSuffix(msg, "\n") {
+ msg += "\n"
+ }
+
+ err := conn.writeString(w.framer, w.formatter, p, w.hostname, w.tag, msg)
+ if err != nil {
+ return 0, err
+ }
+ // Note: return the length of the input, not the number of
+ // bytes printed by Fprintf, because this must behave like
+ // an io.Writer.
+ return len(msg), nil
+}
diff --git a/vendor/github.com/francoispqt/gojay/.gitignore b/vendor/github.com/francoispqt/gojay/.gitignore
new file mode 100644
index 0000000000..43ebdc4b99
--- /dev/null
+++ b/vendor/github.com/francoispqt/gojay/.gitignore
@@ -0,0 +1,5 @@
+vendor
+*.out
+*.log
+*.test
+.vscode
diff --git a/vendor/github.com/francoispqt/gojay/.travis.yml b/vendor/github.com/francoispqt/gojay/.travis.yml
new file mode 100644
index 0000000000..df04aa240d
--- /dev/null
+++ b/vendor/github.com/francoispqt/gojay/.travis.yml
@@ -0,0 +1,15 @@
+language: go
+
+go:
+ - "1.10.x"
+ - "1.11.x"
+ - "1.12.x"
+
+script:
+ - go get github.com/golang/dep/cmd/dep github.com/stretchr/testify
+ - dep ensure -v -vendor-only
+ - go test ./gojay/codegen/test/... -race
+ - go test -race -coverprofile=coverage.txt -covermode=atomic
+
+after_success:
+ - bash <(curl -s https://codecov.io/bash)
diff --git a/vendor/github.com/francoispqt/gojay/Gopkg.lock b/vendor/github.com/francoispqt/gojay/Gopkg.lock
new file mode 100644
index 0000000000..d642e9a753
--- /dev/null
+++ b/vendor/github.com/francoispqt/gojay/Gopkg.lock
@@ -0,0 +1,163 @@
+# This file is autogenerated, do not edit; changes may be undone by the next 'dep ensure'.
+
+
+[[projects]]
+ digest = "1:1a37f9f2ae10d161d9688fb6008ffa14e1631e5068cc3e9698008b9e8d40d575"
+ name = "cloud.google.com/go"
+ packages = ["compute/metadata"]
+ pruneopts = ""
+ revision = "457ea5c15ccf3b87db582c450e80101989da35f7"
+ version = "v0.40.0"
+
+[[projects]]
+ digest = "1:968d8903d598e3fae738325d3410f33f07ea6a2b9ee5591e9c262ee37df6845a"
+ name = "github.com/go-errors/errors"
+ packages = ["."]
+ pruneopts = ""
+ revision = "a6af135bd4e28680facf08a3d206b454abc877a4"
+ version = "v1.0.1"
+
+[[projects]]
+ digest = "1:529d738b7976c3848cae5cf3a8036440166835e389c1f617af701eeb12a0518d"
+ name = "github.com/golang/protobuf"
+ packages = ["proto"]
+ pruneopts = ""
+ revision = "b5d812f8a3706043e23a9cd5babf2e5423744d30"
+ version = "v1.3.1"
+
+[[projects]]
+ branch = "master"
+ digest = "1:cae59d7b8243c671c9f544965522ba35c0fec48ee80adb9f1400cd2f33abbbec"
+ name = "github.com/mailru/easyjson"
+ packages = [
+ ".",
+ "buffer",
+ "jlexer",
+ "jwriter",
+ ]
+ pruneopts = ""
+ revision = "1ea4449da9834f4d333f1cc461c374aea217d249"
+
+[[projects]]
+ digest = "1:1d7e1867c49a6dd9856598ef7c3123604ea3daabf5b83f303ff457bcbc410b1d"
+ name = "github.com/pkg/errors"
+ packages = ["."]
+ pruneopts = ""
+ revision = "ba968bfe8b2f7e042a574c888954fccecfa385b4"
+ version = "v0.8.1"
+
+[[projects]]
+ digest = "1:8d4bbd8ab012efc77ab6b97286f2aff262bcdeac9803bb57d75cf7d0a5e6a877"
+ name = "github.com/viant/assertly"
+ packages = ["."]
+ pruneopts = ""
+ revision = "04f45e0aeb6f3455884877b047a97bcc95dc9493"
+ version = "v0.4.8"
+
+[[projects]]
+ digest = "1:5913451bc2d274673c0716efe226a137625740cd9380641f4d8300ff4f2d82a0"
+ name = "github.com/viant/toolbox"
+ packages = [
+ ".",
+ "cred",
+ "data",
+ "storage",
+ "url",
+ ]
+ pruneopts = ""
+ revision = "1be8e4d172138324f40d55ea61a2aeab0c5ce864"
+ version = "v0.24.0"
+
+[[projects]]
+ branch = "master"
+ digest = "1:9d150270ca2c3356f2224a0878daa1652e4d0b25b345f18b4f6e156cc4b8ec5e"
+ name = "golang.org/x/crypto"
+ packages = [
+ "blowfish",
+ "curve25519",
+ "ed25519",
+ "ed25519/internal/edwards25519",
+ "internal/chacha20",
+ "internal/subtle",
+ "poly1305",
+ "ssh",
+ ]
+ pruneopts = ""
+ revision = "f99c8df09eb5bff426315721bfa5f16a99cad32c"
+
+[[projects]]
+ branch = "master"
+ digest = "1:5a56f211e7c12a65c5585c629457a2fb91d8719844ee8fab92727ea8adb5721c"
+ name = "golang.org/x/net"
+ packages = [
+ "context",
+ "context/ctxhttp",
+ "websocket",
+ ]
+ pruneopts = ""
+ revision = "461777fb6f67e8cb9d70cda16573678d085a74cf"
+
+[[projects]]
+ branch = "master"
+ digest = "1:01bdbbc604dcd5afb6f66a717f69ad45e9643c72d5bc11678d44ffa5c50f9e42"
+ name = "golang.org/x/oauth2"
+ packages = [
+ ".",
+ "google",
+ "internal",
+ "jws",
+ "jwt",
+ ]
+ pruneopts = ""
+ revision = "0f29369cfe4552d0e4bcddc57cc75f4d7e672a33"
+
+[[projects]]
+ branch = "master"
+ digest = "1:8ddb956f67d4c176abbbc42b7514aaeaf9ea30daa24e27d2cf30ad82f9334a2c"
+ name = "golang.org/x/sys"
+ packages = ["cpu"]
+ pruneopts = ""
+ revision = "1e42afee0f762ed3d76e6dd942e4181855fd1849"
+
+[[projects]]
+ digest = "1:47f391ee443f578f01168347818cb234ed819521e49e4d2c8dd2fb80d48ee41a"
+ name = "google.golang.org/appengine"
+ packages = [
+ ".",
+ "internal",
+ "internal/app_identity",
+ "internal/base",
+ "internal/datastore",
+ "internal/log",
+ "internal/modules",
+ "internal/remote_api",
+ "internal/urlfetch",
+ "urlfetch",
+ ]
+ pruneopts = ""
+ revision = "b2f4a3cf3c67576a2ee09e1fe62656a5086ce880"
+ version = "v1.6.1"
+
+[[projects]]
+ digest = "1:cedccf16b71e86db87a24f8d4c70b0a855872eb967cb906a66b95de56aefbd0d"
+ name = "gopkg.in/yaml.v2"
+ packages = ["."]
+ pruneopts = ""
+ revision = "51d6538a90f86fe93ac480b35f37b2be17fef232"
+ version = "v2.2.2"
+
+[solve-meta]
+ analyzer-name = "dep"
+ analyzer-version = 1
+ input-imports = [
+ "github.com/go-errors/errors",
+ "github.com/mailru/easyjson",
+ "github.com/mailru/easyjson/jlexer",
+ "github.com/mailru/easyjson/jwriter",
+ "github.com/viant/assertly",
+ "github.com/viant/toolbox",
+ "github.com/viant/toolbox/url",
+ "golang.org/x/net/websocket",
+ ]
+ solver-name = "gps-cdcl"
+ solver-version = 1
diff --git a/vendor/github.com/francoispqt/gojay/Gopkg.toml b/vendor/github.com/francoispqt/gojay/Gopkg.toml
new file mode 100644
index 0000000000..fa607923a4
--- /dev/null
+++ b/vendor/github.com/francoispqt/gojay/Gopkg.toml
@@ -0,0 +1,23 @@
+# Gopkg.toml example
+#
+# Refer to https://github.com/golang/dep/blob/master/docs/Gopkg.toml.md
+# for detailed Gopkg.toml documentation.
+#
+# required = ["github.com/user/thing/cmd/thing"]
+# ignored = ["github.com/user/project/pkgX", "bitbucket.org/user/project/pkgA/pkgY"]
+#
+# [[constraint]]
+# name = "github.com/user/project"
+# version = "1.0.0"
+#
+# [[constraint]]
+# name = "github.com/user/project2"
+# branch = "dev"
+# source = "github.com/myfork/project2"
+#
+# [[override]]
+# name = "github.com/x/y"
+# version = "2.4.0"
+
+
+ignored = ["github.com/francoispqt/benchmarks*","github.com/stretchr/testify*","github.com/stretchr/testify","github.com/json-iterator/go","github.com/buger/jsonparser"]
diff --git a/vendor/github.com/francoispqt/gojay/LICENSE b/vendor/github.com/francoispqt/gojay/LICENSE
new file mode 100644
index 0000000000..df215964ee
--- /dev/null
+++ b/vendor/github.com/francoispqt/gojay/LICENSE
@@ -0,0 +1,21 @@
+MIT License
+
+Copyright (c) 2016 gojay
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
\ No newline at end of file
diff --git a/vendor/github.com/francoispqt/gojay/Makefile b/vendor/github.com/francoispqt/gojay/Makefile
new file mode 100644
index 0000000000..ce9572391e
--- /dev/null
+++ b/vendor/github.com/francoispqt/gojay/Makefile
@@ -0,0 +1,11 @@
+.PHONY: test
+test:
+ go test -race -run=^Test -v
+
+.PHONY: cover
+cover:
+ go test -coverprofile=coverage.out -covermode=atomic
+
+.PHONY: coverhtml
+coverhtml:
+ go tool cover -html=coverage.out
\ No newline at end of file
diff --git a/vendor/github.com/francoispqt/gojay/README.md b/vendor/github.com/francoispqt/gojay/README.md
new file mode 100644
index 0000000000..b2abd291d8
--- /dev/null
+++ b/vendor/github.com/francoispqt/gojay/README.md
@@ -0,0 +1,855 @@
+[](https://travis-ci.org/francoispqt/gojay)
+[](https://codecov.io/gh/francoispqt/gojay)
+[](https://goreportcard.com/report/github.com/francoispqt/gojay)
+[](https://godoc.org/github.com/francoispqt/gojay)
+
+[](https://sourcegraph.com/github.com/francoispqt/gojay)
+
+
+# GoJay
+
+
+
+GoJay is a performant JSON encoder/decoder for Golang (currently the most performant, [see benchmarks](#benchmark-results)).
+
+It has a simple API and doesn't use reflection. It relies on small interfaces to decode/encode structures and slices.
+
+Gojay also comes with powerful stream decoding features and an even faster [Unsafe](#unsafe-api) API.
+
+There is also a [code generation tool](https://github.com/francoispqt/gojay/tree/master/gojay) to make usage easier and faster.
+
+# Why another JSON parser?
+
+I looked at other fast decoder/encoder and realised it was mostly hardly readable static code generation or a lot of reflection, poor streaming features, and not so fast in the end.
+
+Also, I wanted to build a decoder that could consume an io.Reader of line or comma delimited JSON, in a JIT way. To consume a flow of JSON objects from a TCP connection for example or from a standard output. Same way I wanted to build an encoder that could encode a flow of data to a io.Writer.
+
+This is how GoJay aims to be a very fast, JIT stream parser with 0 reflection, low allocation with a friendly API.
+
+# Get started
+
+```bash
+go get github.com/francoispqt/gojay
+```
+
+* [Encoder](#encoding)
+* [Decoder](#decoding)
+* [Stream API](#stream-api)
+* [Code Generation](https://github.com/francoispqt/gojay/tree/master/gojay)
+
+## Decoding
+
+Decoding is done through two different API similar to standard `encoding/json`:
+* [Unmarshal](#unmarshal-api)
+* [Decode](#decode-api)
+
+
+Example of basic stucture decoding with Unmarshal:
+```go
+import "github.com/francoispqt/gojay"
+
+type user struct {
+ id int
+ name string
+ email string
+}
+// implement gojay.UnmarshalerJSONObject
+func (u *user) UnmarshalJSONObject(dec *gojay.Decoder, key string) error {
+ switch key {
+ case "id":
+ return dec.Int(&u.id)
+ case "name":
+ return dec.String(&u.name)
+ case "email":
+ return dec.String(&u.email)
+ }
+ return nil
+}
+func (u *user) NKeys() int {
+ return 3
+}
+
+func main() {
+ u := &user{}
+ d := []byte(`{"id":1,"name":"gojay","email":"gojay@email.com"}`)
+ err := gojay.UnmarshalJSONObject(d, u)
+ if err != nil {
+ log.Fatal(err)
+ }
+}
+```
+
+with Decode:
+```go
+func main() {
+ u := &user{}
+ dec := gojay.NewDecoder(bytes.NewReader([]byte(`{"id":1,"name":"gojay","email":"gojay@email.com"}`)))
+ err := dec.DecodeObject(d, u)
+ if err != nil {
+ log.Fatal(err)
+ }
+}
+```
+
+### Unmarshal API
+
+Unmarshal API decodes a `[]byte` to a given pointer with a single function.
+
+Behind the doors, Unmarshal API borrows a `*gojay.Decoder` resets its settings and decodes the data to the given pointer and releases the `*gojay.Decoder` to the pool when it finishes, whether it encounters an error or not.
+
+If it cannot find the right Decoding strategy for the type of the given pointer, it returns an `InvalidUnmarshalError`. You can test the error returned by doing `if ok := err.(InvalidUnmarshalError); ok {}`.
+
+Unmarshal API comes with three functions:
+* Unmarshal
+```go
+func Unmarshal(data []byte, v interface{}) error
+```
+
+* UnmarshalJSONObject
+```go
+func UnmarshalJSONObject(data []byte, v gojay.UnmarshalerJSONObject) error
+```
+
+* UnmarshalJSONArray
+```go
+func UnmarshalJSONArray(data []byte, v gojay.UnmarshalerJSONArray) error
+```
+
+
+### Decode API
+
+Decode API decodes a `[]byte` to a given pointer by creating or borrowing a `*gojay.Decoder` with an `io.Reader` and calling `Decode` methods.
+
+__Getting a *gojay.Decoder or Borrowing__
+
+You can either get a fresh `*gojay.Decoder` calling `dec := gojay.NewDecoder(io.Reader)` or borrow one from the pool by calling `dec := gojay.BorrowDecoder(io.Reader)`.
+
+After using a decoder, you can release it by calling `dec.Release()`. Beware, if you reuse the decoder after releasing it, it will panic with an error of type `InvalidUsagePooledDecoderError`. If you want to fully benefit from the pooling, you must release your decoders after using.
+
+Example getting a fresh an releasing:
+```go
+str := ""
+dec := gojay.NewDecoder(strings.NewReader(`"test"`))
+defer dec.Release()
+if err := dec.Decode(&str); err != nil {
+ log.Fatal(err)
+}
+```
+Example borrowing a decoder and releasing:
+```go
+str := ""
+dec := gojay.BorrowDecoder(strings.NewReader(`"test"`))
+defer dec.Release()
+if err := dec.Decode(&str); err != nil {
+ log.Fatal(err)
+}
+```
+
+`*gojay.Decoder` has multiple methods to decode to specific types:
+* Decode
+```go
+func (dec *gojay.Decoder) Decode(v interface{}) error
+```
+* DecodeObject
+```go
+func (dec *gojay.Decoder) DecodeObject(v gojay.UnmarshalerJSONObject) error
+```
+* DecodeArray
+```go
+func (dec *gojay.Decoder) DecodeArray(v gojay.UnmarshalerJSONArray) error
+```
+* DecodeInt
+```go
+func (dec *gojay.Decoder) DecodeInt(v *int) error
+```
+* DecodeBool
+```go
+func (dec *gojay.Decoder) DecodeBool(v *bool) error
+```
+* DecodeString
+```go
+func (dec *gojay.Decoder) DecodeString(v *string) error
+```
+
+All DecodeXxx methods are used to decode top level JSON values. If you are decoding keys or items of a JSON object or array, don't use the Decode methods.
+
+Example:
+```go
+reader := strings.NewReader(`"John Doe"`)
+dec := NewDecoder(reader)
+
+var str string
+err := dec.DecodeString(&str)
+if err != nil {
+ log.Fatal(err)
+}
+
+fmt.Println(str) // John Doe
+```
+
+### Structs and Maps
+#### UnmarshalerJSONObject Interface
+
+To unmarshal a JSON object to a structure, the structure must implement the `UnmarshalerJSONObject` interface:
+```go
+type UnmarshalerJSONObject interface {
+ UnmarshalJSONObject(*gojay.Decoder, string) error
+ NKeys() int
+}
+```
+`UnmarshalJSONObject` method takes two arguments, the first one is a pointer to the Decoder (*gojay.Decoder) and the second one is the string value of the current key being parsed. If the JSON data is not an object, the UnmarshalJSONObject method will never be called.
+
+`NKeys` method must return the number of keys to Unmarshal in the JSON object or 0. If zero is returned, all keys will be parsed.
+
+Example of implementation for a struct:
+```go
+type user struct {
+ id int
+ name string
+ email string
+}
+// implement UnmarshalerJSONObject
+func (u *user) UnmarshalJSONObject(dec *gojay.Decoder, key string) error {
+ switch key {
+ case "id":
+ return dec.Int(&u.id)
+ case "name":
+ return dec.String(&u.name)
+ case "email":
+ return dec.String(&u.email)
+ }
+ return nil
+}
+func (u *user) NKeys() int {
+ return 3
+}
+```
+
+Example of implementation for a `map[string]string`:
+```go
+// define our custom map type implementing UnmarshalerJSONObject
+type message map[string]string
+
+// Implementing Unmarshaler
+func (m message) UnmarshalJSONObject(dec *gojay.Decoder, k string) error {
+ str := ""
+ err := dec.String(&str)
+ if err != nil {
+ return err
+ }
+ m[k] = str
+ return nil
+}
+
+// we return 0, it tells the Decoder to decode all keys
+func (m message) NKeys() int {
+ return 0
+}
+```
+
+### Arrays, Slices and Channels
+
+To unmarshal a JSON object to a slice an array or a channel, it must implement the UnmarshalerJSONArray interface:
+```go
+type UnmarshalerJSONArray interface {
+ UnmarshalJSONArray(*gojay.Decoder) error
+}
+```
+UnmarshalJSONArray method takes one argument, a pointer to the Decoder (*gojay.Decoder). If the JSON data is not an array, the Unmarshal method will never be called.
+
+Example of implementation with a slice:
+```go
+type testSlice []string
+// implement UnmarshalerJSONArray
+func (t *testSlice) UnmarshalJSONArray(dec *gojay.Decoder) error {
+ str := ""
+ if err := dec.String(&str); err != nil {
+ return err
+ }
+ *t = append(*t, str)
+ return nil
+}
+
+func main() {
+ dec := gojay.BorrowDecoder(strings.NewReader(`["Tom", "Jim"]`))
+ var slice testSlice
+ err := dec.DecodeArray(&slice)
+ if err != nil {
+ log.Fatal(err)
+ }
+ fmt.Println(slice) // [Tom Jim]
+ dec.Release()
+}
+```
+
+Example of implementation with a channel:
+```go
+type testChannel chan string
+// implement UnmarshalerJSONArray
+func (c testChannel) UnmarshalJSONArray(dec *gojay.Decoder) error {
+ str := ""
+ if err := dec.String(&str); err != nil {
+ return err
+ }
+ c <- str
+ return nil
+}
+
+func main() {
+ dec := gojay.BorrowDecoder(strings.NewReader(`["Tom", "Jim"]`))
+ c := make(testChannel, 2)
+ err := dec.DecodeArray(c)
+ if err != nil {
+ log.Fatal(err)
+ }
+ for i := 0; i < 2; i++ {
+ fmt.Println(<-c)
+ }
+ close(c)
+ dec.Release()
+}
+```
+
+Example of implementation with an array:
+```go
+type testArray [3]string
+// implement UnmarshalerJSONArray
+func (a *testArray) UnmarshalJSONArray(dec *Decoder) error {
+ var str string
+ if err := dec.String(&str); err != nil {
+ return err
+ }
+ a[dec.Index()] = str
+ return nil
+}
+
+func main() {
+ dec := gojay.BorrowDecoder(strings.NewReader(`["Tom", "Jim", "Bob"]`))
+ var a testArray
+ err := dec.DecodeArray(&a)
+ fmt.Println(a) // [Tom Jim Bob]
+ dec.Release()
+}
+```
+
+### Other types
+To decode other types (string, int, int32, int64, uint32, uint64, float, booleans), you don't need to implement any interface.
+
+Example of encoding strings:
+```go
+func main() {
+ json := []byte(`"Jay"`)
+ var v string
+ err := gojay.Unmarshal(json, &v)
+ if err != nil {
+ log.Fatal(err)
+ }
+ fmt.Println(v) // Jay
+}
+```
+
+### Decode values methods
+When decoding a JSON object of a JSON array using `UnmarshalerJSONObject` or `UnmarshalerJSONArray` interface, the `gojay.Decoder` provides dozens of methods to Decode multiple types.
+
+Non exhaustive list of methods available (to see all methods, check the godoc):
+```go
+dec.Int
+dec.Int8
+dec.Int16
+dec.Int32
+dec.Int64
+dec.Uint8
+dec.Uint16
+dec.Uint32
+dec.Uint64
+dec.String
+dec.Time
+dec.Bool
+dec.SQLNullString
+dec.SQLNullInt64
+```
+
+
+## Encoding
+
+Encoding is done through two different API similar to standard `encoding/json`:
+* [Marshal](#marshal-api)
+* [Encode](#encode-api)
+
+Example of basic structure encoding with Marshal:
+```go
+import "github.com/francoispqt/gojay"
+
+type user struct {
+ id int
+ name string
+ email string
+}
+
+// implement MarshalerJSONObject
+func (u *user) MarshalJSONObject(enc *gojay.Encoder) {
+ enc.IntKey("id", u.id)
+ enc.StringKey("name", u.name)
+ enc.StringKey("email", u.email)
+}
+func (u *user) IsNil() bool {
+ return u == nil
+}
+
+func main() {
+ u := &user{1, "gojay", "gojay@email.com"}
+ b, err := gojay.MarshalJSONObject(u)
+ if err != nil {
+ log.Fatal(err)
+ }
+ fmt.Println(string(b)) // {"id":1,"name":"gojay","email":"gojay@email.com"}
+}
+```
+
+with Encode:
+```go
+func main() {
+ u := &user{1, "gojay", "gojay@email.com"}
+ b := strings.Builder{}
+ enc := gojay.NewEncoder(&b)
+ if err := enc.Encode(u); err != nil {
+ log.Fatal(err)
+ }
+ fmt.Println(b.String()) // {"id":1,"name":"gojay","email":"gojay@email.com"}
+}
+```
+
+### Marshal API
+
+Marshal API encodes a value to a JSON `[]byte` with a single function.
+
+Behind the doors, Marshal API borrows a `*gojay.Encoder` resets its settings and encodes the data to an internal byte buffer and releases the `*gojay.Encoder` to the pool when it finishes, whether it encounters an error or not.
+
+If it cannot find the right Encoding strategy for the type of the given value, it returns an `InvalidMarshalError`. You can test the error returned by doing `if ok := err.(InvalidMarshalError); ok {}`.
+
+Marshal API comes with three functions:
+* Marshal
+```go
+func Marshal(v interface{}) ([]byte, error)
+```
+
+* MarshalJSONObject
+```go
+func MarshalJSONObject(v gojay.MarshalerJSONObject) ([]byte, error)
+```
+
+* MarshalJSONArray
+```go
+func MarshalJSONArray(v gojay.MarshalerJSONArray) ([]byte, error)
+```
+
+### Encode API
+
+Encode API decodes a value to JSON by creating or borrowing a `*gojay.Encoder` sending it to an `io.Writer` and calling `Encode` methods.
+
+__Getting a *gojay.Encoder or Borrowing__
+
+You can either get a fresh `*gojay.Encoder` calling `enc := gojay.NewEncoder(io.Writer)` or borrow one from the pool by calling `enc := gojay.BorrowEncoder(io.Writer)`.
+
+After using an encoder, you can release it by calling `enc.Release()`. Beware, if you reuse the encoder after releasing it, it will panic with an error of type `InvalidUsagePooledEncoderError`. If you want to fully benefit from the pooling, you must release your encoders after using.
+
+Example getting a fresh encoder an releasing:
+```go
+str := "test"
+b := strings.Builder{}
+enc := gojay.NewEncoder(&b)
+defer enc.Release()
+if err := enc.Encode(str); err != nil {
+ log.Fatal(err)
+}
+```
+Example borrowing an encoder and releasing:
+```go
+str := "test"
+b := strings.Builder{}
+enc := gojay.BorrowEncoder(b)
+defer enc.Release()
+if err := enc.Encode(str); err != nil {
+ log.Fatal(err)
+}
+```
+
+`*gojay.Encoder` has multiple methods to encoder specific types to JSON:
+* Encode
+```go
+func (enc *gojay.Encoder) Encode(v interface{}) error
+```
+* EncodeObject
+```go
+func (enc *gojay.Encoder) EncodeObject(v gojay.MarshalerJSONObject) error
+```
+* EncodeArray
+```go
+func (enc *gojay.Encoder) EncodeArray(v gojay.MarshalerJSONArray) error
+```
+* EncodeInt
+```go
+func (enc *gojay.Encoder) EncodeInt(n int) error
+```
+* EncodeInt64
+```go
+func (enc *gojay.Encoder) EncodeInt64(n int64) error
+```
+* EncodeFloat
+```go
+func (enc *gojay.Encoder) EncodeFloat(n float64) error
+```
+* EncodeBool
+```go
+func (enc *gojay.Encoder) EncodeBool(v bool) error
+```
+* EncodeString
+```go
+func (enc *gojay.Encoder) EncodeString(s string) error
+```
+
+### Structs and Maps
+
+To encode a structure, the structure must implement the MarshalerJSONObject interface:
+```go
+type MarshalerJSONObject interface {
+ MarshalJSONObject(enc *gojay.Encoder)
+ IsNil() bool
+}
+```
+`MarshalJSONObject` method takes one argument, a pointer to the Encoder (*gojay.Encoder). The method must add all the keys in the JSON Object by calling Decoder's methods.
+
+IsNil method returns a boolean indicating if the interface underlying value is nil or not. It is used to safely ensure that the underlying value is not nil without using Reflection.
+
+Example of implementation for a struct:
+```go
+type user struct {
+ id int
+ name string
+ email string
+}
+
+// implement MarshalerJSONObject
+func (u *user) MarshalJSONObject(enc *gojay.Encoder) {
+ enc.IntKey("id", u.id)
+ enc.StringKey("name", u.name)
+ enc.StringKey("email", u.email)
+}
+func (u *user) IsNil() bool {
+ return u == nil
+}
+```
+
+Example of implementation for a `map[string]string`:
+```go
+// define our custom map type implementing MarshalerJSONObject
+type message map[string]string
+
+// Implementing Marshaler
+func (m message) MarshalJSONObject(enc *gojay.Encoder) {
+ for k, v := range m {
+ enc.StringKey(k, v)
+ }
+}
+
+func (m message) IsNil() bool {
+ return m == nil
+}
+```
+
+### Arrays and Slices
+To encode an array or a slice, the slice/array must implement the MarshalerJSONArray interface:
+```go
+type MarshalerJSONArray interface {
+ MarshalJSONArray(enc *gojay.Encoder)
+ IsNil() bool
+}
+```
+`MarshalJSONArray` method takes one argument, a pointer to the Encoder (*gojay.Encoder). The method must add all element in the JSON Array by calling Decoder's methods.
+
+`IsNil` method returns a boolean indicating if the interface underlying value is nil(empty) or not. It is used to safely ensure that the underlying value is not nil without using Reflection and also to in `OmitEmpty` feature.
+
+Example of implementation:
+```go
+type users []*user
+// implement MarshalerJSONArray
+func (u *users) MarshalJSONArray(enc *gojay.Encoder) {
+ for _, e := range u {
+ enc.Object(e)
+ }
+}
+func (u *users) IsNil() bool {
+ return len(u) == 0
+}
+```
+
+### Other types
+To encode other types (string, int, float, booleans), you don't need to implement any interface.
+
+Example of encoding strings:
+```go
+func main() {
+ name := "Jay"
+ b, err := gojay.Marshal(name)
+ if err != nil {
+ log.Fatal(err)
+ }
+ fmt.Println(string(b)) // "Jay"
+}
+```
+
+# Stream API
+
+### Stream Decoding
+GoJay ships with a powerful stream decoder.
+
+It allows to read continuously from an io.Reader stream and do JIT decoding writing unmarshalled JSON to a channel to allow async consuming.
+
+When using the Stream API, the Decoder implements context.Context to provide graceful cancellation.
+
+To decode a stream of JSON, you must call `gojay.Stream.DecodeStream` and pass it a `UnmarshalerStream` implementation.
+
+```go
+type UnmarshalerStream interface {
+ UnmarshalStream(*StreamDecoder) error
+}
+```
+
+Example of implementation of stream reading from a WebSocket connection:
+```go
+// implement UnmarshalerStream
+type ChannelStream chan *user
+
+func (c ChannelStream) UnmarshalStream(dec *gojay.StreamDecoder) error {
+ u := &user{}
+ if err := dec.Object(u); err != nil {
+ return err
+ }
+ c <- u
+ return nil
+}
+
+func main() {
+ // get our websocket connection
+ origin := "http://localhost/"
+ url := "ws://localhost:12345/ws"
+ ws, err := websocket.Dial(url, "", origin)
+ if err != nil {
+ log.Fatal(err)
+ }
+ // create our channel which will receive our objects
+ streamChan := ChannelStream(make(chan *user))
+ // borrow a decoder
+ dec := gojay.Stream.BorrowDecoder(ws)
+ // start decoding, it will block until a JSON message is decoded from the WebSocket
+ // or until Done channel is closed
+ go dec.DecodeStream(streamChan)
+ for {
+ select {
+ case v := <-streamChan:
+ // Got something from my websocket!
+ log.Println(v)
+ case <-dec.Done():
+ log.Println("finished reading from WebSocket")
+ os.Exit(0)
+ }
+ }
+}
+```
+
+### Stream Encoding
+GoJay ships with a powerful stream encoder part of the Stream API.
+
+It allows to write continuously to an io.Writer and do JIT encoding of data fed to a channel to allow async consuming. You can set multiple consumers on the channel to be as performant as possible. Consumers are non blocking and are scheduled individually in their own go routine.
+
+When using the Stream API, the Encoder implements context.Context to provide graceful cancellation.
+
+To encode a stream of data, you must call `EncodeStream` and pass it a `MarshalerStream` implementation.
+
+```go
+type MarshalerStream interface {
+ MarshalStream(enc *gojay.StreamEncoder)
+}
+```
+
+Example of implementation of stream writing to a WebSocket:
+```go
+// Our structure which will be pushed to our stream
+type user struct {
+ id int
+ name string
+ email string
+}
+
+func (u *user) MarshalJSONObject(enc *gojay.Encoder) {
+ enc.IntKey("id", u.id)
+ enc.StringKey("name", u.name)
+ enc.StringKey("email", u.email)
+}
+func (u *user) IsNil() bool {
+ return u == nil
+}
+
+// Our MarshalerStream implementation
+type StreamChan chan *user
+
+func (s StreamChan) MarshalStream(enc *gojay.StreamEncoder) {
+ select {
+ case <-enc.Done():
+ return
+ case o := <-s:
+ enc.Object(o)
+ }
+}
+
+// Our main function
+func main() {
+ // get our websocket connection
+ origin := "http://localhost/"
+ url := "ws://localhost:12345/ws"
+ ws, err := websocket.Dial(url, "", origin)
+ if err != nil {
+ log.Fatal(err)
+ }
+ // we borrow an encoder set stdout as the writer,
+ // set the number of consumer to 10
+ // and tell the encoder to separate each encoded element
+ // added to the channel by a new line character
+ enc := gojay.Stream.BorrowEncoder(ws).NConsumer(10).LineDelimited()
+ // instantiate our MarshalerStream
+ s := StreamChan(make(chan *user))
+ // start the stream encoder
+ // will block its goroutine until enc.Cancel(error) is called
+ // or until something is written to the channel
+ go enc.EncodeStream(s)
+ // write to our MarshalerStream
+ for i := 0; i < 1000; i++ {
+ s <- &user{i, "username", "user@email.com"}
+ }
+ // Wait
+ <-enc.Done()
+}
+```
+
+# Unsafe API
+
+Unsafe API has the same functions than the regular API, it only has `Unmarshal API` for now. It is unsafe because it makes assumptions on the quality of the given JSON.
+
+If you are not sure if your JSON is valid, don't use the Unsafe API.
+
+Also, the `Unsafe` API does not copy the buffer when using Unmarshal API, which, in case of string decoding, can lead to data corruption if a byte buffer is reused. Using the `Decode` API makes `Unsafe` API safer as the io.Reader relies on `copy` builtin method and `Decoder` will have its own internal buffer :)
+
+Access the `Unsafe` API this way:
+```go
+gojay.Unsafe.Unmarshal(b, v)
+```
+
+
+# Benchmarks
+
+Benchmarks encode and decode three different data based on size (small, medium, large).
+
+To run benchmark for decoder:
+```bash
+cd $GOPATH/src/github.com/francoispqt/gojay/benchmarks/decoder && make bench
+```
+
+To run benchmark for encoder:
+```bash
+cd $GOPATH/src/github.com/francoispqt/gojay/benchmarks/encoder && make bench
+```
+
+# Benchmark Results
+## Decode
+
+
+
+### Small Payload
+[benchmark code is here](https://github.com/francoispqt/gojay/blob/master/benchmarks/decoder/decoder_bench_small_test.go)
+
+[benchmark data is here](https://github.com/francoispqt/gojay/blob/master/benchmarks/benchmarks_small.go)
+
+| | ns/op | bytes/op | allocs/op |
+|-----------------|-----------|--------------|-----------|
+| Std Library | 2547 | 496 | 4 |
+| JsonIter | 2046 | 312 | 12 |
+| JsonParser | 1408 | 0 | 0 |
+| EasyJson | 929 | 240 | 2 |
+| **GoJay** | **807** | **256** | **2** |
+| **GoJay-unsafe**| **712** | **112** | **1** |
+
+### Medium Payload
+[benchmark code is here](https://github.com/francoispqt/gojay/blob/master/benchmarks/decoder/decoder_bench_medium_test.go)
+
+[benchmark data is here](https://github.com/francoispqt/gojay/blob/master/benchmarks/benchmarks_medium.go)
+
+| | ns/op | bytes/op | allocs/op |
+|-----------------|-----------|----------|-----------|
+| Std Library | 30148 | 2152 | 496 |
+| JsonIter | 16309 | 2976 | 80 |
+| JsonParser | 7793 | 0 | 0 |
+| EasyJson | 7957 | 232 | 6 |
+| **GoJay** | **4984** | **2448** | **8** |
+| **GoJay-unsafe**| **4809** | **144** | **7** |
+
+### Large Payload
+[benchmark code is here](https://github.com/francoispqt/gojay/blob/master/benchmarks/decoder/decoder_bench_large_test.go)
+
+[benchmark data is here](https://github.com/francoispqt/gojay/blob/master/benchmarks/benchmarks_large.go)
+
+| | ns/op | bytes/op | allocs/op |
+|-----------------|-----------|-------------|-----------|
+| JsonIter | 210078 | 41712 | 1136 |
+| EasyJson | 106626 | 160 | 2 |
+| JsonParser | 66813 | 0 | 0 |
+| **GoJay** | **52153** | **31241** | **77** |
+| **GoJay-unsafe**| **48277** | **2561** | **76** |
+
+## Encode
+
+
+
+### Small Struct
+[benchmark code is here](https://github.com/francoispqt/gojay/blob/master/benchmarks/encoder/encoder_bench_small_test.go)
+
+[benchmark data is here](https://github.com/francoispqt/gojay/blob/master/benchmarks/benchmarks_small.go)
+
+| | ns/op | bytes/op | allocs/op |
+|----------------|----------|--------------|-----------|
+| Std Library | 1280 | 464 | 3 |
+| EasyJson | 871 | 944 | 6 |
+| JsonIter | 866 | 272 | 3 |
+| **GoJay** | **543** | **112** | **1** |
+| **GoJay-func** | **347** | **0** | **0** |
+
+### Medium Struct
+[benchmark code is here](https://github.com/francoispqt/gojay/blob/master/benchmarks/encoder/encoder_bench_medium_test.go)
+
+[benchmark data is here](https://github.com/francoispqt/gojay/blob/master/benchmarks/benchmarks_medium.go)
+
+| | ns/op | bytes/op | allocs/op |
+|-------------|----------|--------------|-----------|
+| Std Library | 5006 | 1496 | 25 |
+| JsonIter | 2232 | 1544 | 20 |
+| EasyJson | 1997 | 1544 | 19 |
+| **GoJay** | **1522** | **312** | **14** |
+
+### Large Struct
+[benchmark code is here](https://github.com/francoispqt/gojay/blob/master/benchmarks/encoder/encoder_bench_large_test.go)
+
+[benchmark data is here](https://github.com/francoispqt/gojay/blob/master/benchmarks/benchmarks_large.go)
+
+| | ns/op | bytes/op | allocs/op |
+|-------------|-----------|--------------|-----------|
+| Std Library | 66441 | 20576 | 332 |
+| JsonIter | 35247 | 20255 | 328 |
+| EasyJson | 32053 | 15474 | 327 |
+| **GoJay** | **27847** | **9802** | **318** |
+
+# Contributing
+
+Contributions are welcome :)
+
+If you encounter issues please report it in Github and/or send an email at [francois@parquet.ninja](mailto:francois@parquet.ninja)
+
diff --git a/vendor/github.com/francoispqt/gojay/decode.go b/vendor/github.com/francoispqt/gojay/decode.go
new file mode 100644
index 0000000000..fbd07f76c2
--- /dev/null
+++ b/vendor/github.com/francoispqt/gojay/decode.go
@@ -0,0 +1,386 @@
+package gojay
+
+import (
+ "fmt"
+ "io"
+)
+
+// UnmarshalJSONArray parses the JSON-encoded data and stores the result in the value pointed to by v.
+//
+// v must implement UnmarshalerJSONArray.
+//
+// If a JSON value is not appropriate for a given target type, or if a JSON number
+// overflows the target type, UnmarshalJSONArray skips that field and completes the unmarshaling as best it can.
+func UnmarshalJSONArray(data []byte, v UnmarshalerJSONArray) error {
+ dec := borrowDecoder(nil, 0)
+ defer dec.Release()
+ dec.data = make([]byte, len(data))
+ copy(dec.data, data)
+ dec.length = len(data)
+ _, err := dec.decodeArray(v)
+ if err != nil {
+ return err
+ }
+ if dec.err != nil {
+ return dec.err
+ }
+ return nil
+}
+
+// UnmarshalJSONObject parses the JSON-encoded data and stores the result in the value pointed to by v.
+//
+// v must implement UnmarshalerJSONObject.
+//
+// If a JSON value is not appropriate for a given target type, or if a JSON number
+// overflows the target type, UnmarshalJSONObject skips that field and completes the unmarshaling as best it can.
+func UnmarshalJSONObject(data []byte, v UnmarshalerJSONObject) error {
+ dec := borrowDecoder(nil, 0)
+ defer dec.Release()
+ dec.data = make([]byte, len(data))
+ copy(dec.data, data)
+ dec.length = len(data)
+ _, err := dec.decodeObject(v)
+ if err != nil {
+ return err
+ }
+ if dec.err != nil {
+ return dec.err
+ }
+ return nil
+}
+
+// Unmarshal parses the JSON-encoded data and stores the result in the value pointed to by v.
+// If v is nil, not an implementation of UnmarshalerJSONObject or UnmarshalerJSONArray or not one of the following types:
+// *string, **string, *int, **int, *int8, **int8, *int16, **int16, *int32, **int32, *int64, **int64, *uint8, **uint8, *uint16, **uint16,
+// *uint32, **uint32, *uint64, **uint64, *float64, **float64, *float32, **float32, *bool, **bool
+// Unmarshal returns an InvalidUnmarshalError.
+//
+//
+// If a JSON value is not appropriate for a given target type, or if a JSON number
+// overflows the target type, Unmarshal skips that field and completes the unmarshaling as best it can.
+// If no more serious errors are encountered, Unmarshal returns an UnmarshalTypeError describing the earliest such error.
+// In any case, it's not guaranteed that all the remaining fields following the problematic one will be unmarshaled into the target object.
+func Unmarshal(data []byte, v interface{}) error {
+ var err error
+ var dec *Decoder
+ switch vt := v.(type) {
+ case *string:
+ dec = borrowDecoder(nil, 0)
+ dec.length = len(data)
+ dec.data = data
+ err = dec.decodeString(vt)
+ case **string:
+ dec = borrowDecoder(nil, 0)
+ dec.length = len(data)
+ dec.data = data
+ err = dec.decodeStringNull(vt)
+ case *int:
+ dec = borrowDecoder(nil, 0)
+ dec.length = len(data)
+ dec.data = data
+ err = dec.decodeInt(vt)
+ case **int:
+ dec = borrowDecoder(nil, 0)
+ dec.length = len(data)
+ dec.data = data
+ err = dec.decodeIntNull(vt)
+ case *int8:
+ dec = borrowDecoder(nil, 0)
+ dec.length = len(data)
+ dec.data = data
+ err = dec.decodeInt8(vt)
+ case **int8:
+ dec = borrowDecoder(nil, 0)
+ dec.length = len(data)
+ dec.data = data
+ err = dec.decodeInt8Null(vt)
+ case *int16:
+ dec = borrowDecoder(nil, 0)
+ dec.length = len(data)
+ dec.data = data
+ err = dec.decodeInt16(vt)
+ case **int16:
+ dec = borrowDecoder(nil, 0)
+ dec.length = len(data)
+ dec.data = data
+ err = dec.decodeInt16Null(vt)
+ case *int32:
+ dec = borrowDecoder(nil, 0)
+ dec.length = len(data)
+ dec.data = data
+ err = dec.decodeInt32(vt)
+ case **int32:
+ dec = borrowDecoder(nil, 0)
+ dec.length = len(data)
+ dec.data = data
+ err = dec.decodeInt32Null(vt)
+ case *int64:
+ dec = borrowDecoder(nil, 0)
+ dec.length = len(data)
+ dec.data = data
+ err = dec.decodeInt64(vt)
+ case **int64:
+ dec = borrowDecoder(nil, 0)
+ dec.length = len(data)
+ dec.data = data
+ err = dec.decodeInt64Null(vt)
+ case *uint8:
+ dec = borrowDecoder(nil, 0)
+ dec.length = len(data)
+ dec.data = data
+ err = dec.decodeUint8(vt)
+ case **uint8:
+ dec = borrowDecoder(nil, 0)
+ dec.length = len(data)
+ dec.data = data
+ err = dec.decodeUint8Null(vt)
+ case *uint16:
+ dec = borrowDecoder(nil, 0)
+ dec.length = len(data)
+ dec.data = data
+ err = dec.decodeUint16(vt)
+ case **uint16:
+ dec = borrowDecoder(nil, 0)
+ dec.length = len(data)
+ dec.data = data
+ err = dec.decodeUint16Null(vt)
+ case *uint32:
+ dec = borrowDecoder(nil, 0)
+ dec.length = len(data)
+ dec.data = data
+ err = dec.decodeUint32(vt)
+ case **uint32:
+ dec = borrowDecoder(nil, 0)
+ dec.length = len(data)
+ dec.data = data
+ err = dec.decodeUint32Null(vt)
+ case *uint64:
+ dec = borrowDecoder(nil, 0)
+ dec.length = len(data)
+ dec.data = data
+ err = dec.decodeUint64(vt)
+ case **uint64:
+ dec = borrowDecoder(nil, 0)
+ dec.length = len(data)
+ dec.data = data
+ err = dec.decodeUint64Null(vt)
+ case *float64:
+ dec = borrowDecoder(nil, 0)
+ dec.length = len(data)
+ dec.data = data
+ err = dec.decodeFloat64(vt)
+ case **float64:
+ dec = borrowDecoder(nil, 0)
+ dec.length = len(data)
+ dec.data = data
+ err = dec.decodeFloat64Null(vt)
+ case *float32:
+ dec = borrowDecoder(nil, 0)
+ dec.length = len(data)
+ dec.data = data
+ err = dec.decodeFloat32(vt)
+ case **float32:
+ dec = borrowDecoder(nil, 0)
+ dec.length = len(data)
+ dec.data = data
+ err = dec.decodeFloat32Null(vt)
+ case *bool:
+ dec = borrowDecoder(nil, 0)
+ dec.length = len(data)
+ dec.data = data
+ err = dec.decodeBool(vt)
+ case **bool:
+ dec = borrowDecoder(nil, 0)
+ dec.length = len(data)
+ dec.data = data
+ err = dec.decodeBoolNull(vt)
+ case UnmarshalerJSONObject:
+ dec = borrowDecoder(nil, 0)
+ dec.length = len(data)
+ dec.data = make([]byte, len(data))
+ copy(dec.data, data)
+ _, err = dec.decodeObject(vt)
+ case UnmarshalerJSONArray:
+ dec = borrowDecoder(nil, 0)
+ dec.length = len(data)
+ dec.data = make([]byte, len(data))
+ copy(dec.data, data)
+ _, err = dec.decodeArray(vt)
+ case *interface{}:
+ dec = borrowDecoder(nil, 0)
+ dec.length = len(data)
+ dec.data = make([]byte, len(data))
+ copy(dec.data, data)
+ err = dec.decodeInterface(vt)
+ default:
+ return InvalidUnmarshalError(fmt.Sprintf(invalidUnmarshalErrorMsg, vt))
+ }
+ defer dec.Release()
+ if err != nil {
+ return err
+ }
+ return dec.err
+}
+
+// UnmarshalerJSONObject is the interface to implement to decode a JSON Object.
+type UnmarshalerJSONObject interface {
+ UnmarshalJSONObject(*Decoder, string) error
+ NKeys() int
+}
+
+// UnmarshalerJSONArray is the interface to implement to decode a JSON Array.
+type UnmarshalerJSONArray interface {
+ UnmarshalJSONArray(*Decoder) error
+}
+
+// A Decoder reads and decodes JSON values from an input stream.
+type Decoder struct {
+ r io.Reader
+ data []byte
+ err error
+ isPooled byte
+ called byte
+ child byte
+ cursor int
+ length int
+ keysDone int
+ arrayIndex int
+}
+
+// Decode reads the next JSON-encoded value from the decoder's input (io.Reader) and stores it in the value pointed to by v.
+//
+// See the documentation for Unmarshal for details about the conversion of JSON into a Go value.
+// The differences between Decode and Unmarshal are:
+// - Decode reads from an io.Reader in the Decoder, whereas Unmarshal reads from a []byte
+// - Decode leaves to the user the option of borrowing and releasing a Decoder, whereas Unmarshal internally always borrows a Decoder and releases it when the unmarshaling is completed
+func (dec *Decoder) Decode(v interface{}) error {
+ if dec.isPooled == 1 {
+ panic(InvalidUsagePooledDecoderError("Invalid usage of pooled decoder"))
+ }
+ var err error
+ switch vt := v.(type) {
+ case *string:
+ err = dec.decodeString(vt)
+ case **string:
+ err = dec.decodeStringNull(vt)
+ case *int:
+ err = dec.decodeInt(vt)
+ case **int:
+ err = dec.decodeIntNull(vt)
+ case *int8:
+ err = dec.decodeInt8(vt)
+ case **int8:
+ err = dec.decodeInt8Null(vt)
+ case *int16:
+ err = dec.decodeInt16(vt)
+ case **int16:
+ err = dec.decodeInt16Null(vt)
+ case *int32:
+ err = dec.decodeInt32(vt)
+ case **int32:
+ err = dec.decodeInt32Null(vt)
+ case *int64:
+ err = dec.decodeInt64(vt)
+ case **int64:
+ err = dec.decodeInt64Null(vt)
+ case *uint8:
+ err = dec.decodeUint8(vt)
+ case **uint8:
+ err = dec.decodeUint8Null(vt)
+ case *uint16:
+ err = dec.decodeUint16(vt)
+ case **uint16:
+ err = dec.decodeUint16Null(vt)
+ case *uint32:
+ err = dec.decodeUint32(vt)
+ case **uint32:
+ err = dec.decodeUint32Null(vt)
+ case *uint64:
+ err = dec.decodeUint64(vt)
+ case **uint64:
+ err = dec.decodeUint64Null(vt)
+ case *float64:
+ err = dec.decodeFloat64(vt)
+ case **float64:
+ err = dec.decodeFloat64Null(vt)
+ case *float32:
+ err = dec.decodeFloat32(vt)
+ case **float32:
+ err = dec.decodeFloat32Null(vt)
+ case *bool:
+ err = dec.decodeBool(vt)
+ case **bool:
+ err = dec.decodeBoolNull(vt)
+ case UnmarshalerJSONObject:
+ _, err = dec.decodeObject(vt)
+ case UnmarshalerJSONArray:
+ _, err = dec.decodeArray(vt)
+ case *EmbeddedJSON:
+ err = dec.decodeEmbeddedJSON(vt)
+ case *interface{}:
+ err = dec.decodeInterface(vt)
+ default:
+ return InvalidUnmarshalError(fmt.Sprintf(invalidUnmarshalErrorMsg, vt))
+ }
+ if err != nil {
+ return err
+ }
+ return dec.err
+}
+
+// Non exported
+
+func isDigit(b byte) bool {
+ switch b {
+ case '0', '1', '2', '3', '4', '5', '6', '7', '8', '9':
+ return true
+ default:
+ return false
+ }
+}
+
+func (dec *Decoder) read() bool {
+ if dec.r != nil {
+ // if we reach the end, double the buffer to ensure there's always more space
+ if len(dec.data) == dec.length {
+ nLen := dec.length * 2
+ if nLen == 0 {
+ nLen = 512
+ }
+ Buf := make([]byte, nLen, nLen)
+ copy(Buf, dec.data)
+ dec.data = Buf
+ }
+ var n int
+ var err error
+ for n == 0 {
+ n, err = dec.r.Read(dec.data[dec.length:])
+ if err != nil {
+ if err != io.EOF {
+ dec.err = err
+ return false
+ }
+ if n == 0 {
+ return false
+ }
+ dec.length = dec.length + n
+ return true
+ }
+ }
+ dec.length = dec.length + n
+ return true
+ }
+ return false
+}
+
+func (dec *Decoder) nextChar() byte {
+ for ; dec.cursor < dec.length || dec.read(); dec.cursor++ {
+ switch dec.data[dec.cursor] {
+ case ' ', '\n', '\t', '\r', ',':
+ continue
+ }
+ d := dec.data[dec.cursor]
+ return d
+ }
+ return 0
+}
diff --git a/vendor/github.com/francoispqt/gojay/decode_array.go b/vendor/github.com/francoispqt/gojay/decode_array.go
new file mode 100644
index 0000000000..297f2ee744
--- /dev/null
+++ b/vendor/github.com/francoispqt/gojay/decode_array.go
@@ -0,0 +1,247 @@
+package gojay
+
+import "reflect"
+
+// DecodeArray reads the next JSON-encoded value from the decoder's input (io.Reader)
+// and stores it in the value pointed to by v.
+//
+// v must implement UnmarshalerJSONArray.
+//
+// See the documentation for Unmarshal for details about the conversion of JSON into a Go value.
+func (dec *Decoder) DecodeArray(v UnmarshalerJSONArray) error {
+ if dec.isPooled == 1 {
+ panic(InvalidUsagePooledDecoderError("Invalid usage of pooled decoder"))
+ }
+ _, err := dec.decodeArray(v)
+ return err
+}
+func (dec *Decoder) decodeArray(arr UnmarshalerJSONArray) (int, error) {
+ // remember last array index in case of nested arrays
+ lastArrayIndex := dec.arrayIndex
+ dec.arrayIndex = 0
+ defer func() {
+ dec.arrayIndex = lastArrayIndex
+ }()
+ for ; dec.cursor < dec.length || dec.read(); dec.cursor++ {
+ switch dec.data[dec.cursor] {
+ case ' ', '\n', '\t', '\r', ',':
+ continue
+ case '[':
+ dec.cursor = dec.cursor + 1
+ // array is open, char is not space start readings
+ for dec.nextChar() != 0 {
+ // closing array
+ if dec.data[dec.cursor] == ']' {
+ dec.cursor = dec.cursor + 1
+ return dec.cursor, nil
+ }
+ // calling unmarshall function for each element of the slice
+ err := arr.UnmarshalJSONArray(dec)
+ if err != nil {
+ return 0, err
+ }
+ dec.arrayIndex++
+ }
+ return 0, dec.raiseInvalidJSONErr(dec.cursor)
+ case 'n':
+ // is null
+ dec.cursor++
+ err := dec.assertNull()
+ if err != nil {
+ return 0, err
+ }
+ return dec.cursor, nil
+ case '{', '"', 'f', 't', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9':
+ // can't unmarshall to struct
+ // we skip array and set Error
+ dec.err = dec.makeInvalidUnmarshalErr(arr)
+ err := dec.skipData()
+ if err != nil {
+ return 0, err
+ }
+ return dec.cursor, nil
+ default:
+ return 0, dec.raiseInvalidJSONErr(dec.cursor)
+ }
+ }
+ return 0, dec.raiseInvalidJSONErr(dec.cursor)
+}
+func (dec *Decoder) decodeArrayNull(v interface{}) (int, error) {
+ // remember last array index in case of nested arrays
+ lastArrayIndex := dec.arrayIndex
+ dec.arrayIndex = 0
+ defer func() {
+ dec.arrayIndex = lastArrayIndex
+ }()
+ vv := reflect.ValueOf(v)
+ vvt := vv.Type()
+ if vvt.Kind() != reflect.Ptr || vvt.Elem().Kind() != reflect.Ptr {
+ dec.err = ErrUnmarshalPtrExpected
+ return 0, dec.err
+ }
+ // not an array not an error, but do not know what to do
+ // do not check syntax
+ for ; dec.cursor < dec.length || dec.read(); dec.cursor++ {
+ switch dec.data[dec.cursor] {
+ case ' ', '\n', '\t', '\r', ',':
+ continue
+ case '[':
+ dec.cursor = dec.cursor + 1
+ // create our new type
+ elt := vv.Elem()
+ n := reflect.New(elt.Type().Elem())
+ var arr UnmarshalerJSONArray
+ var ok bool
+ if arr, ok = n.Interface().(UnmarshalerJSONArray); !ok {
+ dec.err = dec.makeInvalidUnmarshalErr((UnmarshalerJSONArray)(nil))
+ return 0, dec.err
+ }
+ // array is open, char is not space start readings
+ for dec.nextChar() != 0 {
+ // closing array
+ if dec.data[dec.cursor] == ']' {
+ elt.Set(n)
+ dec.cursor = dec.cursor + 1
+ return dec.cursor, nil
+ }
+ // calling unmarshall function for each element of the slice
+ err := arr.UnmarshalJSONArray(dec)
+ if err != nil {
+ return 0, err
+ }
+ dec.arrayIndex++
+ }
+ return 0, dec.raiseInvalidJSONErr(dec.cursor)
+ case 'n':
+ // is null
+ dec.cursor++
+ err := dec.assertNull()
+ if err != nil {
+ return 0, err
+ }
+ return dec.cursor, nil
+ case '{', '"', 'f', 't', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9':
+ // can't unmarshall to struct
+ // we skip array and set Error
+ dec.err = dec.makeInvalidUnmarshalErr((UnmarshalerJSONArray)(nil))
+ err := dec.skipData()
+ if err != nil {
+ return 0, err
+ }
+ return dec.cursor, nil
+ default:
+ return 0, dec.raiseInvalidJSONErr(dec.cursor)
+ }
+ }
+ return 0, dec.raiseInvalidJSONErr(dec.cursor)
+}
+
+func (dec *Decoder) skipArray() (int, error) {
+ var arraysOpen = 1
+ var arraysClosed = 0
+ // var stringOpen byte = 0
+ for j := dec.cursor; j < dec.length || dec.read(); j++ {
+ switch dec.data[j] {
+ case ']':
+ arraysClosed++
+ // everything is closed return
+ if arraysOpen == arraysClosed {
+ // add char to object data
+ return j + 1, nil
+ }
+ case '[':
+ arraysOpen++
+ case '"':
+ j++
+ var isInEscapeSeq bool
+ var isFirstQuote = true
+ for ; j < dec.length || dec.read(); j++ {
+ if dec.data[j] != '"' {
+ continue
+ }
+ if dec.data[j-1] != '\\' || (!isInEscapeSeq && !isFirstQuote) {
+ break
+ } else {
+ isInEscapeSeq = false
+ }
+ if isFirstQuote {
+ isFirstQuote = false
+ }
+ // loop backward and count how many anti slash found
+ // to see if string is effectively escaped
+ ct := 0
+ for i := j - 1; i > 0; i-- {
+ if dec.data[i] != '\\' {
+ break
+ }
+ ct++
+ }
+ // is pair number of slashes, quote is not escaped
+ if ct&1 == 0 {
+ break
+ }
+ isInEscapeSeq = true
+ }
+ default:
+ continue
+ }
+ }
+ return 0, dec.raiseInvalidJSONErr(dec.cursor)
+}
+
+// DecodeArrayFunc is a func type implementing UnmarshalerJSONArray.
+// Use it to cast a `func(*Decoder) error` to Unmarshal an array on the fly.
+
+type DecodeArrayFunc func(*Decoder) error
+
+// UnmarshalJSONArray implements UnmarshalerJSONArray.
+func (f DecodeArrayFunc) UnmarshalJSONArray(dec *Decoder) error {
+ return f(dec)
+}
+
+// IsNil implements UnmarshalerJSONArray.
+func (f DecodeArrayFunc) IsNil() bool {
+ return f == nil
+}
+
+// Add Values functions
+
+// AddArray decodes the JSON value within an object or an array to a UnmarshalerJSONArray.
+func (dec *Decoder) AddArray(v UnmarshalerJSONArray) error {
+ return dec.Array(v)
+}
+
+// AddArrayNull decodes the JSON value within an object or an array to a UnmarshalerJSONArray.
+func (dec *Decoder) AddArrayNull(v interface{}) error {
+ return dec.ArrayNull(v)
+}
+
+// Array decodes the JSON value within an object or an array to a UnmarshalerJSONArray.
+func (dec *Decoder) Array(v UnmarshalerJSONArray) error {
+ newCursor, err := dec.decodeArray(v)
+ if err != nil {
+ return err
+ }
+ dec.cursor = newCursor
+ dec.called |= 1
+ return nil
+}
+
+// ArrayNull decodes the JSON value within an object or an array to a UnmarshalerJSONArray.
+// v should be a pointer to an UnmarshalerJSONArray,
+// if `null` value is encountered in JSON, it will leave the value v untouched,
+// else it will create a new instance of the UnmarshalerJSONArray behind v.
+func (dec *Decoder) ArrayNull(v interface{}) error {
+ newCursor, err := dec.decodeArrayNull(v)
+ if err != nil {
+ return err
+ }
+ dec.cursor = newCursor
+ dec.called |= 1
+ return nil
+}
+
+// Index returns the index of an array being decoded.
+func (dec *Decoder) Index() int {
+ return dec.arrayIndex
+}
diff --git a/vendor/github.com/francoispqt/gojay/decode_bool.go b/vendor/github.com/francoispqt/gojay/decode_bool.go
new file mode 100644
index 0000000000..1dc304ba77
--- /dev/null
+++ b/vendor/github.com/francoispqt/gojay/decode_bool.go
@@ -0,0 +1,241 @@
+package gojay
+
+// DecodeBool reads the next JSON-encoded value from the decoder's input (io.Reader)
+// and stores it in the boolean pointed to by v.
+//
+// See the documentation for Unmarshal for details about the conversion of JSON into a Go value.
+func (dec *Decoder) DecodeBool(v *bool) error {
+ if dec.isPooled == 1 {
+ panic(InvalidUsagePooledDecoderError("Invalid usage of pooled decoder"))
+ }
+ return dec.decodeBool(v)
+}
+func (dec *Decoder) decodeBool(v *bool) error {
+ for ; dec.cursor < dec.length || dec.read(); dec.cursor++ {
+ switch dec.data[dec.cursor] {
+ case ' ', '\n', '\t', '\r', ',':
+ continue
+ case 't':
+ dec.cursor++
+ err := dec.assertTrue()
+ if err != nil {
+ return err
+ }
+ *v = true
+ return nil
+ case 'f':
+ dec.cursor++
+ err := dec.assertFalse()
+ if err != nil {
+ return err
+ }
+ *v = false
+ return nil
+ case 'n':
+ dec.cursor++
+ err := dec.assertNull()
+ if err != nil {
+ return err
+ }
+ *v = false
+ return nil
+ default:
+ dec.err = dec.makeInvalidUnmarshalErr(v)
+ err := dec.skipData()
+ if err != nil {
+ return err
+ }
+ return nil
+ }
+ }
+ return nil
+}
+func (dec *Decoder) decodeBoolNull(v **bool) error {
+ for ; dec.cursor < dec.length || dec.read(); dec.cursor++ {
+ switch dec.data[dec.cursor] {
+ case ' ', '\n', '\t', '\r', ',':
+ continue
+ case 't':
+ dec.cursor++
+ err := dec.assertTrue()
+ if err != nil {
+ return err
+ }
+ if *v == nil {
+ *v = new(bool)
+ }
+ **v = true
+ return nil
+ case 'f':
+ dec.cursor++
+ err := dec.assertFalse()
+ if err != nil {
+ return err
+ }
+ if *v == nil {
+ *v = new(bool)
+ }
+ **v = false
+ return nil
+ case 'n':
+ dec.cursor++
+ err := dec.assertNull()
+ if err != nil {
+ return err
+ }
+ return nil
+ default:
+ dec.err = dec.makeInvalidUnmarshalErr(v)
+ err := dec.skipData()
+ if err != nil {
+ return err
+ }
+ return nil
+ }
+ }
+ return nil
+}
+
+func (dec *Decoder) assertTrue() error {
+ i := 0
+ for ; dec.cursor < dec.length || dec.read(); dec.cursor++ {
+ switch i {
+ case 0:
+ if dec.data[dec.cursor] != 'r' {
+ return dec.raiseInvalidJSONErr(dec.cursor)
+ }
+ case 1:
+ if dec.data[dec.cursor] != 'u' {
+ return dec.raiseInvalidJSONErr(dec.cursor)
+ }
+ case 2:
+ if dec.data[dec.cursor] != 'e' {
+ return dec.raiseInvalidJSONErr(dec.cursor)
+ }
+ case 3:
+ switch dec.data[dec.cursor] {
+ case ' ', '\b', '\t', '\n', ',', ']', '}':
+ // dec.cursor--
+ return nil
+ default:
+ return dec.raiseInvalidJSONErr(dec.cursor)
+ }
+ }
+ i++
+ }
+ if i == 3 {
+ return nil
+ }
+ return dec.raiseInvalidJSONErr(dec.cursor)
+}
+
+func (dec *Decoder) assertNull() error {
+ i := 0
+ for ; dec.cursor < dec.length || dec.read(); dec.cursor++ {
+ switch i {
+ case 0:
+ if dec.data[dec.cursor] != 'u' {
+ return dec.raiseInvalidJSONErr(dec.cursor)
+ }
+ case 1:
+ if dec.data[dec.cursor] != 'l' {
+ return dec.raiseInvalidJSONErr(dec.cursor)
+ }
+ case 2:
+ if dec.data[dec.cursor] != 'l' {
+ return dec.raiseInvalidJSONErr(dec.cursor)
+ }
+ case 3:
+ switch dec.data[dec.cursor] {
+ case ' ', '\t', '\n', ',', ']', '}':
+ // dec.cursor--
+ return nil
+ default:
+ return dec.raiseInvalidJSONErr(dec.cursor)
+ }
+ }
+ i++
+ }
+ if i == 3 {
+ return nil
+ }
+ return dec.raiseInvalidJSONErr(dec.cursor)
+}
+
+func (dec *Decoder) assertFalse() error {
+ i := 0
+ for ; dec.cursor < dec.length || dec.read(); dec.cursor++ {
+ switch i {
+ case 0:
+ if dec.data[dec.cursor] != 'a' {
+ return dec.raiseInvalidJSONErr(dec.cursor)
+ }
+ case 1:
+ if dec.data[dec.cursor] != 'l' {
+ return dec.raiseInvalidJSONErr(dec.cursor)
+ }
+ case 2:
+ if dec.data[dec.cursor] != 's' {
+ return dec.raiseInvalidJSONErr(dec.cursor)
+ }
+ case 3:
+ if dec.data[dec.cursor] != 'e' {
+ return dec.raiseInvalidJSONErr(dec.cursor)
+ }
+ case 4:
+ switch dec.data[dec.cursor] {
+ case ' ', '\t', '\n', ',', ']', '}':
+ // dec.cursor--
+ return nil
+ default:
+ return dec.raiseInvalidJSONErr(dec.cursor)
+ }
+ }
+ i++
+ }
+ if i == 4 {
+ return nil
+ }
+ return dec.raiseInvalidJSONErr(dec.cursor)
+}
+
+// Add Values functions
+
+// AddBool decodes the JSON value within an object or an array to a *bool.
+// If next key is neither null nor a JSON boolean, an InvalidUnmarshalError will be returned.
+// If next key is null, bool will be false.
+func (dec *Decoder) AddBool(v *bool) error {
+ return dec.Bool(v)
+}
+
+// AddBoolNull decodes the JSON value within an object or an array to a *bool.
+// If next key is neither null nor a JSON boolean, an InvalidUnmarshalError will be returned.
+// If next key is null, bool will be false.
+// If a `null` is encountered, gojay does not change the value of the pointer.
+func (dec *Decoder) AddBoolNull(v **bool) error {
+ return dec.BoolNull(v)
+}
+
+// Bool decodes the JSON value within an object or an array to a *bool.
+// If next key is neither null nor a JSON boolean, an InvalidUnmarshalError will be returned.
+// If next key is null, bool will be false.
+func (dec *Decoder) Bool(v *bool) error {
+ err := dec.decodeBool(v)
+ if err != nil {
+ return err
+ }
+ dec.called |= 1
+ return nil
+}
+
+// BoolNull decodes the JSON value within an object or an array to a *bool.
+// If next key is neither null nor a JSON boolean, an InvalidUnmarshalError will be returned.
+// If next key is null, bool will be false.
+func (dec *Decoder) BoolNull(v **bool) error {
+ err := dec.decodeBoolNull(v)
+ if err != nil {
+ return err
+ }
+ dec.called |= 1
+ return nil
+}
diff --git a/vendor/github.com/francoispqt/gojay/decode_embedded_json.go b/vendor/github.com/francoispqt/gojay/decode_embedded_json.go
new file mode 100644
index 0000000000..67fcc2eaed
--- /dev/null
+++ b/vendor/github.com/francoispqt/gojay/decode_embedded_json.go
@@ -0,0 +1,85 @@
+package gojay
+
+// EmbeddedJSON is a raw encoded JSON value.
+// It can be used to delay JSON decoding or precompute a JSON encoding.
+type EmbeddedJSON []byte
+
+func (dec *Decoder) decodeEmbeddedJSON(ej *EmbeddedJSON) error {
+ var err error
+ if ej == nil {
+ return InvalidUnmarshalError("Invalid nil pointer given")
+ }
+ var beginOfEmbeddedJSON int
+ for ; dec.cursor < dec.length || dec.read(); dec.cursor++ {
+ switch dec.data[dec.cursor] {
+ case ' ', '\n', '\t', '\r', ',':
+ continue
+ // is null
+ case 'n':
+ beginOfEmbeddedJSON = dec.cursor
+ dec.cursor++
+ err := dec.assertNull()
+ if err != nil {
+ return err
+ }
+ case 't':
+ beginOfEmbeddedJSON = dec.cursor
+ dec.cursor++
+ err := dec.assertTrue()
+ if err != nil {
+ return err
+ }
+ // is false
+ case 'f':
+ beginOfEmbeddedJSON = dec.cursor
+ dec.cursor++
+ err := dec.assertFalse()
+ if err != nil {
+ return err
+ }
+ // is an object
+ case '{':
+ beginOfEmbeddedJSON = dec.cursor
+ dec.cursor = dec.cursor + 1
+ dec.cursor, err = dec.skipObject()
+ // is string
+ case '"':
+ beginOfEmbeddedJSON = dec.cursor
+ dec.cursor = dec.cursor + 1
+ err = dec.skipString() // why no new dec.cursor in result?
+ // is array
+ case '[':
+ beginOfEmbeddedJSON = dec.cursor
+ dec.cursor = dec.cursor + 1
+ dec.cursor, err = dec.skipArray()
+ case '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '-':
+ beginOfEmbeddedJSON = dec.cursor
+ dec.cursor, err = dec.skipNumber()
+ }
+ break
+ }
+ if err == nil {
+ if dec.cursor-1 >= beginOfEmbeddedJSON {
+ *ej = append(*ej, dec.data[beginOfEmbeddedJSON:dec.cursor]...)
+ }
+ dec.called |= 1
+ }
+ return err
+}
+
+// AddEmbeddedJSON adds an EmbeddedsJSON to the value pointed by v.
+// It can be used to delay JSON decoding or precompute a JSON encoding.
+func (dec *Decoder) AddEmbeddedJSON(v *EmbeddedJSON) error {
+ return dec.EmbeddedJSON(v)
+}
+
+// EmbeddedJSON adds an EmbeddedsJSON to the value pointed by v.
+// It can be used to delay JSON decoding or precompute a JSON encoding.
+func (dec *Decoder) EmbeddedJSON(v *EmbeddedJSON) error {
+ err := dec.decodeEmbeddedJSON(v)
+ if err != nil {
+ return err
+ }
+ dec.called |= 1
+ return nil
+}
diff --git a/vendor/github.com/francoispqt/gojay/decode_interface.go b/vendor/github.com/francoispqt/gojay/decode_interface.go
new file mode 100644
index 0000000000..015790d854
--- /dev/null
+++ b/vendor/github.com/francoispqt/gojay/decode_interface.go
@@ -0,0 +1,130 @@
+package gojay
+
+// TODO @afiune for now we are using the standard json unmarshaling but in
+// the future it would be great to implement one here inside this repo
+import "encoding/json"
+
+// DecodeInterface reads the next JSON-encoded value from the decoder's input (io.Reader) and stores it in the value pointed to by i.
+//
+// i must be an interface poiter
+func (dec *Decoder) DecodeInterface(i *interface{}) error {
+ if dec.isPooled == 1 {
+ panic(InvalidUsagePooledDecoderError("Invalid usage of pooled decoder"))
+ }
+ err := dec.decodeInterface(i)
+ return err
+}
+
+func (dec *Decoder) decodeInterface(i *interface{}) error {
+ start, end, err := dec.getObject()
+ if err != nil {
+ dec.cursor = start
+ return err
+ }
+
+ // if start & end are equal the object is a null, don't unmarshal
+ if start == end {
+ return nil
+ }
+
+ object := dec.data[start:end]
+ if err = json.Unmarshal(object, i); err != nil {
+ return err
+ }
+
+ dec.cursor = end
+ return nil
+}
+
+// @afiune Maybe return the type as well?
+func (dec *Decoder) getObject() (start int, end int, err error) {
+ // start cursor
+ for ; dec.cursor < dec.length || dec.read(); dec.cursor++ {
+ switch dec.data[dec.cursor] {
+ case ' ', '\n', '\t', '\r', ',':
+ continue
+ // is null
+ case 'n':
+ dec.cursor++
+ err = dec.assertNull()
+ if err != nil {
+ return
+ }
+ // Set start & end to the same cursor to indicate the object
+ // is a null and should not be unmarshal
+ start = dec.cursor
+ end = dec.cursor
+ return
+ case 't':
+ start = dec.cursor
+ dec.cursor++
+ err = dec.assertTrue()
+ if err != nil {
+ return
+ }
+ end = dec.cursor
+ dec.cursor++
+ return
+ // is false
+ case 'f':
+ start = dec.cursor
+ dec.cursor++
+ err = dec.assertFalse()
+ if err != nil {
+ return
+ }
+ end = dec.cursor
+ dec.cursor++
+ return
+ // is an object
+ case '{':
+ start = dec.cursor
+ dec.cursor++
+ end, err = dec.skipObject()
+ dec.cursor = end
+ return
+ // is string
+ case '"':
+ start = dec.cursor
+ dec.cursor++
+ start, end, err = dec.getString()
+ start--
+ dec.cursor = end
+ return
+ // is array
+ case '[':
+ start = dec.cursor
+ dec.cursor++
+ end, err = dec.skipArray()
+ dec.cursor = end
+ return
+ case '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '-':
+ start = dec.cursor
+ end, err = dec.skipNumber()
+ dec.cursor = end
+ return
+ default:
+ err = dec.raiseInvalidJSONErr(dec.cursor)
+ return
+ }
+ }
+ err = dec.raiseInvalidJSONErr(dec.cursor)
+ return
+}
+
+// Add Values functions
+
+// AddInterface decodes the JSON value within an object or an array to a interface{}.
+func (dec *Decoder) AddInterface(v *interface{}) error {
+ return dec.Interface(v)
+}
+
+// Interface decodes the JSON value within an object or an array to an interface{}.
+func (dec *Decoder) Interface(value *interface{}) error {
+ err := dec.decodeInterface(value)
+ if err != nil {
+ return err
+ }
+ dec.called |= 1
+ return nil
+}
diff --git a/vendor/github.com/francoispqt/gojay/decode_number.go b/vendor/github.com/francoispqt/gojay/decode_number.go
new file mode 100644
index 0000000000..0042b471e2
--- /dev/null
+++ b/vendor/github.com/francoispqt/gojay/decode_number.go
@@ -0,0 +1,118 @@
+package gojay
+
+import (
+ "math"
+)
+
+var digits []int8
+
+const maxInt64toMultiply = math.MaxInt64 / 10
+const maxInt32toMultiply = math.MaxInt32 / 10
+const maxInt16toMultiply = math.MaxInt16 / 10
+const maxInt8toMultiply = math.MaxInt8 / 10
+const maxUint8toMultiply = math.MaxUint8 / 10
+const maxUint16toMultiply = math.MaxUint16 / 10
+const maxUint32toMultiply = math.MaxUint32 / 10
+const maxUint64toMultiply = math.MaxUint64 / 10
+const maxUint32Length = 10
+const maxUint64Length = 20
+const maxUint16Length = 5
+const maxUint8Length = 3
+const maxInt32Length = 10
+const maxInt64Length = 19
+const maxInt16Length = 5
+const maxInt8Length = 3
+const invalidNumber = int8(-1)
+
+var pow10uint64 = [21]uint64{
+ 0,
+ 1,
+ 10,
+ 100,
+ 1000,
+ 10000,
+ 100000,
+ 1000000,
+ 10000000,
+ 100000000,
+ 1000000000,
+ 10000000000,
+ 100000000000,
+ 1000000000000,
+ 10000000000000,
+ 100000000000000,
+ 1000000000000000,
+ 10000000000000000,
+ 100000000000000000,
+ 1000000000000000000,
+ 10000000000000000000,
+}
+
+var skipNumberEndCursorIncrement [256]int
+
+func init() {
+ digits = make([]int8, 256)
+ for i := 0; i < len(digits); i++ {
+ digits[i] = invalidNumber
+ }
+ for i := int8('0'); i <= int8('9'); i++ {
+ digits[i] = i - int8('0')
+ }
+
+ for i := 0; i < 256; i++ {
+ switch i {
+ case '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '.', 'e', 'E', '+', '-':
+ skipNumberEndCursorIncrement[i] = 1
+ }
+ }
+}
+
+func (dec *Decoder) skipNumber() (int, error) {
+ end := dec.cursor + 1
+ // look for following numbers
+ for j := dec.cursor + 1; j < dec.length || dec.read(); j++ {
+ end += skipNumberEndCursorIncrement[dec.data[j]]
+
+ switch dec.data[j] {
+ case '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '.', 'e', 'E', '+', '-', ' ', '\n', '\t', '\r':
+ continue
+ case ',', '}', ']':
+ return end, nil
+ default:
+ // invalid json we expect numbers, dot (single one), comma, or spaces
+ return end, dec.raiseInvalidJSONErr(dec.cursor)
+ }
+ }
+
+ return end, nil
+}
+
+func (dec *Decoder) getExponent() (int64, error) {
+ start := dec.cursor
+ end := dec.cursor
+ for ; dec.cursor < dec.length || dec.read(); dec.cursor++ {
+ switch dec.data[dec.cursor] { // is positive
+ case '0', '1', '2', '3', '4', '5', '6', '7', '8', '9':
+ end = dec.cursor + 1
+ case '-':
+ dec.cursor++
+ exp, err := dec.getExponent()
+ return -exp, err
+ case '+':
+ dec.cursor++
+ return dec.getExponent()
+ default:
+ // if nothing return 0
+ // could raise error
+ if start == end {
+ return 0, dec.raiseInvalidJSONErr(dec.cursor)
+ }
+ return dec.atoi64(start, end-1), nil
+ }
+ }
+ if start == end {
+
+ return 0, dec.raiseInvalidJSONErr(dec.cursor)
+ }
+ return dec.atoi64(start, end-1), nil
+}
diff --git a/vendor/github.com/francoispqt/gojay/decode_number_float.go b/vendor/github.com/francoispqt/gojay/decode_number_float.go
new file mode 100644
index 0000000000..f76c5861e5
--- /dev/null
+++ b/vendor/github.com/francoispqt/gojay/decode_number_float.go
@@ -0,0 +1,516 @@
+package gojay
+
+// DecodeFloat64 reads the next JSON-encoded value from the decoder's input (io.Reader) and stores it in the float64 pointed to by v.
+//
+// See the documentation for Unmarshal for details about the conversion of JSON into a Go value.
+func (dec *Decoder) DecodeFloat64(v *float64) error {
+ if dec.isPooled == 1 {
+ panic(InvalidUsagePooledDecoderError("Invalid usage of pooled decoder"))
+ }
+ return dec.decodeFloat64(v)
+}
+func (dec *Decoder) decodeFloat64(v *float64) error {
+ for ; dec.cursor < dec.length || dec.read(); dec.cursor++ {
+ switch c := dec.data[dec.cursor]; c {
+ case ' ', '\n', '\t', '\r', ',':
+ continue
+ case '0', '1', '2', '3', '4', '5', '6', '7', '8', '9':
+ val, err := dec.getFloat()
+ if err != nil {
+ return err
+ }
+ *v = val
+ return nil
+ case '-':
+ dec.cursor = dec.cursor + 1
+ val, err := dec.getFloatNegative()
+ if err != nil {
+ return err
+ }
+ *v = -val
+ return nil
+ case 'n':
+ dec.cursor++
+ err := dec.assertNull()
+ if err != nil {
+ return err
+ }
+ return nil
+ default:
+ dec.err = dec.makeInvalidUnmarshalErr(v)
+ err := dec.skipData()
+ if err != nil {
+ return err
+ }
+ return nil
+ }
+ }
+ return dec.raiseInvalidJSONErr(dec.cursor)
+}
+func (dec *Decoder) decodeFloat64Null(v **float64) error {
+ for ; dec.cursor < dec.length || dec.read(); dec.cursor++ {
+ switch c := dec.data[dec.cursor]; c {
+ case ' ', '\n', '\t', '\r', ',':
+ continue
+ case '0', '1', '2', '3', '4', '5', '6', '7', '8', '9':
+ val, err := dec.getFloat()
+ if err != nil {
+ return err
+ }
+ if *v == nil {
+ *v = new(float64)
+ }
+ **v = val
+ return nil
+ case '-':
+ dec.cursor = dec.cursor + 1
+ val, err := dec.getFloatNegative()
+ if err != nil {
+ return err
+ }
+ if *v == nil {
+ *v = new(float64)
+ }
+ **v = -val
+ return nil
+ case 'n':
+ dec.cursor++
+ err := dec.assertNull()
+ if err != nil {
+ return err
+ }
+ return nil
+ default:
+ dec.err = dec.makeInvalidUnmarshalErr(v)
+ err := dec.skipData()
+ if err != nil {
+ return err
+ }
+ return nil
+ }
+ }
+ return dec.raiseInvalidJSONErr(dec.cursor)
+}
+
+func (dec *Decoder) getFloatNegative() (float64, error) {
+ // look for following numbers
+ for ; dec.cursor < dec.length || dec.read(); dec.cursor++ {
+ switch dec.data[dec.cursor] {
+ case '0', '1', '2', '3', '4', '5', '6', '7', '8', '9':
+ return dec.getFloat()
+ default:
+ return 0, dec.raiseInvalidJSONErr(dec.cursor)
+ }
+ }
+ return 0, dec.raiseInvalidJSONErr(dec.cursor)
+}
+
+func (dec *Decoder) getFloat() (float64, error) {
+ var end = dec.cursor
+ var start = dec.cursor
+ // look for following numbers
+ for j := dec.cursor + 1; j < dec.length || dec.read(); j++ {
+ switch dec.data[j] {
+ case '0', '1', '2', '3', '4', '5', '6', '7', '8', '9':
+ end = j
+ continue
+ case '.':
+ // we get part before decimal as integer
+ beforeDecimal := dec.atoi64(start, end)
+ // then we get part after decimal as integer
+ start = j + 1
+ // get number after the decimal point
+ for i := j + 1; i < dec.length || dec.read(); i++ {
+ c := dec.data[i]
+ if isDigit(c) {
+ end = i
+ // multiply the before decimal point portion by 10 using bitwise
+ // make sure it doesn't overflow
+ if end-start < 18 {
+ beforeDecimal = (beforeDecimal << 3) + (beforeDecimal << 1)
+ }
+ continue
+ } else if (c == 'e' || c == 'E') && j < i-1 {
+ // we have an exponent, convert first the value we got before the exponent
+ var afterDecimal int64
+ expI := end - start + 2
+ // if exp is too long, it means number is too long, just truncate the number
+ if expI >= len(pow10uint64) || expI < 0 {
+ expI = len(pow10uint64) - 2
+ afterDecimal = dec.atoi64(start, start+expI-2)
+ } else {
+ // then we add both integers
+ // then we divide the number by the power found
+ afterDecimal = dec.atoi64(start, end)
+ }
+ dec.cursor = i + 1
+ pow := pow10uint64[expI]
+ floatVal := float64(beforeDecimal+afterDecimal) / float64(pow)
+ exp, err := dec.getExponent()
+ if err != nil {
+ return 0, err
+ }
+ pExp := (exp + (exp >> 31)) ^ (exp >> 31) + 1 // absolute exponent
+ if pExp >= int64(len(pow10uint64)) || pExp < 0 {
+ return 0, dec.raiseInvalidJSONErr(dec.cursor)
+ }
+ // if exponent is negative
+ if exp < 0 {
+ return float64(floatVal) * (1 / float64(pow10uint64[pExp])), nil
+ }
+ return float64(floatVal) * float64(pow10uint64[pExp]), nil
+ }
+ dec.cursor = i
+ break
+ }
+ if end >= dec.length || end < start {
+ return 0, dec.raiseInvalidJSONErr(dec.cursor)
+ }
+ var afterDecimal int64
+ expI := end - start + 2
+ // if exp is too long, it means number is too long, just truncate the number
+ if expI >= len(pow10uint64) || expI < 0 {
+ expI = 19
+ afterDecimal = dec.atoi64(start, start+expI-2)
+ } else {
+ afterDecimal = dec.atoi64(start, end)
+ }
+
+ pow := pow10uint64[expI]
+ // then we add both integers
+ // then we divide the number by the power found
+ return float64(beforeDecimal+afterDecimal) / float64(pow), nil
+ case 'e', 'E':
+ dec.cursor = j + 1
+ // we get part before decimal as integer
+ beforeDecimal := uint64(dec.atoi64(start, end))
+ // get exponent
+ exp, err := dec.getExponent()
+ if err != nil {
+ return 0, err
+ }
+ pExp := (exp + (exp >> 31)) ^ (exp >> 31) + 1 // abs
+ if pExp >= int64(len(pow10uint64)) || pExp < 0 {
+ return 0, dec.raiseInvalidJSONErr(dec.cursor)
+ }
+ // if exponent is negative
+ if exp < 0 {
+ return float64(beforeDecimal) * (1 / float64(pow10uint64[pExp])), nil
+ }
+ return float64(beforeDecimal) * float64(pow10uint64[pExp]), nil
+ case ' ', '\n', '\t', '\r', ',', '}', ']': // does not have decimal
+ dec.cursor = j
+ return float64(dec.atoi64(start, end)), nil
+ }
+ // invalid json we expect numbers, dot (single one), comma, or spaces
+ return 0, dec.raiseInvalidJSONErr(dec.cursor)
+ }
+ return float64(dec.atoi64(start, end)), nil
+}
+
+// DecodeFloat32 reads the next JSON-encoded value from the decoder's input (io.Reader) and stores it in the float32 pointed to by v.
+//
+// See the documentation for Unmarshal for details about the conversion of JSON into a Go value.
+func (dec *Decoder) DecodeFloat32(v *float32) error {
+ if dec.isPooled == 1 {
+ panic(InvalidUsagePooledDecoderError("Invalid usage of pooled decoder"))
+ }
+ return dec.decodeFloat32(v)
+}
+func (dec *Decoder) decodeFloat32(v *float32) error {
+ for ; dec.cursor < dec.length || dec.read(); dec.cursor++ {
+ switch c := dec.data[dec.cursor]; c {
+ case ' ', '\n', '\t', '\r', ',':
+ continue
+ case '0', '1', '2', '3', '4', '5', '6', '7', '8', '9':
+ val, err := dec.getFloat32()
+ if err != nil {
+ return err
+ }
+ *v = val
+ return nil
+ case '-':
+ dec.cursor = dec.cursor + 1
+ val, err := dec.getFloat32Negative()
+ if err != nil {
+ return err
+ }
+ *v = -val
+ return nil
+ case 'n':
+ dec.cursor++
+ err := dec.assertNull()
+ if err != nil {
+ return err
+ }
+ return nil
+ default:
+ dec.err = dec.makeInvalidUnmarshalErr(v)
+ err := dec.skipData()
+ if err != nil {
+ return err
+ }
+ return nil
+ }
+ }
+ return dec.raiseInvalidJSONErr(dec.cursor)
+}
+func (dec *Decoder) decodeFloat32Null(v **float32) error {
+ for ; dec.cursor < dec.length || dec.read(); dec.cursor++ {
+ switch c := dec.data[dec.cursor]; c {
+ case ' ', '\n', '\t', '\r', ',':
+ continue
+ case '0', '1', '2', '3', '4', '5', '6', '7', '8', '9':
+ val, err := dec.getFloat32()
+ if err != nil {
+ return err
+ }
+ if *v == nil {
+ *v = new(float32)
+ }
+ **v = val
+ return nil
+ case '-':
+ dec.cursor = dec.cursor + 1
+ val, err := dec.getFloat32Negative()
+ if err != nil {
+ return err
+ }
+ if *v == nil {
+ *v = new(float32)
+ }
+ **v = -val
+ return nil
+ case 'n':
+ dec.cursor++
+ err := dec.assertNull()
+ if err != nil {
+ return err
+ }
+ return nil
+ default:
+ dec.err = dec.makeInvalidUnmarshalErr(v)
+ err := dec.skipData()
+ if err != nil {
+ return err
+ }
+ return nil
+ }
+ }
+ return dec.raiseInvalidJSONErr(dec.cursor)
+}
+
+func (dec *Decoder) getFloat32Negative() (float32, error) {
+ // look for following numbers
+ for ; dec.cursor < dec.length || dec.read(); dec.cursor++ {
+ switch dec.data[dec.cursor] {
+ case '0', '1', '2', '3', '4', '5', '6', '7', '8', '9':
+ return dec.getFloat32()
+ default:
+ return 0, dec.raiseInvalidJSONErr(dec.cursor)
+ }
+ }
+ return 0, dec.raiseInvalidJSONErr(dec.cursor)
+}
+
+func (dec *Decoder) getFloat32() (float32, error) {
+ var end = dec.cursor
+ var start = dec.cursor
+ // look for following numbers
+ for j := dec.cursor + 1; j < dec.length || dec.read(); j++ {
+ switch dec.data[j] {
+ case '0', '1', '2', '3', '4', '5', '6', '7', '8', '9':
+ end = j
+ continue
+ case '.':
+ // we get part before decimal as integer
+ beforeDecimal := dec.atoi64(start, end)
+ // then we get part after decimal as integer
+ start = j + 1
+ // get number after the decimal point
+ // multiple the before decimal point portion by 10 using bitwise
+ for i := j + 1; i < dec.length || dec.read(); i++ {
+ c := dec.data[i]
+ if isDigit(c) {
+ end = i
+ // multiply the before decimal point portion by 10 using bitwise
+ // make sure it desn't overflow
+ if end-start < 9 {
+ beforeDecimal = (beforeDecimal << 3) + (beforeDecimal << 1)
+ }
+ continue
+ } else if (c == 'e' || c == 'E') && j < i-1 {
+ // we get the number before decimal
+ var afterDecimal int64
+ expI := end - start + 2
+ // if exp is too long, it means number is too long, just truncate the number
+ if expI >= 12 || expI < 0 {
+ expI = 10
+ afterDecimal = dec.atoi64(start, start+expI-2)
+ } else {
+ afterDecimal = dec.atoi64(start, end)
+ }
+ dec.cursor = i + 1
+ pow := pow10uint64[expI]
+ // then we add both integers
+ // then we divide the number by the power found
+ floatVal := float32(beforeDecimal+afterDecimal) / float32(pow)
+ exp, err := dec.getExponent()
+ if err != nil {
+ return 0, err
+ }
+ pExp := (exp + (exp >> 31)) ^ (exp >> 31) + 1 // abs
+ if pExp >= int64(len(pow10uint64)) || pExp < 0 {
+ return 0, dec.raiseInvalidJSONErr(dec.cursor)
+ }
+ // if exponent is negative
+ if exp < 0 {
+ return float32(floatVal) * (1 / float32(pow10uint64[pExp])), nil
+ }
+ return float32(floatVal) * float32(pow10uint64[pExp]), nil
+ }
+ dec.cursor = i
+ break
+ }
+ if end >= dec.length || end < start {
+ return 0, dec.raiseInvalidJSONErr(dec.cursor)
+ }
+ // then we add both integers
+ // then we divide the number by the power found
+ var afterDecimal int64
+ expI := end - start + 2
+ // if exp is too long, it means number is too long, just truncate the number
+ if expI >= 12 || expI < 0 {
+ expI = 10
+ afterDecimal = dec.atoi64(start, start+expI-2)
+ } else {
+ // then we add both integers
+ // then we divide the number by the power found
+ afterDecimal = dec.atoi64(start, end)
+ }
+ pow := pow10uint64[expI]
+ return float32(beforeDecimal+afterDecimal) / float32(pow), nil
+ case 'e', 'E':
+ dec.cursor = j + 1
+ // we get part before decimal as integer
+ beforeDecimal := dec.atoi64(start, end)
+ // get exponent
+ exp, err := dec.getExponent()
+ if err != nil {
+ return 0, err
+ }
+ pExp := (exp + (exp >> 31)) ^ (exp >> 31) + 1
+ if pExp >= int64(len(pow10uint64)) || pExp < 0 {
+ return 0, dec.raiseInvalidJSONErr(dec.cursor)
+ }
+ // if exponent is negative
+ if exp < 0 {
+ return float32(beforeDecimal) * (1 / float32(pow10uint64[pExp])), nil
+ }
+ return float32(beforeDecimal) * float32(pow10uint64[pExp]), nil
+ case ' ', '\n', '\t', '\r', ',', '}', ']': // does not have decimal
+ dec.cursor = j
+ return float32(dec.atoi64(start, end)), nil
+ }
+ // invalid json we expect numbers, dot (single one), comma, or spaces
+ return 0, dec.raiseInvalidJSONErr(dec.cursor)
+ }
+ return float32(dec.atoi64(start, end)), nil
+}
+
+// Add Values functions
+
+// AddFloat decodes the JSON value within an object or an array to a *float64.
+// If next key value overflows float64, an InvalidUnmarshalError error will be returned.
+func (dec *Decoder) AddFloat(v *float64) error {
+ return dec.Float64(v)
+}
+
+// AddFloatNull decodes the JSON value within an object or an array to a *float64.
+// If next key value overflows float64, an InvalidUnmarshalError error will be returned.
+// If a `null` is encountered, gojay does not change the value of the pointer.
+func (dec *Decoder) AddFloatNull(v **float64) error {
+ return dec.Float64Null(v)
+}
+
+// AddFloat64 decodes the JSON value within an object or an array to a *float64.
+// If next key value overflows float64, an InvalidUnmarshalError error will be returned.
+func (dec *Decoder) AddFloat64(v *float64) error {
+ return dec.Float64(v)
+}
+
+// AddFloat64Null decodes the JSON value within an object or an array to a *float64.
+// If next key value overflows float64, an InvalidUnmarshalError error will be returned.
+// If a `null` is encountered, gojay does not change the value of the pointer.
+func (dec *Decoder) AddFloat64Null(v **float64) error {
+ return dec.Float64Null(v)
+}
+
+// AddFloat32 decodes the JSON value within an object or an array to a *float64.
+// If next key value overflows float64, an InvalidUnmarshalError error will be returned.
+func (dec *Decoder) AddFloat32(v *float32) error {
+ return dec.Float32(v)
+}
+
+// AddFloat32Null decodes the JSON value within an object or an array to a *float64.
+// If next key value overflows float64, an InvalidUnmarshalError error will be returned.
+// If a `null` is encountered, gojay does not change the value of the pointer.
+func (dec *Decoder) AddFloat32Null(v **float32) error {
+ return dec.Float32Null(v)
+}
+
+// Float decodes the JSON value within an object or an array to a *float64.
+// If next key value overflows float64, an InvalidUnmarshalError error will be returned.
+func (dec *Decoder) Float(v *float64) error {
+ return dec.Float64(v)
+}
+
+// FloatNull decodes the JSON value within an object or an array to a *float64.
+// If next key value overflows float64, an InvalidUnmarshalError error will be returned.
+func (dec *Decoder) FloatNull(v **float64) error {
+ return dec.Float64Null(v)
+}
+
+// Float64 decodes the JSON value within an object or an array to a *float64.
+// If next key value overflows float64, an InvalidUnmarshalError error will be returned.
+func (dec *Decoder) Float64(v *float64) error {
+ err := dec.decodeFloat64(v)
+ if err != nil {
+ return err
+ }
+ dec.called |= 1
+ return nil
+}
+
+// Float64Null decodes the JSON value within an object or an array to a *float64.
+// If next key value overflows float64, an InvalidUnmarshalError error will be returned.
+func (dec *Decoder) Float64Null(v **float64) error {
+ err := dec.decodeFloat64Null(v)
+ if err != nil {
+ return err
+ }
+ dec.called |= 1
+ return nil
+}
+
+// Float32 decodes the JSON value within an object or an array to a *float64.
+// If next key value overflows float64, an InvalidUnmarshalError error will be returned.
+func (dec *Decoder) Float32(v *float32) error {
+ err := dec.decodeFloat32(v)
+ if err != nil {
+ return err
+ }
+ dec.called |= 1
+ return nil
+}
+
+// Float32Null decodes the JSON value within an object or an array to a *float64.
+// If next key value overflows float64, an InvalidUnmarshalError error will be returned.
+func (dec *Decoder) Float32Null(v **float32) error {
+ err := dec.decodeFloat32Null(v)
+ if err != nil {
+ return err
+ }
+ dec.called |= 1
+ return nil
+}
diff --git a/vendor/github.com/francoispqt/gojay/decode_number_int.go b/vendor/github.com/francoispqt/gojay/decode_number_int.go
new file mode 100644
index 0000000000..8429049fbf
--- /dev/null
+++ b/vendor/github.com/francoispqt/gojay/decode_number_int.go
@@ -0,0 +1,1338 @@
+package gojay
+
+import (
+ "fmt"
+ "math"
+)
+
+// DecodeInt reads the next JSON-encoded value from the decoder's input (io.Reader) and stores it in the int pointed to by v.
+//
+// See the documentation for Unmarshal for details about the conversion of JSON into a Go value.
+func (dec *Decoder) DecodeInt(v *int) error {
+ if dec.isPooled == 1 {
+ panic(InvalidUsagePooledDecoderError("Invalid usage of pooled decoder"))
+ }
+ return dec.decodeInt(v)
+}
+func (dec *Decoder) decodeInt(v *int) error {
+ for ; dec.cursor < dec.length || dec.read(); dec.cursor++ {
+ switch c := dec.data[dec.cursor]; c {
+ case ' ', '\n', '\t', '\r', ',':
+ continue
+ // we don't look for 0 as leading zeros are invalid per RFC
+ case '0', '1', '2', '3', '4', '5', '6', '7', '8', '9':
+ val, err := dec.getInt64()
+ if err != nil {
+ return err
+ }
+ *v = int(val)
+ return nil
+ case '-':
+ dec.cursor = dec.cursor + 1
+ val, err := dec.getInt64Negative()
+ if err != nil {
+ return err
+ }
+ *v = -int(val)
+ return nil
+ case 'n':
+ dec.cursor++
+ err := dec.assertNull()
+ if err != nil {
+ return err
+ }
+ return nil
+ default:
+ dec.err = InvalidUnmarshalError(
+ fmt.Sprintf(
+ "Cannot unmarshall to int, wrong char '%s' found at pos %d",
+ string(dec.data[dec.cursor]),
+ dec.cursor,
+ ),
+ )
+ err := dec.skipData()
+ if err != nil {
+ return err
+ }
+ return nil
+ }
+ }
+ return dec.raiseInvalidJSONErr(dec.cursor)
+}
+
+func (dec *Decoder) decodeIntNull(v **int) error {
+ for ; dec.cursor < dec.length || dec.read(); dec.cursor++ {
+ switch c := dec.data[dec.cursor]; c {
+ case ' ', '\n', '\t', '\r', ',':
+ continue
+ // we don't look for 0 as leading zeros are invalid per RFC
+ case '0', '1', '2', '3', '4', '5', '6', '7', '8', '9':
+ val, err := dec.getInt64()
+ if err != nil {
+ return err
+ }
+ if *v == nil {
+ *v = new(int)
+ }
+ **v = int(val)
+ return nil
+ case '-':
+ dec.cursor = dec.cursor + 1
+ val, err := dec.getInt64Negative()
+ if err != nil {
+ return err
+ }
+ if *v == nil {
+ *v = new(int)
+ }
+ **v = -int(val)
+ return nil
+ case 'n':
+ dec.cursor++
+ err := dec.assertNull()
+ if err != nil {
+ return err
+ }
+ return nil
+ default:
+ dec.err = InvalidUnmarshalError(
+ fmt.Sprintf(
+ "Cannot unmarshall to int, wrong char '%s' found at pos %d",
+ string(dec.data[dec.cursor]),
+ dec.cursor,
+ ),
+ )
+ err := dec.skipData()
+ if err != nil {
+ return err
+ }
+ return nil
+ }
+ }
+ return dec.raiseInvalidJSONErr(dec.cursor)
+}
+
+// DecodeInt16 reads the next JSON-encoded value from the decoder's input (io.Reader) and stores it in the int16 pointed to by v.
+//
+// See the documentation for Unmarshal for details about the conversion of JSON into a Go value.
+func (dec *Decoder) DecodeInt16(v *int16) error {
+ if dec.isPooled == 1 {
+ panic(InvalidUsagePooledDecoderError("Invalid usage of pooled decoder"))
+ }
+ return dec.decodeInt16(v)
+}
+func (dec *Decoder) decodeInt16(v *int16) error {
+ for ; dec.cursor < dec.length || dec.read(); dec.cursor++ {
+ switch c := dec.data[dec.cursor]; c {
+ case ' ', '\n', '\t', '\r', ',':
+ continue
+ // we don't look for 0 as leading zeros are invalid per RFC
+ case '0', '1', '2', '3', '4', '5', '6', '7', '8', '9':
+ val, err := dec.getInt16()
+ if err != nil {
+ return err
+ }
+ *v = val
+ return nil
+ case '-':
+ dec.cursor = dec.cursor + 1
+ val, err := dec.getInt16Negative()
+ if err != nil {
+ return err
+ }
+ *v = -val
+ return nil
+ case 'n':
+ dec.cursor++
+ err := dec.assertNull()
+ if err != nil {
+ return err
+ }
+ return nil
+ default:
+ dec.err = dec.makeInvalidUnmarshalErr(v)
+ err := dec.skipData()
+ if err != nil {
+ return err
+ }
+ return nil
+ }
+ }
+ return dec.raiseInvalidJSONErr(dec.cursor)
+}
+func (dec *Decoder) decodeInt16Null(v **int16) error {
+ for ; dec.cursor < dec.length || dec.read(); dec.cursor++ {
+ switch c := dec.data[dec.cursor]; c {
+ case ' ', '\n', '\t', '\r', ',':
+ continue
+ // we don't look for 0 as leading zeros are invalid per RFC
+ case '0', '1', '2', '3', '4', '5', '6', '7', '8', '9':
+ val, err := dec.getInt16()
+ if err != nil {
+ return err
+ }
+ if *v == nil {
+ *v = new(int16)
+ }
+ **v = val
+ return nil
+ case '-':
+ dec.cursor = dec.cursor + 1
+ val, err := dec.getInt16Negative()
+ if err != nil {
+ return err
+ }
+ if *v == nil {
+ *v = new(int16)
+ }
+ **v = -val
+ return nil
+ case 'n':
+ dec.cursor++
+ err := dec.assertNull()
+ if err != nil {
+ return err
+ }
+ return nil
+ default:
+ dec.err = dec.makeInvalidUnmarshalErr(v)
+ err := dec.skipData()
+ if err != nil {
+ return err
+ }
+ return nil
+ }
+ }
+ return dec.raiseInvalidJSONErr(dec.cursor)
+}
+
+func (dec *Decoder) getInt16Negative() (int16, error) {
+ // look for following numbers
+ for ; dec.cursor < dec.length || dec.read(); dec.cursor++ {
+ switch dec.data[dec.cursor] {
+ case '1', '2', '3', '4', '5', '6', '7', '8', '9':
+ return dec.getInt16()
+ default:
+ return 0, dec.raiseInvalidJSONErr(dec.cursor)
+ }
+ }
+ return 0, dec.raiseInvalidJSONErr(dec.cursor)
+}
+
+func (dec *Decoder) getInt16() (int16, error) {
+ var end = dec.cursor
+ var start = dec.cursor
+ // look for following numbers
+ for j := dec.cursor + 1; j < dec.length || dec.read(); j++ {
+ switch dec.data[j] {
+ case '0', '1', '2', '3', '4', '5', '6', '7', '8', '9':
+ end = j
+ continue
+ case '.':
+ // if dot is found
+ // look for exponent (e,E) as exponent can change the
+ // way number should be parsed to int.
+ // if no exponent found, just unmarshal the number before decimal point
+ j++
+ startDecimal := j
+ endDecimal := j - 1
+ for ; j < dec.length || dec.read(); j++ {
+ switch dec.data[j] {
+ case '0', '1', '2', '3', '4', '5', '6', '7', '8', '9':
+ endDecimal = j
+ continue
+ case 'e', 'E':
+ if startDecimal > endDecimal {
+ return 0, dec.raiseInvalidJSONErr(dec.cursor)
+ }
+ dec.cursor = j + 1
+ // can try unmarshalling to int as Exponent might change decimal number to non decimal
+ // let's get the float value first
+ // we get part before decimal as integer
+ beforeDecimal := dec.atoi16(start, end)
+ // get number after the decimal point
+ // multiple the before decimal point portion by 10 using bitwise
+ for i := startDecimal; i <= endDecimal; i++ {
+ beforeDecimal = (beforeDecimal << 3) + (beforeDecimal << 1)
+ }
+ // then we add both integers
+ // then we divide the number by the power found
+ afterDecimal := dec.atoi16(startDecimal, endDecimal)
+ expI := endDecimal - startDecimal + 2
+ if expI >= len(pow10uint64) || expI < 0 {
+ return 0, dec.raiseInvalidJSONErr(dec.cursor)
+ }
+ pow := pow10uint64[expI]
+ floatVal := float64(beforeDecimal+afterDecimal) / float64(pow)
+ // we have the floating value, now multiply by the exponent
+ exp, err := dec.getExponent()
+ if err != nil {
+ return 0, err
+ }
+ pExp := (exp + (exp >> 31)) ^ (exp >> 31) + 1 // abs
+ if pExp >= int64(len(pow10uint64)) || pExp < 0 {
+ return 0, dec.raiseInvalidJSONErr(dec.cursor)
+ }
+ val := floatVal * float64(pow10uint64[pExp])
+ return int16(val), nil
+ case ' ', '\t', '\n', ',', ']', '}':
+ dec.cursor = j
+ return dec.atoi16(start, end), nil
+ default:
+ dec.cursor = j
+ return 0, dec.raiseInvalidJSONErr(dec.cursor)
+ }
+ }
+ return dec.atoi16(start, end), nil
+ case 'e', 'E':
+ // get init n
+ dec.cursor = j + 1
+ return dec.getInt16WithExp(dec.atoi16(start, end))
+ case ' ', '\n', '\t', '\r', ',', '}', ']':
+ dec.cursor = j
+ return dec.atoi16(start, end), nil
+ }
+ // invalid json we expect numbers, dot (single one), comma, or spaces
+ return 0, dec.raiseInvalidJSONErr(dec.cursor)
+ }
+ return dec.atoi16(start, end), nil
+}
+
+func (dec *Decoder) getInt16WithExp(init int16) (int16, error) {
+ var exp uint16
+ var sign = int16(1)
+ for ; dec.cursor < dec.length || dec.read(); dec.cursor++ {
+ switch dec.data[dec.cursor] {
+ case '+':
+ continue
+ case '-':
+ sign = -1
+ case '0', '1', '2', '3', '4', '5', '6', '7', '8', '9':
+ uintv := uint16(digits[dec.data[dec.cursor]])
+ exp = (exp << 3) + (exp << 1) + uintv
+ dec.cursor++
+ for ; dec.cursor < dec.length || dec.read(); dec.cursor++ {
+ switch dec.data[dec.cursor] {
+ case '0', '1', '2', '3', '4', '5', '6', '7', '8', '9':
+ uintv := uint16(digits[dec.data[dec.cursor]])
+ exp = (exp << 3) + (exp << 1) + uintv
+ case ' ', '\t', '\n', '}', ',', ']':
+ exp = exp + 1
+ if exp >= uint16(len(pow10uint64)) {
+ return 0, dec.raiseInvalidJSONErr(dec.cursor)
+ }
+ if sign == -1 {
+ return init * (1 / int16(pow10uint64[exp])), nil
+ }
+ return init * int16(pow10uint64[exp]), nil
+ default:
+ return 0, dec.raiseInvalidJSONErr(dec.cursor)
+ }
+ }
+ exp = exp + 1
+ if exp >= uint16(len(pow10uint64)) {
+ return 0, dec.raiseInvalidJSONErr(dec.cursor)
+ }
+ if sign == -1 {
+ return init * (1 / int16(pow10uint64[exp])), nil
+ }
+ return init * int16(pow10uint64[exp]), nil
+ default:
+ return 0, dec.raiseInvalidJSONErr(dec.cursor)
+ }
+ }
+ return 0, dec.raiseInvalidJSONErr(dec.cursor)
+}
+
+// DecodeInt8 reads the next JSON-encoded value from the decoder's input (io.Reader) and stores it in the int8 pointed to by v.
+//
+// See the documentation for Unmarshal for details about the conversion of JSON into a Go value.
+func (dec *Decoder) DecodeInt8(v *int8) error {
+ if dec.isPooled == 1 {
+ panic(InvalidUsagePooledDecoderError("Invalid usage of pooled decoder"))
+ }
+ return dec.decodeInt8(v)
+}
+func (dec *Decoder) decodeInt8(v *int8) error {
+ for ; dec.cursor < dec.length || dec.read(); dec.cursor++ {
+ switch c := dec.data[dec.cursor]; c {
+ case ' ', '\n', '\t', '\r', ',':
+ continue
+ // we don't look for 0 as leading zeros are invalid per RFC
+ case '0', '1', '2', '3', '4', '5', '6', '7', '8', '9':
+ val, err := dec.getInt8()
+ if err != nil {
+ return err
+ }
+ *v = val
+ return nil
+ case '-':
+ dec.cursor = dec.cursor + 1
+ val, err := dec.getInt8Negative()
+ if err != nil {
+ return err
+ }
+ *v = -val
+ return nil
+ case 'n':
+ dec.cursor++
+ err := dec.assertNull()
+ if err != nil {
+ return err
+ }
+ return nil
+ default:
+ dec.err = dec.makeInvalidUnmarshalErr(v)
+ err := dec.skipData()
+ if err != nil {
+ return err
+ }
+ return nil
+ }
+ }
+ return dec.raiseInvalidJSONErr(dec.cursor)
+}
+func (dec *Decoder) decodeInt8Null(v **int8) error {
+ for ; dec.cursor < dec.length || dec.read(); dec.cursor++ {
+ switch c := dec.data[dec.cursor]; c {
+ case ' ', '\n', '\t', '\r', ',':
+ continue
+ // we don't look for 0 as leading zeros are invalid per RFC
+ case '0', '1', '2', '3', '4', '5', '6', '7', '8', '9':
+ val, err := dec.getInt8()
+ if err != nil {
+ return err
+ }
+ if *v == nil {
+ *v = new(int8)
+ }
+ **v = val
+ return nil
+ case '-':
+ dec.cursor = dec.cursor + 1
+ val, err := dec.getInt8Negative()
+ if err != nil {
+ return err
+ }
+ if *v == nil {
+ *v = new(int8)
+ }
+ **v = -val
+ return nil
+ case 'n':
+ dec.cursor++
+ err := dec.assertNull()
+ if err != nil {
+ return err
+ }
+ return nil
+ default:
+ dec.err = dec.makeInvalidUnmarshalErr(v)
+ err := dec.skipData()
+ if err != nil {
+ return err
+ }
+ return nil
+ }
+ }
+ return dec.raiseInvalidJSONErr(dec.cursor)
+}
+
+func (dec *Decoder) getInt8Negative() (int8, error) {
+ // look for following numbers
+ for ; dec.cursor < dec.length || dec.read(); dec.cursor++ {
+ switch dec.data[dec.cursor] {
+ case '1', '2', '3', '4', '5', '6', '7', '8', '9':
+ return dec.getInt8()
+ default:
+ return 0, dec.raiseInvalidJSONErr(dec.cursor)
+ }
+ }
+ return 0, dec.raiseInvalidJSONErr(dec.cursor)
+}
+
+func (dec *Decoder) getInt8() (int8, error) {
+ var end = dec.cursor
+ var start = dec.cursor
+ // look for following numbers
+ for j := dec.cursor + 1; j < dec.length || dec.read(); j++ {
+ switch dec.data[j] {
+ case '0', '1', '2', '3', '4', '5', '6', '7', '8', '9':
+ end = j
+ continue
+ case '.':
+ // if dot is found
+ // look for exponent (e,E) as exponent can change the
+ // way number should be parsed to int.
+ // if no exponent found, just unmarshal the number before decimal point
+ j++
+ startDecimal := j
+ endDecimal := j - 1
+ for ; j < dec.length || dec.read(); j++ {
+ switch dec.data[j] {
+ case '0', '1', '2', '3', '4', '5', '6', '7', '8', '9':
+ endDecimal = j
+ continue
+ case 'e', 'E':
+ if startDecimal > endDecimal {
+ return 0, dec.raiseInvalidJSONErr(dec.cursor)
+ }
+ dec.cursor = j + 1
+ // can try unmarshalling to int as Exponent might change decimal number to non decimal
+ // let's get the float value first
+ // we get part before decimal as integer
+ beforeDecimal := dec.atoi8(start, end)
+ // get number after the decimal point
+ // multiple the before decimal point portion by 10 using bitwise
+ for i := startDecimal; i <= endDecimal; i++ {
+ beforeDecimal = (beforeDecimal << 3) + (beforeDecimal << 1)
+ }
+ // then we add both integers
+ // then we divide the number by the power found
+ afterDecimal := dec.atoi8(startDecimal, endDecimal)
+ expI := endDecimal - startDecimal + 2
+ if expI >= len(pow10uint64) || expI < 0 {
+ return 0, dec.raiseInvalidJSONErr(dec.cursor)
+ }
+ pow := pow10uint64[expI]
+ floatVal := float64(beforeDecimal+afterDecimal) / float64(pow)
+ // we have the floating value, now multiply by the exponent
+ exp, err := dec.getExponent()
+ if err != nil {
+ return 0, err
+ }
+ pExp := (exp + (exp >> 31)) ^ (exp >> 31) + 1 // abs
+ if pExp >= int64(len(pow10uint64)) || pExp < 0 {
+ return 0, dec.raiseInvalidJSONErr(dec.cursor)
+ }
+ val := floatVal * float64(pow10uint64[pExp])
+ return int8(val), nil
+ case ' ', '\t', '\n', ',', ']', '}':
+ dec.cursor = j
+ return dec.atoi8(start, end), nil
+ default:
+ dec.cursor = j
+ return 0, dec.raiseInvalidJSONErr(dec.cursor)
+ }
+ }
+ return dec.atoi8(start, end), nil
+ case 'e', 'E':
+ // get init n
+ dec.cursor = j + 1
+ return dec.getInt8WithExp(dec.atoi8(start, end))
+ case ' ', '\n', '\t', '\r', ',', '}', ']':
+ dec.cursor = j
+ return dec.atoi8(start, end), nil
+ }
+ // invalid json we expect numbers, dot (single one), comma, or spaces
+ return 0, dec.raiseInvalidJSONErr(dec.cursor)
+ }
+ return dec.atoi8(start, end), nil
+}
+
+func (dec *Decoder) getInt8WithExp(init int8) (int8, error) {
+ var exp uint8
+ var sign = int8(1)
+ for ; dec.cursor < dec.length || dec.read(); dec.cursor++ {
+ switch dec.data[dec.cursor] {
+ case '+':
+ continue
+ case '-':
+ sign = -1
+ case '0', '1', '2', '3', '4', '5', '6', '7', '8', '9':
+ uintv := uint8(digits[dec.data[dec.cursor]])
+ exp = (exp << 3) + (exp << 1) + uintv
+ dec.cursor++
+ for ; dec.cursor < dec.length || dec.read(); dec.cursor++ {
+ switch dec.data[dec.cursor] {
+ case '0', '1', '2', '3', '4', '5', '6', '7', '8', '9':
+ uintv := uint8(digits[dec.data[dec.cursor]])
+ exp = (exp << 3) + (exp << 1) + uintv
+ case ' ', '\t', '\n', '}', ',', ']':
+ if exp+1 >= uint8(len(pow10uint64)) {
+ return 0, dec.raiseInvalidJSONErr(dec.cursor)
+ }
+ if sign == -1 {
+ return init * (1 / int8(pow10uint64[exp+1])), nil
+ }
+ return init * int8(pow10uint64[exp+1]), nil
+ default:
+ return 0, dec.raiseInvalidJSONErr(dec.cursor)
+ }
+ }
+ if exp+1 >= uint8(len(pow10uint64)) {
+ return 0, dec.raiseInvalidJSONErr(dec.cursor)
+ }
+ if sign == -1 {
+ return init * (1 / int8(pow10uint64[exp+1])), nil
+ }
+ return init * int8(pow10uint64[exp+1]), nil
+ default:
+ dec.err = dec.raiseInvalidJSONErr(dec.cursor)
+ return 0, dec.err
+ }
+ }
+ return 0, dec.raiseInvalidJSONErr(dec.cursor)
+}
+
+// DecodeInt32 reads the next JSON-encoded value from the decoder's input (io.Reader) and stores it in the int32 pointed to by v.
+//
+// See the documentation for Unmarshal for details about the conversion of JSON into a Go value.
+func (dec *Decoder) DecodeInt32(v *int32) error {
+ if dec.isPooled == 1 {
+ panic(InvalidUsagePooledDecoderError("Invalid usage of pooled decoder"))
+ }
+ return dec.decodeInt32(v)
+}
+func (dec *Decoder) decodeInt32(v *int32) error {
+ for ; dec.cursor < dec.length || dec.read(); dec.cursor++ {
+ switch c := dec.data[dec.cursor]; c {
+ case ' ', '\n', '\t', '\r', ',':
+ continue
+ case '0', '1', '2', '3', '4', '5', '6', '7', '8', '9':
+ val, err := dec.getInt32()
+ if err != nil {
+ return err
+ }
+ *v = val
+ return nil
+ case '-':
+ dec.cursor = dec.cursor + 1
+ val, err := dec.getInt32Negative()
+ if err != nil {
+ return err
+ }
+ *v = -val
+ return nil
+ case 'n':
+ dec.cursor++
+ err := dec.assertNull()
+ if err != nil {
+ return err
+ }
+ return nil
+ default:
+ dec.err = dec.makeInvalidUnmarshalErr(v)
+ err := dec.skipData()
+ if err != nil {
+ return err
+ }
+ return nil
+ }
+ }
+ return dec.raiseInvalidJSONErr(dec.cursor)
+}
+func (dec *Decoder) decodeInt32Null(v **int32) error {
+ for ; dec.cursor < dec.length || dec.read(); dec.cursor++ {
+ switch c := dec.data[dec.cursor]; c {
+ case ' ', '\n', '\t', '\r', ',':
+ continue
+ case '0', '1', '2', '3', '4', '5', '6', '7', '8', '9':
+ val, err := dec.getInt32()
+ if err != nil {
+ return err
+ }
+ if *v == nil {
+ *v = new(int32)
+ }
+ **v = val
+ return nil
+ case '-':
+ dec.cursor = dec.cursor + 1
+ val, err := dec.getInt32Negative()
+ if err != nil {
+ return err
+ }
+ if *v == nil {
+ *v = new(int32)
+ }
+ **v = -val
+ return nil
+ case 'n':
+ dec.cursor++
+ err := dec.assertNull()
+ if err != nil {
+ return err
+ }
+ return nil
+ default:
+ dec.err = dec.makeInvalidUnmarshalErr(v)
+ err := dec.skipData()
+ if err != nil {
+ return err
+ }
+ return nil
+ }
+ }
+ return dec.raiseInvalidJSONErr(dec.cursor)
+}
+
+func (dec *Decoder) getInt32Negative() (int32, error) {
+ // look for following numbers
+ for ; dec.cursor < dec.length || dec.read(); dec.cursor++ {
+ switch dec.data[dec.cursor] {
+ case '1', '2', '3', '4', '5', '6', '7', '8', '9':
+ return dec.getInt32()
+ default:
+ return 0, dec.raiseInvalidJSONErr(dec.cursor)
+ }
+ }
+ return 0, dec.raiseInvalidJSONErr(dec.cursor)
+}
+
+func (dec *Decoder) getInt32() (int32, error) {
+ var end = dec.cursor
+ var start = dec.cursor
+ // look for following numbers
+ for j := dec.cursor + 1; j < dec.length || dec.read(); j++ {
+ switch dec.data[j] {
+ case '0', '1', '2', '3', '4', '5', '6', '7', '8', '9':
+ end = j
+ continue
+ case '.':
+ // if dot is found
+ // look for exponent (e,E) as exponent can change the
+ // way number should be parsed to int.
+ // if no exponent found, just unmarshal the number before decimal point
+ j++
+ startDecimal := j
+ endDecimal := j - 1
+ for ; j < dec.length || dec.read(); j++ {
+ switch dec.data[j] {
+ case '0', '1', '2', '3', '4', '5', '6', '7', '8', '9':
+ endDecimal = j
+ continue
+ case 'e', 'E':
+ // if eg 1.E
+ if startDecimal > endDecimal {
+ return 0, dec.raiseInvalidJSONErr(dec.cursor)
+ }
+ dec.cursor = j + 1
+ // can try unmarshalling to int as Exponent might change decimal number to non decimal
+ // let's get the float value first
+ // we get part before decimal as integer
+ beforeDecimal := dec.atoi64(start, end)
+ // get number after the decimal point
+ // multiple the before decimal point portion by 10 using bitwise
+ for i := startDecimal; i <= endDecimal; i++ {
+ beforeDecimal = (beforeDecimal << 3) + (beforeDecimal << 1)
+ }
+ // then we add both integers
+ // then we divide the number by the power found
+ afterDecimal := dec.atoi64(startDecimal, endDecimal)
+ expI := endDecimal - startDecimal + 2
+ if expI >= len(pow10uint64) || expI < 0 {
+ return 0, dec.raiseInvalidJSONErr(dec.cursor)
+ }
+ pow := pow10uint64[expI]
+ floatVal := float64(beforeDecimal+afterDecimal) / float64(pow)
+ // we have the floating value, now multiply by the exponent
+ exp, err := dec.getExponent()
+ if err != nil {
+ return 0, err
+ }
+ pExp := (exp + (exp >> 31)) ^ (exp >> 31) + 1 // abs
+ if pExp >= int64(len(pow10uint64)) || pExp < 0 {
+ return 0, dec.raiseInvalidJSONErr(dec.cursor)
+ }
+ val := floatVal * float64(pow10uint64[pExp])
+ return int32(val), nil
+ case ' ', '\t', '\n', ',', ']', '}':
+ dec.cursor = j
+ return dec.atoi32(start, end), nil
+ default:
+ dec.cursor = j
+ return 0, dec.raiseInvalidJSONErr(dec.cursor)
+ }
+ }
+ return dec.atoi32(start, end), nil
+ case 'e', 'E':
+ // get init n
+ dec.cursor = j + 1
+ return dec.getInt32WithExp(dec.atoi32(start, end))
+ case ' ', '\n', '\t', '\r', ',', '}', ']':
+ dec.cursor = j
+ return dec.atoi32(start, end), nil
+ }
+ // invalid json we expect numbers, dot (single one), comma, or spaces
+ return 0, dec.raiseInvalidJSONErr(dec.cursor)
+ }
+ return dec.atoi32(start, end), nil
+}
+
+func (dec *Decoder) getInt32WithExp(init int32) (int32, error) {
+ var exp uint32
+ var sign = int32(1)
+ for ; dec.cursor < dec.length || dec.read(); dec.cursor++ {
+ switch dec.data[dec.cursor] {
+ case '+':
+ continue
+ case '-':
+ sign = -1
+ case '0', '1', '2', '3', '4', '5', '6', '7', '8', '9':
+ uintv := uint32(digits[dec.data[dec.cursor]])
+ exp = (exp << 3) + (exp << 1) + uintv
+ dec.cursor++
+ for ; dec.cursor < dec.length || dec.read(); dec.cursor++ {
+ switch dec.data[dec.cursor] {
+ case '0', '1', '2', '3', '4', '5', '6', '7', '8', '9':
+ uintv := uint32(digits[dec.data[dec.cursor]])
+ exp = (exp << 3) + (exp << 1) + uintv
+ case ' ', '\t', '\n', '}', ',', ']':
+ if exp+1 >= uint32(len(pow10uint64)) {
+ return 0, dec.raiseInvalidJSONErr(dec.cursor)
+ }
+ if sign == -1 {
+ return init * (1 / int32(pow10uint64[exp+1])), nil
+ }
+ return init * int32(pow10uint64[exp+1]), nil
+ default:
+ return 0, dec.raiseInvalidJSONErr(dec.cursor)
+ }
+ }
+ if exp+1 >= uint32(len(pow10uint64)) {
+ return 0, dec.raiseInvalidJSONErr(dec.cursor)
+ }
+ if sign == -1 {
+ return init * (1 / int32(pow10uint64[exp+1])), nil
+ }
+ return init * int32(pow10uint64[exp+1]), nil
+ default:
+ dec.err = dec.raiseInvalidJSONErr(dec.cursor)
+ return 0, dec.err
+ }
+ }
+ return 0, dec.raiseInvalidJSONErr(dec.cursor)
+}
+
+// DecodeInt64 reads the next JSON-encoded value from the decoder's input (io.Reader) and stores it in the int64 pointed to by v.
+//
+// See the documentation for Unmarshal for details about the conversion of JSON into a Go value.
+func (dec *Decoder) DecodeInt64(v *int64) error {
+ if dec.isPooled == 1 {
+ panic(InvalidUsagePooledDecoderError("Invalid usage of pooled decoder"))
+ }
+ return dec.decodeInt64(v)
+}
+
+func (dec *Decoder) decodeInt64(v *int64) error {
+ for ; dec.cursor < dec.length || dec.read(); dec.cursor++ {
+ switch c := dec.data[dec.cursor]; c {
+ case ' ', '\n', '\t', '\r', ',':
+ continue
+ case '0', '1', '2', '3', '4', '5', '6', '7', '8', '9':
+ val, err := dec.getInt64()
+ if err != nil {
+ return err
+ }
+ *v = val
+ return nil
+ case '-':
+ dec.cursor = dec.cursor + 1
+ val, err := dec.getInt64Negative()
+ if err != nil {
+ return err
+ }
+ *v = -val
+ return nil
+ case 'n':
+ dec.cursor++
+ err := dec.assertNull()
+ if err != nil {
+ return err
+ }
+ return nil
+ default:
+ dec.err = dec.makeInvalidUnmarshalErr(v)
+ err := dec.skipData()
+ if err != nil {
+ return err
+ }
+ return nil
+ }
+ }
+ return dec.raiseInvalidJSONErr(dec.cursor)
+}
+func (dec *Decoder) decodeInt64Null(v **int64) error {
+ for ; dec.cursor < dec.length || dec.read(); dec.cursor++ {
+ switch c := dec.data[dec.cursor]; c {
+ case ' ', '\n', '\t', '\r', ',':
+ continue
+ case '0', '1', '2', '3', '4', '5', '6', '7', '8', '9':
+ val, err := dec.getInt64()
+ if err != nil {
+ return err
+ }
+ if *v == nil {
+ *v = new(int64)
+ }
+ **v = val
+ return nil
+ case '-':
+ dec.cursor = dec.cursor + 1
+ val, err := dec.getInt64Negative()
+ if err != nil {
+ return err
+ }
+ if *v == nil {
+ *v = new(int64)
+ }
+ **v = -val
+ return nil
+ case 'n':
+ dec.cursor++
+ err := dec.assertNull()
+ if err != nil {
+ return err
+ }
+ return nil
+ default:
+ dec.err = dec.makeInvalidUnmarshalErr(v)
+ err := dec.skipData()
+ if err != nil {
+ return err
+ }
+ return nil
+ }
+ }
+ return dec.raiseInvalidJSONErr(dec.cursor)
+}
+
+func (dec *Decoder) getInt64Negative() (int64, error) {
+ // look for following numbers
+ for ; dec.cursor < dec.length || dec.read(); dec.cursor++ {
+ switch dec.data[dec.cursor] {
+ case '1', '2', '3', '4', '5', '6', '7', '8', '9':
+ return dec.getInt64()
+ default:
+ return 0, dec.raiseInvalidJSONErr(dec.cursor)
+ }
+ }
+ return 0, dec.raiseInvalidJSONErr(dec.cursor)
+}
+
+func (dec *Decoder) getInt64() (int64, error) {
+ var end = dec.cursor
+ var start = dec.cursor
+ // look for following numbers
+ for j := dec.cursor + 1; j < dec.length || dec.read(); j++ {
+ switch dec.data[j] {
+ case '0', '1', '2', '3', '4', '5', '6', '7', '8', '9':
+ end = j
+ continue
+ case ' ', '\t', '\n', ',', '}', ']':
+ dec.cursor = j
+ return dec.atoi64(start, end), nil
+ case '.':
+ // if dot is found
+ // look for exponent (e,E) as exponent can change the
+ // way number should be parsed to int.
+ // if no exponent found, just unmarshal the number before decimal point
+ j++
+ startDecimal := j
+ endDecimal := j - 1
+ for ; j < dec.length || dec.read(); j++ {
+ switch dec.data[j] {
+ case '0', '1', '2', '3', '4', '5', '6', '7', '8', '9':
+ endDecimal = j
+ continue
+ case 'e', 'E':
+ // if eg 1.E
+ if startDecimal > endDecimal {
+ return 0, dec.raiseInvalidJSONErr(dec.cursor)
+ }
+ dec.cursor = j + 1
+ // can try unmarshalling to int as Exponent might change decimal number to non decimal
+ // let's get the float value first
+ // we get part before decimal as integer
+ beforeDecimal := dec.atoi64(start, end)
+ // get number after the decimal point
+ // multiple the before decimal point portion by 10 using bitwise
+ for i := startDecimal; i <= endDecimal; i++ {
+ beforeDecimal = (beforeDecimal << 3) + (beforeDecimal << 1)
+ }
+ // then we add both integers
+ // then we divide the number by the power found
+ afterDecimal := dec.atoi64(startDecimal, endDecimal)
+ expI := endDecimal - startDecimal + 2
+ if expI >= len(pow10uint64) || expI < 0 {
+ return 0, dec.raiseInvalidJSONErr(dec.cursor)
+ }
+ pow := pow10uint64[expI]
+ floatVal := float64(beforeDecimal+afterDecimal) / float64(pow)
+ // we have the floating value, now multiply by the exponent
+ exp, err := dec.getExponent()
+ if err != nil {
+ return 0, err
+ }
+ pExp := (exp + (exp >> 31)) ^ (exp >> 31) + 1 // abs
+ if pExp >= int64(len(pow10uint64)) || pExp < 0 {
+ return 0, dec.raiseInvalidJSONErr(dec.cursor)
+ }
+ val := floatVal * float64(pow10uint64[pExp])
+ return int64(val), nil
+ case ' ', '\t', '\n', ',', ']', '}':
+ dec.cursor = j
+ return dec.atoi64(start, end), nil
+ default:
+ dec.cursor = j
+ return 0, dec.raiseInvalidJSONErr(dec.cursor)
+ }
+ }
+ return dec.atoi64(start, end), nil
+ case 'e', 'E':
+ // get init n
+ dec.cursor = j + 1
+ return dec.getInt64WithExp(dec.atoi64(start, end))
+ }
+ // invalid json we expect numbers, dot (single one), comma, or spaces
+ return 0, dec.raiseInvalidJSONErr(dec.cursor)
+ }
+ return dec.atoi64(start, end), nil
+}
+
+func (dec *Decoder) getInt64WithExp(init int64) (int64, error) {
+ var exp uint64
+ var sign = int64(1)
+ for ; dec.cursor < dec.length || dec.read(); dec.cursor++ {
+ switch dec.data[dec.cursor] {
+ case '+':
+ continue
+ case '-':
+ sign = -1
+ case '0', '1', '2', '3', '4', '5', '6', '7', '8', '9':
+ uintv := uint64(digits[dec.data[dec.cursor]])
+ exp = (exp << 3) + (exp << 1) + uintv
+ dec.cursor++
+ for ; dec.cursor < dec.length || dec.read(); dec.cursor++ {
+ switch dec.data[dec.cursor] {
+ case '0', '1', '2', '3', '4', '5', '6', '7', '8', '9':
+ uintv := uint64(digits[dec.data[dec.cursor]])
+ exp = (exp << 3) + (exp << 1) + uintv
+ case ' ', '\t', '\n', '}', ',', ']':
+ if exp+1 >= uint64(len(pow10uint64)) {
+ return 0, dec.raiseInvalidJSONErr(dec.cursor)
+ }
+ if sign == -1 {
+ return init * (1 / int64(pow10uint64[exp+1])), nil
+ }
+ return init * int64(pow10uint64[exp+1]), nil
+ default:
+ return 0, dec.raiseInvalidJSONErr(dec.cursor)
+ }
+ }
+ if exp+1 >= uint64(len(pow10uint64)) {
+ return 0, dec.raiseInvalidJSONErr(dec.cursor)
+ }
+ if sign == -1 {
+ return init * (1 / int64(pow10uint64[exp+1])), nil
+ }
+ return init * int64(pow10uint64[exp+1]), nil
+ default:
+ return 0, dec.raiseInvalidJSONErr(dec.cursor)
+ }
+ }
+ return 0, dec.raiseInvalidJSONErr(dec.cursor)
+}
+
+func (dec *Decoder) atoi64(start, end int) int64 {
+ var ll = end + 1 - start
+ var val = int64(digits[dec.data[start]])
+ end = end + 1
+ if ll < maxInt64Length {
+ for i := start + 1; i < end; i++ {
+ intv := int64(digits[dec.data[i]])
+ val = (val << 3) + (val << 1) + intv
+ }
+ return val
+ } else if ll == maxInt64Length {
+ for i := start + 1; i < end; i++ {
+ intv := int64(digits[dec.data[i]])
+ if val > maxInt64toMultiply {
+ dec.err = dec.makeInvalidUnmarshalErr(val)
+ return 0
+ }
+ val = (val << 3) + (val << 1)
+ if math.MaxInt64-val < intv {
+ dec.err = dec.makeInvalidUnmarshalErr(val)
+ return 0
+ }
+ val += intv
+ }
+ } else {
+ dec.err = dec.makeInvalidUnmarshalErr(val)
+ return 0
+ }
+ return val
+}
+
+func (dec *Decoder) atoi32(start, end int) int32 {
+ var ll = end + 1 - start
+ var val = int32(digits[dec.data[start]])
+ end = end + 1
+
+ // overflowing
+ if ll < maxInt32Length {
+ for i := start + 1; i < end; i++ {
+ intv := int32(digits[dec.data[i]])
+ val = (val << 3) + (val << 1) + intv
+ }
+ } else if ll == maxInt32Length {
+ for i := start + 1; i < end; i++ {
+ intv := int32(digits[dec.data[i]])
+ if val > maxInt32toMultiply {
+ dec.err = dec.makeInvalidUnmarshalErr(val)
+ return 0
+ }
+ val = (val << 3) + (val << 1)
+ if math.MaxInt32-val < intv {
+ dec.err = dec.makeInvalidUnmarshalErr(val)
+ return 0
+ }
+ val += intv
+ }
+ } else {
+ dec.err = dec.makeInvalidUnmarshalErr(val)
+ return 0
+ }
+ return val
+}
+
+func (dec *Decoder) atoi16(start, end int) int16 {
+ var ll = end + 1 - start
+ var val = int16(digits[dec.data[start]])
+ end = end + 1
+ // overflowing
+ if ll < maxInt16Length {
+ for i := start + 1; i < end; i++ {
+ intv := int16(digits[dec.data[i]])
+ val = (val << 3) + (val << 1) + intv
+ }
+ } else if ll == maxInt16Length {
+ for i := start + 1; i < end; i++ {
+ intv := int16(digits[dec.data[i]])
+ if val > maxInt16toMultiply {
+ dec.err = dec.makeInvalidUnmarshalErr(val)
+ return 0
+ }
+ val = (val << 3) + (val << 1)
+ if math.MaxInt16-val < intv {
+ dec.err = dec.makeInvalidUnmarshalErr(val)
+ return 0
+ }
+ val += intv
+ }
+ } else {
+ dec.err = dec.makeInvalidUnmarshalErr(val)
+ return 0
+ }
+ return val
+}
+
+func (dec *Decoder) atoi8(start, end int) int8 {
+ var ll = end + 1 - start
+ var val = int8(digits[dec.data[start]])
+ end = end + 1
+ // overflowing
+ if ll < maxInt8Length {
+ for i := start + 1; i < end; i++ {
+ intv := int8(digits[dec.data[i]])
+ val = (val << 3) + (val << 1) + intv
+ }
+ } else if ll == maxInt8Length {
+ for i := start + 1; i < end; i++ {
+ intv := int8(digits[dec.data[i]])
+ if val > maxInt8toMultiply {
+ dec.err = dec.makeInvalidUnmarshalErr(val)
+ return 0
+ }
+ val = (val << 3) + (val << 1)
+ if math.MaxInt8-val < intv {
+ dec.err = dec.makeInvalidUnmarshalErr(val)
+ return 0
+ }
+ val += intv
+ }
+ } else {
+ dec.err = dec.makeInvalidUnmarshalErr(val)
+ return 0
+ }
+ return val
+}
+
+// Add Values functions
+
+// AddInt decodes the JSON value within an object or an array to an *int.
+// If next key value overflows int, an InvalidUnmarshalError error will be returned.
+func (dec *Decoder) AddInt(v *int) error {
+ return dec.Int(v)
+}
+
+// AddIntNull decodes the JSON value within an object or an array to an *int.
+// If next key value overflows int, an InvalidUnmarshalError error will be returned.
+// If a `null` is encountered, gojay does not change the value of the pointer.
+func (dec *Decoder) AddIntNull(v **int) error {
+ return dec.IntNull(v)
+}
+
+// AddInt8 decodes the JSON value within an object or an array to an *int.
+// If next key value overflows int8, an InvalidUnmarshalError error will be returned.
+func (dec *Decoder) AddInt8(v *int8) error {
+ return dec.Int8(v)
+}
+
+// AddInt8Null decodes the JSON value within an object or an array to an *int.
+// If next key value overflows int8, an InvalidUnmarshalError error will be returned.
+// If a `null` is encountered, gojay does not change the value of the pointer.
+func (dec *Decoder) AddInt8Null(v **int8) error {
+ return dec.Int8Null(v)
+}
+
+// AddInt16 decodes the JSON value within an object or an array to an *int.
+// If next key value overflows int16, an InvalidUnmarshalError error will be returned.
+func (dec *Decoder) AddInt16(v *int16) error {
+ return dec.Int16(v)
+}
+
+// AddInt16Null decodes the JSON value within an object or an array to an *int.
+// If next key value overflows int16, an InvalidUnmarshalError error will be returned.
+// If a `null` is encountered, gojay does not change the value of the pointer.
+func (dec *Decoder) AddInt16Null(v **int16) error {
+ return dec.Int16Null(v)
+}
+
+// AddInt32 decodes the JSON value within an object or an array to an *int.
+// If next key value overflows int32, an InvalidUnmarshalError error will be returned.
+func (dec *Decoder) AddInt32(v *int32) error {
+ return dec.Int32(v)
+}
+
+// AddInt32Null decodes the JSON value within an object or an array to an *int.
+// If next key value overflows int32, an InvalidUnmarshalError error will be returned.
+// If a `null` is encountered, gojay does not change the value of the pointer.
+func (dec *Decoder) AddInt32Null(v **int32) error {
+ return dec.Int32Null(v)
+}
+
+// AddInt64 decodes the JSON value within an object or an array to an *int.
+// If next key value overflows int64, an InvalidUnmarshalError error will be returned.
+func (dec *Decoder) AddInt64(v *int64) error {
+ return dec.Int64(v)
+}
+
+// AddInt64Null decodes the JSON value within an object or an array to an *int.
+// If next key value overflows int64, an InvalidUnmarshalError error will be returned.
+// If a `null` is encountered, gojay does not change the value of the pointer.
+func (dec *Decoder) AddInt64Null(v **int64) error {
+ return dec.Int64Null(v)
+}
+
+// Int decodes the JSON value within an object or an array to an *int.
+// If next key value overflows int, an InvalidUnmarshalError error will be returned.
+func (dec *Decoder) Int(v *int) error {
+ err := dec.decodeInt(v)
+ if err != nil {
+ return err
+ }
+ dec.called |= 1
+ return nil
+}
+
+// IntNull decodes the JSON value within an object or an array to an *int.
+// If next key value overflows int, an InvalidUnmarshalError error will be returned.
+func (dec *Decoder) IntNull(v **int) error {
+ err := dec.decodeIntNull(v)
+ if err != nil {
+ return err
+ }
+ dec.called |= 1
+ return nil
+}
+
+// Int8 decodes the JSON value within an object or an array to an *int.
+// If next key value overflows int8, an InvalidUnmarshalError error will be returned.
+func (dec *Decoder) Int8(v *int8) error {
+ err := dec.decodeInt8(v)
+ if err != nil {
+ return err
+ }
+ dec.called |= 1
+ return nil
+}
+
+// Int8Null decodes the JSON value within an object or an array to an *int.
+// If next key value overflows int8, an InvalidUnmarshalError error will be returned.
+func (dec *Decoder) Int8Null(v **int8) error {
+ err := dec.decodeInt8Null(v)
+ if err != nil {
+ return err
+ }
+ dec.called |= 1
+ return nil
+}
+
+// Int16 decodes the JSON value within an object or an array to an *int.
+// If next key value overflows int16, an InvalidUnmarshalError error will be returned.
+func (dec *Decoder) Int16(v *int16) error {
+ err := dec.decodeInt16(v)
+ if err != nil {
+ return err
+ }
+ dec.called |= 1
+ return nil
+}
+
+// Int16Null decodes the JSON value within an object or an array to an *int.
+// If next key value overflows int16, an InvalidUnmarshalError error will be returned.
+func (dec *Decoder) Int16Null(v **int16) error {
+ err := dec.decodeInt16Null(v)
+ if err != nil {
+ return err
+ }
+ dec.called |= 1
+ return nil
+}
+
+// Int32 decodes the JSON value within an object or an array to an *int.
+// If next key value overflows int32, an InvalidUnmarshalError error will be returned.
+func (dec *Decoder) Int32(v *int32) error {
+ err := dec.decodeInt32(v)
+ if err != nil {
+ return err
+ }
+ dec.called |= 1
+ return nil
+}
+
+// Int32Null decodes the JSON value within an object or an array to an *int.
+// If next key value overflows int32, an InvalidUnmarshalError error will be returned.
+func (dec *Decoder) Int32Null(v **int32) error {
+ err := dec.decodeInt32Null(v)
+ if err != nil {
+ return err
+ }
+ dec.called |= 1
+ return nil
+}
+
+// Int64 decodes the JSON value within an object or an array to an *int.
+// If next key value overflows int64, an InvalidUnmarshalError error will be returned.
+func (dec *Decoder) Int64(v *int64) error {
+ err := dec.decodeInt64(v)
+ if err != nil {
+ return err
+ }
+ dec.called |= 1
+ return nil
+}
+
+// Int64Null decodes the JSON value within an object or an array to an *int.
+// If next key value overflows int64, an InvalidUnmarshalError error will be returned.
+func (dec *Decoder) Int64Null(v **int64) error {
+ err := dec.decodeInt64Null(v)
+ if err != nil {
+ return err
+ }
+ dec.called |= 1
+ return nil
+}
diff --git a/vendor/github.com/francoispqt/gojay/decode_number_uint.go b/vendor/github.com/francoispqt/gojay/decode_number_uint.go
new file mode 100644
index 0000000000..b57ef7ab63
--- /dev/null
+++ b/vendor/github.com/francoispqt/gojay/decode_number_uint.go
@@ -0,0 +1,715 @@
+package gojay
+
+import (
+ "math"
+)
+
+// DecodeUint8 reads the next JSON-encoded value from the decoder's input (io.Reader) and stores it in the uint8 pointed to by v.
+//
+// See the documentation for Unmarshal for details about the conversion of JSON into a Go value.
+func (dec *Decoder) DecodeUint8(v *uint8) error {
+ if dec.isPooled == 1 {
+ panic(InvalidUsagePooledDecoderError("Invalid usage of pooled decoder"))
+ }
+ return dec.decodeUint8(v)
+}
+
+func (dec *Decoder) decodeUint8(v *uint8) error {
+ for ; dec.cursor < dec.length || dec.read(); dec.cursor++ {
+ switch c := dec.data[dec.cursor]; c {
+ case ' ', '\n', '\t', '\r', ',':
+ continue
+ case '0', '1', '2', '3', '4', '5', '6', '7', '8', '9':
+ val, err := dec.getUint8()
+ if err != nil {
+ return err
+ }
+ *v = val
+ return nil
+ case '-': // if negative, we just set it to 0 and set error
+ dec.err = dec.makeInvalidUnmarshalErr(v)
+ err := dec.skipData()
+ if err != nil {
+ return err
+ }
+ return nil
+ case 'n':
+ dec.cursor++
+ err := dec.assertNull()
+ if err != nil {
+ return err
+ }
+ return nil
+ default:
+ dec.err = dec.makeInvalidUnmarshalErr(v)
+ err := dec.skipData()
+ if err != nil {
+ return err
+ }
+ return nil
+ }
+ }
+ return dec.raiseInvalidJSONErr(dec.cursor)
+}
+func (dec *Decoder) decodeUint8Null(v **uint8) error {
+ for ; dec.cursor < dec.length || dec.read(); dec.cursor++ {
+ switch c := dec.data[dec.cursor]; c {
+ case ' ', '\n', '\t', '\r', ',':
+ continue
+ case '0', '1', '2', '3', '4', '5', '6', '7', '8', '9':
+ val, err := dec.getUint8()
+ if err != nil {
+ return err
+ }
+ if *v == nil {
+ *v = new(uint8)
+ }
+ **v = val
+ return nil
+ case '-': // if negative, we just set it to 0 and set error
+ dec.err = dec.makeInvalidUnmarshalErr(v)
+ err := dec.skipData()
+ if err != nil {
+ return err
+ }
+ if *v == nil {
+ *v = new(uint8)
+ }
+ return nil
+ case 'n':
+ dec.cursor++
+ err := dec.assertNull()
+ if err != nil {
+ return err
+ }
+ return nil
+ default:
+ dec.err = dec.makeInvalidUnmarshalErr(v)
+ err := dec.skipData()
+ if err != nil {
+ return err
+ }
+ return nil
+ }
+ }
+ return dec.raiseInvalidJSONErr(dec.cursor)
+}
+
+func (dec *Decoder) getUint8() (uint8, error) {
+ var end = dec.cursor
+ var start = dec.cursor
+ // look for following numbers
+ for j := dec.cursor + 1; j < dec.length || dec.read(); j++ {
+ switch dec.data[j] {
+ case '0', '1', '2', '3', '4', '5', '6', '7', '8', '9':
+ end = j
+ continue
+ case ' ', '\n', '\t', '\r':
+ continue
+ case '.', ',', '}', ']':
+ dec.cursor = j
+ return dec.atoui8(start, end), nil
+ }
+ // invalid json we expect numbers, dot (single one), comma, or spaces
+ return 0, dec.raiseInvalidJSONErr(dec.cursor)
+ }
+ return dec.atoui8(start, end), nil
+}
+
+// DecodeUint16 reads the next JSON-encoded value from the decoder's input (io.Reader) and stores it in the uint16 pointed to by v.
+//
+// See the documentation for Unmarshal for details about the conversion of JSON into a Go value.
+func (dec *Decoder) DecodeUint16(v *uint16) error {
+ if dec.isPooled == 1 {
+ panic(InvalidUsagePooledDecoderError("Invalid usage of pooled decoder"))
+ }
+ return dec.decodeUint16(v)
+}
+
+func (dec *Decoder) decodeUint16(v *uint16) error {
+ for ; dec.cursor < dec.length || dec.read(); dec.cursor++ {
+ switch c := dec.data[dec.cursor]; c {
+ case ' ', '\n', '\t', '\r', ',':
+ continue
+ case '0', '1', '2', '3', '4', '5', '6', '7', '8', '9':
+ val, err := dec.getUint16()
+ if err != nil {
+ return err
+ }
+ *v = val
+ return nil
+ case '-':
+ dec.err = dec.makeInvalidUnmarshalErr(v)
+ err := dec.skipData()
+ if err != nil {
+ return err
+ }
+ return nil
+ case 'n':
+ dec.cursor++
+ err := dec.assertNull()
+ if err != nil {
+ return err
+ }
+ return nil
+ default:
+ dec.err = dec.makeInvalidUnmarshalErr(v)
+ err := dec.skipData()
+ if err != nil {
+ return err
+ }
+ return nil
+ }
+ }
+ return dec.raiseInvalidJSONErr(dec.cursor)
+}
+func (dec *Decoder) decodeUint16Null(v **uint16) error {
+ for ; dec.cursor < dec.length || dec.read(); dec.cursor++ {
+ switch c := dec.data[dec.cursor]; c {
+ case ' ', '\n', '\t', '\r', ',':
+ continue
+ case '0', '1', '2', '3', '4', '5', '6', '7', '8', '9':
+ val, err := dec.getUint16()
+ if err != nil {
+ return err
+ }
+ if *v == nil {
+ *v = new(uint16)
+ }
+ **v = val
+ return nil
+ case '-':
+ dec.err = dec.makeInvalidUnmarshalErr(v)
+ err := dec.skipData()
+ if err != nil {
+ return err
+ }
+ if *v == nil {
+ *v = new(uint16)
+ }
+ return nil
+ case 'n':
+ dec.cursor++
+ err := dec.assertNull()
+ if err != nil {
+ return err
+ }
+ return nil
+ default:
+ dec.err = dec.makeInvalidUnmarshalErr(v)
+ err := dec.skipData()
+ if err != nil {
+ return err
+ }
+ return nil
+ }
+ }
+ return dec.raiseInvalidJSONErr(dec.cursor)
+}
+
+func (dec *Decoder) getUint16() (uint16, error) {
+ var end = dec.cursor
+ var start = dec.cursor
+ // look for following numbers
+ for j := dec.cursor + 1; j < dec.length || dec.read(); j++ {
+ switch dec.data[j] {
+ case '0', '1', '2', '3', '4', '5', '6', '7', '8', '9':
+ end = j
+ continue
+ case ' ', '\n', '\t', '\r':
+ continue
+ case '.', ',', '}', ']':
+ dec.cursor = j
+ return dec.atoui16(start, end), nil
+ }
+ // invalid json we expect numbers, dot (single one), comma, or spaces
+ return 0, dec.raiseInvalidJSONErr(dec.cursor)
+ }
+ return dec.atoui16(start, end), nil
+}
+
+// DecodeUint32 reads the next JSON-encoded value from the decoder's input (io.Reader) and stores it in the uint32 pointed to by v.
+//
+// See the documentation for Unmarshal for details about the conversion of JSON into a Go value.
+func (dec *Decoder) DecodeUint32(v *uint32) error {
+ if dec.isPooled == 1 {
+ panic(InvalidUsagePooledDecoderError("Invalid usage of pooled decoder"))
+ }
+ return dec.decodeUint32(v)
+}
+
+func (dec *Decoder) decodeUint32(v *uint32) error {
+ for ; dec.cursor < dec.length || dec.read(); dec.cursor++ {
+ switch c := dec.data[dec.cursor]; c {
+ case ' ', '\n', '\t', '\r', ',':
+ continue
+ case '0', '1', '2', '3', '4', '5', '6', '7', '8', '9':
+ val, err := dec.getUint32()
+ if err != nil {
+ return err
+ }
+ *v = val
+ return nil
+ case '-':
+ dec.err = dec.makeInvalidUnmarshalErr(v)
+ err := dec.skipData()
+ if err != nil {
+ return err
+ }
+ return nil
+ case 'n':
+ dec.cursor++
+ err := dec.assertNull()
+ if err != nil {
+ return err
+ }
+ return nil
+ default:
+ dec.err = dec.makeInvalidUnmarshalErr(v)
+ err := dec.skipData()
+ if err != nil {
+ return err
+ }
+ return nil
+ }
+ }
+ return dec.raiseInvalidJSONErr(dec.cursor)
+}
+func (dec *Decoder) decodeUint32Null(v **uint32) error {
+ for ; dec.cursor < dec.length || dec.read(); dec.cursor++ {
+ switch c := dec.data[dec.cursor]; c {
+ case ' ', '\n', '\t', '\r', ',':
+ continue
+ case '0', '1', '2', '3', '4', '5', '6', '7', '8', '9':
+ val, err := dec.getUint32()
+ if err != nil {
+ return err
+ }
+ if *v == nil {
+ *v = new(uint32)
+ }
+ **v = val
+ return nil
+ case '-':
+ dec.err = dec.makeInvalidUnmarshalErr(v)
+ err := dec.skipData()
+ if err != nil {
+ return err
+ }
+ if *v == nil {
+ *v = new(uint32)
+ }
+ return nil
+ case 'n':
+ dec.cursor++
+ err := dec.assertNull()
+ if err != nil {
+ return err
+ }
+ return nil
+ default:
+ dec.err = dec.makeInvalidUnmarshalErr(v)
+ err := dec.skipData()
+ if err != nil {
+ return err
+ }
+ return nil
+ }
+ }
+ return dec.raiseInvalidJSONErr(dec.cursor)
+}
+
+func (dec *Decoder) getUint32() (uint32, error) {
+ var end = dec.cursor
+ var start = dec.cursor
+ // look for following numbers
+ for j := dec.cursor + 1; j < dec.length || dec.read(); j++ {
+ switch dec.data[j] {
+ case '0', '1', '2', '3', '4', '5', '6', '7', '8', '9':
+ end = j
+ continue
+ case ' ', '\n', '\t', '\r':
+ continue
+ case '.', ',', '}', ']':
+ dec.cursor = j
+ return dec.atoui32(start, end), nil
+ }
+ // invalid json we expect numbers, dot (single one), comma, or spaces
+ return 0, dec.raiseInvalidJSONErr(dec.cursor)
+ }
+ return dec.atoui32(start, end), nil
+}
+
+// DecodeUint64 reads the next JSON-encoded value from the decoder's input (io.Reader) and stores it in the uint64 pointed to by v.
+//
+// See the documentation for Unmarshal for details about the conversion of JSON into a Go value.
+func (dec *Decoder) DecodeUint64(v *uint64) error {
+ if dec.isPooled == 1 {
+ panic(InvalidUsagePooledDecoderError("Invalid usage of pooled decoder"))
+ }
+ return dec.decodeUint64(v)
+}
+func (dec *Decoder) decodeUint64(v *uint64) error {
+ for ; dec.cursor < dec.length || dec.read(); dec.cursor++ {
+ switch c := dec.data[dec.cursor]; c {
+ case ' ', '\n', '\t', '\r', ',':
+ continue
+ case '0', '1', '2', '3', '4', '5', '6', '7', '8', '9':
+ val, err := dec.getUint64()
+ if err != nil {
+ return err
+ }
+ *v = val
+ return nil
+ case '-':
+ dec.err = dec.makeInvalidUnmarshalErr(v)
+ err := dec.skipData()
+ if err != nil {
+ return err
+ }
+ return nil
+ case 'n':
+ dec.cursor++
+ err := dec.assertNull()
+ if err != nil {
+ return err
+ }
+ return nil
+ default:
+ dec.err = dec.makeInvalidUnmarshalErr(v)
+ err := dec.skipData()
+ if err != nil {
+ return err
+ }
+ return nil
+ }
+ }
+ return dec.raiseInvalidJSONErr(dec.cursor)
+}
+func (dec *Decoder) decodeUint64Null(v **uint64) error {
+ for ; dec.cursor < dec.length || dec.read(); dec.cursor++ {
+ switch c := dec.data[dec.cursor]; c {
+ case ' ', '\n', '\t', '\r', ',':
+ continue
+ case '0', '1', '2', '3', '4', '5', '6', '7', '8', '9':
+ val, err := dec.getUint64()
+ if err != nil {
+ return err
+ }
+ if *v == nil {
+ *v = new(uint64)
+ }
+ **v = val
+ return nil
+ case '-':
+ dec.err = dec.makeInvalidUnmarshalErr(v)
+ err := dec.skipData()
+ if err != nil {
+ return err
+ }
+ if *v == nil {
+ *v = new(uint64)
+ }
+ return nil
+ case 'n':
+ dec.cursor++
+ err := dec.assertNull()
+ if err != nil {
+ return err
+ }
+ return nil
+ default:
+ dec.err = dec.makeInvalidUnmarshalErr(v)
+ err := dec.skipData()
+ if err != nil {
+ return err
+ }
+ return nil
+ }
+ }
+ return dec.raiseInvalidJSONErr(dec.cursor)
+}
+
+func (dec *Decoder) getUint64() (uint64, error) {
+ var end = dec.cursor
+ var start = dec.cursor
+ // look for following numbers
+ for j := dec.cursor + 1; j < dec.length || dec.read(); j++ {
+ switch dec.data[j] {
+ case '0', '1', '2', '3', '4', '5', '6', '7', '8', '9':
+ end = j
+ continue
+ case ' ', '\n', '\t', '\r', '.', ',', '}', ']':
+ dec.cursor = j
+ return dec.atoui64(start, end), nil
+ }
+ // invalid json we expect numbers, dot (single one), comma, or spaces
+ return 0, dec.raiseInvalidJSONErr(dec.cursor)
+ }
+ return dec.atoui64(start, end), nil
+}
+
+func (dec *Decoder) atoui64(start, end int) uint64 {
+ var ll = end + 1 - start
+ var val = uint64(digits[dec.data[start]])
+ end = end + 1
+ if ll < maxUint64Length {
+ for i := start + 1; i < end; i++ {
+ uintv := uint64(digits[dec.data[i]])
+ val = (val << 3) + (val << 1) + uintv
+ }
+ } else if ll == maxUint64Length {
+ for i := start + 1; i < end; i++ {
+ uintv := uint64(digits[dec.data[i]])
+ if val > maxUint64toMultiply {
+ dec.err = dec.makeInvalidUnmarshalErr(val)
+ return 0
+ }
+ val = (val << 3) + (val << 1)
+ if math.MaxUint64-val < uintv {
+ dec.err = dec.makeInvalidUnmarshalErr(val)
+ return 0
+ }
+ val += uintv
+ }
+ } else {
+ dec.err = dec.makeInvalidUnmarshalErr(val)
+ return 0
+ }
+ return val
+}
+
+func (dec *Decoder) atoui32(start, end int) uint32 {
+ var ll = end + 1 - start
+ var val uint32
+ val = uint32(digits[dec.data[start]])
+ end = end + 1
+ if ll < maxUint32Length {
+ for i := start + 1; i < end; i++ {
+ uintv := uint32(digits[dec.data[i]])
+ val = (val << 3) + (val << 1) + uintv
+ }
+ } else if ll == maxUint32Length {
+ for i := start + 1; i < end; i++ {
+ uintv := uint32(digits[dec.data[i]])
+ if val > maxUint32toMultiply {
+ dec.err = dec.makeInvalidUnmarshalErr(val)
+ return 0
+ }
+ val = (val << 3) + (val << 1)
+ if math.MaxUint32-val < uintv {
+ dec.err = dec.makeInvalidUnmarshalErr(val)
+ return 0
+ }
+ val += uintv
+ }
+ } else if ll > maxUint32Length {
+ dec.err = dec.makeInvalidUnmarshalErr(val)
+ val = 0
+ }
+ return val
+}
+
+func (dec *Decoder) atoui16(start, end int) uint16 {
+ var ll = end + 1 - start
+ var val uint16
+ val = uint16(digits[dec.data[start]])
+ end = end + 1
+ if ll < maxUint16Length {
+ for i := start + 1; i < end; i++ {
+ uintv := uint16(digits[dec.data[i]])
+ val = (val << 3) + (val << 1) + uintv
+ }
+ } else if ll == maxUint16Length {
+ for i := start + 1; i < end; i++ {
+ uintv := uint16(digits[dec.data[i]])
+ if val > maxUint16toMultiply {
+ dec.err = dec.makeInvalidUnmarshalErr(val)
+ return 0
+ }
+ val = (val << 3) + (val << 1)
+ if math.MaxUint16-val < uintv {
+ dec.err = dec.makeInvalidUnmarshalErr(val)
+ return 0
+ }
+ val += uintv
+ }
+ } else if ll > maxUint16Length {
+ dec.err = dec.makeInvalidUnmarshalErr(val)
+ val = 0
+ }
+ return val
+}
+
+func (dec *Decoder) atoui8(start, end int) uint8 {
+ var ll = end + 1 - start
+ var val uint8
+ val = uint8(digits[dec.data[start]])
+ end = end + 1
+ if ll < maxUint8Length {
+ for i := start + 1; i < end; i++ {
+ uintv := uint8(digits[dec.data[i]])
+ val = (val << 3) + (val << 1) + uintv
+ }
+ } else if ll == maxUint8Length {
+ for i := start + 1; i < end; i++ {
+ uintv := uint8(digits[dec.data[i]])
+ if val > maxUint8toMultiply {
+ dec.err = dec.makeInvalidUnmarshalErr(val)
+ return 0
+ }
+ val = (val << 3) + (val << 1)
+ if math.MaxUint8-val < uintv {
+ dec.err = dec.makeInvalidUnmarshalErr(val)
+ return 0
+ }
+ val += uintv
+ }
+ } else if ll > maxUint8Length {
+ dec.err = dec.makeInvalidUnmarshalErr(val)
+ val = 0
+ }
+ return val
+}
+
+// Add Values functions
+
+// AddUint8 decodes the JSON value within an object or an array to an *int.
+// If next key value overflows uint8, an InvalidUnmarshalError error will be returned.
+func (dec *Decoder) AddUint8(v *uint8) error {
+ return dec.Uint8(v)
+}
+
+// AddUint8Null decodes the JSON value within an object or an array to an *int.
+// If next key value overflows uint8, an InvalidUnmarshalError error will be returned.
+// If a `null` is encountered, gojay does not change the value of the pointer.
+func (dec *Decoder) AddUint8Null(v **uint8) error {
+ return dec.Uint8Null(v)
+}
+
+// AddUint16 decodes the JSON value within an object or an array to an *int.
+// If next key value overflows uint16, an InvalidUnmarshalError error will be returned.
+func (dec *Decoder) AddUint16(v *uint16) error {
+ return dec.Uint16(v)
+}
+
+// AddUint16Null decodes the JSON value within an object or an array to an *int.
+// If next key value overflows uint16, an InvalidUnmarshalError error will be returned.
+// If a `null` is encountered, gojay does not change the value of the pointer.
+func (dec *Decoder) AddUint16Null(v **uint16) error {
+ return dec.Uint16Null(v)
+}
+
+// AddUint32 decodes the JSON value within an object or an array to an *int.
+// If next key value overflows uint32, an InvalidUnmarshalError error will be returned.
+func (dec *Decoder) AddUint32(v *uint32) error {
+ return dec.Uint32(v)
+}
+
+// AddUint32Null decodes the JSON value within an object or an array to an *int.
+// If next key value overflows uint32, an InvalidUnmarshalError error will be returned.
+// If a `null` is encountered, gojay does not change the value of the pointer.
+func (dec *Decoder) AddUint32Null(v **uint32) error {
+ return dec.Uint32Null(v)
+}
+
+// AddUint64 decodes the JSON value within an object or an array to an *int.
+// If next key value overflows uint64, an InvalidUnmarshalError error will be returned.
+func (dec *Decoder) AddUint64(v *uint64) error {
+ return dec.Uint64(v)
+}
+
+// AddUint64Null decodes the JSON value within an object or an array to an *int.
+// If next key value overflows uint64, an InvalidUnmarshalError error will be returned.
+// If a `null` is encountered, gojay does not change the value of the pointer.
+func (dec *Decoder) AddUint64Null(v **uint64) error {
+ return dec.Uint64Null(v)
+}
+
+// Uint8 decodes the JSON value within an object or an array to an *int.
+// If next key value overflows uint8, an InvalidUnmarshalError error will be returned.
+func (dec *Decoder) Uint8(v *uint8) error {
+ err := dec.decodeUint8(v)
+ if err != nil {
+ return err
+ }
+ dec.called |= 1
+ return nil
+}
+
+// Uint8Null decodes the JSON value within an object or an array to an *int.
+// If next key value overflows uint8, an InvalidUnmarshalError error will be returned.
+func (dec *Decoder) Uint8Null(v **uint8) error {
+ err := dec.decodeUint8Null(v)
+ if err != nil {
+ return err
+ }
+ dec.called |= 1
+ return nil
+}
+
+// Uint16 decodes the JSON value within an object or an array to an *int.
+// If next key value overflows uint16, an InvalidUnmarshalError error will be returned.
+func (dec *Decoder) Uint16(v *uint16) error {
+ err := dec.decodeUint16(v)
+ if err != nil {
+ return err
+ }
+ dec.called |= 1
+ return nil
+}
+
+// Uint16Null decodes the JSON value within an object or an array to an *int.
+// If next key value overflows uint16, an InvalidUnmarshalError error will be returned.
+func (dec *Decoder) Uint16Null(v **uint16) error {
+ err := dec.decodeUint16Null(v)
+ if err != nil {
+ return err
+ }
+ dec.called |= 1
+ return nil
+}
+
+// Uint32 decodes the JSON value within an object or an array to an *int.
+// If next key value overflows uint32, an InvalidUnmarshalError error will be returned.
+func (dec *Decoder) Uint32(v *uint32) error {
+ err := dec.decodeUint32(v)
+ if err != nil {
+ return err
+ }
+ dec.called |= 1
+ return nil
+}
+
+// Uint32Null decodes the JSON value within an object or an array to an *int.
+// If next key value overflows uint32, an InvalidUnmarshalError error will be returned.
+func (dec *Decoder) Uint32Null(v **uint32) error {
+ err := dec.decodeUint32Null(v)
+ if err != nil {
+ return err
+ }
+ dec.called |= 1
+ return nil
+}
+
+// Uint64 decodes the JSON value within an object or an array to an *int.
+// If next key value overflows uint64, an InvalidUnmarshalError error will be returned.
+func (dec *Decoder) Uint64(v *uint64) error {
+ err := dec.decodeUint64(v)
+ if err != nil {
+ return err
+ }
+ dec.called |= 1
+ return nil
+}
+
+// Uint64Null decodes the JSON value within an object or an array to an *int.
+// If next key value overflows uint64, an InvalidUnmarshalError error will be returned.
+func (dec *Decoder) Uint64Null(v **uint64) error {
+ err := dec.decodeUint64Null(v)
+ if err != nil {
+ return err
+ }
+ dec.called |= 1
+ return nil
+}
diff --git a/vendor/github.com/francoispqt/gojay/decode_object.go b/vendor/github.com/francoispqt/gojay/decode_object.go
new file mode 100644
index 0000000000..0fec9d24ed
--- /dev/null
+++ b/vendor/github.com/francoispqt/gojay/decode_object.go
@@ -0,0 +1,407 @@
+package gojay
+
+import (
+ "reflect"
+ "unsafe"
+)
+
+// DecodeObject reads the next JSON-encoded value from the decoder's input (io.Reader) and stores it in the value pointed to by v.
+//
+// v must implement UnmarshalerJSONObject.
+//
+// See the documentation for Unmarshal for details about the conversion of JSON into a Go value.
+func (dec *Decoder) DecodeObject(j UnmarshalerJSONObject) error {
+ if dec.isPooled == 1 {
+ panic(InvalidUsagePooledDecoderError("Invalid usage of pooled decoder"))
+ }
+ _, err := dec.decodeObject(j)
+ return err
+}
+func (dec *Decoder) decodeObject(j UnmarshalerJSONObject) (int, error) {
+ keys := j.NKeys()
+ for ; dec.cursor < dec.length || dec.read(); dec.cursor++ {
+ switch dec.data[dec.cursor] {
+ case ' ', '\n', '\t', '\r', ',':
+ case '{':
+ dec.cursor = dec.cursor + 1
+ // if keys is zero we will parse all keys
+ // we run two loops for micro optimization
+ if keys == 0 {
+ for dec.cursor < dec.length || dec.read() {
+ k, done, err := dec.nextKey()
+ if err != nil {
+ return 0, err
+ } else if done {
+ return dec.cursor, nil
+ }
+ err = j.UnmarshalJSONObject(dec, k)
+ if err != nil {
+ dec.err = err
+ return 0, err
+ } else if dec.called&1 == 0 {
+ err := dec.skipData()
+ if err != nil {
+ return 0, err
+ }
+ } else {
+ dec.keysDone++
+ }
+ dec.called &= 0
+ }
+ } else {
+ for (dec.cursor < dec.length || dec.read()) && dec.keysDone < keys {
+ k, done, err := dec.nextKey()
+ if err != nil {
+ return 0, err
+ } else if done {
+ return dec.cursor, nil
+ }
+ err = j.UnmarshalJSONObject(dec, k)
+ if err != nil {
+ dec.err = err
+ return 0, err
+ } else if dec.called&1 == 0 {
+ err := dec.skipData()
+ if err != nil {
+ return 0, err
+ }
+ } else {
+ dec.keysDone++
+ }
+ dec.called &= 0
+ }
+ }
+ // will get to that point when keysDone is not lower than keys anymore
+ // in that case, we make sure cursor goes to the end of object, but we skip
+ // unmarshalling
+ if dec.child&1 != 0 {
+ end, err := dec.skipObject()
+ dec.cursor = end
+ return dec.cursor, err
+ }
+ return dec.cursor, nil
+ case 'n':
+ dec.cursor++
+ err := dec.assertNull()
+ if err != nil {
+ return 0, err
+ }
+ return dec.cursor, nil
+ default:
+ // can't unmarshal to struct
+ dec.err = dec.makeInvalidUnmarshalErr(j)
+ err := dec.skipData()
+ if err != nil {
+ return 0, err
+ }
+ return dec.cursor, nil
+ }
+ }
+ return 0, dec.raiseInvalidJSONErr(dec.cursor)
+}
+
+func (dec *Decoder) decodeObjectNull(v interface{}) (int, error) {
+ // make sure the value is a pointer
+ vv := reflect.ValueOf(v)
+ vvt := vv.Type()
+ if vvt.Kind() != reflect.Ptr || vvt.Elem().Kind() != reflect.Ptr {
+ dec.err = ErrUnmarshalPtrExpected
+ return 0, dec.err
+ }
+ for ; dec.cursor < dec.length || dec.read(); dec.cursor++ {
+ switch dec.data[dec.cursor] {
+ case ' ', '\n', '\t', '\r', ',':
+ case '{':
+ elt := vv.Elem()
+ n := reflect.New(elt.Type().Elem())
+ elt.Set(n)
+ var j UnmarshalerJSONObject
+ var ok bool
+ if j, ok = n.Interface().(UnmarshalerJSONObject); !ok {
+ dec.err = dec.makeInvalidUnmarshalErr((UnmarshalerJSONObject)(nil))
+ return 0, dec.err
+ }
+ keys := j.NKeys()
+ dec.cursor = dec.cursor + 1
+ // if keys is zero we will parse all keys
+ // we run two loops for micro optimization
+ if keys == 0 {
+ for dec.cursor < dec.length || dec.read() {
+ k, done, err := dec.nextKey()
+ if err != nil {
+ return 0, err
+ } else if done {
+ return dec.cursor, nil
+ }
+ err = j.UnmarshalJSONObject(dec, k)
+ if err != nil {
+ dec.err = err
+ return 0, err
+ } else if dec.called&1 == 0 {
+ err := dec.skipData()
+ if err != nil {
+ return 0, err
+ }
+ } else {
+ dec.keysDone++
+ }
+ dec.called &= 0
+ }
+ } else {
+ for (dec.cursor < dec.length || dec.read()) && dec.keysDone < keys {
+ k, done, err := dec.nextKey()
+ if err != nil {
+ return 0, err
+ } else if done {
+ return dec.cursor, nil
+ }
+ err = j.UnmarshalJSONObject(dec, k)
+ if err != nil {
+ dec.err = err
+ return 0, err
+ } else if dec.called&1 == 0 {
+ err := dec.skipData()
+ if err != nil {
+ return 0, err
+ }
+ } else {
+ dec.keysDone++
+ }
+ dec.called &= 0
+ }
+ }
+ // will get to that point when keysDone is not lower than keys anymore
+ // in that case, we make sure cursor goes to the end of object, but we skip
+ // unmarshalling
+ if dec.child&1 != 0 {
+ end, err := dec.skipObject()
+ dec.cursor = end
+ return dec.cursor, err
+ }
+ return dec.cursor, nil
+ case 'n':
+ dec.cursor++
+ err := dec.assertNull()
+ if err != nil {
+ return 0, err
+ }
+ return dec.cursor, nil
+ default:
+ // can't unmarshal to struct
+ dec.err = dec.makeInvalidUnmarshalErr((UnmarshalerJSONObject)(nil))
+ err := dec.skipData()
+ if err != nil {
+ return 0, err
+ }
+ return dec.cursor, nil
+ }
+ }
+ return 0, dec.raiseInvalidJSONErr(dec.cursor)
+}
+
+func (dec *Decoder) skipObject() (int, error) {
+ var objectsOpen = 1
+ var objectsClosed = 0
+ for j := dec.cursor; j < dec.length || dec.read(); j++ {
+ switch dec.data[j] {
+ case '}':
+ objectsClosed++
+ // everything is closed return
+ if objectsOpen == objectsClosed {
+ // add char to object data
+ return j + 1, nil
+ }
+ case '{':
+ objectsOpen++
+ case '"':
+ j++
+ var isInEscapeSeq bool
+ var isFirstQuote = true
+ for ; j < dec.length || dec.read(); j++ {
+ if dec.data[j] != '"' {
+ continue
+ }
+ if dec.data[j-1] != '\\' || (!isInEscapeSeq && !isFirstQuote) {
+ break
+ } else {
+ isInEscapeSeq = false
+ }
+ if isFirstQuote {
+ isFirstQuote = false
+ }
+ // loop backward and count how many anti slash found
+ // to see if string is effectively escaped
+ ct := 0
+ for i := j - 1; i > 0; i-- {
+ if dec.data[i] != '\\' {
+ break
+ }
+ ct++
+ }
+ // is pair number of slashes, quote is not escaped
+ if ct&1 == 0 {
+ break
+ }
+ isInEscapeSeq = true
+ }
+ default:
+ continue
+ }
+ }
+ return 0, dec.raiseInvalidJSONErr(dec.cursor)
+}
+
+func (dec *Decoder) nextKey() (string, bool, error) {
+ for ; dec.cursor < dec.length || dec.read(); dec.cursor++ {
+ switch dec.data[dec.cursor] {
+ case ' ', '\n', '\t', '\r', ',':
+ continue
+ case '"':
+ dec.cursor = dec.cursor + 1
+ start, end, err := dec.getString()
+ if err != nil {
+ return "", false, err
+ }
+ var found byte
+ for ; dec.cursor < dec.length || dec.read(); dec.cursor++ {
+ if dec.data[dec.cursor] == ':' {
+ found |= 1
+ break
+ }
+ }
+ if found&1 != 0 {
+ dec.cursor++
+ d := dec.data[start : end-1]
+ return *(*string)(unsafe.Pointer(&d)), false, nil
+ }
+ return "", false, dec.raiseInvalidJSONErr(dec.cursor)
+ case '}':
+ dec.cursor = dec.cursor + 1
+ return "", true, nil
+ default:
+ // can't unmarshall to struct
+ return "", false, dec.raiseInvalidJSONErr(dec.cursor)
+ }
+ }
+ return "", false, dec.raiseInvalidJSONErr(dec.cursor)
+}
+
+func (dec *Decoder) skipData() error {
+ for ; dec.cursor < dec.length || dec.read(); dec.cursor++ {
+ switch dec.data[dec.cursor] {
+ case ' ', '\n', '\t', '\r', ',':
+ continue
+ // is null
+ case 'n':
+ dec.cursor++
+ err := dec.assertNull()
+ if err != nil {
+ return err
+ }
+ return nil
+ case 't':
+ dec.cursor++
+ err := dec.assertTrue()
+ if err != nil {
+ return err
+ }
+ return nil
+ // is false
+ case 'f':
+ dec.cursor++
+ err := dec.assertFalse()
+ if err != nil {
+ return err
+ }
+ return nil
+ // is an object
+ case '{':
+ dec.cursor = dec.cursor + 1
+ end, err := dec.skipObject()
+ dec.cursor = end
+ return err
+ // is string
+ case '"':
+ dec.cursor = dec.cursor + 1
+ err := dec.skipString()
+ return err
+ // is array
+ case '[':
+ dec.cursor = dec.cursor + 1
+ end, err := dec.skipArray()
+ dec.cursor = end
+ return err
+ case '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '-':
+ end, err := dec.skipNumber()
+ dec.cursor = end
+ return err
+ }
+ return dec.raiseInvalidJSONErr(dec.cursor)
+ }
+ return dec.raiseInvalidJSONErr(dec.cursor)
+}
+
+// DecodeObjectFunc is a func type implementing UnmarshalerJSONObject.
+// Use it to cast a `func(*Decoder, k string) error` to Unmarshal an object on the fly.
+type DecodeObjectFunc func(*Decoder, string) error
+
+// UnmarshalJSONObject implements UnmarshalerJSONObject.
+func (f DecodeObjectFunc) UnmarshalJSONObject(dec *Decoder, k string) error {
+ return f(dec, k)
+}
+
+// NKeys implements UnmarshalerJSONObject.
+func (f DecodeObjectFunc) NKeys() int {
+ return 0
+}
+
+// Add Values functions
+
+// AddObject decodes the JSON value within an object or an array to a UnmarshalerJSONObject.
+func (dec *Decoder) AddObject(v UnmarshalerJSONObject) error {
+ return dec.Object(v)
+}
+
+// AddObjectNull decodes the JSON value within an object or an array to a UnmarshalerJSONObject.
+func (dec *Decoder) AddObjectNull(v interface{}) error {
+ return dec.ObjectNull(v)
+}
+
+// Object decodes the JSON value within an object or an array to a UnmarshalerJSONObject.
+func (dec *Decoder) Object(value UnmarshalerJSONObject) error {
+ initialKeysDone := dec.keysDone
+ initialChild := dec.child
+ dec.keysDone = 0
+ dec.called = 0
+ dec.child |= 1
+ newCursor, err := dec.decodeObject(value)
+ if err != nil {
+ return err
+ }
+ dec.cursor = newCursor
+ dec.keysDone = initialKeysDone
+ dec.child = initialChild
+ dec.called |= 1
+ return nil
+}
+
+// ObjectNull decodes the JSON value within an object or an array to a UnmarshalerJSONObject.
+// v should be a pointer to an UnmarshalerJSONObject,
+// if `null` value is encountered in JSON, it will leave the value v untouched,
+// else it will create a new instance of the UnmarshalerJSONObject behind v.
+func (dec *Decoder) ObjectNull(v interface{}) error {
+ initialKeysDone := dec.keysDone
+ initialChild := dec.child
+ dec.keysDone = 0
+ dec.called = 0
+ dec.child |= 1
+ newCursor, err := dec.decodeObjectNull(v)
+ if err != nil {
+ return err
+ }
+ dec.cursor = newCursor
+ dec.keysDone = initialKeysDone
+ dec.child = initialChild
+ dec.called |= 1
+ return nil
+}
diff --git a/vendor/github.com/francoispqt/gojay/decode_pool.go b/vendor/github.com/francoispqt/gojay/decode_pool.go
new file mode 100644
index 0000000000..68c57138a6
--- /dev/null
+++ b/vendor/github.com/francoispqt/gojay/decode_pool.go
@@ -0,0 +1,64 @@
+package gojay
+
+import (
+ "io"
+ "sync"
+)
+
+var decPool = sync.Pool{
+ New: newDecoderPool,
+}
+
+func init() {
+ for i := 0; i < 32; i++ {
+ decPool.Put(NewDecoder(nil))
+ }
+}
+
+// NewDecoder returns a new decoder.
+// It takes an io.Reader implementation as data input.
+func NewDecoder(r io.Reader) *Decoder {
+ return &Decoder{
+ called: 0,
+ cursor: 0,
+ keysDone: 0,
+ err: nil,
+ r: r,
+ data: make([]byte, 512),
+ length: 0,
+ isPooled: 0,
+ }
+}
+func newDecoderPool() interface{} {
+ return NewDecoder(nil)
+}
+
+// BorrowDecoder borrows a Decoder from the pool.
+// It takes an io.Reader implementation as data input.
+//
+// In order to benefit from the pool, a borrowed decoder must be released after usage.
+func BorrowDecoder(r io.Reader) *Decoder {
+ return borrowDecoder(r, 512)
+}
+func borrowDecoder(r io.Reader, bufSize int) *Decoder {
+ dec := decPool.Get().(*Decoder)
+ dec.called = 0
+ dec.keysDone = 0
+ dec.cursor = 0
+ dec.err = nil
+ dec.r = r
+ dec.length = 0
+ dec.isPooled = 0
+ if bufSize > 0 {
+ dec.data = make([]byte, bufSize)
+ }
+ return dec
+}
+
+// Release sends back a Decoder to the pool.
+// If a decoder is used after calling Release
+// a panic will be raised with an InvalidUsagePooledDecoderError error.
+func (dec *Decoder) Release() {
+ dec.isPooled = 1
+ decPool.Put(dec)
+}
diff --git a/vendor/github.com/francoispqt/gojay/decode_slice.go b/vendor/github.com/francoispqt/gojay/decode_slice.go
new file mode 100644
index 0000000000..dbbb4bf3aa
--- /dev/null
+++ b/vendor/github.com/francoispqt/gojay/decode_slice.go
@@ -0,0 +1,89 @@
+package gojay
+
+// AddSliceString unmarshals the next JSON array of strings to the given *[]string s
+func (dec *Decoder) AddSliceString(s *[]string) error {
+ return dec.SliceString(s)
+}
+
+// SliceString unmarshals the next JSON array of strings to the given *[]string s
+func (dec *Decoder) SliceString(s *[]string) error {
+ err := dec.Array(DecodeArrayFunc(func(dec *Decoder) error {
+ var str string
+ if err := dec.String(&str); err != nil {
+ return err
+ }
+ *s = append(*s, str)
+ return nil
+ }))
+
+ if err != nil {
+ return err
+ }
+ return nil
+}
+
+// AddSliceInt unmarshals the next JSON array of integers to the given *[]int s
+func (dec *Decoder) AddSliceInt(s *[]int) error {
+ return dec.SliceInt(s)
+}
+
+// SliceInt unmarshals the next JSON array of integers to the given *[]int s
+func (dec *Decoder) SliceInt(s *[]int) error {
+ err := dec.Array(DecodeArrayFunc(func(dec *Decoder) error {
+ var i int
+ if err := dec.Int(&i); err != nil {
+ return err
+ }
+ *s = append(*s, i)
+ return nil
+ }))
+
+ if err != nil {
+ return err
+ }
+ return nil
+}
+
+// AddFloat64 unmarshals the next JSON array of floats to the given *[]float64 s
+func (dec *Decoder) AddSliceFloat64(s *[]float64) error {
+ return dec.SliceFloat64(s)
+}
+
+// SliceFloat64 unmarshals the next JSON array of floats to the given *[]float64 s
+func (dec *Decoder) SliceFloat64(s *[]float64) error {
+ err := dec.Array(DecodeArrayFunc(func(dec *Decoder) error {
+ var i float64
+ if err := dec.Float64(&i); err != nil {
+ return err
+ }
+ *s = append(*s, i)
+ return nil
+ }))
+
+ if err != nil {
+ return err
+ }
+ return nil
+}
+
+// AddBool unmarshals the next JSON array of boolegers to the given *[]bool s
+func (dec *Decoder) AddSliceBool(s *[]bool) error {
+ return dec.SliceBool(s)
+}
+
+// SliceBool unmarshals the next JSON array of boolegers to the given *[]bool s
+func (dec *Decoder) SliceBool(s *[]bool) error {
+ err := dec.Array(DecodeArrayFunc(func(dec *Decoder) error {
+ var b bool
+ if err := dec.Bool(&b); err != nil {
+ return err
+ }
+ *s = append(*s, b)
+ return nil
+ }))
+
+ if err != nil {
+ return err
+ }
+ return nil
+}
diff --git a/vendor/github.com/francoispqt/gojay/decode_sqlnull.go b/vendor/github.com/francoispqt/gojay/decode_sqlnull.go
new file mode 100644
index 0000000000..c25549f52b
--- /dev/null
+++ b/vendor/github.com/francoispqt/gojay/decode_sqlnull.go
@@ -0,0 +1,157 @@
+package gojay
+
+import "database/sql"
+
+// DecodeSQLNullString decodes a sql.NullString
+func (dec *Decoder) DecodeSQLNullString(v *sql.NullString) error {
+ if dec.isPooled == 1 {
+ panic(InvalidUsagePooledDecoderError("Invalid usage of pooled decoder"))
+ }
+ return dec.decodeSQLNullString(v)
+}
+
+func (dec *Decoder) decodeSQLNullString(v *sql.NullString) error {
+ var str string
+ if err := dec.decodeString(&str); err != nil {
+ return err
+ }
+ v.String = str
+ v.Valid = true
+ return nil
+}
+
+// DecodeSQLNullInt64 decodes a sql.NullInt64
+func (dec *Decoder) DecodeSQLNullInt64(v *sql.NullInt64) error {
+ if dec.isPooled == 1 {
+ panic(InvalidUsagePooledDecoderError("Invalid usage of pooled decoder"))
+ }
+ return dec.decodeSQLNullInt64(v)
+}
+
+func (dec *Decoder) decodeSQLNullInt64(v *sql.NullInt64) error {
+ var i int64
+ if err := dec.decodeInt64(&i); err != nil {
+ return err
+ }
+ v.Int64 = i
+ v.Valid = true
+ return nil
+}
+
+// DecodeSQLNullFloat64 decodes a sql.NullString with the given format
+func (dec *Decoder) DecodeSQLNullFloat64(v *sql.NullFloat64) error {
+ if dec.isPooled == 1 {
+ panic(InvalidUsagePooledDecoderError("Invalid usage of pooled decoder"))
+ }
+ return dec.decodeSQLNullFloat64(v)
+}
+
+func (dec *Decoder) decodeSQLNullFloat64(v *sql.NullFloat64) error {
+ var i float64
+ if err := dec.decodeFloat64(&i); err != nil {
+ return err
+ }
+ v.Float64 = i
+ v.Valid = true
+ return nil
+}
+
+// DecodeSQLNullBool decodes a sql.NullString with the given format
+func (dec *Decoder) DecodeSQLNullBool(v *sql.NullBool) error {
+ if dec.isPooled == 1 {
+ panic(InvalidUsagePooledDecoderError("Invalid usage of pooled decoder"))
+ }
+ return dec.decodeSQLNullBool(v)
+}
+
+func (dec *Decoder) decodeSQLNullBool(v *sql.NullBool) error {
+ var b bool
+ if err := dec.decodeBool(&b); err != nil {
+ return err
+ }
+ v.Bool = b
+ v.Valid = true
+ return nil
+}
+
+// Add Values functions
+
+// AddSQLNullString decodes the JSON value within an object or an array to qn *sql.NullString
+func (dec *Decoder) AddSQLNullString(v *sql.NullString) error {
+ return dec.SQLNullString(v)
+}
+
+// SQLNullString decodes the JSON value within an object or an array to an *sql.NullString
+func (dec *Decoder) SQLNullString(v *sql.NullString) error {
+ var b *string
+ if err := dec.StringNull(&b); err != nil {
+ return err
+ }
+ if b == nil {
+ v.Valid = false
+ } else {
+ v.String = *b
+ v.Valid = true
+ }
+ return nil
+}
+
+// AddSQLNullInt64 decodes the JSON value within an object or an array to qn *sql.NullInt64
+func (dec *Decoder) AddSQLNullInt64(v *sql.NullInt64) error {
+ return dec.SQLNullInt64(v)
+}
+
+// SQLNullInt64 decodes the JSON value within an object or an array to an *sql.NullInt64
+func (dec *Decoder) SQLNullInt64(v *sql.NullInt64) error {
+ var b *int64
+ if err := dec.Int64Null(&b); err != nil {
+ return err
+ }
+ if b == nil {
+ v.Valid = false
+ } else {
+ v.Int64 = *b
+ v.Valid = true
+ }
+ return nil
+}
+
+// AddSQLNullFloat64 decodes the JSON value within an object or an array to qn *sql.NullFloat64
+func (dec *Decoder) AddSQLNullFloat64(v *sql.NullFloat64) error {
+ return dec.SQLNullFloat64(v)
+}
+
+// SQLNullFloat64 decodes the JSON value within an object or an array to an *sql.NullFloat64
+func (dec *Decoder) SQLNullFloat64(v *sql.NullFloat64) error {
+ var b *float64
+ if err := dec.Float64Null(&b); err != nil {
+ return err
+ }
+ if b == nil {
+ v.Valid = false
+ } else {
+ v.Float64 = *b
+ v.Valid = true
+ }
+ return nil
+}
+
+// AddSQLNullBool decodes the JSON value within an object or an array to an *sql.NullBool
+func (dec *Decoder) AddSQLNullBool(v *sql.NullBool) error {
+ return dec.SQLNullBool(v)
+}
+
+// SQLNullBool decodes the JSON value within an object or an array to an *sql.NullBool
+func (dec *Decoder) SQLNullBool(v *sql.NullBool) error {
+ var b *bool
+ if err := dec.BoolNull(&b); err != nil {
+ return err
+ }
+ if b == nil {
+ v.Valid = false
+ } else {
+ v.Bool = *b
+ v.Valid = true
+ }
+ return nil
+}
diff --git a/vendor/github.com/francoispqt/gojay/decode_stream.go b/vendor/github.com/francoispqt/gojay/decode_stream.go
new file mode 100644
index 0000000000..74beee4d75
--- /dev/null
+++ b/vendor/github.com/francoispqt/gojay/decode_stream.go
@@ -0,0 +1,115 @@
+package gojay
+
+import (
+ "sync"
+ "time"
+)
+
+// UnmarshalerStream is the interface to implement for a slice, an array or a slice
+// to decode a line delimited JSON to.
+type UnmarshalerStream interface {
+ UnmarshalStream(*StreamDecoder) error
+}
+
+// Stream is a struct holding the Stream api
+var Stream = stream{}
+
+type stream struct{}
+
+// A StreamDecoder reads and decodes JSON values from an input stream.
+//
+// It implements conext.Context and provide a channel to notify interruption.
+type StreamDecoder struct {
+ mux sync.RWMutex
+ *Decoder
+ done chan struct{}
+ deadline *time.Time
+}
+
+// DecodeStream reads the next line delimited JSON-encoded value from the decoder's input (io.Reader) and stores it in the value pointed to by c.
+//
+// c must implement UnmarshalerStream. Ideally c is a channel. See example for implementation.
+//
+// See the documentation for Unmarshal for details about the conversion of JSON into a Go value.
+func (dec *StreamDecoder) DecodeStream(c UnmarshalerStream) error {
+ if dec.isPooled == 1 {
+ panic(InvalidUsagePooledDecoderError("Invalid usage of pooled decoder"))
+ }
+ if dec.r == nil {
+ dec.err = NoReaderError("No reader given to decode stream")
+ close(dec.done)
+ return dec.err
+ }
+ for ; dec.cursor < dec.length || dec.read(); dec.cursor++ {
+ switch dec.data[dec.cursor] {
+ case ' ', '\n', '\t', '\r', ',':
+ continue
+ default:
+ // char is not space start reading
+ for dec.nextChar() != 0 {
+ // calling unmarshal stream
+ err := c.UnmarshalStream(dec)
+ if err != nil {
+ dec.err = err
+ close(dec.done)
+ return err
+ }
+ // garbage collects buffer
+ // we don't want the buffer to grow extensively
+ dec.data = dec.data[dec.cursor:]
+ dec.length = dec.length - dec.cursor
+ dec.cursor = 0
+ }
+ // close the done channel to signal the end of the job
+ close(dec.done)
+ return nil
+ }
+ }
+ close(dec.done)
+ dec.mux.Lock()
+ err := dec.raiseInvalidJSONErr(dec.cursor)
+ dec.mux.Unlock()
+ return err
+}
+
+// context.Context implementation
+
+// Done returns a channel that's closed when work is done.
+// It implements context.Context
+func (dec *StreamDecoder) Done() <-chan struct{} {
+ return dec.done
+}
+
+// Deadline returns the time when work done on behalf of this context
+// should be canceled. Deadline returns ok==false when no deadline is
+// set. Successive calls to Deadline return the same results.
+func (dec *StreamDecoder) Deadline() (time.Time, bool) {
+ if dec.deadline != nil {
+ return *dec.deadline, true
+ }
+ return time.Time{}, false
+}
+
+// SetDeadline sets the deadline
+func (dec *StreamDecoder) SetDeadline(t time.Time) {
+ dec.deadline = &t
+}
+
+// Err returns nil if Done is not yet closed.
+// If Done is closed, Err returns a non-nil error explaining why.
+// It implements context.Context
+func (dec *StreamDecoder) Err() error {
+ select {
+ case <-dec.done:
+ dec.mux.RLock()
+ defer dec.mux.RUnlock()
+ return dec.err
+ default:
+ return nil
+ }
+}
+
+// Value implements context.Context
+func (dec *StreamDecoder) Value(key interface{}) interface{} {
+ return nil
+}
diff --git a/vendor/github.com/francoispqt/gojay/decode_stream_pool.go b/vendor/github.com/francoispqt/gojay/decode_stream_pool.go
new file mode 100644
index 0000000000..8e1863b920
--- /dev/null
+++ b/vendor/github.com/francoispqt/gojay/decode_stream_pool.go
@@ -0,0 +1,59 @@
+package gojay
+
+import (
+ "io"
+ "sync"
+)
+
+var streamDecPool = sync.Pool{
+ New: newStreamDecoderPool,
+}
+
+// NewDecoder returns a new StreamDecoder.
+// It takes an io.Reader implementation as data input.
+// It initiates the done channel returned by Done().
+func (s stream) NewDecoder(r io.Reader) *StreamDecoder {
+ dec := NewDecoder(r)
+ streamDec := &StreamDecoder{
+ Decoder: dec,
+ done: make(chan struct{}, 1),
+ mux: sync.RWMutex{},
+ }
+ return streamDec
+}
+func newStreamDecoderPool() interface{} {
+ return Stream.NewDecoder(nil)
+}
+
+// BorrowDecoder borrows a StreamDecoder from the pool.
+// It takes an io.Reader implementation as data input.
+// It initiates the done channel returned by Done().
+//
+// If no StreamEncoder is available in the pool, it returns a fresh one
+func (s stream) BorrowDecoder(r io.Reader) *StreamDecoder {
+ return s.borrowDecoder(r, 512)
+}
+
+func (s stream) borrowDecoder(r io.Reader, bufSize int) *StreamDecoder {
+ streamDec := streamDecPool.Get().(*StreamDecoder)
+ streamDec.called = 0
+ streamDec.keysDone = 0
+ streamDec.cursor = 0
+ streamDec.err = nil
+ streamDec.r = r
+ streamDec.length = 0
+ streamDec.isPooled = 0
+ streamDec.done = make(chan struct{}, 1)
+ if bufSize > 0 {
+ streamDec.data = make([]byte, bufSize)
+ }
+ return streamDec
+}
+
+// Release sends back a Decoder to the pool.
+// If a decoder is used after calling Release
+// a panic will be raised with an InvalidUsagePooledDecoderError error.
+func (dec *StreamDecoder) Release() {
+ dec.isPooled = 1
+ streamDecPool.Put(dec)
+}
diff --git a/vendor/github.com/francoispqt/gojay/decode_string.go b/vendor/github.com/francoispqt/gojay/decode_string.go
new file mode 100644
index 0000000000..694359c7b6
--- /dev/null
+++ b/vendor/github.com/francoispqt/gojay/decode_string.go
@@ -0,0 +1,260 @@
+package gojay
+
+import (
+ "unsafe"
+)
+
+// DecodeString reads the next JSON-encoded value from the decoder's input (io.Reader) and stores it in the string pointed to by v.
+//
+// See the documentation for Unmarshal for details about the conversion of JSON into a Go value.
+func (dec *Decoder) DecodeString(v *string) error {
+ if dec.isPooled == 1 {
+ panic(InvalidUsagePooledDecoderError("Invalid usage of pooled decoder"))
+ }
+ return dec.decodeString(v)
+}
+func (dec *Decoder) decodeString(v *string) error {
+ for ; dec.cursor < dec.length || dec.read(); dec.cursor++ {
+ switch dec.data[dec.cursor] {
+ case ' ', '\n', '\t', '\r', ',':
+ // is string
+ continue
+ case '"':
+ dec.cursor++
+ start, end, err := dec.getString()
+ if err != nil {
+ return err
+ }
+ // we do minus one to remove the last quote
+ d := dec.data[start : end-1]
+ *v = *(*string)(unsafe.Pointer(&d))
+ dec.cursor = end
+ return nil
+ // is nil
+ case 'n':
+ dec.cursor++
+ err := dec.assertNull()
+ if err != nil {
+ return err
+ }
+ return nil
+ default:
+ dec.err = dec.makeInvalidUnmarshalErr(v)
+ err := dec.skipData()
+ if err != nil {
+ return err
+ }
+ return nil
+ }
+ }
+ return nil
+}
+
+func (dec *Decoder) decodeStringNull(v **string) error {
+ for ; dec.cursor < dec.length || dec.read(); dec.cursor++ {
+ switch dec.data[dec.cursor] {
+ case ' ', '\n', '\t', '\r', ',':
+ // is string
+ continue
+ case '"':
+ dec.cursor++
+ start, end, err := dec.getString()
+
+ if err != nil {
+ return err
+ }
+ if *v == nil {
+ *v = new(string)
+ }
+ // we do minus one to remove the last quote
+ d := dec.data[start : end-1]
+ **v = *(*string)(unsafe.Pointer(&d))
+ dec.cursor = end
+ return nil
+ // is nil
+ case 'n':
+ dec.cursor++
+ err := dec.assertNull()
+ if err != nil {
+ return err
+ }
+ return nil
+ default:
+ dec.err = dec.makeInvalidUnmarshalErr(v)
+ err := dec.skipData()
+ if err != nil {
+ return err
+ }
+ return nil
+ }
+ }
+ return nil
+}
+
+func (dec *Decoder) parseEscapedString() error {
+ if dec.cursor >= dec.length && !dec.read() {
+ return dec.raiseInvalidJSONErr(dec.cursor)
+ }
+ switch dec.data[dec.cursor] {
+ case '"':
+ dec.data[dec.cursor] = '"'
+ case '\\':
+ dec.data[dec.cursor] = '\\'
+ case '/':
+ dec.data[dec.cursor] = '/'
+ case 'b':
+ dec.data[dec.cursor] = '\b'
+ case 'f':
+ dec.data[dec.cursor] = '\f'
+ case 'n':
+ dec.data[dec.cursor] = '\n'
+ case 'r':
+ dec.data[dec.cursor] = '\r'
+ case 't':
+ dec.data[dec.cursor] = '\t'
+ case 'u':
+ start := dec.cursor
+ dec.cursor++
+ str, err := dec.parseUnicode()
+ if err != nil {
+ return err
+ }
+ diff := dec.cursor - start
+ dec.data = append(append(dec.data[:start-1], str...), dec.data[dec.cursor:]...)
+ dec.length = len(dec.data)
+ dec.cursor += len(str) - diff - 1
+
+ return nil
+ default:
+ return dec.raiseInvalidJSONErr(dec.cursor)
+ }
+
+ dec.data = append(dec.data[:dec.cursor-1], dec.data[dec.cursor:]...)
+ dec.length--
+
+ // Since we've lost a character, our dec.cursor offset is now
+ // 1 past the escaped character which is precisely where we
+ // want it.
+
+ return nil
+}
+
+func (dec *Decoder) getString() (int, int, error) {
+ // extract key
+ var keyStart = dec.cursor
+ // var str *Builder
+ for dec.cursor < dec.length || dec.read() {
+ switch dec.data[dec.cursor] {
+ // string found
+ case '"':
+ dec.cursor = dec.cursor + 1
+ return keyStart, dec.cursor, nil
+ // slash found
+ case '\\':
+ dec.cursor = dec.cursor + 1
+ err := dec.parseEscapedString()
+ if err != nil {
+ return 0, 0, err
+ }
+ default:
+ dec.cursor = dec.cursor + 1
+ continue
+ }
+ }
+ return 0, 0, dec.raiseInvalidJSONErr(dec.cursor)
+}
+
+func (dec *Decoder) skipEscapedString() error {
+ start := dec.cursor
+ for ; dec.cursor < dec.length || dec.read(); dec.cursor++ {
+ if dec.data[dec.cursor] != '\\' {
+ d := dec.data[dec.cursor]
+ dec.cursor = dec.cursor + 1
+ nSlash := dec.cursor - start
+ switch d {
+ case '"':
+ // nSlash must be odd
+ if nSlash&1 != 1 {
+ return dec.raiseInvalidJSONErr(dec.cursor)
+ }
+ return nil
+ case 'u': // is unicode, we skip the following characters and place the cursor one one byte backward to avoid it breaking when returning to skipString
+ if err := dec.skipString(); err != nil {
+ return err
+ }
+ dec.cursor--
+ return nil
+ case 'n', 'r', 't', '/', 'f', 'b':
+ return nil
+ default:
+ // nSlash must be even
+ if nSlash&1 == 1 {
+ return dec.raiseInvalidJSONErr(dec.cursor)
+ }
+ return nil
+ }
+ }
+ }
+ return dec.raiseInvalidJSONErr(dec.cursor)
+}
+
+func (dec *Decoder) skipString() error {
+ for dec.cursor < dec.length || dec.read() {
+ switch dec.data[dec.cursor] {
+ // found the closing quote
+ // let's return
+ case '"':
+ dec.cursor = dec.cursor + 1
+ return nil
+ // solidus found start parsing an escaped string
+ case '\\':
+ dec.cursor = dec.cursor + 1
+ err := dec.skipEscapedString()
+ if err != nil {
+ return err
+ }
+ default:
+ dec.cursor = dec.cursor + 1
+ continue
+ }
+ }
+ return dec.raiseInvalidJSONErr(len(dec.data) - 1)
+}
+
+// Add Values functions
+
+// AddString decodes the JSON value within an object or an array to a *string.
+// If next key is not a JSON string nor null, InvalidUnmarshalError will be returned.
+func (dec *Decoder) AddString(v *string) error {
+ return dec.String(v)
+}
+
+// AddStringNull decodes the JSON value within an object or an array to a *string.
+// If next key is not a JSON string nor null, InvalidUnmarshalError will be returned.
+// If a `null` is encountered, gojay does not change the value of the pointer.
+func (dec *Decoder) AddStringNull(v **string) error {
+ return dec.StringNull(v)
+}
+
+// String decodes the JSON value within an object or an array to a *string.
+// If next key is not a JSON string nor null, InvalidUnmarshalError will be returned.
+func (dec *Decoder) String(v *string) error {
+ err := dec.decodeString(v)
+ if err != nil {
+ return err
+ }
+ dec.called |= 1
+ return nil
+}
+
+// StringNull decodes the JSON value within an object or an array to a **string.
+// If next key is not a JSON string nor null, InvalidUnmarshalError will be returned.
+// If a `null` is encountered, gojay does not change the value of the pointer.
+func (dec *Decoder) StringNull(v **string) error {
+ err := dec.decodeStringNull(v)
+ if err != nil {
+ return err
+ }
+ dec.called |= 1
+ return nil
+}
diff --git a/vendor/github.com/francoispqt/gojay/decode_string_unicode.go b/vendor/github.com/francoispqt/gojay/decode_string_unicode.go
new file mode 100644
index 0000000000..9e14d52b07
--- /dev/null
+++ b/vendor/github.com/francoispqt/gojay/decode_string_unicode.go
@@ -0,0 +1,98 @@
+package gojay
+
+import (
+ "unicode/utf16"
+ "unicode/utf8"
+)
+
+func (dec *Decoder) getUnicode() (rune, error) {
+ i := 0
+ r := rune(0)
+ for ; (dec.cursor < dec.length || dec.read()) && i < 4; dec.cursor++ {
+ c := dec.data[dec.cursor]
+ if c >= '0' && c <= '9' {
+ r = r*16 + rune(c-'0')
+ } else if c >= 'a' && c <= 'f' {
+ r = r*16 + rune(c-'a'+10)
+ } else if c >= 'A' && c <= 'F' {
+ r = r*16 + rune(c-'A'+10)
+ } else {
+ return 0, InvalidJSONError("Invalid unicode code point")
+ }
+ i++
+ }
+ return r, nil
+}
+
+func (dec *Decoder) appendEscapeChar(str []byte, c byte) ([]byte, error) {
+ switch c {
+ case 't':
+ str = append(str, '\t')
+ case 'n':
+ str = append(str, '\n')
+ case 'r':
+ str = append(str, '\r')
+ case 'b':
+ str = append(str, '\b')
+ case 'f':
+ str = append(str, '\f')
+ case '\\':
+ str = append(str, '\\')
+ default:
+ return nil, InvalidJSONError("Invalid JSON")
+ }
+ return str, nil
+}
+
+func (dec *Decoder) parseUnicode() ([]byte, error) {
+ // get unicode after u
+ r, err := dec.getUnicode()
+ if err != nil {
+ return nil, err
+ }
+ // no error start making new string
+ str := make([]byte, 16, 16)
+ i := 0
+ // check if code can be a surrogate utf16
+ if utf16.IsSurrogate(r) {
+ if dec.cursor >= dec.length && !dec.read() {
+ return nil, dec.raiseInvalidJSONErr(dec.cursor)
+ }
+ c := dec.data[dec.cursor]
+ if c != '\\' {
+ i += utf8.EncodeRune(str, r)
+ return str[:i], nil
+ }
+ dec.cursor++
+ if dec.cursor >= dec.length && !dec.read() {
+ return nil, dec.raiseInvalidJSONErr(dec.cursor)
+ }
+ c = dec.data[dec.cursor]
+ if c != 'u' {
+ i += utf8.EncodeRune(str, r)
+ str, err = dec.appendEscapeChar(str[:i], c)
+ if err != nil {
+ dec.err = err
+ return nil, err
+ }
+ i++
+ dec.cursor++
+ return str[:i], nil
+ }
+ dec.cursor++
+ r2, err := dec.getUnicode()
+ if err != nil {
+ return nil, err
+ }
+ combined := utf16.DecodeRune(r, r2)
+ if combined == '\uFFFD' {
+ i += utf8.EncodeRune(str, r)
+ i += utf8.EncodeRune(str, r2)
+ } else {
+ i += utf8.EncodeRune(str, combined)
+ }
+ return str[:i], nil
+ }
+ i += utf8.EncodeRune(str, r)
+ return str[:i], nil
+}
diff --git a/vendor/github.com/francoispqt/gojay/decode_time.go b/vendor/github.com/francoispqt/gojay/decode_time.go
new file mode 100644
index 0000000000..68f906d7f2
--- /dev/null
+++ b/vendor/github.com/francoispqt/gojay/decode_time.go
@@ -0,0 +1,53 @@
+package gojay
+
+import (
+ "time"
+)
+
+// DecodeTime decodes time with the given format
+func (dec *Decoder) DecodeTime(v *time.Time, format string) error {
+ if dec.isPooled == 1 {
+ panic(InvalidUsagePooledDecoderError("Invalid usage of pooled decoder"))
+ }
+ return dec.decodeTime(v, format)
+}
+
+func (dec *Decoder) decodeTime(v *time.Time, format string) error {
+ if format == time.RFC3339 {
+ var ej = make(EmbeddedJSON, 0, 20)
+ if err := dec.decodeEmbeddedJSON(&ej); err != nil {
+ return err
+ }
+ if err := v.UnmarshalJSON(ej); err != nil {
+ return err
+ }
+ return nil
+ }
+ var str string
+ if err := dec.decodeString(&str); err != nil {
+ return err
+ }
+ tt, err := time.Parse(format, str)
+ if err != nil {
+ return err
+ }
+ *v = tt
+ return nil
+}
+
+// Add Values functions
+
+// AddTime decodes the JSON value within an object or an array to a *time.Time with the given format
+func (dec *Decoder) AddTime(v *time.Time, format string) error {
+ return dec.Time(v, format)
+}
+
+// Time decodes the JSON value within an object or an array to a *time.Time with the given format
+func (dec *Decoder) Time(v *time.Time, format string) error {
+ err := dec.decodeTime(v, format)
+ if err != nil {
+ return err
+ }
+ dec.called |= 1
+ return nil
+}
diff --git a/vendor/github.com/francoispqt/gojay/decode_unsafe.go b/vendor/github.com/francoispqt/gojay/decode_unsafe.go
new file mode 100644
index 0000000000..54448fba73
--- /dev/null
+++ b/vendor/github.com/francoispqt/gojay/decode_unsafe.go
@@ -0,0 +1,120 @@
+package gojay
+
+import (
+ "fmt"
+)
+
+// Unsafe is the structure holding the unsafe version of the API.
+// The difference between unsafe api and regular api is that the regular API
+// copies the buffer passed to Unmarshal functions to a new internal buffer.
+// Making it safer because internally GoJay uses unsafe.Pointer to transform slice of bytes into a string.
+var Unsafe = decUnsafe{}
+
+type decUnsafe struct{}
+
+func (u decUnsafe) UnmarshalJSONArray(data []byte, v UnmarshalerJSONArray) error {
+ dec := borrowDecoder(nil, 0)
+ defer dec.Release()
+ dec.data = data
+ dec.length = len(data)
+ _, err := dec.decodeArray(v)
+ return err
+}
+
+func (u decUnsafe) UnmarshalJSONObject(data []byte, v UnmarshalerJSONObject) error {
+ dec := borrowDecoder(nil, 0)
+ defer dec.Release()
+ dec.data = data
+ dec.length = len(data)
+ _, err := dec.decodeObject(v)
+ return err
+}
+
+func (u decUnsafe) Unmarshal(data []byte, v interface{}) error {
+ var err error
+ var dec *Decoder
+ switch vt := v.(type) {
+ case *string:
+ dec = borrowDecoder(nil, 0)
+ dec.length = len(data)
+ dec.data = data
+ err = dec.decodeString(vt)
+ case *int:
+ dec = borrowDecoder(nil, 0)
+ dec.length = len(data)
+ dec.data = data
+ err = dec.decodeInt(vt)
+ case *int8:
+ dec = borrowDecoder(nil, 0)
+ dec.length = len(data)
+ dec.data = data
+ err = dec.decodeInt8(vt)
+ case *int16:
+ dec = borrowDecoder(nil, 0)
+ dec.length = len(data)
+ dec.data = data
+ err = dec.decodeInt16(vt)
+ case *int32:
+ dec = borrowDecoder(nil, 0)
+ dec.length = len(data)
+ dec.data = data
+ err = dec.decodeInt32(vt)
+ case *int64:
+ dec = borrowDecoder(nil, 0)
+ dec.length = len(data)
+ dec.data = data
+ err = dec.decodeInt64(vt)
+ case *uint8:
+ dec = borrowDecoder(nil, 0)
+ dec.length = len(data)
+ dec.data = data
+ err = dec.decodeUint8(vt)
+ case *uint16:
+ dec = borrowDecoder(nil, 0)
+ dec.length = len(data)
+ dec.data = data
+ err = dec.decodeUint16(vt)
+ case *uint32:
+ dec = borrowDecoder(nil, 0)
+ dec.length = len(data)
+ dec.data = data
+ err = dec.decodeUint32(vt)
+ case *uint64:
+ dec = borrowDecoder(nil, 0)
+ dec.length = len(data)
+ dec.data = data
+ err = dec.decodeUint64(vt)
+ case *float64:
+ dec = borrowDecoder(nil, 0)
+ dec.length = len(data)
+ dec.data = data
+ err = dec.decodeFloat64(vt)
+ case *float32:
+ dec = borrowDecoder(nil, 0)
+ dec.length = len(data)
+ dec.data = data
+ err = dec.decodeFloat32(vt)
+ case *bool:
+ dec = borrowDecoder(nil, 0)
+ dec.length = len(data)
+ dec.data = data
+ err = dec.decodeBool(vt)
+ case UnmarshalerJSONObject:
+ dec = borrowDecoder(nil, 0)
+ dec.length = len(data)
+ dec.data = data
+ _, err = dec.decodeObject(vt)
+ case UnmarshalerJSONArray:
+ dec = borrowDecoder(nil, 0)
+ dec.length = len(data)
+ dec.data = data
+ _, err = dec.decodeArray(vt)
+ default:
+ return InvalidUnmarshalError(fmt.Sprintf(invalidUnmarshalErrorMsg, vt))
+ }
+ defer dec.Release()
+ if err != nil {
+ return err
+ }
+ return dec.err
+}
diff --git a/vendor/github.com/francoispqt/gojay/encode.go b/vendor/github.com/francoispqt/gojay/encode.go
new file mode 100644
index 0000000000..92edaafa06
--- /dev/null
+++ b/vendor/github.com/francoispqt/gojay/encode.go
@@ -0,0 +1,202 @@
+package gojay
+
+import (
+ "encoding/json"
+ "fmt"
+ "io"
+)
+
+var nullBytes = []byte("null")
+
+// MarshalJSONArray returns the JSON encoding of v, an implementation of MarshalerJSONArray.
+//
+//
+// Example:
+// type TestSlice []*TestStruct
+//
+// func (t TestSlice) MarshalJSONArray(enc *Encoder) {
+// for _, e := range t {
+// enc.AddObject(e)
+// }
+// }
+//
+// func main() {
+// test := &TestSlice{
+// &TestStruct{123456},
+// &TestStruct{7890},
+// }
+// b, _ := Marshal(test)
+// fmt.Println(b) // [{"id":123456},{"id":7890}]
+// }
+func MarshalJSONArray(v MarshalerJSONArray) ([]byte, error) {
+ enc := BorrowEncoder(nil)
+ enc.grow(512)
+ enc.writeByte('[')
+ v.(MarshalerJSONArray).MarshalJSONArray(enc)
+ enc.writeByte(']')
+
+ defer func() {
+ enc.buf = make([]byte, 0, 512)
+ enc.Release()
+ }()
+
+ return enc.buf, nil
+}
+
+// MarshalJSONObject returns the JSON encoding of v, an implementation of MarshalerJSONObject.
+//
+// Example:
+// type Object struct {
+// id int
+// }
+// func (s *Object) MarshalJSONObject(enc *gojay.Encoder) {
+// enc.IntKey("id", s.id)
+// }
+// func (s *Object) IsNil() bool {
+// return s == nil
+// }
+//
+// func main() {
+// test := &Object{
+// id: 123456,
+// }
+// b, _ := gojay.Marshal(test)
+// fmt.Println(b) // {"id":123456}
+// }
+func MarshalJSONObject(v MarshalerJSONObject) ([]byte, error) {
+ enc := BorrowEncoder(nil)
+ enc.grow(512)
+
+ defer func() {
+ enc.buf = make([]byte, 0, 512)
+ enc.Release()
+ }()
+
+ return enc.encodeObject(v)
+}
+
+// Marshal returns the JSON encoding of v.
+//
+// If v is nil, not an implementation MarshalerJSONObject or MarshalerJSONArray or not one of the following types:
+// string, int, int8, int16, int32, int64, uint8, uint16, uint32, uint64, float64, float32, bool
+// Marshal returns an InvalidMarshalError.
+func Marshal(v interface{}) ([]byte, error) {
+ return marshal(v, false)
+}
+
+// MarshalAny returns the JSON encoding of v.
+//
+// If v is nil, not an implementation MarshalerJSONObject or MarshalerJSONArray or not one of the following types:
+// string, int, int8, int16, int32, int64, uint8, uint16, uint32, uint64, float64, float32, bool
+// MarshalAny falls back to "json/encoding" package to marshal the value.
+func MarshalAny(v interface{}) ([]byte, error) {
+ return marshal(v, true)
+}
+
+func marshal(v interface{}, any bool) ([]byte, error) {
+ var (
+ enc = BorrowEncoder(nil)
+
+ buf []byte
+ err error
+ )
+
+ defer func() {
+ enc.buf = make([]byte, 0, 512)
+ enc.Release()
+ }()
+
+ buf, err = func() ([]byte, error) {
+ switch vt := v.(type) {
+ case MarshalerJSONObject:
+ return enc.encodeObject(vt)
+ case MarshalerJSONArray:
+ return enc.encodeArray(vt)
+ case string:
+ return enc.encodeString(vt)
+ case bool:
+ return enc.encodeBool(vt)
+ case int:
+ return enc.encodeInt(vt)
+ case int64:
+ return enc.encodeInt64(vt)
+ case int32:
+ return enc.encodeInt(int(vt))
+ case int16:
+ return enc.encodeInt(int(vt))
+ case int8:
+ return enc.encodeInt(int(vt))
+ case uint64:
+ return enc.encodeInt(int(vt))
+ case uint32:
+ return enc.encodeInt(int(vt))
+ case uint16:
+ return enc.encodeInt(int(vt))
+ case uint8:
+ return enc.encodeInt(int(vt))
+ case float64:
+ return enc.encodeFloat(vt)
+ case float32:
+ return enc.encodeFloat32(vt)
+ case *EmbeddedJSON:
+ return enc.encodeEmbeddedJSON(vt)
+ default:
+ if any {
+ return json.Marshal(vt)
+ }
+
+ return nil, InvalidMarshalError(fmt.Sprintf(invalidMarshalErrorMsg, vt))
+ }
+ }()
+ return buf, err
+}
+
+// MarshalerJSONObject is the interface to implement for struct to be encoded
+type MarshalerJSONObject interface {
+ MarshalJSONObject(enc *Encoder)
+ IsNil() bool
+}
+
+// MarshalerJSONArray is the interface to implement
+// for a slice or an array to be encoded
+type MarshalerJSONArray interface {
+ MarshalJSONArray(enc *Encoder)
+ IsNil() bool
+}
+
+// An Encoder writes JSON values to an output stream.
+type Encoder struct {
+ buf []byte
+ isPooled byte
+ w io.Writer
+ err error
+ hasKeys bool
+ keys []string
+}
+
+// AppendBytes allows a modular usage by appending bytes manually to the current state of the buffer.
+func (enc *Encoder) AppendBytes(b []byte) {
+ enc.writeBytes(b)
+}
+
+// AppendByte allows a modular usage by appending a single byte manually to the current state of the buffer.
+func (enc *Encoder) AppendByte(b byte) {
+ enc.writeByte(b)
+}
+
+// Buf returns the Encoder's buffer.
+func (enc *Encoder) Buf() []byte {
+ return enc.buf
+}
+
+// Write writes to the io.Writer and resets the buffer.
+func (enc *Encoder) Write() (int, error) {
+ i, err := enc.w.Write(enc.buf)
+ enc.buf = enc.buf[:0]
+ return i, err
+}
+
+func (enc *Encoder) getPreviousRune() byte {
+ last := len(enc.buf) - 1
+ return enc.buf[last]
+}
diff --git a/vendor/github.com/francoispqt/gojay/encode_array.go b/vendor/github.com/francoispqt/gojay/encode_array.go
new file mode 100644
index 0000000000..5e9d49e825
--- /dev/null
+++ b/vendor/github.com/francoispqt/gojay/encode_array.go
@@ -0,0 +1,212 @@
+package gojay
+
+// EncodeArray encodes an implementation of MarshalerJSONArray to JSON
+func (enc *Encoder) EncodeArray(v MarshalerJSONArray) error {
+ if enc.isPooled == 1 {
+ panic(InvalidUsagePooledEncoderError("Invalid usage of pooled encoder"))
+ }
+ _, _ = enc.encodeArray(v)
+ _, err := enc.Write()
+ if err != nil {
+ enc.err = err
+ return err
+ }
+ return nil
+}
+func (enc *Encoder) encodeArray(v MarshalerJSONArray) ([]byte, error) {
+ enc.grow(200)
+ enc.writeByte('[')
+ v.MarshalJSONArray(enc)
+ enc.writeByte(']')
+ return enc.buf, enc.err
+}
+
+// AddArray adds an implementation of MarshalerJSONArray to be encoded, must be used inside a slice or array encoding (does not encode a key)
+// value must implement Marshaler
+func (enc *Encoder) AddArray(v MarshalerJSONArray) {
+ enc.Array(v)
+}
+
+// AddArrayOmitEmpty adds an array or slice to be encoded, must be used inside a slice or array encoding (does not encode a key)
+// value must implement MarshalerAddArrayOmitEmpty
+func (enc *Encoder) AddArrayOmitEmpty(v MarshalerJSONArray) {
+ enc.ArrayOmitEmpty(v)
+}
+
+// AddArrayNullEmpty adds an array or slice to be encoded, must be used inside a slice or array encoding (does not encode a key)
+// value must implement Marshaler, if v is empty, `null` will be encoded`
+func (enc *Encoder) AddArrayNullEmpty(v MarshalerJSONArray) {
+ enc.ArrayNullEmpty(v)
+}
+
+// AddArrayKey adds an array or slice to be encoded, must be used inside an object as it will encode a key
+// value must implement Marshaler
+func (enc *Encoder) AddArrayKey(key string, v MarshalerJSONArray) {
+ enc.ArrayKey(key, v)
+}
+
+// AddArrayKeyOmitEmpty adds an array or slice to be encoded and skips it if it is nil.
+// Must be called inside an object as it will encode a key.
+func (enc *Encoder) AddArrayKeyOmitEmpty(key string, v MarshalerJSONArray) {
+ enc.ArrayKeyOmitEmpty(key, v)
+}
+
+// AddArrayKeyNullEmpty adds an array or slice to be encoded and skips it if it is nil.
+// Must be called inside an object as it will encode a key. `null` will be encoded`
+func (enc *Encoder) AddArrayKeyNullEmpty(key string, v MarshalerJSONArray) {
+ enc.ArrayKeyNullEmpty(key, v)
+}
+
+// Array adds an implementation of MarshalerJSONArray to be encoded, must be used inside a slice or array encoding (does not encode a key)
+// value must implement Marshaler
+func (enc *Encoder) Array(v MarshalerJSONArray) {
+ if v.IsNil() {
+ enc.grow(3)
+ r := enc.getPreviousRune()
+ if r != '[' {
+ enc.writeByte(',')
+ }
+ enc.writeByte('[')
+ enc.writeByte(']')
+ return
+ }
+ enc.grow(100)
+ r := enc.getPreviousRune()
+ if r != '[' {
+ enc.writeByte(',')
+ }
+ enc.writeByte('[')
+ v.MarshalJSONArray(enc)
+ enc.writeByte(']')
+}
+
+// ArrayOmitEmpty adds an array or slice to be encoded, must be used inside a slice or array encoding (does not encode a key)
+// value must implement Marshaler
+func (enc *Encoder) ArrayOmitEmpty(v MarshalerJSONArray) {
+ if v.IsNil() {
+ return
+ }
+ enc.grow(4)
+ r := enc.getPreviousRune()
+ if r != '[' {
+ enc.writeByte(',')
+ }
+ enc.writeByte('[')
+ v.MarshalJSONArray(enc)
+ enc.writeByte(']')
+}
+
+// ArrayNullEmpty adds an array or slice to be encoded, must be used inside a slice or array encoding (does not encode a key)
+// value must implement Marshaler
+func (enc *Encoder) ArrayNullEmpty(v MarshalerJSONArray) {
+ enc.grow(4)
+ r := enc.getPreviousRune()
+ if r != '[' {
+ enc.writeByte(',')
+ }
+ if v.IsNil() {
+ enc.writeBytes(nullBytes)
+ return
+ }
+ enc.writeByte('[')
+ v.MarshalJSONArray(enc)
+ enc.writeByte(']')
+}
+
+// ArrayKey adds an array or slice to be encoded, must be used inside an object as it will encode a key
+// value must implement Marshaler
+func (enc *Encoder) ArrayKey(key string, v MarshalerJSONArray) {
+ if enc.hasKeys {
+ if !enc.keyExists(key) {
+ return
+ }
+ }
+ if v.IsNil() {
+ enc.grow(2 + len(key))
+ r := enc.getPreviousRune()
+ if r != '{' {
+ enc.writeByte(',')
+ }
+ enc.writeByte('"')
+ enc.writeStringEscape(key)
+ enc.writeBytes(objKeyArr)
+ enc.writeByte(']')
+ return
+ }
+ enc.grow(5 + len(key))
+ r := enc.getPreviousRune()
+ if r != '{' {
+ enc.writeByte(',')
+ }
+ enc.writeByte('"')
+ enc.writeStringEscape(key)
+ enc.writeBytes(objKeyArr)
+ v.MarshalJSONArray(enc)
+ enc.writeByte(']')
+}
+
+// ArrayKeyOmitEmpty adds an array or slice to be encoded and skips if it is nil.
+// Must be called inside an object as it will encode a key.
+func (enc *Encoder) ArrayKeyOmitEmpty(key string, v MarshalerJSONArray) {
+ if enc.hasKeys {
+ if !enc.keyExists(key) {
+ return
+ }
+ }
+ if v.IsNil() {
+ return
+ }
+ enc.grow(5 + len(key))
+ r := enc.getPreviousRune()
+ if r != '{' {
+ enc.writeByte(',')
+ }
+ enc.writeByte('"')
+ enc.writeStringEscape(key)
+ enc.writeBytes(objKeyArr)
+ v.MarshalJSONArray(enc)
+ enc.writeByte(']')
+}
+
+// ArrayKeyNullEmpty adds an array or slice to be encoded and encodes `null`` if it is nil.
+// Must be called inside an object as it will encode a key.
+func (enc *Encoder) ArrayKeyNullEmpty(key string, v MarshalerJSONArray) {
+ if enc.hasKeys {
+ if !enc.keyExists(key) {
+ return
+ }
+ }
+ enc.grow(5 + len(key))
+ r := enc.getPreviousRune()
+ if r != '{' {
+ enc.writeByte(',')
+ }
+ if v.IsNil() {
+ enc.writeBytes(nullBytes)
+ return
+ }
+ enc.writeByte('"')
+ enc.writeStringEscape(key)
+ enc.writeBytes(objKeyArr)
+ v.MarshalJSONArray(enc)
+ enc.writeByte(']')
+}
+
+// EncodeArrayFunc is a custom func type implementing MarshaleArray.
+// Use it to cast a func(*Encoder) to Marshal an object.
+//
+// enc := gojay.NewEncoder(io.Writer)
+// enc.EncodeArray(gojay.EncodeArrayFunc(func(enc *gojay.Encoder) {
+// enc.AddStringKey("hello", "world")
+// }))
+type EncodeArrayFunc func(*Encoder)
+
+// MarshalJSONArray implements MarshalerJSONArray.
+func (f EncodeArrayFunc) MarshalJSONArray(enc *Encoder) {
+ f(enc)
+}
+
+// IsNil implements MarshalerJSONArray.
+func (f EncodeArrayFunc) IsNil() bool {
+ return f == nil
+}
diff --git a/vendor/github.com/francoispqt/gojay/encode_bool.go b/vendor/github.com/francoispqt/gojay/encode_bool.go
new file mode 100644
index 0000000000..253e037893
--- /dev/null
+++ b/vendor/github.com/francoispqt/gojay/encode_bool.go
@@ -0,0 +1,164 @@
+package gojay
+
+import "strconv"
+
+// EncodeBool encodes a bool to JSON
+func (enc *Encoder) EncodeBool(v bool) error {
+ if enc.isPooled == 1 {
+ panic(InvalidUsagePooledEncoderError("Invalid usage of pooled encoder"))
+ }
+ _, _ = enc.encodeBool(v)
+ _, err := enc.Write()
+ if err != nil {
+ enc.err = err
+ return err
+ }
+ return nil
+}
+
+// encodeBool encodes a bool to JSON
+func (enc *Encoder) encodeBool(v bool) ([]byte, error) {
+ enc.grow(5)
+ if v {
+ enc.writeString("true")
+ } else {
+ enc.writeString("false")
+ }
+ return enc.buf, enc.err
+}
+
+// AddBool adds a bool to be encoded, must be used inside a slice or array encoding (does not encode a key)
+func (enc *Encoder) AddBool(v bool) {
+ enc.Bool(v)
+}
+
+// AddBoolOmitEmpty adds a bool to be encoded, must be used inside a slice or array encoding (does not encode a key)
+func (enc *Encoder) AddBoolOmitEmpty(v bool) {
+ enc.BoolOmitEmpty(v)
+}
+
+// AddBoolNullEmpty adds a bool to be encoded, must be used inside a slice or array encoding (does not encode a key)
+func (enc *Encoder) AddBoolNullEmpty(v bool) {
+ enc.BoolNullEmpty(v)
+}
+
+// AddBoolKey adds a bool to be encoded, must be used inside an object as it will encode a key.
+func (enc *Encoder) AddBoolKey(key string, v bool) {
+ enc.BoolKey(key, v)
+}
+
+// AddBoolKeyOmitEmpty adds a bool to be encoded and skips if it is zero value.
+// Must be used inside an object as it will encode a key.
+func (enc *Encoder) AddBoolKeyOmitEmpty(key string, v bool) {
+ enc.BoolKeyOmitEmpty(key, v)
+}
+
+// AddBoolKeyNullEmpty adds a bool to be encoded and encodes `null` if it is zero value.
+// Must be used inside an object as it will encode a key.
+func (enc *Encoder) AddBoolKeyNullEmpty(key string, v bool) {
+ enc.BoolKeyNullEmpty(key, v)
+}
+
+// Bool adds a bool to be encoded, must be used inside a slice or array encoding (does not encode a key)
+func (enc *Encoder) Bool(v bool) {
+ enc.grow(5)
+ r := enc.getPreviousRune()
+ if r != '[' {
+ enc.writeByte(',')
+ }
+ if v {
+ enc.writeString("true")
+ } else {
+ enc.writeString("false")
+ }
+}
+
+// BoolOmitEmpty adds a bool to be encoded, must be used inside a slice or array encoding (does not encode a key)
+func (enc *Encoder) BoolOmitEmpty(v bool) {
+ if v == false {
+ return
+ }
+ enc.grow(5)
+ r := enc.getPreviousRune()
+ if r != '[' {
+ enc.writeByte(',')
+ }
+ enc.writeString("true")
+}
+
+// BoolNullEmpty adds a bool to be encoded, must be used inside a slice or array encoding (does not encode a key)
+func (enc *Encoder) BoolNullEmpty(v bool) {
+ enc.grow(5)
+ r := enc.getPreviousRune()
+ if r != '[' {
+ enc.writeByte(',')
+ }
+ if v == false {
+ enc.writeBytes(nullBytes)
+ return
+ }
+ enc.writeString("true")
+}
+
+// BoolKey adds a bool to be encoded, must be used inside an object as it will encode a key.
+func (enc *Encoder) BoolKey(key string, value bool) {
+ if enc.hasKeys {
+ if !enc.keyExists(key) {
+ return
+ }
+ }
+ enc.grow(5 + len(key))
+ r := enc.getPreviousRune()
+ if r != '{' {
+ enc.writeByte(',')
+ }
+ enc.writeByte('"')
+ enc.writeStringEscape(key)
+ enc.writeBytes(objKey)
+ enc.buf = strconv.AppendBool(enc.buf, value)
+}
+
+// BoolKeyOmitEmpty adds a bool to be encoded and skips it if it is zero value.
+// Must be used inside an object as it will encode a key.
+func (enc *Encoder) BoolKeyOmitEmpty(key string, v bool) {
+ if enc.hasKeys {
+ if !enc.keyExists(key) {
+ return
+ }
+ }
+ if v == false {
+ return
+ }
+ enc.grow(5 + len(key))
+ r := enc.getPreviousRune()
+ if r != '{' {
+ enc.writeByte(',')
+ }
+ enc.writeByte('"')
+ enc.writeStringEscape(key)
+ enc.writeBytes(objKey)
+ enc.buf = strconv.AppendBool(enc.buf, v)
+}
+
+// BoolKeyNullEmpty adds a bool to be encoded and skips it if it is zero value.
+// Must be used inside an object as it will encode a key.
+func (enc *Encoder) BoolKeyNullEmpty(key string, v bool) {
+ if enc.hasKeys {
+ if !enc.keyExists(key) {
+ return
+ }
+ }
+ enc.grow(5 + len(key))
+ r := enc.getPreviousRune()
+ if r != '{' {
+ enc.writeByte(',')
+ }
+ enc.writeByte('"')
+ enc.writeStringEscape(key)
+ enc.writeBytes(objKey)
+ if v == false {
+ enc.writeBytes(nullBytes)
+ return
+ }
+ enc.buf = strconv.AppendBool(enc.buf, v)
+}
diff --git a/vendor/github.com/francoispqt/gojay/encode_builder.go b/vendor/github.com/francoispqt/gojay/encode_builder.go
new file mode 100644
index 0000000000..2895ba34a1
--- /dev/null
+++ b/vendor/github.com/francoispqt/gojay/encode_builder.go
@@ -0,0 +1,65 @@
+package gojay
+
+const hex = "0123456789abcdef"
+
+// grow grows b's capacity, if necessary, to guarantee space for
+// another n bytes. After grow(n), at least n bytes can be written to b
+// without another allocation. If n is negative, grow panics.
+func (enc *Encoder) grow(n int) {
+ if cap(enc.buf)-len(enc.buf) < n {
+ Buf := make([]byte, len(enc.buf), 2*cap(enc.buf)+n)
+ copy(Buf, enc.buf)
+ enc.buf = Buf
+ }
+}
+
+// Write appends the contents of p to b's Buffer.
+// Write always returns len(p), nil.
+func (enc *Encoder) writeBytes(p []byte) {
+ enc.buf = append(enc.buf, p...)
+}
+
+func (enc *Encoder) writeTwoBytes(b1 byte, b2 byte) {
+ enc.buf = append(enc.buf, b1, b2)
+}
+
+// WriteByte appends the byte c to b's Buffer.
+// The returned error is always nil.
+func (enc *Encoder) writeByte(c byte) {
+ enc.buf = append(enc.buf, c)
+}
+
+// WriteString appends the contents of s to b's Buffer.
+// It returns the length of s and a nil error.
+func (enc *Encoder) writeString(s string) {
+ enc.buf = append(enc.buf, s...)
+}
+
+func (enc *Encoder) writeStringEscape(s string) {
+ l := len(s)
+ for i := 0; i < l; i++ {
+ c := s[i]
+ if c >= 0x20 && c != '\\' && c != '"' {
+ enc.writeByte(c)
+ continue
+ }
+ switch c {
+ case '\\', '"':
+ enc.writeTwoBytes('\\', c)
+ case '\n':
+ enc.writeTwoBytes('\\', 'n')
+ case '\f':
+ enc.writeTwoBytes('\\', 'f')
+ case '\b':
+ enc.writeTwoBytes('\\', 'b')
+ case '\r':
+ enc.writeTwoBytes('\\', 'r')
+ case '\t':
+ enc.writeTwoBytes('\\', 't')
+ default:
+ enc.writeString(`\u00`)
+ enc.writeTwoBytes(hex[c>>4], hex[c&0xF])
+ }
+ continue
+ }
+}
diff --git a/vendor/github.com/francoispqt/gojay/encode_embedded_json.go b/vendor/github.com/francoispqt/gojay/encode_embedded_json.go
new file mode 100644
index 0000000000..4c99a05789
--- /dev/null
+++ b/vendor/github.com/francoispqt/gojay/encode_embedded_json.go
@@ -0,0 +1,93 @@
+package gojay
+
+// EncodeEmbeddedJSON encodes an embedded JSON.
+// is basically sets the internal buf as the value pointed by v and calls the io.Writer.Write()
+func (enc *Encoder) EncodeEmbeddedJSON(v *EmbeddedJSON) error {
+ if enc.isPooled == 1 {
+ panic(InvalidUsagePooledEncoderError("Invalid usage of pooled encoder"))
+ }
+ enc.buf = *v
+ _, err := enc.Write()
+ if err != nil {
+ return err
+ }
+ return nil
+}
+
+func (enc *Encoder) encodeEmbeddedJSON(v *EmbeddedJSON) ([]byte, error) {
+ enc.writeBytes(*v)
+ return enc.buf, nil
+}
+
+// AddEmbeddedJSON adds an EmbeddedJSON to be encoded.
+//
+// It basically blindly writes the bytes to the final buffer. Therefore,
+// it expects the JSON to be of proper format.
+func (enc *Encoder) AddEmbeddedJSON(v *EmbeddedJSON) {
+ enc.grow(len(*v) + 4)
+ r := enc.getPreviousRune()
+ if r != '[' {
+ enc.writeByte(',')
+ }
+ enc.writeBytes(*v)
+}
+
+// AddEmbeddedJSONOmitEmpty adds an EmbeddedJSON to be encoded or skips it if nil pointer or empty.
+//
+// It basically blindly writes the bytes to the final buffer. Therefore,
+// it expects the JSON to be of proper format.
+func (enc *Encoder) AddEmbeddedJSONOmitEmpty(v *EmbeddedJSON) {
+ if v == nil || len(*v) == 0 {
+ return
+ }
+ r := enc.getPreviousRune()
+ if r != '[' {
+ enc.writeByte(',')
+ }
+ enc.writeBytes(*v)
+}
+
+// AddEmbeddedJSONKey adds an EmbeddedJSON and a key to be encoded.
+//
+// It basically blindly writes the bytes to the final buffer. Therefore,
+// it expects the JSON to be of proper format.
+func (enc *Encoder) AddEmbeddedJSONKey(key string, v *EmbeddedJSON) {
+ if enc.hasKeys {
+ if !enc.keyExists(key) {
+ return
+ }
+ }
+ enc.grow(len(key) + len(*v) + 5)
+ r := enc.getPreviousRune()
+ if r != '{' {
+ enc.writeByte(',')
+ }
+ enc.writeByte('"')
+ enc.writeStringEscape(key)
+ enc.writeBytes(objKey)
+ enc.writeBytes(*v)
+}
+
+// AddEmbeddedJSONKeyOmitEmpty adds an EmbeddedJSON and a key to be encoded or skips it if nil pointer or empty.
+//
+// It basically blindly writes the bytes to the final buffer. Therefore,
+// it expects the JSON to be of proper format.
+func (enc *Encoder) AddEmbeddedJSONKeyOmitEmpty(key string, v *EmbeddedJSON) {
+ if enc.hasKeys {
+ if !enc.keyExists(key) {
+ return
+ }
+ }
+ if v == nil || len(*v) == 0 {
+ return
+ }
+ enc.grow(len(key) + len(*v) + 5)
+ r := enc.getPreviousRune()
+ if r != '{' {
+ enc.writeByte(',')
+ }
+ enc.writeByte('"')
+ enc.writeStringEscape(key)
+ enc.writeBytes(objKey)
+ enc.writeBytes(*v)
+}
diff --git a/vendor/github.com/francoispqt/gojay/encode_interface.go b/vendor/github.com/francoispqt/gojay/encode_interface.go
new file mode 100644
index 0000000000..c4692e5fce
--- /dev/null
+++ b/vendor/github.com/francoispqt/gojay/encode_interface.go
@@ -0,0 +1,173 @@
+package gojay
+
+import (
+ "fmt"
+)
+
+// Encode encodes a value to JSON.
+//
+// If Encode cannot find a way to encode the type to JSON
+// it will return an InvalidMarshalError.
+func (enc *Encoder) Encode(v interface{}) error {
+ if enc.isPooled == 1 {
+ panic(InvalidUsagePooledEncoderError("Invalid usage of pooled encoder"))
+ }
+ switch vt := v.(type) {
+ case string:
+ return enc.EncodeString(vt)
+ case bool:
+ return enc.EncodeBool(vt)
+ case MarshalerJSONArray:
+ return enc.EncodeArray(vt)
+ case MarshalerJSONObject:
+ return enc.EncodeObject(vt)
+ case int:
+ return enc.EncodeInt(vt)
+ case int64:
+ return enc.EncodeInt64(vt)
+ case int32:
+ return enc.EncodeInt(int(vt))
+ case int8:
+ return enc.EncodeInt(int(vt))
+ case uint64:
+ return enc.EncodeUint64(vt)
+ case uint32:
+ return enc.EncodeInt(int(vt))
+ case uint16:
+ return enc.EncodeInt(int(vt))
+ case uint8:
+ return enc.EncodeInt(int(vt))
+ case float64:
+ return enc.EncodeFloat(vt)
+ case float32:
+ return enc.EncodeFloat32(vt)
+ case *EmbeddedJSON:
+ return enc.EncodeEmbeddedJSON(vt)
+ default:
+ return InvalidMarshalError(fmt.Sprintf(invalidMarshalErrorMsg, vt))
+ }
+}
+
+// AddInterface adds an interface{} to be encoded, must be used inside a slice or array encoding (does not encode a key)
+func (enc *Encoder) AddInterface(value interface{}) {
+ switch vt := value.(type) {
+ case string:
+ enc.AddString(vt)
+ case bool:
+ enc.AddBool(vt)
+ case MarshalerJSONArray:
+ enc.AddArray(vt)
+ case MarshalerJSONObject:
+ enc.AddObject(vt)
+ case int:
+ enc.AddInt(vt)
+ case int64:
+ enc.AddInt(int(vt))
+ case int32:
+ enc.AddInt(int(vt))
+ case int8:
+ enc.AddInt(int(vt))
+ case uint64:
+ enc.AddUint64(vt)
+ case uint32:
+ enc.AddInt(int(vt))
+ case uint16:
+ enc.AddInt(int(vt))
+ case uint8:
+ enc.AddInt(int(vt))
+ case float64:
+ enc.AddFloat(vt)
+ case float32:
+ enc.AddFloat32(vt)
+ default:
+ if vt != nil {
+ enc.err = InvalidMarshalError(fmt.Sprintf(invalidMarshalErrorMsg, vt))
+ return
+ }
+ return
+ }
+}
+
+// AddInterfaceKey adds an interface{} to be encoded, must be used inside an object as it will encode a key
+func (enc *Encoder) AddInterfaceKey(key string, value interface{}) {
+ switch vt := value.(type) {
+ case string:
+ enc.AddStringKey(key, vt)
+ case bool:
+ enc.AddBoolKey(key, vt)
+ case MarshalerJSONArray:
+ enc.AddArrayKey(key, vt)
+ case MarshalerJSONObject:
+ enc.AddObjectKey(key, vt)
+ case int:
+ enc.AddIntKey(key, vt)
+ case int64:
+ enc.AddIntKey(key, int(vt))
+ case int32:
+ enc.AddIntKey(key, int(vt))
+ case int16:
+ enc.AddIntKey(key, int(vt))
+ case int8:
+ enc.AddIntKey(key, int(vt))
+ case uint64:
+ enc.AddIntKey(key, int(vt))
+ case uint32:
+ enc.AddIntKey(key, int(vt))
+ case uint16:
+ enc.AddIntKey(key, int(vt))
+ case uint8:
+ enc.AddIntKey(key, int(vt))
+ case float64:
+ enc.AddFloatKey(key, vt)
+ case float32:
+ enc.AddFloat32Key(key, vt)
+ default:
+ if vt != nil {
+ enc.err = InvalidMarshalError(fmt.Sprintf(invalidMarshalErrorMsg, vt))
+ return
+ }
+ return
+ }
+}
+
+// AddInterfaceKeyOmitEmpty adds an interface{} to be encoded, must be used inside an object as it will encode a key
+func (enc *Encoder) AddInterfaceKeyOmitEmpty(key string, v interface{}) {
+ switch vt := v.(type) {
+ case string:
+ enc.AddStringKeyOmitEmpty(key, vt)
+ case bool:
+ enc.AddBoolKeyOmitEmpty(key, vt)
+ case MarshalerJSONArray:
+ enc.AddArrayKeyOmitEmpty(key, vt)
+ case MarshalerJSONObject:
+ enc.AddObjectKeyOmitEmpty(key, vt)
+ case int:
+ enc.AddIntKeyOmitEmpty(key, vt)
+ case int64:
+ enc.AddIntKeyOmitEmpty(key, int(vt))
+ case int32:
+ enc.AddIntKeyOmitEmpty(key, int(vt))
+ case int16:
+ enc.AddIntKeyOmitEmpty(key, int(vt))
+ case int8:
+ enc.AddIntKeyOmitEmpty(key, int(vt))
+ case uint64:
+ enc.AddIntKeyOmitEmpty(key, int(vt))
+ case uint32:
+ enc.AddIntKeyOmitEmpty(key, int(vt))
+ case uint16:
+ enc.AddIntKeyOmitEmpty(key, int(vt))
+ case uint8:
+ enc.AddIntKeyOmitEmpty(key, int(vt))
+ case float64:
+ enc.AddFloatKeyOmitEmpty(key, vt)
+ case float32:
+ enc.AddFloat32KeyOmitEmpty(key, vt)
+ default:
+ if vt != nil {
+ enc.err = InvalidMarshalError(fmt.Sprintf(invalidMarshalErrorMsg, vt))
+ return
+ }
+ return
+ }
+}
diff --git a/vendor/github.com/francoispqt/gojay/encode_null.go b/vendor/github.com/francoispqt/gojay/encode_null.go
new file mode 100644
index 0000000000..cec4e639a0
--- /dev/null
+++ b/vendor/github.com/francoispqt/gojay/encode_null.go
@@ -0,0 +1,39 @@
+package gojay
+
+// AddNull adds a `null` to be encoded. Must be used while encoding an array.`
+func (enc *Encoder) AddNull() {
+ enc.Null()
+}
+
+// Null adds a `null` to be encoded. Must be used while encoding an array.`
+func (enc *Encoder) Null() {
+ enc.grow(5)
+ r := enc.getPreviousRune()
+ if r != '[' {
+ enc.writeByte(',')
+ }
+ enc.writeBytes(nullBytes)
+}
+
+// AddNullKey adds a `null` to be encoded. Must be used while encoding an array.`
+func (enc *Encoder) AddNullKey(key string) {
+ enc.NullKey(key)
+}
+
+// NullKey adds a `null` to be encoded. Must be used while encoding an array.`
+func (enc *Encoder) NullKey(key string) {
+ if enc.hasKeys {
+ if !enc.keyExists(key) {
+ return
+ }
+ }
+ enc.grow(5 + len(key))
+ r := enc.getPreviousRune()
+ if r != '{' {
+ enc.writeByte(',')
+ }
+ enc.writeByte('"')
+ enc.writeStringEscape(key)
+ enc.writeBytes(objKey)
+ enc.writeBytes(nullBytes)
+}
diff --git a/vendor/github.com/francoispqt/gojay/encode_number.go b/vendor/github.com/francoispqt/gojay/encode_number.go
new file mode 100644
index 0000000000..53affb903f
--- /dev/null
+++ b/vendor/github.com/francoispqt/gojay/encode_number.go
@@ -0,0 +1 @@
+package gojay
diff --git a/vendor/github.com/francoispqt/gojay/encode_number_float.go b/vendor/github.com/francoispqt/gojay/encode_number_float.go
new file mode 100644
index 0000000000..b45f8442ab
--- /dev/null
+++ b/vendor/github.com/francoispqt/gojay/encode_number_float.go
@@ -0,0 +1,368 @@
+package gojay
+
+import "strconv"
+
+// EncodeFloat encodes a float64 to JSON
+func (enc *Encoder) EncodeFloat(n float64) error {
+ if enc.isPooled == 1 {
+ panic(InvalidUsagePooledEncoderError("Invalid usage of pooled encoder"))
+ }
+ _, _ = enc.encodeFloat(n)
+ _, err := enc.Write()
+ if err != nil {
+ return err
+ }
+ return nil
+}
+
+// encodeFloat encodes a float64 to JSON
+func (enc *Encoder) encodeFloat(n float64) ([]byte, error) {
+ enc.buf = strconv.AppendFloat(enc.buf, n, 'f', -1, 64)
+ return enc.buf, nil
+}
+
+// EncodeFloat32 encodes a float32 to JSON
+func (enc *Encoder) EncodeFloat32(n float32) error {
+ if enc.isPooled == 1 {
+ panic(InvalidUsagePooledEncoderError("Invalid usage of pooled encoder"))
+ }
+ _, _ = enc.encodeFloat32(n)
+ _, err := enc.Write()
+ if err != nil {
+ return err
+ }
+ return nil
+}
+
+func (enc *Encoder) encodeFloat32(n float32) ([]byte, error) {
+ enc.buf = strconv.AppendFloat(enc.buf, float64(n), 'f', -1, 32)
+ return enc.buf, nil
+}
+
+// AddFloat adds a float64 to be encoded, must be used inside a slice or array encoding (does not encode a key)
+func (enc *Encoder) AddFloat(v float64) {
+ enc.Float64(v)
+}
+
+// AddFloatOmitEmpty adds a float64 to be encoded and skips it if its value is 0,
+// must be used inside a slice or array encoding (does not encode a key).
+func (enc *Encoder) AddFloatOmitEmpty(v float64) {
+ enc.Float64OmitEmpty(v)
+}
+
+// AddFloatNullEmpty adds a float64 to be encoded and skips it if its value is 0,
+// must be used inside a slice or array encoding (does not encode a key).
+func (enc *Encoder) AddFloatNullEmpty(v float64) {
+ enc.Float64NullEmpty(v)
+}
+
+// Float adds a float64 to be encoded, must be used inside a slice or array encoding (does not encode a key)
+func (enc *Encoder) Float(v float64) {
+ enc.Float64(v)
+}
+
+// FloatOmitEmpty adds a float64 to be encoded and skips it if its value is 0,
+// must be used inside a slice or array encoding (does not encode a key).
+func (enc *Encoder) FloatOmitEmpty(v float64) {
+ enc.Float64OmitEmpty(v)
+}
+
+// FloatNullEmpty adds a float64 to be encoded and skips it if its value is 0,
+// must be used inside a slice or array encoding (does not encode a key).
+func (enc *Encoder) FloatNullEmpty(v float64) {
+ enc.Float64NullEmpty(v)
+}
+
+// AddFloatKey adds a float64 to be encoded, must be used inside an object as it will encode a key
+func (enc *Encoder) AddFloatKey(key string, v float64) {
+ enc.Float64Key(key, v)
+}
+
+// AddFloatKeyOmitEmpty adds a float64 to be encoded and skips it if its value is 0.
+// Must be used inside an object as it will encode a key
+func (enc *Encoder) AddFloatKeyOmitEmpty(key string, v float64) {
+ enc.Float64KeyOmitEmpty(key, v)
+}
+
+// AddFloatKeyNullEmpty adds a float64 to be encoded and skips it if its value is 0.
+// Must be used inside an object as it will encode a key
+func (enc *Encoder) AddFloatKeyNullEmpty(key string, v float64) {
+ enc.Float64KeyNullEmpty(key, v)
+}
+
+// FloatKey adds a float64 to be encoded, must be used inside an object as it will encode a key
+func (enc *Encoder) FloatKey(key string, v float64) {
+ enc.Float64Key(key, v)
+}
+
+// FloatKeyOmitEmpty adds a float64 to be encoded and skips it if its value is 0.
+// Must be used inside an object as it will encode a key
+func (enc *Encoder) FloatKeyOmitEmpty(key string, v float64) {
+ enc.Float64KeyOmitEmpty(key, v)
+}
+
+// FloatKeyNullEmpty adds a float64 to be encoded and skips it if its value is 0.
+// Must be used inside an object as it will encode a key
+func (enc *Encoder) FloatKeyNullEmpty(key string, v float64) {
+ enc.Float64KeyNullEmpty(key, v)
+}
+
+// AddFloat64 adds a float64 to be encoded, must be used inside a slice or array encoding (does not encode a key)
+func (enc *Encoder) AddFloat64(v float64) {
+ enc.Float(v)
+}
+
+// AddFloat64OmitEmpty adds a float64 to be encoded and skips it if its value is 0,
+// must be used inside a slice or array encoding (does not encode a key).
+func (enc *Encoder) AddFloat64OmitEmpty(v float64) {
+ enc.FloatOmitEmpty(v)
+}
+
+// Float64 adds a float64 to be encoded, must be used inside a slice or array encoding (does not encode a key)
+func (enc *Encoder) Float64(v float64) {
+ enc.grow(10)
+ r := enc.getPreviousRune()
+ if r != '[' {
+ enc.writeByte(',')
+ }
+ enc.buf = strconv.AppendFloat(enc.buf, v, 'f', -1, 64)
+}
+
+// Float64OmitEmpty adds a float64 to be encoded and skips it if its value is 0,
+// must be used inside a slice or array encoding (does not encode a key).
+func (enc *Encoder) Float64OmitEmpty(v float64) {
+ if v == 0 {
+ return
+ }
+ enc.grow(10)
+ r := enc.getPreviousRune()
+ if r != '[' {
+ enc.writeByte(',')
+ }
+ enc.buf = strconv.AppendFloat(enc.buf, v, 'f', -1, 64)
+}
+
+// Float64NullEmpty adds a float64 to be encoded and skips it if its value is 0,
+// must be used inside a slice or array encoding (does not encode a key).
+func (enc *Encoder) Float64NullEmpty(v float64) {
+ enc.grow(10)
+ r := enc.getPreviousRune()
+ if r != '[' {
+ enc.writeByte(',')
+ }
+ if v == 0 {
+ enc.writeBytes(nullBytes)
+ return
+ }
+ enc.buf = strconv.AppendFloat(enc.buf, v, 'f', -1, 64)
+}
+
+// AddFloat64Key adds a float64 to be encoded, must be used inside an object as it will encode a key
+func (enc *Encoder) AddFloat64Key(key string, v float64) {
+ enc.FloatKey(key, v)
+}
+
+// AddFloat64KeyOmitEmpty adds a float64 to be encoded and skips it if its value is 0.
+// Must be used inside an object as it will encode a key
+func (enc *Encoder) AddFloat64KeyOmitEmpty(key string, v float64) {
+ enc.FloatKeyOmitEmpty(key, v)
+}
+
+// Float64Key adds a float64 to be encoded, must be used inside an object as it will encode a key
+func (enc *Encoder) Float64Key(key string, value float64) {
+ if enc.hasKeys {
+ if !enc.keyExists(key) {
+ return
+ }
+ }
+ r := enc.getPreviousRune()
+ if r != '{' {
+ enc.writeByte(',')
+ }
+ enc.grow(10)
+ enc.writeByte('"')
+ enc.writeStringEscape(key)
+ enc.writeBytes(objKey)
+ enc.buf = strconv.AppendFloat(enc.buf, value, 'f', -1, 64)
+}
+
+// Float64KeyOmitEmpty adds a float64 to be encoded and skips it if its value is 0.
+// Must be used inside an object as it will encode a key
+func (enc *Encoder) Float64KeyOmitEmpty(key string, v float64) {
+ if enc.hasKeys {
+ if !enc.keyExists(key) {
+ return
+ }
+ }
+ if v == 0 {
+ return
+ }
+ enc.grow(10 + len(key))
+ r := enc.getPreviousRune()
+ if r != '{' {
+ enc.writeByte(',')
+ }
+ enc.writeByte('"')
+ enc.writeStringEscape(key)
+ enc.writeBytes(objKey)
+ enc.buf = strconv.AppendFloat(enc.buf, v, 'f', -1, 64)
+}
+
+// Float64KeyNullEmpty adds a float64 to be encoded and skips it if its value is 0,
+// must be used inside a slice or array encoding (does not encode a key).
+func (enc *Encoder) Float64KeyNullEmpty(key string, v float64) {
+ if enc.hasKeys {
+ if !enc.keyExists(key) {
+ return
+ }
+ }
+ enc.grow(10 + len(key))
+ r := enc.getPreviousRune()
+ if r != '{' {
+ enc.writeByte(',')
+ }
+ enc.writeByte('"')
+ enc.writeStringEscape(key)
+ enc.writeBytes(objKey)
+ if v == 0 {
+ enc.writeBytes(nullBytes)
+ return
+ }
+ enc.buf = strconv.AppendFloat(enc.buf, v, 'f', -1, 64)
+}
+
+// AddFloat32 adds a float32 to be encoded, must be used inside a slice or array encoding (does not encode a key)
+func (enc *Encoder) AddFloat32(v float32) {
+ enc.Float32(v)
+}
+
+// AddFloat32OmitEmpty adds an int to be encoded and skips it if its value is 0,
+// must be used inside a slice or array encoding (does not encode a key).
+func (enc *Encoder) AddFloat32OmitEmpty(v float32) {
+ enc.Float32OmitEmpty(v)
+}
+
+// AddFloat32NullEmpty adds an int to be encoded and skips it if its value is 0,
+// must be used inside a slice or array encoding (does not encode a key).
+func (enc *Encoder) AddFloat32NullEmpty(v float32) {
+ enc.Float32NullEmpty(v)
+}
+
+// Float32 adds a float32 to be encoded, must be used inside a slice or array encoding (does not encode a key)
+func (enc *Encoder) Float32(v float32) {
+ r := enc.getPreviousRune()
+ if r != '[' {
+ enc.writeByte(',')
+ }
+ enc.buf = strconv.AppendFloat(enc.buf, float64(v), 'f', -1, 32)
+}
+
+// Float32OmitEmpty adds an int to be encoded and skips it if its value is 0,
+// must be used inside a slice or array encoding (does not encode a key).
+func (enc *Encoder) Float32OmitEmpty(v float32) {
+ if v == 0 {
+ return
+ }
+ enc.grow(10)
+ r := enc.getPreviousRune()
+ if r != '[' {
+ enc.writeByte(',')
+ }
+ enc.buf = strconv.AppendFloat(enc.buf, float64(v), 'f', -1, 32)
+}
+
+// Float32NullEmpty adds an int to be encoded and skips it if its value is 0,
+// must be used inside a slice or array encoding (does not encode a key).
+func (enc *Encoder) Float32NullEmpty(v float32) {
+ enc.grow(10)
+ r := enc.getPreviousRune()
+ if r != '[' {
+ enc.writeByte(',')
+ }
+ if v == 0 {
+ enc.writeBytes(nullBytes)
+ return
+ }
+ enc.buf = strconv.AppendFloat(enc.buf, float64(v), 'f', -1, 32)
+}
+
+// AddFloat32Key adds a float32 to be encoded, must be used inside an object as it will encode a key
+func (enc *Encoder) AddFloat32Key(key string, v float32) {
+ enc.Float32Key(key, v)
+}
+
+// AddFloat32KeyOmitEmpty adds a float64 to be encoded and skips it if its value is 0.
+// Must be used inside an object as it will encode a key
+func (enc *Encoder) AddFloat32KeyOmitEmpty(key string, v float32) {
+ enc.Float32KeyOmitEmpty(key, v)
+}
+
+// AddFloat32KeyNullEmpty adds a float64 to be encoded and skips it if its value is 0.
+// Must be used inside an object as it will encode a key
+func (enc *Encoder) AddFloat32KeyNullEmpty(key string, v float32) {
+ enc.Float32KeyNullEmpty(key, v)
+}
+
+// Float32Key adds a float32 to be encoded, must be used inside an object as it will encode a key
+func (enc *Encoder) Float32Key(key string, v float32) {
+ if enc.hasKeys {
+ if !enc.keyExists(key) {
+ return
+ }
+ }
+ enc.grow(10 + len(key))
+ r := enc.getPreviousRune()
+ if r != '{' {
+ enc.writeByte(',')
+ }
+ enc.writeByte('"')
+ enc.writeStringEscape(key)
+ enc.writeByte('"')
+ enc.writeByte(':')
+ enc.buf = strconv.AppendFloat(enc.buf, float64(v), 'f', -1, 32)
+}
+
+// Float32KeyOmitEmpty adds a float64 to be encoded and skips it if its value is 0.
+// Must be used inside an object as it will encode a key
+func (enc *Encoder) Float32KeyOmitEmpty(key string, v float32) {
+ if enc.hasKeys {
+ if !enc.keyExists(key) {
+ return
+ }
+ }
+ if v == 0 {
+ return
+ }
+ enc.grow(10 + len(key))
+ r := enc.getPreviousRune()
+ if r != '{' {
+ enc.writeByte(',')
+ }
+ enc.writeByte('"')
+ enc.writeStringEscape(key)
+ enc.writeBytes(objKey)
+ enc.buf = strconv.AppendFloat(enc.buf, float64(v), 'f', -1, 32)
+}
+
+// Float32KeyNullEmpty adds a float64 to be encoded and skips it if its value is 0.
+// Must be used inside an object as it will encode a key
+func (enc *Encoder) Float32KeyNullEmpty(key string, v float32) {
+ if enc.hasKeys {
+ if !enc.keyExists(key) {
+ return
+ }
+ }
+ enc.grow(10 + len(key))
+ r := enc.getPreviousRune()
+ if r != '{' {
+ enc.writeByte(',')
+ }
+ enc.writeByte('"')
+ enc.writeStringEscape(key)
+ enc.writeBytes(objKey)
+ if v == 0 {
+ enc.writeBytes(nullBytes)
+ return
+ }
+ enc.buf = strconv.AppendFloat(enc.buf, float64(v), 'f', -1, 32)
+}
diff --git a/vendor/github.com/francoispqt/gojay/encode_number_int.go b/vendor/github.com/francoispqt/gojay/encode_number_int.go
new file mode 100644
index 0000000000..2c4bbe343d
--- /dev/null
+++ b/vendor/github.com/francoispqt/gojay/encode_number_int.go
@@ -0,0 +1,500 @@
+package gojay
+
+import "strconv"
+
+// EncodeInt encodes an int to JSON
+func (enc *Encoder) EncodeInt(n int) error {
+ if enc.isPooled == 1 {
+ panic(InvalidUsagePooledEncoderError("Invalid usage of pooled encoder"))
+ }
+ _, _ = enc.encodeInt(n)
+ _, err := enc.Write()
+ if err != nil {
+ return err
+ }
+ return nil
+}
+
+// encodeInt encodes an int to JSON
+func (enc *Encoder) encodeInt(n int) ([]byte, error) {
+ enc.buf = strconv.AppendInt(enc.buf, int64(n), 10)
+ return enc.buf, nil
+}
+
+// EncodeInt64 encodes an int64 to JSON
+func (enc *Encoder) EncodeInt64(n int64) error {
+ if enc.isPooled == 1 {
+ panic(InvalidUsagePooledEncoderError("Invalid usage of pooled encoder"))
+ }
+ _, _ = enc.encodeInt64(n)
+ _, err := enc.Write()
+ if err != nil {
+ return err
+ }
+ return nil
+}
+
+// encodeInt64 encodes an int to JSON
+func (enc *Encoder) encodeInt64(n int64) ([]byte, error) {
+ enc.buf = strconv.AppendInt(enc.buf, n, 10)
+ return enc.buf, nil
+}
+
+// AddInt adds an int to be encoded, must be used inside a slice or array encoding (does not encode a key)
+func (enc *Encoder) AddInt(v int) {
+ enc.Int(v)
+}
+
+// AddIntOmitEmpty adds an int to be encoded and skips it if its value is 0,
+// must be used inside a slice or array encoding (does not encode a key).
+func (enc *Encoder) AddIntOmitEmpty(v int) {
+ enc.IntOmitEmpty(v)
+}
+
+// AddIntNullEmpty adds an int to be encoded and skips it if its value is 0,
+// must be used inside a slice or array encoding (does not encode a key).
+func (enc *Encoder) AddIntNullEmpty(v int) {
+ enc.IntNullEmpty(v)
+}
+
+// Int adds an int to be encoded, must be used inside a slice or array encoding (does not encode a key)
+func (enc *Encoder) Int(v int) {
+ enc.grow(10)
+ r := enc.getPreviousRune()
+ if r != '[' {
+ enc.writeByte(',')
+ }
+ enc.buf = strconv.AppendInt(enc.buf, int64(v), 10)
+}
+
+// IntOmitEmpty adds an int to be encoded and skips it if its value is 0,
+// must be used inside a slice or array encoding (does not encode a key).
+func (enc *Encoder) IntOmitEmpty(v int) {
+ if v == 0 {
+ return
+ }
+ enc.grow(10)
+ r := enc.getPreviousRune()
+ if r != '[' {
+ enc.writeByte(',')
+ }
+ enc.buf = strconv.AppendInt(enc.buf, int64(v), 10)
+}
+
+// IntNullEmpty adds an int to be encoded and skips it if its value is 0,
+// must be used inside a slice or array encoding (does not encode a key).
+func (enc *Encoder) IntNullEmpty(v int) {
+ enc.grow(10)
+ r := enc.getPreviousRune()
+ if r != '[' {
+ enc.writeByte(',')
+ }
+ if v == 0 {
+ enc.writeBytes(nullBytes)
+ return
+ }
+ enc.buf = strconv.AppendInt(enc.buf, int64(v), 10)
+}
+
+// AddIntKey adds an int to be encoded, must be used inside an object as it will encode a key
+func (enc *Encoder) AddIntKey(key string, v int) {
+ enc.IntKey(key, v)
+}
+
+// AddIntKeyOmitEmpty adds an int to be encoded and skips it if its value is 0.
+// Must be used inside an object as it will encode a key.
+func (enc *Encoder) AddIntKeyOmitEmpty(key string, v int) {
+ enc.IntKeyOmitEmpty(key, v)
+}
+
+// AddIntKeyNullEmpty adds an int to be encoded and skips it if its value is 0.
+// Must be used inside an object as it will encode a key.
+func (enc *Encoder) AddIntKeyNullEmpty(key string, v int) {
+ enc.IntKeyNullEmpty(key, v)
+}
+
+// IntKey adds an int to be encoded, must be used inside an object as it will encode a key
+func (enc *Encoder) IntKey(key string, v int) {
+ if enc.hasKeys {
+ if !enc.keyExists(key) {
+ return
+ }
+ }
+ enc.grow(10 + len(key))
+ r := enc.getPreviousRune()
+ if r != '{' {
+ enc.writeByte(',')
+ }
+ enc.writeByte('"')
+ enc.writeStringEscape(key)
+ enc.writeBytes(objKey)
+ enc.buf = strconv.AppendInt(enc.buf, int64(v), 10)
+}
+
+// IntKeyOmitEmpty adds an int to be encoded and skips it if its value is 0.
+// Must be used inside an object as it will encode a key.
+func (enc *Encoder) IntKeyOmitEmpty(key string, v int) {
+ if enc.hasKeys {
+ if !enc.keyExists(key) {
+ return
+ }
+ }
+ if v == 0 {
+ return
+ }
+ enc.grow(10 + len(key))
+ r := enc.getPreviousRune()
+ if r != '{' && r != '[' {
+ enc.writeByte(',')
+ }
+ enc.writeByte('"')
+ enc.writeStringEscape(key)
+ enc.writeBytes(objKey)
+ enc.buf = strconv.AppendInt(enc.buf, int64(v), 10)
+}
+
+// IntKeyNullEmpty adds an int to be encoded and skips it if its value is 0.
+// Must be used inside an object as it will encode a key.
+func (enc *Encoder) IntKeyNullEmpty(key string, v int) {
+ if enc.hasKeys {
+ if !enc.keyExists(key) {
+ return
+ }
+ }
+ enc.grow(10 + len(key))
+ r := enc.getPreviousRune()
+ if r != '{' && r != '[' {
+ enc.writeByte(',')
+ }
+ enc.writeByte('"')
+ enc.writeStringEscape(key)
+ enc.writeBytes(objKey)
+ if v == 0 {
+ enc.writeBytes(nullBytes)
+ return
+ }
+ enc.buf = strconv.AppendInt(enc.buf, int64(v), 10)
+}
+
+// AddInt64 adds an int to be encoded, must be used inside a slice or array encoding (does not encode a key)
+func (enc *Encoder) AddInt64(v int64) {
+ enc.Int64(v)
+}
+
+// AddInt64OmitEmpty adds an int to be encoded and skips it if its value is 0,
+// must be used inside a slice or array encoding (does not encode a key).
+func (enc *Encoder) AddInt64OmitEmpty(v int64) {
+ enc.Int64OmitEmpty(v)
+}
+
+// AddInt64NullEmpty adds an int to be encoded and skips it if its value is 0,
+// must be used inside a slice or array encoding (does not encode a key).
+func (enc *Encoder) AddInt64NullEmpty(v int64) {
+ enc.Int64NullEmpty(v)
+}
+
+// Int64 adds an int to be encoded, must be used inside a slice or array encoding (does not encode a key)
+func (enc *Encoder) Int64(v int64) {
+ enc.grow(10)
+ r := enc.getPreviousRune()
+ if r != '[' {
+ enc.writeByte(',')
+ }
+ enc.buf = strconv.AppendInt(enc.buf, v, 10)
+}
+
+// Int64OmitEmpty adds an int to be encoded and skips it if its value is 0,
+// must be used inside a slice or array encoding (does not encode a key).
+func (enc *Encoder) Int64OmitEmpty(v int64) {
+ if v == 0 {
+ return
+ }
+ enc.grow(10)
+ r := enc.getPreviousRune()
+ if r != '[' {
+ enc.writeByte(',')
+ }
+ enc.buf = strconv.AppendInt(enc.buf, v, 10)
+}
+
+// Int64NullEmpty adds an int to be encoded and skips it if its value is 0,
+// must be used inside a slice or array encoding (does not encode a key).
+func (enc *Encoder) Int64NullEmpty(v int64) {
+ enc.grow(10)
+ r := enc.getPreviousRune()
+ if r != '[' {
+ enc.writeByte(',')
+ }
+ if v == 0 {
+ enc.writeBytes(nullBytes)
+ return
+ }
+ enc.buf = strconv.AppendInt(enc.buf, v, 10)
+}
+
+// AddInt64Key adds an int64 to be encoded, must be used inside an object as it will encode a key
+func (enc *Encoder) AddInt64Key(key string, v int64) {
+ enc.Int64Key(key, v)
+}
+
+// AddInt64KeyOmitEmpty adds an int64 to be encoded and skips it if its value is 0.
+// Must be used inside an object as it will encode a key.
+func (enc *Encoder) AddInt64KeyOmitEmpty(key string, v int64) {
+ enc.Int64KeyOmitEmpty(key, v)
+}
+
+// AddInt64KeyNullEmpty adds an int64 to be encoded and skips it if its value is 0.
+// Must be used inside an object as it will encode a key.
+func (enc *Encoder) AddInt64KeyNullEmpty(key string, v int64) {
+ enc.Int64KeyNullEmpty(key, v)
+}
+
+// Int64Key adds an int64 to be encoded, must be used inside an object as it will encode a key
+func (enc *Encoder) Int64Key(key string, v int64) {
+ if enc.hasKeys {
+ if !enc.keyExists(key) {
+ return
+ }
+ }
+ enc.grow(10 + len(key))
+ r := enc.getPreviousRune()
+ if r != '{' {
+ enc.writeByte(',')
+ }
+ enc.writeByte('"')
+ enc.writeStringEscape(key)
+ enc.writeBytes(objKey)
+ enc.buf = strconv.AppendInt(enc.buf, v, 10)
+}
+
+// Int64KeyOmitEmpty adds an int64 to be encoded and skips it if its value is 0.
+// Must be used inside an object as it will encode a key.
+func (enc *Encoder) Int64KeyOmitEmpty(key string, v int64) {
+ if v == 0 {
+ return
+ }
+ enc.grow(10 + len(key))
+ r := enc.getPreviousRune()
+ if r != '{' {
+ enc.writeByte(',')
+ }
+ enc.writeByte('"')
+ enc.writeStringEscape(key)
+ enc.writeBytes(objKey)
+ enc.buf = strconv.AppendInt(enc.buf, v, 10)
+}
+
+// Int64KeyNullEmpty adds an int64 to be encoded and skips it if its value is 0.
+// Must be used inside an object as it will encode a key.
+func (enc *Encoder) Int64KeyNullEmpty(key string, v int64) {
+ if enc.hasKeys {
+ if !enc.keyExists(key) {
+ return
+ }
+ }
+ enc.grow(10 + len(key))
+ r := enc.getPreviousRune()
+ if r != '{' {
+ enc.writeByte(',')
+ }
+ enc.writeByte('"')
+ enc.writeStringEscape(key)
+ enc.writeBytes(objKey)
+ if v == 0 {
+ enc.writeBytes(nullBytes)
+ return
+ }
+ enc.buf = strconv.AppendInt(enc.buf, v, 10)
+}
+
+// AddInt32 adds an int to be encoded, must be used inside a slice or array encoding (does not encode a key)
+func (enc *Encoder) AddInt32(v int32) {
+ enc.Int64(int64(v))
+}
+
+// AddInt32OmitEmpty adds an int to be encoded and skips it if its value is 0,
+// must be used inside a slice or array encoding (does not encode a key).
+func (enc *Encoder) AddInt32OmitEmpty(v int32) {
+ enc.Int64OmitEmpty(int64(v))
+}
+
+// AddInt32NullEmpty adds an int to be encoded and skips it if its value is 0,
+// must be used inside a slice or array encoding (does not encode a key).
+func (enc *Encoder) AddInt32NullEmpty(v int32) {
+ enc.Int64NullEmpty(int64(v))
+}
+
+// Int32 adds an int to be encoded, must be used inside a slice or array encoding (does not encode a key)
+func (enc *Encoder) Int32(v int32) {
+ enc.Int64(int64(v))
+}
+
+// Int32OmitEmpty adds an int to be encoded and skips it if its value is 0,
+// must be used inside a slice or array encoding (does not encode a key).
+func (enc *Encoder) Int32OmitEmpty(v int32) {
+ enc.Int64OmitEmpty(int64(v))
+}
+
+// Int32NullEmpty adds an int to be encoded and skips it if its value is 0,
+// must be used inside a slice or array encoding (does not encode a key).
+func (enc *Encoder) Int32NullEmpty(v int32) {
+ enc.Int64NullEmpty(int64(v))
+}
+
+// AddInt32Key adds an int32 to be encoded, must be used inside an object as it will encode a key
+func (enc *Encoder) AddInt32Key(key string, v int32) {
+ enc.Int64Key(key, int64(v))
+}
+
+// AddInt32KeyOmitEmpty adds an int32 to be encoded and skips it if its value is 0.
+// Must be used inside an object as it will encode a key.
+func (enc *Encoder) AddInt32KeyOmitEmpty(key string, v int32) {
+ enc.Int64KeyOmitEmpty(key, int64(v))
+}
+
+// Int32Key adds an int32 to be encoded, must be used inside an object as it will encode a key
+func (enc *Encoder) Int32Key(key string, v int32) {
+ enc.Int64Key(key, int64(v))
+}
+
+// Int32KeyOmitEmpty adds an int32 to be encoded and skips it if its value is 0.
+// Must be used inside an object as it will encode a key.
+func (enc *Encoder) Int32KeyOmitEmpty(key string, v int32) {
+ enc.Int64KeyOmitEmpty(key, int64(v))
+}
+
+// Int32KeyNullEmpty adds an int32 to be encoded and skips it if its value is 0.
+// Must be used inside an object as it will encode a key.
+func (enc *Encoder) Int32KeyNullEmpty(key string, v int32) {
+ enc.Int64KeyNullEmpty(key, int64(v))
+}
+
+// AddInt16 adds an int to be encoded, must be used inside a slice or array encoding (does not encode a key)
+func (enc *Encoder) AddInt16(v int16) {
+ enc.Int64(int64(v))
+}
+
+// AddInt16OmitEmpty adds an int to be encoded and skips it if its value is 0,
+// must be used inside a slice or array encoding (does not encode a key).
+func (enc *Encoder) AddInt16OmitEmpty(v int16) {
+ enc.Int64OmitEmpty(int64(v))
+}
+
+// Int16 adds an int to be encoded, must be used inside a slice or array encoding (does not encode a key)
+func (enc *Encoder) Int16(v int16) {
+ enc.Int64(int64(v))
+}
+
+// Int16OmitEmpty adds an int to be encoded and skips it if its value is 0,
+// must be used inside a slice or array encoding (does not encode a key).
+func (enc *Encoder) Int16OmitEmpty(v int16) {
+ enc.Int64OmitEmpty(int64(v))
+}
+
+// Int16NullEmpty adds an int to be encoded and skips it if its value is 0,
+// must be used inside a slice or array encoding (does not encode a key).
+func (enc *Encoder) Int16NullEmpty(v int16) {
+ enc.Int64NullEmpty(int64(v))
+}
+
+// AddInt16Key adds an int16 to be encoded, must be used inside an object as it will encode a key
+func (enc *Encoder) AddInt16Key(key string, v int16) {
+ enc.Int64Key(key, int64(v))
+}
+
+// AddInt16KeyOmitEmpty adds an int16 to be encoded and skips it if its value is 0.
+// Must be used inside an object as it will encode a key.
+func (enc *Encoder) AddInt16KeyOmitEmpty(key string, v int16) {
+ enc.Int64KeyOmitEmpty(key, int64(v))
+}
+
+// AddInt16KeyNullEmpty adds an int16 to be encoded and skips it if its value is 0.
+// Must be used inside an object as it will encode a key.
+func (enc *Encoder) AddInt16KeyNullEmpty(key string, v int16) {
+ enc.Int64KeyNullEmpty(key, int64(v))
+}
+
+// Int16Key adds an int16 to be encoded, must be used inside an object as it will encode a key
+func (enc *Encoder) Int16Key(key string, v int16) {
+ enc.Int64Key(key, int64(v))
+}
+
+// Int16KeyOmitEmpty adds an int16 to be encoded and skips it if its value is 0.
+// Must be used inside an object as it will encode a key.
+func (enc *Encoder) Int16KeyOmitEmpty(key string, v int16) {
+ enc.Int64KeyOmitEmpty(key, int64(v))
+}
+
+// Int16KeyNullEmpty adds an int16 to be encoded and skips it if its value is 0.
+// Must be used inside an object as it will encode a key.
+func (enc *Encoder) Int16KeyNullEmpty(key string, v int16) {
+ enc.Int64KeyNullEmpty(key, int64(v))
+}
+
+// AddInt8 adds an int to be encoded, must be used inside a slice or array encoding (does not encode a key)
+func (enc *Encoder) AddInt8(v int8) {
+ enc.Int64(int64(v))
+}
+
+// AddInt8OmitEmpty adds an int to be encoded and skips it if its value is 0,
+// must be used inside a slice or array encoding (does not encode a key).
+func (enc *Encoder) AddInt8OmitEmpty(v int8) {
+ enc.Int64OmitEmpty(int64(v))
+}
+
+// AddInt8NullEmpty adds an int to be encoded and skips it if its value is 0,
+// must be used inside a slice or array encoding (does not encode a key).
+func (enc *Encoder) AddInt8NullEmpty(v int8) {
+ enc.Int64NullEmpty(int64(v))
+}
+
+// Int8 adds an int to be encoded, must be used inside a slice or array encoding (does not encode a key)
+func (enc *Encoder) Int8(v int8) {
+ enc.Int64(int64(v))
+}
+
+// Int8OmitEmpty adds an int to be encoded and skips it if its value is 0,
+// must be used inside a slice or array encoding (does not encode a key).
+func (enc *Encoder) Int8OmitEmpty(v int8) {
+ enc.Int64OmitEmpty(int64(v))
+}
+
+// Int8NullEmpty adds an int to be encoded and skips it if its value is 0,
+// must be used inside a slice or array encoding (does not encode a key).
+func (enc *Encoder) Int8NullEmpty(v int8) {
+ enc.Int64NullEmpty(int64(v))
+}
+
+// AddInt8Key adds an int8 to be encoded, must be used inside an object as it will encode a key
+func (enc *Encoder) AddInt8Key(key string, v int8) {
+ enc.Int64Key(key, int64(v))
+}
+
+// AddInt8KeyOmitEmpty adds an int8 to be encoded and skips it if its value is 0.
+// Must be used inside an object as it will encode a key.
+func (enc *Encoder) AddInt8KeyOmitEmpty(key string, v int8) {
+ enc.Int64KeyOmitEmpty(key, int64(v))
+}
+
+// AddInt8KeyNullEmpty adds an int8 to be encoded and skips it if its value is 0.
+// Must be used inside an object as it will encode a key.
+func (enc *Encoder) AddInt8KeyNullEmpty(key string, v int8) {
+ enc.Int64KeyNullEmpty(key, int64(v))
+}
+
+// Int8Key adds an int8 to be encoded, must be used inside an object as it will encode a key
+func (enc *Encoder) Int8Key(key string, v int8) {
+ enc.Int64Key(key, int64(v))
+}
+
+// Int8KeyOmitEmpty adds an int8 to be encoded and skips it if its value is 0.
+// Must be used inside an object as it will encode a key.
+func (enc *Encoder) Int8KeyOmitEmpty(key string, v int8) {
+ enc.Int64KeyOmitEmpty(key, int64(v))
+}
+
+// Int8KeyNullEmpty adds an int8 to be encoded and skips it if its value is 0.
+// Must be used inside an object as it will encode a key.
+func (enc *Encoder) Int8KeyNullEmpty(key string, v int8) {
+ enc.Int64KeyNullEmpty(key, int64(v))
+}
diff --git a/vendor/github.com/francoispqt/gojay/encode_number_uint.go b/vendor/github.com/francoispqt/gojay/encode_number_uint.go
new file mode 100644
index 0000000000..cd69b13fd1
--- /dev/null
+++ b/vendor/github.com/francoispqt/gojay/encode_number_uint.go
@@ -0,0 +1,362 @@
+package gojay
+
+import "strconv"
+
+// EncodeUint64 encodes an int64 to JSON
+func (enc *Encoder) EncodeUint64(n uint64) error {
+ if enc.isPooled == 1 {
+ panic(InvalidUsagePooledEncoderError("Invalid usage of pooled encoder"))
+ }
+ _, _ = enc.encodeUint64(n)
+ _, err := enc.Write()
+ if err != nil {
+ return err
+ }
+ return nil
+}
+
+// encodeUint64 encodes an int to JSON
+func (enc *Encoder) encodeUint64(n uint64) ([]byte, error) {
+ enc.buf = strconv.AppendUint(enc.buf, n, 10)
+ return enc.buf, nil
+}
+
+// AddUint64 adds an int to be encoded, must be used inside a slice or array encoding (does not encode a key)
+func (enc *Encoder) AddUint64(v uint64) {
+ enc.Uint64(v)
+}
+
+// AddUint64OmitEmpty adds an int to be encoded and skips it if its value is 0,
+// must be used inside a slice or array encoding (does not encode a key).
+func (enc *Encoder) AddUint64OmitEmpty(v uint64) {
+ enc.Uint64OmitEmpty(v)
+}
+
+// AddUint64NullEmpty adds an int to be encoded and skips it if its value is 0,
+// must be used inside a slice or array encoding (does not encode a key).
+func (enc *Encoder) AddUint64NullEmpty(v uint64) {
+ enc.Uint64NullEmpty(v)
+}
+
+// Uint64 adds an int to be encoded, must be used inside a slice or array encoding (does not encode a key)
+func (enc *Encoder) Uint64(v uint64) {
+ enc.grow(10)
+ r := enc.getPreviousRune()
+ if r != '[' {
+ enc.writeByte(',')
+ }
+ enc.buf = strconv.AppendUint(enc.buf, v, 10)
+}
+
+// Uint64OmitEmpty adds an int to be encoded and skips it if its value is 0,
+// must be used inside a slice or array encoding (does not encode a key).
+func (enc *Encoder) Uint64OmitEmpty(v uint64) {
+ if v == 0 {
+ return
+ }
+ enc.grow(10)
+ r := enc.getPreviousRune()
+ if r != '[' {
+ enc.writeByte(',')
+ }
+ enc.buf = strconv.AppendUint(enc.buf, v, 10)
+}
+
+// Uint64NullEmpty adds an int to be encoded and skips it if its value is 0,
+// must be used inside a slice or array encoding (does not encode a key).
+func (enc *Encoder) Uint64NullEmpty(v uint64) {
+ enc.grow(10)
+ r := enc.getPreviousRune()
+ if r != '[' {
+ enc.writeByte(',')
+ }
+ if v == 0 {
+ enc.writeBytes(nullBytes)
+ return
+ }
+ enc.buf = strconv.AppendUint(enc.buf, v, 10)
+}
+
+// AddUint64Key adds an int to be encoded, must be used inside an object as it will encode a key
+func (enc *Encoder) AddUint64Key(key string, v uint64) {
+ enc.Uint64Key(key, v)
+}
+
+// AddUint64KeyOmitEmpty adds an int to be encoded and skips it if its value is 0.
+// Must be used inside an object as it will encode a key.
+func (enc *Encoder) AddUint64KeyOmitEmpty(key string, v uint64) {
+ enc.Uint64KeyOmitEmpty(key, v)
+}
+
+// AddUint64KeyNullEmpty adds an int to be encoded and skips it if its value is 0.
+// Must be used inside an object as it will encode a key.
+func (enc *Encoder) AddUint64KeyNullEmpty(key string, v uint64) {
+ enc.Uint64KeyNullEmpty(key, v)
+}
+
+// Uint64Key adds an int to be encoded, must be used inside an object as it will encode a key
+func (enc *Encoder) Uint64Key(key string, v uint64) {
+ if enc.hasKeys {
+ if !enc.keyExists(key) {
+ return
+ }
+ }
+ enc.grow(10 + len(key))
+ r := enc.getPreviousRune()
+ if r != '{' {
+ enc.writeByte(',')
+ }
+ enc.writeByte('"')
+ enc.writeStringEscape(key)
+ enc.writeBytes(objKey)
+ enc.buf = strconv.AppendUint(enc.buf, v, 10)
+}
+
+// Uint64KeyOmitEmpty adds an int to be encoded and skips it if its value is 0.
+// Must be used inside an object as it will encode a key.
+func (enc *Encoder) Uint64KeyOmitEmpty(key string, v uint64) {
+ if enc.hasKeys {
+ if !enc.keyExists(key) {
+ return
+ }
+ }
+ if v == 0 {
+ return
+ }
+ enc.grow(10 + len(key))
+ r := enc.getPreviousRune()
+ if r != '{' && r != '[' {
+ enc.writeByte(',')
+ }
+ enc.writeByte('"')
+ enc.writeStringEscape(key)
+ enc.writeBytes(objKey)
+ enc.buf = strconv.AppendUint(enc.buf, v, 10)
+}
+
+// Uint64KeyNullEmpty adds an int to be encoded and skips it if its value is 0.
+// Must be used inside an object as it will encode a key.
+func (enc *Encoder) Uint64KeyNullEmpty(key string, v uint64) {
+ if enc.hasKeys {
+ if !enc.keyExists(key) {
+ return
+ }
+ }
+ enc.grow(10 + len(key))
+ r := enc.getPreviousRune()
+ if r != '{' && r != '[' {
+ enc.writeByte(',')
+ }
+ enc.writeByte('"')
+ enc.writeStringEscape(key)
+ enc.writeBytes(objKey)
+ if v == 0 {
+ enc.writeBytes(nullBytes)
+ return
+ }
+ enc.buf = strconv.AppendUint(enc.buf, v, 10)
+}
+
+// AddUint32 adds an int to be encoded, must be used inside a slice or array encoding (does not encode a key)
+func (enc *Encoder) AddUint32(v uint32) {
+ enc.Uint64(uint64(v))
+}
+
+// AddUint32OmitEmpty adds an int to be encoded and skips it if its value is 0,
+// must be used inside a slice or array encoding (does not encode a key).
+func (enc *Encoder) AddUint32OmitEmpty(v uint32) {
+ enc.Uint64OmitEmpty(uint64(v))
+}
+
+// AddUint32NullEmpty adds an int to be encoded and skips it if its value is 0,
+// must be used inside a slice or array encoding (does not encode a key).
+func (enc *Encoder) AddUint32NullEmpty(v uint32) {
+ enc.Uint64NullEmpty(uint64(v))
+}
+
+// Uint32 adds an int to be encoded, must be used inside a slice or array encoding (does not encode a key)
+func (enc *Encoder) Uint32(v uint32) {
+ enc.Uint64(uint64(v))
+}
+
+// Uint32OmitEmpty adds an int to be encoded and skips it if its value is 0,
+// must be used inside a slice or array encoding (does not encode a key).
+func (enc *Encoder) Uint32OmitEmpty(v uint32) {
+ enc.Uint64OmitEmpty(uint64(v))
+}
+
+// Uint32NullEmpty adds an int to be encoded and skips it if its value is 0,
+// must be used inside a slice or array encoding (does not encode a key).
+func (enc *Encoder) Uint32NullEmpty(v uint32) {
+ enc.Uint64NullEmpty(uint64(v))
+}
+
+// AddUint32Key adds an int to be encoded, must be used inside an object as it will encode a key
+func (enc *Encoder) AddUint32Key(key string, v uint32) {
+ enc.Uint64Key(key, uint64(v))
+}
+
+// AddUint32KeyOmitEmpty adds an int to be encoded and skips it if its value is 0.
+// Must be used inside an object as it will encode a key.
+func (enc *Encoder) AddUint32KeyOmitEmpty(key string, v uint32) {
+ enc.Uint64KeyOmitEmpty(key, uint64(v))
+}
+
+// AddUint32KeyNullEmpty adds an int to be encoded and skips it if its value is 0.
+// Must be used inside an object as it will encode a key.
+func (enc *Encoder) AddUint32KeyNullEmpty(key string, v uint32) {
+ enc.Uint64KeyNullEmpty(key, uint64(v))
+}
+
+// Uint32Key adds an int to be encoded, must be used inside an object as it will encode a key
+func (enc *Encoder) Uint32Key(key string, v uint32) {
+ enc.Uint64Key(key, uint64(v))
+}
+
+// Uint32KeyOmitEmpty adds an int to be encoded and skips it if its value is 0.
+// Must be used inside an object as it will encode a key.
+func (enc *Encoder) Uint32KeyOmitEmpty(key string, v uint32) {
+ enc.Uint64KeyOmitEmpty(key, uint64(v))
+}
+
+// Uint32KeyNullEmpty adds an int to be encoded and skips it if its value is 0.
+// Must be used inside an object as it will encode a key.
+func (enc *Encoder) Uint32KeyNullEmpty(key string, v uint32) {
+ enc.Uint64KeyNullEmpty(key, uint64(v))
+}
+
+// AddUint16 adds an int to be encoded, must be used inside a slice or array encoding (does not encode a key)
+func (enc *Encoder) AddUint16(v uint16) {
+ enc.Uint64(uint64(v))
+}
+
+// AddUint16OmitEmpty adds an int to be encoded and skips it if its value is 0,
+// must be used inside a slice or array encoding (does not encode a key).
+func (enc *Encoder) AddUint16OmitEmpty(v uint16) {
+ enc.Uint64OmitEmpty(uint64(v))
+}
+
+// AddUint16NullEmpty adds an int to be encoded and skips it if its value is 0,
+// must be used inside a slice or array encoding (does not encode a key).
+func (enc *Encoder) AddUint16NullEmpty(v uint16) {
+ enc.Uint64NullEmpty(uint64(v))
+}
+
+// Uint16 adds an int to be encoded, must be used inside a slice or array encoding (does not encode a key)
+func (enc *Encoder) Uint16(v uint16) {
+ enc.Uint64(uint64(v))
+}
+
+// Uint16OmitEmpty adds an int to be encoded and skips it if its value is 0,
+// must be used inside a slice or array encoding (does not encode a key).
+func (enc *Encoder) Uint16OmitEmpty(v uint16) {
+ enc.Uint64OmitEmpty(uint64(v))
+}
+
+// Uint16NullEmpty adds an int to be encoded and skips it if its value is 0,
+// must be used inside a slice or array encoding (does not encode a key).
+func (enc *Encoder) Uint16NullEmpty(v uint16) {
+ enc.Uint64NullEmpty(uint64(v))
+}
+
+// AddUint16Key adds an int to be encoded, must be used inside an object as it will encode a key
+func (enc *Encoder) AddUint16Key(key string, v uint16) {
+ enc.Uint64Key(key, uint64(v))
+}
+
+// AddUint16KeyOmitEmpty adds an int to be encoded and skips it if its value is 0.
+// Must be used inside an object as it will encode a key.
+func (enc *Encoder) AddUint16KeyOmitEmpty(key string, v uint16) {
+ enc.Uint64KeyOmitEmpty(key, uint64(v))
+}
+
+// AddUint16KeyNullEmpty adds an int to be encoded and skips it if its value is 0.
+// Must be used inside an object as it will encode a key.
+func (enc *Encoder) AddUint16KeyNullEmpty(key string, v uint16) {
+ enc.Uint64KeyNullEmpty(key, uint64(v))
+}
+
+// Uint16Key adds an int to be encoded, must be used inside an object as it will encode a key
+func (enc *Encoder) Uint16Key(key string, v uint16) {
+ enc.Uint64Key(key, uint64(v))
+}
+
+// Uint16KeyOmitEmpty adds an int to be encoded and skips it if its value is 0.
+// Must be used inside an object as it will encode a key.
+func (enc *Encoder) Uint16KeyOmitEmpty(key string, v uint16) {
+ enc.Uint64KeyOmitEmpty(key, uint64(v))
+}
+
+// Uint16KeyNullEmpty adds an int to be encoded and skips it if its value is 0.
+// Must be used inside an object as it will encode a key.
+func (enc *Encoder) Uint16KeyNullEmpty(key string, v uint16) {
+ enc.Uint64KeyNullEmpty(key, uint64(v))
+}
+
+// AddUint8 adds an int to be encoded, must be used inside a slice or array encoding (does not encode a key)
+func (enc *Encoder) AddUint8(v uint8) {
+ enc.Uint64(uint64(v))
+}
+
+// AddUint8OmitEmpty adds an int to be encoded and skips it if its value is 0,
+// must be used inside a slice or array encoding (does not encode a key).
+func (enc *Encoder) AddUint8OmitEmpty(v uint8) {
+ enc.Uint64OmitEmpty(uint64(v))
+}
+
+// AddUint8NullEmpty adds an int to be encoded and skips it if its value is 0,
+// must be used inside a slice or array encoding (does not encode a key).
+func (enc *Encoder) AddUint8NullEmpty(v uint8) {
+ enc.Uint64NullEmpty(uint64(v))
+}
+
+// Uint8 adds an int to be encoded, must be used inside a slice or array encoding (does not encode a key)
+func (enc *Encoder) Uint8(v uint8) {
+ enc.Uint64(uint64(v))
+}
+
+// Uint8OmitEmpty adds an int to be encoded and skips it if its value is 0,
+// must be used inside a slice or array encoding (does not encode a key).
+func (enc *Encoder) Uint8OmitEmpty(v uint8) {
+ enc.Uint64OmitEmpty(uint64(v))
+}
+
+// Uint8NullEmpty adds an int to be encoded and skips it if its value is 0,
+// must be used inside a slice or array encoding (does not encode a key).
+func (enc *Encoder) Uint8NullEmpty(v uint8) {
+ enc.Uint64NullEmpty(uint64(v))
+}
+
+// AddUint8Key adds an int to be encoded, must be used inside an object as it will encode a key
+func (enc *Encoder) AddUint8Key(key string, v uint8) {
+ enc.Uint64Key(key, uint64(v))
+}
+
+// AddUint8KeyOmitEmpty adds an int to be encoded and skips it if its value is 0.
+// Must be used inside an object as it will encode a key.
+func (enc *Encoder) AddUint8KeyOmitEmpty(key string, v uint8) {
+ enc.Uint64KeyOmitEmpty(key, uint64(v))
+}
+
+// AddUint8KeyNullEmpty adds an int to be encoded and skips it if its value is 0.
+// Must be used inside an object as it will encode a key.
+func (enc *Encoder) AddUint8KeyNullEmpty(key string, v uint8) {
+ enc.Uint64KeyNullEmpty(key, uint64(v))
+}
+
+// Uint8Key adds an int to be encoded, must be used inside an object as it will encode a key
+func (enc *Encoder) Uint8Key(key string, v uint8) {
+ enc.Uint64Key(key, uint64(v))
+}
+
+// Uint8KeyOmitEmpty adds an int to be encoded and skips it if its value is 0.
+// Must be used inside an object as it will encode a key.
+func (enc *Encoder) Uint8KeyOmitEmpty(key string, v uint8) {
+ enc.Uint64KeyOmitEmpty(key, uint64(v))
+}
+
+// Uint8KeyNullEmpty adds an int to be encoded and skips it if its value is 0.
+// Must be used inside an object as it will encode a key.
+func (enc *Encoder) Uint8KeyNullEmpty(key string, v uint8) {
+ enc.Uint64KeyNullEmpty(key, uint64(v))
+}
diff --git a/vendor/github.com/francoispqt/gojay/encode_object.go b/vendor/github.com/francoispqt/gojay/encode_object.go
new file mode 100644
index 0000000000..5f2c8cf3f6
--- /dev/null
+++ b/vendor/github.com/francoispqt/gojay/encode_object.go
@@ -0,0 +1,400 @@
+package gojay
+
+var objKeyStr = []byte(`":"`)
+var objKeyObj = []byte(`":{`)
+var objKeyArr = []byte(`":[`)
+var objKey = []byte(`":`)
+
+// EncodeObject encodes an object to JSON
+func (enc *Encoder) EncodeObject(v MarshalerJSONObject) error {
+ if enc.isPooled == 1 {
+ panic(InvalidUsagePooledEncoderError("Invalid usage of pooled encoder"))
+ }
+ _, err := enc.encodeObject(v)
+ if err != nil {
+ enc.err = err
+ return err
+ }
+ _, err = enc.Write()
+ if err != nil {
+ enc.err = err
+ return err
+ }
+ return nil
+}
+
+// EncodeObjectKeys encodes an object to JSON
+func (enc *Encoder) EncodeObjectKeys(v MarshalerJSONObject, keys []string) error {
+ if enc.isPooled == 1 {
+ panic(InvalidUsagePooledEncoderError("Invalid usage of pooled encoder"))
+ }
+ enc.hasKeys = true
+ enc.keys = keys
+ _, err := enc.encodeObject(v)
+ if err != nil {
+ enc.err = err
+ return err
+ }
+ _, err = enc.Write()
+ if err != nil {
+ enc.err = err
+ return err
+ }
+ return nil
+}
+
+func (enc *Encoder) encodeObject(v MarshalerJSONObject) ([]byte, error) {
+ enc.grow(512)
+ enc.writeByte('{')
+ if !v.IsNil() {
+ v.MarshalJSONObject(enc)
+ }
+ if enc.hasKeys {
+ enc.hasKeys = false
+ enc.keys = nil
+ }
+ enc.writeByte('}')
+ return enc.buf, enc.err
+}
+
+// AddObject adds an object to be encoded, must be used inside a slice or array encoding (does not encode a key)
+// value must implement MarshalerJSONObject
+func (enc *Encoder) AddObject(v MarshalerJSONObject) {
+ enc.Object(v)
+}
+
+// AddObjectOmitEmpty adds an object to be encoded or skips it if IsNil returns true.
+// Must be used inside a slice or array encoding (does not encode a key)
+// value must implement MarshalerJSONObject
+func (enc *Encoder) AddObjectOmitEmpty(v MarshalerJSONObject) {
+ enc.ObjectOmitEmpty(v)
+}
+
+// AddObjectNullEmpty adds an object to be encoded or skips it if IsNil returns true.
+// Must be used inside a slice or array encoding (does not encode a key)
+// value must implement MarshalerJSONObject
+func (enc *Encoder) AddObjectNullEmpty(v MarshalerJSONObject) {
+ enc.ObjectNullEmpty(v)
+}
+
+// AddObjectKey adds a struct to be encoded, must be used inside an object as it will encode a key
+// value must implement MarshalerJSONObject
+func (enc *Encoder) AddObjectKey(key string, v MarshalerJSONObject) {
+ enc.ObjectKey(key, v)
+}
+
+// AddObjectKeyOmitEmpty adds an object to be encoded or skips it if IsNil returns true.
+// Must be used inside a slice or array encoding (does not encode a key)
+// value must implement MarshalerJSONObject
+func (enc *Encoder) AddObjectKeyOmitEmpty(key string, v MarshalerJSONObject) {
+ enc.ObjectKeyOmitEmpty(key, v)
+}
+
+// AddObjectKeyNullEmpty adds an object to be encoded or skips it if IsNil returns true.
+// Must be used inside a slice or array encoding (does not encode a key)
+// value must implement MarshalerJSONObject
+func (enc *Encoder) AddObjectKeyNullEmpty(key string, v MarshalerJSONObject) {
+ enc.ObjectKeyNullEmpty(key, v)
+}
+
+// Object adds an object to be encoded, must be used inside a slice or array encoding (does not encode a key)
+// value must implement MarshalerJSONObject
+func (enc *Encoder) Object(v MarshalerJSONObject) {
+ if v.IsNil() {
+ enc.grow(2)
+ r := enc.getPreviousRune()
+ if r != '{' && r != '[' {
+ enc.writeByte(',')
+ }
+ enc.writeByte('{')
+ enc.writeByte('}')
+ return
+ }
+ enc.grow(4)
+ r := enc.getPreviousRune()
+ if r != '[' {
+ enc.writeByte(',')
+ }
+ enc.writeByte('{')
+
+ var origHasKeys = enc.hasKeys
+ var origKeys = enc.keys
+ enc.hasKeys = false
+ enc.keys = nil
+
+ v.MarshalJSONObject(enc)
+
+ enc.hasKeys = origHasKeys
+ enc.keys = origKeys
+
+ enc.writeByte('}')
+}
+
+// ObjectWithKeys adds an object to be encoded, must be used inside a slice or array encoding (does not encode a key)
+// value must implement MarshalerJSONObject. It will only encode the keys in keys.
+func (enc *Encoder) ObjectWithKeys(v MarshalerJSONObject, keys []string) {
+ if v.IsNil() {
+ enc.grow(2)
+ r := enc.getPreviousRune()
+ if r != '{' && r != '[' {
+ enc.writeByte(',')
+ }
+ enc.writeByte('{')
+ enc.writeByte('}')
+ return
+ }
+ enc.grow(4)
+ r := enc.getPreviousRune()
+ if r != '[' {
+ enc.writeByte(',')
+ }
+ enc.writeByte('{')
+
+ var origKeys = enc.keys
+ var origHasKeys = enc.hasKeys
+ enc.hasKeys = true
+ enc.keys = keys
+
+ v.MarshalJSONObject(enc)
+
+ enc.hasKeys = origHasKeys
+ enc.keys = origKeys
+
+ enc.writeByte('}')
+}
+
+// ObjectOmitEmpty adds an object to be encoded or skips it if IsNil returns true.
+// Must be used inside a slice or array encoding (does not encode a key)
+// value must implement MarshalerJSONObject
+func (enc *Encoder) ObjectOmitEmpty(v MarshalerJSONObject) {
+ if v.IsNil() {
+ return
+ }
+ enc.grow(2)
+ r := enc.getPreviousRune()
+ if r != '[' {
+ enc.writeByte(',')
+ }
+ enc.writeByte('{')
+
+ var origHasKeys = enc.hasKeys
+ var origKeys = enc.keys
+ enc.hasKeys = false
+ enc.keys = nil
+
+ v.MarshalJSONObject(enc)
+
+ enc.hasKeys = origHasKeys
+ enc.keys = origKeys
+
+ enc.writeByte('}')
+}
+
+// ObjectNullEmpty adds an object to be encoded or skips it if IsNil returns true.
+// Must be used inside a slice or array encoding (does not encode a key)
+// value must implement MarshalerJSONObject
+func (enc *Encoder) ObjectNullEmpty(v MarshalerJSONObject) {
+ enc.grow(2)
+ r := enc.getPreviousRune()
+ if r != '[' {
+ enc.writeByte(',')
+ }
+ if v.IsNil() {
+ enc.writeBytes(nullBytes)
+ return
+ }
+ enc.writeByte('{')
+
+ var origHasKeys = enc.hasKeys
+ var origKeys = enc.keys
+ enc.hasKeys = false
+ enc.keys = nil
+
+ v.MarshalJSONObject(enc)
+
+ enc.hasKeys = origHasKeys
+ enc.keys = origKeys
+
+ enc.writeByte('}')
+}
+
+// ObjectKey adds a struct to be encoded, must be used inside an object as it will encode a key
+// value must implement MarshalerJSONObject
+func (enc *Encoder) ObjectKey(key string, v MarshalerJSONObject) {
+ if enc.hasKeys {
+ if !enc.keyExists(key) {
+ return
+ }
+ }
+ if v.IsNil() {
+ enc.grow(2 + len(key))
+ r := enc.getPreviousRune()
+ if r != '{' {
+ enc.writeByte(',')
+ }
+ enc.writeByte('"')
+ enc.writeStringEscape(key)
+ enc.writeBytes(objKeyObj)
+ enc.writeByte('}')
+ return
+ }
+ enc.grow(5 + len(key))
+ r := enc.getPreviousRune()
+ if r != '{' {
+ enc.writeByte(',')
+ }
+ enc.writeByte('"')
+ enc.writeStringEscape(key)
+ enc.writeBytes(objKeyObj)
+
+ var origHasKeys = enc.hasKeys
+ var origKeys = enc.keys
+ enc.hasKeys = false
+ enc.keys = nil
+
+ v.MarshalJSONObject(enc)
+
+ enc.hasKeys = origHasKeys
+ enc.keys = origKeys
+
+ enc.writeByte('}')
+}
+
+// ObjectKeyWithKeys adds a struct to be encoded, must be used inside an object as it will encode a key.
+// Value must implement MarshalerJSONObject. It will only encode the keys in keys.
+func (enc *Encoder) ObjectKeyWithKeys(key string, value MarshalerJSONObject, keys []string) {
+ if enc.hasKeys {
+ if !enc.keyExists(key) {
+ return
+ }
+ }
+ if value.IsNil() {
+ enc.grow(2 + len(key))
+ r := enc.getPreviousRune()
+ if r != '{' {
+ enc.writeByte(',')
+ }
+ enc.writeByte('"')
+ enc.writeStringEscape(key)
+ enc.writeBytes(objKeyObj)
+ enc.writeByte('}')
+ return
+ }
+ enc.grow(5 + len(key))
+ r := enc.getPreviousRune()
+ if r != '{' {
+ enc.writeByte(',')
+ }
+ enc.writeByte('"')
+ enc.writeStringEscape(key)
+ enc.writeBytes(objKeyObj)
+ var origKeys = enc.keys
+ var origHasKeys = enc.hasKeys
+ enc.hasKeys = true
+ enc.keys = keys
+ value.MarshalJSONObject(enc)
+ enc.hasKeys = origHasKeys
+ enc.keys = origKeys
+ enc.writeByte('}')
+}
+
+// ObjectKeyOmitEmpty adds an object to be encoded or skips it if IsNil returns true.
+// Must be used inside a slice or array encoding (does not encode a key)
+// value must implement MarshalerJSONObject
+func (enc *Encoder) ObjectKeyOmitEmpty(key string, v MarshalerJSONObject) {
+ if enc.hasKeys {
+ if !enc.keyExists(key) {
+ return
+ }
+ }
+ if v.IsNil() {
+ return
+ }
+ enc.grow(5 + len(key))
+ r := enc.getPreviousRune()
+ if r != '{' {
+ enc.writeByte(',')
+ }
+ enc.writeByte('"')
+ enc.writeStringEscape(key)
+ enc.writeBytes(objKeyObj)
+
+ var origHasKeys = enc.hasKeys
+ var origKeys = enc.keys
+ enc.hasKeys = false
+ enc.keys = nil
+
+ v.MarshalJSONObject(enc)
+
+ enc.hasKeys = origHasKeys
+ enc.keys = origKeys
+
+ enc.writeByte('}')
+}
+
+// ObjectKeyNullEmpty adds an object to be encoded or skips it if IsNil returns true.
+// Must be used inside a slice or array encoding (does not encode a key)
+// value must implement MarshalerJSONObject
+func (enc *Encoder) ObjectKeyNullEmpty(key string, v MarshalerJSONObject) {
+ if enc.hasKeys {
+ if !enc.keyExists(key) {
+ return
+ }
+ }
+ enc.grow(5 + len(key))
+ r := enc.getPreviousRune()
+ if r != '{' {
+ enc.writeByte(',')
+ }
+ enc.writeByte('"')
+ enc.writeStringEscape(key)
+ enc.writeBytes(objKey)
+ if v.IsNil() {
+ enc.writeBytes(nullBytes)
+ return
+ }
+ enc.writeByte('{')
+
+ var origHasKeys = enc.hasKeys
+ var origKeys = enc.keys
+ enc.hasKeys = false
+ enc.keys = nil
+
+ v.MarshalJSONObject(enc)
+
+ enc.hasKeys = origHasKeys
+ enc.keys = origKeys
+
+ enc.writeByte('}')
+}
+
+// EncodeObjectFunc is a custom func type implementing MarshaleObject.
+// Use it to cast a func(*Encoder) to Marshal an object.
+//
+// enc := gojay.NewEncoder(io.Writer)
+// enc.EncodeObject(gojay.EncodeObjectFunc(func(enc *gojay.Encoder) {
+// enc.AddStringKey("hello", "world")
+// }))
+type EncodeObjectFunc func(*Encoder)
+
+// MarshalJSONObject implements MarshalerJSONObject.
+func (f EncodeObjectFunc) MarshalJSONObject(enc *Encoder) {
+ f(enc)
+}
+
+// IsNil implements MarshalerJSONObject.
+func (f EncodeObjectFunc) IsNil() bool {
+ return f == nil
+}
+
+func (enc *Encoder) keyExists(k string) bool {
+ if enc.keys == nil {
+ return false
+ }
+ for _, key := range enc.keys {
+ if key == k {
+ return true
+ }
+ }
+ return false
+}
diff --git a/vendor/github.com/francoispqt/gojay/encode_pool.go b/vendor/github.com/francoispqt/gojay/encode_pool.go
new file mode 100644
index 0000000000..3b26322530
--- /dev/null
+++ b/vendor/github.com/francoispqt/gojay/encode_pool.go
@@ -0,0 +1,50 @@
+package gojay
+
+import (
+ "io"
+ "sync"
+)
+
+var encPool = sync.Pool{
+ New: func() interface{} {
+ return NewEncoder(nil)
+ },
+}
+
+var streamEncPool = sync.Pool{
+ New: func() interface{} {
+ return Stream.NewEncoder(nil)
+ },
+}
+
+func init() {
+ for i := 0; i < 32; i++ {
+ encPool.Put(NewEncoder(nil))
+ }
+ for i := 0; i < 32; i++ {
+ streamEncPool.Put(Stream.NewEncoder(nil))
+ }
+}
+
+// NewEncoder returns a new encoder or borrows one from the pool
+func NewEncoder(w io.Writer) *Encoder {
+ return &Encoder{w: w}
+}
+
+// BorrowEncoder borrows an Encoder from the pool.
+func BorrowEncoder(w io.Writer) *Encoder {
+ enc := encPool.Get().(*Encoder)
+ enc.w = w
+ enc.buf = enc.buf[:0]
+ enc.isPooled = 0
+ enc.err = nil
+ enc.hasKeys = false
+ enc.keys = nil
+ return enc
+}
+
+// Release sends back a Encoder to the pool.
+func (enc *Encoder) Release() {
+ enc.isPooled = 1
+ encPool.Put(enc)
+}
diff --git a/vendor/github.com/francoispqt/gojay/encode_slice.go b/vendor/github.com/francoispqt/gojay/encode_slice.go
new file mode 100644
index 0000000000..7d964df97a
--- /dev/null
+++ b/vendor/github.com/francoispqt/gojay/encode_slice.go
@@ -0,0 +1,113 @@
+package gojay
+
+// AddSliceString marshals the given []string s
+func (enc *Encoder) AddSliceString(s []string) {
+ enc.SliceString(s)
+}
+
+// SliceString marshals the given []string s
+func (enc *Encoder) SliceString(s []string) {
+ enc.Array(EncodeArrayFunc(func(enc *Encoder) {
+ for _, str := range s {
+ enc.String(str)
+ }
+ }))
+}
+
+// AddSliceStringKey marshals the given []string s
+func (enc *Encoder) AddSliceStringKey(k string, s []string) {
+ enc.SliceStringKey(k, s)
+}
+
+// SliceStringKey marshals the given []string s
+func (enc *Encoder) SliceStringKey(k string, s []string) {
+ enc.ArrayKey(k, EncodeArrayFunc(func(enc *Encoder) {
+ for _, str := range s {
+ enc.String(str)
+ }
+ }))
+}
+
+// AddSliceInt marshals the given []int s
+func (enc *Encoder) AddSliceInt(s []int) {
+ enc.SliceInt(s)
+}
+
+// SliceInt marshals the given []int s
+func (enc *Encoder) SliceInt(s []int) {
+ enc.Array(EncodeArrayFunc(func(enc *Encoder) {
+ for _, i := range s {
+ enc.Int(i)
+ }
+ }))
+}
+
+// AddSliceIntKey marshals the given []int s
+func (enc *Encoder) AddSliceIntKey(k string, s []int) {
+ enc.SliceIntKey(k, s)
+}
+
+// SliceIntKey marshals the given []int s
+func (enc *Encoder) SliceIntKey(k string, s []int) {
+ enc.ArrayKey(k, EncodeArrayFunc(func(enc *Encoder) {
+ for _, i := range s {
+ enc.Int(i)
+ }
+ }))
+}
+
+// AddSliceFloat64 marshals the given []float64 s
+func (enc *Encoder) AddSliceFloat64(s []float64) {
+ enc.SliceFloat64(s)
+}
+
+// SliceFloat64 marshals the given []float64 s
+func (enc *Encoder) SliceFloat64(s []float64) {
+ enc.Array(EncodeArrayFunc(func(enc *Encoder) {
+ for _, i := range s {
+ enc.Float64(i)
+ }
+ }))
+}
+
+// AddSliceFloat64Key marshals the given []float64 s
+func (enc *Encoder) AddSliceFloat64Key(k string, s []float64) {
+ enc.SliceFloat64Key(k, s)
+}
+
+// SliceFloat64Key marshals the given []float64 s
+func (enc *Encoder) SliceFloat64Key(k string, s []float64) {
+ enc.ArrayKey(k, EncodeArrayFunc(func(enc *Encoder) {
+ for _, i := range s {
+ enc.Float64(i)
+ }
+ }))
+}
+
+// AddSliceBool marshals the given []bool s
+func (enc *Encoder) AddSliceBool(s []bool) {
+ enc.SliceBool(s)
+}
+
+// SliceBool marshals the given []bool s
+func (enc *Encoder) SliceBool(s []bool) {
+ enc.Array(EncodeArrayFunc(func(enc *Encoder) {
+ for _, i := range s {
+ enc.Bool(i)
+ }
+ }))
+}
+
+// AddSliceBoolKey marshals the given []bool s
+func (enc *Encoder) AddSliceBoolKey(k string, s []bool) {
+ enc.SliceBoolKey(k, s)
+}
+
+// SliceBoolKey marshals the given []bool s
+func (enc *Encoder) SliceBoolKey(k string, s []bool) {
+ enc.ArrayKey(k, EncodeArrayFunc(func(enc *Encoder) {
+ for _, i := range s {
+ enc.Bool(i)
+ }
+ }))
+}
diff --git a/vendor/github.com/francoispqt/gojay/encode_sqlnull.go b/vendor/github.com/francoispqt/gojay/encode_sqlnull.go
new file mode 100644
index 0000000000..04ff5962a5
--- /dev/null
+++ b/vendor/github.com/francoispqt/gojay/encode_sqlnull.go
@@ -0,0 +1,377 @@
+package gojay
+
+import "database/sql"
+
+// EncodeSQLNullString encodes a string to
+func (enc *Encoder) EncodeSQLNullString(v *sql.NullString) error {
+ if enc.isPooled == 1 {
+ panic(InvalidUsagePooledEncoderError("Invalid usage of pooled encoder"))
+ }
+ _, _ = enc.encodeString(v.String)
+ _, err := enc.Write()
+ if err != nil {
+ enc.err = err
+ return err
+ }
+ return nil
+}
+
+// AddSQLNullString adds a string to be encoded, must be used inside a slice or array encoding (does not encode a key)
+func (enc *Encoder) AddSQLNullString(v *sql.NullString) {
+ enc.String(v.String)
+}
+
+// AddSQLNullStringOmitEmpty adds a string to be encoded or skips it if it is zero value.
+// Must be used inside a slice or array encoding (does not encode a key)
+func (enc *Encoder) AddSQLNullStringOmitEmpty(v *sql.NullString) {
+ if v != nil && v.Valid && v.String != "" {
+ enc.StringOmitEmpty(v.String)
+ }
+}
+
+// AddSQLNullStringNullEmpty adds a string to be encoded or skips it if it is zero value.
+// Must be used inside a slice or array encoding (does not encode a key)
+func (enc *Encoder) AddSQLNullStringNullEmpty(v *sql.NullString) {
+ if v != nil && v.Valid {
+ enc.StringNullEmpty(v.String)
+ }
+}
+
+// AddSQLNullStringKey adds a string to be encoded, must be used inside an object as it will encode a key
+func (enc *Encoder) AddSQLNullStringKey(key string, v *sql.NullString) {
+ enc.StringKey(key, v.String)
+}
+
+// AddSQLNullStringKeyOmitEmpty adds a string to be encoded or skips it if it is zero value.
+// Must be used inside an object as it will encode a key
+func (enc *Encoder) AddSQLNullStringKeyOmitEmpty(key string, v *sql.NullString) {
+ if v != nil && v.Valid && v.String != "" {
+ enc.StringKeyOmitEmpty(key, v.String)
+ }
+}
+
+// SQLNullString adds a string to be encoded, must be used inside an object as it will encode a key
+func (enc *Encoder) SQLNullString(v *sql.NullString) {
+ enc.String(v.String)
+}
+
+// SQLNullStringOmitEmpty adds a string to be encoded, must be used inside an object as it will encode a key
+func (enc *Encoder) SQLNullStringOmitEmpty(v *sql.NullString) {
+ if v != nil && v.Valid && v.String != "" {
+ enc.String(v.String)
+ }
+}
+
+// SQLNullStringNullEmpty adds a string to be encoded, must be used inside an object as it will encode a key
+func (enc *Encoder) SQLNullStringNullEmpty(v *sql.NullString) {
+ if v != nil && v.Valid {
+ enc.StringNullEmpty(v.String)
+ }
+}
+
+// SQLNullStringKey adds a string to be encoded, must be used inside an object as it will encode a key
+func (enc *Encoder) SQLNullStringKey(key string, v *sql.NullString) {
+ enc.StringKey(key, v.String)
+}
+
+// SQLNullStringKeyOmitEmpty adds a string to be encoded or skips it if it is zero value.
+// Must be used inside an object as it will encode a key
+func (enc *Encoder) SQLNullStringKeyOmitEmpty(key string, v *sql.NullString) {
+ if v != nil && v.Valid && v.String != "" {
+ enc.StringKeyOmitEmpty(key, v.String)
+ }
+}
+
+// SQLNullStringKeyNullEmpty adds a string to be encoded or skips it if it is zero value.
+// Must be used inside an object as it will encode a key
+func (enc *Encoder) SQLNullStringKeyNullEmpty(key string, v *sql.NullString) {
+ if v != nil && v.Valid {
+ enc.StringKeyNullEmpty(key, v.String)
+ }
+}
+
+// NullInt64
+
+// EncodeSQLNullInt64 encodes a string to
+func (enc *Encoder) EncodeSQLNullInt64(v *sql.NullInt64) error {
+ if enc.isPooled == 1 {
+ panic(InvalidUsagePooledEncoderError("Invalid usage of pooled encoder"))
+ }
+ _, _ = enc.encodeInt64(v.Int64)
+ _, err := enc.Write()
+ if err != nil {
+ enc.err = err
+ return err
+ }
+ return nil
+}
+
+// AddSQLNullInt64 adds a string to be encoded, must be used inside a slice or array encoding (does not encode a key)
+func (enc *Encoder) AddSQLNullInt64(v *sql.NullInt64) {
+ enc.Int64(v.Int64)
+}
+
+// AddSQLNullInt64OmitEmpty adds a string to be encoded or skips it if it is zero value.
+// Must be used inside a slice or array encoding (does not encode a key)
+func (enc *Encoder) AddSQLNullInt64OmitEmpty(v *sql.NullInt64) {
+ if v != nil && v.Valid && v.Int64 != 0 {
+ enc.Int64OmitEmpty(v.Int64)
+ }
+}
+
+// AddSQLNullInt64NullEmpty adds a string to be encoded or skips it if it is zero value.
+// Must be used inside a slice or array encoding (does not encode a key)
+func (enc *Encoder) AddSQLNullInt64NullEmpty(v *sql.NullInt64) {
+ if v != nil && v.Valid {
+ enc.Int64NullEmpty(v.Int64)
+ }
+}
+
+// AddSQLNullInt64Key adds a string to be encoded, must be used inside an object as it will encode a key
+func (enc *Encoder) AddSQLNullInt64Key(key string, v *sql.NullInt64) {
+ enc.Int64Key(key, v.Int64)
+}
+
+// AddSQLNullInt64KeyOmitEmpty adds a string to be encoded or skips it if it is zero value.
+// Must be used inside an object as it will encode a key
+func (enc *Encoder) AddSQLNullInt64KeyOmitEmpty(key string, v *sql.NullInt64) {
+ if v != nil && v.Valid && v.Int64 != 0 {
+ enc.Int64KeyOmitEmpty(key, v.Int64)
+ }
+}
+
+// AddSQLNullInt64KeyNullEmpty adds a string to be encoded or skips it if it is zero value.
+// Must be used inside an object as it will encode a key
+func (enc *Encoder) AddSQLNullInt64KeyNullEmpty(key string, v *sql.NullInt64) {
+ if v != nil && v.Valid {
+ enc.Int64KeyNullEmpty(key, v.Int64)
+ }
+}
+
+// SQLNullInt64 adds a string to be encoded, must be used inside an object as it will encode a key
+func (enc *Encoder) SQLNullInt64(v *sql.NullInt64) {
+ enc.Int64(v.Int64)
+}
+
+// SQLNullInt64OmitEmpty adds a string to be encoded, must be used inside an object as it will encode a key
+func (enc *Encoder) SQLNullInt64OmitEmpty(v *sql.NullInt64) {
+ if v != nil && v.Valid && v.Int64 != 0 {
+ enc.Int64(v.Int64)
+ }
+}
+
+// SQLNullInt64NullEmpty adds a string to be encoded, must be used inside an object as it will encode a key
+func (enc *Encoder) SQLNullInt64NullEmpty(v *sql.NullInt64) {
+ if v != nil && v.Valid {
+ enc.Int64NullEmpty(v.Int64)
+ }
+}
+
+// SQLNullInt64Key adds a string to be encoded, must be used inside an object as it will encode a key
+func (enc *Encoder) SQLNullInt64Key(key string, v *sql.NullInt64) {
+ enc.Int64Key(key, v.Int64)
+}
+
+// SQLNullInt64KeyOmitEmpty adds a string to be encoded or skips it if it is zero value.
+// Must be used inside an object as it will encode a key
+func (enc *Encoder) SQLNullInt64KeyOmitEmpty(key string, v *sql.NullInt64) {
+ if v != nil && v.Valid && v.Int64 != 0 {
+ enc.Int64KeyOmitEmpty(key, v.Int64)
+ }
+}
+
+// SQLNullInt64KeyNullEmpty adds a string to be encoded or skips it if it is zero value.
+// Must be used inside an object as it will encode a key
+func (enc *Encoder) SQLNullInt64KeyNullEmpty(key string, v *sql.NullInt64) {
+ if v != nil && v.Valid {
+ enc.Int64KeyNullEmpty(key, v.Int64)
+ }
+}
+
+// NullFloat64
+
+// EncodeSQLNullFloat64 encodes a string to
+func (enc *Encoder) EncodeSQLNullFloat64(v *sql.NullFloat64) error {
+ if enc.isPooled == 1 {
+ panic(InvalidUsagePooledEncoderError("Invalid usage of pooled encoder"))
+ }
+ _, _ = enc.encodeFloat(v.Float64)
+ _, err := enc.Write()
+ if err != nil {
+ enc.err = err
+ return err
+ }
+ return nil
+}
+
+// AddSQLNullFloat64 adds a string to be encoded, must be used inside a slice or array encoding (does not encode a key)
+func (enc *Encoder) AddSQLNullFloat64(v *sql.NullFloat64) {
+ enc.Float64(v.Float64)
+}
+
+// AddSQLNullFloat64OmitEmpty adds a string to be encoded or skips it if it is zero value.
+// Must be used inside a slice or array encoding (does not encode a key)
+func (enc *Encoder) AddSQLNullFloat64OmitEmpty(v *sql.NullFloat64) {
+ if v != nil && v.Valid && v.Float64 != 0 {
+ enc.Float64OmitEmpty(v.Float64)
+ }
+}
+
+// AddSQLNullFloat64NullEmpty adds a string to be encoded or skips it if it is zero value.
+// Must be used inside a slice or array encoding (does not encode a key)
+func (enc *Encoder) AddSQLNullFloat64NullEmpty(v *sql.NullFloat64) {
+ if v != nil && v.Valid {
+ enc.Float64NullEmpty(v.Float64)
+ }
+}
+
+// AddSQLNullFloat64Key adds a string to be encoded, must be used inside an object as it will encode a key
+func (enc *Encoder) AddSQLNullFloat64Key(key string, v *sql.NullFloat64) {
+ enc.Float64Key(key, v.Float64)
+}
+
+// AddSQLNullFloat64KeyOmitEmpty adds a string to be encoded or skips it if it is zero value.
+// Must be used inside an object as it will encode a key
+func (enc *Encoder) AddSQLNullFloat64KeyOmitEmpty(key string, v *sql.NullFloat64) {
+ if v != nil && v.Valid && v.Float64 != 0 {
+ enc.Float64KeyOmitEmpty(key, v.Float64)
+ }
+}
+
+// AddSQLNullFloat64KeyNullEmpty adds a string to be encoded or skips it if it is zero value.
+// Must be used inside an object as it will encode a key
+func (enc *Encoder) AddSQLNullFloat64KeyNullEmpty(key string, v *sql.NullFloat64) {
+ if v != nil && v.Valid {
+ enc.Float64KeyNullEmpty(key, v.Float64)
+ }
+}
+
+// SQLNullFloat64 adds a string to be encoded, must be used inside an object as it will encode a key
+func (enc *Encoder) SQLNullFloat64(v *sql.NullFloat64) {
+ enc.Float64(v.Float64)
+}
+
+// SQLNullFloat64OmitEmpty adds a string to be encoded, must be used inside an object as it will encode a key
+func (enc *Encoder) SQLNullFloat64OmitEmpty(v *sql.NullFloat64) {
+ if v != nil && v.Valid && v.Float64 != 0 {
+ enc.Float64(v.Float64)
+ }
+}
+
+// SQLNullFloat64NullEmpty adds a string to be encoded, must be used inside an object as it will encode a key
+func (enc *Encoder) SQLNullFloat64NullEmpty(v *sql.NullFloat64) {
+ if v != nil && v.Valid {
+ enc.Float64NullEmpty(v.Float64)
+ }
+}
+
+// SQLNullFloat64Key adds a string to be encoded, must be used inside an object as it will encode a key
+func (enc *Encoder) SQLNullFloat64Key(key string, v *sql.NullFloat64) {
+ enc.Float64Key(key, v.Float64)
+}
+
+// SQLNullFloat64KeyOmitEmpty adds a string to be encoded or skips it if it is zero value.
+// Must be used inside an object as it will encode a key
+func (enc *Encoder) SQLNullFloat64KeyOmitEmpty(key string, v *sql.NullFloat64) {
+ if v != nil && v.Valid && v.Float64 != 0 {
+ enc.Float64KeyOmitEmpty(key, v.Float64)
+ }
+}
+
+// SQLNullFloat64KeyNullEmpty adds a string to be encoded or skips it if it is zero value.
+// Must be used inside an object as it will encode a key
+func (enc *Encoder) SQLNullFloat64KeyNullEmpty(key string, v *sql.NullFloat64) {
+ if v != nil && v.Valid {
+ enc.Float64KeyNullEmpty(key, v.Float64)
+ }
+}
+
+// NullBool
+
+// EncodeSQLNullBool encodes a string to
+func (enc *Encoder) EncodeSQLNullBool(v *sql.NullBool) error {
+ if enc.isPooled == 1 {
+ panic(InvalidUsagePooledEncoderError("Invalid usage of pooled encoder"))
+ }
+ _, _ = enc.encodeBool(v.Bool)
+ _, err := enc.Write()
+ if err != nil {
+ enc.err = err
+ return err
+ }
+ return nil
+}
+
+// AddSQLNullBool adds a string to be encoded, must be used inside a slice or array encoding (does not encode a key)
+func (enc *Encoder) AddSQLNullBool(v *sql.NullBool) {
+ enc.Bool(v.Bool)
+}
+
+// AddSQLNullBoolOmitEmpty adds a string to be encoded or skips it if it is zero value.
+// Must be used inside a slice or array encoding (does not encode a key)
+func (enc *Encoder) AddSQLNullBoolOmitEmpty(v *sql.NullBool) {
+ if v != nil && v.Valid && v.Bool != false {
+ enc.BoolOmitEmpty(v.Bool)
+ }
+}
+
+// AddSQLNullBoolKey adds a string to be encoded, must be used inside an object as it will encode a key
+func (enc *Encoder) AddSQLNullBoolKey(key string, v *sql.NullBool) {
+ enc.BoolKey(key, v.Bool)
+}
+
+// AddSQLNullBoolKeyOmitEmpty adds a string to be encoded or skips it if it is zero value.
+// Must be used inside an object as it will encode a key
+func (enc *Encoder) AddSQLNullBoolKeyOmitEmpty(key string, v *sql.NullBool) {
+ if v != nil && v.Valid && v.Bool != false {
+ enc.BoolKeyOmitEmpty(key, v.Bool)
+ }
+}
+
+// AddSQLNullBoolKeyNullEmpty adds a string to be encoded or skips it if it is zero value.
+// Must be used inside an object as it will encode a key
+func (enc *Encoder) AddSQLNullBoolKeyNullEmpty(key string, v *sql.NullBool) {
+ if v != nil && v.Valid {
+ enc.BoolKeyNullEmpty(key, v.Bool)
+ }
+}
+
+// SQLNullBool adds a string to be encoded, must be used inside an object as it will encode a key
+func (enc *Encoder) SQLNullBool(v *sql.NullBool) {
+ enc.Bool(v.Bool)
+}
+
+// SQLNullBoolOmitEmpty adds a string to be encoded, must be used inside an object as it will encode a key
+func (enc *Encoder) SQLNullBoolOmitEmpty(v *sql.NullBool) {
+ if v != nil && v.Valid && v.Bool != false {
+ enc.Bool(v.Bool)
+ }
+}
+
+// SQLNullBoolNullEmpty adds a string to be encoded, must be used inside an object as it will encode a key
+func (enc *Encoder) SQLNullBoolNullEmpty(v *sql.NullBool) {
+ if v != nil && v.Valid {
+ enc.BoolNullEmpty(v.Bool)
+ }
+}
+
+// SQLNullBoolKey adds a string to be encoded, must be used inside an object as it will encode a key
+func (enc *Encoder) SQLNullBoolKey(key string, v *sql.NullBool) {
+ enc.BoolKey(key, v.Bool)
+}
+
+// SQLNullBoolKeyOmitEmpty adds a string to be encoded or skips it if it is zero value.
+// Must be used inside an object as it will encode a key
+func (enc *Encoder) SQLNullBoolKeyOmitEmpty(key string, v *sql.NullBool) {
+ if v != nil && v.Valid && v.Bool != false {
+ enc.BoolKeyOmitEmpty(key, v.Bool)
+ }
+}
+
+// SQLNullBoolKeyNullEmpty adds a string to be encoded or skips it if it is zero value.
+// Must be used inside an object as it will encode a key
+func (enc *Encoder) SQLNullBoolKeyNullEmpty(key string, v *sql.NullBool) {
+ if v != nil && v.Valid {
+ enc.BoolKeyNullEmpty(key, v.Bool)
+ }
+}
diff --git a/vendor/github.com/francoispqt/gojay/encode_stream.go b/vendor/github.com/francoispqt/gojay/encode_stream.go
new file mode 100644
index 0000000000..fae8a17cf8
--- /dev/null
+++ b/vendor/github.com/francoispqt/gojay/encode_stream.go
@@ -0,0 +1,205 @@
+package gojay
+
+import (
+ "strconv"
+ "sync"
+ "time"
+)
+
+// MarshalerStream is the interface to implement
+// to continuously encode of stream of data.
+type MarshalerStream interface {
+ MarshalStream(enc *StreamEncoder)
+}
+
+// A StreamEncoder reads and encodes values to JSON from an input stream.
+//
+// It implements conext.Context and provide a channel to notify interruption.
+type StreamEncoder struct {
+ mux *sync.RWMutex
+ *Encoder
+ nConsumer int
+ delimiter byte
+ deadline *time.Time
+ done chan struct{}
+}
+
+// EncodeStream spins up a defined number of non blocking consumers of the MarshalerStream m.
+//
+// m must implement MarshalerStream. Ideally m is a channel. See example for implementation.
+//
+// See the documentation for Marshal for details about the conversion of Go value to JSON.
+func (s *StreamEncoder) EncodeStream(m MarshalerStream) {
+ // if a single consumer, just use this encoder
+ if s.nConsumer == 1 {
+ go consume(s, s, m)
+ return
+ }
+ // else use this Encoder only for first consumer
+ // and use new encoders for other consumers
+ // this is to avoid concurrent writing to same buffer
+ // resulting in a weird JSON
+ go consume(s, s, m)
+ for i := 1; i < s.nConsumer; i++ {
+ s.mux.RLock()
+ select {
+ case <-s.done:
+ default:
+ ss := Stream.borrowEncoder(s.w)
+ ss.mux.Lock()
+ ss.done = s.done
+ ss.buf = make([]byte, 0, 512)
+ ss.delimiter = s.delimiter
+ go consume(s, ss, m)
+ ss.mux.Unlock()
+ }
+ s.mux.RUnlock()
+ }
+ return
+}
+
+// LineDelimited sets the delimiter to a new line character.
+//
+// It will add a new line after each JSON marshaled by the MarshalerStream
+func (s *StreamEncoder) LineDelimited() *StreamEncoder {
+ s.delimiter = '\n'
+ return s
+}
+
+// CommaDelimited sets the delimiter to a comma.
+//
+// It will add a new line after each JSON marshaled by the MarshalerStream
+func (s *StreamEncoder) CommaDelimited() *StreamEncoder {
+ s.delimiter = ','
+ return s
+}
+
+// NConsumer sets the number of non blocking go routine to consume the stream.
+func (s *StreamEncoder) NConsumer(n int) *StreamEncoder {
+ s.nConsumer = n
+ return s
+}
+
+// Release sends back a Decoder to the pool.
+// If a decoder is used after calling Release
+// a panic will be raised with an InvalidUsagePooledDecoderError error.
+func (s *StreamEncoder) Release() {
+ s.isPooled = 1
+ streamEncPool.Put(s)
+}
+
+// Done returns a channel that's closed when work is done.
+// It implements context.Context
+func (s *StreamEncoder) Done() <-chan struct{} {
+ return s.done
+}
+
+// Err returns nil if Done is not yet closed.
+// If Done is closed, Err returns a non-nil error explaining why.
+// It implements context.Context
+func (s *StreamEncoder) Err() error {
+ return s.err
+}
+
+// Deadline returns the time when work done on behalf of this context
+// should be canceled. Deadline returns ok==false when no deadline is
+// set. Successive calls to Deadline return the same results.
+func (s *StreamEncoder) Deadline() (time.Time, bool) {
+ if s.deadline != nil {
+ return *s.deadline, true
+ }
+ return time.Time{}, false
+}
+
+// SetDeadline sets the deadline
+func (s *StreamEncoder) SetDeadline(t time.Time) {
+ s.deadline = &t
+}
+
+// Value implements context.Context
+func (s *StreamEncoder) Value(key interface{}) interface{} {
+ return nil
+}
+
+// Cancel cancels the consumers of the stream, interrupting the stream encoding.
+//
+// After calling cancel, Done() will return a closed channel.
+func (s *StreamEncoder) Cancel(err error) {
+ s.mux.Lock()
+ defer s.mux.Unlock()
+
+ select {
+ case <-s.done:
+ default:
+ s.err = err
+ close(s.done)
+ }
+}
+
+// AddObject adds an object to be encoded.
+// value must implement MarshalerJSONObject.
+func (s *StreamEncoder) AddObject(v MarshalerJSONObject) {
+ if v.IsNil() {
+ return
+ }
+ s.Encoder.writeByte('{')
+ v.MarshalJSONObject(s.Encoder)
+ s.Encoder.writeByte('}')
+ s.Encoder.writeByte(s.delimiter)
+}
+
+// AddString adds a string to be encoded.
+func (s *StreamEncoder) AddString(v string) {
+ s.Encoder.writeByte('"')
+ s.Encoder.writeString(v)
+ s.Encoder.writeByte('"')
+ s.Encoder.writeByte(s.delimiter)
+}
+
+// AddArray adds an implementation of MarshalerJSONArray to be encoded.
+func (s *StreamEncoder) AddArray(v MarshalerJSONArray) {
+ s.Encoder.writeByte('[')
+ v.MarshalJSONArray(s.Encoder)
+ s.Encoder.writeByte(']')
+ s.Encoder.writeByte(s.delimiter)
+}
+
+// AddInt adds an int to be encoded.
+func (s *StreamEncoder) AddInt(value int) {
+ s.buf = strconv.AppendInt(s.buf, int64(value), 10)
+ s.Encoder.writeByte(s.delimiter)
+}
+
+// AddFloat64 adds a float64 to be encoded.
+func (s *StreamEncoder) AddFloat64(value float64) {
+ s.buf = strconv.AppendFloat(s.buf, value, 'f', -1, 64)
+ s.Encoder.writeByte(s.delimiter)
+}
+
+// AddFloat adds a float64 to be encoded.
+func (s *StreamEncoder) AddFloat(value float64) {
+ s.AddFloat64(value)
+}
+
+// Non exposed
+
+func consume(init *StreamEncoder, s *StreamEncoder, m MarshalerStream) {
+ defer s.Release()
+ for {
+ select {
+ case <-init.Done():
+ return
+ default:
+ m.MarshalStream(s)
+ if s.Encoder.err != nil {
+ init.Cancel(s.Encoder.err)
+ return
+ }
+ i, err := s.Encoder.Write()
+ if err != nil || i == 0 {
+ init.Cancel(err)
+ return
+ }
+ }
+ }
+}
diff --git a/vendor/github.com/francoispqt/gojay/encode_stream_pool.go b/vendor/github.com/francoispqt/gojay/encode_stream_pool.go
new file mode 100644
index 0000000000..3bb8b1af06
--- /dev/null
+++ b/vendor/github.com/francoispqt/gojay/encode_stream_pool.go
@@ -0,0 +1,38 @@
+package gojay
+
+import (
+ "io"
+ "sync"
+)
+
+// NewEncoder returns a new StreamEncoder.
+// It takes an io.Writer implementation to output data.
+// It initiates the done channel returned by Done().
+func (s stream) NewEncoder(w io.Writer) *StreamEncoder {
+ enc := BorrowEncoder(w)
+ return &StreamEncoder{Encoder: enc, nConsumer: 1, done: make(chan struct{}, 1), mux: &sync.RWMutex{}}
+}
+
+// BorrowEncoder borrows a StreamEncoder from the pool.
+// It takes an io.Writer implementation to output data.
+// It initiates the done channel returned by Done().
+//
+// If no StreamEncoder is available in the pool, it returns a fresh one
+func (s stream) BorrowEncoder(w io.Writer) *StreamEncoder {
+ streamEnc := streamEncPool.Get().(*StreamEncoder)
+ streamEnc.w = w
+ streamEnc.Encoder.err = nil
+ streamEnc.done = make(chan struct{}, 1)
+ streamEnc.Encoder.buf = streamEnc.buf[:0]
+ streamEnc.nConsumer = 1
+ streamEnc.isPooled = 0
+ return streamEnc
+}
+
+func (s stream) borrowEncoder(w io.Writer) *StreamEncoder {
+ streamEnc := streamEncPool.Get().(*StreamEncoder)
+ streamEnc.isPooled = 0
+ streamEnc.w = w
+ streamEnc.Encoder.err = nil
+ return streamEnc
+}
diff --git a/vendor/github.com/francoispqt/gojay/encode_string.go b/vendor/github.com/francoispqt/gojay/encode_string.go
new file mode 100644
index 0000000000..438c773fcb
--- /dev/null
+++ b/vendor/github.com/francoispqt/gojay/encode_string.go
@@ -0,0 +1,186 @@
+package gojay
+
+// EncodeString encodes a string to
+func (enc *Encoder) EncodeString(s string) error {
+ if enc.isPooled == 1 {
+ panic(InvalidUsagePooledEncoderError("Invalid usage of pooled encoder"))
+ }
+ _, _ = enc.encodeString(s)
+ _, err := enc.Write()
+ if err != nil {
+ enc.err = err
+ return err
+ }
+ return nil
+}
+
+// encodeString encodes a string to
+func (enc *Encoder) encodeString(v string) ([]byte, error) {
+ enc.writeByte('"')
+ enc.writeStringEscape(v)
+ enc.writeByte('"')
+ return enc.buf, nil
+}
+
+// AppendString appends a string to the buffer
+func (enc *Encoder) AppendString(v string) {
+ enc.grow(len(v) + 2)
+ enc.writeByte('"')
+ enc.writeStringEscape(v)
+ enc.writeByte('"')
+}
+
+// AddString adds a string to be encoded, must be used inside a slice or array encoding (does not encode a key)
+func (enc *Encoder) AddString(v string) {
+ enc.String(v)
+}
+
+// AddStringOmitEmpty adds a string to be encoded or skips it if it is zero value.
+// Must be used inside a slice or array encoding (does not encode a key)
+func (enc *Encoder) AddStringOmitEmpty(v string) {
+ enc.StringOmitEmpty(v)
+}
+
+// AddStringNullEmpty adds a string to be encoded or skips it if it is zero value.
+// Must be used inside a slice or array encoding (does not encode a key)
+func (enc *Encoder) AddStringNullEmpty(v string) {
+ enc.StringNullEmpty(v)
+}
+
+// AddStringKey adds a string to be encoded, must be used inside an object as it will encode a key
+func (enc *Encoder) AddStringKey(key, v string) {
+ enc.StringKey(key, v)
+}
+
+// AddStringKeyOmitEmpty adds a string to be encoded or skips it if it is zero value.
+// Must be used inside an object as it will encode a key
+func (enc *Encoder) AddStringKeyOmitEmpty(key, v string) {
+ enc.StringKeyOmitEmpty(key, v)
+}
+
+// AddStringKeyNullEmpty adds a string to be encoded or skips it if it is zero value.
+// Must be used inside an object as it will encode a key
+func (enc *Encoder) AddStringKeyNullEmpty(key, v string) {
+ enc.StringKeyNullEmpty(key, v)
+}
+
+// String adds a string to be encoded, must be used inside a slice or array encoding (does not encode a key)
+func (enc *Encoder) String(v string) {
+ enc.grow(len(v) + 4)
+ r := enc.getPreviousRune()
+ if r != '[' {
+ enc.writeTwoBytes(',', '"')
+ } else {
+ enc.writeByte('"')
+ }
+ enc.writeStringEscape(v)
+ enc.writeByte('"')
+}
+
+// StringOmitEmpty adds a string to be encoded or skips it if it is zero value.
+// Must be used inside a slice or array encoding (does not encode a key)
+func (enc *Encoder) StringOmitEmpty(v string) {
+ if v == "" {
+ return
+ }
+ r := enc.getPreviousRune()
+ if r != '[' {
+ enc.writeTwoBytes(',', '"')
+ } else {
+ enc.writeByte('"')
+ }
+ enc.writeStringEscape(v)
+ enc.writeByte('"')
+}
+
+// StringNullEmpty adds a string to be encoded or skips it if it is zero value.
+// Must be used inside a slice or array encoding (does not encode a key)
+func (enc *Encoder) StringNullEmpty(v string) {
+ r := enc.getPreviousRune()
+ if v == "" {
+ if r != '[' {
+ enc.writeByte(',')
+ enc.writeBytes(nullBytes)
+ } else {
+ enc.writeBytes(nullBytes)
+ }
+ return
+ }
+ if r != '[' {
+ enc.writeTwoBytes(',', '"')
+ } else {
+ enc.writeByte('"')
+ }
+ enc.writeStringEscape(v)
+ enc.writeByte('"')
+}
+
+// StringKey adds a string to be encoded, must be used inside an object as it will encode a key
+func (enc *Encoder) StringKey(key, v string) {
+ if enc.hasKeys {
+ if !enc.keyExists(key) {
+ return
+ }
+ }
+ enc.grow(len(key) + len(v) + 5)
+ r := enc.getPreviousRune()
+ if r != '{' {
+ enc.writeTwoBytes(',', '"')
+ } else {
+ enc.writeByte('"')
+ }
+ enc.writeStringEscape(key)
+ enc.writeBytes(objKeyStr)
+ enc.writeStringEscape(v)
+ enc.writeByte('"')
+}
+
+// StringKeyOmitEmpty adds a string to be encoded or skips it if it is zero value.
+// Must be used inside an object as it will encode a key
+func (enc *Encoder) StringKeyOmitEmpty(key, v string) {
+ if enc.hasKeys {
+ if !enc.keyExists(key) {
+ return
+ }
+ }
+ if v == "" {
+ return
+ }
+ enc.grow(len(key) + len(v) + 5)
+ r := enc.getPreviousRune()
+ if r != '{' {
+ enc.writeTwoBytes(',', '"')
+ } else {
+ enc.writeByte('"')
+ }
+ enc.writeStringEscape(key)
+ enc.writeBytes(objKeyStr)
+ enc.writeStringEscape(v)
+ enc.writeByte('"')
+}
+
+// StringKeyNullEmpty adds a string to be encoded or skips it if it is zero value.
+// Must be used inside an object as it will encode a key
+func (enc *Encoder) StringKeyNullEmpty(key, v string) {
+ if enc.hasKeys {
+ if !enc.keyExists(key) {
+ return
+ }
+ }
+ enc.grow(len(key) + len(v) + 5)
+ r := enc.getPreviousRune()
+ if r != '{' {
+ enc.writeTwoBytes(',', '"')
+ } else {
+ enc.writeByte('"')
+ }
+ enc.writeStringEscape(key)
+ enc.writeBytes(objKey)
+ if v == "" {
+ enc.writeBytes(nullBytes)
+ return
+ }
+ enc.writeByte('"')
+ enc.writeStringEscape(v)
+ enc.writeByte('"')
+}
diff --git a/vendor/github.com/francoispqt/gojay/encode_time.go b/vendor/github.com/francoispqt/gojay/encode_time.go
new file mode 100644
index 0000000000..6f99e3426c
--- /dev/null
+++ b/vendor/github.com/francoispqt/gojay/encode_time.go
@@ -0,0 +1,68 @@
+package gojay
+
+import (
+ "time"
+)
+
+// EncodeTime encodes a *time.Time to JSON with the given format
+func (enc *Encoder) EncodeTime(t *time.Time, format string) error {
+ if enc.isPooled == 1 {
+ panic(InvalidUsagePooledEncoderError("Invalid usage of pooled encoder"))
+ }
+ _, _ = enc.encodeTime(t, format)
+ _, err := enc.Write()
+ if err != nil {
+ return err
+ }
+ return nil
+}
+
+// encodeInt encodes an int to JSON
+func (enc *Encoder) encodeTime(t *time.Time, format string) ([]byte, error) {
+ enc.writeByte('"')
+ enc.buf = t.AppendFormat(enc.buf, format)
+ enc.writeByte('"')
+ return enc.buf, nil
+}
+
+// AddTimeKey adds an *time.Time to be encoded with the given format, must be used inside an object as it will encode a key
+func (enc *Encoder) AddTimeKey(key string, t *time.Time, format string) {
+ enc.TimeKey(key, t, format)
+}
+
+// TimeKey adds an *time.Time to be encoded with the given format, must be used inside an object as it will encode a key
+func (enc *Encoder) TimeKey(key string, t *time.Time, format string) {
+ if enc.hasKeys {
+ if !enc.keyExists(key) {
+ return
+ }
+ }
+ enc.grow(10 + len(key))
+ r := enc.getPreviousRune()
+ if r != '{' {
+ enc.writeTwoBytes(',', '"')
+ } else {
+ enc.writeByte('"')
+ }
+ enc.writeStringEscape(key)
+ enc.writeBytes(objKeyStr)
+ enc.buf = t.AppendFormat(enc.buf, format)
+ enc.writeByte('"')
+}
+
+// AddTime adds an *time.Time to be encoded with the given format, must be used inside a slice or array encoding (does not encode a key)
+func (enc *Encoder) AddTime(t *time.Time, format string) {
+ enc.Time(t, format)
+}
+
+// Time adds an *time.Time to be encoded with the given format, must be used inside a slice or array encoding (does not encode a key)
+func (enc *Encoder) Time(t *time.Time, format string) {
+ enc.grow(10)
+ r := enc.getPreviousRune()
+ if r != '[' {
+ enc.writeByte(',')
+ }
+ enc.writeByte('"')
+ enc.buf = t.AppendFormat(enc.buf, format)
+ enc.writeByte('"')
+}
diff --git a/vendor/github.com/francoispqt/gojay/errors.go b/vendor/github.com/francoispqt/gojay/errors.go
new file mode 100644
index 0000000000..0fd52e6633
--- /dev/null
+++ b/vendor/github.com/francoispqt/gojay/errors.go
@@ -0,0 +1,88 @@
+package gojay
+
+import (
+ "errors"
+ "fmt"
+)
+
+const invalidJSONCharErrorMsg = "Invalid JSON, wrong char '%c' found at position %d"
+
+// InvalidJSONError is a type representing an error returned when
+// Decoding encounters invalid JSON.
+type InvalidJSONError string
+
+func (err InvalidJSONError) Error() string {
+ return string(err)
+}
+
+func (dec *Decoder) raiseInvalidJSONErr(pos int) error {
+ var c byte
+ if len(dec.data) > pos {
+ c = dec.data[pos]
+ }
+ dec.err = InvalidJSONError(
+ fmt.Sprintf(
+ invalidJSONCharErrorMsg,
+ c,
+ pos,
+ ),
+ )
+ return dec.err
+}
+
+const invalidUnmarshalErrorMsg = "Cannot unmarshal JSON to type '%T'"
+
+// InvalidUnmarshalError is a type representing an error returned when
+// Decoding cannot unmarshal JSON to the receiver type for various reasons.
+type InvalidUnmarshalError string
+
+func (err InvalidUnmarshalError) Error() string {
+ return string(err)
+}
+
+func (dec *Decoder) makeInvalidUnmarshalErr(v interface{}) error {
+ return InvalidUnmarshalError(
+ fmt.Sprintf(
+ invalidUnmarshalErrorMsg,
+ v,
+ ),
+ )
+}
+
+const invalidMarshalErrorMsg = "Invalid type %T provided to Marshal"
+
+// InvalidMarshalError is a type representing an error returned when
+// Encoding did not find the proper way to encode
+type InvalidMarshalError string
+
+func (err InvalidMarshalError) Error() string {
+ return string(err)
+}
+
+// NoReaderError is a type representing an error returned when
+// decoding requires a reader and none was given
+type NoReaderError string
+
+func (err NoReaderError) Error() string {
+ return string(err)
+}
+
+// InvalidUsagePooledDecoderError is a type representing an error returned
+// when decoding is called on a still pooled Decoder
+type InvalidUsagePooledDecoderError string
+
+func (err InvalidUsagePooledDecoderError) Error() string {
+ return string(err)
+}
+
+// InvalidUsagePooledEncoderError is a type representing an error returned
+// when decoding is called on a still pooled Encoder
+type InvalidUsagePooledEncoderError string
+
+func (err InvalidUsagePooledEncoderError) Error() string {
+ return string(err)
+}
+
+// ErrUnmarshalPtrExpected is the error returned when unmarshal expects a pointer value,
+// When using `dec.ObjectNull` or `dec.ArrayNull` for example.
+var ErrUnmarshalPtrExpected = errors.New("Cannot unmarshal to given value, a pointer is expected")
diff --git a/vendor/github.com/francoispqt/gojay/go.mod b/vendor/github.com/francoispqt/gojay/go.mod
new file mode 100644
index 0000000000..76814eb386
--- /dev/null
+++ b/vendor/github.com/francoispqt/gojay/go.mod
@@ -0,0 +1,24 @@
+module github.com/francoispqt/gojay
+
+go 1.12
+
+require (
+ cloud.google.com/go v0.37.0 // indirect
+ github.com/buger/jsonparser v0.0.0-20181115193947-bf1c66bbce23
+ github.com/go-errors/errors v1.0.1
+ github.com/golang/protobuf v1.3.1 // indirect
+ github.com/json-iterator/go v1.1.6
+ github.com/lunixbochs/vtclean v1.0.0 // indirect
+ github.com/mailru/easyjson v0.0.0-20190312143242-1de009706dbe
+ github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
+ github.com/modern-go/reflect2 v1.0.1 // indirect
+ github.com/pkg/errors v0.8.1 // indirect
+ github.com/stretchr/testify v1.2.2
+ github.com/viant/assertly v0.4.8
+ github.com/viant/toolbox v0.24.0
+ golang.org/x/crypto v0.0.0-20190313024323-a1f597ede03a // indirect
+ golang.org/x/net v0.0.0-20190313220215-9f648a60d977
+ golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421 // indirect
+ golang.org/x/sys v0.0.0-20190316082340-a2f829d7f35f // indirect
+ gopkg.in/yaml.v2 v2.2.2 // indirect
+)
diff --git a/vendor/github.com/francoispqt/gojay/go.sum b/vendor/github.com/francoispqt/gojay/go.sum
new file mode 100644
index 0000000000..06c27493af
--- /dev/null
+++ b/vendor/github.com/francoispqt/gojay/go.sum
@@ -0,0 +1,182 @@
+cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
+cloud.google.com/go v0.31.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
+cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
+cloud.google.com/go v0.37.0 h1:69FNAINiZfsEuwH3fKq8QrAAnHz+2m4XL4kVYi5BX0Q=
+cloud.google.com/go v0.37.0/go.mod h1:TS1dMSSfndXH133OKGwekG838Om/cQT0BUHV3HcBgoo=
+dmitri.shuralyov.com/app/changes v0.0.0-20180602232624-0a106ad413e3/go.mod h1:Yl+fi1br7+Rr3LqpNJf1/uxUdtRUV+Tnj0o93V2B9MU=
+dmitri.shuralyov.com/html/belt v0.0.0-20180602232347-f7d459c86be0/go.mod h1:JLBrvjyP0v+ecvNYvCpyZgu5/xkfAUhi6wJj28eUfSU=
+dmitri.shuralyov.com/service/change v0.0.0-20181023043359-a85b471d5412/go.mod h1:a1inKt/atXimZ4Mv927x+r7UpyzRUf4emIoiiSC2TN4=
+dmitri.shuralyov.com/state v0.0.0-20180228185332-28bcc343414c/go.mod h1:0PRwlb0D6DFvNNtx+9ybjezNCa8XF0xaYcETyp6rHWU=
+git.apache.org/thrift.git v0.0.0-20180902110319-2566ecd5d999/go.mod h1:fPE2ZNJGynbRyZ4dJvy6G277gSllfV2HJqblrnkyeyg=
+github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
+github.com/anmitsu/go-shlex v0.0.0-20161002113705-648efa622239/go.mod h1:2FmKhYUyUczH0OGQWaF5ceTx0UBShxjsH6f8oGKYe2c=
+github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q=
+github.com/bradfitz/go-smtpd v0.0.0-20170404230938-deb6d6237625/go.mod h1:HYsPBTaaSFSlLx/70C2HPIMNZpVV8+vt/A+FMnYP11g=
+github.com/buger/jsonparser v0.0.0-20181115193947-bf1c66bbce23 h1:D21IyuvjDCshj1/qq+pCNd3VZOAEI9jy6Bi131YlXgI=
+github.com/buger/jsonparser v0.0.0-20181115193947-bf1c66bbce23/go.mod h1:bbYlZJ7hK1yFx9hf58LP0zeX7UjIGs20ufpu3evjr+s=
+github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
+github.com/coreos/go-systemd v0.0.0-20181012123002-c6f51f82210d/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4=
+github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
+github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
+github.com/dustin/go-humanize v1.0.0/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk=
+github.com/flynn/go-shlex v0.0.0-20150515145356-3f9db97f8568/go.mod h1:xEzjJPgXI435gkrCt3MPfRiAkVrwSbHsst4LCFVfpJc=
+github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
+github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04=
+github.com/gliderlabs/ssh v0.1.1/go.mod h1:U7qILu1NlMHj9FlMhZLlkCdDnU1DBEAqr0aevW3Awn0=
+github.com/go-errors/errors v1.0.1 h1:LUHzmkK3GUKUrL/1gfBUxAHzcev3apQlezX/+O7ma6w=
+github.com/go-errors/errors v1.0.1/go.mod h1:f4zRHt4oKfwPJE5k8C9vpYG+aDHdBFUsgrm6/TyX73Q=
+github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ=
+github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
+github.com/golang/lint v0.0.0-20180702182130-06c8688daad7/go.mod h1:tluoj9z5200jBnyusfRPU2LqT6J+DAorxEvtC7LHB+E=
+github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
+github.com/golang/mock v1.2.0/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
+github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
+github.com/golang/protobuf v1.3.1 h1:YF8+flBXS5eO826T4nzqPrxfhQThhXl0YzfuUPu4SBg=
+github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
+github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
+github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M=
+github.com/google/go-github v17.0.0+incompatible/go.mod h1:zLgOLi98H3fifZn+44m+umXrS52loVEgC2AApnigrVQ=
+github.com/google/go-querystring v1.0.0/go.mod h1:odCYkC5MyYFN7vkCjXpyrEuKhc/BUO6wN/zVPAxq5ck=
+github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs=
+github.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc=
+github.com/googleapis/gax-go v2.0.0+incompatible/go.mod h1:SFVmujtThgffbyetf+mdk2eWhX2bMyUtNHzFKcPA9HY=
+github.com/googleapis/gax-go/v2 v2.0.3/go.mod h1:LLvjysVCY1JZeum8Z6l8qUty8fiNwE08qbEPm1M08qg=
+github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY=
+github.com/gregjones/httpcache v0.0.0-20180305231024-9cad4c3443a7/go.mod h1:FecbI9+v66THATjSRHfNgh1IVFe/9kFxbXtjV0ctIMA=
+github.com/grpc-ecosystem/grpc-gateway v1.5.0/go.mod h1:RSKVYQBd5MCa4OVpNdGskqpgL2+G+NZTnrVHpWWfpdw=
+github.com/jellevandenhooff/dkim v0.0.0-20150330215556-f50fe3d243e1/go.mod h1:E0B/fFc00Y+Rasa88328GlI/XbtyysCtTHZS8h7IrBU=
+github.com/json-iterator/go v1.1.6 h1:MrUvLMLTMxbqFJ9kzlvat/rYZqZnW3u4wkLzWTaFwKs=
+github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU=
+github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU=
+github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
+github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
+github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
+github.com/kr/pty v1.1.3/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
+github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
+github.com/lunixbochs/vtclean v1.0.0 h1:xu2sLAri4lGiovBDQKxl5mrXyESr3gUr5m5SM5+LVb8=
+github.com/lunixbochs/vtclean v1.0.0/go.mod h1:pHhQNgMf3btfWnGBVipUOjRYhoOsdGqdm/+2c2E2WMI=
+github.com/mailru/easyjson v0.0.0-20190312143242-1de009706dbe h1:W/GaMY0y69G4cFlmsC6B9sbuo2fP8OFP1ABjt4kPz+w=
+github.com/mailru/easyjson v0.0.0-20190312143242-1de009706dbe/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc=
+github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0=
+github.com/microcosm-cc/bluemonday v1.0.1/go.mod h1:hsXNsILzKxV+sX77C5b8FSuKF00vh2OMYv+xgHpAMF4=
+github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg=
+github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
+github.com/modern-go/reflect2 v1.0.1 h1:9f412s+6RmYXLWZSEzVVgPGK7C2PphHj5RJrvfx9AWI=
+github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
+github.com/neelance/astrewrite v0.0.0-20160511093645-99348263ae86/go.mod h1:kHJEU3ofeGjhHklVoIGuVj85JJwZ6kWPaJwCIxgnFmo=
+github.com/neelance/sourcemap v0.0.0-20151028013722-8c68805598ab/go.mod h1:Qr6/a/Q4r9LP1IltGz7tA7iOK1WonHEYhu1HRBA7ZiM=
+github.com/openzipkin/zipkin-go v0.1.1/go.mod h1:NtoC/o8u3JlF1lSlyPNswIbeQH9bJTmOf0Erfk+hxe8=
+github.com/pkg/errors v0.8.1 h1:iURUrRGxPUNPdy5/HRSm+Yj6okJ6UtLINN0Q9M4+h3I=
+github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
+github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
+github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
+github.com/prometheus/client_golang v0.8.0/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw=
+github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo=
+github.com/prometheus/common v0.0.0-20180801064454-c7de2306084e/go.mod h1:daVV7qP5qjZbuso7PdcryaAu0sAZbrN9i7WWcTMWvro=
+github.com/prometheus/procfs v0.0.0-20180725123919-05ee40e3a273/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk=
+github.com/russross/blackfriday v1.5.2/go.mod h1:JO/DiYxRf+HjHt06OyowR9PTA263kcR/rfWxYHBV53g=
+github.com/sergi/go-diff v1.0.0/go.mod h1:0CfEIISq7TuYL3j771MWULgwwjU+GofnZX9QAmXWZgo=
+github.com/shurcooL/component v0.0.0-20170202220835-f88ec8f54cc4/go.mod h1:XhFIlyj5a1fBNx5aJTbKoIq0mNaPvOagO+HjB3EtxrY=
+github.com/shurcooL/events v0.0.0-20181021180414-410e4ca65f48/go.mod h1:5u70Mqkb5O5cxEA8nxTsgrgLehJeAw6Oc4Ab1c/P1HM=
+github.com/shurcooL/github_flavored_markdown v0.0.0-20181002035957-2122de532470/go.mod h1:2dOwnU2uBioM+SGy2aZoq1f/Sd1l9OkAeAUvjSyvgU0=
+github.com/shurcooL/go v0.0.0-20180423040247-9e1955d9fb6e/go.mod h1:TDJrrUr11Vxrven61rcy3hJMUqaf/CLWYhHNPmT14Lk=
+github.com/shurcooL/go-goon v0.0.0-20170922171312-37c2f522c041/go.mod h1:N5mDOmsrJOB+vfqUK+7DmDyjhSLIIBnXo9lvZJj3MWQ=
+github.com/shurcooL/gofontwoff v0.0.0-20180329035133-29b52fc0a18d/go.mod h1:05UtEgK5zq39gLST6uB0cf3NEHjETfB4Fgr3Gx5R9Vw=
+github.com/shurcooL/gopherjslib v0.0.0-20160914041154-feb6d3990c2c/go.mod h1:8d3azKNyqcHP1GaQE/c6dDgjkgSx2BZ4IoEi4F1reUI=
+github.com/shurcooL/highlight_diff v0.0.0-20170515013008-09bb4053de1b/go.mod h1:ZpfEhSmds4ytuByIcDnOLkTHGUI6KNqRNPDLHDk+mUU=
+github.com/shurcooL/highlight_go v0.0.0-20181028180052-98c3abbbae20/go.mod h1:UDKB5a1T23gOMUJrI+uSuH0VRDStOiUVSjBTRDVBVag=
+github.com/shurcooL/home v0.0.0-20181020052607-80b7ffcb30f9/go.mod h1:+rgNQw2P9ARFAs37qieuu7ohDNQ3gds9msbT2yn85sg=
+github.com/shurcooL/htmlg v0.0.0-20170918183704-d01228ac9e50/go.mod h1:zPn1wHpTIePGnXSHpsVPWEktKXHr6+SS6x/IKRb7cpw=
+github.com/shurcooL/httperror v0.0.0-20170206035902-86b7830d14cc/go.mod h1:aYMfkZ6DWSJPJ6c4Wwz3QtW22G7mf/PEgaB9k/ik5+Y=
+github.com/shurcooL/httpfs v0.0.0-20171119174359-809beceb2371/go.mod h1:ZY1cvUeJuFPAdZ/B6v7RHavJWZn2YPVFQ1OSXhCGOkg=
+github.com/shurcooL/httpgzip v0.0.0-20180522190206-b1c53ac65af9/go.mod h1:919LwcH0M7/W4fcZ0/jy0qGght1GIhqyS/EgWGH2j5Q=
+github.com/shurcooL/issues v0.0.0-20181008053335-6292fdc1e191/go.mod h1:e2qWDig5bLteJ4fwvDAc2NHzqFEthkqn7aOZAOpj+PQ=
+github.com/shurcooL/issuesapp v0.0.0-20180602232740-048589ce2241/go.mod h1:NPpHK2TI7iSaM0buivtFUc9offApnI0Alt/K8hcHy0I=
+github.com/shurcooL/notifications v0.0.0-20181007000457-627ab5aea122/go.mod h1:b5uSkrEVM1jQUspwbixRBhaIjIzL2xazXp6kntxYle0=
+github.com/shurcooL/octicon v0.0.0-20181028054416-fa4f57f9efb2/go.mod h1:eWdoE5JD4R5UVWDucdOPg1g2fqQRq78IQa9zlOV1vpQ=
+github.com/shurcooL/reactions v0.0.0-20181006231557-f2e0b4ca5b82/go.mod h1:TCR1lToEk4d2s07G3XGfz2QrgHXg4RJBvjrOozvoWfk=
+github.com/shurcooL/sanitized_anchor_name v0.0.0-20170918181015-86672fcb3f95/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc=
+github.com/shurcooL/users v0.0.0-20180125191416-49c67e49c537/go.mod h1:QJTqeLYEDaXHZDBsXlPCDqdhQuJkuw4NOtaxYe3xii4=
+github.com/shurcooL/webdavfs v0.0.0-20170829043945-18c3829fa133/go.mod h1:hKmq5kWdCj2z2KEozexVbfEZIWiTjhE0+UjmZgPqehw=
+github.com/sourcegraph/annotate v0.0.0-20160123013949-f4cad6c6324d/go.mod h1:UdhH50NIW0fCiwBSr0co2m7BnFLdv4fQTgdqdJTHFeE=
+github.com/sourcegraph/syntaxhighlight v0.0.0-20170531221838-bd320f5d308e/go.mod h1:HuIsMU8RRBOtsCgI77wP899iHVBQpCmg4ErYMZB+2IA=
+github.com/stretchr/testify v1.2.2 h1:bSDNvY7ZPG5RlJ8otE/7V6gMiyenm9RtJ7IUVIAoJ1w=
+github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
+github.com/tarm/serial v0.0.0-20180830185346-98f6abe2eb07/go.mod h1:kDXzergiv9cbyO7IOYJZWg1U88JhDg3PB6klq9Hg2pA=
+github.com/viant/assertly v0.4.8 h1:5x1GzBaRteIwTr5RAGFVG14uNeRFxVNbXPWrK2qAgpc=
+github.com/viant/assertly v0.4.8/go.mod h1:aGifi++jvCrUaklKEKT0BU95igDNaqkvz+49uaYMPRU=
+github.com/viant/toolbox v0.24.0 h1:6TteTDQ68CjgcCe8wH3D3ZhUQQOJXMTbj/D9rkk2a1k=
+github.com/viant/toolbox v0.24.0/go.mod h1:OxMCG57V0PXuIP2HNQrtJf2CjqdmbrOx5EkMILuUhzM=
+go.opencensus.io v0.18.0/go.mod h1:vKdFvxhtzZ9onBp9VKHK8z/sRpBMnKAsufL7wlDrCOA=
+go4.org v0.0.0-20180809161055-417644f6feb5/go.mod h1:MkTOUMDaeVYJUOUsaDXIhWPZYa1yOyC1qaOBpL57BhE=
+golang.org/x/build v0.0.0-20190111050920-041ab4dc3f9d/go.mod h1:OWs+y06UdEOHN4y+MfF/py+xQ/tYqIWW03b70/CG9Rw=
+golang.org/x/crypto v0.0.0-20181030102418-4d3f4d9ffa16/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
+golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
+golang.org/x/crypto v0.0.0-20190313024323-a1f597ede03a h1:YX8ljsm6wXlHZO+aRz9Exqr0evNhKRNe5K/gi+zKh4U=
+golang.org/x/crypto v0.0.0-20190313024323-a1f597ede03a/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
+golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
+golang.org/x/lint v0.0.0-20180702182130-06c8688daad7/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
+golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
+golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU=
+golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
+golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
+golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
+golang.org/x/net v0.0.0-20181029044818-c44066c5c816/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
+golang.org/x/net v0.0.0-20181106065722-10aee1819953/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
+golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
+golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
+golang.org/x/net v0.0.0-20190313220215-9f648a60d977 h1:actzWV6iWn3GLqN8dZjzsB+CLt+gaV2+wsxroxiQI8I=
+golang.org/x/net v0.0.0-20190313220215-9f648a60d977/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
+golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
+golang.org/x/oauth2 v0.0.0-20181017192945-9dcd33a902f4/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
+golang.org/x/oauth2 v0.0.0-20181203162652-d668ce993890/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
+golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421 h1:Wo7BWFiOk0QRFMLYMqJGFMd9CgUAcGx7V+qEg/h5IBI=
+golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
+golang.org/x/perf v0.0.0-20180704124530-6e6d33e29852/go.mod h1:JLpeXjPJfIyPr5TlbXLkXWLhP8nz10XfvxElABhCtcw=
+golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
+golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
+golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
+golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
+golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
+golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
+golang.org/x/sys v0.0.0-20181029174526-d69651ed3497/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
+golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
+golang.org/x/sys v0.0.0-20190316082340-a2f829d7f35f h1:yCrMx/EeIue0+Qca57bWZS7VX6ymEoypmhWyPhz0NHM=
+golang.org/x/sys v0.0.0-20190316082340-a2f829d7f35f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
+golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
+golang.org/x/time v0.0.0-20180412165947-fbb02b2291d2/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
+golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
+golang.org/x/tools v0.0.0-20180828015842-6cd1fcedba52/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
+golang.org/x/tools v0.0.0-20181030000716-a0a13e073c7b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
+golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
+golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY=
+google.golang.org/api v0.0.0-20180910000450-7ca32eb868bf/go.mod h1:4mhQ8q/RsB7i+udVvVy5NUi08OU8ZlA0gRVgrF7VFY0=
+google.golang.org/api v0.0.0-20181030000543-1d582fd0359e/go.mod h1:4mhQ8q/RsB7i+udVvVy5NUi08OU8ZlA0gRVgrF7VFY0=
+google.golang.org/api v0.1.0/go.mod h1:UGEZY7KEX120AnNLIHFMKIo4obdJhkp2tPbaPlQx13Y=
+google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM=
+google.golang.org/appengine v1.2.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
+google.golang.org/appengine v1.3.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
+google.golang.org/appengine v1.4.0 h1:/wp5JvzpHIxhs/dumFmF7BXTf3Z+dd4uXta4kVyO508=
+google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
+google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
+google.golang.org/genproto v0.0.0-20180831171423-11092d34479b/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
+google.golang.org/genproto v0.0.0-20181029155118-b69ba1387ce2/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
+google.golang.org/genproto v0.0.0-20181202183823-bd91e49a0898/go.mod h1:7Ep/1NZk928CDR8SjdVbjWNpdIf6nzjE3BTgJDr2Atg=
+google.golang.org/genproto v0.0.0-20190306203927-b5d61aea6440/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
+google.golang.org/grpc v1.14.0/go.mod h1:yo6s7OP7yaDglbqo1J04qKzAhqBH6lvTonzMVmEdcZw=
+google.golang.org/grpc v1.16.0/go.mod h1:0JHn/cJsOMiMfNA9+DeHDlAU7KAAB5GDlYFpa9MZMio=
+google.golang.org/grpc v1.17.0/go.mod h1:6QZJwpn2B+Zp71q/5VxRsJ6NXXVCE5NRUHRo+f3cWCs=
+google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=
+gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
+gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
+gopkg.in/inf.v0 v0.9.1/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw=
+gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
+gopkg.in/yaml.v2 v2.2.2 h1:ZCJp+EgiOT7lHqUV2J862kp8Qj64Jo6az82+3Td9dZw=
+gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
+grpc.go4.org v0.0.0-20170609214715-11d0a25b4919/go.mod h1:77eQGdRu53HpSqPFJFmuJdjuHRquDANNeA4x7B8WQ9o=
+honnef.co/go/tools v0.0.0-20180728063816-88497007e858/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
+honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
+honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
+sourcegraph.com/sourcegraph/go-diff v0.5.0/go.mod h1:kuch7UrkMzY0X+p9CRK03kfuPQ2zzQcaEFbx8wA8rck=
+sourcegraph.com/sqs/pbtypes v0.0.0-20180604144634-d3ebe8f20ae4/go.mod h1:ketZ/q3QxT9HOBeFhu6RdvsftgpsbFHBF5Cas6cDKZ0=
diff --git a/vendor/github.com/francoispqt/gojay/gojay.go b/vendor/github.com/francoispqt/gojay/gojay.go
new file mode 100644
index 0000000000..d0c542f6b7
--- /dev/null
+++ b/vendor/github.com/francoispqt/gojay/gojay.go
@@ -0,0 +1,10 @@
+// Package gojay implements encoding and decoding of JSON as defined in RFC 7159.
+// The mapping between JSON and Go values is described
+// in the documentation for the Marshal and Unmarshal functions.
+//
+// It aims at performance and usability by relying on simple interfaces
+// to decode and encode structures, slices, arrays and even channels.
+//
+// On top of the simple interfaces to implement, gojay provides lots of helpers to decode and encode
+// multiple of different types natively such as bit.Int, sql.NullString or time.Time
+package gojay
diff --git a/vendor/github.com/francoispqt/gojay/gojay.png b/vendor/github.com/francoispqt/gojay/gojay.png
new file mode 100644
index 0000000000..21090bdd20
Binary files /dev/null and b/vendor/github.com/francoispqt/gojay/gojay.png differ
diff --git a/vendor/github.com/shinji62/logrus-syslog-ng/README.md b/vendor/github.com/shinji62/logrus-syslog-ng/README.md
new file mode 100644
index 0000000000..57314e7400
--- /dev/null
+++ b/vendor/github.com/shinji62/logrus-syslog-ng/README.md
@@ -0,0 +1,70 @@
+# Syslog Hooks for Logrus supporting TLS
+
+## Description
+
+Simple drop-in replacement for the default hook for syslog.
+
+Adding support for TLS and using https://github.com/RackSec/srslog instead of go default `log/syslog` lib.
+
+
+## Usage for tls
+
+Only tcp+tls protocol is supported in this case
+
+```go
+import (
+ syslog "github.com/RackSec/srslog"
+ "github.com/Sirupsen/logrus"
+ logrus_syslog "github.com/shinji62/logrus-syslog-ng"
+)
+
+func main() {
+ log := logrus.New()
+ hook, err := logrus_syslog.NewSyslogHookTLS("localhost:514", syslog.LOG_INFO, "tag","./mycert.pem")
+
+ if err == nil {
+ log.Hooks.Add(hook)
+ }
+}
+```
+
+
+## Usage without TLS
+
+Tcp, udp are supported
+
+```go
+import (
+ syslog "github.com/RackSec/srslog"
+ "github.com/Sirupsen/logrus"
+ logrus_syslog "github.com/shinji62/logrus-syslog-ng"
+)
+
+func main() {
+ log := logrus.New()
+ hook, err := logrus_syslog.NewSyslogHook("udp", "localhost:514", syslog.LOG_INFO, "")
+
+ if err == nil {
+ log.Hooks.Add(hook)
+ }
+}
+```
+
+If you want to connect to local syslog (Ex. "/dev/log" or "/var/run/syslog" or "/var/run/log"). Just assign empty string to the first two parameters of `NewSyslogHook`. It should look like the following.
+
+```go
+import (
+ syslog "github.com/RackSec/srslog"
+ "github.com/Sirupsen/logrus"
+ logrus_syslog "github.com/shinji62/logrus-syslog-ng"
+)
+
+func main() {
+ log := logrus.New()
+ hook, err := logrus_syslog.NewSyslogHook("", "", syslog.LOG_INFO, "")
+
+ if err == nil {
+ log.Hooks.Add(hook)
+ }
+}
+```
diff --git a/vendor/github.com/shinji62/logrus-syslog-ng/glide.lock b/vendor/github.com/shinji62/logrus-syslog-ng/glide.lock
new file mode 100644
index 0000000000..88b419a68a
--- /dev/null
+++ b/vendor/github.com/shinji62/logrus-syslog-ng/glide.lock
@@ -0,0 +1,49 @@
+hash: ab89e9ad4cf3394d549643bff7095cf5a161a19390b75e66515732863bf4c77d
+updated: 2017-09-21T14:10:21.771379602+09:00
+imports:
+- name: github.com/onsi/ginkgo
+ version: 00054c0bb96fc880d4e0be1b90937fad438c5290
+ subpackages:
+ - config
+ - internal/codelocation
+ - internal/containernode
+ - internal/failer
+ - internal/leafnodes
+ - internal/remote
+ - internal/spec
+ - internal/specrunner
+ - internal/suite
+ - internal/testingtproxy
+ - internal/writer
+ - reporters
+ - reporters/stenographer
+ - reporters/stenographer/support/go-colorable
+ - reporters/stenographer/support/go-isatty
+ - types
+- name: github.com/onsi/gomega
+ version: 4dfabf7db2e4147ec99a86db32b2f2a3484cfee8
+ subpackages:
+ - format
+ - internal/assertion
+ - internal/asyncassertion
+ - internal/testingtsupport
+ - matchers
+ - matchers/support/goraph/bipartitegraph
+ - matchers/support/goraph/edge
+ - matchers/support/goraph/node
+ - matchers/support/goraph/util
+ - types
+- name: github.com/RackSec/srslog
+ version: a974ba6f7fb527d2ddc73ee9c05d3e2ccc0af0dc
+- name: github.com/sirupsen/logrus
+ version: 89742aefa4b206dcf400792f3bd35b542998eb3b
+- name: golang.org/x/crypto
+ version: 7d9177d70076375b9a59c8fde23d52d9c4a7ecd5
+ subpackages:
+ - ssh/terminal
+- name: golang.org/x/sys
+ version: d75a52659825e75fff6158388dddc6a5b04f9ba5
+ subpackages:
+ - unix
+ - windows
+testImports: []
diff --git a/vendor/github.com/shinji62/logrus-syslog-ng/glide.yaml b/vendor/github.com/shinji62/logrus-syslog-ng/glide.yaml
new file mode 100644
index 0000000000..fd5a25bf3b
--- /dev/null
+++ b/vendor/github.com/shinji62/logrus-syslog-ng/glide.yaml
@@ -0,0 +1,45 @@
+package: github.com/shinji62/logrus-syslog-ng
+import:
+- package: github.com/RackSec/srslog
+ version: a974ba6f7fb527d2ddc73ee9c05d3e2ccc0af0dc
+- package: github.com/sirupsen/logrus
+ version: 89742aefa4b206dcf400792f3bd35b542998eb3b
+- package: github.com/onsi/ginkgo
+ version: 00054c0bb96fc880d4e0be1b90937fad438c5290
+ subpackages:
+ - config
+ - internal/codelocation
+ - internal/containernode
+ - internal/failer
+ - internal/leafnodes
+ - internal/remote
+ - internal/spec
+ - internal/specrunner
+ - internal/suite
+ - internal/testingtproxy
+ - internal/writer
+ - reporters
+ - reporters/stenographer
+ - reporters/stenographer/support/go-colorable
+ - reporters/stenographer/support/go-isatty
+ - types
+- package: github.com/onsi/gomega
+ version: 4dfabf7db2e4147ec99a86db32b2f2a3484cfee8
+ subpackages:
+ - format
+ - internal/assertion
+ - internal/asyncassertion
+ - internal/testingtsupport
+ - matchers
+ - matchers/support/goraph/bipartitegraph
+ - matchers/support/goraph/edge
+ - matchers/support/goraph/node
+ - matchers/support/goraph/util
+ - types
+- package: golang.org/x/sys
+ version: d75a52659825e75fff6158388dddc6a5b04f9ba5
+ subpackages:
+ - unix
+- package: golang.org/x/crypto
+ subpackages:
+ - ssh/terminal
diff --git a/vendor/github.com/shinji62/logrus-syslog-ng/syslog.go b/vendor/github.com/shinji62/logrus-syslog-ng/syslog.go
new file mode 100644
index 0000000000..2701970483
--- /dev/null
+++ b/vendor/github.com/shinji62/logrus-syslog-ng/syslog.go
@@ -0,0 +1,75 @@
+// +build !windows,!nacl,!plan9
+
+package logrus_syslog
+
+import (
+ "crypto/tls"
+ "crypto/x509"
+ "fmt"
+ "io/ioutil"
+ "os"
+
+ syslog "github.com/RackSec/srslog"
+ "github.com/sirupsen/logrus"
+)
+
+const (
+ SecureProto = "tcp+tls"
+)
+
+// SyslogHook to send logs via syslog.
+type SyslogHook struct {
+ Writer *syslog.Writer
+}
+
+// Creates a hook to be added to an instance of logger. This is called with
+// `hook, err := NewSyslogHook("udp", "localhost:514", syslog.LOG_DEBUG, "")`
+// `if err == nil { log.Hooks.Add(hook) }`
+func NewSyslogHook(network, raddr string, priority syslog.Priority, tag string) (*SyslogHook, error) {
+ w, err := syslog.Dial(network, raddr, priority, tag)
+ return &SyslogHook{w}, err
+}
+
+func NewSyslogHookTls(raddr string, priority syslog.Priority, tag string, certPath string, insecure bool) (*SyslogHook, error) {
+ serverCert, err := ioutil.ReadFile(certPath)
+ if err != nil {
+ return nil, err
+ }
+ pool := x509.NewCertPool()
+ pool.AppendCertsFromPEM(serverCert)
+ config := tls.Config{
+ RootCAs: pool,
+ }
+ config.InsecureSkipVerify = insecure
+ w, err := syslog.DialWithTLSConfig(SecureProto, raddr, priority, tag, &config)
+ return &SyslogHook{w}, err
+}
+
+func (hook *SyslogHook) Fire(entry *logrus.Entry) error {
+ line, err := entry.String()
+ if err != nil {
+ fmt.Fprintf(os.Stderr, "Unable to read entry, %v", err)
+ return err
+ }
+
+ switch entry.Level {
+ case logrus.PanicLevel:
+ return hook.Writer.Crit(line)
+ case logrus.FatalLevel:
+ return hook.Writer.Crit(line)
+ case logrus.ErrorLevel:
+ return hook.Writer.Err(line)
+ case logrus.WarnLevel:
+ return hook.Writer.Warning(line)
+ case logrus.InfoLevel:
+ return hook.Writer.Info(line)
+ case logrus.DebugLevel:
+ return hook.Writer.Debug(line)
+ default:
+ return nil
+ }
+}
+
+func (hook *SyslogHook) Levels() []logrus.Level {
+ return logrus.AllLevels
+}
diff --git a/vendor/github.com/wiggin77/cfg/.gitignore b/vendor/github.com/wiggin77/cfg/.gitignore
new file mode 100644
index 0000000000..f1c181ec9c
--- /dev/null
+++ b/vendor/github.com/wiggin77/cfg/.gitignore
@@ -0,0 +1,12 @@
+# Binaries for programs and plugins
+*.exe
+*.exe~
+*.dll
+*.so
+*.dylib
+
+# Test binary, build with `go test -c`
+*.test
+
+# Output of the go coverage tool, specifically when used with LiteIDE
+*.out
diff --git a/vendor/github.com/wiggin77/cfg/.travis.yml b/vendor/github.com/wiggin77/cfg/.travis.yml
new file mode 100644
index 0000000000..9899b387da
--- /dev/null
+++ b/vendor/github.com/wiggin77/cfg/.travis.yml
@@ -0,0 +1,5 @@
+language: go
+sudo: false
+before_script:
+ - go vet ./...
+
\ No newline at end of file
diff --git a/vendor/github.com/wiggin77/cfg/LICENSE b/vendor/github.com/wiggin77/cfg/LICENSE
new file mode 100644
index 0000000000..2b0bf7efa1
--- /dev/null
+++ b/vendor/github.com/wiggin77/cfg/LICENSE
@@ -0,0 +1,21 @@
+MIT License
+
+Copyright (c) 2018 wiggin77
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
diff --git a/vendor/github.com/wiggin77/cfg/README.md b/vendor/github.com/wiggin77/cfg/README.md
new file mode 100644
index 0000000000..583a82cb19
--- /dev/null
+++ b/vendor/github.com/wiggin77/cfg/README.md
@@ -0,0 +1,43 @@
+# cfg
+
+[](https://godoc.org/github.com/wiggin77/cfg)
+[](https://travis-ci.org/wiggin77/cfg)
+
+Go package for app configuration. Supports chained configuration sources for multiple levels of defaults.
+Includes APIs for loading Linux style configuration files (name/value pairs) or INI files, map based properties,
+or easily create new configuration sources (e.g. load from database).
+
+Supports monitoring configuration sources for changes, hot loading properties, and notifying listeners of changes.
+
+## Usage
+
+```Go
+config := &cfg.Config{}
+defer config.Shutdown() // stops monitoring
+
+// load file via filespec string, os.File
+src, err := Config.NewSrcFileFromFilespec("./myfile.conf")
+if err != nil {
+ return err
+}
+// add src to top of chain, meaning first searched
+cfg.PrependSource(src)
+
+// fetch prop 'retries', default to 3 if not found
+val := config.Int("retries", 3)
+```
+
+See [example](./example_test.go) for more complete example, including listening for configuration changes.
+
+Config API parses the following data types:
+
+| type | method | example property values |
+| ------- | ------ | -------- |
+| string | Config.String | test, "" |
+| int | Config.Int | -1, 77, 0 |
+| int64 | Config.Int64 | -9223372036854775, 372036854775808 |
+| float64 | Config.Float64 | -77.3456, 95642331.1 |
+| bool | Config.Bool | T,t,true,True,1,0,False,false,f,F |
+| time.Duration | Config.Duration | "10ms", "2 hours", "5 min" * |
+
+\* Units of measure supported: ms, sec, min, hour, day, week, year.
diff --git a/vendor/github.com/wiggin77/cfg/config.go b/vendor/github.com/wiggin77/cfg/config.go
new file mode 100644
index 0000000000..0e958102e7
--- /dev/null
+++ b/vendor/github.com/wiggin77/cfg/config.go
@@ -0,0 +1,366 @@
+package cfg
+
+import (
+ "errors"
+ "fmt"
+ "strconv"
+ "strings"
+ "sync"
+ "time"
+
+ "github.com/wiggin77/cfg/timeconv"
+)
+
+// ErrNotFound returned when an operation is attempted on a
+// resource that doesn't exist, such as fetching a non-existing
+// property name.
+var ErrNotFound = errors.New("not found")
+
+type sourceEntry struct {
+ src Source
+ props map[string]string
+}
+
+// Config provides methods for retrieving property values from one or more
+// configuration sources.
+type Config struct {
+ mutexSrc sync.RWMutex
+ mutexListeners sync.RWMutex
+ srcs []*sourceEntry
+ chgListeners []ChangedListener
+ shutdown chan interface{}
+ wantPanicOnError bool
+}
+
+// PrependSource inserts one or more `Sources` at the beginning of
+// the list of sources such that the first source will be the
+// source checked first when resolving a property value.
+func (config *Config) PrependSource(srcs ...Source) {
+ arr := config.wrapSources(srcs...)
+
+ config.mutexSrc.Lock()
+ if config.shutdown == nil {
+ config.shutdown = make(chan interface{})
+ }
+ config.srcs = append(arr, config.srcs...)
+ config.mutexSrc.Unlock()
+
+ for _, se := range arr {
+ if _, ok := se.src.(SourceMonitored); ok {
+ config.monitor(se)
+ }
+ }
+}
+
+// AppendSource appends one or more `Sources` at the end of
+// the list of sources such that the last source will be the
+// source checked last when resolving a property value.
+func (config *Config) AppendSource(srcs ...Source) {
+ arr := config.wrapSources(srcs...)
+
+ config.mutexSrc.Lock()
+ if config.shutdown == nil {
+ config.shutdown = make(chan interface{})
+ }
+ config.srcs = append(config.srcs, arr...)
+ config.mutexSrc.Unlock()
+
+ for _, se := range arr {
+ if _, ok := se.src.(SourceMonitored); ok {
+ config.monitor(se)
+ }
+ }
+}
+
+// wrapSources wraps one or more Source's and returns
+// them as an array of `sourceEntry`.
+func (config *Config) wrapSources(srcs ...Source) []*sourceEntry {
+ arr := make([]*sourceEntry, 0, len(srcs))
+ for _, src := range srcs {
+ se := &sourceEntry{src: src}
+ config.reloadProps(se)
+ arr = append(arr, se)
+ }
+ return arr
+}
+
+// SetWantPanicOnError sets the flag determining if Config
+// should panic when `GetProps` or `GetLastModified` errors
+// for a `Source`.
+func (config *Config) SetWantPanicOnError(b bool) {
+ config.mutexSrc.Lock()
+ config.wantPanicOnError = b
+ config.mutexSrc.Unlock()
+}
+
+// ShouldPanicOnError gets the flag determining if Config
+// should panic when `GetProps` or `GetLastModified` errors
+// for a `Source`.
+func (config *Config) ShouldPanicOnError() (b bool) {
+ config.mutexSrc.RLock()
+ b = config.wantPanicOnError
+ config.mutexSrc.RUnlock()
+ return b
+}
+
+// getProp returns the value of a named property.
+// Each `Source` is checked, in the order created by adding via
+// `AppendSource` and `PrependSource`, until a value for the
+// property is found.
+func (config *Config) getProp(name string) (val string, ok bool) {
+ config.mutexSrc.RLock()
+ defer config.mutexSrc.RUnlock()
+
+ var s string
+ for _, se := range config.srcs {
+ if se.props != nil {
+ if s, ok = se.props[name]; ok {
+ val = strings.TrimSpace(s)
+ return
+ }
+ }
+ }
+ return
+}
+
+// String returns the value of the named prop as a string.
+// If the property is not found then the supplied default `def`
+// and `ErrNotFound` are returned.
+func (config *Config) String(name string, def string) (val string, err error) {
+ if v, ok := config.getProp(name); ok {
+ val = v
+ err = nil
+ return
+ }
+
+ err = ErrNotFound
+ val = def
+ return
+}
+
+// Int returns the value of the named prop as an `int`.
+// If the property is not found then the supplied default `def`
+// and `ErrNotFound` are returned.
+//
+// See config.String
+func (config *Config) Int(name string, def int) (val int, err error) {
+ var s string
+ if s, err = config.String(name, ""); err == nil {
+ var i int64
+ if i, err = strconv.ParseInt(s, 10, 32); err == nil {
+ val = int(i)
+ }
+ }
+ if err != nil {
+ val = def
+ }
+ return
+}
+
+// Int64 returns the value of the named prop as an `int64`.
+// If the property is not found then the supplied default `def`
+// and `ErrNotFound` are returned.
+//
+// See config.String
+func (config *Config) Int64(name string, def int64) (val int64, err error) {
+ var s string
+ if s, err = config.String(name, ""); err == nil {
+ val, err = strconv.ParseInt(s, 10, 64)
+ }
+ if err != nil {
+ val = def
+ }
+ return
+}
+
+// Float64 returns the value of the named prop as a `float64`.
+// If the property is not found then the supplied default `def`
+// and `ErrNotFound` are returned.
+//
+// See config.String
+func (config *Config) Float64(name string, def float64) (val float64, err error) {
+ var s string
+ if s, err = config.String(name, ""); err == nil {
+ val, err = strconv.ParseFloat(s, 64)
+ }
+ if err != nil {
+ val = def
+ }
+ return
+}
+
+// Bool returns the value of the named prop as a `bool`.
+// If the property is not found then the supplied default `def`
+// and `ErrNotFound` are returned.
+//
+// Supports (t, true, 1, y, yes) for true, and (f, false, 0, n, no) for false,
+// all case-insensitive.
+//
+// See config.String
+func (config *Config) Bool(name string, def bool) (val bool, err error) {
+ var s string
+ if s, err = config.String(name, ""); err == nil {
+ switch strings.ToLower(s) {
+ case "t", "true", "1", "y", "yes":
+ val = true
+ case "f", "false", "0", "n", "no":
+ val = false
+ default:
+ err = errors.New("invalid syntax")
+ }
+ }
+ if err != nil {
+ val = def
+ }
+ return
+}
+
+// Duration returns the value of the named prop as a `time.Duration`, representing
+// a span of time.
+//
+// Units of measure are supported: ms, sec, min, hour, day, week, year.
+// See config.UnitsToMillis for a complete list of units supported.
+//
+// If the property is not found then the supplied default `def`
+// and `ErrNotFound` are returned.
+//
+// See config.String
+func (config *Config) Duration(name string, def time.Duration) (val time.Duration, err error) {
+ var s string
+ if s, err = config.String(name, ""); err == nil {
+ var ms int64
+ ms, err = timeconv.ParseMilliseconds(s)
+ val = time.Duration(ms) * time.Millisecond
+ }
+ if err != nil {
+ val = def
+ }
+ return
+}
+
+// AddChangedListener adds a listener that will receive notifications
+// whenever one or more property values change within the config.
+func (config *Config) AddChangedListener(l ChangedListener) {
+ config.mutexListeners.Lock()
+ defer config.mutexListeners.Unlock()
+
+ config.chgListeners = append(config.chgListeners, l)
+}
+
+// RemoveChangedListener removes all instances of a ChangedListener.
+// Returns `ErrNotFound` if the listener was not present.
+func (config *Config) RemoveChangedListener(l ChangedListener) error {
+ config.mutexListeners.Lock()
+ defer config.mutexListeners.Unlock()
+
+ dest := make([]ChangedListener, 0, len(config.chgListeners))
+ err := ErrNotFound
+
+ // Remove all instances of the listener by
+ // copying list while filtering.
+ for _, s := range config.chgListeners {
+ if s != l {
+ dest = append(dest, s)
+ } else {
+ err = nil
+ }
+ }
+ config.chgListeners = dest
+ return err
+}
+
+// Shutdown can be called to stop monitoring of all config sources.
+func (config *Config) Shutdown() {
+ config.mutexSrc.RLock()
+ defer config.mutexSrc.RUnlock()
+ if config.shutdown != nil {
+ close(config.shutdown)
+ }
+}
+
+// onSourceChanged is called whenever one or more properties of a
+// config source has changed.
+func (config *Config) onSourceChanged(src SourceMonitored) {
+ defer func() {
+ if p := recover(); p != nil {
+ fmt.Println(p)
+ }
+ }()
+ config.mutexListeners.RLock()
+ defer config.mutexListeners.RUnlock()
+ for _, l := range config.chgListeners {
+ l.ConfigChanged(config, src)
+ }
+}
+
+// monitor periodically checks a config source for changes.
+func (config *Config) monitor(se *sourceEntry) {
+ go func(se *sourceEntry, shutdown <-chan interface{}) {
+ var src SourceMonitored
+ var ok bool
+ if src, ok = se.src.(SourceMonitored); !ok {
+ return
+ }
+ paused := false
+ last := time.Time{}
+ freq := src.GetMonitorFreq()
+ if freq <= 0 {
+ paused = true
+ freq = 10
+ last, _ = src.GetLastModified()
+ }
+ timer := time.NewTimer(freq)
+ for {
+ select {
+ case <-timer.C:
+ if !paused {
+ if latest, err := src.GetLastModified(); err != nil {
+ if config.ShouldPanicOnError() {
+ panic(fmt.Sprintf("error <%v> getting last modified for %v", err, src))
+ }
+ } else {
+ if last.Before(latest) {
+ last = latest
+ config.reloadProps(se)
+ // TODO: calc diff and provide detailed changes
+ config.onSourceChanged(src)
+ }
+ }
+ }
+ freq = src.GetMonitorFreq()
+ if freq <= 0 {
+ paused = true
+ freq = 10
+ } else {
+ paused = false
+ }
+ timer.Reset(freq)
+ case <-shutdown:
+ // stop the timer and exit
+ if !timer.Stop() {
+ <-timer.C
+ }
+ return
+ }
+ }
+ }(se, config.shutdown)
+}
+
+// reloadProps causes a Source to reload its properties.
+func (config *Config) reloadProps(se *sourceEntry) {
+ config.mutexSrc.Lock()
+ defer config.mutexSrc.Unlock()
+
+ m, err := se.src.GetProps()
+ if err != nil {
+ if config.wantPanicOnError {
+ panic(fmt.Sprintf("GetProps error for %v", se.src))
+ }
+ return
+ }
+
+ se.props = make(map[string]string)
+ for k, v := range m {
+ se.props[k] = v
+ }
+}
diff --git a/vendor/github.com/wiggin77/cfg/go.mod b/vendor/github.com/wiggin77/cfg/go.mod
new file mode 100644
index 0000000000..2e5a038edb
--- /dev/null
+++ b/vendor/github.com/wiggin77/cfg/go.mod
@@ -0,0 +1,5 @@
+module github.com/wiggin77/cfg
+
+go 1.12
+
+require github.com/wiggin77/merror v1.0.2
diff --git a/vendor/github.com/wiggin77/cfg/go.sum b/vendor/github.com/wiggin77/cfg/go.sum
new file mode 100644
index 0000000000..30fd3b5809
--- /dev/null
+++ b/vendor/github.com/wiggin77/cfg/go.sum
@@ -0,0 +1,2 @@
+github.com/wiggin77/merror v1.0.2 h1:V0nH9eFp64ASyaXC+pB5WpvBoCg7NUwvaCSKdzlcHqw=
+github.com/wiggin77/merror v1.0.2/go.mod h1:uQTcIU0Z6jRK4OwqganPYerzQxSFJ4GSHM3aurxxQpg=
diff --git a/vendor/github.com/wiggin77/cfg/ini/ini.go b/vendor/github.com/wiggin77/cfg/ini/ini.go
new file mode 100644
index 0000000000..d28d7444dd
--- /dev/null
+++ b/vendor/github.com/wiggin77/cfg/ini/ini.go
@@ -0,0 +1,167 @@
+package ini
+
+import (
+ "fmt"
+ "io"
+ "io/ioutil"
+ "os"
+ "sync"
+ "time"
+)
+
+// Ini provides parsing and querying of INI format or simple name/value pairs
+// such as a simple config file.
+// A name/value pair format is just an INI with no sections, and properties can
+// be queried using an empty section name.
+type Ini struct {
+ mutex sync.RWMutex
+ m map[string]*Section
+ lm time.Time
+}
+
+// LoadFromFilespec loads an INI file from string containing path and filename.
+func (ini *Ini) LoadFromFilespec(filespec string) error {
+ f, err := os.Open(filespec)
+ if err != nil {
+ return err
+ }
+ return ini.LoadFromFile(f)
+}
+
+// LoadFromFile loads an INI file from `os.File`.
+func (ini *Ini) LoadFromFile(file *os.File) error {
+
+ fi, err := file.Stat()
+ if err != nil {
+ return err
+ }
+ lm := fi.ModTime()
+
+ if err := ini.LoadFromReader(file); err != nil {
+ return err
+ }
+ ini.lm = lm
+ return nil
+}
+
+// LoadFromReader loads an INI file from an `io.Reader`.
+func (ini *Ini) LoadFromReader(reader io.Reader) error {
+ data, err := ioutil.ReadAll(reader)
+ if err != nil {
+ return err
+ }
+ return ini.LoadFromString(string(data))
+}
+
+// LoadFromString parses an INI from a string .
+func (ini *Ini) LoadFromString(s string) error {
+ m, err := getSections(s)
+ if err != nil {
+ return err
+ }
+ ini.mutex.Lock()
+ ini.m = m
+ ini.lm = time.Now()
+ ini.mutex.Unlock()
+ return nil
+}
+
+// GetLastModified returns the last modified timestamp of the
+// INI contents.
+func (ini *Ini) GetLastModified() time.Time {
+ return ini.lm
+}
+
+// GetSectionNames returns the names of all sections in this INI.
+// Note, the returned section names are a snapshot in time, meaning
+// other goroutines may change the contents of this INI as soon as
+// the method returns.
+func (ini *Ini) GetSectionNames() []string {
+ ini.mutex.RLock()
+ defer ini.mutex.RUnlock()
+
+ arr := make([]string, 0, len(ini.m))
+ for key := range ini.m {
+ arr = append(arr, key)
+ }
+ return arr
+}
+
+// GetKeys returns the names of all keys in the specified section.
+// Note, the returned key names are a snapshot in time, meaning other
+// goroutines may change the contents of this INI as soon as the
+// method returns.
+func (ini *Ini) GetKeys(sectionName string) ([]string, error) {
+ sec, err := ini.getSection(sectionName)
+ if err != nil {
+ return nil, err
+ }
+ return sec.getKeys(), nil
+}
+
+// getSection returns the named section.
+func (ini *Ini) getSection(sectionName string) (*Section, error) {
+ ini.mutex.RLock()
+ defer ini.mutex.RUnlock()
+
+ sec, ok := ini.m[sectionName]
+ if !ok {
+ return nil, fmt.Errorf("section '%s' not found", sectionName)
+ }
+ return sec, nil
+}
+
+// GetFlattenedKeys returns all section names plus keys as one
+// flattened array.
+func (ini *Ini) GetFlattenedKeys() []string {
+ ini.mutex.RLock()
+ defer ini.mutex.RUnlock()
+
+ arr := make([]string, 0, len(ini.m)*2)
+ for _, section := range ini.m {
+ keys := section.getKeys()
+ for _, key := range keys {
+ name := section.GetName()
+ if name != "" {
+ key = name + "." + key
+ }
+ arr = append(arr, key)
+ }
+ }
+ return arr
+}
+
+// GetProp returns the value of the specified key in the named section.
+func (ini *Ini) GetProp(section string, key string) (val string, ok bool) {
+ sec, err := ini.getSection(section)
+ if err != nil {
+ return val, false
+ }
+ return sec.GetProp(key)
+}
+
+// ToMap returns a flattened map of the section name plus keys mapped
+// to values.
+func (ini *Ini) ToMap() map[string]string {
+ m := make(map[string]string)
+
+ ini.mutex.RLock()
+ defer ini.mutex.RUnlock()
+
+ for _, section := range ini.m {
+ for _, key := range section.getKeys() {
+ val, ok := section.GetProp(key)
+ if ok {
+ name := section.GetName()
+ var mapkey string
+ if name != "" {
+ mapkey = name + "." + key
+ } else {
+ mapkey = key
+ }
+ m[mapkey] = val
+ }
+ }
+ }
+ return m
+}
diff --git a/vendor/github.com/wiggin77/cfg/ini/parser.go b/vendor/github.com/wiggin77/cfg/ini/parser.go
new file mode 100644
index 0000000000..28916409ae
--- /dev/null
+++ b/vendor/github.com/wiggin77/cfg/ini/parser.go
@@ -0,0 +1,142 @@
+package ini
+
+import (
+ "fmt"
+ "strings"
+
+ "github.com/wiggin77/merror"
+)
+
+// LF is linefeed
+const LF byte = 0x0A
+
+// CR is carriage return
+const CR byte = 0x0D
+
+// getSections parses an INI formatted string, or string containing just name/value pairs,
+// returns map of `Section`'s.
+//
+// Any name/value pairs appearing before a section name are added to the section named
+// with an empty string (""). Also true for Linux-style config files where all props
+// are outside a named section.
+//
+// Any errors encountered are aggregated and returned, along with the partially parsed
+// sections.
+func getSections(str string) (map[string]*Section, error) {
+ merr := merror.New()
+ mapSections := make(map[string]*Section)
+ lines := buildLineArray(str)
+ section := newSection("")
+
+ for _, line := range lines {
+ name, ok := parseSection(line)
+ if ok {
+ // A section name encountered. Stop processing the current one.
+ // Don't add the current section to the map if the section name is blank
+ // and the prop map is empty.
+ nameCurr := section.GetName()
+ if nameCurr != "" || section.hasKeys() {
+ mapSections[nameCurr] = section
+ }
+ // Start processing a new section.
+ section = newSection(name)
+ } else {
+ // Parse the property and add to the current section, or ignore if comment.
+ if k, v, comment, err := parseProp(line); !comment && err == nil {
+ section.setProp(k, v)
+ } else if err != nil {
+ merr.Append(err) // aggregate errors
+ }
+ }
+
+ }
+ // If the current section is not empty, add it.
+ if section.hasKeys() {
+ mapSections[section.GetName()] = section
+ }
+ return mapSections, merr.ErrorOrNil()
+}
+
+// buildLineArray parses the given string buffer and creates a list of strings,
+// one for each line in the string buffer.
+//
+// A line is considered to be terminated by any one of a line feed ('\n'),
+// a carriage return ('\r'), or a carriage return followed immediately by a
+// linefeed.
+//
+// Lines prefixed with ';' or '#' are considered comments and skipped.
+func buildLineArray(str string) []string {
+ arr := make([]string, 0, 10)
+ str = str + "\n"
+
+ iLen := len(str)
+ iPos, iBegin := 0, 0
+ var ch byte
+
+ for iPos < iLen {
+ ch = str[iPos]
+ if ch == LF || ch == CR {
+ sub := str[iBegin:iPos]
+ sub = strings.TrimSpace(sub)
+ if sub != "" && !strings.HasPrefix(sub, ";") && !strings.HasPrefix(sub, "#") {
+ arr = append(arr, sub)
+ }
+ iPos++
+ if ch == CR && iPos < iLen && str[iPos] == LF {
+ iPos++
+ }
+ iBegin = iPos
+ } else {
+ iPos++
+ }
+ }
+ return arr
+}
+
+// parseSection parses the specified string for a section name enclosed in square brackets.
+// Returns the section name found, or `ok=false` if `str` is not a section header.
+func parseSection(str string) (name string, ok bool) {
+ str = strings.TrimSpace(str)
+ if !strings.HasPrefix(str, "[") {
+ return "", false
+ }
+ iCloser := strings.Index(str, "]")
+ if iCloser == -1 {
+ return "", false
+ }
+ return strings.TrimSpace(str[1:iCloser]), true
+}
+
+// parseProp parses the specified string and extracts a key/value pair.
+//
+// If the string is a comment (prefixed with ';' or '#') then `comment=true`
+// and key will be empty.
+func parseProp(str string) (key string, val string, comment bool, err error) {
+ iLen := len(str)
+ iEqPos := strings.Index(str, "=")
+ if iEqPos == -1 {
+ return "", "", false, fmt.Errorf("not a key/value pair:'%s'", str)
+ }
+
+ key = str[0:iEqPos]
+ key = strings.TrimSpace(key)
+ if iEqPos+1 < iLen {
+ val = str[iEqPos+1:]
+ val = strings.TrimSpace(val)
+ }
+
+ // Check that the key has at least 1 char.
+ if key == "" {
+ return "", "", false, fmt.Errorf("key is empty for '%s'", str)
+ }
+
+ // Check if this line is a comment that just happens
+ // to have an equals sign in it. Not an error, but not a
+ // useable line either.
+ if strings.HasPrefix(key, ";") || strings.HasPrefix(key, "#") {
+ key = ""
+ val = ""
+ comment = true
+ }
+ return key, val, comment, err
+}
diff --git a/vendor/github.com/wiggin77/cfg/ini/section.go b/vendor/github.com/wiggin77/cfg/ini/section.go
new file mode 100644
index 0000000000..18c4c25403
--- /dev/null
+++ b/vendor/github.com/wiggin77/cfg/ini/section.go
@@ -0,0 +1,109 @@
+package ini
+
+import (
+ "fmt"
+ "strings"
+ "sync"
+)
+
+// Section represents a section in an INI file. The section has a name, which is
+// enclosed in square brackets in the file. The section also has an array of
+// key/value pairs.
+type Section struct {
+ name string
+ props map[string]string
+ mtx sync.RWMutex
+}
+
+func newSection(name string) *Section {
+ sec := &Section{}
+ sec.name = name
+ sec.props = make(map[string]string)
+ return sec
+}
+
+// addLines addes an array of strings containing name/value pairs
+// of the format `key=value`.
+//func addLines(lines []string) {
+// TODO
+//}
+
+// GetName returns the name of the section.
+func (sec *Section) GetName() (name string) {
+ sec.mtx.RLock()
+ name = sec.name
+ sec.mtx.RUnlock()
+ return
+}
+
+// GetProp returns the value associated with the given key, or
+// `ok=false` if key does not exist.
+func (sec *Section) GetProp(key string) (val string, ok bool) {
+ sec.mtx.RLock()
+ val, ok = sec.props[key]
+ sec.mtx.RUnlock()
+ return
+}
+
+// SetProp sets the value associated with the given key.
+func (sec *Section) setProp(key string, val string) {
+ sec.mtx.Lock()
+ sec.props[key] = val
+ sec.mtx.Unlock()
+}
+
+// hasKeys returns true if there are one or more properties in
+// this section.
+func (sec *Section) hasKeys() (b bool) {
+ sec.mtx.RLock()
+ b = len(sec.props) > 0
+ sec.mtx.RUnlock()
+ return
+}
+
+// getKeys returns an array containing all keys in this section.
+func (sec *Section) getKeys() []string {
+ sec.mtx.RLock()
+ defer sec.mtx.RUnlock()
+
+ arr := make([]string, len(sec.props))
+ idx := 0
+ for k := range sec.props {
+ arr[idx] = k
+ idx++
+ }
+ return arr
+}
+
+// combine the given section with this one.
+func (sec *Section) combine(sec2 *Section) {
+ sec.mtx.Lock()
+ sec2.mtx.RLock()
+ defer sec.mtx.Unlock()
+ defer sec2.mtx.RUnlock()
+
+ for k, v := range sec2.props {
+ sec.props[k] = v
+ }
+}
+
+// String returns a string representation of this section.
+func (sec *Section) String() string {
+ return fmt.Sprintf("[%s]\n%s", sec.GetName(), sec.StringPropsOnly())
+}
+
+// StringPropsOnly returns a string representation of this section
+// without the section header.
+func (sec *Section) StringPropsOnly() string {
+ sec.mtx.RLock()
+ defer sec.mtx.RUnlock()
+ sb := &strings.Builder{}
+
+ for k, v := range sec.props {
+ sb.WriteString(k)
+ sb.WriteString("=")
+ sb.WriteString(v)
+ sb.WriteString("\n")
+ }
+ return sb.String()
+}
diff --git a/vendor/github.com/wiggin77/cfg/listener.go b/vendor/github.com/wiggin77/cfg/listener.go
new file mode 100644
index 0000000000..12ea4e45d6
--- /dev/null
+++ b/vendor/github.com/wiggin77/cfg/listener.go
@@ -0,0 +1,11 @@
+package cfg
+
+// ChangedListener interface is for receiving notifications
+// when one or more properties within monitored config sources
+// (SourceMonitored) have changed values.
+type ChangedListener interface {
+
+ // Changed is called when one or more properties in a `SourceMonitored` has a
+ // changed value.
+ ConfigChanged(cfg *Config, src SourceMonitored)
+}
diff --git a/vendor/github.com/wiggin77/cfg/nocopy.go b/vendor/github.com/wiggin77/cfg/nocopy.go
new file mode 100644
index 0000000000..f2450c0b23
--- /dev/null
+++ b/vendor/github.com/wiggin77/cfg/nocopy.go
@@ -0,0 +1,11 @@
+package cfg
+
+// noCopy may be embedded into structs which must not be copied
+// after the first use.
+//
+// See https://golang.org/issues/8005#issuecomment-190753527
+// for details.
+type noCopy struct{}
+
+// Lock is a no-op used by -copylocks checker from `go vet`.
+func (*noCopy) Lock() {}
diff --git a/vendor/github.com/wiggin77/cfg/source.go b/vendor/github.com/wiggin77/cfg/source.go
new file mode 100644
index 0000000000..09083e970e
--- /dev/null
+++ b/vendor/github.com/wiggin77/cfg/source.go
@@ -0,0 +1,58 @@
+package cfg
+
+import (
+ "sync"
+ "time"
+)
+
+// Source is the interface required for any source of name/value pairs.
+type Source interface {
+
+ // GetProps fetches all the properties from a source and returns
+ // them as a map.
+ GetProps() (map[string]string, error)
+}
+
+// SourceMonitored is the interface required for any config source that is
+// monitored for changes.
+type SourceMonitored interface {
+ Source
+
+ // GetLastModified returns the time of the latest modification to any
+ // property value within the source. If a source does not support
+ // modifying properties at runtime then the zero value for `Time`
+ // should be returned to ensure reload events are not generated.
+ GetLastModified() (time.Time, error)
+
+ // GetMonitorFreq returns the frequency as a `time.Duration` between
+ // checks for changes to this config source.
+ //
+ // Returning zero (or less) will temporarily suspend calls to `GetLastModified`
+ // and `GetMonitorFreq` will be called every 10 seconds until resumed, after which
+ // `GetMontitorFreq` will be called at a frequency roughly equal to the `time.Duration`
+ // returned.
+ GetMonitorFreq() time.Duration
+}
+
+// AbstractSourceMonitor can be embedded in a custom `Source` to provide the
+// basic plumbing for monitor frequency.
+type AbstractSourceMonitor struct {
+ mutex sync.RWMutex
+ freq time.Duration
+}
+
+// GetMonitorFreq returns the frequency as a `time.Duration` between
+// checks for changes to this config source.
+func (asm *AbstractSourceMonitor) GetMonitorFreq() (freq time.Duration) {
+ asm.mutex.RLock()
+ freq = asm.freq
+ asm.mutex.RUnlock()
+ return
+}
+
+// SetMonitorFreq sets the frequency between checks for changes to this config source.
+func (asm *AbstractSourceMonitor) SetMonitorFreq(freq time.Duration) {
+ asm.mutex.Lock()
+ asm.freq = freq
+ asm.mutex.Unlock()
+}
diff --git a/vendor/github.com/wiggin77/cfg/srcfile.go b/vendor/github.com/wiggin77/cfg/srcfile.go
new file mode 100644
index 0000000000..f42c69fac7
--- /dev/null
+++ b/vendor/github.com/wiggin77/cfg/srcfile.go
@@ -0,0 +1,63 @@
+package cfg
+
+import (
+ "os"
+ "time"
+
+ "github.com/wiggin77/cfg/ini"
+)
+
+// SrcFile is a configuration `Source` backed by a file containing
+// name/value pairs or INI format.
+type SrcFile struct {
+ AbstractSourceMonitor
+ ini ini.Ini
+ file *os.File
+}
+
+// NewSrcFileFromFilespec creates a new SrcFile with the specified filespec.
+func NewSrcFileFromFilespec(filespec string) (*SrcFile, error) {
+ file, err := os.Open(filespec)
+ if err != nil {
+ return nil, err
+ }
+ return NewSrcFile(file)
+}
+
+// NewSrcFile creates a new SrcFile with the specified os.File.
+func NewSrcFile(file *os.File) (*SrcFile, error) {
+ sf := &SrcFile{}
+ sf.freq = time.Minute
+ sf.file = file
+ if err := sf.ini.LoadFromFile(file); err != nil {
+ return nil, err
+ }
+ return sf, nil
+}
+
+// GetProps fetches all the properties from a source and returns
+// them as a map.
+func (sf *SrcFile) GetProps() (map[string]string, error) {
+ lm, err := sf.GetLastModified()
+ if err != nil {
+ return nil, err
+ }
+
+ // Check if we need to reload.
+ if sf.ini.GetLastModified() != lm {
+ if err := sf.ini.LoadFromFile(sf.file); err != nil {
+ return nil, err
+ }
+ }
+ return sf.ini.ToMap(), nil
+}
+
+// GetLastModified returns the time of the latest modification to any
+// property value within the source.
+func (sf *SrcFile) GetLastModified() (time.Time, error) {
+ fi, err := sf.file.Stat()
+ if err != nil {
+ return time.Now(), err
+ }
+ return fi.ModTime(), nil
+}
diff --git a/vendor/github.com/wiggin77/cfg/srcmap.go b/vendor/github.com/wiggin77/cfg/srcmap.go
new file mode 100644
index 0000000000..321db27ac9
--- /dev/null
+++ b/vendor/github.com/wiggin77/cfg/srcmap.go
@@ -0,0 +1,78 @@
+package cfg
+
+import (
+ "time"
+)
+
+// SrcMap is a configuration `Source` backed by a simple map.
+type SrcMap struct {
+ AbstractSourceMonitor
+ m map[string]string
+ lm time.Time
+}
+
+// NewSrcMap creates an empty `SrcMap`.
+func NewSrcMap() *SrcMap {
+ sm := &SrcMap{}
+ sm.m = make(map[string]string)
+ sm.lm = time.Now()
+ sm.freq = time.Minute
+ return sm
+}
+
+// NewSrcMapFromMap creates a `SrcMap` containing a copy of the
+// specified map.
+func NewSrcMapFromMap(mapIn map[string]string) *SrcMap {
+ sm := NewSrcMap()
+ sm.PutAll(mapIn)
+ return sm
+}
+
+// Put inserts or updates a value in the `SrcMap`.
+func (sm *SrcMap) Put(key string, val string) {
+ sm.mutex.Lock()
+ sm.m[key] = val
+ sm.lm = time.Now()
+ sm.mutex.Unlock()
+}
+
+// PutAll inserts a copy of `mapIn` into the `SrcMap`
+func (sm *SrcMap) PutAll(mapIn map[string]string) {
+ sm.mutex.Lock()
+ defer sm.mutex.Unlock()
+
+ for k, v := range mapIn {
+ sm.m[k] = v
+ }
+ sm.lm = time.Now()
+}
+
+// GetProps fetches all the properties from a source and returns
+// them as a map.
+func (sm *SrcMap) GetProps() (m map[string]string, err error) {
+ sm.mutex.RLock()
+ m = sm.m
+ sm.mutex.RUnlock()
+ return
+}
+
+// GetLastModified returns the time of the latest modification to any
+// property value within the source. If a source does not support
+// modifying properties at runtime then the zero value for `Time`
+// should be returned to ensure reload events are not generated.
+func (sm *SrcMap) GetLastModified() (last time.Time, err error) {
+ sm.mutex.RLock()
+ last = sm.lm
+ sm.mutex.RUnlock()
+ return
+}
+
+// GetMonitorFreq returns the frequency as a `time.Duration` between
+// checks for changes to this config source. Defaults to 1 minute
+// unless changed with `SetMonitorFreq`.
+func (sm *SrcMap) GetMonitorFreq() (freq time.Duration) {
+ sm.mutex.RLock()
+ freq = sm.freq
+ sm.mutex.RUnlock()
+ return
+}
diff --git a/vendor/github.com/wiggin77/cfg/timeconv/parse.go b/vendor/github.com/wiggin77/cfg/timeconv/parse.go
new file mode 100644
index 0000000000..218ef43a04
--- /dev/null
+++ b/vendor/github.com/wiggin77/cfg/timeconv/parse.go
@@ -0,0 +1,108 @@
+package timeconv
+
+import (
+ "fmt"
+ "math"
+ "regexp"
+ "strconv"
+ "strings"
+)
+
+// MillisPerSecond is the number of millseconds per second.
+const MillisPerSecond int64 = 1000
+
+// MillisPerMinute is the number of millseconds per minute.
+const MillisPerMinute int64 = MillisPerSecond * 60
+
+// MillisPerHour is the number of millseconds per hour.
+const MillisPerHour int64 = MillisPerMinute * 60
+
+// MillisPerDay is the number of millseconds per day.
+const MillisPerDay int64 = MillisPerHour * 24
+
+// MillisPerWeek is the number of millseconds per week.
+const MillisPerWeek int64 = MillisPerDay * 7
+
+// MillisPerYear is the approximate number of millseconds per year.
+const MillisPerYear int64 = MillisPerDay*365 + int64((float64(MillisPerDay) * 0.25))
+
+// ParseMilliseconds parses a string containing a number plus
+// a unit of measure for time and returns the number of milliseconds
+// it represents.
+//
+// Example:
+// * "1 second" returns 1000
+// * "1 minute" returns 60000
+// * "1 hour" returns 3600000
+//
+// See config.UnitsToMillis for a list of supported units of measure.
+func ParseMilliseconds(str string) (int64, error) {
+ s := strings.TrimSpace(str)
+ reg := regexp.MustCompile("([0-9\\.\\-+]*)(.*)")
+ matches := reg.FindStringSubmatch(s)
+ if matches == nil || len(matches) < 1 || matches[1] == "" {
+ return 0, fmt.Errorf("invalid syntax - '%s'", s)
+ }
+ digits := matches[1]
+ units := "ms"
+ if len(matches) > 1 && matches[2] != "" {
+ units = matches[2]
+ }
+
+ fDigits, err := strconv.ParseFloat(digits, 64)
+ if err != nil {
+ return 0, err
+ }
+
+ msPerUnit, err := UnitsToMillis(units)
+ if err != nil {
+ return 0, err
+ }
+
+ // Check for overflow.
+ fms := float64(msPerUnit) * fDigits
+ if fms > math.MaxInt64 || fms < math.MinInt64 {
+ return 0, fmt.Errorf("out of range - '%s' overflows", s)
+ }
+ ms := int64(fms)
+ return ms, nil
+}
+
+// UnitsToMillis returns the number of milliseconds represented by the specified unit of measure.
+//
+// Example:
+// * "second" returns 1000
+// * "minute" returns 60000
+// * "hour" returns 3600000
+//
+// Supported units of measure:
+// * "milliseconds", "millis", "ms", "millisecond"
+// * "seconds", "sec", "s", "second"
+// * "minutes", "mins", "min", "m", "minute"
+// * "hours", "h", "hour"
+// * "days", "d", "day"
+// * "weeks", "w", "week"
+// * "years", "y", "year"
+func UnitsToMillis(units string) (ms int64, err error) {
+ u := strings.TrimSpace(units)
+ u = strings.ToLower(u)
+ switch u {
+ case "milliseconds", "millisecond", "millis", "ms":
+ ms = 1
+ case "seconds", "second", "sec", "s":
+ ms = MillisPerSecond
+ case "minutes", "minute", "mins", "min", "m":
+ ms = MillisPerMinute
+ case "hours", "hour", "h":
+ ms = MillisPerHour
+ case "days", "day", "d":
+ ms = MillisPerDay
+ case "weeks", "week", "w":
+ ms = MillisPerWeek
+ case "years", "year", "y":
+ ms = MillisPerYear
+ default:
+ err = fmt.Errorf("invalid syntax - '%s' not a supported unit of measure", u)
+ }
+ return
+}
diff --git a/vendor/github.com/wiggin77/logr/.gitignore b/vendor/github.com/wiggin77/logr/.gitignore
new file mode 100644
index 0000000000..c2c0a9e2e5
--- /dev/null
+++ b/vendor/github.com/wiggin77/logr/.gitignore
@@ -0,0 +1,36 @@
+# Binaries for programs and plugins
+*.exe
+*.dll
+*.so
+*.dylib
+debug
+dynip
+
+# Test binary, build with `go test -c`
+*.test
+
+# Output of the go coverage tool, specifically when used with LiteIDE
+*.out
+
+# Output of profiler
+*.prof
+
+# Project-local glide cache, RE: https://github.com/Masterminds/glide/issues/736
+.glide/
+
+# IntelliJ config
+.idea
+
+# log files
+*.log
+
+# transient directories
+vendor
+output
+build
+app
+logs
+
+# test apps
+test/cmd/testapp1/testapp1
+test/cmd/simple/simple
diff --git a/vendor/github.com/wiggin77/logr/.travis.yml b/vendor/github.com/wiggin77/logr/.travis.yml
new file mode 100644
index 0000000000..e6c7caf109
--- /dev/null
+++ b/vendor/github.com/wiggin77/logr/.travis.yml
@@ -0,0 +1,4 @@
+language: go
+sudo: false
+go:
+ - 1.x
\ No newline at end of file
diff --git a/vendor/github.com/wiggin77/logr/LICENSE b/vendor/github.com/wiggin77/logr/LICENSE
new file mode 100644
index 0000000000..3bea67884b
--- /dev/null
+++ b/vendor/github.com/wiggin77/logr/LICENSE
@@ -0,0 +1,21 @@
+MIT License
+
+Copyright (c) 2019 wiggin77
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
diff --git a/vendor/github.com/wiggin77/logr/README.md b/vendor/github.com/wiggin77/logr/README.md
new file mode 100644
index 0000000000..17c777a849
--- /dev/null
+++ b/vendor/github.com/wiggin77/logr/README.md
@@ -0,0 +1,194 @@
+# logr
+
+[](http://godoc.org/github.com/wiggin77/logr)
+[](https://travis-ci.com/wiggin77/logr)
+[](https://goreportcard.com/report/github.com/wiggin77/logr)
+
+Logr is a fully asynchronous, contextual logger for Go.
+
+It is very much inspired by [Logrus](https://github.com/sirupsen/logrus) but addresses two issues:
+
+1. Logr is fully asynchronous, meaning that all formatting and writing is done in the background. Latency sensitive applications benefit from not waiting for logging to complete.
+
+2. Logr provides custom filters which provide more flexibility than Trace, Debug, Info... levels. If you need to temporarily increase verbosity of logging while tracking down a problem you can avoid the fire-hose that typically comes from Debug or Trace by using custom filters.
+
+## Concepts
+
+
+| entity | description |
+| ------ | ----------- |
+| Logr | Engine instance typically instantiated once; used to configure logging.
```lgr := &Logr{}```|
+| Logger | Provides contextual logging via fields; lightweight, can be created once and accessed globally or create on demand.
```logger := lgr.NewLogger()```
```logger2 := logger.WithField("user", "Sam")```|
+| Target | A destination for log items such as console, file, database or just about anything that can be written to. Each target has its own filter/level and formatter, and any number of targets can be added to a Logr. Targets for syslog and any io.Writer are built-in and it is easy to create your own. You can also use any [Logrus hooks](https://github.com/sirupsen/logrus/wiki/Hooks) via a simple [adapter](https://github.com/wiggin77/logrus4logr).|
+| Filter | Determines which logging calls get written versus filtered out. Also determines which logging calls generate a stack trace.
```filter := &logr.StdFilter{Lvl: logr.Warn, Stacktrace: logr.Fatal}```|
+| Formatter | Formats the output. Logr includes built-in formatters for JSON and plain text with delimiters. It is easy to create your own formatters or you can also use any [Logrus formatters](https://github.com/sirupsen/logrus#formatters) via a simple [adapter](https://github.com/wiggin77/logrus4logr).
```formatter := &format.Plain{Delim: " \| "}```|
+
+## Usage
+
+```go
+// Create Logr instance.
+lgr := &logr.Logr{}
+
+// Create a filter and formatter. Both can be shared by multiple
+// targets.
+filter := &logr.StdFilter{Lvl: logr.Warn, Stacktrace: logr.Error}
+formatter := &format.Plain{Delim: " | "}
+
+// WriterTarget outputs to any io.Writer
+t := target.NewWriterTarget(filter, formatter, os.StdOut, 1000)
+lgr.AddTarget(t)
+
+// One or more Loggers can be created, shared, used concurrently,
+// or created on demand.
+logger := lgr.NewLogger().WithField("user", "Sarah")
+
+// Now we can log to the target(s).
+logger.Debug("login attempt")
+logger.Error("login failed")
+
+// Ensure targets are drained before application exit.
+lgr.Shutdown()
+```
+
+## Fields
+
+Fields allow for contextual logging, meaning information can be added to log statements without changing the statements themselves. Information can be shared across multiple logging statements thus allowing log analysis tools to group them.
+
+Fields are added via Loggers:
+
+```go
+lgr := &Logr{}
+// ... add targets ...
+logger := lgr.NewLogger().WithFields(logr.Fields{
+ "user": user,
+ "role": role})
+logger.Info("login attempt")
+// ... later ...
+logger.Info("login successful")
+```
+
+`Logger.WithFields` can be used to create additional Loggers that add more fields.
+
+Logr fields are inspired by and work the same as [Logrus fields](https://github.com/sirupsen/logrus#fields).
+
+## Filters
+
+Logr supports the traditional seven log levels via `logr.StdFilter`: Panic, Fatal, Error, Warning, Info, Debug, and Trace.
+
+```go
+// When added to a target, this filter will only allow
+// log statements with level severity Warn or higher.
+// It will also generate stack traces for Error or higher.
+filter := &logr.StdFilter{Lvl: logr.Warn, Stacktrace: logr.Error}
+```
+
+Logr also supports custom filters (logr.CustomFilter) which allow fine grained inclusion of log items without turning on the fire-hose.
+
+```go
+ // create custom levels; use IDs > 10.
+ LoginLevel := logr.Level{ID: 100, Name: "login ", Stacktrace: false}
+ LogoutLevel := logr.Level{ID: 101, Name: "logout", Stacktrace: false}
+
+ lgr := &logr.Logr{}
+
+ // create a custom filter with custom levels.
+ filter := &logr.CustomFilter{}
+ filter.Add(LoginLevel, LogoutLevel)
+
+ formatter := &format.Plain{Delim: " | "}
+ tgr := target.NewWriterTarget(filter, formatter, os.StdOut, 1000)
+ lgr.AddTarget(tgr)
+ logger := lgr.NewLogger().WithFields(logr.Fields{"user": "Bob", "role": "admin"})
+
+ logger.Log(LoginLevel, "this item will get logged")
+ logger.Debug("won't be logged since Debug wasn't added to custom filter")
+```
+
+Both filter types allow you to determine which levels require a stack trace to be output. Note that generating stack traces cannot happen fully asynchronously and thus add latency to the calling goroutine.
+
+## Targets
+
+There are built-in targets for outputting to syslog, file, or any `io.Writer`. More will be added.
+
+You can use any [Logrus hooks](https://github.com/sirupsen/logrus/wiki/Hooks) via a simple [adapter](https://github.com/wiggin77/logrus4logr).
+
+You can create your own target by implementing the [Target](./target.go) interface.
+
+An easier method is to use the [logr.Basic](./target.go) type target and build your functionality on that. Basic handles all the queuing and other plumbing so you only need to implement two methods. Example target that outputs to `io.Writer`:
+
+```go
+type Writer struct {
+ logr.Basic
+ out io.Writer
+}
+
+func NewWriterTarget(filter logr.Filter, formatter logr.Formatter, out io.Writer, maxQueue int) *Writer {
+ w := &Writer{out: out}
+ w.Basic.Start(w, w, filter, formatter, maxQueue)
+ return w
+}
+
+// Write will always be called by a single goroutine, so no locking needed.
+// Just convert a log record to a []byte using the formatter and output the
+// bytes to your sink.
+func (w *Writer) Write(rec *logr.LogRec) error {
+ _, stacktrace := w.IsLevelEnabled(rec.Level())
+
+ // take a buffer from the pool to avoid allocations or just allocate a new one.
+ buf := rec.Logger().Logr().BorrowBuffer()
+ defer rec.Logger().Logr().ReleaseBuffer(buf)
+
+ buf, err := w.Formatter().Format(rec, stacktrace, buf)
+ if err != nil {
+ return err
+ }
+ _, err = w.out.Write(buf.Bytes())
+ return err
+}
+```
+
+## Formatters
+
+Logr has two built-in formatters, one for JSON and the other plain, delimited text.
+
+You can use any [Logrus formatters](https://github.com/sirupsen/logrus#formatters) via a simple [adapter](https://github.com/wiggin77/logrus4logr).
+
+You can create your own formatter by implementing the [Formatter](./formatter.go) interface:
+
+```go
+Format(rec *LogRec, stacktrace bool, buf *bytes.Buffer) (*bytes.Buffer, error)
+```
+
+## Handlers
+
+When creating the Logr instance, you can add several handlers that get called when exceptional events occur:
+
+### ```Logr.OnLoggerError(err error)```
+
+Called any time an internal logging error occurs. For example, this can happen when a target cannot connect to its data sink.
+
+It may be tempting to log this error, however there is a danger that logging this will simply generate another error and so on. If you must log it, use a target and custom level specifically for this event and ensure it cannot generate more errors.
+
+### ```Logr.OnQueueFull func(rec *LogRec, maxQueueSize int) bool```
+
+Called on an attempt to add a log record to a full Logr queue. This generally means the Logr maximum queue size is too small, or at least one target is very slow. Logr maximum queue size can be changed before adding any targets via:
+
+```go
+lgr := logr.Logr{MaxQueueSize: 10000}
+```
+
+Returning true will drop the log record. False will block until the log record can be added, which creates a natural throttle at the expense of latency for the calling goroutine. The default is to block.
+
+### ```Logr.OnTargetQueueFull func(target Target, rec *LogRec, maxQueueSize int) bool```
+
+Called on an attempt to add a log record to a full target queue. This generally means your target's max queue size is too small, or the target is very slow to output.
+
+As with the Logr queue, returning true will drop the log record. False will block until the log record can be added, which creates a natural throttle at the expense of latency for the calling goroutine. The default is to block.
+
+### ```Logr.OnExit func(code int) and Logr.OnPanic func(err interface{})```
+
+OnExit and OnPanic are called when the Logger.FatalXXX and Logger.PanicXXX functions are called respectively.
+
+In both cases the default behavior is to shut down gracefully, draining all targets, and calling `os.Exit` or `panic` respectively.
+
+When adding your own handlers, be sure to call `Logr.Shutdown` before exiting the application to avoid losing log records.
diff --git a/vendor/github.com/wiggin77/logr/config.go b/vendor/github.com/wiggin77/logr/config.go
new file mode 100644
index 0000000000..83d4b0c1c1
--- /dev/null
+++ b/vendor/github.com/wiggin77/logr/config.go
@@ -0,0 +1,11 @@
+package logr
+
+import (
+ "fmt"
+
+ "github.com/wiggin77/cfg"
+)
+
+func ConfigLogger(config *cfg.Config) error {
+ return fmt.Errorf("Not implemented yet")
+}
diff --git a/vendor/github.com/wiggin77/logr/const.go b/vendor/github.com/wiggin77/logr/const.go
new file mode 100644
index 0000000000..a147030759
--- /dev/null
+++ b/vendor/github.com/wiggin77/logr/const.go
@@ -0,0 +1,30 @@
+package logr
+
+import "time"
+
+// Defaults.
+const (
+ // DefaultMaxQueueSize is the default maximum queue size for Logr instances.
+ DefaultMaxQueueSize = 1000
+
+ // DefaultMaxStackFrames is the default maximum max number of stack frames collected
+ // when generating stack traces for logging.
+ DefaultMaxStackFrames = 30
+
+ // DefaultEnqueueTimeout is the default amount of time a log record can take to be queued.
+ // This only applies to blocking enqueue which happen after `logr.OnQueueFull` is called
+ // and returns false.
+ DefaultEnqueueTimeout = time.Second * 30
+
+ // DefaultShutdownTimeout is the default amount of time `logr.Shutdown` can execute before
+ // timing out.
+ DefaultShutdownTimeout = time.Second * 30
+
+ // DefaultFlushTimeout is the default amount of time `logr.Flush` can execute before
+ // timing out.
+ DefaultFlushTimeout = time.Second * 30
+
+ // DefaultMaxPooledBuffer is the maximum size a pooled buffer can be.
+ // Buffers that grow beyond this size are garbage collected.
+ DefaultMaxPooledBuffer = 1024 * 1024
+)
diff --git a/vendor/github.com/wiggin77/logr/filter.go b/vendor/github.com/wiggin77/logr/filter.go
new file mode 100644
index 0000000000..0e0285100d
--- /dev/null
+++ b/vendor/github.com/wiggin77/logr/filter.go
@@ -0,0 +1,26 @@
+package logr
+
+// LevelID is the unique id of each level.
+type LevelID uint8
+
+// Level provides a mechanism to enable/disable specific log lines.
+type Level struct {
+ ID LevelID
+ Name string
+ Stacktrace bool
+}
+
+// String returns the name of this level.
+func (level Level) String() string {
+ return level.Name
+}
+
+// Filter allows targets to determine which Level(s) are active
+// for logging and which Level(s) require a stack trace to be output.
+// A default implementation using "panic, fatal..." is provided, and
+// a more flexible alternative implementation is also provided that
+// allows any number of custom levels.
+type Filter interface {
+ IsEnabled(Level) bool
+ IsStacktraceEnabled(Level) bool
+}
diff --git a/vendor/github.com/wiggin77/logr/format/json.go b/vendor/github.com/wiggin77/logr/format/json.go
new file mode 100644
index 0000000000..8ebc918600
--- /dev/null
+++ b/vendor/github.com/wiggin77/logr/format/json.go
@@ -0,0 +1,241 @@
+package format
+
+import (
+ "bytes"
+ "fmt"
+ "runtime"
+ "sync"
+ "time"
+
+ "github.com/francoispqt/gojay"
+ "github.com/wiggin77/logr"
+)
+
+// JSON formats log records as JSON.
+type JSON struct {
+ // DisableTimestamp disables output of timestamp field.
+ DisableTimestamp bool
+ // DisableLevel disables output of level field.
+ DisableLevel bool
+ // DisableMsg disables output of msg field.
+ DisableMsg bool
+ // DisableContext disables output of all context fields.
+ DisableContext bool
+ // DisableStacktrace disables output of stack trace.
+ DisableStacktrace bool
+
+ // TimestampFormat is an optional format for timestamps. If empty
+ // then DefTimestampFormat is used.
+ TimestampFormat string
+
+ // Indent sets the character used to indent or pretty print the JSON.
+ // Empty string means no pretty print.
+ Indent string
+
+ // EscapeHTML determines if certain characters (e.g. `<`, `>`, `&`)
+ // are escaped.
+ EscapeHTML bool
+
+ // KeyTimestamp overrides the timestamp field key name.
+ KeyTimestamp string
+
+ // KeyLevel overrides the level field key name.
+ KeyLevel string
+
+ // KeyMsg overrides the msg field key name.
+ KeyMsg string
+
+ // KeyContextFields when not empty will group all context fields
+ // under this key.
+ KeyContextFields string
+
+ // KeyStacktrace overrides the stacktrace field key name.
+ KeyStacktrace string
+
+ once sync.Once
+}
+
+// Format converts a log record to bytes in JSON format.
+func (j *JSON) Format(rec *logr.LogRec, stacktrace bool, buf *bytes.Buffer) (*bytes.Buffer, error) {
+ j.once.Do(j.applyDefaultKeyNames)
+
+ if buf == nil {
+ buf = &bytes.Buffer{}
+ }
+ enc := gojay.BorrowEncoder(buf)
+ defer func() {
+ enc.Release()
+ }()
+
+ jlr := JSONLogRec{
+ LogRec: rec,
+ JSON: j,
+ stacktrace: stacktrace,
+ }
+
+ err := enc.EncodeObject(jlr)
+ if err != nil {
+ return nil, err
+ }
+ return buf, nil
+}
+
+func (j *JSON) applyDefaultKeyNames() {
+ if j.KeyTimestamp == "" {
+ j.KeyTimestamp = "timestamp"
+ }
+ if j.KeyLevel == "" {
+ j.KeyLevel = "level"
+ }
+ if j.KeyMsg == "" {
+ j.KeyMsg = "msg"
+ }
+ if j.KeyStacktrace == "" {
+ j.KeyStacktrace = "stacktrace"
+ }
+}
+
+// JSONLogRec decorates a LogRec adding JSON encoding.
+type JSONLogRec struct {
+ *logr.LogRec
+ *JSON
+ stacktrace bool
+}
+
+// MarshalJSONObject encodes the LogRec as JSON.
+func (rec JSONLogRec) MarshalJSONObject(enc *gojay.Encoder) {
+ if !rec.DisableTimestamp {
+ timestampFmt := rec.TimestampFormat
+ if timestampFmt == "" {
+ timestampFmt = logr.DefTimestampFormat
+ }
+ time := rec.Time()
+ enc.AddTimeKey(rec.KeyTimestamp, &time, timestampFmt)
+ }
+ if !rec.DisableLevel {
+ enc.AddStringKey(rec.KeyLevel, rec.Level().Name)
+ }
+ if !rec.DisableMsg {
+ enc.AddStringKey(rec.KeyMsg, rec.Msg())
+ }
+ if !rec.DisableContext {
+ if rec.KeyContextFields != "" {
+ enc.AddObjectKey(rec.KeyContextFields, jsonFields(rec.Fields()))
+ } else {
+ m := rec.Fields()
+ if len(m) > 0 {
+ for k, v := range m {
+ key := rec.prefixCollision(k)
+ encodeField(enc, key, v)
+ }
+ }
+ }
+ }
+ if rec.stacktrace && !rec.DisableStacktrace {
+ frames := rec.StackFrames()
+ if len(frames) > 0 {
+ enc.AddArrayKey(rec.KeyStacktrace, stackFrames(frames))
+ }
+ }
+
+}
+
+// IsNil returns true if the LogRec pointer is nil.
+func (rec JSONLogRec) IsNil() bool {
+ return rec.LogRec == nil
+}
+
+func (rec JSONLogRec) prefixCollision(key string) string {
+ switch key {
+ case rec.KeyTimestamp, rec.KeyLevel, rec.KeyMsg, rec.KeyStacktrace:
+ return rec.prefixCollision("_" + key)
+ }
+ return key
+}
+
+type stackFrames []runtime.Frame
+
+// MarshalJSONArray encodes stackFrames slice as JSON.
+func (s stackFrames) MarshalJSONArray(enc *gojay.Encoder) {
+ for _, frame := range s {
+ enc.AddObject(stackFrame(frame))
+ }
+}
+
+// IsNil returns true if stackFrames is empty slice.
+func (s stackFrames) IsNil() bool {
+ return len(s) == 0
+}
+
+type stackFrame runtime.Frame
+
+// MarshalJSONArray encodes stackFrame as JSON.
+func (f stackFrame) MarshalJSONObject(enc *gojay.Encoder) {
+ enc.AddStringKey("Function", f.Function)
+ enc.AddStringKey("File", f.File)
+ enc.AddIntKey("Line", f.Line)
+}
+
+func (f stackFrame) IsNil() bool {
+ return false
+}
+
+type jsonFields logr.Fields
+
+// MarshalJSONObject encodes Fields map to JSON.
+func (f jsonFields) MarshalJSONObject(enc *gojay.Encoder) {
+ for k, v := range f {
+ encodeField(enc, k, v)
+ }
+}
+
+// IsNil returns true if map is nil.
+func (f jsonFields) IsNil() bool {
+ return f == nil
+}
+
+func encodeField(enc *gojay.Encoder, key string, val interface{}) {
+ switch vt := val.(type) {
+ case gojay.MarshalerJSONObject:
+ enc.AddObjectKey(key, vt)
+ case gojay.MarshalerJSONArray:
+ enc.AddArrayKey(key, vt)
+ case string:
+ enc.AddStringKey(key, vt)
+ case error:
+ enc.AddStringKey(key, vt.Error())
+ case bool:
+ enc.AddBoolKey(key, vt)
+ case int:
+ enc.AddIntKey(key, vt)
+ case int64:
+ enc.AddInt64Key(key, vt)
+ case int32:
+ enc.AddIntKey(key, int(vt))
+ case int16:
+ enc.AddIntKey(key, int(vt))
+ case int8:
+ enc.AddIntKey(key, int(vt))
+ case uint64:
+ enc.AddIntKey(key, int(vt))
+ case uint32:
+ enc.AddIntKey(key, int(vt))
+ case uint16:
+ enc.AddIntKey(key, int(vt))
+ case uint8:
+ enc.AddIntKey(key, int(vt))
+ case float64:
+ enc.AddFloatKey(key, vt)
+ case float32:
+ enc.AddFloat32Key(key, vt)
+ case *gojay.EmbeddedJSON:
+ enc.AddEmbeddedJSONKey(key, vt)
+ case time.Time:
+ enc.AddTimeKey(key, &vt, logr.DefTimestampFormat)
+ case *time.Time:
+ enc.AddTimeKey(key, vt, logr.DefTimestampFormat)
+ default:
+ s := fmt.Sprintf("%v", vt)
+ enc.AddStringKey(key, s)
+ }
+}
diff --git a/vendor/github.com/wiggin77/logr/format/plain.go b/vendor/github.com/wiggin77/logr/format/plain.go
new file mode 100644
index 0000000000..ced9ce54d3
--- /dev/null
+++ b/vendor/github.com/wiggin77/logr/format/plain.go
@@ -0,0 +1,75 @@
+package format
+
+import (
+ "bytes"
+ "fmt"
+
+ "github.com/wiggin77/logr"
+)
+
+// Plain is the simplest formatter, outputting only text with
+// no colors.
+type Plain struct {
+ // DisableTimestamp disables output of timestamp field.
+ DisableTimestamp bool
+ // DisableLevel disables output of level field.
+ DisableLevel bool
+ // DisableMsg disables output of msg field.
+ DisableMsg bool
+ // DisableContext disables output of all context fields.
+ DisableContext bool
+ // DisableStacktrace disables output of stack trace.
+ DisableStacktrace bool
+
+ // Delim is an optional delimiter output between each log field.
+ // Defaults to a single space.
+ Delim string
+
+ // TimestampFormat is an optional format for timestamps. If empty
+ // then DefTimestampFormat is used.
+ TimestampFormat string
+}
+
+// Format converts a log record to bytes.
+func (p *Plain) Format(rec *logr.LogRec, stacktrace bool, buf *bytes.Buffer) (*bytes.Buffer, error) {
+ delim := p.Delim
+ if delim == "" {
+ delim = " "
+ }
+ if buf == nil {
+ buf = &bytes.Buffer{}
+ }
+
+ timestampFmt := p.TimestampFormat
+ if timestampFmt == "" {
+ timestampFmt = logr.DefTimestampFormat
+ }
+
+ if !p.DisableTimestamp {
+ var arr [128]byte
+ tbuf := rec.Time().AppendFormat(arr[:0], timestampFmt)
+ buf.Write(tbuf)
+ buf.WriteString(delim)
+ }
+ if !p.DisableLevel {
+ fmt.Fprintf(buf, "%v%s", rec.Level().Name, delim)
+ }
+ if !p.DisableMsg {
+ fmt.Fprint(buf, rec.Msg(), delim)
+ }
+ if !p.DisableContext {
+ ctx := rec.Fields()
+ if len(ctx) > 0 {
+ logr.WriteFields(buf, ctx, " ")
+ }
+ }
+ if stacktrace && !p.DisableStacktrace {
+ frames := rec.StackFrames()
+ if len(frames) > 0 {
+ buf.WriteString("\n")
+ logr.WriteStacktrace(buf, rec.StackFrames())
+ }
+ }
+ buf.WriteString("\n")
+ return buf, nil
+}
diff --git a/vendor/github.com/wiggin77/logr/formatter.go b/vendor/github.com/wiggin77/logr/formatter.go
new file mode 100644
index 0000000000..bb8df2d414
--- /dev/null
+++ b/vendor/github.com/wiggin77/logr/formatter.go
@@ -0,0 +1,119 @@
+package logr
+
+import (
+ "bytes"
+ "fmt"
+ "io"
+ "runtime"
+ "sort"
+)
+
+// Formatter turns a LogRec into a formatted string.
+type Formatter interface {
+ // Format converts a log record to bytes. If buf is not nil then it will be
+ // be filled with the formatted results, otherwise a new buffer will be allocated.
+ Format(rec *LogRec, stacktrace bool, buf *bytes.Buffer) (*bytes.Buffer, error)
+}
+
+const (
+ // DefTimestampFormat is the default time stamp format used by
+ // Plain formatter and others.
+ DefTimestampFormat = "2006-01-02 15:04:05.000 Z07:00"
+)
+
+// DefaultFormatter is the default formatter, outputting only text with
+// no colors and a space delimiter. Use `format.Plain` instead.
+type DefaultFormatter struct {
+}
+
+// Format converts a log record to bytes.
+func (p *DefaultFormatter) Format(rec *LogRec, stacktrace bool, buf *bytes.Buffer) (*bytes.Buffer, error) {
+ if buf == nil {
+ buf = &bytes.Buffer{}
+ }
+ delim := " "
+ timestampFmt := DefTimestampFormat
+
+ fmt.Fprintf(buf, "%s%s", rec.Time().Format(timestampFmt), delim)
+ fmt.Fprintf(buf, "%v%s", rec.Level(), delim)
+ fmt.Fprint(buf, rec.Msg(), delim)
+
+ ctx := rec.Fields()
+ if len(ctx) > 0 {
+ WriteFields(buf, ctx, " ")
+ }
+
+ if stacktrace {
+ frames := rec.StackFrames()
+ if len(frames) > 0 {
+ buf.WriteString("\n")
+ WriteStacktrace(buf, rec.StackFrames())
+ }
+ }
+ buf.WriteString("\n")
+
+ return buf, nil
+}
+
+// WriteFields writes zero or more name value pairs to the io.Writer.
+// The pairs are sorted by key name and output in key=value format
+// with optional separator between fields.
+func WriteFields(w io.Writer, flds Fields, separator string) {
+ keys := make([]string, 0, len(flds))
+ for k := range flds {
+ keys = append(keys, k)
+ }
+ sort.Strings(keys)
+ sep := ""
+ for _, key := range keys {
+ writeField(w, key, flds[key], sep)
+ sep = separator
+ }
+}
+
+func writeField(w io.Writer, key string, val interface{}, sep string) {
+ var template string
+ switch v := val.(type) {
+ case error:
+ val := v.Error()
+ if shouldQuote(val) {
+ template = "%s%s=%q"
+ } else {
+ template = "%s%s=%s"
+ }
+ case string:
+ if shouldQuote(v) {
+ template = "%s%s=%q"
+ } else {
+ template = "%s%s=%s"
+ }
+ default:
+ template = "%s%s=%v"
+ }
+ fmt.Fprintf(w, template, sep, key, val)
+}
+
+// shouldQuote returns true if val contains any characters that might be unsafe
+// when injecting log output into an aggregator, viewer or report.
+func shouldQuote(val string) bool {
+ for _, c := range val {
+ if !((c >= '0' && c <= '9') ||
+ (c >= 'a' && c <= 'z') ||
+ (c >= 'A' && c <= 'Z')) {
+ return true
+ }
+ }
+ return false
+}
+
+// WriteStacktrace formats and outputs a stack trace to an io.Writer.
+func WriteStacktrace(w io.Writer, frames []runtime.Frame) {
+ for _, frame := range frames {
+ if frame.Function != "" {
+ fmt.Fprintf(w, " %s\n", frame.Function)
+ }
+ if frame.File != "" {
+ fmt.Fprintf(w, " %s:%d\n", frame.File, frame.Line)
+ }
+ }
+}
diff --git a/vendor/github.com/wiggin77/logr/go.mod b/vendor/github.com/wiggin77/logr/go.mod
new file mode 100644
index 0000000000..97cc6bfee2
--- /dev/null
+++ b/vendor/github.com/wiggin77/logr/go.mod
@@ -0,0 +1,11 @@
+module github.com/wiggin77/logr
+
+go 1.12
+
+require (
+ github.com/francoispqt/gojay v1.2.13
+ github.com/nsf/jsondiff v0.0.0-20190712045011-8443391ee9b6
+ github.com/wiggin77/cfg v1.0.2
+ github.com/wiggin77/merror v1.0.2
+ gopkg.in/natefinch/lumberjack.v2 v2.0.0
+)
diff --git a/vendor/github.com/wiggin77/logr/go.sum b/vendor/github.com/wiggin77/logr/go.sum
new file mode 100644
index 0000000000..168e3e8619
--- /dev/null
+++ b/vendor/github.com/wiggin77/logr/go.sum
@@ -0,0 +1,172 @@
+cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
+cloud.google.com/go v0.31.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
+cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
+cloud.google.com/go v0.37.0/go.mod h1:TS1dMSSfndXH133OKGwekG838Om/cQT0BUHV3HcBgoo=
+dmitri.shuralyov.com/app/changes v0.0.0-20180602232624-0a106ad413e3/go.mod h1:Yl+fi1br7+Rr3LqpNJf1/uxUdtRUV+Tnj0o93V2B9MU=
+dmitri.shuralyov.com/html/belt v0.0.0-20180602232347-f7d459c86be0/go.mod h1:JLBrvjyP0v+ecvNYvCpyZgu5/xkfAUhi6wJj28eUfSU=
+dmitri.shuralyov.com/service/change v0.0.0-20181023043359-a85b471d5412/go.mod h1:a1inKt/atXimZ4Mv927x+r7UpyzRUf4emIoiiSC2TN4=
+dmitri.shuralyov.com/state v0.0.0-20180228185332-28bcc343414c/go.mod h1:0PRwlb0D6DFvNNtx+9ybjezNCa8XF0xaYcETyp6rHWU=
+git.apache.org/thrift.git v0.0.0-20180902110319-2566ecd5d999/go.mod h1:fPE2ZNJGynbRyZ4dJvy6G277gSllfV2HJqblrnkyeyg=
+github.com/BurntSushi/toml v0.3.1 h1:WXkYYl6Yr3qBf1K79EBnL4mak0OimBfB0XUf9Vl28OQ=
+github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
+github.com/anmitsu/go-shlex v0.0.0-20161002113705-648efa622239/go.mod h1:2FmKhYUyUczH0OGQWaF5ceTx0UBShxjsH6f8oGKYe2c=
+github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q=
+github.com/bradfitz/go-smtpd v0.0.0-20170404230938-deb6d6237625/go.mod h1:HYsPBTaaSFSlLx/70C2HPIMNZpVV8+vt/A+FMnYP11g=
+github.com/buger/jsonparser v0.0.0-20181115193947-bf1c66bbce23/go.mod h1:bbYlZJ7hK1yFx9hf58LP0zeX7UjIGs20ufpu3evjr+s=
+github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
+github.com/coreos/go-systemd v0.0.0-20181012123002-c6f51f82210d/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4=
+github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
+github.com/dustin/go-humanize v1.0.0/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk=
+github.com/flynn/go-shlex v0.0.0-20150515145356-3f9db97f8568/go.mod h1:xEzjJPgXI435gkrCt3MPfRiAkVrwSbHsst4LCFVfpJc=
+github.com/francoispqt/gojay v1.2.13 h1:d2m3sFjloqoIUQU3TsHBgj6qg/BVGlTBeHDUmyJnXKk=
+github.com/francoispqt/gojay v1.2.13/go.mod h1:ehT5mTG4ua4581f1++1WLG0vPdaA9HaiDsoyrBGkyDY=
+github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
+github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04=
+github.com/gliderlabs/ssh v0.1.1/go.mod h1:U7qILu1NlMHj9FlMhZLlkCdDnU1DBEAqr0aevW3Awn0=
+github.com/go-errors/errors v1.0.1/go.mod h1:f4zRHt4oKfwPJE5k8C9vpYG+aDHdBFUsgrm6/TyX73Q=
+github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ=
+github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
+github.com/golang/lint v0.0.0-20180702182130-06c8688daad7/go.mod h1:tluoj9z5200jBnyusfRPU2LqT6J+DAorxEvtC7LHB+E=
+github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
+github.com/golang/mock v1.2.0/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
+github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
+github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
+github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
+github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M=
+github.com/google/go-github v17.0.0+incompatible/go.mod h1:zLgOLi98H3fifZn+44m+umXrS52loVEgC2AApnigrVQ=
+github.com/google/go-querystring v1.0.0/go.mod h1:odCYkC5MyYFN7vkCjXpyrEuKhc/BUO6wN/zVPAxq5ck=
+github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs=
+github.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc=
+github.com/googleapis/gax-go v2.0.0+incompatible/go.mod h1:SFVmujtThgffbyetf+mdk2eWhX2bMyUtNHzFKcPA9HY=
+github.com/googleapis/gax-go/v2 v2.0.3/go.mod h1:LLvjysVCY1JZeum8Z6l8qUty8fiNwE08qbEPm1M08qg=
+github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY=
+github.com/gregjones/httpcache v0.0.0-20180305231024-9cad4c3443a7/go.mod h1:FecbI9+v66THATjSRHfNgh1IVFe/9kFxbXtjV0ctIMA=
+github.com/grpc-ecosystem/grpc-gateway v1.5.0/go.mod h1:RSKVYQBd5MCa4OVpNdGskqpgL2+G+NZTnrVHpWWfpdw=
+github.com/jellevandenhooff/dkim v0.0.0-20150330215556-f50fe3d243e1/go.mod h1:E0B/fFc00Y+Rasa88328GlI/XbtyysCtTHZS8h7IrBU=
+github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU=
+github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU=
+github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
+github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
+github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
+github.com/kr/pty v1.1.3/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
+github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
+github.com/lunixbochs/vtclean v1.0.0/go.mod h1:pHhQNgMf3btfWnGBVipUOjRYhoOsdGqdm/+2c2E2WMI=
+github.com/mailru/easyjson v0.0.0-20190312143242-1de009706dbe/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc=
+github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0=
+github.com/microcosm-cc/bluemonday v1.0.1/go.mod h1:hsXNsILzKxV+sX77C5b8FSuKF00vh2OMYv+xgHpAMF4=
+github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
+github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
+github.com/neelance/astrewrite v0.0.0-20160511093645-99348263ae86/go.mod h1:kHJEU3ofeGjhHklVoIGuVj85JJwZ6kWPaJwCIxgnFmo=
+github.com/neelance/sourcemap v0.0.0-20151028013722-8c68805598ab/go.mod h1:Qr6/a/Q4r9LP1IltGz7tA7iOK1WonHEYhu1HRBA7ZiM=
+github.com/nsf/jsondiff v0.0.0-20190712045011-8443391ee9b6 h1:qsqscDgSJy+HqgMTR+3NwjYJBbp1+honwDsszLoS+pA=
+github.com/nsf/jsondiff v0.0.0-20190712045011-8443391ee9b6/go.mod h1:uFMI8w+ref4v2r9jz+c9i1IfIttS/OkmLfrk1jne5hs=
+github.com/openzipkin/zipkin-go v0.1.1/go.mod h1:NtoC/o8u3JlF1lSlyPNswIbeQH9bJTmOf0Erfk+hxe8=
+github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
+github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
+github.com/prometheus/client_golang v0.8.0/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw=
+github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo=
+github.com/prometheus/common v0.0.0-20180801064454-c7de2306084e/go.mod h1:daVV7qP5qjZbuso7PdcryaAu0sAZbrN9i7WWcTMWvro=
+github.com/prometheus/procfs v0.0.0-20180725123919-05ee40e3a273/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk=
+github.com/russross/blackfriday v1.5.2/go.mod h1:JO/DiYxRf+HjHt06OyowR9PTA263kcR/rfWxYHBV53g=
+github.com/sergi/go-diff v1.0.0/go.mod h1:0CfEIISq7TuYL3j771MWULgwwjU+GofnZX9QAmXWZgo=
+github.com/shurcooL/component v0.0.0-20170202220835-f88ec8f54cc4/go.mod h1:XhFIlyj5a1fBNx5aJTbKoIq0mNaPvOagO+HjB3EtxrY=
+github.com/shurcooL/events v0.0.0-20181021180414-410e4ca65f48/go.mod h1:5u70Mqkb5O5cxEA8nxTsgrgLehJeAw6Oc4Ab1c/P1HM=
+github.com/shurcooL/github_flavored_markdown v0.0.0-20181002035957-2122de532470/go.mod h1:2dOwnU2uBioM+SGy2aZoq1f/Sd1l9OkAeAUvjSyvgU0=
+github.com/shurcooL/go v0.0.0-20180423040247-9e1955d9fb6e/go.mod h1:TDJrrUr11Vxrven61rcy3hJMUqaf/CLWYhHNPmT14Lk=
+github.com/shurcooL/go-goon v0.0.0-20170922171312-37c2f522c041/go.mod h1:N5mDOmsrJOB+vfqUK+7DmDyjhSLIIBnXo9lvZJj3MWQ=
+github.com/shurcooL/gofontwoff v0.0.0-20180329035133-29b52fc0a18d/go.mod h1:05UtEgK5zq39gLST6uB0cf3NEHjETfB4Fgr3Gx5R9Vw=
+github.com/shurcooL/gopherjslib v0.0.0-20160914041154-feb6d3990c2c/go.mod h1:8d3azKNyqcHP1GaQE/c6dDgjkgSx2BZ4IoEi4F1reUI=
+github.com/shurcooL/highlight_diff v0.0.0-20170515013008-09bb4053de1b/go.mod h1:ZpfEhSmds4ytuByIcDnOLkTHGUI6KNqRNPDLHDk+mUU=
+github.com/shurcooL/highlight_go v0.0.0-20181028180052-98c3abbbae20/go.mod h1:UDKB5a1T23gOMUJrI+uSuH0VRDStOiUVSjBTRDVBVag=
+github.com/shurcooL/home v0.0.0-20181020052607-80b7ffcb30f9/go.mod h1:+rgNQw2P9ARFAs37qieuu7ohDNQ3gds9msbT2yn85sg=
+github.com/shurcooL/htmlg v0.0.0-20170918183704-d01228ac9e50/go.mod h1:zPn1wHpTIePGnXSHpsVPWEktKXHr6+SS6x/IKRb7cpw=
+github.com/shurcooL/httperror v0.0.0-20170206035902-86b7830d14cc/go.mod h1:aYMfkZ6DWSJPJ6c4Wwz3QtW22G7mf/PEgaB9k/ik5+Y=
+github.com/shurcooL/httpfs v0.0.0-20171119174359-809beceb2371/go.mod h1:ZY1cvUeJuFPAdZ/B6v7RHavJWZn2YPVFQ1OSXhCGOkg=
+github.com/shurcooL/httpgzip v0.0.0-20180522190206-b1c53ac65af9/go.mod h1:919LwcH0M7/W4fcZ0/jy0qGght1GIhqyS/EgWGH2j5Q=
+github.com/shurcooL/issues v0.0.0-20181008053335-6292fdc1e191/go.mod h1:e2qWDig5bLteJ4fwvDAc2NHzqFEthkqn7aOZAOpj+PQ=
+github.com/shurcooL/issuesapp v0.0.0-20180602232740-048589ce2241/go.mod h1:NPpHK2TI7iSaM0buivtFUc9offApnI0Alt/K8hcHy0I=
+github.com/shurcooL/notifications v0.0.0-20181007000457-627ab5aea122/go.mod h1:b5uSkrEVM1jQUspwbixRBhaIjIzL2xazXp6kntxYle0=
+github.com/shurcooL/octicon v0.0.0-20181028054416-fa4f57f9efb2/go.mod h1:eWdoE5JD4R5UVWDucdOPg1g2fqQRq78IQa9zlOV1vpQ=
+github.com/shurcooL/reactions v0.0.0-20181006231557-f2e0b4ca5b82/go.mod h1:TCR1lToEk4d2s07G3XGfz2QrgHXg4RJBvjrOozvoWfk=
+github.com/shurcooL/sanitized_anchor_name v0.0.0-20170918181015-86672fcb3f95/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc=
+github.com/shurcooL/users v0.0.0-20180125191416-49c67e49c537/go.mod h1:QJTqeLYEDaXHZDBsXlPCDqdhQuJkuw4NOtaxYe3xii4=
+github.com/shurcooL/webdavfs v0.0.0-20170829043945-18c3829fa133/go.mod h1:hKmq5kWdCj2z2KEozexVbfEZIWiTjhE0+UjmZgPqehw=
+github.com/sourcegraph/annotate v0.0.0-20160123013949-f4cad6c6324d/go.mod h1:UdhH50NIW0fCiwBSr0co2m7BnFLdv4fQTgdqdJTHFeE=
+github.com/sourcegraph/syntaxhighlight v0.0.0-20170531221838-bd320f5d308e/go.mod h1:HuIsMU8RRBOtsCgI77wP899iHVBQpCmg4ErYMZB+2IA=
+github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
+github.com/tarm/serial v0.0.0-20180830185346-98f6abe2eb07/go.mod h1:kDXzergiv9cbyO7IOYJZWg1U88JhDg3PB6klq9Hg2pA=
+github.com/viant/assertly v0.4.8/go.mod h1:aGifi++jvCrUaklKEKT0BU95igDNaqkvz+49uaYMPRU=
+github.com/viant/toolbox v0.24.0/go.mod h1:OxMCG57V0PXuIP2HNQrtJf2CjqdmbrOx5EkMILuUhzM=
+github.com/wiggin77/cfg v1.0.2 h1:NBUX+iJRr+RTncTqTNvajHwzduqbhCQjEqxLHr6Fk7A=
+github.com/wiggin77/cfg v1.0.2/go.mod h1:b3gotba2e5bXTqTW48DwIFoLc+4lWKP7WPi/CdvZ4aE=
+github.com/wiggin77/merror v1.0.2 h1:V0nH9eFp64ASyaXC+pB5WpvBoCg7NUwvaCSKdzlcHqw=
+github.com/wiggin77/merror v1.0.2/go.mod h1:uQTcIU0Z6jRK4OwqganPYerzQxSFJ4GSHM3aurxxQpg=
+go.opencensus.io v0.18.0/go.mod h1:vKdFvxhtzZ9onBp9VKHK8z/sRpBMnKAsufL7wlDrCOA=
+go4.org v0.0.0-20180809161055-417644f6feb5/go.mod h1:MkTOUMDaeVYJUOUsaDXIhWPZYa1yOyC1qaOBpL57BhE=
+golang.org/x/build v0.0.0-20190111050920-041ab4dc3f9d/go.mod h1:OWs+y06UdEOHN4y+MfF/py+xQ/tYqIWW03b70/CG9Rw=
+golang.org/x/crypto v0.0.0-20181030102418-4d3f4d9ffa16/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
+golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
+golang.org/x/crypto v0.0.0-20190313024323-a1f597ede03a/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
+golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
+golang.org/x/lint v0.0.0-20180702182130-06c8688daad7/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
+golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
+golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU=
+golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
+golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
+golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
+golang.org/x/net v0.0.0-20181029044818-c44066c5c816/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
+golang.org/x/net v0.0.0-20181106065722-10aee1819953/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
+golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
+golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
+golang.org/x/net v0.0.0-20190313220215-9f648a60d977/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
+golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
+golang.org/x/oauth2 v0.0.0-20181017192945-9dcd33a902f4/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
+golang.org/x/oauth2 v0.0.0-20181203162652-d668ce993890/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
+golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
+golang.org/x/perf v0.0.0-20180704124530-6e6d33e29852/go.mod h1:JLpeXjPJfIyPr5TlbXLkXWLhP8nz10XfvxElABhCtcw=
+golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
+golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
+golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
+golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
+golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
+golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
+golang.org/x/sys v0.0.0-20181029174526-d69651ed3497/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
+golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
+golang.org/x/sys v0.0.0-20190316082340-a2f829d7f35f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
+golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
+golang.org/x/time v0.0.0-20180412165947-fbb02b2291d2/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
+golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
+golang.org/x/tools v0.0.0-20180828015842-6cd1fcedba52/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
+golang.org/x/tools v0.0.0-20181030000716-a0a13e073c7b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
+golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
+golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY=
+google.golang.org/api v0.0.0-20180910000450-7ca32eb868bf/go.mod h1:4mhQ8q/RsB7i+udVvVy5NUi08OU8ZlA0gRVgrF7VFY0=
+google.golang.org/api v0.0.0-20181030000543-1d582fd0359e/go.mod h1:4mhQ8q/RsB7i+udVvVy5NUi08OU8ZlA0gRVgrF7VFY0=
+google.golang.org/api v0.1.0/go.mod h1:UGEZY7KEX120AnNLIHFMKIo4obdJhkp2tPbaPlQx13Y=
+google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM=
+google.golang.org/appengine v1.2.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
+google.golang.org/appengine v1.3.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
+google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
+google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
+google.golang.org/genproto v0.0.0-20180831171423-11092d34479b/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
+google.golang.org/genproto v0.0.0-20181029155118-b69ba1387ce2/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
+google.golang.org/genproto v0.0.0-20181202183823-bd91e49a0898/go.mod h1:7Ep/1NZk928CDR8SjdVbjWNpdIf6nzjE3BTgJDr2Atg=
+google.golang.org/genproto v0.0.0-20190306203927-b5d61aea6440/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
+google.golang.org/grpc v1.14.0/go.mod h1:yo6s7OP7yaDglbqo1J04qKzAhqBH6lvTonzMVmEdcZw=
+google.golang.org/grpc v1.16.0/go.mod h1:0JHn/cJsOMiMfNA9+DeHDlAU7KAAB5GDlYFpa9MZMio=
+google.golang.org/grpc v1.17.0/go.mod h1:6QZJwpn2B+Zp71q/5VxRsJ6NXXVCE5NRUHRo+f3cWCs=
+google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=
+gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
+gopkg.in/inf.v0 v0.9.1/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw=
+gopkg.in/natefinch/lumberjack.v2 v2.0.0 h1:1Lc07Kr7qY4U2YPouBjpCLxpiyxIVoxqXgkXLknAOE8=
+gopkg.in/natefinch/lumberjack.v2 v2.0.0/go.mod h1:l0ndWWf7gzL7RNwBG7wST/UCcT4T24xpD6X8LsfU/+k=
+gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
+gopkg.in/yaml.v2 v2.2.2 h1:ZCJp+EgiOT7lHqUV2J862kp8Qj64Jo6az82+3Td9dZw=
+gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
+grpc.go4.org v0.0.0-20170609214715-11d0a25b4919/go.mod h1:77eQGdRu53HpSqPFJFmuJdjuHRquDANNeA4x7B8WQ9o=
+honnef.co/go/tools v0.0.0-20180728063816-88497007e858/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
+honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
+honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
+sourcegraph.com/sourcegraph/go-diff v0.5.0/go.mod h1:kuch7UrkMzY0X+p9CRK03kfuPQ2zzQcaEFbx8wA8rck=
+sourcegraph.com/sqs/pbtypes v0.0.0-20180604144634-d3ebe8f20ae4/go.mod h1:ketZ/q3QxT9HOBeFhu6RdvsftgpsbFHBF5Cas6cDKZ0=
diff --git a/vendor/github.com/wiggin77/logr/levelcache.go b/vendor/github.com/wiggin77/logr/levelcache.go
new file mode 100644
index 0000000000..3d0c1c7916
--- /dev/null
+++ b/vendor/github.com/wiggin77/logr/levelcache.go
@@ -0,0 +1,83 @@
+package logr
+
+import (
+ "sync"
+)
+
+// LevelStatus represents whether a level is enabled and
+// requires a stack trace.
+type LevelStatus struct {
+ Enabled bool
+ Stacktrace bool
+ empty bool
+}
+
+type levelCache interface {
+ setup()
+ get(id LevelID) (LevelStatus, bool)
+ put(id LevelID, status LevelStatus)
+ clear()
+}
+
+// syncMapLevelCache uses sync.Map which may better handle large concurrency
+// scenarios.
+type syncMapLevelCache struct {
+ m sync.Map
+}
+
+func (c *syncMapLevelCache) setup() {
+ c.clear()
+}
+
+func (c *syncMapLevelCache) get(id LevelID) (LevelStatus, bool) {
+ s, _ := c.m.Load(id)
+ status := s.(LevelStatus)
+ return status, !status.empty
+}
+
+func (c *syncMapLevelCache) put(id LevelID, status LevelStatus) {
+ c.m.Store(id, status)
+}
+
+func (c *syncMapLevelCache) clear() {
+ var i LevelID
+ for i = 0; i < 255; i++ {
+ c.m.Store(i, LevelStatus{empty: true})
+ }
+}
+
+// arrayLevelCache using array and a mutex.
+type arrayLevelCache struct {
+ arr [256]LevelStatus
+ mux sync.RWMutex
+}
+
+func (c *arrayLevelCache) setup() {
+ c.clear()
+}
+
+//var dummy = LevelStatus{}
+
+func (c *arrayLevelCache) get(id LevelID) (LevelStatus, bool) {
+ c.mux.RLock()
+ status := c.arr[id]
+ ok := !status.empty
+ c.mux.RUnlock()
+ return status, ok
+}
+
+func (c *arrayLevelCache) put(id LevelID, status LevelStatus) {
+ c.mux.Lock()
+ defer c.mux.Unlock()
+
+ c.arr[id] = status
+}
+
+func (c *arrayLevelCache) clear() {
+ c.mux.Lock()
+ defer c.mux.Unlock()
+
+ for i := range c.arr {
+ c.arr[i] = LevelStatus{empty: true}
+ }
+}
diff --git a/vendor/github.com/wiggin77/logr/levelcustom.go b/vendor/github.com/wiggin77/logr/levelcustom.go
new file mode 100644
index 0000000000..384fe4e9ed
--- /dev/null
+++ b/vendor/github.com/wiggin77/logr/levelcustom.go
@@ -0,0 +1,45 @@
+package logr
+
+import (
+ "sync"
+)
+
+// CustomFilter allows targets to enable logging via a list of levels.
+type CustomFilter struct {
+ mux sync.RWMutex
+ levels map[LevelID]Level
+}
+
+// IsEnabled returns true if the specified Level exists in this list.
+func (st *CustomFilter) IsEnabled(level Level) bool {
+ st.mux.RLock()
+ defer st.mux.RUnlock()
+ _, ok := st.levels[level.ID]
+ return ok
+}
+
+// IsStacktraceEnabled returns true if the specified Level requires a stack trace.
+func (st *CustomFilter) IsStacktraceEnabled(level Level) bool {
+ st.mux.RLock()
+ defer st.mux.RUnlock()
+ lvl, ok := st.levels[level.ID]
+ if ok {
+ return lvl.Stacktrace
+ }
+ return false
+}
+
+// Add adds one or more levels to the list. Adding a level enables logging for
+// that level on any targets using this CustomFilter.
+func (st *CustomFilter) Add(levels ...Level) {
+ st.mux.Lock()
+ defer st.mux.Unlock()
+
+ if st.levels == nil {
+ st.levels = make(map[LevelID]Level)
+ }
+
+ for _, s := range levels {
+ st.levels[s.ID] = s
+ }
+}
diff --git a/vendor/github.com/wiggin77/logr/levelstd.go b/vendor/github.com/wiggin77/logr/levelstd.go
new file mode 100644
index 0000000000..f5e0fa4664
--- /dev/null
+++ b/vendor/github.com/wiggin77/logr/levelstd.go
@@ -0,0 +1,37 @@
+package logr
+
+// StdFilter allows targets to filter via classic log levels where any level
+// beyond a certain verbosity/severity is enabled.
+type StdFilter struct {
+ Lvl Level
+ Stacktrace Level
+}
+
+// IsEnabled returns true if the specified Level is at or above this verbosity. Also
+// determines if a stack trace is required.
+func (lt StdFilter) IsEnabled(level Level) bool {
+ return level.ID <= lt.Lvl.ID
+}
+
+// IsStacktraceEnabled returns true if the specified Level requires a stack trace.
+func (lt StdFilter) IsStacktraceEnabled(level Level) bool {
+ return level.ID <= lt.Stacktrace.ID
+}
+
+var (
+ // Panic is the highest level of severity. Logs the message and then panics.
+ Panic = Level{ID: 0, Name: "panic"}
+ // Fatal designates a catastrophic error. Logs the message and then calls
+ // `logr.Exit(1)`.
+ Fatal = Level{ID: 1, Name: "fatal"}
+ // Error designates a serious but possibly recoverable error.
+ Error = Level{ID: 2, Name: "error"}
+ // Warn designates non-critical error.
+ Warn = Level{ID: 3, Name: "warn"}
+ // Info designates information regarding application events.
+ Info = Level{ID: 4, Name: "info"}
+ // Debug designates verbose information typically used for debugging.
+ Debug = Level{ID: 5, Name: "debug"}
+ // Trace designates the highest verbosity of log output.
+ Trace = Level{ID: 6, Name: "trace"}
+)
diff --git a/vendor/github.com/wiggin77/logr/logger.go b/vendor/github.com/wiggin77/logr/logger.go
new file mode 100644
index 0000000000..c2386312f0
--- /dev/null
+++ b/vendor/github.com/wiggin77/logr/logger.go
@@ -0,0 +1,218 @@
+package logr
+
+import (
+ "fmt"
+)
+
+// Fields type, used to pass to `WithFields`.
+type Fields map[string]interface{}
+
+// Logger provides context for logging via fields.
+type Logger struct {
+ logr *Logr
+ fields Fields
+}
+
+// Logr returns the `Logr` instance that created this `Logger`.
+func (logger Logger) Logr() *Logr {
+ return logger.logr
+}
+
+// WithField creates a new `Logger` with any existing fields
+// plus the new one.
+func (logger Logger) WithField(key string, value interface{}) Logger {
+ return logger.WithFields(Fields{key: value})
+}
+
+// WithFields creates a new `Logger` with any existing fields
+// plus the new ones.
+func (logger Logger) WithFields(fields Fields) Logger {
+ l := Logger{logr: logger.logr}
+ // if parent has no fields then avoid creating a new map.
+ oldLen := len(logger.fields)
+ if oldLen == 0 {
+ l.fields = fields
+ return l
+ }
+
+ l.fields = make(Fields, len(fields)+oldLen)
+ for k, v := range logger.fields {
+ l.fields[k] = v
+ }
+ for k, v := range fields {
+ l.fields[k] = v
+ }
+ return l
+}
+
+// Log checks that the level matches one or more targets, and
+// if so, generates a log record that is added to the Logr queue.
+// Arguments are handled in the manner of fmt.Print.
+func (logger Logger) Log(lvl Level, args ...interface{}) {
+ status := logger.logr.IsLevelEnabled(lvl)
+ if status.Enabled {
+ rec := NewLogRec(lvl, logger, "", args, status.Stacktrace)
+ logger.logr.enqueue(rec)
+ }
+}
+
+// Trace is a convenience method equivalent to `Log(TraceLevel, args...)`.
+func (logger Logger) Trace(args ...interface{}) {
+ logger.Log(Trace, args...)
+}
+
+// Debug is a convenience method equivalent to `Log(DebugLevel, args...)`.
+func (logger Logger) Debug(args ...interface{}) {
+ logger.Log(Debug, args...)
+}
+
+// Print ensures compatibility with std lib logger.
+func (logger Logger) Print(args ...interface{}) {
+ logger.Info(args...)
+}
+
+// Info is a convenience method equivalent to `Log(InfoLevel, args...)`.
+func (logger Logger) Info(args ...interface{}) {
+ logger.Log(Info, args...)
+}
+
+// Warn is a convenience method equivalent to `Log(WarnLevel, args...)`.
+func (logger Logger) Warn(args ...interface{}) {
+ logger.Log(Warn, args...)
+}
+
+// Error is a convenience method equivalent to `Log(ErrorLevel, args...)`.
+func (logger Logger) Error(args ...interface{}) {
+ logger.Log(Error, args...)
+}
+
+// Fatal is a convenience method equivalent to `Log(FatalLevel, args...)`
+// followed by a call to os.Exit(1).
+func (logger Logger) Fatal(args ...interface{}) {
+ logger.Log(Fatal, args...)
+ logger.logr.exit(1)
+}
+
+// Panic is a convenience method equivalent to `Log(PanicLevel, args...)`
+// followed by a call to panic().
+func (logger Logger) Panic(args ...interface{}) {
+ logger.Log(Panic, args...)
+ panic(fmt.Sprint(args...))
+}
+
+//
+// Printf style
+//
+
+// Logf checks that the level matches one or more targets, and
+// if so, generates a log record that is added to the main
+// queue (channel). Arguments are handled in the manner of fmt.Printf.
+func (logger Logger) Logf(lvl Level, format string, args ...interface{}) {
+ status := logger.logr.IsLevelEnabled(lvl)
+ if status.Enabled {
+ rec := NewLogRec(lvl, logger, format, args, status.Stacktrace)
+ logger.logr.enqueue(rec)
+ }
+}
+
+// Tracef is a convenience method equivalent to `Logf(TraceLevel, args...)`.
+func (logger Logger) Tracef(format string, args ...interface{}) {
+ logger.Logf(Trace, format, args...)
+}
+
+// Debugf is a convenience method equivalent to `Logf(DebugLevel, args...)`.
+func (logger Logger) Debugf(format string, args ...interface{}) {
+ logger.Logf(Debug, format, args...)
+}
+
+// Infof is a convenience method equivalent to `Logf(InfoLevel, args...)`.
+func (logger Logger) Infof(format string, args ...interface{}) {
+ logger.Logf(Info, format, args...)
+}
+
+// Printf ensures compatibility with std lib logger.
+func (logger Logger) Printf(format string, args ...interface{}) {
+ logger.Infof(format, args...)
+}
+
+// Warnf is a convenience method equivalent to `Logf(WarnLevel, args...)`.
+func (logger Logger) Warnf(format string, args ...interface{}) {
+ logger.Logf(Warn, format, args...)
+}
+
+// Errorf is a convenience method equivalent to `Logf(ErrorLevel, args...)`.
+func (logger Logger) Errorf(format string, args ...interface{}) {
+ logger.Logf(Error, format, args...)
+}
+
+// Fatalf is a convenience method equivalent to `Logf(FatalLevel, args...)`
+// followed by a call to os.Exit(1).
+func (logger Logger) Fatalf(format string, args ...interface{}) {
+ logger.Logf(Fatal, format, args...)
+ logger.logr.exit(1)
+}
+
+// Panicf is a convenience method equivalent to `Logf(PanicLevel, args...)`
+// followed by a call to panic().
+func (logger Logger) Panicf(format string, args ...interface{}) {
+ logger.Logf(Panic, format, args...)
+}
+
+//
+// Println style
+//
+
+// Logln checks that the level matches one or more targets, and
+// if so, generates a log record that is added to the main
+// queue (channel). Arguments are handled in the manner of fmt.Println.
+func (logger Logger) Logln(lvl Level, args ...interface{}) {
+ status := logger.logr.IsLevelEnabled(lvl)
+ if status.Enabled {
+ rec := NewLogRec(lvl, logger, "", args, status.Stacktrace)
+ rec.newline = true
+ logger.logr.enqueue(rec)
+ }
+}
+
+// Traceln is a convenience method equivalent to `Logln(TraceLevel, args...)`.
+func (logger Logger) Traceln(args ...interface{}) {
+ logger.Logln(Trace, args...)
+}
+
+// Debugln is a convenience method equivalent to `Logln(DebugLevel, args...)`.
+func (logger Logger) Debugln(args ...interface{}) {
+ logger.Logln(Debug, args...)
+}
+
+// Infoln is a convenience method equivalent to `Logln(InfoLevel, args...)`.
+func (logger Logger) Infoln(args ...interface{}) {
+ logger.Logln(Info, args...)
+}
+
+// Println ensures compatibility with std lib logger.
+func (logger Logger) Println(args ...interface{}) {
+ logger.Infoln(args...)
+}
+
+// Warnln is a convenience method equivalent to `Logln(WarnLevel, args...)`.
+func (logger Logger) Warnln(args ...interface{}) {
+ logger.Logln(Warn, args...)
+}
+
+// Errorln is a convenience method equivalent to `Logln(ErrorLevel, args...)`.
+func (logger Logger) Errorln(args ...interface{}) {
+ logger.Logln(Error, args...)
+}
+
+// Fatalln is a convenience method equivalent to `Logln(FatalLevel, args...)`
+// followed by a call to os.Exit(1).
+func (logger Logger) Fatalln(args ...interface{}) {
+ logger.Logln(Fatal, args...)
+ logger.logr.exit(1)
+}
+
+// Panicln is a convenience method equivalent to `Logln(PanicLevel, args...)`
+// followed by a call to panic().
+func (logger Logger) Panicln(args ...interface{}) {
+ logger.Logln(Panic, args...)
+}
diff --git a/vendor/github.com/wiggin77/logr/logr.go b/vendor/github.com/wiggin77/logr/logr.go
new file mode 100644
index 0000000000..61fb6a1b38
--- /dev/null
+++ b/vendor/github.com/wiggin77/logr/logr.go
@@ -0,0 +1,461 @@
+package logr
+
+import (
+ "bytes"
+ "context"
+ "errors"
+ "fmt"
+ "os"
+ "sync"
+ "time"
+
+ "github.com/wiggin77/cfg"
+ "github.com/wiggin77/merror"
+)
+
+// Logr maintains a list of log targets and accepts incoming
+// log records.
+type Logr struct {
+ tmux sync.RWMutex // target mutex
+ targets []Target
+
+ mux sync.RWMutex
+ maxQueueSizeActual int
+ in chan *LogRec
+ done chan struct{}
+ once sync.Once
+ shutdown bool
+ lvlCache levelCache
+
+ bufferPool sync.Pool
+
+ // MaxQueueSize is the maximum number of log records that can be queued.
+ // If exceeded, `OnQueueFull` is called which determines if the log
+ // record will be dropped or block until add is successful.
+ // If this is modified, it must be done before `Configure` or
+ // `AddTarget`. Defaults to DefaultMaxQueueSize.
+ MaxQueueSize int
+
+ // OnLoggerError, when not nil, is called any time an internal
+ // logging error occurs. For example, this can happen when a
+ // target cannot connect to its data sink.
+ OnLoggerError func(error)
+
+ // OnQueueFull, when not nil, is called on an attempt to add
+ // a log record to a full Logr queue.
+ // `MaxQueueSize` can be used to modify the maximum queue size.
+ // This function should return quickly, with a bool indicating whether
+ // the log record should be dropped (true) or block until the log record
+ // is successfully added (false). If nil then blocking (false) is assumed.
+ OnQueueFull func(rec *LogRec, maxQueueSize int) bool
+
+ // OnTargetQueueFull, when not nil, is called on an attempt to add
+ // a log record to a full target queue provided the target supports reporting
+ // this condition.
+ // This function should return quickly, with a bool indicating whether
+ // the log record should be dropped (true) or block until the log record
+ // is successfully added (false). If nil then blocking (false) is assumed.
+ OnTargetQueueFull func(target Target, rec *LogRec, maxQueueSize int) bool
+
+ // OnExit, when not nil, is called when a FatalXXX style log API is called.
+ // When nil, then the default behavior is to cleanly shut down this Logr and
+ // call `os.Exit(code)`.
+ OnExit func(code int)
+
+ // OnPanic, when not nil, is called when a PanicXXX style log API is called.
+ // When nil, then the default behavior is to cleanly shut down this Logr and
+ // call `panic(err)`.
+ OnPanic func(err interface{})
+
+ // EnqueueTimeout is the amount of time a log record can take to be queued.
+ // This only applies to blocking enqueue which happen after `logr.OnQueueFull`
+ // is called and returns false.
+ EnqueueTimeout time.Duration
+
+ // ShutdownTimeout is the amount of time `logr.Shutdown` can execute before
+ // timing out.
+ ShutdownTimeout time.Duration
+
+ // FlushTimeout is the amount of time `logr.Flush` can execute before
+ // timing out.
+ FlushTimeout time.Duration
+
+ // UseSyncMapLevelCache can be set to true before the first target is added
+ // when high concurrency (e.g. >32 cores) is expected. This may improve
+ // performance with large numbers of cores - benchmark for your use case.
+ UseSyncMapLevelCache bool
+
+ // MaxPooledFormatBuffer determines the maximum size of a buffer that can be
+ // pooled. To reduce allocations, the buffers needed during formatting (etc)
+ // are pooled. A very large log item will grow a buffer that could stay in
+ // memory indefinitely. This settings lets you control how big a pooled buffer
+ // can be - anything larger will be garbage collected after use.
+ // Defaults to 1MB.
+ MaxPooledBuffer int
+
+ // DisableBufferPool when true disables the buffer pool. See MaxPooledBuffer.
+ DisableBufferPool bool
+}
+
+// Configure adds/removes targets via the supplied `Config`.
+func (logr *Logr) Configure(config *cfg.Config) error {
+ // TODO
+ return fmt.Errorf("not implemented yet")
+}
+
+// AddTarget adds a target to the logger which will receive
+// log records for outputting.
+func (logr *Logr) AddTarget(target Target) error {
+ logr.mux.Lock()
+ defer logr.mux.Unlock()
+
+ if logr.shutdown {
+ return fmt.Errorf("logr shut down")
+ }
+
+ logr.tmux.Lock()
+ defer logr.tmux.Unlock()
+ logr.targets = append(logr.targets, target)
+
+ logr.once.Do(func() {
+ logr.maxQueueSizeActual = logr.MaxQueueSize
+ if logr.maxQueueSizeActual == 0 {
+ logr.maxQueueSizeActual = DefaultMaxQueueSize
+ }
+ if logr.maxQueueSizeActual < 0 {
+ logr.maxQueueSizeActual = 0
+ }
+ logr.in = make(chan *LogRec, logr.maxQueueSizeActual)
+ logr.done = make(chan struct{})
+ if logr.UseSyncMapLevelCache {
+ logr.lvlCache = &syncMapLevelCache{}
+ } else {
+ logr.lvlCache = &arrayLevelCache{}
+ }
+ if logr.MaxPooledBuffer == 0 {
+ logr.MaxPooledBuffer = DefaultMaxPooledBuffer
+ }
+ logr.bufferPool = sync.Pool{
+ New: func() interface{} {
+ return new(bytes.Buffer)
+ },
+ }
+ logr.lvlCache.setup()
+ go logr.start()
+ })
+ logr.resetLevelCache()
+ return nil
+}
+
+// NewLogger creates a Logger using defaults. A `Logger` is light-weight
+// enough to create on-demand, but typically one or more Loggers are
+// created and re-used.
+func (logr *Logr) NewLogger() Logger {
+ logger := Logger{logr: logr}
+ return logger
+}
+
+var levelStatusDisabled = LevelStatus{}
+
+// IsLevelEnabled returns true if at least one target has the specified
+// level enabled. The result is cached so that subsequent checks are fast.
+func (logr *Logr) IsLevelEnabled(lvl Level) LevelStatus {
+ // Check cache. lvlCache may still be nil if no targets added.
+ if logr.lvlCache == nil {
+ return levelStatusDisabled
+ }
+ status, ok := logr.lvlCache.get(lvl.ID)
+ if ok {
+ return status
+ }
+
+ logr.mux.RLock()
+ defer logr.mux.RUnlock()
+
+ // Don't accept new log records after shutdown.
+ if logr.shutdown {
+ return levelStatusDisabled
+ }
+
+ status = LevelStatus{}
+
+ // Check each target.
+ logr.tmux.RLock()
+ defer logr.tmux.RUnlock()
+ for _, t := range logr.targets {
+ e, s := t.IsLevelEnabled(lvl)
+ if e {
+ status.Enabled = true
+ if s {
+ status.Stacktrace = true
+ break // if both enabled then no sense checking more targets
+ }
+ }
+ }
+
+ // Cache and return the result.
+ logr.lvlCache.put(lvl.ID, status)
+ return status
+}
+
+// ResetLevelCache resets the cached results of `IsLevelEnabled`. This is
+// called any time a Target is added or a target's level is changed.
+func (logr *Logr) ResetLevelCache() {
+ // Write lock so that new cache entries cannot be stored while we
+ // clear the cache.
+ logr.mux.Lock()
+ defer logr.mux.Unlock()
+ logr.resetLevelCache()
+}
+
+// resetLevelCache empties the level cache without locking.
+// mux.Lock must be held before calling this function.
+func (logr *Logr) resetLevelCache() {
+ // lvlCache may still be nil if no targets added.
+ if logr.lvlCache != nil {
+ logr.lvlCache.clear()
+ }
+}
+
+// enqueue adds a log record to the logr queue. If the queue is full then
+// this function either blocks or the log record is dropped, depending on
+// the result of calling `OnQueueFull`.
+func (logr *Logr) enqueue(rec *LogRec) {
+ if logr.in == nil {
+ logr.ReportError(fmt.Errorf("AddTarget or Configure must be called before enqueue"))
+ }
+
+ select {
+ case logr.in <- rec:
+ default:
+ if logr.OnQueueFull != nil && logr.OnQueueFull(rec, logr.maxQueueSizeActual) {
+ return // drop the record
+ }
+ select {
+ case <-time.After(logr.enqueueTimeout()):
+ logr.ReportError(fmt.Errorf("enqueue timed out for log rec [%v]", rec))
+ case logr.in <- rec: // block until success or timeout
+ }
+ }
+}
+
+// exit is called by one of the FatalXXX style APIS. If `logr.OnExit` is not nil
+// then that method is called, otherwise the default behavior is to shut down this
+// Logr cleanly then call `os.Exit(code)`.
+func (logr *Logr) exit(code int) {
+ if logr.OnExit != nil {
+ logr.OnExit(code)
+ return
+ }
+
+ if err := logr.Shutdown(); err != nil {
+ logr.ReportError(err)
+ }
+ os.Exit(code)
+}
+
+// panic is called by one of the PanicXXX style APIS. If `logr.OnPanic` is not nil
+// then that method is called, otherwise the default behavior is to shut down this
+// Logr cleanly then call `panic(err)`.
+func (logr *Logr) panic(err interface{}) {
+ if logr.OnPanic != nil {
+ logr.OnPanic(err)
+ return
+ }
+
+ if err := logr.Shutdown(); err != nil {
+ logr.ReportError(err)
+ }
+ panic(err)
+}
+
+// Flush blocks while flushing the logr queue and all target queues, by
+// writing existing log records to valid targets.
+// Any attempts to add new log records will block until flush is complete.
+// `logr.FlushTimeout` determines how long flush can execute before
+// timing out. Use `IsTimeoutError` to determine if the returned error is
+// due to a timeout.
+func (logr *Logr) Flush() error {
+ logr.mux.Lock()
+ defer logr.mux.Unlock()
+
+ ctx, cancel := context.WithTimeout(context.Background(), logr.flushTimeout())
+ defer cancel()
+
+ rec := newFlushLogRec(logr.NewLogger())
+ logr.enqueue(rec)
+
+ select {
+ case <-ctx.Done():
+ return newTimeoutError("logr queue shutdown timeout")
+ case <-rec.flush:
+ }
+ return nil
+}
+
+// Shutdown cleanly stops the logging engine after making best efforts
+// to flush all targets. Call this function right before application
+// exit - logr cannot be restarted once shut down.
+// `logr.ShutdownTimeout` determines how long shutdown can execute before
+// timing out. Use `IsTimeoutError` to determine if the returned error is
+// due to a timeout.
+func (logr *Logr) Shutdown() error {
+ logr.mux.Lock()
+ if logr.shutdown {
+ logr.mux.Unlock()
+ return errors.New("Shutdown called again after shut down")
+ }
+ logr.shutdown = true
+ logr.resetLevelCache()
+ logr.mux.Unlock()
+
+ errs := merror.New()
+
+ ctx, cancel := context.WithTimeout(context.Background(), logr.shutdownTimeout())
+ defer cancel()
+
+ // close the incoming channel and wait for read loop to exit.
+ if logr.in != nil {
+ close(logr.in)
+ select {
+ case <-ctx.Done():
+ errs.Append(newTimeoutError("logr queue shutdown timeout"))
+ case <-logr.done:
+ }
+ }
+
+ // logr.in channel should now be drained to targets and no more log records
+ // can be added.
+ logr.tmux.RLock()
+ defer logr.tmux.RUnlock()
+ for _, t := range logr.targets {
+ err := t.Shutdown(ctx)
+ if err != nil {
+ errs.Append(err)
+ }
+ }
+ return errs.ErrorOrNil()
+}
+
+// ReportError is used to notify the host application of any internal logging errors.
+// If `OnLoggerError` is not nil, it is called with the error, otherwise the error is
+// output to `os.Stderr`.
+func (logr *Logr) ReportError(err interface{}) {
+ if logr.OnLoggerError == nil {
+ fmt.Fprintln(os.Stderr, err)
+ return
+ }
+ logr.OnLoggerError(fmt.Errorf("%v", err))
+}
+
+// BorrowBuffer borrows a buffer from the pool. Release the buffer to reduce garbage collection.
+func (logr *Logr) BorrowBuffer() *bytes.Buffer {
+ if logr.DisableBufferPool {
+ return &bytes.Buffer{}
+ }
+ return logr.bufferPool.Get().(*bytes.Buffer)
+}
+
+// ReleaseBuffer returns a buffer to the pool to reduce garbage collection. The buffer is only
+// retained if less than MaxPooledBuffer.
+func (logr *Logr) ReleaseBuffer(buf *bytes.Buffer) {
+ if !logr.DisableBufferPool && buf.Cap() < logr.MaxPooledBuffer {
+ buf.Reset()
+ logr.bufferPool.Put(buf)
+ }
+}
+
+// enqueueTimeout returns amount of time a log record can take to be queued.
+// This only applies to blocking enqueue which happen after `logr.OnQueueFull` is called
+// and returns false.
+func (logr *Logr) enqueueTimeout() time.Duration {
+ if logr.EnqueueTimeout == 0 {
+ return DefaultEnqueueTimeout
+ }
+ return logr.EnqueueTimeout
+}
+
+// shutdownTimeout returns the timeout duration for `logr.Shutdown`.
+func (logr *Logr) shutdownTimeout() time.Duration {
+ if logr.ShutdownTimeout == 0 {
+ return DefaultShutdownTimeout
+ }
+ return logr.ShutdownTimeout
+}
+
+// flushTimeout returns the timeout duration for `logr.Flush`.
+func (logr *Logr) flushTimeout() time.Duration {
+ if logr.FlushTimeout == 0 {
+ return DefaultFlushTimeout
+ }
+ return logr.FlushTimeout
+}
+
+// start selects on incoming log records until done channel signals.
+// Incoming log records are fanned out to all log targets.
+func (logr *Logr) start() {
+ defer func() {
+ if r := recover(); r != nil {
+ logr.ReportError(r)
+ go logr.start()
+ }
+ }()
+
+ for rec := range logr.in {
+ if rec.flush != nil {
+ logr.flush(rec.flush)
+ } else {
+ rec.prep()
+ logr.fanout(rec)
+ }
+ }
+ close(logr.done)
+}
+
+// fanout pushes a LogRec to all targets.
+func (logr *Logr) fanout(rec *LogRec) {
+ var target Target
+ defer func() {
+ if r := recover(); r != nil {
+ logr.ReportError(fmt.Errorf("fanout failed for target %s, %v", target, r))
+ }
+ }()
+
+ logr.tmux.RLock()
+ defer logr.tmux.RUnlock()
+ for _, target = range logr.targets {
+ if enabled, _ := target.IsLevelEnabled(rec.Level()); enabled {
+ target.Log(rec)
+ }
+ }
+}
+
+// flush drains the queue and notifies when done.
+func (logr *Logr) flush(done chan<- struct{}) {
+ // first drain the logr queue.
+loop:
+ for {
+ var rec *LogRec
+ select {
+ case rec = <-logr.in:
+ if rec.flush == nil {
+ rec.prep()
+ logr.fanout(rec)
+ }
+ default:
+ break loop
+ }
+ }
+
+ logger := logr.NewLogger()
+
+ // drain all the targets; block until finished.
+ logr.tmux.RLock()
+ defer logr.tmux.RUnlock()
+ for _, target := range logr.targets {
+ rec := newFlushLogRec(logger)
+ target.Log(rec)
+ <-rec.flush
+ }
+ done <- struct{}{}
+}
diff --git a/vendor/github.com/wiggin77/logr/logrec.go b/vendor/github.com/wiggin77/logr/logrec.go
new file mode 100644
index 0000000000..9428aaec75
--- /dev/null
+++ b/vendor/github.com/wiggin77/logr/logrec.go
@@ -0,0 +1,189 @@
+package logr
+
+import (
+ "fmt"
+ "runtime"
+ "strings"
+ "sync"
+ "time"
+)
+
+var (
+ logrPkg string
+)
+
+func init() {
+ // Calc current package name
+ pcs := make([]uintptr, 2)
+ _ = runtime.Callers(0, pcs)
+ tmp := runtime.FuncForPC(pcs[1]).Name()
+ logrPkg = getPackageName(tmp)
+}
+
+// LogRec collects raw, unformatted data to be logged.
+// TODO: pool these? how to reliably know when targets are done with them? Copy for each target?
+type LogRec struct {
+ mux sync.RWMutex
+ time time.Time
+
+ level Level
+ logger Logger
+
+ template string
+ newline bool
+ args []interface{}
+
+ stackPC []uintptr
+ stackCount int
+
+ // flushes Logr and target queues when not nil.
+ flush chan struct{}
+
+ // remaining fields calculated by `prep`
+ msg string
+ frames []runtime.Frame
+}
+
+// NewLogRec creates a new LogRec with the current time and optional stack trace.
+func NewLogRec(lvl Level, logger Logger, template string, args []interface{}, incStacktrace bool) *LogRec {
+ rec := &LogRec{time: time.Now(), logger: logger, level: lvl, template: template, args: args}
+ if incStacktrace {
+ rec.stackPC = make([]uintptr, DefaultMaxStackFrames)
+ rec.stackCount = runtime.Callers(2, rec.stackPC)
+ }
+ return rec
+}
+
+// newFlushLogRec creates a LogRec that flushes the Logr queue and
+// any target queues that support flushing.
+func newFlushLogRec(logger Logger) *LogRec {
+ return &LogRec{logger: logger, flush: make(chan struct{})}
+}
+
+// prep resolves all args and field values to strings, and
+// resolves stack trace to frames.
+func (rec *LogRec) prep() {
+ rec.mux.Lock()
+ defer rec.mux.Unlock()
+
+ // resolve args
+ if rec.template == "" {
+ if rec.newline {
+ rec.msg = fmt.Sprintln(rec.args...)
+ } else {
+ rec.msg = fmt.Sprint(rec.args...)
+ }
+ } else {
+ rec.msg = fmt.Sprintf(rec.template, rec.args...)
+ }
+
+ // resolve stack trace
+ if rec.stackCount > 0 {
+ frames := runtime.CallersFrames(rec.stackPC[:rec.stackCount])
+ for {
+ f, more := frames.Next()
+ rec.frames = append(rec.frames, f)
+ if !more {
+ break
+ }
+ }
+
+ // remove leading logr package entries.
+ var start int
+ for i, frame := range rec.frames {
+ pkg := getPackageName(frame.Function)
+ if pkg != "" && pkg != logrPkg {
+ start = i
+ break
+ }
+ }
+ rec.frames = rec.frames[start:]
+ }
+}
+
+// WithTime returns a shallow copy of the log record while replacing
+// the time. This can be used by targets and formatters to adjust
+// the time, or take ownership of the log record.
+func (rec *LogRec) WithTime(time time.Time) *LogRec {
+ rec.mux.RLock()
+ defer rec.mux.RUnlock()
+
+ return &LogRec{
+ time: time,
+ level: rec.level,
+ logger: rec.logger,
+ template: rec.template,
+ newline: rec.newline,
+ args: rec.args,
+ msg: rec.msg,
+ stackPC: rec.stackPC,
+ stackCount: rec.stackCount,
+ frames: rec.frames,
+ }
+}
+
+// Logger returns the `Logger` that created this `LogRec`.
+func (rec *LogRec) Logger() Logger {
+ return rec.logger
+}
+
+// Time returns this log record's time stamp.
+func (rec *LogRec) Time() time.Time {
+ // no locking needed as this field is not mutated.
+ return rec.time
+}
+
+// Level returns this log record's Level.
+func (rec *LogRec) Level() Level {
+ // no locking needed as this field is not mutated.
+ return rec.level
+}
+
+// Fields returns this log record's Fields.
+func (rec *LogRec) Fields() Fields {
+ // no locking needed as this field is not mutated.
+ return rec.logger.fields
+}
+
+// Msg returns this log record's message text.
+func (rec *LogRec) Msg() string {
+ rec.mux.RLock()
+ defer rec.mux.RUnlock()
+ return rec.msg
+}
+
+// StackFrames returns this log record's stack frames or
+// nil if no stack trace was required.
+func (rec *LogRec) StackFrames() []runtime.Frame {
+ rec.mux.RLock()
+ defer rec.mux.RUnlock()
+ return rec.frames
+}
+
+// String returns a string representation of this log record.
+func (rec *LogRec) String() string {
+ if rec.flush != nil {
+ return "[flusher]"
+ }
+
+ f := &DefaultFormatter{}
+ buf := rec.logger.logr.BorrowBuffer()
+ defer rec.logger.logr.ReleaseBuffer(buf)
+ buf, _ = f.Format(rec, true, buf)
+ return strings.TrimSpace(buf.String())
+}
+
+// getPackageName reduces a fully qualified function name to the package name
+// By sirupsen: https://github.com/sirupsen/logrus/blob/master/entry.go
+func getPackageName(f string) string {
+ for {
+ lastPeriod := strings.LastIndex(f, ".")
+ lastSlash := strings.LastIndex(f, "/")
+ if lastPeriod > lastSlash {
+ f = f[:lastPeriod]
+ } else {
+ break
+ }
+ }
+ return f
+}
diff --git a/vendor/github.com/wiggin77/logr/target.go b/vendor/github.com/wiggin77/logr/target.go
new file mode 100644
index 0000000000..bab71ec209
--- /dev/null
+++ b/vendor/github.com/wiggin77/logr/target.go
@@ -0,0 +1,152 @@
+package logr
+
+import (
+ "context"
+ "fmt"
+ "os"
+ "time"
+)
+
+// Target represents a destination for log records such as file,
+// database, TCP socket, etc.
+type Target interface {
+ // IsLevelEnabled returns true if this target should emit
+ // logs for the specified level. Also determines if
+ // a stack trace is required.
+ IsLevelEnabled(Level) (enabled bool, stacktrace bool)
+
+ // Formatter returns the Formatter associated with this Target.
+ Formatter() Formatter
+
+ // Log outputs the log record to this target's destination.
+ Log(rec *LogRec)
+
+ // Shutdown makes best effort to flush target queue and
+ // frees/closes all resources.
+ Shutdown(ctx context.Context) error
+}
+
+// RecordWriter can convert a LogRecord to bytes and output to some data sink.
+type RecordWriter interface {
+ Write(rec *LogRec) error
+}
+
+// Basic provides the basic functionality of a Target that can be used
+// to more easily compose your own Targets. To use, just embed Basic
+// in your target type, implement `RecordWriter`, and call `Start`.
+type Basic struct {
+ target Target
+
+ filter Filter
+ formatter Formatter
+
+ in chan *LogRec
+ done chan struct{}
+ w RecordWriter
+}
+
+// Start initializes this target helper and starts accepting log records for processing.
+func (b *Basic) Start(target Target, rw RecordWriter, filter Filter, formatter Formatter, maxQueued int) {
+ if filter == nil {
+ filter = &StdFilter{Lvl: Fatal}
+ }
+ if formatter == nil {
+ formatter = &DefaultFormatter{}
+ }
+
+ b.target = target
+ b.filter = filter
+ b.formatter = formatter
+ b.in = make(chan *LogRec, maxQueued)
+ b.done = make(chan struct{}, 1)
+ b.w = rw
+ go b.start()
+}
+
+// IsLevelEnabled returns true if this target should emit
+// logs for the specified level. Also determines if
+// a stack trace is required.
+func (b *Basic) IsLevelEnabled(lvl Level) (enabled bool, stacktrace bool) {
+ return b.filter.IsEnabled(lvl), b.filter.IsStacktraceEnabled(lvl)
+}
+
+// Formatter returns the Formatter associated with this Target.
+func (b *Basic) Formatter() Formatter {
+ return b.formatter
+}
+
+// Shutdown stops processing log records after making best
+// effort to flush queue.
+func (b *Basic) Shutdown(ctx context.Context) error {
+ // close the incoming channel and wait for read loop to exit.
+ close(b.in)
+ select {
+ case <-ctx.Done():
+ case <-b.done:
+ }
+
+ // b.in channel should now be drained.
+ return nil
+}
+
+// Log outputs the log record to this targets destination.
+func (b *Basic) Log(rec *LogRec) {
+ lgr := rec.Logger().Logr()
+ select {
+ case b.in <- rec:
+ default:
+ handler := lgr.OnTargetQueueFull
+ if handler != nil && handler(b.target, rec, cap(b.in)) {
+ return // drop the record
+ }
+ select {
+ case <-time.After(lgr.enqueueTimeout()):
+ lgr.ReportError(fmt.Errorf("target enqueue timeout for log rec [%v]", rec))
+ case b.in <- rec: // block until success or timeout
+ }
+ }
+}
+
+// Start accepts log records via In channel and writes to the
+// supplied writer, until Done channel signaled.
+func (b *Basic) start() {
+ defer func() {
+ if r := recover(); r != nil {
+ fmt.Fprintln(os.Stderr, "Basic.start -- ", r)
+ go b.start()
+ }
+ }()
+
+ for rec := range b.in {
+ if rec.flush != nil {
+ b.flush(rec.flush)
+ } else {
+ err := b.w.Write(rec)
+ if err != nil {
+ rec.Logger().Logr().ReportError(err)
+ }
+ }
+ }
+ close(b.done)
+}
+
+// flush drains the queue and notifies when done.
+func (b *Basic) flush(done chan<- struct{}) {
+ for {
+ var rec *LogRec
+ var err error
+ select {
+ case rec = <-b.in:
+ // ignore any redundant flush records.
+ if rec.flush == nil {
+ err = b.w.Write(rec)
+ if err != nil {
+ rec.Logger().Logr().ReportError(err)
+ }
+ }
+ default:
+ done <- struct{}{}
+ return
+ }
+ }
+}
diff --git a/vendor/github.com/wiggin77/logr/timeout.go b/vendor/github.com/wiggin77/logr/timeout.go
new file mode 100644
index 0000000000..37737bcfd6
--- /dev/null
+++ b/vendor/github.com/wiggin77/logr/timeout.go
@@ -0,0 +1,34 @@
+package logr
+
+import "github.com/wiggin77/merror"
+
+// timeoutError is returned from functions that can timeout.
+type timeoutError struct {
+ text string
+}
+
+// newTimeoutError returns a TimeoutError.
+func newTimeoutError(text string) timeoutError {
+ return timeoutError{text: text}
+}
+
+// IsTimeoutError returns true if err is a TimeoutError.
+func IsTimeoutError(err error) bool {
+ if _, ok := err.(timeoutError); ok {
+ return true
+ }
+ // if a multi-error, return true if any of the errors
+ // are TimeoutError
+ if merr, ok := err.(*merror.MError); ok {
+ for _, e := range merr.Errors() {
+ if IsTimeoutError(e) {
+ return true
+ }
+ }
+ }
+ return false
+}
+
+func (err timeoutError) Error() string {
+ return err.text
+}
diff --git a/vendor/github.com/wiggin77/logrus4logr/.gitignore b/vendor/github.com/wiggin77/logrus4logr/.gitignore
new file mode 100644
index 0000000000..632ecb1694
--- /dev/null
+++ b/vendor/github.com/wiggin77/logrus4logr/.gitignore
@@ -0,0 +1,22 @@
+# Binaries for programs and plugins
+*.exe
+*.exe~
+*.dll
+*.so
+*.dylib
+
+# Test binary, build with `go test -c`
+*.test
+
+# Output of the go coverage tool, specifically when used with LiteIDE
+*.out
+
+# Log files
+*.log
+
+# test apps
+test/cmd/textformatter/textformatter
+test/cmd/nestedformatter/nestedformatter
+test/cmd/fluentdformatter/fluentdformatter
+test/cmd/lfshook/lfshook
+test/cmd/lfshook-simple/lfshook-simple
\ No newline at end of file
diff --git a/vendor/github.com/wiggin77/logrus4logr/LICENSE b/vendor/github.com/wiggin77/logrus4logr/LICENSE
new file mode 100644
index 0000000000..3bea67884b
--- /dev/null
+++ b/vendor/github.com/wiggin77/logrus4logr/LICENSE
@@ -0,0 +1,21 @@
+MIT License
+
+Copyright (c) 2019 wiggin77
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
diff --git a/vendor/github.com/wiggin77/logrus4logr/README.md b/vendor/github.com/wiggin77/logrus4logr/README.md
new file mode 100644
index 0000000000..fa891ee719
--- /dev/null
+++ b/vendor/github.com/wiggin77/logrus4logr/README.md
@@ -0,0 +1,91 @@
+# logrus4logr
+
+[](https://godoc.org/github.com/wiggin77/logrus4logr)
+
+Provides adapters for using [Logrus](https://github.com/sirupsen/logrus) hooks and formatters with [Logr](https://github.com/wiggin77/logr).
+
+While Logrus hooks and formatters can easily be modified to work directly with Logr, these adapters are provided for convenience.
+
+## Hooks
+
+A Logrus hook can be adapted to a Logr target. The example below uses [LFSHook](https://github.com/rifflock/lfshook).
+More examples can be found [here](./test/cmd).
+
+```go
+package main
+import (
+ "github.com/rifflock/lfshook"
+ "github.com/sirupsen/logrus"
+ "github.com/wiggin77/logr"
+ "github.com/wiggin77/logrus4logr"
+)
+
+func main() {
+ var lgr = &logr.Logr{}
+
+ // create a Local File System Hook (LFSHook)
+ pathMap := lfshook.PathMap{
+ logrus.InfoLevel: "./info.log",
+ logrus.WarnLevel: "./warn.log",
+ logrus.ErrorLevel: "./error.log",
+ }
+ lfsHook := lfshook.NewHook(pathMap, &logrus.JSONFormatter{})
+
+ // log severity Info or higher.
+ filter := &logr.StdFilter{Lvl: logr.Info}
+
+ // create adapter wrapping lfshook.
+ target := logrus4logr.NewAdapterTarget(filter, nil, lfsHook, 1000)
+ lgr.AddTarget(target)
+
+ // log stuff!
+ logger := lgr.NewLogger().WithField("status", "woot!")
+
+ logger.Info("I'm hooked on Logr")
+ logger.WithField("code", 501).Error("Request failed")
+
+ lgr.Shutdown()
+}
+```
+
+## Formatters
+
+A Logrus formatter can be used by Logr via an adapter. The example below uses Logrus' built-in TextFormatter.
+More examples can be found [here](./test/cmd).
+
+```go
+package main
+import (
+ "github.com/sirupsen/logrus"
+ "github.com/wiggin77/logr"
+ "github.com/wiggin77/logrus4logr"
+)
+
+func main() {
+ var lgr = &logr.Logr{}
+
+ // create a Logrus TextFormatter with whatever settings you prefer.
+ logrusFormatter := &logrus.TextFormatter{
+ // settings...
+ }
+
+ // log severity Info or higher.
+ filter := &logr.StdFilter{Lvl: logr.Info}
+
+ // wrap TextFormatter in Logr adapter.
+ formatter := &logrus4logr.FAdapter{Fmtr: logrusFormatter}
+
+ // create writer target to stdout using adapter.
+ var t logr.Target
+ t = target.NewWriterTarget(filter, formatter, os.Stdout, 1000)
+ lgr.AddTarget(t)
+
+ // log stuff!
+ logger := lgr.NewLogger().WithField("status", "woot!")
+
+ logger.Info("I'm hooked on Logr")
+ logger.WithField("code", 501).Error("Request failed")
+
+ lgr.Shutdown()
+}
+```
diff --git a/vendor/github.com/wiggin77/logrus4logr/convert.go b/vendor/github.com/wiggin77/logrus4logr/convert.go
new file mode 100644
index 0000000000..e1ad8fd4dc
--- /dev/null
+++ b/vendor/github.com/wiggin77/logrus4logr/convert.go
@@ -0,0 +1,51 @@
+package logrus4logr
+
+import (
+ "github.com/wiggin77/logr"
+
+ "github.com/sirupsen/logrus"
+)
+
+func convertLogRec(rec *logr.LogRec, rus *logrus.Logger) *logrus.Entry {
+ entry := &logrus.Entry{
+ Logger: rus,
+ Data: convertFields(rec.Fields()),
+ Time: rec.Time(),
+ Level: convertLevel(rec.Level()),
+ //Caller: *runtime.Frame
+ Message: rec.Msg(),
+ //Buffer *bytes.Buffer
+ //Context context.Context
+ //err string
+ }
+ return entry
+}
+
+func convertLevel(lvl logr.Level) logrus.Level {
+ switch lvl {
+ case logr.Panic:
+ return logrus.PanicLevel
+ case logr.Fatal:
+ return logrus.FatalLevel
+ case logr.Error:
+ return logrus.ErrorLevel
+ case logr.Warn:
+ return logrus.WarnLevel
+ case logr.Info:
+ return logrus.InfoLevel
+ case logr.Debug:
+ return logrus.DebugLevel
+ case logr.Trace:
+ return logrus.TraceLevel
+ default:
+ return logrus.InfoLevel
+ }
+}
+
+func convertFields(flds logr.Fields) logrus.Fields {
+ f := make(logrus.Fields, len(flds))
+ for k, v := range flds {
+ f[k] = v
+ }
+ return f
+}
diff --git a/vendor/github.com/wiggin77/logrus4logr/formatter.go b/vendor/github.com/wiggin77/logrus4logr/formatter.go
new file mode 100644
index 0000000000..3e4521dc4f
--- /dev/null
+++ b/vendor/github.com/wiggin77/logrus4logr/formatter.go
@@ -0,0 +1,36 @@
+package logrus4logr
+
+import (
+ "bytes"
+ "sync"
+
+ "github.com/sirupsen/logrus"
+ "github.com/wiggin77/logr"
+)
+
+// FAdapter wraps a Logrus formatter so it can be used as a Logr formatter.
+type FAdapter struct {
+ // Fmtr is the Logrus formatter to wrap.
+ Fmtr logrus.Formatter
+
+ // Logger is an optional logrus.Logger instance to use instead of the default.
+ Logger *logrus.Logger
+
+ once sync.Once
+}
+
+// Format converts a log record to bytes using a Logrus formatter.
+func (a *FAdapter) Format(rec *logr.LogRec, stacktrace bool, buf *bytes.Buffer) (*bytes.Buffer, error) {
+ a.once.Do(func() {
+ if a.Logger == nil {
+ a.Logger = logrus.StandardLogger()
+ }
+ })
+ entry := convertLogRec(rec, a.Logger)
+
+ data, err := a.Fmtr.Format(entry)
+ if err == nil {
+ buf.Write(data)
+ }
+ return buf, err
+}
diff --git a/vendor/github.com/wiggin77/logrus4logr/go.mod b/vendor/github.com/wiggin77/logrus4logr/go.mod
new file mode 100644
index 0000000000..0d08496cff
--- /dev/null
+++ b/vendor/github.com/wiggin77/logrus4logr/go.mod
@@ -0,0 +1,15 @@
+module github.com/wiggin77/logrus4logr
+
+go 1.13
+
+require (
+ github.com/Freman/eventloghook v0.0.0-20191003051739-e4d803b6b48b
+ github.com/antonfisher/nested-logrus-formatter v1.0.2
+ github.com/joonix/log v0.0.0-20190524090622-13fe31bbdd7a
+ github.com/konsorten/go-windows-terminal-sequences v1.0.2 // indirect
+ github.com/rifflock/lfshook v0.0.0-20180920164130-b9218ef580f5
+ github.com/sirupsen/logrus v1.4.2
+ github.com/wiggin77/logr v1.0.3
+ golang.org/x/sys v0.0.0-20191008105621-543471e840be
+ google.golang.org/genproto v0.0.0-20191007204434-a023cd5227bd // indirect
+)
diff --git a/vendor/github.com/wiggin77/logrus4logr/go.sum b/vendor/github.com/wiggin77/logrus4logr/go.sum
new file mode 100644
index 0000000000..6bc219f910
--- /dev/null
+++ b/vendor/github.com/wiggin77/logrus4logr/go.sum
@@ -0,0 +1,218 @@
+cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
+cloud.google.com/go v0.31.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
+cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
+cloud.google.com/go v0.37.0/go.mod h1:TS1dMSSfndXH133OKGwekG838Om/cQT0BUHV3HcBgoo=
+dmitri.shuralyov.com/app/changes v0.0.0-20180602232624-0a106ad413e3/go.mod h1:Yl+fi1br7+Rr3LqpNJf1/uxUdtRUV+Tnj0o93V2B9MU=
+dmitri.shuralyov.com/html/belt v0.0.0-20180602232347-f7d459c86be0/go.mod h1:JLBrvjyP0v+ecvNYvCpyZgu5/xkfAUhi6wJj28eUfSU=
+dmitri.shuralyov.com/service/change v0.0.0-20181023043359-a85b471d5412/go.mod h1:a1inKt/atXimZ4Mv927x+r7UpyzRUf4emIoiiSC2TN4=
+dmitri.shuralyov.com/state v0.0.0-20180228185332-28bcc343414c/go.mod h1:0PRwlb0D6DFvNNtx+9ybjezNCa8XF0xaYcETyp6rHWU=
+git.apache.org/thrift.git v0.0.0-20180902110319-2566ecd5d999/go.mod h1:fPE2ZNJGynbRyZ4dJvy6G277gSllfV2HJqblrnkyeyg=
+github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
+github.com/Freman/eventloghook v0.0.0-20191003051739-e4d803b6b48b h1:IltY1fRcdIshI/c8KOdmaO8P4lBwDXHJYPymMisvvDs=
+github.com/Freman/eventloghook v0.0.0-20191003051739-e4d803b6b48b/go.mod h1:VGwG8f2pQ8SAFjTSH3PEDmLdlvi0XTd7a4C4AZn+pVw=
+github.com/anmitsu/go-shlex v0.0.0-20161002113705-648efa622239/go.mod h1:2FmKhYUyUczH0OGQWaF5ceTx0UBShxjsH6f8oGKYe2c=
+github.com/antonfisher/nested-logrus-formatter v1.0.2 h1:t65eOqj0fWbOkZR2+OgmxPa0KYIwbPhKdYmseaCMIyI=
+github.com/antonfisher/nested-logrus-formatter v1.0.2/go.mod h1:6WTfyWFkBc9+zyBaKIqRrg/KwMqBbodBjgbHjDz7zjA=
+github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q=
+github.com/bradfitz/go-smtpd v0.0.0-20170404230938-deb6d6237625/go.mod h1:HYsPBTaaSFSlLx/70C2HPIMNZpVV8+vt/A+FMnYP11g=
+github.com/buger/jsonparser v0.0.0-20181115193947-bf1c66bbce23/go.mod h1:bbYlZJ7hK1yFx9hf58LP0zeX7UjIGs20ufpu3evjr+s=
+github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
+github.com/coreos/go-systemd v0.0.0-20181012123002-c6f51f82210d/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4=
+github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
+github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
+github.com/dustin/go-humanize v1.0.0/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk=
+github.com/flynn/go-shlex v0.0.0-20150515145356-3f9db97f8568/go.mod h1:xEzjJPgXI435gkrCt3MPfRiAkVrwSbHsst4LCFVfpJc=
+github.com/francoispqt/gojay v1.2.13 h1:d2m3sFjloqoIUQU3TsHBgj6qg/BVGlTBeHDUmyJnXKk=
+github.com/francoispqt/gojay v1.2.13/go.mod h1:ehT5mTG4ua4581f1++1WLG0vPdaA9HaiDsoyrBGkyDY=
+github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
+github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04=
+github.com/gliderlabs/ssh v0.1.1/go.mod h1:U7qILu1NlMHj9FlMhZLlkCdDnU1DBEAqr0aevW3Awn0=
+github.com/go-errors/errors v1.0.1/go.mod h1:f4zRHt4oKfwPJE5k8C9vpYG+aDHdBFUsgrm6/TyX73Q=
+github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ=
+github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
+github.com/golang/lint v0.0.0-20180702182130-06c8688daad7/go.mod h1:tluoj9z5200jBnyusfRPU2LqT6J+DAorxEvtC7LHB+E=
+github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
+github.com/golang/mock v1.2.0/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
+github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
+github.com/golang/protobuf v1.3.1 h1:YF8+flBXS5eO826T4nzqPrxfhQThhXl0YzfuUPu4SBg=
+github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
+github.com/golang/protobuf v1.3.2 h1:6nsPYzhq5kReh6QImI3k5qWzO4PEbvbIW2cwSfR/6xs=
+github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
+github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
+github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M=
+github.com/google/go-github v17.0.0+incompatible/go.mod h1:zLgOLi98H3fifZn+44m+umXrS52loVEgC2AApnigrVQ=
+github.com/google/go-querystring v1.0.0/go.mod h1:odCYkC5MyYFN7vkCjXpyrEuKhc/BUO6wN/zVPAxq5ck=
+github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs=
+github.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc=
+github.com/googleapis/gax-go v2.0.0+incompatible/go.mod h1:SFVmujtThgffbyetf+mdk2eWhX2bMyUtNHzFKcPA9HY=
+github.com/googleapis/gax-go/v2 v2.0.3/go.mod h1:LLvjysVCY1JZeum8Z6l8qUty8fiNwE08qbEPm1M08qg=
+github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY=
+github.com/gregjones/httpcache v0.0.0-20180305231024-9cad4c3443a7/go.mod h1:FecbI9+v66THATjSRHfNgh1IVFe/9kFxbXtjV0ctIMA=
+github.com/grpc-ecosystem/grpc-gateway v1.5.0/go.mod h1:RSKVYQBd5MCa4OVpNdGskqpgL2+G+NZTnrVHpWWfpdw=
+github.com/jellevandenhooff/dkim v0.0.0-20150330215556-f50fe3d243e1/go.mod h1:E0B/fFc00Y+Rasa88328GlI/XbtyysCtTHZS8h7IrBU=
+github.com/joonix/log v0.0.0-20190524090622-13fe31bbdd7a h1:LL1gwNo4Z1LG68SaaNb8bxB+YnMSilYzytRfkF3AigE=
+github.com/joonix/log v0.0.0-20190524090622-13fe31bbdd7a/go.mod h1:fS54ONkjDV71zS9CDx3V9K21gJg7byKSvI4ajuWFNJw=
+github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU=
+github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU=
+github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
+github.com/konsorten/go-windows-terminal-sequences v1.0.1 h1:mweAR1A6xJ3oS2pRaGiHgQ4OO8tzTaLawm8vnODuwDk=
+github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
+github.com/konsorten/go-windows-terminal-sequences v1.0.2 h1:DB17ag19krx9CFsz4o3enTrPXyIXCl+2iCXH/aMAp9s=
+github.com/konsorten/go-windows-terminal-sequences v1.0.2/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
+github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
+github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
+github.com/kr/pty v1.1.3/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
+github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
+github.com/lunixbochs/vtclean v1.0.0/go.mod h1:pHhQNgMf3btfWnGBVipUOjRYhoOsdGqdm/+2c2E2WMI=
+github.com/mailru/easyjson v0.0.0-20190312143242-1de009706dbe/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc=
+github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0=
+github.com/microcosm-cc/bluemonday v1.0.1/go.mod h1:hsXNsILzKxV+sX77C5b8FSuKF00vh2OMYv+xgHpAMF4=
+github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
+github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
+github.com/neelance/astrewrite v0.0.0-20160511093645-99348263ae86/go.mod h1:kHJEU3ofeGjhHklVoIGuVj85JJwZ6kWPaJwCIxgnFmo=
+github.com/neelance/sourcemap v0.0.0-20151028013722-8c68805598ab/go.mod h1:Qr6/a/Q4r9LP1IltGz7tA7iOK1WonHEYhu1HRBA7ZiM=
+github.com/nsf/jsondiff v0.0.0-20190712045011-8443391ee9b6/go.mod h1:uFMI8w+ref4v2r9jz+c9i1IfIttS/OkmLfrk1jne5hs=
+github.com/openzipkin/zipkin-go v0.1.1/go.mod h1:NtoC/o8u3JlF1lSlyPNswIbeQH9bJTmOf0Erfk+hxe8=
+github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
+github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
+github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
+github.com/prometheus/client_golang v0.8.0/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw=
+github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo=
+github.com/prometheus/common v0.0.0-20180801064454-c7de2306084e/go.mod h1:daVV7qP5qjZbuso7PdcryaAu0sAZbrN9i7WWcTMWvro=
+github.com/prometheus/procfs v0.0.0-20180725123919-05ee40e3a273/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk=
+github.com/rifflock/lfshook v0.0.0-20180920164130-b9218ef580f5 h1:mZHayPoR0lNmnHyvtYjDeq0zlVHn9K/ZXoy17ylucdo=
+github.com/rifflock/lfshook v0.0.0-20180920164130-b9218ef580f5/go.mod h1:GEXHk5HgEKCvEIIrSpFI3ozzG5xOKA2DVlEX/gGnewM=
+github.com/russross/blackfriday v1.5.2/go.mod h1:JO/DiYxRf+HjHt06OyowR9PTA263kcR/rfWxYHBV53g=
+github.com/sergi/go-diff v1.0.0/go.mod h1:0CfEIISq7TuYL3j771MWULgwwjU+GofnZX9QAmXWZgo=
+github.com/shurcooL/component v0.0.0-20170202220835-f88ec8f54cc4/go.mod h1:XhFIlyj5a1fBNx5aJTbKoIq0mNaPvOagO+HjB3EtxrY=
+github.com/shurcooL/events v0.0.0-20181021180414-410e4ca65f48/go.mod h1:5u70Mqkb5O5cxEA8nxTsgrgLehJeAw6Oc4Ab1c/P1HM=
+github.com/shurcooL/github_flavored_markdown v0.0.0-20181002035957-2122de532470/go.mod h1:2dOwnU2uBioM+SGy2aZoq1f/Sd1l9OkAeAUvjSyvgU0=
+github.com/shurcooL/go v0.0.0-20180423040247-9e1955d9fb6e/go.mod h1:TDJrrUr11Vxrven61rcy3hJMUqaf/CLWYhHNPmT14Lk=
+github.com/shurcooL/go-goon v0.0.0-20170922171312-37c2f522c041/go.mod h1:N5mDOmsrJOB+vfqUK+7DmDyjhSLIIBnXo9lvZJj3MWQ=
+github.com/shurcooL/gofontwoff v0.0.0-20180329035133-29b52fc0a18d/go.mod h1:05UtEgK5zq39gLST6uB0cf3NEHjETfB4Fgr3Gx5R9Vw=
+github.com/shurcooL/gopherjslib v0.0.0-20160914041154-feb6d3990c2c/go.mod h1:8d3azKNyqcHP1GaQE/c6dDgjkgSx2BZ4IoEi4F1reUI=
+github.com/shurcooL/highlight_diff v0.0.0-20170515013008-09bb4053de1b/go.mod h1:ZpfEhSmds4ytuByIcDnOLkTHGUI6KNqRNPDLHDk+mUU=
+github.com/shurcooL/highlight_go v0.0.0-20181028180052-98c3abbbae20/go.mod h1:UDKB5a1T23gOMUJrI+uSuH0VRDStOiUVSjBTRDVBVag=
+github.com/shurcooL/home v0.0.0-20181020052607-80b7ffcb30f9/go.mod h1:+rgNQw2P9ARFAs37qieuu7ohDNQ3gds9msbT2yn85sg=
+github.com/shurcooL/htmlg v0.0.0-20170918183704-d01228ac9e50/go.mod h1:zPn1wHpTIePGnXSHpsVPWEktKXHr6+SS6x/IKRb7cpw=
+github.com/shurcooL/httperror v0.0.0-20170206035902-86b7830d14cc/go.mod h1:aYMfkZ6DWSJPJ6c4Wwz3QtW22G7mf/PEgaB9k/ik5+Y=
+github.com/shurcooL/httpfs v0.0.0-20171119174359-809beceb2371/go.mod h1:ZY1cvUeJuFPAdZ/B6v7RHavJWZn2YPVFQ1OSXhCGOkg=
+github.com/shurcooL/httpgzip v0.0.0-20180522190206-b1c53ac65af9/go.mod h1:919LwcH0M7/W4fcZ0/jy0qGght1GIhqyS/EgWGH2j5Q=
+github.com/shurcooL/issues v0.0.0-20181008053335-6292fdc1e191/go.mod h1:e2qWDig5bLteJ4fwvDAc2NHzqFEthkqn7aOZAOpj+PQ=
+github.com/shurcooL/issuesapp v0.0.0-20180602232740-048589ce2241/go.mod h1:NPpHK2TI7iSaM0buivtFUc9offApnI0Alt/K8hcHy0I=
+github.com/shurcooL/notifications v0.0.0-20181007000457-627ab5aea122/go.mod h1:b5uSkrEVM1jQUspwbixRBhaIjIzL2xazXp6kntxYle0=
+github.com/shurcooL/octicon v0.0.0-20181028054416-fa4f57f9efb2/go.mod h1:eWdoE5JD4R5UVWDucdOPg1g2fqQRq78IQa9zlOV1vpQ=
+github.com/shurcooL/reactions v0.0.0-20181006231557-f2e0b4ca5b82/go.mod h1:TCR1lToEk4d2s07G3XGfz2QrgHXg4RJBvjrOozvoWfk=
+github.com/shurcooL/sanitized_anchor_name v0.0.0-20170918181015-86672fcb3f95/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc=
+github.com/shurcooL/users v0.0.0-20180125191416-49c67e49c537/go.mod h1:QJTqeLYEDaXHZDBsXlPCDqdhQuJkuw4NOtaxYe3xii4=
+github.com/shurcooL/webdavfs v0.0.0-20170829043945-18c3829fa133/go.mod h1:hKmq5kWdCj2z2KEozexVbfEZIWiTjhE0+UjmZgPqehw=
+github.com/sirupsen/logrus v1.4.2 h1:SPIRibHv4MatM3XXNO2BJeFLZwZ2LvZgfQ5+UNI2im4=
+github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE=
+github.com/sourcegraph/annotate v0.0.0-20160123013949-f4cad6c6324d/go.mod h1:UdhH50NIW0fCiwBSr0co2m7BnFLdv4fQTgdqdJTHFeE=
+github.com/sourcegraph/syntaxhighlight v0.0.0-20170531221838-bd320f5d308e/go.mod h1:HuIsMU8RRBOtsCgI77wP899iHVBQpCmg4ErYMZB+2IA=
+github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
+github.com/stretchr/testify v1.2.2 h1:bSDNvY7ZPG5RlJ8otE/7V6gMiyenm9RtJ7IUVIAoJ1w=
+github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
+github.com/tarm/serial v0.0.0-20180830185346-98f6abe2eb07/go.mod h1:kDXzergiv9cbyO7IOYJZWg1U88JhDg3PB6klq9Hg2pA=
+github.com/viant/assertly v0.4.8/go.mod h1:aGifi++jvCrUaklKEKT0BU95igDNaqkvz+49uaYMPRU=
+github.com/viant/toolbox v0.24.0/go.mod h1:OxMCG57V0PXuIP2HNQrtJf2CjqdmbrOx5EkMILuUhzM=
+github.com/wiggin77/cfg v1.0.2 h1:NBUX+iJRr+RTncTqTNvajHwzduqbhCQjEqxLHr6Fk7A=
+github.com/wiggin77/cfg v1.0.2/go.mod h1:b3gotba2e5bXTqTW48DwIFoLc+4lWKP7WPi/CdvZ4aE=
+github.com/wiggin77/logr v0.0.0-20191008153504-baac1b801e73 h1:lRao0Wc0zDcje991m1OQzCDXDhJ2dAU2J+214tEoUdQ=
+github.com/wiggin77/logr v0.0.0-20191008153504-baac1b801e73/go.mod h1:VZeJzSbyHqC3zInXnFBJZURn9rGbqlTzlO2m5wdijZ8=
+github.com/wiggin77/logr v0.0.0-20191009035425-189ce7304c34 h1:XhYLDM920VmlED8oElS+0/odwPeH3qkkmlvS0VVwCPE=
+github.com/wiggin77/logr v0.0.0-20191009035425-189ce7304c34/go.mod h1:VZeJzSbyHqC3zInXnFBJZURn9rGbqlTzlO2m5wdijZ8=
+github.com/wiggin77/logr v0.0.0-20191011233000-e3bc20703517 h1:vdjEWflo0H1hEt2Iw9A3aqtCWx8dnUmIYstZtLsF2Hg=
+github.com/wiggin77/logr v0.0.0-20191011233000-e3bc20703517/go.mod h1:9sOJ1T4F2YdtgMrMaq9I1PiTV8R6wKModG5qZH+ba/s=
+github.com/wiggin77/logr v0.0.0-20191014204114-2df550845cfc h1:ZTtN7Tg+n2OAhQTy++YLQBl52pdUyOWPXTB6wjFdb5g=
+github.com/wiggin77/logr v0.0.0-20191014204114-2df550845cfc/go.mod h1:9sOJ1T4F2YdtgMrMaq9I1PiTV8R6wKModG5qZH+ba/s=
+github.com/wiggin77/logr v0.0.0-20200207002347-890d83b925be h1:lGz7zKuDckJCH48b4YCEV8nNOmETu6bW3CCws7/Euvc=
+github.com/wiggin77/logr v0.0.0-20200207002347-890d83b925be/go.mod h1:oIvnsSkyTQojUsr7QO0d4rE2afZbsTj/5WbiikGJu3E=
+github.com/wiggin77/logr v0.0.0-20200207172336-0c93e6357eb5 h1:PahhDMzc5XrJg/mgT87Aav4npUCkA5cBGyN9WQw3GVY=
+github.com/wiggin77/logr v0.0.0-20200207172336-0c93e6357eb5/go.mod h1:oIvnsSkyTQojUsr7QO0d4rE2afZbsTj/5WbiikGJu3E=
+github.com/wiggin77/logr v0.0.0-20200207192333-388c3fe88257 h1:SusN+cM1cWx20H9vv4N3dQUIlhfncUxLi8lokmo8ZUs=
+github.com/wiggin77/logr v0.0.0-20200207192333-388c3fe88257/go.mod h1:oIvnsSkyTQojUsr7QO0d4rE2afZbsTj/5WbiikGJu3E=
+github.com/wiggin77/logr v1.0.0 h1:i0hCQjlUpwBYdcu7iqf3hQCNIH9uf9Y2jbwcReNPscc=
+github.com/wiggin77/logr v1.0.0/go.mod h1:oIvnsSkyTQojUsr7QO0d4rE2afZbsTj/5WbiikGJu3E=
+github.com/wiggin77/logr v1.0.2 h1:zNuYJ+UABevEFhvlEi/MXGYiZgVrMmUJPPt60xmByBs=
+github.com/wiggin77/logr v1.0.2/go.mod h1:oIvnsSkyTQojUsr7QO0d4rE2afZbsTj/5WbiikGJu3E=
+github.com/wiggin77/logr v1.0.3 h1:4Cj899GZJInB9vudlxsmLRDsBlsw9pE/nyJo9XZ/yzo=
+github.com/wiggin77/logr v1.0.3/go.mod h1:oIvnsSkyTQojUsr7QO0d4rE2afZbsTj/5WbiikGJu3E=
+github.com/wiggin77/merror v1.0.2 h1:V0nH9eFp64ASyaXC+pB5WpvBoCg7NUwvaCSKdzlcHqw=
+github.com/wiggin77/merror v1.0.2/go.mod h1:uQTcIU0Z6jRK4OwqganPYerzQxSFJ4GSHM3aurxxQpg=
+go.opencensus.io v0.18.0/go.mod h1:vKdFvxhtzZ9onBp9VKHK8z/sRpBMnKAsufL7wlDrCOA=
+go4.org v0.0.0-20180809161055-417644f6feb5/go.mod h1:MkTOUMDaeVYJUOUsaDXIhWPZYa1yOyC1qaOBpL57BhE=
+golang.org/x/build v0.0.0-20190111050920-041ab4dc3f9d/go.mod h1:OWs+y06UdEOHN4y+MfF/py+xQ/tYqIWW03b70/CG9Rw=
+golang.org/x/crypto v0.0.0-20181030102418-4d3f4d9ffa16/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
+golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
+golang.org/x/crypto v0.0.0-20190313024323-a1f597ede03a/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
+golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
+golang.org/x/lint v0.0.0-20180702182130-06c8688daad7/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
+golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
+golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU=
+golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
+golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
+golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
+golang.org/x/net v0.0.0-20181029044818-c44066c5c816/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
+golang.org/x/net v0.0.0-20181106065722-10aee1819953/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
+golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
+golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
+golang.org/x/net v0.0.0-20190313220215-9f648a60d977/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
+golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
+golang.org/x/oauth2 v0.0.0-20181017192945-9dcd33a902f4/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
+golang.org/x/oauth2 v0.0.0-20181203162652-d668ce993890/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
+golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
+golang.org/x/perf v0.0.0-20180704124530-6e6d33e29852/go.mod h1:JLpeXjPJfIyPr5TlbXLkXWLhP8nz10XfvxElABhCtcw=
+golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
+golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
+golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
+golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
+golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
+golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
+golang.org/x/sys v0.0.0-20181029174526-d69651ed3497/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
+golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
+golang.org/x/sys v0.0.0-20190316082340-a2f829d7f35f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20190422165155-953cdadca894 h1:Cz4ceDQGXuKRnVBDTS23GTn/pU5OE2C0WrNTOYK1Uuc=
+golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20191008105621-543471e840be h1:QAcqgptGM8IQBC9K/RC4o+O9YmqEm0diQn9QmZw/0mU=
+golang.org/x/sys v0.0.0-20191008105621-543471e840be/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
+golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
+golang.org/x/time v0.0.0-20180412165947-fbb02b2291d2/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
+golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
+golang.org/x/tools v0.0.0-20180828015842-6cd1fcedba52/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
+golang.org/x/tools v0.0.0-20181030000716-a0a13e073c7b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
+golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
+golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY=
+google.golang.org/api v0.0.0-20180910000450-7ca32eb868bf/go.mod h1:4mhQ8q/RsB7i+udVvVy5NUi08OU8ZlA0gRVgrF7VFY0=
+google.golang.org/api v0.0.0-20181030000543-1d582fd0359e/go.mod h1:4mhQ8q/RsB7i+udVvVy5NUi08OU8ZlA0gRVgrF7VFY0=
+google.golang.org/api v0.1.0/go.mod h1:UGEZY7KEX120AnNLIHFMKIo4obdJhkp2tPbaPlQx13Y=
+google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM=
+google.golang.org/appengine v1.2.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
+google.golang.org/appengine v1.3.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
+google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
+google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
+google.golang.org/genproto v0.0.0-20180831171423-11092d34479b/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
+google.golang.org/genproto v0.0.0-20181029155118-b69ba1387ce2/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
+google.golang.org/genproto v0.0.0-20181202183823-bd91e49a0898/go.mod h1:7Ep/1NZk928CDR8SjdVbjWNpdIf6nzjE3BTgJDr2Atg=
+google.golang.org/genproto v0.0.0-20190306203927-b5d61aea6440/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
+google.golang.org/genproto v0.0.0-20190522204451-c2c4e71fbf69 h1:4rNOqY4ULrKzS6twXa619uQgI7h9PaVd4ZhjFQ7C5zs=
+google.golang.org/genproto v0.0.0-20190522204451-c2c4e71fbf69/go.mod h1:z3L6/3dTEVtUr6QSP8miRzeRqwQOioJ9I66odjN4I7s=
+google.golang.org/genproto v0.0.0-20191007204434-a023cd5227bd h1:84VQPzup3IpKLxuIAZjHMhVjJ8fZ4/i3yUnj3k6fUdw=
+google.golang.org/genproto v0.0.0-20191007204434-a023cd5227bd/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=
+google.golang.org/grpc v1.14.0/go.mod h1:yo6s7OP7yaDglbqo1J04qKzAhqBH6lvTonzMVmEdcZw=
+google.golang.org/grpc v1.16.0/go.mod h1:0JHn/cJsOMiMfNA9+DeHDlAU7KAAB5GDlYFpa9MZMio=
+google.golang.org/grpc v1.17.0/go.mod h1:6QZJwpn2B+Zp71q/5VxRsJ6NXXVCE5NRUHRo+f3cWCs=
+google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=
+gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
+gopkg.in/inf.v0 v0.9.1/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw=
+gopkg.in/natefinch/lumberjack.v2 v2.0.0 h1:1Lc07Kr7qY4U2YPouBjpCLxpiyxIVoxqXgkXLknAOE8=
+gopkg.in/natefinch/lumberjack.v2 v2.0.0/go.mod h1:l0ndWWf7gzL7RNwBG7wST/UCcT4T24xpD6X8LsfU/+k=
+gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
+gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
+grpc.go4.org v0.0.0-20170609214715-11d0a25b4919/go.mod h1:77eQGdRu53HpSqPFJFmuJdjuHRquDANNeA4x7B8WQ9o=
+honnef.co/go/tools v0.0.0-20180728063816-88497007e858/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
+honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
+honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
+sourcegraph.com/sourcegraph/go-diff v0.5.0/go.mod h1:kuch7UrkMzY0X+p9CRK03kfuPQ2zzQcaEFbx8wA8rck=
+sourcegraph.com/sqs/pbtypes v0.0.0-20180604144634-d3ebe8f20ae4/go.mod h1:ketZ/q3QxT9HOBeFhu6RdvsftgpsbFHBF5Cas6cDKZ0=
diff --git a/vendor/github.com/wiggin77/logrus4logr/target.go b/vendor/github.com/wiggin77/logrus4logr/target.go
new file mode 100644
index 0000000000..5a33fba9c4
--- /dev/null
+++ b/vendor/github.com/wiggin77/logrus4logr/target.go
@@ -0,0 +1,44 @@
+package logrus4logr
+
+import (
+ "fmt"
+
+ "github.com/wiggin77/logr"
+
+ "github.com/sirupsen/logrus"
+)
+
+// TAdapter wraps a Logrus hook allowing the hook be used as a Logr target.
+// Create instances with `NewAdapterTarget`.
+type TAdapter struct {
+ logr.Basic
+ hook logrus.Hook
+
+ // Logger is an optional logrus.Logger instance to use instead of the default.
+ Logger *logrus.Logger
+}
+
+// NewAdapterTarget creates a target wrapper for a Logrus hook.
+// If filter and/or formatter are nil then defaults will be used (Panic level; Plain formatter).
+func NewAdapterTarget(filter logr.Filter, formatter logr.Formatter, hook logrus.Hook, maxQueue int) *TAdapter {
+ a := &TAdapter{hook: hook}
+ a.Basic.Start(a, a, filter, formatter, maxQueue)
+ return a
+}
+
+// Write converts a log record to a Logrus entry and
+// passes it to the Logrus hook.
+func (a *TAdapter) Write(rec *logr.LogRec) error {
+ rus := a.Logger
+ if rus == nil {
+ rus = logrus.StandardLogger()
+ }
+
+ entry := convertLogRec(rec, rus)
+ return a.hook.Fire(entry)
+}
+
+// String returns the type name of the Logrus hook.
+func (a *TAdapter) String() string {
+ return fmt.Sprintf("%T", a.hook)
+}
diff --git a/vendor/github.com/wiggin77/merror/.gitignore b/vendor/github.com/wiggin77/merror/.gitignore
new file mode 100644
index 0000000000..f1c181ec9c
--- /dev/null
+++ b/vendor/github.com/wiggin77/merror/.gitignore
@@ -0,0 +1,12 @@
+# Binaries for programs and plugins
+*.exe
+*.exe~
+*.dll
+*.so
+*.dylib
+
+# Test binary, build with `go test -c`
+*.test
+
+# Output of the go coverage tool, specifically when used with LiteIDE
+*.out
diff --git a/vendor/github.com/wiggin77/merror/LICENSE b/vendor/github.com/wiggin77/merror/LICENSE
new file mode 100644
index 0000000000..2b0bf7efa1
--- /dev/null
+++ b/vendor/github.com/wiggin77/merror/LICENSE
@@ -0,0 +1,21 @@
+MIT License
+
+Copyright (c) 2018 wiggin77
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
diff --git a/vendor/github.com/wiggin77/merror/README.md b/vendor/github.com/wiggin77/merror/README.md
new file mode 100644
index 0000000000..8a31687fc6
--- /dev/null
+++ b/vendor/github.com/wiggin77/merror/README.md
@@ -0,0 +1,2 @@
+# merror
+Multiple Error aggregator for Golang.
diff --git a/vendor/github.com/wiggin77/merror/format.go b/vendor/github.com/wiggin77/merror/format.go
new file mode 100644
index 0000000000..8ba9aa8298
--- /dev/null
+++ b/vendor/github.com/wiggin77/merror/format.go
@@ -0,0 +1,43 @@
+package merror
+
+import (
+ "fmt"
+ "strings"
+)
+
+// FormatterFunc is a function that converts a merror
+// to a string.
+type FormatterFunc func(merr *MError) string
+
+// GlobalFormatter is the global merror formatter.
+// Set this to a custom formatter if desired.
+var GlobalFormatter = defaultFormatter
+
+// defaultFormatter
+func defaultFormatter(merr *MError) string {
+ count := 0
+ overflow := 0
+
+ var format func(sb *strings.Builder, merr *MError, indent string)
+ format = func(sb *strings.Builder, merr *MError, indent string) {
+ count += merr.Len()
+ overflow += merr.Overflow()
+
+ fmt.Fprintf(sb, "%sMError:\n", indent)
+ for _, err := range merr.Errors() {
+ if e, ok := err.(*MError); ok {
+ format(sb, e, indent+" ")
+ } else {
+ fmt.Fprintf(sb, "%s%s\n", indent, err.Error())
+ }
+ }
+ }
+
+ sb := &strings.Builder{}
+ format(sb, merr, "")
+ fmt.Fprintf(sb, "%d errors total.\n", count)
+ if merr.overflow > 0 {
+ fmt.Fprintf(sb, "%d errors truncated.\n", overflow)
+ }
+ return sb.String()
+}
diff --git a/vendor/github.com/wiggin77/merror/go.mod b/vendor/github.com/wiggin77/merror/go.mod
new file mode 100644
index 0000000000..44982f781d
--- /dev/null
+++ b/vendor/github.com/wiggin77/merror/go.mod
@@ -0,0 +1 @@
+module github.com/wiggin77/merror
diff --git a/vendor/github.com/wiggin77/merror/merror.go b/vendor/github.com/wiggin77/merror/merror.go
new file mode 100644
index 0000000000..01f19913de
--- /dev/null
+++ b/vendor/github.com/wiggin77/merror/merror.go
@@ -0,0 +1,87 @@
+package merror
+
+// MError represents zero or more errors that can be
+// accumulated via the `Append` method.
+type MError struct {
+ cap int
+ errors []error
+ overflow int
+ formatter FormatterFunc
+}
+
+// New returns a new instance of `MError` with no limit on the
+// number of errors that can be appended.
+func New() *MError {
+ me := &MError{}
+ me.errors = make([]error, 0, 10)
+ return me
+}
+
+// NewWithCap returns a new instance of `MError` with a maximum
+// capacity of `cap` errors. If exceeded only the overflow counter
+// will be incremented.
+//
+// A `cap` of zero of less means no cap and max size of a slice
+// on the current platform is the upper bound.
+func NewWithCap(cap int) *MError {
+ me := New()
+ me.cap = cap
+ return me
+}
+
+// Append adds an error to the aggregated error list.
+func (me *MError) Append(err error) {
+ if err == nil {
+ return
+ }
+ if me.cap > 0 && len(me.errors) >= me.cap {
+ me.overflow++
+ } else {
+ me.errors = append(me.errors, err)
+ }
+}
+
+// Errors returns an array of the `error` instances that have been
+// appended to this `MError`.
+func (me *MError) Errors() []error {
+ return me.errors
+}
+
+// Len returns the number of errors that have been appended.
+func (me *MError) Len() int {
+ return len(me.errors)
+}
+
+// Overflow returns the number of errors that have been truncated
+// because maximum capacity was exceeded.
+func (me *MError) Overflow() int {
+ return me.overflow
+}
+
+// SetFormatter sets the `FormatterFunc` to be used when `Error` is
+// called. The previous `FormatterFunc` is returned.
+func (me *MError) SetFormatter(f FormatterFunc) (old FormatterFunc) {
+ old = me.formatter
+ me.formatter = f
+ return
+}
+
+// ErrorOrNil returns nil if this `MError` contains no errors,
+// otherwise this `MError` is returned.
+func (me *MError) ErrorOrNil() error {
+ if me == nil || len(me.errors) == 0 {
+ return nil
+ }
+ return me
+}
+
+// Error returns a string representation of this MError.
+// The output format depends on the `Formatter` set for this
+// merror instance, or the global formatter if none set.
+func (me *MError) Error() string {
+ f := me.formatter
+ if f == nil {
+ f = GlobalFormatter
+ }
+ return f(me)
+}
diff --git a/vendor/modules.txt b/vendor/modules.txt
index fcd5a063d1..1ec3d1b5e9 100644
--- a/vendor/modules.txt
+++ b/vendor/modules.txt
@@ -4,6 +4,8 @@ github.com/BurntSushi/toml
github.com/Masterminds/squirrel
# github.com/NYTimes/gziphandler v1.1.1
github.com/NYTimes/gziphandler
+# github.com/RackSec/srslog v0.0.0-20180709174129-a4725f04ec91
+github.com/RackSec/srslog
# github.com/armon/go-metrics v0.3.0
github.com/armon/go-metrics
# github.com/avct/uasurfer v0.0.0-20191028135549-26b5daa857f1
@@ -28,6 +30,8 @@ github.com/disintegration/imaging
github.com/dyatlov/go-opengraph/opengraph
# github.com/fatih/color v1.9.0
github.com/fatih/color
+# github.com/francoispqt/gojay v1.2.13
+github.com/francoispqt/gojay
# github.com/fsnotify/fsnotify v1.4.7
github.com/fsnotify/fsnotify
# github.com/go-asn1-ber/asn1-ber v1.3.2-0.20191121212151-29be175fc3a3
@@ -223,6 +227,8 @@ github.com/sean-/seed
github.com/segmentio/analytics-go
# github.com/segmentio/backo-go v0.0.0-20160424052352-204274ad699c
github.com/segmentio/backo-go
+# github.com/shinji62/logrus-syslog-ng v0.0.0-20180605090607-3974ebb047a0
+github.com/shinji62/logrus-syslog-ng
# github.com/sirupsen/logrus v1.4.2
github.com/sirupsen/logrus
# github.com/spf13/afero v1.2.2
@@ -272,6 +278,17 @@ github.com/uber/jaeger-client-go/utils
github.com/uber/jaeger-client-go/zipkin
# github.com/uber/jaeger-lib v2.2.0+incompatible
github.com/uber/jaeger-lib/metrics
+# github.com/wiggin77/cfg v1.0.2
+github.com/wiggin77/cfg
+github.com/wiggin77/cfg/ini
+github.com/wiggin77/cfg/timeconv
+# github.com/wiggin77/logr v1.0.3
+github.com/wiggin77/logr
+github.com/wiggin77/logr/format
+# github.com/wiggin77/logrus4logr v1.0.2
+github.com/wiggin77/logrus4logr
+# github.com/wiggin77/merror v1.0.2
+github.com/wiggin77/merror
# github.com/xtgo/uuid v0.0.0-20140804021211-a0b114877d4c
github.com/xtgo/uuid
# go.uber.org/atomic v1.5.1
diff --git a/web/context.go b/web/context.go
index 69fcb7948f..901da0f232 100644
--- a/web/context.go
+++ b/web/context.go
@@ -10,6 +10,7 @@ import (
"strings"
"github.com/mattermost/mattermost-server/v5/app"
+ "github.com/mattermost/mattermost-server/v5/audit"
"github.com/mattermost/mattermost-server/v5/mlog"
"github.com/mattermost/mattermost-server/v5/model"
"github.com/mattermost/mattermost-server/v5/utils"
@@ -23,6 +24,56 @@ type Context struct {
siteURLHeader string
}
+// LogAuditRec logs an audit record using default RestLevel.
+func (c *Context) LogAuditRec(rec *audit.Record) {
+ c.LogAuditRecWithLevel(rec, app.RestLevel)
+}
+
+// LogAuditRec logs an audit record using specificed Level.
+func (c *Context) LogAuditRecWithLevel(rec *audit.Record, level audit.Level) {
+ if rec == nil {
+ return
+ }
+ if c.Err != nil {
+ rec.AddMeta("err", c.Err.Id)
+ rec.AddMeta("code", c.Err.StatusCode)
+ if c.Err.Id == "api.context.permissions.app_error" {
+ level = app.RestPermsLevel
+ }
+ rec.Fail()
+ }
+ c.App.Srv().Audit.LogRecord(level, *rec)
+}
+
+// LogAuditMeta creates an audit record and logs it.
+func (c *Context) LogAuditEx(event string, status string) {
+ rec := c.MakeAuditRecord(event, status)
+ c.LogAuditRec(rec)
+}
+
+// LogAuditMeta creates an audit record with metadata and logs it.
+func (c *Context) LogAuditMeta(event string, status string, meta audit.Meta) {
+ rec := c.MakeAuditRecord(event, status)
+ if meta != nil {
+ rec.Meta = meta
+ }
+ c.LogAuditRec(rec)
+}
+
+// MakeAuditRecord creates a audit record pre-populated with data from this context.
+func (c *Context) MakeAuditRecord(event string, initialStatus string) *audit.Record {
+ return &audit.Record{
+ APIPath: c.App.Path(),
+ Event: event,
+ Status: initialStatus,
+ UserID: c.App.Session().UserId,
+ SessionID: c.App.Session().Id,
+ Client: c.App.UserAgent(),
+ IPAddress: c.App.IpAddress(),
+ Meta: audit.Meta{},
+ }
+}
+
func (c *Context) LogAudit(extraInfo string) {
audit := &model.Audit{UserId: c.App.Session().UserId, IpAddress: c.App.IpAddress(), Action: c.App.Path(), ExtraInfo: extraInfo, SessionId: c.App.Session().Id}
if err := c.App.Srv().Store.Audit().Save(audit); err != nil {
diff --git a/web/oauth.go b/web/oauth.go
index 264ea280c1..328c81a0c5 100644
--- a/web/oauth.go
+++ b/web/oauth.go
@@ -10,6 +10,7 @@ import (
"strings"
"github.com/mattermost/mattermost-server/v5/app"
+ "github.com/mattermost/mattermost-server/v5/audit"
"github.com/mattermost/mattermost-server/v5/mlog"
"github.com/mattermost/mattermost-server/v5/model"
"github.com/mattermost/mattermost-server/v5/utils"
@@ -57,6 +58,8 @@ func authorizeOAuthApp(c *Context, w http.ResponseWriter, r *http.Request) {
return
}
+ auditRec := c.MakeAuditRecord("authorizeOAuthApp", audit.Fail)
+ defer c.LogAuditRec(auditRec)
c.LogAudit("attempt")
redirectUrl, err := c.App.AllowOAuthAppAccessToUser(c.App.Session().UserId, authRequest)
@@ -66,6 +69,7 @@ func authorizeOAuthApp(c *Context, w http.ResponseWriter, r *http.Request) {
return
}
+ auditRec.Success()
c.LogAudit("")
w.Write([]byte(model.MapToJson(map[string]string{"redirect": redirectUrl})))
@@ -80,13 +84,18 @@ func deauthorizeOAuthApp(c *Context, w http.ResponseWriter, r *http.Request) {
return
}
+ auditRec := c.MakeAuditRecord("deauthorizeOAuthApp", audit.Fail)
+ defer c.LogAuditRec(auditRec)
+
err := c.App.DeauthorizeOAuthAppForUser(c.App.Session().UserId, clientId)
if err != nil {
c.Err = err
return
}
+ auditRec.Success()
c.LogAudit("success")
+
ReturnStatusOK(w)
}
@@ -200,6 +209,10 @@ func getAccessToken(c *Context, w http.ResponseWriter, r *http.Request) {
redirectUri := r.FormValue("redirect_uri")
+ auditRec := c.MakeAuditRecord("getAccessToken", audit.Fail)
+ defer c.LogAuditRec(auditRec)
+ auditRec.AddMeta("grant_type", grantType)
+ auditRec.AddMeta("client_id", clientId)
c.LogAudit("attempt")
accessRsp, err := c.App.GetOAuthAccessTokenForCodeFlow(clientId, grantType, redirectUri, code, secret, refreshToken)
@@ -212,6 +225,7 @@ func getAccessToken(c *Context, w http.ResponseWriter, r *http.Request) {
w.Header().Set("Cache-Control", "no-store")
w.Header().Set("Pragma", "no-cache")
+ auditRec.Success()
c.LogAudit("success")
w.Write([]byte(accessRsp.ToJson()))
diff --git a/web/saml.go b/web/saml.go
index 59ff24ad91..bf917dd021 100644
--- a/web/saml.go
+++ b/web/saml.go
@@ -8,6 +8,7 @@ import (
"net/http"
"strings"
+ "github.com/mattermost/mattermost-server/v5/audit"
"github.com/mattermost/mattermost-server/v5/mlog"
"github.com/mattermost/mattermost-server/v5/model"
)
@@ -84,13 +85,16 @@ func completeSaml(c *Context, w http.ResponseWriter, r *http.Request) {
relayProps = model.MapFromJson(strings.NewReader(stateStr))
}
+ auditRec := c.MakeAuditRecord("completeSaml", audit.Fail)
+ defer c.LogAuditRec(auditRec)
c.LogAudit("attempt")
action := relayProps["action"]
+ auditRec.AddMeta("action", action)
+
user, err := samlInterface.DoLogin(encodedXML, relayProps)
if err != nil {
c.LogAudit("fail")
-
if action == model.OAUTH_ACTION_MOBILE {
err.Translate(c.App.T)
w.Write([]byte(err.ToJson()))
@@ -124,6 +128,9 @@ func completeSaml(c *Context, w http.ResponseWriter, r *http.Request) {
c.Err = err
return
}
+ auditRec.AddMeta("revoked_user_id", user.Id)
+ auditRec.AddMeta("revoked", "Revoked all sessions for user")
+
c.LogAuditWithUserId(user.Id, "Revoked all sessions for user")
c.App.Srv().Go(func() {
if err = c.App.SendSignInChangeEmail(user.Email, strings.Title(model.USER_AUTH_SERVICE_SAML)+" SSO", user.Locale, c.App.GetSiteURL()); err != nil {
@@ -132,6 +139,7 @@ func completeSaml(c *Context, w http.ResponseWriter, r *http.Request) {
})
}
+ auditRec.AddMeta("obtained_user_id", user.Id)
c.LogAuditWithUserId(user.Id, "obtained user")
err = c.App.DoLogin(w, r, user, "")
@@ -140,6 +148,7 @@ func completeSaml(c *Context, w http.ResponseWriter, r *http.Request) {
return
}
+ auditRec.Success()
c.LogAuditWithUserId(user.Id, "success")
c.App.AttachSessionCookies(w, r)