Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: Add entity file #1

Merged
merged 1 commit into from Mar 1, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
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=