2017-04-12 08:27:57 -04:00
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
2015-09-21 14:22:23 -04:00
// See License.txt for license information.
package store
import (
2017-02-21 19:42:34 -05:00
"net/http"
2017-03-12 04:10:56 +05:30
"database/sql"
2017-01-27 14:07:34 -05:00
"github.com/mattermost/platform/einterfaces"
2015-09-21 14:22:23 -04:00
"github.com/mattermost/platform/model"
2017-01-27 14:07:34 -05:00
"github.com/mattermost/platform/utils"
2015-09-21 14:22:23 -04:00
)
type SqlWebhookStore struct {
2017-06-27 08:02:08 -07:00
SqlStore
2015-09-21 14:22:23 -04:00
}
2017-01-27 14:07:34 -05:00
const (
WEBHOOK_CACHE_SIZE = 25000
WEBHOOK_CACHE_SEC = 900 // 15 minutes
)
var webhookCache = utils . NewLru ( WEBHOOK_CACHE_SIZE )
func ClearWebhookCaches ( ) {
webhookCache . Purge ( )
}
2017-06-27 08:02:08 -07:00
func NewSqlWebhookStore ( sqlStore SqlStore ) WebhookStore {
2015-09-21 14:22:23 -04:00
s := & SqlWebhookStore { sqlStore }
for _ , db := range sqlStore . GetAllConns ( ) {
table := db . AddTableWithName ( model . IncomingWebhook { } , "IncomingWebhooks" ) . SetKeys ( false , "Id" )
table . ColMap ( "Id" ) . SetMaxSize ( 26 )
table . ColMap ( "UserId" ) . SetMaxSize ( 26 )
table . ColMap ( "ChannelId" ) . SetMaxSize ( 26 )
table . ColMap ( "TeamId" ) . SetMaxSize ( 26 )
2016-03-30 10:56:20 -04:00
table . ColMap ( "DisplayName" ) . SetMaxSize ( 64 )
table . ColMap ( "Description" ) . SetMaxSize ( 128 )
2015-10-01 14:07:20 -04:00
tableo := db . AddTableWithName ( model . OutgoingWebhook { } , "OutgoingWebhooks" ) . SetKeys ( false , "Id" )
tableo . ColMap ( "Id" ) . SetMaxSize ( 26 )
tableo . ColMap ( "Token" ) . SetMaxSize ( 26 )
tableo . ColMap ( "CreatorId" ) . SetMaxSize ( 26 )
tableo . ColMap ( "ChannelId" ) . SetMaxSize ( 26 )
tableo . ColMap ( "TeamId" ) . SetMaxSize ( 26 )
tableo . ColMap ( "TriggerWords" ) . SetMaxSize ( 1024 )
tableo . ColMap ( "CallbackURLs" ) . SetMaxSize ( 1024 )
2016-03-30 10:56:20 -04:00
tableo . ColMap ( "DisplayName" ) . SetMaxSize ( 64 )
tableo . ColMap ( "Description" ) . SetMaxSize ( 128 )
2016-05-31 16:51:28 +02:00
tableo . ColMap ( "ContentType" ) . SetMaxSize ( 128 )
2016-07-26 14:04:28 +02:00
tableo . ColMap ( "TriggerWhen" ) . SetMaxSize ( 1 )
2015-09-21 14:22:23 -04:00
}
return s
}
func ( s SqlWebhookStore ) CreateIndexesIfNotExists ( ) {
2015-10-01 14:07:20 -04:00
s . CreateIndexIfNotExists ( "idx_incoming_webhook_user_id" , "IncomingWebhooks" , "UserId" )
s . CreateIndexIfNotExists ( "idx_incoming_webhook_team_id" , "IncomingWebhooks" , "TeamId" )
2015-10-19 11:12:40 -04:00
s . CreateIndexIfNotExists ( "idx_outgoing_webhook_team_id" , "OutgoingWebhooks" , "TeamId" )
2016-10-24 17:05:27 -03:00
s . CreateIndexIfNotExists ( "idx_incoming_webhook_update_at" , "IncomingWebhooks" , "UpdateAt" )
s . CreateIndexIfNotExists ( "idx_incoming_webhook_create_at" , "IncomingWebhooks" , "CreateAt" )
s . CreateIndexIfNotExists ( "idx_incoming_webhook_delete_at" , "IncomingWebhooks" , "DeleteAt" )
s . CreateIndexIfNotExists ( "idx_outgoing_webhook_update_at" , "OutgoingWebhooks" , "UpdateAt" )
s . CreateIndexIfNotExists ( "idx_outgoing_webhook_create_at" , "OutgoingWebhooks" , "CreateAt" )
s . CreateIndexIfNotExists ( "idx_outgoing_webhook_delete_at" , "OutgoingWebhooks" , "DeleteAt" )
2015-09-21 14:22:23 -04:00
}
2017-01-27 14:07:34 -05:00
func ( s SqlWebhookStore ) InvalidateWebhookCache ( webhookId string ) {
webhookCache . Remove ( webhookId )
}
2015-09-21 14:22:23 -04:00
func ( s SqlWebhookStore ) SaveIncoming ( webhook * model . IncomingWebhook ) StoreChannel {
2016-09-19 19:30:41 +07:00
storeChannel := make ( StoreChannel , 1 )
2015-09-21 14:22:23 -04:00
go func ( ) {
result := StoreResult { }
if len ( webhook . Id ) > 0 {
2016-01-25 16:08:06 -03:00
result . Err = model . NewLocAppError ( "SqlWebhookStore.SaveIncoming" ,
"store.sql_webhooks.save_incoming.existing.app_error" , nil , "id=" + webhook . Id )
2015-09-21 14:22:23 -04:00
storeChannel <- result
close ( storeChannel )
return
}
webhook . PreSave ( )
if result . Err = webhook . IsValid ( ) ; result . Err != nil {
storeChannel <- result
close ( storeChannel )
return
}
if err := s . GetMaster ( ) . Insert ( webhook ) ; err != nil {
2016-01-25 16:08:06 -03:00
result . Err = model . NewLocAppError ( "SqlWebhookStore.SaveIncoming" , "store.sql_webhooks.save_incoming.app_error" , nil , "id=" + webhook . Id + ", " + err . Error ( ) )
2015-09-21 14:22:23 -04:00
} else {
result . Data = webhook
}
storeChannel <- result
close ( storeChannel )
} ( )
return storeChannel
}
2017-02-27 00:18:20 +05:30
func ( s SqlWebhookStore ) UpdateIncoming ( hook * model . IncomingWebhook ) StoreChannel {
storeChannel := make ( StoreChannel , 1 )
go func ( ) {
result := StoreResult { }
hook . UpdateAt = model . GetMillis ( )
if _ , err := s . GetMaster ( ) . Update ( hook ) ; err != nil {
result . Err = model . NewLocAppError ( "SqlWebhookStore.UpdateIncoming" , "store.sql_webhooks.update_incoming.app_error" , nil , "id=" + hook . Id + ", " + err . Error ( ) )
} else {
result . Data = hook
}
storeChannel <- result
close ( storeChannel )
} ( )
return storeChannel
}
2017-01-27 14:07:34 -05:00
func ( s SqlWebhookStore ) GetIncoming ( id string , allowFromCache bool ) StoreChannel {
2016-09-19 19:30:41 +07:00
storeChannel := make ( StoreChannel , 1 )
2015-09-21 14:22:23 -04:00
go func ( ) {
result := StoreResult { }
2017-01-27 14:07:34 -05:00
if allowFromCache {
metrics := einterfaces . GetMetricsInterface ( )
if cacheItem , ok := webhookCache . Get ( id ) ; ok {
if metrics != nil {
metrics . IncrementMemCacheHitCounter ( "Webhook" )
}
result . Data = cacheItem . ( * model . IncomingWebhook )
storeChannel <- result
close ( storeChannel )
return
} else {
if metrics != nil {
metrics . IncrementMemCacheMissCounter ( "Webhook" )
}
}
}
2015-09-21 14:22:23 -04:00
var webhook model . IncomingWebhook
if err := s . GetReplica ( ) . SelectOne ( & webhook , "SELECT * FROM IncomingWebhooks WHERE Id = :Id AND DeleteAt = 0" , map [ string ] interface { } { "Id" : id } ) ; err != nil {
2016-01-25 16:08:06 -03:00
result . Err = model . NewLocAppError ( "SqlWebhookStore.GetIncoming" , "store.sql_webhooks.get_incoming.app_error" , nil , "id=" + id + ", err=" + err . Error ( ) )
2017-03-12 04:10:56 +05:30
if err == sql . ErrNoRows {
result . Err . StatusCode = http . StatusNotFound
}
2015-09-21 14:22:23 -04:00
}
2017-01-27 14:07:34 -05:00
if result . Err == nil {
webhookCache . AddWithExpiresInSecs ( id , & webhook , WEBHOOK_CACHE_SEC )
}
2015-09-21 14:22:23 -04:00
result . Data = & webhook
storeChannel <- result
close ( storeChannel )
} ( )
return storeChannel
}
func ( s SqlWebhookStore ) DeleteIncoming ( webhookId string , time int64 ) StoreChannel {
2016-09-19 19:30:41 +07:00
storeChannel := make ( StoreChannel , 1 )
2015-09-21 14:22:23 -04:00
go func ( ) {
result := StoreResult { }
_ , err := s . GetMaster ( ) . Exec ( "Update IncomingWebhooks SET DeleteAt = :DeleteAt, UpdateAt = :UpdateAt WHERE Id = :Id" , map [ string ] interface { } { "DeleteAt" : time , "UpdateAt" : time , "Id" : webhookId } )
if err != nil {
2016-01-25 16:08:06 -03:00
result . Err = model . NewLocAppError ( "SqlWebhookStore.DeleteIncoming" , "store.sql_webhooks.delete_incoming.app_error" , nil , "id=" + webhookId + ", err=" + err . Error ( ) )
2015-09-21 14:22:23 -04:00
}
storeChannel <- result
close ( storeChannel )
} ( )
return storeChannel
}
2015-11-16 17:12:49 -08:00
func ( s SqlWebhookStore ) PermanentDeleteIncomingByUser ( userId string ) StoreChannel {
2016-09-19 19:30:41 +07:00
storeChannel := make ( StoreChannel , 1 )
2015-11-15 18:18:02 -08:00
go func ( ) {
result := StoreResult { }
_ , err := s . GetMaster ( ) . Exec ( "DELETE FROM IncomingWebhooks WHERE UserId = :UserId" , map [ string ] interface { } { "UserId" : userId } )
if err != nil {
2016-01-25 16:08:06 -03:00
result . Err = model . NewLocAppError ( "SqlWebhookStore.DeleteIncomingByUser" , "store.sql_webhooks.permanent_delete_incoming_by_user.app_error" , nil , "id=" + userId + ", err=" + err . Error ( ) )
2015-11-15 18:18:02 -08:00
}
storeChannel <- result
close ( storeChannel )
} ( )
return storeChannel
}
2017-02-21 19:42:34 -05:00
func ( s SqlWebhookStore ) GetIncomingList ( offset , limit int ) StoreChannel {
storeChannel := make ( StoreChannel , 1 )
go func ( ) {
result := StoreResult { }
var webhooks [ ] * model . IncomingWebhook
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 {
result . Err = model . NewAppError ( "SqlWebhookStore.GetIncomingList" , "store.sql_webhooks.get_incoming_by_user.app_error" , nil , "err=" + err . Error ( ) , http . StatusInternalServerError )
}
result . Data = webhooks
storeChannel <- result
close ( storeChannel )
} ( )
return storeChannel
}
func ( s SqlWebhookStore ) GetIncomingByTeam ( teamId string , offset , limit int ) StoreChannel {
2016-09-19 19:30:41 +07:00
storeChannel := make ( StoreChannel , 1 )
2015-09-21 14:22:23 -04:00
go func ( ) {
result := StoreResult { }
var webhooks [ ] * model . IncomingWebhook
2017-02-21 19:42:34 -05:00
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 {
result . Err = model . NewAppError ( "SqlWebhookStore.GetIncomingByUser" , "store.sql_webhooks.get_incoming_by_user.app_error" , nil , "teamId=" + teamId + ", err=" + err . Error ( ) , http . StatusInternalServerError )
2015-09-21 14:22:23 -04:00
}
result . Data = webhooks
storeChannel <- result
close ( storeChannel )
} ( )
return storeChannel
}
2015-10-01 14:07:20 -04:00
2015-10-22 14:27:47 -04:00
func ( s SqlWebhookStore ) GetIncomingByChannel ( channelId string ) StoreChannel {
2016-09-19 19:30:41 +07:00
storeChannel := make ( StoreChannel , 1 )
2015-10-22 14:27:47 -04:00
go func ( ) {
result := StoreResult { }
var webhooks [ ] * model . IncomingWebhook
if _ , err := s . GetReplica ( ) . Select ( & webhooks , "SELECT * FROM IncomingWebhooks WHERE ChannelId = :ChannelId AND DeleteAt = 0" , map [ string ] interface { } { "ChannelId" : channelId } ) ; err != nil {
2016-01-25 16:08:06 -03:00
result . Err = model . NewLocAppError ( "SqlWebhookStore.GetIncomingByChannel" , "store.sql_webhooks.get_incoming_by_channel.app_error" , nil , "channelId=" + channelId + ", err=" + err . Error ( ) )
2015-10-22 14:27:47 -04:00
}
result . Data = webhooks
storeChannel <- result
close ( storeChannel )
} ( )
return storeChannel
}
2015-10-01 14:07:20 -04:00
func ( s SqlWebhookStore ) SaveOutgoing ( webhook * model . OutgoingWebhook ) StoreChannel {
2016-09-19 19:30:41 +07:00
storeChannel := make ( StoreChannel , 1 )
2015-10-01 14:07:20 -04:00
go func ( ) {
result := StoreResult { }
if len ( webhook . Id ) > 0 {
2016-01-25 16:08:06 -03:00
result . Err = model . NewLocAppError ( "SqlWebhookStore.SaveOutgoing" ,
"store.sql_webhooks.save_outgoing.override.app_error" , nil , "id=" + webhook . Id )
2015-10-01 14:07:20 -04:00
storeChannel <- result
close ( storeChannel )
return
}
webhook . PreSave ( )
if result . Err = webhook . IsValid ( ) ; result . Err != nil {
storeChannel <- result
close ( storeChannel )
return
}
if err := s . GetMaster ( ) . Insert ( webhook ) ; err != nil {
2016-01-25 16:08:06 -03:00
result . Err = model . NewLocAppError ( "SqlWebhookStore.SaveOutgoing" , "store.sql_webhooks.save_outgoing.app_error" , nil , "id=" + webhook . Id + ", " + err . Error ( ) )
2015-10-01 14:07:20 -04:00
} else {
result . Data = webhook
}
storeChannel <- result
close ( storeChannel )
} ( )
return storeChannel
}
func ( s SqlWebhookStore ) GetOutgoing ( id string ) StoreChannel {
2016-09-19 19:30:41 +07:00
storeChannel := make ( StoreChannel , 1 )
2015-10-01 14:07:20 -04:00
go func ( ) {
result := StoreResult { }
var webhook model . OutgoingWebhook
if err := s . GetReplica ( ) . SelectOne ( & webhook , "SELECT * FROM OutgoingWebhooks WHERE Id = :Id AND DeleteAt = 0" , map [ string ] interface { } { "Id" : id } ) ; err != nil {
2016-01-25 16:08:06 -03:00
result . Err = model . NewLocAppError ( "SqlWebhookStore.GetOutgoing" , "store.sql_webhooks.get_outgoing.app_error" , nil , "id=" + id + ", err=" + err . Error ( ) )
2015-10-01 14:07:20 -04:00
}
result . Data = & webhook
storeChannel <- result
close ( storeChannel )
} ( )
return storeChannel
}
2017-03-13 10:40:43 -04:00
func ( s SqlWebhookStore ) GetOutgoingList ( offset , limit int ) StoreChannel {
2016-09-19 19:30:41 +07:00
storeChannel := make ( StoreChannel , 1 )
2015-10-01 14:07:20 -04:00
go func ( ) {
result := StoreResult { }
var webhooks [ ] * model . OutgoingWebhook
2017-03-13 10:40:43 -04:00
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 {
result . Err = model . NewLocAppError ( "SqlWebhookStore.GetOutgoingList" , "store.sql_webhooks.get_outgoing_by_channel.app_error" , nil , "err=" + err . Error ( ) )
}
result . Data = webhooks
storeChannel <- result
close ( storeChannel )
} ( )
return storeChannel
}
func ( s SqlWebhookStore ) GetOutgoingByChannel ( channelId string , offset , limit int ) StoreChannel {
storeChannel := make ( StoreChannel , 1 )
go func ( ) {
result := StoreResult { }
var webhooks [ ] * model . OutgoingWebhook
query := ""
if limit < 0 || offset < 0 {
query = "SELECT * FROM OutgoingWebhooks WHERE ChannelId = :ChannelId AND DeleteAt = 0"
} else {
query = "SELECT * FROM OutgoingWebhooks WHERE ChannelId = :ChannelId AND DeleteAt = 0 LIMIT :Limit OFFSET :Offset"
}
if _ , err := s . GetReplica ( ) . Select ( & webhooks , query , map [ string ] interface { } { "ChannelId" : channelId , "Offset" : offset , "Limit" : limit } ) ; err != nil {
2016-01-25 16:08:06 -03:00
result . Err = model . NewLocAppError ( "SqlWebhookStore.GetOutgoingByChannel" , "store.sql_webhooks.get_outgoing_by_channel.app_error" , nil , "channelId=" + channelId + ", err=" + err . Error ( ) )
2015-10-01 14:07:20 -04:00
}
result . Data = webhooks
storeChannel <- result
close ( storeChannel )
} ( )
return storeChannel
}
2017-03-13 10:40:43 -04:00
func ( s SqlWebhookStore ) GetOutgoingByTeam ( teamId string , offset , limit int ) StoreChannel {
2016-09-19 19:30:41 +07:00
storeChannel := make ( StoreChannel , 1 )
2015-10-01 14:07:20 -04:00
go func ( ) {
result := StoreResult { }
var webhooks [ ] * model . OutgoingWebhook
2017-03-13 10:40:43 -04:00
query := ""
if limit < 0 || offset < 0 {
query = "SELECT * FROM OutgoingWebhooks WHERE TeamId = :TeamId AND DeleteAt = 0"
} else {
query = "SELECT * FROM OutgoingWebhooks WHERE TeamId = :TeamId AND DeleteAt = 0 LIMIT :Limit OFFSET :Offset"
}
if _ , err := s . GetReplica ( ) . Select ( & webhooks , query , map [ string ] interface { } { "TeamId" : teamId , "Offset" : offset , "Limit" : limit } ) ; err != nil {
2016-01-25 16:08:06 -03:00
result . Err = model . NewLocAppError ( "SqlWebhookStore.GetOutgoingByTeam" , "store.sql_webhooks.get_outgoing_by_team.app_error" , nil , "teamId=" + teamId + ", err=" + err . Error ( ) )
2015-10-01 14:07:20 -04:00
}
result . Data = webhooks
storeChannel <- result
close ( storeChannel )
} ( )
return storeChannel
}
func ( s SqlWebhookStore ) DeleteOutgoing ( webhookId string , time int64 ) StoreChannel {
2016-09-19 19:30:41 +07:00
storeChannel := make ( StoreChannel , 1 )
2015-10-01 14:07:20 -04:00
go func ( ) {
result := StoreResult { }
_ , err := s . GetMaster ( ) . Exec ( "Update OutgoingWebhooks SET DeleteAt = :DeleteAt, UpdateAt = :UpdateAt WHERE Id = :Id" , map [ string ] interface { } { "DeleteAt" : time , "UpdateAt" : time , "Id" : webhookId } )
if err != nil {
2016-01-25 16:08:06 -03:00
result . Err = model . NewLocAppError ( "SqlWebhookStore.DeleteOutgoing" , "store.sql_webhooks.delete_outgoing.app_error" , nil , "id=" + webhookId + ", err=" + err . Error ( ) )
2015-10-01 14:07:20 -04:00
}
storeChannel <- result
close ( storeChannel )
} ( )
return storeChannel
}
2015-11-16 17:12:49 -08:00
func ( s SqlWebhookStore ) PermanentDeleteOutgoingByUser ( userId string ) StoreChannel {
2016-09-19 19:30:41 +07:00
storeChannel := make ( StoreChannel , 1 )
2015-11-15 18:18:02 -08:00
go func ( ) {
result := StoreResult { }
_ , err := s . GetMaster ( ) . Exec ( "DELETE FROM OutgoingWebhooks WHERE CreatorId = :UserId" , map [ string ] interface { } { "UserId" : userId } )
if err != nil {
2016-01-25 16:08:06 -03:00
result . Err = model . NewLocAppError ( "SqlWebhookStore.DeleteOutgoingByUser" , "store.sql_webhooks.permanent_delete_outgoing_by_user.app_error" , nil , "id=" + userId + ", err=" + err . Error ( ) )
2015-11-15 18:18:02 -08:00
}
storeChannel <- result
close ( storeChannel )
} ( )
return storeChannel
}
2015-10-01 14:07:20 -04:00
func ( s SqlWebhookStore ) UpdateOutgoing ( hook * model . OutgoingWebhook ) StoreChannel {
2016-09-19 19:30:41 +07:00
storeChannel := make ( StoreChannel , 1 )
2015-10-01 14:07:20 -04:00
go func ( ) {
result := StoreResult { }
hook . UpdateAt = model . GetMillis ( )
if _ , err := s . GetMaster ( ) . Update ( hook ) ; err != nil {
2016-01-25 16:08:06 -03:00
result . Err = model . NewLocAppError ( "SqlWebhookStore.UpdateOutgoing" , "store.sql_webhooks.update_outgoing.app_error" , nil , "id=" + hook . Id + ", " + err . Error ( ) )
2015-10-01 14:07:20 -04:00
} else {
result . Data = hook
}
storeChannel <- result
close ( storeChannel )
} ( )
return storeChannel
}
2016-02-02 08:41:02 -05:00
func ( s SqlWebhookStore ) AnalyticsIncomingCount ( teamId string ) StoreChannel {
2016-09-19 19:30:41 +07:00
storeChannel := make ( StoreChannel , 1 )
2016-02-02 08:41:02 -05:00
go func ( ) {
result := StoreResult { }
query :=
` SELECT
COUNT ( * )
FROM
IncomingWebhooks
WHERE
DeleteAt = 0 `
if len ( teamId ) > 0 {
query += " AND TeamId = :TeamId"
}
if v , err := s . GetReplica ( ) . SelectInt ( query , map [ string ] interface { } { "TeamId" : teamId } ) ; err != nil {
result . Err = model . NewLocAppError ( "SqlWebhookStore.AnalyticsIncomingCount" , "store.sql_webhooks.analytics_incoming_count.app_error" , nil , "team_id=" + teamId + ", err=" + err . Error ( ) )
} else {
result . Data = v
}
storeChannel <- result
close ( storeChannel )
} ( )
return storeChannel
}
func ( s SqlWebhookStore ) AnalyticsOutgoingCount ( teamId string ) StoreChannel {
2016-09-19 19:30:41 +07:00
storeChannel := make ( StoreChannel , 1 )
2016-02-02 08:41:02 -05:00
go func ( ) {
result := StoreResult { }
query :=
` SELECT
COUNT ( * )
FROM
OutgoingWebhooks
WHERE
DeleteAt = 0 `
if len ( teamId ) > 0 {
query += " AND TeamId = :TeamId"
}
if v , err := s . GetReplica ( ) . SelectInt ( query , map [ string ] interface { } { "TeamId" : teamId } ) ; err != nil {
result . Err = model . NewLocAppError ( "SqlWebhookStore.AnalyticsOutgoingCount" , "store.sql_webhooks.analytics_outgoing_count.app_error" , nil , "team_id=" + teamId + ", err=" + err . Error ( ) )
} else {
result . Data = v
}
storeChannel <- result
close ( storeChannel )
} ( )
return storeChannel
}