Skip to content

Commit

Permalink
Merge pull request #127 from wepala/feature/WEOS-1342
Browse files Browse the repository at this point in the history
feature: WEOS-1342 As a developer I should be able have a datetime field automatically update on update
  • Loading branch information
akeemphilbert authored Mar 17, 2022
2 parents 982e8bd + d082973 commit 1e232e0
Show file tree
Hide file tree
Showing 12 changed files with 713 additions and 5 deletions.
5 changes: 5 additions & 0 deletions api.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -94,9 +94,14 @@ components:
lastUpdated:
type: string
format: date-time
x-update:
- Add Blog
- Update Blog
created:
type: string
format: date-time
x-update:
- Add Blog
required:
- title
- url
Expand Down
1 change: 1 addition & 0 deletions context/context.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ const HeaderXLogLevel = "X-LOG-LEVEL"

//add more keys here if needed
const ACCOUNT_ID ContextKey = "ACCOUNT_ID"
const OPERATION_ID = "OPERATION_ID"
const USER_ID ContextKey = "USER_ID"
const LOG_LEVEL ContextKey = "LOG_LEVEL"
const REQUEST_ID ContextKey = "REQUEST_ID"
Expand Down
31 changes: 31 additions & 0 deletions controllers/rest/api.go
Original file line number Diff line number Diff line change
Expand Up @@ -416,6 +416,37 @@ func (p *RESTAPI) Initialize(ctxt context.Context) error {
}
}

//this ranges over the paths and pulls out the operationIDs into an array
opIDs := []string{}
idFound := false
for _, pathData := range p.Swagger.Paths {
for _, op := range pathData.Operations() {
if op.OperationID != "" {
opIDs = append(opIDs, op.OperationID)
}
}
}

//this ranges over the properties, pulls the x-update and them compares it against the valid operation ids in the yaml
for _, scheme := range p.Swagger.Components.Schemas {
for _, prop := range scheme.Value.Properties {
xUpdate := []string{}
xUpdateBytes, _ := json.Marshal(prop.Value.Extensions["x-update"])
json.Unmarshal(xUpdateBytes, &xUpdate)
for _, r := range xUpdate {
idFound = false
for _, id := range opIDs {
if r == id {
idFound = true
}
}
if !idFound {
return fmt.Errorf("provided x-update operation id: %s is invalid", r)
}
}
}
}

//get the database schema
schemas = CreateSchema(ctxt, p.EchoInstance(), p.Swagger)
p.Schemas = schemas
Expand Down
43 changes: 43 additions & 0 deletions controllers/rest/api_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import (
"bytes"
"encoding/json"
"github.com/labstack/echo/v4"
"io/ioutil"
"net/http"
"net/http/httptest"
"os"
Expand Down Expand Up @@ -747,3 +748,45 @@ components:
})
os.Remove("test.db")
}

func TestRESTAPI_Integration_AutomaticallyUpdateDateTime(t *testing.T) {
tapi, err := api.New("./fixtures/blog.yaml")
if err != nil {
t.Fatalf("un expected error loading spec '%s'", err)
}
err = tapi.Initialize(nil)
if err != nil {
t.Fatalf("un expected error loading spec '%s'", err)
}
e := tapi.EchoInstance()
t.Run("automatically updating create", func(t *testing.T) {
mockBlog := map[string]interface{}{"title": "Test Blog", "url": "www.testBlog.com"}
reqBytes, err := json.Marshal(mockBlog)
if err != nil {
t.Fatalf("error setting up request %s", err)
}
body := bytes.NewReader(reqBytes)
resp := httptest.NewRecorder()
req := httptest.NewRequest(http.MethodPost, "/blogs", body)
req.Header.Set(echo.HeaderContentType, echo.MIMEApplicationJSON)
e.ServeHTTP(resp, req)
if resp.Result().StatusCode != http.StatusCreated {
t.Errorf("expected the response code to be %d, got %d", http.StatusCreated, resp.Result().StatusCode)
}
defer resp.Result().Body.Close()
tResults, err := ioutil.ReadAll(resp.Result().Body)
if err != nil {
t.Errorf("unexpect error: %s", err)
}
results := map[string]interface{}{}
err = json.Unmarshal(tResults, &results)
if err != nil {
t.Errorf("unexpect error: %s", err)
}
if results["created"] == nil || results["lastUpdated"] == nil {
t.Errorf("unexpect error expected to find created and lastUpdated")
}

})

}
5 changes: 5 additions & 0 deletions controllers/rest/fixtures/blog.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -91,9 +91,14 @@ components:
lastUpdated:
type: string
format: date-time
x-update:
- Add Blog
- Update Blog
created:
type: string
format: date-time
x-update:
- Add Blog
required:
- title
- url
Expand Down
3 changes: 3 additions & 0 deletions controllers/rest/middleware_context.go
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,9 @@ func Context(api *RESTAPI, projection projections.Projection, commandDispatcher

cc, err = parseResponses(c, cc, operation)

//add OperationID to context
cc = context.WithValue(cc, weosContext.OPERATION_ID, operation.OperationID)

//parse request body based on content type
var payload []byte
ct := c.Request().Header.Get("Content-Type")
Expand Down
22 changes: 22 additions & 0 deletions controllers/rest/middleware_context_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -561,4 +561,26 @@ func TestContext(t *testing.T) {
e.GET("/blogs/:id", handler)
e.ServeHTTP(resp, req)
})

