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

Weos 1240 #46

Merged
merged 28 commits into from
Jan 18, 2022
Merged
Show file tree
Hide file tree
Changes from 26 commits
Commits
Show all changes
28 commits
Select commit Hold shift + click to select a range
39dbb94
WEOS-1256
atoniaw Jan 9, 2022
bb2afef
Merge remote-tracking branch 'origin/dev' into WEOS-1256
atoniaw Jan 9, 2022
2290033
Merge branch 'feature/WEOS-1135' into WEOS-1256
atoniaw Jan 9, 2022
1eaff9a
WEOS-1256 - Create projection functions to get entity by keys and by …
atoniaw Jan 9, 2022
da58ed9
WEOS-1256
atoniaw Jan 9, 2022
161f3df
WEOS-1256
atoniaw Jan 9, 2022
ac2e1ab
WEOS-1256
atoniaw Jan 10, 2022
ffddb07
Merge branch 'WEOS-1245' into WEOS-1256
atoniaw Jan 13, 2022
0f91fa6
WEOS-1256
atoniaw Jan 13, 2022
a13209a
WEOS-1245
atoniaw Jan 13, 2022
b29ebae
feature:WEOS-1132
RandyDeo Jan 13, 2022
e1b20d5
WEOS-1256
atoniaw Jan 13, 2022
baf3f71
feature:WEOS-1132
RandyDeo Jan 13, 2022
b3d8a0f
feature:WEOS-1132
RandyDeo Jan 14, 2022
1d313e4
feature:WEOS-1132
RandyDeo Jan 14, 2022
349245c
Merge pull request #40 from wepala/WEOS-1245
RandyDeo Jan 14, 2022
3d1901b
Merge pull request #41 from wepala/WEOS-1256
shaniah868 Jan 14, 2022
ee88035
feature:WEOS-1132
RandyDeo Jan 14, 2022
11548cf
Merge remote-tracking branch 'origin/feature/WEOS-1135' into WEOS-1240
RandyDeo Jan 14, 2022
3e56793
Merge branch 'feature/WEOS-1135' into WEOS-1240
RandyDeo Jan 14, 2022
39f725b
feature:WEOS-1132
RandyDeo Jan 14, 2022
6a5f7b2
feature:WEOS-1132
RandyDeo Jan 17, 2022
81a9cc4
Merge branch 'feature/WEOS-1132' into WEOS-1240
RandyDeo Jan 17, 2022
453fb2d
feature:WEOS-1132
RandyDeo Jan 17, 2022
bb6106c
feature:WEOS-1123
RandyDeo Jan 17, 2022
5221533
feature:WEOS-1132
RandyDeo Jan 17, 2022
71a506e
feature:WEOS-1132
RandyDeo Jan 17, 2022
c1433c2
feature:WEOS-1132
RandyDeo Jan 18, 2022
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
47 changes: 47 additions & 0 deletions controllers/rest/api_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -269,3 +269,50 @@ func TestRESTAPI_Initialize_ViewAddedToGet(t *testing.T) {
}
os.Remove("test.db")
}

