Skip to content

Commit

Permalink
feat: Add DeliveryAttemptRepository (#4)
Browse files Browse the repository at this point in the history
  • Loading branch information
allisson committed Mar 2, 2021
1 parent b4e974e commit ea04c82
Show file tree
Hide file tree
Showing 6 changed files with 216 additions and 7 deletions.
22 changes: 22 additions & 0 deletions db/migrations/000001_create_initial_schema.up.sql
Expand Up @@ -38,3 +38,25 @@ CREATE INDEX IF NOT EXISTS deliveries_webhook_id_idx ON deliveries (webhook_id);
CREATE INDEX IF NOT EXISTS deliveries_status_idx ON deliveries (status);
CREATE INDEX IF NOT EXISTS deliveries_scheduled_at_idx ON deliveries USING BRIN(scheduled_at);
CREATE INDEX IF NOT EXISTS deliveries_created_at_idx ON deliveries USING BRIN(created_at);

-- delivery_attempts table

CREATE TABLE IF NOT EXISTS delivery_attempts(
id UUID PRIMARY KEY,
webhook_id UUID NOT NULL,
delivery_id UUID NOT NULL,
response_headers TEXT NOT NULL,
response_body TEXT NOT NULL,
response_status_code SMALLINT NOT NULL,
execution_duration SMALLINT NOT NULL,
success BOOLEAN NOT NULL,
error TEXT NOT NULL,
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
FOREIGN KEY (webhook_id) REFERENCES webhooks (id) ON DELETE CASCADE,
FOREIGN KEY (delivery_id) REFERENCES deliveries (id) ON DELETE CASCADE
);

CREATE INDEX IF NOT EXISTS delivery_attempts_webhook_id_idx ON delivery_attempts (webhook_id);
CREATE INDEX IF NOT EXISTS delivery_attempts_delivery_id_idx ON delivery_attempts (delivery_id);
CREATE INDEX IF NOT EXISTS delivery_attempts_success_idx ON delivery_attempts (success);
CREATE INDEX IF NOT EXISTS delivery_attempts_created_at_idx ON delivery_attempts USING BRIN(created_at);
2 changes: 1 addition & 1 deletion entity.go
Expand Up @@ -80,7 +80,7 @@ func (d Delivery) Validate() error {
// DeliveryAttempt represents a delivery attempt.
type DeliveryAttempt struct {
ID ID `json:"id" db:"id"`
WebhookID ID `json:"destination_id" db:"destination_id"`
WebhookID ID `json:"webhook_id" db:"webhook_id"`
DeliveryID ID `json:"delivery_id" db:"delivery_id"`
ResponseHeaders string `json:"response_headers" db:"response_headers"`
ResponseBody string `json:"response_body" db:"response_body"`
Expand Down
7 changes: 7 additions & 0 deletions repository.go
Expand Up @@ -30,3 +30,10 @@ type DeliveryRepository interface {
Update(delivery *Delivery) error
Delete(id ID) error
}

// DeliveryAttemptRepository is the interface that will be used to iterate with the DeliveryAttempt data.
type DeliveryAttemptRepository interface {
Get(getOptions *RepositoryGetOptions) (*DeliveryAttempt, error)
List(listOptions *RepositoryListOptions) ([]*DeliveryAttempt, error)
Create(deliveryAttempt *DeliveryAttempt) error
}
78 changes: 78 additions & 0 deletions repository/delivery_attempt.go
@@ -0,0 +1,78 @@
package repository

import (
"github.com/allisson/postmand"
"github.com/huandu/go-sqlbuilder"
"github.com/jmoiron/sqlx"
)

// DeliveryAttempt implements postmand.DeliveryAttemptRepository interface.
type DeliveryAttempt struct {
db *sqlx.DB
}

// Get returns postmand.DeliveryAttempt by options filter.
func (d DeliveryAttempt) Get(getOptions *postmand.RepositoryGetOptions) (*postmand.DeliveryAttempt, error) {
deliveryAttempt := postmand.DeliveryAttempt{}
sb := sqlbuilder.PostgreSQL.NewSelectBuilder()
sb.Select("*").From("delivery_attempts")
for key, value := range getOptions.Filters {
sb.Where(sb.Equal(key, value))
}
sql, args := sb.Build()
err := d.db.Get(&deliveryAttempt, sql, args...)
return &deliveryAttempt, err
}

// List returns a slice of postmand.DeliveryAttempt by options filter.
func (d DeliveryAttempt) List(listOptions *postmand.RepositoryListOptions) ([]*postmand.DeliveryAttempt, error) {
deliveryAttempts := []*postmand.DeliveryAttempt{}
sb := sqlbuilder.PostgreSQL.NewSelectBuilder()
sb.Select("*").From("delivery_attempts").Limit(listOptions.Limit).Offset(listOptions.Offset)
for key, value := range listOptions.Filters {
sb.Where(sb.Equal(key, value))
}
if listOptions.OrderBy != "" {
sb.OrderBy(listOptions.OrderBy)
}
sql, args := sb.Build()
err := d.db.Select(&deliveryAttempts, sql, args...)
return deliveryAttempts, err
}

// Create postmand.DeliveryAttempt on database.
func (d DeliveryAttempt) Create(deliveryAttempt *postmand.DeliveryAttempt) error {
sqlStatement := `
INSERT INTO delivery_attempts (
"id",
"webhook_id",
"delivery_id",
"response_headers",
"response_body",
"response_status_code",
"execution_duration",
"success",
"error",
"created_at"
)
VALUES (
:id,
:webhook_id,
:delivery_id,
:response_headers,
:response_body,
:response_status_code,
:execution_duration,
:success,
:error,
:created_at
)
`
_, err := d.db.NamedExec(sqlStatement, deliveryAttempt)
return err
}

// NewDeliveryAttempt returns postmand.DeliveryAttempt with db connection.
func NewDeliveryAttempt(db *sqlx.DB) *DeliveryAttempt {
return &DeliveryAttempt{db: db}
}
100 changes: 100 additions & 0 deletions repository/delivery_attempt_test.go
@@ -0,0 +1,100 @@
package repository

import (
"testing"

"github.com/allisson/postmand"
"github.com/google/uuid"
"github.com/stretchr/testify/assert"
)

func makeDeliveryAttempt() *postmand.DeliveryAttempt {
return &postmand.DeliveryAttempt{
ID: uuid.New(),
ResponseHeaders: `{"Content-Type": ["application/json; charset=utf-8"]}`,
ResponseBody: `{"message":"A requisição para registrar essa transação foi enviada."}`,
ResponseStatusCode: 201,
ExecutionDuration: 150,
Success: true,
Error: "",
}
}

func TestDeliveryAttempt(t *testing.T) {
t.Run("Create delivery attempt", func(t *testing.T) {
th := newTestHelper()
defer th.db.Close()

webhook := makeWebhook()
err := th.webhookRepository.Create(webhook)
assert.Nil(t, err)

delivery := makeDelivery()
delivery.WebhookID = webhook.ID
err = th.deliveryRepository.Create(delivery)
assert.Nil(t, err)

deliveryAttempt := makeDeliveryAttempt()
deliveryAttempt.WebhookID = webhook.ID
deliveryAttempt.DeliveryID = delivery.ID
err = th.deliveryAttemptRepository.Create(deliveryAttempt)
assert.Nil(t, err)
})

t.Run("Get delivery attempt", func(t *testing.T) {
th := newTestHelper()
defer th.db.Close()

webhook := makeWebhook()
err := th.webhookRepository.Create(webhook)
assert.Nil(t, err)

delivery := makeDelivery()
delivery.WebhookID = webhook.ID
err = th.deliveryRepository.Create(delivery)
assert.Nil(t, err)

deliveryAttempt := makeDeliveryAttempt()
deliveryAttempt.WebhookID = webhook.ID
deliveryAttempt.DeliveryID = delivery.ID
err = th.deliveryAttemptRepository.Create(deliveryAttempt)
assert.Nil(t, err)

options := postmand.RepositoryGetOptions{Filters: map[string]interface{}{"id": deliveryAttempt.ID}}
deliveryAttemptFromRepository, err := th.deliveryAttemptRepository.Get(&options)
assert.Nil(t, err)
assert.Equal(t, deliveryAttempt.ID, deliveryAttemptFromRepository.ID)
})

t.Run("List delivery attempts", func(t *testing.T) {
th := newTestHelper()
defer th.db.Close()

webhook := makeWebhook()
err := th.webhookRepository.Create(webhook)
assert.Nil(t, err)

delivery := makeDelivery()
delivery.WebhookID = webhook.ID
err = th.deliveryRepository.Create(delivery)
assert.Nil(t, err)

deliveryAttempt1 := makeDeliveryAttempt()
deliveryAttempt1.WebhookID = webhook.ID
deliveryAttempt1.DeliveryID = delivery.ID
err = th.deliveryAttemptRepository.Create(deliveryAttempt1)
assert.Nil(t, err)

deliveryAttempt2 := makeDeliveryAttempt()
deliveryAttempt2.WebhookID = webhook.ID
deliveryAttempt2.DeliveryID = delivery.ID
err = th.deliveryAttemptRepository.Create(deliveryAttempt2)
assert.Nil(t, err)

options := postmand.RepositoryListOptions{Limit: 1, Offset: 1, OrderBy: "created_at DESC"}
deliveryAttempts, err := th.deliveryAttemptRepository.List(&options)
assert.Nil(t, err)
assert.Len(t, deliveryAttempts, 1)
assert.Equal(t, deliveryAttempt2.ID, deliveryAttempts[0].ID)
})
}
14 changes: 8 additions & 6 deletions repository/util_test.go
Expand Up @@ -17,17 +17,19 @@ func init() {
}

type testHelper struct {
db *sqlx.DB
webhookRepository *Webhook
deliveryRepository *Delivery
db *sqlx.DB
webhookRepository *Webhook
deliveryRepository *Delivery
deliveryAttemptRepository *DeliveryAttempt
}

func newTestHelper() testHelper {
cName := fmt.Sprintf("connection_%d", time.Now().UnixNano())
db, _ := sqlx.Open("pgx", cName)
return testHelper{
db: db,
webhookRepository: NewWebhook(db),
deliveryRepository: NewDelivery(db),
db: db,
webhookRepository: NewWebhook(db),
deliveryRepository: NewDelivery(db),
deliveryAttemptRepository: NewDeliveryAttempt(db),
}
}

0 comments on commit ea04c82

Please sign in to comment.