snapshot of https://github.com/cortezaproject/corteza.git (2024.9.x, 2026-09-18; blobs > 131072B omitted)
Some checks failed
SonarQube / SonarQube (push) Failing after 5m28s
E2e test on main branch push / server and client test (map[name:admin port:8080]) (push) Failing after 3m33s
Server and lib tests / Library test (js) (push) Failing after 1m36s
Server and lib tests / Library test (vue) (push) Successful in 2m24s
Server and lib tests / Web client test (admin) (push) Successful in 2m52s
Server and lib tests / Web client test (compose) (push) Successful in 3m23s
Server and lib tests / Web client test (discovery) (push) Successful in 2m49s
Server and lib tests / Web client test (one) (push) Successful in 2m52s
Server and lib tests / Web client test (privacy) (push) Successful in 2m44s
Server and lib tests / Web client test (reporter) (push) Successful in 2m56s
Server and lib tests / Web client test (workflow) (push) Successful in 3m0s
Server and lib tests / Server tests (push) Failing after 4m55s
E2e test on main branch push / notify-failure (push) Failing after 9s
Server and lib tests / Send matrix testing notification (push) Successful in 2s
Mark stale issues and pull requests / stale (push) Successful in 40s

This commit is contained in:
upstream-mirror
2026-09-18 01:22:23 +05:00
commit 7e385fc126
8391 changed files with 1766236 additions and 0 deletions

View File

@@ -0,0 +1,14 @@
package federation
import (
"io"
"github.com/cortezaproject/corteza/server/pkg/options"
)
type (
EncoderAdapter interface {
BuildStructure(io.Writer, options.FederationOpt, interface{}) (interface{}, error)
BuildData(io.Writer, options.FederationOpt, interface{}) (interface{}, error)
}
)

View File

@@ -0,0 +1,33 @@
package federation
import (
"io"
"github.com/cortezaproject/corteza/server/pkg/options"
)
type (
EncoderAdapterCortezaInternal struct{}
ResponseWrapper struct {
Response interface{} `json:"response"`
}
)
// Build a default Corteza response
func (a EncoderAdapterCortezaInternal) BuildStructure(w io.Writer, o options.FederationOpt, p interface{}) (interface{}, error) {
return ResponseWrapper{
Response: listModuleResponseCortezaInternal{
Filter: p.(ListStructurePayload).Filter,
Set: p.(ListStructurePayload).Set,
}}, nil
}
// Build a default Corteza response
func (a EncoderAdapterCortezaInternal) BuildData(w io.Writer, o options.FederationOpt, p interface{}) (interface{}, error) {
return ResponseWrapper{
Response: listRecordResponseCortezaInternal{
Filter: p.(ListDataPayload).Filter,
Set: p.(ListDataPayload).Set,
}}, nil
}

View File

@@ -0,0 +1,63 @@
package federation
import (
"encoding/json"
"io"
"github.com/cortezaproject/corteza/server/pkg/options"
)
const (
ActivityStreamsStructure EncodingFormat = 0
CortezaInternalStructure EncodingFormat = 1
ActivityStreamsData EncodingFormat = 2
CortezaInternalData EncodingFormat = 3
)
type (
EncodingFormat int
Encoder struct {
w io.Writer
o options.FederationOpt
}
)
func NewEncoder(w io.Writer, o options.FederationOpt) *Encoder {
return &Encoder{w: w, o: o}
}
// Encode the specific format per payload
func (e Encoder) Encode(payload interface{}, t EncodingFormat) error {
var (
ea EncoderAdapter
enc = json.NewEncoder(e.w)
resp interface{}
err error
)
switch t {
case ActivityStreamsStructure:
ea = &EncoderAdapterActivityStreams{}
resp, err = ea.BuildStructure(e.w, e.o, payload)
break
case CortezaInternalStructure:
ea = &EncoderAdapterCortezaInternal{}
resp, err = ea.BuildStructure(e.w, e.o, payload)
break
case ActivityStreamsData:
ea = &EncoderAdapterActivityStreams{}
resp, err = ea.BuildData(e.w, e.o, payload)
break
case CortezaInternalData:
ea = &EncoderAdapterCortezaInternal{}
resp, err = ea.BuildData(e.w, e.o, payload)
break
}
if err != nil {
return err
}
return enc.Encode(resp)
}

