github.com/wgh-/mattermost-server@v4.8.0-rc2+incompatible/store/sqlstore/webhook_store.go (about)

     1  // Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
     2  // See License.txt for license information.
     3  
     4  package sqlstore
     5  
     6  import (
     7  	"net/http"
     8  
     9  	"database/sql"
    10  
    11  	"github.com/mattermost/mattermost-server/einterfaces"
    12  	"github.com/mattermost/mattermost-server/model"
    13  	"github.com/mattermost/mattermost-server/store"
    14  	"github.com/mattermost/mattermost-server/utils"
    15  )
    16  
    17  type SqlWebhookStore struct {
    18  	SqlStore
    19  	metrics einterfaces.MetricsInterface
    20  }
    21  
    22  const (
    23  	WEBHOOK_CACHE_SIZE = 25000
    24  	WEBHOOK_CACHE_SEC  = 900 // 15 minutes
    25  )
    26  
    27  var webhookCache = utils.NewLru(WEBHOOK_CACHE_SIZE)
    28  
    29  func ClearWebhookCaches() {
    30  	webhookCache.Purge()
    31  }
    32  
    33  func NewSqlWebhookStore(sqlStore SqlStore, metrics einterfaces.MetricsInterface) store.WebhookStore {
    34  	s := &SqlWebhookStore{
    35  		SqlStore: sqlStore,
    36  		metrics:  metrics,
    37  	}
    38  
    39  	for _, db := range sqlStore.GetAllConns() {
    40  		table := db.AddTableWithName(model.IncomingWebhook{}, "IncomingWebhooks").SetKeys(false, "Id")
    41  		table.ColMap("Id").SetMaxSize(26)
    42  		table.ColMap("UserId").SetMaxSize(26)
    43  		table.ColMap("ChannelId").SetMaxSize(26)
    44  		table.ColMap("TeamId").SetMaxSize(26)
    45  		table.ColMap("DisplayName").SetMaxSize(64)
    46  		table.ColMap("Description").SetMaxSize(128)
    47  
    48  		tableo := db.AddTableWithName(model.OutgoingWebhook{}, "OutgoingWebhooks").SetKeys(false, "Id")
    49  		tableo.ColMap("Id").SetMaxSize(26)
    50  		tableo.ColMap("Token").SetMaxSize(26)
    51  		tableo.ColMap("CreatorId").SetMaxSize(26)
    52  		tableo.ColMap("ChannelId").SetMaxSize(26)
    53  		tableo.ColMap("TeamId").SetMaxSize(26)
    54  		tableo.ColMap("TriggerWords").SetMaxSize(1024)
    55  		tableo.ColMap("CallbackURLs").SetMaxSize(1024)
    56  		tableo.ColMap("DisplayName").SetMaxSize(64)
    57  		tableo.ColMap("Description").SetMaxSize(128)
    58  		tableo.ColMap("ContentType").SetMaxSize(128)
    59  		tableo.ColMap("TriggerWhen").SetMaxSize(1)
    60  	}
    61  
    62  	return s
    63  }
    64  
    65  func (s SqlWebhookStore) CreateIndexesIfNotExists() {
    66  	s.CreateIndexIfNotExists("idx_incoming_webhook_user_id", "IncomingWebhooks", "UserId")
    67  	s.CreateIndexIfNotExists("idx_incoming_webhook_team_id", "IncomingWebhooks", "TeamId")
    68  	s.CreateIndexIfNotExists("idx_outgoing_webhook_team_id", "OutgoingWebhooks", "TeamId")
    69  
    70  	s.CreateIndexIfNotExists("idx_incoming_webhook_update_at", "IncomingWebhooks", "UpdateAt")
    71  	s.CreateIndexIfNotExists("idx_incoming_webhook_create_at", "IncomingWebhooks", "CreateAt")
    72  	s.CreateIndexIfNotExists("idx_incoming_webhook_delete_at", "IncomingWebhooks", "DeleteAt")
    73  
    74  	s.CreateIndexIfNotExists("idx_outgoing_webhook_update_at", "OutgoingWebhooks", "UpdateAt")
    75  	s.CreateIndexIfNotExists("idx_outgoing_webhook_create_at", "OutgoingWebhooks", "CreateAt")
    76  	s.CreateIndexIfNotExists("idx_outgoing_webhook_delete_at", "OutgoingWebhooks", "DeleteAt")
    77  }
    78  
    79  func (s SqlWebhookStore) InvalidateWebhookCache(webhookId string) {
    80  	webhookCache.Remove(webhookId)
    81  }
    82  
    83  func (s SqlWebhookStore) SaveIncoming(webhook *model.IncomingWebhook) store.StoreChannel {
    84  	return store.Do(func(result *store.StoreResult) {
    85  		if len(webhook.Id) > 0 {
    86  			result.Err = model.NewAppError("SqlWebhookStore.SaveIncoming", "store.sql_webhooks.save_incoming.existing.app_error", nil, "id="+webhook.Id, http.StatusBadRequest)
    87  			return
    88  		}
    89  
    90  		webhook.PreSave()
    91  		if result.Err = webhook.IsValid(); result.Err != nil {
    92  			return
    93  		}
    94  
    95  		if err := s.GetMaster().Insert(webhook); err != nil {
    96  			result.Err = model.NewAppError("SqlWebhookStore.SaveIncoming", "store.sql_webhooks.save_incoming.app_error", nil, "id="+webhook.Id+", "+err.Error(), http.StatusInternalServerError)
    97  		} else {
    98  			result.Data = webhook
    99  		}
   100  	})
   101  }
   102  
   103  func (s SqlWebhookStore) UpdateIncoming(hook *model.IncomingWebhook) store.StoreChannel {
   104  	return store.Do(func(result *store.StoreResult) {
   105  		hook.UpdateAt = model.GetMillis()
   106  
   107  		if _, err := s.GetMaster().Update(hook); err != nil {
   108  			result.Err = model.NewAppError("SqlWebhookStore.UpdateIncoming", "store.sql_webhooks.update_incoming.app_error", nil, "id="+hook.Id+", "+err.Error(), http.StatusInternalServerError)
   109  		} else {
   110  			result.Data = hook
   111  		}
   112  	})
   113  }
   114  
   115  func (s SqlWebhookStore) GetIncoming(id string, allowFromCache bool) store.StoreChannel {
   116  	return store.Do(func(result *store.StoreResult) {
   117  		if allowFromCache {
   118  			if cacheItem, ok := webhookCache.Get(id); ok {
   119  				if s.metrics != nil {
   120  					s.metrics.IncrementMemCacheHitCounter("Webhook")
   121  				}
   122  				result.Data = cacheItem.(*model.IncomingWebhook)
   123  				return
   124  			} else {
   125  				if s.metrics != nil {
   126  					s.metrics.IncrementMemCacheMissCounter("Webhook")
   127  				}
   128  			}
   129  		}
   130  
   131  		var webhook model.IncomingWebhook
   132  
   133  		if err := s.GetReplica().SelectOne(&webhook, "SELECT * FROM IncomingWebhooks WHERE Id = :Id AND DeleteAt = 0", map[string]interface{}{"Id": id}); err != nil {
   134  			if err == sql.ErrNoRows {
   135  				result.Err = model.NewAppError("SqlWebhookStore.GetIncoming", "store.sql_webhooks.get_incoming.app_error", nil, "id="+id+", err="+err.Error(), http.StatusNotFound)
   136  			} else {
   137  				result.Err = model.NewAppError("SqlWebhookStore.GetIncoming", "store.sql_webhooks.get_incoming.app_error", nil, "id="+id+", err="+err.Error(), http.StatusInternalServerError)
   138  			}
   139  		}
   140  
   141  		if result.Err == nil {
   142  			webhookCache.AddWithExpiresInSecs(id, &webhook, WEBHOOK_CACHE_SEC)
   143  		}
   144  
   145  		result.Data = &webhook
   146  	})
   147  }
   148  
   149  func (s SqlWebhookStore) DeleteIncoming(webhookId string, time int64) store.StoreChannel {
   150  	return store.Do(func(result *store.StoreResult) {
   151  		_, err := s.GetMaster().Exec("Update IncomingWebhooks SET DeleteAt = :DeleteAt, UpdateAt = :UpdateAt WHERE Id = :Id", map[string]interface{}{"DeleteAt": time, "UpdateAt": time, "Id": webhookId})
   152  		if err != nil {
   153  			result.Err = model.NewAppError("SqlWebhookStore.DeleteIncoming", "store.sql_webhooks.delete_incoming.app_error", nil, "id="+webhookId+", err="+err.Error(), http.StatusInternalServerError)
   154  		}
   155  
   156  		s.InvalidateWebhookCache(webhookId)
   157  	})
   158  }
   159  
   160  func (s SqlWebhookStore) PermanentDeleteIncomingByUser(userId string) store.StoreChannel {
   161  	return store.Do(func(result *store.StoreResult) {
   162  		_, err := s.GetMaster().Exec("DELETE FROM IncomingWebhooks WHERE UserId = :UserId", map[string]interface{}{"UserId": userId})
   163  		if err != nil {
   164  			result.Err = model.NewAppError("SqlWebhookStore.DeleteIncomingByUser", "store.sql_webhooks.permanent_delete_incoming_by_user.app_error", nil, "id="+userId+", err="+err.Error(), http.StatusInternalServerError)
   165  		}
   166  
   167  		ClearWebhookCaches()
   168  	})
   169  }
   170  
   171  func (s SqlWebhookStore) PermanentDeleteIncomingByChannel(channelId string) store.StoreChannel {
   172  	return store.Do(func(result *store.StoreResult) {
   173  		_, err := s.GetMaster().Exec("DELETE FROM IncomingWebhooks WHERE ChannelId = :ChannelId", map[string]interface{}{"ChannelId": channelId})
   174  		if err != nil {
   175  			result.Err = model.NewAppError("SqlWebhookStore.DeleteIncomingByChannel", "store.sql_webhooks.permanent_delete_incoming_by_channel.app_error", nil, "id="+channelId+", err="+err.Error(), http.StatusInternalServerError)
   176  		}
   177  
   178  		ClearWebhookCaches()
   179  	})
   180  }
   181  
   182  func (s SqlWebhookStore) GetIncomingList(offset, limit int) store.StoreChannel {
   183  	return store.Do(func(result *store.StoreResult) {
   184  		var webhooks []*model.IncomingWebhook
   185  
   186  		if _, err := s.GetReplica().Select(&webhooks, "SELECT * FROM IncomingWebhooks WHERE DeleteAt = 0 LIMIT :Limit OFFSET :Offset", map[string]interface{}{"Limit": limit, "Offset": offset}); err != nil {
   187  			result.Err = model.NewAppError("SqlWebhookStore.GetIncomingList", "store.sql_webhooks.get_incoming_by_user.app_error", nil, "err="+err.Error(), http.StatusInternalServerError)
   188  		}
   189  
   190  		result.Data = webhooks
   191  	})
   192  }
   193  
   194  func (s SqlWebhookStore) GetIncomingByTeam(teamId string, offset, limit int) store.StoreChannel {
   195  	return store.Do(func(result *store.StoreResult) {
   196  		var webhooks []*model.IncomingWebhook
   197  
   198  		if _, err := s.GetReplica().Select(&webhooks, "SELECT * FROM IncomingWebhooks WHERE TeamId = :TeamId AND DeleteAt = 0 LIMIT :Limit OFFSET :Offset", map[string]interface{}{"TeamId": teamId, "Limit": limit, "Offset": offset}); err != nil {
   199  			result.Err = model.NewAppError("SqlWebhookStore.GetIncomingByUser", "store.sql_webhooks.get_incoming_by_user.app_error", nil, "teamId="+teamId+", err="+err.Error(), http.StatusInternalServerError)
   200  		}
   201  
   202  		result.Data = webhooks
   203  	})
   204  }
   205  
   206  func (s SqlWebhookStore) GetIncomingByChannel(channelId string) store.StoreChannel {
   207  	return store.Do(func(result *store.StoreResult) {
   208  		var webhooks []*model.IncomingWebhook
   209  
   210  		if _, err := s.GetReplica().Select(&webhooks, "SELECT * FROM IncomingWebhooks WHERE ChannelId = :ChannelId AND DeleteAt = 0", map[string]interface{}{"ChannelId": channelId}); err != nil {
   211  			result.Err = model.NewAppError("SqlWebhookStore.GetIncomingByChannel", "store.sql_webhooks.get_incoming_by_channel.app_error", nil, "channelId="+channelId+", err="+err.Error(), http.StatusInternalServerError)
   212  		}
   213  
   214  		result.Data = webhooks
   215  	})
   216  }
   217  
   218  func (s SqlWebhookStore) SaveOutgoing(webhook *model.OutgoingWebhook) store.StoreChannel {
   219  	return store.Do(func(result *store.StoreResult) {
   220  		if len(webhook.Id) > 0 {
   221  			result.Err = model.NewAppError("SqlWebhookStore.SaveOutgoing", "store.sql_webhooks.save_outgoing.override.app_error", nil, "id="+webhook.Id, http.StatusBadRequest)
   222  			return
   223  		}
   224  
   225  		webhook.PreSave()
   226  		if result.Err = webhook.IsValid(); result.Err != nil {
   227  			return
   228  		}
   229  
   230  		if err := s.GetMaster().Insert(webhook); err != nil {
   231  			result.Err = model.NewAppError("SqlWebhookStore.SaveOutgoing", "store.sql_webhooks.save_outgoing.app_error", nil, "id="+webhook.Id+", "+err.Error(), http.StatusInternalServerError)
   232  		} else {
   233  			result.Data = webhook
   234  		}
   235  	})
   236  }
   237  
   238  func (s SqlWebhookStore) GetOutgoing(id string) store.StoreChannel {
   239  	return store.Do(func(result *store.StoreResult) {
   240  		var webhook model.OutgoingWebhook
   241  
   242  		if err := s.GetReplica().SelectOne(&webhook, "SELECT * FROM OutgoingWebhooks WHERE Id = :Id AND DeleteAt = 0", map[string]interface{}{"Id": id}); err != nil {
   243  			result.Err = model.NewAppError("SqlWebhookStore.GetOutgoing", "store.sql_webhooks.get_outgoing.app_error", nil, "id="+id+", err="+err.Error(), http.StatusInternalServerError)
   244  		}
   245  
   246  		result.Data = &webhook
   247  	})
   248  }
   249  
   250  func (s SqlWebhookStore) GetOutgoingList(offset, limit int) store.StoreChannel {
   251  	return store.Do(func(result *store.StoreResult) {
   252  		var webhooks []*model.OutgoingWebhook
   253  
   254  		if _, err := s.GetReplica().Select(&webhooks, "SELECT * FROM OutgoingWebhooks WHERE DeleteAt = 0 LIMIT :Limit OFFSET :Offset", map[string]interface{}{"Offset": offset, "Limit": limit}); err != nil {
   255  			result.Err = model.NewAppError("SqlWebhookStore.GetOutgoingList", "store.sql_webhooks.get_outgoing_by_channel.app_error", nil, "err="+err.Error(), http.StatusInternalServerError)
   256  		}
   257  
   258  		result.Data = webhooks
   259  	})
   260  }
   261  
   262  func (s SqlWebhookStore) GetOutgoingByChannel(channelId string, offset, limit int) store.StoreChannel {
   263  	return store.Do(func(result *store.StoreResult) {
   264  		var webhooks []*model.OutgoingWebhook
   265  
   266  		query := ""
   267  		if limit < 0 || offset < 0 {
   268  			query = "SELECT * FROM OutgoingWebhooks WHERE ChannelId = :ChannelId AND DeleteAt = 0"
   269  		} else {
   270  			query = "SELECT * FROM OutgoingWebhooks WHERE ChannelId = :ChannelId AND DeleteAt = 0 LIMIT :Limit OFFSET :Offset"
   271  		}
   272  
   273  		if _, err := s.GetReplica().Select(&webhooks, query, map[string]interface{}{"ChannelId": channelId, "Offset": offset, "Limit": limit}); err != nil {
   274  			result.Err = model.NewAppError("SqlWebhookStore.GetOutgoingByChannel", "store.sql_webhooks.get_outgoing_by_channel.app_error", nil, "channelId="+channelId+", err="+err.Error(), http.StatusInternalServerError)
   275  		}
   276  
   277  		result.Data = webhooks
   278  	})
   279  }
   280  
   281  func (s SqlWebhookStore) GetOutgoingByTeam(teamId string, offset, limit int) store.StoreChannel {
   282  	return store.Do(func(result *store.StoreResult) {
   283  		var webhooks []*model.OutgoingWebhook
   284  
   285  		query := ""
   286  		if limit < 0 || offset < 0 {
   287  			query = "SELECT * FROM OutgoingWebhooks WHERE TeamId = :TeamId AND DeleteAt = 0"
   288  		} else {
   289  			query = "SELECT * FROM OutgoingWebhooks WHERE TeamId = :TeamId AND DeleteAt = 0 LIMIT :Limit OFFSET :Offset"
   290  		}
   291  
   292  		if _, err := s.GetReplica().Select(&webhooks, query, map[string]interface{}{"TeamId": teamId, "Offset": offset, "Limit": limit}); err != nil {
   293  			result.Err = model.NewAppError("SqlWebhookStore.GetOutgoingByTeam", "store.sql_webhooks.get_outgoing_by_team.app_error", nil, "teamId="+teamId+", err="+err.Error(), http.StatusInternalServerError)
   294  		}
   295  
   296  		result.Data = webhooks
   297  	})
   298  }
   299  
   300  func (s SqlWebhookStore) DeleteOutgoing(webhookId string, time int64) store.StoreChannel {
   301  	return store.Do(func(result *store.StoreResult) {
   302  		_, err := s.GetMaster().Exec("Update OutgoingWebhooks SET DeleteAt = :DeleteAt, UpdateAt = :UpdateAt WHERE Id = :Id", map[string]interface{}{"DeleteAt": time, "UpdateAt": time, "Id": webhookId})
   303  		if err != nil {
   304  			result.Err = model.NewAppError("SqlWebhookStore.DeleteOutgoing", "store.sql_webhooks.delete_outgoing.app_error", nil, "id="+webhookId+", err="+err.Error(), http.StatusInternalServerError)
   305  		}
   306  	})
   307  }
   308  
   309  func (s SqlWebhookStore) PermanentDeleteOutgoingByUser(userId string) store.StoreChannel {
   310  	return store.Do(func(result *store.StoreResult) {
   311  		_, err := s.GetMaster().Exec("DELETE FROM OutgoingWebhooks WHERE CreatorId = :UserId", map[string]interface{}{"UserId": userId})
   312  		if err != nil {
   313  			result.Err = model.NewAppError("SqlWebhookStore.DeleteOutgoingByUser", "store.sql_webhooks.permanent_delete_outgoing_by_user.app_error", nil, "id="+userId+", err="+err.Error(), http.StatusInternalServerError)
   314  		}
   315  	})
   316  }
   317  
   318  func (s SqlWebhookStore) PermanentDeleteOutgoingByChannel(channelId string) store.StoreChannel {
   319  	return store.Do(func(result *store.StoreResult) {
   320  		_, err := s.GetMaster().Exec("DELETE FROM OutgoingWebhooks WHERE ChannelId = :ChannelId", map[string]interface{}{"ChannelId": channelId})
   321  		if err != nil {
   322  			result.Err = model.NewAppError("SqlWebhookStore.DeleteOutgoingByChannel", "store.sql_webhooks.permanent_delete_outgoing_by_channel.app_error", nil, "id="+channelId+", err="+err.Error(), http.StatusInternalServerError)
   323  		}
   324  
   325  		ClearWebhookCaches()
   326  	})
   327  }
   328  
   329  func (s SqlWebhookStore) UpdateOutgoing(hook *model.OutgoingWebhook) store.StoreChannel {
   330  	return store.Do(func(result *store.StoreResult) {
   331  		hook.UpdateAt = model.GetMillis()
   332  
   333  		if _, err := s.GetMaster().Update(hook); err != nil {
   334  			result.Err = model.NewAppError("SqlWebhookStore.UpdateOutgoing", "store.sql_webhooks.update_outgoing.app_error", nil, "id="+hook.Id+", "+err.Error(), http.StatusInternalServerError)
   335  		} else {
   336  			result.Data = hook
   337  		}
   338  	})
   339  }
   340  
   341  func (s SqlWebhookStore) AnalyticsIncomingCount(teamId string) store.StoreChannel {
   342  	return store.Do(func(result *store.StoreResult) {
   343  		query :=
   344  			`SELECT 
   345  			    COUNT(*)
   346  			FROM
   347  			    IncomingWebhooks
   348  			WHERE
   349                  DeleteAt = 0`
   350  
   351  		if len(teamId) > 0 {
   352  			query += " AND TeamId = :TeamId"
   353  		}
   354  
   355  		if v, err := s.GetReplica().SelectInt(query, map[string]interface{}{"TeamId": teamId}); err != nil {
   356  			result.Err = model.NewAppError("SqlWebhookStore.AnalyticsIncomingCount", "store.sql_webhooks.analytics_incoming_count.app_error", nil, "team_id="+teamId+", err="+err.Error(), http.StatusInternalServerError)
   357  		} else {
   358  			result.Data = v
   359  		}
   360  	})
   361  }
   362  
   363  func (s SqlWebhookStore) AnalyticsOutgoingCount(teamId string) store.StoreChannel {
   364  	return store.Do(func(result *store.StoreResult) {
   365  		query :=
   366  			`SELECT 
   367  			    COUNT(*)
   368  			FROM
   369  			    OutgoingWebhooks
   370  			WHERE
   371                  DeleteAt = 0`
   372  
   373  		if len(teamId) > 0 {
   374  			query += " AND TeamId = :TeamId"
   375  		}
   376  
   377  		if v, err := s.GetReplica().SelectInt(query, map[string]interface{}{"TeamId": teamId}); err != nil {
   378  			result.Err = model.NewAppError("SqlWebhookStore.AnalyticsOutgoingCount", "store.sql_webhooks.analytics_outgoing_count.app_error", nil, "team_id="+teamId+", err="+err.Error(), http.StatusInternalServerError)
   379  		} else {
   380  			result.Data = v
   381  		}
   382  	})
   383  }