diff --git a/backend/README.md b/backend/README.md index 1c62204..c9311d8 100644 --- a/backend/README.md +++ b/backend/README.md @@ -145,7 +145,7 @@ Request: ``` bash curl --location --request GET 'http://localhost:8081/organizations' \ --header 'Content-Type: application/json' \ ---header 'Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJleHAiOjE3MTU2MDIyNDMwOTEsInVpZCI6IjUyNTNkMzdjLTMxZDQtNDgxMi1iZTcxLWE5ODQwMTVlNGVlMyJ9.IKd-sM9cy5ehj0Scvbi3HPvhjnWD1MDl-POUlvVo9sA' \ +--header 'Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJleHAiOjE3MTU4OTU4MTc3MDYsInVpZCI6IjUyNTNkMzdjLTMxZDQtNDgxMi1iZTcxLWE5ODQwMTVlNGVlMyJ9.YjjHWz7FiMM73e-98pZYHCW9tKDZ_mRWKG3m1PcVTo0' \ --data '{ "limit":5, "cursor":"eyJpZCI6IjAxOGY2ZTc3LWUxNDMtNzcyZi04NjJkLTlkZDM5NzUxYTZkMyJ9" @@ -185,34 +185,11 @@ Response: "address": "1", "created_at": 1715556106613, "updated_at": 1715556106613 - }, - { - "_links": { - "self": { - "href": "/organizations/018f6e78-0029-7913-9016-d0793c378c42" - } - }, - "id": "018f6e78-0029-7913-9016-d0793c378c42", - "name": "The Drain Gang Inc 8", - "address": "1", - "created_at": 1715556109225, - "updated_at": 1715556109225 - }, - { - "_links": { - "self": { - "href": "/organizations/018f6e7e-9f41-7d7b-8d95-f9be832c09c1" - } - }, - "id": "018f6e7e-9f41-7d7b-8d95-f9be832c09c1", - "name": "The Drain Gang Inc 9", - "address": "1", - "created_at": 1715556543169, - "updated_at": 1715556543169 } ], "pagination": { - "total_items": 4 + "next_cursor": "eyJpZCI6IjAxOGY2ZTc3LWY1ZjUtN2JjYi1iOThmLTk5NjZlN2E4YjcwNiJ9", + "total_items": 2 } } ``` \ No newline at end of file diff --git a/backend/internal/interface/rest/domain/dto.go b/backend/internal/interface/rest/domain/dto.go index 471de17..0b43150 100644 --- a/backend/internal/interface/rest/domain/dto.go +++ b/backend/internal/interface/rest/domain/dto.go @@ -3,14 +3,11 @@ package domain import ( "encoding/json" "fmt" - - "github.com/emochka2007/block-accounting/internal/interface/rest/domain/hal" ) // Generic type Collection[T any] struct { - *hal.Resource Items []T `json:"items,omitempty"` Pagination Pagination `json:"pagination,omitempty"` } diff --git a/backend/internal/interface/rest/domain/hal/hal.go b/backend/internal/interface/rest/domain/hal/hal.go index 2d428d8..2e3f565 100644 --- a/backend/internal/interface/rest/domain/hal/hal.go +++ b/backend/internal/interface/rest/domain/hal/hal.go @@ -1,5 +1,11 @@ package hal +import ( + "bytes" + "encoding/json" + "fmt" +) + type Link struct { Href string `json:"href"` Title string `json:"title,omitempty"` @@ -10,19 +16,21 @@ type Resource struct { Type string `json:"_type,omitempty"` Links map[string]Link `json:"_links"` Embedded map[string]any `json:"_embedded,omitempty"` + Payload } type Payload interface{} type NewResourceOption func(r *Resource) -func NewResource(selfLink string, opts ...NewResourceOption) *Resource { +func NewResource(res any, selfLink string, opts ...NewResourceOption) *Resource { r := &Resource{ Links: map[string]Link{ "self": { Href: selfLink, }, }, + Payload: res, } for _, o := range opts { @@ -67,3 +75,48 @@ func (r *Resource) AddLink(relation string, link Link) { func (r *Resource) SetType(t string) { r.Type = t } + +func (r *Resource) MarshalJSON() ([]byte, error) { + + rootData := struct { + Type string `json:"_type,omitempty"` + Links map[string]Link `json:"_links"` + Embedded map[string]any `json:"_embedded,omitempty"` + }{} + + rootData.Type = r.Type + + if len(r.Links) > 0 { + rootData.Links = r.Links + } + + if len(r.Embedded) > 0 { + rootData.Embedded = r.Embedded + } + + dataRoot, err := json.Marshal(rootData) + if err != nil { + return nil, fmt.Errorf("error marshal root data. %w", err) + } + + dataChild, err := json.Marshal(r.Payload) + if err != nil { + return nil, fmt.Errorf("error marshal payload data. %w", err) + } + + if len(dataRoot) == 2 { + return dataChild, nil + } + + var b bytes.Buffer + + b.Write(dataRoot[:len(dataRoot)-1]) + + if len(dataChild) != 2 { + b.Write([]byte(`,`)) + } + + b.Write(dataChild[1:]) + + return b.Bytes(), nil +} diff --git a/backend/internal/interface/rest/domain/organization.go b/backend/internal/interface/rest/domain/organization.go index 6896d9a..d61926a 100644 --- a/backend/internal/interface/rest/domain/organization.go +++ b/backend/internal/interface/rest/domain/organization.go @@ -1,11 +1,6 @@ package domain -import ( - "github.com/emochka2007/block-accounting/internal/interface/rest/domain/hal" -) - type Organization struct { - *hal.Resource Id string `json:"id"` Name string `json:"name"` Address string `json:"address"` diff --git a/backend/internal/interface/rest/presenters/organizations.go b/backend/internal/interface/rest/presenters/organizations.go index c1251ad..3b255a6 100644 --- a/backend/internal/interface/rest/presenters/organizations.go +++ b/backend/internal/interface/rest/presenters/organizations.go @@ -12,7 +12,7 @@ import ( type OrganizationsPresenter interface { ResponseCreate(organization *models.Organization) ([]byte, error) ResponseList(orgs []*models.Organization, nextCursor string) ([]byte, error) - Organizations(orgs []*models.Organization) []domain.Organization + Organizations(orgs []*models.Organization) []*hal.Resource } type organizationsPresenter struct { @@ -32,6 +32,7 @@ func (p *organizationsPresenter) ResponseCreate(o *models.Organization) ([]byte, } r := hal.NewResource( + org, "/organizations/"+org.Id, hal.WithType("organization"), ) @@ -45,11 +46,7 @@ func (p *organizationsPresenter) ResponseCreate(o *models.Organization) ([]byte, } func (p *organizationsPresenter) ResponseList(orgs []*models.Organization, nextCursor string) ([]byte, error) { - dtoOrgs := domain.Collection[domain.Organization]{ - Resource: hal.NewResource( - "/organizations", - hal.WithType("organizations"), - ), + dtoOrgs := domain.Collection[*hal.Resource]{ Items: p.Organizations(orgs), Pagination: domain.Pagination{ NextCursor: nextCursor, @@ -57,7 +54,13 @@ func (p *organizationsPresenter) ResponseList(orgs []*models.Organization, nextC }, } - out, err := json.Marshal(dtoOrgs) + r := hal.NewResource( + dtoOrgs, + "/organizations", + hal.WithType("organizations"), + ) + + out, err := json.Marshal(r) if err != nil { return nil, fmt.Errorf("error marshal organizations list response. %w", err) } @@ -65,12 +68,11 @@ func (p *organizationsPresenter) ResponseList(orgs []*models.Organization, nextC return out, nil } -func (p *organizationsPresenter) Organizations(orgs []*models.Organization) []domain.Organization { - out := make([]domain.Organization, len(orgs)) +func (p *organizationsPresenter) Organizations(orgs []*models.Organization) []*hal.Resource { + out := make([]*hal.Resource, len(orgs)) for i, o := range orgs { org := domain.Organization{ - Resource: hal.NewResource("/organizations/" + o.ID.String()), Id: o.ID.String(), Name: o.Name, Address: o.Address, @@ -78,7 +80,9 @@ func (p *organizationsPresenter) Organizations(orgs []*models.Organization) []do UpdatedAt: uint64(o.UpdatedAt.UnixMilli()), } - out[i] = org + r := hal.NewResource(org, "/organizations/"+org.Id) + + out[i] = r } return out