Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
7 changed files
with
261 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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 ./... |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -13,3 +13,6 @@ | |
|
||
# Dependency directories (remove the comment below to include it) | ||
# vendor/ | ||
|
||
*.log | ||
bin/ |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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"` | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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 | ||
) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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= |