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