View File

@@ -0,0 +1,155 @@
package federation
import (
"strings"
"testing"
"time"
ct "github.com/cortezaproject/corteza/server/compose/types"
"github.com/cortezaproject/corteza/server/federation/types"
"github.com/cortezaproject/corteza/server/pkg/filter"
"github.com/cortezaproject/corteza/server/pkg/options"
"github.com/stretchr/testify/require"
)
func TestEncoder_encodeStructure(t *testing.T) {
var (
opts = options.FederationOpt{
Enabled: true,
Host: "example.ltd",
}
payload = ListStructurePayload{
Filter: &types.ExposedModuleFilter{
Paging: filter.Paging{
Limit: 42,
},
},
Set: &types.ExposedModuleSet{
&types.ExposedModule{
ID: 123,
ComposeModuleID: 456,
ComposeNamespaceID: 789,
NodeID: 1111,
Handle: "test_module",
Name: "Test Module",
CreatedBy: 2222,
CreatedAt: time.Date(2020, 10, 10, 10, 10, 10, 10, time.UTC),
Fields: types.ModuleFieldSet{
&types.ModuleField{Kind: "String", Name: "test_module_first_field", Label: "Test Module First Field", IsMulti: false},
&types.ModuleField{Kind: "String", Name: "test_module_second_field", Label: "Test Module Second Field", IsMulti: false},
},
},
},
}
tcc = []struct {
name string
format EncodingFormat
expect string
}{
{
"Encode structure in Activity Streams format",
ActivityStreamsStructure,
`{"@context":"https://www.w3.org/ns/activitystreams","itemsPerPage":42,"items":[{"@context":"https://www.w3.org/ns/activitystreams","type":"Module","summary":"Structure for module Test Module on node 1111","name":"Test Module","handle":"test_module","url":"https://example.ltd/nodes/1111/modules/123","node":"1111","federationModule":"123","composeModule":"456","composeNamespace":"789","createdAt":"2020-10-10T10:10:10.00000001Z","createdBy":"2222","attributedTo":[{"@context":"https://www.w3.org/ns/activitystreams","id":"https://example.ltd/system/users/2222","type":"User"}],"fields":[{"kind":"String","name":"test_module_first_field","label":"Test Module First Field","isMulti":false},{"kind":"String","name":"test_module_second_field","label":"Test Module Second Field","isMulti":false}]}]}`,
},
{
"Encode structure in internal Corteza format",
CortezaInternalStructure,
`{"response":{"filter":{"nodeID":"0","composeModuleID":"0","composeNamespaceID":"0","lastSync":0,"handle":"","name":"","query":"","limit":42},"set":[{"moduleID":"123","nodeID":"1111","composeModuleID":"456","composeNamespaceID":"789","handle":"test_module","name":"Test Module","fields":[{"kind":"String","name":"test_module_first_field","label":"Test Module First Field","isMulti":false},{"kind":"String","name":"test_module_second_field","label":"Test Module Second Field","isMulti":false}],"createdAt":"2020-10-10T10:10:10.00000001Z","createdBy":"2222"}]}}`,
},
}
)
for _, tc := range tcc {
t.Run(tc.name, func(t *testing.T) {
var (
req = require.New(t)
writer = strings.Builder{}
)
encoder := NewEncoder(&writer, opts)
err := encoder.Encode(payload, tc.format)
req.NoError(err)
req.Equal(tc.expect, strings.TrimSuffix(writer.String(), "\n"))
})
}
}
func TestEncoder_encodeData(t *testing.T) {
var (
opts = options.FederationOpt{
Enabled: true,
Host: "example.ltd",
}
payload = ListDataPayload{
Filter: &ct.RecordFilter{
ModuleID: 456,
NamespaceID: 789,
Paging: filter.Paging{
Limit: 42,
},
},
Set: &ct.RecordSet{
&ct.Record{
ID: 123,
ModuleID: 456,
NamespaceID: 789,
CreatedBy: 2222,
CreatedAt: time.Date(2020, 10, 10, 10, 10, 10, 10, time.UTC),
Values: ct.RecordValueSet{
&ct.RecordValue{RecordID: 1231111, Name: "First Record First Value", Value: "First Record First Value"},
&ct.RecordValue{RecordID: 1231112, Name: "First Record Second Value", Value: "First Record Second Value"},
},
},
&ct.Record{
ID: 124,
ModuleID: 456,
NamespaceID: 789,
CreatedBy: 2222,
CreatedAt: time.Date(2020, 10, 10, 10, 10, 10, 10, time.UTC),
Values: ct.RecordValueSet{
&ct.RecordValue{RecordID: 1231111, Name: "Second Record First Value", Value: "Second Record First Value"},
&ct.RecordValue{RecordID: 1231112, Name: "Second Record Second Value", Value: "Second Record Second Value"},
},
},
},
NodeID: 1111,
ModuleID: 3333,
}
tcc = []struct {
name string
format EncodingFormat
expect string
}{
{
"Encode structure in Activity Streams format",
ActivityStreamsData,
`{"@context":"https://www.w3.org/ns/activitystreams","itemsPerPage":42,"items":[{"@context":"https://www.w3.org/ns/activitystreams","type":"Record","summary":"Data for module 456 on node 1111","url":"https://example.ltd/nodes/1111/modules/3333/records/social/","node":"1111","federationModule":"456","composeModule":"456","composeNamespace":"789","createdAt":"2020-10-10T10:10:10.00000001Z","createdBy":"2222","attributedTo":[{"@context":"https://www.w3.org/ns/activitystreams","id":"https://example.ltd/system/users/2222","type":"User"}],"values":[{"name":"First Record First Value","value":"First Record First Value"},{"name":"First Record Second Value","value":"First Record Second Value"}]},{"@context":"https://www.w3.org/ns/activitystreams","type":"Record","summary":"Data for module 456 on node 1111","url":"https://example.ltd/nodes/1111/modules/3333/records/social/","node":"1111","federationModule":"456","composeModule":"456","composeNamespace":"789","createdAt":"2020-10-10T10:10:10.00000001Z","createdBy":"2222","attributedTo":[{"@context":"https://www.w3.org/ns/activitystreams","id":"https://example.ltd/system/users/2222","type":"User"}],"values":[{"name":"Second Record First Value","value":"Second Record First Value"},{"name":"Second Record Second Value","value":"Second Record Second Value"}]}]}`,
},
{
"Encode structure in internal Corteza format",
CortezaInternalData,
`{"response":{"filter":{"moduleID":"456","namespaceID":"789","query":"","deleted":0,"limit":42},"set":[{"recordID":"123","moduleID":"456","values":[{"name":"First Record First Value","value":"First Record First Value"},{"name":"First Record Second Value","value":"First Record Second Value"}],"namespaceID":"789","ownedBy":"0","createdAt":"2020-10-10T10:10:10.00000001Z","createdBy":"2222"},{"recordID":"124","moduleID":"456","values":[{"name":"Second Record First Value","value":"Second Record First Value"},{"name":"Second Record Second Value","value":"Second Record Second Value"}],"namespaceID":"789","ownedBy":"0","createdAt":"2020-10-10T10:10:10.00000001Z","createdBy":"2222"}]}}`,
},
}
)
for _, tc := range tcc {
t.Run(tc.name, func(t *testing.T) {
var (
req = require.New(t)
writer = strings.Builder{}
)
encoder := NewEncoder(&writer, opts)
err := encoder.Encode(payload, tc.format)
req.NoError(err)
req.Equal(tc.expect, strings.TrimSuffix(writer.String(), "\n"))
})
}
}