func TestRESTAPI_Initialize_GetEntityBySequenceNuber(t *testing.T) {
os.Remove("test.db")
time.Sleep(1 * time.Second)
e := echo.New()
tapi := api.RESTAPI{}
_, err := api.Initialize(e, &tapi, "./fixtures/blog-create-batch.yaml")
if err != nil {
t.Fatalf("unexpected error '%s'", err)
}
mockBlog := &[3]Blog{
{ID: "1asdas3", Title: "Blog 1", Url: "www.testBlog1.com"},
{ID: "2gf233", Title: "Blog 2", Url: "www.testBlog2.com"},
{ID: "3dgff3", Title: "Blog 3", Url: "www.testBlog3.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)
e.ServeHTTP(resp, req)
//confirm that the response is 201
if resp.Result().StatusCode != http.StatusCreated {
t.Errorf("expected the response code to be %d, got %d", http.StatusCreated, resp.Result().StatusCode)
}

blogEntity, err := api.GetContentBySequenceNumber(tapi.Application.EventRepository(), "3dgff3", 4)
if err != nil {
t.Fatal(err)
}

mapEntity, ok := blogEntity.Property.(map[string]interface{})

if !ok {
t.Fatal("expected the properties of the blog entity to be mapable")
}
if mapEntity["title"] != "Blog 3" {
t.Errorf("expected the title to be %s got %s", "Blog 3", mapEntity["title"])
}

if blogEntity.SequenceNo != int64(1) {
t.Errorf("expected the sequence number to be %d got %d", blogEntity.SequenceNo, 1)
}
os.Remove("test.db")
}
17 changes: 14 additions & 3 deletions controllers/rest/utils.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,14 +4,15 @@ import (
"bufio"
"bytes"
"fmt"
"github.com/labstack/echo/v4"
"github.com/labstack/gommon/log"
"github.com/wepala/weos-service/model"
"io"
"io/ioutil"
"net/http"
"strconv"
"strings"

"github.com/labstack/echo/v4"
"github.com/labstack/gommon/log"
"github.com/wepala/weos-service/model"
)

//LoadHttpRequestFixture wrapper around the test helper to make it easier to use it with test table
Expand Down Expand Up @@ -197,3 +198,13 @@ func SplitEtag(Etag string) (string, string) {
seqNo := result[1]
return weosID, seqNo
}

func GetContentBySequenceNumber(eventRepository model.EventRepository, id string, sequence_no int64) (*model.ContentEntity, error) {
entity := &model.ContentEntity{}
events, err := eventRepository.GetByAggregateAndSequenceRange(id, 0, sequence_no)
if err != nil {
return nil, err
}
err = entity.ApplyChanges(events)
return entity, err
}
56 changes: 56 additions & 0 deletions controllers/rest/weos_mocks_test.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

6 changes: 5 additions & 1 deletion end2end_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -538,10 +538,14 @@ func theHeaderShouldBe(key, value string) error {
if seqNoEtag == "" {
return fmt.Errorf("expected the Etag to contain a sequence no, got %s", seqNoEtag)
}

if seqNoEtag != strings.Split(value, ".")[1] {
return fmt.Errorf("expected the Etag to contain a sequence no %s, got %s", strings.Split(value, ".")[1], seqNoEtag)
}
return nil
}

headers := rec.HeaderMap
headers := rec.Result().Header
val := []string{}

for k, v := range headers {
Expand Down
13 changes: 13 additions & 0 deletions model/content_entity.go
Original file line number Diff line number Diff line change
Expand Up @@ -131,6 +131,13 @@ func (w *ContentEntity) FromSchemaWithValues(ctx context.Context, schema *openap
return w, w.ApplyChanges([]*Event{event})
}

func (w *ContentEntity) Update(payload json.RawMessage) (*ContentEntity, error) {

event := NewEntityEvent("update", w, w.ID, payload)
w.NewChange(event)
return w, w.ApplyChanges([]*Event{event})
}

//GetString returns the string property value stored of a given the property name
func (w *ContentEntity) GetString(name string) string {
if w.Property == nil {
Expand Down Expand Up @@ -210,6 +217,12 @@ func (w *ContentEntity) ApplyChanges(changes []*Event) error {
return err
}
w.User.BasicEntity.ID = change.Meta.User
case "update":
err := json.Unmarshal(change.Payload, &w.Property)
if err != nil {
return err
}
w.User.BasicEntity.ID = change.Meta.User

}
}
Expand Down
59 changes: 59 additions & 0 deletions model/content_entity_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -116,3 +116,62 @@ func TestContentEntity_IsValid(t *testing.T) {
}
})
}

