github.com/kaleido-io/firefly@v0.0.0-20210622132723-8b4b6aacb971/internal/database/sqlcommon/subscription_sql.go (about) 1 // Copyright © 2021 Kaleido, Inc. 2 // 3 // SPDX-License-Identifier: Apache-2.0 4 // 5 // Licensed under the Apache License, Version 2.0 (the "License"); 6 // you may not use this file except in compliance with the License. 7 // You may obtain a copy of the License at 8 // 9 // http://www.apache.org/licenses/LICENSE-2.0 10 // 11 // Unless required by applicable law or agreed to in writing, software 12 // distributed under the License is distributed on an "AS IS" BASIS, 13 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 // See the License for the specific language governing permissions and 15 // limitations under the License. 16 17 package sqlcommon 18 19 import ( 20 "context" 21 "database/sql" 22 "fmt" 23 24 sq "github.com/Masterminds/squirrel" 25 "github.com/kaleido-io/firefly/internal/i18n" 26 "github.com/kaleido-io/firefly/internal/log" 27 "github.com/kaleido-io/firefly/pkg/database" 28 "github.com/kaleido-io/firefly/pkg/fftypes" 29 ) 30 31 var ( 32 subscriptionColumns = []string{ 33 "id", 34 "namespace", 35 "name", 36 "transport", 37 "filter_events", 38 "filter_topics", 39 "filter_tag", 40 "filter_group", 41 "options", 42 "created", 43 } 44 subscriptionFilterTypeMap = map[string]string{ 45 "filter.events": "filter_events", 46 "filter.topics": "filter_topics", 47 "filter.tag": "filter_tag", 48 "filter.group": "filter_group", 49 } 50 ) 51 52 func (s *SQLCommon) UpsertSubscription(ctx context.Context, subscription *fftypes.Subscription, allowExisting bool) (err error) { 53 ctx, tx, autoCommit, err := s.beginOrUseTx(ctx) 54 if err != nil { 55 return err 56 } 57 defer s.rollbackTx(ctx, tx, autoCommit) 58 59 existing := false 60 if allowExisting { 61 // Do a select within the transaction to detemine if the UUID already exists 62 subscriptionRows, err := s.queryTx(ctx, tx, 63 sq.Select("id"). 64 From("subscriptions"). 65 Where(sq.Eq{ 66 "namespace": subscription.Namespace, 67 "name": subscription.Name, 68 }), 69 ) 70 if err != nil { 71 return err 72 } 73 74 existing = subscriptionRows.Next() 75 if existing { 76 var id fftypes.UUID 77 _ = subscriptionRows.Scan(&id) 78 if subscription.ID != nil { 79 if *subscription.ID != id { 80 subscriptionRows.Close() 81 return database.IDMismatch 82 } 83 } 84 subscription.ID = &id // Update on returned object 85 } 86 subscriptionRows.Close() 87 } 88 89 if existing { 90 // Update the subscription 91 if err = s.updateTx(ctx, tx, 92 sq.Update("subscriptions"). 93 // Note we do not update ID 94 Set("namespace", subscription.Namespace). 95 Set("name", subscription.Name). 96 Set("transport", subscription.Transport). 97 Set("filter_events", subscription.Filter.Events). 98 Set("filter_topics", subscription.Filter.Topics). 99 Set("filter_tag", subscription.Filter.Tag). 100 Set("filter_group", subscription.Filter.Group). 101 Set("options", subscription.Options). 102 Set("created", subscription.Created). 103 Where(sq.Eq{ 104 "namespace": subscription.Namespace, 105 "name": subscription.Name, 106 }), 107 ); err != nil { 108 return err 109 } 110 } else { 111 if subscription.ID == nil { 112 subscription.ID = fftypes.NewUUID() 113 } 114 115 if _, err = s.insertTx(ctx, tx, 116 sq.Insert("subscriptions"). 117 Columns(subscriptionColumns...). 118 Values( 119 subscription.ID, 120 subscription.Namespace, 121 subscription.Name, 122 subscription.Transport, 123 subscription.Filter.Events, 124 subscription.Filter.Topics, 125 subscription.Filter.Tag, 126 subscription.Filter.Group, 127 subscription.Options, 128 subscription.Created, 129 ), 130 ); err != nil { 131 return err 132 } 133 134 s.postCommitEvent(tx, func() { 135 s.callbacks.SubscriptionCreated(subscription.ID) 136 }) 137 138 } 139 140 return s.commitTx(ctx, tx, autoCommit) 141 } 142 143 func (s *SQLCommon) subscriptionResult(ctx context.Context, row *sql.Rows) (*fftypes.Subscription, error) { 144 subscription := fftypes.Subscription{} 145 err := row.Scan( 146 &subscription.ID, 147 &subscription.Namespace, 148 &subscription.Name, 149 &subscription.Transport, 150 &subscription.Filter.Events, 151 &subscription.Filter.Topics, 152 &subscription.Filter.Tag, 153 &subscription.Filter.Group, 154 &subscription.Options, 155 &subscription.Created, 156 ) 157 if err != nil { 158 return nil, i18n.WrapError(ctx, err, i18n.MsgDBReadErr, "subscriptions") 159 } 160 return &subscription, nil 161 } 162 163 func (s *SQLCommon) getSubscriptionEq(ctx context.Context, eq sq.Eq, textName string) (message *fftypes.Subscription, err error) { 164 165 rows, err := s.query(ctx, 166 sq.Select(subscriptionColumns...). 167 From("subscriptions"). 168 Where(eq), 169 ) 170 if err != nil { 171 return nil, err 172 } 173 defer rows.Close() 174 175 if !rows.Next() { 176 log.L(ctx).Debugf("Subscription '%s' not found", textName) 177 return nil, nil 178 } 179 180 subscription, err := s.subscriptionResult(ctx, rows) 181 if err != nil { 182 return nil, err 183 } 184 185 return subscription, nil 186 } 187 188 func (s *SQLCommon) GetSubscriptionByID(ctx context.Context, id *fftypes.UUID) (message *fftypes.Subscription, err error) { 189 return s.getSubscriptionEq(ctx, sq.Eq{"id": id}, id.String()) 190 } 191 192 func (s *SQLCommon) GetSubscriptionByName(ctx context.Context, ns, name string) (message *fftypes.Subscription, err error) { 193 return s.getSubscriptionEq(ctx, sq.Eq{"namespace": ns, "name": name}, fmt.Sprintf("%s:%s", ns, name)) 194 } 195 196 func (s *SQLCommon) GetSubscriptions(ctx context.Context, filter database.Filter) (message []*fftypes.Subscription, err error) { 197 198 query, err := s.filterSelect(ctx, "", sq.Select(subscriptionColumns...).From("subscriptions"), filter, subscriptionFilterTypeMap) 199 if err != nil { 200 return nil, err 201 } 202 203 rows, err := s.query(ctx, query) 204 if err != nil { 205 return nil, err 206 } 207 defer rows.Close() 208 209 subscription := []*fftypes.Subscription{} 210 for rows.Next() { 211 d, err := s.subscriptionResult(ctx, rows) 212 if err != nil { 213 return nil, err 214 } 215 subscription = append(subscription, d) 216 } 217 218 return subscription, err 219 220 } 221 222 func (s *SQLCommon) UpdateSubscription(ctx context.Context, namespace, name string, update database.Update) (err error) { 223 224 ctx, tx, autoCommit, err := s.beginOrUseTx(ctx) 225 if err != nil { 226 return err 227 } 228 defer s.rollbackTx(ctx, tx, autoCommit) 229 230 query, err := s.buildUpdate(sq.Update("subscriptions"), update, subscriptionFilterTypeMap) 231 if err != nil { 232 return err 233 } 234 query = query.Where(sq.Eq{ 235 "namespace": namespace, 236 "name": name, 237 }) 238 239 err = s.updateTx(ctx, tx, query) 240 if err != nil { 241 return err 242 } 243 244 return s.commitTx(ctx, tx, autoCommit) 245 } 246 247 func (s *SQLCommon) DeleteSubscriptionByID(ctx context.Context, id *fftypes.UUID) (err error) { 248 249 ctx, tx, autoCommit, err := s.beginOrUseTx(ctx) 250 if err != nil { 251 return err 252 } 253 defer s.rollbackTx(ctx, tx, autoCommit) 254 255 err = s.deleteTx(ctx, tx, sq.Delete("subscriptions").Where(sq.Eq{ 256 "id": id, 257 })) 258 if err != nil { 259 return err 260 } 261 262 s.postCommitEvent(tx, func() { 263 s.callbacks.SubscriptionDeleted(id) 264 }) 265 266 return s.commitTx(ctx, tx, autoCommit) 267 }