View File

@@ -0,0 +1,79 @@
package federation
import (
"time"
ct "github.com/cortezaproject/corteza/server/compose/types"
"github.com/cortezaproject/corteza/server/federation/types"
)
type (
listResponsePagingActivityStreams struct {
Type string `json:"type"`
Name string `json:"name"`
Href string `json:"href"`
}
listResponseItemActivityStreams struct {
Context string `json:"@context"`
Type string `json:"type"`
Summary string `json:"summary"`
Name string `json:"name,omitempty"`
Handle string `json:"handle,omitempty"`
Url string `json:"url"`
Node uint64 `json:"node,string"`
FederationModule uint64 `json:"federationModule,string"`
ComposeModule uint64 `json:"composeModule,string"`
ComposeNamespace uint64 `json:"composeNamespace,string"`
CreatedAt time.Time `json:"createdAt,omitempty"`
CreatedBy uint64 `json:"createdBy,string" `
UpdatedAt *time.Time `json:"updatedAt,omitempty"`
UpdatedBy uint64 `json:"updatedBy,string,omitempty" `
DeletedAt *time.Time `json:"deletedAt,omitempty"`
DeletedBy uint64 `json:"deletedBy,string,omitempty" `
Attribution []listResponseItemAttribution `json:"attributedTo"`
Fields types.ModuleFieldSet `json:"fields,omitempty"`
Values ct.RecordValueSet `json:"values,omitempty"`
}
listResponseItemAttribution struct {
Context string `json:"@context"`
Id string `json:"id"`
Type string `json:"type"`
}
listModuleResponseActivityStreams struct {
Context string `json:"@context"`
ItemsPerPage uint `json:"itemsPerPage"`
Next *listResponsePagingActivityStreams `json:"next,omitempty"`
Prev *listResponsePagingActivityStreams `json:"prev,omitempty"`
Items interface{} `json:"items"`
}
listModuleResponseCortezaInternal struct {
Filter *types.ExposedModuleFilter `json:"filter"`
Set *types.ExposedModuleSet `json:"set"`
}
listRecordResponseCortezaInternal struct {
Filter *ct.RecordFilter `json:"filter"`
Set *ct.RecordSet `json:"set"`
}
ListStructurePayload struct {
NodeID uint64
Filter *types.ExposedModuleFilter `json:"filter"`
Set *types.ExposedModuleSet `json:"set"`
}
ListDataPayload struct {
NodeID uint64
ModuleID uint64
Filter *ct.RecordFilter `json:"filter"`
Set *ct.RecordSet `json:"set"`
}
)