t.Run("add operationId to context", func(t *testing.T) {
path := swagger.Paths.Find("/blogs")
mw := rest.Context(restApi, nil, nil, nil, entityFactory, path, path.Get)
handler := mw(func(ctxt echo.Context) error {
//check that certain parameters are in the context
cc := ctxt.Request().Context()
value := cc.Value(context.OPERATION_ID)
if value == nil {
t.Fatalf("expected the operation id to have a value")
}
if value.(string) != "Get Blogs" {
t.Fatalf("expected the operation id to be Get Blogs")
}
return nil
})
e := echo.New()
resp := httptest.NewRecorder()
req := httptest.NewRequest(http.MethodGet, "/blogs", nil)
e.GET("/blogs", handler)
e.ServeHTTP(resp, req)
})
}
40 changes: 37 additions & 3 deletions end2end_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -760,7 +760,11 @@ func theServiceIsRunning() error {
})
err := API.Initialize(scenarioContext)
if err != nil {
return err
if strings.Contains(err.Error(), "provided x-update operation id") {
errs = err
} else {
return err
}
}
proj, err := API.GetProjection("Default")
if err == nil {
Expand Down Expand Up @@ -879,7 +883,7 @@ func theIsUpdated(contentType string, details *godog.Table) error {
}
}

contentEntity := map[string]interface{}{}
contentEntity = map[string]interface{}{}
var result *gorm.DB
//ETag would help with this
for key, value := range compare {
Expand Down Expand Up @@ -1758,6 +1762,35 @@ func theIdShouldBeA(arg1, format string) error {
return nil
}

func anErrorIsReturned() error {
if !strings.Contains(errs.Error(), "provided x-update operation id") {
return fmt.Errorf("expected the error to contain: %s, got %s", "provided x-update operation id", errs.Error())
}
return nil
}

func theFieldShouldHaveTodaysDate(field string) error {

timeNow := time.Now()
todaysDate := timeNow.Format("2006-01-02")

switch dbconfig.Driver {
case "postgres", "mysql":
date := contentEntity[field].(time.Time).Format("2006-01-02")
if !strings.Contains(date, todaysDate) {
return fmt.Errorf("expected the %s date: %s to contain the current date: %s ", field, date, todaysDate)
}

case "sqlite3":
date := contentEntity[field].(string)
if !strings.Contains(date, todaysDate) {
return fmt.Errorf("expected the %s date: %s to contain the current date: %s ", field, date, todaysDate)
}
}

return nil
}

func InitializeScenario(ctx *godog.ScenarioContext) {
ctx.Before(reset)
ctx.After(func(ctx context.Context, sc *godog.Scenario, err error) (context.Context, error) {
Expand Down Expand Up @@ -1859,7 +1892,8 @@ func InitializeScenario(ctx *godog.ScenarioContext) {
ctx.Step(`^"([^"]*)" set the default event store as "([^"]*)"$`, setTheDefaultEventStoreAs)
ctx.Step(`^the projection "([^"]*)" is not called$`, theProjectionIsNotCalled)
ctx.Step(`^the "([^"]*)" id should be a "([^"]*)"$`, theIdShouldBeA)

ctx.Step(`^an error is returned$`, anErrorIsReturned)
ctx.Step(`^the "([^"]*)" field should have today\'s date$`, theFieldShouldHaveTodaysDate)
}

func TestBDD(t *testing.T) {
Expand Down
Loading

0 comments on commit 1e232e0

Please sign in to comment.