Added base federation module
This commit is contained in:
@@ -22,6 +22,7 @@ type (
|
||||
HTTPServer options.HTTPServerOpt
|
||||
Websocket options.WebsocketOpt
|
||||
Eventbus options.EventbusOpt
|
||||
Federation options.FederationOpt
|
||||
}
|
||||
)
|
||||
|
||||
@@ -44,5 +45,6 @@ func NewOptions() *Options {
|
||||
HTTPServer: *options.HTTPServer(),
|
||||
Websocket: *options.Websocket(),
|
||||
Eventbus: *options.Eventbus(),
|
||||
Federation: *options.Federation(),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,6 +3,7 @@ package app
|
||||
import (
|
||||
"context"
|
||||
composeRest "github.com/cortezaproject/corteza-server/compose/rest"
|
||||
federationRest "github.com/cortezaproject/corteza-server/federation/rest"
|
||||
messagingRest "github.com/cortezaproject/corteza-server/messaging/rest"
|
||||
"github.com/cortezaproject/corteza-server/pkg/actionlog"
|
||||
"github.com/cortezaproject/corteza-server/pkg/api/server"
|
||||
@@ -70,4 +71,8 @@ func (app *CortezaApp) mountHttpRoutes(r chi.Router) {
|
||||
)
|
||||
r.Route("/"+webappBaseUrl, webapp.MakeWebappServer(app.Opt.HTTPServer))
|
||||
}
|
||||
|
||||
if app.Opt.Federation.Enabled {
|
||||
r.Route("/federation", federationRest.MountRoutes)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,17 @@
|
||||
---
|
||||
# This is direct conversion from old api/*/spec.json files
|
||||
# using https://www.json2yaml.com/
|
||||
#
|
||||
# Next step: swagger.
|
||||
|
||||
endpoints:
|
||||
- title: Foobar
|
||||
entrypoint: foobar
|
||||
path: "/foobar"
|
||||
authentication: []
|
||||
imports:
|
||||
apis:
|
||||
- name: foobar
|
||||
method: GET
|
||||
title: Foo bar
|
||||
path: "/"
|
||||
@@ -0,0 +1,37 @@
|
||||
package rest
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/cortezaproject/corteza-server/compose/types"
|
||||
"github.com/cortezaproject/corteza-server/federation/rest/request"
|
||||
)
|
||||
|
||||
type (
|
||||
namespacePayload struct {
|
||||
*types.Namespace
|
||||
|
||||
CanGrant bool `json:"canGrant"`
|
||||
CanUpdateNamespace bool `json:"canUpdateNamespace"`
|
||||
CanDeleteNamespace bool `json:"canDeleteNamespace"`
|
||||
CanManageNamespace bool `json:"canManageNamespace"`
|
||||
CanCreateModule bool `json:"canCreateModule"`
|
||||
CanCreateChart bool `json:"canCreateChart"`
|
||||
CanCreatePage bool `json:"canCreatePage"`
|
||||
}
|
||||
|
||||
payload struct{}
|
||||
Foobar struct{}
|
||||
|
||||
accessController interface {
|
||||
CanGrant(context.Context) bool
|
||||
}
|
||||
)
|
||||
|
||||
func (Foobar) New() *Foobar {
|
||||
return &Foobar{}
|
||||
}
|
||||
|
||||
func (ctrl Foobar) Foobar(ctx context.Context, r *request.FoobarFoobar) (interface{}, error) {
|
||||
return &struct{}{}, nil
|
||||
}
|
||||
@@ -0,0 +1,63 @@
|
||||
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/go-chi/chi"
|
||||
"github.com/titpetric/factory/resputil"
|
||||
"net/http"
|
||||
|
||||
"github.com/cortezaproject/corteza-server/federation/rest/request"
|
||||
"github.com/cortezaproject/corteza-server/pkg/logger"
|
||||
)
|
||||
|
||||
type (
|
||||
// Internal API interface
|
||||
FoobarAPI interface {
|
||||
Foobar(context.Context, *request.FoobarFoobar) (interface{}, error)
|
||||
}
|
||||
|
||||
// HTTP API interface
|
||||
Foobar struct {
|
||||
Foobar func(http.ResponseWriter, *http.Request)
|
||||
}
|
||||
)
|
||||
|
||||
func NewFoobar(h FoobarAPI) *Foobar {
|
||||
return &Foobar{
|
||||
Foobar: func(w http.ResponseWriter, r *http.Request) {
|
||||
defer r.Body.Close()
|
||||
params := request.NewFoobarFoobar()
|
||||
if err := params.Fill(r); err != nil {
|
||||
logger.LogParamError("Foobar.Foobar", r, err)
|
||||
resputil.JSON(w, err)
|
||||
return
|
||||
}
|
||||
|
||||
value, err := h.Foobar(r.Context(), params)
|
||||
if err != nil {
|
||||
logger.LogControllerError("Foobar.Foobar", r, err, params.Auditable())
|
||||
resputil.JSON(w, err)
|
||||
return
|
||||
}
|
||||
logger.LogControllerCall("Foobar.Foobar", r, params.Auditable())
|
||||
if !serveHTTP(value, w, r) {
|
||||
resputil.JSON(w, value)
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func (h Foobar) MountRoutes(r chi.Router, middlewares ...func(http.Handler) http.Handler) {
|
||||
r.Group(func(r chi.Router) {
|
||||
r.Use(middlewares...)
|
||||
r.Get("/foobar/", h.Foobar)
|
||||
})
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
package handlers
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
)
|
||||
|
||||
// Edit this file in `codegen/common/rest/handlers/util.go`;
|
||||
// any changes under [service] will be overwritten by codegen.
|
||||
|
||||
func serveHTTP(value interface{}, w http.ResponseWriter, r *http.Request) bool {
|
||||
switch fn := value.(type) {
|
||||
case func(http.ResponseWriter, *http.Request):
|
||||
fn(w, r)
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
package rest
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
|
||||
"github.com/cortezaproject/corteza-server/compose/service"
|
||||
)
|
||||
|
||||
func middlewareAllowedAccess(next http.Handler) http.Handler {
|
||||
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
if !service.DefaultAccessControl.CanAccess(r.Context()) {
|
||||
http.Error(w, http.StatusText(http.StatusForbidden), http.StatusForbidden)
|
||||
return
|
||||
}
|
||||
|
||||
next.ServeHTTP(w, r)
|
||||
})
|
||||
}
|
||||
@@ -0,0 +1,60 @@
|
||||
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"
|
||||
"io"
|
||||
"mime/multipart"
|
||||
"net/http"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// dummy vars to prevent
|
||||
// unused imports complain
|
||||
var (
|
||||
_ = chi.URLParam
|
||||
_ = multipart.ErrMessageTooLarge
|
||||
_ = payload.ParseUint64s
|
||||
)
|
||||
|
||||
type (
|
||||
// Internal API interface
|
||||
FoobarFoobar struct {
|
||||
}
|
||||
)
|
||||
|
||||
// NewFoobarFoobar request
|
||||
func NewFoobarFoobar() *FoobarFoobar {
|
||||
return &FoobarFoobar{}
|
||||
}
|
||||
|
||||
// Auditable returns all auditable/loggable parameters
|
||||
func (r FoobarFoobar) Auditable() map[string]interface{} {
|
||||
return map[string]interface{}{}
|
||||
}
|
||||
|
||||
// Fill processes request and fills internal variables
|
||||
func (r *FoobarFoobar) Fill(req *http.Request) (err error) {
|
||||
if 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)
|
||||
}
|
||||
}
|
||||
|
||||
return err
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
package rest
|
||||
|
||||
import (
|
||||
"github.com/go-chi/chi"
|
||||
|
||||
"github.com/cortezaproject/corteza-server/federation/rest/handlers"
|
||||
"github.com/cortezaproject/corteza-server/pkg/auth"
|
||||
)
|
||||
|
||||
func MountRoutes(r chi.Router) {
|
||||
var (
|
||||
foobar = Foobar{}.New()
|
||||
)
|
||||
|
||||
// Protect all _private_ routes
|
||||
r.Group(func(r chi.Router) {
|
||||
r.Use(auth.MiddlewareValidOnly)
|
||||
r.Use(middlewareAllowedAccess)
|
||||
|
||||
handlers.NewFoobar(foobar).MountRoutes(r)
|
||||
})
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
package types
|
||||
|
||||
type (
|
||||
Node struct {
|
||||
ID uint64 `json:"recordID,string"`
|
||||
Name string `json:"name,string"`
|
||||
}
|
||||
)
|
||||
@@ -0,0 +1,73 @@
|
||||
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 (
|
||||
|
||||
// NodeSet slice of Node
|
||||
//
|
||||
// This type is auto-generated.
|
||||
NodeSet []*Node
|
||||
)
|
||||
|
||||
// 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
|
||||
}
|
||||
@@ -0,0 +1,105 @@
|
||||
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 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))
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,2 @@
|
||||
types:
|
||||
Node:
|
||||
@@ -0,0 +1,18 @@
|
||||
package options
|
||||
|
||||
type (
|
||||
FederationOpt struct {
|
||||
Enabled bool `env:"FEDERATION_ENABLED"`
|
||||
}
|
||||
)
|
||||
|
||||
func Federation() (o *FederationOpt) {
|
||||
|
||||
o = &FederationOpt{
|
||||
Enabled: false,
|
||||
}
|
||||
|
||||
fill(o)
|
||||
|
||||
return
|
||||
}
|
||||
Reference in New Issue
Block a user