View File

@@ -0,0 +1,155 @@
package federation
import (
"fmt"
"io"
"github.com/cortezaproject/corteza/server/pkg/options"
)
type (
EncoderAdapterActivityStreams struct{}
)
// Build an activity streams format from default internal Corteza
// payload, including the author, activitystreams metadata and paging
// custom metadata
func (a EncoderAdapterActivityStreams) BuildStructure(w io.Writer, o options.FederationOpt, p interface{}) (interface{}, error) {
var (
next, prev *listResponsePagingActivityStreams
items []listResponseItemActivityStreams
)
payload := p.(ListStructurePayload)
if payload.Filter.Paging.NextPage != nil {
next = &listResponsePagingActivityStreams{
Type: "Link",
Name: "Next page",
Href: fmt.Sprintf("https://%s/nodes/%d/modules/exposed/?pageCursor=%s", o.Host, payload.NodeID, payload.Filter.Paging.NextPage.Encode()),
}
}
if payload.Filter.Paging.PrevPage != nil {
prev = &listResponsePagingActivityStreams{
Type: "Link",
Name: "Previous page",
Href: fmt.Sprintf("https://%s/nodes/%d/modules/exposed/?pageCursor=%s", o.Host, payload.NodeID, payload.Filter.Paging.PrevPage.Encode()),
}
}
// loop through items and format them
for _, v := range *payload.Set {
item := listResponseItemActivityStreams{
Context: "https://www.w3.org/ns/activitystreams",
Type: "Module",
Summary: fmt.Sprintf("Structure for module %s on node %d", v.Name, v.NodeID),
Url: fmt.Sprintf("https://%s/nodes/%d/modules/%d", o.Host, v.NodeID, v.ID),
Name: v.Name,
Handle: v.Handle,
Node: v.NodeID,
FederationModule: v.ID,
ComposeModule: v.ComposeModuleID,
ComposeNamespace: v.ComposeNamespaceID,
Attribution: []listResponseItemAttribution{
{
Context: "https://www.w3.org/ns/activitystreams",
Type: "User",
Id: fmt.Sprintf("https://%s/system/users/%d", o.Host, v.CreatedBy),
},
},
CreatedAt: v.CreatedAt,
CreatedBy: v.CreatedBy,
UpdatedAt: v.UpdatedAt,
UpdatedBy: v.UpdatedBy,
DeletedAt: v.DeletedAt,
DeletedBy: v.DeletedBy,
Fields: v.Fields,
}
items = append(items, item)
}
return listModuleResponseActivityStreams{
Context: "https://www.w3.org/ns/activitystreams",
ItemsPerPage: payload.Filter.Limit,
Items: items,
Next: next,
Prev: prev,
}, nil
}
// Build an activity streams format from default internal Corteza
// payload, including the author, activitystreams metadata and paging
// custom metadata
func (a EncoderAdapterActivityStreams) BuildData(w io.Writer, o options.FederationOpt, p interface{}) (interface{}, error) {
var (
next, prev *listResponsePagingActivityStreams
items []listResponseItemActivityStreams
)
payload := p.(ListDataPayload)
if payload.Filter.Paging.NextPage != nil {
next = &listResponsePagingActivityStreams{
Type: "Link",
Name: "Next page",
Href: fmt.Sprintf("https://%s/nodes/%d/modules/%d/records/social/?pageCursor=%s", o.Host, payload.NodeID, payload.ModuleID, payload.Filter.Paging.NextPage.Encode()),
}
}
if payload.Filter.Paging.PrevPage != nil {
prev = &listResponsePagingActivityStreams{
Type: "Link",
Name: "Previous page",
Href: fmt.Sprintf("https://%s/nodes/%d/modules/%d/records/social/?pageCursor=%s", o.Host, payload.NodeID, payload.ModuleID, payload.Filter.Paging.PrevPage.Encode()),
}
}
// loop through items and format them
for _, v := range *payload.Set {
item := listResponseItemActivityStreams{
Context: "https://www.w3.org/ns/activitystreams",
Type: "Record",
Summary: fmt.Sprintf("Data for module %d on node %d", v.ModuleID, payload.NodeID),
Url: fmt.Sprintf("https://%s/nodes/%d/modules/%d/records/social/", o.Host, payload.NodeID, payload.ModuleID),
Node: payload.NodeID,
FederationModule: v.ModuleID,
ComposeModule: v.ModuleID,
ComposeNamespace: v.NamespaceID,
Attribution: []listResponseItemAttribution{
{
Context: "https://www.w3.org/ns/activitystreams",
Type: "User",
Id: fmt.Sprintf("https://%s/system/users/%d", o.Host, v.CreatedBy),
},
},
CreatedAt: v.CreatedAt,
CreatedBy: v.CreatedBy,
UpdatedAt: v.UpdatedAt,
UpdatedBy: v.UpdatedBy,
DeletedAt: v.DeletedAt,
DeletedBy: v.DeletedBy,
Values: v.Values,
}
items = append(items, item)
}
return listModuleResponseActivityStreams{
Context: "https://www.w3.org/ns/activitystreams",
ItemsPerPage: payload.Filter.Limit,
Items: items,
Next: next,
Prev: prev,
}, nil
}