├── .gitignore ├── LICENSE ├── README.md ├── amqp ├── amqp_sink.go ├── amqp_source.go └── example │ └── example.go ├── consumer ├── backoff │ ├── backoff.go │ └── backoff_test.go └── deadletter │ ├── deadletter.go │ └── deadletter_test.go ├── instrumented ├── instrumented_concurrent_pubsub.go ├── instrumented_pubsub.go └── instrumented_pubsub_test.go ├── kafka ├── example │ └── example.go ├── kafka.go ├── kafka_test.go ├── kafkasink.go ├── kafkasource.go └── status.go ├── mockqueue ├── example │ └── main.go └── mockqueue.go ├── natss ├── example │ └── example.go ├── natssink.go ├── source.go └── status.go ├── proximo ├── internal │ └── proximoc │ │ ├── client.go │ │ └── proximo.pb.go ├── proximo_sink.go ├── proximo_source.go └── proximo_test.go ├── pubsub.go ├── sns ├── export_test.go ├── mocks │ └── snsapi.go ├── snssink.go └── snssink_test.go └── sqs ├── example ├── sink.go └── source.go ├── sink.go ├── sink_test.go ├── source.go ├── source_test.go └── sqs.go /.gitignore: -------------------------------------------------------------------------------- 1 | .idea/ 2 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2016 Utility Warehouse 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | go-pubsub 2 | ========= 3 | 4 | A simple abstraction for message publishing and consumption that presents a uniform API, regardless of the underlying implementation. 5 | 6 | Current implementations and their status 7 | ---------------------------------------- 8 | 9 | | Implementation | Status | 10 | | ---------------------------------------- | ------------- | 11 | | Mock (in memory testing implementation) | incomplete | 12 | | Apache Kafka | beta | 13 | | Nats streaming | beta | 14 | | AMQP | incomplete | 15 | | AWS SQS | beta | 16 | | AWS SNS | beta | 17 | 18 | 19 | The API is not yet guaranteed but changes should be minimal from now. 20 | 21 | -------------------------------------------------------------------------------- /amqp/amqp_sink.go: -------------------------------------------------------------------------------- 1 | package amqp 2 | 3 | import ( 4 | "errors" 5 | "fmt" 6 | "sync" 7 | 8 | "github.com/streadway/amqp" 9 | "github.com/utilitywarehouse/go-pubsub" 10 | ) 11 | 12 | var _ pubsub.MessageSink = (*messageSink)(nil) 13 | 14 | type messageSink struct { 15 | topic string 16 | 17 | lk sync.Mutex 18 | closed bool 19 | channel *amqp.Channel 20 | q amqp.Queue 21 | conn *amqp.Connection 22 | } 23 | 24 | type MessageSinkConfig struct { 25 | Topic string 26 | Address string 27 | } 28 | 29 | func NewMessageSink(config MessageSinkConfig) (pubsub.MessageSink, error) { 30 | 31 | conn, err := amqp.Dial(config.Address) 32 | if err != nil { 33 | return nil, err 34 | } 35 | 36 | ch, err := conn.Channel() 37 | if err != nil { 38 | return nil, err 39 | } 40 | 41 | q, err := ch.QueueDeclare( 42 | config.Topic, // name 43 | true, // durable 44 | false, // delete when unused 45 | false, // exclusive 46 | false, // no-wait 47 | nil, // arguments 48 | ) 49 | if err != nil { 50 | return nil, err 51 | } 52 | 53 | if err := ch.Confirm(false); err != nil { 54 | return nil, err 55 | } 56 | 57 | return &messageSink{ 58 | topic: config.Topic, 59 | 60 | conn: conn, 61 | q: q, 62 | channel: ch, 63 | }, nil 64 | } 65 | 66 | func (mq *messageSink) PutMessage(m pubsub.ProducerMessage) error { 67 | 68 | data, err := m.Marshal() 69 | if err != nil { 70 | return err 71 | } 72 | 73 | p := amqp.Publishing{ 74 | Body: data, 75 | } 76 | 77 | return mq.channel.Publish( 78 | "", // exchange 79 | mq.q.Name, // routing key 80 | true, // mandatory 81 | false, // immediate 82 | p, 83 | ) 84 | } 85 | 86 | func (mq *messageSink) Close() error { 87 | mq.lk.Lock() 88 | defer mq.lk.Unlock() 89 | 90 | if mq.closed { 91 | return errors.New("Already closed") 92 | } 93 | 94 | mq.closed = true 95 | 96 | err1 := mq.channel.Close() 97 | err2 := mq.conn.Close() 98 | if err1 != nil { 99 | if err2 != nil { 100 | return fmt.Errorf("errors closing amqp channel and connection :\n%v\n%v\n", err1, err2) 101 | } 102 | return err1 103 | } 104 | return err2 105 | } 106 | func (mq *messageSink) Status() (*pubsub.Status, error) { 107 | return nil, errors.New("status is not implemented") 108 | 109 | } 110 | -------------------------------------------------------------------------------- /amqp/amqp_source.go: -------------------------------------------------------------------------------- 1 | package amqp 2 | 3 | import ( 4 | "context" 5 | "errors" 6 | 7 | "github.com/streadway/amqp" 8 | "github.com/utilitywarehouse/go-pubsub" 9 | ) 10 | 11 | var _ pubsub.MessageSource = (*messageSource)(nil) 12 | 13 | type messageSource struct { 14 | consumergroup string 15 | topic string 16 | address string 17 | } 18 | 19 | type MessageSourceConfig struct { 20 | ConsumerGroup string 21 | Topic string 22 | Address string 23 | } 24 | 25 | func NewMessageSource(config MessageSourceConfig) pubsub.MessageSource { 26 | return &messageSource{ 27 | consumergroup: config.ConsumerGroup, 28 | topic: config.Topic, 29 | address: config.Address, 30 | } 31 | } 32 | 33 | func (mq *messageSource) ConsumeMessages(ctx context.Context, handler pubsub.ConsumerMessageHandler, onError pubsub.ConsumerErrorHandler) error { 34 | 35 | conn, err := amqp.Dial(mq.address) 36 | if err != nil { 37 | return err 38 | } 39 | defer conn.Close() 40 | 41 | ch, err := conn.Channel() 42 | if err != nil { 43 | return err 44 | } 45 | defer ch.Close() 46 | 47 | q, err := ch.QueueDeclare( 48 | mq.topic, // name 49 | true, // durable 50 | false, // delete when usused 51 | false, // exclusive 52 | false, // no-wait 53 | nil, // arguments 54 | ) 55 | if err != nil { 56 | return err 57 | } 58 | 59 | msgs, err := ch.Consume( 60 | q.Name, // queue 61 | mq.consumergroup, // consumer 62 | false, // auto-ack 63 | false, // exclusive 64 | false, // no-local 65 | false, // no-wait 66 | nil, // args 67 | ) 68 | if err != nil { 69 | return err 70 | } 71 | 72 | for { 73 | select { 74 | case msg := <-msgs: 75 | message := pubsub.ConsumerMessage{msg.Body} 76 | err := handler(message) 77 | if err != nil { 78 | err := onError(message, err) 79 | if err != nil { 80 | return err 81 | } 82 | } 83 | 84 | msg.Ack(false) 85 | case <-ctx.Done(): 86 | return ch.Close() 87 | } 88 | } 89 | } 90 | 91 | func (mq *messageSource) Status() (*pubsub.Status, error) { 92 | return nil, errors.New("status is not implemented") 93 | } 94 | -------------------------------------------------------------------------------- /amqp/example/example.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "context" 5 | "encoding/json" 6 | "fmt" 7 | "log" 8 | "time" 9 | 10 | "github.com/utilitywarehouse/go-pubsub" 11 | "github.com/utilitywarehouse/go-pubsub/amqp" 12 | ) 13 | 14 | func main() { 15 | 16 | sink, err := amqp.NewMessageSink(amqp.MessageSinkConfig{ 17 | Address: "amqp://localhost:5672/", 18 | Topic: "demo-topic", 19 | }) 20 | 21 | if err != nil { 22 | log.Fatal(err) 23 | } 24 | 25 | sink.PutMessage(MyMessage{ 26 | CustomerID: "customer-01", 27 | Message: fmt.Sprintf("hello. it is currently %v", time.Now()), 28 | }) 29 | 30 | cons := amqp.NewMessageSource(amqp.MessageSourceConfig{ 31 | Address: "amqp://localhost:5672/", 32 | ConsumerGroup: "demo-group", 33 | Topic: "demo-topic", 34 | }) 35 | 36 | // consume messages for 2 seconds 37 | ctx, _ := context.WithTimeout(context.Background(), 2*time.Second) 38 | 39 | handler := func(m pubsub.ConsumerMessage) error { 40 | fmt.Printf("message is: %s\n", m.Data) 41 | return nil 42 | } 43 | 44 | onError := func(m pubsub.ConsumerMessage, e error) error { 45 | panic("unexpected error") 46 | } 47 | 48 | if err := cons.ConsumeMessages(ctx, handler, onError); err != nil { 49 | log.Fatal(err) 50 | } 51 | 52 | } 53 | 54 | type MyMessage struct { 55 | CustomerID string 56 | Message string 57 | } 58 | 59 | func (m MyMessage) Marshal() ([]byte, error) { 60 | return json.Marshal(m) 61 | } 62 | -------------------------------------------------------------------------------- /consumer/backoff/backoff.go: -------------------------------------------------------------------------------- 1 | package backoff 2 | 3 | import ( 4 | "math" 5 | "time" 6 | 7 | "github.com/utilitywarehouse/go-pubsub" 8 | ) 9 | 10 | // New returns a new ExponentialBackOffRetryingErrorHandler 11 | func New(handler pubsub.ConsumerMessageHandler, retries int) pubsub.ConsumerErrorHandler { 12 | return NewWithFallback( 13 | handler, 14 | func(msg pubsub.ConsumerMessage, err error) error { 15 | return err 16 | }, 17 | retries, 18 | ) 19 | } 20 | 21 | // NewWithFallback returns a new ExponentialBackOffRetryingErrorHandler with fallback 22 | func NewWithFallback(handler pubsub.ConsumerMessageHandler, errHandler pubsub.ConsumerErrorHandler, retries int) pubsub.ConsumerErrorHandler { 23 | return func(msg pubsub.ConsumerMessage, err error) error { 24 | for i := 0; i < retries; i++ { 25 | err := handler(msg) 26 | if err == nil { 27 | return nil 28 | } 29 | <-time.After(time.Duration(3*math.Pow(2, float64(i+1))) * time.Second) 30 | } 31 | return errHandler(msg, err) 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /consumer/backoff/backoff_test.go: -------------------------------------------------------------------------------- 1 | package backoff 2 | 3 | import ( 4 | "context" 5 | "testing" 6 | 7 | "github.com/pkg/errors" 8 | "github.com/utilitywarehouse/go-pubsub" 9 | "github.com/utilitywarehouse/go-pubsub/mockqueue" 10 | 11 | "github.com/magiconair/properties/assert" 12 | ) 13 | 14 | func TestBackOff(t *testing.T) { 15 | in := mockqueue.NewMockQueue() 16 | 17 | err := in.PutMessage(pubsub.SimpleProducerMessage([]byte("test"))) 18 | if err != nil { 19 | t.Fatalf("error publishing message: [%+v]", err) 20 | } 21 | 22 | retryCount := 0 23 | ctx, cancel := context.WithCancel(context.Background()) 24 | handler := func(m pubsub.ConsumerMessage) error { 25 | if retryCount == 3 { 26 | cancel() 27 | return nil 28 | } 29 | retryCount++ 30 | return errors.New("error message") 31 | } 32 | boErrHanlder := pubsub.ConsumerErrorHandler(New(handler, 3)) 33 | 34 | in.ConsumeMessages(ctx, handler, boErrHanlder) 35 | assert.Equal(t, retryCount, 3) 36 | } 37 | -------------------------------------------------------------------------------- /consumer/deadletter/deadletter.go: -------------------------------------------------------------------------------- 1 | package deadletter 2 | 3 | import ( 4 | "encoding/json" 5 | "fmt" 6 | "time" 7 | 8 | "github.com/utilitywarehouse/go-pubsub" 9 | ) 10 | 11 | // FailedConsumerMessage a struct for storing failed consumer messages 12 | type FailedConsumerMessage struct { 13 | Message []byte `json:"message"` 14 | MessageTopic string `json:"messageTopic"` 15 | Consumer string `json:"consumer"` 16 | Err string `json:"error"` 17 | Timestamp time.Time `json:"timestamp"` 18 | } 19 | 20 | // New returns a new ConsumerErrorHandler which produces JSON serialized FailedConsumerMessage to sink 21 | func New(sink pubsub.MessageSink, messageTopic string, consumer string) pubsub.ConsumerErrorHandler { 22 | return NewWithFallback( 23 | sink, 24 | func(msg pubsub.ConsumerMessage, err error) error { 25 | return err 26 | }, 27 | messageTopic, 28 | consumer, 29 | ) 30 | } 31 | 32 | // NewWithFallback returns a new ConsumerErrorHandler which produces JSON serialized FailedConsumerMessage to sink with fallback handler 33 | func NewWithFallback(sink pubsub.MessageSink, errHandler pubsub.ConsumerErrorHandler, messageTopic string, consumer string) pubsub.ConsumerErrorHandler { 34 | return func(msg pubsub.ConsumerMessage, err error) error { 35 | failedMsg := FailedConsumerMessage{ 36 | Message: msg.Data, 37 | MessageTopic: messageTopic, 38 | Consumer: consumer, 39 | Err: err.Error(), 40 | Timestamp: time.Now(), 41 | } 42 | failedMsgJSON, err := json.Marshal(failedMsg) 43 | if err != nil { 44 | return errHandler(msg, fmt.Errorf("Error serialising failed message to JSON: %v", err)) 45 | } 46 | if err := sink.PutMessage(pubsub.SimpleProducerMessage(failedMsgJSON)); err != nil { 47 | return errHandler(msg, fmt.Errorf("Error producing failed message to dead letter queue: %v", err)) 48 | } 49 | return nil 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /consumer/deadletter/deadletter_test.go: -------------------------------------------------------------------------------- 1 | package deadletter 2 | 3 | import ( 4 | "context" 5 | "testing" 6 | 7 | "encoding/json" 8 | "github.com/magiconair/properties/assert" 9 | "github.com/pkg/errors" 10 | "github.com/utilitywarehouse/go-pubsub" 11 | "github.com/utilitywarehouse/go-pubsub/mockqueue" 12 | ) 13 | 14 | func TestDeadLetter(t *testing.T) { 15 | in := mockqueue.NewMockQueue() 16 | dl := mockqueue.NewMockQueue() 17 | 18 | msg := []byte("test") 19 | errMsg := "error message" 20 | consumerTopic := "consumerTopic" 21 | consumer := "consumer" 22 | 23 | err := in.PutMessage(pubsub.SimpleProducerMessage(msg)) 24 | if err != nil { 25 | t.Fatalf("error publishing message: [%+v]", err) 26 | } 27 | 28 | inCtx, inCancel := context.WithCancel(context.Background()) 29 | handler := func(m pubsub.ConsumerMessage) error { 30 | inCancel() 31 | return errors.New(errMsg) 32 | } 33 | dlErrHanlder := pubsub.ConsumerErrorHandler(New(dl, consumerTopic, consumer)) 34 | 35 | in.ConsumeMessages(inCtx, handler, dlErrHanlder) 36 | 37 | var dlMsg []byte 38 | dlCtx, dlCancel := context.WithCancel(context.Background()) 39 | dlHandler := func(m pubsub.ConsumerMessage) error { 40 | dlMsg = m.Data 41 | dlCancel() 42 | return nil 43 | } 44 | errHandler := func(m pubsub.ConsumerMessage, e error) error { 45 | t.Error("unexpected error") 46 | return nil 47 | } 48 | 49 | dl.ConsumeMessages(dlCtx, dlHandler, errHandler) 50 | 51 | fsm := FailedConsumerMessage{} 52 | err = json.Unmarshal(dlMsg, &fsm) 53 | if err != nil { 54 | t.Error(err) 55 | } 56 | 57 | assert.Equal(t, fsm.Message, msg) 58 | assert.Equal(t, fsm.Err, errMsg) 59 | assert.Equal(t, fsm.MessageTopic, consumerTopic) 60 | assert.Equal(t, fsm.Consumer, consumer) 61 | } 62 | -------------------------------------------------------------------------------- /instrumented/instrumented_concurrent_pubsub.go: -------------------------------------------------------------------------------- 1 | package instrumented 2 | 3 | import ( 4 | "context" 5 | 6 | "github.com/prometheus/client_golang/prometheus" 7 | pubsub "github.com/utilitywarehouse/go-pubsub" 8 | ) 9 | 10 | // ConcurrentMessageSource is an an Instrumented pubsub MessageSource 11 | // The counter vector will have the labels "status" and "topic" 12 | type ConcurrentMessageSource struct { 13 | impl pubsub.ConcurrentMessageSource 14 | counter *prometheus.CounterVec 15 | topic string 16 | } 17 | 18 | 19 | // NewDefaultConcurrentMessageSource returns a new pubsub MessageSource wrapped in default instrumentation 20 | func NewDefaultConcurrentMessageSource(source pubsub.ConcurrentMessageSource, topic string) pubsub.ConcurrentMessageSource { 21 | return NewConcurrentMessageSource( 22 | source, 23 | prometheus.CounterOpts{ 24 | Name: "messages_consumed_total", 25 | Help: "The total count of messages consumed", 26 | }, 27 | topic, 28 | ) 29 | } 30 | 31 | // NewConcurrentMessageSource returns a new MessageSource 32 | func NewConcurrentMessageSource( 33 | source pubsub.ConcurrentMessageSource, 34 | counterOpts prometheus.CounterOpts, 35 | topic string) pubsub.ConcurrentMessageSource { 36 | counter := prometheus.NewCounterVec(counterOpts, []string{"status", "topic"}) 37 | if err := prometheus.Register(counter); err != nil { 38 | if are, ok := err.(prometheus.AlreadyRegisteredError); ok { 39 | counter = are.ExistingCollector.(*prometheus.CounterVec) 40 | } else { 41 | panic(err) 42 | } 43 | } 44 | counter.WithLabelValues("error", topic).Add(0) 45 | counter.WithLabelValues("success", topic).Add(0) 46 | 47 | return &ConcurrentMessageSource{source, counter, topic} 48 | } 49 | 50 | 51 | // ConsumeMessages is an implementation of interface method, wrapping the call in instrumentation 52 | func (ims *ConcurrentMessageSource) ConsumeMessages( 53 | ctx context.Context, handler pubsub.ConsumerMessageHandler, onError pubsub.ConsumerErrorHandler) error { 54 | instrumentedHandler := newMsgHandler(handler, ims.counter, ims.topic) 55 | return ims.impl.ConsumeMessages(ctx, instrumentedHandler, onError) 56 | } 57 | 58 | // ConsumeMessagesConcurrently is an implementation of interface method, wrapping the call in instrumentation 59 | func (ims *ConcurrentMessageSource) ConsumeMessagesConcurrently(ctx context.Context, handler pubsub.ConsumerMessageHandler, onError pubsub.ConsumerErrorHandler) error { 60 | instrumentedHandler := newMsgHandler(handler, ims.counter, ims.topic) 61 | return ims.impl.ConsumeMessagesConcurrently(ctx, instrumentedHandler, onError) 62 | } 63 | 64 | 65 | // Status returns the status of this source, or an error if the status could not be determined. 66 | func (ims *ConcurrentMessageSource) Status() (*pubsub.Status, error) { 67 | return ims.impl.Status() 68 | } 69 | -------------------------------------------------------------------------------- /instrumented/instrumented_pubsub.go: -------------------------------------------------------------------------------- 1 | package instrumented 2 | 3 | import ( 4 | "context" 5 | 6 | "github.com/prometheus/client_golang/prometheus" 7 | pubsub "github.com/utilitywarehouse/go-pubsub" 8 | ) 9 | 10 | // MessageSource is an an Instrumented pubsub MessageSource 11 | // The counter vector will have the labels "status" and "topic" 12 | type MessageSource struct { 13 | impl pubsub.MessageSource 14 | counter *prometheus.CounterVec 15 | topic string 16 | } 17 | 18 | // NewDefaultMessageSource returns a new pubsub MessageSource wrapped in default instrumentation 19 | func NewDefaultMessageSource(source pubsub.MessageSource, topic string) pubsub.MessageSource { 20 | return NewMessageSource( 21 | source, 22 | prometheus.CounterOpts{ 23 | Name: "messages_consumed_total", 24 | Help: "The total count of messages consumed", 25 | }, 26 | topic, 27 | ) 28 | } 29 | 30 | // NewMessageSource returns a new MessageSource 31 | func NewMessageSource( 32 | source pubsub.MessageSource, 33 | counterOpts prometheus.CounterOpts, 34 | topic string) pubsub.MessageSource { 35 | counter := prometheus.NewCounterVec(counterOpts, []string{"status", "topic"}) 36 | if err := prometheus.Register(counter); err != nil { 37 | if are, ok := err.(prometheus.AlreadyRegisteredError); ok { 38 | counter = are.ExistingCollector.(*prometheus.CounterVec) 39 | } else { 40 | panic(err) 41 | } 42 | } 43 | counter.WithLabelValues("error", topic).Add(0) 44 | counter.WithLabelValues("success", topic).Add(0) 45 | 46 | return &MessageSource{source, counter, topic} 47 | } 48 | 49 | func newMsgHandler( 50 | handler func(msg pubsub.ConsumerMessage) error, 51 | vec *prometheus.CounterVec, topic string) func(msg pubsub.ConsumerMessage) error { 52 | 53 | return func(msg pubsub.ConsumerMessage) error { 54 | if err := handler(msg); err != nil { 55 | vec.WithLabelValues("error", topic).Inc() 56 | return err 57 | } 58 | vec.WithLabelValues("success", topic).Inc() 59 | return nil 60 | } 61 | } 62 | 63 | // ConsumeMessages is an implementation of interface method, wrapping the call in instrumentation 64 | func (ims *MessageSource) ConsumeMessages( 65 | ctx context.Context, handler pubsub.ConsumerMessageHandler, onError pubsub.ConsumerErrorHandler) error { 66 | instrumentedHandler := newMsgHandler(handler, ims.counter, ims.topic) 67 | return ims.impl.ConsumeMessages(ctx, instrumentedHandler, onError) 68 | } 69 | 70 | // Status returns the status of this source, or an error if the status could not be determined. 71 | func (ims *MessageSource) Status() (*pubsub.Status, error) { 72 | return ims.impl.Status() 73 | } 74 | 75 | // MessageSink is an instrumented implementation of the pubsub MessageSink 76 | type MessageSink struct { 77 | impl pubsub.MessageSink 78 | produce func(pubsub.ProducerMessage) error 79 | } 80 | 81 | // PutMessage implements pubsub MessageSink interface method wrapped in instrumentation 82 | func (ims *MessageSink) PutMessage(m pubsub.ProducerMessage) error { 83 | return ims.produce(m) 84 | } 85 | 86 | // Close closes the message sink 87 | func (ims *MessageSink) Close() error { 88 | return ims.impl.Close() 89 | } 90 | 91 | // Status returns the status of this sink, or an error if the status could not be determined. 92 | func (ims *MessageSink) Status() (*pubsub.Status, error) { 93 | return ims.impl.Status() 94 | } 95 | 96 | // NewDefaultMessageSink returns a new pubsub MessageSink wrapped in default instrumentation 97 | func NewDefaultMessageSink(sink pubsub.MessageSink, topic string) pubsub.MessageSink { 98 | return NewMessageSink( 99 | sink, 100 | prometheus.CounterOpts{ 101 | Name: "messages_produced_total", 102 | Help: "The total count of messages produced", 103 | }, 104 | topic, 105 | ) 106 | } 107 | 108 | // NewMessageSink constructs a new pubsub MessageSink wrapped in instrumentation 109 | // The counter vector will have the labels status and topic 110 | func NewMessageSink(sink pubsub.MessageSink, counterOpts prometheus.CounterOpts, topic string) pubsub.MessageSink { 111 | sinkCounter := prometheus.NewCounterVec(counterOpts, []string{"status", "topic"}) 112 | if err := prometheus.Register(sinkCounter); err != nil { 113 | if are, ok := err.(prometheus.AlreadyRegisteredError); ok { 114 | sinkCounter = are.ExistingCollector.(*prometheus.CounterVec) 115 | } else { 116 | panic(err) 117 | } 118 | } 119 | produceMessage := func(m pubsub.ProducerMessage) error { 120 | err := sink.PutMessage(m) 121 | if err != nil { 122 | sinkCounter.WithLabelValues("error", topic).Add(1) 123 | } else { 124 | sinkCounter.WithLabelValues("success", topic).Add(1) 125 | } 126 | return err 127 | } 128 | return &MessageSink{ 129 | impl: sink, 130 | produce: produceMessage, 131 | } 132 | } 133 | -------------------------------------------------------------------------------- /instrumented/instrumented_pubsub_test.go: -------------------------------------------------------------------------------- 1 | package instrumented_test 2 | 3 | import ( 4 | "context" 5 | "testing" 6 | "time" 7 | 8 | "github.com/prometheus/client_golang/prometheus" 9 | pubsub "github.com/utilitywarehouse/go-pubsub" 10 | "github.com/utilitywarehouse/go-pubsub/instrumented" 11 | "github.com/utilitywarehouse/go-pubsub/mockqueue" 12 | ) 13 | 14 | func TestInstrumentation(t *testing.T) { 15 | q := mockqueue.NewMockQueue() 16 | 17 | var source = q 18 | var sink = q 19 | 20 | instrumentedSink := instrumented.NewMessageSink(sink, prometheus.CounterOpts{ 21 | Help: "help_sink", 22 | Name: "test_sink", 23 | }, "test_topic") 24 | 25 | go func() { 26 | 27 | for i := 0; i < 10; i++ { 28 | err := instrumentedSink.PutMessage(pubsub.SimpleProducerMessage([]byte("test"))) 29 | if err != nil { 30 | t.Fatalf("error publishing message: [%+v]", err) 31 | } 32 | } 33 | }() 34 | instrumentedSource := instrumented.NewMessageSource(source, prometheus.CounterOpts{ 35 | Help: "help_source", 36 | Name: "test_source", 37 | }, "test_topic") 38 | consumed := make(chan pubsub.ConsumerMessage) 39 | count := 0 40 | ctx, cancel := context.WithTimeout(context.Background(), 3*time.Second) 41 | handler := func(m pubsub.ConsumerMessage) error { 42 | consumed <- m 43 | count++ 44 | if count == 9 { 45 | cancel() 46 | } 47 | return nil 48 | } 49 | errH := func(m pubsub.ConsumerMessage, e error) error { 50 | panic(e) 51 | } 52 | go func() { 53 | err := instrumentedSource.ConsumeMessages(ctx, handler, errH) 54 | if err != nil { 55 | panic(err) 56 | } 57 | close(consumed) 58 | }() 59 | for { 60 | _, ok := <-consumed 61 | if !ok { 62 | break 63 | } 64 | } 65 | } 66 | 67 | func TestInstrumentationSameCollector(t *testing.T) { 68 | q := mockqueue.NewMockQueue() 69 | var sink = q 70 | var source = q 71 | 72 | instrumented.NewMessageSink(sink, prometheus.CounterOpts{ 73 | Help: "help_sink", 74 | Name: "test_sink", 75 | }, "test_topic") 76 | instrumented.NewMessageSink(sink, prometheus.CounterOpts{ 77 | Help: "help_sink", 78 | Name: "test_sink", 79 | }, "test_topic1") 80 | 81 | instrumented.NewMessageSource(source, prometheus.CounterOpts{ 82 | Help: "help_source", 83 | Name: "test_source", 84 | }, "test_topic") 85 | instrumented.NewMessageSource(source, prometheus.CounterOpts{ 86 | Help: "help_source", 87 | Name: "test_source", 88 | }, "test_topic1") 89 | } 90 | -------------------------------------------------------------------------------- /kafka/example/example.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "context" 5 | "encoding/json" 6 | "fmt" 7 | "log" 8 | "time" 9 | 10 | "github.com/utilitywarehouse/go-pubsub" 11 | "github.com/utilitywarehouse/go-pubsub/kafka" 12 | ) 13 | 14 | func main() { 15 | 16 | produce() 17 | 18 | cons := kafka.NewMessageSource(kafka.MessageSourceConfig{ 19 | ConsumerGroup: "demo-group", 20 | Topic: "demo-topic", 21 | Brokers: []string{"localhost:9092"}, 22 | Offset: kafka.OffsetOldest, 23 | }) 24 | 25 | // consume messages for 2 seconds 26 | ctx, cancel := context.WithTimeout(context.Background(), 2*time.Second) 27 | defer cancel() 28 | 29 | handler := func(m pubsub.ConsumerMessage) error { 30 | fmt.Printf("message is: %s\n", m.Data) 31 | return nil 32 | } 33 | 34 | onError := func(m pubsub.ConsumerMessage, e error) error { 35 | panic("unexpected error") 36 | } 37 | 38 | if err := cons.ConsumeMessages(ctx, handler, onError); err != nil { 39 | log.Fatal(err) 40 | } 41 | 42 | } 43 | 44 | type MyMessage struct { 45 | CustomerID string 46 | Message string 47 | } 48 | 49 | func (m MyMessage) Marshal() ([]byte, error) { 50 | return json.Marshal(m) 51 | } 52 | 53 | func produce() { 54 | 55 | sink, err := kafka.NewMessageSink( 56 | kafka.MessageSinkConfig{ 57 | Topic: "demo-topic", 58 | Brokers: []string{"localhost:9092"}, 59 | KeyFunc: func(m pubsub.ProducerMessage) []byte { 60 | return []byte(m.(MyMessage).CustomerID) 61 | }, 62 | }) 63 | if err != nil { 64 | log.Fatal(err) 65 | } 66 | 67 | sink.PutMessage(MyMessage{ 68 | CustomerID: "customer-01", 69 | Message: fmt.Sprintf("hello. it is currently %v", time.Now()), 70 | }) 71 | 72 | sink.Close() 73 | } 74 | -------------------------------------------------------------------------------- /kafka/kafka.go: -------------------------------------------------------------------------------- 1 | package kafka 2 | 3 | import metrics "github.com/rcrowley/go-metrics" 4 | 5 | func init() { 6 | metrics.UseNilMetrics = true 7 | } 8 | -------------------------------------------------------------------------------- /kafka/kafka_test.go: -------------------------------------------------------------------------------- 1 | package kafka 2 | 3 | import ( 4 | "context" 5 | "errors" 6 | "flag" 7 | "os" 8 | "testing" 9 | "time" 10 | 11 | "github.com/pborman/uuid" 12 | "github.com/utilitywarehouse/go-pubsub" 13 | ) 14 | 15 | var ( 16 | broker = flag.String("broker", "", "kafka broker address") 17 | ) 18 | 19 | func TestMain(m *testing.M) { 20 | flag.Parse() 21 | result := m.Run() 22 | os.Exit(result) 23 | } 24 | 25 | func TestNewMessageSource(t *testing.T) { 26 | cons := NewMessageSource(MessageSourceConfig{ConsumerGroup: "test-group", Topic: "topic", Brokers: []string{"localhost:9092"}, Offset: OffsetOldest, MetadataRefreshFrequency: 1 * time.Second}).(*messageSource) 27 | if cons.consumergroup != "test-group" { 28 | t.Error("unexpected consumer group") 29 | } 30 | if cons.topic != "topic" { 31 | t.Error("unexpected topic") 32 | } 33 | 34 | if cons.brokers[0] != "localhost:9092" { 35 | t.Error("unexpected brokers") 36 | } 37 | if cons.offset != OffsetOldest { 38 | t.Error("unexpected offset") 39 | } 40 | if cons.metadataRefreshFrequency != 1*time.Second { 41 | t.Error("unexpected metadata refresh frequency") 42 | } 43 | 44 | } 45 | 46 | func TestNewMessageSourceOffsetsDefaultToLatest(t *testing.T) { 47 | cons := NewMessageSource(MessageSourceConfig{ConsumerGroup: "test-group", Topic: "topic", Brokers: []string{"localhost:9092"}}).(*messageSource) 48 | if cons.consumergroup != "test-group" { 49 | t.Error("unexpected consumer group") 50 | } 51 | if cons.topic != "topic" { 52 | t.Error("unexpected topic") 53 | } 54 | if cons.brokers[0] != "localhost:9092" { 55 | t.Error("unexpected brokers") 56 | } 57 | if cons.offset != OffsetLatest { 58 | t.Error("unexpected offset") 59 | } 60 | 61 | } 62 | 63 | func TestNewMessageSourceMetadataRefreshFrequencyDefault(t *testing.T) { 64 | cons := NewMessageSource(MessageSourceConfig{ConsumerGroup: "test-group", Topic: "topic", Brokers: []string{"localhost:9092"}}).(*messageSource) 65 | if cons.consumergroup != "test-group" { 66 | t.Error("unexpected consumer group") 67 | } 68 | if cons.topic != "topic" { 69 | t.Error("unexpected topic") 70 | } 71 | if cons.brokers[0] != "localhost:9092" { 72 | t.Error("unexpected brokers") 73 | } 74 | if cons.offset != OffsetLatest { 75 | t.Error("unexpected offset") 76 | } 77 | if cons.metadataRefreshFrequency != defaultMetadataRefreshFrequency { 78 | t.Error("unexpected metadata refresh frequency") 79 | } 80 | 81 | } 82 | 83 | func TestSimpleProduceConsume(t *testing.T) { 84 | if *broker == "" { 85 | t.Skip("kafka not configured") 86 | } 87 | 88 | topicName := uuid.NewRandom().String() 89 | message := uuid.NewRandom().String() 90 | 91 | produceMessage(t, topicName, message) 92 | 93 | msg := make(chan string, 1) 94 | 95 | cons := NewMessageSource(MessageSourceConfig{ConsumerGroup: "test-group", Topic: topicName, Brokers: []string{*broker}, Offset: OffsetOldest}) 96 | 97 | handler := func(m pubsub.ConsumerMessage) error { 98 | msg <- string(m.Data) 99 | close(msg) 100 | return nil 101 | } 102 | 103 | onError := func(m pubsub.ConsumerMessage, e error) error { 104 | t.Error("unexpected error") 105 | return nil 106 | } 107 | 108 | ctx, cancel := context.WithTimeout(context.Background(), 2*time.Second) 109 | defer cancel() 110 | 111 | if err := cons.ConsumeMessages(ctx, handler, onError); err != nil { 112 | t.Error(err) 113 | } 114 | 115 | select { 116 | case m := <-msg: 117 | if m != message { 118 | t.Error("message not as expected") 119 | } 120 | default: 121 | t.Error("didn't get message") 122 | } 123 | } 124 | 125 | func TestConsumeError(t *testing.T) { 126 | if *broker == "" { 127 | t.Skip("kafka not configured") 128 | } 129 | 130 | topicName := uuid.NewRandom().String() 131 | message := uuid.NewRandom().String() 132 | 133 | produceMessage(t, topicName, message) 134 | 135 | { 136 | cons := NewMessageSource(MessageSourceConfig{ConsumerGroup: "test-group", Topic: topicName, Brokers: []string{*broker}, Offset: OffsetOldest}) 137 | 138 | doneMsg := make(chan struct{}) 139 | 140 | handler := func(m pubsub.ConsumerMessage) error { 141 | return errors.New("test error") 142 | } 143 | 144 | onError := func(m pubsub.ConsumerMessage, e error) error { 145 | close(doneMsg) 146 | return errors.New("onError error") 147 | } 148 | 149 | ctx, cancel := context.WithTimeout(context.Background(), 2*time.Second) 150 | defer cancel() 151 | 152 | consumeErr := cons.ConsumeMessages(ctx, handler, onError) 153 | if consumeErr == nil { 154 | t.Error("expected error") 155 | } 156 | } 157 | 158 | { 159 | // message should be still available to consume 160 | cons2 := NewMessageSource(MessageSourceConfig{ConsumerGroup: "test-group", Topic: topicName, Brokers: []string{*broker}, Offset: OffsetOldest}) 161 | 162 | msg := make(chan string, 1) 163 | 164 | handler := func(m pubsub.ConsumerMessage) error { 165 | msg <- string(m.Data) 166 | close(msg) 167 | return nil 168 | } 169 | 170 | onError := func(m pubsub.ConsumerMessage, e error) error { 171 | t.Error("unexpected error") 172 | return nil 173 | } 174 | 175 | ctx, cancel := context.WithTimeout(context.Background(), 2*time.Second) 176 | defer cancel() 177 | 178 | if err := cons2.ConsumeMessages(ctx, handler, onError); err != nil { 179 | t.Error(err) 180 | } 181 | 182 | select { 183 | case m := <-msg: 184 | if m != message { 185 | t.Error("message not as expected") 186 | } 187 | case <-time.After(2 * time.Second): 188 | t.Error("timeout waiting for message") 189 | } 190 | } 191 | } 192 | 193 | func produceMessage(t *testing.T, topicName, message string) { 194 | sink, err := NewMessageSink(MessageSinkConfig{Topic: topicName, Brokers: []string{*broker}}) 195 | if err != nil { 196 | t.Fatal(err) 197 | } 198 | 199 | sink.PutMessage(pubsub.SimpleProducerMessage([]byte(message))) 200 | 201 | if err := sink.Close(); err != nil { 202 | t.Error(err) 203 | } 204 | } 205 | -------------------------------------------------------------------------------- /kafka/kafkasink.go: -------------------------------------------------------------------------------- 1 | package kafka 2 | 3 | import ( 4 | "errors" 5 | "sync" 6 | 7 | "time" 8 | 9 | "github.com/Shopify/sarama" 10 | "github.com/utilitywarehouse/go-pubsub" 11 | ) 12 | 13 | var _ pubsub.MessageSink = (*messageSink)(nil) 14 | 15 | type messageSink struct { 16 | topic string 17 | 18 | keyFunc func(pubsub.ProducerMessage) []byte 19 | 20 | lk sync.Mutex 21 | producer sarama.SyncProducer 22 | closed bool 23 | 24 | brokers []string 25 | } 26 | 27 | type MessageSinkConfig struct { 28 | Topic string 29 | Brokers []string 30 | KeyFunc func(pubsub.ProducerMessage) []byte 31 | MaxMessageBytes int 32 | Version *sarama.KafkaVersion 33 | } 34 | 35 | func NewMessageSink(config MessageSinkConfig) (pubsub.MessageSink, error) { 36 | conf := sarama.NewConfig() 37 | conf.Producer.RequiredAcks = sarama.WaitForAll 38 | conf.Producer.Return.Successes = true 39 | conf.Producer.Return.Errors = true 40 | conf.Producer.Retry.Max = 3 41 | conf.Producer.Timeout = time.Duration(60) * time.Second 42 | 43 | if config.MaxMessageBytes != 0 { 44 | if config.MaxMessageBytes > int(sarama.MaxRequestSize) { 45 | sarama.MaxRequestSize = int32(config.MaxMessageBytes) 46 | } 47 | conf.Producer.MaxMessageBytes = config.MaxMessageBytes 48 | } 49 | 50 | if config.KeyFunc != nil { 51 | conf.Producer.Partitioner = sarama.NewHashPartitioner 52 | } else { 53 | conf.Producer.Partitioner = sarama.NewRoundRobinPartitioner 54 | } 55 | 56 | if config.Version != nil { 57 | conf.Version = *config.Version 58 | } 59 | 60 | producer, err := sarama.NewSyncProducer(config.Brokers, conf) 61 | if err != nil { 62 | return nil, err 63 | } 64 | 65 | return &messageSink{ 66 | topic: config.Topic, 67 | producer: producer, 68 | keyFunc: config.KeyFunc, 69 | brokers: config.Brokers, 70 | }, nil 71 | } 72 | 73 | func (mq *messageSink) PutMessage(m pubsub.ProducerMessage) error { 74 | message := &sarama.ProducerMessage{ 75 | Topic: mq.topic, 76 | } 77 | 78 | data, err := m.Marshal() 79 | if err != nil { 80 | return err 81 | } 82 | message.Value = sarama.ByteEncoder(data) 83 | if mq.keyFunc != nil { 84 | message.Key = sarama.ByteEncoder(mq.keyFunc(m)) 85 | } 86 | 87 | _, _, err = mq.producer.SendMessage(message) 88 | return err 89 | } 90 | 91 | func (mq *messageSink) Close() error { 92 | mq.lk.Lock() 93 | defer mq.lk.Unlock() 94 | 95 | if mq.closed { 96 | return errors.New("Already closed") 97 | } 98 | 99 | mq.closed = true 100 | return mq.producer.Close() 101 | } 102 | 103 | // Status reports the status of the message sink 104 | func (mq *messageSink) Status() (*pubsub.Status, error) { 105 | return status(mq.brokers, mq.topic) 106 | } 107 | -------------------------------------------------------------------------------- /kafka/kafkasource.go: -------------------------------------------------------------------------------- 1 | package kafka 2 | 3 | import ( 4 | "context" 5 | "time" 6 | 7 | "github.com/Shopify/sarama" 8 | "github.com/utilitywarehouse/sarama-cluster" 9 | "github.com/utilitywarehouse/go-pubsub" 10 | "golang.org/x/sync/errgroup" 11 | ) 12 | 13 | var _ pubsub.MessageSource = (*messageSource)(nil) 14 | 15 | const ( 16 | OffsetOldest int64 = -2 17 | OffsetLatest int64 = -1 18 | defaultMetadataRefreshFrequency = 10 * time.Minute 19 | ) 20 | 21 | type messageSource struct { 22 | consumergroup string 23 | topic string 24 | brokers []string 25 | offset int64 26 | metadataRefreshFrequency time.Duration 27 | offsetsRetention time.Duration 28 | Version *sarama.KafkaVersion 29 | } 30 | 31 | type MessageSourceConfig struct { 32 | ConsumerGroup string 33 | Topic string 34 | Brokers []string 35 | Offset int64 36 | MetadataRefreshFrequency time.Duration 37 | OffsetsRetention time.Duration 38 | Version *sarama.KafkaVersion 39 | } 40 | 41 | // NewMessageSource provides a new kafka message source 42 | func NewMessageSource(config MessageSourceConfig) pubsub.ConcurrentMessageSource { 43 | offset := OffsetLatest 44 | if config.Offset != 0 { 45 | offset = config.Offset 46 | } 47 | 48 | mrf := defaultMetadataRefreshFrequency 49 | if config.MetadataRefreshFrequency > 0 { 50 | mrf = config.MetadataRefreshFrequency 51 | } 52 | 53 | return &messageSource{ 54 | consumergroup: config.ConsumerGroup, 55 | topic: config.Topic, 56 | brokers: config.Brokers, 57 | offset: offset, 58 | offsetsRetention: config.OffsetsRetention, 59 | metadataRefreshFrequency: mrf, 60 | Version: config.Version, 61 | } 62 | } 63 | 64 | func (mq *messageSource) ConsumeMessages(ctx context.Context, handler pubsub.ConsumerMessageHandler, onError pubsub.ConsumerErrorHandler) error { 65 | config := cluster.NewConfig() 66 | config.Consumer.Return.Errors = true 67 | config.Consumer.Offsets.Initial = mq.offset 68 | config.Metadata.RefreshFrequency = mq.metadataRefreshFrequency 69 | config.Consumer.Offsets.Retention = mq.offsetsRetention 70 | 71 | if mq.Version != nil { 72 | config.Version = *mq.Version 73 | } 74 | 75 | c, err := cluster.NewConsumer(mq.brokers, mq.consumergroup, []string{mq.topic}, config) 76 | if err != nil { 77 | return err 78 | } 79 | 80 | defer func() { 81 | _ = c.Close() 82 | }() 83 | 84 | for { 85 | select { 86 | case msg := <-c.Messages(): 87 | message := pubsub.ConsumerMessage{Data: msg.Value} 88 | err := handler(message) 89 | if err != nil { 90 | err = onError(message, err) 91 | if err != nil { 92 | return err 93 | } 94 | } 95 | 96 | c.MarkOffset(msg, "") 97 | case err := <-c.Errors(): 98 | return err 99 | case <-ctx.Done(): 100 | return nil 101 | } 102 | } 103 | } 104 | 105 | // ConsumeMessagesConcurrently consumes messages concurrently through the use of separate go-routines 106 | // in the context of Kafka this is done by providing a new routine by partition made available to 107 | // the application by kafka at runtime 108 | func (mq *messageSource) ConsumeMessagesConcurrently(ctx context.Context, handler pubsub.ConsumerMessageHandler, onError pubsub.ConsumerErrorHandler) error { 109 | config := cluster.NewConfig() 110 | config.Consumer.Return.Errors = true 111 | config.Consumer.Offsets.Initial = mq.offset 112 | config.Metadata.RefreshFrequency = mq.metadataRefreshFrequency 113 | config.Consumer.Offsets.Retention = mq.offsetsRetention 114 | config.Group.Mode = cluster.ConsumerModePartitions 115 | 116 | if mq.Version != nil { 117 | config.Version = *mq.Version 118 | } 119 | 120 | c, err := cluster.NewConsumer(mq.brokers, mq.consumergroup, []string{mq.topic}, config) 121 | if err != nil { 122 | return err 123 | } 124 | 125 | defer func() { 126 | _ = c.Close() 127 | }() 128 | 129 | pGroup, pContext := errgroup.WithContext(ctx) 130 | 131 | for { 132 | select { 133 | case part, ok := <-c.Partitions(): 134 | // partitions will emit a nil pointer (part) when the parent channel is tombed as 135 | // the client is closed. ok will be false when the partitions channel is closed 136 | // in both cases we want to wait for the errgroup to handle any errors correctly and 137 | // gracefully close the subroutines. If either the part is nil or ok is false then 138 | // we simply ignore it to give the errgroup and subroutines time to finish 139 | if ok && part != nil { 140 | pGroup.Go(newConcurrentMessageHandler(pContext, c, part, handler, onError)) 141 | } 142 | case err := <-c.Errors(): 143 | 144 | if err == nil { 145 | // our parent chanel was possibly closed due to context cancel, in which case 146 | // we have stopped consuming but should wait for any errors returned from 147 | // subroutines 148 | return pGroup.Wait() 149 | } 150 | // tell the errgroup to cancel all running subroutines, were ignoring any other errors 151 | // // at this point 152 | pGroup.Wait() 153 | // return the original consumer error 154 | return err 155 | case <-ctx.Done(): 156 | // main context was cancelled, our errgroup will also be cancelled so we should 157 | // gracefully wait until subroutines finish just in case one returns an error 158 | return pGroup.Wait() 159 | case <-pContext.Done(): 160 | // gracefully wait for any error 161 | return pGroup.Wait() 162 | } 163 | } 164 | } 165 | 166 | func newConcurrentMessageHandler( 167 | ctx context.Context, 168 | consumer *cluster.Consumer, 169 | part cluster.PartitionConsumer, 170 | handler pubsub.ConsumerMessageHandler, 171 | onError pubsub.ConsumerErrorHandler) func() error { 172 | 173 | return func() error { 174 | for { 175 | select { 176 | case msg, ok := <-part.Messages(): 177 | if !ok { 178 | // message channel closed so bail cleanly 179 | return nil 180 | } 181 | message := pubsub.ConsumerMessage{Data: msg.Value} 182 | err := handler(message) 183 | if err != nil { 184 | err = onError(message, err) 185 | if err != nil { 186 | // this error will trigger the errgroup to cancel its context, this will trigger 187 | // a graceful shutdown of the consumer bubbling the error back up to the 188 | // main partition loop 189 | return err 190 | } 191 | } 192 | consumer.MarkOffset(msg, "") 193 | case err, ok := <-part.Errors(): 194 | if !ok { 195 | // error chanel closed so bail cleanly. If we don't do this we will kill the 196 | // consumer every time there is a consumer rebalance 197 | return nil 198 | } 199 | return err 200 | case <-ctx.Done(): 201 | // another routine has encountered an error, we should really stop 202 | // processing any other partitions 203 | return nil 204 | } 205 | } 206 | } 207 | } 208 | 209 | // Status reports the status of the message source 210 | func (mq *messageSource) Status() (*pubsub.Status, error) { 211 | return status(mq.brokers, mq.topic) 212 | } 213 | -------------------------------------------------------------------------------- /kafka/status.go: -------------------------------------------------------------------------------- 1 | package kafka 2 | 3 | import ( 4 | "github.com/Shopify/sarama" 5 | "github.com/utilitywarehouse/go-pubsub" 6 | ) 7 | 8 | func status(brokerAddrs []string, topic string) (*pubsub.Status, error) { 9 | status := &pubsub.Status{} 10 | 11 | client, err := sarama.NewClient(brokerAddrs, sarama.NewConfig()) 12 | if err != nil { 13 | return nil, err 14 | } 15 | defer client.Close() 16 | 17 | writablePartitions, err := client.WritablePartitions(topic) 18 | if err != nil { 19 | status.Working = false 20 | status.Problems = append(status.Problems, err.Error()) 21 | return status, nil 22 | } 23 | if len(writablePartitions) == 0 { 24 | status.Working = false 25 | status.Problems = append(status.Problems, "no writable partitions") 26 | return status, nil 27 | } 28 | 29 | status.Working = true 30 | return status, nil 31 | } 32 | -------------------------------------------------------------------------------- /mockqueue/example/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "context" 5 | "fmt" 6 | "log" 7 | "time" 8 | 9 | "github.com/utilitywarehouse/go-pubsub" 10 | "github.com/utilitywarehouse/go-pubsub/mockqueue" 11 | ) 12 | 13 | func main() { 14 | 15 | q := mockqueue.NewMockQueue() 16 | 17 | // MockQueue implements both source and sink 18 | var cons pubsub.MessageSource = q 19 | var sink pubsub.MessageSink = q 20 | 21 | go func() { 22 | tick := time.NewTicker(500 * time.Millisecond) 23 | for { 24 | <-tick.C 25 | sink.PutMessage(pubsub.SimpleProducerMessage([]byte("hello"))) 26 | } 27 | }() 28 | 29 | onError := func(m pubsub.ConsumerMessage, err error) error { 30 | panic("unexpected error") 31 | } 32 | handler := func(m pubsub.ConsumerMessage) error { 33 | fmt.Println(string(m.Data)) 34 | return nil 35 | } 36 | 37 | ctx, _ := context.WithTimeout(context.Background(), 750*time.Millisecond) 38 | 39 | if err := cons.ConsumeMessages(ctx, handler, onError); err != nil { 40 | log.Fatal(err) 41 | } 42 | 43 | } 44 | -------------------------------------------------------------------------------- /mockqueue/mockqueue.go: -------------------------------------------------------------------------------- 1 | package mockqueue 2 | 3 | import ( 4 | "context" 5 | "errors" 6 | 7 | "github.com/utilitywarehouse/go-pubsub" 8 | ) 9 | 10 | var _ pubsub.MessageSink = (*MockQueue)(nil) 11 | var _ pubsub.MessageSource = (*MockQueue)(nil) 12 | 13 | // MockQueue is intended for testing purposes. 14 | // It provides a simple in-memory sink and source 15 | type MockQueue struct { 16 | q chan []byte 17 | close chan struct{} 18 | closed chan struct{} 19 | } 20 | 21 | func NewMockQueue() *MockQueue { 22 | return &MockQueue{make(chan []byte, 9999), make(chan struct{}), make(chan struct{})} 23 | } 24 | 25 | func (mq *MockQueue) PutMessage(m pubsub.ProducerMessage) error { 26 | data, err := m.Marshal() 27 | if err != nil { 28 | return err 29 | } 30 | mq.q <- data 31 | return nil 32 | } 33 | 34 | func (mq *MockQueue) ConsumeMessages(ctx context.Context, handler pubsub.ConsumerMessageHandler, onError pubsub.ConsumerErrorHandler) error { 35 | for { 36 | select { 37 | case <-ctx.Done(): 38 | return nil 39 | case m := <-mq.q: 40 | cm := pubsub.ConsumerMessage{m} 41 | err := handler(cm) 42 | if err != nil { 43 | if err := onError(cm, err); err != nil { 44 | return err 45 | } 46 | } 47 | } 48 | } 49 | } 50 | 51 | func (mq *MockQueue) Close() error { 52 | // no-op in mock. 53 | return nil 54 | } 55 | 56 | // Status reports the status of the queue 57 | func (mq *MockQueue) Status() (*pubsub.Status, error) { 58 | return nil, errors.New("status is not implemented") 59 | } 60 | -------------------------------------------------------------------------------- /natss/example/example.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "context" 5 | "fmt" 6 | "log" 7 | "time" 8 | 9 | "github.com/utilitywarehouse/go-pubsub" 10 | nats "github.com/utilitywarehouse/go-pubsub/natss" 11 | ) 12 | 13 | func main() { 14 | 15 | produce() 16 | 17 | cons, err := nats.NewMessageSource(nats.MessageSourceConfig{ 18 | NatsURL: "nats://localhost:4222", 19 | ClusterID: "cluster-id", 20 | ConsumerID: "consumer-02", 21 | Topic: "demo-topic", 22 | }) 23 | if err != nil { 24 | panic(err) 25 | log.Fatal(err) 26 | } 27 | 28 | ctx, _ := context.WithTimeout(context.Background(), 2*time.Second) 29 | 30 | handler := func(m pubsub.ConsumerMessage) error { 31 | fmt.Printf("message is: %s\n", m.Data) 32 | return nil 33 | } 34 | 35 | onError := func(m pubsub.ConsumerMessage, e error) error { 36 | panic("unexpected error") 37 | } 38 | 39 | if err := cons.ConsumeMessages(ctx, handler, onError); err != nil { 40 | log.Fatal(err) 41 | } 42 | } 43 | 44 | func produce() { 45 | sink, err := nats.NewMessageSink(nats.MessageSinkConfig{ 46 | ClusterID: "cluster-id", 47 | Topic: "demo-topic", 48 | ClientID: "client-01", 49 | NatsURL: "nats://localhost:4222", 50 | }) 51 | if err != nil { 52 | panic(err) 53 | log.Fatal(err) 54 | } 55 | 56 | if err := sink.PutMessage(pubsub.SimpleProducerMessage([]byte(fmt.Sprintf("hello. it is currently %v", time.Now())))); err != nil { 57 | log.Fatal(err) 58 | } 59 | 60 | if err := sink.Close(); err != nil { 61 | log.Fatal(err) 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /natss/natssink.go: -------------------------------------------------------------------------------- 1 | package natss 2 | 3 | import ( 4 | "sync" 5 | 6 | "github.com/nats-io/stan.go" 7 | "github.com/pkg/errors" 8 | "github.com/utilitywarehouse/go-pubsub" 9 | ) 10 | 11 | var _ pubsub.MessageSink = (*messageSink)(nil) 12 | 13 | type MessageSinkConfig struct { 14 | NatsURL string 15 | ClusterID string 16 | Topic string 17 | ClientID string 18 | } 19 | 20 | type connectionState struct { 21 | sync.RWMutex 22 | err error 23 | } 24 | 25 | // ErrConnLost is the error created when the underlying nats streaming client 26 | // has established that the connection to the nats streaming server has been lost 27 | var ErrConnLost = errors.New("nats streaming connection lost") 28 | 29 | type messageSink struct { 30 | topic string 31 | sc stan.Conn // nats streaming 32 | state connectionState 33 | } 34 | 35 | func NewMessageSink(config MessageSinkConfig) (pubsub.MessageSink, error) { 36 | sink := messageSink{topic: config.Topic} 37 | 38 | sc, err := stan.Connect(config.ClusterID, config.ClientID, stan.NatsURL(config.NatsURL), stan.SetConnectionLostHandler(func(_ stan.Conn, e error) { 39 | sink.state.Lock() 40 | sink.state.err = errors.Wrapf(ErrConnLost, "underlying error: %s", e.Error()) 41 | sink.state.Unlock() 42 | })) 43 | if err != nil { 44 | return nil, errors.Wrapf(err, "connecting nats streaming client to cluster: %s at: %s", config.ClusterID, config.NatsURL) 45 | } 46 | sink.sc = sc 47 | return &sink, nil 48 | } 49 | 50 | func (mq *messageSink) PutMessage(m pubsub.ProducerMessage) error { 51 | mq.state.RLock() 52 | if mq.state.err != nil { 53 | mq.state.RUnlock() 54 | return mq.state.err 55 | } 56 | mq.state.RUnlock() 57 | data, err := m.Marshal() 58 | if err != nil { 59 | return err 60 | } 61 | return mq.sc.Publish(mq.topic, data) 62 | } 63 | 64 | func (mq *messageSink) Close() error { 65 | return mq.sc.Close() 66 | } 67 | 68 | func (mq *messageSink) Status() (*pubsub.Status, error) { 69 | natsStatus, err := natsStatus(mq.sc.NatsConn()) 70 | if err != nil { 71 | return nil, err 72 | } 73 | mq.state.RLock() 74 | if mq.state.err != nil { 75 | natsStatus.Working = false 76 | natsStatus.Problems = append(natsStatus.Problems, mq.state.err.Error()) 77 | } 78 | mq.state.RUnlock() 79 | return natsStatus, nil 80 | } 81 | -------------------------------------------------------------------------------- /natss/source.go: -------------------------------------------------------------------------------- 1 | package natss 2 | 3 | import ( 4 | "context" 5 | "crypto/rand" 6 | "encoding/hex" 7 | "time" 8 | 9 | "github.com/nats-io/nats.go" 10 | "github.com/nats-io/stan.go" 11 | "github.com/nats-io/stan.go/pb" 12 | "github.com/pkg/errors" 13 | "github.com/utilitywarehouse/go-pubsub" 14 | ) 15 | 16 | func generateID() string { 17 | random := []byte{0, 0, 0, 0, 0, 0, 0, 0} 18 | _, err := rand.Read(random) 19 | if err != nil { 20 | panic(err) 21 | } 22 | return hex.EncodeToString(random) 23 | } 24 | 25 | var _ pubsub.MessageSource = (*messageSource)(nil) 26 | 27 | type MessageSourceConfig struct { 28 | NatsURL string 29 | ClusterID string 30 | Topic string 31 | ConsumerID string 32 | 33 | //Offset - offset used for consuming messages 34 | Offset Offset 35 | 36 | //OffsetStartAtIndex - start delivering messages from this index 37 | OffsetStartAtIndex uint64 38 | 39 | //OffsetStartDuration - start delivering messages from this duration ago 40 | OffsetStartDuration time.Duration 41 | 42 | NonDurable bool 43 | AckWait *time.Duration 44 | MaxInflight *int 45 | } 46 | 47 | //Offset - represents offset used for consuming msgs from the queue 48 | type Offset int 49 | 50 | type messageSource struct { 51 | natsURL string 52 | clusterID string 53 | consumerID string 54 | topic string 55 | offset Offset 56 | offsetStartAtIndex uint64 57 | offsetStartDuration time.Duration 58 | nonDurable bool 59 | ackWait *time.Duration 60 | maxInflight *int 61 | conn stan.Conn 62 | } 63 | 64 | const ( 65 | //OffsetStartAt - to start at a given msg number" 66 | OffsetStartAt = Offset(iota) 67 | 68 | //OffsetDeliverLast - deliver from the last message 69 | OffsetDeliverLast 70 | 71 | //OffsetDeliverAll - deliver all the messages form the beginning 72 | OffsetDeliverAll 73 | 74 | //OffsetStartDuration - deliver messages from a certain time 75 | OffsetStartDuration 76 | ) 77 | 78 | func NewMessageSource(conf MessageSourceConfig) (pubsub.MessageSource, error) { 79 | return &messageSource{ 80 | natsURL: conf.NatsURL, 81 | clusterID: conf.ClusterID, 82 | consumerID: conf.ConsumerID, 83 | topic: conf.Topic, 84 | offset: conf.Offset, 85 | offsetStartAtIndex: conf.OffsetStartAtIndex, 86 | offsetStartDuration: conf.OffsetStartDuration, 87 | nonDurable: conf.NonDurable, 88 | ackWait: conf.AckWait, 89 | maxInflight: conf.MaxInflight, 90 | }, nil 91 | } 92 | 93 | func (mq *messageSource) ConsumeMessages(ctx context.Context, handler pubsub.ConsumerMessageHandler, onError pubsub.ConsumerErrorHandler) error { 94 | 95 | ctx, cancel := context.WithCancel(ctx) 96 | 97 | anyError := make(chan error, 2) 98 | conn, err := stan.Connect(mq.clusterID, mq.consumerID+generateID(), stan.NatsURL(mq.natsURL), stan.SetConnectionLostHandler(func(_ stan.Conn, e error) { 99 | anyError <- errors.Wrap(e, "nats streaming connection lost") 100 | })) 101 | if err != nil { 102 | return err 103 | } 104 | mq.conn = conn 105 | defer conn.Close() 106 | 107 | natsConn := conn.NatsConn() 108 | closedHandler := func(natsConnection *nats.Conn) { 109 | select { 110 | 111 | case anyError <- errors.New("underlying nats connection to " + mq.natsURL + " failed"): 112 | default: 113 | } 114 | } 115 | 116 | natsConn.SetDisconnectHandler(closedHandler) 117 | 118 | active := make(chan struct{}, 1) 119 | 120 | f := func(msg *stan.Msg) { 121 | 122 | select { 123 | case <-ctx.Done(): 124 | return 125 | case active <- struct{}{}: 126 | } 127 | defer func() { <-active }() 128 | 129 | m := pubsub.ConsumerMessage{Data: msg.Data} 130 | err := handler(m) 131 | if err != nil { 132 | if err := onError(m, err); err != nil { 133 | select { 134 | case anyError <- err: 135 | default: 136 | } 137 | 138 | } else { 139 | msg.Ack() 140 | } 141 | } else { 142 | msg.Ack() 143 | } 144 | } 145 | 146 | // default to start from first index 147 | startOpt := stan.StartAt(pb.StartPosition_First) 148 | 149 | switch offset := mq.offset; offset { 150 | 151 | case OffsetStartAt: 152 | startOpt = stan.StartAtSequence(mq.offsetStartAtIndex) 153 | 154 | case OffsetDeliverLast: 155 | startOpt = stan.StartWithLastReceived() 156 | 157 | case OffsetDeliverAll: 158 | startOpt = stan.DeliverAllAvailable() 159 | 160 | case OffsetStartDuration: 161 | startOpt = stan.StartAtTimeDelta(mq.offsetStartDuration) 162 | } 163 | 164 | opts := []stan.SubscriptionOption{ 165 | startOpt, 166 | stan.SetManualAckMode(), 167 | } 168 | if !mq.nonDurable { 169 | opts = append(opts, stan.DurableName(mq.consumerID)) 170 | } 171 | if mq.ackWait != nil { 172 | opts = append(opts, stan.AckWait(*mq.ackWait)) 173 | } 174 | if mq.maxInflight != nil { 175 | opts = append(opts, stan.MaxInflight(*mq.maxInflight)) 176 | } 177 | 178 | subscription, err := conn.QueueSubscribe(mq.topic, mq.consumerID, f, opts...) 179 | if err != nil { 180 | return err 181 | } 182 | defer subscription.Close() 183 | 184 | select { 185 | case <-ctx.Done(): 186 | case err = <-anyError: 187 | cancel() 188 | } 189 | 190 | active <- struct{}{} // Wait for running callback 191 | 192 | return err 193 | } 194 | 195 | func (mq *messageSource) Status() (*pubsub.Status, error) { 196 | if mq.conn == nil { 197 | return nil, ErrNotConnected 198 | } 199 | return natsStatus(mq.conn.NatsConn()) 200 | } 201 | -------------------------------------------------------------------------------- /natss/status.go: -------------------------------------------------------------------------------- 1 | package natss 2 | 3 | import ( 4 | "errors" 5 | "fmt" 6 | 7 | "github.com/nats-io/nats.go" 8 | pubsub "github.com/utilitywarehouse/go-pubsub" 9 | ) 10 | 11 | // ErrNotConnected is returned if a status is requested before the connection has been initialized 12 | var ErrNotConnected = errors.New("nats not connected") 13 | 14 | func natsStatus(nc *nats.Conn) (*pubsub.Status, error) { 15 | if nc == nil { 16 | return nil, ErrNotConnected 17 | } 18 | working := nc.IsConnected() 19 | var problems []string 20 | if !working { 21 | notConnected := ErrNotConnected.Error() 22 | if lastErr := nc.LastError(); lastErr != nil { 23 | notConnected = fmt.Sprintf("%s - last error: %s", notConnected, lastErr.Error()) 24 | } 25 | problems = append(problems, notConnected) 26 | } 27 | return &pubsub.Status{ 28 | Problems: problems, 29 | Working: working, 30 | }, nil 31 | } 32 | -------------------------------------------------------------------------------- /proximo/internal/proximoc/client.go: -------------------------------------------------------------------------------- 1 | package proximoc 2 | 3 | import ( 4 | "context" 5 | "crypto/rand" 6 | "encoding/base64" 7 | "io" 8 | "log" 9 | "sync" 10 | 11 | "github.com/pkg/errors" 12 | "google.golang.org/grpc" 13 | ) 14 | 15 | func ConsumeContext(ctx context.Context, proximoAddress string, consumer string, topic string, f func(*Message) error) error { 16 | return consumeContext(ctx, proximoAddress, consumer, topic, f, grpc.WithInsecure()) 17 | } 18 | 19 | func consumeContext(ctx context.Context, proximoAddress string, consumer string, topic string, f func(*Message) error, opts ...grpc.DialOption) error { 20 | 21 | var wg sync.WaitGroup 22 | defer wg.Wait() 23 | 24 | conn, err := grpc.DialContext(ctx, proximoAddress, opts...) 25 | if err != nil { 26 | return errors.Wrapf(err, "fail to dial %s", proximoAddress) 27 | } 28 | defer conn.Close() 29 | client := NewMessageSourceClient(conn) 30 | 31 | stream, err := client.Consume(ctx) 32 | if err != nil { 33 | return errors.Wrap(err, "fail to consume") 34 | } 35 | 36 | defer stream.CloseSend() 37 | 38 | handled := make(chan string) 39 | errs := make(chan error, 2) 40 | 41 | ins := make(chan *Message, 16) // TODO: make buffer size configurable? 42 | 43 | localCtx, cancel := context.WithCancel(ctx) 44 | defer cancel() 45 | 46 | wg.Add(1) 47 | go func() { 48 | defer wg.Done() 49 | defer close(ins) 50 | for { 51 | in, err := stream.Recv() 52 | if err != nil { 53 | if err != io.EOF && grpc.Code(err) != 1 { // 1 means cancelled 54 | errs <- err 55 | } 56 | return 57 | } 58 | select { 59 | case ins <- in: 60 | case <-localCtx.Done(): 61 | return 62 | } 63 | } 64 | }() 65 | 66 | wg.Add(1) 67 | go func() { 68 | defer wg.Done() 69 | for { 70 | select { 71 | case in, ok := <-ins: 72 | if !ok { 73 | return 74 | } 75 | if err := f(in); err != nil { 76 | errs <- err 77 | return 78 | } 79 | select { 80 | case handled <- in.GetId(): 81 | case <-localCtx.Done(): 82 | return 83 | } 84 | case <-localCtx.Done(): 85 | return 86 | } 87 | } 88 | }() 89 | 90 | if err := stream.Send(&ConsumerRequest{ 91 | StartRequest: &StartConsumeRequest{ 92 | Topic: topic, 93 | Consumer: consumer, 94 | }, 95 | }); err != nil { 96 | return err 97 | } 98 | 99 | for { 100 | select { 101 | case id := <-handled: 102 | if err := stream.Send(&ConsumerRequest{Confirmation: &Confirmation{MsgID: id}}); err != nil { 103 | if grpc.Code(err) == 1 { 104 | return nil 105 | } 106 | return err 107 | } 108 | case err := <-errs: 109 | return err 110 | case <-localCtx.Done(): 111 | return nil //ctx.Err() 112 | } 113 | 114 | } 115 | 116 | } 117 | 118 | func DialProducer(ctx context.Context, proximoAddress string, topic string) (*ProducerConn, error) { 119 | return dialProducer(ctx, proximoAddress, topic, grpc.WithInsecure()) 120 | } 121 | 122 | func dialProducer(ctx context.Context, proximoAddress string, topic string, opts ...grpc.DialOption) (*ProducerConn, error) { 123 | 124 | conn, err := grpc.DialContext(ctx, proximoAddress, opts...) 125 | if err != nil { 126 | return nil, err 127 | } 128 | 129 | client := NewMessageSinkClient(conn) 130 | 131 | stream, err := client.Publish(ctx) 132 | if err != nil { 133 | conn.Close() 134 | return nil, err 135 | } 136 | 137 | if err := stream.Send(&PublisherRequest{ 138 | StartRequest: &StartPublishRequest{ 139 | Topic: topic, 140 | }, 141 | }); err != nil { 142 | conn.Close() 143 | return nil, err 144 | } 145 | 146 | ctx, cancel := context.WithCancel(context.Background()) 147 | pc := &ProducerConn{conn, ctx, cancel, stream, make(chan req), make(chan error), sync.WaitGroup{}} 148 | pc.start() 149 | return pc, nil 150 | } 151 | 152 | type req struct { 153 | data []byte 154 | resp chan error 155 | } 156 | 157 | type ProducerConn struct { 158 | cc *grpc.ClientConn 159 | 160 | ctx context.Context 161 | cancel func() 162 | 163 | stream MessageSink_PublishClient 164 | 165 | reqs chan req 166 | 167 | errs chan error 168 | 169 | wg sync.WaitGroup 170 | } 171 | 172 | func (p *ProducerConn) Produce(message []byte) error { 173 | err := make(chan error) 174 | r := req{message, err} 175 | select { 176 | case p.reqs <- r: 177 | case e := <-p.errs: 178 | return e 179 | case <-p.ctx.Done(): 180 | return p.ctx.Err() 181 | } 182 | var e error 183 | select { 184 | case e = <-err: 185 | case e = <-p.errs: 186 | case <-p.ctx.Done(): 187 | } 188 | return e 189 | } 190 | 191 | func (p *ProducerConn) Close() error { 192 | p.cancel() 193 | err := p.cc.Close() 194 | p.wg.Wait() 195 | return err 196 | } 197 | 198 | func (p *ProducerConn) start() error { 199 | 200 | // defer p.stream.CloseSend() 201 | 202 | confirmations := make(chan *Confirmation, 16) // TODO: make buffer size configurable? 203 | 204 | recvErr := make(chan error, 1) 205 | 206 | p.wg.Add(1) 207 | go func() { 208 | defer p.wg.Done() 209 | for { 210 | conf, err := p.stream.Recv() 211 | if err != nil { 212 | if err != io.EOF && grpc.Code(err) != 1 { // 1 means cancelled 213 | recvErr <- err 214 | } 215 | return 216 | } 217 | confirmations <- conf 218 | } 219 | }() 220 | 221 | idErr := make(map[string]chan error) 222 | 223 | p.wg.Add(1) 224 | go func() { 225 | defer p.wg.Done() 226 | var lerr error 227 | 228 | mainLoop: 229 | for { 230 | select { 231 | case err := <-recvErr: 232 | lerr = err 233 | break mainLoop 234 | case in := <-confirmations: 235 | ec := idErr[in.GetMsgID()] 236 | if ec == nil { 237 | lerr = errUnexpectedMessageId 238 | break mainLoop 239 | } 240 | ec <- nil 241 | delete(idErr, in.GetMsgID()) 242 | case req := <-p.reqs: 243 | id := makeId() 244 | idErr[id] = req.resp 245 | if err := p.stream.Send(&PublisherRequest{Msg: &Message{Data: req.data, Id: id}}); err != nil { 246 | if grpc.Code(err) != 1 { 247 | lerr = err 248 | log.Printf("err error %v\n", err) 249 | } 250 | break mainLoop 251 | } 252 | case <-p.ctx.Done(): 253 | break mainLoop 254 | } 255 | } 256 | 257 | var errs chan error 258 | if lerr != nil { 259 | errs = p.errs 260 | } 261 | 262 | errLoop: 263 | for { 264 | select { 265 | case errs <- lerr: 266 | case <-p.ctx.Done(): 267 | break errLoop 268 | } 269 | } 270 | }() 271 | 272 | return nil 273 | } 274 | 275 | var ( 276 | errUnexpectedMessageId = errors.New("unexpected message id") 277 | ) 278 | 279 | func makeId() string { 280 | random := []byte{0, 0, 0, 0, 0, 0, 0, 0} 281 | _, err := rand.Read(random) 282 | if err != nil { 283 | panic(err) 284 | } 285 | return base64.URLEncoding.EncodeToString(random) 286 | } 287 | -------------------------------------------------------------------------------- /proximo/internal/proximoc/proximo.pb.go: -------------------------------------------------------------------------------- 1 | // Code generated by protoc-gen-go. DO NOT EDIT. 2 | // source: proximo.proto 3 | 4 | package proximoc 5 | 6 | import proto "github.com/golang/protobuf/proto" 7 | import fmt "fmt" 8 | import math "math" 9 | 10 | import ( 11 | context "golang.org/x/net/context" 12 | grpc "google.golang.org/grpc" 13 | ) 14 | 15 | // Reference imports to suppress errors if they are not otherwise used. 16 | var _ = proto.Marshal 17 | var _ = fmt.Errorf 18 | var _ = math.Inf 19 | 20 | // This is a compile-time assertion to ensure that this generated file 21 | // is compatible with the proto package it is being compiled against. 22 | // A compilation error at this line likely means your copy of the 23 | // proto package needs to be updated. 24 | const _ = proto.ProtoPackageIsVersion2 // please upgrade the proto package 25 | 26 | type Offset int32 27 | 28 | const ( 29 | Offset_OFFSET_DEFAULT Offset = 0 30 | Offset_OFFSET_NEWEST Offset = 1 31 | Offset_OFFSET_OLDEST Offset = 2 32 | ) 33 | 34 | var Offset_name = map[int32]string{ 35 | 0: "OFFSET_DEFAULT", 36 | 1: "OFFSET_NEWEST", 37 | 2: "OFFSET_OLDEST", 38 | } 39 | var Offset_value = map[string]int32{ 40 | "OFFSET_DEFAULT": 0, 41 | "OFFSET_NEWEST": 1, 42 | "OFFSET_OLDEST": 2, 43 | } 44 | 45 | func (x Offset) String() string { 46 | return proto.EnumName(Offset_name, int32(x)) 47 | } 48 | func (Offset) EnumDescriptor() ([]byte, []int) { 49 | return fileDescriptor_proximo_5f5942b27bb5209c, []int{0} 50 | } 51 | 52 | type Message struct { 53 | Data []byte `protobuf:"bytes,1,opt,name=data,proto3" json:"data,omitempty"` 54 | Id string `protobuf:"bytes,2,opt,name=id,proto3" json:"id,omitempty"` 55 | XXX_NoUnkeyedLiteral struct{} `json:"-"` 56 | XXX_unrecognized []byte `json:"-"` 57 | XXX_sizecache int32 `json:"-"` 58 | } 59 | 60 | func (m *Message) Reset() { *m = Message{} } 61 | func (m *Message) String() string { return proto.CompactTextString(m) } 62 | func (*Message) ProtoMessage() {} 63 | func (*Message) Descriptor() ([]byte, []int) { 64 | return fileDescriptor_proximo_5f5942b27bb5209c, []int{0} 65 | } 66 | func (m *Message) XXX_Unmarshal(b []byte) error { 67 | return xxx_messageInfo_Message.Unmarshal(m, b) 68 | } 69 | func (m *Message) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { 70 | return xxx_messageInfo_Message.Marshal(b, m, deterministic) 71 | } 72 | func (dst *Message) XXX_Merge(src proto.Message) { 73 | xxx_messageInfo_Message.Merge(dst, src) 74 | } 75 | func (m *Message) XXX_Size() int { 76 | return xxx_messageInfo_Message.Size(m) 77 | } 78 | func (m *Message) XXX_DiscardUnknown() { 79 | xxx_messageInfo_Message.DiscardUnknown(m) 80 | } 81 | 82 | var xxx_messageInfo_Message proto.InternalMessageInfo 83 | 84 | func (m *Message) GetData() []byte { 85 | if m != nil { 86 | return m.Data 87 | } 88 | return nil 89 | } 90 | 91 | func (m *Message) GetId() string { 92 | if m != nil { 93 | return m.Id 94 | } 95 | return "" 96 | } 97 | 98 | type ConsumerRequest struct { 99 | // expected if this is a start request 100 | StartRequest *StartConsumeRequest `protobuf:"bytes,2,opt,name=startRequest,proto3" json:"startRequest,omitempty"` 101 | // expected if this is a confirmation 102 | Confirmation *Confirmation `protobuf:"bytes,3,opt,name=confirmation,proto3" json:"confirmation,omitempty"` 103 | XXX_NoUnkeyedLiteral struct{} `json:"-"` 104 | XXX_unrecognized []byte `json:"-"` 105 | XXX_sizecache int32 `json:"-"` 106 | } 107 | 108 | func (m *ConsumerRequest) Reset() { *m = ConsumerRequest{} } 109 | func (m *ConsumerRequest) String() string { return proto.CompactTextString(m) } 110 | func (*ConsumerRequest) ProtoMessage() {} 111 | func (*ConsumerRequest) Descriptor() ([]byte, []int) { 112 | return fileDescriptor_proximo_5f5942b27bb5209c, []int{1} 113 | } 114 | func (m *ConsumerRequest) XXX_Unmarshal(b []byte) error { 115 | return xxx_messageInfo_ConsumerRequest.Unmarshal(m, b) 116 | } 117 | func (m *ConsumerRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { 118 | return xxx_messageInfo_ConsumerRequest.Marshal(b, m, deterministic) 119 | } 120 | func (dst *ConsumerRequest) XXX_Merge(src proto.Message) { 121 | xxx_messageInfo_ConsumerRequest.Merge(dst, src) 122 | } 123 | func (m *ConsumerRequest) XXX_Size() int { 124 | return xxx_messageInfo_ConsumerRequest.Size(m) 125 | } 126 | func (m *ConsumerRequest) XXX_DiscardUnknown() { 127 | xxx_messageInfo_ConsumerRequest.DiscardUnknown(m) 128 | } 129 | 130 | var xxx_messageInfo_ConsumerRequest proto.InternalMessageInfo 131 | 132 | func (m *ConsumerRequest) GetStartRequest() *StartConsumeRequest { 133 | if m != nil { 134 | return m.StartRequest 135 | } 136 | return nil 137 | } 138 | 139 | func (m *ConsumerRequest) GetConfirmation() *Confirmation { 140 | if m != nil { 141 | return m.Confirmation 142 | } 143 | return nil 144 | } 145 | 146 | type StartConsumeRequest struct { 147 | Topic string `protobuf:"bytes,1,opt,name=topic,proto3" json:"topic,omitempty"` 148 | Consumer string `protobuf:"bytes,2,opt,name=consumer,proto3" json:"consumer,omitempty"` 149 | InitialOffset Offset `protobuf:"varint,3,opt,name=initial_offset,json=initialOffset,proto3,enum=proximo.Offset" json:"initial_offset,omitempty"` 150 | XXX_NoUnkeyedLiteral struct{} `json:"-"` 151 | XXX_unrecognized []byte `json:"-"` 152 | XXX_sizecache int32 `json:"-"` 153 | } 154 | 155 | func (m *StartConsumeRequest) Reset() { *m = StartConsumeRequest{} } 156 | func (m *StartConsumeRequest) String() string { return proto.CompactTextString(m) } 157 | func (*StartConsumeRequest) ProtoMessage() {} 158 | func (*StartConsumeRequest) Descriptor() ([]byte, []int) { 159 | return fileDescriptor_proximo_5f5942b27bb5209c, []int{2} 160 | } 161 | func (m *StartConsumeRequest) XXX_Unmarshal(b []byte) error { 162 | return xxx_messageInfo_StartConsumeRequest.Unmarshal(m, b) 163 | } 164 | func (m *StartConsumeRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { 165 | return xxx_messageInfo_StartConsumeRequest.Marshal(b, m, deterministic) 166 | } 167 | func (dst *StartConsumeRequest) XXX_Merge(src proto.Message) { 168 | xxx_messageInfo_StartConsumeRequest.Merge(dst, src) 169 | } 170 | func (m *StartConsumeRequest) XXX_Size() int { 171 | return xxx_messageInfo_StartConsumeRequest.Size(m) 172 | } 173 | func (m *StartConsumeRequest) XXX_DiscardUnknown() { 174 | xxx_messageInfo_StartConsumeRequest.DiscardUnknown(m) 175 | } 176 | 177 | var xxx_messageInfo_StartConsumeRequest proto.InternalMessageInfo 178 | 179 | func (m *StartConsumeRequest) GetTopic() string { 180 | if m != nil { 181 | return m.Topic 182 | } 183 | return "" 184 | } 185 | 186 | func (m *StartConsumeRequest) GetConsumer() string { 187 | if m != nil { 188 | return m.Consumer 189 | } 190 | return "" 191 | } 192 | 193 | func (m *StartConsumeRequest) GetInitialOffset() Offset { 194 | if m != nil { 195 | return m.InitialOffset 196 | } 197 | return Offset_OFFSET_DEFAULT 198 | } 199 | 200 | type Confirmation struct { 201 | MsgID string `protobuf:"bytes,1,opt,name=msgID,proto3" json:"msgID,omitempty"` 202 | XXX_NoUnkeyedLiteral struct{} `json:"-"` 203 | XXX_unrecognized []byte `json:"-"` 204 | XXX_sizecache int32 `json:"-"` 205 | } 206 | 207 | func (m *Confirmation) Reset() { *m = Confirmation{} } 208 | func (m *Confirmation) String() string { return proto.CompactTextString(m) } 209 | func (*Confirmation) ProtoMessage() {} 210 | func (*Confirmation) Descriptor() ([]byte, []int) { 211 | return fileDescriptor_proximo_5f5942b27bb5209c, []int{3} 212 | } 213 | func (m *Confirmation) XXX_Unmarshal(b []byte) error { 214 | return xxx_messageInfo_Confirmation.Unmarshal(m, b) 215 | } 216 | func (m *Confirmation) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { 217 | return xxx_messageInfo_Confirmation.Marshal(b, m, deterministic) 218 | } 219 | func (dst *Confirmation) XXX_Merge(src proto.Message) { 220 | xxx_messageInfo_Confirmation.Merge(dst, src) 221 | } 222 | func (m *Confirmation) XXX_Size() int { 223 | return xxx_messageInfo_Confirmation.Size(m) 224 | } 225 | func (m *Confirmation) XXX_DiscardUnknown() { 226 | xxx_messageInfo_Confirmation.DiscardUnknown(m) 227 | } 228 | 229 | var xxx_messageInfo_Confirmation proto.InternalMessageInfo 230 | 231 | func (m *Confirmation) GetMsgID() string { 232 | if m != nil { 233 | return m.MsgID 234 | } 235 | return "" 236 | } 237 | 238 | type PublisherRequest struct { 239 | // expected if this is a start request 240 | StartRequest *StartPublishRequest `protobuf:"bytes,2,opt,name=startRequest,proto3" json:"startRequest,omitempty"` 241 | // expected if this is a message 242 | Msg *Message `protobuf:"bytes,3,opt,name=msg,proto3" json:"msg,omitempty"` 243 | XXX_NoUnkeyedLiteral struct{} `json:"-"` 244 | XXX_unrecognized []byte `json:"-"` 245 | XXX_sizecache int32 `json:"-"` 246 | } 247 | 248 | func (m *PublisherRequest) Reset() { *m = PublisherRequest{} } 249 | func (m *PublisherRequest) String() string { return proto.CompactTextString(m) } 250 | func (*PublisherRequest) ProtoMessage() {} 251 | func (*PublisherRequest) Descriptor() ([]byte, []int) { 252 | return fileDescriptor_proximo_5f5942b27bb5209c, []int{4} 253 | } 254 | func (m *PublisherRequest) XXX_Unmarshal(b []byte) error { 255 | return xxx_messageInfo_PublisherRequest.Unmarshal(m, b) 256 | } 257 | func (m *PublisherRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { 258 | return xxx_messageInfo_PublisherRequest.Marshal(b, m, deterministic) 259 | } 260 | func (dst *PublisherRequest) XXX_Merge(src proto.Message) { 261 | xxx_messageInfo_PublisherRequest.Merge(dst, src) 262 | } 263 | func (m *PublisherRequest) XXX_Size() int { 264 | return xxx_messageInfo_PublisherRequest.Size(m) 265 | } 266 | func (m *PublisherRequest) XXX_DiscardUnknown() { 267 | xxx_messageInfo_PublisherRequest.DiscardUnknown(m) 268 | } 269 | 270 | var xxx_messageInfo_PublisherRequest proto.InternalMessageInfo 271 | 272 | func (m *PublisherRequest) GetStartRequest() *StartPublishRequest { 273 | if m != nil { 274 | return m.StartRequest 275 | } 276 | return nil 277 | } 278 | 279 | func (m *PublisherRequest) GetMsg() *Message { 280 | if m != nil { 281 | return m.Msg 282 | } 283 | return nil 284 | } 285 | 286 | type StartPublishRequest struct { 287 | Topic string `protobuf:"bytes,1,opt,name=topic,proto3" json:"topic,omitempty"` 288 | XXX_NoUnkeyedLiteral struct{} `json:"-"` 289 | XXX_unrecognized []byte `json:"-"` 290 | XXX_sizecache int32 `json:"-"` 291 | } 292 | 293 | func (m *StartPublishRequest) Reset() { *m = StartPublishRequest{} } 294 | func (m *StartPublishRequest) String() string { return proto.CompactTextString(m) } 295 | func (*StartPublishRequest) ProtoMessage() {} 296 | func (*StartPublishRequest) Descriptor() ([]byte, []int) { 297 | return fileDescriptor_proximo_5f5942b27bb5209c, []int{5} 298 | } 299 | func (m *StartPublishRequest) XXX_Unmarshal(b []byte) error { 300 | return xxx_messageInfo_StartPublishRequest.Unmarshal(m, b) 301 | } 302 | func (m *StartPublishRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { 303 | return xxx_messageInfo_StartPublishRequest.Marshal(b, m, deterministic) 304 | } 305 | func (dst *StartPublishRequest) XXX_Merge(src proto.Message) { 306 | xxx_messageInfo_StartPublishRequest.Merge(dst, src) 307 | } 308 | func (m *StartPublishRequest) XXX_Size() int { 309 | return xxx_messageInfo_StartPublishRequest.Size(m) 310 | } 311 | func (m *StartPublishRequest) XXX_DiscardUnknown() { 312 | xxx_messageInfo_StartPublishRequest.DiscardUnknown(m) 313 | } 314 | 315 | var xxx_messageInfo_StartPublishRequest proto.InternalMessageInfo 316 | 317 | func (m *StartPublishRequest) GetTopic() string { 318 | if m != nil { 319 | return m.Topic 320 | } 321 | return "" 322 | } 323 | 324 | func init() { 325 | proto.RegisterType((*Message)(nil), "proximo.Message") 326 | proto.RegisterType((*ConsumerRequest)(nil), "proximo.ConsumerRequest") 327 | proto.RegisterType((*StartConsumeRequest)(nil), "proximo.StartConsumeRequest") 328 | proto.RegisterType((*Confirmation)(nil), "proximo.Confirmation") 329 | proto.RegisterType((*PublisherRequest)(nil), "proximo.PublisherRequest") 330 | proto.RegisterType((*StartPublishRequest)(nil), "proximo.StartPublishRequest") 331 | proto.RegisterEnum("proximo.Offset", Offset_name, Offset_value) 332 | } 333 | 334 | // Reference imports to suppress errors if they are not otherwise used. 335 | var _ context.Context 336 | var _ grpc.ClientConn 337 | 338 | // This is a compile-time assertion to ensure that this generated file 339 | // is compatible with the grpc package it is being compiled against. 340 | const _ = grpc.SupportPackageIsVersion4 341 | 342 | // MessageSourceClient is the client API for MessageSource service. 343 | // 344 | // For semantics around ctx use and closing/ending streaming RPCs, please refer to https://godoc.org/google.golang.org/grpc#ClientConn.NewStream. 345 | type MessageSourceClient interface { 346 | Consume(ctx context.Context, opts ...grpc.CallOption) (MessageSource_ConsumeClient, error) 347 | } 348 | 349 | type messageSourceClient struct { 350 | cc *grpc.ClientConn 351 | } 352 | 353 | func NewMessageSourceClient(cc *grpc.ClientConn) MessageSourceClient { 354 | return &messageSourceClient{cc} 355 | } 356 | 357 | func (c *messageSourceClient) Consume(ctx context.Context, opts ...grpc.CallOption) (MessageSource_ConsumeClient, error) { 358 | stream, err := c.cc.NewStream(ctx, &_MessageSource_serviceDesc.Streams[0], "/proximo.MessageSource/Consume", opts...) 359 | if err != nil { 360 | return nil, err 361 | } 362 | x := &messageSourceConsumeClient{stream} 363 | return x, nil 364 | } 365 | 366 | type MessageSource_ConsumeClient interface { 367 | Send(*ConsumerRequest) error 368 | Recv() (*Message, error) 369 | grpc.ClientStream 370 | } 371 | 372 | type messageSourceConsumeClient struct { 373 | grpc.ClientStream 374 | } 375 | 376 | func (x *messageSourceConsumeClient) Send(m *ConsumerRequest) error { 377 | return x.ClientStream.SendMsg(m) 378 | } 379 | 380 | func (x *messageSourceConsumeClient) Recv() (*Message, error) { 381 | m := new(Message) 382 | if err := x.ClientStream.RecvMsg(m); err != nil { 383 | return nil, err 384 | } 385 | return m, nil 386 | } 387 | 388 | // MessageSourceServer is the server API for MessageSource service. 389 | type MessageSourceServer interface { 390 | Consume(MessageSource_ConsumeServer) error 391 | } 392 | 393 | func RegisterMessageSourceServer(s *grpc.Server, srv MessageSourceServer) { 394 | s.RegisterService(&_MessageSource_serviceDesc, srv) 395 | } 396 | 397 | func _MessageSource_Consume_Handler(srv interface{}, stream grpc.ServerStream) error { 398 | return srv.(MessageSourceServer).Consume(&messageSourceConsumeServer{stream}) 399 | } 400 | 401 | type MessageSource_ConsumeServer interface { 402 | Send(*Message) error 403 | Recv() (*ConsumerRequest, error) 404 | grpc.ServerStream 405 | } 406 | 407 | type messageSourceConsumeServer struct { 408 | grpc.ServerStream 409 | } 410 | 411 | func (x *messageSourceConsumeServer) Send(m *Message) error { 412 | return x.ServerStream.SendMsg(m) 413 | } 414 | 415 | func (x *messageSourceConsumeServer) Recv() (*ConsumerRequest, error) { 416 | m := new(ConsumerRequest) 417 | if err := x.ServerStream.RecvMsg(m); err != nil { 418 | return nil, err 419 | } 420 | return m, nil 421 | } 422 | 423 | var _MessageSource_serviceDesc = grpc.ServiceDesc{ 424 | ServiceName: "proximo.MessageSource", 425 | HandlerType: (*MessageSourceServer)(nil), 426 | Methods: []grpc.MethodDesc{}, 427 | Streams: []grpc.StreamDesc{ 428 | { 429 | StreamName: "Consume", 430 | Handler: _MessageSource_Consume_Handler, 431 | ServerStreams: true, 432 | ClientStreams: true, 433 | }, 434 | }, 435 | Metadata: "proximo.proto", 436 | } 437 | 438 | // MessageSinkClient is the client API for MessageSink service. 439 | // 440 | // For semantics around ctx use and closing/ending streaming RPCs, please refer to https://godoc.org/google.golang.org/grpc#ClientConn.NewStream. 441 | type MessageSinkClient interface { 442 | Publish(ctx context.Context, opts ...grpc.CallOption) (MessageSink_PublishClient, error) 443 | } 444 | 445 | type messageSinkClient struct { 446 | cc *grpc.ClientConn 447 | } 448 | 449 | func NewMessageSinkClient(cc *grpc.ClientConn) MessageSinkClient { 450 | return &messageSinkClient{cc} 451 | } 452 | 453 | func (c *messageSinkClient) Publish(ctx context.Context, opts ...grpc.CallOption) (MessageSink_PublishClient, error) { 454 | stream, err := c.cc.NewStream(ctx, &_MessageSink_serviceDesc.Streams[0], "/proximo.MessageSink/Publish", opts...) 455 | if err != nil { 456 | return nil, err 457 | } 458 | x := &messageSinkPublishClient{stream} 459 | return x, nil 460 | } 461 | 462 | type MessageSink_PublishClient interface { 463 | Send(*PublisherRequest) error 464 | Recv() (*Confirmation, error) 465 | grpc.ClientStream 466 | } 467 | 468 | type messageSinkPublishClient struct { 469 | grpc.ClientStream 470 | } 471 | 472 | func (x *messageSinkPublishClient) Send(m *PublisherRequest) error { 473 | return x.ClientStream.SendMsg(m) 474 | } 475 | 476 | func (x *messageSinkPublishClient) Recv() (*Confirmation, error) { 477 | m := new(Confirmation) 478 | if err := x.ClientStream.RecvMsg(m); err != nil { 479 | return nil, err 480 | } 481 | return m, nil 482 | } 483 | 484 | // MessageSinkServer is the server API for MessageSink service. 485 | type MessageSinkServer interface { 486 | Publish(MessageSink_PublishServer) error 487 | } 488 | 489 | func RegisterMessageSinkServer(s *grpc.Server, srv MessageSinkServer) { 490 | s.RegisterService(&_MessageSink_serviceDesc, srv) 491 | } 492 | 493 | func _MessageSink_Publish_Handler(srv interface{}, stream grpc.ServerStream) error { 494 | return srv.(MessageSinkServer).Publish(&messageSinkPublishServer{stream}) 495 | } 496 | 497 | type MessageSink_PublishServer interface { 498 | Send(*Confirmation) error 499 | Recv() (*PublisherRequest, error) 500 | grpc.ServerStream 501 | } 502 | 503 | type messageSinkPublishServer struct { 504 | grpc.ServerStream 505 | } 506 | 507 | func (x *messageSinkPublishServer) Send(m *Confirmation) error { 508 | return x.ServerStream.SendMsg(m) 509 | } 510 | 511 | func (x *messageSinkPublishServer) Recv() (*PublisherRequest, error) { 512 | m := new(PublisherRequest) 513 | if err := x.ServerStream.RecvMsg(m); err != nil { 514 | return nil, err 515 | } 516 | return m, nil 517 | } 518 | 519 | var _MessageSink_serviceDesc = grpc.ServiceDesc{ 520 | ServiceName: "proximo.MessageSink", 521 | HandlerType: (*MessageSinkServer)(nil), 522 | Methods: []grpc.MethodDesc{}, 523 | Streams: []grpc.StreamDesc{ 524 | { 525 | StreamName: "Publish", 526 | Handler: _MessageSink_Publish_Handler, 527 | ServerStreams: true, 528 | ClientStreams: true, 529 | }, 530 | }, 531 | Metadata: "proximo.proto", 532 | } 533 | 534 | func init() { proto.RegisterFile("proximo.proto", fileDescriptor_proximo_5f5942b27bb5209c) } 535 | 536 | var fileDescriptor_proximo_5f5942b27bb5209c = []byte{ 537 | // 394 bytes of a gzipped FileDescriptorProto 538 | 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x94, 0x53, 0x4d, 0xab, 0xd3, 0x40, 539 | 0x14, 0x7d, 0xc9, 0xd3, 0x17, 0xdf, 0x7d, 0x49, 0x1a, 0xaf, 0x0a, 0xb1, 0xb8, 0x28, 0x83, 0x8b, 540 | 0xa2, 0x58, 0x24, 0x82, 0x20, 0x6e, 0xac, 0x6d, 0x0a, 0x42, 0xb5, 0x65, 0x52, 0x71, 0x59, 0xd2, 541 | 0x34, 0xad, 0x83, 0x4d, 0xa6, 0x66, 0x26, 0xd0, 0x9d, 0xff, 0xc0, 0xdf, 0x2c, 0x99, 0x4c, 0x43, 542 | 0xaa, 0xd9, 0xb8, 0xcb, 0xb9, 0x1f, 0xe7, 0x9e, 0x7b, 0xee, 0x04, 0x9c, 0x63, 0xc1, 0x4f, 0x2c, 543 | 0xe3, 0xa3, 0x63, 0xc1, 0x25, 0x47, 0x4b, 0x43, 0xf2, 0x0a, 0xac, 0xcf, 0xa9, 0x10, 0xf1, 0x3e, 544 | 0x45, 0x84, 0x7b, 0xdb, 0x58, 0xc6, 0xbe, 0x31, 0x30, 0x86, 0x36, 0x55, 0xdf, 0xe8, 0x82, 0xc9, 545 | 0xb6, 0xbe, 0x39, 0x30, 0x86, 0xb7, 0xd4, 0x64, 0x5b, 0xf2, 0xdb, 0x80, 0xde, 0x84, 0xe7, 0xa2, 546 | 0xcc, 0xd2, 0x82, 0xa6, 0x3f, 0xcb, 0x54, 0x48, 0xfc, 0x00, 0xb6, 0x90, 0x71, 0x21, 0x35, 0x56, 547 | 0xd5, 0x77, 0xc1, 0xb3, 0xd1, 0x79, 0x62, 0x54, 0x25, 0x75, 0x93, 0xae, 0xa1, 0x17, 0x1d, 0xf8, 548 | 0x0e, 0xec, 0x84, 0xe7, 0x3b, 0x56, 0x64, 0xb1, 0x64, 0x3c, 0xf7, 0xaf, 0x15, 0xc3, 0x93, 0x86, 549 | 0x61, 0xd2, 0x4a, 0xd2, 0x8b, 0x52, 0xf2, 0x0b, 0x1e, 0x75, 0xf0, 0xe3, 0x63, 0xb8, 0x2f, 0xf9, 550 | 0x91, 0x25, 0x6a, 0x99, 0x5b, 0x5a, 0x03, 0xec, 0xc3, 0x83, 0x44, 0x8b, 0xd7, 0x3b, 0x35, 0x18, 551 | 0xdf, 0x82, 0xcb, 0x72, 0x26, 0x59, 0x7c, 0x58, 0xf3, 0xdd, 0x4e, 0xa4, 0x52, 0xa9, 0x70, 0x83, 552 | 0x5e, 0xa3, 0x62, 0xa1, 0xc2, 0xd4, 0xd1, 0x65, 0x35, 0x24, 0xcf, 0xc1, 0x6e, 0xcb, 0xab, 0x26, 553 | 0x67, 0x62, 0xff, 0x69, 0x7a, 0x9e, 0xac, 0x00, 0x39, 0x81, 0xb7, 0x2c, 0x37, 0x07, 0x26, 0xbe, 554 | 0xff, 0xa7, 0x6f, 0xba, 0xab, 0xdb, 0x37, 0x02, 0xd7, 0x99, 0xd8, 0x6b, 0xbb, 0xbc, 0xa6, 0x51, 555 | 0x1f, 0x94, 0x56, 0x49, 0xf2, 0x52, 0x1b, 0x74, 0x49, 0xd4, 0x6d, 0xd0, 0x8b, 0x8f, 0x70, 0x53, 556 | 0xaf, 0x85, 0x08, 0xee, 0x62, 0x36, 0x8b, 0xc2, 0xd5, 0x7a, 0x1a, 0xce, 0xc6, 0x5f, 0xe7, 0x2b, 557 | 0xef, 0x0a, 0x1f, 0x82, 0xa3, 0x63, 0x5f, 0xc2, 0x6f, 0x61, 0xb4, 0xf2, 0x8c, 0x56, 0x68, 0x31, 558 | 0x9f, 0x56, 0x21, 0x33, 0x98, 0x83, 0xa3, 0x05, 0x44, 0xbc, 0x2c, 0x92, 0x14, 0xdf, 0x83, 0xa5, 559 | 0xaf, 0x83, 0x7e, 0xfb, 0xa4, 0xed, 0x47, 0xd4, 0xff, 0x47, 0x3d, 0xb9, 0x1a, 0x1a, 0xaf, 0x8d, 560 | 0x60, 0x09, 0x77, 0x67, 0x36, 0x96, 0xff, 0xc0, 0x31, 0x58, 0x7a, 0x11, 0x7c, 0xda, 0x74, 0xfc, 561 | 0xed, 0x6c, 0xbf, 0xfb, 0xe5, 0xd4, 0x8c, 0x9b, 0x1b, 0xf5, 0x07, 0xbc, 0xf9, 0x13, 0x00, 0x00, 562 | 0xff, 0xff, 0x44, 0x70, 0xab, 0xad, 0x12, 0x03, 0x00, 0x00, 563 | } 564 | -------------------------------------------------------------------------------- /proximo/proximo_sink.go: -------------------------------------------------------------------------------- 1 | package proximo 2 | 3 | import ( 4 | "context" 5 | "errors" 6 | "sync" 7 | 8 | "github.com/utilitywarehouse/go-pubsub" 9 | "github.com/utilitywarehouse/go-pubsub/proximo/internal/proximoc" 10 | ) 11 | 12 | var _ pubsub.MessageSink = (*messageSink)(nil) 13 | 14 | type messageSink struct { 15 | topic string 16 | 17 | keyFunc func(pubsub.ProducerMessage) []byte 18 | 19 | lk sync.Mutex 20 | producer *proximoc.ProducerConn 21 | closed bool 22 | 23 | broker string 24 | } 25 | 26 | type MessageSinkConfig struct { 27 | Topic string 28 | Broker string 29 | } 30 | 31 | func NewMessageSink(config MessageSinkConfig) (pubsub.MessageSink, error) { 32 | 33 | ctx := context.Background() 34 | producer, err := proximoc.DialProducer(ctx, config.Broker, config.Topic) 35 | 36 | if err != nil { 37 | return nil, err 38 | } 39 | 40 | return &messageSink{ 41 | topic: config.Topic, 42 | producer: producer, 43 | broker: config.Broker, 44 | }, nil 45 | } 46 | 47 | func (mq *messageSink) PutMessage(m pubsub.ProducerMessage) error { 48 | data, err := m.Marshal() 49 | if err != nil { 50 | return err 51 | } 52 | return mq.producer.Produce(data) 53 | } 54 | 55 | func (mq *messageSink) Close() error { 56 | mq.lk.Lock() 57 | defer mq.lk.Unlock() 58 | 59 | if mq.closed { 60 | return errors.New("Already closed") 61 | } 62 | 63 | mq.closed = true 64 | return mq.producer.Close() 65 | } 66 | 67 | // Status reports the status of the message sink 68 | func (mq *messageSink) Status() (*pubsub.Status, error) { 69 | return nil, errors.New("status is not implemented") 70 | } 71 | -------------------------------------------------------------------------------- /proximo/proximo_source.go: -------------------------------------------------------------------------------- 1 | package proximo 2 | 3 | import ( 4 | "context" 5 | "errors" 6 | 7 | "github.com/utilitywarehouse/go-pubsub" 8 | "github.com/utilitywarehouse/go-pubsub/proximo/internal/proximoc" 9 | ) 10 | 11 | var _ pubsub.MessageSource = (*messageSource)(nil) 12 | 13 | type messageSource struct { 14 | consumergroup string 15 | topic string 16 | broker string 17 | } 18 | 19 | type MessageSourceConfig struct { 20 | ConsumerGroup string 21 | Topic string 22 | Broker string 23 | } 24 | 25 | func NewMessageSource(config MessageSourceConfig) pubsub.MessageSource { 26 | 27 | return &messageSource{ 28 | consumergroup: config.ConsumerGroup, 29 | topic: config.Topic, 30 | broker: config.Broker, 31 | } 32 | } 33 | 34 | func (mq *messageSource) ConsumeMessages(ctx context.Context, handler pubsub.ConsumerMessageHandler, onError pubsub.ConsumerErrorHandler) error { 35 | cf := func(msg *proximoc.Message) error { 36 | m := pubsub.ConsumerMessage{msg.GetData()} 37 | err := handler(m) 38 | if err != nil { 39 | err = onError(m, err) 40 | } 41 | return err 42 | } 43 | return proximoc.ConsumeContext(ctx, mq.broker, mq.consumergroup, mq.topic, cf) 44 | } 45 | 46 | // Status reports the status of the message source 47 | func (mq *messageSource) Status() (*pubsub.Status, error) { 48 | return nil, errors.New("status is not implemented") 49 | } 50 | -------------------------------------------------------------------------------- /proximo/proximo_test.go: -------------------------------------------------------------------------------- 1 | package proximo 2 | 3 | import ( 4 | "context" 5 | "errors" 6 | "flag" 7 | "io" 8 | "os" 9 | "os/exec" 10 | "testing" 11 | "time" 12 | 13 | "github.com/pborman/uuid" 14 | "github.com/utilitywarehouse/go-pubsub" 15 | ) 16 | 17 | func TestMain(m *testing.M) { 18 | flag.Parse() 19 | 20 | cmd := exec.Command("proximo-server", "--port=6869", "mem") 21 | 22 | ep, err := cmd.StderrPipe() 23 | if err != nil { 24 | panic(err) 25 | } 26 | 27 | if err := cmd.Start(); err != nil { 28 | panic(err) 29 | } 30 | time.Sleep(200 * time.Millisecond) 31 | 32 | result := m.Run() 33 | 34 | if result != 0 { 35 | io.Copy(os.Stderr, ep) 36 | } 37 | 38 | cmd.Process.Kill() 39 | 40 | os.Exit(result) 41 | } 42 | 43 | func TestSimpleProduceConsume(t *testing.T) { 44 | 45 | topicName := uuid.NewRandom().String() 46 | message := uuid.NewRandom().String() 47 | 48 | produceMessage(t, topicName, message) 49 | 50 | msg := make(chan string, 1) 51 | 52 | cons := NewMessageSource(MessageSourceConfig{ConsumerGroup: "test-group", Topic: topicName, Broker: "localhost:6869"}) 53 | 54 | ctx, canc := context.WithCancel(context.Background()) 55 | defer canc() 56 | 57 | handler := func(m pubsub.ConsumerMessage) error { 58 | msg <- string(m.Data) 59 | canc() 60 | close(msg) 61 | return nil 62 | } 63 | 64 | onError := func(m pubsub.ConsumerMessage, e error) error { 65 | t.Error("unexpected error") 66 | return nil 67 | } 68 | 69 | if err := cons.ConsumeMessages(ctx, handler, onError); err != nil { 70 | t.Error(err) 71 | } 72 | 73 | select { 74 | case <-time.After(2 * time.Second): 75 | t.Error("didn't get message") 76 | case m := <-msg: 77 | if m != message { 78 | t.Error("message not as expected") 79 | } 80 | } 81 | } 82 | 83 | func TestConsumeError(t *testing.T) { 84 | 85 | topicName := uuid.NewRandom().String() 86 | message := uuid.NewRandom().String() 87 | 88 | produceMessage(t, topicName, message) 89 | 90 | { 91 | ctx, cancel := context.WithTimeout(context.Background(), 50*time.Millisecond) 92 | defer cancel() 93 | 94 | cons := NewMessageSource(MessageSourceConfig{ConsumerGroup: "test-group", Topic: topicName, Broker: "localhost:6869"}) 95 | 96 | handler := func(m pubsub.ConsumerMessage) error { 97 | return errors.New("test error") 98 | } 99 | 100 | onError := func(m pubsub.ConsumerMessage, e error) error { 101 | return errors.New("onError error") 102 | } 103 | 104 | consumeErr := cons.ConsumeMessages(ctx, handler, onError) 105 | if consumeErr == nil { 106 | t.Error("expected error") 107 | } 108 | } 109 | 110 | { 111 | ctx, cancel := context.WithCancel(context.Background()) 112 | defer cancel() 113 | 114 | // message should be still available to consume 115 | cons2 := NewMessageSource(MessageSourceConfig{ConsumerGroup: "test-group", Topic: topicName, Broker: "localhost:6869"}) 116 | 117 | msg := make(chan string, 1) 118 | 119 | handler := func(m pubsub.ConsumerMessage) error { 120 | msg <- string(m.Data) 121 | close(msg) 122 | cancel() 123 | return nil 124 | } 125 | 126 | onError := func(m pubsub.ConsumerMessage, e error) error { 127 | t.Error("unexpected error") 128 | return nil 129 | } 130 | 131 | if err := cons2.ConsumeMessages(ctx, handler, onError); err != nil { 132 | t.Error(err) 133 | } 134 | 135 | select { 136 | case m := <-msg: 137 | if m != message { 138 | t.Error("message not as expected") 139 | } 140 | case <-time.After(2 * time.Second): 141 | t.Error("timeout waiting for message") 142 | } 143 | } 144 | } 145 | 146 | func produceMessage(t *testing.T, topicName, message string) { 147 | sink, err := NewMessageSink(MessageSinkConfig{topicName, "localhost:6869"}) 148 | if err != nil { 149 | t.Fatal(err) 150 | } 151 | 152 | sink.PutMessage(pubsub.SimpleProducerMessage([]byte(message))) 153 | 154 | if err := sink.Close(); err != nil { 155 | t.Error(err) 156 | } 157 | } 158 | -------------------------------------------------------------------------------- /pubsub.go: -------------------------------------------------------------------------------- 1 | package pubsub 2 | 3 | import ( 4 | "context" 5 | "io" 6 | ) 7 | 8 | // ProducerMessage is an individual message that can be sent 9 | type ProducerMessage interface { 10 | // Marshal returns the message in wire format 11 | Marshal() ([]byte, error) 12 | } 13 | 14 | // SimpleProducerMessage is a convenience type for simply sending byte slices. 15 | type SimpleProducerMessage []byte 16 | 17 | func (sm SimpleProducerMessage) Marshal() ([]byte, error) { 18 | return []byte(sm), nil 19 | } 20 | 21 | type ConsumerMessage struct { 22 | Data []byte 23 | } 24 | 25 | type MessageSink interface { 26 | io.Closer 27 | PutMessage(ProducerMessage) error 28 | Statuser 29 | } 30 | 31 | type MessageSource interface { 32 | // Consume messages will block until error or until the context is done. 33 | ConsumeMessages(ctx context.Context, handler ConsumerMessageHandler, onError ConsumerErrorHandler) error 34 | Statuser 35 | } 36 | // ConcurrentMessageSource concurrent message consumer 37 | type ConcurrentMessageSource interface { 38 | MessageSource 39 | // ConsumeMessagesConcurrently provides similar functionality to ConsumeMessages but utilises 40 | // multiple routines to achieve concurrency, the exact amount of routines that will 41 | // be created depends on the underlying technology 42 | ConsumeMessagesConcurrently(ctx context.Context, handler ConsumerMessageHandler, onError ConsumerErrorHandler) error 43 | } 44 | 45 | // Statuser is the interface that wraps the Status method. 46 | type Statuser interface { 47 | Status() (*Status, error) 48 | } 49 | 50 | // Status represents a snapshot of the state of a source or sink. 51 | type Status struct { 52 | // Working indicates whether the source or sink is in a working state 53 | Working bool 54 | // Problems indicates and problems with the source or sink, whether or not they prevent it working. 55 | Problems []string 56 | } 57 | 58 | // ConsumerMessageHandler processes messages, and should return an error if it 59 | // is unable to do so. 60 | type ConsumerMessageHandler func(ConsumerMessage) error 61 | 62 | // ConsumerErrorHandler is invoked when a message can not be processed. If an 63 | // error handler returns an error itself, processing of messages is aborted 64 | type ConsumerErrorHandler func(ConsumerMessage, error) error 65 | -------------------------------------------------------------------------------- /sns/export_test.go: -------------------------------------------------------------------------------- 1 | package sns 2 | 3 | import "github.com/aws/aws-sdk-go/service/sns/snsiface" 4 | 5 | func NewTestSNSSink(snsapi snsiface.SNSAPI, topic string) *messageSink { 6 | return &messageSink{ 7 | client: snsapi, 8 | topic: topic, 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /sns/mocks/snsapi.go: -------------------------------------------------------------------------------- 1 | // Code generated by MockGen. DO NOT EDIT. 2 | // Source: interface.go 3 | 4 | // Package mocks is a generated GoMock package. 5 | package mocks 6 | 7 | import ( 8 | aws "github.com/aws/aws-sdk-go/aws" 9 | request "github.com/aws/aws-sdk-go/aws/request" 10 | sns "github.com/aws/aws-sdk-go/service/sns" 11 | gomock "github.com/golang/mock/gomock" 12 | reflect "reflect" 13 | ) 14 | 15 | // MockSNSAPI is a mock of SNSAPI interface 16 | type MockSNSAPI struct { 17 | ctrl *gomock.Controller 18 | recorder *MockSNSAPIMockRecorder 19 | } 20 | 21 | // MockSNSAPIMockRecorder is the mock recorder for MockSNSAPI 22 | type MockSNSAPIMockRecorder struct { 23 | mock *MockSNSAPI 24 | } 25 | 26 | // NewMockSNSAPI creates a new mock instance 27 | func NewMockSNSAPI(ctrl *gomock.Controller) *MockSNSAPI { 28 | mock := &MockSNSAPI{ctrl: ctrl} 29 | mock.recorder = &MockSNSAPIMockRecorder{mock} 30 | return mock 31 | } 32 | 33 | // EXPECT returns an object that allows the caller to indicate expected use 34 | func (m *MockSNSAPI) EXPECT() *MockSNSAPIMockRecorder { 35 | return m.recorder 36 | } 37 | 38 | // AddPermission mocks base method 39 | func (m *MockSNSAPI) AddPermission(arg0 *sns.AddPermissionInput) (*sns.AddPermissionOutput, error) { 40 | ret := m.ctrl.Call(m, "AddPermission", arg0) 41 | ret0, _ := ret[0].(*sns.AddPermissionOutput) 42 | ret1, _ := ret[1].(error) 43 | return ret0, ret1 44 | } 45 | 46 | // AddPermission indicates an expected call of AddPermission 47 | func (mr *MockSNSAPIMockRecorder) AddPermission(arg0 interface{}) *gomock.Call { 48 | return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "AddPermission", reflect.TypeOf((*MockSNSAPI)(nil).AddPermission), arg0) 49 | } 50 | 51 | // AddPermissionWithContext mocks base method 52 | func (m *MockSNSAPI) AddPermissionWithContext(arg0 aws.Context, arg1 *sns.AddPermissionInput, arg2 ...request.Option) (*sns.AddPermissionOutput, error) { 53 | varargs := []interface{}{arg0, arg1} 54 | for _, a := range arg2 { 55 | varargs = append(varargs, a) 56 | } 57 | ret := m.ctrl.Call(m, "AddPermissionWithContext", varargs...) 58 | ret0, _ := ret[0].(*sns.AddPermissionOutput) 59 | ret1, _ := ret[1].(error) 60 | return ret0, ret1 61 | } 62 | 63 | // AddPermissionWithContext indicates an expected call of AddPermissionWithContext 64 | func (mr *MockSNSAPIMockRecorder) AddPermissionWithContext(arg0, arg1 interface{}, arg2 ...interface{}) *gomock.Call { 65 | varargs := append([]interface{}{arg0, arg1}, arg2...) 66 | return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "AddPermissionWithContext", reflect.TypeOf((*MockSNSAPI)(nil).AddPermissionWithContext), varargs...) 67 | } 68 | 69 | // AddPermissionRequest mocks base method 70 | func (m *MockSNSAPI) AddPermissionRequest(arg0 *sns.AddPermissionInput) (*request.Request, *sns.AddPermissionOutput) { 71 | ret := m.ctrl.Call(m, "AddPermissionRequest", arg0) 72 | ret0, _ := ret[0].(*request.Request) 73 | ret1, _ := ret[1].(*sns.AddPermissionOutput) 74 | return ret0, ret1 75 | } 76 | 77 | // AddPermissionRequest indicates an expected call of AddPermissionRequest 78 | func (mr *MockSNSAPIMockRecorder) AddPermissionRequest(arg0 interface{}) *gomock.Call { 79 | return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "AddPermissionRequest", reflect.TypeOf((*MockSNSAPI)(nil).AddPermissionRequest), arg0) 80 | } 81 | 82 | // CheckIfPhoneNumberIsOptedOut mocks base method 83 | func (m *MockSNSAPI) CheckIfPhoneNumberIsOptedOut(arg0 *sns.CheckIfPhoneNumberIsOptedOutInput) (*sns.CheckIfPhoneNumberIsOptedOutOutput, error) { 84 | ret := m.ctrl.Call(m, "CheckIfPhoneNumberIsOptedOut", arg0) 85 | ret0, _ := ret[0].(*sns.CheckIfPhoneNumberIsOptedOutOutput) 86 | ret1, _ := ret[1].(error) 87 | return ret0, ret1 88 | } 89 | 90 | // CheckIfPhoneNumberIsOptedOut indicates an expected call of CheckIfPhoneNumberIsOptedOut 91 | func (mr *MockSNSAPIMockRecorder) CheckIfPhoneNumberIsOptedOut(arg0 interface{}) *gomock.Call { 92 | return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "CheckIfPhoneNumberIsOptedOut", reflect.TypeOf((*MockSNSAPI)(nil).CheckIfPhoneNumberIsOptedOut), arg0) 93 | } 94 | 95 | // CheckIfPhoneNumberIsOptedOutWithContext mocks base method 96 | func (m *MockSNSAPI) CheckIfPhoneNumberIsOptedOutWithContext(arg0 aws.Context, arg1 *sns.CheckIfPhoneNumberIsOptedOutInput, arg2 ...request.Option) (*sns.CheckIfPhoneNumberIsOptedOutOutput, error) { 97 | varargs := []interface{}{arg0, arg1} 98 | for _, a := range arg2 { 99 | varargs = append(varargs, a) 100 | } 101 | ret := m.ctrl.Call(m, "CheckIfPhoneNumberIsOptedOutWithContext", varargs...) 102 | ret0, _ := ret[0].(*sns.CheckIfPhoneNumberIsOptedOutOutput) 103 | ret1, _ := ret[1].(error) 104 | return ret0, ret1 105 | } 106 | 107 | // CheckIfPhoneNumberIsOptedOutWithContext indicates an expected call of CheckIfPhoneNumberIsOptedOutWithContext 108 | func (mr *MockSNSAPIMockRecorder) CheckIfPhoneNumberIsOptedOutWithContext(arg0, arg1 interface{}, arg2 ...interface{}) *gomock.Call { 109 | varargs := append([]interface{}{arg0, arg1}, arg2...) 110 | return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "CheckIfPhoneNumberIsOptedOutWithContext", reflect.TypeOf((*MockSNSAPI)(nil).CheckIfPhoneNumberIsOptedOutWithContext), varargs...) 111 | } 112 | 113 | // CheckIfPhoneNumberIsOptedOutRequest mocks base method 114 | func (m *MockSNSAPI) CheckIfPhoneNumberIsOptedOutRequest(arg0 *sns.CheckIfPhoneNumberIsOptedOutInput) (*request.Request, *sns.CheckIfPhoneNumberIsOptedOutOutput) { 115 | ret := m.ctrl.Call(m, "CheckIfPhoneNumberIsOptedOutRequest", arg0) 116 | ret0, _ := ret[0].(*request.Request) 117 | ret1, _ := ret[1].(*sns.CheckIfPhoneNumberIsOptedOutOutput) 118 | return ret0, ret1 119 | } 120 | 121 | // CheckIfPhoneNumberIsOptedOutRequest indicates an expected call of CheckIfPhoneNumberIsOptedOutRequest 122 | func (mr *MockSNSAPIMockRecorder) CheckIfPhoneNumberIsOptedOutRequest(arg0 interface{}) *gomock.Call { 123 | return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "CheckIfPhoneNumberIsOptedOutRequest", reflect.TypeOf((*MockSNSAPI)(nil).CheckIfPhoneNumberIsOptedOutRequest), arg0) 124 | } 125 | 126 | // ConfirmSubscription mocks base method 127 | func (m *MockSNSAPI) ConfirmSubscription(arg0 *sns.ConfirmSubscriptionInput) (*sns.ConfirmSubscriptionOutput, error) { 128 | ret := m.ctrl.Call(m, "ConfirmSubscription", arg0) 129 | ret0, _ := ret[0].(*sns.ConfirmSubscriptionOutput) 130 | ret1, _ := ret[1].(error) 131 | return ret0, ret1 132 | } 133 | 134 | // ConfirmSubscription indicates an expected call of ConfirmSubscription 135 | func (mr *MockSNSAPIMockRecorder) ConfirmSubscription(arg0 interface{}) *gomock.Call { 136 | return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ConfirmSubscription", reflect.TypeOf((*MockSNSAPI)(nil).ConfirmSubscription), arg0) 137 | } 138 | 139 | // ConfirmSubscriptionWithContext mocks base method 140 | func (m *MockSNSAPI) ConfirmSubscriptionWithContext(arg0 aws.Context, arg1 *sns.ConfirmSubscriptionInput, arg2 ...request.Option) (*sns.ConfirmSubscriptionOutput, error) { 141 | varargs := []interface{}{arg0, arg1} 142 | for _, a := range arg2 { 143 | varargs = append(varargs, a) 144 | } 145 | ret := m.ctrl.Call(m, "ConfirmSubscriptionWithContext", varargs...) 146 | ret0, _ := ret[0].(*sns.ConfirmSubscriptionOutput) 147 | ret1, _ := ret[1].(error) 148 | return ret0, ret1 149 | } 150 | 151 | // ConfirmSubscriptionWithContext indicates an expected call of ConfirmSubscriptionWithContext 152 | func (mr *MockSNSAPIMockRecorder) ConfirmSubscriptionWithContext(arg0, arg1 interface{}, arg2 ...interface{}) *gomock.Call { 153 | varargs := append([]interface{}{arg0, arg1}, arg2...) 154 | return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ConfirmSubscriptionWithContext", reflect.TypeOf((*MockSNSAPI)(nil).ConfirmSubscriptionWithContext), varargs...) 155 | } 156 | 157 | // ConfirmSubscriptionRequest mocks base method 158 | func (m *MockSNSAPI) ConfirmSubscriptionRequest(arg0 *sns.ConfirmSubscriptionInput) (*request.Request, *sns.ConfirmSubscriptionOutput) { 159 | ret := m.ctrl.Call(m, "ConfirmSubscriptionRequest", arg0) 160 | ret0, _ := ret[0].(*request.Request) 161 | ret1, _ := ret[1].(*sns.ConfirmSubscriptionOutput) 162 | return ret0, ret1 163 | } 164 | 165 | // ConfirmSubscriptionRequest indicates an expected call of ConfirmSubscriptionRequest 166 | func (mr *MockSNSAPIMockRecorder) ConfirmSubscriptionRequest(arg0 interface{}) *gomock.Call { 167 | return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ConfirmSubscriptionRequest", reflect.TypeOf((*MockSNSAPI)(nil).ConfirmSubscriptionRequest), arg0) 168 | } 169 | 170 | // CreatePlatformApplication mocks base method 171 | func (m *MockSNSAPI) CreatePlatformApplication(arg0 *sns.CreatePlatformApplicationInput) (*sns.CreatePlatformApplicationOutput, error) { 172 | ret := m.ctrl.Call(m, "CreatePlatformApplication", arg0) 173 | ret0, _ := ret[0].(*sns.CreatePlatformApplicationOutput) 174 | ret1, _ := ret[1].(error) 175 | return ret0, ret1 176 | } 177 | 178 | // CreatePlatformApplication indicates an expected call of CreatePlatformApplication 179 | func (mr *MockSNSAPIMockRecorder) CreatePlatformApplication(arg0 interface{}) *gomock.Call { 180 | return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "CreatePlatformApplication", reflect.TypeOf((*MockSNSAPI)(nil).CreatePlatformApplication), arg0) 181 | } 182 | 183 | // CreatePlatformApplicationWithContext mocks base method 184 | func (m *MockSNSAPI) CreatePlatformApplicationWithContext(arg0 aws.Context, arg1 *sns.CreatePlatformApplicationInput, arg2 ...request.Option) (*sns.CreatePlatformApplicationOutput, error) { 185 | varargs := []interface{}{arg0, arg1} 186 | for _, a := range arg2 { 187 | varargs = append(varargs, a) 188 | } 189 | ret := m.ctrl.Call(m, "CreatePlatformApplicationWithContext", varargs...) 190 | ret0, _ := ret[0].(*sns.CreatePlatformApplicationOutput) 191 | ret1, _ := ret[1].(error) 192 | return ret0, ret1 193 | } 194 | 195 | // CreatePlatformApplicationWithContext indicates an expected call of CreatePlatformApplicationWithContext 196 | func (mr *MockSNSAPIMockRecorder) CreatePlatformApplicationWithContext(arg0, arg1 interface{}, arg2 ...interface{}) *gomock.Call { 197 | varargs := append([]interface{}{arg0, arg1}, arg2...) 198 | return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "CreatePlatformApplicationWithContext", reflect.TypeOf((*MockSNSAPI)(nil).CreatePlatformApplicationWithContext), varargs...) 199 | } 200 | 201 | // CreatePlatformApplicationRequest mocks base method 202 | func (m *MockSNSAPI) CreatePlatformApplicationRequest(arg0 *sns.CreatePlatformApplicationInput) (*request.Request, *sns.CreatePlatformApplicationOutput) { 203 | ret := m.ctrl.Call(m, "CreatePlatformApplicationRequest", arg0) 204 | ret0, _ := ret[0].(*request.Request) 205 | ret1, _ := ret[1].(*sns.CreatePlatformApplicationOutput) 206 | return ret0, ret1 207 | } 208 | 209 | // CreatePlatformApplicationRequest indicates an expected call of CreatePlatformApplicationRequest 210 | func (mr *MockSNSAPIMockRecorder) CreatePlatformApplicationRequest(arg0 interface{}) *gomock.Call { 211 | return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "CreatePlatformApplicationRequest", reflect.TypeOf((*MockSNSAPI)(nil).CreatePlatformApplicationRequest), arg0) 212 | } 213 | 214 | // CreatePlatformEndpoint mocks base method 215 | func (m *MockSNSAPI) CreatePlatformEndpoint(arg0 *sns.CreatePlatformEndpointInput) (*sns.CreatePlatformEndpointOutput, error) { 216 | ret := m.ctrl.Call(m, "CreatePlatformEndpoint", arg0) 217 | ret0, _ := ret[0].(*sns.CreatePlatformEndpointOutput) 218 | ret1, _ := ret[1].(error) 219 | return ret0, ret1 220 | } 221 | 222 | // CreatePlatformEndpoint indicates an expected call of CreatePlatformEndpoint 223 | func (mr *MockSNSAPIMockRecorder) CreatePlatformEndpoint(arg0 interface{}) *gomock.Call { 224 | return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "CreatePlatformEndpoint", reflect.TypeOf((*MockSNSAPI)(nil).CreatePlatformEndpoint), arg0) 225 | } 226 | 227 | // CreatePlatformEndpointWithContext mocks base method 228 | func (m *MockSNSAPI) CreatePlatformEndpointWithContext(arg0 aws.Context, arg1 *sns.CreatePlatformEndpointInput, arg2 ...request.Option) (*sns.CreatePlatformEndpointOutput, error) { 229 | varargs := []interface{}{arg0, arg1} 230 | for _, a := range arg2 { 231 | varargs = append(varargs, a) 232 | } 233 | ret := m.ctrl.Call(m, "CreatePlatformEndpointWithContext", varargs...) 234 | ret0, _ := ret[0].(*sns.CreatePlatformEndpointOutput) 235 | ret1, _ := ret[1].(error) 236 | return ret0, ret1 237 | } 238 | 239 | // CreatePlatformEndpointWithContext indicates an expected call of CreatePlatformEndpointWithContext 240 | func (mr *MockSNSAPIMockRecorder) CreatePlatformEndpointWithContext(arg0, arg1 interface{}, arg2 ...interface{}) *gomock.Call { 241 | varargs := append([]interface{}{arg0, arg1}, arg2...) 242 | return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "CreatePlatformEndpointWithContext", reflect.TypeOf((*MockSNSAPI)(nil).CreatePlatformEndpointWithContext), varargs...) 243 | } 244 | 245 | // CreatePlatformEndpointRequest mocks base method 246 | func (m *MockSNSAPI) CreatePlatformEndpointRequest(arg0 *sns.CreatePlatformEndpointInput) (*request.Request, *sns.CreatePlatformEndpointOutput) { 247 | ret := m.ctrl.Call(m, "CreatePlatformEndpointRequest", arg0) 248 | ret0, _ := ret[0].(*request.Request) 249 | ret1, _ := ret[1].(*sns.CreatePlatformEndpointOutput) 250 | return ret0, ret1 251 | } 252 | 253 | // CreatePlatformEndpointRequest indicates an expected call of CreatePlatformEndpointRequest 254 | func (mr *MockSNSAPIMockRecorder) CreatePlatformEndpointRequest(arg0 interface{}) *gomock.Call { 255 | return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "CreatePlatformEndpointRequest", reflect.TypeOf((*MockSNSAPI)(nil).CreatePlatformEndpointRequest), arg0) 256 | } 257 | 258 | // CreateTopic mocks base method 259 | func (m *MockSNSAPI) CreateTopic(arg0 *sns.CreateTopicInput) (*sns.CreateTopicOutput, error) { 260 | ret := m.ctrl.Call(m, "CreateTopic", arg0) 261 | ret0, _ := ret[0].(*sns.CreateTopicOutput) 262 | ret1, _ := ret[1].(error) 263 | return ret0, ret1 264 | } 265 | 266 | // CreateTopic indicates an expected call of CreateTopic 267 | func (mr *MockSNSAPIMockRecorder) CreateTopic(arg0 interface{}) *gomock.Call { 268 | return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "CreateTopic", reflect.TypeOf((*MockSNSAPI)(nil).CreateTopic), arg0) 269 | } 270 | 271 | // CreateTopicWithContext mocks base method 272 | func (m *MockSNSAPI) CreateTopicWithContext(arg0 aws.Context, arg1 *sns.CreateTopicInput, arg2 ...request.Option) (*sns.CreateTopicOutput, error) { 273 | varargs := []interface{}{arg0, arg1} 274 | for _, a := range arg2 { 275 | varargs = append(varargs, a) 276 | } 277 | ret := m.ctrl.Call(m, "CreateTopicWithContext", varargs...) 278 | ret0, _ := ret[0].(*sns.CreateTopicOutput) 279 | ret1, _ := ret[1].(error) 280 | return ret0, ret1 281 | } 282 | 283 | // CreateTopicWithContext indicates an expected call of CreateTopicWithContext 284 | func (mr *MockSNSAPIMockRecorder) CreateTopicWithContext(arg0, arg1 interface{}, arg2 ...interface{}) *gomock.Call { 285 | varargs := append([]interface{}{arg0, arg1}, arg2...) 286 | return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "CreateTopicWithContext", reflect.TypeOf((*MockSNSAPI)(nil).CreateTopicWithContext), varargs...) 287 | } 288 | 289 | // CreateTopicRequest mocks base method 290 | func (m *MockSNSAPI) CreateTopicRequest(arg0 *sns.CreateTopicInput) (*request.Request, *sns.CreateTopicOutput) { 291 | ret := m.ctrl.Call(m, "CreateTopicRequest", arg0) 292 | ret0, _ := ret[0].(*request.Request) 293 | ret1, _ := ret[1].(*sns.CreateTopicOutput) 294 | return ret0, ret1 295 | } 296 | 297 | // CreateTopicRequest indicates an expected call of CreateTopicRequest 298 | func (mr *MockSNSAPIMockRecorder) CreateTopicRequest(arg0 interface{}) *gomock.Call { 299 | return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "CreateTopicRequest", reflect.TypeOf((*MockSNSAPI)(nil).CreateTopicRequest), arg0) 300 | } 301 | 302 | // DeleteEndpoint mocks base method 303 | func (m *MockSNSAPI) DeleteEndpoint(arg0 *sns.DeleteEndpointInput) (*sns.DeleteEndpointOutput, error) { 304 | ret := m.ctrl.Call(m, "DeleteEndpoint", arg0) 305 | ret0, _ := ret[0].(*sns.DeleteEndpointOutput) 306 | ret1, _ := ret[1].(error) 307 | return ret0, ret1 308 | } 309 | 310 | // DeleteEndpoint indicates an expected call of DeleteEndpoint 311 | func (mr *MockSNSAPIMockRecorder) DeleteEndpoint(arg0 interface{}) *gomock.Call { 312 | return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DeleteEndpoint", reflect.TypeOf((*MockSNSAPI)(nil).DeleteEndpoint), arg0) 313 | } 314 | 315 | // DeleteEndpointWithContext mocks base method 316 | func (m *MockSNSAPI) DeleteEndpointWithContext(arg0 aws.Context, arg1 *sns.DeleteEndpointInput, arg2 ...request.Option) (*sns.DeleteEndpointOutput, error) { 317 | varargs := []interface{}{arg0, arg1} 318 | for _, a := range arg2 { 319 | varargs = append(varargs, a) 320 | } 321 | ret := m.ctrl.Call(m, "DeleteEndpointWithContext", varargs...) 322 | ret0, _ := ret[0].(*sns.DeleteEndpointOutput) 323 | ret1, _ := ret[1].(error) 324 | return ret0, ret1 325 | } 326 | 327 | // DeleteEndpointWithContext indicates an expected call of DeleteEndpointWithContext 328 | func (mr *MockSNSAPIMockRecorder) DeleteEndpointWithContext(arg0, arg1 interface{}, arg2 ...interface{}) *gomock.Call { 329 | varargs := append([]interface{}{arg0, arg1}, arg2...) 330 | return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DeleteEndpointWithContext", reflect.TypeOf((*MockSNSAPI)(nil).DeleteEndpointWithContext), varargs...) 331 | } 332 | 333 | // DeleteEndpointRequest mocks base method 334 | func (m *MockSNSAPI) DeleteEndpointRequest(arg0 *sns.DeleteEndpointInput) (*request.Request, *sns.DeleteEndpointOutput) { 335 | ret := m.ctrl.Call(m, "DeleteEndpointRequest", arg0) 336 | ret0, _ := ret[0].(*request.Request) 337 | ret1, _ := ret[1].(*sns.DeleteEndpointOutput) 338 | return ret0, ret1 339 | } 340 | 341 | // DeleteEndpointRequest indicates an expected call of DeleteEndpointRequest 342 | func (mr *MockSNSAPIMockRecorder) DeleteEndpointRequest(arg0 interface{}) *gomock.Call { 343 | return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DeleteEndpointRequest", reflect.TypeOf((*MockSNSAPI)(nil).DeleteEndpointRequest), arg0) 344 | } 345 | 346 | // DeletePlatformApplication mocks base method 347 | func (m *MockSNSAPI) DeletePlatformApplication(arg0 *sns.DeletePlatformApplicationInput) (*sns.DeletePlatformApplicationOutput, error) { 348 | ret := m.ctrl.Call(m, "DeletePlatformApplication", arg0) 349 | ret0, _ := ret[0].(*sns.DeletePlatformApplicationOutput) 350 | ret1, _ := ret[1].(error) 351 | return ret0, ret1 352 | } 353 | 354 | // DeletePlatformApplication indicates an expected call of DeletePlatformApplication 355 | func (mr *MockSNSAPIMockRecorder) DeletePlatformApplication(arg0 interface{}) *gomock.Call { 356 | return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DeletePlatformApplication", reflect.TypeOf((*MockSNSAPI)(nil).DeletePlatformApplication), arg0) 357 | } 358 | 359 | // DeletePlatformApplicationWithContext mocks base method 360 | func (m *MockSNSAPI) DeletePlatformApplicationWithContext(arg0 aws.Context, arg1 *sns.DeletePlatformApplicationInput, arg2 ...request.Option) (*sns.DeletePlatformApplicationOutput, error) { 361 | varargs := []interface{}{arg0, arg1} 362 | for _, a := range arg2 { 363 | varargs = append(varargs, a) 364 | } 365 | ret := m.ctrl.Call(m, "DeletePlatformApplicationWithContext", varargs...) 366 | ret0, _ := ret[0].(*sns.DeletePlatformApplicationOutput) 367 | ret1, _ := ret[1].(error) 368 | return ret0, ret1 369 | } 370 | 371 | // DeletePlatformApplicationWithContext indicates an expected call of DeletePlatformApplicationWithContext 372 | func (mr *MockSNSAPIMockRecorder) DeletePlatformApplicationWithContext(arg0, arg1 interface{}, arg2 ...interface{}) *gomock.Call { 373 | varargs := append([]interface{}{arg0, arg1}, arg2...) 374 | return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DeletePlatformApplicationWithContext", reflect.TypeOf((*MockSNSAPI)(nil).DeletePlatformApplicationWithContext), varargs...) 375 | } 376 | 377 | // DeletePlatformApplicationRequest mocks base method 378 | func (m *MockSNSAPI) DeletePlatformApplicationRequest(arg0 *sns.DeletePlatformApplicationInput) (*request.Request, *sns.DeletePlatformApplicationOutput) { 379 | ret := m.ctrl.Call(m, "DeletePlatformApplicationRequest", arg0) 380 | ret0, _ := ret[0].(*request.Request) 381 | ret1, _ := ret[1].(*sns.DeletePlatformApplicationOutput) 382 | return ret0, ret1 383 | } 384 | 385 | // DeletePlatformApplicationRequest indicates an expected call of DeletePlatformApplicationRequest 386 | func (mr *MockSNSAPIMockRecorder) DeletePlatformApplicationRequest(arg0 interface{}) *gomock.Call { 387 | return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DeletePlatformApplicationRequest", reflect.TypeOf((*MockSNSAPI)(nil).DeletePlatformApplicationRequest), arg0) 388 | } 389 | 390 | // DeleteTopic mocks base method 391 | func (m *MockSNSAPI) DeleteTopic(arg0 *sns.DeleteTopicInput) (*sns.DeleteTopicOutput, error) { 392 | ret := m.ctrl.Call(m, "DeleteTopic", arg0) 393 | ret0, _ := ret[0].(*sns.DeleteTopicOutput) 394 | ret1, _ := ret[1].(error) 395 | return ret0, ret1 396 | } 397 | 398 | // DeleteTopic indicates an expected call of DeleteTopic 399 | func (mr *MockSNSAPIMockRecorder) DeleteTopic(arg0 interface{}) *gomock.Call { 400 | return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DeleteTopic", reflect.TypeOf((*MockSNSAPI)(nil).DeleteTopic), arg0) 401 | } 402 | 403 | // DeleteTopicWithContext mocks base method 404 | func (m *MockSNSAPI) DeleteTopicWithContext(arg0 aws.Context, arg1 *sns.DeleteTopicInput, arg2 ...request.Option) (*sns.DeleteTopicOutput, error) { 405 | varargs := []interface{}{arg0, arg1} 406 | for _, a := range arg2 { 407 | varargs = append(varargs, a) 408 | } 409 | ret := m.ctrl.Call(m, "DeleteTopicWithContext", varargs...) 410 | ret0, _ := ret[0].(*sns.DeleteTopicOutput) 411 | ret1, _ := ret[1].(error) 412 | return ret0, ret1 413 | } 414 | 415 | // DeleteTopicWithContext indicates an expected call of DeleteTopicWithContext 416 | func (mr *MockSNSAPIMockRecorder) DeleteTopicWithContext(arg0, arg1 interface{}, arg2 ...interface{}) *gomock.Call { 417 | varargs := append([]interface{}{arg0, arg1}, arg2...) 418 | return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DeleteTopicWithContext", reflect.TypeOf((*MockSNSAPI)(nil).DeleteTopicWithContext), varargs...) 419 | } 420 | 421 | // DeleteTopicRequest mocks base method 422 | func (m *MockSNSAPI) DeleteTopicRequest(arg0 *sns.DeleteTopicInput) (*request.Request, *sns.DeleteTopicOutput) { 423 | ret := m.ctrl.Call(m, "DeleteTopicRequest", arg0) 424 | ret0, _ := ret[0].(*request.Request) 425 | ret1, _ := ret[1].(*sns.DeleteTopicOutput) 426 | return ret0, ret1 427 | } 428 | 429 | // DeleteTopicRequest indicates an expected call of DeleteTopicRequest 430 | func (mr *MockSNSAPIMockRecorder) DeleteTopicRequest(arg0 interface{}) *gomock.Call { 431 | return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DeleteTopicRequest", reflect.TypeOf((*MockSNSAPI)(nil).DeleteTopicRequest), arg0) 432 | } 433 | 434 | // GetEndpointAttributes mocks base method 435 | func (m *MockSNSAPI) GetEndpointAttributes(arg0 *sns.GetEndpointAttributesInput) (*sns.GetEndpointAttributesOutput, error) { 436 | ret := m.ctrl.Call(m, "GetEndpointAttributes", arg0) 437 | ret0, _ := ret[0].(*sns.GetEndpointAttributesOutput) 438 | ret1, _ := ret[1].(error) 439 | return ret0, ret1 440 | } 441 | 442 | // GetEndpointAttributes indicates an expected call of GetEndpointAttributes 443 | func (mr *MockSNSAPIMockRecorder) GetEndpointAttributes(arg0 interface{}) *gomock.Call { 444 | return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetEndpointAttributes", reflect.TypeOf((*MockSNSAPI)(nil).GetEndpointAttributes), arg0) 445 | } 446 | 447 | // GetEndpointAttributesWithContext mocks base method 448 | func (m *MockSNSAPI) GetEndpointAttributesWithContext(arg0 aws.Context, arg1 *sns.GetEndpointAttributesInput, arg2 ...request.Option) (*sns.GetEndpointAttributesOutput, error) { 449 | varargs := []interface{}{arg0, arg1} 450 | for _, a := range arg2 { 451 | varargs = append(varargs, a) 452 | } 453 | ret := m.ctrl.Call(m, "GetEndpointAttributesWithContext", varargs...) 454 | ret0, _ := ret[0].(*sns.GetEndpointAttributesOutput) 455 | ret1, _ := ret[1].(error) 456 | return ret0, ret1 457 | } 458 | 459 | // GetEndpointAttributesWithContext indicates an expected call of GetEndpointAttributesWithContext 460 | func (mr *MockSNSAPIMockRecorder) GetEndpointAttributesWithContext(arg0, arg1 interface{}, arg2 ...interface{}) *gomock.Call { 461 | varargs := append([]interface{}{arg0, arg1}, arg2...) 462 | return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetEndpointAttributesWithContext", reflect.TypeOf((*MockSNSAPI)(nil).GetEndpointAttributesWithContext), varargs...) 463 | } 464 | 465 | // GetEndpointAttributesRequest mocks base method 466 | func (m *MockSNSAPI) GetEndpointAttributesRequest(arg0 *sns.GetEndpointAttributesInput) (*request.Request, *sns.GetEndpointAttributesOutput) { 467 | ret := m.ctrl.Call(m, "GetEndpointAttributesRequest", arg0) 468 | ret0, _ := ret[0].(*request.Request) 469 | ret1, _ := ret[1].(*sns.GetEndpointAttributesOutput) 470 | return ret0, ret1 471 | } 472 | 473 | // GetEndpointAttributesRequest indicates an expected call of GetEndpointAttributesRequest 474 | func (mr *MockSNSAPIMockRecorder) GetEndpointAttributesRequest(arg0 interface{}) *gomock.Call { 475 | return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetEndpointAttributesRequest", reflect.TypeOf((*MockSNSAPI)(nil).GetEndpointAttributesRequest), arg0) 476 | } 477 | 478 | // GetPlatformApplicationAttributes mocks base method 479 | func (m *MockSNSAPI) GetPlatformApplicationAttributes(arg0 *sns.GetPlatformApplicationAttributesInput) (*sns.GetPlatformApplicationAttributesOutput, error) { 480 | ret := m.ctrl.Call(m, "GetPlatformApplicationAttributes", arg0) 481 | ret0, _ := ret[0].(*sns.GetPlatformApplicationAttributesOutput) 482 | ret1, _ := ret[1].(error) 483 | return ret0, ret1 484 | } 485 | 486 | // GetPlatformApplicationAttributes indicates an expected call of GetPlatformApplicationAttributes 487 | func (mr *MockSNSAPIMockRecorder) GetPlatformApplicationAttributes(arg0 interface{}) *gomock.Call { 488 | return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetPlatformApplicationAttributes", reflect.TypeOf((*MockSNSAPI)(nil).GetPlatformApplicationAttributes), arg0) 489 | } 490 | 491 | // GetPlatformApplicationAttributesWithContext mocks base method 492 | func (m *MockSNSAPI) GetPlatformApplicationAttributesWithContext(arg0 aws.Context, arg1 *sns.GetPlatformApplicationAttributesInput, arg2 ...request.Option) (*sns.GetPlatformApplicationAttributesOutput, error) { 493 | varargs := []interface{}{arg0, arg1} 494 | for _, a := range arg2 { 495 | varargs = append(varargs, a) 496 | } 497 | ret := m.ctrl.Call(m, "GetPlatformApplicationAttributesWithContext", varargs...) 498 | ret0, _ := ret[0].(*sns.GetPlatformApplicationAttributesOutput) 499 | ret1, _ := ret[1].(error) 500 | return ret0, ret1 501 | } 502 | 503 | // GetPlatformApplicationAttributesWithContext indicates an expected call of GetPlatformApplicationAttributesWithContext 504 | func (mr *MockSNSAPIMockRecorder) GetPlatformApplicationAttributesWithContext(arg0, arg1 interface{}, arg2 ...interface{}) *gomock.Call { 505 | varargs := append([]interface{}{arg0, arg1}, arg2...) 506 | return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetPlatformApplicationAttributesWithContext", reflect.TypeOf((*MockSNSAPI)(nil).GetPlatformApplicationAttributesWithContext), varargs...) 507 | } 508 | 509 | // GetPlatformApplicationAttributesRequest mocks base method 510 | func (m *MockSNSAPI) GetPlatformApplicationAttributesRequest(arg0 *sns.GetPlatformApplicationAttributesInput) (*request.Request, *sns.GetPlatformApplicationAttributesOutput) { 511 | ret := m.ctrl.Call(m, "GetPlatformApplicationAttributesRequest", arg0) 512 | ret0, _ := ret[0].(*request.Request) 513 | ret1, _ := ret[1].(*sns.GetPlatformApplicationAttributesOutput) 514 | return ret0, ret1 515 | } 516 | 517 | // GetPlatformApplicationAttributesRequest indicates an expected call of GetPlatformApplicationAttributesRequest 518 | func (mr *MockSNSAPIMockRecorder) GetPlatformApplicationAttributesRequest(arg0 interface{}) *gomock.Call { 519 | return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetPlatformApplicationAttributesRequest", reflect.TypeOf((*MockSNSAPI)(nil).GetPlatformApplicationAttributesRequest), arg0) 520 | } 521 | 522 | // GetSMSAttributes mocks base method 523 | func (m *MockSNSAPI) GetSMSAttributes(arg0 *sns.GetSMSAttributesInput) (*sns.GetSMSAttributesOutput, error) { 524 | ret := m.ctrl.Call(m, "GetSMSAttributes", arg0) 525 | ret0, _ := ret[0].(*sns.GetSMSAttributesOutput) 526 | ret1, _ := ret[1].(error) 527 | return ret0, ret1 528 | } 529 | 530 | // GetSMSAttributes indicates an expected call of GetSMSAttributes 531 | func (mr *MockSNSAPIMockRecorder) GetSMSAttributes(arg0 interface{}) *gomock.Call { 532 | return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetSMSAttributes", reflect.TypeOf((*MockSNSAPI)(nil).GetSMSAttributes), arg0) 533 | } 534 | 535 | // GetSMSAttributesWithContext mocks base method 536 | func (m *MockSNSAPI) GetSMSAttributesWithContext(arg0 aws.Context, arg1 *sns.GetSMSAttributesInput, arg2 ...request.Option) (*sns.GetSMSAttributesOutput, error) { 537 | varargs := []interface{}{arg0, arg1} 538 | for _, a := range arg2 { 539 | varargs = append(varargs, a) 540 | } 541 | ret := m.ctrl.Call(m, "GetSMSAttributesWithContext", varargs...) 542 | ret0, _ := ret[0].(*sns.GetSMSAttributesOutput) 543 | ret1, _ := ret[1].(error) 544 | return ret0, ret1 545 | } 546 | 547 | // GetSMSAttributesWithContext indicates an expected call of GetSMSAttributesWithContext 548 | func (mr *MockSNSAPIMockRecorder) GetSMSAttributesWithContext(arg0, arg1 interface{}, arg2 ...interface{}) *gomock.Call { 549 | varargs := append([]interface{}{arg0, arg1}, arg2...) 550 | return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetSMSAttributesWithContext", reflect.TypeOf((*MockSNSAPI)(nil).GetSMSAttributesWithContext), varargs...) 551 | } 552 | 553 | // GetSMSAttributesRequest mocks base method 554 | func (m *MockSNSAPI) GetSMSAttributesRequest(arg0 *sns.GetSMSAttributesInput) (*request.Request, *sns.GetSMSAttributesOutput) { 555 | ret := m.ctrl.Call(m, "GetSMSAttributesRequest", arg0) 556 | ret0, _ := ret[0].(*request.Request) 557 | ret1, _ := ret[1].(*sns.GetSMSAttributesOutput) 558 | return ret0, ret1 559 | } 560 | 561 | // GetSMSAttributesRequest indicates an expected call of GetSMSAttributesRequest 562 | func (mr *MockSNSAPIMockRecorder) GetSMSAttributesRequest(arg0 interface{}) *gomock.Call { 563 | return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetSMSAttributesRequest", reflect.TypeOf((*MockSNSAPI)(nil).GetSMSAttributesRequest), arg0) 564 | } 565 | 566 | // GetSubscriptionAttributes mocks base method 567 | func (m *MockSNSAPI) GetSubscriptionAttributes(arg0 *sns.GetSubscriptionAttributesInput) (*sns.GetSubscriptionAttributesOutput, error) { 568 | ret := m.ctrl.Call(m, "GetSubscriptionAttributes", arg0) 569 | ret0, _ := ret[0].(*sns.GetSubscriptionAttributesOutput) 570 | ret1, _ := ret[1].(error) 571 | return ret0, ret1 572 | } 573 | 574 | // GetSubscriptionAttributes indicates an expected call of GetSubscriptionAttributes 575 | func (mr *MockSNSAPIMockRecorder) GetSubscriptionAttributes(arg0 interface{}) *gomock.Call { 576 | return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetSubscriptionAttributes", reflect.TypeOf((*MockSNSAPI)(nil).GetSubscriptionAttributes), arg0) 577 | } 578 | 579 | // GetSubscriptionAttributesWithContext mocks base method 580 | func (m *MockSNSAPI) GetSubscriptionAttributesWithContext(arg0 aws.Context, arg1 *sns.GetSubscriptionAttributesInput, arg2 ...request.Option) (*sns.GetSubscriptionAttributesOutput, error) { 581 | varargs := []interface{}{arg0, arg1} 582 | for _, a := range arg2 { 583 | varargs = append(varargs, a) 584 | } 585 | ret := m.ctrl.Call(m, "GetSubscriptionAttributesWithContext", varargs...) 586 | ret0, _ := ret[0].(*sns.GetSubscriptionAttributesOutput) 587 | ret1, _ := ret[1].(error) 588 | return ret0, ret1 589 | } 590 | 591 | // GetSubscriptionAttributesWithContext indicates an expected call of GetSubscriptionAttributesWithContext 592 | func (mr *MockSNSAPIMockRecorder) GetSubscriptionAttributesWithContext(arg0, arg1 interface{}, arg2 ...interface{}) *gomock.Call { 593 | varargs := append([]interface{}{arg0, arg1}, arg2...) 594 | return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetSubscriptionAttributesWithContext", reflect.TypeOf((*MockSNSAPI)(nil).GetSubscriptionAttributesWithContext), varargs...) 595 | } 596 | 597 | // GetSubscriptionAttributesRequest mocks base method 598 | func (m *MockSNSAPI) GetSubscriptionAttributesRequest(arg0 *sns.GetSubscriptionAttributesInput) (*request.Request, *sns.GetSubscriptionAttributesOutput) { 599 | ret := m.ctrl.Call(m, "GetSubscriptionAttributesRequest", arg0) 600 | ret0, _ := ret[0].(*request.Request) 601 | ret1, _ := ret[1].(*sns.GetSubscriptionAttributesOutput) 602 | return ret0, ret1 603 | } 604 | 605 | // GetSubscriptionAttributesRequest indicates an expected call of GetSubscriptionAttributesRequest 606 | func (mr *MockSNSAPIMockRecorder) GetSubscriptionAttributesRequest(arg0 interface{}) *gomock.Call { 607 | return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetSubscriptionAttributesRequest", reflect.TypeOf((*MockSNSAPI)(nil).GetSubscriptionAttributesRequest), arg0) 608 | } 609 | 610 | // GetTopicAttributes mocks base method 611 | func (m *MockSNSAPI) GetTopicAttributes(arg0 *sns.GetTopicAttributesInput) (*sns.GetTopicAttributesOutput, error) { 612 | ret := m.ctrl.Call(m, "GetTopicAttributes", arg0) 613 | ret0, _ := ret[0].(*sns.GetTopicAttributesOutput) 614 | ret1, _ := ret[1].(error) 615 | return ret0, ret1 616 | } 617 | 618 | // GetTopicAttributes indicates an expected call of GetTopicAttributes 619 | func (mr *MockSNSAPIMockRecorder) GetTopicAttributes(arg0 interface{}) *gomock.Call { 620 | return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetTopicAttributes", reflect.TypeOf((*MockSNSAPI)(nil).GetTopicAttributes), arg0) 621 | } 622 | 623 | // GetTopicAttributesWithContext mocks base method 624 | func (m *MockSNSAPI) GetTopicAttributesWithContext(arg0 aws.Context, arg1 *sns.GetTopicAttributesInput, arg2 ...request.Option) (*sns.GetTopicAttributesOutput, error) { 625 | varargs := []interface{}{arg0, arg1} 626 | for _, a := range arg2 { 627 | varargs = append(varargs, a) 628 | } 629 | ret := m.ctrl.Call(m, "GetTopicAttributesWithContext", varargs...) 630 | ret0, _ := ret[0].(*sns.GetTopicAttributesOutput) 631 | ret1, _ := ret[1].(error) 632 | return ret0, ret1 633 | } 634 | 635 | // GetTopicAttributesWithContext indicates an expected call of GetTopicAttributesWithContext 636 | func (mr *MockSNSAPIMockRecorder) GetTopicAttributesWithContext(arg0, arg1 interface{}, arg2 ...interface{}) *gomock.Call { 637 | varargs := append([]interface{}{arg0, arg1}, arg2...) 638 | return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetTopicAttributesWithContext", reflect.TypeOf((*MockSNSAPI)(nil).GetTopicAttributesWithContext), varargs...) 639 | } 640 | 641 | // GetTopicAttributesRequest mocks base method 642 | func (m *MockSNSAPI) GetTopicAttributesRequest(arg0 *sns.GetTopicAttributesInput) (*request.Request, *sns.GetTopicAttributesOutput) { 643 | ret := m.ctrl.Call(m, "GetTopicAttributesRequest", arg0) 644 | ret0, _ := ret[0].(*request.Request) 645 | ret1, _ := ret[1].(*sns.GetTopicAttributesOutput) 646 | return ret0, ret1 647 | } 648 | 649 | // GetTopicAttributesRequest indicates an expected call of GetTopicAttributesRequest 650 | func (mr *MockSNSAPIMockRecorder) GetTopicAttributesRequest(arg0 interface{}) *gomock.Call { 651 | return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetTopicAttributesRequest", reflect.TypeOf((*MockSNSAPI)(nil).GetTopicAttributesRequest), arg0) 652 | } 653 | 654 | // ListEndpointsByPlatformApplication mocks base method 655 | func (m *MockSNSAPI) ListEndpointsByPlatformApplication(arg0 *sns.ListEndpointsByPlatformApplicationInput) (*sns.ListEndpointsByPlatformApplicationOutput, error) { 656 | ret := m.ctrl.Call(m, "ListEndpointsByPlatformApplication", arg0) 657 | ret0, _ := ret[0].(*sns.ListEndpointsByPlatformApplicationOutput) 658 | ret1, _ := ret[1].(error) 659 | return ret0, ret1 660 | } 661 | 662 | // ListEndpointsByPlatformApplication indicates an expected call of ListEndpointsByPlatformApplication 663 | func (mr *MockSNSAPIMockRecorder) ListEndpointsByPlatformApplication(arg0 interface{}) *gomock.Call { 664 | return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ListEndpointsByPlatformApplication", reflect.TypeOf((*MockSNSAPI)(nil).ListEndpointsByPlatformApplication), arg0) 665 | } 666 | 667 | // ListEndpointsByPlatformApplicationWithContext mocks base method 668 | func (m *MockSNSAPI) ListEndpointsByPlatformApplicationWithContext(arg0 aws.Context, arg1 *sns.ListEndpointsByPlatformApplicationInput, arg2 ...request.Option) (*sns.ListEndpointsByPlatformApplicationOutput, error) { 669 | varargs := []interface{}{arg0, arg1} 670 | for _, a := range arg2 { 671 | varargs = append(varargs, a) 672 | } 673 | ret := m.ctrl.Call(m, "ListEndpointsByPlatformApplicationWithContext", varargs...) 674 | ret0, _ := ret[0].(*sns.ListEndpointsByPlatformApplicationOutput) 675 | ret1, _ := ret[1].(error) 676 | return ret0, ret1 677 | } 678 | 679 | // ListEndpointsByPlatformApplicationWithContext indicates an expected call of ListEndpointsByPlatformApplicationWithContext 680 | func (mr *MockSNSAPIMockRecorder) ListEndpointsByPlatformApplicationWithContext(arg0, arg1 interface{}, arg2 ...interface{}) *gomock.Call { 681 | varargs := append([]interface{}{arg0, arg1}, arg2...) 682 | return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ListEndpointsByPlatformApplicationWithContext", reflect.TypeOf((*MockSNSAPI)(nil).ListEndpointsByPlatformApplicationWithContext), varargs...) 683 | } 684 | 685 | // ListEndpointsByPlatformApplicationRequest mocks base method 686 | func (m *MockSNSAPI) ListEndpointsByPlatformApplicationRequest(arg0 *sns.ListEndpointsByPlatformApplicationInput) (*request.Request, *sns.ListEndpointsByPlatformApplicationOutput) { 687 | ret := m.ctrl.Call(m, "ListEndpointsByPlatformApplicationRequest", arg0) 688 | ret0, _ := ret[0].(*request.Request) 689 | ret1, _ := ret[1].(*sns.ListEndpointsByPlatformApplicationOutput) 690 | return ret0, ret1 691 | } 692 | 693 | // ListEndpointsByPlatformApplicationRequest indicates an expected call of ListEndpointsByPlatformApplicationRequest 694 | func (mr *MockSNSAPIMockRecorder) ListEndpointsByPlatformApplicationRequest(arg0 interface{}) *gomock.Call { 695 | return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ListEndpointsByPlatformApplicationRequest", reflect.TypeOf((*MockSNSAPI)(nil).ListEndpointsByPlatformApplicationRequest), arg0) 696 | } 697 | 698 | // ListEndpointsByPlatformApplicationPages mocks base method 699 | func (m *MockSNSAPI) ListEndpointsByPlatformApplicationPages(arg0 *sns.ListEndpointsByPlatformApplicationInput, arg1 func(*sns.ListEndpointsByPlatformApplicationOutput, bool) bool) error { 700 | ret := m.ctrl.Call(m, "ListEndpointsByPlatformApplicationPages", arg0, arg1) 701 | ret0, _ := ret[0].(error) 702 | return ret0 703 | } 704 | 705 | // ListEndpointsByPlatformApplicationPages indicates an expected call of ListEndpointsByPlatformApplicationPages 706 | func (mr *MockSNSAPIMockRecorder) ListEndpointsByPlatformApplicationPages(arg0, arg1 interface{}) *gomock.Call { 707 | return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ListEndpointsByPlatformApplicationPages", reflect.TypeOf((*MockSNSAPI)(nil).ListEndpointsByPlatformApplicationPages), arg0, arg1) 708 | } 709 | 710 | // ListEndpointsByPlatformApplicationPagesWithContext mocks base method 711 | func (m *MockSNSAPI) ListEndpointsByPlatformApplicationPagesWithContext(arg0 aws.Context, arg1 *sns.ListEndpointsByPlatformApplicationInput, arg2 func(*sns.ListEndpointsByPlatformApplicationOutput, bool) bool, arg3 ...request.Option) error { 712 | varargs := []interface{}{arg0, arg1, arg2} 713 | for _, a := range arg3 { 714 | varargs = append(varargs, a) 715 | } 716 | ret := m.ctrl.Call(m, "ListEndpointsByPlatformApplicationPagesWithContext", varargs...) 717 | ret0, _ := ret[0].(error) 718 | return ret0 719 | } 720 | 721 | // ListEndpointsByPlatformApplicationPagesWithContext indicates an expected call of ListEndpointsByPlatformApplicationPagesWithContext 722 | func (mr *MockSNSAPIMockRecorder) ListEndpointsByPlatformApplicationPagesWithContext(arg0, arg1, arg2 interface{}, arg3 ...interface{}) *gomock.Call { 723 | varargs := append([]interface{}{arg0, arg1, arg2}, arg3...) 724 | return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ListEndpointsByPlatformApplicationPagesWithContext", reflect.TypeOf((*MockSNSAPI)(nil).ListEndpointsByPlatformApplicationPagesWithContext), varargs...) 725 | } 726 | 727 | // ListPhoneNumbersOptedOut mocks base method 728 | func (m *MockSNSAPI) ListPhoneNumbersOptedOut(arg0 *sns.ListPhoneNumbersOptedOutInput) (*sns.ListPhoneNumbersOptedOutOutput, error) { 729 | ret := m.ctrl.Call(m, "ListPhoneNumbersOptedOut", arg0) 730 | ret0, _ := ret[0].(*sns.ListPhoneNumbersOptedOutOutput) 731 | ret1, _ := ret[1].(error) 732 | return ret0, ret1 733 | } 734 | 735 | // ListPhoneNumbersOptedOut indicates an expected call of ListPhoneNumbersOptedOut 736 | func (mr *MockSNSAPIMockRecorder) ListPhoneNumbersOptedOut(arg0 interface{}) *gomock.Call { 737 | return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ListPhoneNumbersOptedOut", reflect.TypeOf((*MockSNSAPI)(nil).ListPhoneNumbersOptedOut), arg0) 738 | } 739 | 740 | // ListPhoneNumbersOptedOutWithContext mocks base method 741 | func (m *MockSNSAPI) ListPhoneNumbersOptedOutWithContext(arg0 aws.Context, arg1 *sns.ListPhoneNumbersOptedOutInput, arg2 ...request.Option) (*sns.ListPhoneNumbersOptedOutOutput, error) { 742 | varargs := []interface{}{arg0, arg1} 743 | for _, a := range arg2 { 744 | varargs = append(varargs, a) 745 | } 746 | ret := m.ctrl.Call(m, "ListPhoneNumbersOptedOutWithContext", varargs...) 747 | ret0, _ := ret[0].(*sns.ListPhoneNumbersOptedOutOutput) 748 | ret1, _ := ret[1].(error) 749 | return ret0, ret1 750 | } 751 | 752 | // ListPhoneNumbersOptedOutWithContext indicates an expected call of ListPhoneNumbersOptedOutWithContext 753 | func (mr *MockSNSAPIMockRecorder) ListPhoneNumbersOptedOutWithContext(arg0, arg1 interface{}, arg2 ...interface{}) *gomock.Call { 754 | varargs := append([]interface{}{arg0, arg1}, arg2...) 755 | return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ListPhoneNumbersOptedOutWithContext", reflect.TypeOf((*MockSNSAPI)(nil).ListPhoneNumbersOptedOutWithContext), varargs...) 756 | } 757 | 758 | // ListPhoneNumbersOptedOutRequest mocks base method 759 | func (m *MockSNSAPI) ListPhoneNumbersOptedOutRequest(arg0 *sns.ListPhoneNumbersOptedOutInput) (*request.Request, *sns.ListPhoneNumbersOptedOutOutput) { 760 | ret := m.ctrl.Call(m, "ListPhoneNumbersOptedOutRequest", arg0) 761 | ret0, _ := ret[0].(*request.Request) 762 | ret1, _ := ret[1].(*sns.ListPhoneNumbersOptedOutOutput) 763 | return ret0, ret1 764 | } 765 | 766 | // ListPhoneNumbersOptedOutRequest indicates an expected call of ListPhoneNumbersOptedOutRequest 767 | func (mr *MockSNSAPIMockRecorder) ListPhoneNumbersOptedOutRequest(arg0 interface{}) *gomock.Call { 768 | return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ListPhoneNumbersOptedOutRequest", reflect.TypeOf((*MockSNSAPI)(nil).ListPhoneNumbersOptedOutRequest), arg0) 769 | } 770 | 771 | // ListPlatformApplications mocks base method 772 | func (m *MockSNSAPI) ListPlatformApplications(arg0 *sns.ListPlatformApplicationsInput) (*sns.ListPlatformApplicationsOutput, error) { 773 | ret := m.ctrl.Call(m, "ListPlatformApplications", arg0) 774 | ret0, _ := ret[0].(*sns.ListPlatformApplicationsOutput) 775 | ret1, _ := ret[1].(error) 776 | return ret0, ret1 777 | } 778 | 779 | // ListPlatformApplications indicates an expected call of ListPlatformApplications 780 | func (mr *MockSNSAPIMockRecorder) ListPlatformApplications(arg0 interface{}) *gomock.Call { 781 | return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ListPlatformApplications", reflect.TypeOf((*MockSNSAPI)(nil).ListPlatformApplications), arg0) 782 | } 783 | 784 | // ListPlatformApplicationsWithContext mocks base method 785 | func (m *MockSNSAPI) ListPlatformApplicationsWithContext(arg0 aws.Context, arg1 *sns.ListPlatformApplicationsInput, arg2 ...request.Option) (*sns.ListPlatformApplicationsOutput, error) { 786 | varargs := []interface{}{arg0, arg1} 787 | for _, a := range arg2 { 788 | varargs = append(varargs, a) 789 | } 790 | ret := m.ctrl.Call(m, "ListPlatformApplicationsWithContext", varargs...) 791 | ret0, _ := ret[0].(*sns.ListPlatformApplicationsOutput) 792 | ret1, _ := ret[1].(error) 793 | return ret0, ret1 794 | } 795 | 796 | // ListPlatformApplicationsWithContext indicates an expected call of ListPlatformApplicationsWithContext 797 | func (mr *MockSNSAPIMockRecorder) ListPlatformApplicationsWithContext(arg0, arg1 interface{}, arg2 ...interface{}) *gomock.Call { 798 | varargs := append([]interface{}{arg0, arg1}, arg2...) 799 | return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ListPlatformApplicationsWithContext", reflect.TypeOf((*MockSNSAPI)(nil).ListPlatformApplicationsWithContext), varargs...) 800 | } 801 | 802 | // ListPlatformApplicationsRequest mocks base method 803 | func (m *MockSNSAPI) ListPlatformApplicationsRequest(arg0 *sns.ListPlatformApplicationsInput) (*request.Request, *sns.ListPlatformApplicationsOutput) { 804 | ret := m.ctrl.Call(m, "ListPlatformApplicationsRequest", arg0) 805 | ret0, _ := ret[0].(*request.Request) 806 | ret1, _ := ret[1].(*sns.ListPlatformApplicationsOutput) 807 | return ret0, ret1 808 | } 809 | 810 | // ListPlatformApplicationsRequest indicates an expected call of ListPlatformApplicationsRequest 811 | func (mr *MockSNSAPIMockRecorder) ListPlatformApplicationsRequest(arg0 interface{}) *gomock.Call { 812 | return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ListPlatformApplicationsRequest", reflect.TypeOf((*MockSNSAPI)(nil).ListPlatformApplicationsRequest), arg0) 813 | } 814 | 815 | // ListPlatformApplicationsPages mocks base method 816 | func (m *MockSNSAPI) ListPlatformApplicationsPages(arg0 *sns.ListPlatformApplicationsInput, arg1 func(*sns.ListPlatformApplicationsOutput, bool) bool) error { 817 | ret := m.ctrl.Call(m, "ListPlatformApplicationsPages", arg0, arg1) 818 | ret0, _ := ret[0].(error) 819 | return ret0 820 | } 821 | 822 | // ListPlatformApplicationsPages indicates an expected call of ListPlatformApplicationsPages 823 | func (mr *MockSNSAPIMockRecorder) ListPlatformApplicationsPages(arg0, arg1 interface{}) *gomock.Call { 824 | return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ListPlatformApplicationsPages", reflect.TypeOf((*MockSNSAPI)(nil).ListPlatformApplicationsPages), arg0, arg1) 825 | } 826 | 827 | // ListPlatformApplicationsPagesWithContext mocks base method 828 | func (m *MockSNSAPI) ListPlatformApplicationsPagesWithContext(arg0 aws.Context, arg1 *sns.ListPlatformApplicationsInput, arg2 func(*sns.ListPlatformApplicationsOutput, bool) bool, arg3 ...request.Option) error { 829 | varargs := []interface{}{arg0, arg1, arg2} 830 | for _, a := range arg3 { 831 | varargs = append(varargs, a) 832 | } 833 | ret := m.ctrl.Call(m, "ListPlatformApplicationsPagesWithContext", varargs...) 834 | ret0, _ := ret[0].(error) 835 | return ret0 836 | } 837 | 838 | // ListPlatformApplicationsPagesWithContext indicates an expected call of ListPlatformApplicationsPagesWithContext 839 | func (mr *MockSNSAPIMockRecorder) ListPlatformApplicationsPagesWithContext(arg0, arg1, arg2 interface{}, arg3 ...interface{}) *gomock.Call { 840 | varargs := append([]interface{}{arg0, arg1, arg2}, arg3...) 841 | return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ListPlatformApplicationsPagesWithContext", reflect.TypeOf((*MockSNSAPI)(nil).ListPlatformApplicationsPagesWithContext), varargs...) 842 | } 843 | 844 | // ListSubscriptions mocks base method 845 | func (m *MockSNSAPI) ListSubscriptions(arg0 *sns.ListSubscriptionsInput) (*sns.ListSubscriptionsOutput, error) { 846 | ret := m.ctrl.Call(m, "ListSubscriptions", arg0) 847 | ret0, _ := ret[0].(*sns.ListSubscriptionsOutput) 848 | ret1, _ := ret[1].(error) 849 | return ret0, ret1 850 | } 851 | 852 | // ListSubscriptions indicates an expected call of ListSubscriptions 853 | func (mr *MockSNSAPIMockRecorder) ListSubscriptions(arg0 interface{}) *gomock.Call { 854 | return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ListSubscriptions", reflect.TypeOf((*MockSNSAPI)(nil).ListSubscriptions), arg0) 855 | } 856 | 857 | // ListSubscriptionsWithContext mocks base method 858 | func (m *MockSNSAPI) ListSubscriptionsWithContext(arg0 aws.Context, arg1 *sns.ListSubscriptionsInput, arg2 ...request.Option) (*sns.ListSubscriptionsOutput, error) { 859 | varargs := []interface{}{arg0, arg1} 860 | for _, a := range arg2 { 861 | varargs = append(varargs, a) 862 | } 863 | ret := m.ctrl.Call(m, "ListSubscriptionsWithContext", varargs...) 864 | ret0, _ := ret[0].(*sns.ListSubscriptionsOutput) 865 | ret1, _ := ret[1].(error) 866 | return ret0, ret1 867 | } 868 | 869 | // ListSubscriptionsWithContext indicates an expected call of ListSubscriptionsWithContext 870 | func (mr *MockSNSAPIMockRecorder) ListSubscriptionsWithContext(arg0, arg1 interface{}, arg2 ...interface{}) *gomock.Call { 871 | varargs := append([]interface{}{arg0, arg1}, arg2...) 872 | return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ListSubscriptionsWithContext", reflect.TypeOf((*MockSNSAPI)(nil).ListSubscriptionsWithContext), varargs...) 873 | } 874 | 875 | // ListSubscriptionsRequest mocks base method 876 | func (m *MockSNSAPI) ListSubscriptionsRequest(arg0 *sns.ListSubscriptionsInput) (*request.Request, *sns.ListSubscriptionsOutput) { 877 | ret := m.ctrl.Call(m, "ListSubscriptionsRequest", arg0) 878 | ret0, _ := ret[0].(*request.Request) 879 | ret1, _ := ret[1].(*sns.ListSubscriptionsOutput) 880 | return ret0, ret1 881 | } 882 | 883 | // ListSubscriptionsRequest indicates an expected call of ListSubscriptionsRequest 884 | func (mr *MockSNSAPIMockRecorder) ListSubscriptionsRequest(arg0 interface{}) *gomock.Call { 885 | return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ListSubscriptionsRequest", reflect.TypeOf((*MockSNSAPI)(nil).ListSubscriptionsRequest), arg0) 886 | } 887 | 888 | // ListSubscriptionsPages mocks base method 889 | func (m *MockSNSAPI) ListSubscriptionsPages(arg0 *sns.ListSubscriptionsInput, arg1 func(*sns.ListSubscriptionsOutput, bool) bool) error { 890 | ret := m.ctrl.Call(m, "ListSubscriptionsPages", arg0, arg1) 891 | ret0, _ := ret[0].(error) 892 | return ret0 893 | } 894 | 895 | // ListSubscriptionsPages indicates an expected call of ListSubscriptionsPages 896 | func (mr *MockSNSAPIMockRecorder) ListSubscriptionsPages(arg0, arg1 interface{}) *gomock.Call { 897 | return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ListSubscriptionsPages", reflect.TypeOf((*MockSNSAPI)(nil).ListSubscriptionsPages), arg0, arg1) 898 | } 899 | 900 | // ListSubscriptionsPagesWithContext mocks base method 901 | func (m *MockSNSAPI) ListSubscriptionsPagesWithContext(arg0 aws.Context, arg1 *sns.ListSubscriptionsInput, arg2 func(*sns.ListSubscriptionsOutput, bool) bool, arg3 ...request.Option) error { 902 | varargs := []interface{}{arg0, arg1, arg2} 903 | for _, a := range arg3 { 904 | varargs = append(varargs, a) 905 | } 906 | ret := m.ctrl.Call(m, "ListSubscriptionsPagesWithContext", varargs...) 907 | ret0, _ := ret[0].(error) 908 | return ret0 909 | } 910 | 911 | // ListSubscriptionsPagesWithContext indicates an expected call of ListSubscriptionsPagesWithContext 912 | func (mr *MockSNSAPIMockRecorder) ListSubscriptionsPagesWithContext(arg0, arg1, arg2 interface{}, arg3 ...interface{}) *gomock.Call { 913 | varargs := append([]interface{}{arg0, arg1, arg2}, arg3...) 914 | return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ListSubscriptionsPagesWithContext", reflect.TypeOf((*MockSNSAPI)(nil).ListSubscriptionsPagesWithContext), varargs...) 915 | } 916 | 917 | // ListSubscriptionsByTopic mocks base method 918 | func (m *MockSNSAPI) ListSubscriptionsByTopic(arg0 *sns.ListSubscriptionsByTopicInput) (*sns.ListSubscriptionsByTopicOutput, error) { 919 | ret := m.ctrl.Call(m, "ListSubscriptionsByTopic", arg0) 920 | ret0, _ := ret[0].(*sns.ListSubscriptionsByTopicOutput) 921 | ret1, _ := ret[1].(error) 922 | return ret0, ret1 923 | } 924 | 925 | // ListSubscriptionsByTopic indicates an expected call of ListSubscriptionsByTopic 926 | func (mr *MockSNSAPIMockRecorder) ListSubscriptionsByTopic(arg0 interface{}) *gomock.Call { 927 | return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ListSubscriptionsByTopic", reflect.TypeOf((*MockSNSAPI)(nil).ListSubscriptionsByTopic), arg0) 928 | } 929 | 930 | // ListSubscriptionsByTopicWithContext mocks base method 931 | func (m *MockSNSAPI) ListSubscriptionsByTopicWithContext(arg0 aws.Context, arg1 *sns.ListSubscriptionsByTopicInput, arg2 ...request.Option) (*sns.ListSubscriptionsByTopicOutput, error) { 932 | varargs := []interface{}{arg0, arg1} 933 | for _, a := range arg2 { 934 | varargs = append(varargs, a) 935 | } 936 | ret := m.ctrl.Call(m, "ListSubscriptionsByTopicWithContext", varargs...) 937 | ret0, _ := ret[0].(*sns.ListSubscriptionsByTopicOutput) 938 | ret1, _ := ret[1].(error) 939 | return ret0, ret1 940 | } 941 | 942 | // ListSubscriptionsByTopicWithContext indicates an expected call of ListSubscriptionsByTopicWithContext 943 | func (mr *MockSNSAPIMockRecorder) ListSubscriptionsByTopicWithContext(arg0, arg1 interface{}, arg2 ...interface{}) *gomock.Call { 944 | varargs := append([]interface{}{arg0, arg1}, arg2...) 945 | return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ListSubscriptionsByTopicWithContext", reflect.TypeOf((*MockSNSAPI)(nil).ListSubscriptionsByTopicWithContext), varargs...) 946 | } 947 | 948 | // ListSubscriptionsByTopicRequest mocks base method 949 | func (m *MockSNSAPI) ListSubscriptionsByTopicRequest(arg0 *sns.ListSubscriptionsByTopicInput) (*request.Request, *sns.ListSubscriptionsByTopicOutput) { 950 | ret := m.ctrl.Call(m, "ListSubscriptionsByTopicRequest", arg0) 951 | ret0, _ := ret[0].(*request.Request) 952 | ret1, _ := ret[1].(*sns.ListSubscriptionsByTopicOutput) 953 | return ret0, ret1 954 | } 955 | 956 | // ListSubscriptionsByTopicRequest indicates an expected call of ListSubscriptionsByTopicRequest 957 | func (mr *MockSNSAPIMockRecorder) ListSubscriptionsByTopicRequest(arg0 interface{}) *gomock.Call { 958 | return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ListSubscriptionsByTopicRequest", reflect.TypeOf((*MockSNSAPI)(nil).ListSubscriptionsByTopicRequest), arg0) 959 | } 960 | 961 | // ListSubscriptionsByTopicPages mocks base method 962 | func (m *MockSNSAPI) ListSubscriptionsByTopicPages(arg0 *sns.ListSubscriptionsByTopicInput, arg1 func(*sns.ListSubscriptionsByTopicOutput, bool) bool) error { 963 | ret := m.ctrl.Call(m, "ListSubscriptionsByTopicPages", arg0, arg1) 964 | ret0, _ := ret[0].(error) 965 | return ret0 966 | } 967 | 968 | // ListSubscriptionsByTopicPages indicates an expected call of ListSubscriptionsByTopicPages 969 | func (mr *MockSNSAPIMockRecorder) ListSubscriptionsByTopicPages(arg0, arg1 interface{}) *gomock.Call { 970 | return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ListSubscriptionsByTopicPages", reflect.TypeOf((*MockSNSAPI)(nil).ListSubscriptionsByTopicPages), arg0, arg1) 971 | } 972 | 973 | // ListSubscriptionsByTopicPagesWithContext mocks base method 974 | func (m *MockSNSAPI) ListSubscriptionsByTopicPagesWithContext(arg0 aws.Context, arg1 *sns.ListSubscriptionsByTopicInput, arg2 func(*sns.ListSubscriptionsByTopicOutput, bool) bool, arg3 ...request.Option) error { 975 | varargs := []interface{}{arg0, arg1, arg2} 976 | for _, a := range arg3 { 977 | varargs = append(varargs, a) 978 | } 979 | ret := m.ctrl.Call(m, "ListSubscriptionsByTopicPagesWithContext", varargs...) 980 | ret0, _ := ret[0].(error) 981 | return ret0 982 | } 983 | 984 | // ListSubscriptionsByTopicPagesWithContext indicates an expected call of ListSubscriptionsByTopicPagesWithContext 985 | func (mr *MockSNSAPIMockRecorder) ListSubscriptionsByTopicPagesWithContext(arg0, arg1, arg2 interface{}, arg3 ...interface{}) *gomock.Call { 986 | varargs := append([]interface{}{arg0, arg1, arg2}, arg3...) 987 | return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ListSubscriptionsByTopicPagesWithContext", reflect.TypeOf((*MockSNSAPI)(nil).ListSubscriptionsByTopicPagesWithContext), varargs...) 988 | } 989 | 990 | // ListTopics mocks base method 991 | func (m *MockSNSAPI) ListTopics(arg0 *sns.ListTopicsInput) (*sns.ListTopicsOutput, error) { 992 | ret := m.ctrl.Call(m, "ListTopics", arg0) 993 | ret0, _ := ret[0].(*sns.ListTopicsOutput) 994 | ret1, _ := ret[1].(error) 995 | return ret0, ret1 996 | } 997 | 998 | // ListTopics indicates an expected call of ListTopics 999 | func (mr *MockSNSAPIMockRecorder) ListTopics(arg0 interface{}) *gomock.Call { 1000 | return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ListTopics", reflect.TypeOf((*MockSNSAPI)(nil).ListTopics), arg0) 1001 | } 1002 | 1003 | // ListTopicsWithContext mocks base method 1004 | func (m *MockSNSAPI) ListTopicsWithContext(arg0 aws.Context, arg1 *sns.ListTopicsInput, arg2 ...request.Option) (*sns.ListTopicsOutput, error) { 1005 | varargs := []interface{}{arg0, arg1} 1006 | for _, a := range arg2 { 1007 | varargs = append(varargs, a) 1008 | } 1009 | ret := m.ctrl.Call(m, "ListTopicsWithContext", varargs...) 1010 | ret0, _ := ret[0].(*sns.ListTopicsOutput) 1011 | ret1, _ := ret[1].(error) 1012 | return ret0, ret1 1013 | } 1014 | 1015 | // ListTopicsWithContext indicates an expected call of ListTopicsWithContext 1016 | func (mr *MockSNSAPIMockRecorder) ListTopicsWithContext(arg0, arg1 interface{}, arg2 ...interface{}) *gomock.Call { 1017 | varargs := append([]interface{}{arg0, arg1}, arg2...) 1018 | return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ListTopicsWithContext", reflect.TypeOf((*MockSNSAPI)(nil).ListTopicsWithContext), varargs...) 1019 | } 1020 | 1021 | // ListTopicsRequest mocks base method 1022 | func (m *MockSNSAPI) ListTopicsRequest(arg0 *sns.ListTopicsInput) (*request.Request, *sns.ListTopicsOutput) { 1023 | ret := m.ctrl.Call(m, "ListTopicsRequest", arg0) 1024 | ret0, _ := ret[0].(*request.Request) 1025 | ret1, _ := ret[1].(*sns.ListTopicsOutput) 1026 | return ret0, ret1 1027 | } 1028 | 1029 | // ListTopicsRequest indicates an expected call of ListTopicsRequest 1030 | func (mr *MockSNSAPIMockRecorder) ListTopicsRequest(arg0 interface{}) *gomock.Call { 1031 | return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ListTopicsRequest", reflect.TypeOf((*MockSNSAPI)(nil).ListTopicsRequest), arg0) 1032 | } 1033 | 1034 | // ListTopicsPages mocks base method 1035 | func (m *MockSNSAPI) ListTopicsPages(arg0 *sns.ListTopicsInput, arg1 func(*sns.ListTopicsOutput, bool) bool) error { 1036 | ret := m.ctrl.Call(m, "ListTopicsPages", arg0, arg1) 1037 | ret0, _ := ret[0].(error) 1038 | return ret0 1039 | } 1040 | 1041 | // ListTopicsPages indicates an expected call of ListTopicsPages 1042 | func (mr *MockSNSAPIMockRecorder) ListTopicsPages(arg0, arg1 interface{}) *gomock.Call { 1043 | return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ListTopicsPages", reflect.TypeOf((*MockSNSAPI)(nil).ListTopicsPages), arg0, arg1) 1044 | } 1045 | 1046 | // ListTopicsPagesWithContext mocks base method 1047 | func (m *MockSNSAPI) ListTopicsPagesWithContext(arg0 aws.Context, arg1 *sns.ListTopicsInput, arg2 func(*sns.ListTopicsOutput, bool) bool, arg3 ...request.Option) error { 1048 | varargs := []interface{}{arg0, arg1, arg2} 1049 | for _, a := range arg3 { 1050 | varargs = append(varargs, a) 1051 | } 1052 | ret := m.ctrl.Call(m, "ListTopicsPagesWithContext", varargs...) 1053 | ret0, _ := ret[0].(error) 1054 | return ret0 1055 | } 1056 | 1057 | // ListTopicsPagesWithContext indicates an expected call of ListTopicsPagesWithContext 1058 | func (mr *MockSNSAPIMockRecorder) ListTopicsPagesWithContext(arg0, arg1, arg2 interface{}, arg3 ...interface{}) *gomock.Call { 1059 | varargs := append([]interface{}{arg0, arg1, arg2}, arg3...) 1060 | return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ListTopicsPagesWithContext", reflect.TypeOf((*MockSNSAPI)(nil).ListTopicsPagesWithContext), varargs...) 1061 | } 1062 | 1063 | // OptInPhoneNumber mocks base method 1064 | func (m *MockSNSAPI) OptInPhoneNumber(arg0 *sns.OptInPhoneNumberInput) (*sns.OptInPhoneNumberOutput, error) { 1065 | ret := m.ctrl.Call(m, "OptInPhoneNumber", arg0) 1066 | ret0, _ := ret[0].(*sns.OptInPhoneNumberOutput) 1067 | ret1, _ := ret[1].(error) 1068 | return ret0, ret1 1069 | } 1070 | 1071 | // OptInPhoneNumber indicates an expected call of OptInPhoneNumber 1072 | func (mr *MockSNSAPIMockRecorder) OptInPhoneNumber(arg0 interface{}) *gomock.Call { 1073 | return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "OptInPhoneNumber", reflect.TypeOf((*MockSNSAPI)(nil).OptInPhoneNumber), arg0) 1074 | } 1075 | 1076 | // OptInPhoneNumberWithContext mocks base method 1077 | func (m *MockSNSAPI) OptInPhoneNumberWithContext(arg0 aws.Context, arg1 *sns.OptInPhoneNumberInput, arg2 ...request.Option) (*sns.OptInPhoneNumberOutput, error) { 1078 | varargs := []interface{}{arg0, arg1} 1079 | for _, a := range arg2 { 1080 | varargs = append(varargs, a) 1081 | } 1082 | ret := m.ctrl.Call(m, "OptInPhoneNumberWithContext", varargs...) 1083 | ret0, _ := ret[0].(*sns.OptInPhoneNumberOutput) 1084 | ret1, _ := ret[1].(error) 1085 | return ret0, ret1 1086 | } 1087 | 1088 | // OptInPhoneNumberWithContext indicates an expected call of OptInPhoneNumberWithContext 1089 | func (mr *MockSNSAPIMockRecorder) OptInPhoneNumberWithContext(arg0, arg1 interface{}, arg2 ...interface{}) *gomock.Call { 1090 | varargs := append([]interface{}{arg0, arg1}, arg2...) 1091 | return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "OptInPhoneNumberWithContext", reflect.TypeOf((*MockSNSAPI)(nil).OptInPhoneNumberWithContext), varargs...) 1092 | } 1093 | 1094 | // OptInPhoneNumberRequest mocks base method 1095 | func (m *MockSNSAPI) OptInPhoneNumberRequest(arg0 *sns.OptInPhoneNumberInput) (*request.Request, *sns.OptInPhoneNumberOutput) { 1096 | ret := m.ctrl.Call(m, "OptInPhoneNumberRequest", arg0) 1097 | ret0, _ := ret[0].(*request.Request) 1098 | ret1, _ := ret[1].(*sns.OptInPhoneNumberOutput) 1099 | return ret0, ret1 1100 | } 1101 | 1102 | // OptInPhoneNumberRequest indicates an expected call of OptInPhoneNumberRequest 1103 | func (mr *MockSNSAPIMockRecorder) OptInPhoneNumberRequest(arg0 interface{}) *gomock.Call { 1104 | return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "OptInPhoneNumberRequest", reflect.TypeOf((*MockSNSAPI)(nil).OptInPhoneNumberRequest), arg0) 1105 | } 1106 | 1107 | // Publish mocks base method 1108 | func (m *MockSNSAPI) Publish(arg0 *sns.PublishInput) (*sns.PublishOutput, error) { 1109 | ret := m.ctrl.Call(m, "Publish", arg0) 1110 | ret0, _ := ret[0].(*sns.PublishOutput) 1111 | ret1, _ := ret[1].(error) 1112 | return ret0, ret1 1113 | } 1114 | 1115 | // Publish indicates an expected call of Publish 1116 | func (mr *MockSNSAPIMockRecorder) Publish(arg0 interface{}) *gomock.Call { 1117 | return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Publish", reflect.TypeOf((*MockSNSAPI)(nil).Publish), arg0) 1118 | } 1119 | 1120 | // PublishWithContext mocks base method 1121 | func (m *MockSNSAPI) PublishWithContext(arg0 aws.Context, arg1 *sns.PublishInput, arg2 ...request.Option) (*sns.PublishOutput, error) { 1122 | varargs := []interface{}{arg0, arg1} 1123 | for _, a := range arg2 { 1124 | varargs = append(varargs, a) 1125 | } 1126 | ret := m.ctrl.Call(m, "PublishWithContext", varargs...) 1127 | ret0, _ := ret[0].(*sns.PublishOutput) 1128 | ret1, _ := ret[1].(error) 1129 | return ret0, ret1 1130 | } 1131 | 1132 | // PublishWithContext indicates an expected call of PublishWithContext 1133 | func (mr *MockSNSAPIMockRecorder) PublishWithContext(arg0, arg1 interface{}, arg2 ...interface{}) *gomock.Call { 1134 | varargs := append([]interface{}{arg0, arg1}, arg2...) 1135 | return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "PublishWithContext", reflect.TypeOf((*MockSNSAPI)(nil).PublishWithContext), varargs...) 1136 | } 1137 | 1138 | // PublishRequest mocks base method 1139 | func (m *MockSNSAPI) PublishRequest(arg0 *sns.PublishInput) (*request.Request, *sns.PublishOutput) { 1140 | ret := m.ctrl.Call(m, "PublishRequest", arg0) 1141 | ret0, _ := ret[0].(*request.Request) 1142 | ret1, _ := ret[1].(*sns.PublishOutput) 1143 | return ret0, ret1 1144 | } 1145 | 1146 | // PublishRequest indicates an expected call of PublishRequest 1147 | func (mr *MockSNSAPIMockRecorder) PublishRequest(arg0 interface{}) *gomock.Call { 1148 | return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "PublishRequest", reflect.TypeOf((*MockSNSAPI)(nil).PublishRequest), arg0) 1149 | } 1150 | 1151 | // RemovePermission mocks base method 1152 | func (m *MockSNSAPI) RemovePermission(arg0 *sns.RemovePermissionInput) (*sns.RemovePermissionOutput, error) { 1153 | ret := m.ctrl.Call(m, "RemovePermission", arg0) 1154 | ret0, _ := ret[0].(*sns.RemovePermissionOutput) 1155 | ret1, _ := ret[1].(error) 1156 | return ret0, ret1 1157 | } 1158 | 1159 | // RemovePermission indicates an expected call of RemovePermission 1160 | func (mr *MockSNSAPIMockRecorder) RemovePermission(arg0 interface{}) *gomock.Call { 1161 | return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "RemovePermission", reflect.TypeOf((*MockSNSAPI)(nil).RemovePermission), arg0) 1162 | } 1163 | 1164 | // RemovePermissionWithContext mocks base method 1165 | func (m *MockSNSAPI) RemovePermissionWithContext(arg0 aws.Context, arg1 *sns.RemovePermissionInput, arg2 ...request.Option) (*sns.RemovePermissionOutput, error) { 1166 | varargs := []interface{}{arg0, arg1} 1167 | for _, a := range arg2 { 1168 | varargs = append(varargs, a) 1169 | } 1170 | ret := m.ctrl.Call(m, "RemovePermissionWithContext", varargs...) 1171 | ret0, _ := ret[0].(*sns.RemovePermissionOutput) 1172 | ret1, _ := ret[1].(error) 1173 | return ret0, ret1 1174 | } 1175 | 1176 | // RemovePermissionWithContext indicates an expected call of RemovePermissionWithContext 1177 | func (mr *MockSNSAPIMockRecorder) RemovePermissionWithContext(arg0, arg1 interface{}, arg2 ...interface{}) *gomock.Call { 1178 | varargs := append([]interface{}{arg0, arg1}, arg2...) 1179 | return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "RemovePermissionWithContext", reflect.TypeOf((*MockSNSAPI)(nil).RemovePermissionWithContext), varargs...) 1180 | } 1181 | 1182 | // RemovePermissionRequest mocks base method 1183 | func (m *MockSNSAPI) RemovePermissionRequest(arg0 *sns.RemovePermissionInput) (*request.Request, *sns.RemovePermissionOutput) { 1184 | ret := m.ctrl.Call(m, "RemovePermissionRequest", arg0) 1185 | ret0, _ := ret[0].(*request.Request) 1186 | ret1, _ := ret[1].(*sns.RemovePermissionOutput) 1187 | return ret0, ret1 1188 | } 1189 | 1190 | // RemovePermissionRequest indicates an expected call of RemovePermissionRequest 1191 | func (mr *MockSNSAPIMockRecorder) RemovePermissionRequest(arg0 interface{}) *gomock.Call { 1192 | return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "RemovePermissionRequest", reflect.TypeOf((*MockSNSAPI)(nil).RemovePermissionRequest), arg0) 1193 | } 1194 | 1195 | // SetEndpointAttributes mocks base method 1196 | func (m *MockSNSAPI) SetEndpointAttributes(arg0 *sns.SetEndpointAttributesInput) (*sns.SetEndpointAttributesOutput, error) { 1197 | ret := m.ctrl.Call(m, "SetEndpointAttributes", arg0) 1198 | ret0, _ := ret[0].(*sns.SetEndpointAttributesOutput) 1199 | ret1, _ := ret[1].(error) 1200 | return ret0, ret1 1201 | } 1202 | 1203 | // SetEndpointAttributes indicates an expected call of SetEndpointAttributes 1204 | func (mr *MockSNSAPIMockRecorder) SetEndpointAttributes(arg0 interface{}) *gomock.Call { 1205 | return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SetEndpointAttributes", reflect.TypeOf((*MockSNSAPI)(nil).SetEndpointAttributes), arg0) 1206 | } 1207 | 1208 | // SetEndpointAttributesWithContext mocks base method 1209 | func (m *MockSNSAPI) SetEndpointAttributesWithContext(arg0 aws.Context, arg1 *sns.SetEndpointAttributesInput, arg2 ...request.Option) (*sns.SetEndpointAttributesOutput, error) { 1210 | varargs := []interface{}{arg0, arg1} 1211 | for _, a := range arg2 { 1212 | varargs = append(varargs, a) 1213 | } 1214 | ret := m.ctrl.Call(m, "SetEndpointAttributesWithContext", varargs...) 1215 | ret0, _ := ret[0].(*sns.SetEndpointAttributesOutput) 1216 | ret1, _ := ret[1].(error) 1217 | return ret0, ret1 1218 | } 1219 | 1220 | // SetEndpointAttributesWithContext indicates an expected call of SetEndpointAttributesWithContext 1221 | func (mr *MockSNSAPIMockRecorder) SetEndpointAttributesWithContext(arg0, arg1 interface{}, arg2 ...interface{}) *gomock.Call { 1222 | varargs := append([]interface{}{arg0, arg1}, arg2...) 1223 | return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SetEndpointAttributesWithContext", reflect.TypeOf((*MockSNSAPI)(nil).SetEndpointAttributesWithContext), varargs...) 1224 | } 1225 | 1226 | // SetEndpointAttributesRequest mocks base method 1227 | func (m *MockSNSAPI) SetEndpointAttributesRequest(arg0 *sns.SetEndpointAttributesInput) (*request.Request, *sns.SetEndpointAttributesOutput) { 1228 | ret := m.ctrl.Call(m, "SetEndpointAttributesRequest", arg0) 1229 | ret0, _ := ret[0].(*request.Request) 1230 | ret1, _ := ret[1].(*sns.SetEndpointAttributesOutput) 1231 | return ret0, ret1 1232 | } 1233 | 1234 | // SetEndpointAttributesRequest indicates an expected call of SetEndpointAttributesRequest 1235 | func (mr *MockSNSAPIMockRecorder) SetEndpointAttributesRequest(arg0 interface{}) *gomock.Call { 1236 | return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SetEndpointAttributesRequest", reflect.TypeOf((*MockSNSAPI)(nil).SetEndpointAttributesRequest), arg0) 1237 | } 1238 | 1239 | // SetPlatformApplicationAttributes mocks base method 1240 | func (m *MockSNSAPI) SetPlatformApplicationAttributes(arg0 *sns.SetPlatformApplicationAttributesInput) (*sns.SetPlatformApplicationAttributesOutput, error) { 1241 | ret := m.ctrl.Call(m, "SetPlatformApplicationAttributes", arg0) 1242 | ret0, _ := ret[0].(*sns.SetPlatformApplicationAttributesOutput) 1243 | ret1, _ := ret[1].(error) 1244 | return ret0, ret1 1245 | } 1246 | 1247 | // SetPlatformApplicationAttributes indicates an expected call of SetPlatformApplicationAttributes 1248 | func (mr *MockSNSAPIMockRecorder) SetPlatformApplicationAttributes(arg0 interface{}) *gomock.Call { 1249 | return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SetPlatformApplicationAttributes", reflect.TypeOf((*MockSNSAPI)(nil).SetPlatformApplicationAttributes), arg0) 1250 | } 1251 | 1252 | // SetPlatformApplicationAttributesWithContext mocks base method 1253 | func (m *MockSNSAPI) SetPlatformApplicationAttributesWithContext(arg0 aws.Context, arg1 *sns.SetPlatformApplicationAttributesInput, arg2 ...request.Option) (*sns.SetPlatformApplicationAttributesOutput, error) { 1254 | varargs := []interface{}{arg0, arg1} 1255 | for _, a := range arg2 { 1256 | varargs = append(varargs, a) 1257 | } 1258 | ret := m.ctrl.Call(m, "SetPlatformApplicationAttributesWithContext", varargs...) 1259 | ret0, _ := ret[0].(*sns.SetPlatformApplicationAttributesOutput) 1260 | ret1, _ := ret[1].(error) 1261 | return ret0, ret1 1262 | } 1263 | 1264 | // SetPlatformApplicationAttributesWithContext indicates an expected call of SetPlatformApplicationAttributesWithContext 1265 | func (mr *MockSNSAPIMockRecorder) SetPlatformApplicationAttributesWithContext(arg0, arg1 interface{}, arg2 ...interface{}) *gomock.Call { 1266 | varargs := append([]interface{}{arg0, arg1}, arg2...) 1267 | return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SetPlatformApplicationAttributesWithContext", reflect.TypeOf((*MockSNSAPI)(nil).SetPlatformApplicationAttributesWithContext), varargs...) 1268 | } 1269 | 1270 | // SetPlatformApplicationAttributesRequest mocks base method 1271 | func (m *MockSNSAPI) SetPlatformApplicationAttributesRequest(arg0 *sns.SetPlatformApplicationAttributesInput) (*request.Request, *sns.SetPlatformApplicationAttributesOutput) { 1272 | ret := m.ctrl.Call(m, "SetPlatformApplicationAttributesRequest", arg0) 1273 | ret0, _ := ret[0].(*request.Request) 1274 | ret1, _ := ret[1].(*sns.SetPlatformApplicationAttributesOutput) 1275 | return ret0, ret1 1276 | } 1277 | 1278 | // SetPlatformApplicationAttributesRequest indicates an expected call of SetPlatformApplicationAttributesRequest 1279 | func (mr *MockSNSAPIMockRecorder) SetPlatformApplicationAttributesRequest(arg0 interface{}) *gomock.Call { 1280 | return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SetPlatformApplicationAttributesRequest", reflect.TypeOf((*MockSNSAPI)(nil).SetPlatformApplicationAttributesRequest), arg0) 1281 | } 1282 | 1283 | // SetSMSAttributes mocks base method 1284 | func (m *MockSNSAPI) SetSMSAttributes(arg0 *sns.SetSMSAttributesInput) (*sns.SetSMSAttributesOutput, error) { 1285 | ret := m.ctrl.Call(m, "SetSMSAttributes", arg0) 1286 | ret0, _ := ret[0].(*sns.SetSMSAttributesOutput) 1287 | ret1, _ := ret[1].(error) 1288 | return ret0, ret1 1289 | } 1290 | 1291 | // SetSMSAttributes indicates an expected call of SetSMSAttributes 1292 | func (mr *MockSNSAPIMockRecorder) SetSMSAttributes(arg0 interface{}) *gomock.Call { 1293 | return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SetSMSAttributes", reflect.TypeOf((*MockSNSAPI)(nil).SetSMSAttributes), arg0) 1294 | } 1295 | 1296 | // SetSMSAttributesWithContext mocks base method 1297 | func (m *MockSNSAPI) SetSMSAttributesWithContext(arg0 aws.Context, arg1 *sns.SetSMSAttributesInput, arg2 ...request.Option) (*sns.SetSMSAttributesOutput, error) { 1298 | varargs := []interface{}{arg0, arg1} 1299 | for _, a := range arg2 { 1300 | varargs = append(varargs, a) 1301 | } 1302 | ret := m.ctrl.Call(m, "SetSMSAttributesWithContext", varargs...) 1303 | ret0, _ := ret[0].(*sns.SetSMSAttributesOutput) 1304 | ret1, _ := ret[1].(error) 1305 | return ret0, ret1 1306 | } 1307 | 1308 | // SetSMSAttributesWithContext indicates an expected call of SetSMSAttributesWithContext 1309 | func (mr *MockSNSAPIMockRecorder) SetSMSAttributesWithContext(arg0, arg1 interface{}, arg2 ...interface{}) *gomock.Call { 1310 | varargs := append([]interface{}{arg0, arg1}, arg2...) 1311 | return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SetSMSAttributesWithContext", reflect.TypeOf((*MockSNSAPI)(nil).SetSMSAttributesWithContext), varargs...) 1312 | } 1313 | 1314 | // SetSMSAttributesRequest mocks base method 1315 | func (m *MockSNSAPI) SetSMSAttributesRequest(arg0 *sns.SetSMSAttributesInput) (*request.Request, *sns.SetSMSAttributesOutput) { 1316 | ret := m.ctrl.Call(m, "SetSMSAttributesRequest", arg0) 1317 | ret0, _ := ret[0].(*request.Request) 1318 | ret1, _ := ret[1].(*sns.SetSMSAttributesOutput) 1319 | return ret0, ret1 1320 | } 1321 | 1322 | // SetSMSAttributesRequest indicates an expected call of SetSMSAttributesRequest 1323 | func (mr *MockSNSAPIMockRecorder) SetSMSAttributesRequest(arg0 interface{}) *gomock.Call { 1324 | return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SetSMSAttributesRequest", reflect.TypeOf((*MockSNSAPI)(nil).SetSMSAttributesRequest), arg0) 1325 | } 1326 | 1327 | // SetSubscriptionAttributes mocks base method 1328 | func (m *MockSNSAPI) SetSubscriptionAttributes(arg0 *sns.SetSubscriptionAttributesInput) (*sns.SetSubscriptionAttributesOutput, error) { 1329 | ret := m.ctrl.Call(m, "SetSubscriptionAttributes", arg0) 1330 | ret0, _ := ret[0].(*sns.SetSubscriptionAttributesOutput) 1331 | ret1, _ := ret[1].(error) 1332 | return ret0, ret1 1333 | } 1334 | 1335 | // SetSubscriptionAttributes indicates an expected call of SetSubscriptionAttributes 1336 | func (mr *MockSNSAPIMockRecorder) SetSubscriptionAttributes(arg0 interface{}) *gomock.Call { 1337 | return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SetSubscriptionAttributes", reflect.TypeOf((*MockSNSAPI)(nil).SetSubscriptionAttributes), arg0) 1338 | } 1339 | 1340 | // SetSubscriptionAttributesWithContext mocks base method 1341 | func (m *MockSNSAPI) SetSubscriptionAttributesWithContext(arg0 aws.Context, arg1 *sns.SetSubscriptionAttributesInput, arg2 ...request.Option) (*sns.SetSubscriptionAttributesOutput, error) { 1342 | varargs := []interface{}{arg0, arg1} 1343 | for _, a := range arg2 { 1344 | varargs = append(varargs, a) 1345 | } 1346 | ret := m.ctrl.Call(m, "SetSubscriptionAttributesWithContext", varargs...) 1347 | ret0, _ := ret[0].(*sns.SetSubscriptionAttributesOutput) 1348 | ret1, _ := ret[1].(error) 1349 | return ret0, ret1 1350 | } 1351 | 1352 | // SetSubscriptionAttributesWithContext indicates an expected call of SetSubscriptionAttributesWithContext 1353 | func (mr *MockSNSAPIMockRecorder) SetSubscriptionAttributesWithContext(arg0, arg1 interface{}, arg2 ...interface{}) *gomock.Call { 1354 | varargs := append([]interface{}{arg0, arg1}, arg2...) 1355 | return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SetSubscriptionAttributesWithContext", reflect.TypeOf((*MockSNSAPI)(nil).SetSubscriptionAttributesWithContext), varargs...) 1356 | } 1357 | 1358 | // SetSubscriptionAttributesRequest mocks base method 1359 | func (m *MockSNSAPI) SetSubscriptionAttributesRequest(arg0 *sns.SetSubscriptionAttributesInput) (*request.Request, *sns.SetSubscriptionAttributesOutput) { 1360 | ret := m.ctrl.Call(m, "SetSubscriptionAttributesRequest", arg0) 1361 | ret0, _ := ret[0].(*request.Request) 1362 | ret1, _ := ret[1].(*sns.SetSubscriptionAttributesOutput) 1363 | return ret0, ret1 1364 | } 1365 | 1366 | // SetSubscriptionAttributesRequest indicates an expected call of SetSubscriptionAttributesRequest 1367 | func (mr *MockSNSAPIMockRecorder) SetSubscriptionAttributesRequest(arg0 interface{}) *gomock.Call { 1368 | return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SetSubscriptionAttributesRequest", reflect.TypeOf((*MockSNSAPI)(nil).SetSubscriptionAttributesRequest), arg0) 1369 | } 1370 | 1371 | // SetTopicAttributes mocks base method 1372 | func (m *MockSNSAPI) SetTopicAttributes(arg0 *sns.SetTopicAttributesInput) (*sns.SetTopicAttributesOutput, error) { 1373 | ret := m.ctrl.Call(m, "SetTopicAttributes", arg0) 1374 | ret0, _ := ret[0].(*sns.SetTopicAttributesOutput) 1375 | ret1, _ := ret[1].(error) 1376 | return ret0, ret1 1377 | } 1378 | 1379 | // SetTopicAttributes indicates an expected call of SetTopicAttributes 1380 | func (mr *MockSNSAPIMockRecorder) SetTopicAttributes(arg0 interface{}) *gomock.Call { 1381 | return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SetTopicAttributes", reflect.TypeOf((*MockSNSAPI)(nil).SetTopicAttributes), arg0) 1382 | } 1383 | 1384 | // SetTopicAttributesWithContext mocks base method 1385 | func (m *MockSNSAPI) SetTopicAttributesWithContext(arg0 aws.Context, arg1 *sns.SetTopicAttributesInput, arg2 ...request.Option) (*sns.SetTopicAttributesOutput, error) { 1386 | varargs := []interface{}{arg0, arg1} 1387 | for _, a := range arg2 { 1388 | varargs = append(varargs, a) 1389 | } 1390 | ret := m.ctrl.Call(m, "SetTopicAttributesWithContext", varargs...) 1391 | ret0, _ := ret[0].(*sns.SetTopicAttributesOutput) 1392 | ret1, _ := ret[1].(error) 1393 | return ret0, ret1 1394 | } 1395 | 1396 | // SetTopicAttributesWithContext indicates an expected call of SetTopicAttributesWithContext 1397 | func (mr *MockSNSAPIMockRecorder) SetTopicAttributesWithContext(arg0, arg1 interface{}, arg2 ...interface{}) *gomock.Call { 1398 | varargs := append([]interface{}{arg0, arg1}, arg2...) 1399 | return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SetTopicAttributesWithContext", reflect.TypeOf((*MockSNSAPI)(nil).SetTopicAttributesWithContext), varargs...) 1400 | } 1401 | 1402 | // SetTopicAttributesRequest mocks base method 1403 | func (m *MockSNSAPI) SetTopicAttributesRequest(arg0 *sns.SetTopicAttributesInput) (*request.Request, *sns.SetTopicAttributesOutput) { 1404 | ret := m.ctrl.Call(m, "SetTopicAttributesRequest", arg0) 1405 | ret0, _ := ret[0].(*request.Request) 1406 | ret1, _ := ret[1].(*sns.SetTopicAttributesOutput) 1407 | return ret0, ret1 1408 | } 1409 | 1410 | // SetTopicAttributesRequest indicates an expected call of SetTopicAttributesRequest 1411 | func (mr *MockSNSAPIMockRecorder) SetTopicAttributesRequest(arg0 interface{}) *gomock.Call { 1412 | return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SetTopicAttributesRequest", reflect.TypeOf((*MockSNSAPI)(nil).SetTopicAttributesRequest), arg0) 1413 | } 1414 | 1415 | // Subscribe mocks base method 1416 | func (m *MockSNSAPI) Subscribe(arg0 *sns.SubscribeInput) (*sns.SubscribeOutput, error) { 1417 | ret := m.ctrl.Call(m, "Subscribe", arg0) 1418 | ret0, _ := ret[0].(*sns.SubscribeOutput) 1419 | ret1, _ := ret[1].(error) 1420 | return ret0, ret1 1421 | } 1422 | 1423 | // Subscribe indicates an expected call of Subscribe 1424 | func (mr *MockSNSAPIMockRecorder) Subscribe(arg0 interface{}) *gomock.Call { 1425 | return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Subscribe", reflect.TypeOf((*MockSNSAPI)(nil).Subscribe), arg0) 1426 | } 1427 | 1428 | // SubscribeWithContext mocks base method 1429 | func (m *MockSNSAPI) SubscribeWithContext(arg0 aws.Context, arg1 *sns.SubscribeInput, arg2 ...request.Option) (*sns.SubscribeOutput, error) { 1430 | varargs := []interface{}{arg0, arg1} 1431 | for _, a := range arg2 { 1432 | varargs = append(varargs, a) 1433 | } 1434 | ret := m.ctrl.Call(m, "SubscribeWithContext", varargs...) 1435 | ret0, _ := ret[0].(*sns.SubscribeOutput) 1436 | ret1, _ := ret[1].(error) 1437 | return ret0, ret1 1438 | } 1439 | 1440 | // SubscribeWithContext indicates an expected call of SubscribeWithContext 1441 | func (mr *MockSNSAPIMockRecorder) SubscribeWithContext(arg0, arg1 interface{}, arg2 ...interface{}) *gomock.Call { 1442 | varargs := append([]interface{}{arg0, arg1}, arg2...) 1443 | return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SubscribeWithContext", reflect.TypeOf((*MockSNSAPI)(nil).SubscribeWithContext), varargs...) 1444 | } 1445 | 1446 | // SubscribeRequest mocks base method 1447 | func (m *MockSNSAPI) SubscribeRequest(arg0 *sns.SubscribeInput) (*request.Request, *sns.SubscribeOutput) { 1448 | ret := m.ctrl.Call(m, "SubscribeRequest", arg0) 1449 | ret0, _ := ret[0].(*request.Request) 1450 | ret1, _ := ret[1].(*sns.SubscribeOutput) 1451 | return ret0, ret1 1452 | } 1453 | 1454 | // SubscribeRequest indicates an expected call of SubscribeRequest 1455 | func (mr *MockSNSAPIMockRecorder) SubscribeRequest(arg0 interface{}) *gomock.Call { 1456 | return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SubscribeRequest", reflect.TypeOf((*MockSNSAPI)(nil).SubscribeRequest), arg0) 1457 | } 1458 | 1459 | // Unsubscribe mocks base method 1460 | func (m *MockSNSAPI) Unsubscribe(arg0 *sns.UnsubscribeInput) (*sns.UnsubscribeOutput, error) { 1461 | ret := m.ctrl.Call(m, "Unsubscribe", arg0) 1462 | ret0, _ := ret[0].(*sns.UnsubscribeOutput) 1463 | ret1, _ := ret[1].(error) 1464 | return ret0, ret1 1465 | } 1466 | 1467 | // Unsubscribe indicates an expected call of Unsubscribe 1468 | func (mr *MockSNSAPIMockRecorder) Unsubscribe(arg0 interface{}) *gomock.Call { 1469 | return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Unsubscribe", reflect.TypeOf((*MockSNSAPI)(nil).Unsubscribe), arg0) 1470 | } 1471 | 1472 | // UnsubscribeWithContext mocks base method 1473 | func (m *MockSNSAPI) UnsubscribeWithContext(arg0 aws.Context, arg1 *sns.UnsubscribeInput, arg2 ...request.Option) (*sns.UnsubscribeOutput, error) { 1474 | varargs := []interface{}{arg0, arg1} 1475 | for _, a := range arg2 { 1476 | varargs = append(varargs, a) 1477 | } 1478 | ret := m.ctrl.Call(m, "UnsubscribeWithContext", varargs...) 1479 | ret0, _ := ret[0].(*sns.UnsubscribeOutput) 1480 | ret1, _ := ret[1].(error) 1481 | return ret0, ret1 1482 | } 1483 | 1484 | // UnsubscribeWithContext indicates an expected call of UnsubscribeWithContext 1485 | func (mr *MockSNSAPIMockRecorder) UnsubscribeWithContext(arg0, arg1 interface{}, arg2 ...interface{}) *gomock.Call { 1486 | varargs := append([]interface{}{arg0, arg1}, arg2...) 1487 | return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "UnsubscribeWithContext", reflect.TypeOf((*MockSNSAPI)(nil).UnsubscribeWithContext), varargs...) 1488 | } 1489 | 1490 | // UnsubscribeRequest mocks base method 1491 | func (m *MockSNSAPI) UnsubscribeRequest(arg0 *sns.UnsubscribeInput) (*request.Request, *sns.UnsubscribeOutput) { 1492 | ret := m.ctrl.Call(m, "UnsubscribeRequest", arg0) 1493 | ret0, _ := ret[0].(*request.Request) 1494 | ret1, _ := ret[1].(*sns.UnsubscribeOutput) 1495 | return ret0, ret1 1496 | } 1497 | 1498 | // UnsubscribeRequest indicates an expected call of UnsubscribeRequest 1499 | func (mr *MockSNSAPIMockRecorder) UnsubscribeRequest(arg0 interface{}) *gomock.Call { 1500 | return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "UnsubscribeRequest", reflect.TypeOf((*MockSNSAPI)(nil).UnsubscribeRequest), arg0) 1501 | } 1502 | -------------------------------------------------------------------------------- /sns/snssink.go: -------------------------------------------------------------------------------- 1 | package sns 2 | 3 | import ( 4 | "fmt" 5 | "github.com/aws/aws-sdk-go/aws" 6 | "github.com/aws/aws-sdk-go/aws/session" 7 | "github.com/aws/aws-sdk-go/service/sns" 8 | "github.com/aws/aws-sdk-go/service/sns/snsiface" 9 | "github.com/pkg/errors" 10 | "github.com/utilitywarehouse/go-pubsub" 11 | ) 12 | 13 | type messageSink struct { 14 | client snsiface.SNSAPI 15 | topic string 16 | } 17 | 18 | // NewSNSSink is a constructor for new AWS SNS MessageSink type 19 | func NewSNSSink(conf *aws.Config, topic string) (pubsub.MessageSink, error) { 20 | sess, err := session.NewSession(conf) 21 | if err != nil { 22 | return nil, errors.Wrap(err, "could not construct AWS Session") 23 | } 24 | 25 | return &messageSink{ 26 | client: sns.New(sess), 27 | topic: topic, 28 | }, nil 29 | } 30 | 31 | // PutMessage sends ProducerMessage types to AWS SNS 32 | func (s *messageSink) PutMessage(message pubsub.ProducerMessage) error { 33 | b, err := message.Marshal() 34 | if err != nil { 35 | return errors.Wrap(err, "SNS Sink could not marshal ProducerMessage") 36 | } 37 | 38 | input := &sns.PublishInput{ 39 | Message: aws.String(string(b)), 40 | TopicArn: &s.topic, 41 | } 42 | 43 | _, err = s.client.Publish(input) 44 | if err != nil { 45 | return errors.Wrap(err, "error publishing to SNS") 46 | } 47 | 48 | return nil 49 | } 50 | 51 | // Status used to check status of connection to AWS SNS 52 | func (s *messageSink) Status() (*pubsub.Status, error) { 53 | topics, err := s.client.ListTopics(&sns.ListTopicsInput{}) 54 | if err != nil { 55 | return nil, err 56 | } 57 | 58 | status := &pubsub.Status{} 59 | if len(topics.Topics) < 1 { 60 | status.Working = false 61 | status.Problems = append(status.Problems, "no SNS topics") 62 | return status, nil 63 | } 64 | 65 | for _, x := range topics.Topics { 66 | if *x.TopicArn == s.topic { 67 | status.Working = true 68 | return status, nil 69 | } 70 | } 71 | 72 | status.Working = false 73 | status.Problems = append(status.Problems, fmt.Sprintf("%s not in topic list [%v]", s.topic, topics.Topics)) 74 | return status, nil 75 | } 76 | 77 | // Close is stubbed as there is no equivalent on the SNS Client. 78 | func (s *messageSink) Close() error { 79 | return nil 80 | } 81 | -------------------------------------------------------------------------------- /sns/snssink_test.go: -------------------------------------------------------------------------------- 1 | package sns_test 2 | 3 | import ( 4 | "github.com/aws/aws-sdk-go/aws" 5 | sns2 "github.com/aws/aws-sdk-go/service/sns" 6 | . "github.com/golang/mock/gomock" 7 | "github.com/pkg/errors" 8 | "github.com/utilitywarehouse/go-pubsub" 9 | "github.com/utilitywarehouse/go-pubsub/sns" 10 | "github.com/utilitywarehouse/go-pubsub/sns/mocks" 11 | "testing" 12 | ) 13 | 14 | const topic = "test topic" 15 | 16 | func TestSNSSink_PutMessage(t *testing.T) { 17 | mockCtrl := NewController(t) 18 | defer mockCtrl.Finish() 19 | mockAPI := mocks.NewMockSNSAPI(mockCtrl) 20 | 21 | sink := sns.NewTestSNSSink(mockAPI, topic) 22 | 23 | var payload pubsub.SimpleProducerMessage = []byte("simple payload") 24 | 25 | mockAPI.EXPECT().Publish(Any()).Times(1) 26 | err := sink.PutMessage(payload) 27 | if err != nil { 28 | t.Errorf("call to PutMessage() returned unexpected error %v", err) 29 | } 30 | } 31 | 32 | func TestSNSSink_PutMessagePublishError(t *testing.T) { 33 | mockCtrl := NewController(t) 34 | defer mockCtrl.Finish() 35 | mockAPI := mocks.NewMockSNSAPI(mockCtrl) 36 | 37 | sink := sns.NewTestSNSSink(mockAPI, topic) 38 | var payload pubsub.SimpleProducerMessage = []byte("simple payload") 39 | 40 | mockAPI.EXPECT().Publish(Any()).Return(nil, errors.New("test error")).Times(1) 41 | err := sink.PutMessage(payload) 42 | if err == nil { 43 | t.Error("PutMessage() failed to return expected error on Publish().") 44 | } 45 | } 46 | 47 | type ErrorPayload struct{} 48 | 49 | func (e ErrorPayload) Marshal() ([]byte, error) { 50 | return nil, errors.New("an error") 51 | } 52 | 53 | func TestSNSSink_PutMessageMarshallError(t *testing.T) { 54 | mockCtrl := NewController(t) 55 | defer mockCtrl.Finish() 56 | mockAPI := mocks.NewMockSNSAPI(mockCtrl) 57 | 58 | sink := sns.NewTestSNSSink(mockAPI, topic) 59 | 60 | mockAPI.EXPECT().Publish(Any()).Times(0) 61 | err := sink.PutMessage(ErrorPayload{}) 62 | if err == nil { 63 | t.Errorf("PutMessage() failed to return expected error on Marshall().") 64 | } 65 | } 66 | 67 | func TestSNSSink_Status(t *testing.T) { 68 | mockCtrl := NewController(t) 69 | defer mockCtrl.Finish() 70 | mockAPI := mocks.NewMockSNSAPI(mockCtrl) 71 | 72 | sink := sns.NewTestSNSSink(mockAPI, topic) 73 | topics := sns2.ListTopicsOutput{NextToken: aws.String("bleble")} 74 | topics.Topics = append(topics.Topics, &sns2.Topic{TopicArn: aws.String(topic)}) 75 | 76 | mockAPI.EXPECT().ListTopics(Any()).Times(1).Return(&topics, nil) 77 | status, err := sink.Status() 78 | 79 | if err != nil { 80 | t.Errorf("Status() returned unexpected error %v", err) 81 | } 82 | 83 | if status.Working != true { 84 | t.Error("status.Working should be true") 85 | } 86 | 87 | if len(status.Problems) != 0 { 88 | t.Error("status.Problems should be empty") 89 | } 90 | } 91 | 92 | func TestSNSSink_StatusError(t *testing.T) { 93 | mockCtrl := NewController(t) 94 | defer mockCtrl.Finish() 95 | mockAPI := mocks.NewMockSNSAPI(mockCtrl) 96 | 97 | sink := sns.NewTestSNSSink(mockAPI, topic) 98 | mockAPI.EXPECT().ListTopics(Any()).Times(1).Return(nil, errors.New("test")) 99 | status, err := sink.Status() 100 | 101 | if status != nil { 102 | t.Error("expected returned status to be nil!") 103 | } 104 | 105 | if err == nil { 106 | t.Error("Expected Status() to return error") 107 | } 108 | } 109 | 110 | func TestSNSSink_StatusMissingTopic(t *testing.T) { 111 | mockCtrl := NewController(t) 112 | defer mockCtrl.Finish() 113 | mockAPI := mocks.NewMockSNSAPI(mockCtrl) 114 | 115 | sink := sns.NewTestSNSSink(mockAPI, "other") 116 | topics := sns2.ListTopicsOutput{NextToken: aws.String("")} 117 | topics.Topics = append(topics.Topics, &sns2.Topic{TopicArn: aws.String("some other topic")}) 118 | 119 | mockAPI.EXPECT().ListTopics(Any()).Times(1).Return(&topics, nil) 120 | status, err := sink.Status() 121 | 122 | if err != nil { 123 | t.Errorf("unexpected error from Status(): %v", err) 124 | } 125 | 126 | if status.Working != false { 127 | t.Error("status.Working should be false") 128 | } 129 | 130 | if len(status.Problems) == 0 { 131 | t.Error("status.Problems should be populated") 132 | } 133 | } 134 | -------------------------------------------------------------------------------- /sqs/example/sink.go: -------------------------------------------------------------------------------- 1 | package example 2 | 3 | import ( 4 | "log" 5 | 6 | "github.com/aws/aws-sdk-go/aws" 7 | "github.com/aws/aws-sdk-go/aws/credentials" 8 | "github.com/aws/aws-sdk-go/aws/session" 9 | awsSQS "github.com/aws/aws-sdk-go/service/sqs" 10 | "github.com/utilitywarehouse/go-pubsub/sqs" 11 | ) 12 | 13 | // This is an example struct that satisfies pubsub.ProducerMessage interface. 14 | // You'd have to create a struct in your application that satisfies this interface. 15 | type myMessage struct { 16 | data string 17 | } 18 | 19 | // setting up SQS client is the same for both sink and source 20 | func getClient() *awsSQS.SQS { 21 | creds := credentials.NewStaticCredentials("accessKey", "secretKey", "") 22 | p := session.Must(session.NewSession()) 23 | cfg := aws.NewConfig().WithCredentials(creds).WithRegion("eu-west-1") 24 | 25 | return awsSQS.New(p, cfg) 26 | } 27 | 28 | func sinkExample() { 29 | queueURL := "https://sqs.eu-west-1.amazonaws.com/123/queueName" 30 | 31 | // create message sink passing AWS SQS client 32 | sink, err := sqs.NewMessageSink(sqs.MessageSinkConfig{ 33 | Client: getClient(), 34 | QueueURL: &queueURL, 35 | }) 36 | 37 | if err != nil { 38 | log.Panicf("failed to create sqs sink: %v", err) 39 | } 40 | 41 | // Although this doesn't close an actual connection, it is available in the API and 42 | // can act as a safeguard to prevent you from writing messages to a closed SQS sink. 43 | // NOTE: We use panic in this example and not Fatal to ensure defer is called. 44 | defer sink.Close() 45 | 46 | // Create your sqs message (struct must satisfy pubsub.ProducerMessage interface). 47 | msg := myMessage{data: "some payload"} 48 | 49 | // and sink it 50 | if err := sink.PutMessage(&msg); err != nil { 51 | log.Panicf("failed to sink in SQS: %v", err) 52 | } 53 | } 54 | 55 | func (m *myMessage) Marshal() ([]byte, error) { 56 | return []byte(m.data), nil 57 | } 58 | -------------------------------------------------------------------------------- /sqs/example/source.go: -------------------------------------------------------------------------------- 1 | package example 2 | 3 | import ( 4 | "context" 5 | "log" 6 | 7 | pubsub "github.com/utilitywarehouse/go-pubsub" 8 | "github.com/utilitywarehouse/go-pubsub/sqs" 9 | ) 10 | 11 | func consumerExample() { 12 | source, err := sqs.NewMessageSource(sqs.MessageSourceConfig{ 13 | Client: getClient(), // defined in sink.go 14 | QueueURL: "https://sqs.eu-west-1.amazonaws.com/123/queueName", 15 | // Optionally set how long you want to wait between API poll requests (in seconds). 16 | // Defaults to 0 (disabled) if not set. 17 | WaitSeconds: 1, 18 | }) 19 | 20 | if err != nil { 21 | log.Fatalf("failed to get SQS source: %v", err) 22 | } 23 | 24 | ctx, cancel := context.WithCancel(context.Background()) 25 | defer cancel() 26 | 27 | // Poll for messages. To stop the loop, call `cancel()` and it will cancel the context. 28 | if err := source.ConsumeMessages(ctx, msgHandler, errHandler); err != nil { 29 | log.Fatalf("failed to consume from SQS: %v", err) 30 | } 31 | } 32 | 33 | // msgHandler will process messages from SQS. 34 | func msgHandler(msg pubsub.ConsumerMessage) error { 35 | // Add your message handling logic here. 36 | 37 | return nil 38 | } 39 | 40 | // errHandler will be called if message handler fails. 41 | func errHandler(pubsub.ConsumerMessage, error) error { 42 | // Add your error handling logic here. 43 | 44 | return nil 45 | } 46 | -------------------------------------------------------------------------------- /sqs/sink.go: -------------------------------------------------------------------------------- 1 | package sqs 2 | 3 | import ( 4 | "sync" 5 | 6 | awsSQS "github.com/aws/aws-sdk-go/service/sqs" 7 | "github.com/pkg/errors" 8 | pubsub "github.com/utilitywarehouse/go-pubsub" 9 | ) 10 | 11 | // MessageSinkConfig allows you to set sink options. 12 | type MessageSinkConfig struct { 13 | Client Queue 14 | QueueURL *string 15 | } 16 | 17 | type messageSink struct { 18 | q Queue 19 | sinkErr error // holds error when SQS poll failed 20 | lk sync.Mutex 21 | closed bool 22 | sendInput *awsSQS.SendMessageInput 23 | } 24 | 25 | // NewMessageSink is the sink constructor. If SQS client is nil, an error is returned. 26 | func NewMessageSink(config MessageSinkConfig) (pubsub.MessageSink, error) { 27 | if config.Client == nil { 28 | return nil, errMissingClient 29 | } 30 | 31 | return &messageSink{ 32 | q: config.Client, 33 | sendInput: &awsSQS.SendMessageInput{QueueUrl: config.QueueURL}, 34 | }, nil 35 | } 36 | 37 | // PutMessage sinks a pubsub.ProducerMessage in SQS. 38 | func (s *messageSink) PutMessage(msg pubsub.ProducerMessage) error { 39 | if s.closed { 40 | return errors.New("SQS connection closed") 41 | } 42 | 43 | // this is needed to mark status as healthy if a temporary issue is resolved 44 | s.sinkErr = nil 45 | 46 | marshalledMsg, err := msg.Marshal() 47 | if err != nil { 48 | return errors.Wrapf(err, "failed to marshal SQS message: %+v", msg) 49 | } 50 | 51 | payload := string(marshalledMsg) 52 | 53 | sendInput := s.sendInput 54 | sendInput.MessageBody = &payload 55 | 56 | if _, err := s.q.SendMessage(sendInput); err != nil { 57 | s.sinkErr = err 58 | return errors.Wrap(err, "failed to sink message in SQS") 59 | } 60 | 61 | return nil 62 | } 63 | 64 | // Status reports whether sink is healthy or not. Instead of doing API requests 65 | // we wait for SendMessage to fail to degrade the status. 66 | func (s *messageSink) Status() (*pubsub.Status, error) { 67 | status := pubsub.Status{Working: true} 68 | if s.sinkErr != nil { 69 | status.Working = false 70 | status.Problems = append(status.Problems, s.sinkErr.Error()) 71 | } 72 | 73 | return &status, nil 74 | } 75 | 76 | // Close is added to satisfy MessageSink interface. 77 | func (s *messageSink) Close() error { 78 | s.lk.Lock() 79 | defer s.lk.Unlock() 80 | 81 | if s.closed { 82 | return errors.New("already closed") 83 | } 84 | 85 | // there's no connection to close, so we set closed flag to true and 86 | // prevent sending messages to SQS. 87 | s.closed = true 88 | 89 | return nil 90 | } 91 | -------------------------------------------------------------------------------- /sqs/sink_test.go: -------------------------------------------------------------------------------- 1 | package sqs_test 2 | 3 | import ( 4 | "strings" 5 | "testing" 6 | 7 | awsSQS "github.com/aws/aws-sdk-go/service/sqs" 8 | "github.com/pkg/errors" 9 | pubSQS "github.com/utilitywarehouse/go-pubsub/sqs" 10 | ) 11 | 12 | func Test_PutMessageMissingClient_Fail(t *testing.T) { 13 | _, err := pubSQS.NewMessageSink(pubSQS.MessageSinkConfig{}) 14 | if err == nil { 15 | t.Fatal("expected error about missing SQS client, got: ") 16 | } 17 | 18 | if !strings.Contains(err.Error(), "SQS client must not be nil") { 19 | t.Errorf("expected error about missing SQS client: got: %v", err) 20 | } 21 | } 22 | 23 | func Test_PutMessage_Success(t *testing.T) { 24 | s, err := pubSQS.NewMessageSink(pubSQS.MessageSinkConfig{ 25 | Client: &mockQueue{}, 26 | }) 27 | if err != nil { 28 | t.Fatalf("failed toget SQS message sink: %v", err) 29 | } 30 | 31 | msg := message{payload: "test string"} 32 | if err := s.PutMessage(&msg); err != nil { 33 | t.Errorf("unexpected error: %v", err) 34 | } 35 | } 36 | 37 | func Test_PutMessageSinkClosed_Fail(t *testing.T) { 38 | s, err := pubSQS.NewMessageSink(pubSQS.MessageSinkConfig{ 39 | Client: &mockQueue{}, 40 | }) 41 | if err != nil { 42 | t.Fatalf("failed toget SQS message sink: %v", err) 43 | } 44 | 45 | if err := s.Close(); err != nil { 46 | t.Fatalf("unexpected error while closing sink: %v", err) 47 | } 48 | 49 | err = s.PutMessage(&message{}) 50 | if !strings.Contains(err.Error(), "SQS connection closed") { 51 | t.Errorf("expected error about closed connection, got: %v", err) 52 | } 53 | } 54 | 55 | func Test_PutMessageInvalid_Fail(t *testing.T) { 56 | fakeErr := errors.New("fake error") 57 | s, err := pubSQS.NewMessageSink(pubSQS.MessageSinkConfig{ 58 | Client: &mockQueue{}, 59 | }) 60 | if err != nil { 61 | t.Fatalf("failed toget SQS message sink: %v", err) 62 | } 63 | 64 | err = s.PutMessage(&mockMessage{err: fakeErr}) 65 | if err == nil { 66 | t.Fatalf("expected marshalling error, got: ") 67 | } 68 | 69 | if errors.Cause(err) != fakeErr { 70 | t.Errorf("expected fake error, got: %v", err) 71 | } 72 | 73 | if !strings.Contains(err.Error(), "failed to marshal SQS message") { 74 | t.Errorf("expected failed marshalling error, got: %v", err) 75 | } 76 | 77 | st, err := s.Status() 78 | if err != nil { 79 | t.Errorf("unexpected status error: %v", err) 80 | } 81 | 82 | if !st.Working { 83 | t.Errorf("status working should be true because SQS didn't fail") 84 | } 85 | 86 | if len(st.Problems) != 0 { 87 | t.Errorf("expected 0 status problems, got: %d: %v", len(st.Problems), st.Problems) 88 | } 89 | } 90 | 91 | func Test_PutMessage_Fail(t *testing.T) { 92 | fakeErr := errors.New("fake error") 93 | s, err := pubSQS.NewMessageSink(pubSQS.MessageSinkConfig{ 94 | Client: &mockQueue{sendErr: fakeErr}, 95 | }) 96 | 97 | if err != nil { 98 | t.Fatalf("failed toget SQS message sink: %v", err) 99 | } 100 | 101 | err = s.PutMessage(&message{}) 102 | if errors.Cause(err) != fakeErr { 103 | t.Errorf("expected fake error, got: %v", err) 104 | } 105 | 106 | st, err := s.Status() 107 | if err != nil { 108 | t.Errorf("unexpected status error: %v", err) 109 | } 110 | 111 | if st.Working { 112 | t.Errorf("status working should be false because SQS failed") 113 | } 114 | 115 | if len(st.Problems) != 1 { 116 | t.Errorf("expected 1 status problems, got: %d: %v", len(st.Problems), st.Problems) 117 | } 118 | 119 | if st.Problems[0] != fakeErr.Error() { 120 | t.Errorf("status problem should be fake error, got: %v", st.Problems[0]) 121 | } 122 | } 123 | 124 | func Test_PutMessageAlreadyClosed_Fail(t *testing.T) { 125 | s, err := pubSQS.NewMessageSink(pubSQS.MessageSinkConfig{ 126 | Client: &mockQueue{}, 127 | }) 128 | 129 | if err != nil { 130 | t.Fatalf("failed toget SQS message sink: %v", err) 131 | } 132 | 133 | if err := s.Close(); err != nil { 134 | t.Fatalf("unexpected error while closing sink: %v", err) 135 | } 136 | 137 | err = s.Close() 138 | if err == nil { 139 | t.Fatalf("expected error about already closed connection") 140 | } 141 | 142 | if !strings.Contains(err.Error(), "already closed") { 143 | t.Errorf("expected closed error, got: %v", err) 144 | } 145 | } 146 | 147 | type mockMessage struct { 148 | err error 149 | } 150 | 151 | func (mm *mockMessage) Marshal() ([]byte, error) { 152 | return nil, mm.err 153 | } 154 | 155 | type mockQueue struct { 156 | messages []*awsSQS.Message 157 | receiveErr error 158 | deleteErr error 159 | sendErr error 160 | } 161 | 162 | func (m *mockQueue) SendMessage(input *awsSQS.SendMessageInput) (*awsSQS.SendMessageOutput, error) { 163 | return nil, m.sendErr 164 | } 165 | 166 | func (m *mockQueue) ReceiveMessage(input *awsSQS.ReceiveMessageInput) (*awsSQS.ReceiveMessageOutput, error) { 167 | return &awsSQS.ReceiveMessageOutput{ 168 | Messages: m.messages, 169 | }, m.receiveErr 170 | } 171 | 172 | func (m *mockQueue) DeleteMessage(input *awsSQS.DeleteMessageInput) (*awsSQS.DeleteMessageOutput, error) { 173 | return nil, m.deleteErr 174 | } 175 | 176 | type message struct { 177 | payload string 178 | err error 179 | } 180 | 181 | func (msg *message) Marshal() ([]byte, error) { 182 | return []byte(msg.payload), msg.err 183 | } 184 | -------------------------------------------------------------------------------- /sqs/source.go: -------------------------------------------------------------------------------- 1 | package sqs 2 | 3 | import ( 4 | "context" 5 | "time" 6 | 7 | "github.com/aws/aws-sdk-go/aws" 8 | awsSQS "github.com/aws/aws-sdk-go/service/sqs" 9 | "github.com/pkg/errors" 10 | pubsub "github.com/utilitywarehouse/go-pubsub" 11 | ) 12 | 13 | // MessageSourceConfig allows you to configure various source options. 14 | type MessageSourceConfig struct { 15 | // WaitSeconds is the wait time in seconds to wait between API polling requests. 16 | // If used, will make fewer requests per day which results in smaller AWS bill. 17 | // Defaults to 0 which effectively disables this. 18 | WaitSeconds time.Duration 19 | Client Queue 20 | QueueURL string 21 | } 22 | 23 | var errMissingClient = errors.New("SQS client must not be nil") 24 | 25 | type messageSource struct { 26 | q Queue 27 | receiveErr error // populated when receiving messages from SQS fails 28 | receiveInput *awsSQS.ReceiveMessageInput 29 | deleteInput *awsSQS.DeleteMessageInput 30 | waitSeconds time.Duration 31 | } 32 | 33 | // ConsumerError satisfies error interface and is returned when a message fails to be 34 | // handled. If you do a type switch on the error, you can extract the MsgID. 35 | type ConsumerError struct { 36 | MsgID string 37 | Value error 38 | } 39 | 40 | // NewMessageSource is the source constructor. If a nil client is passed an error is returned. 41 | func NewMessageSource(config MessageSourceConfig) (pubsub.MessageSource, error) { 42 | if config.Client == nil { 43 | return nil, errMissingClient 44 | } 45 | 46 | return &messageSource{ 47 | q: config.Client, 48 | waitSeconds: config.WaitSeconds, 49 | receiveInput: &awsSQS.ReceiveMessageInput{ 50 | QueueUrl: aws.String(config.QueueURL), 51 | AttributeNames: aws.StringSlice([]string{"All"}), 52 | MaxNumberOfMessages: aws.Int64(1), 53 | }, 54 | deleteInput: &awsSQS.DeleteMessageInput{QueueUrl: aws.String(config.QueueURL)}, 55 | }, nil 56 | } 57 | 58 | // ConsumeMessages polls messages from SQS 59 | func (s *messageSource) ConsumeMessages(ctx context.Context, handler pubsub.ConsumerMessageHandler, 60 | onError pubsub.ConsumerErrorHandler) error { 61 | 62 | for { 63 | select { 64 | case <-ctx.Done(): 65 | return ctx.Err() 66 | default: 67 | time.Sleep(s.waitSeconds * time.Second) 68 | msgs, err := s.q.ReceiveMessage(s.receiveInput) 69 | s.receiveErr = nil 70 | 71 | if err != nil { 72 | s.receiveErr = err 73 | return err 74 | } 75 | 76 | for _, msg := range msgs.Messages { 77 | consumerMsg := pubsub.ConsumerMessage{Data: []byte(*msg.Body)} 78 | 79 | if err := handler(consumerMsg); err != nil { 80 | msgErr := ConsumerError{ 81 | MsgID: *msg.MessageId, 82 | Value: err, 83 | } 84 | 85 | if innerErr := onError(consumerMsg, &msgErr); innerErr != nil { 86 | return innerErr 87 | } 88 | } 89 | 90 | delInput := s.deleteInput 91 | delInput.ReceiptHandle = msg.ReceiptHandle 92 | 93 | // once the message has been successfully consumed, delete it 94 | if _, err := s.q.DeleteMessage(delInput); err != nil { 95 | return errors.Wrap(err, "failed to delete SQS message") 96 | } 97 | } 98 | } 99 | } 100 | } 101 | 102 | // Error method satisfies Error interface of standard library for ConsumerError struct. 103 | func (e *ConsumerError) Error() string { 104 | return e.Value.Error() 105 | } 106 | 107 | // Status method is used to understand if the service is healthy or not. 108 | // In order to prevent making unnecessary API requests to AWS we rely on 109 | // sqs.ReceiveMessage error to set `receiveErr` field in Consumer. 110 | func (s *messageSource) Status() (*pubsub.Status, error) { 111 | st := pubsub.Status{Working: true} 112 | 113 | if s.receiveErr != nil { 114 | st.Working = false 115 | st.Problems = append(st.Problems, s.receiveErr.Error()) 116 | } 117 | 118 | return &st, nil 119 | } 120 | -------------------------------------------------------------------------------- /sqs/source_test.go: -------------------------------------------------------------------------------- 1 | package sqs_test 2 | 3 | import ( 4 | "context" 5 | "strings" 6 | "testing" 7 | "time" 8 | 9 | "github.com/aws/aws-sdk-go/service/sqs" 10 | "github.com/pkg/errors" 11 | pubsub "github.com/utilitywarehouse/go-pubsub" 12 | pubSQS "github.com/utilitywarehouse/go-pubsub/sqs" 13 | ) 14 | 15 | func Test_ConsumeMessagesMissingClient_Fail(t *testing.T) { 16 | _, err := pubSQS.NewMessageSource(pubSQS.MessageSourceConfig{}) 17 | if err == nil { 18 | t.Fatal("expected error about missing SQS client, got: ") 19 | } 20 | 21 | if !strings.Contains(err.Error(), "SQS client must not be nil") { 22 | t.Errorf("expected error about missing SQS client: got: %v", err) 23 | } 24 | } 25 | 26 | // context canceled 27 | func Test_ConsumeMessagesCtxCancelled_Fail(t *testing.T) { 28 | ctx, cancel := context.WithCancel(context.Background()) 29 | handler := func(pubsub.ConsumerMessage) error { return nil } 30 | errHandler := func(pubsub.ConsumerMessage, error) error { return nil } 31 | 32 | timer := time.NewTimer(10 * time.Millisecond) 33 | go func() { 34 | <-timer.C 35 | cancel() 36 | }() 37 | 38 | c, err := pubSQS.NewMessageSource(pubSQS.MessageSourceConfig{ 39 | Client: &mockQueue{}, 40 | }) 41 | if err != nil { 42 | t.Fatalf("failed to get message source: %v", err) 43 | } 44 | 45 | err = c.ConsumeMessages(ctx, handler, errHandler) 46 | if err == nil { 47 | t.Error("expected context cancelation error, got ") 48 | } 49 | 50 | if !strings.Contains(err.Error(), "context canceled") { 51 | t.Errorf("expected context cancelation error, got: %v", err) 52 | } 53 | } 54 | 55 | // failed to receive messages, working = false, and 1st problem matches fake error 56 | func Test_ConsumeMessages_Fail(t *testing.T) { 57 | ctx := context.Background() 58 | handler := func(pubsub.ConsumerMessage) error { return nil } 59 | errHandler := func(pubsub.ConsumerMessage, error) error { return nil } 60 | 61 | fakeErr := errors.New("failed to connect to SQS") 62 | c, err := pubSQS.NewMessageSource(pubSQS.MessageSourceConfig{ 63 | Client: &mockQueue{receiveErr: fakeErr}, 64 | }) 65 | if err != nil { 66 | t.Fatalf("failed to get message source: %v", err) 67 | } 68 | 69 | err = c.ConsumeMessages(ctx, handler, errHandler) 70 | if errors.Cause(err) != fakeErr { 71 | t.Errorf("expected fake error, got: %v", err) 72 | } 73 | 74 | st, err := c.Status() 75 | if err != nil { 76 | t.Fatalf("unexpected error while trying to get status: %v", err) 77 | } 78 | 79 | if st.Working { 80 | t.Errorf("expected status to be not working") 81 | } 82 | 83 | if len(st.Problems) != 1 { 84 | t.Fatalf("expected 1 status problem, got: %d: %v", len(st.Problems), st.Problems) 85 | } 86 | 87 | if st.Problems[0] != fakeErr.Error() { 88 | t.Fatalf("expected 1st problem to be a fake error when polling, got: %v", st.Problems[0]) 89 | } 90 | } 91 | 92 | // handler and onError fails 93 | func Test_ConsumeMessages_Handler_Fail(t *testing.T) { 94 | ctx := context.Background() 95 | handler := func(pubsub.ConsumerMessage) error { return errors.New("could not handle message") } 96 | fakeErr := errors.New("error handler returned fake error") 97 | errHandler := func(pubsub.ConsumerMessage, error) error { return fakeErr } 98 | 99 | var msgs []*sqs.Message 100 | msgID, pld := "12345", "some payload for sqs" 101 | msg := sqs.Message{ 102 | MessageId: &msgID, 103 | Body: &pld, 104 | } 105 | msgs = append(msgs, &msg) 106 | 107 | c, err := pubSQS.NewMessageSource(pubSQS.MessageSourceConfig{ 108 | Client: &mockQueue{messages: msgs}, 109 | }) 110 | if err != nil { 111 | t.Fatalf("failed to get message source: %v", err) 112 | } 113 | 114 | err = c.ConsumeMessages(ctx, handler, errHandler) 115 | if errors.Cause(err) != fakeErr { 116 | t.Errorf("expected fake error, got: %v", err) 117 | } 118 | 119 | st, err := c.Status() 120 | if err != nil { 121 | t.Fatalf("unexpected error while trying to get status: %v", err) 122 | } 123 | 124 | if !st.Working { 125 | t.Errorf("expected status to be working") 126 | } 127 | 128 | if len(st.Problems) != 0 { 129 | t.Fatalf("expected 0 status problem, got: %d: %v", len(st.Problems), st.Problems) 130 | } 131 | } 132 | 133 | // handler succeeds, message fails to be deleted 134 | func Test_ConsumeMessages_DeleteMessage_Fail(t *testing.T) { 135 | ctx := context.Background() 136 | handler := func(pubsub.ConsumerMessage) error { return nil } 137 | errHandler := func(pubsub.ConsumerMessage, error) error { return nil } 138 | 139 | var msgs []*sqs.Message 140 | msgID, pld := "12345", "some payload for sqs" 141 | msg := sqs.Message{ 142 | MessageId: &msgID, 143 | Body: &pld, 144 | } 145 | msgs = append(msgs, &msg) 146 | 147 | fakeErr := errors.New("couldn't delete message") 148 | c, err := pubSQS.NewMessageSource(pubSQS.MessageSourceConfig{ 149 | Client: &mockQueue{ 150 | messages: msgs, 151 | deleteErr: fakeErr, 152 | }, 153 | }) 154 | if err != nil { 155 | t.Fatalf("failed to get message source: %v", err) 156 | } 157 | 158 | err = c.ConsumeMessages(ctx, handler, errHandler) 159 | if err == nil { 160 | t.Fatal("expected message deletion error, got ") 161 | } 162 | 163 | if errors.Cause(err) != fakeErr { 164 | t.Errorf("expected sqs response error, got: %v", err) 165 | } 166 | 167 | if !strings.Contains(err.Error(), "failed to delete SQS message") { 168 | t.Errorf("expected message deletion error, got: %v", err) 169 | } 170 | 171 | st, err := c.Status() 172 | if err != nil { 173 | t.Fatalf("unexpected error while trying to get status: %v", err) 174 | } 175 | 176 | // when we fail to delete a message we do not degrade status 177 | if !st.Working { 178 | t.Errorf("expected status to be working") 179 | } 180 | 181 | if len(st.Problems) != 0 { 182 | t.Fatalf("expected 0 status problem, got: %d: %v", len(st.Problems), st.Problems) 183 | } 184 | } 185 | 186 | // handler succeeds, message is deleted successfully 187 | func Test_ConsumeMessages_DeleteMessage_Success(t *testing.T) { 188 | ctx, cancel := context.WithCancel(context.Background()) 189 | handler := func(pubsub.ConsumerMessage) error { return nil } 190 | errHandler := func(pubsub.ConsumerMessage, error) error { return nil } 191 | 192 | var msgs []*sqs.Message 193 | msgID, pld := "12345", "some payload for sqs" 194 | msg := sqs.Message{ 195 | MessageId: &msgID, 196 | Body: &pld, 197 | } 198 | msgs = append(msgs, &msg) 199 | 200 | timer := time.NewTimer(time.Millisecond) 201 | go func() { 202 | <-timer.C 203 | cancel() 204 | }() 205 | 206 | c, err := pubSQS.NewMessageSource(pubSQS.MessageSourceConfig{ 207 | Client: &mockQueue{messages: msgs}, 208 | }) 209 | if err != nil { 210 | t.Fatalf("failed to get message source: %v", err) 211 | } 212 | 213 | err = c.ConsumeMessages(ctx, handler, errHandler) 214 | if err == nil { 215 | t.Fatal("expected cancelation error, got ") 216 | } 217 | 218 | if !strings.Contains(err.Error(), "context canceled") { 219 | t.Fatalf("expected cancelation error, got: %v", err) 220 | } 221 | 222 | st, err := c.Status() 223 | if err != nil { 224 | t.Fatalf("unexpected error while trying to get status: %v", err) 225 | } 226 | 227 | if !st.Working { 228 | t.Errorf("expected status to be working") 229 | } 230 | 231 | if len(st.Problems) != 0 { 232 | t.Fatalf("expected 0 status problem, got: %d: %v", len(st.Problems), st.Problems) 233 | } 234 | } 235 | 236 | func Test_ConsumerError_Success(t *testing.T) { 237 | msgID, value := "12345", errors.New("couldn't handle message") 238 | 239 | err := pubSQS.ConsumerError{ 240 | MsgID: msgID, 241 | Value: value, 242 | } 243 | 244 | if err.Error() != value.Error() { 245 | t.Errorf("unexpected consumer error value, expected: %v, got: %v", value, err.Error()) 246 | } 247 | } 248 | -------------------------------------------------------------------------------- /sqs/sqs.go: -------------------------------------------------------------------------------- 1 | package sqs 2 | 3 | import awsSQS "github.com/aws/aws-sdk-go/service/sqs" 4 | 5 | // Queue interface has the same signature as the methods in SQS struct in aws-sdk-go. 6 | // Holds only the messages we're interested in pubsub. 7 | type Queue interface { 8 | ReceiveMessage(*awsSQS.ReceiveMessageInput) (*awsSQS.ReceiveMessageOutput, error) 9 | DeleteMessage(*awsSQS.DeleteMessageInput) (*awsSQS.DeleteMessageOutput, error) 10 | SendMessage(*awsSQS.SendMessageInput) (*awsSQS.SendMessageOutput, error) 11 | } 12 | --------------------------------------------------------------------------------