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