func TestContentEntity_Update(t *testing.T) {
swagger, err := openapi3.NewSwaggerLoader().LoadSwaggerFromFile("../controllers/rest/fixtures/blog.yaml")
if err != nil {
t.Fatalf("unexpected error occured '%s'", err)
}
var contentType string
var contentTypeSchema *openapi3.SchemaRef
contentType = "Blog"
contentTypeSchema = swagger.Components.Schemas[contentType]
ctx := context.Background()
ctx = context.WithValue(ctx, weosContext.CONTENT_TYPE, &weosContext.ContentType{
Name: contentType,
Schema: contentTypeSchema.Value,
})
ctx = context.WithValue(ctx, weosContext.USER_ID, "123")

mockBlog := &Blog{
Title: "test 1",
Description: "lorem ipsum",
Url: "www.ShaniahsBlog.com",
}
payload, err := json.Marshal(mockBlog)
if err != nil {
t.Fatalf("unexpected error marshalling payload '%s'", err)
}

existingEntity, err := new(model.ContentEntity).FromSchemaWithValues(ctx, swagger.Components.Schemas["Blog"].Value, payload)
if err != nil {
t.Fatalf("unexpected error instantiating content entity '%s'", err)
}

if existingEntity.GetString("Title") != "test 1" {
t.Errorf("expected the title to be '%s', got '%s'", "test 1", existingEntity.GetString("Title"))
}

input := &Blog{
Title: "updated title",
Description: "updated desc",
}

updatedPayload, err := json.Marshal(input)
if err != nil {
t.Fatalf("unexpected error marshalling update payload '%s'", err)
}

updatedEntity, err := existingEntity.Update(updatedPayload)
if err != nil {
t.Fatalf("unexpected error updating existing entity '%s'", err)
}

if updatedEntity.GetString("Title") != "updated title" {
t.Errorf("expected the updated title to be '%s', got '%s'", "updated title", existingEntity.GetString("Title"))
}

if updatedEntity.GetString("Description") != "updated desc" {
t.Errorf("expected the updated description to be '%s', got '%s'", "updated desc", existingEntity.GetString("Description"))
}
}
77 changes: 76 additions & 1 deletion model/domain_service.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,14 @@ package model

import (
"encoding/json"
"strconv"

weosContext "github.com/wepala/weos-service/context"
"golang.org/x/net/context"
)

type DomainService struct {
Projection
Repository
eventRepository EventRepository
}
Expand Down Expand Up @@ -56,8 +59,80 @@ func (s *DomainService) CreateBatch(ctx context.Context, payload json.RawMessage

}

func NewDomainService(ctx context.Context, eventRepository EventRepository) *DomainService {
//Update is used for a single payload. It gets an existing entity and updates it with the new payload
func (s *DomainService) Update(ctx context.Context, payload json.RawMessage, entityType string) (*ContentEntity, error) {
var existingEntity *ContentEntity
var updatedEntity *ContentEntity
var identifier map[string]interface{}
var weosID string
contentType := weosContext.GetContentType(ctx)

//Fetch the weosID from the payload
weosID, err := GetIDfromPayload(payload)
if err != nil {
return nil, NewDomainError("invalid: unexpected error unmarshalling payload to get weosID", entityType, "", err)
}

//If there is a weosID present use this
if weosID != "" {
seqNo, err := GetSeqfromPayload(payload)
if err != nil {
return nil, NewDomainError("invalid: unexpected error unmarshalling payload to get sequence number", entityType, "", err)
}

if seqNo == "" {
atoniaw marked this conversation as resolved.
Show resolved Hide resolved
return nil, NewDomainError("invalid: no sequence number provided", entityType, "", nil)
}

existingEntity, err := s.GetContentEntity(ctx, weosID)
if err != nil {
return nil, NewDomainError("invalid: unexpected error fetching existing entity", entityType, weosID, err)
}

entitySeqNo := strconv.Itoa(int(existingEntity.SequenceNo))

if seqNo != entitySeqNo {
return nil, NewDomainError("error updating entity. This is a stale item", entityType, weosID, nil)
}

updatedEntity, err = existingEntity.Update(payload)
if err != nil {
return nil, NewDomainError("invalid: unexpected error updating existingEntity", entityType, weosID, err)
}

//If there is no weosID, use the id passed from the param
} else if weosID == "" {
paramID := ctx.Value("id")
atoniaw marked this conversation as resolved.
Show resolved Hide resolved

if paramID == "" {
return nil, NewDomainError("invalid: no ID provided", entityType, "", nil)
}

identifier = map[string]interface{}{"id": paramID}
entityInterface, err := s.GetByKey(ctx, contentType, identifier)

data, err := json.Marshal(entityInterface)
if err != nil {
return nil, NewDomainError("invalid: unexpected error marshalling existingEntity interface", entityType, paramID.(string), err)
}

err = json.Unmarshal(data, &existingEntity)
if err != nil {
return nil, NewDomainError("invalid: unexpected error unmarshalling existingEntity", entityType, paramID.(string), err)
}

updatedEntity, err = existingEntity.Update(payload)
if err != nil {
return nil, NewDomainError("invalid: unexpected error updating existingEntity", entityType, paramID.(string), err)
}

}
return updatedEntity, nil
}

func NewDomainService(ctx context.Context, eventRepository EventRepository, projections Projection) *DomainService {
return &DomainService{
eventRepository: eventRepository,
Projection: projections,
}
}
Loading