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
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:
51
server/federation/commands/sync.go
Normal file
51
server/federation/commands/sync.go
Normal file
@@ -0,0 +1,51 @@
|
||||
package commands
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
type (
|
||||
serviceInitializer interface {
|
||||
InitServices(ctx context.Context) error
|
||||
}
|
||||
)
|
||||
|
||||
func Sync(ctx context.Context, app serviceInitializer) *cobra.Command {
|
||||
// Sync commands.
|
||||
cmd := &cobra.Command{
|
||||
Use: "sync",
|
||||
Short: "Sync commands",
|
||||
}
|
||||
|
||||
// Sync structure.
|
||||
syncStructureCmd := &cobra.Command{
|
||||
Use: "structure",
|
||||
Short: "Sync structure",
|
||||
|
||||
PreRunE: commandPreRunInitService(ctx, app),
|
||||
Run: commandSyncStructure(ctx),
|
||||
}
|
||||
|
||||
syncDataCmd := &cobra.Command{
|
||||
Use: "data",
|
||||
Short: "Sync data",
|
||||
|
||||
PreRunE: commandPreRunInitService(ctx, app),
|
||||
Run: commandSyncData(ctx),
|
||||
}
|
||||
|
||||
cmd.AddCommand(
|
||||
syncStructureCmd,
|
||||
syncDataCmd,
|
||||
)
|
||||
|
||||
return cmd
|
||||
}
|
||||
|
||||
func commandPreRunInitService(ctx context.Context, app serviceInitializer) func(*cobra.Command, []string) error {
|
||||
return func(_ *cobra.Command, _ []string) error {
|
||||
return app.InitServices(ctx)
|
||||
}
|
||||
}
|
||||
25
server/federation/commands/sync_data.go
Normal file
25
server/federation/commands/sync_data.go
Normal file
@@ -0,0 +1,25 @@
|
||||
package commands
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
cs "github.com/cortezaproject/corteza/server/compose/service"
|
||||
"github.com/cortezaproject/corteza/server/federation/service"
|
||||
ss "github.com/cortezaproject/corteza/server/system/service"
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
func commandSyncData(ctx context.Context) func(*cobra.Command, []string) {
|
||||
return func(_ *cobra.Command, _ []string) {
|
||||
syncService := service.NewSync(
|
||||
&service.Syncer{},
|
||||
&service.Mapper{},
|
||||
service.DefaultSharedModule,
|
||||
cs.DefaultRecord,
|
||||
ss.DefaultUser,
|
||||
ss.DefaultRole)
|
||||
|
||||
syncData := service.WorkerData(syncService, service.DefaultLogger)
|
||||
syncData.Watch(ctx, service.DefaultOptions.DataMonitorInterval, service.DefaultOptions.DataPageSize)
|
||||
}
|
||||
}
|
||||
25
server/federation/commands/sync_structure.go
Normal file
25
server/federation/commands/sync_structure.go
Normal file
@@ -0,0 +1,25 @@
|
||||
package commands
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
cs "github.com/cortezaproject/corteza/server/compose/service"
|
||||
"github.com/cortezaproject/corteza/server/federation/service"
|
||||
ss "github.com/cortezaproject/corteza/server/system/service"
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
func commandSyncStructure(ctx context.Context) func(*cobra.Command, []string) {
|
||||
return func(_ *cobra.Command, _ []string) {
|
||||
syncService := service.NewSync(
|
||||
&service.Syncer{},
|
||||
&service.Mapper{},
|
||||
service.DefaultSharedModule,
|
||||
cs.DefaultRecord,
|
||||
ss.DefaultUser,
|
||||
ss.DefaultRole)
|
||||
|
||||
syncStructure := service.WorkerStructure(syncService, service.DefaultLogger)
|
||||
syncStructure.Watch(ctx, service.DefaultOptions.StructureMonitorInterval, service.DefaultOptions.StructurePageSize)
|
||||
}
|
||||
}
|
||||
30
server/federation/component.cue
Normal file
30
server/federation/component.cue
Normal file
@@ -0,0 +1,30 @@
|
||||
package federation
|
||||
|
||||
import (
|
||||
"github.com/cortezaproject/corteza/server/codegen/schema"
|
||||
)
|
||||
|
||||
component: schema.#component & {
|
||||
handle: "federation"
|
||||
|
||||
resources: {
|
||||
"node": node
|
||||
"node-sync": nodeSync
|
||||
"exposed-module": exposedModule
|
||||
"shared-module": sharedModule
|
||||
"module-mapping": moduleMapping
|
||||
}
|
||||
|
||||
envoy: {
|
||||
omit: true
|
||||
}
|
||||
|
||||
rbac: operations: {
|
||||
"grant": description: "Manage federation permissions"
|
||||
"pair": description: "Pair federation nodes"
|
||||
"settings.read": description: "Read settings"
|
||||
"settings.manage": description: "Manage settings"
|
||||
"node.create": description: "Create new federation node"
|
||||
"nodes.search": description: "List, search or filter federation nodes"
|
||||
}
|
||||
}
|
||||
26
server/federation/model/init.gen.go
generated
Normal file
26
server/federation/model/init.gen.go
generated
Normal file
@@ -0,0 +1,26 @@
|
||||
package model
|
||||
|
||||
// This file is auto-generated.
|
||||
//
|
||||
// Changes to this file may cause incorrect behavior and will be lost if
|
||||
// the code is regenerated.
|
||||
//
|
||||
|
||||
import (
|
||||
"context"
|
||||
"github.com/cortezaproject/corteza/server/pkg/dal"
|
||||
)
|
||||
|
||||
type (
|
||||
modelReplacer interface {
|
||||
ReplaceModel(ctx context.Context, model *dal.Model) (err error)
|
||||
}
|
||||
)
|
||||
|
||||
var (
|
||||
models []*dal.Model
|
||||
)
|
||||
|
||||
func Models() dal.ModelSet {
|
||||
return models
|
||||
}
|
||||
508
server/federation/model/models.gen.go
generated
Normal file
508
server/federation/model/models.gen.go
generated
Normal file
@@ -0,0 +1,508 @@
|
||||
package model
|
||||
|
||||
// This file is auto-generated.
|
||||
//
|
||||
// Changes to this file may cause incorrect behavior and will be lost if
|
||||
// the code is regenerated.
|
||||
//
|
||||
|
||||
import (
|
||||
"github.com/cortezaproject/corteza/server/federation/types"
|
||||
"github.com/cortezaproject/corteza/server/pkg/dal"
|
||||
)
|
||||
|
||||
var ExposedModule = &dal.Model{
|
||||
Ident: "federation_module_exposed",
|
||||
ResourceType: types.ExposedModuleResourceType,
|
||||
|
||||
Attributes: dal.AttributeSet{
|
||||
&dal.Attribute{
|
||||
Ident: "ID",
|
||||
Type: &dal.TypeID{},
|
||||
Store: &dal.CodecAlias{Ident: "id"},
|
||||
},
|
||||
|
||||
&dal.Attribute{
|
||||
Ident: "Handle",
|
||||
Type: &dal.TypeText{Length: 64},
|
||||
Store: &dal.CodecAlias{Ident: "handle"},
|
||||
},
|
||||
|
||||
&dal.Attribute{
|
||||
Ident: "Name", Sortable: true,
|
||||
Type: &dal.TypeText{},
|
||||
Store: &dal.CodecAlias{Ident: "name"},
|
||||
},
|
||||
|
||||
&dal.Attribute{
|
||||
Ident: "NodeID", Sortable: true,
|
||||
Type: &dal.TypeRef{HasDefault: true,
|
||||
DefaultValue: 0,
|
||||
|
||||
RefAttribute: "id",
|
||||
RefModel: &dal.ModelRef{
|
||||
ResourceType: "corteza::federation:node",
|
||||
},
|
||||
},
|
||||
Store: &dal.CodecAlias{Ident: "rel_node"},
|
||||
},
|
||||
|
||||
&dal.Attribute{
|
||||
Ident: "ComposeModuleID",
|
||||
Type: &dal.TypeID{},
|
||||
Store: &dal.CodecAlias{Ident: "rel_compose_module"},
|
||||
},
|
||||
|
||||
&dal.Attribute{
|
||||
Ident: "ComposeNamespaceID",
|
||||
Type: &dal.TypeID{},
|
||||
Store: &dal.CodecAlias{Ident: "rel_compose_namespace"},
|
||||
},
|
||||
|
||||
&dal.Attribute{
|
||||
Ident: "Fields",
|
||||
Type: &dal.TypeJSON{
|
||||
DefaultValue: "{}",
|
||||
},
|
||||
Store: &dal.CodecAlias{Ident: "fields"},
|
||||
},
|
||||
|
||||
&dal.Attribute{
|
||||
Ident: "CreatedAt", Sortable: true,
|
||||
Type: &dal.TypeTimestamp{
|
||||
DefaultCurrentTimestamp: true, Timezone: true, Precision: -1,
|
||||
},
|
||||
Store: &dal.CodecAlias{Ident: "created_at"},
|
||||
},
|
||||
|
||||
&dal.Attribute{
|
||||
Ident: "UpdatedAt", Sortable: true,
|
||||
Type: &dal.TypeTimestamp{Nullable: true, Timezone: true, Precision: -1},
|
||||
Store: &dal.CodecAlias{Ident: "updated_at"},
|
||||
},
|
||||
|
||||
&dal.Attribute{
|
||||
Ident: "DeletedAt", Sortable: true,
|
||||
Type: &dal.TypeTimestamp{Nullable: true, Timezone: true, Precision: -1},
|
||||
Store: &dal.CodecAlias{Ident: "deleted_at"},
|
||||
},
|
||||
|
||||
&dal.Attribute{
|
||||
Ident: "CreatedBy",
|
||||
Type: &dal.TypeRef{HasDefault: true,
|
||||
DefaultValue: 0,
|
||||
|
||||
RefAttribute: "id",
|
||||
RefModel: &dal.ModelRef{
|
||||
ResourceType: "corteza::system:user",
|
||||
},
|
||||
},
|
||||
Store: &dal.CodecAlias{Ident: "created_by"},
|
||||
},
|
||||
|
||||
&dal.Attribute{
|
||||
Ident: "UpdatedBy",
|
||||
Type: &dal.TypeRef{HasDefault: true,
|
||||
DefaultValue: 0,
|
||||
|
||||
RefAttribute: "id",
|
||||
RefModel: &dal.ModelRef{
|
||||
ResourceType: "corteza::system:user",
|
||||
},
|
||||
},
|
||||
Store: &dal.CodecAlias{Ident: "updated_by"},
|
||||
},
|
||||
|
||||
&dal.Attribute{
|
||||
Ident: "DeletedBy",
|
||||
Type: &dal.TypeRef{HasDefault: true,
|
||||
DefaultValue: 0,
|
||||
|
||||
RefAttribute: "id",
|
||||
RefModel: &dal.ModelRef{
|
||||
ResourceType: "corteza::system:user",
|
||||
},
|
||||
},
|
||||
Store: &dal.CodecAlias{Ident: "deleted_by"},
|
||||
},
|
||||
},
|
||||
|
||||
Indexes: dal.IndexSet{
|
||||
&dal.Index{
|
||||
Ident: "PRIMARY",
|
||||
Type: "BTREE",
|
||||
|
||||
Fields: []*dal.IndexField{
|
||||
{
|
||||
AttributeIdent: "ID",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
var ModuleMapping = &dal.Model{
|
||||
Ident: "federation_module_mapping",
|
||||
ResourceType: types.ModuleMappingResourceType,
|
||||
|
||||
Attributes: dal.AttributeSet{
|
||||
&dal.Attribute{
|
||||
Ident: "NodeID",
|
||||
Type: &dal.TypeID{},
|
||||
Store: &dal.CodecAlias{Ident: "node_id"},
|
||||
},
|
||||
|
||||
&dal.Attribute{
|
||||
Ident: "FederationModuleID", Sortable: true,
|
||||
Type: &dal.TypeID{},
|
||||
Store: &dal.CodecAlias{Ident: "rel_federation_module"},
|
||||
},
|
||||
|
||||
&dal.Attribute{
|
||||
Ident: "ComposeModuleID", Sortable: true,
|
||||
Type: &dal.TypeID{},
|
||||
Store: &dal.CodecAlias{Ident: "rel_compose_module"},
|
||||
},
|
||||
|
||||
&dal.Attribute{
|
||||
Ident: "ComposeNamespaceID", Sortable: true,
|
||||
Type: &dal.TypeID{},
|
||||
Store: &dal.CodecAlias{Ident: "rel_compose_namespace"},
|
||||
},
|
||||
|
||||
&dal.Attribute{
|
||||
Ident: "FieldMapping",
|
||||
Type: &dal.TypeJSON{
|
||||
DefaultValue: "{}",
|
||||
},
|
||||
Store: &dal.CodecAlias{Ident: "field_mapping"},
|
||||
},
|
||||
},
|
||||
|
||||
Indexes: dal.IndexSet{
|
||||
&dal.Index{
|
||||
Ident: "federation_module_mapping_uniqueModuleComposeModule",
|
||||
Type: "BTREE",
|
||||
Unique: true,
|
||||
|
||||
Fields: []*dal.IndexField{
|
||||
{
|
||||
AttributeIdent: "FederationModuleID",
|
||||
},
|
||||
|
||||
{
|
||||
AttributeIdent: "ComposeModuleID",
|
||||
},
|
||||
|
||||
{
|
||||
AttributeIdent: "ComposeNamespaceID",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
var Node = &dal.Model{
|
||||
Ident: "federation_nodes",
|
||||
ResourceType: types.NodeResourceType,
|
||||
|
||||
Attributes: dal.AttributeSet{
|
||||
&dal.Attribute{
|
||||
Ident: "ID",
|
||||
Type: &dal.TypeID{},
|
||||
Store: &dal.CodecAlias{Ident: "id"},
|
||||
},
|
||||
|
||||
&dal.Attribute{
|
||||
Ident: "SharedNodeID", Sortable: true,
|
||||
Type: &dal.TypeID{},
|
||||
Store: &dal.CodecAlias{Ident: "shared_node_id"},
|
||||
},
|
||||
|
||||
&dal.Attribute{
|
||||
Ident: "Name", Sortable: true,
|
||||
Type: &dal.TypeText{},
|
||||
Store: &dal.CodecAlias{Ident: "name"},
|
||||
},
|
||||
|
||||
&dal.Attribute{
|
||||
Ident: "BaseURL", Sortable: true,
|
||||
Type: &dal.TypeText{},
|
||||
Store: &dal.CodecAlias{Ident: "base_url"},
|
||||
},
|
||||
|
||||
&dal.Attribute{
|
||||
Ident: "Status", Sortable: true,
|
||||
Type: &dal.TypeText{},
|
||||
Store: &dal.CodecAlias{Ident: "status"},
|
||||
},
|
||||
|
||||
&dal.Attribute{
|
||||
Ident: "Contact", Sortable: true,
|
||||
Type: &dal.TypeText{},
|
||||
Store: &dal.CodecAlias{Ident: "contact"},
|
||||
},
|
||||
|
||||
&dal.Attribute{
|
||||
Ident: "PairToken",
|
||||
Type: &dal.TypeText{},
|
||||
Store: &dal.CodecAlias{Ident: "pair_token"},
|
||||
},
|
||||
|
||||
&dal.Attribute{
|
||||
Ident: "AuthToken",
|
||||
Type: &dal.TypeText{},
|
||||
Store: &dal.CodecAlias{Ident: "auth_token"},
|
||||
},
|
||||
|
||||
&dal.Attribute{
|
||||
Ident: "CreatedAt", Sortable: true,
|
||||
Type: &dal.TypeTimestamp{
|
||||
DefaultCurrentTimestamp: true, Timezone: true, Precision: -1,
|
||||
},
|
||||
Store: &dal.CodecAlias{Ident: "created_at"},
|
||||
},
|
||||
|
||||
&dal.Attribute{
|
||||
Ident: "UpdatedAt", Sortable: true,
|
||||
Type: &dal.TypeTimestamp{Nullable: true, Timezone: true, Precision: -1},
|
||||
Store: &dal.CodecAlias{Ident: "updated_at"},
|
||||
},
|
||||
|
||||
&dal.Attribute{
|
||||
Ident: "DeletedAt", Sortable: true,
|
||||
Type: &dal.TypeTimestamp{Nullable: true, Timezone: true, Precision: -1},
|
||||
Store: &dal.CodecAlias{Ident: "deleted_at"},
|
||||
},
|
||||
|
||||
&dal.Attribute{
|
||||
Ident: "CreatedBy",
|
||||
Type: &dal.TypeRef{HasDefault: true,
|
||||
DefaultValue: 0,
|
||||
|
||||
RefAttribute: "id",
|
||||
RefModel: &dal.ModelRef{
|
||||
ResourceType: "corteza::system:user",
|
||||
},
|
||||
},
|
||||
Store: &dal.CodecAlias{Ident: "created_by"},
|
||||
},
|
||||
|
||||
&dal.Attribute{
|
||||
Ident: "UpdatedBy",
|
||||
Type: &dal.TypeRef{HasDefault: true,
|
||||
DefaultValue: 0,
|
||||
|
||||
RefAttribute: "id",
|
||||
RefModel: &dal.ModelRef{
|
||||
ResourceType: "corteza::system:user",
|
||||
},
|
||||
},
|
||||
Store: &dal.CodecAlias{Ident: "updated_by"},
|
||||
},
|
||||
|
||||
&dal.Attribute{
|
||||
Ident: "DeletedBy",
|
||||
Type: &dal.TypeRef{HasDefault: true,
|
||||
DefaultValue: 0,
|
||||
|
||||
RefAttribute: "id",
|
||||
RefModel: &dal.ModelRef{
|
||||
ResourceType: "corteza::system:user",
|
||||
},
|
||||
},
|
||||
Store: &dal.CodecAlias{Ident: "deleted_by"},
|
||||
},
|
||||
},
|
||||
|
||||
Indexes: dal.IndexSet{
|
||||
&dal.Index{
|
||||
Ident: "PRIMARY",
|
||||
Type: "BTREE",
|
||||
|
||||
Fields: []*dal.IndexField{
|
||||
{
|
||||
AttributeIdent: "ID",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
var NodeSync = &dal.Model{
|
||||
Ident: "federation_nodes_sync",
|
||||
ResourceType: types.NodeSyncResourceType,
|
||||
|
||||
Attributes: dal.AttributeSet{
|
||||
&dal.Attribute{
|
||||
Ident: "NodeID", Sortable: true,
|
||||
Type: &dal.TypeID{},
|
||||
Store: &dal.CodecAlias{Ident: "rel_node"},
|
||||
},
|
||||
|
||||
&dal.Attribute{
|
||||
Ident: "ModuleID", Sortable: true,
|
||||
Type: &dal.TypeID{},
|
||||
Store: &dal.CodecAlias{Ident: "rel_compose_module"},
|
||||
},
|
||||
|
||||
&dal.Attribute{
|
||||
Ident: "SyncType", Sortable: true,
|
||||
Type: &dal.TypeText{},
|
||||
Store: &dal.CodecAlias{Ident: "sync_type"},
|
||||
},
|
||||
|
||||
&dal.Attribute{
|
||||
Ident: "SyncStatus", Sortable: true,
|
||||
Type: &dal.TypeText{},
|
||||
Store: &dal.CodecAlias{Ident: "sync_status"},
|
||||
},
|
||||
|
||||
&dal.Attribute{
|
||||
Ident: "TimeOfAction", Sortable: true,
|
||||
Type: &dal.TypeTimestamp{Timezone: true, Precision: -1},
|
||||
Store: &dal.CodecAlias{Ident: "time_of_action"},
|
||||
},
|
||||
},
|
||||
|
||||
Indexes: dal.IndexSet{
|
||||
&dal.Index{
|
||||
Ident: "federation_nodes_sync_idxRelNode",
|
||||
Type: "BTREE",
|
||||
|
||||
Fields: []*dal.IndexField{
|
||||
{
|
||||
AttributeIdent: "NodeID",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
var SharedModule = &dal.Model{
|
||||
Ident: "federation_module_shared",
|
||||
ResourceType: types.SharedModuleResourceType,
|
||||
|
||||
Attributes: dal.AttributeSet{
|
||||
&dal.Attribute{
|
||||
Ident: "ID",
|
||||
Type: &dal.TypeID{},
|
||||
Store: &dal.CodecAlias{Ident: "id"},
|
||||
},
|
||||
|
||||
&dal.Attribute{
|
||||
Ident: "Handle",
|
||||
Type: &dal.TypeText{Length: 64},
|
||||
Store: &dal.CodecAlias{Ident: "handle"},
|
||||
},
|
||||
|
||||
&dal.Attribute{
|
||||
Ident: "NodeID", Sortable: true,
|
||||
Type: &dal.TypeID{},
|
||||
Store: &dal.CodecAlias{Ident: "rel_node"},
|
||||
},
|
||||
|
||||
&dal.Attribute{
|
||||
Ident: "Name", Sortable: true,
|
||||
Type: &dal.TypeText{},
|
||||
Store: &dal.CodecAlias{Ident: "name"},
|
||||
},
|
||||
|
||||
&dal.Attribute{
|
||||
Ident: "ExternalFederationModuleID", Sortable: true,
|
||||
Type: &dal.TypeID{},
|
||||
Store: &dal.CodecAlias{Ident: "xref_module"},
|
||||
},
|
||||
|
||||
&dal.Attribute{
|
||||
Ident: "Fields",
|
||||
Type: &dal.TypeJSON{
|
||||
DefaultValue: "{}",
|
||||
},
|
||||
Store: &dal.CodecAlias{Ident: "fields"},
|
||||
},
|
||||
|
||||
&dal.Attribute{
|
||||
Ident: "CreatedAt", Sortable: true,
|
||||
Type: &dal.TypeTimestamp{
|
||||
DefaultCurrentTimestamp: true, Timezone: true, Precision: -1,
|
||||
},
|
||||
Store: &dal.CodecAlias{Ident: "created_at"},
|
||||
},
|
||||
|
||||
&dal.Attribute{
|
||||
Ident: "UpdatedAt", Sortable: true,
|
||||
Type: &dal.TypeTimestamp{Nullable: true, Timezone: true, Precision: -1},
|
||||
Store: &dal.CodecAlias{Ident: "updated_at"},
|
||||
},
|
||||
|
||||
&dal.Attribute{
|
||||
Ident: "DeletedAt", Sortable: true,
|
||||
Type: &dal.TypeTimestamp{Nullable: true, Timezone: true, Precision: -1},
|
||||
Store: &dal.CodecAlias{Ident: "deleted_at"},
|
||||
},
|
||||
|
||||
&dal.Attribute{
|
||||
Ident: "CreatedBy",
|
||||
Type: &dal.TypeRef{HasDefault: true,
|
||||
DefaultValue: 0,
|
||||
|
||||
RefAttribute: "id",
|
||||
RefModel: &dal.ModelRef{
|
||||
ResourceType: "corteza::system:user",
|
||||
},
|
||||
},
|
||||
Store: &dal.CodecAlias{Ident: "created_by"},
|
||||
},
|
||||
|
||||
&dal.Attribute{
|
||||
Ident: "UpdatedBy",
|
||||
Type: &dal.TypeRef{HasDefault: true,
|
||||
DefaultValue: 0,
|
||||
|
||||
RefAttribute: "id",
|
||||
RefModel: &dal.ModelRef{
|
||||
ResourceType: "corteza::system:user",
|
||||
},
|
||||
},
|
||||
Store: &dal.CodecAlias{Ident: "updated_by"},
|
||||
},
|
||||
|
||||
&dal.Attribute{
|
||||
Ident: "DeletedBy",
|
||||
Type: &dal.TypeRef{HasDefault: true,
|
||||
DefaultValue: 0,
|
||||
|
||||
RefAttribute: "id",
|
||||
RefModel: &dal.ModelRef{
|
||||
ResourceType: "corteza::system:user",
|
||||
},
|
||||
},
|
||||
Store: &dal.CodecAlias{Ident: "deleted_by"},
|
||||
},
|
||||
},
|
||||
|
||||
Indexes: dal.IndexSet{
|
||||
&dal.Index{
|
||||
Ident: "PRIMARY",
|
||||
Type: "BTREE",
|
||||
|
||||
Fields: []*dal.IndexField{
|
||||
{
|
||||
AttributeIdent: "ID",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
func init() {
|
||||
models = append(
|
||||
models,
|
||||
ExposedModule,
|
||||
ModuleMapping,
|
||||
Node,
|
||||
NodeSync,
|
||||
SharedModule,
|
||||
)
|
||||
}
|
||||
100
server/federation/module_exposed.cue
Normal file
100
server/federation/module_exposed.cue
Normal file
@@ -0,0 +1,100 @@
|
||||
package federation
|
||||
|
||||
import (
|
||||
"github.com/cortezaproject/corteza/server/codegen/schema"
|
||||
)
|
||||
|
||||
exposedModule: {
|
||||
parents: [
|
||||
{handle: "node"},
|
||||
]
|
||||
|
||||
features: {
|
||||
labels: false
|
||||
}
|
||||
|
||||
model: {
|
||||
ident: "federation_module_exposed"
|
||||
attributes: {
|
||||
id: schema.IdField
|
||||
handle: schema.HandleField
|
||||
name: {
|
||||
sortable: true
|
||||
dal: {}
|
||||
}
|
||||
node_id: {
|
||||
sortable: true,
|
||||
ident: "nodeID",
|
||||
goType: "uint64",
|
||||
storeIdent: "rel_node"
|
||||
dal: { type: "Ref", refModelResType: "corteza::federation:node", default: 0 }
|
||||
}
|
||||
compose_module_id: {
|
||||
ident: "composeModuleID",
|
||||
goType: "uint64",
|
||||
storeIdent: "rel_compose_module"
|
||||
dal: { type: "ID" }
|
||||
}
|
||||
compose_namespace_id: {
|
||||
ident: "composeNamespaceID",
|
||||
goType: "uint64",
|
||||
storeIdent: "rel_compose_namespace"
|
||||
dal: { type: "ID" }
|
||||
}
|
||||
fields: {
|
||||
goType: "types.ModuleFieldSet"
|
||||
dal: { type: "JSON", defaultEmptyObject: true }
|
||||
omitSetter: true
|
||||
omitGetter: true
|
||||
}
|
||||
|
||||
created_at: schema.SortableTimestampNowField
|
||||
updated_at: schema.SortableTimestampNilField
|
||||
deleted_at: schema.SortableTimestampNilField
|
||||
created_by: schema.AttributeUserRef
|
||||
updated_by: schema.AttributeUserRef
|
||||
deleted_by: schema.AttributeUserRef
|
||||
}
|
||||
|
||||
indexes: {
|
||||
"primary": { attribute: "id" }
|
||||
}
|
||||
}
|
||||
|
||||
filter: {
|
||||
struct: {
|
||||
node_id: { goType: "uint64", ident: "nodeID", storeIdent: "rel_node" }
|
||||
compose_module_id: { goType: "uint64", ident: "composeModuleID", storeIdent: "rel_compose_module" }
|
||||
compose_namespace_id: { goType: "uint64", ident: "composeNamespaceID", storeIdent: "rel_compose_namespace" }
|
||||
}
|
||||
|
||||
byValue: ["compose_module_id", "compose_namespace_id", "node_id"]
|
||||
}
|
||||
|
||||
envoy: {
|
||||
omit: true
|
||||
}
|
||||
|
||||
rbac: {
|
||||
operations: {
|
||||
"manage": description: "Manage exposed module module"
|
||||
}
|
||||
}
|
||||
|
||||
store: {
|
||||
ident: "federationExposedModule"
|
||||
|
||||
api: {
|
||||
lookups: [
|
||||
{
|
||||
fields: ["id"]
|
||||
description: """
|
||||
searches for federation module by ID
|
||||
|
||||
It returns federation module
|
||||
"""
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
94
server/federation/module_mapping.cue
Normal file
94
server/federation/module_mapping.cue
Normal file
@@ -0,0 +1,94 @@
|
||||
package federation
|
||||
|
||||
moduleMapping: {
|
||||
parents: [
|
||||
{handle: "node"},
|
||||
]
|
||||
|
||||
features: {
|
||||
labels: false
|
||||
}
|
||||
|
||||
model: {
|
||||
ident: "federation_module_mapping"
|
||||
attributes: {
|
||||
node_id: {
|
||||
ident: "nodeID"
|
||||
unique: true
|
||||
goType: "uint64"
|
||||
dal: { type: "ID" }
|
||||
}
|
||||
federation_module_id: {
|
||||
sortable: true,
|
||||
ident: "federationModuleID",
|
||||
goType: "uint64"
|
||||
storeIdent: "rel_federation_module"
|
||||
dal: { type: "ID" }
|
||||
}
|
||||
compose_module_id: {
|
||||
sortable: true,
|
||||
ident: "composeModuleID",
|
||||
goType: "uint64"
|
||||
storeIdent: "rel_compose_module"
|
||||
dal: { type: "ID" }
|
||||
}
|
||||
compose_namespace_id: {
|
||||
sortable: true,
|
||||
ident: "composeNamespaceID",
|
||||
goType: "uint64"
|
||||
storeIdent: "rel_compose_namespace"
|
||||
dal: { type: "ID" }
|
||||
}
|
||||
field_mapping: {
|
||||
goType: "types.ModuleFieldMappingSet"
|
||||
dal: { type: "JSON", defaultEmptyObject: true }
|
||||
omitSetter: true
|
||||
omitGetter: true
|
||||
}
|
||||
}
|
||||
|
||||
indexes: {
|
||||
"unique_module_compose_module": {
|
||||
attributes: ["federation_module_id", "compose_module_id", "compose_namespace_id" ]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
envoy: {
|
||||
omit: true
|
||||
}
|
||||
|
||||
filter: {
|
||||
struct: {
|
||||
compose_module_id: { goType: "uint64", ident: "composeModuleID", storeIdent: "rel_compose_module" }
|
||||
compose_namespace_id: { goType: "uint64", ident: "composeNamespaceID", storeIdent: "rel_compose_namespace" }
|
||||
federation_module_id: { goType: "uint64", ident: "federationModuleID", storeIdent: "rel_federation_module" }
|
||||
}
|
||||
|
||||
byValue: ["compose_module_id", "compose_namespace_id", "federation_module_id"]
|
||||
}
|
||||
|
||||
store: {
|
||||
ident: "federationModuleMapping"
|
||||
|
||||
api: {
|
||||
lookups: [
|
||||
{
|
||||
fields: ["federation_module_id", "compose_module_id", "compose_namespace_id"]
|
||||
description: """
|
||||
searches for module mapping by federation module id and compose module id
|
||||
|
||||
It returns module mapping
|
||||
"""
|
||||
}, {
|
||||
fields: ["federation_module_id"]
|
||||
description: """
|
||||
searches for module mapping by federation module id
|
||||
|
||||
It returns module mapping
|
||||
"""
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
109
server/federation/node.cue
Normal file
109
server/federation/node.cue
Normal file
@@ -0,0 +1,109 @@
|
||||
package federation
|
||||
|
||||
import (
|
||||
"github.com/cortezaproject/corteza/server/codegen/schema"
|
||||
)
|
||||
|
||||
node: {
|
||||
features: {
|
||||
labels: false
|
||||
paging: true
|
||||
sorting: true
|
||||
}
|
||||
|
||||
model: {
|
||||
ident: "federation_nodes"
|
||||
attributes: {
|
||||
id: schema.IdField
|
||||
shared_node_id: {
|
||||
sortable: true,
|
||||
ident: "sharedNodeID",
|
||||
goType: "uint64"
|
||||
dal: { type: "ID" }
|
||||
}
|
||||
name: {
|
||||
sortable: true
|
||||
dal: {}
|
||||
}
|
||||
base_url: {
|
||||
sortable: true,
|
||||
ident: "baseURL"
|
||||
dal: {}
|
||||
}
|
||||
status: {
|
||||
sortable: true,
|
||||
dal: {}
|
||||
}
|
||||
contact: {
|
||||
sortable: true,
|
||||
dal: {}
|
||||
}
|
||||
pair_token: {
|
||||
dal: {}
|
||||
}
|
||||
auth_token: {
|
||||
dal: {}
|
||||
}
|
||||
created_at: schema.SortableTimestampNowField
|
||||
updated_at: schema.SortableTimestampNilField
|
||||
deleted_at: schema.SortableTimestampNilField
|
||||
created_by: schema.AttributeUserRef
|
||||
updated_by: schema.AttributeUserRef
|
||||
deleted_by: schema.AttributeUserRef
|
||||
}
|
||||
|
||||
indexes: {
|
||||
"primary": { attribute: "id" }
|
||||
}
|
||||
}
|
||||
|
||||
envoy: {
|
||||
omit: true
|
||||
}
|
||||
|
||||
filter: {
|
||||
struct: {
|
||||
name: { goType: "string" }
|
||||
base_url: { goType: "string", ident: "baseURL" }
|
||||
status: { goType: "string" }
|
||||
deleted: { goType: "filter.State", storeIdent: "deleted_at" }
|
||||
}
|
||||
|
||||
query: ["name", "base_url"]
|
||||
byNilState: ["deleted"]
|
||||
}
|
||||
|
||||
rbac: {
|
||||
operations: {
|
||||
"manage": description: "Manage federation node"
|
||||
"module.create": description: "Create shared module"
|
||||
}
|
||||
}
|
||||
|
||||
store: {
|
||||
ident: "federationNode"
|
||||
|
||||
api: {
|
||||
lookups: [
|
||||
{
|
||||
fields: ["id"]
|
||||
description: """
|
||||
searches for federation node by ID
|
||||
|
||||
It returns federation node
|
||||
"""
|
||||
}, {
|
||||
fields: ["base_url", "shared_node_id"]
|
||||
description: """
|
||||
searches for node by shared-node-id and base-url
|
||||
"""
|
||||
}, {
|
||||
fields: ["shared_node_id"]
|
||||
description: """
|
||||
searches for node by shared-node-id
|
||||
"""
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
85
server/federation/node_sync.cue
Normal file
85
server/federation/node_sync.cue
Normal file
@@ -0,0 +1,85 @@
|
||||
package federation
|
||||
|
||||
import (
|
||||
"github.com/cortezaproject/corteza/server/codegen/schema"
|
||||
)
|
||||
|
||||
nodeSync: {
|
||||
features: {
|
||||
labels: false
|
||||
}
|
||||
|
||||
model: {
|
||||
ident: "federation_nodes_sync"
|
||||
attributes: {
|
||||
rel_node: {
|
||||
sortable: true,
|
||||
ident: "nodeID",
|
||||
storeIdent: "rel_node",
|
||||
goType: "uint64",
|
||||
dal: { type: "ID" }
|
||||
}
|
||||
rel_module: {
|
||||
sortable: true,
|
||||
ident: "moduleID",
|
||||
storeIdent: "rel_compose_module",
|
||||
goType: "uint64"
|
||||
dal: { type: "ID" }
|
||||
}
|
||||
sync_type: {
|
||||
sortable: true,
|
||||
goType: "string"
|
||||
dal: {}
|
||||
}
|
||||
sync_status: {
|
||||
sortable: true,
|
||||
goType: "string"
|
||||
dal: {}
|
||||
}
|
||||
time_of_action: schema.SortableTimestampField
|
||||
}
|
||||
|
||||
indexes: {
|
||||
"idx_rel_node": { attribute: "rel_node" }
|
||||
}
|
||||
}
|
||||
|
||||
filter: {
|
||||
struct: {
|
||||
rel_node: { goType: "uint64", storeIdent: "rel_node", ident: "nodeID" }
|
||||
rel_module: { goType: "uint64", storeIdent: "rel_compose_module", ident: "moduleID" }
|
||||
sync_status: { goType: "string", storeIdent: "sync_status" }
|
||||
sync_type: { goType: "string", storeIdent: "sync_type" }
|
||||
}
|
||||
|
||||
byValue: ["rel_node", "rel_module", "sync_status", "sync_type"]
|
||||
}
|
||||
|
||||
envoy: {
|
||||
omit: true
|
||||
}
|
||||
|
||||
store: {
|
||||
ident: "federationNodeSync"
|
||||
|
||||
api: {
|
||||
lookups: [
|
||||
{
|
||||
fields: ["rel_node"]
|
||||
description: """
|
||||
searches for sync activity by node ID
|
||||
|
||||
It returns sync activity
|
||||
"""
|
||||
}, {
|
||||
fields: ["rel_node", "rel_module", "sync_type", "sync_status"]
|
||||
description: """
|
||||
searches for activity by node, type and status
|
||||
|
||||
It returns sync activity
|
||||
"""
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
590
server/federation/rest.yaml
Normal file
590
server/federation/rest.yaml
Normal file
@@ -0,0 +1,590 @@
|
||||
---
|
||||
# This is direct conversion from old api/*/spec.json files
|
||||
# using https://www.json2yaml.com/
|
||||
#
|
||||
# Next step: swagger.
|
||||
|
||||
endpoints:
|
||||
- title: Federation node handshake
|
||||
path: "/nodes/{nodeID}/handshake"
|
||||
entrypoint: nodeHandshake
|
||||
parameters:
|
||||
path:
|
||||
- name: nodeID
|
||||
type: uint64
|
||||
required: true
|
||||
title: NodeID
|
||||
apis:
|
||||
- name: initialize
|
||||
method: POST
|
||||
title: Initialize the handshake step with node B
|
||||
path: ""
|
||||
parameters:
|
||||
post:
|
||||
- name: pairToken
|
||||
type: string
|
||||
required: true
|
||||
title: Pairing token to authenticate handshake initialization request
|
||||
- name: sharedNodeID
|
||||
type: uint64
|
||||
required: true
|
||||
title: Remote (invoker's) node ID
|
||||
- name: authToken
|
||||
type: string
|
||||
required: true
|
||||
title: Authentication token so that remote
|
||||
|
||||
- title: Federation nodes
|
||||
path: "/nodes"
|
||||
entrypoint: node
|
||||
apis:
|
||||
- name: search
|
||||
method: GET
|
||||
title: Search federated nodes
|
||||
path: "/"
|
||||
parameters:
|
||||
get:
|
||||
- name: query
|
||||
type: string
|
||||
title: Filter nodes by name and host
|
||||
- name: status
|
||||
type: string
|
||||
title: Filter by status
|
||||
- type: uint
|
||||
name: limit
|
||||
title: Limit
|
||||
- type: bool
|
||||
name: incTotal
|
||||
title: Include total counter
|
||||
- type: string
|
||||
name: pageCursor
|
||||
title: Page cursor
|
||||
- type: string
|
||||
name: sort
|
||||
title: Sort items
|
||||
|
||||
- name: create
|
||||
method: POST
|
||||
title: Create a new federation node
|
||||
path: "/"
|
||||
parameters:
|
||||
post:
|
||||
- name: baseURL
|
||||
type: string
|
||||
required: false
|
||||
title: Federation API base URL
|
||||
- name: name
|
||||
type: string
|
||||
required: false
|
||||
title: Name for this node
|
||||
- name: contact
|
||||
type: string
|
||||
required: false
|
||||
title: Contact email
|
||||
- name: pairingURI
|
||||
type: string
|
||||
required: false
|
||||
title: Pairing URI
|
||||
|
||||
- name: read
|
||||
method: GET
|
||||
title: Read a federation node
|
||||
path: "/{nodeID}"
|
||||
parameters:
|
||||
path: [ { name: nodeID, type: uint64, required: true, title: NodeID } ]
|
||||
|
||||
- name: generateURI
|
||||
method: POST
|
||||
title: Creates new sharable federation URI
|
||||
path: "/{nodeID}/uri"
|
||||
parameters: { path: [ { name: nodeID, type: uint64, required: true, title: NodeID } ] }
|
||||
|
||||
- name: update
|
||||
method: POST
|
||||
title: Updates existing node
|
||||
path: "/{nodeID}"
|
||||
parameters:
|
||||
path: [ { name: nodeID, type: uint64, required: true, title: NodeID } ]
|
||||
post:
|
||||
- name: name
|
||||
type: string
|
||||
required: false
|
||||
title: Name for this node
|
||||
- name: contact
|
||||
type: string
|
||||
required: false
|
||||
title: Contact email
|
||||
- name: baseURL
|
||||
type: string
|
||||
required: false
|
||||
title: Federation API base URL
|
||||
|
||||
- name: delete
|
||||
method: DELETE
|
||||
title: Deletes node
|
||||
path: "/{nodeID}"
|
||||
parameters: { path: [ { name: nodeID, type: uint64, required: true, title: NodeID } ] }
|
||||
|
||||
- name: undelete
|
||||
method: POST
|
||||
title: Undeletes a node
|
||||
path: "/{nodeID}/undelete"
|
||||
parameters: { path: [ { name: nodeID, type: uint64, required: true, title: NodeID } ] }
|
||||
|
||||
- name: pair
|
||||
method: POST
|
||||
title: Initialize the pairing process between the two nodes
|
||||
path: "/{nodeID}/pair"
|
||||
parameters: { path: [ { name: nodeID, type: uint64, required: true, title: NodeID } ] }
|
||||
|
||||
- name: handshake confirm
|
||||
method: POST
|
||||
title: Confirm the requested handshake
|
||||
path: "/{nodeID}/handshake-confirm"
|
||||
parameters: { path: [ { name: nodeID, type: uint64, required: true, title: NodeID } ] }
|
||||
|
||||
- name: handshake complete
|
||||
method: POST
|
||||
title: Complete the handshake
|
||||
path: "/{nodeID}/handshake-complete"
|
||||
parameters:
|
||||
path: [ { name: nodeID, type: uint64, required: true, title: NodeID } ]
|
||||
post:
|
||||
- name: authToken
|
||||
type: string
|
||||
required: true
|
||||
title: Node A token
|
||||
|
||||
- title: Manage structure
|
||||
description: Manage structure
|
||||
entrypoint: manageStructure
|
||||
path: "/nodes/{nodeID}/modules"
|
||||
authentication: []
|
||||
imports:
|
||||
- github.com/cortezaproject/corteza/server/federation/types
|
||||
apis:
|
||||
- name: readExposed
|
||||
method: GET
|
||||
title: Exposed settings for module
|
||||
path: "/{moduleID}/exposed"
|
||||
parameters:
|
||||
path:
|
||||
- type: uint64
|
||||
name: nodeID
|
||||
required: true
|
||||
title: Node ID
|
||||
- type: uint64
|
||||
name: moduleID
|
||||
required: true
|
||||
title: Module ID
|
||||
- name: createExposed
|
||||
method: PUT
|
||||
title: Add module to federation
|
||||
path: "/"
|
||||
parameters:
|
||||
path:
|
||||
- type: uint64
|
||||
name: nodeID
|
||||
required: true
|
||||
title: Node ID
|
||||
post:
|
||||
- type: uint64
|
||||
name: composeModuleID
|
||||
required: true
|
||||
title: Compose module id
|
||||
- type: uint64
|
||||
name: composeNamespaceID
|
||||
required: true
|
||||
title: Compose namespace id
|
||||
- type: string
|
||||
name: name
|
||||
required: true
|
||||
title: Module name
|
||||
- type: string
|
||||
name: handle
|
||||
required: true
|
||||
title: Module handle
|
||||
- type: types.ModuleFieldSet
|
||||
name: fields
|
||||
required: false
|
||||
title: Exposed module fields
|
||||
- name: updateExposed
|
||||
method: POST
|
||||
title: Update already exposed module
|
||||
path: "/{moduleID}/exposed"
|
||||
parameters:
|
||||
path:
|
||||
- type: uint64
|
||||
name: nodeID
|
||||
required: true
|
||||
title: Node ID
|
||||
- type: uint64
|
||||
name: moduleID
|
||||
required: true
|
||||
title: Module ID
|
||||
post:
|
||||
- type: uint64
|
||||
name: composeModuleID
|
||||
required: true
|
||||
title: Compose module id
|
||||
- type: uint64
|
||||
name: composeNamespaceID
|
||||
required: true
|
||||
title: Compose namespace id
|
||||
- type: string
|
||||
name: name
|
||||
required: true
|
||||
title: Module name
|
||||
- type: string
|
||||
name: handle
|
||||
required: true
|
||||
title: Module handle
|
||||
- type: types.ModuleFieldSet
|
||||
name: fields
|
||||
required: false
|
||||
title: Exposed module fields
|
||||
- name: removeExposed
|
||||
method: DELETE
|
||||
title: Remove from federation
|
||||
path: "/{moduleID}/exposed"
|
||||
parameters:
|
||||
path:
|
||||
- type: uint64
|
||||
name: nodeID
|
||||
required: true
|
||||
title: Node ID
|
||||
- type: uint64
|
||||
name: moduleID
|
||||
required: true
|
||||
title: Module ID
|
||||
- name: readShared
|
||||
method: GET
|
||||
title: Shared settings for module
|
||||
path: "/{moduleID}/shared"
|
||||
parameters:
|
||||
path:
|
||||
- type: uint64
|
||||
name: nodeID
|
||||
required: true
|
||||
title: Node ID
|
||||
- type: uint64
|
||||
name: moduleID
|
||||
required: true
|
||||
title: Module ID
|
||||
- name: createMappings
|
||||
method: PUT
|
||||
title: Add fields mappings to federated module
|
||||
path: "/{moduleID}/mapped"
|
||||
parameters:
|
||||
path:
|
||||
- type: uint64
|
||||
name: nodeID
|
||||
required: true
|
||||
title: Node ID
|
||||
- type: uint64
|
||||
name: moduleID
|
||||
required: true
|
||||
title: Module ID
|
||||
post:
|
||||
- type: uint64
|
||||
name: composeModuleID
|
||||
required: true
|
||||
title: Compose module id
|
||||
- type: uint64
|
||||
name: composeNamespaceID
|
||||
required: true
|
||||
title: Compose namespace id
|
||||
- type: types.ModuleFieldMappingSet
|
||||
name: fields
|
||||
required: false
|
||||
title: Exposed module fields
|
||||
- name: readMappings
|
||||
method: GET
|
||||
title: Fields mappings for module
|
||||
path: "/{moduleID}/mapped"
|
||||
parameters:
|
||||
path:
|
||||
- type: uint64
|
||||
name: nodeID
|
||||
required: true
|
||||
title: Node ID
|
||||
- type: uint64
|
||||
name: moduleID
|
||||
required: true
|
||||
title: Module ID
|
||||
get:
|
||||
- name: composeModuleID
|
||||
type: uint64
|
||||
required: false
|
||||
title: Compose module id
|
||||
- name: listAll
|
||||
method: GET
|
||||
title: List of shared/exposed/mapped modules
|
||||
path: "/"
|
||||
parameters:
|
||||
path:
|
||||
- type: uint64
|
||||
name: nodeID
|
||||
required: true
|
||||
title: Node ID
|
||||
get:
|
||||
- name: shared
|
||||
type: bool
|
||||
required: false
|
||||
title: List shared modules
|
||||
- name: exposed
|
||||
type: bool
|
||||
required: false
|
||||
title: List exposed modules
|
||||
- name: mapped
|
||||
type: bool
|
||||
required: false
|
||||
title: List mapped modules
|
||||
|
||||
- title: Sync structure
|
||||
description: Sync structure
|
||||
entrypoint: syncStructure
|
||||
path: "/nodes/{nodeID}/modules/exposed"
|
||||
authentication: []
|
||||
apis:
|
||||
- name: readExposedInternal
|
||||
method: GET
|
||||
title: List all exposed modules changes
|
||||
path: "/"
|
||||
parameters:
|
||||
path:
|
||||
- type: uint64
|
||||
name: nodeID
|
||||
required: true
|
||||
title: Node ID
|
||||
get:
|
||||
- type: uint64
|
||||
name: lastSync
|
||||
required: false
|
||||
title: Last sync timestamp
|
||||
- type: string
|
||||
name: query
|
||||
required: false
|
||||
title: Search query
|
||||
- type: uint
|
||||
name: limit
|
||||
title: Limit
|
||||
- type: string
|
||||
name: pageCursor
|
||||
title: Page cursor
|
||||
- type: string
|
||||
name: sort
|
||||
title: Sort items
|
||||
- name: readExposedSocial
|
||||
method: GET
|
||||
title: List all exposed modules changes in activity streams format
|
||||
path: "/activity-stream"
|
||||
parameters:
|
||||
path:
|
||||
- type: uint64
|
||||
name: nodeID
|
||||
required: true
|
||||
title: Node ID
|
||||
get:
|
||||
- type: uint64
|
||||
name: lastSync
|
||||
required: false
|
||||
title: Last sync timestamp
|
||||
- type: string
|
||||
name: query
|
||||
required: false
|
||||
title: Search query
|
||||
- type: uint
|
||||
name: limit
|
||||
title: Limit
|
||||
- type: string
|
||||
name: pageCursor
|
||||
title: Page cursor
|
||||
- type: string
|
||||
name: sort
|
||||
title: Sort items
|
||||
|
||||
- title: Sync data
|
||||
description: Sync data
|
||||
entrypoint: syncData
|
||||
path: "/nodes/{nodeID}/modules"
|
||||
authentication: []
|
||||
apis:
|
||||
- name: readExposedAll
|
||||
method: GET
|
||||
title: List all record changes
|
||||
path: "/exposed/records/"
|
||||
parameters:
|
||||
path:
|
||||
- type: uint64
|
||||
name: nodeID
|
||||
required: true
|
||||
title: Node ID
|
||||
get:
|
||||
- type: uint64
|
||||
name: lastSync
|
||||
required: false
|
||||
title: Last sync timestamp
|
||||
- type: string
|
||||
name: query
|
||||
required: false
|
||||
title: Search query
|
||||
- type: uint
|
||||
name: limit
|
||||
title: Limit
|
||||
- type: string
|
||||
name: pageCursor
|
||||
title: Page cursor
|
||||
- type: string
|
||||
name: sort
|
||||
title: Sort items
|
||||
- name: readExposedInternal
|
||||
method: GET
|
||||
title: List all records per module
|
||||
path: "/{moduleID}/records/"
|
||||
parameters:
|
||||
path:
|
||||
- type: uint64
|
||||
name: nodeID
|
||||
required: true
|
||||
title: Node ID
|
||||
- type: uint64
|
||||
name: moduleID
|
||||
required: true
|
||||
title: Module ID
|
||||
get:
|
||||
- type: uint64
|
||||
name: lastSync
|
||||
required: false
|
||||
title: Last sync timestamp
|
||||
- type: string
|
||||
name: query
|
||||
required: false
|
||||
title: Search query
|
||||
- type: uint
|
||||
name: limit
|
||||
required: false
|
||||
title: Limit
|
||||
- type: string
|
||||
name: pageCursor
|
||||
required: false
|
||||
title: Page cursor
|
||||
- type: string
|
||||
name: sort
|
||||
required: false
|
||||
title: Sort items
|
||||
- name: readExposedSocial
|
||||
method: GET
|
||||
title: List all records per module in activitystreams format
|
||||
path: "/{moduleID}/records/activity-stream/"
|
||||
parameters:
|
||||
path:
|
||||
- type: uint64
|
||||
name: nodeID
|
||||
required: true
|
||||
title: Node ID
|
||||
- type: uint64
|
||||
name: moduleID
|
||||
required: true
|
||||
title: Module ID
|
||||
get:
|
||||
- type: uint64
|
||||
name: lastSync
|
||||
required: false
|
||||
title: Last sync timestamp
|
||||
- type: string
|
||||
name: query
|
||||
required: false
|
||||
title: Search query
|
||||
- type: uint
|
||||
name: limit
|
||||
required: false
|
||||
title: Limit
|
||||
- type: string
|
||||
name: pageCursor
|
||||
required: false
|
||||
title: Page cursor
|
||||
- type: string
|
||||
name: sort
|
||||
required: false
|
||||
title: Sort items
|
||||
|
||||
- title: Permissions
|
||||
entrypoint: permissions
|
||||
path: "/permissions"
|
||||
authentication:
|
||||
- Client ID
|
||||
- Session ID
|
||||
imports:
|
||||
- github.com/cortezaproject/corteza/server/pkg/rbac
|
||||
apis:
|
||||
- name: list
|
||||
path: "/"
|
||||
method: GET
|
||||
title: Retrieve defined permissions
|
||||
- name: effective
|
||||
path: "/effective"
|
||||
method: GET
|
||||
title: Effective rules for current user
|
||||
parameters:
|
||||
get:
|
||||
- name: resource
|
||||
type: string
|
||||
required: false
|
||||
title: Show only rules for a specific resource
|
||||
- name: trace
|
||||
path: "/trace"
|
||||
method: GET
|
||||
title: Evaluate rules for given user/role combo
|
||||
parameters:
|
||||
get:
|
||||
- name: resource
|
||||
type: "[]string"
|
||||
required: false
|
||||
title: Show only rules for a specific resource
|
||||
- name: userID
|
||||
type: uint64
|
||||
required: false
|
||||
- name: roleID
|
||||
type: "[]uint64"
|
||||
required: false
|
||||
- name: read
|
||||
path: "/{roleID}/rules"
|
||||
method: GET
|
||||
title: Retrieve role permissions
|
||||
parameters:
|
||||
path:
|
||||
- name: roleID
|
||||
type: uint64
|
||||
required: true
|
||||
title: Role ID
|
||||
get:
|
||||
- name: resource
|
||||
type: "[]string"
|
||||
required: false
|
||||
title: Show only rules for a specific resource
|
||||
- name: delete
|
||||
path: "/{roleID}/rules"
|
||||
method: DELETE
|
||||
title: Remove all defined role permissions
|
||||
parameters:
|
||||
path:
|
||||
- name: roleID
|
||||
type: uint64
|
||||
required: true
|
||||
title: Role ID
|
||||
- name: update
|
||||
path: "/{roleID}/rules"
|
||||
method: PATCH
|
||||
title: Update permission settings
|
||||
parameters:
|
||||
path:
|
||||
- name: roleID
|
||||
type: uint64
|
||||
required: true
|
||||
title: Role ID
|
||||
post:
|
||||
- name: rules
|
||||
type: rbac.RuleSet
|
||||
required: true
|
||||
title: List of permission rules to set
|
||||
190
server/federation/rest/handlers/manageStructure.go
Normal file
190
server/federation/rest/handlers/manageStructure.go
Normal file
@@ -0,0 +1,190 @@
|
||||
package handlers
|
||||
|
||||
// This file is auto-generated.
|
||||
//
|
||||
// Changes to this file may cause incorrect behavior and will be lost if
|
||||
// the code is regenerated.
|
||||
//
|
||||
// Definitions file that controls how this file is generated:
|
||||
//
|
||||
|
||||
import (
|
||||
"context"
|
||||
"github.com/cortezaproject/corteza/server/federation/rest/request"
|
||||
"github.com/cortezaproject/corteza/server/pkg/api"
|
||||
"github.com/go-chi/chi/v5"
|
||||
"net/http"
|
||||
)
|
||||
|
||||
type (
|
||||
// Internal API interface
|
||||
ManageStructureAPI interface {
|
||||
ReadExposed(context.Context, *request.ManageStructureReadExposed) (interface{}, error)
|
||||
CreateExposed(context.Context, *request.ManageStructureCreateExposed) (interface{}, error)
|
||||
UpdateExposed(context.Context, *request.ManageStructureUpdateExposed) (interface{}, error)
|
||||
RemoveExposed(context.Context, *request.ManageStructureRemoveExposed) (interface{}, error)
|
||||
ReadShared(context.Context, *request.ManageStructureReadShared) (interface{}, error)
|
||||
CreateMappings(context.Context, *request.ManageStructureCreateMappings) (interface{}, error)
|
||||
ReadMappings(context.Context, *request.ManageStructureReadMappings) (interface{}, error)
|
||||
ListAll(context.Context, *request.ManageStructureListAll) (interface{}, error)
|
||||
}
|
||||
|
||||
// HTTP API interface
|
||||
ManageStructure struct {
|
||||
ReadExposed func(http.ResponseWriter, *http.Request)
|
||||
CreateExposed func(http.ResponseWriter, *http.Request)
|
||||
UpdateExposed func(http.ResponseWriter, *http.Request)
|
||||
RemoveExposed func(http.ResponseWriter, *http.Request)
|
||||
ReadShared func(http.ResponseWriter, *http.Request)
|
||||
CreateMappings func(http.ResponseWriter, *http.Request)
|
||||
ReadMappings func(http.ResponseWriter, *http.Request)
|
||||
ListAll func(http.ResponseWriter, *http.Request)
|
||||
}
|
||||
)
|
||||
|
||||
func NewManageStructure(h ManageStructureAPI) *ManageStructure {
|
||||
return &ManageStructure{
|
||||
ReadExposed: func(w http.ResponseWriter, r *http.Request) {
|
||||
defer r.Body.Close()
|
||||
params := request.NewManageStructureReadExposed()
|
||||
if err := params.Fill(r); err != nil {
|
||||
api.Send(w, r, err)
|
||||
return
|
||||
}
|
||||
|
||||
value, err := h.ReadExposed(r.Context(), params)
|
||||
if err != nil {
|
||||
api.Send(w, r, err)
|
||||
return
|
||||
}
|
||||
|
||||
api.Send(w, r, value)
|
||||
},
|
||||
CreateExposed: func(w http.ResponseWriter, r *http.Request) {
|
||||
defer r.Body.Close()
|
||||
params := request.NewManageStructureCreateExposed()
|
||||
if err := params.Fill(r); err != nil {
|
||||
api.Send(w, r, err)
|
||||
return
|
||||
}
|
||||
|
||||
value, err := h.CreateExposed(r.Context(), params)
|
||||
if err != nil {
|
||||
api.Send(w, r, err)
|
||||
return
|
||||
}
|
||||
|
||||
api.Send(w, r, value)
|
||||
},
|
||||
UpdateExposed: func(w http.ResponseWriter, r *http.Request) {
|
||||
defer r.Body.Close()
|
||||
params := request.NewManageStructureUpdateExposed()
|
||||
if err := params.Fill(r); err != nil {
|
||||
api.Send(w, r, err)
|
||||
return
|
||||
}
|
||||
|
||||
value, err := h.UpdateExposed(r.Context(), params)
|
||||
if err != nil {
|
||||
api.Send(w, r, err)
|
||||
return
|
||||
}
|
||||
|
||||
api.Send(w, r, value)
|
||||
},
|
||||
RemoveExposed: func(w http.ResponseWriter, r *http.Request) {
|
||||
defer r.Body.Close()
|
||||
params := request.NewManageStructureRemoveExposed()
|
||||
if err := params.Fill(r); err != nil {
|
||||
api.Send(w, r, err)
|
||||
return
|
||||
}
|
||||
|
||||
value, err := h.RemoveExposed(r.Context(), params)
|
||||
if err != nil {
|
||||
api.Send(w, r, err)
|
||||
return
|
||||
}
|
||||
|
||||
api.Send(w, r, value)
|
||||
},
|
||||
ReadShared: func(w http.ResponseWriter, r *http.Request) {
|
||||
defer r.Body.Close()
|
||||
params := request.NewManageStructureReadShared()
|
||||
if err := params.Fill(r); err != nil {
|
||||
api.Send(w, r, err)
|
||||
return
|
||||
}
|
||||
|
||||
value, err := h.ReadShared(r.Context(), params)
|
||||
if err != nil {
|
||||
api.Send(w, r, err)
|
||||
return
|
||||
}
|
||||
|
||||
api.Send(w, r, value)
|
||||
},
|
||||
CreateMappings: func(w http.ResponseWriter, r *http.Request) {
|
||||
defer r.Body.Close()
|
||||
params := request.NewManageStructureCreateMappings()
|
||||
if err := params.Fill(r); err != nil {
|
||||
api.Send(w, r, err)
|
||||
return
|
||||
}
|
||||
|
||||
value, err := h.CreateMappings(r.Context(), params)
|
||||
if err != nil {
|
||||
api.Send(w, r, err)
|
||||
return
|
||||
}
|
||||
|
||||
api.Send(w, r, value)
|
||||
},
|
||||
ReadMappings: func(w http.ResponseWriter, r *http.Request) {
|
||||
defer r.Body.Close()
|
||||
params := request.NewManageStructureReadMappings()
|
||||
if err := params.Fill(r); err != nil {
|
||||
api.Send(w, r, err)
|
||||
return
|
||||
}
|
||||
|
||||
value, err := h.ReadMappings(r.Context(), params)
|
||||
if err != nil {
|
||||
api.Send(w, r, err)
|
||||
return
|
||||
}
|
||||
|
||||
api.Send(w, r, value)
|
||||
},
|
||||
ListAll: func(w http.ResponseWriter, r *http.Request) {
|
||||
defer r.Body.Close()
|
||||
params := request.NewManageStructureListAll()
|
||||
if err := params.Fill(r); err != nil {
|
||||
api.Send(w, r, err)
|
||||
return
|
||||
}
|
||||
|
||||
value, err := h.ListAll(r.Context(), params)
|
||||
if err != nil {
|
||||
api.Send(w, r, err)
|
||||
return
|
||||
}
|
||||
|
||||
api.Send(w, r, value)
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func (h ManageStructure) MountRoutes(r chi.Router, middlewares ...func(http.Handler) http.Handler) {
|
||||
r.Group(func(r chi.Router) {
|
||||
r.Use(middlewares...)
|
||||
r.Get("/nodes/{nodeID}/modules/{moduleID}/exposed", h.ReadExposed)
|
||||
r.Put("/nodes/{nodeID}/modules/", h.CreateExposed)
|
||||
r.Post("/nodes/{nodeID}/modules/{moduleID}/exposed", h.UpdateExposed)
|
||||
r.Delete("/nodes/{nodeID}/modules/{moduleID}/exposed", h.RemoveExposed)
|
||||
r.Get("/nodes/{nodeID}/modules/{moduleID}/shared", h.ReadShared)
|
||||
r.Put("/nodes/{nodeID}/modules/{moduleID}/mapped", h.CreateMappings)
|
||||
r.Get("/nodes/{nodeID}/modules/{moduleID}/mapped", h.ReadMappings)
|
||||
r.Get("/nodes/{nodeID}/modules/", h.ListAll)
|
||||
})
|
||||
}
|
||||
228
server/federation/rest/handlers/node.go
Normal file
228
server/federation/rest/handlers/node.go
Normal file
@@ -0,0 +1,228 @@
|
||||
package handlers
|
||||
|
||||
// This file is auto-generated.
|
||||
//
|
||||
// Changes to this file may cause incorrect behavior and will be lost if
|
||||
// the code is regenerated.
|
||||
//
|
||||
// Definitions file that controls how this file is generated:
|
||||
//
|
||||
|
||||
import (
|
||||
"context"
|
||||
"github.com/cortezaproject/corteza/server/federation/rest/request"
|
||||
"github.com/cortezaproject/corteza/server/pkg/api"
|
||||
"github.com/go-chi/chi/v5"
|
||||
"net/http"
|
||||
)
|
||||
|
||||
type (
|
||||
// Internal API interface
|
||||
NodeAPI interface {
|
||||
Search(context.Context, *request.NodeSearch) (interface{}, error)
|
||||
Create(context.Context, *request.NodeCreate) (interface{}, error)
|
||||
Read(context.Context, *request.NodeRead) (interface{}, error)
|
||||
GenerateURI(context.Context, *request.NodeGenerateURI) (interface{}, error)
|
||||
Update(context.Context, *request.NodeUpdate) (interface{}, error)
|
||||
Delete(context.Context, *request.NodeDelete) (interface{}, error)
|
||||
Undelete(context.Context, *request.NodeUndelete) (interface{}, error)
|
||||
Pair(context.Context, *request.NodePair) (interface{}, error)
|
||||
HandshakeConfirm(context.Context, *request.NodeHandshakeConfirm) (interface{}, error)
|
||||
HandshakeComplete(context.Context, *request.NodeHandshakeComplete) (interface{}, error)
|
||||
}
|
||||
|
||||
// HTTP API interface
|
||||
Node struct {
|
||||
Search func(http.ResponseWriter, *http.Request)
|
||||
Create func(http.ResponseWriter, *http.Request)
|
||||
Read func(http.ResponseWriter, *http.Request)
|
||||
GenerateURI func(http.ResponseWriter, *http.Request)
|
||||
Update func(http.ResponseWriter, *http.Request)
|
||||
Delete func(http.ResponseWriter, *http.Request)
|
||||
Undelete func(http.ResponseWriter, *http.Request)
|
||||
Pair func(http.ResponseWriter, *http.Request)
|
||||
HandshakeConfirm func(http.ResponseWriter, *http.Request)
|
||||
HandshakeComplete func(http.ResponseWriter, *http.Request)
|
||||
}
|
||||
)
|
||||
|
||||
func NewNode(h NodeAPI) *Node {
|
||||
return &Node{
|
||||
Search: func(w http.ResponseWriter, r *http.Request) {
|
||||
defer r.Body.Close()
|
||||
params := request.NewNodeSearch()
|
||||
if err := params.Fill(r); err != nil {
|
||||
api.Send(w, r, err)
|
||||
return
|
||||
}
|
||||
|
||||
value, err := h.Search(r.Context(), params)
|
||||
if err != nil {
|
||||
api.Send(w, r, err)
|
||||
return
|
||||
}
|
||||
|
||||
api.Send(w, r, value)
|
||||
},
|
||||
Create: func(w http.ResponseWriter, r *http.Request) {
|
||||
defer r.Body.Close()
|
||||
params := request.NewNodeCreate()
|
||||
if err := params.Fill(r); err != nil {
|
||||
api.Send(w, r, err)
|
||||
return
|
||||
}
|
||||
|
||||
value, err := h.Create(r.Context(), params)
|
||||
if err != nil {
|
||||
api.Send(w, r, err)
|
||||
return
|
||||
}
|
||||
|
||||
api.Send(w, r, value)
|
||||
},
|
||||
Read: func(w http.ResponseWriter, r *http.Request) {
|
||||
defer r.Body.Close()
|
||||
params := request.NewNodeRead()
|
||||
if err := params.Fill(r); err != nil {
|
||||
api.Send(w, r, err)
|
||||
return
|
||||
}
|
||||
|
||||
value, err := h.Read(r.Context(), params)
|
||||
if err != nil {
|
||||
api.Send(w, r, err)
|
||||
return
|
||||
}
|
||||
|
||||
api.Send(w, r, value)
|
||||
},
|
||||
GenerateURI: func(w http.ResponseWriter, r *http.Request) {
|
||||
defer r.Body.Close()
|
||||
params := request.NewNodeGenerateURI()
|
||||
if err := params.Fill(r); err != nil {
|
||||
api.Send(w, r, err)
|
||||
return
|
||||
}
|
||||
|
||||
value, err := h.GenerateURI(r.Context(), params)
|
||||
if err != nil {
|
||||
api.Send(w, r, err)
|
||||
return
|
||||
}
|
||||
|
||||
api.Send(w, r, value)
|
||||
},
|
||||
Update: func(w http.ResponseWriter, r *http.Request) {
|
||||
defer r.Body.Close()
|
||||
params := request.NewNodeUpdate()
|
||||
if err := params.Fill(r); err != nil {
|
||||
api.Send(w, r, err)
|
||||
return
|
||||
}
|
||||
|
||||
value, err := h.Update(r.Context(), params)
|
||||
if err != nil {
|
||||
api.Send(w, r, err)
|
||||
return
|
||||
}
|
||||
|
||||
api.Send(w, r, value)
|
||||
},
|
||||
Delete: func(w http.ResponseWriter, r *http.Request) {
|
||||
defer r.Body.Close()
|
||||
params := request.NewNodeDelete()
|
||||
if err := params.Fill(r); err != nil {
|
||||
api.Send(w, r, err)
|
||||
return
|
||||
}
|
||||
|
||||
value, err := h.Delete(r.Context(), params)
|
||||
if err != nil {
|
||||
api.Send(w, r, err)
|
||||
return
|
||||
}
|
||||
|
||||
api.Send(w, r, value)
|
||||
},
|
||||
Undelete: func(w http.ResponseWriter, r *http.Request) {
|
||||
defer r.Body.Close()
|
||||
params := request.NewNodeUndelete()
|
||||
if err := params.Fill(r); err != nil {
|
||||
api.Send(w, r, err)
|
||||
return
|
||||
}
|
||||
|
||||
value, err := h.Undelete(r.Context(), params)
|
||||
if err != nil {
|
||||
api.Send(w, r, err)
|
||||
return
|
||||
}
|
||||
|
||||
api.Send(w, r, value)
|
||||
},
|
||||
Pair: func(w http.ResponseWriter, r *http.Request) {
|
||||
defer r.Body.Close()
|
||||
params := request.NewNodePair()
|
||||
if err := params.Fill(r); err != nil {
|
||||
api.Send(w, r, err)
|
||||
return
|
||||
}
|
||||
|
||||
value, err := h.Pair(r.Context(), params)
|
||||
if err != nil {
|
||||
api.Send(w, r, err)
|
||||
return
|
||||
}
|
||||
|
||||
api.Send(w, r, value)
|
||||
},
|
||||
HandshakeConfirm: func(w http.ResponseWriter, r *http.Request) {
|
||||
defer r.Body.Close()
|
||||
params := request.NewNodeHandshakeConfirm()
|
||||
if err := params.Fill(r); err != nil {
|
||||
api.Send(w, r, err)
|
||||
return
|
||||
}
|
||||
|
||||
value, err := h.HandshakeConfirm(r.Context(), params)
|
||||
if err != nil {
|
||||
api.Send(w, r, err)
|
||||
return
|
||||
}
|
||||
|
||||
api.Send(w, r, value)
|
||||
},
|
||||
HandshakeComplete: func(w http.ResponseWriter, r *http.Request) {
|
||||
defer r.Body.Close()
|
||||
params := request.NewNodeHandshakeComplete()
|
||||
if err := params.Fill(r); err != nil {
|
||||
api.Send(w, r, err)
|
||||
return
|
||||
}
|
||||
|
||||
value, err := h.HandshakeComplete(r.Context(), params)
|
||||
if err != nil {
|
||||
api.Send(w, r, err)
|
||||
return
|
||||
}
|
||||
|
||||
api.Send(w, r, value)
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func (h Node) MountRoutes(r chi.Router, middlewares ...func(http.Handler) http.Handler) {
|
||||
r.Group(func(r chi.Router) {
|
||||
r.Use(middlewares...)
|
||||
r.Get("/nodes/", h.Search)
|
||||
r.Post("/nodes/", h.Create)
|
||||
r.Get("/nodes/{nodeID}", h.Read)
|
||||
r.Post("/nodes/{nodeID}/uri", h.GenerateURI)
|
||||
r.Post("/nodes/{nodeID}", h.Update)
|
||||
r.Delete("/nodes/{nodeID}", h.Delete)
|
||||
r.Post("/nodes/{nodeID}/undelete", h.Undelete)
|
||||
r.Post("/nodes/{nodeID}/pair", h.Pair)
|
||||
r.Post("/nodes/{nodeID}/handshake-confirm", h.HandshakeConfirm)
|
||||
r.Post("/nodes/{nodeID}/handshake-complete", h.HandshakeComplete)
|
||||
})
|
||||
}
|
||||
57
server/federation/rest/handlers/nodeHandshake.go
Normal file
57
server/federation/rest/handlers/nodeHandshake.go
Normal file
@@ -0,0 +1,57 @@
|
||||
package handlers
|
||||
|
||||
// This file is auto-generated.
|
||||
//
|
||||
// Changes to this file may cause incorrect behavior and will be lost if
|
||||
// the code is regenerated.
|
||||
//
|
||||
// Definitions file that controls how this file is generated:
|
||||
//
|
||||
|
||||
import (
|
||||
"context"
|
||||
"github.com/cortezaproject/corteza/server/federation/rest/request"
|
||||
"github.com/cortezaproject/corteza/server/pkg/api"
|
||||
"github.com/go-chi/chi/v5"
|
||||
"net/http"
|
||||
)
|
||||
|
||||
type (
|
||||
// Internal API interface
|
||||
NodeHandshakeAPI interface {
|
||||
Initialize(context.Context, *request.NodeHandshakeInitialize) (interface{}, error)
|
||||
}
|
||||
|
||||
// HTTP API interface
|
||||
NodeHandshake struct {
|
||||
Initialize func(http.ResponseWriter, *http.Request)
|
||||
}
|
||||
)
|
||||
|
||||
func NewNodeHandshake(h NodeHandshakeAPI) *NodeHandshake {
|
||||
return &NodeHandshake{
|
||||
Initialize: func(w http.ResponseWriter, r *http.Request) {
|
||||
defer r.Body.Close()
|
||||
params := request.NewNodeHandshakeInitialize()
|
||||
if err := params.Fill(r); err != nil {
|
||||
api.Send(w, r, err)
|
||||
return
|
||||
}
|
||||
|
||||
value, err := h.Initialize(r.Context(), params)
|
||||
if err != nil {
|
||||
api.Send(w, r, err)
|
||||
return
|
||||
}
|
||||
|
||||
api.Send(w, r, value)
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func (h NodeHandshake) MountRoutes(r chi.Router, middlewares ...func(http.Handler) http.Handler) {
|
||||
r.Group(func(r chi.Router) {
|
||||
r.Use(middlewares...)
|
||||
r.Post("/nodes/{nodeID}/handshake", h.Initialize)
|
||||
})
|
||||
}
|
||||
152
server/federation/rest/handlers/permissions.go
Normal file
152
server/federation/rest/handlers/permissions.go
Normal file
@@ -0,0 +1,152 @@
|
||||
package handlers
|
||||
|
||||
// This file is auto-generated.
|
||||
//
|
||||
// Changes to this file may cause incorrect behavior and will be lost if
|
||||
// the code is regenerated.
|
||||
//
|
||||
// Definitions file that controls how this file is generated:
|
||||
//
|
||||
|
||||
import (
|
||||
"context"
|
||||
"github.com/cortezaproject/corteza/server/federation/rest/request"
|
||||
"github.com/cortezaproject/corteza/server/pkg/api"
|
||||
"github.com/go-chi/chi/v5"
|
||||
"net/http"
|
||||
)
|
||||
|
||||
type (
|
||||
// Internal API interface
|
||||
PermissionsAPI interface {
|
||||
List(context.Context, *request.PermissionsList) (interface{}, error)
|
||||
Effective(context.Context, *request.PermissionsEffective) (interface{}, error)
|
||||
Trace(context.Context, *request.PermissionsTrace) (interface{}, error)
|
||||
Read(context.Context, *request.PermissionsRead) (interface{}, error)
|
||||
Delete(context.Context, *request.PermissionsDelete) (interface{}, error)
|
||||
Update(context.Context, *request.PermissionsUpdate) (interface{}, error)
|
||||
}
|
||||
|
||||
// HTTP API interface
|
||||
Permissions struct {
|
||||
List func(http.ResponseWriter, *http.Request)
|
||||
Effective func(http.ResponseWriter, *http.Request)
|
||||
Trace func(http.ResponseWriter, *http.Request)
|
||||
Read func(http.ResponseWriter, *http.Request)
|
||||
Delete func(http.ResponseWriter, *http.Request)
|
||||
Update func(http.ResponseWriter, *http.Request)
|
||||
}
|
||||
)
|
||||
|
||||
func NewPermissions(h PermissionsAPI) *Permissions {
|
||||
return &Permissions{
|
||||
List: func(w http.ResponseWriter, r *http.Request) {
|
||||
defer r.Body.Close()
|
||||
params := request.NewPermissionsList()
|
||||
if err := params.Fill(r); err != nil {
|
||||
api.Send(w, r, err)
|
||||
return
|
||||
}
|
||||
|
||||
value, err := h.List(r.Context(), params)
|
||||
if err != nil {
|
||||
api.Send(w, r, err)
|
||||
return
|
||||
}
|
||||
|
||||
api.Send(w, r, value)
|
||||
},
|
||||
Effective: func(w http.ResponseWriter, r *http.Request) {
|
||||
defer r.Body.Close()
|
||||
params := request.NewPermissionsEffective()
|
||||
if err := params.Fill(r); err != nil {
|
||||
api.Send(w, r, err)
|
||||
return
|
||||
}
|
||||
|
||||
value, err := h.Effective(r.Context(), params)
|
||||
if err != nil {
|
||||
api.Send(w, r, err)
|
||||
return
|
||||
}
|
||||
|
||||
api.Send(w, r, value)
|
||||
},
|
||||
Trace: func(w http.ResponseWriter, r *http.Request) {
|
||||
defer r.Body.Close()
|
||||
params := request.NewPermissionsTrace()
|
||||
if err := params.Fill(r); err != nil {
|
||||
api.Send(w, r, err)
|
||||
return
|
||||
}
|
||||
|
||||
value, err := h.Trace(r.Context(), params)
|
||||
if err != nil {
|
||||
api.Send(w, r, err)
|
||||
return
|
||||
}
|
||||
|
||||
api.Send(w, r, value)
|
||||
},
|
||||
Read: func(w http.ResponseWriter, r *http.Request) {
|
||||
defer r.Body.Close()
|
||||
params := request.NewPermissionsRead()
|
||||
if err := params.Fill(r); err != nil {
|
||||
api.Send(w, r, err)
|
||||
return
|
||||
}
|
||||
|
||||
value, err := h.Read(r.Context(), params)
|
||||
if err != nil {
|
||||
api.Send(w, r, err)
|
||||
return
|
||||
}
|
||||
|
||||
api.Send(w, r, value)
|
||||
},
|
||||
Delete: func(w http.ResponseWriter, r *http.Request) {
|
||||
defer r.Body.Close()
|
||||
params := request.NewPermissionsDelete()
|
||||
if err := params.Fill(r); err != nil {
|
||||
api.Send(w, r, err)
|
||||
return
|
||||
}
|
||||
|
||||
value, err := h.Delete(r.Context(), params)
|
||||
if err != nil {
|
||||
api.Send(w, r, err)
|
||||
return
|
||||
}
|
||||
|
||||
api.Send(w, r, value)
|
||||
},
|
||||
Update: func(w http.ResponseWriter, r *http.Request) {
|
||||
defer r.Body.Close()
|
||||
params := request.NewPermissionsUpdate()
|
||||
if err := params.Fill(r); err != nil {
|
||||
api.Send(w, r, err)
|
||||
return
|
||||
}
|
||||
|
||||
value, err := h.Update(r.Context(), params)
|
||||
if err != nil {
|
||||
api.Send(w, r, err)
|
||||
return
|
||||
}
|
||||
|
||||
api.Send(w, r, value)
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func (h Permissions) MountRoutes(r chi.Router, middlewares ...func(http.Handler) http.Handler) {
|
||||
r.Group(func(r chi.Router) {
|
||||
r.Use(middlewares...)
|
||||
r.Get("/permissions/", h.List)
|
||||
r.Get("/permissions/effective", h.Effective)
|
||||
r.Get("/permissions/trace", h.Trace)
|
||||
r.Get("/permissions/{roleID}/rules", h.Read)
|
||||
r.Delete("/permissions/{roleID}/rules", h.Delete)
|
||||
r.Patch("/permissions/{roleID}/rules", h.Update)
|
||||
})
|
||||
}
|
||||
95
server/federation/rest/handlers/syncData.go
Normal file
95
server/federation/rest/handlers/syncData.go
Normal file
@@ -0,0 +1,95 @@
|
||||
package handlers
|
||||
|
||||
// This file is auto-generated.
|
||||
//
|
||||
// Changes to this file may cause incorrect behavior and will be lost if
|
||||
// the code is regenerated.
|
||||
//
|
||||
// Definitions file that controls how this file is generated:
|
||||
//
|
||||
|
||||
import (
|
||||
"context"
|
||||
"github.com/cortezaproject/corteza/server/federation/rest/request"
|
||||
"github.com/cortezaproject/corteza/server/pkg/api"
|
||||
"github.com/go-chi/chi/v5"
|
||||
"net/http"
|
||||
)
|
||||
|
||||
type (
|
||||
// Internal API interface
|
||||
SyncDataAPI interface {
|
||||
ReadExposedAll(context.Context, *request.SyncDataReadExposedAll) (interface{}, error)
|
||||
ReadExposedInternal(context.Context, *request.SyncDataReadExposedInternal) (interface{}, error)
|
||||
ReadExposedSocial(context.Context, *request.SyncDataReadExposedSocial) (interface{}, error)
|
||||
}
|
||||
|
||||
// HTTP API interface
|
||||
SyncData struct {
|
||||
ReadExposedAll func(http.ResponseWriter, *http.Request)
|
||||
ReadExposedInternal func(http.ResponseWriter, *http.Request)
|
||||
ReadExposedSocial func(http.ResponseWriter, *http.Request)
|
||||
}
|
||||
)
|
||||
|
||||
func NewSyncData(h SyncDataAPI) *SyncData {
|
||||
return &SyncData{
|
||||
ReadExposedAll: func(w http.ResponseWriter, r *http.Request) {
|
||||
defer r.Body.Close()
|
||||
params := request.NewSyncDataReadExposedAll()
|
||||
if err := params.Fill(r); err != nil {
|
||||
api.Send(w, r, err)
|
||||
return
|
||||
}
|
||||
|
||||
value, err := h.ReadExposedAll(r.Context(), params)
|
||||
if err != nil {
|
||||
api.Send(w, r, err)
|
||||
return
|
||||
}
|
||||
|
||||
api.Send(w, r, value)
|
||||
},
|
||||
ReadExposedInternal: func(w http.ResponseWriter, r *http.Request) {
|
||||
defer r.Body.Close()
|
||||
params := request.NewSyncDataReadExposedInternal()
|
||||
if err := params.Fill(r); err != nil {
|
||||
api.Send(w, r, err)
|
||||
return
|
||||
}
|
||||
|
||||
value, err := h.ReadExposedInternal(r.Context(), params)
|
||||
if err != nil {
|
||||
api.Send(w, r, err)
|
||||
return
|
||||
}
|
||||
|
||||
api.Send(w, r, value)
|
||||
},
|
||||
ReadExposedSocial: func(w http.ResponseWriter, r *http.Request) {
|
||||
defer r.Body.Close()
|
||||
params := request.NewSyncDataReadExposedSocial()
|
||||
if err := params.Fill(r); err != nil {
|
||||
api.Send(w, r, err)
|
||||
return
|
||||
}
|
||||
|
||||
value, err := h.ReadExposedSocial(r.Context(), params)
|
||||
if err != nil {
|
||||
api.Send(w, r, err)
|
||||
return
|
||||
}
|
||||
|
||||
api.Send(w, r, value)
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func (h SyncData) MountRoutes(r chi.Router, middlewares ...func(http.Handler) http.Handler) {
|
||||
r.Group(func(r chi.Router) {
|
||||
r.Use(middlewares...)
|
||||
r.Get("/nodes/{nodeID}/modules/exposed/records/", h.ReadExposedAll)
|
||||
r.Get("/nodes/{nodeID}/modules/{moduleID}/records/", h.ReadExposedInternal)
|
||||
r.Get("/nodes/{nodeID}/modules/{moduleID}/records/activity-stream/", h.ReadExposedSocial)
|
||||
})
|
||||
}
|
||||
76
server/federation/rest/handlers/syncStructure.go
Normal file
76
server/federation/rest/handlers/syncStructure.go
Normal file
@@ -0,0 +1,76 @@
|
||||
package handlers
|
||||
|
||||
// This file is auto-generated.
|
||||
//
|
||||
// Changes to this file may cause incorrect behavior and will be lost if
|
||||
// the code is regenerated.
|
||||
//
|
||||
// Definitions file that controls how this file is generated:
|
||||
//
|
||||
|
||||
import (
|
||||
"context"
|
||||
"github.com/cortezaproject/corteza/server/federation/rest/request"
|
||||
"github.com/cortezaproject/corteza/server/pkg/api"
|
||||
"github.com/go-chi/chi/v5"
|
||||
"net/http"
|
||||
)
|
||||
|
||||
type (
|
||||
// Internal API interface
|
||||
SyncStructureAPI interface {
|
||||
ReadExposedInternal(context.Context, *request.SyncStructureReadExposedInternal) (interface{}, error)
|
||||
ReadExposedSocial(context.Context, *request.SyncStructureReadExposedSocial) (interface{}, error)
|
||||
}
|
||||
|
||||
// HTTP API interface
|
||||
SyncStructure struct {
|
||||
ReadExposedInternal func(http.ResponseWriter, *http.Request)
|
||||
ReadExposedSocial func(http.ResponseWriter, *http.Request)
|
||||
}
|
||||
)
|
||||
|
||||
func NewSyncStructure(h SyncStructureAPI) *SyncStructure {
|
||||
return &SyncStructure{
|
||||
ReadExposedInternal: func(w http.ResponseWriter, r *http.Request) {
|
||||
defer r.Body.Close()
|
||||
params := request.NewSyncStructureReadExposedInternal()
|
||||
if err := params.Fill(r); err != nil {
|
||||
api.Send(w, r, err)
|
||||
return
|
||||
}
|
||||
|
||||
value, err := h.ReadExposedInternal(r.Context(), params)
|
||||
if err != nil {
|
||||
api.Send(w, r, err)
|
||||
return
|
||||
}
|
||||
|
||||
api.Send(w, r, value)
|
||||
},
|
||||
ReadExposedSocial: func(w http.ResponseWriter, r *http.Request) {
|
||||
defer r.Body.Close()
|
||||
params := request.NewSyncStructureReadExposedSocial()
|
||||
if err := params.Fill(r); err != nil {
|
||||
api.Send(w, r, err)
|
||||
return
|
||||
}
|
||||
|
||||
value, err := h.ReadExposedSocial(r.Context(), params)
|
||||
if err != nil {
|
||||
api.Send(w, r, err)
|
||||
return
|
||||
}
|
||||
|
||||
api.Send(w, r, value)
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func (h SyncStructure) MountRoutes(r chi.Router, middlewares ...func(http.Handler) http.Handler) {
|
||||
r.Group(func(r chi.Router) {
|
||||
r.Use(middlewares...)
|
||||
r.Get("/nodes/{nodeID}/modules/exposed/", h.ReadExposedInternal)
|
||||
r.Get("/nodes/{nodeID}/modules/exposed/activity-stream", h.ReadExposedSocial)
|
||||
})
|
||||
}
|
||||
240
server/federation/rest/manage_structure.go
Normal file
240
server/federation/rest/manage_structure.go
Normal file
@@ -0,0 +1,240 @@
|
||||
package rest
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/cortezaproject/corteza/server/federation/rest/request"
|
||||
"github.com/cortezaproject/corteza/server/federation/service"
|
||||
"github.com/cortezaproject/corteza/server/federation/types"
|
||||
)
|
||||
|
||||
type (
|
||||
ManageStructure struct{}
|
||||
|
||||
exposedModulePayload struct {
|
||||
*types.ExposedModule
|
||||
|
||||
CanManageModule bool `json:"canManageModule"`
|
||||
}
|
||||
|
||||
sharedModulePayload struct {
|
||||
*types.SharedModule
|
||||
|
||||
CanMapModule bool `json:"canMapModule"`
|
||||
}
|
||||
|
||||
moduleMappingPayload struct {
|
||||
*types.ModuleMapping
|
||||
}
|
||||
)
|
||||
|
||||
func (ManageStructure) New() *ManageStructure {
|
||||
return &ManageStructure{}
|
||||
}
|
||||
|
||||
func (ctrl ManageStructure) CreateExposed(ctx context.Context, r *request.ManageStructureCreateExposed) (interface{}, error) {
|
||||
var (
|
||||
mod = &types.ExposedModule{
|
||||
NodeID: r.NodeID,
|
||||
ComposeModuleID: r.ComposeModuleID,
|
||||
ComposeNamespaceID: r.ComposeNamespaceID,
|
||||
Name: r.Name,
|
||||
Handle: r.Handle,
|
||||
Fields: r.Fields,
|
||||
}
|
||||
)
|
||||
|
||||
em, err := (service.DefaultExposedModule).Create(ctx, mod)
|
||||
return ctrl.makePayload(ctx, em, err)
|
||||
}
|
||||
|
||||
func (ctrl ManageStructure) ReadExposed(ctx context.Context, r *request.ManageStructureReadExposed) (interface{}, error) {
|
||||
em, err := (service.DefaultExposedModule).FindByID(ctx, r.GetNodeID(), r.GetModuleID())
|
||||
return ctrl.makePayload(ctx, em, err)
|
||||
}
|
||||
|
||||
func (ctrl ManageStructure) UpdateExposed(ctx context.Context, r *request.ManageStructureUpdateExposed) (interface{}, error) {
|
||||
var (
|
||||
em = &types.ExposedModule{
|
||||
ID: r.ModuleID,
|
||||
NodeID: r.NodeID,
|
||||
ComposeModuleID: r.ComposeModuleID,
|
||||
ComposeNamespaceID: r.ComposeNamespaceID,
|
||||
Name: r.Name,
|
||||
Handle: r.Handle,
|
||||
Fields: r.Fields,
|
||||
}
|
||||
)
|
||||
|
||||
em, err := (service.DefaultExposedModule).Update(ctx, em)
|
||||
return ctrl.makePayload(ctx, em, err)
|
||||
}
|
||||
|
||||
func (ctrl ManageStructure) RemoveExposed(ctx context.Context, r *request.ManageStructureRemoveExposed) (interface{}, error) {
|
||||
em, err := (service.DefaultExposedModule).DeleteByID(ctx, r.NodeID, r.ModuleID)
|
||||
return ctrl.makePayload(ctx, em, err)
|
||||
}
|
||||
|
||||
func (ctrl ManageStructure) ReadShared(ctx context.Context, r *request.ManageStructureReadShared) (interface{}, error) {
|
||||
list, err := (service.DefaultSharedModule).FindByID(ctx, r.GetNodeID(), r.GetModuleID())
|
||||
return ctrl.makePayload(ctx, list, err)
|
||||
}
|
||||
|
||||
func (ctrl ManageStructure) CreateMappings(ctx context.Context, r *request.ManageStructureCreateMappings) (interface{}, error) {
|
||||
mm := &types.ModuleMapping{
|
||||
NodeID: r.NodeID,
|
||||
FederationModuleID: r.ModuleID,
|
||||
ComposeModuleID: r.ComposeModuleID,
|
||||
ComposeNamespaceID: r.ComposeNamespaceID,
|
||||
FieldMapping: r.Fields,
|
||||
}
|
||||
|
||||
// check if it exists, do an upsert
|
||||
existing, _, err := service.DefaultModuleMapping.Find(ctx, types.ModuleMappingFilter{
|
||||
NodeID: r.NodeID,
|
||||
ComposeModuleID: r.ComposeModuleID,
|
||||
ComposeNamespaceID: r.ComposeNamespaceID,
|
||||
FederationModuleID: r.ModuleID,
|
||||
})
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if len(existing) > 0 {
|
||||
// do an update
|
||||
return service.DefaultModuleMapping.Update(ctx, mm)
|
||||
}
|
||||
|
||||
return service.DefaultModuleMapping.Create(ctx, mm)
|
||||
}
|
||||
|
||||
// ReadMappings outputs an object with the module mappings per each federation module
|
||||
func (ctrl ManageStructure) ReadMappings(ctx context.Context, r *request.ManageStructureReadMappings) (interface{}, error) {
|
||||
f := types.ModuleMappingFilter{
|
||||
NodeID: r.NodeID,
|
||||
FederationModuleID: r.ModuleID,
|
||||
}
|
||||
|
||||
if r.ComposeModuleID > 0 {
|
||||
f.ComposeModuleID = r.ComposeModuleID
|
||||
}
|
||||
|
||||
set, _, err := service.DefaultModuleMapping.Find(ctx, f)
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if len(set) != 1 {
|
||||
return nil, service.ModuleMappingErrNotFound()
|
||||
}
|
||||
|
||||
return set[0], nil
|
||||
}
|
||||
|
||||
// ListAll show the list of exposed / shared / mapped modules
|
||||
func (ctrl ManageStructure) ListAll(ctx context.Context, r *request.ManageStructureListAll) (interface{}, error) {
|
||||
var (
|
||||
list interface{}
|
||||
err error
|
||||
)
|
||||
|
||||
switch true {
|
||||
case r.Exposed:
|
||||
list, _, err = (service.DefaultExposedModule).Find(ctx, types.ExposedModuleFilter{
|
||||
NodeID: r.NodeID,
|
||||
})
|
||||
break
|
||||
case r.Shared:
|
||||
list, _, err = (service.DefaultSharedModule).Find(ctx, types.SharedModuleFilter{
|
||||
NodeID: r.NodeID,
|
||||
})
|
||||
break
|
||||
case r.Mapped:
|
||||
list, _, err = (service.DefaultModuleMapping).Find(ctx, types.ModuleMappingFilter{
|
||||
NodeID: r.NodeID,
|
||||
})
|
||||
break
|
||||
default:
|
||||
return nil, service.ExposedModuleErrRequestParametersInvalid()
|
||||
}
|
||||
|
||||
return ctrl.makePayload(ctx, list, err)
|
||||
}
|
||||
|
||||
func (ctrl ManageStructure) makeSharedModulePayload(ctx context.Context, sm *types.SharedModule, err error) (*sharedModulePayload, error) {
|
||||
if err != nil || sm == nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &sharedModulePayload{
|
||||
SharedModule: sm,
|
||||
|
||||
CanMapModule: service.DefaultAccessControl.CanMapSharedModule(ctx, sm),
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (ctrl ManageStructure) makeExposedModulePayload(ctx context.Context, em *types.ExposedModule, err error) (*exposedModulePayload, error) {
|
||||
if err != nil || em == nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &exposedModulePayload{
|
||||
ExposedModule: em,
|
||||
|
||||
CanManageModule: service.DefaultAccessControl.CanManageExposedModule(ctx, em),
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (ctrl ManageStructure) makeModuleMappingPayload(ctx context.Context, em *types.ModuleMapping, err error) (*moduleMappingPayload, error) {
|
||||
if err != nil || em == nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &moduleMappingPayload{
|
||||
ModuleMapping: em,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (ctrl ManageStructure) makePayload(ctx context.Context, payload interface{}, err error) (interface{}, error) {
|
||||
if err != nil || payload == nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
switch payload.(type) {
|
||||
case types.SharedModuleSet:
|
||||
set := make([]sharedModulePayload, len(payload.(types.SharedModuleSet)))
|
||||
|
||||
for i, d := range payload.(types.SharedModuleSet) {
|
||||
mp, _ := ctrl.makeSharedModulePayload(ctx, d, err)
|
||||
set[i] = *mp
|
||||
}
|
||||
|
||||
return set, err
|
||||
case types.ExposedModuleSet:
|
||||
set := make([]exposedModulePayload, len(payload.(types.ExposedModuleSet)))
|
||||
|
||||
for i, d := range payload.(types.ExposedModuleSet) {
|
||||
mp, _ := ctrl.makeExposedModulePayload(ctx, d, err)
|
||||
set[i] = *mp
|
||||
}
|
||||
|
||||
return set, err
|
||||
case types.ModuleMappingSet:
|
||||
set := make([]moduleMappingPayload, len(payload.(types.ModuleMappingSet)))
|
||||
|
||||
for i, d := range payload.(types.ModuleMappingSet) {
|
||||
mp, _ := ctrl.makeModuleMappingPayload(ctx, d, err)
|
||||
set[i] = *mp
|
||||
}
|
||||
|
||||
return set, err
|
||||
case *types.ExposedModule:
|
||||
return ctrl.makeExposedModulePayload(ctx, payload.(*types.ExposedModule), err)
|
||||
case *types.SharedModule:
|
||||
return ctrl.makeSharedModulePayload(ctx, payload.(*types.SharedModule), err)
|
||||
default:
|
||||
return nil, nil
|
||||
}
|
||||
}
|
||||
156
server/federation/rest/node.go
Normal file
156
server/federation/rest/node.go
Normal file
@@ -0,0 +1,156 @@
|
||||
package rest
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/cortezaproject/corteza/server/federation/rest/request"
|
||||
"github.com/cortezaproject/corteza/server/federation/service"
|
||||
"github.com/cortezaproject/corteza/server/federation/types"
|
||||
"github.com/cortezaproject/corteza/server/pkg/api"
|
||||
"github.com/cortezaproject/corteza/server/pkg/filter"
|
||||
)
|
||||
|
||||
type (
|
||||
nodeServicer interface {
|
||||
Search(ctx context.Context, f types.NodeFilter) (types.NodeSet, types.NodeFilter, error)
|
||||
Create(ctx context.Context, n *types.Node) (*types.Node, error)
|
||||
CreateFromPairingURI(ctx context.Context, uri string) (*types.Node, error)
|
||||
Read(ctx context.Context, ID uint64) (*types.Node, error)
|
||||
Update(ctx context.Context, n *types.Node) (*types.Node, error)
|
||||
DeleteByID(ctx context.Context, ID uint64) error
|
||||
UndeleteByID(ctx context.Context, ID uint64) error
|
||||
|
||||
Pair(ctx context.Context, nodeID uint64) error
|
||||
HandshakeConfirm(ctx context.Context, nodeID uint64) error
|
||||
HandshakeComplete(ctx context.Context, nodeID uint64, token string) error
|
||||
|
||||
RegenerateNodeURI(ctx context.Context, nodeID uint64) (string, error)
|
||||
}
|
||||
|
||||
Node struct {
|
||||
svcNode nodeServicer
|
||||
}
|
||||
|
||||
moduleSetPayload struct {
|
||||
Filter types.NodeFilter `json:"filter"`
|
||||
Set []*nodePayload `json:"set"`
|
||||
}
|
||||
|
||||
nodePayload struct {
|
||||
*types.Node
|
||||
|
||||
CanManageNode bool `json:"canManageNode"`
|
||||
CanCreateModule bool `json:"canCreateModule"`
|
||||
}
|
||||
)
|
||||
|
||||
func (Node) New() *Node {
|
||||
return &Node{
|
||||
svcNode: service.DefaultNode,
|
||||
}
|
||||
}
|
||||
|
||||
func (ctrl Node) Search(ctx context.Context, r *request.NodeSearch) (interface{}, error) {
|
||||
var (
|
||||
err error
|
||||
f = types.NodeFilter{
|
||||
Query: r.Query,
|
||||
Status: r.Status,
|
||||
}
|
||||
)
|
||||
|
||||
if f.Paging, err = filter.NewPaging(r.Limit, r.PageCursor); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
f.IncTotal = r.IncTotal
|
||||
|
||||
if f.Sorting, err = filter.NewSorting(r.Sort); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
set, f, err := ctrl.svcNode.Search(ctx, f)
|
||||
|
||||
return ctrl.makeFilterPayload(ctx, set, f, err)
|
||||
}
|
||||
|
||||
func (ctrl Node) Create(ctx context.Context, r *request.NodeCreate) (interface{}, error) {
|
||||
if r.PairingURI != "" {
|
||||
return ctrl.svcNode.CreateFromPairingURI(ctx, r.PairingURI)
|
||||
} else {
|
||||
n := &types.Node{
|
||||
BaseURL: r.BaseURL,
|
||||
Name: r.Name,
|
||||
Contact: r.Contact,
|
||||
}
|
||||
|
||||
n, err := ctrl.svcNode.Create(ctx, n)
|
||||
return ctrl.makePayload(ctx, n, err)
|
||||
}
|
||||
}
|
||||
|
||||
func (ctrl Node) Read(ctx context.Context, r *request.NodeRead) (interface{}, error) {
|
||||
n, err := ctrl.svcNode.Read(ctx, r.NodeID)
|
||||
|
||||
return ctrl.makePayload(ctx, n, err)
|
||||
}
|
||||
func (ctrl Node) Update(ctx context.Context, r *request.NodeUpdate) (interface{}, error) {
|
||||
n, err := ctrl.svcNode.Update(ctx, &types.Node{
|
||||
ID: r.NodeID,
|
||||
Name: r.Name,
|
||||
BaseURL: r.BaseURL,
|
||||
Contact: r.Contact,
|
||||
})
|
||||
|
||||
return ctrl.makePayload(ctx, n, err)
|
||||
}
|
||||
func (ctrl Node) Delete(ctx context.Context, r *request.NodeDelete) (interface{}, error) {
|
||||
return api.OK(), ctrl.svcNode.DeleteByID(ctx, r.NodeID)
|
||||
}
|
||||
|
||||
func (ctrl Node) Undelete(ctx context.Context, r *request.NodeUndelete) (interface{}, error) {
|
||||
return api.OK(), ctrl.svcNode.UndeleteByID(ctx, r.NodeID)
|
||||
}
|
||||
|
||||
func (ctrl Node) GenerateURI(ctx context.Context, r *request.NodeGenerateURI) (interface{}, error) {
|
||||
return ctrl.svcNode.RegenerateNodeURI(ctx, r.NodeID)
|
||||
}
|
||||
|
||||
func (ctrl Node) Pair(ctx context.Context, r *request.NodePair) (interface{}, error) {
|
||||
return api.OK(), ctrl.svcNode.Pair(ctx, r.NodeID)
|
||||
}
|
||||
|
||||
func (ctrl Node) HandshakeConfirm(ctx context.Context, r *request.NodeHandshakeConfirm) (interface{}, error) {
|
||||
return api.OK(), ctrl.svcNode.HandshakeConfirm(ctx, r.NodeID)
|
||||
}
|
||||
|
||||
func (ctrl Node) HandshakeComplete(ctx context.Context, r *request.NodeHandshakeComplete) (interface{}, error) {
|
||||
return api.OK(), ctrl.svcNode.HandshakeComplete(ctx, r.NodeID, r.AuthToken)
|
||||
}
|
||||
|
||||
func (ctrl Node) makePayload(ctx context.Context, m *types.Node, err error) (*nodePayload, error) {
|
||||
if err != nil || m == nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &nodePayload{
|
||||
Node: m,
|
||||
|
||||
CanCreateModule: service.DefaultAccessControl.CanCreateModuleOnNode(ctx, m),
|
||||
CanManageNode: service.DefaultAccessControl.CanManageNode(ctx, m),
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (ctrl Node) makeFilterPayload(ctx context.Context, nn types.NodeSet, f types.NodeFilter, err error) (*moduleSetPayload, error) {
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
msp := &moduleSetPayload{Filter: f, Set: make([]*nodePayload, len(nn))}
|
||||
|
||||
for i := range nn {
|
||||
msp.Set[i], _ = ctrl.makePayload(ctx, nn[i], nil)
|
||||
}
|
||||
|
||||
return msp, nil
|
||||
}
|
||||
28
server/federation/rest/node_handshake.go
Normal file
28
server/federation/rest/node_handshake.go
Normal file
@@ -0,0 +1,28 @@
|
||||
package rest
|
||||
|
||||
import (
|
||||
"context"
|
||||
"github.com/cortezaproject/corteza/server/federation/rest/request"
|
||||
"github.com/cortezaproject/corteza/server/federation/service"
|
||||
"github.com/cortezaproject/corteza/server/pkg/api"
|
||||
)
|
||||
|
||||
type (
|
||||
handshakeInitializer interface {
|
||||
HandshakeInit(context.Context, uint64, string, uint64, string) error
|
||||
}
|
||||
|
||||
NodeHandshake struct {
|
||||
svcNode handshakeInitializer
|
||||
}
|
||||
)
|
||||
|
||||
func (NodeHandshake) New() *NodeHandshake {
|
||||
return &NodeHandshake{
|
||||
svcNode: service.DefaultNode,
|
||||
}
|
||||
}
|
||||
|
||||
func (ctrl NodeHandshake) Initialize(ctx context.Context, r *request.NodeHandshakeInitialize) (interface{}, error) {
|
||||
return api.OK(), ctrl.svcNode.HandshakeInit(ctx, r.NodeID, r.PairToken, r.SharedNodeID, r.AuthToken)
|
||||
}
|
||||
69
server/federation/rest/permissions.go
Normal file
69
server/federation/rest/permissions.go
Normal file
@@ -0,0 +1,69 @@
|
||||
package rest
|
||||
|
||||
import (
|
||||
"context"
|
||||
"github.com/cortezaproject/corteza/server/federation/rest/request"
|
||||
"github.com/cortezaproject/corteza/server/federation/service"
|
||||
"github.com/cortezaproject/corteza/server/federation/types"
|
||||
"github.com/cortezaproject/corteza/server/pkg/api"
|
||||
"github.com/cortezaproject/corteza/server/pkg/rbac"
|
||||
)
|
||||
|
||||
type (
|
||||
Permissions struct {
|
||||
ac permissionsAccessController
|
||||
}
|
||||
|
||||
permissionsAccessController interface {
|
||||
Effective(context.Context, ...rbac.Resource) rbac.EffectiveSet
|
||||
Trace(context.Context, uint64, []uint64, ...string) ([]*rbac.Trace, error)
|
||||
List() []map[string]string
|
||||
FindRulesByRoleID(context.Context, uint64) (rbac.RuleSet, error)
|
||||
FindRules(ctx context.Context, roleID uint64, rr ...string) (rbac.RuleSet, error)
|
||||
Grant(ctx context.Context, rr ...*rbac.Rule) error
|
||||
}
|
||||
)
|
||||
|
||||
func (Permissions) New() *Permissions {
|
||||
return &Permissions{
|
||||
ac: service.DefaultAccessControl,
|
||||
}
|
||||
}
|
||||
|
||||
func (ctrl Permissions) Effective(ctx context.Context, r *request.PermissionsEffective) (interface{}, error) {
|
||||
return ctrl.ac.Effective(ctx, types.Component{}), nil
|
||||
}
|
||||
|
||||
func (ctrl Permissions) Trace(ctx context.Context, r *request.PermissionsTrace) (interface{}, error) {
|
||||
return ctrl.ac.Trace(ctx, r.UserID, r.RoleID, r.Resource...)
|
||||
}
|
||||
|
||||
func (ctrl Permissions) List(ctx context.Context, r *request.PermissionsList) (interface{}, error) {
|
||||
return ctrl.ac.List(), nil
|
||||
}
|
||||
|
||||
func (ctrl Permissions) Read(ctx context.Context, r *request.PermissionsRead) (interface{}, error) {
|
||||
return ctrl.ac.FindRules(ctx, r.RoleID, r.Resource...)
|
||||
}
|
||||
|
||||
func (ctrl Permissions) Delete(ctx context.Context, r *request.PermissionsDelete) (interface{}, error) {
|
||||
rr, err := ctrl.ac.FindRulesByRoleID(ctx, r.RoleID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
for _, r := range rr {
|
||||
r.Access = rbac.Inherit
|
||||
}
|
||||
|
||||
return api.OK(), ctrl.ac.Grant(ctx, rr...)
|
||||
}
|
||||
|
||||
func (ctrl Permissions) Update(ctx context.Context, r *request.PermissionsUpdate) (interface{}, error) {
|
||||
for _, rule := range r.Rules {
|
||||
// Make sure everything is properly set
|
||||
rule.RoleID = r.RoleID
|
||||
}
|
||||
|
||||
return api.OK(), ctrl.ac.Grant(ctx, r.Rules...)
|
||||
}
|
||||
945
server/federation/rest/request/manageStructure.go
Normal file
945
server/federation/rest/request/manageStructure.go
Normal file
@@ -0,0 +1,945 @@
|
||||
package request
|
||||
|
||||
// This file is auto-generated.
|
||||
//
|
||||
// Changes to this file may cause incorrect behavior and will be lost if
|
||||
// the code is regenerated.
|
||||
//
|
||||
// Definitions file that controls how this file is generated:
|
||||
//
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"github.com/cortezaproject/corteza/server/federation/types"
|
||||
"github.com/cortezaproject/corteza/server/pkg/payload"
|
||||
"github.com/go-chi/chi/v5"
|
||||
"io"
|
||||
"mime/multipart"
|
||||
"net/http"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// dummy vars to prevent
|
||||
// unused imports complain
|
||||
var (
|
||||
_ = chi.URLParam
|
||||
_ = multipart.ErrMessageTooLarge
|
||||
_ = payload.ParseUint64s
|
||||
_ = strings.ToLower
|
||||
_ = io.EOF
|
||||
_ = fmt.Errorf
|
||||
_ = json.NewEncoder
|
||||
)
|
||||
|
||||
type (
|
||||
// Internal API interface
|
||||
ManageStructureReadExposed struct {
|
||||
// NodeID PATH parameter
|
||||
//
|
||||
// Node ID
|
||||
NodeID uint64 `json:",string"`
|
||||
|
||||
// ModuleID PATH parameter
|
||||
//
|
||||
// Module ID
|
||||
ModuleID uint64 `json:",string"`
|
||||
}
|
||||
|
||||
ManageStructureCreateExposed struct {
|
||||
// NodeID PATH parameter
|
||||
//
|
||||
// Node ID
|
||||
NodeID uint64 `json:",string"`
|
||||
|
||||
// ComposeModuleID POST parameter
|
||||
//
|
||||
// Compose module id
|
||||
ComposeModuleID uint64 `json:",string"`
|
||||
|
||||
// ComposeNamespaceID POST parameter
|
||||
//
|
||||
// Compose namespace id
|
||||
ComposeNamespaceID uint64 `json:",string"`
|
||||
|
||||
// Name POST parameter
|
||||
//
|
||||
// Module name
|
||||
Name string
|
||||
|
||||
// Handle POST parameter
|
||||
//
|
||||
// Module handle
|
||||
Handle string
|
||||
|
||||
// Fields POST parameter
|
||||
//
|
||||
// Exposed module fields
|
||||
Fields types.ModuleFieldSet
|
||||
}
|
||||
|
||||
ManageStructureUpdateExposed struct {
|
||||
// NodeID PATH parameter
|
||||
//
|
||||
// Node ID
|
||||
NodeID uint64 `json:",string"`
|
||||
|
||||
// ModuleID PATH parameter
|
||||
//
|
||||
// Module ID
|
||||
ModuleID uint64 `json:",string"`
|
||||
|
||||
// ComposeModuleID POST parameter
|
||||
//
|
||||
// Compose module id
|
||||
ComposeModuleID uint64 `json:",string"`
|
||||
|
||||
// ComposeNamespaceID POST parameter
|
||||
//
|
||||
// Compose namespace id
|
||||
ComposeNamespaceID uint64 `json:",string"`
|
||||
|
||||
// Name POST parameter
|
||||
//
|
||||
// Module name
|
||||
Name string
|
||||
|
||||
// Handle POST parameter
|
||||
//
|
||||
// Module handle
|
||||
Handle string
|
||||
|
||||
// Fields POST parameter
|
||||
//
|
||||
// Exposed module fields
|
||||
Fields types.ModuleFieldSet
|
||||
}
|
||||
|
||||
ManageStructureRemoveExposed struct {
|
||||
// NodeID PATH parameter
|
||||
//
|
||||
// Node ID
|
||||
NodeID uint64 `json:",string"`
|
||||
|
||||
// ModuleID PATH parameter
|
||||
//
|
||||
// Module ID
|
||||
ModuleID uint64 `json:",string"`
|
||||
}
|
||||
|
||||
ManageStructureReadShared struct {
|
||||
// NodeID PATH parameter
|
||||
//
|
||||
// Node ID
|
||||
NodeID uint64 `json:",string"`
|
||||
|
||||
// ModuleID PATH parameter
|
||||
//
|
||||
// Module ID
|
||||
ModuleID uint64 `json:",string"`
|
||||
}
|
||||
|
||||
ManageStructureCreateMappings struct {
|
||||
// NodeID PATH parameter
|
||||
//
|
||||
// Node ID
|
||||
NodeID uint64 `json:",string"`
|
||||
|
||||
// ModuleID PATH parameter
|
||||
//
|
||||
// Module ID
|
||||
ModuleID uint64 `json:",string"`
|
||||
|
||||
// ComposeModuleID POST parameter
|
||||
//
|
||||
// Compose module id
|
||||
ComposeModuleID uint64 `json:",string"`
|
||||
|
||||
// ComposeNamespaceID POST parameter
|
||||
//
|
||||
// Compose namespace id
|
||||
ComposeNamespaceID uint64 `json:",string"`
|
||||
|
||||
// Fields POST parameter
|
||||
//
|
||||
// Exposed module fields
|
||||
Fields types.ModuleFieldMappingSet
|
||||
}
|
||||
|
||||
ManageStructureReadMappings struct {
|
||||
// NodeID PATH parameter
|
||||
//
|
||||
// Node ID
|
||||
NodeID uint64 `json:",string"`
|
||||
|
||||
// ModuleID PATH parameter
|
||||
//
|
||||
// Module ID
|
||||
ModuleID uint64 `json:",string"`
|
||||
|
||||
// ComposeModuleID GET parameter
|
||||
//
|
||||
// Compose module id
|
||||
ComposeModuleID uint64 `json:",string"`
|
||||
}
|
||||
|
||||
ManageStructureListAll struct {
|
||||
// NodeID PATH parameter
|
||||
//
|
||||
// Node ID
|
||||
NodeID uint64 `json:",string"`
|
||||
|
||||
// Shared GET parameter
|
||||
//
|
||||
// List shared modules
|
||||
Shared bool
|
||||
|
||||
// Exposed GET parameter
|
||||
//
|
||||
// List exposed modules
|
||||
Exposed bool
|
||||
|
||||
// Mapped GET parameter
|
||||
//
|
||||
// List mapped modules
|
||||
Mapped bool
|
||||
}
|
||||
)
|
||||
|
||||
// NewManageStructureReadExposed request
|
||||
func NewManageStructureReadExposed() *ManageStructureReadExposed {
|
||||
return &ManageStructureReadExposed{}
|
||||
}
|
||||
|
||||
// Auditable returns all auditable/loggable parameters
|
||||
func (r ManageStructureReadExposed) Auditable() map[string]interface{} {
|
||||
return map[string]interface{}{
|
||||
"nodeID": r.NodeID,
|
||||
"moduleID": r.ModuleID,
|
||||
}
|
||||
}
|
||||
|
||||
// Auditable returns all auditable/loggable parameters
|
||||
func (r ManageStructureReadExposed) GetNodeID() uint64 {
|
||||
return r.NodeID
|
||||
}
|
||||
|
||||
// Auditable returns all auditable/loggable parameters
|
||||
func (r ManageStructureReadExposed) GetModuleID() uint64 {
|
||||
return r.ModuleID
|
||||
}
|
||||
|
||||
// Fill processes request and fills internal variables
|
||||
func (r *ManageStructureReadExposed) Fill(req *http.Request) (err error) {
|
||||
|
||||
{
|
||||
var val string
|
||||
// path params
|
||||
|
||||
val = chi.URLParam(req, "nodeID")
|
||||
r.NodeID, err = payload.ParseUint64(val), nil
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
val = chi.URLParam(req, "moduleID")
|
||||
r.ModuleID, err = payload.ParseUint64(val), nil
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
// NewManageStructureCreateExposed request
|
||||
func NewManageStructureCreateExposed() *ManageStructureCreateExposed {
|
||||
return &ManageStructureCreateExposed{}
|
||||
}
|
||||
|
||||
// Auditable returns all auditable/loggable parameters
|
||||
func (r ManageStructureCreateExposed) Auditable() map[string]interface{} {
|
||||
return map[string]interface{}{
|
||||
"nodeID": r.NodeID,
|
||||
"composeModuleID": r.ComposeModuleID,
|
||||
"composeNamespaceID": r.ComposeNamespaceID,
|
||||
"name": r.Name,
|
||||
"handle": r.Handle,
|
||||
"fields": r.Fields,
|
||||
}
|
||||
}
|
||||
|
||||
// Auditable returns all auditable/loggable parameters
|
||||
func (r ManageStructureCreateExposed) GetNodeID() uint64 {
|
||||
return r.NodeID
|
||||
}
|
||||
|
||||
// Auditable returns all auditable/loggable parameters
|
||||
func (r ManageStructureCreateExposed) GetComposeModuleID() uint64 {
|
||||
return r.ComposeModuleID
|
||||
}
|
||||
|
||||
// Auditable returns all auditable/loggable parameters
|
||||
func (r ManageStructureCreateExposed) GetComposeNamespaceID() uint64 {
|
||||
return r.ComposeNamespaceID
|
||||
}
|
||||
|
||||
// Auditable returns all auditable/loggable parameters
|
||||
func (r ManageStructureCreateExposed) GetName() string {
|
||||
return r.Name
|
||||
}
|
||||
|
||||
// Auditable returns all auditable/loggable parameters
|
||||
func (r ManageStructureCreateExposed) GetHandle() string {
|
||||
return r.Handle
|
||||
}
|
||||
|
||||
// Auditable returns all auditable/loggable parameters
|
||||
func (r ManageStructureCreateExposed) GetFields() types.ModuleFieldSet {
|
||||
return r.Fields
|
||||
}
|
||||
|
||||
// Fill processes request and fills internal variables
|
||||
func (r *ManageStructureCreateExposed) Fill(req *http.Request) (err error) {
|
||||
|
||||
if strings.HasPrefix(strings.ToLower(req.Header.Get("content-type")), "application/json") {
|
||||
err = json.NewDecoder(req.Body).Decode(r)
|
||||
|
||||
switch {
|
||||
case err == io.EOF:
|
||||
err = nil
|
||||
case err != nil:
|
||||
return fmt.Errorf("error parsing http request body: %w", err)
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
// Caching 32MB to memory, the rest to disk
|
||||
if err = req.ParseMultipartForm(32 << 20); err != nil && err != http.ErrNotMultipart {
|
||||
return err
|
||||
} else if err == nil {
|
||||
// Multipart params
|
||||
|
||||
if val, ok := req.MultipartForm.Value["composeModuleID"]; ok && len(val) > 0 {
|
||||
r.ComposeModuleID, err = payload.ParseUint64(val[0]), nil
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
if val, ok := req.MultipartForm.Value["composeNamespaceID"]; ok && len(val) > 0 {
|
||||
r.ComposeNamespaceID, err = payload.ParseUint64(val[0]), nil
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
if val, ok := req.MultipartForm.Value["name"]; ok && len(val) > 0 {
|
||||
r.Name, err = val[0], nil
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
if val, ok := req.MultipartForm.Value["handle"]; ok && len(val) > 0 {
|
||||
r.Handle, err = val[0], nil
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
if err = req.ParseForm(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// POST params
|
||||
|
||||
if val, ok := req.Form["composeModuleID"]; ok && len(val) > 0 {
|
||||
r.ComposeModuleID, err = payload.ParseUint64(val[0]), nil
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
if val, ok := req.Form["composeNamespaceID"]; ok && len(val) > 0 {
|
||||
r.ComposeNamespaceID, err = payload.ParseUint64(val[0]), nil
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
if val, ok := req.Form["name"]; ok && len(val) > 0 {
|
||||
r.Name, err = val[0], nil
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
if val, ok := req.Form["handle"]; ok && len(val) > 0 {
|
||||
r.Handle, err = val[0], nil
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
//if val, ok := req.Form["fields[]"]; ok && len(val) > 0 {
|
||||
// r.Fields, err = types.ModuleFieldSet(val), nil
|
||||
// if err != nil {
|
||||
// return err
|
||||
// }
|
||||
//}
|
||||
}
|
||||
|
||||
{
|
||||
var val string
|
||||
// path params
|
||||
|
||||
val = chi.URLParam(req, "nodeID")
|
||||
r.NodeID, err = payload.ParseUint64(val), nil
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
// NewManageStructureUpdateExposed request
|
||||
func NewManageStructureUpdateExposed() *ManageStructureUpdateExposed {
|
||||
return &ManageStructureUpdateExposed{}
|
||||
}
|
||||
|
||||
// Auditable returns all auditable/loggable parameters
|
||||
func (r ManageStructureUpdateExposed) Auditable() map[string]interface{} {
|
||||
return map[string]interface{}{
|
||||
"nodeID": r.NodeID,
|
||||
"moduleID": r.ModuleID,
|
||||
"composeModuleID": r.ComposeModuleID,
|
||||
"composeNamespaceID": r.ComposeNamespaceID,
|
||||
"name": r.Name,
|
||||
"handle": r.Handle,
|
||||
"fields": r.Fields,
|
||||
}
|
||||
}
|
||||
|
||||
// Auditable returns all auditable/loggable parameters
|
||||
func (r ManageStructureUpdateExposed) GetNodeID() uint64 {
|
||||
return r.NodeID
|
||||
}
|
||||
|
||||
// Auditable returns all auditable/loggable parameters
|
||||
func (r ManageStructureUpdateExposed) GetModuleID() uint64 {
|
||||
return r.ModuleID
|
||||
}
|
||||
|
||||
// Auditable returns all auditable/loggable parameters
|
||||
func (r ManageStructureUpdateExposed) GetComposeModuleID() uint64 {
|
||||
return r.ComposeModuleID
|
||||
}
|
||||
|
||||
// Auditable returns all auditable/loggable parameters
|
||||
func (r ManageStructureUpdateExposed) GetComposeNamespaceID() uint64 {
|
||||
return r.ComposeNamespaceID
|
||||
}
|
||||
|
||||
// Auditable returns all auditable/loggable parameters
|
||||
func (r ManageStructureUpdateExposed) GetName() string {
|
||||
return r.Name
|
||||
}
|
||||
|
||||
// Auditable returns all auditable/loggable parameters
|
||||
func (r ManageStructureUpdateExposed) GetHandle() string {
|
||||
return r.Handle
|
||||
}
|
||||
|
||||
// Auditable returns all auditable/loggable parameters
|
||||
func (r ManageStructureUpdateExposed) GetFields() types.ModuleFieldSet {
|
||||
return r.Fields
|
||||
}
|
||||
|
||||
// Fill processes request and fills internal variables
|
||||
func (r *ManageStructureUpdateExposed) Fill(req *http.Request) (err error) {
|
||||
|
||||
if strings.HasPrefix(strings.ToLower(req.Header.Get("content-type")), "application/json") {
|
||||
err = json.NewDecoder(req.Body).Decode(r)
|
||||
|
||||
switch {
|
||||
case err == io.EOF:
|
||||
err = nil
|
||||
case err != nil:
|
||||
return fmt.Errorf("error parsing http request body: %w", err)
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
// Caching 32MB to memory, the rest to disk
|
||||
if err = req.ParseMultipartForm(32 << 20); err != nil && err != http.ErrNotMultipart {
|
||||
return err
|
||||
} else if err == nil {
|
||||
// Multipart params
|
||||
|
||||
if val, ok := req.MultipartForm.Value["composeModuleID"]; ok && len(val) > 0 {
|
||||
r.ComposeModuleID, err = payload.ParseUint64(val[0]), nil
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
if val, ok := req.MultipartForm.Value["composeNamespaceID"]; ok && len(val) > 0 {
|
||||
r.ComposeNamespaceID, err = payload.ParseUint64(val[0]), nil
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
if val, ok := req.MultipartForm.Value["name"]; ok && len(val) > 0 {
|
||||
r.Name, err = val[0], nil
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
if val, ok := req.MultipartForm.Value["handle"]; ok && len(val) > 0 {
|
||||
r.Handle, err = val[0], nil
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
if err = req.ParseForm(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// POST params
|
||||
|
||||
if val, ok := req.Form["composeModuleID"]; ok && len(val) > 0 {
|
||||
r.ComposeModuleID, err = payload.ParseUint64(val[0]), nil
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
if val, ok := req.Form["composeNamespaceID"]; ok && len(val) > 0 {
|
||||
r.ComposeNamespaceID, err = payload.ParseUint64(val[0]), nil
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
if val, ok := req.Form["name"]; ok && len(val) > 0 {
|
||||
r.Name, err = val[0], nil
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
if val, ok := req.Form["handle"]; ok && len(val) > 0 {
|
||||
r.Handle, err = val[0], nil
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
//if val, ok := req.Form["fields[]"]; ok && len(val) > 0 {
|
||||
// r.Fields, err = types.ModuleFieldSet(val), nil
|
||||
// if err != nil {
|
||||
// return err
|
||||
// }
|
||||
//}
|
||||
}
|
||||
|
||||
{
|
||||
var val string
|
||||
// path params
|
||||
|
||||
val = chi.URLParam(req, "nodeID")
|
||||
r.NodeID, err = payload.ParseUint64(val), nil
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
val = chi.URLParam(req, "moduleID")
|
||||
r.ModuleID, err = payload.ParseUint64(val), nil
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
// NewManageStructureRemoveExposed request
|
||||
func NewManageStructureRemoveExposed() *ManageStructureRemoveExposed {
|
||||
return &ManageStructureRemoveExposed{}
|
||||
}
|
||||
|
||||
// Auditable returns all auditable/loggable parameters
|
||||
func (r ManageStructureRemoveExposed) Auditable() map[string]interface{} {
|
||||
return map[string]interface{}{
|
||||
"nodeID": r.NodeID,
|
||||
"moduleID": r.ModuleID,
|
||||
}
|
||||
}
|
||||
|
||||
// Auditable returns all auditable/loggable parameters
|
||||
func (r ManageStructureRemoveExposed) GetNodeID() uint64 {
|
||||
return r.NodeID
|
||||
}
|
||||
|
||||
// Auditable returns all auditable/loggable parameters
|
||||
func (r ManageStructureRemoveExposed) GetModuleID() uint64 {
|
||||
return r.ModuleID
|
||||
}
|
||||
|
||||
// Fill processes request and fills internal variables
|
||||
func (r *ManageStructureRemoveExposed) Fill(req *http.Request) (err error) {
|
||||
|
||||
{
|
||||
var val string
|
||||
// path params
|
||||
|
||||
val = chi.URLParam(req, "nodeID")
|
||||
r.NodeID, err = payload.ParseUint64(val), nil
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
val = chi.URLParam(req, "moduleID")
|
||||
r.ModuleID, err = payload.ParseUint64(val), nil
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
// NewManageStructureReadShared request
|
||||
func NewManageStructureReadShared() *ManageStructureReadShared {
|
||||
return &ManageStructureReadShared{}
|
||||
}
|
||||
|
||||
// Auditable returns all auditable/loggable parameters
|
||||
func (r ManageStructureReadShared) Auditable() map[string]interface{} {
|
||||
return map[string]interface{}{
|
||||
"nodeID": r.NodeID,
|
||||
"moduleID": r.ModuleID,
|
||||
}
|
||||
}
|
||||
|
||||
// Auditable returns all auditable/loggable parameters
|
||||
func (r ManageStructureReadShared) GetNodeID() uint64 {
|
||||
return r.NodeID
|
||||
}
|
||||
|
||||
// Auditable returns all auditable/loggable parameters
|
||||
func (r ManageStructureReadShared) GetModuleID() uint64 {
|
||||
return r.ModuleID
|
||||
}
|
||||
|
||||
// Fill processes request and fills internal variables
|
||||
func (r *ManageStructureReadShared) Fill(req *http.Request) (err error) {
|
||||
|
||||
{
|
||||
var val string
|
||||
// path params
|
||||
|
||||
val = chi.URLParam(req, "nodeID")
|
||||
r.NodeID, err = payload.ParseUint64(val), nil
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
val = chi.URLParam(req, "moduleID")
|
||||
r.ModuleID, err = payload.ParseUint64(val), nil
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
// NewManageStructureCreateMappings request
|
||||
func NewManageStructureCreateMappings() *ManageStructureCreateMappings {
|
||||
return &ManageStructureCreateMappings{}
|
||||
}
|
||||
|
||||
// Auditable returns all auditable/loggable parameters
|
||||
func (r ManageStructureCreateMappings) Auditable() map[string]interface{} {
|
||||
return map[string]interface{}{
|
||||
"nodeID": r.NodeID,
|
||||
"moduleID": r.ModuleID,
|
||||
"composeModuleID": r.ComposeModuleID,
|
||||
"composeNamespaceID": r.ComposeNamespaceID,
|
||||
"fields": r.Fields,
|
||||
}
|
||||
}
|
||||
|
||||
// Auditable returns all auditable/loggable parameters
|
||||
func (r ManageStructureCreateMappings) GetNodeID() uint64 {
|
||||
return r.NodeID
|
||||
}
|
||||
|
||||
// Auditable returns all auditable/loggable parameters
|
||||
func (r ManageStructureCreateMappings) GetModuleID() uint64 {
|
||||
return r.ModuleID
|
||||
}
|
||||
|
||||
// Auditable returns all auditable/loggable parameters
|
||||
func (r ManageStructureCreateMappings) GetComposeModuleID() uint64 {
|
||||
return r.ComposeModuleID
|
||||
}
|
||||
|
||||
// Auditable returns all auditable/loggable parameters
|
||||
func (r ManageStructureCreateMappings) GetComposeNamespaceID() uint64 {
|
||||
return r.ComposeNamespaceID
|
||||
}
|
||||
|
||||
// Auditable returns all auditable/loggable parameters
|
||||
func (r ManageStructureCreateMappings) GetFields() types.ModuleFieldMappingSet {
|
||||
return r.Fields
|
||||
}
|
||||
|
||||
// Fill processes request and fills internal variables
|
||||
func (r *ManageStructureCreateMappings) Fill(req *http.Request) (err error) {
|
||||
|
||||
if strings.HasPrefix(strings.ToLower(req.Header.Get("content-type")), "application/json") {
|
||||
err = json.NewDecoder(req.Body).Decode(r)
|
||||
|
||||
switch {
|
||||
case err == io.EOF:
|
||||
err = nil
|
||||
case err != nil:
|
||||
return fmt.Errorf("error parsing http request body: %w", err)
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
// Caching 32MB to memory, the rest to disk
|
||||
if err = req.ParseMultipartForm(32 << 20); err != nil && err != http.ErrNotMultipart {
|
||||
return err
|
||||
} else if err == nil {
|
||||
// Multipart params
|
||||
|
||||
if val, ok := req.MultipartForm.Value["composeModuleID"]; ok && len(val) > 0 {
|
||||
r.ComposeModuleID, err = payload.ParseUint64(val[0]), nil
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
if val, ok := req.MultipartForm.Value["composeNamespaceID"]; ok && len(val) > 0 {
|
||||
r.ComposeNamespaceID, err = payload.ParseUint64(val[0]), nil
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
if err = req.ParseForm(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// POST params
|
||||
|
||||
if val, ok := req.Form["composeModuleID"]; ok && len(val) > 0 {
|
||||
r.ComposeModuleID, err = payload.ParseUint64(val[0]), nil
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
if val, ok := req.Form["composeNamespaceID"]; ok && len(val) > 0 {
|
||||
r.ComposeNamespaceID, err = payload.ParseUint64(val[0]), nil
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
//if val, ok := req.Form["fields[]"]; ok && len(val) > 0 {
|
||||
// r.Fields, err = types.ModuleFieldMappingSet(val), nil
|
||||
// if err != nil {
|
||||
// return err
|
||||
// }
|
||||
//}
|
||||
}
|
||||
|
||||
{
|
||||
var val string
|
||||
// path params
|
||||
|
||||
val = chi.URLParam(req, "nodeID")
|
||||
r.NodeID, err = payload.ParseUint64(val), nil
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
val = chi.URLParam(req, "moduleID")
|
||||
r.ModuleID, err = payload.ParseUint64(val), nil
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
// NewManageStructureReadMappings request
|
||||
func NewManageStructureReadMappings() *ManageStructureReadMappings {
|
||||
return &ManageStructureReadMappings{}
|
||||
}
|
||||
|
||||
// Auditable returns all auditable/loggable parameters
|
||||
func (r ManageStructureReadMappings) Auditable() map[string]interface{} {
|
||||
return map[string]interface{}{
|
||||
"nodeID": r.NodeID,
|
||||
"moduleID": r.ModuleID,
|
||||
"composeModuleID": r.ComposeModuleID,
|
||||
}
|
||||
}
|
||||
|
||||
// Auditable returns all auditable/loggable parameters
|
||||
func (r ManageStructureReadMappings) GetNodeID() uint64 {
|
||||
return r.NodeID
|
||||
}
|
||||
|
||||
// Auditable returns all auditable/loggable parameters
|
||||
func (r ManageStructureReadMappings) GetModuleID() uint64 {
|
||||
return r.ModuleID
|
||||
}
|
||||
|
||||
// Auditable returns all auditable/loggable parameters
|
||||
func (r ManageStructureReadMappings) GetComposeModuleID() uint64 {
|
||||
return r.ComposeModuleID
|
||||
}
|
||||
|
||||
// Fill processes request and fills internal variables
|
||||
func (r *ManageStructureReadMappings) Fill(req *http.Request) (err error) {
|
||||
|
||||
{
|
||||
// GET params
|
||||
tmp := req.URL.Query()
|
||||
|
||||
if val, ok := tmp["composeModuleID"]; ok && len(val) > 0 {
|
||||
r.ComposeModuleID, err = payload.ParseUint64(val[0]), nil
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
var val string
|
||||
// path params
|
||||
|
||||
val = chi.URLParam(req, "nodeID")
|
||||
r.NodeID, err = payload.ParseUint64(val), nil
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
val = chi.URLParam(req, "moduleID")
|
||||
r.ModuleID, err = payload.ParseUint64(val), nil
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
// NewManageStructureListAll request
|
||||
func NewManageStructureListAll() *ManageStructureListAll {
|
||||
return &ManageStructureListAll{}
|
||||
}
|
||||
|
||||
// Auditable returns all auditable/loggable parameters
|
||||
func (r ManageStructureListAll) Auditable() map[string]interface{} {
|
||||
return map[string]interface{}{
|
||||
"nodeID": r.NodeID,
|
||||
"shared": r.Shared,
|
||||
"exposed": r.Exposed,
|
||||
"mapped": r.Mapped,
|
||||
}
|
||||
}
|
||||
|
||||
// Auditable returns all auditable/loggable parameters
|
||||
func (r ManageStructureListAll) GetNodeID() uint64 {
|
||||
return r.NodeID
|
||||
}
|
||||
|
||||
// Auditable returns all auditable/loggable parameters
|
||||
func (r ManageStructureListAll) GetShared() bool {
|
||||
return r.Shared
|
||||
}
|
||||
|
||||
// Auditable returns all auditable/loggable parameters
|
||||
func (r ManageStructureListAll) GetExposed() bool {
|
||||
return r.Exposed
|
||||
}
|
||||
|
||||
// Auditable returns all auditable/loggable parameters
|
||||
func (r ManageStructureListAll) GetMapped() bool {
|
||||
return r.Mapped
|
||||
}
|
||||
|
||||
// Fill processes request and fills internal variables
|
||||
func (r *ManageStructureListAll) Fill(req *http.Request) (err error) {
|
||||
|
||||
{
|
||||
// GET params
|
||||
tmp := req.URL.Query()
|
||||
|
||||
if val, ok := tmp["shared"]; ok && len(val) > 0 {
|
||||
r.Shared, err = payload.ParseBool(val[0]), nil
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
if val, ok := tmp["exposed"]; ok && len(val) > 0 {
|
||||
r.Exposed, err = payload.ParseBool(val[0]), nil
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
if val, ok := tmp["mapped"]; ok && len(val) > 0 {
|
||||
r.Mapped, err = payload.ParseBool(val[0]), nil
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
var val string
|
||||
// path params
|
||||
|
||||
val = chi.URLParam(req, "nodeID")
|
||||
r.NodeID, err = payload.ParseUint64(val), nil
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return err
|
||||
}
|
||||
801
server/federation/rest/request/node.go
Normal file
801
server/federation/rest/request/node.go
Normal file
@@ -0,0 +1,801 @@
|
||||
package request
|
||||
|
||||
// This file is auto-generated.
|
||||
//
|
||||
// Changes to this file may cause incorrect behavior and will be lost if
|
||||
// the code is regenerated.
|
||||
//
|
||||
// Definitions file that controls how this file is generated:
|
||||
//
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"github.com/cortezaproject/corteza/server/pkg/payload"
|
||||
"github.com/go-chi/chi/v5"
|
||||
"io"
|
||||
"mime/multipart"
|
||||
"net/http"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// dummy vars to prevent
|
||||
// unused imports complain
|
||||
var (
|
||||
_ = chi.URLParam
|
||||
_ = multipart.ErrMessageTooLarge
|
||||
_ = payload.ParseUint64s
|
||||
_ = strings.ToLower
|
||||
_ = io.EOF
|
||||
_ = fmt.Errorf
|
||||
_ = json.NewEncoder
|
||||
)
|
||||
|
||||
type (
|
||||
// Internal API interface
|
||||
NodeSearch struct {
|
||||
// Query GET parameter
|
||||
//
|
||||
// Filter nodes by name and host
|
||||
Query string
|
||||
|
||||
// Status GET parameter
|
||||
//
|
||||
// Filter by status
|
||||
Status string
|
||||
|
||||
// Limit GET parameter
|
||||
//
|
||||
// Limit
|
||||
Limit uint
|
||||
|
||||
// IncTotal GET parameter
|
||||
//
|
||||
// Include total counter
|
||||
IncTotal bool
|
||||
|
||||
// PageCursor GET parameter
|
||||
//
|
||||
// Page cursor
|
||||
PageCursor string
|
||||
|
||||
// Sort GET parameter
|
||||
//
|
||||
// Sort items
|
||||
Sort string
|
||||
}
|
||||
|
||||
NodeCreate struct {
|
||||
// BaseURL POST parameter
|
||||
//
|
||||
// Federation API base URL
|
||||
BaseURL string
|
||||
|
||||
// Name POST parameter
|
||||
//
|
||||
// Name for this node
|
||||
Name string
|
||||
|
||||
// Contact POST parameter
|
||||
//
|
||||
// Contact email
|
||||
Contact string
|
||||
|
||||
// PairingURI POST parameter
|
||||
//
|
||||
// Pairing URI
|
||||
PairingURI string
|
||||
}
|
||||
|
||||
NodeRead struct {
|
||||
// NodeID PATH parameter
|
||||
//
|
||||
// NodeID
|
||||
NodeID uint64 `json:",string"`
|
||||
}
|
||||
|
||||
NodeGenerateURI struct {
|
||||
// NodeID PATH parameter
|
||||
//
|
||||
// NodeID
|
||||
NodeID uint64 `json:",string"`
|
||||
}
|
||||
|
||||
NodeUpdate struct {
|
||||
// NodeID PATH parameter
|
||||
//
|
||||
// NodeID
|
||||
NodeID uint64 `json:",string"`
|
||||
|
||||
// Name POST parameter
|
||||
//
|
||||
// Name for this node
|
||||
Name string
|
||||
|
||||
// Contact POST parameter
|
||||
//
|
||||
// Contact email
|
||||
Contact string
|
||||
|
||||
// BaseURL POST parameter
|
||||
//
|
||||
// Federation API base URL
|
||||
BaseURL string
|
||||
}
|
||||
|
||||
NodeDelete struct {
|
||||
// NodeID PATH parameter
|
||||
//
|
||||
// NodeID
|
||||
NodeID uint64 `json:",string"`
|
||||
}
|
||||
|
||||
NodeUndelete struct {
|
||||
// NodeID PATH parameter
|
||||
//
|
||||
// NodeID
|
||||
NodeID uint64 `json:",string"`
|
||||
}
|
||||
|
||||
NodePair struct {
|
||||
// NodeID PATH parameter
|
||||
//
|
||||
// NodeID
|
||||
NodeID uint64 `json:",string"`
|
||||
}
|
||||
|
||||
NodeHandshakeConfirm struct {
|
||||
// NodeID PATH parameter
|
||||
//
|
||||
// NodeID
|
||||
NodeID uint64 `json:",string"`
|
||||
}
|
||||
|
||||
NodeHandshakeComplete struct {
|
||||
// NodeID PATH parameter
|
||||
//
|
||||
// NodeID
|
||||
NodeID uint64 `json:",string"`
|
||||
|
||||
// AuthToken POST parameter
|
||||
//
|
||||
// Node A token
|
||||
AuthToken string
|
||||
}
|
||||
)
|
||||
|
||||
// NewNodeSearch request
|
||||
func NewNodeSearch() *NodeSearch {
|
||||
return &NodeSearch{}
|
||||
}
|
||||
|
||||
// Auditable returns all auditable/loggable parameters
|
||||
func (r NodeSearch) Auditable() map[string]interface{} {
|
||||
return map[string]interface{}{
|
||||
"query": r.Query,
|
||||
"status": r.Status,
|
||||
"limit": r.Limit,
|
||||
"incTotal": r.IncTotal,
|
||||
"pageCursor": r.PageCursor,
|
||||
"sort": r.Sort,
|
||||
}
|
||||
}
|
||||
|
||||
// Auditable returns all auditable/loggable parameters
|
||||
func (r NodeSearch) GetQuery() string {
|
||||
return r.Query
|
||||
}
|
||||
|
||||
// Auditable returns all auditable/loggable parameters
|
||||
func (r NodeSearch) GetStatus() string {
|
||||
return r.Status
|
||||
}
|
||||
|
||||
// Auditable returns all auditable/loggable parameters
|
||||
func (r NodeSearch) GetLimit() uint {
|
||||
return r.Limit
|
||||
}
|
||||
|
||||
// Auditable returns all auditable/loggable parameters
|
||||
func (r NodeSearch) GetIncTotal() bool {
|
||||
return r.IncTotal
|
||||
}
|
||||
|
||||
// Auditable returns all auditable/loggable parameters
|
||||
func (r NodeSearch) GetPageCursor() string {
|
||||
return r.PageCursor
|
||||
}
|
||||
|
||||
// Auditable returns all auditable/loggable parameters
|
||||
func (r NodeSearch) GetSort() string {
|
||||
return r.Sort
|
||||
}
|
||||
|
||||
// Fill processes request and fills internal variables
|
||||
func (r *NodeSearch) Fill(req *http.Request) (err error) {
|
||||
|
||||
{
|
||||
// GET params
|
||||
tmp := req.URL.Query()
|
||||
|
||||
if val, ok := tmp["query"]; ok && len(val) > 0 {
|
||||
r.Query, err = val[0], nil
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
if val, ok := tmp["status"]; ok && len(val) > 0 {
|
||||
r.Status, err = val[0], nil
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
if val, ok := tmp["limit"]; ok && len(val) > 0 {
|
||||
r.Limit, err = payload.ParseUint(val[0]), nil
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
if val, ok := tmp["incTotal"]; ok && len(val) > 0 {
|
||||
r.IncTotal, err = payload.ParseBool(val[0]), nil
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
if val, ok := tmp["pageCursor"]; ok && len(val) > 0 {
|
||||
r.PageCursor, err = val[0], nil
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
if val, ok := tmp["sort"]; ok && len(val) > 0 {
|
||||
r.Sort, err = val[0], nil
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
// NewNodeCreate request
|
||||
func NewNodeCreate() *NodeCreate {
|
||||
return &NodeCreate{}
|
||||
}
|
||||
|
||||
// Auditable returns all auditable/loggable parameters
|
||||
func (r NodeCreate) Auditable() map[string]interface{} {
|
||||
return map[string]interface{}{
|
||||
"baseURL": r.BaseURL,
|
||||
"name": r.Name,
|
||||
"contact": r.Contact,
|
||||
"pairingURI": r.PairingURI,
|
||||
}
|
||||
}
|
||||
|
||||
// Auditable returns all auditable/loggable parameters
|
||||
func (r NodeCreate) GetBaseURL() string {
|
||||
return r.BaseURL
|
||||
}
|
||||
|
||||
// Auditable returns all auditable/loggable parameters
|
||||
func (r NodeCreate) GetName() string {
|
||||
return r.Name
|
||||
}
|
||||
|
||||
// Auditable returns all auditable/loggable parameters
|
||||
func (r NodeCreate) GetContact() string {
|
||||
return r.Contact
|
||||
}
|
||||
|
||||
// Auditable returns all auditable/loggable parameters
|
||||
func (r NodeCreate) GetPairingURI() string {
|
||||
return r.PairingURI
|
||||
}
|
||||
|
||||
// Fill processes request and fills internal variables
|
||||
func (r *NodeCreate) Fill(req *http.Request) (err error) {
|
||||
|
||||
if strings.HasPrefix(strings.ToLower(req.Header.Get("content-type")), "application/json") {
|
||||
err = json.NewDecoder(req.Body).Decode(r)
|
||||
|
||||
switch {
|
||||
case err == io.EOF:
|
||||
err = nil
|
||||
case err != nil:
|
||||
return fmt.Errorf("error parsing http request body: %w", err)
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
// Caching 32MB to memory, the rest to disk
|
||||
if err = req.ParseMultipartForm(32 << 20); err != nil && err != http.ErrNotMultipart {
|
||||
return err
|
||||
} else if err == nil {
|
||||
// Multipart params
|
||||
|
||||
if val, ok := req.MultipartForm.Value["baseURL"]; ok && len(val) > 0 {
|
||||
r.BaseURL, err = val[0], nil
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
if val, ok := req.MultipartForm.Value["name"]; ok && len(val) > 0 {
|
||||
r.Name, err = val[0], nil
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
if val, ok := req.MultipartForm.Value["contact"]; ok && len(val) > 0 {
|
||||
r.Contact, err = val[0], nil
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
if val, ok := req.MultipartForm.Value["pairingURI"]; ok && len(val) > 0 {
|
||||
r.PairingURI, err = val[0], nil
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
if err = req.ParseForm(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// POST params
|
||||
|
||||
if val, ok := req.Form["baseURL"]; ok && len(val) > 0 {
|
||||
r.BaseURL, err = val[0], nil
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
if val, ok := req.Form["name"]; ok && len(val) > 0 {
|
||||
r.Name, err = val[0], nil
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
if val, ok := req.Form["contact"]; ok && len(val) > 0 {
|
||||
r.Contact, err = val[0], nil
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
if val, ok := req.Form["pairingURI"]; ok && len(val) > 0 {
|
||||
r.PairingURI, err = val[0], nil
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
// NewNodeRead request
|
||||
func NewNodeRead() *NodeRead {
|
||||
return &NodeRead{}
|
||||
}
|
||||
|
||||
// Auditable returns all auditable/loggable parameters
|
||||
func (r NodeRead) Auditable() map[string]interface{} {
|
||||
return map[string]interface{}{
|
||||
"nodeID": r.NodeID,
|
||||
}
|
||||
}
|
||||
|
||||
// Auditable returns all auditable/loggable parameters
|
||||
func (r NodeRead) GetNodeID() uint64 {
|
||||
return r.NodeID
|
||||
}
|
||||
|
||||
// Fill processes request and fills internal variables
|
||||
func (r *NodeRead) Fill(req *http.Request) (err error) {
|
||||
|
||||
{
|
||||
var val string
|
||||
// path params
|
||||
|
||||
val = chi.URLParam(req, "nodeID")
|
||||
r.NodeID, err = payload.ParseUint64(val), nil
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
// NewNodeGenerateURI request
|
||||
func NewNodeGenerateURI() *NodeGenerateURI {
|
||||
return &NodeGenerateURI{}
|
||||
}
|
||||
|
||||
// Auditable returns all auditable/loggable parameters
|
||||
func (r NodeGenerateURI) Auditable() map[string]interface{} {
|
||||
return map[string]interface{}{
|
||||
"nodeID": r.NodeID,
|
||||
}
|
||||
}
|
||||
|
||||
// Auditable returns all auditable/loggable parameters
|
||||
func (r NodeGenerateURI) GetNodeID() uint64 {
|
||||
return r.NodeID
|
||||
}
|
||||
|
||||
// Fill processes request and fills internal variables
|
||||
func (r *NodeGenerateURI) Fill(req *http.Request) (err error) {
|
||||
|
||||
{
|
||||
var val string
|
||||
// path params
|
||||
|
||||
val = chi.URLParam(req, "nodeID")
|
||||
r.NodeID, err = payload.ParseUint64(val), nil
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
// NewNodeUpdate request
|
||||
func NewNodeUpdate() *NodeUpdate {
|
||||
return &NodeUpdate{}
|
||||
}
|
||||
|
||||
// Auditable returns all auditable/loggable parameters
|
||||
func (r NodeUpdate) Auditable() map[string]interface{} {
|
||||
return map[string]interface{}{
|
||||
"nodeID": r.NodeID,
|
||||
"name": r.Name,
|
||||
"contact": r.Contact,
|
||||
"baseURL": r.BaseURL,
|
||||
}
|
||||
}
|
||||
|
||||
// Auditable returns all auditable/loggable parameters
|
||||
func (r NodeUpdate) GetNodeID() uint64 {
|
||||
return r.NodeID
|
||||
}
|
||||
|
||||
// Auditable returns all auditable/loggable parameters
|
||||
func (r NodeUpdate) GetName() string {
|
||||
return r.Name
|
||||
}
|
||||
|
||||
// Auditable returns all auditable/loggable parameters
|
||||
func (r NodeUpdate) GetContact() string {
|
||||
return r.Contact
|
||||
}
|
||||
|
||||
// Auditable returns all auditable/loggable parameters
|
||||
func (r NodeUpdate) GetBaseURL() string {
|
||||
return r.BaseURL
|
||||
}
|
||||
|
||||
// Fill processes request and fills internal variables
|
||||
func (r *NodeUpdate) Fill(req *http.Request) (err error) {
|
||||
|
||||
if strings.HasPrefix(strings.ToLower(req.Header.Get("content-type")), "application/json") {
|
||||
err = json.NewDecoder(req.Body).Decode(r)
|
||||
|
||||
switch {
|
||||
case err == io.EOF:
|
||||
err = nil
|
||||
case err != nil:
|
||||
return fmt.Errorf("error parsing http request body: %w", err)
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
// Caching 32MB to memory, the rest to disk
|
||||
if err = req.ParseMultipartForm(32 << 20); err != nil && err != http.ErrNotMultipart {
|
||||
return err
|
||||
} else if err == nil {
|
||||
// Multipart params
|
||||
|
||||
if val, ok := req.MultipartForm.Value["name"]; ok && len(val) > 0 {
|
||||
r.Name, err = val[0], nil
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
if val, ok := req.MultipartForm.Value["contact"]; ok && len(val) > 0 {
|
||||
r.Contact, err = val[0], nil
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
if val, ok := req.MultipartForm.Value["baseURL"]; ok && len(val) > 0 {
|
||||
r.BaseURL, err = val[0], nil
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
if err = req.ParseForm(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// POST params
|
||||
|
||||
if val, ok := req.Form["name"]; ok && len(val) > 0 {
|
||||
r.Name, err = val[0], nil
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
if val, ok := req.Form["contact"]; ok && len(val) > 0 {
|
||||
r.Contact, err = val[0], nil
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
if val, ok := req.Form["baseURL"]; ok && len(val) > 0 {
|
||||
r.BaseURL, err = val[0], nil
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
var val string
|
||||
// path params
|
||||
|
||||
val = chi.URLParam(req, "nodeID")
|
||||
r.NodeID, err = payload.ParseUint64(val), nil
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
// NewNodeDelete request
|
||||
func NewNodeDelete() *NodeDelete {
|
||||
return &NodeDelete{}
|
||||
}
|
||||
|
||||
// Auditable returns all auditable/loggable parameters
|
||||
func (r NodeDelete) Auditable() map[string]interface{} {
|
||||
return map[string]interface{}{
|
||||
"nodeID": r.NodeID,
|
||||
}
|
||||
}
|
||||
|
||||
// Auditable returns all auditable/loggable parameters
|
||||
func (r NodeDelete) GetNodeID() uint64 {
|
||||
return r.NodeID
|
||||
}
|
||||
|
||||
// Fill processes request and fills internal variables
|
||||
func (r *NodeDelete) Fill(req *http.Request) (err error) {
|
||||
|
||||
{
|
||||
var val string
|
||||
// path params
|
||||
|
||||
val = chi.URLParam(req, "nodeID")
|
||||
r.NodeID, err = payload.ParseUint64(val), nil
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
// NewNodeUndelete request
|
||||
func NewNodeUndelete() *NodeUndelete {
|
||||
return &NodeUndelete{}
|
||||
}
|
||||
|
||||
// Auditable returns all auditable/loggable parameters
|
||||
func (r NodeUndelete) Auditable() map[string]interface{} {
|
||||
return map[string]interface{}{
|
||||
"nodeID": r.NodeID,
|
||||
}
|
||||
}
|
||||
|
||||
// Auditable returns all auditable/loggable parameters
|
||||
func (r NodeUndelete) GetNodeID() uint64 {
|
||||
return r.NodeID
|
||||
}
|
||||
|
||||
// Fill processes request and fills internal variables
|
||||
func (r *NodeUndelete) Fill(req *http.Request) (err error) {
|
||||
|
||||
{
|
||||
var val string
|
||||
// path params
|
||||
|
||||
val = chi.URLParam(req, "nodeID")
|
||||
r.NodeID, err = payload.ParseUint64(val), nil
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
// NewNodePair request
|
||||
func NewNodePair() *NodePair {
|
||||
return &NodePair{}
|
||||
}
|
||||
|
||||
// Auditable returns all auditable/loggable parameters
|
||||
func (r NodePair) Auditable() map[string]interface{} {
|
||||
return map[string]interface{}{
|
||||
"nodeID": r.NodeID,
|
||||
}
|
||||
}
|
||||
|
||||
// Auditable returns all auditable/loggable parameters
|
||||
func (r NodePair) GetNodeID() uint64 {
|
||||
return r.NodeID
|
||||
}
|
||||
|
||||
// Fill processes request and fills internal variables
|
||||
func (r *NodePair) Fill(req *http.Request) (err error) {
|
||||
|
||||
{
|
||||
var val string
|
||||
// path params
|
||||
|
||||
val = chi.URLParam(req, "nodeID")
|
||||
r.NodeID, err = payload.ParseUint64(val), nil
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
// NewNodeHandshakeConfirm request
|
||||
func NewNodeHandshakeConfirm() *NodeHandshakeConfirm {
|
||||
return &NodeHandshakeConfirm{}
|
||||
}
|
||||
|
||||
// Auditable returns all auditable/loggable parameters
|
||||
func (r NodeHandshakeConfirm) Auditable() map[string]interface{} {
|
||||
return map[string]interface{}{
|
||||
"nodeID": r.NodeID,
|
||||
}
|
||||
}
|
||||
|
||||
// Auditable returns all auditable/loggable parameters
|
||||
func (r NodeHandshakeConfirm) GetNodeID() uint64 {
|
||||
return r.NodeID
|
||||
}
|
||||
|
||||
// Fill processes request and fills internal variables
|
||||
func (r *NodeHandshakeConfirm) Fill(req *http.Request) (err error) {
|
||||
|
||||
{
|
||||
var val string
|
||||
// path params
|
||||
|
||||
val = chi.URLParam(req, "nodeID")
|
||||
r.NodeID, err = payload.ParseUint64(val), nil
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
// NewNodeHandshakeComplete request
|
||||
func NewNodeHandshakeComplete() *NodeHandshakeComplete {
|
||||
return &NodeHandshakeComplete{}
|
||||
}
|
||||
|
||||
// Auditable returns all auditable/loggable parameters
|
||||
func (r NodeHandshakeComplete) Auditable() map[string]interface{} {
|
||||
return map[string]interface{}{
|
||||
"nodeID": r.NodeID,
|
||||
"authToken": r.AuthToken,
|
||||
}
|
||||
}
|
||||
|
||||
// Auditable returns all auditable/loggable parameters
|
||||
func (r NodeHandshakeComplete) GetNodeID() uint64 {
|
||||
return r.NodeID
|
||||
}
|
||||
|
||||
// Auditable returns all auditable/loggable parameters
|
||||
func (r NodeHandshakeComplete) GetAuthToken() string {
|
||||
return r.AuthToken
|
||||
}
|
||||
|
||||
// Fill processes request and fills internal variables
|
||||
func (r *NodeHandshakeComplete) Fill(req *http.Request) (err error) {
|
||||
|
||||
if strings.HasPrefix(strings.ToLower(req.Header.Get("content-type")), "application/json") {
|
||||
err = json.NewDecoder(req.Body).Decode(r)
|
||||
|
||||
switch {
|
||||
case err == io.EOF:
|
||||
err = nil
|
||||
case err != nil:
|
||||
return fmt.Errorf("error parsing http request body: %w", err)
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
// Caching 32MB to memory, the rest to disk
|
||||
if err = req.ParseMultipartForm(32 << 20); err != nil && err != http.ErrNotMultipart {
|
||||
return err
|
||||
} else if err == nil {
|
||||
// Multipart params
|
||||
|
||||
if val, ok := req.MultipartForm.Value["authToken"]; ok && len(val) > 0 {
|
||||
r.AuthToken, err = val[0], nil
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
if err = req.ParseForm(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// POST params
|
||||
|
||||
if val, ok := req.Form["authToken"]; ok && len(val) > 0 {
|
||||
r.AuthToken, err = val[0], nil
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
var val string
|
||||
// path params
|
||||
|
||||
val = chi.URLParam(req, "nodeID")
|
||||
r.NodeID, err = payload.ParseUint64(val), nil
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return err
|
||||
}
|
||||
180
server/federation/rest/request/nodeHandshake.go
Normal file
180
server/federation/rest/request/nodeHandshake.go
Normal file
@@ -0,0 +1,180 @@
|
||||
package request
|
||||
|
||||
// This file is auto-generated.
|
||||
//
|
||||
// Changes to this file may cause incorrect behavior and will be lost if
|
||||
// the code is regenerated.
|
||||
//
|
||||
// Definitions file that controls how this file is generated:
|
||||
//
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"github.com/cortezaproject/corteza/server/pkg/payload"
|
||||
"github.com/go-chi/chi/v5"
|
||||
"io"
|
||||
"mime/multipart"
|
||||
"net/http"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// dummy vars to prevent
|
||||
// unused imports complain
|
||||
var (
|
||||
_ = chi.URLParam
|
||||
_ = multipart.ErrMessageTooLarge
|
||||
_ = payload.ParseUint64s
|
||||
_ = strings.ToLower
|
||||
_ = io.EOF
|
||||
_ = fmt.Errorf
|
||||
_ = json.NewEncoder
|
||||
)
|
||||
|
||||
type (
|
||||
// Internal API interface
|
||||
NodeHandshakeInitialize struct {
|
||||
// NodeID PATH parameter
|
||||
//
|
||||
// NodeID
|
||||
NodeID uint64 `json:",string"`
|
||||
|
||||
// PairToken POST parameter
|
||||
//
|
||||
// Pairing token to authenticate handshake initialization request
|
||||
PairToken string
|
||||
|
||||
// SharedNodeID POST parameter
|
||||
//
|
||||
// Remote (invoker's) node ID
|
||||
SharedNodeID uint64 `json:",string"`
|
||||
|
||||
// AuthToken POST parameter
|
||||
//
|
||||
// Authentication token so that remote
|
||||
AuthToken string
|
||||
}
|
||||
)
|
||||
|
||||
// NewNodeHandshakeInitialize request
|
||||
func NewNodeHandshakeInitialize() *NodeHandshakeInitialize {
|
||||
return &NodeHandshakeInitialize{}
|
||||
}
|
||||
|
||||
// Auditable returns all auditable/loggable parameters
|
||||
func (r NodeHandshakeInitialize) Auditable() map[string]interface{} {
|
||||
return map[string]interface{}{
|
||||
"nodeID": r.NodeID,
|
||||
"pairToken": r.PairToken,
|
||||
"sharedNodeID": r.SharedNodeID,
|
||||
"authToken": r.AuthToken,
|
||||
}
|
||||
}
|
||||
|
||||
// Auditable returns all auditable/loggable parameters
|
||||
func (r NodeHandshakeInitialize) GetNodeID() uint64 {
|
||||
return r.NodeID
|
||||
}
|
||||
|
||||
// Auditable returns all auditable/loggable parameters
|
||||
func (r NodeHandshakeInitialize) GetPairToken() string {
|
||||
return r.PairToken
|
||||
}
|
||||
|
||||
// Auditable returns all auditable/loggable parameters
|
||||
func (r NodeHandshakeInitialize) GetSharedNodeID() uint64 {
|
||||
return r.SharedNodeID
|
||||
}
|
||||
|
||||
// Auditable returns all auditable/loggable parameters
|
||||
func (r NodeHandshakeInitialize) GetAuthToken() string {
|
||||
return r.AuthToken
|
||||
}
|
||||
|
||||
// Fill processes request and fills internal variables
|
||||
func (r *NodeHandshakeInitialize) Fill(req *http.Request) (err error) {
|
||||
|
||||
if strings.HasPrefix(strings.ToLower(req.Header.Get("content-type")), "application/json") {
|
||||
err = json.NewDecoder(req.Body).Decode(r)
|
||||
|
||||
switch {
|
||||
case err == io.EOF:
|
||||
err = nil
|
||||
case err != nil:
|
||||
return fmt.Errorf("error parsing http request body: %w", err)
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
// Caching 32MB to memory, the rest to disk
|
||||
if err = req.ParseMultipartForm(32 << 20); err != nil && err != http.ErrNotMultipart {
|
||||
return err
|
||||
} else if err == nil {
|
||||
// Multipart params
|
||||
|
||||
if val, ok := req.MultipartForm.Value["pairToken"]; ok && len(val) > 0 {
|
||||
r.PairToken, err = val[0], nil
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
if val, ok := req.MultipartForm.Value["sharedNodeID"]; ok && len(val) > 0 {
|
||||
r.SharedNodeID, err = payload.ParseUint64(val[0]), nil
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
if val, ok := req.MultipartForm.Value["authToken"]; ok && len(val) > 0 {
|
||||
r.AuthToken, err = val[0], nil
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
if err = req.ParseForm(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// POST params
|
||||
|
||||
if val, ok := req.Form["pairToken"]; ok && len(val) > 0 {
|
||||
r.PairToken, err = val[0], nil
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
if val, ok := req.Form["sharedNodeID"]; ok && len(val) > 0 {
|
||||
r.SharedNodeID, err = payload.ParseUint64(val[0]), nil
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
if val, ok := req.Form["authToken"]; ok && len(val) > 0 {
|
||||
r.AuthToken, err = val[0], nil
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
var val string
|
||||
// path params
|
||||
|
||||
val = chi.URLParam(req, "nodeID")
|
||||
r.NodeID, err = payload.ParseUint64(val), nil
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return err
|
||||
}
|
||||
384
server/federation/rest/request/permissions.go
Normal file
384
server/federation/rest/request/permissions.go
Normal file
@@ -0,0 +1,384 @@
|
||||
package request
|
||||
|
||||
// This file is auto-generated.
|
||||
//
|
||||
// Changes to this file may cause incorrect behavior and will be lost if
|
||||
// the code is regenerated.
|
||||
//
|
||||
// Definitions file that controls how this file is generated:
|
||||
//
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"github.com/cortezaproject/corteza/server/pkg/payload"
|
||||
"github.com/cortezaproject/corteza/server/pkg/rbac"
|
||||
"github.com/go-chi/chi/v5"
|
||||
"io"
|
||||
"mime/multipart"
|
||||
"net/http"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// dummy vars to prevent
|
||||
// unused imports complain
|
||||
var (
|
||||
_ = chi.URLParam
|
||||
_ = multipart.ErrMessageTooLarge
|
||||
_ = payload.ParseUint64s
|
||||
_ = strings.ToLower
|
||||
_ = io.EOF
|
||||
_ = fmt.Errorf
|
||||
_ = json.NewEncoder
|
||||
)
|
||||
|
||||
type (
|
||||
// Internal API interface
|
||||
PermissionsList struct {
|
||||
}
|
||||
|
||||
PermissionsEffective struct {
|
||||
// Resource GET parameter
|
||||
//
|
||||
// Show only rules for a specific resource
|
||||
Resource string
|
||||
}
|
||||
|
||||
PermissionsTrace struct {
|
||||
// Resource GET parameter
|
||||
//
|
||||
// Show only rules for a specific resource
|
||||
Resource []string
|
||||
|
||||
// UserID GET parameter
|
||||
//
|
||||
//
|
||||
UserID uint64 `json:",string"`
|
||||
|
||||
// RoleID GET parameter
|
||||
//
|
||||
//
|
||||
RoleID []uint64
|
||||
}
|
||||
|
||||
PermissionsRead struct {
|
||||
// RoleID PATH parameter
|
||||
//
|
||||
// Role ID
|
||||
RoleID uint64 `json:",string"`
|
||||
|
||||
// Resource GET parameter
|
||||
//
|
||||
// Show only rules for a specific resource
|
||||
Resource []string
|
||||
}
|
||||
|
||||
PermissionsDelete struct {
|
||||
// RoleID PATH parameter
|
||||
//
|
||||
// Role ID
|
||||
RoleID uint64 `json:",string"`
|
||||
}
|
||||
|
||||
PermissionsUpdate struct {
|
||||
// RoleID PATH parameter
|
||||
//
|
||||
// Role ID
|
||||
RoleID uint64 `json:",string"`
|
||||
|
||||
// Rules POST parameter
|
||||
//
|
||||
// List of permission rules to set
|
||||
Rules rbac.RuleSet
|
||||
}
|
||||
)
|
||||
|
||||
// NewPermissionsList request
|
||||
func NewPermissionsList() *PermissionsList {
|
||||
return &PermissionsList{}
|
||||
}
|
||||
|
||||
// Auditable returns all auditable/loggable parameters
|
||||
func (r PermissionsList) Auditable() map[string]interface{} {
|
||||
return map[string]interface{}{}
|
||||
}
|
||||
|
||||
// Fill processes request and fills internal variables
|
||||
func (r *PermissionsList) Fill(req *http.Request) (err error) {
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
// NewPermissionsEffective request
|
||||
func NewPermissionsEffective() *PermissionsEffective {
|
||||
return &PermissionsEffective{}
|
||||
}
|
||||
|
||||
// Auditable returns all auditable/loggable parameters
|
||||
func (r PermissionsEffective) Auditable() map[string]interface{} {
|
||||
return map[string]interface{}{
|
||||
"resource": r.Resource,
|
||||
}
|
||||
}
|
||||
|
||||
// Auditable returns all auditable/loggable parameters
|
||||
func (r PermissionsEffective) GetResource() string {
|
||||
return r.Resource
|
||||
}
|
||||
|
||||
// Fill processes request and fills internal variables
|
||||
func (r *PermissionsEffective) Fill(req *http.Request) (err error) {
|
||||
|
||||
{
|
||||
// GET params
|
||||
tmp := req.URL.Query()
|
||||
|
||||
if val, ok := tmp["resource"]; ok && len(val) > 0 {
|
||||
r.Resource, err = val[0], nil
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
// NewPermissionsTrace request
|
||||
func NewPermissionsTrace() *PermissionsTrace {
|
||||
return &PermissionsTrace{}
|
||||
}
|
||||
|
||||
// Auditable returns all auditable/loggable parameters
|
||||
func (r PermissionsTrace) Auditable() map[string]interface{} {
|
||||
return map[string]interface{}{
|
||||
"resource": r.Resource,
|
||||
"userID": r.UserID,
|
||||
"roleID": r.RoleID,
|
||||
}
|
||||
}
|
||||
|
||||
// Auditable returns all auditable/loggable parameters
|
||||
func (r PermissionsTrace) GetResource() []string {
|
||||
return r.Resource
|
||||
}
|
||||
|
||||
// Auditable returns all auditable/loggable parameters
|
||||
func (r PermissionsTrace) GetUserID() uint64 {
|
||||
return r.UserID
|
||||
}
|
||||
|
||||
// Auditable returns all auditable/loggable parameters
|
||||
func (r PermissionsTrace) GetRoleID() []uint64 {
|
||||
return r.RoleID
|
||||
}
|
||||
|
||||
// Fill processes request and fills internal variables
|
||||
func (r *PermissionsTrace) Fill(req *http.Request) (err error) {
|
||||
|
||||
{
|
||||
// GET params
|
||||
tmp := req.URL.Query()
|
||||
|
||||
if val, ok := tmp["resource[]"]; ok {
|
||||
r.Resource, err = val, nil
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
} else if val, ok := tmp["resource"]; ok {
|
||||
r.Resource, err = val, nil
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
if val, ok := tmp["userID"]; ok && len(val) > 0 {
|
||||
r.UserID, err = payload.ParseUint64(val[0]), nil
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
if val, ok := tmp["roleID[]"]; ok {
|
||||
r.RoleID, err = payload.ParseUint64s(val), nil
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
} else if val, ok := tmp["roleID"]; ok {
|
||||
r.RoleID, err = payload.ParseUint64s(val), nil
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
// NewPermissionsRead request
|
||||
func NewPermissionsRead() *PermissionsRead {
|
||||
return &PermissionsRead{}
|
||||
}
|
||||
|
||||
// Auditable returns all auditable/loggable parameters
|
||||
func (r PermissionsRead) Auditable() map[string]interface{} {
|
||||
return map[string]interface{}{
|
||||
"roleID": r.RoleID,
|
||||
"resource": r.Resource,
|
||||
}
|
||||
}
|
||||
|
||||
// Auditable returns all auditable/loggable parameters
|
||||
func (r PermissionsRead) GetRoleID() uint64 {
|
||||
return r.RoleID
|
||||
}
|
||||
|
||||
// Auditable returns all auditable/loggable parameters
|
||||
func (r PermissionsRead) GetResource() []string {
|
||||
return r.Resource
|
||||
}
|
||||
|
||||
// Fill processes request and fills internal variables
|
||||
func (r *PermissionsRead) Fill(req *http.Request) (err error) {
|
||||
|
||||
{
|
||||
// GET params
|
||||
tmp := req.URL.Query()
|
||||
|
||||
if val, ok := tmp["resource[]"]; ok {
|
||||
r.Resource, err = val, nil
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
} else if val, ok := tmp["resource"]; ok {
|
||||
r.Resource, err = val, nil
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
var val string
|
||||
// path params
|
||||
|
||||
val = chi.URLParam(req, "roleID")
|
||||
r.RoleID, err = payload.ParseUint64(val), nil
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
// NewPermissionsDelete request
|
||||
func NewPermissionsDelete() *PermissionsDelete {
|
||||
return &PermissionsDelete{}
|
||||
}
|
||||
|
||||
// Auditable returns all auditable/loggable parameters
|
||||
func (r PermissionsDelete) Auditable() map[string]interface{} {
|
||||
return map[string]interface{}{
|
||||
"roleID": r.RoleID,
|
||||
}
|
||||
}
|
||||
|
||||
// Auditable returns all auditable/loggable parameters
|
||||
func (r PermissionsDelete) GetRoleID() uint64 {
|
||||
return r.RoleID
|
||||
}
|
||||
|
||||
// Fill processes request and fills internal variables
|
||||
func (r *PermissionsDelete) Fill(req *http.Request) (err error) {
|
||||
|
||||
{
|
||||
var val string
|
||||
// path params
|
||||
|
||||
val = chi.URLParam(req, "roleID")
|
||||
r.RoleID, err = payload.ParseUint64(val), nil
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
// NewPermissionsUpdate request
|
||||
func NewPermissionsUpdate() *PermissionsUpdate {
|
||||
return &PermissionsUpdate{}
|
||||
}
|
||||
|
||||
// Auditable returns all auditable/loggable parameters
|
||||
func (r PermissionsUpdate) Auditable() map[string]interface{} {
|
||||
return map[string]interface{}{
|
||||
"roleID": r.RoleID,
|
||||
"rules": r.Rules,
|
||||
}
|
||||
}
|
||||
|
||||
// Auditable returns all auditable/loggable parameters
|
||||
func (r PermissionsUpdate) GetRoleID() uint64 {
|
||||
return r.RoleID
|
||||
}
|
||||
|
||||
// Auditable returns all auditable/loggable parameters
|
||||
func (r PermissionsUpdate) GetRules() rbac.RuleSet {
|
||||
return r.Rules
|
||||
}
|
||||
|
||||
// Fill processes request and fills internal variables
|
||||
func (r *PermissionsUpdate) Fill(req *http.Request) (err error) {
|
||||
|
||||
if strings.HasPrefix(strings.ToLower(req.Header.Get("content-type")), "application/json") {
|
||||
err = json.NewDecoder(req.Body).Decode(r)
|
||||
|
||||
switch {
|
||||
case err == io.EOF:
|
||||
err = nil
|
||||
case err != nil:
|
||||
return fmt.Errorf("error parsing http request body: %w", err)
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
// Caching 32MB to memory, the rest to disk
|
||||
if err = req.ParseMultipartForm(32 << 20); err != nil && err != http.ErrNotMultipart {
|
||||
return err
|
||||
} else if err == nil {
|
||||
// Multipart params
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
if err = req.ParseForm(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// POST params
|
||||
|
||||
//if val, ok := req.Form["rules[]"]; ok && len(val) > 0 {
|
||||
// r.Rules, err = rbac.RuleSet(val), nil
|
||||
// if err != nil {
|
||||
// return err
|
||||
// }
|
||||
//}
|
||||
}
|
||||
|
||||
{
|
||||
var val string
|
||||
// path params
|
||||
|
||||
val = chi.URLParam(req, "roleID")
|
||||
r.RoleID, err = payload.ParseUint64(val), nil
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return err
|
||||
}
|
||||
468
server/federation/rest/request/syncData.go
Normal file
468
server/federation/rest/request/syncData.go
Normal file
@@ -0,0 +1,468 @@
|
||||
package request
|
||||
|
||||
// This file is auto-generated.
|
||||
//
|
||||
// Changes to this file may cause incorrect behavior and will be lost if
|
||||
// the code is regenerated.
|
||||
//
|
||||
// Definitions file that controls how this file is generated:
|
||||
//
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"github.com/cortezaproject/corteza/server/pkg/payload"
|
||||
"github.com/go-chi/chi/v5"
|
||||
"io"
|
||||
"mime/multipart"
|
||||
"net/http"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// dummy vars to prevent
|
||||
// unused imports complain
|
||||
var (
|
||||
_ = chi.URLParam
|
||||
_ = multipart.ErrMessageTooLarge
|
||||
_ = payload.ParseUint64s
|
||||
_ = strings.ToLower
|
||||
_ = io.EOF
|
||||
_ = fmt.Errorf
|
||||
_ = json.NewEncoder
|
||||
)
|
||||
|
||||
type (
|
||||
// Internal API interface
|
||||
SyncDataReadExposedAll struct {
|
||||
// NodeID PATH parameter
|
||||
//
|
||||
// Node ID
|
||||
NodeID uint64 `json:",string"`
|
||||
|
||||
// LastSync GET parameter
|
||||
//
|
||||
// Last sync timestamp
|
||||
LastSync uint64 `json:",string"`
|
||||
|
||||
// Query GET parameter
|
||||
//
|
||||
// Search query
|
||||
Query string
|
||||
|
||||
// Limit GET parameter
|
||||
//
|
||||
// Limit
|
||||
Limit uint
|
||||
|
||||
// PageCursor GET parameter
|
||||
//
|
||||
// Page cursor
|
||||
PageCursor string
|
||||
|
||||
// Sort GET parameter
|
||||
//
|
||||
// Sort items
|
||||
Sort string
|
||||
}
|
||||
|
||||
SyncDataReadExposedInternal struct {
|
||||
// NodeID PATH parameter
|
||||
//
|
||||
// Node ID
|
||||
NodeID uint64 `json:",string"`
|
||||
|
||||
// ModuleID PATH parameter
|
||||
//
|
||||
// Module ID
|
||||
ModuleID uint64 `json:",string"`
|
||||
|
||||
// LastSync GET parameter
|
||||
//
|
||||
// Last sync timestamp
|
||||
LastSync uint64 `json:",string"`
|
||||
|
||||
// Query GET parameter
|
||||
//
|
||||
// Search query
|
||||
Query string
|
||||
|
||||
// Limit GET parameter
|
||||
//
|
||||
// Limit
|
||||
Limit uint
|
||||
|
||||
// PageCursor GET parameter
|
||||
//
|
||||
// Page cursor
|
||||
PageCursor string
|
||||
|
||||
// Sort GET parameter
|
||||
//
|
||||
// Sort items
|
||||
Sort string
|
||||
}
|
||||
|
||||
SyncDataReadExposedSocial struct {
|
||||
// NodeID PATH parameter
|
||||
//
|
||||
// Node ID
|
||||
NodeID uint64 `json:",string"`
|
||||
|
||||
// ModuleID PATH parameter
|
||||
//
|
||||
// Module ID
|
||||
ModuleID uint64 `json:",string"`
|
||||
|
||||
// LastSync GET parameter
|
||||
//
|
||||
// Last sync timestamp
|
||||
LastSync uint64 `json:",string"`
|
||||
|
||||
// Query GET parameter
|
||||
//
|
||||
// Search query
|
||||
Query string
|
||||
|
||||
// Limit GET parameter
|
||||
//
|
||||
// Limit
|
||||
Limit uint
|
||||
|
||||
// PageCursor GET parameter
|
||||
//
|
||||
// Page cursor
|
||||
PageCursor string
|
||||
|
||||
// Sort GET parameter
|
||||
//
|
||||
// Sort items
|
||||
Sort string
|
||||
}
|
||||
)
|
||||
|
||||
// NewSyncDataReadExposedAll request
|
||||
func NewSyncDataReadExposedAll() *SyncDataReadExposedAll {
|
||||
return &SyncDataReadExposedAll{}
|
||||
}
|
||||
|
||||
// Auditable returns all auditable/loggable parameters
|
||||
func (r SyncDataReadExposedAll) Auditable() map[string]interface{} {
|
||||
return map[string]interface{}{
|
||||
"nodeID": r.NodeID,
|
||||
"lastSync": r.LastSync,
|
||||
"query": r.Query,
|
||||
"limit": r.Limit,
|
||||
"pageCursor": r.PageCursor,
|
||||
"sort": r.Sort,
|
||||
}
|
||||
}
|
||||
|
||||
// Auditable returns all auditable/loggable parameters
|
||||
func (r SyncDataReadExposedAll) GetNodeID() uint64 {
|
||||
return r.NodeID
|
||||
}
|
||||
|
||||
// Auditable returns all auditable/loggable parameters
|
||||
func (r SyncDataReadExposedAll) GetLastSync() uint64 {
|
||||
return r.LastSync
|
||||
}
|
||||
|
||||
// Auditable returns all auditable/loggable parameters
|
||||
func (r SyncDataReadExposedAll) GetQuery() string {
|
||||
return r.Query
|
||||
}
|
||||
|
||||
// Auditable returns all auditable/loggable parameters
|
||||
func (r SyncDataReadExposedAll) GetLimit() uint {
|
||||
return r.Limit
|
||||
}
|
||||
|
||||
// Auditable returns all auditable/loggable parameters
|
||||
func (r SyncDataReadExposedAll) GetPageCursor() string {
|
||||
return r.PageCursor
|
||||
}
|
||||
|
||||
// Auditable returns all auditable/loggable parameters
|
||||
func (r SyncDataReadExposedAll) GetSort() string {
|
||||
return r.Sort
|
||||
}
|
||||
|
||||
// Fill processes request and fills internal variables
|
||||
func (r *SyncDataReadExposedAll) Fill(req *http.Request) (err error) {
|
||||
|
||||
{
|
||||
// GET params
|
||||
tmp := req.URL.Query()
|
||||
|
||||
if val, ok := tmp["lastSync"]; ok && len(val) > 0 {
|
||||
r.LastSync, err = payload.ParseUint64(val[0]), nil
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
if val, ok := tmp["query"]; ok && len(val) > 0 {
|
||||
r.Query, err = val[0], nil
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
if val, ok := tmp["limit"]; ok && len(val) > 0 {
|
||||
r.Limit, err = payload.ParseUint(val[0]), nil
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
if val, ok := tmp["pageCursor"]; ok && len(val) > 0 {
|
||||
r.PageCursor, err = val[0], nil
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
if val, ok := tmp["sort"]; ok && len(val) > 0 {
|
||||
r.Sort, err = val[0], nil
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
var val string
|
||||
// path params
|
||||
|
||||
val = chi.URLParam(req, "nodeID")
|
||||
r.NodeID, err = payload.ParseUint64(val), nil
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
// NewSyncDataReadExposedInternal request
|
||||
func NewSyncDataReadExposedInternal() *SyncDataReadExposedInternal {
|
||||
return &SyncDataReadExposedInternal{}
|
||||
}
|
||||
|
||||
// Auditable returns all auditable/loggable parameters
|
||||
func (r SyncDataReadExposedInternal) Auditable() map[string]interface{} {
|
||||
return map[string]interface{}{
|
||||
"nodeID": r.NodeID,
|
||||
"moduleID": r.ModuleID,
|
||||
"lastSync": r.LastSync,
|
||||
"query": r.Query,
|
||||
"limit": r.Limit,
|
||||
"pageCursor": r.PageCursor,
|
||||
"sort": r.Sort,
|
||||
}
|
||||
}
|
||||
|
||||
// Auditable returns all auditable/loggable parameters
|
||||
func (r SyncDataReadExposedInternal) GetNodeID() uint64 {
|
||||
return r.NodeID
|
||||
}
|
||||
|
||||
// Auditable returns all auditable/loggable parameters
|
||||
func (r SyncDataReadExposedInternal) GetModuleID() uint64 {
|
||||
return r.ModuleID
|
||||
}
|
||||
|
||||
// Auditable returns all auditable/loggable parameters
|
||||
func (r SyncDataReadExposedInternal) GetLastSync() uint64 {
|
||||
return r.LastSync
|
||||
}
|
||||
|
||||
// Auditable returns all auditable/loggable parameters
|
||||
func (r SyncDataReadExposedInternal) GetQuery() string {
|
||||
return r.Query
|
||||
}
|
||||
|
||||
// Auditable returns all auditable/loggable parameters
|
||||
func (r SyncDataReadExposedInternal) GetLimit() uint {
|
||||
return r.Limit
|
||||
}
|
||||
|
||||
// Auditable returns all auditable/loggable parameters
|
||||
func (r SyncDataReadExposedInternal) GetPageCursor() string {
|
||||
return r.PageCursor
|
||||
}
|
||||
|
||||
// Auditable returns all auditable/loggable parameters
|
||||
func (r SyncDataReadExposedInternal) GetSort() string {
|
||||
return r.Sort
|
||||
}
|
||||
|
||||
// Fill processes request and fills internal variables
|
||||
func (r *SyncDataReadExposedInternal) Fill(req *http.Request) (err error) {
|
||||
|
||||
{
|
||||
// GET params
|
||||
tmp := req.URL.Query()
|
||||
|
||||
if val, ok := tmp["lastSync"]; ok && len(val) > 0 {
|
||||
r.LastSync, err = payload.ParseUint64(val[0]), nil
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
if val, ok := tmp["query"]; ok && len(val) > 0 {
|
||||
r.Query, err = val[0], nil
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
if val, ok := tmp["limit"]; ok && len(val) > 0 {
|
||||
r.Limit, err = payload.ParseUint(val[0]), nil
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
if val, ok := tmp["pageCursor"]; ok && len(val) > 0 {
|
||||
r.PageCursor, err = val[0], nil
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
if val, ok := tmp["sort"]; ok && len(val) > 0 {
|
||||
r.Sort, err = val[0], nil
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
var val string
|
||||
// path params
|
||||
|
||||
val = chi.URLParam(req, "nodeID")
|
||||
r.NodeID, err = payload.ParseUint64(val), nil
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
val = chi.URLParam(req, "moduleID")
|
||||
r.ModuleID, err = payload.ParseUint64(val), nil
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
// NewSyncDataReadExposedSocial request
|
||||
func NewSyncDataReadExposedSocial() *SyncDataReadExposedSocial {
|
||||
return &SyncDataReadExposedSocial{}
|
||||
}
|
||||
|
||||
// Auditable returns all auditable/loggable parameters
|
||||
func (r SyncDataReadExposedSocial) Auditable() map[string]interface{} {
|
||||
return map[string]interface{}{
|
||||
"nodeID": r.NodeID,
|
||||
"moduleID": r.ModuleID,
|
||||
"lastSync": r.LastSync,
|
||||
"query": r.Query,
|
||||
"limit": r.Limit,
|
||||
"pageCursor": r.PageCursor,
|
||||
"sort": r.Sort,
|
||||
}
|
||||
}
|
||||
|
||||
// Auditable returns all auditable/loggable parameters
|
||||
func (r SyncDataReadExposedSocial) GetNodeID() uint64 {
|
||||
return r.NodeID
|
||||
}
|
||||
|
||||
// Auditable returns all auditable/loggable parameters
|
||||
func (r SyncDataReadExposedSocial) GetModuleID() uint64 {
|
||||
return r.ModuleID
|
||||
}
|
||||
|
||||
// Auditable returns all auditable/loggable parameters
|
||||
func (r SyncDataReadExposedSocial) GetLastSync() uint64 {
|
||||
return r.LastSync
|
||||
}
|
||||
|
||||
// Auditable returns all auditable/loggable parameters
|
||||
func (r SyncDataReadExposedSocial) GetQuery() string {
|
||||
return r.Query
|
||||
}
|
||||
|
||||
// Auditable returns all auditable/loggable parameters
|
||||
func (r SyncDataReadExposedSocial) GetLimit() uint {
|
||||
return r.Limit
|
||||
}
|
||||
|
||||
// Auditable returns all auditable/loggable parameters
|
||||
func (r SyncDataReadExposedSocial) GetPageCursor() string {
|
||||
return r.PageCursor
|
||||
}
|
||||
|
||||
// Auditable returns all auditable/loggable parameters
|
||||
func (r SyncDataReadExposedSocial) GetSort() string {
|
||||
return r.Sort
|
||||
}
|
||||
|
||||
// Fill processes request and fills internal variables
|
||||
func (r *SyncDataReadExposedSocial) Fill(req *http.Request) (err error) {
|
||||
|
||||
{
|
||||
// GET params
|
||||
tmp := req.URL.Query()
|
||||
|
||||
if val, ok := tmp["lastSync"]; ok && len(val) > 0 {
|
||||
r.LastSync, err = payload.ParseUint64(val[0]), nil
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
if val, ok := tmp["query"]; ok && len(val) > 0 {
|
||||
r.Query, err = val[0], nil
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
if val, ok := tmp["limit"]; ok && len(val) > 0 {
|
||||
r.Limit, err = payload.ParseUint(val[0]), nil
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
if val, ok := tmp["pageCursor"]; ok && len(val) > 0 {
|
||||
r.PageCursor, err = val[0], nil
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
if val, ok := tmp["sort"]; ok && len(val) > 0 {
|
||||
r.Sort, err = val[0], nil
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
var val string
|
||||
// path params
|
||||
|
||||
val = chi.URLParam(req, "nodeID")
|
||||
r.NodeID, err = payload.ParseUint64(val), nil
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
val = chi.URLParam(req, "moduleID")
|
||||
r.ModuleID, err = payload.ParseUint64(val), nil
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return err
|
||||
}
|
||||
301
server/federation/rest/request/syncStructure.go
Normal file
301
server/federation/rest/request/syncStructure.go
Normal file
@@ -0,0 +1,301 @@
|
||||
package request
|
||||
|
||||
// This file is auto-generated.
|
||||
//
|
||||
// Changes to this file may cause incorrect behavior and will be lost if
|
||||
// the code is regenerated.
|
||||
//
|
||||
// Definitions file that controls how this file is generated:
|
||||
//
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"github.com/cortezaproject/corteza/server/pkg/payload"
|
||||
"github.com/go-chi/chi/v5"
|
||||
"io"
|
||||
"mime/multipart"
|
||||
"net/http"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// dummy vars to prevent
|
||||
// unused imports complain
|
||||
var (
|
||||
_ = chi.URLParam
|
||||
_ = multipart.ErrMessageTooLarge
|
||||
_ = payload.ParseUint64s
|
||||
_ = strings.ToLower
|
||||
_ = io.EOF
|
||||
_ = fmt.Errorf
|
||||
_ = json.NewEncoder
|
||||
)
|
||||
|
||||
type (
|
||||
// Internal API interface
|
||||
SyncStructureReadExposedInternal struct {
|
||||
// NodeID PATH parameter
|
||||
//
|
||||
// Node ID
|
||||
NodeID uint64 `json:",string"`
|
||||
|
||||
// LastSync GET parameter
|
||||
//
|
||||
// Last sync timestamp
|
||||
LastSync uint64 `json:",string"`
|
||||
|
||||
// Query GET parameter
|
||||
//
|
||||
// Search query
|
||||
Query string
|
||||
|
||||
// Limit GET parameter
|
||||
//
|
||||
// Limit
|
||||
Limit uint
|
||||
|
||||
// PageCursor GET parameter
|
||||
//
|
||||
// Page cursor
|
||||
PageCursor string
|
||||
|
||||
// Sort GET parameter
|
||||
//
|
||||
// Sort items
|
||||
Sort string
|
||||
}
|
||||
|
||||
SyncStructureReadExposedSocial struct {
|
||||
// NodeID PATH parameter
|
||||
//
|
||||
// Node ID
|
||||
NodeID uint64 `json:",string"`
|
||||
|
||||
// LastSync GET parameter
|
||||
//
|
||||
// Last sync timestamp
|
||||
LastSync uint64 `json:",string"`
|
||||
|
||||
// Query GET parameter
|
||||
//
|
||||
// Search query
|
||||
Query string
|
||||
|
||||
// Limit GET parameter
|
||||
//
|
||||
// Limit
|
||||
Limit uint
|
||||
|
||||
// PageCursor GET parameter
|
||||
//
|
||||
// Page cursor
|
||||
PageCursor string
|
||||
|
||||
// Sort GET parameter
|
||||
//
|
||||
// Sort items
|
||||
Sort string
|
||||
}
|
||||
)
|
||||
|
||||
// NewSyncStructureReadExposedInternal request
|
||||
func NewSyncStructureReadExposedInternal() *SyncStructureReadExposedInternal {
|
||||
return &SyncStructureReadExposedInternal{}
|
||||
}
|
||||
|
||||
// Auditable returns all auditable/loggable parameters
|
||||
func (r SyncStructureReadExposedInternal) Auditable() map[string]interface{} {
|
||||
return map[string]interface{}{
|
||||
"nodeID": r.NodeID,
|
||||
"lastSync": r.LastSync,
|
||||
"query": r.Query,
|
||||
"limit": r.Limit,
|
||||
"pageCursor": r.PageCursor,
|
||||
"sort": r.Sort,
|
||||
}
|
||||
}
|
||||
|
||||
// Auditable returns all auditable/loggable parameters
|
||||
func (r SyncStructureReadExposedInternal) GetNodeID() uint64 {
|
||||
return r.NodeID
|
||||
}
|
||||
|
||||
// Auditable returns all auditable/loggable parameters
|
||||
func (r SyncStructureReadExposedInternal) GetLastSync() uint64 {
|
||||
return r.LastSync
|
||||
}
|
||||
|
||||
// Auditable returns all auditable/loggable parameters
|
||||
func (r SyncStructureReadExposedInternal) GetQuery() string {
|
||||
return r.Query
|
||||
}
|
||||
|
||||
// Auditable returns all auditable/loggable parameters
|
||||
func (r SyncStructureReadExposedInternal) GetLimit() uint {
|
||||
return r.Limit
|
||||
}
|
||||
|
||||
// Auditable returns all auditable/loggable parameters
|
||||
func (r SyncStructureReadExposedInternal) GetPageCursor() string {
|
||||
return r.PageCursor
|
||||
}
|
||||
|
||||
// Auditable returns all auditable/loggable parameters
|
||||
func (r SyncStructureReadExposedInternal) GetSort() string {
|
||||
return r.Sort
|
||||
}
|
||||
|
||||
// Fill processes request and fills internal variables
|
||||
func (r *SyncStructureReadExposedInternal) Fill(req *http.Request) (err error) {
|
||||
|
||||
{
|
||||
// GET params
|
||||
tmp := req.URL.Query()
|
||||
|
||||
if val, ok := tmp["lastSync"]; ok && len(val) > 0 {
|
||||
r.LastSync, err = payload.ParseUint64(val[0]), nil
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
if val, ok := tmp["query"]; ok && len(val) > 0 {
|
||||
r.Query, err = val[0], nil
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
if val, ok := tmp["limit"]; ok && len(val) > 0 {
|
||||
r.Limit, err = payload.ParseUint(val[0]), nil
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
if val, ok := tmp["pageCursor"]; ok && len(val) > 0 {
|
||||
r.PageCursor, err = val[0], nil
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
if val, ok := tmp["sort"]; ok && len(val) > 0 {
|
||||
r.Sort, err = val[0], nil
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
var val string
|
||||
// path params
|
||||
|
||||
val = chi.URLParam(req, "nodeID")
|
||||
r.NodeID, err = payload.ParseUint64(val), nil
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
// NewSyncStructureReadExposedSocial request
|
||||
func NewSyncStructureReadExposedSocial() *SyncStructureReadExposedSocial {
|
||||
return &SyncStructureReadExposedSocial{}
|
||||
}
|
||||
|
||||
// Auditable returns all auditable/loggable parameters
|
||||
func (r SyncStructureReadExposedSocial) Auditable() map[string]interface{} {
|
||||
return map[string]interface{}{
|
||||
"nodeID": r.NodeID,
|
||||
"lastSync": r.LastSync,
|
||||
"query": r.Query,
|
||||
"limit": r.Limit,
|
||||
"pageCursor": r.PageCursor,
|
||||
"sort": r.Sort,
|
||||
}
|
||||
}
|
||||
|
||||
// Auditable returns all auditable/loggable parameters
|
||||
func (r SyncStructureReadExposedSocial) GetNodeID() uint64 {
|
||||
return r.NodeID
|
||||
}
|
||||
|
||||
// Auditable returns all auditable/loggable parameters
|
||||
func (r SyncStructureReadExposedSocial) GetLastSync() uint64 {
|
||||
return r.LastSync
|
||||
}
|
||||
|
||||
// Auditable returns all auditable/loggable parameters
|
||||
func (r SyncStructureReadExposedSocial) GetQuery() string {
|
||||
return r.Query
|
||||
}
|
||||
|
||||
// Auditable returns all auditable/loggable parameters
|
||||
func (r SyncStructureReadExposedSocial) GetLimit() uint {
|
||||
return r.Limit
|
||||
}
|
||||
|
||||
// Auditable returns all auditable/loggable parameters
|
||||
func (r SyncStructureReadExposedSocial) GetPageCursor() string {
|
||||
return r.PageCursor
|
||||
}
|
||||
|
||||
// Auditable returns all auditable/loggable parameters
|
||||
func (r SyncStructureReadExposedSocial) GetSort() string {
|
||||
return r.Sort
|
||||
}
|
||||
|
||||
// Fill processes request and fills internal variables
|
||||
func (r *SyncStructureReadExposedSocial) Fill(req *http.Request) (err error) {
|
||||
|
||||
{
|
||||
// GET params
|
||||
tmp := req.URL.Query()
|
||||
|
||||
if val, ok := tmp["lastSync"]; ok && len(val) > 0 {
|
||||
r.LastSync, err = payload.ParseUint64(val[0]), nil
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
if val, ok := tmp["query"]; ok && len(val) > 0 {
|
||||
r.Query, err = val[0], nil
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
if val, ok := tmp["limit"]; ok && len(val) > 0 {
|
||||
r.Limit, err = payload.ParseUint(val[0]), nil
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
if val, ok := tmp["pageCursor"]; ok && len(val) > 0 {
|
||||
r.PageCursor, err = val[0], nil
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
if val, ok := tmp["sort"]; ok && len(val) > 0 {
|
||||
r.Sort, err = val[0], nil
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
var val string
|
||||
// path params
|
||||
|
||||
val = chi.URLParam(req, "nodeID")
|
||||
r.NodeID, err = payload.ParseUint64(val), nil
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return err
|
||||
}
|
||||
30
server/federation/rest/router.go
Normal file
30
server/federation/rest/router.go
Normal file
@@ -0,0 +1,30 @@
|
||||
package rest
|
||||
|
||||
import (
|
||||
"github.com/cortezaproject/corteza/server/pkg/options"
|
||||
"github.com/go-chi/chi/v5"
|
||||
|
||||
"github.com/cortezaproject/corteza/server/federation/rest/handlers"
|
||||
"github.com/cortezaproject/corteza/server/pkg/auth"
|
||||
)
|
||||
|
||||
func MountRoutes(opts options.LimitOpt) func(r chi.Router) {
|
||||
return func(r chi.Router) {
|
||||
r.Group(func(r chi.Router) {
|
||||
handlers.NewNodeHandshake(NodeHandshake{}.New()).MountRoutes(r)
|
||||
})
|
||||
|
||||
// Protect all _private_ routes
|
||||
r.Group(func(r chi.Router) {
|
||||
r.Use(auth.HttpTokenValidator("api"))
|
||||
|
||||
handlers.NewPermissions(Permissions{}.New()).MountRoutes(r)
|
||||
|
||||
handlers.NewNode(Node{}.New()).MountRoutes(r)
|
||||
handlers.NewManageStructure((ManageStructure{}.New())).MountRoutes(r)
|
||||
|
||||
handlers.NewSyncData((SyncData{}.New(opts))).MountRoutes(r)
|
||||
handlers.NewSyncStructure((SyncStructure{}.New())).MountRoutes(r)
|
||||
})
|
||||
}
|
||||
}
|
||||
301
server/federation/rest/sync_data.go
Normal file
301
server/federation/rest/sync_data.go
Normal file
@@ -0,0 +1,301 @@
|
||||
package rest
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/cortezaproject/corteza/server/pkg/options"
|
||||
|
||||
cs "github.com/cortezaproject/corteza/server/compose/service"
|
||||
ct "github.com/cortezaproject/corteza/server/compose/types"
|
||||
"github.com/cortezaproject/corteza/server/federation/rest/request"
|
||||
"github.com/cortezaproject/corteza/server/federation/service"
|
||||
"github.com/cortezaproject/corteza/server/federation/types"
|
||||
"github.com/cortezaproject/corteza/server/pkg/auth"
|
||||
"github.com/cortezaproject/corteza/server/pkg/errors"
|
||||
"github.com/cortezaproject/corteza/server/pkg/federation"
|
||||
"github.com/cortezaproject/corteza/server/pkg/filter"
|
||||
ss "github.com/cortezaproject/corteza/server/system/service"
|
||||
st "github.com/cortezaproject/corteza/server/system/types"
|
||||
)
|
||||
|
||||
type (
|
||||
SyncData struct {
|
||||
opts options.LimitOpt
|
||||
}
|
||||
|
||||
recordPayload struct {
|
||||
*ct.Record
|
||||
|
||||
Records ct.RecordSet `json:"records,omitempty"`
|
||||
|
||||
CanUpdateRecord bool `json:"canUpdateRecord"`
|
||||
CanDeleteRecord bool `json:"canDeleteRecord"`
|
||||
}
|
||||
|
||||
listRecordResponse struct {
|
||||
Filter *ct.RecordFilter `json:"filter,omitempty"`
|
||||
Set *ct.RecordSet `json:"set"`
|
||||
}
|
||||
|
||||
listResponse struct {
|
||||
Set *responseSet `json:"set"`
|
||||
}
|
||||
|
||||
responseSet []*moduleResponse
|
||||
|
||||
moduleResponse struct {
|
||||
Type string `json:"type"`
|
||||
Rel string `json:"rel"`
|
||||
Href string `json:"href"`
|
||||
}
|
||||
)
|
||||
|
||||
func (SyncData) New(opts options.LimitOpt) *SyncData {
|
||||
return &SyncData{
|
||||
opts: opts,
|
||||
}
|
||||
}
|
||||
|
||||
func (ctrl SyncData) ReadExposedAll(ctx context.Context, r *request.SyncDataReadExposedAll) (interface{}, error) {
|
||||
// todo - handle paging
|
||||
var (
|
||||
err error
|
||||
node *types.Node
|
||||
)
|
||||
|
||||
if node, err = service.DefaultNode.FindBySharedNodeID(ctx, r.NodeID); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
s, _, err := service.DefaultExposedModule.Find(ctx, types.ExposedModuleFilter{NodeID: node.ID})
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
composeModuleList := make(map[uint64][]uint64, len(s))
|
||||
|
||||
err = s.Walk(func(em *types.ExposedModule) error {
|
||||
composeModuleList[em.ComposeModuleID] = []uint64{em.ComposeNamespaceID, em.ID}
|
||||
return nil
|
||||
})
|
||||
|
||||
if err != nil || len(composeModuleList) == 0 {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
recordQuery := buildLastSyncQuery(r.LastSync)
|
||||
responseSet := responseSet{}
|
||||
|
||||
for composeModuleID, idList := range composeModuleList {
|
||||
rf := ct.RecordFilter{
|
||||
NamespaceID: idList[0],
|
||||
ModuleID: composeModuleID,
|
||||
Query: recordQuery,
|
||||
Paging: filter.Paging{Limit: 1},
|
||||
}
|
||||
|
||||
// todo - handle error properly
|
||||
// @todo !!!
|
||||
if list, _, err := (cs.Record(cs.RecordOptions{LimitRecords: ctrl.opts.RecordCountPerModule})).Find(ctx, rf); err != nil || len(list) == 0 {
|
||||
continue
|
||||
}
|
||||
|
||||
// generate url
|
||||
moduleResponse := &moduleResponse{
|
||||
Type: "GET",
|
||||
Rel: "Federation Module",
|
||||
Href: fmt.Sprintf("/nodes/%d/modules/%d/records/", node.ID, idList[1]),
|
||||
}
|
||||
|
||||
responseSet = append(responseSet, moduleResponse)
|
||||
}
|
||||
|
||||
return listResponse{&responseSet}, nil
|
||||
}
|
||||
|
||||
// ReadExposedInternal fetches all the data - records (with paging)
|
||||
// for an exposed module in an internal format
|
||||
func (ctrl SyncData) ReadExposedInternal(ctx context.Context, r *request.SyncDataReadExposedInternal) (interface{}, error) {
|
||||
return func(w http.ResponseWriter, req *http.Request) {
|
||||
payload, err := ctrl.readExposed(ctx, r)
|
||||
|
||||
if err != nil {
|
||||
errors.ServeHTTP(w, req, err, false)
|
||||
return
|
||||
}
|
||||
|
||||
fEncoder := federation.NewEncoder(w, service.DefaultOptions)
|
||||
|
||||
err = fEncoder.Encode(payload, federation.CortezaInternalData)
|
||||
|
||||
if err != nil {
|
||||
errors.ServeHTTP(w, req, err, false)
|
||||
return
|
||||
}
|
||||
|
||||
return
|
||||
}, nil
|
||||
}
|
||||
|
||||
// ReadExposedInternal fetches all the data - records (with paging)
|
||||
// for an exposed module in activity streams format
|
||||
func (ctrl SyncData) ReadExposedSocial(ctx context.Context, r *request.SyncDataReadExposedSocial) (interface{}, error) {
|
||||
return func(w http.ResponseWriter, req *http.Request) {
|
||||
rr := request.SyncDataReadExposedInternal{
|
||||
NodeID: r.NodeID,
|
||||
ModuleID: r.ModuleID,
|
||||
LastSync: r.LastSync,
|
||||
Query: r.Query,
|
||||
Limit: r.Limit,
|
||||
PageCursor: r.PageCursor,
|
||||
Sort: r.Sort,
|
||||
}
|
||||
|
||||
payload, err := ctrl.readExposed(ctx, &rr)
|
||||
|
||||
if err != nil {
|
||||
errors.ServeHTTP(w, req, err, false)
|
||||
return
|
||||
}
|
||||
|
||||
fEncoder := federation.NewEncoder(w, service.DefaultOptions)
|
||||
|
||||
err = fEncoder.Encode(payload, federation.ActivityStreamsData)
|
||||
|
||||
if err != nil {
|
||||
errors.ServeHTTP(w, req, err, false)
|
||||
return
|
||||
}
|
||||
|
||||
return
|
||||
}, nil
|
||||
}
|
||||
|
||||
// readExposed fetches all the data - records (with paging) for an exposed module in an internal format
|
||||
func (ctrl SyncData) readExposed(ctx context.Context, r *request.SyncDataReadExposedInternal) (interface{}, error) {
|
||||
var (
|
||||
err error
|
||||
em *types.ExposedModule
|
||||
users st.UserSet
|
||||
node *types.Node
|
||||
)
|
||||
|
||||
if node, err = service.DefaultNode.FindBySharedNodeID(ctx, r.NodeID); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if em, err = service.DefaultExposedModule.FindByID(ctx, r.NodeID, r.ModuleID); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if users, _, err = ss.DefaultUser.Find(ctx, st.UserFilter{}); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
users, _ = users.Filter(func(u *st.User) (bool, error) {
|
||||
return strings.Contains(u.Handle, "federation_"), nil
|
||||
})
|
||||
|
||||
users = append(users, auth.FederationUser())
|
||||
|
||||
query := buildLastSyncQuery(r.LastSync)
|
||||
ignoredUsersQuery := buildIgnoredUsersQuery(users)
|
||||
|
||||
if ignoredUsersQuery != "" {
|
||||
if query != "" {
|
||||
query += fmt.Sprintf(" AND %s", ignoredUsersQuery)
|
||||
} else {
|
||||
query = ignoredUsersQuery
|
||||
}
|
||||
}
|
||||
|
||||
f := ct.RecordFilter{
|
||||
NamespaceID: em.ComposeNamespaceID,
|
||||
ModuleID: em.ComposeModuleID,
|
||||
Query: query,
|
||||
Deleted: filter.StateInclusive,
|
||||
}
|
||||
|
||||
if f.Paging, err = filter.NewPaging(r.Limit, r.PageCursor); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if f.Sorting, err = filter.NewSorting(r.Sort); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// @todo !!!
|
||||
list, f, err := (cs.Record(cs.RecordOptions{LimitRecords: ctrl.opts.RecordCountPerModule})).Find(ctx, f)
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// do the actual field filtering
|
||||
err = list.Walk(filterExposedFields(em))
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return federation.ListDataPayload{
|
||||
NodeID: node.ID,
|
||||
ModuleID: em.ID,
|
||||
Filter: &f,
|
||||
Set: &list,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func buildLastSyncQuery(ts uint64) string {
|
||||
if ts == 0 {
|
||||
return ""
|
||||
}
|
||||
|
||||
t := time.Unix(int64(ts), 0)
|
||||
|
||||
if t.IsZero() {
|
||||
return ""
|
||||
}
|
||||
|
||||
return fmt.Sprintf(
|
||||
"(updated_at >= '%s' OR created_at >= '%s' OR deleted_at >= '%s')",
|
||||
t.UTC().Format(time.RFC3339),
|
||||
t.UTC().Format(time.RFC3339),
|
||||
t.UTC().Format(time.RFC3339))
|
||||
}
|
||||
|
||||
func buildIgnoredUsersQuery(users st.UserSet) string {
|
||||
query := ""
|
||||
|
||||
if len(users) == 0 {
|
||||
return query
|
||||
}
|
||||
|
||||
for i, u := range users {
|
||||
query += fmt.Sprintf("createdBy != %d", u.ID)
|
||||
|
||||
if i < len(users)-1 {
|
||||
query += " AND "
|
||||
}
|
||||
}
|
||||
|
||||
return fmt.Sprintf("(%s)", query)
|
||||
}
|
||||
|
||||
// filterExposedFields omits the fields that are not exposed as defined
|
||||
// in the exposed module definition in store
|
||||
func filterExposedFields(em *types.ExposedModule) func(r *ct.Record) error {
|
||||
return func(r *ct.Record) error {
|
||||
var err error
|
||||
r.Values, err = r.Values.Filter(func(rv *ct.RecordValue) (bool, error) {
|
||||
return em.Fields.HasField(rv.Name)
|
||||
})
|
||||
|
||||
return err
|
||||
}
|
||||
}
|
||||
129
server/federation/rest/sync_structure.go
Normal file
129
server/federation/rest/sync_structure.go
Normal file
@@ -0,0 +1,129 @@
|
||||
package rest
|
||||
|
||||
import (
|
||||
"context"
|
||||
"net/http"
|
||||
|
||||
"github.com/cortezaproject/corteza/server/federation/rest/request"
|
||||
"github.com/cortezaproject/corteza/server/federation/service"
|
||||
"github.com/cortezaproject/corteza/server/federation/types"
|
||||
"github.com/cortezaproject/corteza/server/pkg/errors"
|
||||
"github.com/cortezaproject/corteza/server/pkg/federation"
|
||||
"github.com/cortezaproject/corteza/server/pkg/filter"
|
||||
)
|
||||
|
||||
type (
|
||||
SyncStructure struct{}
|
||||
)
|
||||
|
||||
func (SyncStructure) New() *SyncStructure {
|
||||
return &SyncStructure{}
|
||||
}
|
||||
|
||||
// ReadExposedInternal gets the exposed module info and serves
|
||||
// the internal Corteza format of the structure
|
||||
func (ctrl SyncStructure) ReadExposedInternal(ctx context.Context, r *request.SyncStructureReadExposedInternal) (interface{}, error) {
|
||||
return func(w http.ResponseWriter, req *http.Request) {
|
||||
var (
|
||||
err error
|
||||
ef federation.EncodingFormat = federation.CortezaInternalStructure
|
||||
)
|
||||
|
||||
w.Header().Add("Content-Type", "application/json")
|
||||
|
||||
fEncoder := federation.NewEncoder(w, service.DefaultOptions)
|
||||
|
||||
payload, err := ctrl.readExposedAll(ctx, r)
|
||||
|
||||
if err != nil {
|
||||
errors.ServeHTTP(w, req, err, false)
|
||||
return
|
||||
}
|
||||
|
||||
err = fEncoder.Encode(*payload, ef)
|
||||
|
||||
if err != nil {
|
||||
errors.ServeHTTP(w, req, err, false)
|
||||
return
|
||||
}
|
||||
|
||||
return
|
||||
}, nil
|
||||
}
|
||||
|
||||
// ReadExposedSocial gets the exposed module info and serves
|
||||
// the activity streams format of the structure
|
||||
func (ctrl SyncStructure) ReadExposedSocial(ctx context.Context, r *request.SyncStructureReadExposedSocial) (interface{}, error) {
|
||||
return func(w http.ResponseWriter, req *http.Request) {
|
||||
var (
|
||||
err error
|
||||
ef federation.EncodingFormat = federation.ActivityStreamsStructure
|
||||
)
|
||||
|
||||
w.Header().Add("Content-Type", "application/json")
|
||||
|
||||
fEncoder := federation.NewEncoder(w, service.DefaultOptions)
|
||||
|
||||
rr := request.SyncStructureReadExposedInternal{
|
||||
NodeID: r.NodeID,
|
||||
LastSync: r.LastSync,
|
||||
Query: r.Query,
|
||||
Limit: r.Limit,
|
||||
PageCursor: r.PageCursor,
|
||||
Sort: r.Sort,
|
||||
}
|
||||
|
||||
payload, err := ctrl.readExposedAll(ctx, &rr)
|
||||
|
||||
if err != nil {
|
||||
errors.ServeHTTP(w, req, err, false)
|
||||
return
|
||||
}
|
||||
|
||||
err = fEncoder.Encode(*payload, ef)
|
||||
|
||||
if err != nil {
|
||||
errors.ServeHTTP(w, req, err, false)
|
||||
return
|
||||
}
|
||||
|
||||
return
|
||||
}, nil
|
||||
}
|
||||
|
||||
// readExposedAll fetches the exposed modules for the specific node
|
||||
func (ctrl SyncStructure) readExposedAll(ctx context.Context, r *request.SyncStructureReadExposedInternal) (*federation.ListStructurePayload, error) {
|
||||
var (
|
||||
err error
|
||||
node *types.Node
|
||||
)
|
||||
|
||||
if node, err = service.DefaultNode.FindBySharedNodeID(ctx, r.NodeID); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
f := types.ExposedModuleFilter{
|
||||
NodeID: node.ID,
|
||||
LastSync: r.LastSync,
|
||||
}
|
||||
|
||||
if f.Paging, err = filter.NewPaging(r.Limit, r.PageCursor); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if f.Sorting, err = filter.NewSorting(r.Sort); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
list, f, err := (service.ExposedModule()).Find(ctx, f)
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &federation.ListStructurePayload{
|
||||
NodeID: node.ID,
|
||||
Filter: &f,
|
||||
Set: &list,
|
||||
}, nil
|
||||
}
|
||||
622
server/federation/service/access_control.gen.go
generated
Normal file
622
server/federation/service/access_control.gen.go
generated
Normal file
@@ -0,0 +1,622 @@
|
||||
package service
|
||||
|
||||
// This file is auto-generated.
|
||||
//
|
||||
// Changes to this file may cause incorrect behavior and will be lost if
|
||||
// the code is regenerated.
|
||||
//
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"github.com/cortezaproject/corteza/server/federation/types"
|
||||
"github.com/cortezaproject/corteza/server/pkg/actionlog"
|
||||
internalAuth "github.com/cortezaproject/corteza/server/pkg/auth"
|
||||
"github.com/cortezaproject/corteza/server/pkg/rbac"
|
||||
"github.com/cortezaproject/corteza/server/store"
|
||||
systemTypes "github.com/cortezaproject/corteza/server/system/types"
|
||||
"github.com/spf13/cast"
|
||||
"strings"
|
||||
)
|
||||
|
||||
type (
|
||||
rbacService interface {
|
||||
Can(rbac.Session, string, rbac.Resource) bool
|
||||
Trace(rbac.Session, string, rbac.Resource) *rbac.Trace
|
||||
Grant(context.Context, ...*rbac.Rule) error
|
||||
FindRulesByRoleID(roleID uint64) (rr rbac.RuleSet)
|
||||
}
|
||||
|
||||
accessControl struct {
|
||||
actionlog actionlog.Recorder
|
||||
|
||||
store store.Storer
|
||||
rbac rbacService
|
||||
}
|
||||
)
|
||||
|
||||
func AccessControl(s store.Storer) *accessControl {
|
||||
return &accessControl{
|
||||
store: s,
|
||||
rbac: rbac.Global(),
|
||||
actionlog: DefaultActionlog,
|
||||
}
|
||||
}
|
||||
|
||||
func (svc accessControl) can(ctx context.Context, op string, res rbac.Resource) bool {
|
||||
return svc.rbac.Can(rbac.ContextToSession(ctx), op, res)
|
||||
}
|
||||
|
||||
// Effective returns a list of effective permissions for all given resource
|
||||
//
|
||||
// This function is auto-generated
|
||||
func (svc accessControl) Effective(ctx context.Context, rr ...rbac.Resource) (ee rbac.EffectiveSet) {
|
||||
for _, res := range rr {
|
||||
r := res.RbacResource()
|
||||
for op := range rbacResourceOperations(r) {
|
||||
ee.Push(r, op, svc.can(ctx, op, res))
|
||||
}
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
// Evaluate returns a list of permissions evaluated for the given user/roles combo
|
||||
//
|
||||
// This function is auto-generated
|
||||
func (svc accessControl) Trace(ctx context.Context, userID uint64, roles []uint64, rr ...string) (ee []*rbac.Trace, err error) {
|
||||
// Reusing the grant permission since this is who the feature is for
|
||||
if !svc.CanGrant(ctx) {
|
||||
// @todo should be altered to check grant permissions PER resource
|
||||
return nil, AccessControlErrNotAllowedToSetPermissions()
|
||||
}
|
||||
|
||||
var (
|
||||
resource rbac.Resource
|
||||
resources []rbac.Resource
|
||||
members systemTypes.RoleMemberSet
|
||||
)
|
||||
if len(rr) > 0 {
|
||||
resources = make([]rbac.Resource, 0, len(rr))
|
||||
for _, r := range rr {
|
||||
if err = rbacResourceValidator(r); err != nil {
|
||||
return nil, fmt.Errorf("can not use resource %q: %w", r, err)
|
||||
}
|
||||
|
||||
resource, err = svc.resourceLoader(ctx, r)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
resources = append(resources, resource)
|
||||
}
|
||||
} else {
|
||||
resources = svc.Resources()
|
||||
}
|
||||
|
||||
// User ID specified, load its roles
|
||||
if userID != 0 {
|
||||
if len(roles) > 0 {
|
||||
// should be prevented on the client
|
||||
return nil, fmt.Errorf("userID and roles are mutually exclusive")
|
||||
}
|
||||
|
||||
members, _, err = store.SearchRoleMembers(ctx, svc.store, systemTypes.RoleMemberFilter{Resource: fmt.Sprintf("corteza::system:user/%d", userID)})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
for _, m := range members {
|
||||
roles = append(roles, m.RoleID)
|
||||
}
|
||||
|
||||
for _, r := range internalAuth.AuthenticatedRoles() {
|
||||
roles = append(roles, r.ID)
|
||||
}
|
||||
}
|
||||
|
||||
if len(roles) == 0 {
|
||||
// should be prevented on the client
|
||||
return nil, fmt.Errorf("no roles specified")
|
||||
}
|
||||
|
||||
session := rbac.ParamsToSession(ctx, userID, roles...)
|
||||
for _, res := range resources {
|
||||
r := res.RbacResource()
|
||||
for op := range rbacResourceOperations(r) {
|
||||
ee = append(ee, svc.rbac.Trace(session, op, res))
|
||||
}
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
// Resources returns list of resources
|
||||
//
|
||||
// This function is auto-generated
|
||||
func (svc accessControl) Resources() []rbac.Resource {
|
||||
return []rbac.Resource{
|
||||
rbac.NewResource(types.NodeRbacResource(0)),
|
||||
rbac.NewResource(types.ExposedModuleRbacResource(0, 0)),
|
||||
rbac.NewResource(types.SharedModuleRbacResource(0, 0)),
|
||||
rbac.NewResource(types.ComponentRbacResource()),
|
||||
}
|
||||
}
|
||||
|
||||
// List returns list of operations on all resources
|
||||
//
|
||||
// This function is auto-generated
|
||||
func (svc accessControl) List() (out []map[string]string) {
|
||||
def := []map[string]string{
|
||||
{
|
||||
"type": types.NodeResourceType,
|
||||
"any": types.NodeRbacResource(0),
|
||||
"op": "manage",
|
||||
},
|
||||
{
|
||||
"type": types.NodeResourceType,
|
||||
"any": types.NodeRbacResource(0),
|
||||
"op": "module.create",
|
||||
},
|
||||
{
|
||||
"type": types.ExposedModuleResourceType,
|
||||
"any": types.ExposedModuleRbacResource(0, 0),
|
||||
"op": "manage",
|
||||
},
|
||||
{
|
||||
"type": types.SharedModuleResourceType,
|
||||
"any": types.SharedModuleRbacResource(0, 0),
|
||||
"op": "map",
|
||||
},
|
||||
{
|
||||
"type": types.ComponentResourceType,
|
||||
"any": types.ComponentRbacResource(),
|
||||
"op": "grant",
|
||||
},
|
||||
{
|
||||
"type": types.ComponentResourceType,
|
||||
"any": types.ComponentRbacResource(),
|
||||
"op": "pair",
|
||||
},
|
||||
{
|
||||
"type": types.ComponentResourceType,
|
||||
"any": types.ComponentRbacResource(),
|
||||
"op": "settings.read",
|
||||
},
|
||||
{
|
||||
"type": types.ComponentResourceType,
|
||||
"any": types.ComponentRbacResource(),
|
||||
"op": "settings.manage",
|
||||
},
|
||||
{
|
||||
"type": types.ComponentResourceType,
|
||||
"any": types.ComponentRbacResource(),
|
||||
"op": "node.create",
|
||||
},
|
||||
{
|
||||
"type": types.ComponentResourceType,
|
||||
"any": types.ComponentRbacResource(),
|
||||
"op": "nodes.search",
|
||||
},
|
||||
}
|
||||
|
||||
func(svc interface{}) {
|
||||
if svc, is := svc.(interface{}).(interface{ list() []map[string]string }); is {
|
||||
def = append(def, svc.list()...)
|
||||
}
|
||||
}(svc)
|
||||
|
||||
return def
|
||||
}
|
||||
|
||||
// Grant applies one or more RBAC rules
|
||||
//
|
||||
// This function is auto-generated
|
||||
func (svc accessControl) Grant(ctx context.Context, rr ...*rbac.Rule) error {
|
||||
if !svc.CanGrant(ctx) {
|
||||
// @todo should be altered to check grant permissions PER resource
|
||||
return AccessControlErrNotAllowedToSetPermissions()
|
||||
}
|
||||
|
||||
for _, r := range rr {
|
||||
err := rbacResourceValidator(r.Resource, r.Operation)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
if err := svc.rbac.Grant(ctx, rr...); err != nil {
|
||||
return AccessControlErrGeneric().Wrap(err)
|
||||
}
|
||||
|
||||
svc.logGrants(ctx, rr)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// This function is auto-generated
|
||||
func (svc accessControl) logGrants(ctx context.Context, rr []*rbac.Rule) {
|
||||
if svc.actionlog == nil {
|
||||
return
|
||||
}
|
||||
|
||||
for _, r := range rr {
|
||||
g := AccessControlActionGrant(&accessControlActionProps{r})
|
||||
g.log = r.String()
|
||||
g.resource = r.Resource
|
||||
|
||||
svc.actionlog.Record(ctx, g.ToAction())
|
||||
}
|
||||
}
|
||||
|
||||
// FindRules find all rules based on filters
|
||||
//
|
||||
// This function is auto-generated
|
||||
func (svc accessControl) FindRules(ctx context.Context, roleID uint64, rr ...string) (out rbac.RuleSet, err error) {
|
||||
if !svc.CanGrant(ctx) {
|
||||
return nil, AccessControlErrNotAllowedToSetPermissions()
|
||||
}
|
||||
|
||||
out, err = svc.FindRulesByRoleID(ctx, roleID)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
var resources []rbac.Resource
|
||||
if len(rr) > 0 {
|
||||
resources = make([]rbac.Resource, 0, len(rr))
|
||||
for _, r := range rr {
|
||||
if err = rbacResourceValidator(r); err != nil {
|
||||
return nil, fmt.Errorf("can not use resource %q: %w", r, err)
|
||||
}
|
||||
|
||||
resources = append(resources, rbac.NewResource(r))
|
||||
}
|
||||
} else {
|
||||
resources = svc.Resources()
|
||||
}
|
||||
|
||||
return out.FilterResource(resources...), nil
|
||||
}
|
||||
|
||||
// FindRulesByRoleID find all rules for a specific role
|
||||
//
|
||||
// This function is auto-generated
|
||||
func (svc accessControl) FindRulesByRoleID(ctx context.Context, roleID uint64) (rbac.RuleSet, error) {
|
||||
if !svc.CanGrant(ctx) {
|
||||
return nil, AccessControlErrNotAllowedToSetPermissions()
|
||||
}
|
||||
|
||||
return svc.rbac.FindRulesByRoleID(roleID), nil
|
||||
}
|
||||
|
||||
// CanManageNode checks if current user can manage federation node
|
||||
//
|
||||
// This function is auto-generated
|
||||
func (svc accessControl) CanManageNode(ctx context.Context, r *types.Node) bool {
|
||||
return svc.can(ctx, "manage", r)
|
||||
}
|
||||
|
||||
// CanCreateModuleOnNode checks if current user can create shared module
|
||||
//
|
||||
// This function is auto-generated
|
||||
func (svc accessControl) CanCreateModuleOnNode(ctx context.Context, r *types.Node) bool {
|
||||
return svc.can(ctx, "module.create", r)
|
||||
}
|
||||
|
||||
// CanManageExposedModule checks if current user can manage exposed module module
|
||||
//
|
||||
// This function is auto-generated
|
||||
func (svc accessControl) CanManageExposedModule(ctx context.Context, r *types.ExposedModule) bool {
|
||||
return svc.can(ctx, "manage", r)
|
||||
}
|
||||
|
||||
// CanMapSharedModule checks if current user can map shared module
|
||||
//
|
||||
// This function is auto-generated
|
||||
func (svc accessControl) CanMapSharedModule(ctx context.Context, r *types.SharedModule) bool {
|
||||
return svc.can(ctx, "map", r)
|
||||
}
|
||||
|
||||
// CanGrant checks if current user can manage federation permissions
|
||||
//
|
||||
// This function is auto-generated
|
||||
func (svc accessControl) CanGrant(ctx context.Context) bool {
|
||||
r := &types.Component{}
|
||||
return svc.can(ctx, "grant", r)
|
||||
}
|
||||
|
||||
// CanPair checks if current user can pair federation nodes
|
||||
//
|
||||
// This function is auto-generated
|
||||
func (svc accessControl) CanPair(ctx context.Context) bool {
|
||||
r := &types.Component{}
|
||||
return svc.can(ctx, "pair", r)
|
||||
}
|
||||
|
||||
// CanReadSettings checks if current user can read settings
|
||||
//
|
||||
// This function is auto-generated
|
||||
func (svc accessControl) CanReadSettings(ctx context.Context) bool {
|
||||
r := &types.Component{}
|
||||
return svc.can(ctx, "settings.read", r)
|
||||
}
|
||||
|
||||
// CanManageSettings checks if current user can manage settings
|
||||
//
|
||||
// This function is auto-generated
|
||||
func (svc accessControl) CanManageSettings(ctx context.Context) bool {
|
||||
r := &types.Component{}
|
||||
return svc.can(ctx, "settings.manage", r)
|
||||
}
|
||||
|
||||
// CanCreateNode checks if current user can create new federation node
|
||||
//
|
||||
// This function is auto-generated
|
||||
func (svc accessControl) CanCreateNode(ctx context.Context) bool {
|
||||
r := &types.Component{}
|
||||
return svc.can(ctx, "node.create", r)
|
||||
}
|
||||
|
||||
// CanSearchNodes checks if current user can list, search or filter federation nodes
|
||||
//
|
||||
// This function is auto-generated
|
||||
func (svc accessControl) CanSearchNodes(ctx context.Context) bool {
|
||||
r := &types.Component{}
|
||||
return svc.can(ctx, "nodes.search", r)
|
||||
}
|
||||
|
||||
// rbacResourceValidator validates known component's resource by routing it to the appropriate validator
|
||||
//
|
||||
// This function is auto-generated
|
||||
func rbacResourceValidator(r string, oo ...string) error {
|
||||
switch rbac.ResourceType(r) {
|
||||
case types.NodeResourceType:
|
||||
return rbacNodeResourceValidator(r, oo...)
|
||||
case types.ExposedModuleResourceType:
|
||||
return rbacExposedModuleResourceValidator(r, oo...)
|
||||
case types.SharedModuleResourceType:
|
||||
return rbacSharedModuleResourceValidator(r, oo...)
|
||||
case types.ComponentResourceType:
|
||||
return rbacComponentResourceValidator(r, oo...)
|
||||
}
|
||||
|
||||
return fmt.Errorf("unknown resource type %q", r)
|
||||
}
|
||||
|
||||
// resourceLoader loads resource from store
|
||||
//
|
||||
// # Notes
|
||||
// Function assumes existence of loader functions for all resource types
|
||||
//
|
||||
// This function is auto-generated
|
||||
func (svc accessControl) resourceLoader(ctx context.Context, resource string) (rbac.Resource, error) {
|
||||
var (
|
||||
hasWildcard = false
|
||||
resourceType, ids = rbac.ParseResourceID(resource)
|
||||
)
|
||||
|
||||
for _, id := range ids {
|
||||
if id == 0 {
|
||||
hasWildcard = true
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
switch rbac.ResourceType(resourceType) {
|
||||
case types.NodeResourceType:
|
||||
if hasWildcard {
|
||||
return rbac.NewResource(types.NodeRbacResource(ids[0])), nil
|
||||
}
|
||||
|
||||
return loadNode(ctx, svc.store, ids[0])
|
||||
case types.ExposedModuleResourceType:
|
||||
if hasWildcard {
|
||||
return rbac.NewResource(types.ExposedModuleRbacResource(ids[0], ids[1])), nil
|
||||
}
|
||||
|
||||
return loadExposedModule(ctx, svc.store, ids[0], ids[1])
|
||||
case types.SharedModuleResourceType:
|
||||
if hasWildcard {
|
||||
return rbac.NewResource(types.SharedModuleRbacResource(ids[0], ids[1])), nil
|
||||
}
|
||||
|
||||
return loadSharedModule(ctx, svc.store, ids[0], ids[1])
|
||||
case types.ComponentResourceType:
|
||||
return &types.Component{}, nil
|
||||
}
|
||||
|
||||
_ = ids
|
||||
return nil, fmt.Errorf("unknown resource type %q", resourceType)
|
||||
}
|
||||
|
||||
// rbacResourceOperations returns defined operations for a requested resource
|
||||
//
|
||||
// This function is auto-generated
|
||||
func rbacResourceOperations(r string) map[string]bool {
|
||||
switch rbac.ResourceType(r) {
|
||||
case types.NodeResourceType:
|
||||
return map[string]bool{
|
||||
"manage": true,
|
||||
"module.create": true,
|
||||
}
|
||||
case types.ExposedModuleResourceType:
|
||||
return map[string]bool{
|
||||
"manage": true,
|
||||
}
|
||||
case types.SharedModuleResourceType:
|
||||
return map[string]bool{
|
||||
"map": true,
|
||||
}
|
||||
case types.ComponentResourceType:
|
||||
return map[string]bool{
|
||||
"grant": true,
|
||||
"pair": true,
|
||||
"settings.read": true,
|
||||
"settings.manage": true,
|
||||
"node.create": true,
|
||||
"nodes.search": true,
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// rbacNodeResourceValidator checks validity of RBAC resource and operations
|
||||
//
|
||||
// # Notes
|
||||
// Can be called without operations to check for validity of resource string only
|
||||
//
|
||||
// This function is auto-generated
|
||||
func rbacNodeResourceValidator(r string, oo ...string) error {
|
||||
if !strings.HasPrefix(r, types.NodeResourceType) {
|
||||
// expecting resource to always include path
|
||||
return fmt.Errorf("invalid resource type")
|
||||
}
|
||||
|
||||
defOps := rbacResourceOperations(r)
|
||||
for _, o := range oo {
|
||||
if !defOps[o] {
|
||||
return fmt.Errorf("invalid operation '%s' for node resource", o)
|
||||
}
|
||||
}
|
||||
|
||||
const sep = "/"
|
||||
var (
|
||||
pp = strings.Split(strings.Trim(r[len(types.NodeResourceType):], sep), sep)
|
||||
prc = []string{
|
||||
"ID",
|
||||
}
|
||||
)
|
||||
|
||||
if len(pp) != len(prc) {
|
||||
return fmt.Errorf("invalid resource path structure")
|
||||
}
|
||||
|
||||
for i := 0; i < len(pp); i++ {
|
||||
if pp[i] != "*" {
|
||||
if i > 0 && pp[i-1] == "*" {
|
||||
return fmt.Errorf("invalid path wildcard level (%d) for node resource", i)
|
||||
}
|
||||
|
||||
if _, err := cast.ToUint64E(pp[i]); err != nil {
|
||||
return fmt.Errorf("invalid reference for %s: '%s'", prc[i], pp[i])
|
||||
}
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// rbacExposedModuleResourceValidator checks validity of RBAC resource and operations
|
||||
//
|
||||
// # Notes
|
||||
// Can be called without operations to check for validity of resource string only
|
||||
//
|
||||
// This function is auto-generated
|
||||
func rbacExposedModuleResourceValidator(r string, oo ...string) error {
|
||||
if !strings.HasPrefix(r, types.ExposedModuleResourceType) {
|
||||
// expecting resource to always include path
|
||||
return fmt.Errorf("invalid resource type")
|
||||
}
|
||||
|
||||
defOps := rbacResourceOperations(r)
|
||||
for _, o := range oo {
|
||||
if !defOps[o] {
|
||||
return fmt.Errorf("invalid operation '%s' for exposedModule resource", o)
|
||||
}
|
||||
}
|
||||
|
||||
const sep = "/"
|
||||
var (
|
||||
pp = strings.Split(strings.Trim(r[len(types.ExposedModuleResourceType):], sep), sep)
|
||||
prc = []string{
|
||||
"NodeID",
|
||||
"ID",
|
||||
}
|
||||
)
|
||||
|
||||
if len(pp) != len(prc) {
|
||||
return fmt.Errorf("invalid resource path structure")
|
||||
}
|
||||
|
||||
for i := 0; i < len(pp); i++ {
|
||||
if pp[i] != "*" {
|
||||
if i > 0 && pp[i-1] == "*" {
|
||||
return fmt.Errorf("invalid path wildcard level (%d) for exposedModule resource", i)
|
||||
}
|
||||
|
||||
if _, err := cast.ToUint64E(pp[i]); err != nil {
|
||||
return fmt.Errorf("invalid reference for %s: '%s'", prc[i], pp[i])
|
||||
}
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// rbacSharedModuleResourceValidator checks validity of RBAC resource and operations
|
||||
//
|
||||
// # Notes
|
||||
// Can be called without operations to check for validity of resource string only
|
||||
//
|
||||
// This function is auto-generated
|
||||
func rbacSharedModuleResourceValidator(r string, oo ...string) error {
|
||||
if !strings.HasPrefix(r, types.SharedModuleResourceType) {
|
||||
// expecting resource to always include path
|
||||
return fmt.Errorf("invalid resource type")
|
||||
}
|
||||
|
||||
defOps := rbacResourceOperations(r)
|
||||
for _, o := range oo {
|
||||
if !defOps[o] {
|
||||
return fmt.Errorf("invalid operation '%s' for sharedModule resource", o)
|
||||
}
|
||||
}
|
||||
|
||||
const sep = "/"
|
||||
var (
|
||||
pp = strings.Split(strings.Trim(r[len(types.SharedModuleResourceType):], sep), sep)
|
||||
prc = []string{
|
||||
"NodeID",
|
||||
"ID",
|
||||
}
|
||||
)
|
||||
|
||||
if len(pp) != len(prc) {
|
||||
return fmt.Errorf("invalid resource path structure")
|
||||
}
|
||||
|
||||
for i := 0; i < len(pp); i++ {
|
||||
if pp[i] != "*" {
|
||||
if i > 0 && pp[i-1] == "*" {
|
||||
return fmt.Errorf("invalid path wildcard level (%d) for sharedModule resource", i)
|
||||
}
|
||||
|
||||
if _, err := cast.ToUint64E(pp[i]); err != nil {
|
||||
return fmt.Errorf("invalid reference for %s: '%s'", prc[i], pp[i])
|
||||
}
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// rbacComponentResourceValidator checks validity of RBAC resource and operations
|
||||
//
|
||||
// # Notes
|
||||
// Can be called without operations to check for validity of resource string only
|
||||
//
|
||||
// This function is auto-generated
|
||||
func rbacComponentResourceValidator(r string, oo ...string) error {
|
||||
if r != types.ComponentResourceType+"/" {
|
||||
// expecting resource to always include path
|
||||
return fmt.Errorf("invalid component resource, expecting " + types.ComponentResourceType + "/")
|
||||
}
|
||||
|
||||
defOps := rbacResourceOperations(r)
|
||||
for _, o := range oo {
|
||||
if !defOps[o] {
|
||||
return fmt.Errorf("invalid operation '%s' for federation component resource", o)
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
284
server/federation/service/access_control_actions.gen.go
generated
Normal file
284
server/federation/service/access_control_actions.gen.go
generated
Normal file
@@ -0,0 +1,284 @@
|
||||
package service
|
||||
|
||||
// This file is auto-generated.
|
||||
//
|
||||
// Changes to this file may cause incorrect behavior and will be lost if
|
||||
// the code is regenerated.
|
||||
//
|
||||
// Definitions file that controls how this file is generated:
|
||||
// federation/service/access_control_actions.yaml
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"github.com/cortezaproject/corteza/server/pkg/actionlog"
|
||||
"github.com/cortezaproject/corteza/server/pkg/errors"
|
||||
"github.com/cortezaproject/corteza/server/pkg/locale"
|
||||
"github.com/cortezaproject/corteza/server/pkg/rbac"
|
||||
"strings"
|
||||
"time"
|
||||
)
|
||||
|
||||
type (
|
||||
accessControlActionProps struct {
|
||||
rule *rbac.Rule
|
||||
}
|
||||
|
||||
accessControlAction struct {
|
||||
timestamp time.Time
|
||||
resource string
|
||||
action string
|
||||
log string
|
||||
severity actionlog.Severity
|
||||
|
||||
// prefix for error when action fails
|
||||
errorMessage string
|
||||
|
||||
props *accessControlActionProps
|
||||
}
|
||||
|
||||
accessControlLogMetaKey struct{}
|
||||
accessControlPropsMetaKey struct{}
|
||||
)
|
||||
|
||||
var (
|
||||
// just a placeholder to cover template cases w/o fmt package use
|
||||
_ = fmt.Println
|
||||
)
|
||||
|
||||
// *********************************************************************************************************************
|
||||
// *********************************************************************************************************************
|
||||
// Props methods
|
||||
// setRule updates accessControlActionProps's rule
|
||||
//
|
||||
// This function is auto-generated.
|
||||
func (p *accessControlActionProps) setRule(rule *rbac.Rule) *accessControlActionProps {
|
||||
p.rule = rule
|
||||
return p
|
||||
}
|
||||
|
||||
// Serialize converts accessControlActionProps to actionlog.Meta
|
||||
//
|
||||
// This function is auto-generated.
|
||||
func (p accessControlActionProps) Serialize() actionlog.Meta {
|
||||
var (
|
||||
m = make(actionlog.Meta)
|
||||
)
|
||||
|
||||
if p.rule != nil {
|
||||
m.Set("rule.operation", p.rule.Operation, true)
|
||||
m.Set("rule.roleID", p.rule.RoleID, true)
|
||||
m.Set("rule.access", p.rule.Access, true)
|
||||
m.Set("rule.resource", p.rule.Resource, true)
|
||||
}
|
||||
|
||||
return m
|
||||
}
|
||||
|
||||
// tr translates string and replaces meta value placeholder with values
|
||||
//
|
||||
// This function is auto-generated.
|
||||
func (p accessControlActionProps) Format(in string, err error) string {
|
||||
var (
|
||||
pairs = []string{"{{err}}"}
|
||||
// first non-empty string
|
||||
fns = func(ii ...interface{}) string {
|
||||
for _, i := range ii {
|
||||
if s := fmt.Sprintf("%v", i); len(s) > 0 {
|
||||
return s
|
||||
}
|
||||
}
|
||||
|
||||
return ""
|
||||
}
|
||||
)
|
||||
|
||||
if err != nil {
|
||||
pairs = append(pairs, err.Error())
|
||||
} else {
|
||||
pairs = append(pairs, "nil")
|
||||
}
|
||||
|
||||
if p.rule != nil {
|
||||
// replacement for "{{rule}}" (in order how fields are defined)
|
||||
pairs = append(
|
||||
pairs,
|
||||
"{{rule}}",
|
||||
fns(
|
||||
p.rule.Operation,
|
||||
p.rule.RoleID,
|
||||
p.rule.Access,
|
||||
p.rule.Resource,
|
||||
),
|
||||
)
|
||||
pairs = append(pairs, "{{rule.operation}}", fns(p.rule.Operation))
|
||||
pairs = append(pairs, "{{rule.roleID}}", fns(p.rule.RoleID))
|
||||
pairs = append(pairs, "{{rule.access}}", fns(p.rule.Access))
|
||||
pairs = append(pairs, "{{rule.resource}}", fns(p.rule.Resource))
|
||||
}
|
||||
return strings.NewReplacer(pairs...).Replace(in)
|
||||
}
|
||||
|
||||
// *********************************************************************************************************************
|
||||
// *********************************************************************************************************************
|
||||
// Action methods
|
||||
|
||||
// String returns loggable description as string
|
||||
//
|
||||
// This function is auto-generated.
|
||||
func (a *accessControlAction) String() string {
|
||||
var props = &accessControlActionProps{}
|
||||
|
||||
if a.props != nil {
|
||||
props = a.props
|
||||
}
|
||||
|
||||
return props.Format(a.log, nil)
|
||||
}
|
||||
|
||||
func (e *accessControlAction) ToAction() *actionlog.Action {
|
||||
return &actionlog.Action{
|
||||
Resource: e.resource,
|
||||
Action: e.action,
|
||||
Severity: e.severity,
|
||||
Description: e.String(),
|
||||
Meta: e.props.Serialize(),
|
||||
}
|
||||
}
|
||||
|
||||
// *********************************************************************************************************************
|
||||
// *********************************************************************************************************************
|
||||
// Action constructors
|
||||
|
||||
// AccessControlActionGrant returns "federation:access_control.grant" action
|
||||
//
|
||||
// This function is auto-generated.
|
||||
func AccessControlActionGrant(props ...*accessControlActionProps) *accessControlAction {
|
||||
a := &accessControlAction{
|
||||
timestamp: time.Now(),
|
||||
resource: "federation:access_control",
|
||||
action: "grant",
|
||||
log: "grant",
|
||||
severity: actionlog.Notice,
|
||||
}
|
||||
|
||||
if len(props) > 0 {
|
||||
a.props = props[0]
|
||||
}
|
||||
|
||||
return a
|
||||
}
|
||||
|
||||
// *********************************************************************************************************************
|
||||
// *********************************************************************************************************************
|
||||
// Error constructors
|
||||
|
||||
// AccessControlErrGeneric returns "federation:access_control.generic" as *errors.Error
|
||||
//
|
||||
// This function is auto-generated.
|
||||
func AccessControlErrGeneric(mm ...*accessControlActionProps) *errors.Error {
|
||||
var p = &accessControlActionProps{}
|
||||
if len(mm) > 0 {
|
||||
p = mm[0]
|
||||
}
|
||||
|
||||
var e = errors.New(
|
||||
errors.KindInternal,
|
||||
|
||||
p.Format("failed to complete request due to internal error", nil),
|
||||
|
||||
errors.Meta("type", "generic"),
|
||||
errors.Meta("resource", "federation:access_control"),
|
||||
|
||||
// action log entry; no formatting, it will be applied inside recordAction fn.
|
||||
errors.Meta(accessControlLogMetaKey{}, "{err}"),
|
||||
errors.Meta(accessControlPropsMetaKey{}, p),
|
||||
|
||||
// translation namespace & key
|
||||
errors.Meta(locale.ErrorMetaNamespace{}, "federation"),
|
||||
errors.Meta(locale.ErrorMetaKey{}, "access-control.errors.generic"),
|
||||
|
||||
errors.StackSkip(1),
|
||||
)
|
||||
|
||||
if len(mm) > 0 {
|
||||
}
|
||||
|
||||
return e
|
||||
}
|
||||
|
||||
// AccessControlErrNotAllowedToSetPermissions returns "federation:access_control.notAllowedToSetPermissions" as *errors.Error
|
||||
//
|
||||
// This function is auto-generated.
|
||||
func AccessControlErrNotAllowedToSetPermissions(mm ...*accessControlActionProps) *errors.Error {
|
||||
var p = &accessControlActionProps{}
|
||||
if len(mm) > 0 {
|
||||
p = mm[0]
|
||||
}
|
||||
|
||||
var e = errors.New(
|
||||
errors.KindInternal,
|
||||
|
||||
p.Format("not allowed to set permissions", nil),
|
||||
|
||||
errors.Meta("type", "notAllowedToSetPermissions"),
|
||||
errors.Meta("resource", "federation:access_control"),
|
||||
|
||||
errors.Meta(accessControlPropsMetaKey{}, p),
|
||||
|
||||
// translation namespace & key
|
||||
errors.Meta(locale.ErrorMetaNamespace{}, "federation"),
|
||||
errors.Meta(locale.ErrorMetaKey{}, "access-control.errors.notAllowedToSetPermissions"),
|
||||
|
||||
errors.StackSkip(1),
|
||||
)
|
||||
|
||||
if len(mm) > 0 {
|
||||
}
|
||||
|
||||
return e
|
||||
}
|
||||
|
||||
// *********************************************************************************************************************
|
||||
// *********************************************************************************************************************
|
||||
|
||||
// recordAction is a service helper function wraps function that can return error
|
||||
//
|
||||
// It will wrap unrecognized/internal errors with generic errors.
|
||||
//
|
||||
// This function is auto-generated.
|
||||
func (svc accessControl) recordAction(ctx context.Context, props *accessControlActionProps, actionFn func(...*accessControlActionProps) *accessControlAction, err error) error {
|
||||
if svc.actionlog == nil || actionFn == nil {
|
||||
// action log disabled or no action fn passed, return error as-is
|
||||
return err
|
||||
} else if err == nil {
|
||||
// action completed w/o error, record it
|
||||
svc.actionlog.Record(ctx, actionFn(props).ToAction())
|
||||
return nil
|
||||
}
|
||||
|
||||
a := actionFn(props).ToAction()
|
||||
|
||||
// Extracting error information and recording it as action
|
||||
a.Error = err.Error()
|
||||
|
||||
switch c := err.(type) {
|
||||
case *errors.Error:
|
||||
m := c.Meta()
|
||||
|
||||
a.Error = err.Error()
|
||||
a.Severity = actionlog.Severity(m.AsInt("severity"))
|
||||
a.Description = props.Format(m.AsString(accessControlLogMetaKey{}), err)
|
||||
|
||||
if p, has := m[accessControlPropsMetaKey{}]; has {
|
||||
a.Meta = p.(*accessControlActionProps).Serialize()
|
||||
}
|
||||
|
||||
svc.actionlog.Record(ctx, a)
|
||||
default:
|
||||
svc.actionlog.Record(ctx, a)
|
||||
}
|
||||
|
||||
// Original error is passed on
|
||||
return err
|
||||
}
|
||||
25
server/federation/service/access_control_actions.yaml
Normal file
25
server/federation/service/access_control_actions.yaml
Normal file
@@ -0,0 +1,25 @@
|
||||
# List of security/audit events and errors that we need to log
|
||||
|
||||
resource: federation:access_control
|
||||
service: accessControl
|
||||
|
||||
# Default sensitivity for actions
|
||||
defaultActionSeverity: notice
|
||||
|
||||
# default severity for errors
|
||||
defaultErrorSeverity: alert
|
||||
|
||||
import:
|
||||
- github.com/cortezaproject/corteza/server/pkg/rbac
|
||||
|
||||
props:
|
||||
- name: rule
|
||||
type: "*rbac.Rule"
|
||||
fields: [ operation, roleID, access, resource ]
|
||||
|
||||
actions:
|
||||
- action: grant
|
||||
|
||||
errors:
|
||||
- error: notAllowedToSetPermissions
|
||||
message: "not allowed to set permissions"
|
||||
11
server/federation/service/decoder/compose_module.go
Normal file
11
server/federation/service/decoder/compose_module.go
Normal file
@@ -0,0 +1,11 @@
|
||||
package decoder
|
||||
|
||||
import (
|
||||
"github.com/cortezaproject/corteza/server/compose/types"
|
||||
)
|
||||
|
||||
type (
|
||||
ComposeModule struct {
|
||||
types.Module
|
||||
}
|
||||
)
|
||||
16
server/federation/service/decoder/compose_record.go
Normal file
16
server/federation/service/decoder/compose_record.go
Normal file
@@ -0,0 +1,16 @@
|
||||
package decoder
|
||||
|
||||
import (
|
||||
"github.com/cortezaproject/corteza/server/compose/types"
|
||||
)
|
||||
|
||||
type (
|
||||
ComposeRecord struct {
|
||||
types.Record
|
||||
}
|
||||
ComposeRecordSet []*ComposeRecord
|
||||
|
||||
ComposeRecordFilter struct {
|
||||
types.RecordFilter
|
||||
}
|
||||
)
|
||||
105
server/federation/service/decoder/decoder.go
Normal file
105
server/federation/service/decoder/decoder.go
Normal file
@@ -0,0 +1,105 @@
|
||||
package decoder
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"time"
|
||||
|
||||
"github.com/cortezaproject/corteza/server/compose/types"
|
||||
ftypes "github.com/cortezaproject/corteza/server/federation/types"
|
||||
)
|
||||
|
||||
type (
|
||||
ExposedRecord struct {
|
||||
ID uint64 `json:"recordID,string"`
|
||||
Values types.RecordValueSet `json:"values"`
|
||||
|
||||
CreatedAt time.Time `json:"createdAt,omitempty"`
|
||||
UpdatedAt *time.Time `json:"updatedAt,omitempty"`
|
||||
DeletedAt *time.Time `json:"deletedAt,omitempty"`
|
||||
}
|
||||
|
||||
// Bits for decoding structure sync
|
||||
ModuleDocument struct {
|
||||
Response ComposeModule
|
||||
}
|
||||
|
||||
ExposedModuleDocument struct {
|
||||
Response struct {
|
||||
Set ftypes.ExposedModuleSet
|
||||
}
|
||||
}
|
||||
|
||||
ExposedRecordDocument struct {
|
||||
Response struct {
|
||||
Set []*ExposedRecord
|
||||
}
|
||||
}
|
||||
|
||||
ExposedModule struct {
|
||||
ftypes.ExposedModule
|
||||
}
|
||||
|
||||
// Bits for decoding data sync
|
||||
ComposeRecordResponse struct {
|
||||
Filter ComposeRecordFilter `json:",omitempty"`
|
||||
Set ComposeRecordSet `json:",omitempty"`
|
||||
}
|
||||
ComposeRecordDocument struct {
|
||||
Response ComposeRecordResponse
|
||||
}
|
||||
)
|
||||
|
||||
// DecodeModuleSync decodes the response from the structure sync request.
|
||||
//
|
||||
// It returns a set of modules.
|
||||
func DecodeModuleSync(in []byte) (types.ModuleSet, error) {
|
||||
out := &ModuleDocument{}
|
||||
|
||||
err := json.Unmarshal(in, out)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return types.ModuleSet{&out.Response.Module}, nil
|
||||
}
|
||||
|
||||
func DecodeFederationModuleSync(in []byte) (ftypes.ExposedModuleSet, error) {
|
||||
out := &ExposedModuleDocument{}
|
||||
|
||||
err := json.Unmarshal(in, out)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return out.Response.Set, nil
|
||||
}
|
||||
|
||||
func DecodeFederationRecordSync(in []byte) ([]*ExposedRecord, error) {
|
||||
out := &ExposedRecordDocument{}
|
||||
|
||||
err := json.Unmarshal(in, out)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return out.Response.Set, nil
|
||||
}
|
||||
|
||||
// DecodeRecordSync decodes the response from the data sync request.
|
||||
//
|
||||
// It returns a set of records along with the used filter.
|
||||
func DecodeRecordSync(in []byte) (types.RecordSet, *types.RecordFilter, error) {
|
||||
out := &ComposeRecordDocument{}
|
||||
|
||||
err := json.Unmarshal(in, out)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
ss := make(types.RecordSet, 0, len(out.Response.Set))
|
||||
for _, r := range out.Response.Set {
|
||||
ss = append(ss, &r.Record)
|
||||
}
|
||||
|
||||
return ss, &out.Response.Filter.RecordFilter, nil
|
||||
}
|
||||
341
server/federation/service/exposed_module.go
Normal file
341
server/federation/service/exposed_module.go
Normal file
@@ -0,0 +1,341 @@
|
||||
package service
|
||||
|
||||
import (
|
||||
"context"
|
||||
"strconv"
|
||||
|
||||
"github.com/cortezaproject/corteza/server/pkg/errors"
|
||||
|
||||
cs "github.com/cortezaproject/corteza/server/compose/service"
|
||||
ct "github.com/cortezaproject/corteza/server/compose/types"
|
||||
"github.com/cortezaproject/corteza/server/federation/types"
|
||||
"github.com/cortezaproject/corteza/server/pkg/actionlog"
|
||||
"github.com/cortezaproject/corteza/server/pkg/auth"
|
||||
"github.com/cortezaproject/corteza/server/pkg/rbac"
|
||||
"github.com/cortezaproject/corteza/server/store"
|
||||
ss "github.com/cortezaproject/corteza/server/system/service"
|
||||
st "github.com/cortezaproject/corteza/server/system/types"
|
||||
)
|
||||
|
||||
type (
|
||||
exposedModule struct {
|
||||
node node
|
||||
ac exposedModuleAccessController
|
||||
module cs.ModuleService
|
||||
namespace cs.NamespaceService
|
||||
role ss.RoleService
|
||||
store store.Storer
|
||||
actionlog actionlog.Recorder
|
||||
}
|
||||
|
||||
exposedModuleAccessController interface {
|
||||
CanCreateModuleOnNode(ctx context.Context, r *types.Node) bool
|
||||
CanManageExposedModule(ctx context.Context, r *types.ExposedModule) bool
|
||||
}
|
||||
|
||||
ExposedModuleService interface {
|
||||
Create(ctx context.Context, new *types.ExposedModule) (*types.ExposedModule, error)
|
||||
Update(ctx context.Context, updated *types.ExposedModule) (*types.ExposedModule, error)
|
||||
Find(ctx context.Context, filter types.ExposedModuleFilter) (types.ExposedModuleSet, types.ExposedModuleFilter, error)
|
||||
FindByID(ctx context.Context, nodeID uint64, moduleID uint64) (*types.ExposedModule, error)
|
||||
DeleteByID(ctx context.Context, nodeID, moduleID uint64) (*types.ExposedModule, error)
|
||||
}
|
||||
|
||||
moduleUpdateHandler func(ctx context.Context, ns *types.Node, c *types.ExposedModule) (bool, bool, error)
|
||||
)
|
||||
|
||||
func ExposedModule() *exposedModule {
|
||||
return &exposedModule{
|
||||
ac: DefaultAccessControl,
|
||||
node: *DefaultNode,
|
||||
module: cs.DefaultModule,
|
||||
role: ss.DefaultRole,
|
||||
namespace: cs.DefaultNamespace,
|
||||
store: DefaultStore,
|
||||
actionlog: DefaultActionlog,
|
||||
}
|
||||
}
|
||||
|
||||
// FindByAny tries to find module in a particular namespace by id, handle or name
|
||||
func (svc exposedModule) FindByAny(ctx context.Context, nodeID uint64, identifier interface{}) (m *types.ExposedModule, err error) {
|
||||
if ID, ok := identifier.(uint64); ok {
|
||||
m, err = svc.FindByID(ctx, nodeID, ID)
|
||||
} else if strIdentifier, ok := identifier.(string); ok {
|
||||
if ID, _ := strconv.ParseUint(strIdentifier, 10, 64); ID > 0 {
|
||||
m, err = svc.FindByID(ctx, nodeID, ID)
|
||||
}
|
||||
} else {
|
||||
// force invalid ID error
|
||||
// we do that to wrap error with lookup action context
|
||||
_, err = svc.FindByID(ctx, nodeID, 0)
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return m, nil
|
||||
}
|
||||
|
||||
func (svc exposedModule) FindByID(ctx context.Context, nodeID uint64, moduleID uint64) (module *types.ExposedModule, err error) {
|
||||
err = func() error {
|
||||
if module, err = loadExposedModule(ctx, svc.store, nodeID, moduleID); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if !svc.ac.CanManageExposedModule(ctx, module) {
|
||||
return ExposedModuleErrNotAllowedToManage()
|
||||
}
|
||||
|
||||
return nil
|
||||
}()
|
||||
|
||||
return module, err
|
||||
}
|
||||
|
||||
func (svc exposedModule) Update(ctx context.Context, updated *types.ExposedModule) (*types.ExposedModule, error) {
|
||||
var (
|
||||
aProps = &exposedModuleActionProps{update: updated}
|
||||
)
|
||||
|
||||
err := store.Tx(ctx, svc.store, func(ctx context.Context, s store.Storer) (err error) {
|
||||
var (
|
||||
m *ct.Module
|
||||
node *types.Node
|
||||
old *types.ExposedModule
|
||||
)
|
||||
|
||||
if node, err = svc.node.FindByID(ctx, updated.NodeID); err != nil {
|
||||
return ExposedModuleErrNodeNotFound()
|
||||
}
|
||||
|
||||
if !svc.ac.CanManageExposedModule(ctx, updated) {
|
||||
return ExposedModuleErrNotAllowedToManage()
|
||||
}
|
||||
|
||||
if _, err := svc.namespace.FindByID(ctx, updated.ComposeNamespaceID); err != nil {
|
||||
return ExposedModuleErrComposeNamespaceNotFound()
|
||||
}
|
||||
|
||||
if m, err = svc.module.FindByID(ctx, updated.ComposeNamespaceID, updated.ComposeModuleID); err != nil {
|
||||
return ExposedModuleErrComposeModuleNotFound()
|
||||
}
|
||||
|
||||
if old, err = svc.FindByID(ctx, updated.NodeID, updated.ID); err != nil {
|
||||
return ExposedModuleErrNotFound()
|
||||
}
|
||||
|
||||
updated.UpdatedAt = now()
|
||||
updated.CreatedAt = old.CreatedAt
|
||||
updated.UpdatedBy = auth.GetIdentityFromContext(ctx).Identity()
|
||||
|
||||
// set labels
|
||||
AddFederationLabel(m, "federation", node.BaseURL)
|
||||
|
||||
if _, err := svc.module.Update(ctx, m); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
aProps.setModule(updated)
|
||||
|
||||
if err = store.UpdateFederationExposedModule(ctx, s, updated); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
})
|
||||
|
||||
return updated, svc.recordAction(ctx, aProps, ExposedModuleActionUpdate, err)
|
||||
}
|
||||
|
||||
func (svc exposedModule) updater(ctx context.Context, nodeID, moduleID uint64, action func(...*exposedModuleActionProps) *exposedModuleAction, fn moduleUpdateHandler) (*types.ExposedModule, error) {
|
||||
var (
|
||||
moduleChanged, fieldsChanged bool
|
||||
|
||||
n *types.Node
|
||||
m *types.ExposedModule
|
||||
aProps = &exposedModuleActionProps{module: &types.ExposedModule{ID: moduleID, NodeID: nodeID}}
|
||||
err error
|
||||
)
|
||||
|
||||
err = store.Tx(ctx, svc.store, func(ctx context.Context, s store.Storer) (err error) {
|
||||
if m, err = loadExposedModule(ctx, svc.store, nodeID, moduleID); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// TODO - handle node id also
|
||||
if moduleChanged, fieldsChanged, err = fn(ctx, n, m); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
_ = moduleChanged
|
||||
_ = fieldsChanged
|
||||
|
||||
return err
|
||||
})
|
||||
|
||||
return m, svc.recordAction(ctx, aProps, action, err)
|
||||
}
|
||||
|
||||
func (svc exposedModule) DeleteByID(ctx context.Context, nodeID, moduleID uint64) (m *types.ExposedModule, err error) {
|
||||
var (
|
||||
aProps = &exposedModuleActionProps{}
|
||||
)
|
||||
|
||||
err = store.Tx(ctx, svc.store, func(ctx context.Context, s store.Storer) (err error) {
|
||||
var (
|
||||
m *types.ExposedModule
|
||||
)
|
||||
|
||||
if _, err = svc.node.FindByID(ctx, nodeID); err != nil {
|
||||
return ExposedModuleErrNodeNotFound()
|
||||
}
|
||||
|
||||
if m, err = svc.FindByID(ctx, nodeID, moduleID); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if !svc.ac.CanManageExposedModule(ctx, m) {
|
||||
return ExposedModuleErrNotAllowedToManage()
|
||||
}
|
||||
|
||||
m.DeletedAt = now()
|
||||
m.DeletedBy = auth.GetIdentityFromContext(ctx).Identity()
|
||||
|
||||
if err = store.UpdateFederationExposedModule(ctx, s, m); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
aProps.delete = m
|
||||
|
||||
return nil
|
||||
})
|
||||
|
||||
return m, svc.recordAction(ctx, aProps, ExposedModuleActionDelete, err)
|
||||
}
|
||||
|
||||
func (svc exposedModule) Find(ctx context.Context, filter types.ExposedModuleFilter) (set types.ExposedModuleSet, f types.ExposedModuleFilter, err error) {
|
||||
filter.Check = func(res *types.ExposedModule) (bool, error) {
|
||||
if !svc.ac.CanManageExposedModule(ctx, res) {
|
||||
return false, ExposedModuleErrNotAllowedToManage()
|
||||
}
|
||||
|
||||
return true, nil
|
||||
}
|
||||
|
||||
err = func() error {
|
||||
if set, f, err = store.SearchFederationExposedModules(ctx, svc.store, filter); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}()
|
||||
|
||||
return set, f, err
|
||||
}
|
||||
|
||||
func (svc exposedModule) Create(ctx context.Context, new *types.ExposedModule) (*types.ExposedModule, error) {
|
||||
var (
|
||||
aProps = &exposedModuleActionProps{create: new}
|
||||
)
|
||||
|
||||
err := store.Tx(ctx, svc.store, func(ctx context.Context, s store.Storer) (err error) {
|
||||
var (
|
||||
m *ct.Module
|
||||
node *types.Node
|
||||
fedRole *st.Role
|
||||
)
|
||||
|
||||
if node, err = svc.node.FindByID(ctx, new.NodeID); err != nil {
|
||||
return ExposedModuleErrNodeNotFound()
|
||||
}
|
||||
|
||||
if !svc.ac.CanCreateModuleOnNode(ctx, node) {
|
||||
return ExposedModuleErrNotAllowedToCreate()
|
||||
}
|
||||
|
||||
if _, err := svc.namespace.FindByID(ctx, new.ComposeNamespaceID); err != nil {
|
||||
return ExposedModuleErrComposeNamespaceNotFound()
|
||||
}
|
||||
|
||||
if m, err = svc.module.FindByID(ctx, new.ComposeNamespaceID, new.ComposeModuleID); err != nil {
|
||||
return ExposedModuleErrComposeModuleNotFound()
|
||||
}
|
||||
|
||||
if err = svc.uniqueCheck(ctx, new); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
ctxIdentity := auth.GetIdentityFromContext(ctx)
|
||||
|
||||
new.ID = nextID()
|
||||
new.CreatedAt = *now()
|
||||
new.CreatedBy = ctxIdentity.Identity()
|
||||
|
||||
// find the federation role
|
||||
if fedRole, err = svc.role.FindByHandle(ctx, "federation"); err != nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
if fedRole != nil {
|
||||
// get first ID from role and add it as allow rule
|
||||
err = cs.DefaultAccessControl.Grant(ctx, rbac.AllowRule(fedRole.ID, ct.RecordRbacResource(m.NamespaceID, m.ID, 0), "read"))
|
||||
}
|
||||
|
||||
// set labels
|
||||
AddFederationLabel(m, "federation", node.BaseURL)
|
||||
|
||||
if _, err := svc.module.Update(ctx, m); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
aProps.setModule(new)
|
||||
|
||||
if err = store.CreateFederationExposedModule(ctx, s, new); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
})
|
||||
|
||||
return new, svc.recordAction(ctx, aProps, ExposedModuleActionCreate, err)
|
||||
}
|
||||
|
||||
func (svc exposedModule) uniqueCheck(ctx context.Context, m *types.ExposedModule) (err error) {
|
||||
f := types.ExposedModuleFilter{
|
||||
NodeID: m.NodeID,
|
||||
ComposeModuleID: m.ComposeModuleID,
|
||||
ComposeNamespaceID: m.ComposeNamespaceID,
|
||||
}
|
||||
|
||||
set, _, err := store.SearchFederationExposedModules(ctx, svc.store, f)
|
||||
|
||||
if len(set) > 0 && err == nil {
|
||||
return ExposedModuleErrNotUnique()
|
||||
} else if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func loadExposedModule(ctx context.Context, s store.FederationExposedModules, nodeID, ID uint64) (res *types.ExposedModule, err error) {
|
||||
if ID == 0 || nodeID == 0 {
|
||||
return nil, SharedModuleErrInvalidID()
|
||||
}
|
||||
|
||||
if res, err = store.LookupFederationExposedModuleByID(ctx, s, ID); errors.IsNotFound(err) {
|
||||
err = SharedModuleErrNotFound()
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
if nodeID != res.NodeID {
|
||||
// Make sure chart belongs to the right namespace
|
||||
return nil, SharedModuleErrNotFound()
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
829
server/federation/service/exposed_module_actions.gen.go
generated
Normal file
829
server/federation/service/exposed_module_actions.gen.go
generated
Normal file
@@ -0,0 +1,829 @@
|
||||
package service
|
||||
|
||||
// This file is auto-generated.
|
||||
//
|
||||
// Changes to this file may cause incorrect behavior and will be lost if
|
||||
// the code is regenerated.
|
||||
//
|
||||
// Definitions file that controls how this file is generated:
|
||||
// federation/service/exposed_module_actions.yaml
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"github.com/cortezaproject/corteza/server/federation/types"
|
||||
"github.com/cortezaproject/corteza/server/pkg/actionlog"
|
||||
"github.com/cortezaproject/corteza/server/pkg/errors"
|
||||
"github.com/cortezaproject/corteza/server/pkg/locale"
|
||||
"strings"
|
||||
"time"
|
||||
)
|
||||
|
||||
type (
|
||||
exposedModuleActionProps struct {
|
||||
module *types.ExposedModule
|
||||
update *types.ExposedModule
|
||||
create *types.ExposedModule
|
||||
delete *types.ExposedModule
|
||||
filter *types.ExposedModuleFilter
|
||||
node *types.Node
|
||||
}
|
||||
|
||||
exposedModuleAction struct {
|
||||
timestamp time.Time
|
||||
resource string
|
||||
action string
|
||||
log string
|
||||
severity actionlog.Severity
|
||||
|
||||
// prefix for error when action fails
|
||||
errorMessage string
|
||||
|
||||
props *exposedModuleActionProps
|
||||
}
|
||||
|
||||
exposedModuleLogMetaKey struct{}
|
||||
exposedModulePropsMetaKey struct{}
|
||||
)
|
||||
|
||||
var (
|
||||
// just a placeholder to cover template cases w/o fmt package use
|
||||
_ = fmt.Println
|
||||
)
|
||||
|
||||
// *********************************************************************************************************************
|
||||
// *********************************************************************************************************************
|
||||
// Props methods
|
||||
// setModule updates exposedModuleActionProps's module
|
||||
//
|
||||
// This function is auto-generated.
|
||||
func (p *exposedModuleActionProps) setModule(module *types.ExposedModule) *exposedModuleActionProps {
|
||||
p.module = module
|
||||
return p
|
||||
}
|
||||
|
||||
// setUpdate updates exposedModuleActionProps's update
|
||||
//
|
||||
// This function is auto-generated.
|
||||
func (p *exposedModuleActionProps) setUpdate(update *types.ExposedModule) *exposedModuleActionProps {
|
||||
p.update = update
|
||||
return p
|
||||
}
|
||||
|
||||
// setCreate updates exposedModuleActionProps's create
|
||||
//
|
||||
// This function is auto-generated.
|
||||
func (p *exposedModuleActionProps) setCreate(create *types.ExposedModule) *exposedModuleActionProps {
|
||||
p.create = create
|
||||
return p
|
||||
}
|
||||
|
||||
// setDelete updates exposedModuleActionProps's delete
|
||||
//
|
||||
// This function is auto-generated.
|
||||
func (p *exposedModuleActionProps) setDelete(delete *types.ExposedModule) *exposedModuleActionProps {
|
||||
p.delete = delete
|
||||
return p
|
||||
}
|
||||
|
||||
// setFilter updates exposedModuleActionProps's filter
|
||||
//
|
||||
// This function is auto-generated.
|
||||
func (p *exposedModuleActionProps) setFilter(filter *types.ExposedModuleFilter) *exposedModuleActionProps {
|
||||
p.filter = filter
|
||||
return p
|
||||
}
|
||||
|
||||
// setNode updates exposedModuleActionProps's node
|
||||
//
|
||||
// This function is auto-generated.
|
||||
func (p *exposedModuleActionProps) setNode(node *types.Node) *exposedModuleActionProps {
|
||||
p.node = node
|
||||
return p
|
||||
}
|
||||
|
||||
// Serialize converts exposedModuleActionProps to actionlog.Meta
|
||||
//
|
||||
// This function is auto-generated.
|
||||
func (p exposedModuleActionProps) Serialize() actionlog.Meta {
|
||||
var (
|
||||
m = make(actionlog.Meta)
|
||||
)
|
||||
|
||||
if p.module != nil {
|
||||
m.Set("module.ID", p.module.ID, true)
|
||||
m.Set("module.ComposeNamespaceID", p.module.ComposeNamespaceID, true)
|
||||
m.Set("module.ComposeModuleID", p.module.ComposeModuleID, true)
|
||||
m.Set("module.NodeID", p.module.NodeID, true)
|
||||
}
|
||||
if p.update != nil {
|
||||
m.Set("update.ID", p.update.ID, true)
|
||||
m.Set("update.ComposeNamespaceID", p.update.ComposeNamespaceID, true)
|
||||
m.Set("update.ComposeModuleID", p.update.ComposeModuleID, true)
|
||||
m.Set("update.NodeID", p.update.NodeID, true)
|
||||
}
|
||||
if p.create != nil {
|
||||
m.Set("create.ID", p.create.ID, true)
|
||||
m.Set("create.ComposeNamespaceID", p.create.ComposeNamespaceID, true)
|
||||
m.Set("create.ComposeModuleID", p.create.ComposeModuleID, true)
|
||||
m.Set("create.NodeID", p.create.NodeID, true)
|
||||
}
|
||||
if p.delete != nil {
|
||||
m.Set("delete.ID", p.delete.ID, true)
|
||||
m.Set("delete.ComposeNamespaceID", p.delete.ComposeNamespaceID, true)
|
||||
m.Set("delete.ComposeModuleID", p.delete.ComposeModuleID, true)
|
||||
m.Set("delete.NodeID", p.delete.NodeID, true)
|
||||
}
|
||||
if p.filter != nil {
|
||||
m.Set("filter.query", p.filter.Query, true)
|
||||
m.Set("filter.sort", p.filter.Sort, true)
|
||||
m.Set("filter.limit", p.filter.Limit, true)
|
||||
}
|
||||
if p.node != nil {
|
||||
m.Set("node.ID", p.node.ID, true)
|
||||
m.Set("node.Name", p.node.Name, true)
|
||||
}
|
||||
|
||||
return m
|
||||
}
|
||||
|
||||
// tr translates string and replaces meta value placeholder with values
|
||||
//
|
||||
// This function is auto-generated.
|
||||
func (p exposedModuleActionProps) Format(in string, err error) string {
|
||||
var (
|
||||
pairs = []string{"{{err}}"}
|
||||
// first non-empty string
|
||||
fns = func(ii ...interface{}) string {
|
||||
for _, i := range ii {
|
||||
if s := fmt.Sprintf("%v", i); len(s) > 0 {
|
||||
return s
|
||||
}
|
||||
}
|
||||
|
||||
return ""
|
||||
}
|
||||
)
|
||||
|
||||
if err != nil {
|
||||
pairs = append(pairs, err.Error())
|
||||
} else {
|
||||
pairs = append(pairs, "nil")
|
||||
}
|
||||
|
||||
if p.module != nil {
|
||||
// replacement for "{{module}}" (in order how fields are defined)
|
||||
pairs = append(
|
||||
pairs,
|
||||
"{{module}}",
|
||||
fns(
|
||||
p.module.ID,
|
||||
p.module.ComposeNamespaceID,
|
||||
p.module.ComposeModuleID,
|
||||
p.module.NodeID,
|
||||
),
|
||||
)
|
||||
pairs = append(pairs, "{{module.ID}}", fns(p.module.ID))
|
||||
pairs = append(pairs, "{{module.ComposeNamespaceID}}", fns(p.module.ComposeNamespaceID))
|
||||
pairs = append(pairs, "{{module.ComposeModuleID}}", fns(p.module.ComposeModuleID))
|
||||
pairs = append(pairs, "{{module.NodeID}}", fns(p.module.NodeID))
|
||||
}
|
||||
|
||||
if p.update != nil {
|
||||
// replacement for "{{update}}" (in order how fields are defined)
|
||||
pairs = append(
|
||||
pairs,
|
||||
"{{update}}",
|
||||
fns(
|
||||
p.update.ID,
|
||||
p.update.ComposeNamespaceID,
|
||||
p.update.ComposeModuleID,
|
||||
p.update.NodeID,
|
||||
),
|
||||
)
|
||||
pairs = append(pairs, "{{update.ID}}", fns(p.update.ID))
|
||||
pairs = append(pairs, "{{update.ComposeNamespaceID}}", fns(p.update.ComposeNamespaceID))
|
||||
pairs = append(pairs, "{{update.ComposeModuleID}}", fns(p.update.ComposeModuleID))
|
||||
pairs = append(pairs, "{{update.NodeID}}", fns(p.update.NodeID))
|
||||
}
|
||||
|
||||
if p.create != nil {
|
||||
// replacement for "{{create}}" (in order how fields are defined)
|
||||
pairs = append(
|
||||
pairs,
|
||||
"{{create}}",
|
||||
fns(
|
||||
p.create.ID,
|
||||
p.create.ComposeNamespaceID,
|
||||
p.create.ComposeModuleID,
|
||||
p.create.NodeID,
|
||||
),
|
||||
)
|
||||
pairs = append(pairs, "{{create.ID}}", fns(p.create.ID))
|
||||
pairs = append(pairs, "{{create.ComposeNamespaceID}}", fns(p.create.ComposeNamespaceID))
|
||||
pairs = append(pairs, "{{create.ComposeModuleID}}", fns(p.create.ComposeModuleID))
|
||||
pairs = append(pairs, "{{create.NodeID}}", fns(p.create.NodeID))
|
||||
}
|
||||
|
||||
if p.delete != nil {
|
||||
// replacement for "{{delete}}" (in order how fields are defined)
|
||||
pairs = append(
|
||||
pairs,
|
||||
"{{delete}}",
|
||||
fns(
|
||||
p.delete.ID,
|
||||
p.delete.ComposeNamespaceID,
|
||||
p.delete.ComposeModuleID,
|
||||
p.delete.NodeID,
|
||||
),
|
||||
)
|
||||
pairs = append(pairs, "{{delete.ID}}", fns(p.delete.ID))
|
||||
pairs = append(pairs, "{{delete.ComposeNamespaceID}}", fns(p.delete.ComposeNamespaceID))
|
||||
pairs = append(pairs, "{{delete.ComposeModuleID}}", fns(p.delete.ComposeModuleID))
|
||||
pairs = append(pairs, "{{delete.NodeID}}", fns(p.delete.NodeID))
|
||||
}
|
||||
|
||||
if p.filter != nil {
|
||||
// replacement for "{{filter}}" (in order how fields are defined)
|
||||
pairs = append(
|
||||
pairs,
|
||||
"{{filter}}",
|
||||
fns(
|
||||
p.filter.Query,
|
||||
p.filter.Sort,
|
||||
p.filter.Limit,
|
||||
),
|
||||
)
|
||||
pairs = append(pairs, "{{filter.query}}", fns(p.filter.Query))
|
||||
pairs = append(pairs, "{{filter.sort}}", fns(p.filter.Sort))
|
||||
pairs = append(pairs, "{{filter.limit}}", fns(p.filter.Limit))
|
||||
}
|
||||
|
||||
if p.node != nil {
|
||||
// replacement for "{{node}}" (in order how fields are defined)
|
||||
pairs = append(
|
||||
pairs,
|
||||
"{{node}}",
|
||||
fns(
|
||||
p.node.ID,
|
||||
p.node.Name,
|
||||
),
|
||||
)
|
||||
pairs = append(pairs, "{{node.ID}}", fns(p.node.ID))
|
||||
pairs = append(pairs, "{{node.Name}}", fns(p.node.Name))
|
||||
}
|
||||
return strings.NewReplacer(pairs...).Replace(in)
|
||||
}
|
||||
|
||||
// *********************************************************************************************************************
|
||||
// *********************************************************************************************************************
|
||||
// Action methods
|
||||
|
||||
// String returns loggable description as string
|
||||
//
|
||||
// This function is auto-generated.
|
||||
func (a *exposedModuleAction) String() string {
|
||||
var props = &exposedModuleActionProps{}
|
||||
|
||||
if a.props != nil {
|
||||
props = a.props
|
||||
}
|
||||
|
||||
return props.Format(a.log, nil)
|
||||
}
|
||||
|
||||
func (e *exposedModuleAction) ToAction() *actionlog.Action {
|
||||
return &actionlog.Action{
|
||||
Resource: e.resource,
|
||||
Action: e.action,
|
||||
Severity: e.severity,
|
||||
Description: e.String(),
|
||||
Meta: e.props.Serialize(),
|
||||
}
|
||||
}
|
||||
|
||||
// *********************************************************************************************************************
|
||||
// *********************************************************************************************************************
|
||||
// Action constructors
|
||||
|
||||
// ExposedModuleActionSearch returns "federation:exposed_module.search" action
|
||||
//
|
||||
// This function is auto-generated.
|
||||
func ExposedModuleActionSearch(props ...*exposedModuleActionProps) *exposedModuleAction {
|
||||
a := &exposedModuleAction{
|
||||
timestamp: time.Now(),
|
||||
resource: "federation:exposed_module",
|
||||
action: "search",
|
||||
log: "searched for modules",
|
||||
severity: actionlog.Info,
|
||||
}
|
||||
|
||||
if len(props) > 0 {
|
||||
a.props = props[0]
|
||||
}
|
||||
|
||||
return a
|
||||
}
|
||||
|
||||
// ExposedModuleActionLookup returns "federation:exposed_module.lookup" action
|
||||
//
|
||||
// This function is auto-generated.
|
||||
func ExposedModuleActionLookup(props ...*exposedModuleActionProps) *exposedModuleAction {
|
||||
a := &exposedModuleAction{
|
||||
timestamp: time.Now(),
|
||||
resource: "federation:exposed_module",
|
||||
action: "lookup",
|
||||
log: "looked-up for a {{module}}",
|
||||
severity: actionlog.Info,
|
||||
}
|
||||
|
||||
if len(props) > 0 {
|
||||
a.props = props[0]
|
||||
}
|
||||
|
||||
return a
|
||||
}
|
||||
|
||||
// ExposedModuleActionCreate returns "federation:exposed_module.create" action
|
||||
//
|
||||
// This function is auto-generated.
|
||||
func ExposedModuleActionCreate(props ...*exposedModuleActionProps) *exposedModuleAction {
|
||||
a := &exposedModuleAction{
|
||||
timestamp: time.Now(),
|
||||
resource: "federation:exposed_module",
|
||||
action: "create",
|
||||
log: "created {{module}}",
|
||||
severity: actionlog.Notice,
|
||||
}
|
||||
|
||||
if len(props) > 0 {
|
||||
a.props = props[0]
|
||||
}
|
||||
|
||||
return a
|
||||
}
|
||||
|
||||
// ExposedModuleActionUpdate returns "federation:exposed_module.update" action
|
||||
//
|
||||
// This function is auto-generated.
|
||||
func ExposedModuleActionUpdate(props ...*exposedModuleActionProps) *exposedModuleAction {
|
||||
a := &exposedModuleAction{
|
||||
timestamp: time.Now(),
|
||||
resource: "federation:exposed_module",
|
||||
action: "update",
|
||||
log: "updated {{module}}",
|
||||
severity: actionlog.Notice,
|
||||
}
|
||||
|
||||
if len(props) > 0 {
|
||||
a.props = props[0]
|
||||
}
|
||||
|
||||
return a
|
||||
}
|
||||
|
||||
// ExposedModuleActionDelete returns "federation:exposed_module.delete" action
|
||||
//
|
||||
// This function is auto-generated.
|
||||
func ExposedModuleActionDelete(props ...*exposedModuleActionProps) *exposedModuleAction {
|
||||
a := &exposedModuleAction{
|
||||
timestamp: time.Now(),
|
||||
resource: "federation:exposed_module",
|
||||
action: "delete",
|
||||
log: "deleted {{module}}",
|
||||
severity: actionlog.Notice,
|
||||
}
|
||||
|
||||
if len(props) > 0 {
|
||||
a.props = props[0]
|
||||
}
|
||||
|
||||
return a
|
||||
}
|
||||
|
||||
// ExposedModuleActionUndelete returns "federation:exposed_module.undelete" action
|
||||
//
|
||||
// This function is auto-generated.
|
||||
func ExposedModuleActionUndelete(props ...*exposedModuleActionProps) *exposedModuleAction {
|
||||
a := &exposedModuleAction{
|
||||
timestamp: time.Now(),
|
||||
resource: "federation:exposed_module",
|
||||
action: "undelete",
|
||||
log: "undeleted {{module}}",
|
||||
severity: actionlog.Notice,
|
||||
}
|
||||
|
||||
if len(props) > 0 {
|
||||
a.props = props[0]
|
||||
}
|
||||
|
||||
return a
|
||||
}
|
||||
|
||||
// *********************************************************************************************************************
|
||||
// *********************************************************************************************************************
|
||||
// Error constructors
|
||||
|
||||
// ExposedModuleErrGeneric returns "federation:exposed_module.generic" as *errors.Error
|
||||
//
|
||||
// This function is auto-generated.
|
||||
func ExposedModuleErrGeneric(mm ...*exposedModuleActionProps) *errors.Error {
|
||||
var p = &exposedModuleActionProps{}
|
||||
if len(mm) > 0 {
|
||||
p = mm[0]
|
||||
}
|
||||
|
||||
var e = errors.New(
|
||||
errors.KindInternal,
|
||||
|
||||
p.Format("failed to complete request due to internal error", nil),
|
||||
|
||||
errors.Meta("type", "generic"),
|
||||
errors.Meta("resource", "federation:exposed_module"),
|
||||
|
||||
// action log entry; no formatting, it will be applied inside recordAction fn.
|
||||
errors.Meta(exposedModuleLogMetaKey{}, "{err}"),
|
||||
errors.Meta(exposedModulePropsMetaKey{}, p),
|
||||
|
||||
// translation namespace & key
|
||||
errors.Meta(locale.ErrorMetaNamespace{}, "federation"),
|
||||
errors.Meta(locale.ErrorMetaKey{}, "exposed-module.errors.generic"),
|
||||
|
||||
errors.StackSkip(1),
|
||||
)
|
||||
|
||||
if len(mm) > 0 {
|
||||
}
|
||||
|
||||
return e
|
||||
}
|
||||
|
||||
// ExposedModuleErrNotFound returns "federation:exposed_module.notFound" as *errors.Error
|
||||
//
|
||||
// This function is auto-generated.
|
||||
func ExposedModuleErrNotFound(mm ...*exposedModuleActionProps) *errors.Error {
|
||||
var p = &exposedModuleActionProps{}
|
||||
if len(mm) > 0 {
|
||||
p = mm[0]
|
||||
}
|
||||
|
||||
var e = errors.New(
|
||||
errors.KindInternal,
|
||||
|
||||
p.Format("module does not exist", nil),
|
||||
|
||||
errors.Meta("type", "notFound"),
|
||||
errors.Meta("resource", "federation:exposed_module"),
|
||||
|
||||
errors.Meta(exposedModulePropsMetaKey{}, p),
|
||||
|
||||
// translation namespace & key
|
||||
errors.Meta(locale.ErrorMetaNamespace{}, "federation"),
|
||||
errors.Meta(locale.ErrorMetaKey{}, "exposed-module.errors.notFound"),
|
||||
|
||||
errors.StackSkip(1),
|
||||
)
|
||||
|
||||
if len(mm) > 0 {
|
||||
}
|
||||
|
||||
return e
|
||||
}
|
||||
|
||||
// ExposedModuleErrInvalidID returns "federation:exposed_module.invalidID" as *errors.Error
|
||||
//
|
||||
// This function is auto-generated.
|
||||
func ExposedModuleErrInvalidID(mm ...*exposedModuleActionProps) *errors.Error {
|
||||
var p = &exposedModuleActionProps{}
|
||||
if len(mm) > 0 {
|
||||
p = mm[0]
|
||||
}
|
||||
|
||||
var e = errors.New(
|
||||
errors.KindInternal,
|
||||
|
||||
p.Format("invalid ID", nil),
|
||||
|
||||
errors.Meta("type", "invalidID"),
|
||||
errors.Meta("resource", "federation:exposed_module"),
|
||||
|
||||
errors.Meta(exposedModulePropsMetaKey{}, p),
|
||||
|
||||
// translation namespace & key
|
||||
errors.Meta(locale.ErrorMetaNamespace{}, "federation"),
|
||||
errors.Meta(locale.ErrorMetaKey{}, "exposed-module.errors.invalidID"),
|
||||
|
||||
errors.StackSkip(1),
|
||||
)
|
||||
|
||||
if len(mm) > 0 {
|
||||
}
|
||||
|
||||
return e
|
||||
}
|
||||
|
||||
// ExposedModuleErrStaleData returns "federation:exposed_module.staleData" as *errors.Error
|
||||
//
|
||||
// This function is auto-generated.
|
||||
func ExposedModuleErrStaleData(mm ...*exposedModuleActionProps) *errors.Error {
|
||||
var p = &exposedModuleActionProps{}
|
||||
if len(mm) > 0 {
|
||||
p = mm[0]
|
||||
}
|
||||
|
||||
var e = errors.New(
|
||||
errors.KindInternal,
|
||||
|
||||
p.Format("module was modified by someone else or by a workflow after you've opened it. Please refresh to see the latest updated version", nil),
|
||||
|
||||
errors.Meta("type", "staleData"),
|
||||
errors.Meta("resource", "federation:exposed_module"),
|
||||
|
||||
errors.Meta(exposedModulePropsMetaKey{}, p),
|
||||
|
||||
// translation namespace & key
|
||||
errors.Meta(locale.ErrorMetaNamespace{}, "federation"),
|
||||
errors.Meta(locale.ErrorMetaKey{}, "exposed-module.errors.staleData"),
|
||||
|
||||
errors.StackSkip(1),
|
||||
)
|
||||
|
||||
if len(mm) > 0 {
|
||||
}
|
||||
|
||||
return e
|
||||
}
|
||||
|
||||
// ExposedModuleErrNotUnique returns "federation:exposed_module.notUnique" as *errors.Error
|
||||
//
|
||||
// This function is auto-generated.
|
||||
func ExposedModuleErrNotUnique(mm ...*exposedModuleActionProps) *errors.Error {
|
||||
var p = &exposedModuleActionProps{}
|
||||
if len(mm) > 0 {
|
||||
p = mm[0]
|
||||
}
|
||||
|
||||
var e = errors.New(
|
||||
errors.KindInternal,
|
||||
|
||||
p.Format("node not unique", nil),
|
||||
|
||||
errors.Meta("type", "notUnique"),
|
||||
errors.Meta("resource", "federation:exposed_module"),
|
||||
|
||||
// action log entry; no formatting, it will be applied inside recordAction fn.
|
||||
errors.Meta(exposedModuleLogMetaKey{}, "used duplicate node TODO - {{module.NodeID}} for this compose module TODO - module.rel_compose_module"),
|
||||
errors.Meta(exposedModulePropsMetaKey{}, p),
|
||||
|
||||
// translation namespace & key
|
||||
errors.Meta(locale.ErrorMetaNamespace{}, "federation"),
|
||||
errors.Meta(locale.ErrorMetaKey{}, "exposed-module.errors.notUnique"),
|
||||
|
||||
errors.StackSkip(1),
|
||||
)
|
||||
|
||||
if len(mm) > 0 {
|
||||
}
|
||||
|
||||
return e
|
||||
}
|
||||
|
||||
// ExposedModuleErrNodeNotFound returns "federation:exposed_module.nodeNotFound" as *errors.Error
|
||||
//
|
||||
// This function is auto-generated.
|
||||
func ExposedModuleErrNodeNotFound(mm ...*exposedModuleActionProps) *errors.Error {
|
||||
var p = &exposedModuleActionProps{}
|
||||
if len(mm) > 0 {
|
||||
p = mm[0]
|
||||
}
|
||||
|
||||
var e = errors.New(
|
||||
errors.KindInternal,
|
||||
|
||||
p.Format("node does not exist", nil),
|
||||
|
||||
errors.Meta("type", "nodeNotFound"),
|
||||
errors.Meta("resource", "federation:exposed_module"),
|
||||
|
||||
errors.Meta(exposedModulePropsMetaKey{}, p),
|
||||
|
||||
// translation namespace & key
|
||||
errors.Meta(locale.ErrorMetaNamespace{}, "federation"),
|
||||
errors.Meta(locale.ErrorMetaKey{}, "exposed-module.errors.nodeNotFound"),
|
||||
|
||||
errors.StackSkip(1),
|
||||
)
|
||||
|
||||
if len(mm) > 0 {
|
||||
}
|
||||
|
||||
return e
|
||||
}
|
||||
|
||||
// ExposedModuleErrComposeModuleNotFound returns "federation:exposed_module.composeModuleNotFound" as *errors.Error
|
||||
//
|
||||
// This function is auto-generated.
|
||||
func ExposedModuleErrComposeModuleNotFound(mm ...*exposedModuleActionProps) *errors.Error {
|
||||
var p = &exposedModuleActionProps{}
|
||||
if len(mm) > 0 {
|
||||
p = mm[0]
|
||||
}
|
||||
|
||||
var e = errors.New(
|
||||
errors.KindInternal,
|
||||
|
||||
p.Format("compose module not found", nil),
|
||||
|
||||
errors.Meta("type", "composeModuleNotFound"),
|
||||
errors.Meta("resource", "federation:exposed_module"),
|
||||
|
||||
errors.Meta(exposedModulePropsMetaKey{}, p),
|
||||
|
||||
// translation namespace & key
|
||||
errors.Meta(locale.ErrorMetaNamespace{}, "federation"),
|
||||
errors.Meta(locale.ErrorMetaKey{}, "exposed-module.errors.composeModuleNotFound"),
|
||||
|
||||
errors.StackSkip(1),
|
||||
)
|
||||
|
||||
if len(mm) > 0 {
|
||||
}
|
||||
|
||||
return e
|
||||
}
|
||||
|
||||
// ExposedModuleErrComposeNamespaceNotFound returns "federation:exposed_module.composeNamespaceNotFound" as *errors.Error
|
||||
//
|
||||
// This function is auto-generated.
|
||||
func ExposedModuleErrComposeNamespaceNotFound(mm ...*exposedModuleActionProps) *errors.Error {
|
||||
var p = &exposedModuleActionProps{}
|
||||
if len(mm) > 0 {
|
||||
p = mm[0]
|
||||
}
|
||||
|
||||
var e = errors.New(
|
||||
errors.KindInternal,
|
||||
|
||||
p.Format("compose namespace not found", nil),
|
||||
|
||||
errors.Meta("type", "composeNamespaceNotFound"),
|
||||
errors.Meta("resource", "federation:exposed_module"),
|
||||
|
||||
errors.Meta(exposedModulePropsMetaKey{}, p),
|
||||
|
||||
// translation namespace & key
|
||||
errors.Meta(locale.ErrorMetaNamespace{}, "federation"),
|
||||
errors.Meta(locale.ErrorMetaKey{}, "exposed-module.errors.composeNamespaceNotFound"),
|
||||
|
||||
errors.StackSkip(1),
|
||||
)
|
||||
|
||||
if len(mm) > 0 {
|
||||
}
|
||||
|
||||
return e
|
||||
}
|
||||
|
||||
// ExposedModuleErrRequestParametersInvalid returns "federation:exposed_module.requestParametersInvalid" as *errors.Error
|
||||
//
|
||||
// This function is auto-generated.
|
||||
func ExposedModuleErrRequestParametersInvalid(mm ...*exposedModuleActionProps) *errors.Error {
|
||||
var p = &exposedModuleActionProps{}
|
||||
if len(mm) > 0 {
|
||||
p = mm[0]
|
||||
}
|
||||
|
||||
var e = errors.New(
|
||||
errors.KindInternal,
|
||||
|
||||
p.Format("request parameters invalid", nil),
|
||||
|
||||
errors.Meta("type", "requestParametersInvalid"),
|
||||
errors.Meta("resource", "federation:exposed_module"),
|
||||
|
||||
errors.Meta(exposedModulePropsMetaKey{}, p),
|
||||
|
||||
// translation namespace & key
|
||||
errors.Meta(locale.ErrorMetaNamespace{}, "federation"),
|
||||
errors.Meta(locale.ErrorMetaKey{}, "exposed-module.errors.requestParametersInvalid"),
|
||||
|
||||
errors.StackSkip(1),
|
||||
)
|
||||
|
||||
if len(mm) > 0 {
|
||||
}
|
||||
|
||||
return e
|
||||
}
|
||||
|
||||
// ExposedModuleErrNotAllowedToCreate returns "federation:exposed_module.notAllowedToCreate" as *errors.Error
|
||||
//
|
||||
// This function is auto-generated.
|
||||
func ExposedModuleErrNotAllowedToCreate(mm ...*exposedModuleActionProps) *errors.Error {
|
||||
var p = &exposedModuleActionProps{}
|
||||
if len(mm) > 0 {
|
||||
p = mm[0]
|
||||
}
|
||||
|
||||
var e = errors.New(
|
||||
errors.KindInternal,
|
||||
|
||||
p.Format("not allowed to create modules", nil),
|
||||
|
||||
errors.Meta("type", "notAllowedToCreate"),
|
||||
errors.Meta("resource", "federation:exposed_module"),
|
||||
|
||||
// action log entry; no formatting, it will be applied inside recordAction fn.
|
||||
errors.Meta(exposedModuleLogMetaKey{}, "could not create modules; insufficient permissions"),
|
||||
errors.Meta(exposedModulePropsMetaKey{}, p),
|
||||
|
||||
// translation namespace & key
|
||||
errors.Meta(locale.ErrorMetaNamespace{}, "federation"),
|
||||
errors.Meta(locale.ErrorMetaKey{}, "exposed-module.errors.notAllowedToCreate"),
|
||||
|
||||
errors.StackSkip(1),
|
||||
)
|
||||
|
||||
if len(mm) > 0 {
|
||||
}
|
||||
|
||||
return e
|
||||
}
|
||||
|
||||
// ExposedModuleErrNotAllowedToManage returns "federation:exposed_module.notAllowedToManage" as *errors.Error
|
||||
//
|
||||
// This function is auto-generated.
|
||||
func ExposedModuleErrNotAllowedToManage(mm ...*exposedModuleActionProps) *errors.Error {
|
||||
var p = &exposedModuleActionProps{}
|
||||
if len(mm) > 0 {
|
||||
p = mm[0]
|
||||
}
|
||||
|
||||
var e = errors.New(
|
||||
errors.KindInternal,
|
||||
|
||||
p.Format("not allowed to manage this module", nil),
|
||||
|
||||
errors.Meta("type", "notAllowedToManage"),
|
||||
errors.Meta("resource", "federation:exposed_module"),
|
||||
|
||||
// action log entry; no formatting, it will be applied inside recordAction fn.
|
||||
errors.Meta(exposedModuleLogMetaKey{}, "could not manage {{module}}; insufficient permissions"),
|
||||
errors.Meta(exposedModulePropsMetaKey{}, p),
|
||||
|
||||
// translation namespace & key
|
||||
errors.Meta(locale.ErrorMetaNamespace{}, "federation"),
|
||||
errors.Meta(locale.ErrorMetaKey{}, "exposed-module.errors.notAllowedToManage"),
|
||||
|
||||
errors.StackSkip(1),
|
||||
)
|
||||
|
||||
if len(mm) > 0 {
|
||||
}
|
||||
|
||||
return e
|
||||
}
|
||||
|
||||
// *********************************************************************************************************************
|
||||
// *********************************************************************************************************************
|
||||
|
||||
// recordAction is a service helper function wraps function that can return error
|
||||
//
|
||||
// It will wrap unrecognized/internal errors with generic errors.
|
||||
//
|
||||
// This function is auto-generated.
|
||||
func (svc exposedModule) recordAction(ctx context.Context, props *exposedModuleActionProps, actionFn func(...*exposedModuleActionProps) *exposedModuleAction, err error) error {
|
||||
if svc.actionlog == nil || actionFn == nil {
|
||||
// action log disabled or no action fn passed, return error as-is
|
||||
return err
|
||||
} else if err == nil {
|
||||
// action completed w/o error, record it
|
||||
svc.actionlog.Record(ctx, actionFn(props).ToAction())
|
||||
return nil
|
||||
}
|
||||
|
||||
a := actionFn(props).ToAction()
|
||||
|
||||
// Extracting error information and recording it as action
|
||||
a.Error = err.Error()
|
||||
|
||||
switch c := err.(type) {
|
||||
case *errors.Error:
|
||||
m := c.Meta()
|
||||
|
||||
a.Error = err.Error()
|
||||
a.Severity = actionlog.Severity(m.AsInt("severity"))
|
||||
a.Description = props.Format(m.AsString(exposedModuleLogMetaKey{}), err)
|
||||
|
||||
if p, has := m[exposedModulePropsMetaKey{}]; has {
|
||||
a.Meta = p.(*exposedModuleActionProps).Serialize()
|
||||
}
|
||||
|
||||
svc.actionlog.Record(ctx, a)
|
||||
default:
|
||||
svc.actionlog.Record(ctx, a)
|
||||
}
|
||||
|
||||
// Original error is passed on
|
||||
return err
|
||||
}
|
||||
97
server/federation/service/exposed_module_actions.yaml
Normal file
97
server/federation/service/exposed_module_actions.yaml
Normal file
@@ -0,0 +1,97 @@
|
||||
# List of loggable service actions
|
||||
|
||||
resource: federation:exposed_module
|
||||
service: exposedModule
|
||||
|
||||
# Default sensitivity for actions
|
||||
defaultActionSeverity: notice
|
||||
|
||||
# default severity for errors
|
||||
defaultErrorSeverity: error
|
||||
|
||||
import:
|
||||
- github.com/cortezaproject/corteza/server/federation/types
|
||||
|
||||
props:
|
||||
- name: module
|
||||
type: "*types.ExposedModule"
|
||||
fields: [ ID, ComposeNamespaceID, ComposeModuleID, NodeID ]
|
||||
- name: update
|
||||
type: "*types.ExposedModule"
|
||||
fields: [ ID, ComposeNamespaceID, ComposeModuleID, NodeID ]
|
||||
- name: create
|
||||
type: "*types.ExposedModule"
|
||||
fields: [ ID, ComposeNamespaceID, ComposeModuleID, NodeID ]
|
||||
- name: delete
|
||||
type: "*types.ExposedModule"
|
||||
fields: [ ID, ComposeNamespaceID, ComposeModuleID, NodeID ]
|
||||
- name: filter
|
||||
type: "*types.ExposedModuleFilter"
|
||||
fields: [ query, sort, limit ]
|
||||
- name: node
|
||||
type: "*types.Node"
|
||||
fields: [ ID, Name ]
|
||||
|
||||
actions:
|
||||
- action: search
|
||||
log: "searched for modules"
|
||||
severity: info
|
||||
|
||||
- action: lookup
|
||||
log: "looked-up for a {{module}}"
|
||||
severity: info
|
||||
|
||||
- action: create
|
||||
log: "created {{module}}"
|
||||
|
||||
- action: update
|
||||
log: "updated {{module}}"
|
||||
|
||||
- action: delete
|
||||
log: "deleted {{module}}"
|
||||
|
||||
- action: undelete
|
||||
log: "undeleted {{module}}"
|
||||
|
||||
errors:
|
||||
- error: notFound
|
||||
message: "module does not exist"
|
||||
severity: warning
|
||||
|
||||
- error: invalidID
|
||||
message: "invalid ID"
|
||||
severity: warning
|
||||
|
||||
- error: staleData
|
||||
message: "module was modified by someone else or by a workflow after you've opened it. Please refresh to see the latest updated version"
|
||||
severity: warning
|
||||
|
||||
- error: notUnique
|
||||
message: "node not unique"
|
||||
log: "used duplicate node TODO - {{module.NodeID}} for this compose module TODO - module.rel_compose_module"
|
||||
severity: warning
|
||||
|
||||
- error: nodeNotFound
|
||||
message: "node does not exist"
|
||||
severity: warning
|
||||
|
||||
- error: composeModuleNotFound
|
||||
message: "compose module not found"
|
||||
severity: "warning"
|
||||
|
||||
- error: composeNamespaceNotFound
|
||||
message: "compose namespace not found"
|
||||
severity: "warning"
|
||||
|
||||
- error: requestParametersInvalid
|
||||
message: "request parameters invalid"
|
||||
severity: "warning"
|
||||
|
||||
- error: notAllowedToCreate
|
||||
message: "not allowed to create modules"
|
||||
log: "could not create modules; insufficient permissions"
|
||||
|
||||
- error: notAllowedToManage
|
||||
message: "not allowed to manage this module"
|
||||
log: "could not manage {{module}}; insufficient permissions"
|
||||
|
||||
54
server/federation/service/mapper.go
Normal file
54
server/federation/service/mapper.go
Normal file
@@ -0,0 +1,54 @@
|
||||
package service
|
||||
|
||||
import (
|
||||
ct "github.com/cortezaproject/corteza/server/compose/types"
|
||||
"github.com/cortezaproject/corteza/server/federation/types"
|
||||
)
|
||||
|
||||
type (
|
||||
Mapper struct{}
|
||||
)
|
||||
|
||||
// Merge copies the values from originating structure
|
||||
// do the destination
|
||||
//
|
||||
// mostly, there will be less mapped fields on the destination
|
||||
// side, so start looping from here
|
||||
func (m *Mapper) Merge(in *ct.RecordValueSet, out *ct.RecordValueSet, mappings *types.ModuleFieldMappingSet) {
|
||||
var match *types.ModuleFieldMapping
|
||||
|
||||
for _, destVal := range *out {
|
||||
// preset the value, since we're working with
|
||||
// a pointer to an existing structure, could be some leftovers
|
||||
destVal.Value = ""
|
||||
|
||||
// find origin field
|
||||
if match, _ = mappings.FindByName(destVal.Name, types.ModuleFieldMappingSetFindTypeDestination); match == nil {
|
||||
continue
|
||||
}
|
||||
|
||||
// use the found origin, so the mapping is correct
|
||||
for _, origVal := range *in {
|
||||
if origVal.Name == match.Origin.Name {
|
||||
destVal.Value = origVal.Value
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
// Prepare creates a set of Records to be used later
|
||||
// when the fields will be mapped via Merge()
|
||||
func (m *Mapper) Prepare(mappings types.ModuleFieldMappingSet) (out ct.RecordValueSet) {
|
||||
for _, mm := range mappings {
|
||||
rv := &ct.RecordValue{
|
||||
Name: mm.Destination.Name,
|
||||
Value: "",
|
||||
}
|
||||
out = append(out, rv)
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
97
server/federation/service/mapper_test.go
Normal file
97
server/federation/service/mapper_test.go
Normal file
@@ -0,0 +1,97 @@
|
||||
package service
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"testing"
|
||||
|
||||
ct "github.com/cortezaproject/corteza/server/compose/types"
|
||||
"github.com/cortezaproject/corteza/server/federation/types"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func TestMapper_merge(t *testing.T) {
|
||||
var (
|
||||
tcc = []struct {
|
||||
name string
|
||||
m string
|
||||
in ct.RecordValueSet
|
||||
out ct.RecordValueSet
|
||||
expect interface{}
|
||||
}{
|
||||
{
|
||||
"merge_missing_destination_field",
|
||||
`[{"origin":{"kind":"String","name":"Description","label":"Description","isMulti":false},"destination":{"kind":"String","name":"Name","label":"Description","isMulti":false}},{"origin":{"kind":"Url","name":"Facebook","label":"Facebook","isMulti":false},"destination":{"kind":"Url","name":"Fb","label":"Facebook","isMulti":false}}]`,
|
||||
ct.RecordValueSet{&ct.RecordValue{Name: "Facebook", Value: "https://fb.com/user_1"}, &ct.RecordValue{Name: "Phone", Value: "000 111 222"}, &ct.RecordValue{Name: "Twitter", Value: "https://twitter.com/@russian_bot_0"}},
|
||||
ct.RecordValueSet{&ct.RecordValue{Name: "Name", Value: ""}, &ct.RecordValue{Name: "Fb", Value: ""}},
|
||||
ct.RecordValueSet{&ct.RecordValue{Name: "Name", Value: ""}, &ct.RecordValue{Name: "Fb", Value: "https://fb.com/user_1"}},
|
||||
},
|
||||
{
|
||||
"merge_empty_origin",
|
||||
`[{"origin":{"kind":"String","name":"Description","label":"Description","isMulti":false},"destination":{"kind":"String","name":"Name","label":"Description","isMulti":false}},{"origin":{"kind":"Url","name":"Facebook","label":"Facebook","isMulti":false},"destination":{"kind":"Url","name":"Fb","label":"Facebook","isMulti":false}}]`,
|
||||
ct.RecordValueSet{},
|
||||
ct.RecordValueSet{&ct.RecordValue{Name: "Name", Value: ""}, &ct.RecordValue{Name: "Fb", Value: ""}},
|
||||
ct.RecordValueSet{&ct.RecordValue{Name: "Name", Value: ""}, &ct.RecordValue{Name: "Fb", Value: ""}},
|
||||
},
|
||||
{
|
||||
"merge_empty_destination",
|
||||
`[{"origin":{"kind":"String","name":"Description","label":"Description","isMulti":false},"destination":{"kind":"String","name":"Name","label":"Description","isMulti":false}},{"origin":{"kind":"Url","name":"Facebook","label":"Facebook","isMulti":false},"destination":{"kind":"Url","name":"Fb","label":"Facebook","isMulti":false}}]`,
|
||||
ct.RecordValueSet{&ct.RecordValue{Name: "Facebook", Value: "https://fb.com/user_1"}, &ct.RecordValue{Name: "Phone", Value: "000 111 222"}, &ct.RecordValue{Name: "Twitter", Value: "https://twitter.com/@russian_bot_0"}},
|
||||
ct.RecordValueSet{},
|
||||
ct.RecordValueSet{},
|
||||
},
|
||||
}
|
||||
)
|
||||
|
||||
for _, tc := range tcc {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
var (
|
||||
req = require.New(t)
|
||||
mapper = &Mapper{}
|
||||
)
|
||||
|
||||
// dont catch any helper errors
|
||||
mm := &types.ModuleFieldMappingSet{}
|
||||
json.Unmarshal([]byte(tc.m), mm)
|
||||
|
||||
mapper.Merge(&tc.in, &tc.out, mm)
|
||||
req.Equal(tc.out, tc.expect)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestMapper_prepare(t *testing.T) {
|
||||
var (
|
||||
tcc = []struct {
|
||||
name string
|
||||
m string
|
||||
expect ct.RecordValueSet
|
||||
}{
|
||||
{
|
||||
"prepare values",
|
||||
`[
|
||||
{"origin":{"kind":"String","name":"AccountName","label":"Account Name","isMulti":false},"destination":{"kind":"String","name":"AccountName","label":"Account Name","isMulti":false}},
|
||||
{"origin":{"kind":"String","name":"Phone","label":"Phone","isMulti":false},"destination":{"kind":"String","name":"Phone","label":"Phone","isMulti":false}},
|
||||
{"origin":{"kind":"Url","name":"Twitter","label":"Twitter","isMulti":false},"destination":{"kind":"Url","name":"Twitter","label":"Twitter","isMulti":false}}
|
||||
]`,
|
||||
ct.RecordValueSet{&ct.RecordValue{Name: "AccountName", Value: ""}, &ct.RecordValue{Name: "Phone", Value: ""}, &ct.RecordValue{Name: "Twitter", Value: ""}},
|
||||
},
|
||||
}
|
||||
)
|
||||
|
||||
for _, tc := range tcc {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
var (
|
||||
req = require.New(t)
|
||||
|
||||
mapper = &Mapper{}
|
||||
)
|
||||
|
||||
// dont catch any helper errors
|
||||
mm := &types.ModuleFieldMappingSet{}
|
||||
json.Unmarshal([]byte(tc.m), mm)
|
||||
|
||||
out := mapper.Prepare(*mm)
|
||||
req.Equal(tc.expect, out)
|
||||
})
|
||||
}
|
||||
}
|
||||
219
server/federation/service/module_mapping.go
Normal file
219
server/federation/service/module_mapping.go
Normal file
@@ -0,0 +1,219 @@
|
||||
package service
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
cs "github.com/cortezaproject/corteza/server/compose/service"
|
||||
ct "github.com/cortezaproject/corteza/server/compose/types"
|
||||
"github.com/cortezaproject/corteza/server/federation/types"
|
||||
"github.com/cortezaproject/corteza/server/pkg/actionlog"
|
||||
"github.com/cortezaproject/corteza/server/store"
|
||||
)
|
||||
|
||||
type (
|
||||
moduleMapping struct {
|
||||
store store.Storer
|
||||
node node
|
||||
ac moduleMappingAccessController
|
||||
module cs.ModuleService
|
||||
smodule SharedModuleService
|
||||
namespace cs.NamespaceService
|
||||
actionlog actionlog.Recorder
|
||||
}
|
||||
|
||||
moduleMappingAccessController interface {
|
||||
CanMapSharedModule(ctx context.Context, r *types.SharedModule) bool
|
||||
}
|
||||
|
||||
ModuleMappingService interface {
|
||||
Find(ctx context.Context, filter types.ModuleMappingFilter) (types.ModuleMappingSet, types.ModuleMappingFilter, error)
|
||||
FindByID(ctx context.Context, federationModuleID uint64) (*types.ModuleMapping, error)
|
||||
Create(ctx context.Context, new *types.ModuleMapping) (*types.ModuleMapping, error)
|
||||
Update(ctx context.Context, updated *types.ModuleMapping) (*types.ModuleMapping, error)
|
||||
}
|
||||
|
||||
moduleMappingUpdateHandler func(ctx context.Context, c *types.ModuleMapping) (bool, bool, error)
|
||||
)
|
||||
|
||||
func ModuleMapping() *moduleMapping {
|
||||
return &moduleMapping{
|
||||
ac: DefaultAccessControl,
|
||||
node: *DefaultNode,
|
||||
store: DefaultStore,
|
||||
actionlog: DefaultActionlog,
|
||||
smodule: DefaultSharedModule,
|
||||
module: cs.DefaultModule,
|
||||
namespace: cs.DefaultNamespace,
|
||||
}
|
||||
}
|
||||
|
||||
func (svc moduleMapping) FindByID(ctx context.Context, federationModuleID uint64) (mm *types.ModuleMapping, err error) {
|
||||
err = func() error {
|
||||
var (
|
||||
sm *types.SharedModule
|
||||
)
|
||||
|
||||
if mm, err = store.LookupFederationModuleMappingByFederationModuleID(ctx, svc.store, federationModuleID); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// fetch shared module for access check
|
||||
if sm, err = svc.smodule.FindByID(ctx, mm.NodeID, federationModuleID); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if !svc.ac.CanMapSharedModule(ctx, sm) {
|
||||
return ModuleMappingErrNotAllowedToMap()
|
||||
}
|
||||
|
||||
return nil
|
||||
}()
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
func (svc moduleMapping) Find(ctx context.Context, filter types.ModuleMappingFilter) (set types.ModuleMappingSet, f types.ModuleMappingFilter, err error) {
|
||||
// @todo - optimise this access check
|
||||
filter.Check = func(res *types.ModuleMapping) (bool, error) {
|
||||
// fetch shared module for this
|
||||
sm, err := svc.smodule.FindByID(ctx, res.NodeID, res.FederationModuleID)
|
||||
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
|
||||
if !svc.ac.CanMapSharedModule(ctx, sm) {
|
||||
return false, ModuleMappingErrNotAllowedToMap()
|
||||
}
|
||||
|
||||
return true, nil
|
||||
}
|
||||
|
||||
err = func() error {
|
||||
if set, f, err = store.SearchFederationModuleMappings(ctx, svc.store, filter); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}()
|
||||
|
||||
set.Walk(func(mm *types.ModuleMapping) error {
|
||||
mm.NodeID = f.NodeID
|
||||
return nil
|
||||
})
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
func (svc moduleMapping) Create(ctx context.Context, new *types.ModuleMapping) (*types.ModuleMapping, error) {
|
||||
var (
|
||||
aProps = &moduleMappingActionProps{created: new}
|
||||
)
|
||||
|
||||
err := store.Tx(ctx, svc.store, func(ctx context.Context, s store.Storer) (err error) {
|
||||
var (
|
||||
m *ct.Module
|
||||
sm *types.SharedModule
|
||||
)
|
||||
|
||||
if _, err := svc.namespace.FindByID(ctx, new.ComposeNamespaceID); err != nil {
|
||||
return ModuleMappingErrComposeNamespaceNotFound()
|
||||
}
|
||||
|
||||
if _, err = svc.node.FindByID(ctx, new.NodeID); err != nil {
|
||||
return ModuleMappingErrNodeNotFound()
|
||||
}
|
||||
|
||||
// Check for federation module - compose.Module combo
|
||||
if err = svc.uniqueCheck(ctx, new); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if m, err = svc.module.FindByID(ctx, new.ComposeNamespaceID, new.ComposeModuleID); err != nil {
|
||||
return ModuleMappingErrComposeModuleNotFound()
|
||||
}
|
||||
|
||||
if sm, err = svc.smodule.FindByID(ctx, new.NodeID, new.FederationModuleID); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if !svc.ac.CanMapSharedModule(ctx, sm) {
|
||||
return ModuleMappingErrNotAllowedToMap()
|
||||
}
|
||||
|
||||
if err = store.CreateFederationModuleMapping(ctx, s, new); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// set labels
|
||||
AddFederationLabel(m, "federation", "")
|
||||
|
||||
if _, err := svc.module.Update(ctx, m); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
})
|
||||
|
||||
return new, svc.recordAction(ctx, aProps, ModuleMappingActionCreate, err)
|
||||
}
|
||||
|
||||
func (svc moduleMapping) Update(ctx context.Context, updated *types.ModuleMapping) (*types.ModuleMapping, error) {
|
||||
var (
|
||||
aProps = &moduleMappingActionProps{changed: updated}
|
||||
)
|
||||
|
||||
err := store.Tx(ctx, svc.store, func(ctx context.Context, s store.Storer) (err error) {
|
||||
var (
|
||||
m *ct.Module
|
||||
sm *types.SharedModule
|
||||
)
|
||||
|
||||
if _, err := svc.namespace.FindByID(ctx, updated.ComposeNamespaceID); err != nil {
|
||||
return ModuleMappingErrComposeNamespaceNotFound()
|
||||
}
|
||||
|
||||
if m, err = svc.module.FindByID(ctx, updated.ComposeNamespaceID, updated.ComposeModuleID); err != nil {
|
||||
return ModuleMappingErrComposeModuleNotFound()
|
||||
}
|
||||
|
||||
if sm, err = svc.smodule.FindByID(ctx, updated.NodeID, updated.FederationModuleID); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if !svc.ac.CanMapSharedModule(ctx, sm) {
|
||||
return ModuleMappingErrNotAllowedToMap()
|
||||
}
|
||||
|
||||
if err = store.UpdateFederationModuleMapping(ctx, s, updated); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// set labels
|
||||
AddFederationLabel(m, "federation", "")
|
||||
|
||||
if _, err := svc.module.Update(ctx, m); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
})
|
||||
|
||||
return updated, svc.recordAction(ctx, aProps, ModuleMappingActionUpdate, err)
|
||||
}
|
||||
|
||||
func (svc moduleMapping) uniqueCheck(ctx context.Context, m *types.ModuleMapping) (err error) {
|
||||
f := types.ModuleMappingFilter{
|
||||
FederationModuleID: m.FederationModuleID,
|
||||
ComposeModuleID: m.ComposeModuleID,
|
||||
ComposeNamespaceID: m.ComposeNamespaceID,
|
||||
}
|
||||
|
||||
if set, _, err := store.SearchFederationModuleMappings(ctx, svc.store, f); len(set) > 0 && err == nil {
|
||||
return ModuleMappingErrModuleMappingExists()
|
||||
} else if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return err
|
||||
}
|
||||
632
server/federation/service/module_mapping_actions.gen.go
generated
Normal file
632
server/federation/service/module_mapping_actions.gen.go
generated
Normal file
@@ -0,0 +1,632 @@
|
||||
package service
|
||||
|
||||
// This file is auto-generated.
|
||||
//
|
||||
// Changes to this file may cause incorrect behavior and will be lost if
|
||||
// the code is regenerated.
|
||||
//
|
||||
// Definitions file that controls how this file is generated:
|
||||
// federation/service/module_mapping_actions.yaml
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"github.com/cortezaproject/corteza/server/federation/types"
|
||||
"github.com/cortezaproject/corteza/server/pkg/actionlog"
|
||||
"github.com/cortezaproject/corteza/server/pkg/errors"
|
||||
"github.com/cortezaproject/corteza/server/pkg/locale"
|
||||
"strings"
|
||||
"time"
|
||||
)
|
||||
|
||||
type (
|
||||
moduleMappingActionProps struct {
|
||||
created *types.ModuleMapping
|
||||
mapping *types.ModuleMapping
|
||||
changed *types.ModuleMapping
|
||||
filter *types.ModuleMappingFilter
|
||||
}
|
||||
|
||||
moduleMappingAction struct {
|
||||
timestamp time.Time
|
||||
resource string
|
||||
action string
|
||||
log string
|
||||
severity actionlog.Severity
|
||||
|
||||
// prefix for error when action fails
|
||||
errorMessage string
|
||||
|
||||
props *moduleMappingActionProps
|
||||
}
|
||||
|
||||
moduleMappingLogMetaKey struct{}
|
||||
moduleMappingPropsMetaKey struct{}
|
||||
)
|
||||
|
||||
var (
|
||||
// just a placeholder to cover template cases w/o fmt package use
|
||||
_ = fmt.Println
|
||||
)
|
||||
|
||||
// *********************************************************************************************************************
|
||||
// *********************************************************************************************************************
|
||||
// Props methods
|
||||
// setCreated updates moduleMappingActionProps's created
|
||||
//
|
||||
// This function is auto-generated.
|
||||
func (p *moduleMappingActionProps) setCreated(created *types.ModuleMapping) *moduleMappingActionProps {
|
||||
p.created = created
|
||||
return p
|
||||
}
|
||||
|
||||
// setMapping updates moduleMappingActionProps's mapping
|
||||
//
|
||||
// This function is auto-generated.
|
||||
func (p *moduleMappingActionProps) setMapping(mapping *types.ModuleMapping) *moduleMappingActionProps {
|
||||
p.mapping = mapping
|
||||
return p
|
||||
}
|
||||
|
||||
// setChanged updates moduleMappingActionProps's changed
|
||||
//
|
||||
// This function is auto-generated.
|
||||
func (p *moduleMappingActionProps) setChanged(changed *types.ModuleMapping) *moduleMappingActionProps {
|
||||
p.changed = changed
|
||||
return p
|
||||
}
|
||||
|
||||
// setFilter updates moduleMappingActionProps's filter
|
||||
//
|
||||
// This function is auto-generated.
|
||||
func (p *moduleMappingActionProps) setFilter(filter *types.ModuleMappingFilter) *moduleMappingActionProps {
|
||||
p.filter = filter
|
||||
return p
|
||||
}
|
||||
|
||||
// Serialize converts moduleMappingActionProps to actionlog.Meta
|
||||
//
|
||||
// This function is auto-generated.
|
||||
func (p moduleMappingActionProps) Serialize() actionlog.Meta {
|
||||
var (
|
||||
m = make(actionlog.Meta)
|
||||
)
|
||||
|
||||
if p.created != nil {
|
||||
m.Set("created.FederationModuleID", p.created.FederationModuleID, true)
|
||||
m.Set("created.ComposeModuleID", p.created.ComposeModuleID, true)
|
||||
}
|
||||
if p.mapping != nil {
|
||||
m.Set("mapping.FederationModuleID", p.mapping.FederationModuleID, true)
|
||||
m.Set("mapping.ComposeModuleID", p.mapping.ComposeModuleID, true)
|
||||
}
|
||||
if p.changed != nil {
|
||||
m.Set("changed.FederationModuleID", p.changed.FederationModuleID, true)
|
||||
m.Set("changed.ComposeModuleID", p.changed.ComposeModuleID, true)
|
||||
}
|
||||
if p.filter != nil {
|
||||
m.Set("filter.query", p.filter.Query, true)
|
||||
m.Set("filter.sort", p.filter.Sort, true)
|
||||
m.Set("filter.limit", p.filter.Limit, true)
|
||||
}
|
||||
|
||||
return m
|
||||
}
|
||||
|
||||
// tr translates string and replaces meta value placeholder with values
|
||||
//
|
||||
// This function is auto-generated.
|
||||
func (p moduleMappingActionProps) Format(in string, err error) string {
|
||||
var (
|
||||
pairs = []string{"{{err}}"}
|
||||
// first non-empty string
|
||||
fns = func(ii ...interface{}) string {
|
||||
for _, i := range ii {
|
||||
if s := fmt.Sprintf("%v", i); len(s) > 0 {
|
||||
return s
|
||||
}
|
||||
}
|
||||
|
||||
return ""
|
||||
}
|
||||
)
|
||||
|
||||
if err != nil {
|
||||
pairs = append(pairs, err.Error())
|
||||
} else {
|
||||
pairs = append(pairs, "nil")
|
||||
}
|
||||
|
||||
if p.created != nil {
|
||||
// replacement for "{{created}}" (in order how fields are defined)
|
||||
pairs = append(
|
||||
pairs,
|
||||
"{{created}}",
|
||||
fns(
|
||||
p.created.FederationModuleID,
|
||||
p.created.ComposeModuleID,
|
||||
),
|
||||
)
|
||||
pairs = append(pairs, "{{created.FederationModuleID}}", fns(p.created.FederationModuleID))
|
||||
pairs = append(pairs, "{{created.ComposeModuleID}}", fns(p.created.ComposeModuleID))
|
||||
}
|
||||
|
||||
if p.mapping != nil {
|
||||
// replacement for "{{mapping}}" (in order how fields are defined)
|
||||
pairs = append(
|
||||
pairs,
|
||||
"{{mapping}}",
|
||||
fns(
|
||||
p.mapping.FederationModuleID,
|
||||
p.mapping.ComposeModuleID,
|
||||
),
|
||||
)
|
||||
pairs = append(pairs, "{{mapping.FederationModuleID}}", fns(p.mapping.FederationModuleID))
|
||||
pairs = append(pairs, "{{mapping.ComposeModuleID}}", fns(p.mapping.ComposeModuleID))
|
||||
}
|
||||
|
||||
if p.changed != nil {
|
||||
// replacement for "{{changed}}" (in order how fields are defined)
|
||||
pairs = append(
|
||||
pairs,
|
||||
"{{changed}}",
|
||||
fns(
|
||||
p.changed.FederationModuleID,
|
||||
p.changed.ComposeModuleID,
|
||||
),
|
||||
)
|
||||
pairs = append(pairs, "{{changed.FederationModuleID}}", fns(p.changed.FederationModuleID))
|
||||
pairs = append(pairs, "{{changed.ComposeModuleID}}", fns(p.changed.ComposeModuleID))
|
||||
}
|
||||
|
||||
if p.filter != nil {
|
||||
// replacement for "{{filter}}" (in order how fields are defined)
|
||||
pairs = append(
|
||||
pairs,
|
||||
"{{filter}}",
|
||||
fns(
|
||||
p.filter.Query,
|
||||
p.filter.Sort,
|
||||
p.filter.Limit,
|
||||
),
|
||||
)
|
||||
pairs = append(pairs, "{{filter.query}}", fns(p.filter.Query))
|
||||
pairs = append(pairs, "{{filter.sort}}", fns(p.filter.Sort))
|
||||
pairs = append(pairs, "{{filter.limit}}", fns(p.filter.Limit))
|
||||
}
|
||||
return strings.NewReplacer(pairs...).Replace(in)
|
||||
}
|
||||
|
||||
// *********************************************************************************************************************
|
||||
// *********************************************************************************************************************
|
||||
// Action methods
|
||||
|
||||
// String returns loggable description as string
|
||||
//
|
||||
// This function is auto-generated.
|
||||
func (a *moduleMappingAction) String() string {
|
||||
var props = &moduleMappingActionProps{}
|
||||
|
||||
if a.props != nil {
|
||||
props = a.props
|
||||
}
|
||||
|
||||
return props.Format(a.log, nil)
|
||||
}
|
||||
|
||||
func (e *moduleMappingAction) ToAction() *actionlog.Action {
|
||||
return &actionlog.Action{
|
||||
Resource: e.resource,
|
||||
Action: e.action,
|
||||
Severity: e.severity,
|
||||
Description: e.String(),
|
||||
Meta: e.props.Serialize(),
|
||||
}
|
||||
}
|
||||
|
||||
// *********************************************************************************************************************
|
||||
// *********************************************************************************************************************
|
||||
// Action constructors
|
||||
|
||||
// ModuleMappingActionSearch returns "federation:module_mapping.search" action
|
||||
//
|
||||
// This function is auto-generated.
|
||||
func ModuleMappingActionSearch(props ...*moduleMappingActionProps) *moduleMappingAction {
|
||||
a := &moduleMappingAction{
|
||||
timestamp: time.Now(),
|
||||
resource: "federation:module_mapping",
|
||||
action: "search",
|
||||
log: "searched for modules",
|
||||
severity: actionlog.Info,
|
||||
}
|
||||
|
||||
if len(props) > 0 {
|
||||
a.props = props[0]
|
||||
}
|
||||
|
||||
return a
|
||||
}
|
||||
|
||||
// ModuleMappingActionLookup returns "federation:module_mapping.lookup" action
|
||||
//
|
||||
// This function is auto-generated.
|
||||
func ModuleMappingActionLookup(props ...*moduleMappingActionProps) *moduleMappingAction {
|
||||
a := &moduleMappingAction{
|
||||
timestamp: time.Now(),
|
||||
resource: "federation:module_mapping",
|
||||
action: "lookup",
|
||||
log: "looked-up for a module",
|
||||
severity: actionlog.Info,
|
||||
}
|
||||
|
||||
if len(props) > 0 {
|
||||
a.props = props[0]
|
||||
}
|
||||
|
||||
return a
|
||||
}
|
||||
|
||||
// ModuleMappingActionCreate returns "federation:module_mapping.create" action
|
||||
//
|
||||
// This function is auto-generated.
|
||||
func ModuleMappingActionCreate(props ...*moduleMappingActionProps) *moduleMappingAction {
|
||||
a := &moduleMappingAction{
|
||||
timestamp: time.Now(),
|
||||
resource: "federation:module_mapping",
|
||||
action: "create",
|
||||
log: "created module",
|
||||
severity: actionlog.Notice,
|
||||
}
|
||||
|
||||
if len(props) > 0 {
|
||||
a.props = props[0]
|
||||
}
|
||||
|
||||
return a
|
||||
}
|
||||
|
||||
// ModuleMappingActionUpdate returns "federation:module_mapping.update" action
|
||||
//
|
||||
// This function is auto-generated.
|
||||
func ModuleMappingActionUpdate(props ...*moduleMappingActionProps) *moduleMappingAction {
|
||||
a := &moduleMappingAction{
|
||||
timestamp: time.Now(),
|
||||
resource: "federation:module_mapping",
|
||||
action: "update",
|
||||
log: "updated module",
|
||||
severity: actionlog.Notice,
|
||||
}
|
||||
|
||||
if len(props) > 0 {
|
||||
a.props = props[0]
|
||||
}
|
||||
|
||||
return a
|
||||
}
|
||||
|
||||
// ModuleMappingActionDelete returns "federation:module_mapping.delete" action
|
||||
//
|
||||
// This function is auto-generated.
|
||||
func ModuleMappingActionDelete(props ...*moduleMappingActionProps) *moduleMappingAction {
|
||||
a := &moduleMappingAction{
|
||||
timestamp: time.Now(),
|
||||
resource: "federation:module_mapping",
|
||||
action: "delete",
|
||||
log: "deleted module",
|
||||
severity: actionlog.Notice,
|
||||
}
|
||||
|
||||
if len(props) > 0 {
|
||||
a.props = props[0]
|
||||
}
|
||||
|
||||
return a
|
||||
}
|
||||
|
||||
// *********************************************************************************************************************
|
||||
// *********************************************************************************************************************
|
||||
// Error constructors
|
||||
|
||||
// ModuleMappingErrGeneric returns "federation:module_mapping.generic" as *errors.Error
|
||||
//
|
||||
// This function is auto-generated.
|
||||
func ModuleMappingErrGeneric(mm ...*moduleMappingActionProps) *errors.Error {
|
||||
var p = &moduleMappingActionProps{}
|
||||
if len(mm) > 0 {
|
||||
p = mm[0]
|
||||
}
|
||||
|
||||
var e = errors.New(
|
||||
errors.KindInternal,
|
||||
|
||||
p.Format("failed to complete request due to internal error", nil),
|
||||
|
||||
errors.Meta("type", "generic"),
|
||||
errors.Meta("resource", "federation:module_mapping"),
|
||||
|
||||
// action log entry; no formatting, it will be applied inside recordAction fn.
|
||||
errors.Meta(moduleMappingLogMetaKey{}, "{err}"),
|
||||
errors.Meta(moduleMappingPropsMetaKey{}, p),
|
||||
|
||||
// translation namespace & key
|
||||
errors.Meta(locale.ErrorMetaNamespace{}, "federation"),
|
||||
errors.Meta(locale.ErrorMetaKey{}, "module-mapping.errors.generic"),
|
||||
|
||||
errors.StackSkip(1),
|
||||
)
|
||||
|
||||
if len(mm) > 0 {
|
||||
}
|
||||
|
||||
return e
|
||||
}
|
||||
|
||||
// ModuleMappingErrNotFound returns "federation:module_mapping.notFound" as *errors.Error
|
||||
//
|
||||
// This function is auto-generated.
|
||||
func ModuleMappingErrNotFound(mm ...*moduleMappingActionProps) *errors.Error {
|
||||
var p = &moduleMappingActionProps{}
|
||||
if len(mm) > 0 {
|
||||
p = mm[0]
|
||||
}
|
||||
|
||||
var e = errors.New(
|
||||
errors.KindInternal,
|
||||
|
||||
p.Format("module mapping does not exist", nil),
|
||||
|
||||
errors.Meta("type", "notFound"),
|
||||
errors.Meta("resource", "federation:module_mapping"),
|
||||
|
||||
errors.Meta(moduleMappingPropsMetaKey{}, p),
|
||||
|
||||
// translation namespace & key
|
||||
errors.Meta(locale.ErrorMetaNamespace{}, "federation"),
|
||||
errors.Meta(locale.ErrorMetaKey{}, "module-mapping.errors.notFound"),
|
||||
|
||||
errors.StackSkip(1),
|
||||
)
|
||||
|
||||
if len(mm) > 0 {
|
||||
}
|
||||
|
||||
return e
|
||||
}
|
||||
|
||||
// ModuleMappingErrComposeModuleNotFound returns "federation:module_mapping.composeModuleNotFound" as *errors.Error
|
||||
//
|
||||
// This function is auto-generated.
|
||||
func ModuleMappingErrComposeModuleNotFound(mm ...*moduleMappingActionProps) *errors.Error {
|
||||
var p = &moduleMappingActionProps{}
|
||||
if len(mm) > 0 {
|
||||
p = mm[0]
|
||||
}
|
||||
|
||||
var e = errors.New(
|
||||
errors.KindInternal,
|
||||
|
||||
p.Format("compose module not found", nil),
|
||||
|
||||
errors.Meta("type", "composeModuleNotFound"),
|
||||
errors.Meta("resource", "federation:module_mapping"),
|
||||
|
||||
errors.Meta(moduleMappingPropsMetaKey{}, p),
|
||||
|
||||
// translation namespace & key
|
||||
errors.Meta(locale.ErrorMetaNamespace{}, "federation"),
|
||||
errors.Meta(locale.ErrorMetaKey{}, "module-mapping.errors.composeModuleNotFound"),
|
||||
|
||||
errors.StackSkip(1),
|
||||
)
|
||||
|
||||
if len(mm) > 0 {
|
||||
}
|
||||
|
||||
return e
|
||||
}
|
||||
|
||||
// ModuleMappingErrComposeNamespaceNotFound returns "federation:module_mapping.composeNamespaceNotFound" as *errors.Error
|
||||
//
|
||||
// This function is auto-generated.
|
||||
func ModuleMappingErrComposeNamespaceNotFound(mm ...*moduleMappingActionProps) *errors.Error {
|
||||
var p = &moduleMappingActionProps{}
|
||||
if len(mm) > 0 {
|
||||
p = mm[0]
|
||||
}
|
||||
|
||||
var e = errors.New(
|
||||
errors.KindInternal,
|
||||
|
||||
p.Format("compose namespace not found", nil),
|
||||
|
||||
errors.Meta("type", "composeNamespaceNotFound"),
|
||||
errors.Meta("resource", "federation:module_mapping"),
|
||||
|
||||
errors.Meta(moduleMappingPropsMetaKey{}, p),
|
||||
|
||||
// translation namespace & key
|
||||
errors.Meta(locale.ErrorMetaNamespace{}, "federation"),
|
||||
errors.Meta(locale.ErrorMetaKey{}, "module-mapping.errors.composeNamespaceNotFound"),
|
||||
|
||||
errors.StackSkip(1),
|
||||
)
|
||||
|
||||
if len(mm) > 0 {
|
||||
}
|
||||
|
||||
return e
|
||||
}
|
||||
|
||||
// ModuleMappingErrFederationModuleNotFound returns "federation:module_mapping.federationModuleNotFound" as *errors.Error
|
||||
//
|
||||
// This function is auto-generated.
|
||||
func ModuleMappingErrFederationModuleNotFound(mm ...*moduleMappingActionProps) *errors.Error {
|
||||
var p = &moduleMappingActionProps{}
|
||||
if len(mm) > 0 {
|
||||
p = mm[0]
|
||||
}
|
||||
|
||||
var e = errors.New(
|
||||
errors.KindInternal,
|
||||
|
||||
p.Format("federation module not found", nil),
|
||||
|
||||
errors.Meta("type", "federationModuleNotFound"),
|
||||
errors.Meta("resource", "federation:module_mapping"),
|
||||
|
||||
errors.Meta(moduleMappingPropsMetaKey{}, p),
|
||||
|
||||
// translation namespace & key
|
||||
errors.Meta(locale.ErrorMetaNamespace{}, "federation"),
|
||||
errors.Meta(locale.ErrorMetaKey{}, "module-mapping.errors.federationModuleNotFound"),
|
||||
|
||||
errors.StackSkip(1),
|
||||
)
|
||||
|
||||
if len(mm) > 0 {
|
||||
}
|
||||
|
||||
return e
|
||||
}
|
||||
|
||||
// ModuleMappingErrNodeNotFound returns "federation:module_mapping.nodeNotFound" as *errors.Error
|
||||
//
|
||||
// This function is auto-generated.
|
||||
func ModuleMappingErrNodeNotFound(mm ...*moduleMappingActionProps) *errors.Error {
|
||||
var p = &moduleMappingActionProps{}
|
||||
if len(mm) > 0 {
|
||||
p = mm[0]
|
||||
}
|
||||
|
||||
var e = errors.New(
|
||||
errors.KindInternal,
|
||||
|
||||
p.Format("node does not exist", nil),
|
||||
|
||||
errors.Meta("type", "nodeNotFound"),
|
||||
errors.Meta("resource", "federation:module_mapping"),
|
||||
|
||||
errors.Meta(moduleMappingPropsMetaKey{}, p),
|
||||
|
||||
// translation namespace & key
|
||||
errors.Meta(locale.ErrorMetaNamespace{}, "federation"),
|
||||
errors.Meta(locale.ErrorMetaKey{}, "module-mapping.errors.nodeNotFound"),
|
||||
|
||||
errors.StackSkip(1),
|
||||
)
|
||||
|
||||
if len(mm) > 0 {
|
||||
}
|
||||
|
||||
return e
|
||||
}
|
||||
|
||||
// ModuleMappingErrModuleMappingExists returns "federation:module_mapping.moduleMappingExists" as *errors.Error
|
||||
//
|
||||
// This function is auto-generated.
|
||||
func ModuleMappingErrModuleMappingExists(mm ...*moduleMappingActionProps) *errors.Error {
|
||||
var p = &moduleMappingActionProps{}
|
||||
if len(mm) > 0 {
|
||||
p = mm[0]
|
||||
}
|
||||
|
||||
var e = errors.New(
|
||||
errors.KindInternal,
|
||||
|
||||
p.Format("module mapping already exists", nil),
|
||||
|
||||
errors.Meta("type", "moduleMappingExists"),
|
||||
errors.Meta("resource", "federation:module_mapping"),
|
||||
|
||||
errors.Meta(moduleMappingPropsMetaKey{}, p),
|
||||
|
||||
// translation namespace & key
|
||||
errors.Meta(locale.ErrorMetaNamespace{}, "federation"),
|
||||
errors.Meta(locale.ErrorMetaKey{}, "module-mapping.errors.moduleMappingExists"),
|
||||
|
||||
errors.StackSkip(1),
|
||||
)
|
||||
|
||||
if len(mm) > 0 {
|
||||
}
|
||||
|
||||
return e
|
||||
}
|
||||
|
||||
// ModuleMappingErrNotAllowedToMap returns "federation:module_mapping.notAllowedToMap" as *errors.Error
|
||||
//
|
||||
// This function is auto-generated.
|
||||
func ModuleMappingErrNotAllowedToMap(mm ...*moduleMappingActionProps) *errors.Error {
|
||||
var p = &moduleMappingActionProps{}
|
||||
if len(mm) > 0 {
|
||||
p = mm[0]
|
||||
}
|
||||
|
||||
var e = errors.New(
|
||||
errors.KindInternal,
|
||||
|
||||
p.Format("not allowed to map this module", nil),
|
||||
|
||||
errors.Meta("type", "notAllowedToMap"),
|
||||
errors.Meta("resource", "federation:module_mapping"),
|
||||
|
||||
// action log entry; no formatting, it will be applied inside recordAction fn.
|
||||
errors.Meta(moduleMappingLogMetaKey{}, "could not manage mapping; insufficient permissions"),
|
||||
errors.Meta(moduleMappingPropsMetaKey{}, p),
|
||||
|
||||
// translation namespace & key
|
||||
errors.Meta(locale.ErrorMetaNamespace{}, "federation"),
|
||||
errors.Meta(locale.ErrorMetaKey{}, "module-mapping.errors.notAllowedToMap"),
|
||||
|
||||
errors.StackSkip(1),
|
||||
)
|
||||
|
||||
if len(mm) > 0 {
|
||||
}
|
||||
|
||||
return e
|
||||
}
|
||||
|
||||
// *********************************************************************************************************************
|
||||
// *********************************************************************************************************************
|
||||
|
||||
// recordAction is a service helper function wraps function that can return error
|
||||
//
|
||||
// It will wrap unrecognized/internal errors with generic errors.
|
||||
//
|
||||
// This function is auto-generated.
|
||||
func (svc moduleMapping) recordAction(ctx context.Context, props *moduleMappingActionProps, actionFn func(...*moduleMappingActionProps) *moduleMappingAction, err error) error {
|
||||
if svc.actionlog == nil || actionFn == nil {
|
||||
// action log disabled or no action fn passed, return error as-is
|
||||
return err
|
||||
} else if err == nil {
|
||||
// action completed w/o error, record it
|
||||
svc.actionlog.Record(ctx, actionFn(props).ToAction())
|
||||
return nil
|
||||
}
|
||||
|
||||
a := actionFn(props).ToAction()
|
||||
|
||||
// Extracting error information and recording it as action
|
||||
a.Error = err.Error()
|
||||
|
||||
switch c := err.(type) {
|
||||
case *errors.Error:
|
||||
m := c.Meta()
|
||||
|
||||
a.Error = err.Error()
|
||||
a.Severity = actionlog.Severity(m.AsInt("severity"))
|
||||
a.Description = props.Format(m.AsString(moduleMappingLogMetaKey{}), err)
|
||||
|
||||
if p, has := m[moduleMappingPropsMetaKey{}]; has {
|
||||
a.Meta = p.(*moduleMappingActionProps).Serialize()
|
||||
}
|
||||
|
||||
svc.actionlog.Record(ctx, a)
|
||||
default:
|
||||
svc.actionlog.Record(ctx, a)
|
||||
}
|
||||
|
||||
// Original error is passed on
|
||||
return err
|
||||
}
|
||||
74
server/federation/service/module_mapping_actions.yaml
Normal file
74
server/federation/service/module_mapping_actions.yaml
Normal file
@@ -0,0 +1,74 @@
|
||||
# List of loggable service actions
|
||||
|
||||
resource: federation:module_mapping
|
||||
service: moduleMapping
|
||||
|
||||
# Default sensitivity for actions
|
||||
defaultActionSeverity: notice
|
||||
|
||||
# default severity for errors
|
||||
defaultErrorSeverity: error
|
||||
|
||||
import:
|
||||
- github.com/cortezaproject/corteza/server/federation/types
|
||||
|
||||
props:
|
||||
- name: created
|
||||
type: "*types.ModuleMapping"
|
||||
fields: [ FederationModuleID, ComposeModuleID ]
|
||||
- name: mapping
|
||||
type: "*types.ModuleMapping"
|
||||
fields: [ FederationModuleID, ComposeModuleID ]
|
||||
- name: changed
|
||||
type: "*types.ModuleMapping"
|
||||
fields: [ FederationModuleID, ComposeModuleID ]
|
||||
- name: filter
|
||||
type: "*types.ModuleMappingFilter"
|
||||
fields: [ query, sort, limit ]
|
||||
|
||||
actions:
|
||||
- action: search
|
||||
log: "searched for modules"
|
||||
severity: info
|
||||
|
||||
- action: lookup
|
||||
log: "looked-up for a module"
|
||||
severity: info
|
||||
|
||||
- action: create
|
||||
log: "created module"
|
||||
|
||||
- action: update
|
||||
log: "updated module"
|
||||
|
||||
- action: delete
|
||||
log: "deleted module"
|
||||
|
||||
errors:
|
||||
- error: notFound
|
||||
message: "module mapping does not exist"
|
||||
severity: warning
|
||||
|
||||
- error: composeModuleNotFound
|
||||
message: "compose module not found"
|
||||
severity: "warning"
|
||||
|
||||
- error: composeNamespaceNotFound
|
||||
message: "compose namespace not found"
|
||||
severity: "warning"
|
||||
|
||||
- error: federationModuleNotFound
|
||||
message: "federation module not found"
|
||||
severity: "warning"
|
||||
|
||||
- error: nodeNotFound
|
||||
message: "node does not exist"
|
||||
severity: warning
|
||||
|
||||
- error: moduleMappingExists
|
||||
message: "module mapping already exists"
|
||||
# severity: "warning"
|
||||
|
||||
- error: notAllowedToMap
|
||||
message: "not allowed to map this module"
|
||||
log: "could not manage mapping; insufficient permissions"
|
||||
602
server/federation/service/node.go
Normal file
602
server/federation/service/node.go
Normal file
@@ -0,0 +1,602 @@
|
||||
package service
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"github.com/cortezaproject/corteza/server/pkg/errors"
|
||||
|
||||
"github.com/cortezaproject/corteza/server/federation/types"
|
||||
"github.com/cortezaproject/corteza/server/pkg/actionlog"
|
||||
"github.com/cortezaproject/corteza/server/pkg/auth"
|
||||
"github.com/cortezaproject/corteza/server/pkg/options"
|
||||
"github.com/cortezaproject/corteza/server/pkg/rand"
|
||||
"github.com/cortezaproject/corteza/server/store"
|
||||
"github.com/cortezaproject/corteza/server/system/service"
|
||||
sysTypes "github.com/cortezaproject/corteza/server/system/types"
|
||||
)
|
||||
|
||||
const (
|
||||
TokenLength = 32
|
||||
)
|
||||
|
||||
type (
|
||||
tokenIssuer func(context.Context, auth.Identifiable) (token []byte, err error)
|
||||
|
||||
node struct {
|
||||
store store.Storer
|
||||
sysUser service.UserService
|
||||
|
||||
actionlog actionlog.Recorder
|
||||
|
||||
tokenIssuer tokenIssuer
|
||||
|
||||
name string
|
||||
host string
|
||||
baseURL string
|
||||
|
||||
handshaker nodeHandshaker
|
||||
|
||||
ac nodeAccessController
|
||||
}
|
||||
|
||||
nodeAccessController interface {
|
||||
CanPair(ctx context.Context) bool
|
||||
CanSearchNodes(ctx context.Context) bool
|
||||
CanCreateNode(ctx context.Context) bool
|
||||
CanManageNode(ctx context.Context, r *types.Node) bool
|
||||
}
|
||||
|
||||
nodeUpdateHandler func(ctx context.Context, n *types.Node) error
|
||||
|
||||
nodeHandshaker interface {
|
||||
Init(ctx context.Context, n *types.Node, authToken string) error
|
||||
Complete(ctx context.Context, n *types.Node, authToken string) error
|
||||
}
|
||||
)
|
||||
|
||||
func Node(s store.Storer, u service.UserService, al actionlog.Recorder, th tokenIssuer, options options.FederationOpt, sopt options.HttpServerOpt, ac nodeAccessController) *node {
|
||||
return &node{
|
||||
store: s,
|
||||
sysUser: u,
|
||||
actionlog: al,
|
||||
tokenIssuer: th,
|
||||
ac: ac,
|
||||
name: options.Label,
|
||||
host: options.Host,
|
||||
|
||||
baseURL: fmt.Sprintf("%s/federation", strings.TrimRight(sopt.ApiBaseUrl, "/")),
|
||||
|
||||
handshaker: HttpHandshake(http.DefaultClient),
|
||||
}
|
||||
}
|
||||
|
||||
func (svc *node) SetHandshaker(h nodeHandshaker) {
|
||||
svc.handshaker = h
|
||||
}
|
||||
|
||||
func (svc node) Search(ctx context.Context, filter types.NodeFilter) (set types.NodeSet, f types.NodeFilter, err error) {
|
||||
var (
|
||||
aProps = &nodeActionProps{filter: &filter}
|
||||
)
|
||||
|
||||
filter.Check = func(res *types.Node) (bool, error) {
|
||||
if !svc.ac.CanManageNode(ctx, res) {
|
||||
return false, NodeErrNotAllowedToManage()
|
||||
}
|
||||
|
||||
return true, nil
|
||||
}
|
||||
|
||||
err = func() error {
|
||||
if !svc.ac.CanSearchNodes(ctx) {
|
||||
return NodeErrNotAllowedToSearch()
|
||||
}
|
||||
|
||||
if set, f, err = store.SearchFederationNodes(ctx, svc.store, filter); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}()
|
||||
|
||||
return set, f, svc.recordAction(ctx, aProps, NodeActionSearch, err)
|
||||
}
|
||||
|
||||
// Create is used on server A to create federation with server B
|
||||
func (svc node) Create(ctx context.Context, new *types.Node) (*types.Node, error) {
|
||||
var (
|
||||
aProps = &nodeActionProps{}
|
||||
n *types.Node
|
||||
err error
|
||||
)
|
||||
|
||||
// @todo verify new.baseURL; if it's set it must be valid URL that returns 200
|
||||
err = store.Tx(ctx, svc.store, func(ctx context.Context, s store.Storer) (err error) {
|
||||
n = &types.Node{
|
||||
ID: nextID(),
|
||||
Name: new.Name,
|
||||
BaseURL: new.BaseURL,
|
||||
Contact: new.Contact,
|
||||
Status: types.NodeStatusPending,
|
||||
CreatedAt: *now(),
|
||||
}
|
||||
|
||||
if !svc.ac.CanCreateNode(ctx) {
|
||||
return NodeErrNotAllowedToCreate()
|
||||
}
|
||||
|
||||
return store.CreateFederationNode(ctx, s, n)
|
||||
})
|
||||
|
||||
return n, svc.recordAction(ctx, aProps, NodeActionCreate, err)
|
||||
}
|
||||
|
||||
// Read is used mainly in UI, when retrieving details about the node
|
||||
func (svc node) Read(ctx context.Context, ID uint64) (*types.Node, error) {
|
||||
var (
|
||||
n, err = loadNode(ctx, svc.store, ID)
|
||||
aProps = &nodeActionProps{node: n}
|
||||
)
|
||||
|
||||
// permission takes precedence over any db error
|
||||
if n != nil && !svc.ac.CanManageNode(ctx, n) {
|
||||
err = NodeErrNotAllowedToManage()
|
||||
}
|
||||
|
||||
return n, svc.recordAction(ctx, aProps, NodeActionCreate, err)
|
||||
}
|
||||
|
||||
// CreateFromURI is used on server B to create federation with server A
|
||||
func (svc node) CreateFromPairingURI(ctx context.Context, uri string) (n *types.Node, err error) {
|
||||
var (
|
||||
existing *types.Node
|
||||
aProps = &nodeActionProps{pairingURI: uri}
|
||||
)
|
||||
|
||||
err = func(ctx context.Context) error {
|
||||
if !svc.ac.CanPair(ctx) {
|
||||
return NodeErrNotAllowedToPair()
|
||||
}
|
||||
|
||||
n, err = svc.decodePairingURI(uri)
|
||||
|
||||
return err
|
||||
}(ctx)
|
||||
|
||||
if err != nil {
|
||||
return nil, svc.recordAction(ctx, aProps, NodeActionCreateFromPairingURI, err)
|
||||
}
|
||||
|
||||
existing, err = store.LookupFederationNodeByBaseURLSharedNodeID(ctx, svc.store, n.BaseURL, n.SharedNodeID)
|
||||
aProps.setNode(existing)
|
||||
|
||||
if errors.Is(err, store.ErrNotFound) {
|
||||
if !svc.ac.CanCreateNode(ctx) {
|
||||
return n, svc.recordAction(ctx, aProps, NodeActionCreateFromPairingURI, NodeErrNotAllowedToCreate())
|
||||
}
|
||||
|
||||
n.ID = nextID()
|
||||
n.CreatedAt = *now()
|
||||
n.UpdatedBy = auth.GetIdentityFromContext(ctx).Identity()
|
||||
n.Status = types.NodeStatusPending
|
||||
|
||||
err = store.CreateFederationNode(ctx, svc.store, n)
|
||||
return n, svc.recordAction(ctx, aProps, NodeActionCreateFromPairingURI, err)
|
||||
|
||||
} else if err != nil {
|
||||
return nil, svc.recordAction(ctx, aProps, NodeActionCreateFromPairingURI, err)
|
||||
} else {
|
||||
if !svc.ac.CanManageNode(ctx, n) {
|
||||
return n, svc.recordAction(ctx, aProps, NodeActionCreateFromPairingURI, NodeErrNotAllowedToManage())
|
||||
}
|
||||
|
||||
// Node with the same sharing ID and domain already exists
|
||||
// so we'll reset status and tokens
|
||||
|
||||
existing.Status = types.NodeStatusPending
|
||||
|
||||
// Remove existing auth token
|
||||
existing.AuthToken = ""
|
||||
|
||||
// Reset pairing token
|
||||
existing.PairToken = n.PairToken
|
||||
existing.UpdatedBy = auth.GetIdentityFromContext(ctx).Identity()
|
||||
existing.UpdatedAt = now()
|
||||
err = store.UpdateFederationNode(ctx, svc.store, n)
|
||||
return n, svc.recordAction(ctx, aProps, NodeActionRecreateFromPairingURI, err)
|
||||
}
|
||||
}
|
||||
|
||||
// RegenerateNodeURI loads and updates node with the new OTT and returns sharable link
|
||||
func (svc node) RegenerateNodeURI(ctx context.Context, nodeID uint64) (string, error) {
|
||||
var (
|
||||
uri string
|
||||
)
|
||||
|
||||
_, err := svc.updater(
|
||||
ctx,
|
||||
nodeID,
|
||||
NodeActionOttRegenerated,
|
||||
func(ctx context.Context, n *types.Node) error {
|
||||
n.PairToken = string(rand.Bytes(TokenLength))
|
||||
return nil
|
||||
},
|
||||
func(ctx context.Context, n *types.Node) error {
|
||||
uri = svc.makePairingURI(n)
|
||||
return nil
|
||||
},
|
||||
)
|
||||
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
return uri, nil
|
||||
}
|
||||
|
||||
func (svc node) Update(ctx context.Context, upd *types.Node) (*types.Node, error) {
|
||||
return svc.updater(ctx, upd.ID, NodeActionUpdate, func(ctx context.Context, n *types.Node) error {
|
||||
n.Name = upd.Name
|
||||
n.BaseURL = upd.BaseURL
|
||||
n.Contact = upd.Contact
|
||||
|
||||
n.UpdatedBy = auth.GetIdentityFromContext(ctx).Identity()
|
||||
n.UpdatedAt = now()
|
||||
|
||||
return nil
|
||||
}, nil)
|
||||
}
|
||||
|
||||
func (svc node) DeleteByID(ctx context.Context, ID uint64) error {
|
||||
_, err := svc.updater(ctx, ID, NodeActionDelete, func(ctx context.Context, n *types.Node) error {
|
||||
n.DeletedAt = now()
|
||||
n.DeletedBy = auth.GetIdentityFromContext(ctx).Identity()
|
||||
|
||||
return nil
|
||||
}, nil)
|
||||
return err
|
||||
}
|
||||
|
||||
func (svc node) UndeleteByID(ctx context.Context, ID uint64) error {
|
||||
_, err := svc.updater(ctx, ID, NodeActionUndelete, func(ctx context.Context, n *types.Node) error {
|
||||
n.DeletedAt = nil
|
||||
n.DeletedBy = 0
|
||||
|
||||
return nil
|
||||
}, nil)
|
||||
return err
|
||||
}
|
||||
|
||||
// Pair is used on server B to send request to server A
|
||||
func (svc node) Pair(ctx context.Context, nodeID uint64) error {
|
||||
if !svc.ac.CanPair(ctx) {
|
||||
return NodeErrNotAllowedToPair()
|
||||
}
|
||||
|
||||
// elevate permissions for user lookup & creation!
|
||||
ctx = auth.SetIdentityToContext(ctx, auth.FederationUser())
|
||||
|
||||
_, err := svc.updater(
|
||||
ctx,
|
||||
nodeID,
|
||||
NodeActionPair,
|
||||
func(ctx context.Context, n *types.Node) error {
|
||||
// Handle federated user
|
||||
u, err := svc.fetchFederatedUser(ctx, n)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
var accessToken []byte
|
||||
// Generate JWT token for the federated user
|
||||
accessToken, err = svc.tokenIssuer(ctx, u)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
n.UpdatedBy = auth.GetIdentityFromContext(ctx).Identity()
|
||||
n.UpdatedAt = now()
|
||||
|
||||
// Start handshake initialization remote node
|
||||
if err = svc.handshaker.Init(ctx, n, string(accessToken)); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
n.Status = types.NodeStatusPairRequested
|
||||
return nil
|
||||
},
|
||||
nil,
|
||||
)
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
// HandshakeInit is used on server A to handle pairing request (see Pair fn above) from server B
|
||||
func (svc node) HandshakeInit(ctx context.Context, nodeID uint64, pairToken string, sharedNodeID uint64, authToken string) error {
|
||||
// if !svc.ac.CanPair(ctx) {
|
||||
// return NodeErrNotAllowedToPair()
|
||||
// }
|
||||
|
||||
_, err := svc.updater(
|
||||
ctx,
|
||||
sharedNodeID,
|
||||
NodeActionHandshakeInit,
|
||||
func(ctx context.Context, n *types.Node) error {
|
||||
// @todo need to check node status before we can proceed with initialization
|
||||
if n.PairToken != pairToken {
|
||||
return NodeErrPairingTokenInvalid()
|
||||
}
|
||||
|
||||
n.SharedNodeID = sharedNodeID
|
||||
n.AuthToken = authToken
|
||||
|
||||
n.UpdatedBy = auth.GetIdentityFromContext(ctx).Identity()
|
||||
n.UpdatedAt = now()
|
||||
|
||||
n.Status = types.NodeStatusPairRequested
|
||||
return nil
|
||||
},
|
||||
func(ctx context.Context, n *types.Node) error {
|
||||
// @todo notify the node administrator about the request
|
||||
return nil
|
||||
},
|
||||
)
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
// HandshakeConfirm is used by server A to manually confirm the handshake
|
||||
func (svc node) HandshakeConfirm(ctx context.Context, nodeID uint64) error {
|
||||
if !svc.ac.CanPair(ctx) {
|
||||
return NodeErrNotAllowedToPair()
|
||||
}
|
||||
|
||||
_, err := svc.updater(ctx, nodeID, NodeActionHandshakeConfirm, func(ctx context.Context, n *types.Node) error {
|
||||
// Handle federated user
|
||||
u, err := svc.fetchFederatedUser(ctx, n)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Generate JWT token for the federated user
|
||||
var accessToken []byte
|
||||
if accessToken, err = svc.tokenIssuer(ctx, u); err != nil {
|
||||
return fmt.Errorf("could not confirm handshake: %w", err)
|
||||
}
|
||||
|
||||
n.UpdatedBy = auth.GetIdentityFromContext(ctx).Identity()
|
||||
n.UpdatedAt = now()
|
||||
|
||||
// Complete handshake on remote node
|
||||
if err = svc.handshaker.Complete(ctx, n, string(accessToken)); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
n.Status = types.NodeStatusPaired
|
||||
return nil
|
||||
}, nil)
|
||||
return err
|
||||
}
|
||||
|
||||
// HandshakeComplete is used by server B to handle handshake confirmation
|
||||
func (svc node) HandshakeComplete(ctx context.Context, sharedNodeID uint64, token string) error {
|
||||
var (
|
||||
n *types.Node
|
||||
err error
|
||||
)
|
||||
|
||||
if !svc.ac.CanPair(ctx) {
|
||||
return NodeErrNotAllowedToPair()
|
||||
}
|
||||
|
||||
if n, err = store.LookupFederationNodeBySharedNodeID(ctx, svc.store, sharedNodeID); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
_, err = svc.updater(ctx, n.ID, NodeActionHandshakeComplete, func(ctx context.Context, n *types.Node) error {
|
||||
n.AuthToken = token
|
||||
n.Status = types.NodeStatusPaired
|
||||
n.UpdatedBy = auth.GetIdentityFromContext(ctx).Identity()
|
||||
n.UpdatedAt = now()
|
||||
|
||||
return nil
|
||||
}, nil)
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
func (svc node) updater(ctx context.Context, nodeID uint64, action func(...*nodeActionProps) *nodeAction, fn, afterFn nodeUpdateHandler) (*types.Node, error) {
|
||||
var (
|
||||
err error
|
||||
n *types.Node
|
||||
aProps = &nodeActionProps{node: &types.Node{ID: nodeID}}
|
||||
)
|
||||
|
||||
err = func() error {
|
||||
n, err = loadNode(ctx, svc.store, nodeID)
|
||||
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
aProps.setNode(n)
|
||||
|
||||
if err = fn(ctx, n); err != nil {
|
||||
n.Status = types.NodeStatusFailed
|
||||
|
||||
// persist the node status change
|
||||
if updateErr := store.UpdateFederationNode(ctx, svc.store, n); updateErr != nil {
|
||||
return updateErr
|
||||
}
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
// any other type of access check
|
||||
// should be done in fn() before
|
||||
// if !svc.ac.CanManageNode(ctx, n) {
|
||||
// return NodeErrNotAllowedToManage()
|
||||
// }
|
||||
|
||||
if err = store.UpdateFederationNode(ctx, svc.store, n); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if afterFn != nil {
|
||||
if err = afterFn(ctx, n); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}()
|
||||
|
||||
return n, svc.recordAction(ctx, aProps, action, err)
|
||||
}
|
||||
|
||||
func (svc node) FindBySharedNodeID(ctx context.Context, sharedNodeID uint64) (*types.Node, error) {
|
||||
n, err := store.LookupFederationNodeBySharedNodeID(ctx, svc.store, sharedNodeID)
|
||||
|
||||
if n != nil && !svc.ac.CanManageNode(ctx, n) {
|
||||
return nil, NodeErrNotAllowedToManage()
|
||||
}
|
||||
|
||||
return n, err
|
||||
}
|
||||
|
||||
func (svc node) FindByID(ctx context.Context, nodeID uint64) (n *types.Node, err error) {
|
||||
if n, err = loadNode(ctx, svc.store, nodeID); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
if !svc.ac.CanManageNode(ctx, n) {
|
||||
return nil, NodeErrNotAllowedToManage()
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
// Looks for existing user or crates a new one
|
||||
func (svc node) fetchFederatedUser(ctx context.Context, n *types.Node) (*sysTypes.User, error) {
|
||||
// Generate handle for user that se this node
|
||||
uHandle := fmt.Sprintf("federation_%d", n.ID)
|
||||
|
||||
u, err := svc.sysUser.FindByHandle(ctx, uHandle)
|
||||
if err == nil {
|
||||
// Reuse existing user
|
||||
return u, nil
|
||||
}
|
||||
|
||||
if service.UserErrNotFound().Is(err) {
|
||||
user := &sysTypes.User{
|
||||
Email: strconv.FormatUint(n.ID, 10) + "@federation.corteza",
|
||||
Handle: uHandle,
|
||||
}
|
||||
|
||||
AddFederationLabel(user, "federation", n.BaseURL)
|
||||
|
||||
// Create a user to service this node
|
||||
r, err := service.DefaultRole.FindByHandle(ctx, "federation")
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// context with fed. user credentials
|
||||
ctxfed := auth.SetIdentityToContext(ctx, auth.FederationUser())
|
||||
|
||||
u, err = svc.sysUser.Create(ctxfed, user)
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if err = service.DefaultRole.MemberAdd(ctxfed, r.ID, u.ID); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
u.SetRoles(append(u.Roles(), r.ID)...)
|
||||
|
||||
return u, nil
|
||||
}
|
||||
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// decodePairingURI decodes URI (string) to federation node
|
||||
//
|
||||
// Four parts are collected from the given URI:
|
||||
// 1. node host from URI's host
|
||||
// 2. shared node ID from URI's username
|
||||
// 3. shared token from URI's password
|
||||
// 4. name of the node from query string param "name" (optional)
|
||||
func (node) decodePairingURI(uri string) (*types.Node, error) {
|
||||
var (
|
||||
n = &types.Node{}
|
||||
)
|
||||
|
||||
return n, func() error {
|
||||
parsedURI, err := url.Parse(uri)
|
||||
if err != nil {
|
||||
return NodeErrPairingURIInvalid().Wrap(err)
|
||||
}
|
||||
|
||||
n.PairToken, _ = parsedURI.User.Password()
|
||||
if len(n.PairToken) != TokenLength {
|
||||
return NodeErrPairingURITokenInvalid()
|
||||
}
|
||||
|
||||
n.SharedNodeID, err = strconv.ParseUint(parsedURI.User.Username(), 10, 64)
|
||||
if err != nil || n.SharedNodeID == 0 {
|
||||
return NodeErrPairingURISourceIDInvalid().Wrap(err)
|
||||
}
|
||||
|
||||
n.Name = parsedURI.Query().Get("name")
|
||||
n.BaseURL = fmt.Sprintf("%s://%s/%s", parsedURI.Scheme, parsedURI.Host, strings.Trim(parsedURI.Path, "/"))
|
||||
return nil
|
||||
}()
|
||||
}
|
||||
|
||||
// makePairingURI encodes details about this deployment and pairing token into sharable URI
|
||||
//
|
||||
// This URI contains info about this server (name, host, fed. api base url) and node created here
|
||||
// that is used to identify remote federation server ID with pairing token
|
||||
//
|
||||
// URI structure:
|
||||
// corteza+federation://<node ID>:<pairing token>@<this-host-where-the-api-is></path-to-federation-api>?qs-meta-data
|
||||
func (svc node) makePairingURI(n *types.Node) string {
|
||||
uri := url.URL{
|
||||
Scheme: "https",
|
||||
User: url.UserPassword(strconv.FormatUint(n.ID, 10), n.PairToken),
|
||||
Host: svc.host,
|
||||
Path: svc.baseURL,
|
||||
}
|
||||
|
||||
qs := url.Values{}
|
||||
if len(svc.name) > 0 {
|
||||
qs.Add("name", svc.name)
|
||||
}
|
||||
|
||||
if len(qs) > 0 {
|
||||
uri.RawQuery = qs.Encode()
|
||||
}
|
||||
|
||||
return uri.String()
|
||||
}
|
||||
|
||||
func loadNode(ctx context.Context, s store.FederationNodes, ID uint64) (res *types.Node, err error) {
|
||||
if ID == 0 {
|
||||
return nil, NodeErrInvalidID()
|
||||
}
|
||||
|
||||
if res, err = store.LookupFederationNodeByID(ctx, s, ID); errors.IsNotFound(err) {
|
||||
err = NodeErrNotFound()
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
846
server/federation/service/node_actions.gen.go
generated
Normal file
846
server/federation/service/node_actions.gen.go
generated
Normal file
@@ -0,0 +1,846 @@
|
||||
package service
|
||||
|
||||
// This file is auto-generated.
|
||||
//
|
||||
// Changes to this file may cause incorrect behavior and will be lost if
|
||||
// the code is regenerated.
|
||||
//
|
||||
// Definitions file that controls how this file is generated:
|
||||
// federation/service/node_actions.yaml
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"github.com/cortezaproject/corteza/server/federation/types"
|
||||
"github.com/cortezaproject/corteza/server/pkg/actionlog"
|
||||
"github.com/cortezaproject/corteza/server/pkg/errors"
|
||||
"github.com/cortezaproject/corteza/server/pkg/locale"
|
||||
"strings"
|
||||
"time"
|
||||
)
|
||||
|
||||
type (
|
||||
nodeActionProps struct {
|
||||
node *types.Node
|
||||
pairingURI string
|
||||
filter *types.NodeFilter
|
||||
}
|
||||
|
||||
nodeAction struct {
|
||||
timestamp time.Time
|
||||
resource string
|
||||
action string
|
||||
log string
|
||||
severity actionlog.Severity
|
||||
|
||||
// prefix for error when action fails
|
||||
errorMessage string
|
||||
|
||||
props *nodeActionProps
|
||||
}
|
||||
|
||||
nodeLogMetaKey struct{}
|
||||
nodePropsMetaKey struct{}
|
||||
)
|
||||
|
||||
var (
|
||||
// just a placeholder to cover template cases w/o fmt package use
|
||||
_ = fmt.Println
|
||||
)
|
||||
|
||||
// *********************************************************************************************************************
|
||||
// *********************************************************************************************************************
|
||||
// Props methods
|
||||
// setNode updates nodeActionProps's node
|
||||
//
|
||||
// This function is auto-generated.
|
||||
func (p *nodeActionProps) setNode(node *types.Node) *nodeActionProps {
|
||||
p.node = node
|
||||
return p
|
||||
}
|
||||
|
||||
// setPairingURI updates nodeActionProps's pairingURI
|
||||
//
|
||||
// This function is auto-generated.
|
||||
func (p *nodeActionProps) setPairingURI(pairingURI string) *nodeActionProps {
|
||||
p.pairingURI = pairingURI
|
||||
return p
|
||||
}
|
||||
|
||||
// setFilter updates nodeActionProps's filter
|
||||
//
|
||||
// This function is auto-generated.
|
||||
func (p *nodeActionProps) setFilter(filter *types.NodeFilter) *nodeActionProps {
|
||||
p.filter = filter
|
||||
return p
|
||||
}
|
||||
|
||||
// Serialize converts nodeActionProps to actionlog.Meta
|
||||
//
|
||||
// This function is auto-generated.
|
||||
func (p nodeActionProps) Serialize() actionlog.Meta {
|
||||
var (
|
||||
m = make(actionlog.Meta)
|
||||
)
|
||||
|
||||
if p.node != nil {
|
||||
m.Set("node.Name", p.node.Name, true)
|
||||
m.Set("node.BaseURL", p.node.BaseURL, true)
|
||||
m.Set("node.ID", p.node.ID, true)
|
||||
m.Set("node.Status", p.node.Status, true)
|
||||
}
|
||||
m.Set("pairingURI", p.pairingURI, true)
|
||||
if p.filter != nil {
|
||||
m.Set("filter.query", p.filter.Query, true)
|
||||
m.Set("filter.status", p.filter.Status, true)
|
||||
}
|
||||
|
||||
return m
|
||||
}
|
||||
|
||||
// tr translates string and replaces meta value placeholder with values
|
||||
//
|
||||
// This function is auto-generated.
|
||||
func (p nodeActionProps) Format(in string, err error) string {
|
||||
var (
|
||||
pairs = []string{"{{err}}"}
|
||||
// first non-empty string
|
||||
fns = func(ii ...interface{}) string {
|
||||
for _, i := range ii {
|
||||
if s := fmt.Sprintf("%v", i); len(s) > 0 {
|
||||
return s
|
||||
}
|
||||
}
|
||||
|
||||
return ""
|
||||
}
|
||||
)
|
||||
|
||||
if err != nil {
|
||||
pairs = append(pairs, err.Error())
|
||||
} else {
|
||||
pairs = append(pairs, "nil")
|
||||
}
|
||||
|
||||
if p.node != nil {
|
||||
// replacement for "{{node}}" (in order how fields are defined)
|
||||
pairs = append(
|
||||
pairs,
|
||||
"{{node}}",
|
||||
fns(
|
||||
p.node.Name,
|
||||
p.node.BaseURL,
|
||||
p.node.ID,
|
||||
p.node.Status,
|
||||
),
|
||||
)
|
||||
pairs = append(pairs, "{{node.Name}}", fns(p.node.Name))
|
||||
pairs = append(pairs, "{{node.BaseURL}}", fns(p.node.BaseURL))
|
||||
pairs = append(pairs, "{{node.ID}}", fns(p.node.ID))
|
||||
pairs = append(pairs, "{{node.Status}}", fns(p.node.Status))
|
||||
}
|
||||
pairs = append(pairs, "{{pairingURI}}", fns(p.pairingURI))
|
||||
|
||||
if p.filter != nil {
|
||||
// replacement for "{{filter}}" (in order how fields are defined)
|
||||
pairs = append(
|
||||
pairs,
|
||||
"{{filter}}",
|
||||
fns(
|
||||
p.filter.Query,
|
||||
p.filter.Status,
|
||||
),
|
||||
)
|
||||
pairs = append(pairs, "{{filter.query}}", fns(p.filter.Query))
|
||||
pairs = append(pairs, "{{filter.status}}", fns(p.filter.Status))
|
||||
}
|
||||
return strings.NewReplacer(pairs...).Replace(in)
|
||||
}
|
||||
|
||||
// *********************************************************************************************************************
|
||||
// *********************************************************************************************************************
|
||||
// Action methods
|
||||
|
||||
// String returns loggable description as string
|
||||
//
|
||||
// This function is auto-generated.
|
||||
func (a *nodeAction) String() string {
|
||||
var props = &nodeActionProps{}
|
||||
|
||||
if a.props != nil {
|
||||
props = a.props
|
||||
}
|
||||
|
||||
return props.Format(a.log, nil)
|
||||
}
|
||||
|
||||
func (e *nodeAction) ToAction() *actionlog.Action {
|
||||
return &actionlog.Action{
|
||||
Resource: e.resource,
|
||||
Action: e.action,
|
||||
Severity: e.severity,
|
||||
Description: e.String(),
|
||||
Meta: e.props.Serialize(),
|
||||
}
|
||||
}
|
||||
|
||||
// *********************************************************************************************************************
|
||||
// *********************************************************************************************************************
|
||||
// Action constructors
|
||||
|
||||
// NodeActionSearch returns "federation:node.search" action
|
||||
//
|
||||
// This function is auto-generated.
|
||||
func NodeActionSearch(props ...*nodeActionProps) *nodeAction {
|
||||
a := &nodeAction{
|
||||
timestamp: time.Now(),
|
||||
resource: "federation:node",
|
||||
action: "search",
|
||||
log: "searched for nodes",
|
||||
severity: actionlog.Notice,
|
||||
}
|
||||
|
||||
if len(props) > 0 {
|
||||
a.props = props[0]
|
||||
}
|
||||
|
||||
return a
|
||||
}
|
||||
|
||||
// NodeActionLookup returns "federation:node.lookup" action
|
||||
//
|
||||
// This function is auto-generated.
|
||||
func NodeActionLookup(props ...*nodeActionProps) *nodeAction {
|
||||
a := &nodeAction{
|
||||
timestamp: time.Now(),
|
||||
resource: "federation:node",
|
||||
action: "lookup",
|
||||
log: "looked-up for a {{node}}",
|
||||
severity: actionlog.Info,
|
||||
}
|
||||
|
||||
if len(props) > 0 {
|
||||
a.props = props[0]
|
||||
}
|
||||
|
||||
return a
|
||||
}
|
||||
|
||||
// NodeActionCreate returns "federation:node.create" action
|
||||
//
|
||||
// This function is auto-generated.
|
||||
func NodeActionCreate(props ...*nodeActionProps) *nodeAction {
|
||||
a := &nodeAction{
|
||||
timestamp: time.Now(),
|
||||
resource: "federation:node",
|
||||
action: "create",
|
||||
log: "created {{node}}",
|
||||
severity: actionlog.Notice,
|
||||
}
|
||||
|
||||
if len(props) > 0 {
|
||||
a.props = props[0]
|
||||
}
|
||||
|
||||
return a
|
||||
}
|
||||
|
||||
// NodeActionCreateFromPairingURI returns "federation:node.createFromPairingURI" action
|
||||
//
|
||||
// This function is auto-generated.
|
||||
func NodeActionCreateFromPairingURI(props ...*nodeActionProps) *nodeAction {
|
||||
a := &nodeAction{
|
||||
timestamp: time.Now(),
|
||||
resource: "federation:node",
|
||||
action: "createFromPairingURI",
|
||||
log: "created {{node}} from pairing URI",
|
||||
severity: actionlog.Notice,
|
||||
}
|
||||
|
||||
if len(props) > 0 {
|
||||
a.props = props[0]
|
||||
}
|
||||
|
||||
return a
|
||||
}
|
||||
|
||||
// NodeActionRecreateFromPairingURI returns "federation:node.recreateFromPairingURI" action
|
||||
//
|
||||
// This function is auto-generated.
|
||||
func NodeActionRecreateFromPairingURI(props ...*nodeActionProps) *nodeAction {
|
||||
a := &nodeAction{
|
||||
timestamp: time.Now(),
|
||||
resource: "federation:node",
|
||||
action: "recreateFromPairingURI",
|
||||
log: "recreate {{node}} from pairing URI",
|
||||
severity: actionlog.Notice,
|
||||
}
|
||||
|
||||
if len(props) > 0 {
|
||||
a.props = props[0]
|
||||
}
|
||||
|
||||
return a
|
||||
}
|
||||
|
||||
// NodeActionUpdate returns "federation:node.update" action
|
||||
//
|
||||
// This function is auto-generated.
|
||||
func NodeActionUpdate(props ...*nodeActionProps) *nodeAction {
|
||||
a := &nodeAction{
|
||||
timestamp: time.Now(),
|
||||
resource: "federation:node",
|
||||
action: "update",
|
||||
log: "updated {{node}}",
|
||||
severity: actionlog.Notice,
|
||||
}
|
||||
|
||||
if len(props) > 0 {
|
||||
a.props = props[0]
|
||||
}
|
||||
|
||||
return a
|
||||
}
|
||||
|
||||
// NodeActionDelete returns "federation:node.delete" action
|
||||
//
|
||||
// This function is auto-generated.
|
||||
func NodeActionDelete(props ...*nodeActionProps) *nodeAction {
|
||||
a := &nodeAction{
|
||||
timestamp: time.Now(),
|
||||
resource: "federation:node",
|
||||
action: "delete",
|
||||
log: "deleted {{node}}",
|
||||
severity: actionlog.Notice,
|
||||
}
|
||||
|
||||
if len(props) > 0 {
|
||||
a.props = props[0]
|
||||
}
|
||||
|
||||
return a
|
||||
}
|
||||
|
||||
// NodeActionUndelete returns "federation:node.undelete" action
|
||||
//
|
||||
// This function is auto-generated.
|
||||
func NodeActionUndelete(props ...*nodeActionProps) *nodeAction {
|
||||
a := &nodeAction{
|
||||
timestamp: time.Now(),
|
||||
resource: "federation:node",
|
||||
action: "undelete",
|
||||
log: "undeleted {{node}}",
|
||||
severity: actionlog.Notice,
|
||||
}
|
||||
|
||||
if len(props) > 0 {
|
||||
a.props = props[0]
|
||||
}
|
||||
|
||||
return a
|
||||
}
|
||||
|
||||
// NodeActionOttRegenerated returns "federation:node.ottRegenerated" action
|
||||
//
|
||||
// This function is auto-generated.
|
||||
func NodeActionOttRegenerated(props ...*nodeActionProps) *nodeAction {
|
||||
a := &nodeAction{
|
||||
timestamp: time.Now(),
|
||||
resource: "federation:node",
|
||||
action: "ottRegenerated",
|
||||
log: "regenerated one-time-token for {{node}}",
|
||||
severity: actionlog.Notice,
|
||||
}
|
||||
|
||||
if len(props) > 0 {
|
||||
a.props = props[0]
|
||||
}
|
||||
|
||||
return a
|
||||
}
|
||||
|
||||
// NodeActionPair returns "federation:node.pair" action
|
||||
//
|
||||
// This function is auto-generated.
|
||||
func NodeActionPair(props ...*nodeActionProps) *nodeAction {
|
||||
a := &nodeAction{
|
||||
timestamp: time.Now(),
|
||||
resource: "federation:node",
|
||||
action: "pair",
|
||||
log: "{{node}} pairing started",
|
||||
severity: actionlog.Notice,
|
||||
}
|
||||
|
||||
if len(props) > 0 {
|
||||
a.props = props[0]
|
||||
}
|
||||
|
||||
return a
|
||||
}
|
||||
|
||||
// NodeActionHandshakeInit returns "federation:node.handshakeInit" action
|
||||
//
|
||||
// This function is auto-generated.
|
||||
func NodeActionHandshakeInit(props ...*nodeActionProps) *nodeAction {
|
||||
a := &nodeAction{
|
||||
timestamp: time.Now(),
|
||||
resource: "federation:node",
|
||||
action: "handshakeInit",
|
||||
log: "{{node}} handshake initialized",
|
||||
severity: actionlog.Notice,
|
||||
}
|
||||
|
||||
if len(props) > 0 {
|
||||
a.props = props[0]
|
||||
}
|
||||
|
||||
return a
|
||||
}
|
||||
|
||||
// NodeActionHandshakeConfirm returns "federation:node.handshakeConfirm" action
|
||||
//
|
||||
// This function is auto-generated.
|
||||
func NodeActionHandshakeConfirm(props ...*nodeActionProps) *nodeAction {
|
||||
a := &nodeAction{
|
||||
timestamp: time.Now(),
|
||||
resource: "federation:node",
|
||||
action: "handshakeConfirm",
|
||||
log: "{{node}} handshake confirmed",
|
||||
severity: actionlog.Notice,
|
||||
}
|
||||
|
||||
if len(props) > 0 {
|
||||
a.props = props[0]
|
||||
}
|
||||
|
||||
return a
|
||||
}
|
||||
|
||||
// NodeActionHandshakeComplete returns "federation:node.handshakeComplete" action
|
||||
//
|
||||
// This function is auto-generated.
|
||||
func NodeActionHandshakeComplete(props ...*nodeActionProps) *nodeAction {
|
||||
a := &nodeAction{
|
||||
timestamp: time.Now(),
|
||||
resource: "federation:node",
|
||||
action: "handshakeComplete",
|
||||
log: "{{node}} handshake completed",
|
||||
severity: actionlog.Notice,
|
||||
}
|
||||
|
||||
if len(props) > 0 {
|
||||
a.props = props[0]
|
||||
}
|
||||
|
||||
return a
|
||||
}
|
||||
|
||||
// *********************************************************************************************************************
|
||||
// *********************************************************************************************************************
|
||||
// Error constructors
|
||||
|
||||
// NodeErrGeneric returns "federation:node.generic" as *errors.Error
|
||||
//
|
||||
// This function is auto-generated.
|
||||
func NodeErrGeneric(mm ...*nodeActionProps) *errors.Error {
|
||||
var p = &nodeActionProps{}
|
||||
if len(mm) > 0 {
|
||||
p = mm[0]
|
||||
}
|
||||
|
||||
var e = errors.New(
|
||||
errors.KindInternal,
|
||||
|
||||
p.Format("failed to complete request due to internal error", nil),
|
||||
|
||||
errors.Meta("type", "generic"),
|
||||
errors.Meta("resource", "federation:node"),
|
||||
|
||||
// action log entry; no formatting, it will be applied inside recordAction fn.
|
||||
errors.Meta(nodeLogMetaKey{}, "{err}"),
|
||||
errors.Meta(nodePropsMetaKey{}, p),
|
||||
|
||||
// translation namespace & key
|
||||
errors.Meta(locale.ErrorMetaNamespace{}, "federation"),
|
||||
errors.Meta(locale.ErrorMetaKey{}, "node.errors.generic"),
|
||||
|
||||
errors.StackSkip(1),
|
||||
)
|
||||
|
||||
if len(mm) > 0 {
|
||||
}
|
||||
|
||||
return e
|
||||
}
|
||||
|
||||
// NodeErrNotFound returns "federation:node.notFound" as *errors.Error
|
||||
//
|
||||
// This function is auto-generated.
|
||||
func NodeErrNotFound(mm ...*nodeActionProps) *errors.Error {
|
||||
var p = &nodeActionProps{}
|
||||
if len(mm) > 0 {
|
||||
p = mm[0]
|
||||
}
|
||||
|
||||
var e = errors.New(
|
||||
errors.KindInternal,
|
||||
|
||||
p.Format("node does not exist", nil),
|
||||
|
||||
errors.Meta("type", "notFound"),
|
||||
errors.Meta("resource", "federation:node"),
|
||||
|
||||
errors.Meta(nodePropsMetaKey{}, p),
|
||||
|
||||
// translation namespace & key
|
||||
errors.Meta(locale.ErrorMetaNamespace{}, "federation"),
|
||||
errors.Meta(locale.ErrorMetaKey{}, "node.errors.notFound"),
|
||||
|
||||
errors.StackSkip(1),
|
||||
)
|
||||
|
||||
if len(mm) > 0 {
|
||||
}
|
||||
|
||||
return e
|
||||
}
|
||||
|
||||
// NodeErrInvalidID returns "federation:node.invalidID" as *errors.Error
|
||||
//
|
||||
// This function is auto-generated.
|
||||
func NodeErrInvalidID(mm ...*nodeActionProps) *errors.Error {
|
||||
var p = &nodeActionProps{}
|
||||
if len(mm) > 0 {
|
||||
p = mm[0]
|
||||
}
|
||||
|
||||
var e = errors.New(
|
||||
errors.KindInternal,
|
||||
|
||||
p.Format("invalid ID", nil),
|
||||
|
||||
errors.Meta("type", "invalidID"),
|
||||
errors.Meta("resource", "federation:node"),
|
||||
|
||||
errors.Meta(nodePropsMetaKey{}, p),
|
||||
|
||||
// translation namespace & key
|
||||
errors.Meta(locale.ErrorMetaNamespace{}, "federation"),
|
||||
errors.Meta(locale.ErrorMetaKey{}, "node.errors.invalidID"),
|
||||
|
||||
errors.StackSkip(1),
|
||||
)
|
||||
|
||||
if len(mm) > 0 {
|
||||
}
|
||||
|
||||
return e
|
||||
}
|
||||
|
||||
// NodeErrPairingURIInvalid returns "federation:node.pairingURIInvalid" as *errors.Error
|
||||
//
|
||||
// This function is auto-generated.
|
||||
func NodeErrPairingURIInvalid(mm ...*nodeActionProps) *errors.Error {
|
||||
var p = &nodeActionProps{}
|
||||
if len(mm) > 0 {
|
||||
p = mm[0]
|
||||
}
|
||||
|
||||
var e = errors.New(
|
||||
errors.KindInternal,
|
||||
|
||||
p.Format("pairing URI invalid: {{err}}", nil),
|
||||
|
||||
errors.Meta("type", "pairingURIInvalid"),
|
||||
errors.Meta("resource", "federation:node"),
|
||||
|
||||
errors.Meta(nodePropsMetaKey{}, p),
|
||||
|
||||
// translation namespace & key
|
||||
errors.Meta(locale.ErrorMetaNamespace{}, "federation"),
|
||||
errors.Meta(locale.ErrorMetaKey{}, "node.errors.pairingURIInvalid"),
|
||||
|
||||
errors.StackSkip(1),
|
||||
)
|
||||
|
||||
if len(mm) > 0 {
|
||||
}
|
||||
|
||||
return e
|
||||
}
|
||||
|
||||
// NodeErrPairingURITokenInvalid returns "federation:node.pairingURITokenInvalid" as *errors.Error
|
||||
//
|
||||
// This function is auto-generated.
|
||||
func NodeErrPairingURITokenInvalid(mm ...*nodeActionProps) *errors.Error {
|
||||
var p = &nodeActionProps{}
|
||||
if len(mm) > 0 {
|
||||
p = mm[0]
|
||||
}
|
||||
|
||||
var e = errors.New(
|
||||
errors.KindInternal,
|
||||
|
||||
p.Format("pairing URI with invalid pairing token", nil),
|
||||
|
||||
errors.Meta("type", "pairingURITokenInvalid"),
|
||||
errors.Meta("resource", "federation:node"),
|
||||
|
||||
errors.Meta(nodePropsMetaKey{}, p),
|
||||
|
||||
// translation namespace & key
|
||||
errors.Meta(locale.ErrorMetaNamespace{}, "federation"),
|
||||
errors.Meta(locale.ErrorMetaKey{}, "node.errors.pairingURITokenInvalid"),
|
||||
|
||||
errors.StackSkip(1),
|
||||
)
|
||||
|
||||
if len(mm) > 0 {
|
||||
}
|
||||
|
||||
return e
|
||||
}
|
||||
|
||||
// NodeErrPairingURISourceIDInvalid returns "federation:node.pairingURISourceIDInvalid" as *errors.Error
|
||||
//
|
||||
// This function is auto-generated.
|
||||
func NodeErrPairingURISourceIDInvalid(mm ...*nodeActionProps) *errors.Error {
|
||||
var p = &nodeActionProps{}
|
||||
if len(mm) > 0 {
|
||||
p = mm[0]
|
||||
}
|
||||
|
||||
var e = errors.New(
|
||||
errors.KindInternal,
|
||||
|
||||
p.Format("pairing URI without source node ID", nil),
|
||||
|
||||
errors.Meta("type", "pairingURISourceIDInvalid"),
|
||||
errors.Meta("resource", "federation:node"),
|
||||
|
||||
errors.Meta(nodePropsMetaKey{}, p),
|
||||
|
||||
// translation namespace & key
|
||||
errors.Meta(locale.ErrorMetaNamespace{}, "federation"),
|
||||
errors.Meta(locale.ErrorMetaKey{}, "node.errors.pairingURISourceIDInvalid"),
|
||||
|
||||
errors.StackSkip(1),
|
||||
)
|
||||
|
||||
if len(mm) > 0 {
|
||||
}
|
||||
|
||||
return e
|
||||
}
|
||||
|
||||
// NodeErrPairingTokenInvalid returns "federation:node.pairingTokenInvalid" as *errors.Error
|
||||
//
|
||||
// This function is auto-generated.
|
||||
func NodeErrPairingTokenInvalid(mm ...*nodeActionProps) *errors.Error {
|
||||
var p = &nodeActionProps{}
|
||||
if len(mm) > 0 {
|
||||
p = mm[0]
|
||||
}
|
||||
|
||||
var e = errors.New(
|
||||
errors.KindInternal,
|
||||
|
||||
p.Format("pairing token invalid", nil),
|
||||
|
||||
errors.Meta("type", "pairingTokenInvalid"),
|
||||
errors.Meta("resource", "federation:node"),
|
||||
|
||||
errors.Meta(nodePropsMetaKey{}, p),
|
||||
|
||||
// translation namespace & key
|
||||
errors.Meta(locale.ErrorMetaNamespace{}, "federation"),
|
||||
errors.Meta(locale.ErrorMetaKey{}, "node.errors.pairingTokenInvalid"),
|
||||
|
||||
errors.StackSkip(1),
|
||||
)
|
||||
|
||||
if len(mm) > 0 {
|
||||
}
|
||||
|
||||
return e
|
||||
}
|
||||
|
||||
// NodeErrNotAllowedToCreate returns "federation:node.notAllowedToCreate" as *errors.Error
|
||||
//
|
||||
// This function is auto-generated.
|
||||
func NodeErrNotAllowedToCreate(mm ...*nodeActionProps) *errors.Error {
|
||||
var p = &nodeActionProps{}
|
||||
if len(mm) > 0 {
|
||||
p = mm[0]
|
||||
}
|
||||
|
||||
var e = errors.New(
|
||||
errors.KindInternal,
|
||||
|
||||
p.Format("not allowed to create nodes", nil),
|
||||
|
||||
errors.Meta("type", "notAllowedToCreate"),
|
||||
errors.Meta("resource", "federation:node"),
|
||||
|
||||
// action log entry; no formatting, it will be applied inside recordAction fn.
|
||||
errors.Meta(nodeLogMetaKey{}, "could not create nodes; insufficient permissions"),
|
||||
errors.Meta(nodePropsMetaKey{}, p),
|
||||
|
||||
// translation namespace & key
|
||||
errors.Meta(locale.ErrorMetaNamespace{}, "federation"),
|
||||
errors.Meta(locale.ErrorMetaKey{}, "node.errors.notAllowedToCreate"),
|
||||
|
||||
errors.StackSkip(1),
|
||||
)
|
||||
|
||||
if len(mm) > 0 {
|
||||
}
|
||||
|
||||
return e
|
||||
}
|
||||
|
||||
// NodeErrNotAllowedToSearch returns "federation:node.notAllowedToSearch" as *errors.Error
|
||||
//
|
||||
// This function is auto-generated.
|
||||
func NodeErrNotAllowedToSearch(mm ...*nodeActionProps) *errors.Error {
|
||||
var p = &nodeActionProps{}
|
||||
if len(mm) > 0 {
|
||||
p = mm[0]
|
||||
}
|
||||
|
||||
var e = errors.New(
|
||||
errors.KindInternal,
|
||||
|
||||
p.Format("not allowed to search or list nodes", nil),
|
||||
|
||||
errors.Meta("type", "notAllowedToSearch"),
|
||||
errors.Meta("resource", "federation:node"),
|
||||
|
||||
// action log entry; no formatting, it will be applied inside recordAction fn.
|
||||
errors.Meta(nodeLogMetaKey{}, "could not search or list nodes; insufficient permissions"),
|
||||
errors.Meta(nodePropsMetaKey{}, p),
|
||||
|
||||
// translation namespace & key
|
||||
errors.Meta(locale.ErrorMetaNamespace{}, "federation"),
|
||||
errors.Meta(locale.ErrorMetaKey{}, "node.errors.notAllowedToSearch"),
|
||||
|
||||
errors.StackSkip(1),
|
||||
)
|
||||
|
||||
if len(mm) > 0 {
|
||||
}
|
||||
|
||||
return e
|
||||
}
|
||||
|
||||
// NodeErrNotAllowedToManage returns "federation:node.notAllowedToManage" as *errors.Error
|
||||
//
|
||||
// This function is auto-generated.
|
||||
func NodeErrNotAllowedToManage(mm ...*nodeActionProps) *errors.Error {
|
||||
var p = &nodeActionProps{}
|
||||
if len(mm) > 0 {
|
||||
p = mm[0]
|
||||
}
|
||||
|
||||
var e = errors.New(
|
||||
errors.KindInternal,
|
||||
|
||||
p.Format("not allowed to manage this node", nil),
|
||||
|
||||
errors.Meta("type", "notAllowedToManage"),
|
||||
errors.Meta("resource", "federation:node"),
|
||||
|
||||
// action log entry; no formatting, it will be applied inside recordAction fn.
|
||||
errors.Meta(nodeLogMetaKey{}, "could not manage {{node}}; insufficient permissions"),
|
||||
errors.Meta(nodePropsMetaKey{}, p),
|
||||
|
||||
// translation namespace & key
|
||||
errors.Meta(locale.ErrorMetaNamespace{}, "federation"),
|
||||
errors.Meta(locale.ErrorMetaKey{}, "node.errors.notAllowedToManage"),
|
||||
|
||||
errors.StackSkip(1),
|
||||
)
|
||||
|
||||
if len(mm) > 0 {
|
||||
}
|
||||
|
||||
return e
|
||||
}
|
||||
|
||||
// NodeErrNotAllowedToPair returns "federation:node.notAllowedToPair" as *errors.Error
|
||||
//
|
||||
// This function is auto-generated.
|
||||
func NodeErrNotAllowedToPair(mm ...*nodeActionProps) *errors.Error {
|
||||
var p = &nodeActionProps{}
|
||||
if len(mm) > 0 {
|
||||
p = mm[0]
|
||||
}
|
||||
|
||||
var e = errors.New(
|
||||
errors.KindInternal,
|
||||
|
||||
p.Format("not allowed to pair this node", nil),
|
||||
|
||||
errors.Meta("type", "notAllowedToPair"),
|
||||
errors.Meta("resource", "federation:node"),
|
||||
|
||||
// action log entry; no formatting, it will be applied inside recordAction fn.
|
||||
errors.Meta(nodeLogMetaKey{}, "could not pair {{node}}; insufficient permissions"),
|
||||
errors.Meta(nodePropsMetaKey{}, p),
|
||||
|
||||
// translation namespace & key
|
||||
errors.Meta(locale.ErrorMetaNamespace{}, "federation"),
|
||||
errors.Meta(locale.ErrorMetaKey{}, "node.errors.notAllowedToPair"),
|
||||
|
||||
errors.StackSkip(1),
|
||||
)
|
||||
|
||||
if len(mm) > 0 {
|
||||
}
|
||||
|
||||
return e
|
||||
}
|
||||
|
||||
// *********************************************************************************************************************
|
||||
// *********************************************************************************************************************
|
||||
|
||||
// recordAction is a service helper function wraps function that can return error
|
||||
//
|
||||
// It will wrap unrecognized/internal errors with generic errors.
|
||||
//
|
||||
// This function is auto-generated.
|
||||
func (svc node) recordAction(ctx context.Context, props *nodeActionProps, actionFn func(...*nodeActionProps) *nodeAction, err error) error {
|
||||
if svc.actionlog == nil || actionFn == nil {
|
||||
// action log disabled or no action fn passed, return error as-is
|
||||
return err
|
||||
} else if err == nil {
|
||||
// action completed w/o error, record it
|
||||
svc.actionlog.Record(ctx, actionFn(props).ToAction())
|
||||
return nil
|
||||
}
|
||||
|
||||
a := actionFn(props).ToAction()
|
||||
|
||||
// Extracting error information and recording it as action
|
||||
a.Error = err.Error()
|
||||
|
||||
switch c := err.(type) {
|
||||
case *errors.Error:
|
||||
m := c.Meta()
|
||||
|
||||
a.Error = err.Error()
|
||||
a.Severity = actionlog.Severity(m.AsInt("severity"))
|
||||
a.Description = props.Format(m.AsString(nodeLogMetaKey{}), err)
|
||||
|
||||
if p, has := m[nodePropsMetaKey{}]; has {
|
||||
a.Meta = p.(*nodeActionProps).Serialize()
|
||||
}
|
||||
|
||||
svc.actionlog.Record(ctx, a)
|
||||
default:
|
||||
svc.actionlog.Record(ctx, a)
|
||||
}
|
||||
|
||||
// Original error is passed on
|
||||
return err
|
||||
}
|
||||
100
server/federation/service/node_actions.yaml
Normal file
100
server/federation/service/node_actions.yaml
Normal file
@@ -0,0 +1,100 @@
|
||||
# List of loggable service actions
|
||||
|
||||
resource: federation:node
|
||||
service: node
|
||||
|
||||
# Default sensitivity for actions
|
||||
defaultActionSeverity: notice
|
||||
|
||||
# default severity for errors
|
||||
defaultErrorSeverity: error
|
||||
|
||||
import:
|
||||
- github.com/cortezaproject/corteza/server/federation/types
|
||||
|
||||
props:
|
||||
- name: node
|
||||
type: "*types.Node"
|
||||
fields: [ Name, BaseURL, ID, Status ]
|
||||
- name: pairingURI
|
||||
- name: filter
|
||||
type: "*types.NodeFilter"
|
||||
fields: [ query, status ]
|
||||
|
||||
actions:
|
||||
- action: search
|
||||
log: "searched for nodes"
|
||||
|
||||
- action: lookup
|
||||
log: "looked-up for a {{node}}"
|
||||
severity: info
|
||||
|
||||
- action: create
|
||||
log: "created {{node}}"
|
||||
|
||||
- action: createFromPairingURI
|
||||
log: "created {{node}} from pairing URI"
|
||||
|
||||
- action: recreateFromPairingURI
|
||||
log: "recreate {{node}} from pairing URI"
|
||||
|
||||
- action: update
|
||||
log: "updated {{node}}"
|
||||
|
||||
- action: delete
|
||||
log: "deleted {{node}}"
|
||||
|
||||
- action: undelete
|
||||
log: "undeleted {{node}}"
|
||||
|
||||
- action: ottRegenerated
|
||||
log: "regenerated one-time-token for {{node}}"
|
||||
|
||||
- action: pair
|
||||
log: "{{node}} pairing started"
|
||||
|
||||
- action: handshakeInit
|
||||
log: "{{node}} handshake initialized"
|
||||
|
||||
- action: handshakeConfirm
|
||||
log: "{{node}} handshake confirmed"
|
||||
|
||||
- action: handshakeComplete
|
||||
log: "{{node}} handshake completed"
|
||||
|
||||
errors:
|
||||
- error: notFound
|
||||
message: "node does not exist"
|
||||
severity: warning
|
||||
|
||||
- error: invalidID
|
||||
message: "invalid ID"
|
||||
severity: warning
|
||||
|
||||
- error: pairingURIInvalid
|
||||
message: "pairing URI invalid: {{err}}"
|
||||
#
|
||||
- error: pairingURITokenInvalid
|
||||
message: "pairing URI with invalid pairing token"
|
||||
|
||||
- error: pairingURISourceIDInvalid
|
||||
message: "pairing URI without source node ID"
|
||||
|
||||
- error: pairingTokenInvalid
|
||||
message: "pairing token invalid"
|
||||
|
||||
- error: notAllowedToCreate
|
||||
message: "not allowed to create nodes"
|
||||
log: "could not create nodes; insufficient permissions"
|
||||
|
||||
- error: notAllowedToSearch
|
||||
message: "not allowed to search or list nodes"
|
||||
log: "could not search or list nodes; insufficient permissions"
|
||||
|
||||
- error: notAllowedToManage
|
||||
message: "not allowed to manage this node"
|
||||
log: "could not manage {{node}}; insufficient permissions"
|
||||
|
||||
- error: notAllowedToPair
|
||||
message: "not allowed to pair this node"
|
||||
log: "could not pair {{node}}; insufficient permissions"
|
||||
97
server/federation/service/node_handshaker.go
Normal file
97
server/federation/service/node_handshaker.go
Normal file
@@ -0,0 +1,97 @@
|
||||
package service
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"strconv"
|
||||
|
||||
"github.com/cortezaproject/corteza/server/federation/types"
|
||||
)
|
||||
|
||||
type (
|
||||
// HttpHandshake performs HTTP handshake with remote node
|
||||
httpNodeHandshake struct {
|
||||
httpClient httpNodeHandshaker
|
||||
}
|
||||
|
||||
httpNodeHandshaker interface {
|
||||
Do(*http.Request) (*http.Response, error)
|
||||
}
|
||||
)
|
||||
|
||||
func HttpHandshake(client httpNodeHandshaker) *httpNodeHandshake {
|
||||
return &httpNodeHandshake{httpClient: client}
|
||||
}
|
||||
|
||||
// Init calls handshake init on a remote node via HTTP
|
||||
//
|
||||
// Called from node.Pair() and sends POST request to remote Corteza
|
||||
// There, it is handled with node.HandshakeInit
|
||||
//
|
||||
// Fn is not part of the node struct to allow injection
|
||||
func (h httpNodeHandshake) Init(ctx context.Context, n *types.Node, authToken string) error {
|
||||
var (
|
||||
endpoint = fmt.Sprintf("%s/nodes/%d/handshake", n.BaseURL, n.SharedNodeID)
|
||||
payload = map[string]string{
|
||||
// Share pairing token, so we can authenticate ourselves
|
||||
"pairToken": n.PairToken,
|
||||
|
||||
// Share auth token for the federation service user for this node
|
||||
"authToken": authToken,
|
||||
"sharedNodeID": strconv.FormatUint(n.SharedNodeID, 10),
|
||||
}
|
||||
)
|
||||
|
||||
return h.send(ctx, endpoint, payload)
|
||||
}
|
||||
|
||||
// Confirm calls handshake init on a remote node via HTTP
|
||||
//
|
||||
// Called from node.Confirm and sends POST request to remote Corteza
|
||||
// There, it's handled with node.HandshakeComplete
|
||||
func (h httpNodeHandshake) Complete(ctx context.Context, n *types.Node, authToken string) error {
|
||||
var (
|
||||
endpoint = fmt.Sprintf("%s/nodes/%d/handshake-complete", n.BaseURL, n.SharedNodeID)
|
||||
payload = map[string]string{
|
||||
// Share auth token for the federation service user for this node
|
||||
"authToken": authToken,
|
||||
}
|
||||
)
|
||||
|
||||
// use n.AuthToken in context
|
||||
ctx = context.WithValue(ctx, "authToken", n.AuthToken)
|
||||
|
||||
return h.send(ctx, endpoint, payload)
|
||||
}
|
||||
|
||||
func (h httpNodeHandshake) send(ctx context.Context, endpoint string, payload map[string]string) error {
|
||||
body := bytes.NewBuffer(nil)
|
||||
if err := json.NewEncoder(body).Encode(payload); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
req, err := http.NewRequest(http.MethodPost, endpoint, body)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
req.Header.Set("Content-Type", "application/json")
|
||||
|
||||
if authToken := ctx.Value("authToken"); authToken != nil {
|
||||
req.Header.Set("Authorization", fmt.Sprintf("Bearer %s", authToken))
|
||||
}
|
||||
|
||||
rsp, err := h.httpClient.Do(req.WithContext(ctx))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if rsp.StatusCode != http.StatusOK {
|
||||
return fmt.Errorf("pairing failed, remote host retuned non-200 response")
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
71
server/federation/service/node_sync.go
Normal file
71
server/federation/service/node_sync.go
Normal file
@@ -0,0 +1,71 @@
|
||||
package service
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/cortezaproject/corteza/server/federation/types"
|
||||
"github.com/cortezaproject/corteza/server/pkg/actionlog"
|
||||
"github.com/cortezaproject/corteza/server/pkg/filter"
|
||||
"github.com/cortezaproject/corteza/server/store"
|
||||
)
|
||||
|
||||
type (
|
||||
nodeSync struct {
|
||||
store store.Storer
|
||||
actionlog actionlog.Recorder
|
||||
}
|
||||
|
||||
NodeSyncService interface {
|
||||
Create(ctx context.Context, new *types.NodeSync) (*types.NodeSync, error)
|
||||
Search(ctx context.Context, f types.NodeSyncFilter) (types.NodeSyncSet, types.NodeSyncFilter, error)
|
||||
LookupLastSuccessfulSync(ctx context.Context, nodeID uint64, syncType string) (*types.NodeSync, error)
|
||||
}
|
||||
)
|
||||
|
||||
func NodeSync() NodeSyncService {
|
||||
return &nodeSync{
|
||||
store: DefaultStore,
|
||||
actionlog: DefaultActionlog,
|
||||
}
|
||||
}
|
||||
|
||||
func (svc nodeSync) Create(ctx context.Context, new *types.NodeSync) (*types.NodeSync, error) {
|
||||
var (
|
||||
aProps = &nodeSyncActionProps{nodeSync: new}
|
||||
)
|
||||
|
||||
err := store.Tx(ctx, svc.store, func(ctx context.Context, s store.Storer) (err error) {
|
||||
if _, err := DefaultNode.FindByID(ctx, new.NodeID); err != nil {
|
||||
return NodeSyncErrNodeNotFound()
|
||||
}
|
||||
|
||||
return store.CreateFederationNodeSync(ctx, s, new)
|
||||
})
|
||||
|
||||
return new, svc.recordAction(ctx, aProps, NodeSyncActionCreate, err)
|
||||
}
|
||||
|
||||
func (svc nodeSync) Search(ctx context.Context, f types.NodeSyncFilter) (types.NodeSyncSet, types.NodeSyncFilter, error) {
|
||||
return store.SearchFederationNodeSyncs(ctx, svc.store, f)
|
||||
}
|
||||
|
||||
func (svc nodeSync) LookupLastSuccessfulSync(ctx context.Context, nodeID uint64, syncType string) (ns *types.NodeSync, err error) {
|
||||
// todo - filter by sync-type does not work
|
||||
s, _, err := store.SearchFederationNodeSyncs(ctx, svc.store, types.NodeSyncFilter{
|
||||
NodeID: nodeID,
|
||||
SyncType: syncType,
|
||||
SyncStatus: types.NodeSyncStatusSuccess,
|
||||
Sorting: filter.Sorting{
|
||||
Sort: filter.SortExprSet{
|
||||
&filter.SortExpr{Column: "time_of_action", Descending: true},
|
||||
},
|
||||
},
|
||||
Paging: filter.Paging{Limit: 1},
|
||||
})
|
||||
|
||||
if err != nil || len(s) == 0 {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return s[0], nil
|
||||
}
|
||||
359
server/federation/service/node_sync_actions.gen.go
generated
Normal file
359
server/federation/service/node_sync_actions.gen.go
generated
Normal file
@@ -0,0 +1,359 @@
|
||||
package service
|
||||
|
||||
// This file is auto-generated.
|
||||
//
|
||||
// Changes to this file may cause incorrect behavior and will be lost if
|
||||
// the code is regenerated.
|
||||
//
|
||||
// Definitions file that controls how this file is generated:
|
||||
// federation/service/node_sync_actions.yaml
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"github.com/cortezaproject/corteza/server/federation/types"
|
||||
"github.com/cortezaproject/corteza/server/pkg/actionlog"
|
||||
"github.com/cortezaproject/corteza/server/pkg/errors"
|
||||
"github.com/cortezaproject/corteza/server/pkg/locale"
|
||||
"strings"
|
||||
"time"
|
||||
)
|
||||
|
||||
type (
|
||||
nodeSyncActionProps struct {
|
||||
nodeSync *types.NodeSync
|
||||
nodeSyncFilter *types.NodeSyncFilter
|
||||
}
|
||||
|
||||
nodeSyncAction struct {
|
||||
timestamp time.Time
|
||||
resource string
|
||||
action string
|
||||
log string
|
||||
severity actionlog.Severity
|
||||
|
||||
// prefix for error when action fails
|
||||
errorMessage string
|
||||
|
||||
props *nodeSyncActionProps
|
||||
}
|
||||
|
||||
nodeSyncLogMetaKey struct{}
|
||||
nodeSyncPropsMetaKey struct{}
|
||||
)
|
||||
|
||||
var (
|
||||
// just a placeholder to cover template cases w/o fmt package use
|
||||
_ = fmt.Println
|
||||
)
|
||||
|
||||
// *********************************************************************************************************************
|
||||
// *********************************************************************************************************************
|
||||
// Props methods
|
||||
// setNodeSync updates nodeSyncActionProps's nodeSync
|
||||
//
|
||||
// This function is auto-generated.
|
||||
func (p *nodeSyncActionProps) setNodeSync(nodeSync *types.NodeSync) *nodeSyncActionProps {
|
||||
p.nodeSync = nodeSync
|
||||
return p
|
||||
}
|
||||
|
||||
// setNodeSyncFilter updates nodeSyncActionProps's nodeSyncFilter
|
||||
//
|
||||
// This function is auto-generated.
|
||||
func (p *nodeSyncActionProps) setNodeSyncFilter(nodeSyncFilter *types.NodeSyncFilter) *nodeSyncActionProps {
|
||||
p.nodeSyncFilter = nodeSyncFilter
|
||||
return p
|
||||
}
|
||||
|
||||
// Serialize converts nodeSyncActionProps to actionlog.Meta
|
||||
//
|
||||
// This function is auto-generated.
|
||||
func (p nodeSyncActionProps) Serialize() actionlog.Meta {
|
||||
var (
|
||||
m = make(actionlog.Meta)
|
||||
)
|
||||
|
||||
if p.nodeSync != nil {
|
||||
m.Set("nodeSync.NodeID", p.nodeSync.NodeID, true)
|
||||
m.Set("nodeSync.SyncStatus", p.nodeSync.SyncStatus, true)
|
||||
m.Set("nodeSync.SyncType", p.nodeSync.SyncType, true)
|
||||
m.Set("nodeSync.TimeOfAction", p.nodeSync.TimeOfAction, true)
|
||||
}
|
||||
if p.nodeSyncFilter != nil {
|
||||
m.Set("nodeSyncFilter.Query", p.nodeSyncFilter.Query, true)
|
||||
}
|
||||
|
||||
return m
|
||||
}
|
||||
|
||||
// tr translates string and replaces meta value placeholder with values
|
||||
//
|
||||
// This function is auto-generated.
|
||||
func (p nodeSyncActionProps) Format(in string, err error) string {
|
||||
var (
|
||||
pairs = []string{"{{err}}"}
|
||||
// first non-empty string
|
||||
fns = func(ii ...interface{}) string {
|
||||
for _, i := range ii {
|
||||
if s := fmt.Sprintf("%v", i); len(s) > 0 {
|
||||
return s
|
||||
}
|
||||
}
|
||||
|
||||
return ""
|
||||
}
|
||||
)
|
||||
|
||||
if err != nil {
|
||||
pairs = append(pairs, err.Error())
|
||||
} else {
|
||||
pairs = append(pairs, "nil")
|
||||
}
|
||||
|
||||
if p.nodeSync != nil {
|
||||
// replacement for "{{nodeSync}}" (in order how fields are defined)
|
||||
pairs = append(
|
||||
pairs,
|
||||
"{{nodeSync}}",
|
||||
fns(
|
||||
p.nodeSync.NodeID,
|
||||
p.nodeSync.SyncStatus,
|
||||
p.nodeSync.SyncType,
|
||||
p.nodeSync.TimeOfAction,
|
||||
),
|
||||
)
|
||||
pairs = append(pairs, "{{nodeSync.NodeID}}", fns(p.nodeSync.NodeID))
|
||||
pairs = append(pairs, "{{nodeSync.SyncStatus}}", fns(p.nodeSync.SyncStatus))
|
||||
pairs = append(pairs, "{{nodeSync.SyncType}}", fns(p.nodeSync.SyncType))
|
||||
pairs = append(pairs, "{{nodeSync.TimeOfAction}}", fns(p.nodeSync.TimeOfAction))
|
||||
}
|
||||
|
||||
if p.nodeSyncFilter != nil {
|
||||
// replacement for "{{nodeSyncFilter}}" (in order how fields are defined)
|
||||
pairs = append(
|
||||
pairs,
|
||||
"{{nodeSyncFilter}}",
|
||||
fns(
|
||||
p.nodeSyncFilter.Query,
|
||||
),
|
||||
)
|
||||
pairs = append(pairs, "{{nodeSyncFilter.Query}}", fns(p.nodeSyncFilter.Query))
|
||||
}
|
||||
return strings.NewReplacer(pairs...).Replace(in)
|
||||
}
|
||||
|
||||
// *********************************************************************************************************************
|
||||
// *********************************************************************************************************************
|
||||
// Action methods
|
||||
|
||||
// String returns loggable description as string
|
||||
//
|
||||
// This function is auto-generated.
|
||||
func (a *nodeSyncAction) String() string {
|
||||
var props = &nodeSyncActionProps{}
|
||||
|
||||
if a.props != nil {
|
||||
props = a.props
|
||||
}
|
||||
|
||||
return props.Format(a.log, nil)
|
||||
}
|
||||
|
||||
func (e *nodeSyncAction) ToAction() *actionlog.Action {
|
||||
return &actionlog.Action{
|
||||
Resource: e.resource,
|
||||
Action: e.action,
|
||||
Severity: e.severity,
|
||||
Description: e.String(),
|
||||
Meta: e.props.Serialize(),
|
||||
}
|
||||
}
|
||||
|
||||
// *********************************************************************************************************************
|
||||
// *********************************************************************************************************************
|
||||
// Action constructors
|
||||
|
||||
// NodeSyncActionLookup returns "federation:node_sync.lookup" action
|
||||
//
|
||||
// This function is auto-generated.
|
||||
func NodeSyncActionLookup(props ...*nodeSyncActionProps) *nodeSyncAction {
|
||||
a := &nodeSyncAction{
|
||||
timestamp: time.Now(),
|
||||
resource: "federation:node_sync",
|
||||
action: "lookup",
|
||||
log: "looked-up for the last successful sync",
|
||||
severity: actionlog.Info,
|
||||
}
|
||||
|
||||
if len(props) > 0 {
|
||||
a.props = props[0]
|
||||
}
|
||||
|
||||
return a
|
||||
}
|
||||
|
||||
// NodeSyncActionCreate returns "federation:node_sync.create" action
|
||||
//
|
||||
// This function is auto-generated.
|
||||
func NodeSyncActionCreate(props ...*nodeSyncActionProps) *nodeSyncAction {
|
||||
a := &nodeSyncAction{
|
||||
timestamp: time.Now(),
|
||||
resource: "federation:node_sync",
|
||||
action: "create",
|
||||
log: "created node_sync",
|
||||
severity: actionlog.Notice,
|
||||
}
|
||||
|
||||
if len(props) > 0 {
|
||||
a.props = props[0]
|
||||
}
|
||||
|
||||
return a
|
||||
}
|
||||
|
||||
// *********************************************************************************************************************
|
||||
// *********************************************************************************************************************
|
||||
// Error constructors
|
||||
|
||||
// NodeSyncErrGeneric returns "federation:node_sync.generic" as *errors.Error
|
||||
//
|
||||
// This function is auto-generated.
|
||||
func NodeSyncErrGeneric(mm ...*nodeSyncActionProps) *errors.Error {
|
||||
var p = &nodeSyncActionProps{}
|
||||
if len(mm) > 0 {
|
||||
p = mm[0]
|
||||
}
|
||||
|
||||
var e = errors.New(
|
||||
errors.KindInternal,
|
||||
|
||||
p.Format("failed to complete request due to internal error", nil),
|
||||
|
||||
errors.Meta("type", "generic"),
|
||||
errors.Meta("resource", "federation:node_sync"),
|
||||
|
||||
// action log entry; no formatting, it will be applied inside recordAction fn.
|
||||
errors.Meta(nodeSyncLogMetaKey{}, "{err}"),
|
||||
errors.Meta(nodeSyncPropsMetaKey{}, p),
|
||||
|
||||
// translation namespace & key
|
||||
errors.Meta(locale.ErrorMetaNamespace{}, "federation"),
|
||||
errors.Meta(locale.ErrorMetaKey{}, "node-sync.errors.generic"),
|
||||
|
||||
errors.StackSkip(1),
|
||||
)
|
||||
|
||||
if len(mm) > 0 {
|
||||
}
|
||||
|
||||
return e
|
||||
}
|
||||
|
||||
// NodeSyncErrNotFound returns "federation:node_sync.notFound" as *errors.Error
|
||||
//
|
||||
// This function is auto-generated.
|
||||
func NodeSyncErrNotFound(mm ...*nodeSyncActionProps) *errors.Error {
|
||||
var p = &nodeSyncActionProps{}
|
||||
if len(mm) > 0 {
|
||||
p = mm[0]
|
||||
}
|
||||
|
||||
var e = errors.New(
|
||||
errors.KindInternal,
|
||||
|
||||
p.Format("node_sync does not exist", nil),
|
||||
|
||||
errors.Meta("type", "notFound"),
|
||||
errors.Meta("resource", "federation:node_sync"),
|
||||
|
||||
errors.Meta(nodeSyncPropsMetaKey{}, p),
|
||||
|
||||
// translation namespace & key
|
||||
errors.Meta(locale.ErrorMetaNamespace{}, "federation"),
|
||||
errors.Meta(locale.ErrorMetaKey{}, "node-sync.errors.notFound"),
|
||||
|
||||
errors.StackSkip(1),
|
||||
)
|
||||
|
||||
if len(mm) > 0 {
|
||||
}
|
||||
|
||||
return e
|
||||
}
|
||||
|
||||
// NodeSyncErrNodeNotFound returns "federation:node_sync.nodeNotFound" as *errors.Error
|
||||
//
|
||||
// This function is auto-generated.
|
||||
func NodeSyncErrNodeNotFound(mm ...*nodeSyncActionProps) *errors.Error {
|
||||
var p = &nodeSyncActionProps{}
|
||||
if len(mm) > 0 {
|
||||
p = mm[0]
|
||||
}
|
||||
|
||||
var e = errors.New(
|
||||
errors.KindInternal,
|
||||
|
||||
p.Format("node does not exist", nil),
|
||||
|
||||
errors.Meta("type", "nodeNotFound"),
|
||||
errors.Meta("resource", "federation:node_sync"),
|
||||
|
||||
errors.Meta(nodeSyncPropsMetaKey{}, p),
|
||||
|
||||
// translation namespace & key
|
||||
errors.Meta(locale.ErrorMetaNamespace{}, "federation"),
|
||||
errors.Meta(locale.ErrorMetaKey{}, "node-sync.errors.nodeNotFound"),
|
||||
|
||||
errors.StackSkip(1),
|
||||
)
|
||||
|
||||
if len(mm) > 0 {
|
||||
}
|
||||
|
||||
return e
|
||||
}
|
||||
|
||||
// *********************************************************************************************************************
|
||||
// *********************************************************************************************************************
|
||||
|
||||
// recordAction is a service helper function wraps function that can return error
|
||||
//
|
||||
// It will wrap unrecognized/internal errors with generic errors.
|
||||
//
|
||||
// This function is auto-generated.
|
||||
func (svc nodeSync) recordAction(ctx context.Context, props *nodeSyncActionProps, actionFn func(...*nodeSyncActionProps) *nodeSyncAction, err error) error {
|
||||
if svc.actionlog == nil || actionFn == nil {
|
||||
// action log disabled or no action fn passed, return error as-is
|
||||
return err
|
||||
} else if err == nil {
|
||||
// action completed w/o error, record it
|
||||
svc.actionlog.Record(ctx, actionFn(props).ToAction())
|
||||
return nil
|
||||
}
|
||||
|
||||
a := actionFn(props).ToAction()
|
||||
|
||||
// Extracting error information and recording it as action
|
||||
a.Error = err.Error()
|
||||
|
||||
switch c := err.(type) {
|
||||
case *errors.Error:
|
||||
m := c.Meta()
|
||||
|
||||
a.Error = err.Error()
|
||||
a.Severity = actionlog.Severity(m.AsInt("severity"))
|
||||
a.Description = props.Format(m.AsString(nodeSyncLogMetaKey{}), err)
|
||||
|
||||
if p, has := m[nodeSyncPropsMetaKey{}]; has {
|
||||
a.Meta = p.(*nodeSyncActionProps).Serialize()
|
||||
}
|
||||
|
||||
svc.actionlog.Record(ctx, a)
|
||||
default:
|
||||
svc.actionlog.Record(ctx, a)
|
||||
}
|
||||
|
||||
// Original error is passed on
|
||||
return err
|
||||
}
|
||||
38
server/federation/service/node_sync_actions.yaml
Normal file
38
server/federation/service/node_sync_actions.yaml
Normal file
@@ -0,0 +1,38 @@
|
||||
# List of loggable service actions
|
||||
|
||||
resource: federation:node_sync
|
||||
service: nodeSync
|
||||
|
||||
# Default sensitivity for actions
|
||||
defaultActionSeverity: notice
|
||||
|
||||
# default severity for errors
|
||||
defaultErrorSeverity: error
|
||||
|
||||
import:
|
||||
- github.com/cortezaproject/corteza/server/federation/types
|
||||
|
||||
props:
|
||||
- name: nodeSync
|
||||
type: "*types.NodeSync"
|
||||
fields: [ NodeID, SyncStatus, SyncType, TimeOfAction ]
|
||||
- name: nodeSyncFilter
|
||||
type: "*types.NodeSyncFilter"
|
||||
fields: [ Query ]
|
||||
|
||||
actions:
|
||||
- action: lookup
|
||||
log: "looked-up for the last successful sync"
|
||||
severity: info
|
||||
|
||||
- action: create
|
||||
log: "created node_sync"
|
||||
|
||||
errors:
|
||||
- error: notFound
|
||||
message: "node_sync does not exist"
|
||||
severity: warning
|
||||
|
||||
- error: nodeNotFound
|
||||
message: "node does not exist"
|
||||
severity: warning
|
||||
77
server/federation/service/node_test.go
Normal file
77
server/federation/service/node_test.go
Normal file
@@ -0,0 +1,77 @@
|
||||
package service
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/cortezaproject/corteza/server/federation/types"
|
||||
"github.com/stretchr/testify/require"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestNode_decodePairingURI(t *testing.T) {
|
||||
var (
|
||||
tcc = []struct {
|
||||
name string
|
||||
uri string
|
||||
node *types.Node
|
||||
err error
|
||||
}{
|
||||
{
|
||||
"happy path",
|
||||
"https://42:secret-secret-secret-secret-1234@example.tld/federation?name=Noddy",
|
||||
&types.Node{SharedNodeID: 42, Name: "Noddy", BaseURL: "https://example.tld/federation", PairToken: "secret-secret-secret-secret-1234"},
|
||||
nil,
|
||||
},
|
||||
{
|
||||
"no name",
|
||||
"https://42:secret-secret-secret-secret-1234@example.tld",
|
||||
&types.Node{SharedNodeID: 42, BaseURL: "https://example.tld/", PairToken: "secret-secret-secret-secret-1234"},
|
||||
nil,
|
||||
},
|
||||
{
|
||||
"no token",
|
||||
"https://42@example.tld",
|
||||
nil,
|
||||
NodeErrPairingURITokenInvalid(),
|
||||
},
|
||||
{
|
||||
"no node id",
|
||||
"https://:secret-secret-secret-secret-1234@example.tld",
|
||||
nil,
|
||||
NodeErrPairingURISourceIDInvalid(),
|
||||
},
|
||||
{
|
||||
"invalid URL",
|
||||
"https://this is not a valid url",
|
||||
nil,
|
||||
NodeErrPairingURIInvalid().Wrap(fmt.Errorf(`invalid character " " in host name`)),
|
||||
},
|
||||
}
|
||||
)
|
||||
|
||||
for _, tc := range tcc {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
var (
|
||||
req = require.New(t)
|
||||
out, err = (&node{}).decodePairingURI(tc.uri)
|
||||
)
|
||||
|
||||
if tc.err != nil {
|
||||
req.Equal(tc.err.Error(), err.Error())
|
||||
} else {
|
||||
req.NoError(err)
|
||||
req.Equal(tc.node, out)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestNode_makePairingURI(t *testing.T) {
|
||||
var (
|
||||
svc = &node{host: "example.tld", baseURL: "federation", name: "Noddy"}
|
||||
req = require.New(t)
|
||||
n = &types.Node{ID: 42, PairToken: "secret"}
|
||||
uri = svc.makePairingURI(n)
|
||||
)
|
||||
|
||||
req.Equal("https://42:secret@example.tld/federation?name=Noddy", uri)
|
||||
}
|
||||
11
server/federation/service/processer.go
Normal file
11
server/federation/service/processer.go
Normal file
@@ -0,0 +1,11 @@
|
||||
package service
|
||||
|
||||
import "context"
|
||||
|
||||
type (
|
||||
Processer interface {
|
||||
Process(ctx context.Context, payload []byte) (ProcesserResponse, error)
|
||||
}
|
||||
|
||||
ProcesserResponse interface{}
|
||||
)
|
||||
134
server/federation/service/processer_data.go
Normal file
134
server/federation/service/processer_data.go
Normal file
@@ -0,0 +1,134 @@
|
||||
package service
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
|
||||
ct "github.com/cortezaproject/corteza/server/compose/types"
|
||||
"github.com/cortezaproject/corteza/server/federation/service/decoder"
|
||||
"github.com/cortezaproject/corteza/server/federation/types"
|
||||
"github.com/cortezaproject/corteza/server/pkg/auth"
|
||||
st "github.com/cortezaproject/corteza/server/system/types"
|
||||
)
|
||||
|
||||
type (
|
||||
dataProcesser struct {
|
||||
ID uint64
|
||||
ComposeModuleID uint64
|
||||
ComposeNamespaceID uint64
|
||||
NodeBaseURL string
|
||||
ModuleMappings *types.ModuleFieldMappingSet
|
||||
ModuleMappingValues *ct.RecordValueSet
|
||||
SyncService *Sync
|
||||
Node *types.Node
|
||||
User *st.User
|
||||
}
|
||||
|
||||
dataProcesserResponse struct {
|
||||
Processed int
|
||||
}
|
||||
)
|
||||
|
||||
// Process gets the payload from syncer and
|
||||
// uses the decode package to decode the whole set, depending on
|
||||
// the filtering that was used (limit)
|
||||
func (dp *dataProcesser) Process(ctx context.Context, payload []byte) (ProcesserResponse, error) {
|
||||
processed := 0
|
||||
o, err := decoder.DecodeFederationRecordSync([]byte(payload))
|
||||
|
||||
if err != nil {
|
||||
return dataProcesserResponse{
|
||||
Processed: processed,
|
||||
}, err
|
||||
}
|
||||
|
||||
if len(o) == 0 {
|
||||
return dataProcesserResponse{
|
||||
Processed: processed,
|
||||
}, nil
|
||||
}
|
||||
|
||||
ctx = auth.SetIdentityToContext(ctx, auth.FederationUser())
|
||||
|
||||
for _, er := range o {
|
||||
var (
|
||||
rec *ct.Record
|
||||
err error
|
||||
)
|
||||
|
||||
dp.SyncService.mapper.Merge(&er.Values, dp.ModuleMappingValues, dp.ModuleMappings)
|
||||
|
||||
if er.DeletedAt != nil {
|
||||
// find the record
|
||||
if rec, err = dp.findRecordByFederationID(ctx, er.ID, dp.ComposeModuleID, dp.ComposeNamespaceID); err != nil {
|
||||
continue
|
||||
}
|
||||
|
||||
// Handle edge cases where the data doesn't exist anymore
|
||||
if rec != nil {
|
||||
dp.SyncService.DeleteRecord(ctx, rec)
|
||||
}
|
||||
processed++
|
||||
|
||||
continue
|
||||
}
|
||||
|
||||
if er.UpdatedAt != nil {
|
||||
if rec, err = dp.findRecordByFederationID(ctx, er.ID, dp.ComposeModuleID, dp.ComposeNamespaceID); err != nil {
|
||||
// could not find existing record
|
||||
continue
|
||||
}
|
||||
|
||||
if rec != nil {
|
||||
rec.Values = *dp.ModuleMappingValues
|
||||
}
|
||||
}
|
||||
|
||||
// if the record was updated on origin, but we somehow do not have it
|
||||
// create it anyway
|
||||
if rec == nil {
|
||||
rec = &ct.Record{
|
||||
ModuleID: dp.ComposeModuleID,
|
||||
NamespaceID: dp.ComposeNamespaceID,
|
||||
Values: *dp.ModuleMappingValues,
|
||||
Meta: map[string]any{
|
||||
"federation": dp.NodeBaseURL,
|
||||
"federation_extrecord": fmt.Sprintf("%d", er.ID),
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
if rec.ID != 0 {
|
||||
_, err = dp.SyncService.UpdateRecord(ctx, rec)
|
||||
} else {
|
||||
_, err = dp.SyncService.CreateRecord(ctx, rec)
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
|
||||
processed++
|
||||
}
|
||||
|
||||
return dataProcesserResponse{
|
||||
Processed: processed,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// findRecordByFederationID finds any already existing records via
|
||||
// federation label
|
||||
func (dp *dataProcesser) findRecordByFederationID(ctx context.Context, recordID, moduleID, namespaceID uint64) (r *ct.Record, err error) {
|
||||
filter := ct.RecordFilter{
|
||||
NamespaceID: namespaceID,
|
||||
ModuleID: moduleID,
|
||||
Meta: map[string]any{"federation_extrecord": fmt.Sprintf("%d", recordID)}}
|
||||
|
||||
if s, err := dp.SyncService.FindRecords(ctx, filter); err == nil {
|
||||
if len(s) == 1 {
|
||||
r = s[0]
|
||||
}
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
230
server/federation/service/processer_data_test.go
Normal file
230
server/federation/service/processer_data_test.go
Normal file
@@ -0,0 +1,230 @@
|
||||
package service
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"testing"
|
||||
|
||||
cs "github.com/cortezaproject/corteza/server/compose/service"
|
||||
ct "github.com/cortezaproject/corteza/server/compose/types"
|
||||
"github.com/cortezaproject/corteza/server/federation/types"
|
||||
ss "github.com/cortezaproject/corteza/server/system/service"
|
||||
st "github.com/cortezaproject/corteza/server/system/types"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
type (
|
||||
testSyncService struct {
|
||||
Sync
|
||||
}
|
||||
|
||||
testSharedModuleService struct {
|
||||
SharedModuleService
|
||||
}
|
||||
testRecordServicePersistSuccess struct {
|
||||
cs.RecordService
|
||||
}
|
||||
testRecordServiceUpdateSuccess struct {
|
||||
cs.RecordService
|
||||
}
|
||||
testRecordServiceDeleteSuccess struct {
|
||||
cs.RecordService
|
||||
}
|
||||
testRecordServicePersistError struct {
|
||||
cs.RecordService
|
||||
}
|
||||
testUserService struct {
|
||||
ss.UserService
|
||||
}
|
||||
testRoleService struct {
|
||||
ss.RoleService
|
||||
}
|
||||
)
|
||||
|
||||
func TestProcesserData_persist(t *testing.T) {
|
||||
|
||||
var (
|
||||
tcc = []struct {
|
||||
name string
|
||||
payload string
|
||||
mappings string
|
||||
persisted int
|
||||
err string
|
||||
values *ct.RecordValueSet
|
||||
s *Sync
|
||||
}{
|
||||
{
|
||||
"successful persist on valid mapping",
|
||||
`{"response": {"set": [{"recordID":"1","values":[{"name":"Facebook","value":"foobar"}],"createdAt":"2020-12-05T10:10:10Z"}]}}`,
|
||||
`[{"origin":{"kind":"String","name":"Description","label":"Description","isMulti":false},"destination":{"kind":"String","name":"Name","label":"Description","isMulti":false}},{"origin":{"kind":"Url","name":"Facebook","label":"Facebook","isMulti":false},"destination":{"kind":"Url","name":"Fb","label":"Facebook","isMulti":false}}]`,
|
||||
1,
|
||||
"",
|
||||
&ct.RecordValueSet{&ct.RecordValue{Name: "Fb", Value: ""}},
|
||||
NewSync(
|
||||
&Syncer{},
|
||||
&Mapper{},
|
||||
&testSharedModuleService{},
|
||||
&testRecordServicePersistSuccess{},
|
||||
&testUserService{},
|
||||
&testRoleService{}),
|
||||
},
|
||||
{
|
||||
"successful update on valid mapping and existing federated record",
|
||||
`{"response": {"set": [{"recordID":"1","values":[{"name":"Facebook","value":"foobar"}],"createdAt":"2020-12-05T10:10:10Z", "updatedAt":"2020-12-06T10:10:10Z"}]}}`,
|
||||
`[{"origin":{"kind":"String","name":"Description","label":"Description","isMulti":false},"destination":{"kind":"String","name":"Name","label":"Description","isMulti":false}},{"origin":{"kind":"Url","name":"Facebook","label":"Facebook","isMulti":false},"destination":{"kind":"Url","name":"Fb","label":"Facebook","isMulti":false}}]`,
|
||||
1,
|
||||
"",
|
||||
&ct.RecordValueSet{&ct.RecordValue{Name: "Fb", Value: ""}},
|
||||
NewSync(
|
||||
&Syncer{},
|
||||
&Mapper{},
|
||||
&testSharedModuleService{},
|
||||
&testRecordServiceUpdateSuccess{},
|
||||
&testUserService{},
|
||||
&testRoleService{}),
|
||||
},
|
||||
{
|
||||
"successful delete on valid mapping and existing federated record",
|
||||
`{"response": {"set": [{"recordID":"1","values":[{"name":"Facebook","value":"foobar"}],"createdAt":"2020-12-05T10:10:10Z", "deletedAt":"2020-12-07T10:10:10Z"}]}}`,
|
||||
`[{"origin":{"kind":"String","name":"Description","label":"Description","isMulti":false},"destination":{"kind":"String","name":"Name","label":"Description","isMulti":false}},{"origin":{"kind":"Url","name":"Facebook","label":"Facebook","isMulti":false},"destination":{"kind":"Url","name":"Fb","label":"Facebook","isMulti":false}}]`,
|
||||
1,
|
||||
"",
|
||||
&ct.RecordValueSet{&ct.RecordValue{Name: "Fb", Value: ""}},
|
||||
NewSync(
|
||||
&Syncer{},
|
||||
&Mapper{},
|
||||
&testSharedModuleService{},
|
||||
&testRecordServiceDeleteSuccess{},
|
||||
&testUserService{},
|
||||
&testRoleService{}),
|
||||
},
|
||||
{
|
||||
"persist error on valid mapping",
|
||||
`{"response": {"set": [{"recordID":"1","values":[{"name":"Facebook","value":"foobar"}]}]}}`,
|
||||
`[{"origin":{"kind":"String","name":"Description","label":"Description","isMulti":false},"destination":{"kind":"String","name":"Name","label":"Description","isMulti":false}},{"origin":{"kind":"Url","name":"Facebook","label":"Facebook","isMulti":false},"destination":{"kind":"Url","name":"Fb","label":"Facebook","isMulti":false}}]`,
|
||||
0,
|
||||
"",
|
||||
&ct.RecordValueSet{&ct.RecordValue{Name: "Fb", Value: ""}},
|
||||
NewSync(
|
||||
&Syncer{},
|
||||
&Mapper{},
|
||||
&testSharedModuleService{},
|
||||
&testRecordServicePersistError{},
|
||||
&testUserService{},
|
||||
&testRoleService{}),
|
||||
},
|
||||
{
|
||||
"persist one record on mixed mappings",
|
||||
`{"response": {"set": [{"recordID":"1","values":[{"name":"THIS_FIELD_NAME_IS_NOT_ON_ORIGIN","value":"this value will not be set"}]}, {"recordID":"1","values":[{"name":"Facebook","value":"this value WILL be set"}]}]}}`,
|
||||
`[{"origin":{"kind":"String","name":"Description","label":"Description","isMulti":false},"destination":{"kind":"String","name":"Name","label":"Description","isMulti":false}},{"origin":{"kind":"Url","name":"Facebook","label":"Facebook","isMulti":false},"destination":{"kind":"Url","name":"Fb","label":"Facebook","isMulti":false}}]`,
|
||||
2,
|
||||
"",
|
||||
&ct.RecordValueSet{&ct.RecordValue{Name: "Fb", Value: ""}},
|
||||
NewSync(
|
||||
&Syncer{},
|
||||
&Mapper{},
|
||||
&testSharedModuleService{},
|
||||
&testRecordServicePersistSuccess{},
|
||||
&testUserService{},
|
||||
&testRoleService{}),
|
||||
},
|
||||
{
|
||||
"no records from payload",
|
||||
`{"response": {"set": []}}`,
|
||||
`[{"origin":{"kind":"String","name":"Description","label":"Description","isMulti":false},"destination":{"kind":"String","name":"Name","label":"Description","isMulti":false}},{"origin":{"kind":"Url","name":"Facebook","label":"Facebook","isMulti":false},"destination":{"kind":"Url","name":"Fb","label":"Facebook","isMulti":false}}]`,
|
||||
0,
|
||||
"",
|
||||
&ct.RecordValueSet{&ct.RecordValue{Name: "Fb", Value: ""}},
|
||||
NewSync(
|
||||
&Syncer{},
|
||||
&Mapper{},
|
||||
&testSharedModuleService{},
|
||||
&testRecordServicePersistSuccess{},
|
||||
&testUserService{},
|
||||
&testRoleService{}),
|
||||
},
|
||||
{
|
||||
"validation error, no persist",
|
||||
`{"respon`,
|
||||
`[{"origin":{"kind":"String","name":"Description","label":"Description","isMulti":false},"destination":{"kind":"String","name":"Name","label":"Description","isMulti":false}},{"origin":{"kind":"Url","name":"Facebook","label":"Facebook","isMulti":false},"destination":{"kind":"Url","name":"Fb","label":"Facebook","isMulti":false}}]`,
|
||||
0,
|
||||
"unexpected end of JSON input",
|
||||
&ct.RecordValueSet{&ct.RecordValue{Name: "Fb", Value: ""}},
|
||||
NewSync(
|
||||
&Syncer{},
|
||||
&Mapper{},
|
||||
&testSharedModuleService{},
|
||||
&testRecordServicePersistSuccess{},
|
||||
&testUserService{},
|
||||
&testRoleService{}),
|
||||
},
|
||||
}
|
||||
)
|
||||
|
||||
for _, tc := range tcc {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
var (
|
||||
ctx = context.Background()
|
||||
req = require.New(t)
|
||||
)
|
||||
|
||||
mm := &types.ModuleFieldMappingSet{}
|
||||
json.Unmarshal([]byte(tc.mappings), mm)
|
||||
|
||||
dp := &dataProcesser{
|
||||
ID: 1,
|
||||
ComposeModuleID: 1,
|
||||
ComposeNamespaceID: 1,
|
||||
NodeBaseURL: "",
|
||||
ModuleMappings: mm,
|
||||
ModuleMappingValues: tc.values,
|
||||
SyncService: tc.s,
|
||||
Node: &types.Node{},
|
||||
User: &st.User{},
|
||||
}
|
||||
|
||||
out, err := dp.Process(ctx, []byte(tc.payload))
|
||||
|
||||
if tc.err != "" {
|
||||
req.Equal(tc.err, err.Error())
|
||||
} else {
|
||||
req.NoError(err)
|
||||
}
|
||||
|
||||
req.Equal(tc.persisted, out.(dataProcesserResponse).Processed)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// create success
|
||||
func (s testRecordServicePersistSuccess) Create(_ context.Context, record *ct.Record) (*ct.Record, *ct.RecordValueErrorSet, error) {
|
||||
return nil, nil, nil
|
||||
}
|
||||
|
||||
func (s testRecordServicePersistSuccess) Find(_ context.Context, filter ct.RecordFilter) (ct.RecordSet, ct.RecordFilter, error) {
|
||||
return ct.RecordSet{}, ct.RecordFilter{}, nil
|
||||
}
|
||||
|
||||
// update success
|
||||
func (s testRecordServiceUpdateSuccess) Update(_ context.Context, record *ct.Record) (*ct.Record, *ct.RecordValueErrorSet, error) {
|
||||
return nil, nil, nil
|
||||
}
|
||||
|
||||
func (s testRecordServiceUpdateSuccess) Find(_ context.Context, filter ct.RecordFilter) (ct.RecordSet, ct.RecordFilter, error) {
|
||||
return ct.RecordSet{&ct.Record{ID: 2}}, ct.RecordFilter{}, nil
|
||||
}
|
||||
|
||||
// delete success
|
||||
func (s testRecordServiceDeleteSuccess) DeleteByID(_ context.Context, namespaceID, moduleID uint64, recordID ...uint64) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s testRecordServiceDeleteSuccess) Find(_ context.Context, filter ct.RecordFilter) (ct.RecordSet, ct.RecordFilter, error) {
|
||||
return ct.RecordSet{&ct.Record{ID: 2, ModuleID: 2, NamespaceID: 2}}, ct.RecordFilter{}, nil
|
||||
}
|
||||
|
||||
// create error
|
||||
func (s testRecordServicePersistError) Create(_ context.Context, record *ct.Record) (*ct.Record, *ct.RecordValueErrorSet, error) {
|
||||
return nil, nil, fmt.Errorf("mocked error")
|
||||
}
|
||||
120
server/federation/service/processer_structure.go
Normal file
120
server/federation/service/processer_structure.go
Normal file
@@ -0,0 +1,120 @@
|
||||
package service
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/cortezaproject/corteza/server/federation/service/decoder"
|
||||
"github.com/cortezaproject/corteza/server/federation/types"
|
||||
st "github.com/cortezaproject/corteza/server/system/types"
|
||||
)
|
||||
|
||||
type (
|
||||
structureProcesser struct {
|
||||
SyncService *Sync
|
||||
Node *types.Node
|
||||
User *st.User
|
||||
}
|
||||
|
||||
structureProcesserResponse struct {
|
||||
ModuleID uint64
|
||||
Processed int
|
||||
}
|
||||
)
|
||||
|
||||
// Process gets the payload from syncer and
|
||||
// uses the decode package to decode the whole set, depending on
|
||||
// the filtering that was used (limit)
|
||||
func (dp *structureProcesser) Process(ctx context.Context, payload []byte) (ProcesserResponse, error) {
|
||||
processed := 0
|
||||
o, err := decoder.DecodeFederationModuleSync([]byte(payload))
|
||||
|
||||
if err != nil {
|
||||
return structureProcesserResponse{
|
||||
Processed: processed,
|
||||
}, err
|
||||
}
|
||||
|
||||
if len(o) == 0 {
|
||||
return structureProcesserResponse{
|
||||
Processed: processed,
|
||||
}, nil
|
||||
}
|
||||
|
||||
for _, em := range o {
|
||||
new := &types.SharedModule{
|
||||
NodeID: dp.Node.ID,
|
||||
ExternalFederationModuleID: em.ID,
|
||||
Fields: em.Fields,
|
||||
Handle: em.Handle,
|
||||
Name: em.Name,
|
||||
}
|
||||
|
||||
existing, err := dp.SyncService.LookupSharedModule(ctx, new)
|
||||
|
||||
if err != nil {
|
||||
return structureProcesserResponse{
|
||||
ModuleID: new.ExternalFederationModuleID,
|
||||
Processed: processed,
|
||||
}, err
|
||||
}
|
||||
|
||||
// create
|
||||
{
|
||||
if existing == nil {
|
||||
_, err := dp.SyncService.CreateSharedModule(ctx, new)
|
||||
|
||||
if err != nil {
|
||||
return structureProcesserResponse{
|
||||
ModuleID: new.ExternalFederationModuleID,
|
||||
Processed: processed,
|
||||
}, err
|
||||
}
|
||||
|
||||
processed++
|
||||
continue
|
||||
}
|
||||
}
|
||||
|
||||
// update
|
||||
{
|
||||
canUpdate, err := dp.SyncService.CanUpdateSharedModule(ctx, new, existing)
|
||||
|
||||
if err != nil {
|
||||
return structureProcesserResponse{
|
||||
ModuleID: existing.ExternalFederationModuleID,
|
||||
Processed: processed,
|
||||
}, err
|
||||
}
|
||||
|
||||
// stop the sync for this module
|
||||
if !canUpdate {
|
||||
return structureProcesserResponse{
|
||||
ModuleID: new.ExternalFederationModuleID,
|
||||
Processed: processed,
|
||||
}, SharedModuleErrFederationSyncStructureChanged(&sharedModuleActionProps{module: existing})
|
||||
}
|
||||
|
||||
existing.Name = new.Name
|
||||
existing.Handle = new.Handle
|
||||
existing.Fields = new.Fields
|
||||
|
||||
_, err = dp.SyncService.UpdateSharedModule(ctx, existing)
|
||||
|
||||
if err != nil {
|
||||
return structureProcesserResponse{
|
||||
ModuleID: existing.ExternalFederationModuleID,
|
||||
Processed: processed,
|
||||
}, err
|
||||
}
|
||||
|
||||
processed++
|
||||
continue
|
||||
}
|
||||
}
|
||||
|
||||
// use paging, but only one per-time
|
||||
return structureProcesserResponse{
|
||||
ModuleID: o[0].ID,
|
||||
Processed: processed,
|
||||
}, err
|
||||
}
|
||||
232
server/federation/service/processer_structure_test.go
Normal file
232
server/federation/service/processer_structure_test.go
Normal file
@@ -0,0 +1,232 @@
|
||||
package service
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"testing"
|
||||
|
||||
cs "github.com/cortezaproject/corteza/server/compose/service"
|
||||
"github.com/cortezaproject/corteza/server/federation/types"
|
||||
ss "github.com/cortezaproject/corteza/server/system/service"
|
||||
st "github.com/cortezaproject/corteza/server/system/types"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
type (
|
||||
testSyncStructureService struct{ Sync }
|
||||
testSyncStructureServiceCanUpdateFieldsError struct{ Sync }
|
||||
|
||||
testStructureSharedModuleService struct{ SharedModuleService }
|
||||
testStructureSharedModuleServiceError struct{ SharedModuleService }
|
||||
testStructureSharedModuleServiceCreateNewModule struct{ SharedModuleService }
|
||||
testStructureSharedModuleServiceCreateNewModuleErr struct{ SharedModuleService }
|
||||
testStructureSharedModuleServiceUpdateModule struct{ SharedModuleService }
|
||||
testStructureSharedModuleServiceUpdateModuleErr struct{ SharedModuleService }
|
||||
|
||||
testStructureRecordServicePersistSuccess struct{ cs.RecordService }
|
||||
testStructureUserService struct{ ss.UserService }
|
||||
testStructureRoleService struct{ ss.RoleService }
|
||||
)
|
||||
|
||||
func TestProcesserStructure_persist(t *testing.T) {
|
||||
|
||||
var (
|
||||
tcc = []struct {
|
||||
name string
|
||||
payload string
|
||||
persisted int
|
||||
err string
|
||||
s *Sync
|
||||
}{
|
||||
{
|
||||
"no modules from payload",
|
||||
`{"response": {"set": []}}`,
|
||||
0,
|
||||
"",
|
||||
NewSync(
|
||||
&Syncer{},
|
||||
&Mapper{},
|
||||
&testStructureSharedModuleService{},
|
||||
&testRecordServicePersistSuccess{},
|
||||
&testUserService{},
|
||||
&testRoleService{}),
|
||||
},
|
||||
{
|
||||
"validation error, no persist",
|
||||
`{"respon`,
|
||||
0,
|
||||
"unexpected end of JSON input",
|
||||
NewSync(
|
||||
&Syncer{},
|
||||
&Mapper{},
|
||||
&testStructureSharedModuleService{},
|
||||
&testRecordServicePersistSuccess{},
|
||||
&testUserService{},
|
||||
&testRoleService{}),
|
||||
},
|
||||
{
|
||||
"lookup existing module error",
|
||||
`{"response":{"set": [{"moduleID": "11", "nodeID": "21", "composeModuleID": "31", "composeNamespaceID":"32","handle":"handle","name":"name","fields":[]}]}}`,
|
||||
0,
|
||||
"db error",
|
||||
NewSync(
|
||||
&Syncer{},
|
||||
&Mapper{},
|
||||
&testStructureSharedModuleServiceError{},
|
||||
&testRecordServicePersistSuccess{},
|
||||
&testUserService{},
|
||||
&testRoleService{}),
|
||||
},
|
||||
{
|
||||
"create new module",
|
||||
`{"response":{"set": [{"moduleID": "11", "nodeID": "21", "composeModuleID": "31", "composeNamespaceID":"32","handle":"handle","name":"name","fields":[]}]}}`,
|
||||
1,
|
||||
"",
|
||||
NewSync(
|
||||
&Syncer{},
|
||||
&Mapper{},
|
||||
&testStructureSharedModuleServiceCreateNewModule{},
|
||||
&testRecordServicePersistSuccess{},
|
||||
&testUserService{},
|
||||
&testRoleService{}),
|
||||
},
|
||||
{
|
||||
"create new module error",
|
||||
`{"response":{"set": [{"moduleID": "11", "nodeID": "21", "composeModuleID": "31", "composeNamespaceID":"32","handle":"handle","name":"name","fields":[]}]}}`,
|
||||
0,
|
||||
"could not create new module",
|
||||
NewSync(
|
||||
&Syncer{},
|
||||
&Mapper{},
|
||||
&testStructureSharedModuleServiceCreateNewModuleErr{},
|
||||
&testRecordServicePersistSuccess{},
|
||||
&testUserService{},
|
||||
&testRoleService{}),
|
||||
},
|
||||
{
|
||||
"could not update module fields error",
|
||||
`{"response":{"set": [{"moduleID": "11", "nodeID": "21", "composeModuleID": "31", "composeNamespaceID":"32","handle":"handle","name":"name","fields":[]}]}}`,
|
||||
0,
|
||||
"module structure changed",
|
||||
NewSync(
|
||||
&Syncer{},
|
||||
&Mapper{},
|
||||
&testStructureSharedModuleServiceUpdateModule{},
|
||||
&testRecordServicePersistSuccess{},
|
||||
&testUserService{},
|
||||
&testRoleService{}),
|
||||
},
|
||||
{
|
||||
"update module fields success",
|
||||
`{"response":{"set": [{"moduleID": "11", "nodeID": "21", "composeModuleID": "31", "composeNamespaceID":"32","handle":"handle","name":"name","fields":[{"kind":"String","label":"label","isMulti":false,"name":"existingField"}]}]}}`,
|
||||
1,
|
||||
"",
|
||||
NewSync(
|
||||
&Syncer{},
|
||||
&Mapper{},
|
||||
&testStructureSharedModuleServiceUpdateModule{},
|
||||
&testRecordServicePersistSuccess{},
|
||||
&testUserService{},
|
||||
&testRoleService{}),
|
||||
},
|
||||
{
|
||||
"update module error",
|
||||
`{"response":{"set": [{"moduleID": "11", "nodeID": "21", "composeModuleID": "31", "composeNamespaceID":"32","handle":"handle","name":"name","fields":[]}]}}`,
|
||||
0,
|
||||
"could not update module",
|
||||
NewSync(
|
||||
&Syncer{},
|
||||
&Mapper{},
|
||||
&testStructureSharedModuleServiceUpdateModuleErr{},
|
||||
&testRecordServicePersistSuccess{},
|
||||
&testUserService{},
|
||||
&testRoleService{}),
|
||||
},
|
||||
}
|
||||
)
|
||||
|
||||
for _, tc := range tcc {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
var (
|
||||
ctx = context.Background()
|
||||
req = require.New(t)
|
||||
)
|
||||
|
||||
dp := &structureProcesser{
|
||||
SyncService: tc.s,
|
||||
Node: &types.Node{},
|
||||
User: &st.User{},
|
||||
}
|
||||
|
||||
out, err := dp.Process(ctx, []byte(tc.payload))
|
||||
|
||||
if tc.err != "" {
|
||||
if err != nil {
|
||||
req.Equal(tc.err, err.Error())
|
||||
} else {
|
||||
req.Fail("err should not be nil")
|
||||
}
|
||||
} else {
|
||||
req.NoError(err)
|
||||
}
|
||||
|
||||
req.Equal(tc.persisted, out.(structureProcesserResponse).Processed)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func (s testStructureSharedModuleServiceError) Find(ctx context.Context, filter types.SharedModuleFilter) (types.SharedModuleSet, types.SharedModuleFilter, error) {
|
||||
return types.SharedModuleSet{}, types.SharedModuleFilter{}, fmt.Errorf("db error")
|
||||
}
|
||||
|
||||
func (s testStructureSharedModuleServiceCreateNewModule) Find(ctx context.Context, filter types.SharedModuleFilter) (types.SharedModuleSet, types.SharedModuleFilter, error) {
|
||||
return types.SharedModuleSet{}, types.SharedModuleFilter{}, nil
|
||||
}
|
||||
|
||||
func (s testStructureSharedModuleServiceCreateNewModule) Create(ctx context.Context, new *types.SharedModule) (*types.SharedModule, error) {
|
||||
return &types.SharedModule{}, nil
|
||||
}
|
||||
|
||||
func (s testStructureSharedModuleServiceCreateNewModuleErr) Find(ctx context.Context, filter types.SharedModuleFilter) (types.SharedModuleSet, types.SharedModuleFilter, error) {
|
||||
return types.SharedModuleSet{}, types.SharedModuleFilter{}, nil
|
||||
}
|
||||
|
||||
func (s testStructureSharedModuleServiceCreateNewModuleErr) Create(ctx context.Context, new *types.SharedModule) (*types.SharedModule, error) {
|
||||
return nil, fmt.Errorf("could not create new module")
|
||||
}
|
||||
|
||||
func (s testStructureSharedModuleServiceUpdateModule) Find(ctx context.Context, filter types.SharedModuleFilter) (types.SharedModuleSet, types.SharedModuleFilter, error) {
|
||||
return types.SharedModuleSet{
|
||||
&types.SharedModule{
|
||||
ID: 11,
|
||||
NodeID: 21,
|
||||
Handle: "handle",
|
||||
Name: "name",
|
||||
Fields: types.ModuleFieldSet{
|
||||
&types.ModuleField{
|
||||
Kind: "String",
|
||||
Label: "label",
|
||||
IsMulti: false,
|
||||
Name: "existingField",
|
||||
},
|
||||
},
|
||||
}},
|
||||
types.SharedModuleFilter{},
|
||||
nil
|
||||
}
|
||||
|
||||
func (s testStructureSharedModuleServiceUpdateModule) Update(ctx context.Context, updated *types.SharedModule) (*types.SharedModule, error) {
|
||||
return &types.SharedModule{}, nil
|
||||
}
|
||||
|
||||
func (s testStructureSharedModuleServiceUpdateModuleErr) Find(ctx context.Context, filter types.SharedModuleFilter) (types.SharedModuleSet, types.SharedModuleFilter, error) {
|
||||
return types.SharedModuleSet{&types.SharedModule{
|
||||
Fields: types.ModuleFieldSet{},
|
||||
}},
|
||||
types.SharedModuleFilter{},
|
||||
nil
|
||||
}
|
||||
|
||||
func (s testStructureSharedModuleServiceUpdateModuleErr) Update(ctx context.Context, updated *types.SharedModule) (*types.SharedModule, error) {
|
||||
return nil, fmt.Errorf("could not update module")
|
||||
}
|
||||
139
server/federation/service/service.go
Normal file
139
server/federation/service/service.go
Normal file
@@ -0,0 +1,139 @@
|
||||
package service
|
||||
|
||||
import (
|
||||
"context"
|
||||
cs "github.com/cortezaproject/corteza/server/compose/service"
|
||||
"github.com/cortezaproject/corteza/server/pkg/actionlog"
|
||||
"github.com/cortezaproject/corteza/server/pkg/auth"
|
||||
"github.com/cortezaproject/corteza/server/pkg/id"
|
||||
"github.com/cortezaproject/corteza/server/pkg/label"
|
||||
"github.com/cortezaproject/corteza/server/pkg/logger"
|
||||
"github.com/cortezaproject/corteza/server/pkg/options"
|
||||
"github.com/cortezaproject/corteza/server/store"
|
||||
ss "github.com/cortezaproject/corteza/server/system/service"
|
||||
"github.com/cortezaproject/corteza/server/system/types"
|
||||
"go.uber.org/zap"
|
||||
"time"
|
||||
labelTypes "github.com/cortezaproject/corteza/server/pkg/label/types"
|
||||
)
|
||||
|
||||
type (
|
||||
Config struct {
|
||||
ActionLog options.ActionLogOpt
|
||||
Storage options.ObjectStoreOpt
|
||||
Federation options.FederationOpt
|
||||
Server options.HttpServerOpt
|
||||
}
|
||||
)
|
||||
|
||||
var (
|
||||
DefaultStore store.Storer
|
||||
|
||||
DefaultLogger *zap.Logger
|
||||
|
||||
DefaultAccessControl *accessControl
|
||||
|
||||
// CurrentSettings represents current system settings
|
||||
CurrentSettings = &types.AppSettings{}
|
||||
|
||||
DefaultOptions options.FederationOpt
|
||||
|
||||
DefaultActionlog actionlog.Recorder
|
||||
|
||||
DefaultNode *node
|
||||
DefaultNodeSync NodeSyncService
|
||||
DefaultExposedModule ExposedModuleService
|
||||
DefaultSharedModule SharedModuleService
|
||||
DefaultModuleMapping ModuleMappingService
|
||||
|
||||
// wrapper around time.Now() that will aid service testing
|
||||
now = func() *time.Time {
|
||||
c := time.Now().Round(time.Second)
|
||||
return &c
|
||||
}
|
||||
|
||||
// wrapper around id.Next() that will aid service testing
|
||||
nextID = func() uint64 {
|
||||
return id.Next()
|
||||
}
|
||||
)
|
||||
|
||||
func Initialize(_ context.Context, log *zap.Logger, s store.Storer, c Config) (err error) {
|
||||
DefaultOptions = c.Federation
|
||||
|
||||
// we're doing conversion to avoid having
|
||||
// store interface exposed or generated inside app package
|
||||
DefaultStore = s
|
||||
|
||||
DefaultLogger = log.Named("service")
|
||||
|
||||
// @todo add healthcheck(s) for federation sync services
|
||||
|
||||
{
|
||||
tee := zap.NewNop()
|
||||
policy := actionlog.MakeProductionPolicy()
|
||||
|
||||
if !c.ActionLog.Enabled {
|
||||
policy = actionlog.MakeDisabledPolicy()
|
||||
} else if c.ActionLog.Debug {
|
||||
policy = actionlog.MakeDebugPolicy()
|
||||
tee = logger.MakeDebugLogger()
|
||||
}
|
||||
|
||||
DefaultActionlog = actionlog.NewService(DefaultStore, log, tee, policy)
|
||||
}
|
||||
|
||||
DefaultAccessControl = AccessControl(s)
|
||||
|
||||
DefaultNode = Node(
|
||||
DefaultStore,
|
||||
ss.DefaultUser,
|
||||
DefaultActionlog,
|
||||
func(ctx context.Context, i auth.Identifiable) (token []byte, err error) {
|
||||
return auth.TokenIssuer.Issue(
|
||||
ctx,
|
||||
auth.WithIdentity(i),
|
||||
auth.WithScope("api"),
|
||||
auth.WithAudience("federation"),
|
||||
)
|
||||
},
|
||||
c.Federation,
|
||||
c.Server,
|
||||
DefaultAccessControl,
|
||||
)
|
||||
DefaultNodeSync = NodeSync()
|
||||
DefaultExposedModule = ExposedModule()
|
||||
DefaultSharedModule = SharedModule()
|
||||
DefaultModuleMapping = ModuleMapping()
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
func Watchers(ctx context.Context) {
|
||||
DefaultLogger.Info("Starting federation - warning, this is still an experimental feature")
|
||||
|
||||
syncService := NewSync(
|
||||
&Syncer{},
|
||||
&Mapper{},
|
||||
DefaultSharedModule,
|
||||
cs.DefaultRecord,
|
||||
ss.DefaultUser,
|
||||
ss.DefaultRole)
|
||||
|
||||
syncStructure := WorkerStructure(syncService, DefaultLogger)
|
||||
syncData := WorkerData(syncService, DefaultLogger)
|
||||
|
||||
go syncStructure.Watch(
|
||||
ctx,
|
||||
DefaultOptions.StructureMonitorInterval,
|
||||
DefaultOptions.StructurePageSize)
|
||||
|
||||
go syncData.Watch(
|
||||
ctx,
|
||||
DefaultOptions.DataMonitorInterval,
|
||||
DefaultOptions.DataPageSize)
|
||||
}
|
||||
|
||||
func AddFederationLabel(entity label.LabeledResource, key string, value string) {
|
||||
entity.SetLabel(key, labelTypes.LabelValue{Val: value})
|
||||
}
|
||||
171
server/federation/service/shared_module.go
Normal file
171
server/federation/service/shared_module.go
Normal file
@@ -0,0 +1,171 @@
|
||||
package service
|
||||
|
||||
import (
|
||||
"context"
|
||||
"github.com/cortezaproject/corteza/server/pkg/errors"
|
||||
|
||||
composeService "github.com/cortezaproject/corteza/server/compose/service"
|
||||
"github.com/cortezaproject/corteza/server/federation/types"
|
||||
"github.com/cortezaproject/corteza/server/pkg/actionlog"
|
||||
"github.com/cortezaproject/corteza/server/pkg/auth"
|
||||
"github.com/cortezaproject/corteza/server/store"
|
||||
)
|
||||
|
||||
type (
|
||||
sharedModule struct {
|
||||
node node
|
||||
ac sharedModuleAccessController
|
||||
compose composeService.ModuleService
|
||||
store store.Storer
|
||||
actionlog actionlog.Recorder
|
||||
}
|
||||
|
||||
sharedModuleAccessController interface {
|
||||
CanCreateModuleOnNode(ctx context.Context, r *types.Node) bool
|
||||
}
|
||||
|
||||
SharedModuleService interface {
|
||||
Create(ctx context.Context, new *types.SharedModule) (*types.SharedModule, error)
|
||||
Update(ctx context.Context, updated *types.SharedModule) (*types.SharedModule, error)
|
||||
Find(ctx context.Context, filter types.SharedModuleFilter) (types.SharedModuleSet, types.SharedModuleFilter, error)
|
||||
FindByID(ctx context.Context, nodeID uint64, moduleID uint64) (*types.SharedModule, error)
|
||||
}
|
||||
)
|
||||
|
||||
func SharedModule() *sharedModule {
|
||||
return &sharedModule{
|
||||
ac: DefaultAccessControl,
|
||||
node: *DefaultNode,
|
||||
compose: composeService.DefaultModule,
|
||||
store: DefaultStore,
|
||||
actionlog: DefaultActionlog,
|
||||
}
|
||||
}
|
||||
|
||||
func (svc sharedModule) FindByID(ctx context.Context, nodeID uint64, moduleID uint64) (module *types.SharedModule, err error) {
|
||||
err = func() error {
|
||||
if module, err = loadSharedModule(ctx, svc.store, nodeID, moduleID); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}()
|
||||
|
||||
return module, err
|
||||
}
|
||||
|
||||
func (svc sharedModule) Create(ctx context.Context, new *types.SharedModule) (*types.SharedModule, error) {
|
||||
var (
|
||||
aProps = &sharedModuleActionProps{changed: new}
|
||||
)
|
||||
|
||||
err := store.Tx(ctx, svc.store, func(ctx context.Context, s store.Storer) (err error) {
|
||||
var (
|
||||
node *types.Node
|
||||
)
|
||||
|
||||
if node, err = svc.node.FindByID(ctx, new.NodeID); err != nil {
|
||||
return SharedModuleErrNodeNotFound()
|
||||
}
|
||||
|
||||
if !svc.ac.CanCreateModuleOnNode(ctx, node) {
|
||||
return SharedModuleErrNotAllowedToCreate()
|
||||
}
|
||||
|
||||
if err = svc.uniqueCheck(ctx, new); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
new.ID = nextID()
|
||||
new.CreatedAt = *now()
|
||||
new.CreatedBy = auth.GetIdentityFromContext(ctx).Identity()
|
||||
|
||||
// check if Fields can be unmarshaled to the fields structure
|
||||
if new.Fields != nil {
|
||||
}
|
||||
|
||||
aProps.setModule(new)
|
||||
|
||||
if err = store.CreateFederationSharedModule(ctx, s, new); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
})
|
||||
|
||||
return new, svc.recordAction(ctx, aProps, SharedModuleActionCreate, err)
|
||||
}
|
||||
|
||||
func (svc sharedModule) Update(ctx context.Context, updated *types.SharedModule) (*types.SharedModule, error) {
|
||||
var (
|
||||
aProps = &sharedModuleActionProps{module: updated}
|
||||
)
|
||||
|
||||
err := store.Tx(ctx, svc.store, func(ctx context.Context, s store.Storer) (err error) {
|
||||
updated.UpdatedAt = now()
|
||||
updated.UpdatedBy = auth.GetIdentityFromContext(ctx).Identity()
|
||||
|
||||
aProps.setModule(updated)
|
||||
|
||||
if _, err = svc.node.FindByID(ctx, updated.NodeID); err != nil {
|
||||
return SharedModuleErrNodeNotFound()
|
||||
}
|
||||
|
||||
if err = store.UpdateFederationSharedModule(ctx, s, updated); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
})
|
||||
|
||||
return updated, svc.recordAction(ctx, aProps, SharedModuleActionUpdate, err)
|
||||
}
|
||||
|
||||
func (svc sharedModule) uniqueCheck(ctx context.Context, m *types.SharedModule) (err error) {
|
||||
f := types.SharedModuleFilter{
|
||||
NodeID: m.NodeID,
|
||||
Handle: m.Handle,
|
||||
Name: m.Name,
|
||||
}
|
||||
|
||||
if set, _, err := store.SearchFederationSharedModules(ctx, svc.store, f); len(set) > 0 && err == nil {
|
||||
return SharedModuleErrNotUnique()
|
||||
} else if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (svc sharedModule) Find(ctx context.Context, filter types.SharedModuleFilter) (set types.SharedModuleSet, f types.SharedModuleFilter, err error) {
|
||||
var (
|
||||
aProps = &sharedModuleActionProps{filter: &filter}
|
||||
)
|
||||
|
||||
err = func() error {
|
||||
if set, f, err = store.SearchFederationSharedModules(ctx, svc.store, filter); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}()
|
||||
|
||||
return set, f, svc.recordAction(ctx, aProps, SharedModuleActionSearch, err)
|
||||
}
|
||||
|
||||
func loadSharedModule(ctx context.Context, s store.FederationSharedModules, nodeID, ID uint64) (res *types.SharedModule, err error) {
|
||||
if ID == 0 || nodeID == 0 {
|
||||
return nil, SharedModuleErrInvalidID()
|
||||
}
|
||||
|
||||
if res, err = store.LookupFederationSharedModuleByID(ctx, s, ID); errors.IsNotFound(err) {
|
||||
err = SharedModuleErrNotFound()
|
||||
}
|
||||
|
||||
if err == nil && nodeID != res.NodeID {
|
||||
// Make sure chart belongs to the right namespace
|
||||
return nil, SharedModuleErrNotFound()
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
717
server/federation/service/shared_module_actions.gen.go
generated
Normal file
717
server/federation/service/shared_module_actions.gen.go
generated
Normal file
@@ -0,0 +1,717 @@
|
||||
package service
|
||||
|
||||
// This file is auto-generated.
|
||||
//
|
||||
// Changes to this file may cause incorrect behavior and will be lost if
|
||||
// the code is regenerated.
|
||||
//
|
||||
// Definitions file that controls how this file is generated:
|
||||
// federation/service/shared_module_actions.yaml
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"github.com/cortezaproject/corteza/server/federation/types"
|
||||
"github.com/cortezaproject/corteza/server/pkg/actionlog"
|
||||
"github.com/cortezaproject/corteza/server/pkg/errors"
|
||||
"github.com/cortezaproject/corteza/server/pkg/locale"
|
||||
"strings"
|
||||
"time"
|
||||
)
|
||||
|
||||
type (
|
||||
sharedModuleActionProps struct {
|
||||
module *types.SharedModule
|
||||
changed *types.SharedModule
|
||||
filter *types.SharedModuleFilter
|
||||
node *types.Node
|
||||
}
|
||||
|
||||
sharedModuleAction struct {
|
||||
timestamp time.Time
|
||||
resource string
|
||||
action string
|
||||
log string
|
||||
severity actionlog.Severity
|
||||
|
||||
// prefix for error when action fails
|
||||
errorMessage string
|
||||
|
||||
props *sharedModuleActionProps
|
||||
}
|
||||
|
||||
sharedModuleLogMetaKey struct{}
|
||||
sharedModulePropsMetaKey struct{}
|
||||
)
|
||||
|
||||
var (
|
||||
// just a placeholder to cover template cases w/o fmt package use
|
||||
_ = fmt.Println
|
||||
)
|
||||
|
||||
// *********************************************************************************************************************
|
||||
// *********************************************************************************************************************
|
||||
// Props methods
|
||||
// setModule updates sharedModuleActionProps's module
|
||||
//
|
||||
// This function is auto-generated.
|
||||
func (p *sharedModuleActionProps) setModule(module *types.SharedModule) *sharedModuleActionProps {
|
||||
p.module = module
|
||||
return p
|
||||
}
|
||||
|
||||
// setChanged updates sharedModuleActionProps's changed
|
||||
//
|
||||
// This function is auto-generated.
|
||||
func (p *sharedModuleActionProps) setChanged(changed *types.SharedModule) *sharedModuleActionProps {
|
||||
p.changed = changed
|
||||
return p
|
||||
}
|
||||
|
||||
// setFilter updates sharedModuleActionProps's filter
|
||||
//
|
||||
// This function is auto-generated.
|
||||
func (p *sharedModuleActionProps) setFilter(filter *types.SharedModuleFilter) *sharedModuleActionProps {
|
||||
p.filter = filter
|
||||
return p
|
||||
}
|
||||
|
||||
// setNode updates sharedModuleActionProps's node
|
||||
//
|
||||
// This function is auto-generated.
|
||||
func (p *sharedModuleActionProps) setNode(node *types.Node) *sharedModuleActionProps {
|
||||
p.node = node
|
||||
return p
|
||||
}
|
||||
|
||||
// Serialize converts sharedModuleActionProps to actionlog.Meta
|
||||
//
|
||||
// This function is auto-generated.
|
||||
func (p sharedModuleActionProps) Serialize() actionlog.Meta {
|
||||
var (
|
||||
m = make(actionlog.Meta)
|
||||
)
|
||||
|
||||
if p.module != nil {
|
||||
m.Set("module.ID", p.module.ID, true)
|
||||
}
|
||||
if p.changed != nil {
|
||||
m.Set("changed.ID", p.changed.ID, true)
|
||||
}
|
||||
if p.filter != nil {
|
||||
m.Set("filter.query", p.filter.Query, true)
|
||||
m.Set("filter.sort", p.filter.Sort, true)
|
||||
m.Set("filter.limit", p.filter.Limit, true)
|
||||
}
|
||||
if p.node != nil {
|
||||
m.Set("node.ID", p.node.ID, true)
|
||||
m.Set("node.Name", p.node.Name, true)
|
||||
}
|
||||
|
||||
return m
|
||||
}
|
||||
|
||||
// tr translates string and replaces meta value placeholder with values
|
||||
//
|
||||
// This function is auto-generated.
|
||||
func (p sharedModuleActionProps) Format(in string, err error) string {
|
||||
var (
|
||||
pairs = []string{"{{err}}"}
|
||||
// first non-empty string
|
||||
fns = func(ii ...interface{}) string {
|
||||
for _, i := range ii {
|
||||
if s := fmt.Sprintf("%v", i); len(s) > 0 {
|
||||
return s
|
||||
}
|
||||
}
|
||||
|
||||
return ""
|
||||
}
|
||||
)
|
||||
|
||||
if err != nil {
|
||||
pairs = append(pairs, err.Error())
|
||||
} else {
|
||||
pairs = append(pairs, "nil")
|
||||
}
|
||||
|
||||
if p.module != nil {
|
||||
// replacement for "{{module}}" (in order how fields are defined)
|
||||
pairs = append(
|
||||
pairs,
|
||||
"{{module}}",
|
||||
fns(
|
||||
p.module.ID,
|
||||
),
|
||||
)
|
||||
pairs = append(pairs, "{{module.ID}}", fns(p.module.ID))
|
||||
}
|
||||
|
||||
if p.changed != nil {
|
||||
// replacement for "{{changed}}" (in order how fields are defined)
|
||||
pairs = append(
|
||||
pairs,
|
||||
"{{changed}}",
|
||||
fns(
|
||||
p.changed.ID,
|
||||
),
|
||||
)
|
||||
pairs = append(pairs, "{{changed.ID}}", fns(p.changed.ID))
|
||||
}
|
||||
|
||||
if p.filter != nil {
|
||||
// replacement for "{{filter}}" (in order how fields are defined)
|
||||
pairs = append(
|
||||
pairs,
|
||||
"{{filter}}",
|
||||
fns(
|
||||
p.filter.Query,
|
||||
p.filter.Sort,
|
||||
p.filter.Limit,
|
||||
),
|
||||
)
|
||||
pairs = append(pairs, "{{filter.query}}", fns(p.filter.Query))
|
||||
pairs = append(pairs, "{{filter.sort}}", fns(p.filter.Sort))
|
||||
pairs = append(pairs, "{{filter.limit}}", fns(p.filter.Limit))
|
||||
}
|
||||
|
||||
if p.node != nil {
|
||||
// replacement for "{{node}}" (in order how fields are defined)
|
||||
pairs = append(
|
||||
pairs,
|
||||
"{{node}}",
|
||||
fns(
|
||||
p.node.ID,
|
||||
p.node.Name,
|
||||
),
|
||||
)
|
||||
pairs = append(pairs, "{{node.ID}}", fns(p.node.ID))
|
||||
pairs = append(pairs, "{{node.Name}}", fns(p.node.Name))
|
||||
}
|
||||
return strings.NewReplacer(pairs...).Replace(in)
|
||||
}
|
||||
|
||||
// *********************************************************************************************************************
|
||||
// *********************************************************************************************************************
|
||||
// Action methods
|
||||
|
||||
// String returns loggable description as string
|
||||
//
|
||||
// This function is auto-generated.
|
||||
func (a *sharedModuleAction) String() string {
|
||||
var props = &sharedModuleActionProps{}
|
||||
|
||||
if a.props != nil {
|
||||
props = a.props
|
||||
}
|
||||
|
||||
return props.Format(a.log, nil)
|
||||
}
|
||||
|
||||
func (e *sharedModuleAction) ToAction() *actionlog.Action {
|
||||
return &actionlog.Action{
|
||||
Resource: e.resource,
|
||||
Action: e.action,
|
||||
Severity: e.severity,
|
||||
Description: e.String(),
|
||||
Meta: e.props.Serialize(),
|
||||
}
|
||||
}
|
||||
|
||||
// *********************************************************************************************************************
|
||||
// *********************************************************************************************************************
|
||||
// Action constructors
|
||||
|
||||
// SharedModuleActionSearch returns "federation:shared_module.search" action
|
||||
//
|
||||
// This function is auto-generated.
|
||||
func SharedModuleActionSearch(props ...*sharedModuleActionProps) *sharedModuleAction {
|
||||
a := &sharedModuleAction{
|
||||
timestamp: time.Now(),
|
||||
resource: "federation:shared_module",
|
||||
action: "search",
|
||||
log: "searched for modules",
|
||||
severity: actionlog.Info,
|
||||
}
|
||||
|
||||
if len(props) > 0 {
|
||||
a.props = props[0]
|
||||
}
|
||||
|
||||
return a
|
||||
}
|
||||
|
||||
// SharedModuleActionLookup returns "federation:shared_module.lookup" action
|
||||
//
|
||||
// This function is auto-generated.
|
||||
func SharedModuleActionLookup(props ...*sharedModuleActionProps) *sharedModuleAction {
|
||||
a := &sharedModuleAction{
|
||||
timestamp: time.Now(),
|
||||
resource: "federation:shared_module",
|
||||
action: "lookup",
|
||||
log: "looked-up for a {{module}}",
|
||||
severity: actionlog.Info,
|
||||
}
|
||||
|
||||
if len(props) > 0 {
|
||||
a.props = props[0]
|
||||
}
|
||||
|
||||
return a
|
||||
}
|
||||
|
||||
// SharedModuleActionCreate returns "federation:shared_module.create" action
|
||||
//
|
||||
// This function is auto-generated.
|
||||
func SharedModuleActionCreate(props ...*sharedModuleActionProps) *sharedModuleAction {
|
||||
a := &sharedModuleAction{
|
||||
timestamp: time.Now(),
|
||||
resource: "federation:shared_module",
|
||||
action: "create",
|
||||
log: "created {{module}}",
|
||||
severity: actionlog.Notice,
|
||||
}
|
||||
|
||||
if len(props) > 0 {
|
||||
a.props = props[0]
|
||||
}
|
||||
|
||||
return a
|
||||
}
|
||||
|
||||
// SharedModuleActionUpdate returns "federation:shared_module.update" action
|
||||
//
|
||||
// This function is auto-generated.
|
||||
func SharedModuleActionUpdate(props ...*sharedModuleActionProps) *sharedModuleAction {
|
||||
a := &sharedModuleAction{
|
||||
timestamp: time.Now(),
|
||||
resource: "federation:shared_module",
|
||||
action: "update",
|
||||
log: "updated {{module}}",
|
||||
severity: actionlog.Notice,
|
||||
}
|
||||
|
||||
if len(props) > 0 {
|
||||
a.props = props[0]
|
||||
}
|
||||
|
||||
return a
|
||||
}
|
||||
|
||||
// SharedModuleActionDelete returns "federation:shared_module.delete" action
|
||||
//
|
||||
// This function is auto-generated.
|
||||
func SharedModuleActionDelete(props ...*sharedModuleActionProps) *sharedModuleAction {
|
||||
a := &sharedModuleAction{
|
||||
timestamp: time.Now(),
|
||||
resource: "federation:shared_module",
|
||||
action: "delete",
|
||||
log: "deleted {{module}}",
|
||||
severity: actionlog.Notice,
|
||||
}
|
||||
|
||||
if len(props) > 0 {
|
||||
a.props = props[0]
|
||||
}
|
||||
|
||||
return a
|
||||
}
|
||||
|
||||
// SharedModuleActionUndelete returns "federation:shared_module.undelete" action
|
||||
//
|
||||
// This function is auto-generated.
|
||||
func SharedModuleActionUndelete(props ...*sharedModuleActionProps) *sharedModuleAction {
|
||||
a := &sharedModuleAction{
|
||||
timestamp: time.Now(),
|
||||
resource: "federation:shared_module",
|
||||
action: "undelete",
|
||||
log: "undeleted {{module}}",
|
||||
severity: actionlog.Notice,
|
||||
}
|
||||
|
||||
if len(props) > 0 {
|
||||
a.props = props[0]
|
||||
}
|
||||
|
||||
return a
|
||||
}
|
||||
|
||||
// *********************************************************************************************************************
|
||||
// *********************************************************************************************************************
|
||||
// Error constructors
|
||||
|
||||
// SharedModuleErrGeneric returns "federation:shared_module.generic" as *errors.Error
|
||||
//
|
||||
// This function is auto-generated.
|
||||
func SharedModuleErrGeneric(mm ...*sharedModuleActionProps) *errors.Error {
|
||||
var p = &sharedModuleActionProps{}
|
||||
if len(mm) > 0 {
|
||||
p = mm[0]
|
||||
}
|
||||
|
||||
var e = errors.New(
|
||||
errors.KindInternal,
|
||||
|
||||
p.Format("failed to complete request due to internal error", nil),
|
||||
|
||||
errors.Meta("type", "generic"),
|
||||
errors.Meta("resource", "federation:shared_module"),
|
||||
|
||||
// action log entry; no formatting, it will be applied inside recordAction fn.
|
||||
errors.Meta(sharedModuleLogMetaKey{}, "{err}"),
|
||||
errors.Meta(sharedModulePropsMetaKey{}, p),
|
||||
|
||||
// translation namespace & key
|
||||
errors.Meta(locale.ErrorMetaNamespace{}, "federation"),
|
||||
errors.Meta(locale.ErrorMetaKey{}, "shared-module.errors.generic"),
|
||||
|
||||
errors.StackSkip(1),
|
||||
)
|
||||
|
||||
if len(mm) > 0 {
|
||||
}
|
||||
|
||||
return e
|
||||
}
|
||||
|
||||
// SharedModuleErrNotFound returns "federation:shared_module.notFound" as *errors.Error
|
||||
//
|
||||
// This function is auto-generated.
|
||||
func SharedModuleErrNotFound(mm ...*sharedModuleActionProps) *errors.Error {
|
||||
var p = &sharedModuleActionProps{}
|
||||
if len(mm) > 0 {
|
||||
p = mm[0]
|
||||
}
|
||||
|
||||
var e = errors.New(
|
||||
errors.KindInternal,
|
||||
|
||||
p.Format("module does not exist", nil),
|
||||
|
||||
errors.Meta("type", "notFound"),
|
||||
errors.Meta("resource", "federation:shared_module"),
|
||||
|
||||
errors.Meta(sharedModulePropsMetaKey{}, p),
|
||||
|
||||
// translation namespace & key
|
||||
errors.Meta(locale.ErrorMetaNamespace{}, "federation"),
|
||||
errors.Meta(locale.ErrorMetaKey{}, "shared-module.errors.notFound"),
|
||||
|
||||
errors.StackSkip(1),
|
||||
)
|
||||
|
||||
if len(mm) > 0 {
|
||||
}
|
||||
|
||||
return e
|
||||
}
|
||||
|
||||
// SharedModuleErrInvalidID returns "federation:shared_module.invalidID" as *errors.Error
|
||||
//
|
||||
// This function is auto-generated.
|
||||
func SharedModuleErrInvalidID(mm ...*sharedModuleActionProps) *errors.Error {
|
||||
var p = &sharedModuleActionProps{}
|
||||
if len(mm) > 0 {
|
||||
p = mm[0]
|
||||
}
|
||||
|
||||
var e = errors.New(
|
||||
errors.KindInternal,
|
||||
|
||||
p.Format("invalid ID", nil),
|
||||
|
||||
errors.Meta("type", "invalidID"),
|
||||
errors.Meta("resource", "federation:shared_module"),
|
||||
|
||||
errors.Meta(sharedModulePropsMetaKey{}, p),
|
||||
|
||||
// translation namespace & key
|
||||
errors.Meta(locale.ErrorMetaNamespace{}, "federation"),
|
||||
errors.Meta(locale.ErrorMetaKey{}, "shared-module.errors.invalidID"),
|
||||
|
||||
errors.StackSkip(1),
|
||||
)
|
||||
|
||||
if len(mm) > 0 {
|
||||
}
|
||||
|
||||
return e
|
||||
}
|
||||
|
||||
// SharedModuleErrStaleData returns "federation:shared_module.staleData" as *errors.Error
|
||||
//
|
||||
// This function is auto-generated.
|
||||
func SharedModuleErrStaleData(mm ...*sharedModuleActionProps) *errors.Error {
|
||||
var p = &sharedModuleActionProps{}
|
||||
if len(mm) > 0 {
|
||||
p = mm[0]
|
||||
}
|
||||
|
||||
var e = errors.New(
|
||||
errors.KindInternal,
|
||||
|
||||
p.Format("module was modified by someone else or by a workflow after you've opened it. Please refresh to see the latest updated version", nil),
|
||||
|
||||
errors.Meta("type", "staleData"),
|
||||
errors.Meta("resource", "federation:shared_module"),
|
||||
|
||||
errors.Meta(sharedModulePropsMetaKey{}, p),
|
||||
|
||||
// translation namespace & key
|
||||
errors.Meta(locale.ErrorMetaNamespace{}, "federation"),
|
||||
errors.Meta(locale.ErrorMetaKey{}, "shared-module.errors.staleData"),
|
||||
|
||||
errors.StackSkip(1),
|
||||
)
|
||||
|
||||
if len(mm) > 0 {
|
||||
}
|
||||
|
||||
return e
|
||||
}
|
||||
|
||||
// SharedModuleErrFederationSyncStructureChanged returns "federation:shared_module.federationSyncStructureChanged" as *errors.Error
|
||||
//
|
||||
// This function is auto-generated.
|
||||
func SharedModuleErrFederationSyncStructureChanged(mm ...*sharedModuleActionProps) *errors.Error {
|
||||
var p = &sharedModuleActionProps{}
|
||||
if len(mm) > 0 {
|
||||
p = mm[0]
|
||||
}
|
||||
|
||||
var e = errors.New(
|
||||
errors.KindInternal,
|
||||
|
||||
p.Format("module structure changed", nil),
|
||||
|
||||
errors.Meta("type", "federationSyncStructureChanged"),
|
||||
errors.Meta("resource", "federation:shared_module"),
|
||||
|
||||
// action log entry; no formatting, it will be applied inside recordAction fn.
|
||||
errors.Meta(sharedModuleLogMetaKey{}, "could not update shared module, structure different"),
|
||||
errors.Meta(sharedModulePropsMetaKey{}, p),
|
||||
|
||||
// translation namespace & key
|
||||
errors.Meta(locale.ErrorMetaNamespace{}, "federation"),
|
||||
errors.Meta(locale.ErrorMetaKey{}, "shared-module.errors.federationSyncStructureChanged"),
|
||||
|
||||
errors.StackSkip(1),
|
||||
)
|
||||
|
||||
if len(mm) > 0 {
|
||||
}
|
||||
|
||||
return e
|
||||
}
|
||||
|
||||
// SharedModuleErrNotUnique returns "federation:shared_module.notUnique" as *errors.Error
|
||||
//
|
||||
// This function is auto-generated.
|
||||
func SharedModuleErrNotUnique(mm ...*sharedModuleActionProps) *errors.Error {
|
||||
var p = &sharedModuleActionProps{}
|
||||
if len(mm) > 0 {
|
||||
p = mm[0]
|
||||
}
|
||||
|
||||
var e = errors.New(
|
||||
errors.KindInternal,
|
||||
|
||||
p.Format("node not unique", nil),
|
||||
|
||||
errors.Meta("type", "notUnique"),
|
||||
errors.Meta("resource", "federation:shared_module"),
|
||||
|
||||
// action log entry; no formatting, it will be applied inside recordAction fn.
|
||||
errors.Meta(sharedModuleLogMetaKey{}, "used duplicate node TODO"),
|
||||
errors.Meta(sharedModulePropsMetaKey{}, p),
|
||||
|
||||
// translation namespace & key
|
||||
errors.Meta(locale.ErrorMetaNamespace{}, "federation"),
|
||||
errors.Meta(locale.ErrorMetaKey{}, "shared-module.errors.notUnique"),
|
||||
|
||||
errors.StackSkip(1),
|
||||
)
|
||||
|
||||
if len(mm) > 0 {
|
||||
}
|
||||
|
||||
return e
|
||||
}
|
||||
|
||||
// SharedModuleErrNodeNotFound returns "federation:shared_module.nodeNotFound" as *errors.Error
|
||||
//
|
||||
// This function is auto-generated.
|
||||
func SharedModuleErrNodeNotFound(mm ...*sharedModuleActionProps) *errors.Error {
|
||||
var p = &sharedModuleActionProps{}
|
||||
if len(mm) > 0 {
|
||||
p = mm[0]
|
||||
}
|
||||
|
||||
var e = errors.New(
|
||||
errors.KindInternal,
|
||||
|
||||
p.Format("node does not exist", nil),
|
||||
|
||||
errors.Meta("type", "nodeNotFound"),
|
||||
errors.Meta("resource", "federation:shared_module"),
|
||||
|
||||
errors.Meta(sharedModulePropsMetaKey{}, p),
|
||||
|
||||
// translation namespace & key
|
||||
errors.Meta(locale.ErrorMetaNamespace{}, "federation"),
|
||||
errors.Meta(locale.ErrorMetaKey{}, "shared-module.errors.nodeNotFound"),
|
||||
|
||||
errors.StackSkip(1),
|
||||
)
|
||||
|
||||
if len(mm) > 0 {
|
||||
}
|
||||
|
||||
return e
|
||||
}
|
||||
|
||||
// SharedModuleErrNotAllowedToCreate returns "federation:shared_module.notAllowedToCreate" as *errors.Error
|
||||
//
|
||||
// This function is auto-generated.
|
||||
func SharedModuleErrNotAllowedToCreate(mm ...*sharedModuleActionProps) *errors.Error {
|
||||
var p = &sharedModuleActionProps{}
|
||||
if len(mm) > 0 {
|
||||
p = mm[0]
|
||||
}
|
||||
|
||||
var e = errors.New(
|
||||
errors.KindInternal,
|
||||
|
||||
p.Format("not allowed to create modules", nil),
|
||||
|
||||
errors.Meta("type", "notAllowedToCreate"),
|
||||
errors.Meta("resource", "federation:shared_module"),
|
||||
|
||||
// action log entry; no formatting, it will be applied inside recordAction fn.
|
||||
errors.Meta(sharedModuleLogMetaKey{}, "could not create modules; insufficient permissions"),
|
||||
errors.Meta(sharedModulePropsMetaKey{}, p),
|
||||
|
||||
// translation namespace & key
|
||||
errors.Meta(locale.ErrorMetaNamespace{}, "federation"),
|
||||
errors.Meta(locale.ErrorMetaKey{}, "shared-module.errors.notAllowedToCreate"),
|
||||
|
||||
errors.StackSkip(1),
|
||||
)
|
||||
|
||||
if len(mm) > 0 {
|
||||
}
|
||||
|
||||
return e
|
||||
}
|
||||
|
||||
// SharedModuleErrNotAllowedToManage returns "federation:shared_module.notAllowedToManage" as *errors.Error
|
||||
//
|
||||
// This function is auto-generated.
|
||||
func SharedModuleErrNotAllowedToManage(mm ...*sharedModuleActionProps) *errors.Error {
|
||||
var p = &sharedModuleActionProps{}
|
||||
if len(mm) > 0 {
|
||||
p = mm[0]
|
||||
}
|
||||
|
||||
var e = errors.New(
|
||||
errors.KindInternal,
|
||||
|
||||
p.Format("not allowed to manage this module", nil),
|
||||
|
||||
errors.Meta("type", "notAllowedToManage"),
|
||||
errors.Meta("resource", "federation:shared_module"),
|
||||
|
||||
// action log entry; no formatting, it will be applied inside recordAction fn.
|
||||
errors.Meta(sharedModuleLogMetaKey{}, "could not read {{module}}; insufficient permissions"),
|
||||
errors.Meta(sharedModulePropsMetaKey{}, p),
|
||||
|
||||
// translation namespace & key
|
||||
errors.Meta(locale.ErrorMetaNamespace{}, "federation"),
|
||||
errors.Meta(locale.ErrorMetaKey{}, "shared-module.errors.notAllowedToManage"),
|
||||
|
||||
errors.StackSkip(1),
|
||||
)
|
||||
|
||||
if len(mm) > 0 {
|
||||
}
|
||||
|
||||
return e
|
||||
}
|
||||
|
||||
// SharedModuleErrNotAllowedToMap returns "federation:shared_module.notAllowedToMap" as *errors.Error
|
||||
//
|
||||
// This function is auto-generated.
|
||||
func SharedModuleErrNotAllowedToMap(mm ...*sharedModuleActionProps) *errors.Error {
|
||||
var p = &sharedModuleActionProps{}
|
||||
if len(mm) > 0 {
|
||||
p = mm[0]
|
||||
}
|
||||
|
||||
var e = errors.New(
|
||||
errors.KindInternal,
|
||||
|
||||
p.Format("not allowed to map this module", nil),
|
||||
|
||||
errors.Meta("type", "notAllowedToMap"),
|
||||
errors.Meta("resource", "federation:shared_module"),
|
||||
|
||||
// action log entry; no formatting, it will be applied inside recordAction fn.
|
||||
errors.Meta(sharedModuleLogMetaKey{}, "could not map {{module}}; insufficient permissions"),
|
||||
errors.Meta(sharedModulePropsMetaKey{}, p),
|
||||
|
||||
// translation namespace & key
|
||||
errors.Meta(locale.ErrorMetaNamespace{}, "federation"),
|
||||
errors.Meta(locale.ErrorMetaKey{}, "shared-module.errors.notAllowedToMap"),
|
||||
|
||||
errors.StackSkip(1),
|
||||
)
|
||||
|
||||
if len(mm) > 0 {
|
||||
}
|
||||
|
||||
return e
|
||||
}
|
||||
|
||||
// *********************************************************************************************************************
|
||||
// *********************************************************************************************************************
|
||||
|
||||
// recordAction is a service helper function wraps function that can return error
|
||||
//
|
||||
// It will wrap unrecognized/internal errors with generic errors.
|
||||
//
|
||||
// This function is auto-generated.
|
||||
func (svc sharedModule) recordAction(ctx context.Context, props *sharedModuleActionProps, actionFn func(...*sharedModuleActionProps) *sharedModuleAction, err error) error {
|
||||
if svc.actionlog == nil || actionFn == nil {
|
||||
// action log disabled or no action fn passed, return error as-is
|
||||
return err
|
||||
} else if err == nil {
|
||||
// action completed w/o error, record it
|
||||
svc.actionlog.Record(ctx, actionFn(props).ToAction())
|
||||
return nil
|
||||
}
|
||||
|
||||
a := actionFn(props).ToAction()
|
||||
|
||||
// Extracting error information and recording it as action
|
||||
a.Error = err.Error()
|
||||
|
||||
switch c := err.(type) {
|
||||
case *errors.Error:
|
||||
m := c.Meta()
|
||||
|
||||
a.Error = err.Error()
|
||||
a.Severity = actionlog.Severity(m.AsInt("severity"))
|
||||
a.Description = props.Format(m.AsString(sharedModuleLogMetaKey{}), err)
|
||||
|
||||
if p, has := m[sharedModulePropsMetaKey{}]; has {
|
||||
a.Meta = p.(*sharedModuleActionProps).Serialize()
|
||||
}
|
||||
|
||||
svc.actionlog.Record(ctx, a)
|
||||
default:
|
||||
svc.actionlog.Record(ctx, a)
|
||||
}
|
||||
|
||||
// Original error is passed on
|
||||
return err
|
||||
}
|
||||
86
server/federation/service/shared_module_actions.yaml
Normal file
86
server/federation/service/shared_module_actions.yaml
Normal file
@@ -0,0 +1,86 @@
|
||||
# List of loggable service actions
|
||||
|
||||
resource: federation:shared_module
|
||||
service: sharedModule
|
||||
|
||||
# Default sensitivity for actions
|
||||
defaultActionSeverity: notice
|
||||
|
||||
# default severity for errors
|
||||
defaultErrorSeverity: error
|
||||
|
||||
import:
|
||||
- github.com/cortezaproject/corteza/server/federation/types
|
||||
|
||||
props:
|
||||
- name: module
|
||||
type: "*types.SharedModule"
|
||||
fields: [ ID ]
|
||||
- name: changed
|
||||
type: "*types.SharedModule"
|
||||
fields: [ ID ]
|
||||
- name: filter
|
||||
type: "*types.SharedModuleFilter"
|
||||
fields: [ query, sort, limit ]
|
||||
- name: node
|
||||
type: "*types.Node"
|
||||
fields: [ ID, Name ]
|
||||
|
||||
actions:
|
||||
- action: search
|
||||
log: "searched for modules"
|
||||
severity: info
|
||||
|
||||
- action: lookup
|
||||
log: "looked-up for a {{module}}"
|
||||
severity: info
|
||||
|
||||
- action: create
|
||||
log: "created {{module}}"
|
||||
|
||||
- action: update
|
||||
log: "updated {{module}}"
|
||||
|
||||
- action: delete
|
||||
log: "deleted {{module}}"
|
||||
|
||||
- action: undelete
|
||||
log: "undeleted {{module}}"
|
||||
|
||||
errors:
|
||||
- error: notFound
|
||||
message: "module does not exist"
|
||||
severity: warning
|
||||
|
||||
- error: invalidID
|
||||
message: "invalid ID"
|
||||
severity: warning
|
||||
|
||||
- error: staleData
|
||||
message: "module was modified by someone else or by a workflow after you've opened it. Please refresh to see the latest updated version"
|
||||
severity: warning
|
||||
|
||||
- error: federationSyncStructureChanged
|
||||
message: "module structure changed"
|
||||
log: "could not update shared module, structure different"
|
||||
|
||||
- error: notUnique
|
||||
message: "node not unique"
|
||||
log: "used duplicate node TODO"
|
||||
severity: warning
|
||||
|
||||
- error: nodeNotFound
|
||||
message: "node does not exist"
|
||||
severity: warning
|
||||
|
||||
- error: notAllowedToCreate
|
||||
message: "not allowed to create modules"
|
||||
log: "could not create modules; insufficient permissions"
|
||||
|
||||
- error: notAllowedToManage
|
||||
message: "not allowed to manage this module"
|
||||
log: "could not read {{module}}; insufficient permissions"
|
||||
|
||||
- error: notAllowedToMap
|
||||
message: "not allowed to map this module"
|
||||
log: "could not map {{module}}; insufficient permissions"
|
||||
228
server/federation/service/sync.go
Normal file
228
server/federation/service/sync.go
Normal file
@@ -0,0 +1,228 @@
|
||||
package service
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io"
|
||||
"time"
|
||||
|
||||
cs "github.com/cortezaproject/corteza/server/compose/service"
|
||||
ct "github.com/cortezaproject/corteza/server/compose/types"
|
||||
"github.com/cortezaproject/corteza/server/federation/types"
|
||||
"github.com/cortezaproject/corteza/server/pkg/auth"
|
||||
"github.com/cortezaproject/corteza/server/pkg/filter"
|
||||
ss "github.com/cortezaproject/corteza/server/system/service"
|
||||
st "github.com/cortezaproject/corteza/server/system/types"
|
||||
)
|
||||
|
||||
type (
|
||||
Sync struct {
|
||||
syncer *Syncer
|
||||
mapper *Mapper
|
||||
sharedModuleService SharedModuleService
|
||||
composeRecordService cs.RecordService
|
||||
systemUserService ss.UserService
|
||||
systemRoleService ss.RoleService
|
||||
}
|
||||
)
|
||||
|
||||
func NewSync(s *Syncer, m *Mapper, sm SharedModuleService, cs cs.RecordService, us ss.UserService, rs ss.RoleService) *Sync {
|
||||
return &Sync{
|
||||
syncer: s,
|
||||
mapper: m,
|
||||
sharedModuleService: sm,
|
||||
composeRecordService: cs,
|
||||
systemUserService: us,
|
||||
systemRoleService: rs,
|
||||
}
|
||||
}
|
||||
|
||||
// CanUpdateSharedModule checks the origin and destination module
|
||||
// compatibility of fields
|
||||
// It currently checks if all of the fields are exactly the same
|
||||
// TODO - check if any of the newly missing fields are actually being used so a safe update
|
||||
// is possible
|
||||
func (s *Sync) CanUpdateSharedModule(ctx context.Context, new *types.SharedModule, existing *types.SharedModule) (bool, error) {
|
||||
// check for mapped fields
|
||||
var (
|
||||
fstr, f2str []byte
|
||||
err error
|
||||
)
|
||||
|
||||
if fstr, err = json.Marshal(new.Fields); err != nil {
|
||||
return false, err
|
||||
}
|
||||
|
||||
if f2str, err = json.Marshal(existing.Fields); err != nil {
|
||||
return false, err
|
||||
}
|
||||
|
||||
return string(fstr) == string(f2str), nil
|
||||
}
|
||||
|
||||
// ProcessPayload passes the payload to the syncer lib
|
||||
func (s *Sync) ProcessPayload(ctx context.Context, payload []byte, out chan Url, url types.SyncerURI, processer Processer) (ProcesserResponse, error) {
|
||||
ctx = auth.SetIdentityToContext(ctx, auth.FederationUser())
|
||||
|
||||
return s.syncer.Process(ctx, payload, out, url, processer)
|
||||
}
|
||||
|
||||
// QueueUrl passes the url to the syncer
|
||||
func (s *Sync) QueueUrl(url Url, out chan Url) {
|
||||
s.syncer.Queue(url, out)
|
||||
}
|
||||
|
||||
// FetchUrl passes the url to be fetched to the syncer
|
||||
func (s *Sync) FetchUrl(ctx context.Context, url string) (io.Reader, error) {
|
||||
return s.syncer.Fetch(ctx, url)
|
||||
}
|
||||
|
||||
// CreateRecord wraps the compose Record service Create
|
||||
func (s *Sync) CreateRecord(ctx context.Context, rec *ct.Record) (out *ct.Record, err error) {
|
||||
out, _, err = s.composeRecordService.Create(ctx, rec)
|
||||
return
|
||||
}
|
||||
|
||||
// UpdateRecord wraps the compose Record service Update
|
||||
func (s *Sync) UpdateRecord(ctx context.Context, rec *ct.Record) (out *ct.Record, err error) {
|
||||
out, _, err = s.composeRecordService.Update(ctx, rec)
|
||||
return
|
||||
}
|
||||
|
||||
// DeleteRecord wraps the compose Record service Update
|
||||
func (s *Sync) DeleteRecord(ctx context.Context, rec *ct.Record) error {
|
||||
return s.composeRecordService.DeleteByID(ctx, rec.NamespaceID, rec.ModuleID, rec.ID)
|
||||
}
|
||||
|
||||
// FindRecord find the record via federation label
|
||||
func (s *Sync) FindRecords(ctx context.Context, filter ct.RecordFilter) (set ct.RecordSet, err error) {
|
||||
set, _, err = s.composeRecordService.Find(ctx, filter)
|
||||
return
|
||||
}
|
||||
|
||||
// LookupSharedModule find the shared module if exists
|
||||
func (s *Sync) LookupSharedModule(ctx context.Context, new *types.SharedModule) (*types.SharedModule, error) {
|
||||
var sm *types.SharedModule
|
||||
|
||||
list, _, err := s.sharedModuleService.Find(ctx, types.SharedModuleFilter{
|
||||
NodeID: new.NodeID,
|
||||
ExternalFederationModuleID: new.ExternalFederationModuleID})
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if len(list) == 0 {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
sm = list[0]
|
||||
|
||||
return sm, nil
|
||||
}
|
||||
|
||||
// UpdateSharedModule wraps the federation SharedModule service Update
|
||||
func (s *Sync) UpdateSharedModule(ctx context.Context, updated *types.SharedModule) (*types.SharedModule, error) {
|
||||
return s.sharedModuleService.Update(ctx, updated)
|
||||
}
|
||||
|
||||
// CreateSharedModule wraps the federation SharedModule service Create
|
||||
func (s *Sync) CreateSharedModule(ctx context.Context, new *types.SharedModule) (*types.SharedModule, error) {
|
||||
return s.sharedModuleService.Create(ctx, new)
|
||||
}
|
||||
|
||||
// GetPairedNodes finds successfuly paired nodes
|
||||
func (s *Sync) GetPairedNodes(ctx context.Context) (types.NodeSet, error) {
|
||||
set, _, err := DefaultNode.Search(ctx, types.NodeFilter{Status: types.NodeStatusPaired})
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return set, nil
|
||||
}
|
||||
|
||||
func (s *Sync) GetSharedModules(ctx context.Context, nodeID uint64) (types.SharedModuleSet, error) {
|
||||
set, _, err := DefaultSharedModule.Find(ctx, types.SharedModuleFilter{NodeID: nodeID})
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
set, _ = set.Filter(func(sm *types.SharedModule) (bool, error) {
|
||||
return len(sm.Fields) > 0, nil
|
||||
})
|
||||
|
||||
return set, nil
|
||||
}
|
||||
|
||||
func (s *Sync) GetModuleMappings(ctx context.Context, moduleID uint64) (out *types.ModuleMapping, err error) {
|
||||
out, err = DefaultModuleMapping.FindByID(ctx, moduleID)
|
||||
return
|
||||
}
|
||||
|
||||
func (s *Sync) PrepareModuleMappings(ctx context.Context, mappings *types.ModuleMapping) (ct.RecordValueSet, error) {
|
||||
return s.mapper.Prepare((*mappings).FieldMapping), nil
|
||||
}
|
||||
|
||||
func (s *Sync) GetLastStructureSyncStatus(ctx context.Context, nodeID, externalFederationModuleID uint64) (sync types.NodeSync, err error) {
|
||||
var list types.NodeSyncSet
|
||||
|
||||
list, _, err = DefaultNodeSync.Search(ctx, types.NodeSyncFilter{
|
||||
NodeID: nodeID,
|
||||
ModuleID: externalFederationModuleID,
|
||||
SyncType: types.NodeSyncTypeStructure,
|
||||
Sorting: filter.Sorting{
|
||||
Sort: filter.SortExprSet{
|
||||
&filter.SortExpr{Column: "time_of_action", Descending: true},
|
||||
},
|
||||
},
|
||||
Paging: filter.Paging{Limit: 1},
|
||||
})
|
||||
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
if len(list) == 0 {
|
||||
return
|
||||
}
|
||||
|
||||
return *list[0], nil
|
||||
}
|
||||
|
||||
func (s *Sync) GetLastSyncTime(ctx context.Context, nodeID uint64, syncType string) (*time.Time, error) {
|
||||
ns, err := DefaultNodeSync.LookupLastSuccessfulSync(ctx, nodeID, syncType)
|
||||
|
||||
if err != nil || ns == nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &ns.TimeOfAction, nil
|
||||
}
|
||||
|
||||
// LoadUserWithRoles gets the federation user, that was
|
||||
// created at node pairing process
|
||||
func (s *Sync) LoadUserWithRoles(ctx context.Context, nodeID uint64) (*st.User, error) {
|
||||
var (
|
||||
u *st.User
|
||||
err error
|
||||
)
|
||||
|
||||
// get the federated user, associated for this node
|
||||
if u, err = s.systemUserService.FindByHandle(ctx, fmt.Sprintf("federation_%d", nodeID)); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// attach the roles
|
||||
rr, _, err := s.systemRoleService.Find(ctx, st.RoleFilter{MemberID: u.ID})
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
u.SetRoles(rr.IDs()...)
|
||||
|
||||
return u, nil
|
||||
}
|
||||
18
server/federation/service/sync_worker.go
Normal file
18
server/federation/service/sync_worker.go
Normal file
@@ -0,0 +1,18 @@
|
||||
package service
|
||||
|
||||
import (
|
||||
"context"
|
||||
"time"
|
||||
)
|
||||
|
||||
type (
|
||||
worker interface {
|
||||
PrepareForNodes(ctx context.Context, sync *Sync, urls chan Url)
|
||||
Watch(ctx context.Context, delay time.Duration, limit int)
|
||||
}
|
||||
)
|
||||
|
||||
const (
|
||||
defaultDelay = time.Second * 10
|
||||
defaultPage = 10
|
||||
)
|
||||
281
server/federation/service/sync_worker_data.go
Normal file
281
server/federation/service/sync_worker_data.go
Normal file
@@ -0,0 +1,281 @@
|
||||
package service
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"io"
|
||||
"time"
|
||||
|
||||
"github.com/cortezaproject/corteza/server/federation/types"
|
||||
"github.com/cortezaproject/corteza/server/pkg/auth"
|
||||
"github.com/cortezaproject/corteza/server/pkg/logger"
|
||||
"go.uber.org/zap"
|
||||
)
|
||||
|
||||
type (
|
||||
syncWorkerData struct {
|
||||
syncService *Sync
|
||||
logger *zap.Logger
|
||||
delay time.Duration
|
||||
limit int
|
||||
}
|
||||
)
|
||||
|
||||
func WorkerData(sync *Sync, logger *zap.Logger) *syncWorkerData {
|
||||
return &syncWorkerData{
|
||||
syncService: sync,
|
||||
logger: logger,
|
||||
limit: defaultPage,
|
||||
delay: defaultDelay,
|
||||
}
|
||||
}
|
||||
|
||||
func (w *syncWorkerData) queueUrl(url *types.SyncerURI, urls chan Url, meta Processer) {
|
||||
t := Url{
|
||||
Url: *url,
|
||||
Meta: meta,
|
||||
}
|
||||
|
||||
w.syncService.QueueUrl(t, urls)
|
||||
}
|
||||
|
||||
func (w *syncWorkerData) PrepareForNodes(ctx context.Context, urls chan Url) {
|
||||
|
||||
nodes, err := w.syncService.GetPairedNodes(ctx)
|
||||
|
||||
if err != nil {
|
||||
w.logger.Info("could not get paired nodes", zap.Error(err))
|
||||
return
|
||||
}
|
||||
|
||||
// get all shared modules and their module mappings
|
||||
for _, n := range nodes {
|
||||
// get the user, associated for this node
|
||||
u, err := w.syncService.LoadUserWithRoles(ctx, n.ID)
|
||||
|
||||
if err != nil {
|
||||
w.logger.Info("could not preload federation user, skipping",
|
||||
logger.Uint64("nodeID", n.ID),
|
||||
zap.Error(err))
|
||||
|
||||
continue
|
||||
}
|
||||
|
||||
set, err := w.syncService.GetSharedModules(ctx, n.ID)
|
||||
|
||||
if err != nil {
|
||||
w.logger.Info("could not get shared modules, skipping",
|
||||
logger.Uint64("nodeID", n.ID),
|
||||
zap.Error(err))
|
||||
|
||||
continue
|
||||
}
|
||||
|
||||
if len(set) == 0 {
|
||||
w.logger.Info("there are no valid shared modules, skipping",
|
||||
logger.Uint64("nodeID", n.ID),
|
||||
zap.Error(err))
|
||||
|
||||
continue
|
||||
}
|
||||
|
||||
// go through set and prepare module mappings for it
|
||||
for _, sm := range set {
|
||||
z := []zap.Field{
|
||||
logger.Uint64("nodeID", n.ID),
|
||||
logger.Uint64("moduleID", sm.ID),
|
||||
zap.String("host", n.BaseURL),
|
||||
zap.String("delay", w.delay.String()),
|
||||
zap.Int("pagesize", w.limit),
|
||||
}
|
||||
|
||||
// TODO: rename lastSyncStatus to lastSybc prolly!!!!
|
||||
|
||||
// if the last sync was error'd, log and skip
|
||||
lastSyncStatus, err := w.syncService.GetLastStructureSyncStatus(ctx, n.ID, sm.ExternalFederationModuleID)
|
||||
if err != nil {
|
||||
w.logger.Info("could not get last sync status, skipping", z...)
|
||||
continue
|
||||
}
|
||||
|
||||
if lastSyncStatus.SyncStatus == types.NodeSyncStatusError {
|
||||
w.logger.Info("last structure sync was not complete, admin resolve needed, skipping", z...)
|
||||
continue
|
||||
}
|
||||
|
||||
mappings, _ := w.syncService.GetModuleMappings(ctx, sm.ID)
|
||||
|
||||
if mappings == nil {
|
||||
w.logger.Info("could not prepare module mappings for shared module, skipping", z...)
|
||||
continue
|
||||
}
|
||||
|
||||
mappingValues, err := w.syncService.PrepareModuleMappings(ctx, mappings)
|
||||
|
||||
if err != nil || mappingValues == nil {
|
||||
w.logger.Info("could not prepare module mappings for shared module, skipping", z...)
|
||||
continue
|
||||
}
|
||||
|
||||
// get the last sync per-node
|
||||
lastSync, _ := w.syncService.GetLastSyncTime(ctx, n.ID, types.NodeSyncTypeData)
|
||||
// todo this is where we get the last sync value. prolly the issue could be on the fetch!
|
||||
|
||||
basePath := fmt.Sprintf("/nodes/%d/modules/%d/records/", n.SharedNodeID, sm.ExternalFederationModuleID)
|
||||
|
||||
if lastSync != nil {
|
||||
z = append(z, zap.Time("lastSync", *lastSync))
|
||||
}
|
||||
|
||||
w.logger.Info("starting data sync", z...)
|
||||
|
||||
url := types.SyncerURI{
|
||||
BaseURL: n.BaseURL,
|
||||
Path: basePath,
|
||||
Limit: w.limit,
|
||||
LastSync: lastSync,
|
||||
}
|
||||
|
||||
processer := &dataProcesser{
|
||||
ID: sm.ExternalFederationModuleID,
|
||||
ComposeModuleID: mappings.ComposeModuleID,
|
||||
ComposeNamespaceID: mappings.ComposeNamespaceID,
|
||||
ModuleMappings: &mappings.FieldMapping,
|
||||
ModuleMappingValues: &mappingValues,
|
||||
SyncService: w.syncService,
|
||||
User: u,
|
||||
Node: n,
|
||||
}
|
||||
|
||||
go w.queueUrl(&url, urls, processer)
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
func (w *syncWorkerData) Watch(ctx context.Context, delay time.Duration, limit int) {
|
||||
var (
|
||||
urls = make(chan Url, 100)
|
||||
payloads = make(chan Payload, 10)
|
||||
|
||||
countProcess = 0
|
||||
)
|
||||
|
||||
w.delay = delay
|
||||
w.limit = limit
|
||||
|
||||
ctx, cancel := context.WithCancel(ctx)
|
||||
defer cancel()
|
||||
|
||||
ctx = auth.SetIdentityToContext(ctx, auth.FederationUser())
|
||||
|
||||
ticker := time.NewTicker(delay)
|
||||
|
||||
w.PrepareForNodes(ctx, urls)
|
||||
|
||||
for {
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
// do any cleanup here
|
||||
w.logger.Info("stopping sync", zap.Int("processed", countProcess))
|
||||
return
|
||||
case <-ticker.C:
|
||||
// do the whole process again
|
||||
w.PrepareForNodes(ctx, urls)
|
||||
case url := <-urls:
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
// do any cleanup here
|
||||
w.logger.Info("stopping sync", zap.Int("processed", countProcess))
|
||||
return
|
||||
default:
|
||||
}
|
||||
|
||||
s, _ := url.Url.String()
|
||||
meta := url.Meta.(*dataProcesser)
|
||||
|
||||
// use the authToken from node pairing
|
||||
ctx = context.WithValue(ctx, FederationUserToken, meta.Node.AuthToken)
|
||||
|
||||
// TODO: Implement logic to handle last sync time before sending request
|
||||
|
||||
responseBody, err := w.syncService.FetchUrl(ctx, s)
|
||||
|
||||
if err != nil {
|
||||
w.logger.Error("could not fetch data from url, skipping",
|
||||
zap.Error(err),
|
||||
zap.String("url", s),
|
||||
logger.Uint64("nodeID", meta.Node.ID),
|
||||
zap.String("host", meta.Node.BaseURL))
|
||||
|
||||
continue
|
||||
}
|
||||
|
||||
spayload := Payload{
|
||||
Payload: responseBody,
|
||||
Meta: url.Meta,
|
||||
}
|
||||
|
||||
payloads <- spayload
|
||||
case p := <-payloads:
|
||||
meta := p.Meta.(*dataProcesser)
|
||||
body, err := io.ReadAll(p.Payload)
|
||||
|
||||
// handle error
|
||||
if err != nil {
|
||||
w.logger.Error("could not read data from synced url, skipping",
|
||||
zap.Error(err),
|
||||
logger.Uint64("nodeID", meta.Node.ID),
|
||||
zap.String("host", meta.Node.BaseURL))
|
||||
|
||||
continue
|
||||
}
|
||||
|
||||
basePath := fmt.Sprintf("/nodes/%d/modules/%d/records/", meta.Node.SharedNodeID, meta.ID)
|
||||
|
||||
u := types.SyncerURI{
|
||||
BaseURL: meta.NodeBaseURL,
|
||||
Path: basePath,
|
||||
Limit: w.limit,
|
||||
}
|
||||
|
||||
// the base path is okay
|
||||
processed, errProcess := w.syncService.ProcessPayload(ctx, body, urls, u, meta)
|
||||
|
||||
if response, ok := processed.(dataProcesserResponse); ok {
|
||||
countProcess += response.Processed
|
||||
} else {
|
||||
w.logger.Error("unexpected response type", zap.Any("response", processed))
|
||||
}
|
||||
|
||||
// error raised before the actual persist process
|
||||
// ignore
|
||||
syncStatus := types.NodeSyncStatusSuccess
|
||||
if errProcess != nil {
|
||||
syncStatus = types.NodeSyncStatusError
|
||||
}
|
||||
|
||||
new := &types.NodeSync{
|
||||
NodeID: meta.Node.ID,
|
||||
ModuleID: meta.ID,
|
||||
SyncStatus: syncStatus,
|
||||
SyncType: types.NodeSyncTypeData,
|
||||
TimeOfAction: time.Now().UTC(),
|
||||
}
|
||||
|
||||
new, err = DefaultNodeSync.Create(ctx, new)
|
||||
|
||||
if err != nil {
|
||||
w.logger.Info("could not update sync status", zap.Error(err))
|
||||
}
|
||||
|
||||
if errProcess != nil {
|
||||
w.logger.Info("error on persisting structure", zap.Error(errProcess))
|
||||
} else {
|
||||
w.logger.Info("processed objects",
|
||||
zap.Int("processed", processed.(dataProcesserResponse).Processed),
|
||||
logger.Uint64("nodeID", meta.Node.ID))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
230
server/federation/service/sync_worker_structure.go
Normal file
230
server/federation/service/sync_worker_structure.go
Normal file
@@ -0,0 +1,230 @@
|
||||
package service
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"time"
|
||||
|
||||
"github.com/cortezaproject/corteza/server/federation/types"
|
||||
"github.com/cortezaproject/corteza/server/pkg/auth"
|
||||
"github.com/cortezaproject/corteza/server/pkg/logger"
|
||||
"go.uber.org/zap"
|
||||
)
|
||||
|
||||
type (
|
||||
syncWorkerStructure struct {
|
||||
syncService *Sync
|
||||
logger *zap.Logger
|
||||
delay time.Duration
|
||||
limit int
|
||||
}
|
||||
)
|
||||
|
||||
func WorkerStructure(sync *Sync, logger *zap.Logger) *syncWorkerStructure {
|
||||
return &syncWorkerStructure{
|
||||
syncService: sync,
|
||||
logger: logger,
|
||||
limit: defaultPage,
|
||||
delay: defaultDelay,
|
||||
}
|
||||
}
|
||||
|
||||
func (w *syncWorkerStructure) queueUrl(url *types.SyncerURI, urls chan Url, meta Processer) {
|
||||
t := Url{
|
||||
Url: *url,
|
||||
Meta: meta,
|
||||
}
|
||||
|
||||
w.syncService.QueueUrl(t, urls)
|
||||
}
|
||||
|
||||
func (w *syncWorkerStructure) PrepareForNodes(ctx context.Context, urls chan Url) {
|
||||
nodes, err := w.syncService.GetPairedNodes(ctx)
|
||||
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
// get all shared modules and their module mappings
|
||||
for _, n := range nodes {
|
||||
// get the user, associated for this node
|
||||
u, err := w.syncService.LoadUserWithRoles(ctx, n.ID)
|
||||
|
||||
if err != nil {
|
||||
w.logger.Info("could not preload federation user, skipping",
|
||||
logger.Uint64("nodeID", n.ID),
|
||||
zap.Error(err))
|
||||
|
||||
continue
|
||||
}
|
||||
|
||||
// get the last sync per-node
|
||||
lastSync, _ := w.syncService.GetLastSyncTime(ctx, n.ID, types.NodeSyncTypeStructure)
|
||||
basePath := fmt.Sprintf("/nodes/%d/modules/exposed/", n.SharedNodeID)
|
||||
|
||||
z := []zap.Field{
|
||||
logger.Uint64("nodeID", n.ID),
|
||||
zap.String("host", n.BaseURL),
|
||||
zap.String("delay", w.delay.String()),
|
||||
zap.Int("pagesize", w.limit),
|
||||
}
|
||||
|
||||
if lastSync != nil {
|
||||
z = append(z, zap.Time("lastSync", *lastSync))
|
||||
}
|
||||
|
||||
w.logger.Info("starting structure sync", z...)
|
||||
|
||||
url := types.SyncerURI{
|
||||
BaseURL: n.BaseURL,
|
||||
Path: basePath,
|
||||
Limit: w.limit,
|
||||
LastSync: lastSync,
|
||||
}
|
||||
|
||||
processer := &structureProcesser{
|
||||
SyncService: w.syncService,
|
||||
User: u,
|
||||
Node: n,
|
||||
}
|
||||
|
||||
go w.queueUrl(&url, urls, processer)
|
||||
}
|
||||
}
|
||||
|
||||
func (w *syncWorkerStructure) Watch(ctx context.Context, delay time.Duration, limit int) {
|
||||
var (
|
||||
urls = make(chan Url, 100)
|
||||
payloads = make(chan Payload, 10)
|
||||
|
||||
countProcess = 0
|
||||
)
|
||||
|
||||
w.delay = delay
|
||||
w.limit = limit
|
||||
|
||||
ctx, cancel := context.WithCancel(ctx)
|
||||
defer cancel()
|
||||
|
||||
ctx = auth.SetIdentityToContext(ctx, auth.FederationUser())
|
||||
|
||||
ticker := time.NewTicker(delay)
|
||||
|
||||
w.PrepareForNodes(ctx, urls)
|
||||
|
||||
for {
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
// do any cleanup here
|
||||
w.logger.Info("stopping sync", zap.Int("processed", countProcess))
|
||||
return
|
||||
case <-ticker.C:
|
||||
// do the whole process again
|
||||
w.PrepareForNodes(ctx, urls)
|
||||
case url := <-urls:
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
// do any cleanup here
|
||||
w.logger.Info("stopping sync", zap.Int("processed", countProcess))
|
||||
return
|
||||
default:
|
||||
}
|
||||
s, _ := url.Url.String()
|
||||
meta := url.Meta.(*structureProcesser)
|
||||
|
||||
// use the authToken from node pairing
|
||||
ctx = context.WithValue(ctx, FederationUserToken, meta.Node.AuthToken)
|
||||
|
||||
responseBody, err := w.syncService.FetchUrl(ctx, s)
|
||||
|
||||
if err != nil {
|
||||
w.logger.Error("could not fetch data from url, skipping",
|
||||
zap.Error(err),
|
||||
zap.String("url", s),
|
||||
logger.Uint64("nodeID", meta.Node.ID),
|
||||
zap.String("host", meta.Node.BaseURL))
|
||||
|
||||
continue
|
||||
}
|
||||
|
||||
spayload := Payload{
|
||||
Payload: responseBody,
|
||||
Meta: url.Meta,
|
||||
}
|
||||
|
||||
payloads <- spayload
|
||||
case p := <-payloads:
|
||||
meta := p.Meta.(*structureProcesser)
|
||||
body, err := ioutil.ReadAll(p.Payload)
|
||||
|
||||
// handle error
|
||||
if err != nil {
|
||||
w.logger.Error("could not read data from synced url, skipping",
|
||||
zap.Error(err),
|
||||
logger.Uint64("nodeID", meta.Node.ID),
|
||||
zap.String("host", meta.Node.BaseURL))
|
||||
|
||||
continue
|
||||
}
|
||||
|
||||
basePath := fmt.Sprintf("/nodes/%d/modules/exposed/", meta.Node.SharedNodeID)
|
||||
|
||||
u := types.SyncerURI{
|
||||
BaseURL: p.Meta.(*structureProcesser).Node.BaseURL,
|
||||
Path: basePath,
|
||||
Limit: w.limit,
|
||||
}
|
||||
|
||||
processed, errProcess := w.syncService.ProcessPayload(ctx, body, urls, u, meta)
|
||||
|
||||
var processedModID uint64
|
||||
switch v := processed.(type) {
|
||||
case structureProcesserResponse:
|
||||
processedModID = v.ModuleID
|
||||
countProcess += v.Processed
|
||||
case int:
|
||||
countProcess += v
|
||||
}
|
||||
|
||||
// error raised before the actual persist process
|
||||
// ignore
|
||||
if moduleID := processedModID; moduleID == 0 {
|
||||
w.logger.Info("no structure change from last change, skipping",
|
||||
logger.Uint64("nodeID", meta.Node.ID),
|
||||
zap.String("host", meta.Node.BaseURL))
|
||||
|
||||
continue
|
||||
}
|
||||
|
||||
syncStatus := types.NodeSyncStatusSuccess
|
||||
if errProcess != nil {
|
||||
syncStatus = types.NodeSyncStatusError
|
||||
}
|
||||
|
||||
new := &types.NodeSync{
|
||||
NodeID: meta.Node.ID,
|
||||
ModuleID: processedModID,
|
||||
SyncStatus: syncStatus,
|
||||
SyncType: types.NodeSyncTypeStructure,
|
||||
TimeOfAction: time.Now().UTC(),
|
||||
}
|
||||
|
||||
new, err = DefaultNodeSync.Create(ctx, new)
|
||||
|
||||
if err != nil {
|
||||
w.logger.Info("could not update sync status", zap.Error(err))
|
||||
}
|
||||
|
||||
if errProcess != nil {
|
||||
w.logger.Info("error on handling payload", zap.Error(errProcess))
|
||||
} else {
|
||||
w.logger.Info("processed objects",
|
||||
zap.Int("processed", processed.(structureProcesserResponse).Processed),
|
||||
logger.Uint64("nodeID", meta.Node.ID),
|
||||
zap.String("host", meta.Node.BaseURL))
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
102
server/federation/service/syncer.go
Normal file
102
server/federation/service/syncer.go
Normal file
@@ -0,0 +1,102 @@
|
||||
package service
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"net/http"
|
||||
"time"
|
||||
|
||||
"github.com/cortezaproject/corteza/server/federation/types"
|
||||
)
|
||||
|
||||
type (
|
||||
Syncer struct {
|
||||
client http.Client
|
||||
}
|
||||
|
||||
Url struct {
|
||||
Url types.SyncerURI
|
||||
Meta Processer
|
||||
}
|
||||
Payload struct {
|
||||
Payload io.Reader
|
||||
Meta Processer
|
||||
}
|
||||
|
||||
AuxResponseSet struct {
|
||||
Response struct {
|
||||
Filter struct {
|
||||
NodeID string
|
||||
ComposeModuleID string
|
||||
Query string
|
||||
Limit int
|
||||
NextPage string
|
||||
PrevPage string
|
||||
} `json:"filter"`
|
||||
} `json:"response"`
|
||||
}
|
||||
)
|
||||
|
||||
const FederationUserToken string = "authToken"
|
||||
|
||||
func (h *Syncer) Queue(url Url, out chan Url) {
|
||||
out <- url
|
||||
}
|
||||
|
||||
func (h *Syncer) Fetch(ctx context.Context, url string) (io.Reader, error) {
|
||||
req, err := http.NewRequest("GET", url, nil)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if authToken := ctx.Value(FederationUserToken); authToken != nil {
|
||||
req.Header.Add("Authorization", `Bearer `+authToken.(string))
|
||||
}
|
||||
|
||||
req.Header.Add("Accept", "application/json")
|
||||
|
||||
resp, err := h.client.Do(req)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if resp.StatusCode != 200 {
|
||||
return nil, errors.New(fmt.Sprintf("invalid return status: %d", resp.StatusCode))
|
||||
}
|
||||
|
||||
return resp.Body, nil
|
||||
}
|
||||
|
||||
func (h *Syncer) Process(ctx context.Context, payload []byte, out chan Url, url types.SyncerURI, processer Processer) (ProcesserResponse, error) {
|
||||
aux, err := h.ParseHeader(ctx, payload)
|
||||
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
if aux.Response.Filter.NextPage != "" {
|
||||
url.NextPage = aux.Response.Filter.NextPage
|
||||
|
||||
out <- Url{
|
||||
Url: url,
|
||||
Meta: processer,
|
||||
}
|
||||
}
|
||||
return processer.Process(ctx, payload)
|
||||
}
|
||||
|
||||
func (h *Syncer) ParseHeader(ctx context.Context, payload []byte) (aux AuxResponseSet, err error) {
|
||||
err = json.Unmarshal(payload, &aux)
|
||||
return
|
||||
}
|
||||
|
||||
func NewSyncer() *Syncer {
|
||||
return &Syncer{
|
||||
client: http.Client{
|
||||
Timeout: time.Duration(3) * time.Second,
|
||||
},
|
||||
}
|
||||
}
|
||||
161
server/federation/service/syncer_test.go
Normal file
161
server/federation/service/syncer_test.go
Normal file
@@ -0,0 +1,161 @@
|
||||
package service
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"io/ioutil"
|
||||
"net/http"
|
||||
"testing"
|
||||
|
||||
"github.com/cortezaproject/corteza/server/federation/types"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
type (
|
||||
RoundTripFunc func(req *http.Request) *http.Response
|
||||
|
||||
testProcesser struct{}
|
||||
)
|
||||
|
||||
func (p *testProcesser) Process(ctx context.Context, payload []byte) (ProcesserResponse, error) {
|
||||
return 0, nil
|
||||
}
|
||||
|
||||
func TestSyncer_process(t *testing.T) {
|
||||
var (
|
||||
req = require.New(t)
|
||||
syncer = &Syncer{}
|
||||
|
||||
ctx = context.Background()
|
||||
)
|
||||
|
||||
c := make(chan Url, 2)
|
||||
u := types.SyncerURI{
|
||||
Limit: 10,
|
||||
NextPage: "123",
|
||||
}
|
||||
tp := &testProcesser{}
|
||||
|
||||
go syncer.Process(ctx, []byte(`{"response":{"filter":{"limit":1, "nextPage":"456"}}}`), c, u, tp)
|
||||
|
||||
select {
|
||||
case url := <-c:
|
||||
req.Equal(url.Url.NextPage, "456")
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
func TestSyncer_parseHeader(t *testing.T) {
|
||||
var (
|
||||
req = require.New(t)
|
||||
syncer = NewSyncer()
|
||||
|
||||
ctx = context.Background()
|
||||
)
|
||||
|
||||
c := make(chan Url, 2)
|
||||
u := types.SyncerURI{
|
||||
Limit: 10,
|
||||
NextPage: "123",
|
||||
}
|
||||
tp := &testProcesser{}
|
||||
|
||||
n, err := syncer.Process(ctx, []byte(`{"response":{"filt`), c, u, tp)
|
||||
|
||||
req.EqualError(err, "unexpected end of JSON input")
|
||||
req.Equal(n, 0)
|
||||
}
|
||||
|
||||
func TestSyncer_response(t *testing.T) {
|
||||
var (
|
||||
req = require.New(t)
|
||||
)
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
url string
|
||||
expect string
|
||||
syncer *Syncer
|
||||
ctx context.Context
|
||||
}{
|
||||
{
|
||||
name: "non 200 request",
|
||||
url: "http://example.ltd",
|
||||
syncer: &Syncer{
|
||||
client: *NewHttpClient(func(req *http.Request) *http.Response {
|
||||
return &http.Response{
|
||||
StatusCode: 500,
|
||||
Body: ioutil.NopCloser(bytes.NewBufferString(`{"error":{"message":"common error response"}}`)),
|
||||
Header: make(http.Header),
|
||||
}
|
||||
}),
|
||||
},
|
||||
ctx: context.Background(),
|
||||
expect: "invalid return status: 500",
|
||||
},
|
||||
{
|
||||
name: "invalid hostname",
|
||||
url: "http://examp le.ltd",
|
||||
syncer: &Syncer{
|
||||
client: *NewHttpClient(func(req *http.Request) *http.Response {
|
||||
return &http.Response{
|
||||
StatusCode: 200,
|
||||
Body: ioutil.NopCloser(bytes.NewBufferString(`{"error":{"message":"common error response"}}`)),
|
||||
Header: make(http.Header),
|
||||
}
|
||||
}),
|
||||
},
|
||||
ctx: context.Background(),
|
||||
expect: "parse \"http://examp le.ltd\": invalid character \" \" in host name",
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
_, err := tt.syncer.Fetch(tt.ctx, tt.url)
|
||||
req.EqualError(err, tt.expect)
|
||||
})
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
func TestSyncer_authTokenCorrectlySet(t *testing.T) {
|
||||
var (
|
||||
req = require.New(t)
|
||||
expectedAuthToken = "TEST_JWT_TOKEN"
|
||||
actualAuthToken = ""
|
||||
)
|
||||
|
||||
ctx := context.WithValue(
|
||||
context.Background(),
|
||||
FederationUserToken,
|
||||
expectedAuthToken)
|
||||
|
||||
syncer := &Syncer{
|
||||
client: *NewHttpClient(func(r *http.Request) *http.Response {
|
||||
|
||||
actualAuthToken = r.Header.Get("Authorization")
|
||||
|
||||
return &http.Response{
|
||||
StatusCode: 200,
|
||||
Body: ioutil.NopCloser(bytes.NewBufferString("OK")),
|
||||
Header: make(http.Header),
|
||||
}
|
||||
}),
|
||||
}
|
||||
|
||||
_, err := syncer.Fetch(ctx, "http://example.ltd")
|
||||
|
||||
req.Equal("Bearer "+expectedAuthToken, actualAuthToken)
|
||||
req.NoError(err)
|
||||
}
|
||||
|
||||
func (f RoundTripFunc) RoundTrip(req *http.Request) (*http.Response, error) {
|
||||
return f(req), nil
|
||||
}
|
||||
|
||||
func NewHttpClient(rt RoundTripFunc) *http.Client {
|
||||
return &http.Client{
|
||||
Transport: RoundTripFunc(rt),
|
||||
}
|
||||
}
|
||||
97
server/federation/shared_module.cue
Normal file
97
server/federation/shared_module.cue
Normal file
@@ -0,0 +1,97 @@
|
||||
package federation
|
||||
|
||||
import (
|
||||
"github.com/cortezaproject/corteza/server/codegen/schema"
|
||||
)
|
||||
|
||||
sharedModule: {
|
||||
features: {
|
||||
labels: false
|
||||
}
|
||||
|
||||
parents: [
|
||||
{handle: "node"},
|
||||
]
|
||||
|
||||
model: {
|
||||
ident: "federation_module_shared"
|
||||
attributes: {
|
||||
id: schema.IdField
|
||||
handle: schema.HandleField
|
||||
node_id: {
|
||||
sortable: true,
|
||||
ident: "nodeID",
|
||||
goType: "uint64",
|
||||
storeIdent: "rel_node"
|
||||
dal: { type: "ID" }
|
||||
}
|
||||
name: {
|
||||
sortable: true
|
||||
dal: {}
|
||||
}
|
||||
external_federation_module_id: {
|
||||
sortable: true,
|
||||
ident: "externalFederationModuleID",
|
||||
goType: "uint64",
|
||||
storeIdent: "xref_module",
|
||||
dal: { type: "ID" }
|
||||
}
|
||||
fields: {
|
||||
goType: "types.ModuleFieldSet"
|
||||
dal: { type: "JSON", defaultEmptyObject: true }
|
||||
omitSetter: true
|
||||
omitGetter: true
|
||||
}
|
||||
|
||||
created_at: schema.SortableTimestampNowField
|
||||
updated_at: schema.SortableTimestampNilField
|
||||
deleted_at: schema.SortableTimestampNilField
|
||||
created_by: schema.AttributeUserRef
|
||||
updated_by: schema.AttributeUserRef
|
||||
deleted_by: schema.AttributeUserRef
|
||||
}
|
||||
|
||||
indexes: {
|
||||
"primary": { attribute: "id" }
|
||||
}
|
||||
}
|
||||
|
||||
envoy: {
|
||||
omit: true
|
||||
}
|
||||
|
||||
filter: {
|
||||
struct: {
|
||||
node_id: { goType: "uint64", ident: "nodeID", storeIdent: "rel_node" }
|
||||
handle: { goType: "string" }
|
||||
name: { goType: "string" }
|
||||
external_federation_module_id: { goType: "uint64", storeIdent: "xref_module", ident: "externalFederationModuleID" }
|
||||
}
|
||||
|
||||
query: ["name", "handle"]
|
||||
byValue: ["handle", "node_id", "name", "external_federation_module_id"]
|
||||
}
|
||||
|
||||
rbac: {
|
||||
operations: {
|
||||
"map": description: "Map shared module"
|
||||
}
|
||||
}
|
||||
|
||||
store: {
|
||||
ident: "federationSharedModule"
|
||||
|
||||
api: {
|
||||
lookups: [
|
||||
{
|
||||
fields: ["id"]
|
||||
description: """
|
||||
searches for shared federation module by ID
|
||||
|
||||
It returns shared federation module
|
||||
"""
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
42
server/federation/types/exposed_module.go
Normal file
42
server/federation/types/exposed_module.go
Normal file
@@ -0,0 +1,42 @@
|
||||
package types
|
||||
|
||||
import (
|
||||
"time"
|
||||
|
||||
"github.com/cortezaproject/corteza/server/pkg/filter"
|
||||
)
|
||||
|
||||
type (
|
||||
ExposedModule struct {
|
||||
ID uint64 `json:"moduleID,string"`
|
||||
NodeID uint64 `json:"nodeID,string"`
|
||||
ComposeModuleID uint64 `json:"composeModuleID,string"`
|
||||
ComposeNamespaceID uint64 `json:"composeNamespaceID,string"`
|
||||
Handle string `json:"handle"`
|
||||
Name string `json:"name"`
|
||||
Fields ModuleFieldSet `json:"fields"`
|
||||
|
||||
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" `
|
||||
}
|
||||
|
||||
ExposedModuleFilter struct {
|
||||
NodeID uint64 `json:"nodeID,string"`
|
||||
ComposeModuleID uint64 `json:"composeModuleID,string"`
|
||||
ComposeNamespaceID uint64 `json:"composeNamespaceID,string"`
|
||||
|
||||
LastSync uint64 `json:"lastSync"`
|
||||
Handle string `json:"handle"`
|
||||
Name string `json:"name"`
|
||||
Query string `json:"query"`
|
||||
|
||||
Check func(*ExposedModule) (bool, error) `json:"-"`
|
||||
|
||||
filter.Sorting
|
||||
filter.Paging
|
||||
}
|
||||
)
|
||||
321
server/federation/types/getters_setters.gen.go
generated
Normal file
321
server/federation/types/getters_setters.gen.go
generated
Normal file
@@ -0,0 +1,321 @@
|
||||
package types
|
||||
|
||||
// This file is auto-generated.
|
||||
//
|
||||
// Changes to this file may cause incorrect behavior and will be lost if
|
||||
// the code is regenerated.
|
||||
//
|
||||
|
||||
import (
|
||||
"github.com/cortezaproject/corteza/server/pkg/cast2"
|
||||
)
|
||||
|
||||
func (r Node) GetID() uint64 { return r.ID }
|
||||
|
||||
func (r *Node) GetValue(name string, pos uint) (any, error) {
|
||||
if r == nil {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
switch name {
|
||||
case "authToken", "AuthToken":
|
||||
return r.AuthToken, nil
|
||||
case "baseURL", "BaseURL":
|
||||
return r.BaseURL, nil
|
||||
case "contact", "Contact":
|
||||
return r.Contact, nil
|
||||
case "createdAt", "CreatedAt":
|
||||
return r.CreatedAt, nil
|
||||
case "createdBy", "CreatedBy":
|
||||
return r.CreatedBy, nil
|
||||
case "deletedAt", "DeletedAt":
|
||||
return r.DeletedAt, nil
|
||||
case "deletedBy", "DeletedBy":
|
||||
return r.DeletedBy, nil
|
||||
case "id", "ID":
|
||||
return r.ID, nil
|
||||
case "name", "Name":
|
||||
return r.Name, nil
|
||||
case "pairToken", "PairToken":
|
||||
return r.PairToken, nil
|
||||
case "sharedNodeID", "SharedNodeID":
|
||||
return r.SharedNodeID, nil
|
||||
case "status", "Status":
|
||||
return r.Status, nil
|
||||
case "updatedAt", "UpdatedAt":
|
||||
return r.UpdatedAt, nil
|
||||
case "updatedBy", "UpdatedBy":
|
||||
return r.UpdatedBy, nil
|
||||
|
||||
}
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
func (r *Node) SetValue(name string, pos uint, value any) (err error) {
|
||||
if r == nil {
|
||||
r = &Node{}
|
||||
}
|
||||
|
||||
switch name {
|
||||
case "authToken", "AuthToken":
|
||||
return cast2.String(value, &r.AuthToken)
|
||||
case "baseURL", "BaseURL":
|
||||
return cast2.String(value, &r.BaseURL)
|
||||
case "contact", "Contact":
|
||||
return cast2.String(value, &r.Contact)
|
||||
case "createdAt", "CreatedAt":
|
||||
return cast2.Time(value, &r.CreatedAt)
|
||||
case "createdBy", "CreatedBy":
|
||||
return cast2.Uint64(value, &r.CreatedBy)
|
||||
case "deletedAt", "DeletedAt":
|
||||
return cast2.TimePtr(value, &r.DeletedAt)
|
||||
case "deletedBy", "DeletedBy":
|
||||
return cast2.Uint64(value, &r.DeletedBy)
|
||||
case "id", "ID":
|
||||
return cast2.Uint64(value, &r.ID)
|
||||
case "name", "Name":
|
||||
return cast2.String(value, &r.Name)
|
||||
case "pairToken", "PairToken":
|
||||
return cast2.String(value, &r.PairToken)
|
||||
case "sharedNodeID", "SharedNodeID":
|
||||
return cast2.Uint64(value, &r.SharedNodeID)
|
||||
case "status", "Status":
|
||||
return cast2.String(value, &r.Status)
|
||||
case "updatedAt", "UpdatedAt":
|
||||
return cast2.TimePtr(value, &r.UpdatedAt)
|
||||
case "updatedBy", "UpdatedBy":
|
||||
return cast2.Uint64(value, &r.UpdatedBy)
|
||||
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (r NodeSync) GetID() uint64 {
|
||||
// The resource does not define an ID field
|
||||
return 0
|
||||
}
|
||||
|
||||
func (r *NodeSync) GetValue(name string, pos uint) (any, error) {
|
||||
if r == nil {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
switch name {
|
||||
case "moduleID", "ModuleID":
|
||||
return r.ModuleID, nil
|
||||
case "nodeID", "NodeID":
|
||||
return r.NodeID, nil
|
||||
case "syncStatus", "SyncStatus":
|
||||
return r.SyncStatus, nil
|
||||
case "syncType", "SyncType":
|
||||
return r.SyncType, nil
|
||||
case "timeOfAction", "TimeOfAction":
|
||||
return r.TimeOfAction, nil
|
||||
|
||||
}
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
func (r *NodeSync) SetValue(name string, pos uint, value any) (err error) {
|
||||
if r == nil {
|
||||
r = &NodeSync{}
|
||||
}
|
||||
|
||||
switch name {
|
||||
case "moduleID", "ModuleID":
|
||||
return cast2.Uint64(value, &r.ModuleID)
|
||||
case "nodeID", "NodeID":
|
||||
return cast2.Uint64(value, &r.NodeID)
|
||||
case "syncStatus", "SyncStatus":
|
||||
return cast2.String(value, &r.SyncStatus)
|
||||
case "syncType", "SyncType":
|
||||
return cast2.String(value, &r.SyncType)
|
||||
case "timeOfAction", "TimeOfAction":
|
||||
return cast2.Time(value, &r.TimeOfAction)
|
||||
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (r ExposedModule) GetID() uint64 { return r.ID }
|
||||
|
||||
func (r *ExposedModule) GetValue(name string, pos uint) (any, error) {
|
||||
if r == nil {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
switch name {
|
||||
case "composeModuleID", "ComposeModuleID":
|
||||
return r.ComposeModuleID, nil
|
||||
case "composeNamespaceID", "ComposeNamespaceID":
|
||||
return r.ComposeNamespaceID, nil
|
||||
case "createdAt", "CreatedAt":
|
||||
return r.CreatedAt, nil
|
||||
case "createdBy", "CreatedBy":
|
||||
return r.CreatedBy, nil
|
||||
case "deletedAt", "DeletedAt":
|
||||
return r.DeletedAt, nil
|
||||
case "deletedBy", "DeletedBy":
|
||||
return r.DeletedBy, nil
|
||||
case "handle", "Handle":
|
||||
return r.Handle, nil
|
||||
case "id", "ID":
|
||||
return r.ID, nil
|
||||
case "name", "Name":
|
||||
return r.Name, nil
|
||||
case "nodeID", "NodeID":
|
||||
return r.NodeID, nil
|
||||
case "updatedAt", "UpdatedAt":
|
||||
return r.UpdatedAt, nil
|
||||
case "updatedBy", "UpdatedBy":
|
||||
return r.UpdatedBy, nil
|
||||
|
||||
}
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
func (r *ExposedModule) SetValue(name string, pos uint, value any) (err error) {
|
||||
if r == nil {
|
||||
r = &ExposedModule{}
|
||||
}
|
||||
|
||||
switch name {
|
||||
case "composeModuleID", "ComposeModuleID":
|
||||
return cast2.Uint64(value, &r.ComposeModuleID)
|
||||
case "composeNamespaceID", "ComposeNamespaceID":
|
||||
return cast2.Uint64(value, &r.ComposeNamespaceID)
|
||||
case "createdAt", "CreatedAt":
|
||||
return cast2.Time(value, &r.CreatedAt)
|
||||
case "createdBy", "CreatedBy":
|
||||
return cast2.Uint64(value, &r.CreatedBy)
|
||||
case "deletedAt", "DeletedAt":
|
||||
return cast2.TimePtr(value, &r.DeletedAt)
|
||||
case "deletedBy", "DeletedBy":
|
||||
return cast2.Uint64(value, &r.DeletedBy)
|
||||
case "handle", "Handle":
|
||||
return cast2.String(value, &r.Handle)
|
||||
case "id", "ID":
|
||||
return cast2.Uint64(value, &r.ID)
|
||||
case "name", "Name":
|
||||
return cast2.String(value, &r.Name)
|
||||
case "nodeID", "NodeID":
|
||||
return cast2.Uint64(value, &r.NodeID)
|
||||
case "updatedAt", "UpdatedAt":
|
||||
return cast2.TimePtr(value, &r.UpdatedAt)
|
||||
case "updatedBy", "UpdatedBy":
|
||||
return cast2.Uint64(value, &r.UpdatedBy)
|
||||
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (r SharedModule) GetID() uint64 { return r.ID }
|
||||
|
||||
func (r *SharedModule) GetValue(name string, pos uint) (any, error) {
|
||||
if r == nil {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
switch name {
|
||||
case "createdAt", "CreatedAt":
|
||||
return r.CreatedAt, nil
|
||||
case "createdBy", "CreatedBy":
|
||||
return r.CreatedBy, nil
|
||||
case "deletedAt", "DeletedAt":
|
||||
return r.DeletedAt, nil
|
||||
case "deletedBy", "DeletedBy":
|
||||
return r.DeletedBy, nil
|
||||
case "externalFederationModuleID", "ExternalFederationModuleID":
|
||||
return r.ExternalFederationModuleID, nil
|
||||
case "handle", "Handle":
|
||||
return r.Handle, nil
|
||||
case "id", "ID":
|
||||
return r.ID, nil
|
||||
case "name", "Name":
|
||||
return r.Name, nil
|
||||
case "nodeID", "NodeID":
|
||||
return r.NodeID, nil
|
||||
case "updatedAt", "UpdatedAt":
|
||||
return r.UpdatedAt, nil
|
||||
case "updatedBy", "UpdatedBy":
|
||||
return r.UpdatedBy, nil
|
||||
|
||||
}
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
func (r *SharedModule) SetValue(name string, pos uint, value any) (err error) {
|
||||
if r == nil {
|
||||
r = &SharedModule{}
|
||||
}
|
||||
|
||||
switch name {
|
||||
case "createdAt", "CreatedAt":
|
||||
return cast2.Time(value, &r.CreatedAt)
|
||||
case "createdBy", "CreatedBy":
|
||||
return cast2.Uint64(value, &r.CreatedBy)
|
||||
case "deletedAt", "DeletedAt":
|
||||
return cast2.TimePtr(value, &r.DeletedAt)
|
||||
case "deletedBy", "DeletedBy":
|
||||
return cast2.Uint64(value, &r.DeletedBy)
|
||||
case "externalFederationModuleID", "ExternalFederationModuleID":
|
||||
return cast2.Uint64(value, &r.ExternalFederationModuleID)
|
||||
case "handle", "Handle":
|
||||
return cast2.String(value, &r.Handle)
|
||||
case "id", "ID":
|
||||
return cast2.Uint64(value, &r.ID)
|
||||
case "name", "Name":
|
||||
return cast2.String(value, &r.Name)
|
||||
case "nodeID", "NodeID":
|
||||
return cast2.Uint64(value, &r.NodeID)
|
||||
case "updatedAt", "UpdatedAt":
|
||||
return cast2.TimePtr(value, &r.UpdatedAt)
|
||||
case "updatedBy", "UpdatedBy":
|
||||
return cast2.Uint64(value, &r.UpdatedBy)
|
||||
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (r ModuleMapping) GetID() uint64 {
|
||||
// The resource does not define an ID field
|
||||
return 0
|
||||
}
|
||||
|
||||
func (r *ModuleMapping) GetValue(name string, pos uint) (any, error) {
|
||||
if r == nil {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
switch name {
|
||||
case "composeModuleID", "ComposeModuleID":
|
||||
return r.ComposeModuleID, nil
|
||||
case "composeNamespaceID", "ComposeNamespaceID":
|
||||
return r.ComposeNamespaceID, nil
|
||||
case "federationModuleID", "FederationModuleID":
|
||||
return r.FederationModuleID, nil
|
||||
case "nodeID", "NodeID":
|
||||
return r.NodeID, nil
|
||||
|
||||
}
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
func (r *ModuleMapping) SetValue(name string, pos uint, value any) (err error) {
|
||||
if r == nil {
|
||||
r = &ModuleMapping{}
|
||||
}
|
||||
|
||||
switch name {
|
||||
case "composeModuleID", "ComposeModuleID":
|
||||
return cast2.Uint64(value, &r.ComposeModuleID)
|
||||
case "composeNamespaceID", "ComposeNamespaceID":
|
||||
return cast2.Uint64(value, &r.ComposeNamespaceID)
|
||||
case "federationModuleID", "FederationModuleID":
|
||||
return cast2.Uint64(value, &r.FederationModuleID)
|
||||
case "nodeID", "NodeID":
|
||||
return cast2.Uint64(value, &r.NodeID)
|
||||
|
||||
}
|
||||
return nil
|
||||
}
|
||||
35
server/federation/types/module_field.go
Normal file
35
server/federation/types/module_field.go
Normal file
@@ -0,0 +1,35 @@
|
||||
package types
|
||||
|
||||
import (
|
||||
"database/sql/driver"
|
||||
"encoding/json"
|
||||
"github.com/cortezaproject/corteza/server/pkg/sql"
|
||||
"strings"
|
||||
)
|
||||
|
||||
type (
|
||||
ModuleFieldSet []*ModuleField
|
||||
|
||||
ModuleField struct {
|
||||
Kind string `json:"kind"`
|
||||
Name string `json:"name"`
|
||||
Label string `json:"label"`
|
||||
IsMulti bool `json:"isMulti"`
|
||||
}
|
||||
)
|
||||
|
||||
// HasField checks if the fieldset has a value by name
|
||||
// keeping error field to better match the existing Filter()
|
||||
// methods in generated sets
|
||||
func (list ModuleFieldSet) HasField(name string) (bool, error) {
|
||||
for _, f := range list {
|
||||
if strings.ToLower(f.Name) == strings.ToLower(name) {
|
||||
return true, nil
|
||||
}
|
||||
}
|
||||
|
||||
return false, nil
|
||||
}
|
||||
|
||||
func (list *ModuleFieldSet) Scan(src any) error { return sql.ParseJSON(src, list) }
|
||||
func (list ModuleFieldSet) Value() (driver.Value, error) { return json.Marshal(list) }
|
||||
46
server/federation/types/module_field_mapping.go
Normal file
46
server/federation/types/module_field_mapping.go
Normal file
@@ -0,0 +1,46 @@
|
||||
package types
|
||||
|
||||
import (
|
||||
"database/sql/driver"
|
||||
"encoding/json"
|
||||
"github.com/cortezaproject/corteza/server/pkg/sql"
|
||||
)
|
||||
|
||||
const (
|
||||
ModuleFieldMappingSetFindTypeOrigin ModuleFieldMappingSetFindType = iota
|
||||
ModuleFieldMappingSetFindTypeDestination
|
||||
)
|
||||
|
||||
type (
|
||||
ModuleFieldMappingSet []*ModuleFieldMapping
|
||||
|
||||
ModuleFieldMapping struct {
|
||||
Origin ModuleField `json:"origin"`
|
||||
Destination ModuleField `json:"destination"`
|
||||
}
|
||||
|
||||
ModuleFieldMappingSetFindType int
|
||||
)
|
||||
|
||||
// Find looks up a origin or destination mapping
|
||||
func (list *ModuleFieldMappingSet) FindByName(name string, findType ModuleFieldMappingSetFindType) (*ModuleFieldMapping, error) {
|
||||
for _, mfm := range *list {
|
||||
switch findType {
|
||||
|
||||
case ModuleFieldMappingSetFindTypeOrigin:
|
||||
if mfm.Origin.Name == name {
|
||||
return mfm, nil
|
||||
}
|
||||
|
||||
case ModuleFieldMappingSetFindTypeDestination:
|
||||
if mfm.Destination.Name == name {
|
||||
return mfm, nil
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
func (list *ModuleFieldMappingSet) Scan(src any) error { return sql.ParseJSON(src, list) }
|
||||
func (list ModuleFieldMappingSet) Value() (driver.Value, error) { return json.Marshal(list) }
|
||||
28
server/federation/types/module_mapping.go
Normal file
28
server/federation/types/module_mapping.go
Normal file
@@ -0,0 +1,28 @@
|
||||
package types
|
||||
|
||||
import (
|
||||
"github.com/cortezaproject/corteza/server/pkg/filter"
|
||||
)
|
||||
|
||||
type (
|
||||
ModuleMapping struct {
|
||||
NodeID uint64 `json:"nodeID,string"`
|
||||
FederationModuleID uint64 `json:"federationModuleID,string"`
|
||||
ComposeModuleID uint64 `json:"composeModuleID,string"`
|
||||
ComposeNamespaceID uint64 `json:"composeNamespaceID,string"`
|
||||
FieldMapping ModuleFieldMappingSet `json:"fields"`
|
||||
}
|
||||
|
||||
ModuleMappingFilter struct {
|
||||
NodeID uint64 `json:"nodeID"`
|
||||
ComposeModuleID uint64 `json:"composeModuleID"`
|
||||
ComposeNamespaceID uint64 `json:"composeNamespaceID"`
|
||||
FederationModuleID uint64 `json:"federationModuleID"`
|
||||
Query string `json:"query"`
|
||||
|
||||
Check func(*ModuleMapping) (bool, error) `json:"-"`
|
||||
|
||||
filter.Sorting
|
||||
filter.Paging
|
||||
}
|
||||
)
|
||||
57
server/federation/types/node.go
Normal file
57
server/federation/types/node.go
Normal file
@@ -0,0 +1,57 @@
|
||||
package types
|
||||
|
||||
import (
|
||||
"time"
|
||||
|
||||
"github.com/cortezaproject/corteza/server/pkg/filter"
|
||||
)
|
||||
|
||||
var (
|
||||
NodeStatusPending = "pending"
|
||||
NodeStatusPairRequested = "pair_requested"
|
||||
NodeStatusPaired = "paired"
|
||||
NodeStatusFailed = "failed"
|
||||
)
|
||||
|
||||
type (
|
||||
Node struct {
|
||||
ID uint64 `json:"nodeID,string"`
|
||||
Name string `json:"name"`
|
||||
Status string `json:"status"`
|
||||
|
||||
// Base URL of the remote server
|
||||
BaseURL string `json:"baseURL"`
|
||||
|
||||
Contact string `json:"contact"`
|
||||
|
||||
// Node ID on the remote server that points back to us
|
||||
SharedNodeID uint64 `json:"sharedNodeID,string"`
|
||||
|
||||
PairToken string `json:"-"`
|
||||
AuthToken string `json:"-"`
|
||||
|
||||
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" `
|
||||
}
|
||||
|
||||
NodeFilter struct {
|
||||
Query string `json:"name"`
|
||||
Status string `json:"status"`
|
||||
|
||||
// Check fn is called by store backend for each resource found function can
|
||||
// modify the resource and return false if store should not return it
|
||||
//
|
||||
// Store then loads additional resources to satisfy the paging parameters
|
||||
Check func(node *Node) (bool, error) `json:"-"`
|
||||
|
||||
Deleted filter.State `json:"deleted"`
|
||||
|
||||
// Standard helpers for paging and sorting
|
||||
filter.Sorting
|
||||
filter.Paging
|
||||
}
|
||||
)
|
||||
40
server/federation/types/node_sync.go
Normal file
40
server/federation/types/node_sync.go
Normal file
@@ -0,0 +1,40 @@
|
||||
package types
|
||||
|
||||
import (
|
||||
"time"
|
||||
|
||||
"github.com/cortezaproject/corteza/server/pkg/filter"
|
||||
)
|
||||
|
||||
var (
|
||||
NodeSyncTypeStructure = "sync_structure"
|
||||
NodeSyncTypeData = "sync_data"
|
||||
NodeSyncStatusSuccess = "success"
|
||||
NodeSyncStatusError = "error"
|
||||
)
|
||||
|
||||
type (
|
||||
NodeSync struct {
|
||||
NodeID uint64 `json:"nodeID,string"`
|
||||
ModuleID uint64 `json:"moduleID,string"`
|
||||
SyncStatus string `json:"syncStatus"`
|
||||
SyncType string `json:"syncType"`
|
||||
|
||||
TimeOfAction time.Time `json:"timeOfAction"`
|
||||
}
|
||||
|
||||
NodeSyncFilter struct {
|
||||
NodeID uint64 `json:"nodeID"`
|
||||
RelNodeID uint64 `json:"relNodeID"`
|
||||
ModuleID uint64 `json:"moduleID"`
|
||||
SyncStatus string `json:"syncStatus"`
|
||||
SyncType string `json:"syncType"`
|
||||
|
||||
Query string `json:"name"`
|
||||
|
||||
Check func(*NodeSync) (bool, error) `json:"-"`
|
||||
|
||||
filter.Sorting
|
||||
filter.Paging
|
||||
}
|
||||
)
|
||||
149
server/federation/types/rbac.gen.go
generated
Normal file
149
server/federation/types/rbac.gen.go
generated
Normal file
@@ -0,0 +1,149 @@
|
||||
package types
|
||||
|
||||
// This file is auto-generated.
|
||||
//
|
||||
// Changes to this file may cause incorrect behavior and will be lost if
|
||||
// the code is regenerated.
|
||||
//
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strconv"
|
||||
)
|
||||
|
||||
type (
|
||||
// Component struct serves as a virtual resource type for the federation component
|
||||
//
|
||||
// This struct is auto-generated
|
||||
Component struct{}
|
||||
)
|
||||
|
||||
var (
|
||||
_ = fmt.Printf
|
||||
_ = strconv.FormatUint
|
||||
)
|
||||
|
||||
// RbacResource returns string representation of RBAC resource for Node by calling NodeRbacResource fn
|
||||
//
|
||||
// RBAC resource is in the corteza::federation:node/... format
|
||||
//
|
||||
// This function is auto-generated
|
||||
func (r Node) RbacResource() string {
|
||||
return NodeRbacResource(r.ID)
|
||||
}
|
||||
|
||||
// NodeRbacResource returns string representation of RBAC resource for Node
|
||||
//
|
||||
// RBAC resource is in the corteza::federation:node/... format
|
||||
//
|
||||
// This function is auto-generated
|
||||
func NodeRbacResource(id uint64) string {
|
||||
cpts := []interface{}{NodeResourceType}
|
||||
if id != 0 {
|
||||
cpts = append(cpts, strconv.FormatUint(id, 10))
|
||||
} else {
|
||||
cpts = append(cpts, "*")
|
||||
}
|
||||
|
||||
return fmt.Sprintf(NodeRbacResourceTpl(), cpts...)
|
||||
|
||||
}
|
||||
|
||||
func NodeRbacResourceTpl() string {
|
||||
return "%s/%s"
|
||||
}
|
||||
|
||||
// RbacResource returns string representation of RBAC resource for ExposedModule by calling ExposedModuleRbacResource fn
|
||||
//
|
||||
// RBAC resource is in the corteza::federation:exposed-module/... format
|
||||
//
|
||||
// This function is auto-generated
|
||||
func (r ExposedModule) RbacResource() string {
|
||||
return ExposedModuleRbacResource(r.NodeID, r.ID)
|
||||
}
|
||||
|
||||
// ExposedModuleRbacResource returns string representation of RBAC resource for ExposedModule
|
||||
//
|
||||
// RBAC resource is in the corteza::federation:exposed-module/... format
|
||||
//
|
||||
// This function is auto-generated
|
||||
func ExposedModuleRbacResource(nodeID uint64, id uint64) string {
|
||||
cpts := []interface{}{ExposedModuleResourceType}
|
||||
if nodeID != 0 {
|
||||
cpts = append(cpts, strconv.FormatUint(nodeID, 10))
|
||||
} else {
|
||||
cpts = append(cpts, "*")
|
||||
}
|
||||
|
||||
if id != 0 {
|
||||
cpts = append(cpts, strconv.FormatUint(id, 10))
|
||||
} else {
|
||||
cpts = append(cpts, "*")
|
||||
}
|
||||
|
||||
return fmt.Sprintf(ExposedModuleRbacResourceTpl(), cpts...)
|
||||
|
||||
}
|
||||
|
||||
func ExposedModuleRbacResourceTpl() string {
|
||||
return "%s/%s/%s"
|
||||
}
|
||||
|
||||
// RbacResource returns string representation of RBAC resource for SharedModule by calling SharedModuleRbacResource fn
|
||||
//
|
||||
// RBAC resource is in the corteza::federation:shared-module/... format
|
||||
//
|
||||
// This function is auto-generated
|
||||
func (r SharedModule) RbacResource() string {
|
||||
return SharedModuleRbacResource(r.NodeID, r.ID)
|
||||
}
|
||||
|
||||
// SharedModuleRbacResource returns string representation of RBAC resource for SharedModule
|
||||
//
|
||||
// RBAC resource is in the corteza::federation:shared-module/... format
|
||||
//
|
||||
// This function is auto-generated
|
||||
func SharedModuleRbacResource(nodeID uint64, id uint64) string {
|
||||
cpts := []interface{}{SharedModuleResourceType}
|
||||
if nodeID != 0 {
|
||||
cpts = append(cpts, strconv.FormatUint(nodeID, 10))
|
||||
} else {
|
||||
cpts = append(cpts, "*")
|
||||
}
|
||||
|
||||
if id != 0 {
|
||||
cpts = append(cpts, strconv.FormatUint(id, 10))
|
||||
} else {
|
||||
cpts = append(cpts, "*")
|
||||
}
|
||||
|
||||
return fmt.Sprintf(SharedModuleRbacResourceTpl(), cpts...)
|
||||
|
||||
}
|
||||
|
||||
func SharedModuleRbacResourceTpl() string {
|
||||
return "%s/%s/%s"
|
||||
}
|
||||
|
||||
// RbacResource returns string representation of RBAC resource for Component by calling ComponentRbacResource fn
|
||||
//
|
||||
// RBAC resource is in the corteza::federation/... format
|
||||
//
|
||||
// This function is auto-generated
|
||||
func (r Component) RbacResource() string {
|
||||
return ComponentRbacResource()
|
||||
}
|
||||
|
||||
// ComponentRbacResource returns string representation of RBAC resource for Component
|
||||
//
|
||||
// RBAC resource is in the corteza::federation/ format
|
||||
//
|
||||
// This function is auto-generated
|
||||
func ComponentRbacResource() string {
|
||||
return ComponentResourceType + "/"
|
||||
|
||||
}
|
||||
|
||||
func ComponentRbacResourceTpl() string {
|
||||
return "%s"
|
||||
}
|
||||
16
server/federation/types/resources.gen.go
generated
Normal file
16
server/federation/types/resources.gen.go
generated
Normal file
@@ -0,0 +1,16 @@
|
||||
package types
|
||||
|
||||
// This file is auto-generated.
|
||||
//
|
||||
// Changes to this file may cause incorrect behavior and will be lost if
|
||||
// the code is regenerated.
|
||||
//
|
||||
|
||||
const (
|
||||
NodeResourceType = "corteza::federation:node"
|
||||
NodeSyncResourceType = "corteza::federation:node-sync"
|
||||
ExposedModuleResourceType = "corteza::federation:exposed-module"
|
||||
SharedModuleResourceType = "corteza::federation:shared-module"
|
||||
ModuleMappingResourceType = "corteza::federation:module-mapping"
|
||||
ComponentResourceType = "corteza::federation"
|
||||
)
|
||||
39
server/federation/types/shared_module.go
Normal file
39
server/federation/types/shared_module.go
Normal file
@@ -0,0 +1,39 @@
|
||||
package types
|
||||
|
||||
import (
|
||||
"time"
|
||||
|
||||
"github.com/cortezaproject/corteza/server/pkg/filter"
|
||||
)
|
||||
|
||||
type (
|
||||
SharedModule struct {
|
||||
ID uint64 `json:"moduleID,string"`
|
||||
NodeID uint64 `json:"nodeID,string"`
|
||||
Handle string `json:"handle"`
|
||||
Name string `json:"name"`
|
||||
ExternalFederationModuleID uint64 `json:"externalFederationModuleID,string"`
|
||||
Fields ModuleFieldSet `json:"fields"`
|
||||
|
||||
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" `
|
||||
}
|
||||
|
||||
SharedModuleFilter struct {
|
||||
NodeID uint64 `json:"nodeID,string"`
|
||||
ExternalFederationModuleID uint64 `json:"externalFederationModuleID,string"`
|
||||
|
||||
Handle string `json:"handle"`
|
||||
Name string `json:"name"`
|
||||
Query string `json:"query"`
|
||||
|
||||
Check func(*SharedModule) (bool, error) `json:"-"`
|
||||
|
||||
filter.Sorting
|
||||
filter.Paging
|
||||
}
|
||||
)
|
||||
100
server/federation/types/syncer_uri.go
Normal file
100
server/federation/types/syncer_uri.go
Normal file
@@ -0,0 +1,100 @@
|
||||
package types
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net/url"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
)
|
||||
|
||||
// SyncerURI serves as a default struct for handling url queues
|
||||
// for fetcher
|
||||
type (
|
||||
SyncerURI struct {
|
||||
Limit int
|
||||
LastSync *time.Time
|
||||
Path string
|
||||
BaseURL string
|
||||
NextPage string
|
||||
LastPage string
|
||||
}
|
||||
)
|
||||
|
||||
// Does the transformation to string, does not
|
||||
// url encode page cursor, since it needs to be base64
|
||||
// encoded
|
||||
func (s *SyncerURI) String() (string, error) {
|
||||
var query []string
|
||||
|
||||
// using query instead of url.Values
|
||||
// because pageCursor should not be url encoded
|
||||
query = append(query, fmt.Sprintf("limit=%d", s.Limit))
|
||||
|
||||
if s.NextPage != "" {
|
||||
query = append(query, fmt.Sprintf("pageCursor=%s", s.NextPage))
|
||||
} else if s.LastPage != "" {
|
||||
query = append(query, fmt.Sprintf("pageCursor=%s", s.LastPage))
|
||||
}
|
||||
|
||||
if s.LastSync != nil {
|
||||
query = append(query, fmt.Sprintf("lastSync=%d", s.LastSync.Unix()))
|
||||
}
|
||||
|
||||
return fmt.Sprintf("%s%s?%s", s.BaseURL, s.Path, strings.Join(query[:], "&")), nil
|
||||
}
|
||||
|
||||
// Parse the uri to the struct
|
||||
func (s *SyncerURI) Parse(uri string) error {
|
||||
u, err := url.Parse(uri)
|
||||
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
var limit int
|
||||
l := u.Query().Get("limit")
|
||||
|
||||
if l != "" {
|
||||
if limit, err = strconv.Atoi(l); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
s.Path = u.Path
|
||||
s.Limit = limit
|
||||
|
||||
if u.Host != "" {
|
||||
s.BaseURL = fmt.Sprintf("%s://%s", u.Scheme, u.Host)
|
||||
}
|
||||
|
||||
ls := u.Query().Get("lastSync")
|
||||
|
||||
if ls != "" {
|
||||
parsed, err := parseLastSync(ls)
|
||||
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
s.LastSync = parsed
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func parseLastSync(lastSync string) (*time.Time, error) {
|
||||
if i, err := strconv.ParseInt(lastSync, 10, 64); err == nil {
|
||||
t := time.Unix(i, 0)
|
||||
return &t, nil
|
||||
}
|
||||
|
||||
// try different format if above fails
|
||||
t, err := time.Parse(time.RFC3339, lastSync)
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &t, nil
|
||||
}
|
||||
143
server/federation/types/syncer_uri_test.go
Normal file
143
server/federation/types/syncer_uri_test.go
Normal file
@@ -0,0 +1,143 @@
|
||||
package types
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func TestSyncerURIString(t *testing.T) {
|
||||
var (
|
||||
req = require.New(t)
|
||||
)
|
||||
|
||||
now, _ := time.Parse("2006-01-02 15:04:05", "2020-10-23 11:11:11")
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
url *SyncerURI
|
||||
expect string
|
||||
}{
|
||||
{
|
||||
name: "next page cursor",
|
||||
url: &SyncerURI{BaseURL: "https://example.url", NextPage: "NEXT_PAGE_CURSOR=="},
|
||||
expect: "https://example.url?limit=0&pageCursor=NEXT_PAGE_CURSOR==",
|
||||
},
|
||||
{
|
||||
name: "last page cursor",
|
||||
url: &SyncerURI{BaseURL: "https://example.url", LastPage: "LAST_PAGE_CURSOR=="},
|
||||
expect: "https://example.url?limit=0&pageCursor=LAST_PAGE_CURSOR==",
|
||||
},
|
||||
{
|
||||
name: "limit results",
|
||||
url: &SyncerURI{BaseURL: "https://example.url", Limit: 666},
|
||||
expect: "https://example.url?limit=666",
|
||||
},
|
||||
{
|
||||
name: "base path",
|
||||
url: &SyncerURI{Path: "/relative/path"},
|
||||
expect: "/relative/path?limit=0",
|
||||
},
|
||||
{
|
||||
name: "last sync",
|
||||
url: &SyncerURI{Path: "/relative/path", LastSync: &now},
|
||||
expect: fmt.Sprintf("/relative/path?limit=0&lastSync=%d", now.Unix()),
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
r, _ := tt.url.String()
|
||||
|
||||
req.Equal(tt.expect, r)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestSyncerURIParse(t *testing.T) {
|
||||
var (
|
||||
req = require.New(t)
|
||||
)
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
url string
|
||||
expect *SyncerURI
|
||||
err string
|
||||
}{
|
||||
{
|
||||
name: "parse limit",
|
||||
url: "https://example.url?limit=11",
|
||||
expect: &SyncerURI{BaseURL: "https://example.url", Limit: 11},
|
||||
err: "",
|
||||
},
|
||||
{
|
||||
name: "parse path",
|
||||
url: "/path/to/endpoint/",
|
||||
expect: &SyncerURI{Path: "/path/to/endpoint/"},
|
||||
err: "",
|
||||
},
|
||||
{
|
||||
name: "parse invalid url",
|
||||
url: "ht tps:/ /in valid",
|
||||
expect: &SyncerURI{},
|
||||
err: `parse "ht tps:/ /in valid": first path segment in URL cannot contain colon`,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
s := &SyncerURI{}
|
||||
err := s.Parse(tt.url)
|
||||
|
||||
errString := ""
|
||||
if err != nil {
|
||||
errString = err.Error()
|
||||
}
|
||||
|
||||
req.Equal(tt.expect, s)
|
||||
req.Equal(tt.err, errString)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestSyncerURIParseLastSync(t *testing.T) {
|
||||
var (
|
||||
req = require.New(t)
|
||||
)
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
url string
|
||||
expect time.Time
|
||||
}{
|
||||
{
|
||||
name: "parse timestamp",
|
||||
url: "https://example.url?limit=11&lastSync=1603451471",
|
||||
expect: dateToUTC("Fri Oct 23 11:11:11 2020"),
|
||||
},
|
||||
{
|
||||
name: "parse RFC3339",
|
||||
url: "https://example.url?limit=11&lastSync=2020-10-23T13:11:11Z",
|
||||
expect: dateToUTC("Fri Oct 23 13:11:11 2020"),
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
s := &SyncerURI{}
|
||||
err := s.Parse(tt.url)
|
||||
|
||||
req.NoError(err)
|
||||
req.Equal(tt.expect.Unix(), s.LastSync.UTC().Unix())
|
||||
})
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
func dateToUTC(dt string) time.Time {
|
||||
d, _ := time.Parse(time.ANSIC, dt)
|
||||
return d.UTC()
|
||||
}
|
||||
265
server/federation/types/type_set.gen.go
generated
Normal file
265
server/federation/types/type_set.gen.go
generated
Normal file
@@ -0,0 +1,265 @@
|
||||
package types
|
||||
|
||||
// This file is auto-generated.
|
||||
//
|
||||
// Changes to this file may cause incorrect behavior and will be lost if
|
||||
// the code is regenerated.
|
||||
//
|
||||
// Definitions file that controls how this file is generated:
|
||||
// federation/types/types.yaml
|
||||
|
||||
type (
|
||||
|
||||
// ExposedModuleSet slice of ExposedModule
|
||||
//
|
||||
// This type is auto-generated.
|
||||
ExposedModuleSet []*ExposedModule
|
||||
|
||||
// ModuleMappingSet slice of ModuleMapping
|
||||
//
|
||||
// This type is auto-generated.
|
||||
ModuleMappingSet []*ModuleMapping
|
||||
|
||||
// NodeSet slice of Node
|
||||
//
|
||||
// This type is auto-generated.
|
||||
NodeSet []*Node
|
||||
|
||||
// NodeSyncSet slice of NodeSync
|
||||
//
|
||||
// This type is auto-generated.
|
||||
NodeSyncSet []*NodeSync
|
||||
|
||||
// SharedModuleSet slice of SharedModule
|
||||
//
|
||||
// This type is auto-generated.
|
||||
SharedModuleSet []*SharedModule
|
||||
)
|
||||
|
||||
// Walk iterates through every slice item and calls w(ExposedModule) err
|
||||
//
|
||||
// This function is auto-generated.
|
||||
func (set ExposedModuleSet) Walk(w func(*ExposedModule) error) (err error) {
|
||||
for i := range set {
|
||||
if err = w(set[i]); err != nil {
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
// Filter iterates through every slice item, calls f(ExposedModule) (bool, err) and return filtered slice
|
||||
//
|
||||
// This function is auto-generated.
|
||||
func (set ExposedModuleSet) Filter(f func(*ExposedModule) (bool, error)) (out ExposedModuleSet, err error) {
|
||||
var ok bool
|
||||
out = ExposedModuleSet{}
|
||||
for i := range set {
|
||||
if ok, err = f(set[i]); err != nil {
|
||||
return
|
||||
} else if ok {
|
||||
out = append(out, set[i])
|
||||
}
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
// FindByID finds items from slice by its ID property
|
||||
//
|
||||
// This function is auto-generated.
|
||||
func (set ExposedModuleSet) FindByID(ID uint64) *ExposedModule {
|
||||
for i := range set {
|
||||
if set[i].ID == ID {
|
||||
return set[i]
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// IDs returns a slice of uint64s from all items in the set
|
||||
//
|
||||
// This function is auto-generated.
|
||||
func (set ExposedModuleSet) IDs() (IDs []uint64) {
|
||||
IDs = make([]uint64, len(set))
|
||||
|
||||
for i := range set {
|
||||
IDs[i] = set[i].ID
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
// Walk iterates through every slice item and calls w(ModuleMapping) err
|
||||
//
|
||||
// This function is auto-generated.
|
||||
func (set ModuleMappingSet) Walk(w func(*ModuleMapping) error) (err error) {
|
||||
for i := range set {
|
||||
if err = w(set[i]); err != nil {
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
// Filter iterates through every slice item, calls f(ModuleMapping) (bool, err) and return filtered slice
|
||||
//
|
||||
// This function is auto-generated.
|
||||
func (set ModuleMappingSet) Filter(f func(*ModuleMapping) (bool, error)) (out ModuleMappingSet, err error) {
|
||||
var ok bool
|
||||
out = ModuleMappingSet{}
|
||||
for i := range set {
|
||||
if ok, err = f(set[i]); err != nil {
|
||||
return
|
||||
} else if ok {
|
||||
out = append(out, set[i])
|
||||
}
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
// Walk iterates through every slice item and calls w(Node) err
|
||||
//
|
||||
// This function is auto-generated.
|
||||
func (set NodeSet) Walk(w func(*Node) error) (err error) {
|
||||
for i := range set {
|
||||
if err = w(set[i]); err != nil {
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
// Filter iterates through every slice item, calls f(Node) (bool, err) and return filtered slice
|
||||
//
|
||||
// This function is auto-generated.
|
||||
func (set NodeSet) Filter(f func(*Node) (bool, error)) (out NodeSet, err error) {
|
||||
var ok bool
|
||||
out = NodeSet{}
|
||||
for i := range set {
|
||||
if ok, err = f(set[i]); err != nil {
|
||||
return
|
||||
} else if ok {
|
||||
out = append(out, set[i])
|
||||
}
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
// FindByID finds items from slice by its ID property
|
||||
//
|
||||
// This function is auto-generated.
|
||||
func (set NodeSet) FindByID(ID uint64) *Node {
|
||||
for i := range set {
|
||||
if set[i].ID == ID {
|
||||
return set[i]
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// IDs returns a slice of uint64s from all items in the set
|
||||
//
|
||||
// This function is auto-generated.
|
||||
func (set NodeSet) IDs() (IDs []uint64) {
|
||||
IDs = make([]uint64, len(set))
|
||||
|
||||
for i := range set {
|
||||
IDs[i] = set[i].ID
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
// Walk iterates through every slice item and calls w(NodeSync) err
|
||||
//
|
||||
// This function is auto-generated.
|
||||
func (set NodeSyncSet) Walk(w func(*NodeSync) error) (err error) {
|
||||
for i := range set {
|
||||
if err = w(set[i]); err != nil {
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
// Filter iterates through every slice item, calls f(NodeSync) (bool, err) and return filtered slice
|
||||
//
|
||||
// This function is auto-generated.
|
||||
func (set NodeSyncSet) Filter(f func(*NodeSync) (bool, error)) (out NodeSyncSet, err error) {
|
||||
var ok bool
|
||||
out = NodeSyncSet{}
|
||||
for i := range set {
|
||||
if ok, err = f(set[i]); err != nil {
|
||||
return
|
||||
} else if ok {
|
||||
out = append(out, set[i])
|
||||
}
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
// Walk iterates through every slice item and calls w(SharedModule) err
|
||||
//
|
||||
// This function is auto-generated.
|
||||
func (set SharedModuleSet) Walk(w func(*SharedModule) error) (err error) {
|
||||
for i := range set {
|
||||
if err = w(set[i]); err != nil {
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
// Filter iterates through every slice item, calls f(SharedModule) (bool, err) and return filtered slice
|
||||
//
|
||||
// This function is auto-generated.
|
||||
func (set SharedModuleSet) Filter(f func(*SharedModule) (bool, error)) (out SharedModuleSet, err error) {
|
||||
var ok bool
|
||||
out = SharedModuleSet{}
|
||||
for i := range set {
|
||||
if ok, err = f(set[i]); err != nil {
|
||||
return
|
||||
} else if ok {
|
||||
out = append(out, set[i])
|
||||
}
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
// FindByID finds items from slice by its ID property
|
||||
//
|
||||
// This function is auto-generated.
|
||||
func (set SharedModuleSet) FindByID(ID uint64) *SharedModule {
|
||||
for i := range set {
|
||||
if set[i].ID == ID {
|
||||
return set[i]
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// IDs returns a slice of uint64s from all items in the set
|
||||
//
|
||||
// This function is auto-generated.
|
||||
func (set SharedModuleSet) IDs() (IDs []uint64) {
|
||||
IDs = make([]uint64, len(set))
|
||||
|
||||
for i := range set {
|
||||
IDs[i] = set[i].ID
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
397
server/federation/types/type_set.gen_test.go
generated
Normal file
397
server/federation/types/type_set.gen_test.go
generated
Normal file
@@ -0,0 +1,397 @@
|
||||
package types
|
||||
|
||||
// This file is auto-generated.
|
||||
//
|
||||
// Changes to this file may cause incorrect behavior and will be lost if
|
||||
// the code is regenerated.
|
||||
//
|
||||
// Definitions file that controls how this file is generated:
|
||||
// federation/types/types.yaml
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/stretchr/testify/require"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestExposedModuleSetWalk(t *testing.T) {
|
||||
var (
|
||||
value = make(ExposedModuleSet, 3)
|
||||
req = require.New(t)
|
||||
)
|
||||
|
||||
// check walk with no errors
|
||||
{
|
||||
err := value.Walk(func(*ExposedModule) error {
|
||||
return nil
|
||||
})
|
||||
req.NoError(err)
|
||||
}
|
||||
|
||||
// check walk with error
|
||||
req.Error(value.Walk(func(*ExposedModule) error { return fmt.Errorf("walk error") }))
|
||||
}
|
||||
|
||||
func TestExposedModuleSetFilter(t *testing.T) {
|
||||
var (
|
||||
value = make(ExposedModuleSet, 3)
|
||||
req = require.New(t)
|
||||
)
|
||||
|
||||
// filter nothing
|
||||
{
|
||||
set, err := value.Filter(func(*ExposedModule) (bool, error) {
|
||||
return true, nil
|
||||
})
|
||||
req.NoError(err)
|
||||
req.Equal(len(set), len(value))
|
||||
}
|
||||
|
||||
// filter one item
|
||||
{
|
||||
found := false
|
||||
set, err := value.Filter(func(*ExposedModule) (bool, error) {
|
||||
if !found {
|
||||
found = true
|
||||
return found, nil
|
||||
}
|
||||
return false, nil
|
||||
})
|
||||
req.NoError(err)
|
||||
req.Len(set, 1)
|
||||
}
|
||||
|
||||
// filter error
|
||||
{
|
||||
_, err := value.Filter(func(*ExposedModule) (bool, error) {
|
||||
return false, fmt.Errorf("filter error")
|
||||
})
|
||||
req.Error(err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestExposedModuleSetIDs(t *testing.T) {
|
||||
var (
|
||||
value = make(ExposedModuleSet, 3)
|
||||
req = require.New(t)
|
||||
)
|
||||
|
||||
// construct objects
|
||||
value[0] = new(ExposedModule)
|
||||
value[1] = new(ExposedModule)
|
||||
value[2] = new(ExposedModule)
|
||||
// set ids
|
||||
value[0].ID = 1
|
||||
value[1].ID = 2
|
||||
value[2].ID = 3
|
||||
|
||||
// Find existing
|
||||
{
|
||||
val := value.FindByID(2)
|
||||
req.Equal(uint64(2), val.ID)
|
||||
}
|
||||
|
||||
// Find non-existing
|
||||
{
|
||||
val := value.FindByID(4)
|
||||
req.Nil(val)
|
||||
}
|
||||
|
||||
// List IDs from set
|
||||
{
|
||||
val := value.IDs()
|
||||
req.Equal(len(val), len(value))
|
||||
}
|
||||
}
|
||||
|
||||
func TestModuleMappingSetWalk(t *testing.T) {
|
||||
var (
|
||||
value = make(ModuleMappingSet, 3)
|
||||
req = require.New(t)
|
||||
)
|
||||
|
||||
// check walk with no errors
|
||||
{
|
||||
err := value.Walk(func(*ModuleMapping) error {
|
||||
return nil
|
||||
})
|
||||
req.NoError(err)
|
||||
}
|
||||
|
||||
// check walk with error
|
||||
req.Error(value.Walk(func(*ModuleMapping) error { return fmt.Errorf("walk error") }))
|
||||
}
|
||||
|
||||
func TestModuleMappingSetFilter(t *testing.T) {
|
||||
var (
|
||||
value = make(ModuleMappingSet, 3)
|
||||
req = require.New(t)
|
||||
)
|
||||
|
||||
// filter nothing
|
||||
{
|
||||
set, err := value.Filter(func(*ModuleMapping) (bool, error) {
|
||||
return true, nil
|
||||
})
|
||||
req.NoError(err)
|
||||
req.Equal(len(set), len(value))
|
||||
}
|
||||
|
||||
// filter one item
|
||||
{
|
||||
found := false
|
||||
set, err := value.Filter(func(*ModuleMapping) (bool, error) {
|
||||
if !found {
|
||||
found = true
|
||||
return found, nil
|
||||
}
|
||||
return false, nil
|
||||
})
|
||||
req.NoError(err)
|
||||
req.Len(set, 1)
|
||||
}
|
||||
|
||||
// filter error
|
||||
{
|
||||
_, err := value.Filter(func(*ModuleMapping) (bool, error) {
|
||||
return false, fmt.Errorf("filter error")
|
||||
})
|
||||
req.Error(err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestNodeSetWalk(t *testing.T) {
|
||||
var (
|
||||
value = make(NodeSet, 3)
|
||||
req = require.New(t)
|
||||
)
|
||||
|
||||
// check walk with no errors
|
||||
{
|
||||
err := value.Walk(func(*Node) error {
|
||||
return nil
|
||||
})
|
||||
req.NoError(err)
|
||||
}
|
||||
|
||||
// check walk with error
|
||||
req.Error(value.Walk(func(*Node) error { return fmt.Errorf("walk error") }))
|
||||
}
|
||||
|
||||
func TestNodeSetFilter(t *testing.T) {
|
||||
var (
|
||||
value = make(NodeSet, 3)
|
||||
req = require.New(t)
|
||||
)
|
||||
|
||||
// filter nothing
|
||||
{
|
||||
set, err := value.Filter(func(*Node) (bool, error) {
|
||||
return true, nil
|
||||
})
|
||||
req.NoError(err)
|
||||
req.Equal(len(set), len(value))
|
||||
}
|
||||
|
||||
// filter one item
|
||||
{
|
||||
found := false
|
||||
set, err := value.Filter(func(*Node) (bool, error) {
|
||||
if !found {
|
||||
found = true
|
||||
return found, nil
|
||||
}
|
||||
return false, nil
|
||||
})
|
||||
req.NoError(err)
|
||||
req.Len(set, 1)
|
||||
}
|
||||
|
||||
// filter error
|
||||
{
|
||||
_, err := value.Filter(func(*Node) (bool, error) {
|
||||
return false, fmt.Errorf("filter error")
|
||||
})
|
||||
req.Error(err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestNodeSetIDs(t *testing.T) {
|
||||
var (
|
||||
value = make(NodeSet, 3)
|
||||
req = require.New(t)
|
||||
)
|
||||
|
||||
// construct objects
|
||||
value[0] = new(Node)
|
||||
value[1] = new(Node)
|
||||
value[2] = new(Node)
|
||||
// set ids
|
||||
value[0].ID = 1
|
||||
value[1].ID = 2
|
||||
value[2].ID = 3
|
||||
|
||||
// Find existing
|
||||
{
|
||||
val := value.FindByID(2)
|
||||
req.Equal(uint64(2), val.ID)
|
||||
}
|
||||
|
||||
// Find non-existing
|
||||
{
|
||||
val := value.FindByID(4)
|
||||
req.Nil(val)
|
||||
}
|
||||
|
||||
// List IDs from set
|
||||
{
|
||||
val := value.IDs()
|
||||
req.Equal(len(val), len(value))
|
||||
}
|
||||
}
|
||||
|
||||
func TestNodeSyncSetWalk(t *testing.T) {
|
||||
var (
|
||||
value = make(NodeSyncSet, 3)
|
||||
req = require.New(t)
|
||||
)
|
||||
|
||||
// check walk with no errors
|
||||
{
|
||||
err := value.Walk(func(*NodeSync) error {
|
||||
return nil
|
||||
})
|
||||
req.NoError(err)
|
||||
}
|
||||
|
||||
// check walk with error
|
||||
req.Error(value.Walk(func(*NodeSync) error { return fmt.Errorf("walk error") }))
|
||||
}
|
||||
|
||||
func TestNodeSyncSetFilter(t *testing.T) {
|
||||
var (
|
||||
value = make(NodeSyncSet, 3)
|
||||
req = require.New(t)
|
||||
)
|
||||
|
||||
// filter nothing
|
||||
{
|
||||
set, err := value.Filter(func(*NodeSync) (bool, error) {
|
||||
return true, nil
|
||||
})
|
||||
req.NoError(err)
|
||||
req.Equal(len(set), len(value))
|
||||
}
|
||||
|
||||
// filter one item
|
||||
{
|
||||
found := false
|
||||
set, err := value.Filter(func(*NodeSync) (bool, error) {
|
||||
if !found {
|
||||
found = true
|
||||
return found, nil
|
||||
}
|
||||
return false, nil
|
||||
})
|
||||
req.NoError(err)
|
||||
req.Len(set, 1)
|
||||
}
|
||||
|
||||
// filter error
|
||||
{
|
||||
_, err := value.Filter(func(*NodeSync) (bool, error) {
|
||||
return false, fmt.Errorf("filter error")
|
||||
})
|
||||
req.Error(err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestSharedModuleSetWalk(t *testing.T) {
|
||||
var (
|
||||
value = make(SharedModuleSet, 3)
|
||||
req = require.New(t)
|
||||
)
|
||||
|
||||
// check walk with no errors
|
||||
{
|
||||
err := value.Walk(func(*SharedModule) error {
|
||||
return nil
|
||||
})
|
||||
req.NoError(err)
|
||||
}
|
||||
|
||||
// check walk with error
|
||||
req.Error(value.Walk(func(*SharedModule) error { return fmt.Errorf("walk error") }))
|
||||
}
|
||||
|
||||
func TestSharedModuleSetFilter(t *testing.T) {
|
||||
var (
|
||||
value = make(SharedModuleSet, 3)
|
||||
req = require.New(t)
|
||||
)
|
||||
|
||||
// filter nothing
|
||||
{
|
||||
set, err := value.Filter(func(*SharedModule) (bool, error) {
|
||||
return true, nil
|
||||
})
|
||||
req.NoError(err)
|
||||
req.Equal(len(set), len(value))
|
||||
}
|
||||
|
||||
// filter one item
|
||||
{
|
||||
found := false
|
||||
set, err := value.Filter(func(*SharedModule) (bool, error) {
|
||||
if !found {
|
||||
found = true
|
||||
return found, nil
|
||||
}
|
||||
return false, nil
|
||||
})
|
||||
req.NoError(err)
|
||||
req.Len(set, 1)
|
||||
}
|
||||
|
||||
// filter error
|
||||
{
|
||||
_, err := value.Filter(func(*SharedModule) (bool, error) {
|
||||
return false, fmt.Errorf("filter error")
|
||||
})
|
||||
req.Error(err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestSharedModuleSetIDs(t *testing.T) {
|
||||
var (
|
||||
value = make(SharedModuleSet, 3)
|
||||
req = require.New(t)
|
||||
)
|
||||
|
||||
// construct objects
|
||||
value[0] = new(SharedModule)
|
||||
value[1] = new(SharedModule)
|
||||
value[2] = new(SharedModule)
|
||||
// set ids
|
||||
value[0].ID = 1
|
||||
value[1].ID = 2
|
||||
value[2].ID = 3
|
||||
|
||||
// Find existing
|
||||
{
|
||||
val := value.FindByID(2)
|
||||
req.Equal(uint64(2), val.ID)
|
||||
}
|
||||
|
||||
// Find non-existing
|
||||
{
|
||||
val := value.FindByID(4)
|
||||
req.Nil(val)
|
||||
}
|
||||
|
||||
// List IDs from set
|
||||
{
|
||||
val := value.IDs()
|
||||
req.Equal(len(val), len(value))
|
||||
}
|
||||
}
|
||||
8
server/federation/types/types.yaml
Normal file
8
server/federation/types/types.yaml
Normal file
@@ -0,0 +1,8 @@
|
||||
types:
|
||||
Node: {}
|
||||
NodeSync:
|
||||
noIdField: true
|
||||
ExposedModule: {}
|
||||
SharedModule: {}
|
||||
ModuleMapping:
|
||||
noIdField: true
|
||||
71
server/federation/util/util.go
Normal file
71
server/federation/util/util.go
Normal file
@@ -0,0 +1,71 @@
|
||||
package util
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net/url"
|
||||
"strconv"
|
||||
)
|
||||
|
||||
var (
|
||||
ErrorURIMissingToken = fmt.Errorf("uri: token missing")
|
||||
)
|
||||
|
||||
const ()
|
||||
|
||||
type (
|
||||
NodeURIParams struct {
|
||||
Name string
|
||||
}
|
||||
|
||||
DecodedURI struct {
|
||||
Domain string
|
||||
Token string
|
||||
NodeID uint64
|
||||
Params NodeURIParams
|
||||
}
|
||||
)
|
||||
|
||||
func DecodeURI(i string) (du *DecodedURI, err error) {
|
||||
du = &DecodedURI{
|
||||
Params: NodeURIParams{},
|
||||
}
|
||||
|
||||
u, err := url.Parse(i)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
du.Domain = u.Host
|
||||
nodeS := u.User.Username()
|
||||
if nodeS == "" {
|
||||
return nil, ErrorURIMissingToken
|
||||
}
|
||||
nodeID, err := strconv.ParseUint(nodeS, 10, 64)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
du.NodeID = nodeID
|
||||
|
||||
token, has := u.User.Password()
|
||||
if !has {
|
||||
return nil, ErrorURIMissingToken
|
||||
}
|
||||
du.Token = token
|
||||
|
||||
params := u.Query()
|
||||
if name, has := params["name"]; has && len(name) > 0 {
|
||||
du.Params.Name = name[0]
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
func EncodeURI(ott, domain string, nodeID uint64) string {
|
||||
u := url.URL{
|
||||
Scheme: "corteza",
|
||||
User: url.UserPassword(strconv.FormatUint(nodeID, 10), ott),
|
||||
Host: domain,
|
||||
}
|
||||
|
||||
return u.String()
|
||||
}
|
||||
Reference in New Issue
Block a user