Skip to content

Commit

Permalink
feat: Add entity file (#1)
Browse files Browse the repository at this point in the history
  • Loading branch information
allisson committed Mar 1, 2021
1 parent 9de70b5 commit 846ef5c
Show file tree
Hide file tree
Showing 7 changed files with 261 additions and 0 deletions.
41 changes: 41 additions & 0 deletions .github/workflows/verify.yml
@@ -0,0 +1,41 @@
name: Verify

on:
push:
branches:
- "**"
- "!main"

jobs:
verify:
name: Test
runs-on: ubuntu-latest
steps:
- name: Set up Go 1.16
uses: actions/setup-go@v2
with:
go-version: 1.16
id: go

- name: Check out code into the Go module directory
uses: actions/checkout@v2

- name: Set cache
uses: actions/cache@v2
with:
path: ~/go/pkg/mod
key: ${{ runner.os }}-go-${{ hashFiles('**/go.sum') }}
restore-keys: |
${{ runner.os }}-go-
- name: Get dependencies
run: go mod download

- name: Run golangci-lint
uses: golangci/golangci-lint-action@v2
with:
version: latest
args: -E gosec

- name: Run Tests
run: go test -covermode=count -coverprofile=count.out -v ./...
3 changes: 3 additions & 0 deletions .gitignore
Expand Up @@ -13,3 +13,6 @@

# Dependency directories (remove the comment below to include it)
# vendor/

*.log
bin/
13 changes: 13 additions & 0 deletions Makefile
@@ -0,0 +1,13 @@
PLATFORM := $(shell uname | tr A-Z a-z)

lint:
if [ ! -f ./bin/golangci-lint ] ; \
then \
curl -sfL https://raw.githubusercontent.com/golangci/golangci-lint/master/install.sh | sh -s v1.37.1; \
fi;
./bin/golangci-lint run

test:
go test -covermode=count -coverprofile=count.out -v ./...

.PHONY: lint test
83 changes: 83 additions & 0 deletions entity.go
@@ -0,0 +1,83 @@
package postmand

import (
"time"

validation "github.com/go-ozzo/ozzo-validation/v4"
"github.com/go-ozzo/ozzo-validation/v4/is"
"github.com/google/uuid"
)

const (
// DeliveryStatusPending represents the delivery pending status
DeliveryStatusPending = "pending"
// DeliveryStatusFailed represents the delivery failed status
DeliveryStatusFailed = "failed"
// DeliveryStatusCompleted represents the delivery completed status
DeliveryStatusCompleted = "completed"
)

// Webhook represents a webhook in the system.
type Webhook struct {
ID uuid.UUID `json:"id" db:"id"`
Name string `json:"name" db:"name"`
URL string `json:"url" db:"url"`
ContentType string `json:"content_type" db:"content_type"`
SecretToken string `json:"secret_token" db:"secret_token"`
MaxDeliveryAttempts int `json:"max_delivery_attempts" db:"max_delivery_attempts"`
DeliveryAttemptTimeout int `json:"delivery_attempt_timeout" db:"delivery_attempt_timeout"`
RetryMinBackoff int `json:"retry_min_backoff" db:"retry_min_backoff"`
RetryMaxBackoff int `json:"retry_max_backoff" db:"retry_max_backoff"`
CreatedAt time.Time `json:"created_at" db:"created_at"`
UpdatedAt time.Time `json:"updated_at" db:"updated_at"`
}

// Validate implements ozzo validation Validatable interface
func (w Webhook) Validate() error {
return validation.ValidateStruct(&w,
validation.Field(&w.ID, validation.Required, is.UUIDv4),
validation.Field(&w.Name, validation.Required, validation.Length(3, 255)),
validation.Field(&w.URL, validation.Required, is.URL),
validation.Field(&w.ContentType, validation.Required, validation.In("application/x-www-form-urlencoded", "application/json")),
validation.Field(&w.MaxDeliveryAttempts, validation.Required, validation.Min(1)),
validation.Field(&w.DeliveryAttemptTimeout, validation.Required, validation.Min(1)),
validation.Field(&w.RetryMinBackoff, validation.Required, validation.Min(1)),
validation.Field(&w.RetryMaxBackoff, validation.Required, validation.Min(1)),
)
}

// Delivery represents a payload that must be delivery using webhook context.
type Delivery struct {
ID uuid.UUID `json:"id" db:"id"`
WebhookID uuid.UUID `json:"webhook_id" db:"webhook_id"`
Payload string `json:"payload" db:"payload"`
ScheduledAt time.Time `json:"scheduled_at" db:"scheduled_at"`
DeliveryAttempts int `json:"delivery_attempts" db:"delivery_attempts"`
Status string `json:"status" db:"status"`
CreatedAt time.Time `json:"created_at" db:"created_at"`
UpdatedAt time.Time `json:"updated_at" db:"updated_at"`
}

// Validate implements ozzo validation Validatable interface
func (d Delivery) Validate() error {
return validation.ValidateStruct(&d,
validation.Field(&d.ID, validation.Required, is.UUIDv4),
validation.Field(&d.WebhookID, validation.Required, is.UUIDv4),
validation.Field(&d.ScheduledAt, validation.Required),
validation.Field(&d.Status, validation.Required, validation.In("pending", "completed", "failed")),
)
}

// DeliveryAttempt represents a delivery attempt.
type DeliveryAttempt struct {
ID uuid.UUID `json:"id" db:"id"`
WebhookID uuid.UUID `json:"destination_id" db:"destination_id"`
DeliveryID uuid.UUID `json:"delivery_id" db:"delivery_id"`
ResponseHeaders string `json:"response_headers" db:"response_headers"`
ResponseBody string `json:"response_body" db:"response_body"`
ResponseStatusCode int `json:"response_status_code" db:"response_status_code"`
ExecutionDuration int `json:"execution_duration" db:"execution_duration"`
Success bool `json:"success" db:"success"`
Error string `json:"error" db:"error"`
CreatedAt time.Time `json:"created_at" db:"created_at"`
}
93 changes: 93 additions & 0 deletions entity_test.go
@@ -0,0 +1,93 @@
package postmand

import (
"encoding/json"
"strings"
"testing"
"time"

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

func TestWebhook(t *testing.T) {
var tests = []struct {
kind string
request Webhook
expectedPayload string
}{
{
"required fields",
Webhook{},
`{"content_type":"cannot be blank","delivery_attempt_timeout":"cannot be blank","id":"must be a valid UUID v4","max_delivery_attempts":"cannot be blank","name":"cannot be blank","retry_max_backoff":"cannot be blank","retry_min_backoff":"cannot be blank","url":"cannot be blank"}`,
},
{
"Short name",
Webhook{ID: uuid.New(), Name: "A", URL: "https://httpbin.org/post", ContentType: "application/json", MaxDeliveryAttempts: 1, DeliveryAttemptTimeout: 1, RetryMinBackoff: 1, RetryMaxBackoff: 1},
`{"name":"the length must be between 3 and 255"}`,
},
{
"Long name",
Webhook{ID: uuid.New(), Name: strings.Repeat("A", 300), URL: "https://httpbin.org/post", ContentType: "application/json", MaxDeliveryAttempts: 1, DeliveryAttemptTimeout: 1, RetryMinBackoff: 1, RetryMaxBackoff: 1},
`{"name":"the length must be between 3 and 255"}`,
},
{
"Content type invalid option",
Webhook{ID: uuid.New(), Name: "AAA", URL: "https://httpbin.org/post", ContentType: "text/html", MaxDeliveryAttempts: 1, DeliveryAttemptTimeout: 1, RetryMinBackoff: 1, RetryMaxBackoff: 1},
`{"content_type":"must be a valid value"}`,
},
}
for _, tt := range tests {
t.Run(tt.kind, func(t *testing.T) {
err := tt.request.Validate()
assert.NotNil(t, err)
errorPayload, err := json.Marshal(err)
assert.Nil(t, err)
assert.Equal(t, tt.expectedPayload, string(errorPayload))
})
}

webhook := Webhook{ID: uuid.New(), Name: "AAA", URL: "https://httpbin.org/post", ContentType: "application/json", MaxDeliveryAttempts: 1, DeliveryAttemptTimeout: 1, RetryMinBackoff: 1, RetryMaxBackoff: 1}
err := webhook.Validate()
assert.Nil(t, err)
}

func TestDelivery(t *testing.T) {
var tests = []struct {
kind string
request Delivery
expectedPayload string
}{
{
"required fields",
Delivery{},
`{"id":"must be a valid UUID v4","scheduled_at":"cannot be blank","status":"cannot be blank","webhook_id":"must be a valid UUID v4"}`,
},
{
"invalid status option",
Delivery{ID: uuid.New(), WebhookID: uuid.New(), ScheduledAt: time.Now().UTC(), Status: "error"},
`{"status":"must be a valid value"}`,
},
}
for _, tt := range tests {
t.Run(tt.kind, func(t *testing.T) {
err := tt.request.Validate()
assert.NotNil(t, err)
errorPayload, err := json.Marshal(err)
assert.Nil(t, err)
assert.Equal(t, tt.expectedPayload, string(errorPayload))
})
}

delivery := Delivery{
ID: uuid.New(),
WebhookID: uuid.New(),
Payload: `{"success": true}`,
ScheduledAt: time.Now().UTC(),
Status: "pending",
CreatedAt: time.Now().UTC(),
UpdatedAt: time.Now().UTC(),
}
err := delivery.Validate()
assert.Nil(t, err)
}
9 changes: 9 additions & 0 deletions go.mod
@@ -0,0 +1,9 @@
module github.com/allisson/postmand

go 1.16

require (
github.com/go-ozzo/ozzo-validation/v4 v4.3.0
github.com/google/uuid v1.2.0
github.com/stretchr/testify v1.7.0
)
19 changes: 19 additions & 0 deletions go.sum
@@ -0,0 +1,19 @@
github.com/asaskevich/govalidator v0.0.0-20200108200545-475eaeb16496 h1:zV3ejI06GQ59hwDQAvmK1qxOQGB3WuVTRoY0okPTAv0=
github.com/asaskevich/govalidator v0.0.0-20200108200545-475eaeb16496/go.mod h1:oGkLhpf+kjZl6xBf758TQhh5XrAeiJv/7FRz/2spLIg=
github.com/davecgh/go-spew v1.1.0 h1:ZDRjVQ15GmhC3fiQ8ni8+OwkZQO4DARzQgrnXU1Liz8=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/go-ozzo/ozzo-validation/v4 v4.3.0 h1:byhDUpfEwjsVQb1vBunvIjh2BHQ9ead57VkAEY4V+Es=
github.com/go-ozzo/ozzo-validation/v4 v4.3.0/go.mod h1:2NKgrcHl3z6cJs+3Oo940FPRiTzuqKbvfrL2RxCj6Ew=
github.com/google/uuid v1.2.0 h1:qJYtXnJRWmpe7m/3XlyhrsLrEURqHRM2kxzoxXqyUDs=
github.com/google/uuid v1.2.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY=
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c h1:dUUwHk2QECo/6vqA44rthZ8ie2QXMNeKRTHCNY2nXvo=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=

0 comments on commit 846ef5c

Please sign in to comment.