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 }