Implement namespace CRUD + various small fixes
Other fixes and improvements: - add parseISODateWithErr and parseISODatePtrWithErr handlers for incoming data - add service & repository errors - cleanup old (unbound) attachment controllers from router - fix system repository error prefix (auth => system)
This commit is contained in:
@@ -24,6 +24,165 @@
|
|||||||
}
|
}
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"title": "Namespaces",
|
||||||
|
"parameters": {},
|
||||||
|
"entrypoint": "namespace",
|
||||||
|
"path": "/namespace",
|
||||||
|
"authentication": [],
|
||||||
|
"struct": [
|
||||||
|
{
|
||||||
|
"imports": [
|
||||||
|
"sqlxTypes github.com/jmoiron/sqlx/types",
|
||||||
|
"time"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"apis": [
|
||||||
|
{
|
||||||
|
"name": "list",
|
||||||
|
"method": "GET",
|
||||||
|
"title": "List namespaces",
|
||||||
|
"path": "/",
|
||||||
|
"parameters": {
|
||||||
|
"get": [
|
||||||
|
{
|
||||||
|
"type": "string",
|
||||||
|
"name": "query",
|
||||||
|
"required": false,
|
||||||
|
"title": "Search query"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "page",
|
||||||
|
"type": "uint",
|
||||||
|
"required": false,
|
||||||
|
"title": "Page number (0 based)"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "perPage",
|
||||||
|
"type": "uint",
|
||||||
|
"required": false,
|
||||||
|
"title": "Returned items per page (default 50)"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "create",
|
||||||
|
"method": "POST",
|
||||||
|
"title": "Create namespace",
|
||||||
|
"path": "/",
|
||||||
|
"parameters": {
|
||||||
|
"post": [
|
||||||
|
{
|
||||||
|
"type": "string",
|
||||||
|
"name": "name",
|
||||||
|
"required": true,
|
||||||
|
"title": "Name"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "string",
|
||||||
|
"name": "slug",
|
||||||
|
"required": true,
|
||||||
|
"title": "Slug (url path part)"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "bool",
|
||||||
|
"name": "enabled",
|
||||||
|
"required": true,
|
||||||
|
"title": "Enabled"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "sqlxTypes.JSONText",
|
||||||
|
"name": "meta",
|
||||||
|
"required": true,
|
||||||
|
"title": "Meta data"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "read",
|
||||||
|
"method": "GET",
|
||||||
|
"title": "Read namespace",
|
||||||
|
"path": "/{namespaceID}",
|
||||||
|
"parameters": {
|
||||||
|
"path": [
|
||||||
|
{
|
||||||
|
"type": "uint64",
|
||||||
|
"name": "namespaceID",
|
||||||
|
"required": true,
|
||||||
|
"title": "ID"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "update",
|
||||||
|
"method": "POST",
|
||||||
|
"title": "Update namespace",
|
||||||
|
"path": "/{namespaceID}",
|
||||||
|
"parameters": {
|
||||||
|
"path": [
|
||||||
|
{
|
||||||
|
"type": "uint64",
|
||||||
|
"name": "namespaceID",
|
||||||
|
"required": true,
|
||||||
|
"title": "ID"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"post": [
|
||||||
|
{
|
||||||
|
"type": "string",
|
||||||
|
"name": "name",
|
||||||
|
"required": true,
|
||||||
|
"title": "Name"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "string",
|
||||||
|
"name": "slug",
|
||||||
|
"required": true,
|
||||||
|
"title": "Slug (url path part)"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "bool",
|
||||||
|
"name": "enabled",
|
||||||
|
"required": true,
|
||||||
|
"title": "Enabled"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "sqlxTypes.JSONText",
|
||||||
|
"name": "meta",
|
||||||
|
"required": true,
|
||||||
|
"title": "Meta data"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "*time.Time",
|
||||||
|
"name": "updatedAt",
|
||||||
|
"required": true,
|
||||||
|
"title": "Last update (or creation) date"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "delete",
|
||||||
|
"method": "DELETE",
|
||||||
|
"title": "Delete namespace",
|
||||||
|
"path": "/{namespaceID}",
|
||||||
|
"parameters": {
|
||||||
|
"path": [
|
||||||
|
{
|
||||||
|
"type": "uint64",
|
||||||
|
"name": "namespaceID",
|
||||||
|
"required": true,
|
||||||
|
"title": "ID"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"title": "Pages",
|
"title": "Pages",
|
||||||
"description": "Compose module pages",
|
"description": "Compose module pages",
|
||||||
@@ -374,7 +533,7 @@
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
"title": "Records",
|
"title": "Records",
|
||||||
"description": "Compose records ",
|
"description": "Compose records",
|
||||||
"entrypoint": "record",
|
"entrypoint": "record",
|
||||||
"path": "/module/{moduleID}/record",
|
"path": "/module/{moduleID}/record",
|
||||||
"authentication": [],
|
"authentication": [],
|
||||||
|
|||||||
160
api/compose/spec/namespace.json
Normal file
160
api/compose/spec/namespace.json
Normal file
@@ -0,0 +1,160 @@
|
|||||||
|
{
|
||||||
|
"Title": "Namespaces",
|
||||||
|
"Interface": "Namespace",
|
||||||
|
"Struct": [
|
||||||
|
{
|
||||||
|
"imports": [
|
||||||
|
"sqlxTypes github.com/jmoiron/sqlx/types",
|
||||||
|
"time"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"Parameters": {},
|
||||||
|
"Protocol": "",
|
||||||
|
"Authentication": [],
|
||||||
|
"Path": "/namespace",
|
||||||
|
"APIs": [
|
||||||
|
{
|
||||||
|
"Name": "list",
|
||||||
|
"Method": "GET",
|
||||||
|
"Title": "List namespaces",
|
||||||
|
"Path": "/",
|
||||||
|
"Parameters": {
|
||||||
|
"get": [
|
||||||
|
{
|
||||||
|
"name": "query",
|
||||||
|
"required": false,
|
||||||
|
"title": "Search query",
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "page",
|
||||||
|
"required": false,
|
||||||
|
"title": "Page number (0 based)",
|
||||||
|
"type": "uint"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "perPage",
|
||||||
|
"required": false,
|
||||||
|
"title": "Returned items per page (default 50)",
|
||||||
|
"type": "uint"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Name": "create",
|
||||||
|
"Method": "POST",
|
||||||
|
"Title": "Create namespace",
|
||||||
|
"Path": "/",
|
||||||
|
"Parameters": {
|
||||||
|
"post": [
|
||||||
|
{
|
||||||
|
"name": "name",
|
||||||
|
"required": true,
|
||||||
|
"title": "Name",
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "slug",
|
||||||
|
"required": true,
|
||||||
|
"title": "Slug (url path part)",
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "enabled",
|
||||||
|
"required": true,
|
||||||
|
"title": "Enabled",
|
||||||
|
"type": "bool"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "meta",
|
||||||
|
"required": true,
|
||||||
|
"title": "Meta data",
|
||||||
|
"type": "sqlxTypes.JSONText"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Name": "read",
|
||||||
|
"Method": "GET",
|
||||||
|
"Title": "Read namespace",
|
||||||
|
"Path": "/{namespaceID}",
|
||||||
|
"Parameters": {
|
||||||
|
"path": [
|
||||||
|
{
|
||||||
|
"name": "namespaceID",
|
||||||
|
"required": true,
|
||||||
|
"title": "ID",
|
||||||
|
"type": "uint64"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Name": "update",
|
||||||
|
"Method": "POST",
|
||||||
|
"Title": "Update namespace",
|
||||||
|
"Path": "/{namespaceID}",
|
||||||
|
"Parameters": {
|
||||||
|
"path": [
|
||||||
|
{
|
||||||
|
"name": "namespaceID",
|
||||||
|
"required": true,
|
||||||
|
"title": "ID",
|
||||||
|
"type": "uint64"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"post": [
|
||||||
|
{
|
||||||
|
"name": "name",
|
||||||
|
"required": true,
|
||||||
|
"title": "Name",
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "slug",
|
||||||
|
"required": true,
|
||||||
|
"title": "Slug (url path part)",
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "enabled",
|
||||||
|
"required": true,
|
||||||
|
"title": "Enabled",
|
||||||
|
"type": "bool"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "meta",
|
||||||
|
"required": true,
|
||||||
|
"title": "Meta data",
|
||||||
|
"type": "sqlxTypes.JSONText"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "updatedAt",
|
||||||
|
"required": true,
|
||||||
|
"title": "Last update (or creation) date",
|
||||||
|
"type": "*time.Time"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Name": "delete",
|
||||||
|
"Method": "DELETE",
|
||||||
|
"Title": "Delete namespace",
|
||||||
|
"Path": "/{namespaceID}",
|
||||||
|
"Parameters": {
|
||||||
|
"path": [
|
||||||
|
{
|
||||||
|
"name": "namespaceID",
|
||||||
|
"required": true,
|
||||||
|
"title": "ID",
|
||||||
|
"type": "uint64"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
@@ -22,6 +22,7 @@ function types {
|
|||||||
CGO_ENABLED=0 go build -o ./build/gen-type-set codegen/v2/type-set.go
|
CGO_ENABLED=0 go build -o ./build/gen-type-set codegen/v2/type-set.go
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
./build/gen-type-set --types Namespace --output compose/types/namespace.gen.go
|
||||||
./build/gen-type-set --types Attachment --output compose/types/attachment.gen.go
|
./build/gen-type-set --types Attachment --output compose/types/attachment.gen.go
|
||||||
./build/gen-type-set --types Module --output compose/types/module.gen.go
|
./build/gen-type-set --types Module --output compose/types/module.gen.go
|
||||||
./build/gen-type-set --types Page --output compose/types/page.gen.go
|
./build/gen-type-set --types Page --output compose/types/page.gen.go
|
||||||
|
|||||||
@@ -101,6 +101,8 @@ $parsers = array(
|
|||||||
"int" => "parseInt",
|
"int" => "parseInt",
|
||||||
"uint" => "parseUint",
|
"uint" => "parseUint",
|
||||||
"bool" => "parseBool",
|
"bool" => "parseBool",
|
||||||
|
"time.Time" => "parseISODateWithErr",
|
||||||
|
"*time.Time" => "parseISODatePtrWithErr",
|
||||||
"sqlxTypes.JSONText" => "parseJSONTextWithErr",
|
"sqlxTypes.JSONText" => "parseJSONTextWithErr",
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|||||||
File diff suppressed because one or more lines are too long
14
compose/db/schema/mysql/20190427210922.namespace-tbl.up.sql
Normal file
14
compose/db/schema/mysql/20190427210922.namespace-tbl.up.sql
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
CREATE TABLE `compose_namespace` (
|
||||||
|
`id` BIGINT(20) UNSIGNED NOT NULL,
|
||||||
|
`name` VARCHAR(64) NOT NULL COMMENT 'Name',
|
||||||
|
`slug` VARCHAR(64) NOT NULL COMMENT 'URL slug',
|
||||||
|
`enabled` BOOLEAN NOT NULL COMMENT 'Is namespace enabled?',
|
||||||
|
`meta` JSON NOT NULL COMMENT 'Meta data',
|
||||||
|
|
||||||
|
`created_at` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||||
|
`updated_at` DATETIME DEFAULT NULL,
|
||||||
|
`deleted_at` DATETIME DEFAULT NULL,
|
||||||
|
|
||||||
|
PRIMARY KEY (`id`)
|
||||||
|
|
||||||
|
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
|
||||||
25
compose/internal/repository/error.go
Normal file
25
compose/internal/repository/error.go
Normal file
@@ -0,0 +1,25 @@
|
|||||||
|
package repository
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/pkg/errors"
|
||||||
|
)
|
||||||
|
|
||||||
|
type (
|
||||||
|
repositoryError string
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
ErrNotImplemented = repositoryError("NotImplemented")
|
||||||
|
)
|
||||||
|
|
||||||
|
func (e repositoryError) Error() string {
|
||||||
|
return e.String()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e repositoryError) String() string {
|
||||||
|
return "crust.compose.repository." + string(e)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e repositoryError) new() error {
|
||||||
|
return errors.WithStack(e)
|
||||||
|
}
|
||||||
168
compose/internal/repository/namespace.go
Normal file
168
compose/internal/repository/namespace.go
Normal file
@@ -0,0 +1,168 @@
|
|||||||
|
package repository
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/titpetric/factory"
|
||||||
|
"gopkg.in/Masterminds/squirrel.v1"
|
||||||
|
|
||||||
|
"github.com/crusttech/crust/compose/types"
|
||||||
|
)
|
||||||
|
|
||||||
|
type (
|
||||||
|
NamespaceRepository interface {
|
||||||
|
With(ctx context.Context, db *factory.DB) NamespaceRepository
|
||||||
|
|
||||||
|
FindByID(id uint64) (*types.Namespace, error)
|
||||||
|
Find(filter types.NamespaceFilter) (types.NamespaceSet, types.NamespaceFilter, error)
|
||||||
|
Create(mod *types.Namespace) (*types.Namespace, error)
|
||||||
|
Update(mod *types.Namespace) (*types.Namespace, error)
|
||||||
|
DeleteByID(id uint64) error
|
||||||
|
}
|
||||||
|
|
||||||
|
namespace struct {
|
||||||
|
*repository
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
ErrNamespaceNotFound = repositoryError("NamespaceNotFound")
|
||||||
|
)
|
||||||
|
|
||||||
|
func Namespace(ctx context.Context, db *factory.DB) NamespaceRepository {
|
||||||
|
return (&namespace{}).With(ctx, db)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r namespace) table() string {
|
||||||
|
return "compose_namespace"
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r namespace) columns() []string {
|
||||||
|
return []string{
|
||||||
|
"id",
|
||||||
|
"name",
|
||||||
|
"slug",
|
||||||
|
"enabled",
|
||||||
|
"meta",
|
||||||
|
"created_at",
|
||||||
|
"updated_at",
|
||||||
|
"deleted_at",
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *namespace) With(ctx context.Context, db *factory.DB) NamespaceRepository {
|
||||||
|
return &namespace{
|
||||||
|
repository: r.repository.With(ctx, db),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *namespace) FindByID(id uint64) (n *types.Namespace, err error) {
|
||||||
|
var (
|
||||||
|
sql string
|
||||||
|
args []interface{}
|
||||||
|
)
|
||||||
|
|
||||||
|
n = &types.Namespace{}
|
||||||
|
|
||||||
|
qb := r.query().
|
||||||
|
Columns(r.columns()...).
|
||||||
|
Where("id = ?", id)
|
||||||
|
|
||||||
|
if sql, args, err = qb.ToSql(); err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if err = r.db().Get(n, sql, args...); err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if n == nil || n.ID == 0 {
|
||||||
|
return nil, ErrNamespaceNotFound
|
||||||
|
}
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *namespace) Find(filter types.NamespaceFilter) (nn types.NamespaceSet, f types.NamespaceFilter, err error) {
|
||||||
|
var (
|
||||||
|
sql string
|
||||||
|
args []interface{}
|
||||||
|
)
|
||||||
|
|
||||||
|
f = filter
|
||||||
|
|
||||||
|
if f.PerPage > 100 {
|
||||||
|
f.PerPage = 100
|
||||||
|
} else if f.PerPage == 0 {
|
||||||
|
f.PerPage = 50
|
||||||
|
}
|
||||||
|
|
||||||
|
qb := r.query()
|
||||||
|
if f.Query != "" {
|
||||||
|
q := "%" + f.Query + "%"
|
||||||
|
qb = qb.Where("name like ? OR slug like ?", q, q)
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
cq := qb.Column(squirrel.Alias(squirrel.Expr("COUNT(*)"), "count"))
|
||||||
|
if sql, args, err = cq.ToSql(); err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if err = r.db().Get(&f.Count, sql, args...); err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if f.Count == 0 {
|
||||||
|
// No rows with this filter no need to continue
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if f.Page > 0 {
|
||||||
|
qb = qb.Offset(uint64(f.PerPage * f.Page))
|
||||||
|
}
|
||||||
|
|
||||||
|
qb = qb.
|
||||||
|
Limit(uint64(f.PerPage)).
|
||||||
|
Columns(r.columns()...).
|
||||||
|
OrderBy("id ASC")
|
||||||
|
|
||||||
|
if sql, args, err = qb.ToSql(); err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if err = r.db().Select(&nn, sql, args...); err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r namespace) query() squirrel.SelectBuilder {
|
||||||
|
return squirrel.
|
||||||
|
Select().
|
||||||
|
From(r.table()).
|
||||||
|
Where("deleted_at IS NULL")
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *namespace) Create(mod *types.Namespace) (*types.Namespace, error) {
|
||||||
|
mod.ID = factory.Sonyflake.NextID()
|
||||||
|
mod.CreatedAt = time.Now()
|
||||||
|
|
||||||
|
return mod, r.db().Insert(r.table(), mod)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *namespace) Update(mod *types.Namespace) (*types.Namespace, error) {
|
||||||
|
now := time.Now()
|
||||||
|
mod.UpdatedAt = &now
|
||||||
|
|
||||||
|
return mod, r.db().Replace(r.table(), mod)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *namespace) DeleteByID(id uint64) error {
|
||||||
|
_, err := r.db().Exec("DELETE FROM compose_namespace WHERE id=?", id)
|
||||||
|
return err
|
||||||
|
}
|
||||||
31
compose/internal/service/error.go
Normal file
31
compose/internal/service/error.go
Normal file
@@ -0,0 +1,31 @@
|
|||||||
|
package service
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/pkg/errors"
|
||||||
|
)
|
||||||
|
|
||||||
|
type (
|
||||||
|
serviceError string
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
ErrInvalidID serviceError = "InvalidID"
|
||||||
|
ErrStaleData serviceError = "StaleData"
|
||||||
|
ErrNoCreatePermissions serviceError = "NoCreatePermissions"
|
||||||
|
ErrNoReadPermissions serviceError = "NoReadPermissions"
|
||||||
|
ErrNoUpdatePermissions serviceError = "NoUpdatePermissions"
|
||||||
|
ErrNoDeletePermissions serviceError = "NoDeletePermissions"
|
||||||
|
ErrNotImplemented serviceError = "NotImplemented"
|
||||||
|
)
|
||||||
|
|
||||||
|
func (e serviceError) Error() string {
|
||||||
|
return e.String()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e serviceError) String() string {
|
||||||
|
return "crust.compose.service." + string(e)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e serviceError) withStack() error {
|
||||||
|
return errors.WithStack(e)
|
||||||
|
}
|
||||||
132
compose/internal/service/namespace.go
Normal file
132
compose/internal/service/namespace.go
Normal file
@@ -0,0 +1,132 @@
|
|||||||
|
package service
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/titpetric/factory"
|
||||||
|
|
||||||
|
"github.com/crusttech/crust/compose/internal/repository"
|
||||||
|
"github.com/crusttech/crust/compose/types"
|
||||||
|
)
|
||||||
|
|
||||||
|
type (
|
||||||
|
namespace struct {
|
||||||
|
db *factory.DB
|
||||||
|
ctx context.Context
|
||||||
|
|
||||||
|
prmSvc PermissionsService
|
||||||
|
|
||||||
|
namespaceRepo repository.NamespaceRepository
|
||||||
|
}
|
||||||
|
|
||||||
|
NamespaceService interface {
|
||||||
|
With(ctx context.Context) NamespaceService
|
||||||
|
|
||||||
|
FindByID(namespaceID uint64) (*types.Namespace, error)
|
||||||
|
Find(types.NamespaceFilter) (types.NamespaceSet, types.NamespaceFilter, error)
|
||||||
|
|
||||||
|
Create(namespace *types.Namespace) (*types.Namespace, error)
|
||||||
|
Update(namespace *types.Namespace) (*types.Namespace, error)
|
||||||
|
DeleteByID(namespaceID uint64) error
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
func Namespace() NamespaceService {
|
||||||
|
return (&namespace{
|
||||||
|
prmSvc: DefaultPermissions,
|
||||||
|
}).With(context.Background())
|
||||||
|
}
|
||||||
|
|
||||||
|
func (svc *namespace) With(ctx context.Context) NamespaceService {
|
||||||
|
db := repository.DB(ctx)
|
||||||
|
return &namespace{
|
||||||
|
db: db,
|
||||||
|
ctx: ctx,
|
||||||
|
|
||||||
|
prmSvc: svc.prmSvc.With(ctx),
|
||||||
|
|
||||||
|
namespaceRepo: repository.Namespace(ctx, db),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (svc *namespace) FindByID(ID uint64) (n *types.Namespace, err error) {
|
||||||
|
if ID == 0 {
|
||||||
|
return nil, ErrInvalidID.withStack()
|
||||||
|
}
|
||||||
|
|
||||||
|
if n, err = svc.namespaceRepo.FindByID(ID); err != nil {
|
||||||
|
return
|
||||||
|
} else if !svc.prmSvc.CanReadNamespace(n) {
|
||||||
|
return nil, ErrNoReadPermissions.withStack()
|
||||||
|
}
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func (svc *namespace) Find(filter types.NamespaceFilter) (types.NamespaceSet, types.NamespaceFilter, error) {
|
||||||
|
nn, f, err := svc.namespaceRepo.Find(filter)
|
||||||
|
if err != nil {
|
||||||
|
return nil, f, err
|
||||||
|
}
|
||||||
|
|
||||||
|
nn, _ = nn.Filter(func(m *types.Namespace) (bool, error) {
|
||||||
|
return svc.prmSvc.CanReadNamespace(m), nil
|
||||||
|
})
|
||||||
|
|
||||||
|
return nn, f, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (svc *namespace) Create(mod *types.Namespace) (*types.Namespace, error) {
|
||||||
|
if !svc.prmSvc.CanCreateNamespace() {
|
||||||
|
return nil, ErrNoCreatePermissions.withStack()
|
||||||
|
}
|
||||||
|
|
||||||
|
return svc.namespaceRepo.Create(mod)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (svc *namespace) Update(updated *types.Namespace) (m *types.Namespace, err error) {
|
||||||
|
m, err = svc.FindByID(updated.ID)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
if isStale(updated.UpdatedAt, m.UpdatedAt, m.CreatedAt) {
|
||||||
|
return nil, ErrStaleData
|
||||||
|
}
|
||||||
|
|
||||||
|
if !svc.prmSvc.CanUpdateNamespace(m) {
|
||||||
|
return nil, ErrNoUpdatePermissions.withStack()
|
||||||
|
}
|
||||||
|
|
||||||
|
m.Name = updated.Name
|
||||||
|
m.Slug = updated.Slug
|
||||||
|
m.Meta = updated.Meta
|
||||||
|
m.Enabled = updated.Enabled
|
||||||
|
|
||||||
|
return svc.namespaceRepo.Update(m)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (svc *namespace) DeleteByID(ID uint64) error {
|
||||||
|
if m, err := svc.namespaceRepo.FindByID(ID); err != nil {
|
||||||
|
return err
|
||||||
|
} else if !svc.prmSvc.CanDeleteNamespace(m) {
|
||||||
|
return ErrNoDeletePermissions.withStack()
|
||||||
|
}
|
||||||
|
|
||||||
|
return svc.namespaceRepo.DeleteByID(ID)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Data is stale when new date does not match updatedAt or createdAt (before first update)
|
||||||
|
func isStale(new *time.Time, updatedAt *time.Time, createdAt time.Time) bool {
|
||||||
|
|
||||||
|
if new == nil {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
if updatedAt != nil {
|
||||||
|
return !new.Equal(*updatedAt)
|
||||||
|
}
|
||||||
|
|
||||||
|
return new.Equal(createdAt)
|
||||||
|
}
|
||||||
@@ -27,7 +27,10 @@ type (
|
|||||||
Effective() (ee []effectivePermission, err error)
|
Effective() (ee []effectivePermission, err error)
|
||||||
|
|
||||||
CanAccess() bool
|
CanAccess() bool
|
||||||
CanCreateNamspace() bool
|
CanCreateNamespace() bool
|
||||||
|
CanReadNamespace(r permissionResource) bool
|
||||||
|
CanUpdateNamespace(r permissionResource) bool
|
||||||
|
CanDeleteNamespace(r permissionResource) bool
|
||||||
CanCreateModule(r permissionResource) bool
|
CanCreateModule(r permissionResource) bool
|
||||||
CanReadModule(r permissionResource) bool
|
CanReadModule(r permissionResource) bool
|
||||||
CanUpdateModule(r permissionResource) bool
|
CanUpdateModule(r permissionResource) bool
|
||||||
@@ -95,7 +98,7 @@ func (p *permissions) Effective() (ee []effectivePermission, err error) {
|
|||||||
|
|
||||||
ee = append(ee, ep("compose", "access", p.CanAccess()))
|
ee = append(ee, ep("compose", "access", p.CanAccess()))
|
||||||
ee = append(ee, ep("compose", "grant", p.CanGrant()))
|
ee = append(ee, ep("compose", "grant", p.CanGrant()))
|
||||||
ee = append(ee, ep("compose", "namespace.create", p.CanCreateNamspace()))
|
ee = append(ee, ep("compose", "namespace.create", p.CanCreateNamespace()))
|
||||||
|
|
||||||
ee = append(ee, ep("compose:namespace:crm", "module.create", p.CanCreateModule(crmNamespace())))
|
ee = append(ee, ep("compose:namespace:crm", "module.create", p.CanCreateModule(crmNamespace())))
|
||||||
ee = append(ee, ep("compose:namespace:crm", "chart.create", p.CanCreateChart(crmNamespace())))
|
ee = append(ee, ep("compose:namespace:crm", "chart.create", p.CanCreateChart(crmNamespace())))
|
||||||
@@ -113,13 +116,24 @@ func (p *permissions) CanGrant() bool {
|
|||||||
return p.checkAccess(types.PermissionResource, "grant")
|
return p.checkAccess(types.PermissionResource, "grant")
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p *permissions) CanCreateNamspace() bool {
|
func (p *permissions) CanCreateNamespace() bool {
|
||||||
return p.checkAccess(types.PermissionResource, "namespace.create")
|
return p.checkAccess(types.PermissionResource, "namespace.create")
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p *permissions) CanCreateModule(ns permissionResource) bool {
|
func (p *permissions) CanReadNamespace(r permissionResource) bool {
|
||||||
// @todo move to func args when namespaces are implemented
|
return p.checkAccess(r, "read", p.allow())
|
||||||
return p.checkAccess(ns, "module.create")
|
}
|
||||||
|
|
||||||
|
func (p *permissions) CanUpdateNamespace(r permissionResource) bool {
|
||||||
|
return p.checkAccess(r, "update")
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *permissions) CanDeleteNamespace(r permissionResource) bool {
|
||||||
|
return p.checkAccess(r, "delete")
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *permissions) CanCreateModule(r permissionResource) bool {
|
||||||
|
return p.checkAccess(r, "module.create")
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p *permissions) CanReadModule(r permissionResource) bool {
|
func (p *permissions) CanReadModule(r permissionResource) bool {
|
||||||
@@ -206,3 +220,9 @@ func (p *permissions) checkAccess(resource permissionResource, operation string,
|
|||||||
}
|
}
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (p permissions) allow() func() internalRules.Access {
|
||||||
|
return func() internalRules.Access {
|
||||||
|
return internalRules.Allow
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -19,6 +19,7 @@ var (
|
|||||||
DefaultNotification NotificationService
|
DefaultNotification NotificationService
|
||||||
DefaultPermissions PermissionsService
|
DefaultPermissions PermissionsService
|
||||||
DefaultAttachment AttachmentService
|
DefaultAttachment AttachmentService
|
||||||
|
DefaultNamespace NamespaceService
|
||||||
)
|
)
|
||||||
|
|
||||||
func Init() error {
|
func Init() error {
|
||||||
@@ -35,6 +36,7 @@ func Init() error {
|
|||||||
DefaultChart = Chart()
|
DefaultChart = Chart()
|
||||||
DefaultNotification = Notification()
|
DefaultNotification = Notification()
|
||||||
DefaultAttachment = Attachment(fs)
|
DefaultAttachment = Attachment(fs)
|
||||||
|
DefaultNamespace = Namespace()
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -30,12 +30,6 @@ type (
|
|||||||
UpdatedAt *time.Time `json:"updatedAt,omitempty"`
|
UpdatedAt *time.Time `json:"updatedAt,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
file struct {
|
|
||||||
*types.Attachment
|
|
||||||
content io.ReadSeeker
|
|
||||||
download bool
|
|
||||||
}
|
|
||||||
|
|
||||||
Attachment struct {
|
Attachment struct {
|
||||||
attachment service.AttachmentService
|
attachment service.AttachmentService
|
||||||
}
|
}
|
||||||
@@ -176,23 +170,3 @@ func makeAttachmentPayload(a *types.Attachment, userID uint64) *attachmentPayloa
|
|||||||
UpdatedAt: a.UpdatedAt,
|
UpdatedAt: a.UpdatedAt,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (f *file) Download() bool {
|
|
||||||
return f.download
|
|
||||||
}
|
|
||||||
|
|
||||||
func (f *file) Name() string {
|
|
||||||
return f.Attachment.Name
|
|
||||||
}
|
|
||||||
|
|
||||||
func (f *file) ModTime() time.Time {
|
|
||||||
return f.Attachment.CreatedAt
|
|
||||||
}
|
|
||||||
|
|
||||||
func (f *file) Content() io.ReadSeeker {
|
|
||||||
return f.content
|
|
||||||
}
|
|
||||||
|
|
||||||
func (f *file) Valid() bool {
|
|
||||||
return f.content != nil
|
|
||||||
}
|
|
||||||
|
|||||||
161
compose/rest/handlers/namespace.go
Normal file
161
compose/rest/handlers/namespace.go
Normal file
@@ -0,0 +1,161 @@
|
|||||||
|
package handlers
|
||||||
|
|
||||||
|
/*
|
||||||
|
Hello! This file is auto-generated from `docs/src/spec.json`.
|
||||||
|
|
||||||
|
For development:
|
||||||
|
In order to update the generated files, edit this file under the location,
|
||||||
|
add your struct fields, imports, API definitions and whatever you want, and:
|
||||||
|
|
||||||
|
1. run [spec](https://github.com/titpetric/spec) in the same folder,
|
||||||
|
2. run `./_gen.php` in this folder.
|
||||||
|
|
||||||
|
You may edit `namespace.go`, `namespace.util.go` or `namespace_test.go` to
|
||||||
|
implement your API calls, helper functions and tests. The file `namespace.go`
|
||||||
|
is only generated the first time, and will not be overwritten if it exists.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
|
||||||
|
"net/http"
|
||||||
|
|
||||||
|
"github.com/go-chi/chi"
|
||||||
|
"github.com/titpetric/factory/resputil"
|
||||||
|
|
||||||
|
"github.com/crusttech/crust/compose/rest/request"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Internal API interface
|
||||||
|
type NamespaceAPI interface {
|
||||||
|
List(context.Context, *request.NamespaceList) (interface{}, error)
|
||||||
|
Create(context.Context, *request.NamespaceCreate) (interface{}, error)
|
||||||
|
Read(context.Context, *request.NamespaceRead) (interface{}, error)
|
||||||
|
Update(context.Context, *request.NamespaceUpdate) (interface{}, error)
|
||||||
|
Delete(context.Context, *request.NamespaceDelete) (interface{}, error)
|
||||||
|
}
|
||||||
|
|
||||||
|
// HTTP API interface
|
||||||
|
type Namespace struct {
|
||||||
|
List func(http.ResponseWriter, *http.Request)
|
||||||
|
Create func(http.ResponseWriter, *http.Request)
|
||||||
|
Read func(http.ResponseWriter, *http.Request)
|
||||||
|
Update func(http.ResponseWriter, *http.Request)
|
||||||
|
Delete func(http.ResponseWriter, *http.Request)
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewNamespace(nh NamespaceAPI) *Namespace {
|
||||||
|
return &Namespace{
|
||||||
|
List: func(w http.ResponseWriter, r *http.Request) {
|
||||||
|
defer r.Body.Close()
|
||||||
|
params := request.NewNamespaceList()
|
||||||
|
if err := params.Fill(r); err != nil {
|
||||||
|
resputil.JSON(w, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if value, err := nh.List(r.Context(), params); err != nil {
|
||||||
|
resputil.JSON(w, err)
|
||||||
|
return
|
||||||
|
} else {
|
||||||
|
switch fn := value.(type) {
|
||||||
|
case func(http.ResponseWriter, *http.Request):
|
||||||
|
fn(w, r)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
resputil.JSON(w, value)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
},
|
||||||
|
Create: func(w http.ResponseWriter, r *http.Request) {
|
||||||
|
defer r.Body.Close()
|
||||||
|
params := request.NewNamespaceCreate()
|
||||||
|
if err := params.Fill(r); err != nil {
|
||||||
|
resputil.JSON(w, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if value, err := nh.Create(r.Context(), params); err != nil {
|
||||||
|
resputil.JSON(w, err)
|
||||||
|
return
|
||||||
|
} else {
|
||||||
|
switch fn := value.(type) {
|
||||||
|
case func(http.ResponseWriter, *http.Request):
|
||||||
|
fn(w, r)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
resputil.JSON(w, value)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
},
|
||||||
|
Read: func(w http.ResponseWriter, r *http.Request) {
|
||||||
|
defer r.Body.Close()
|
||||||
|
params := request.NewNamespaceRead()
|
||||||
|
if err := params.Fill(r); err != nil {
|
||||||
|
resputil.JSON(w, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if value, err := nh.Read(r.Context(), params); err != nil {
|
||||||
|
resputil.JSON(w, err)
|
||||||
|
return
|
||||||
|
} else {
|
||||||
|
switch fn := value.(type) {
|
||||||
|
case func(http.ResponseWriter, *http.Request):
|
||||||
|
fn(w, r)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
resputil.JSON(w, value)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
},
|
||||||
|
Update: func(w http.ResponseWriter, r *http.Request) {
|
||||||
|
defer r.Body.Close()
|
||||||
|
params := request.NewNamespaceUpdate()
|
||||||
|
if err := params.Fill(r); err != nil {
|
||||||
|
resputil.JSON(w, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if value, err := nh.Update(r.Context(), params); err != nil {
|
||||||
|
resputil.JSON(w, err)
|
||||||
|
return
|
||||||
|
} else {
|
||||||
|
switch fn := value.(type) {
|
||||||
|
case func(http.ResponseWriter, *http.Request):
|
||||||
|
fn(w, r)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
resputil.JSON(w, value)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
},
|
||||||
|
Delete: func(w http.ResponseWriter, r *http.Request) {
|
||||||
|
defer r.Body.Close()
|
||||||
|
params := request.NewNamespaceDelete()
|
||||||
|
if err := params.Fill(r); err != nil {
|
||||||
|
resputil.JSON(w, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if value, err := nh.Delete(r.Context(), params); err != nil {
|
||||||
|
resputil.JSON(w, err)
|
||||||
|
return
|
||||||
|
} else {
|
||||||
|
switch fn := value.(type) {
|
||||||
|
case func(http.ResponseWriter, *http.Request):
|
||||||
|
fn(w, r)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
resputil.JSON(w, value)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (nh *Namespace) MountRoutes(r chi.Router, middlewares ...func(http.Handler) http.Handler) {
|
||||||
|
r.Group(func(r chi.Router) {
|
||||||
|
r.Use(middlewares...)
|
||||||
|
r.Get("/namespace/", nh.List)
|
||||||
|
r.Post("/namespace/", nh.Create)
|
||||||
|
r.Get("/namespace/{namespaceID}", nh.Read)
|
||||||
|
r.Post("/namespace/{namespaceID}", nh.Update)
|
||||||
|
r.Delete("/namespace/{namespaceID}", nh.Delete)
|
||||||
|
})
|
||||||
|
}
|
||||||
134
compose/rest/namespace.go
Normal file
134
compose/rest/namespace.go
Normal file
@@ -0,0 +1,134 @@
|
|||||||
|
package rest
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
|
||||||
|
"github.com/titpetric/factory/resputil"
|
||||||
|
|
||||||
|
"github.com/crusttech/crust/compose/internal/service"
|
||||||
|
"github.com/crusttech/crust/compose/rest/request"
|
||||||
|
"github.com/crusttech/crust/compose/types"
|
||||||
|
)
|
||||||
|
|
||||||
|
type (
|
||||||
|
namespacePayload struct {
|
||||||
|
*types.Namespace
|
||||||
|
|
||||||
|
CanUpdateNamespace bool `json:"canUpdateNamespace"`
|
||||||
|
CanDeleteNamespace bool `json:"canDeleteNamespace"`
|
||||||
|
CanCreateModule bool `json:"canCreateModule"`
|
||||||
|
CanCreateChart bool `json:"canCreateChart"`
|
||||||
|
CanCreateTrigger bool `json:"canCreateTrigger"`
|
||||||
|
CanCreatePage bool `json:"canCreatePage"`
|
||||||
|
}
|
||||||
|
|
||||||
|
namespaceSetPayload struct {
|
||||||
|
Filter types.NamespaceFilter `json:"filter"`
|
||||||
|
Set []*namespacePayload `json:"set"`
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
type Namespace struct {
|
||||||
|
namespace service.NamespaceService
|
||||||
|
permissions service.PermissionsService
|
||||||
|
}
|
||||||
|
|
||||||
|
func (Namespace) New() *Namespace {
|
||||||
|
return &Namespace{
|
||||||
|
namespace: service.DefaultNamespace,
|
||||||
|
permissions: service.DefaultPermissions,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (ctrl Namespace) List(ctx context.Context, r *request.NamespaceList) (interface{}, error) {
|
||||||
|
f := types.NamespaceFilter{
|
||||||
|
Query: r.Query,
|
||||||
|
PerPage: r.PerPage,
|
||||||
|
Page: r.Page,
|
||||||
|
}
|
||||||
|
|
||||||
|
nn, filter, err := ctrl.namespace.With(ctx).Find(f)
|
||||||
|
return ctrl.makeFilterPayload(ctx, nn, filter, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (ctrl Namespace) Create(ctx context.Context, r *request.NamespaceCreate) (interface{}, error) {
|
||||||
|
var err error
|
||||||
|
ns := &types.Namespace{
|
||||||
|
Name: r.Name,
|
||||||
|
Slug: r.Slug,
|
||||||
|
Meta: r.Meta,
|
||||||
|
Enabled: r.Enabled,
|
||||||
|
}
|
||||||
|
|
||||||
|
ns, err = ctrl.namespace.With(ctx).Create(ns)
|
||||||
|
return ctrl.makePayload(ctx, ns, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (ctrl Namespace) Read(ctx context.Context, r *request.NamespaceRead) (interface{}, error) {
|
||||||
|
ns, err := ctrl.namespace.With(ctx).FindByID(r.NamespaceID)
|
||||||
|
return ctrl.makePayload(ctx, ns, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (ctrl Namespace) Update(ctx context.Context, r *request.NamespaceUpdate) (interface{}, error) {
|
||||||
|
var (
|
||||||
|
ns = &types.Namespace{}
|
||||||
|
err error
|
||||||
|
)
|
||||||
|
|
||||||
|
ns.ID = r.NamespaceID
|
||||||
|
ns.Name = r.Name
|
||||||
|
ns.Slug = r.Slug
|
||||||
|
ns.Meta = r.Meta
|
||||||
|
ns.Enabled = r.Enabled
|
||||||
|
ns.UpdatedAt = r.UpdatedAt
|
||||||
|
|
||||||
|
ns, err = ctrl.namespace.With(ctx).Update(ns)
|
||||||
|
return ctrl.makePayload(ctx, ns, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (ctrl Namespace) Delete(ctx context.Context, r *request.NamespaceDelete) (interface{}, error) {
|
||||||
|
_, err := ctrl.namespace.With(ctx).FindByID(r.NamespaceID)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
err = ctrl.namespace.With(ctx).DeleteByID(r.NamespaceID)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
} else {
|
||||||
|
return resputil.Success("deleted"), nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (ctrl Namespace) makePayload(ctx context.Context, ns *types.Namespace, err error) (*namespacePayload, error) {
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
perm := ctrl.permissions.With(ctx)
|
||||||
|
|
||||||
|
return &namespacePayload{
|
||||||
|
Namespace: ns,
|
||||||
|
|
||||||
|
CanUpdateNamespace: perm.CanUpdateNamespace(ns),
|
||||||
|
CanDeleteNamespace: perm.CanDeleteNamespace(ns),
|
||||||
|
CanCreateModule: perm.CanCreateModule(ns),
|
||||||
|
CanCreateChart: perm.CanCreateChart(ns),
|
||||||
|
CanCreateTrigger: perm.CanCreateTrigger(ns),
|
||||||
|
CanCreatePage: perm.CanCreatePage(ns),
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (ctrl Namespace) makeFilterPayload(ctx context.Context, nn types.NamespaceSet, f types.NamespaceFilter, err error) (*namespaceSetPayload, error) {
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
nsp := &namespaceSetPayload{Filter: f, Set: make([]*namespacePayload, len(nn))}
|
||||||
|
|
||||||
|
for i := range nn {
|
||||||
|
nsp.Set[i], _ = ctrl.makePayload(ctx, nn[i], nil)
|
||||||
|
}
|
||||||
|
|
||||||
|
return nsp, nil
|
||||||
|
}
|
||||||
311
compose/rest/request/namespace.go
Normal file
311
compose/rest/request/namespace.go
Normal file
@@ -0,0 +1,311 @@
|
|||||||
|
package request
|
||||||
|
|
||||||
|
/*
|
||||||
|
Hello! This file is auto-generated from `docs/src/spec.json`.
|
||||||
|
|
||||||
|
For development:
|
||||||
|
In order to update the generated files, edit this file under the location,
|
||||||
|
add your struct fields, imports, API definitions and whatever you want, and:
|
||||||
|
|
||||||
|
1. run [spec](https://github.com/titpetric/spec) in the same folder,
|
||||||
|
2. run `./_gen.php` in this folder.
|
||||||
|
|
||||||
|
You may edit `namespace.go`, `namespace.util.go` or `namespace_test.go` to
|
||||||
|
implement your API calls, helper functions and tests. The file `namespace.go`
|
||||||
|
is only generated the first time, and will not be overwritten if it exists.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import (
|
||||||
|
"io"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"encoding/json"
|
||||||
|
"mime/multipart"
|
||||||
|
"net/http"
|
||||||
|
|
||||||
|
"github.com/go-chi/chi"
|
||||||
|
"github.com/pkg/errors"
|
||||||
|
|
||||||
|
sqlxTypes "github.com/jmoiron/sqlx/types"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
var _ = chi.URLParam
|
||||||
|
var _ = multipart.FileHeader{}
|
||||||
|
|
||||||
|
// Namespace list request parameters
|
||||||
|
type NamespaceList struct {
|
||||||
|
Query string
|
||||||
|
Page uint
|
||||||
|
PerPage uint
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewNamespaceList() *NamespaceList {
|
||||||
|
return &NamespaceList{}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (nReq *NamespaceList) Fill(r *http.Request) (err error) {
|
||||||
|
if strings.ToLower(r.Header.Get("content-type")) == "application/json" {
|
||||||
|
err = json.NewDecoder(r.Body).Decode(nReq)
|
||||||
|
|
||||||
|
switch {
|
||||||
|
case err == io.EOF:
|
||||||
|
err = nil
|
||||||
|
case err != nil:
|
||||||
|
return errors.Wrap(err, "error parsing http request body")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if err = r.ParseForm(); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
get := map[string]string{}
|
||||||
|
post := map[string]string{}
|
||||||
|
urlQuery := r.URL.Query()
|
||||||
|
for name, param := range urlQuery {
|
||||||
|
get[name] = string(param[0])
|
||||||
|
}
|
||||||
|
postVars := r.Form
|
||||||
|
for name, param := range postVars {
|
||||||
|
post[name] = string(param[0])
|
||||||
|
}
|
||||||
|
|
||||||
|
if val, ok := get["query"]; ok {
|
||||||
|
|
||||||
|
nReq.Query = val
|
||||||
|
}
|
||||||
|
if val, ok := get["page"]; ok {
|
||||||
|
|
||||||
|
nReq.Page = parseUint(val)
|
||||||
|
}
|
||||||
|
if val, ok := get["perPage"]; ok {
|
||||||
|
|
||||||
|
nReq.PerPage = parseUint(val)
|
||||||
|
}
|
||||||
|
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
var _ RequestFiller = NewNamespaceList()
|
||||||
|
|
||||||
|
// Namespace create request parameters
|
||||||
|
type NamespaceCreate struct {
|
||||||
|
Name string
|
||||||
|
Slug string
|
||||||
|
Enabled bool
|
||||||
|
Meta sqlxTypes.JSONText
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewNamespaceCreate() *NamespaceCreate {
|
||||||
|
return &NamespaceCreate{}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (nReq *NamespaceCreate) Fill(r *http.Request) (err error) {
|
||||||
|
if strings.ToLower(r.Header.Get("content-type")) == "application/json" {
|
||||||
|
err = json.NewDecoder(r.Body).Decode(nReq)
|
||||||
|
|
||||||
|
switch {
|
||||||
|
case err == io.EOF:
|
||||||
|
err = nil
|
||||||
|
case err != nil:
|
||||||
|
return errors.Wrap(err, "error parsing http request body")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if err = r.ParseForm(); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
get := map[string]string{}
|
||||||
|
post := map[string]string{}
|
||||||
|
urlQuery := r.URL.Query()
|
||||||
|
for name, param := range urlQuery {
|
||||||
|
get[name] = string(param[0])
|
||||||
|
}
|
||||||
|
postVars := r.Form
|
||||||
|
for name, param := range postVars {
|
||||||
|
post[name] = string(param[0])
|
||||||
|
}
|
||||||
|
|
||||||
|
if val, ok := post["name"]; ok {
|
||||||
|
|
||||||
|
nReq.Name = val
|
||||||
|
}
|
||||||
|
if val, ok := post["slug"]; ok {
|
||||||
|
|
||||||
|
nReq.Slug = val
|
||||||
|
}
|
||||||
|
if val, ok := post["enabled"]; ok {
|
||||||
|
|
||||||
|
nReq.Enabled = parseBool(val)
|
||||||
|
}
|
||||||
|
if val, ok := post["meta"]; ok {
|
||||||
|
|
||||||
|
if nReq.Meta, err = parseJSONTextWithErr(val); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
var _ RequestFiller = NewNamespaceCreate()
|
||||||
|
|
||||||
|
// Namespace read request parameters
|
||||||
|
type NamespaceRead struct {
|
||||||
|
NamespaceID uint64 `json:",string"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewNamespaceRead() *NamespaceRead {
|
||||||
|
return &NamespaceRead{}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (nReq *NamespaceRead) Fill(r *http.Request) (err error) {
|
||||||
|
if strings.ToLower(r.Header.Get("content-type")) == "application/json" {
|
||||||
|
err = json.NewDecoder(r.Body).Decode(nReq)
|
||||||
|
|
||||||
|
switch {
|
||||||
|
case err == io.EOF:
|
||||||
|
err = nil
|
||||||
|
case err != nil:
|
||||||
|
return errors.Wrap(err, "error parsing http request body")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if err = r.ParseForm(); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
get := map[string]string{}
|
||||||
|
post := map[string]string{}
|
||||||
|
urlQuery := r.URL.Query()
|
||||||
|
for name, param := range urlQuery {
|
||||||
|
get[name] = string(param[0])
|
||||||
|
}
|
||||||
|
postVars := r.Form
|
||||||
|
for name, param := range postVars {
|
||||||
|
post[name] = string(param[0])
|
||||||
|
}
|
||||||
|
|
||||||
|
nReq.NamespaceID = parseUInt64(chi.URLParam(r, "namespaceID"))
|
||||||
|
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
var _ RequestFiller = NewNamespaceRead()
|
||||||
|
|
||||||
|
// Namespace update request parameters
|
||||||
|
type NamespaceUpdate struct {
|
||||||
|
NamespaceID uint64 `json:",string"`
|
||||||
|
Name string
|
||||||
|
Slug string
|
||||||
|
Enabled bool
|
||||||
|
Meta sqlxTypes.JSONText
|
||||||
|
UpdatedAt *time.Time
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewNamespaceUpdate() *NamespaceUpdate {
|
||||||
|
return &NamespaceUpdate{}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (nReq *NamespaceUpdate) Fill(r *http.Request) (err error) {
|
||||||
|
if strings.ToLower(r.Header.Get("content-type")) == "application/json" {
|
||||||
|
err = json.NewDecoder(r.Body).Decode(nReq)
|
||||||
|
|
||||||
|
switch {
|
||||||
|
case err == io.EOF:
|
||||||
|
err = nil
|
||||||
|
case err != nil:
|
||||||
|
return errors.Wrap(err, "error parsing http request body")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if err = r.ParseForm(); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
get := map[string]string{}
|
||||||
|
post := map[string]string{}
|
||||||
|
urlQuery := r.URL.Query()
|
||||||
|
for name, param := range urlQuery {
|
||||||
|
get[name] = string(param[0])
|
||||||
|
}
|
||||||
|
postVars := r.Form
|
||||||
|
for name, param := range postVars {
|
||||||
|
post[name] = string(param[0])
|
||||||
|
}
|
||||||
|
|
||||||
|
nReq.NamespaceID = parseUInt64(chi.URLParam(r, "namespaceID"))
|
||||||
|
if val, ok := post["name"]; ok {
|
||||||
|
|
||||||
|
nReq.Name = val
|
||||||
|
}
|
||||||
|
if val, ok := post["slug"]; ok {
|
||||||
|
|
||||||
|
nReq.Slug = val
|
||||||
|
}
|
||||||
|
if val, ok := post["enabled"]; ok {
|
||||||
|
|
||||||
|
nReq.Enabled = parseBool(val)
|
||||||
|
}
|
||||||
|
if val, ok := post["meta"]; ok {
|
||||||
|
|
||||||
|
if nReq.Meta, err = parseJSONTextWithErr(val); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if val, ok := post["updatedAt"]; ok {
|
||||||
|
|
||||||
|
if nReq.UpdatedAt, err = parseISODatePtrWithErr(val); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
var _ RequestFiller = NewNamespaceUpdate()
|
||||||
|
|
||||||
|
// Namespace delete request parameters
|
||||||
|
type NamespaceDelete struct {
|
||||||
|
NamespaceID uint64 `json:",string"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewNamespaceDelete() *NamespaceDelete {
|
||||||
|
return &NamespaceDelete{}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (nReq *NamespaceDelete) Fill(r *http.Request) (err error) {
|
||||||
|
if strings.ToLower(r.Header.Get("content-type")) == "application/json" {
|
||||||
|
err = json.NewDecoder(r.Body).Decode(nReq)
|
||||||
|
|
||||||
|
switch {
|
||||||
|
case err == io.EOF:
|
||||||
|
err = nil
|
||||||
|
case err != nil:
|
||||||
|
return errors.Wrap(err, "error parsing http request body")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if err = r.ParseForm(); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
get := map[string]string{}
|
||||||
|
post := map[string]string{}
|
||||||
|
urlQuery := r.URL.Query()
|
||||||
|
for name, param := range urlQuery {
|
||||||
|
get[name] = string(param[0])
|
||||||
|
}
|
||||||
|
postVars := r.Form
|
||||||
|
for name, param := range postVars {
|
||||||
|
post[name] = string(param[0])
|
||||||
|
}
|
||||||
|
|
||||||
|
nReq.NamespaceID = parseUInt64(chi.URLParam(r, "namespaceID"))
|
||||||
|
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
var _ RequestFiller = NewNamespaceDelete()
|
||||||
@@ -4,6 +4,7 @@ import (
|
|||||||
"regexp"
|
"regexp"
|
||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
|
"time"
|
||||||
|
|
||||||
"github.com/jmoiron/sqlx/types"
|
"github.com/jmoiron/sqlx/types"
|
||||||
"github.com/pkg/errors"
|
"github.com/pkg/errors"
|
||||||
@@ -17,6 +18,19 @@ func parseJSONTextWithErr(s string) (types.JSONText, error) {
|
|||||||
return *result, err
|
return *result, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func parseISODateWithErr(s string) (time.Time, error) {
|
||||||
|
return time.Parse(time.RFC3339, s)
|
||||||
|
}
|
||||||
|
|
||||||
|
func parseISODatePtrWithErr(s string) (*time.Time, error) {
|
||||||
|
t, err := parseISODateWithErr(s)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return &t, nil
|
||||||
|
}
|
||||||
|
|
||||||
// parseInt parses a string to int
|
// parseInt parses a string to int
|
||||||
func parseInt(s string) int {
|
func parseInt(s string) int {
|
||||||
if s == "" {
|
if s == "" {
|
||||||
|
|||||||
@@ -9,6 +9,7 @@ import (
|
|||||||
|
|
||||||
func MountRoutes() func(chi.Router) {
|
func MountRoutes() func(chi.Router) {
|
||||||
var (
|
var (
|
||||||
|
namespace = Namespace{}.New()
|
||||||
module = Module{}.New()
|
module = Module{}.New()
|
||||||
record = Record{}.New()
|
record = Record{}.New()
|
||||||
page = Page{}.New()
|
page = Page{}.New()
|
||||||
@@ -16,8 +17,6 @@ func MountRoutes() func(chi.Router) {
|
|||||||
trigger = Trigger{}.New()
|
trigger = Trigger{}.New()
|
||||||
notification = Notification{}.New()
|
notification = Notification{}.New()
|
||||||
attachment = Attachment{}.New()
|
attachment = Attachment{}.New()
|
||||||
// pageAttachment = PageAttachment{}.New()
|
|
||||||
// recordAttachment = RecordAttachment{}.New()
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// Initialize handlers & controllers.
|
// Initialize handlers & controllers.
|
||||||
@@ -32,6 +31,7 @@ func MountRoutes() func(chi.Router) {
|
|||||||
r.Use(auth.MiddlewareValidOnly)
|
r.Use(auth.MiddlewareValidOnly)
|
||||||
r.Use(middlewareAllowedAccess)
|
r.Use(middlewareAllowedAccess)
|
||||||
|
|
||||||
|
handlers.NewNamespace(namespace).MountRoutes(r)
|
||||||
handlers.NewPage(page).MountRoutes(r)
|
handlers.NewPage(page).MountRoutes(r)
|
||||||
handlers.NewModule(module).MountRoutes(r)
|
handlers.NewModule(module).MountRoutes(r)
|
||||||
handlers.NewRecord(record).MountRoutes(r)
|
handlers.NewRecord(record).MountRoutes(r)
|
||||||
|
|||||||
67
compose/types/namespace.gen.go
Normal file
67
compose/types/namespace.gen.go
Normal file
@@ -0,0 +1,67 @@
|
|||||||
|
package types
|
||||||
|
|
||||||
|
// Hello! This file is auto-generated.
|
||||||
|
|
||||||
|
type (
|
||||||
|
|
||||||
|
// NamespaceSet slice of Namespace
|
||||||
|
//
|
||||||
|
// This type is auto-generated.
|
||||||
|
NamespaceSet []*Namespace
|
||||||
|
)
|
||||||
|
|
||||||
|
// Walk iterates through every slice item and calls w(Namespace) err
|
||||||
|
//
|
||||||
|
// This function is auto-generated.
|
||||||
|
func (set NamespaceSet) Walk(w func(*Namespace) error) (err error) {
|
||||||
|
for i := range set {
|
||||||
|
if err = w(set[i]); err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Filter iterates through every slice item, calls f(Namespace) (bool, err) and return filtered slice
|
||||||
|
//
|
||||||
|
// This function is auto-generated.
|
||||||
|
func (set NamespaceSet) Filter(f func(*Namespace) (bool, error)) (out NamespaceSet, err error) {
|
||||||
|
var ok bool
|
||||||
|
out = NamespaceSet{}
|
||||||
|
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 NamespaceSet) FindByID(ID uint64) *Namespace {
|
||||||
|
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 NamespaceSet) IDs() (IDs []uint64) {
|
||||||
|
IDs = make([]uint64, len(set))
|
||||||
|
|
||||||
|
for i := range set {
|
||||||
|
IDs[i] = set[i].ID
|
||||||
|
}
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
@@ -1,12 +1,32 @@
|
|||||||
package types
|
package types
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/jmoiron/sqlx/types"
|
||||||
|
|
||||||
"github.com/crusttech/crust/internal/rules"
|
"github.com/crusttech/crust/internal/rules"
|
||||||
)
|
)
|
||||||
|
|
||||||
type (
|
type (
|
||||||
Namespace struct {
|
Namespace struct {
|
||||||
ID uint64 `json:"id,string" db:"id"`
|
ID uint64 `json:"namespaceID,string" db:"id"`
|
||||||
|
Name string `json:"name" db:"name"`
|
||||||
|
Slug string `json:"slug" db:"slug"`
|
||||||
|
Enabled bool `json:"enabled" db:"enabled"`
|
||||||
|
Meta types.JSONText `json:"meta" db:"meta"`
|
||||||
|
|
||||||
|
CreatedAt time.Time `json:"createdAt,omitempty" db:"created_at"`
|
||||||
|
UpdatedAt *time.Time `json:"updatedAt,omitempty" db:"updated_at"`
|
||||||
|
DeletedAt *time.Time `json:"deletedAt,omitempty" db:"deleted_at"`
|
||||||
|
}
|
||||||
|
|
||||||
|
NamespaceFilter struct {
|
||||||
|
Query string `json:"query"`
|
||||||
|
Page uint `json:"page"`
|
||||||
|
PerPage uint `json:"perPage"`
|
||||||
|
Sort string `json:"sort"`
|
||||||
|
Count uint `json:"count"`
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|||||||
788
docs/compose/README.md
Normal file
788
docs/compose/README.md
Normal file
@@ -0,0 +1,788 @@
|
|||||||
|
# Attachments
|
||||||
|
|
||||||
|
| Method | Endpoint | Purpose |
|
||||||
|
| ------ | -------- | ------- |
|
||||||
|
| `GET` | `/attachment/{kind}/` | List, filter all page attachments |
|
||||||
|
| `GET` | `/attachment/{kind}/{attachmentID}` | Attachment details |
|
||||||
|
| `GET` | `/attachment/{kind}/{attachmentID}/original/{name}` | Serves attached file |
|
||||||
|
| `GET` | `/attachment/{kind}/{attachmentID}/preview.{ext}` | Serves preview of an attached file |
|
||||||
|
|
||||||
|
## List, filter all page attachments
|
||||||
|
|
||||||
|
#### Method
|
||||||
|
|
||||||
|
| URI | Protocol | Method | Authentication |
|
||||||
|
| --- | -------- | ------ | -------------- |
|
||||||
|
| `/attachment/{kind}/` | HTTP/S | GET | Client ID, Session ID |
|
||||||
|
|
||||||
|
#### Request parameters
|
||||||
|
|
||||||
|
| Parameter | Type | Method | Description | Default | Required? |
|
||||||
|
| --------- | ---- | ------ | ----------- | ------- | --------- |
|
||||||
|
| pageID | uint64 | GET | Filter attachments by page ID | N/A | NO |
|
||||||
|
| moduleID | uint64 | GET | Filter attachments by mnodule ID | N/A | NO |
|
||||||
|
| recordID | uint64 | GET | Filter attachments by record ID | N/A | NO |
|
||||||
|
| fieldName | string | GET | Filter attachments by field name | N/A | NO |
|
||||||
|
| page | uint | GET | Page number (0 based) | N/A | NO |
|
||||||
|
| perPage | uint | GET | Returned items per page (default 50) | N/A | NO |
|
||||||
|
| sign | string | GET | Signature | N/A | YES |
|
||||||
|
| userID | uint64 | GET | User ID | N/A | YES |
|
||||||
|
| kind | string | PATH | Attachment kind | N/A | YES |
|
||||||
|
|
||||||
|
## Attachment details
|
||||||
|
|
||||||
|
#### Method
|
||||||
|
|
||||||
|
| URI | Protocol | Method | Authentication |
|
||||||
|
| --- | -------- | ------ | -------------- |
|
||||||
|
| `/attachment/{kind}/{attachmentID}` | HTTP/S | GET | Client ID, Session ID |
|
||||||
|
|
||||||
|
#### Request parameters
|
||||||
|
|
||||||
|
| Parameter | Type | Method | Description | Default | Required? |
|
||||||
|
| --------- | ---- | ------ | ----------- | ------- | --------- |
|
||||||
|
| attachmentID | uint64 | PATH | Attachment ID | N/A | YES |
|
||||||
|
| kind | string | PATH | Attachment kind | N/A | YES |
|
||||||
|
| sign | string | GET | Signature | N/A | YES |
|
||||||
|
| userID | uint64 | GET | User ID | N/A | YES |
|
||||||
|
|
||||||
|
## Serves attached file
|
||||||
|
|
||||||
|
#### Method
|
||||||
|
|
||||||
|
| URI | Protocol | Method | Authentication |
|
||||||
|
| --- | -------- | ------ | -------------- |
|
||||||
|
| `/attachment/{kind}/{attachmentID}/original/{name}` | HTTP/S | GET | Client ID, Session ID |
|
||||||
|
|
||||||
|
#### Request parameters
|
||||||
|
|
||||||
|
| Parameter | Type | Method | Description | Default | Required? |
|
||||||
|
| --------- | ---- | ------ | ----------- | ------- | --------- |
|
||||||
|
| download | bool | GET | Force file download | N/A | NO |
|
||||||
|
| sign | string | GET | Signature | N/A | YES |
|
||||||
|
| userID | uint64 | GET | User ID | N/A | YES |
|
||||||
|
| attachmentID | uint64 | PATH | Attachment ID | N/A | YES |
|
||||||
|
| name | string | PATH | File name | N/A | YES |
|
||||||
|
| kind | string | PATH | Attachment kind | N/A | YES |
|
||||||
|
|
||||||
|
## Serves preview of an attached file
|
||||||
|
|
||||||
|
#### Method
|
||||||
|
|
||||||
|
| URI | Protocol | Method | Authentication |
|
||||||
|
| --- | -------- | ------ | -------------- |
|
||||||
|
| `/attachment/{kind}/{attachmentID}/preview.{ext}` | HTTP/S | GET | Client ID, Session ID |
|
||||||
|
|
||||||
|
#### Request parameters
|
||||||
|
|
||||||
|
| Parameter | Type | Method | Description | Default | Required? |
|
||||||
|
| --------- | ---- | ------ | ----------- | ------- | --------- |
|
||||||
|
| attachmentID | uint64 | PATH | Attachment ID | N/A | YES |
|
||||||
|
| ext | string | PATH | Preview extension/format | N/A | YES |
|
||||||
|
| kind | string | PATH | Attachment kind | N/A | YES |
|
||||||
|
| sign | string | GET | Signature | N/A | YES |
|
||||||
|
| userID | uint64 | GET | User ID | N/A | YES |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
# Charts
|
||||||
|
|
||||||
|
| Method | Endpoint | Purpose |
|
||||||
|
| ------ | -------- | ------- |
|
||||||
|
| `GET` | `/chart/` | List/read charts from module section |
|
||||||
|
| `POST` | `/chart/` | List/read charts from module section |
|
||||||
|
| `GET` | `/chart/{chartID}` | Read charts by ID from module section |
|
||||||
|
| `POST` | `/chart/{chartID}` | Add/update charts in module section |
|
||||||
|
| `DELETE` | `/chart/{chartID}` | Delete chart |
|
||||||
|
|
||||||
|
## List/read charts from module section
|
||||||
|
|
||||||
|
#### Method
|
||||||
|
|
||||||
|
| URI | Protocol | Method | Authentication |
|
||||||
|
| --- | -------- | ------ | -------------- |
|
||||||
|
| `/chart/` | HTTP/S | GET | |
|
||||||
|
|
||||||
|
#### Request parameters
|
||||||
|
|
||||||
|
| Parameter | Type | Method | Description | Default | Required? |
|
||||||
|
| --------- | ---- | ------ | ----------- | ------- | --------- |
|
||||||
|
|
||||||
|
## List/read charts from module section
|
||||||
|
|
||||||
|
#### Method
|
||||||
|
|
||||||
|
| URI | Protocol | Method | Authentication |
|
||||||
|
| --- | -------- | ------ | -------------- |
|
||||||
|
| `/chart/` | HTTP/S | POST | |
|
||||||
|
|
||||||
|
#### Request parameters
|
||||||
|
|
||||||
|
| Parameter | Type | Method | Description | Default | Required? |
|
||||||
|
| --------- | ---- | ------ | ----------- | ------- | --------- |
|
||||||
|
| config | sqlxTypes.JSONText | POST | Chart JSON | N/A | YES |
|
||||||
|
| name | string | POST | Chart name | N/A | YES |
|
||||||
|
|
||||||
|
## Read charts by ID from module section
|
||||||
|
|
||||||
|
#### Method
|
||||||
|
|
||||||
|
| URI | Protocol | Method | Authentication |
|
||||||
|
| --- | -------- | ------ | -------------- |
|
||||||
|
| `/chart/{chartID}` | HTTP/S | GET | |
|
||||||
|
|
||||||
|
#### Request parameters
|
||||||
|
|
||||||
|
| Parameter | Type | Method | Description | Default | Required? |
|
||||||
|
| --------- | ---- | ------ | ----------- | ------- | --------- |
|
||||||
|
| chartID | uint64 | PATH | Chart ID | N/A | YES |
|
||||||
|
|
||||||
|
## Add/update charts in module section
|
||||||
|
|
||||||
|
#### Method
|
||||||
|
|
||||||
|
| URI | Protocol | Method | Authentication |
|
||||||
|
| --- | -------- | ------ | -------------- |
|
||||||
|
| `/chart/{chartID}` | HTTP/S | POST | |
|
||||||
|
|
||||||
|
#### Request parameters
|
||||||
|
|
||||||
|
| Parameter | Type | Method | Description | Default | Required? |
|
||||||
|
| --------- | ---- | ------ | ----------- | ------- | --------- |
|
||||||
|
| chartID | uint64 | PATH | Chart ID | N/A | YES |
|
||||||
|
| config | sqlxTypes.JSONText | POST | Chart JSON | N/A | YES |
|
||||||
|
| name | string | POST | Chart name | N/A | YES |
|
||||||
|
|
||||||
|
## Delete chart
|
||||||
|
|
||||||
|
#### Method
|
||||||
|
|
||||||
|
| URI | Protocol | Method | Authentication |
|
||||||
|
| --- | -------- | ------ | -------------- |
|
||||||
|
| `/chart/{chartID}` | HTTP/S | DELETE | |
|
||||||
|
|
||||||
|
#### Request parameters
|
||||||
|
|
||||||
|
| Parameter | Type | Method | Description | Default | Required? |
|
||||||
|
| --------- | ---- | ------ | ----------- | ------- | --------- |
|
||||||
|
| chartID | uint64 | PATH | Chart ID | N/A | YES |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
# Modules
|
||||||
|
|
||||||
|
Compose module definitions
|
||||||
|
|
||||||
|
| Method | Endpoint | Purpose |
|
||||||
|
| ------ | -------- | ------- |
|
||||||
|
| `GET` | `/module/` | List modules |
|
||||||
|
| `POST` | `/module/` | Create module |
|
||||||
|
| `GET` | `/module/{moduleID}` | Read module |
|
||||||
|
| `POST` | `/module/{moduleID}` | Update module |
|
||||||
|
| `DELETE` | `/module/{moduleID}` | Delete module |
|
||||||
|
|
||||||
|
## List modules
|
||||||
|
|
||||||
|
#### Method
|
||||||
|
|
||||||
|
| URI | Protocol | Method | Authentication |
|
||||||
|
| --- | -------- | ------ | -------------- |
|
||||||
|
| `/module/` | HTTP/S | GET | |
|
||||||
|
|
||||||
|
#### Request parameters
|
||||||
|
|
||||||
|
| Parameter | Type | Method | Description | Default | Required? |
|
||||||
|
| --------- | ---- | ------ | ----------- | ------- | --------- |
|
||||||
|
| query | string | GET | Search query | N/A | NO |
|
||||||
|
|
||||||
|
## Create module
|
||||||
|
|
||||||
|
#### Method
|
||||||
|
|
||||||
|
| URI | Protocol | Method | Authentication |
|
||||||
|
| --- | -------- | ------ | -------------- |
|
||||||
|
| `/module/` | HTTP/S | POST | |
|
||||||
|
|
||||||
|
#### Request parameters
|
||||||
|
|
||||||
|
| Parameter | Type | Method | Description | Default | Required? |
|
||||||
|
| --------- | ---- | ------ | ----------- | ------- | --------- |
|
||||||
|
| name | string | POST | Module Name | N/A | YES |
|
||||||
|
| fields | types.ModuleFieldSet | POST | Fields JSON | N/A | YES |
|
||||||
|
| meta | sqlxTypes.JSONText | POST | Module meta data | N/A | YES |
|
||||||
|
|
||||||
|
## Read module
|
||||||
|
|
||||||
|
#### Method
|
||||||
|
|
||||||
|
| URI | Protocol | Method | Authentication |
|
||||||
|
| --- | -------- | ------ | -------------- |
|
||||||
|
| `/module/{moduleID}` | HTTP/S | GET | |
|
||||||
|
|
||||||
|
#### Request parameters
|
||||||
|
|
||||||
|
| Parameter | Type | Method | Description | Default | Required? |
|
||||||
|
| --------- | ---- | ------ | ----------- | ------- | --------- |
|
||||||
|
| moduleID | uint64 | PATH | Module ID | N/A | YES |
|
||||||
|
|
||||||
|
## Update module
|
||||||
|
|
||||||
|
#### Method
|
||||||
|
|
||||||
|
| URI | Protocol | Method | Authentication |
|
||||||
|
| --- | -------- | ------ | -------------- |
|
||||||
|
| `/module/{moduleID}` | HTTP/S | POST | |
|
||||||
|
|
||||||
|
#### Request parameters
|
||||||
|
|
||||||
|
| Parameter | Type | Method | Description | Default | Required? |
|
||||||
|
| --------- | ---- | ------ | ----------- | ------- | --------- |
|
||||||
|
| moduleID | uint64 | PATH | Module ID | N/A | YES |
|
||||||
|
| name | string | POST | Module Name | N/A | YES |
|
||||||
|
| fields | types.ModuleFieldSet | POST | Fields JSON | N/A | YES |
|
||||||
|
| meta | sqlxTypes.JSONText | POST | Module meta data | N/A | YES |
|
||||||
|
|
||||||
|
## Delete module
|
||||||
|
|
||||||
|
#### Method
|
||||||
|
|
||||||
|
| URI | Protocol | Method | Authentication |
|
||||||
|
| --- | -------- | ------ | -------------- |
|
||||||
|
| `/module/{moduleID}` | HTTP/S | DELETE | |
|
||||||
|
|
||||||
|
#### Request parameters
|
||||||
|
|
||||||
|
| Parameter | Type | Method | Description | Default | Required? |
|
||||||
|
| --------- | ---- | ------ | ----------- | ------- | --------- |
|
||||||
|
| moduleID | uint64 | PATH | Module ID | N/A | YES |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
# Namespaces
|
||||||
|
|
||||||
|
| Method | Endpoint | Purpose |
|
||||||
|
| ------ | -------- | ------- |
|
||||||
|
| `GET` | `/namespace/` | List namespaces |
|
||||||
|
| `POST` | `/namespace/` | Create namespace |
|
||||||
|
| `GET` | `/namespace/{namespaceID}` | Read namespace |
|
||||||
|
| `POST` | `/namespace/{namespaceID}` | Update namespace |
|
||||||
|
| `DELETE` | `/namespace/{namespaceID}` | Delete namespace |
|
||||||
|
|
||||||
|
## List namespaces
|
||||||
|
|
||||||
|
#### Method
|
||||||
|
|
||||||
|
| URI | Protocol | Method | Authentication |
|
||||||
|
| --- | -------- | ------ | -------------- |
|
||||||
|
| `/namespace/` | HTTP/S | GET | |
|
||||||
|
|
||||||
|
#### Request parameters
|
||||||
|
|
||||||
|
| Parameter | Type | Method | Description | Default | Required? |
|
||||||
|
| --------- | ---- | ------ | ----------- | ------- | --------- |
|
||||||
|
| query | string | GET | Search query | N/A | NO |
|
||||||
|
| page | uint | GET | Page number (0 based) | N/A | NO |
|
||||||
|
| perPage | uint | GET | Returned items per page (default 50) | N/A | NO |
|
||||||
|
|
||||||
|
## Create namespace
|
||||||
|
|
||||||
|
#### Method
|
||||||
|
|
||||||
|
| URI | Protocol | Method | Authentication |
|
||||||
|
| --- | -------- | ------ | -------------- |
|
||||||
|
| `/namespace/` | HTTP/S | POST | |
|
||||||
|
|
||||||
|
#### Request parameters
|
||||||
|
|
||||||
|
| Parameter | Type | Method | Description | Default | Required? |
|
||||||
|
| --------- | ---- | ------ | ----------- | ------- | --------- |
|
||||||
|
| name | string | POST | Name | N/A | YES |
|
||||||
|
| slug | string | POST | Slug (url path part) | N/A | YES |
|
||||||
|
| enabled | bool | POST | Enabled | N/A | YES |
|
||||||
|
| meta | sqlxTypes.JSONText | POST | Meta data | N/A | YES |
|
||||||
|
|
||||||
|
## Read namespace
|
||||||
|
|
||||||
|
#### Method
|
||||||
|
|
||||||
|
| URI | Protocol | Method | Authentication |
|
||||||
|
| --- | -------- | ------ | -------------- |
|
||||||
|
| `/namespace/{namespaceID}` | HTTP/S | GET | |
|
||||||
|
|
||||||
|
#### Request parameters
|
||||||
|
|
||||||
|
| Parameter | Type | Method | Description | Default | Required? |
|
||||||
|
| --------- | ---- | ------ | ----------- | ------- | --------- |
|
||||||
|
| namespaceID | uint64 | PATH | ID | N/A | YES |
|
||||||
|
|
||||||
|
## Update namespace
|
||||||
|
|
||||||
|
#### Method
|
||||||
|
|
||||||
|
| URI | Protocol | Method | Authentication |
|
||||||
|
| --- | -------- | ------ | -------------- |
|
||||||
|
| `/namespace/{namespaceID}` | HTTP/S | POST | |
|
||||||
|
|
||||||
|
#### Request parameters
|
||||||
|
|
||||||
|
| Parameter | Type | Method | Description | Default | Required? |
|
||||||
|
| --------- | ---- | ------ | ----------- | ------- | --------- |
|
||||||
|
| namespaceID | uint64 | PATH | ID | N/A | YES |
|
||||||
|
| name | string | POST | Name | N/A | YES |
|
||||||
|
| slug | string | POST | Slug (url path part) | N/A | YES |
|
||||||
|
| enabled | bool | POST | Enabled | N/A | YES |
|
||||||
|
| meta | sqlxTypes.JSONText | POST | Meta data | N/A | YES |
|
||||||
|
| updatedAt | *time.Time | POST | Last update (or creation) date | N/A | YES |
|
||||||
|
|
||||||
|
## Delete namespace
|
||||||
|
|
||||||
|
#### Method
|
||||||
|
|
||||||
|
| URI | Protocol | Method | Authentication |
|
||||||
|
| --- | -------- | ------ | -------------- |
|
||||||
|
| `/namespace/{namespaceID}` | HTTP/S | DELETE | |
|
||||||
|
|
||||||
|
#### Request parameters
|
||||||
|
|
||||||
|
| Parameter | Type | Method | Description | Default | Required? |
|
||||||
|
| --------- | ---- | ------ | ----------- | ------- | --------- |
|
||||||
|
| namespaceID | uint64 | PATH | ID | N/A | YES |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
# Notifications
|
||||||
|
|
||||||
|
Compose Notifications
|
||||||
|
|
||||||
|
| Method | Endpoint | Purpose |
|
||||||
|
| ------ | -------- | ------- |
|
||||||
|
| `POST` | `/notification/email` | Send email from the Compose |
|
||||||
|
|
||||||
|
## Send email from the Compose
|
||||||
|
|
||||||
|
#### Method
|
||||||
|
|
||||||
|
| URI | Protocol | Method | Authentication |
|
||||||
|
| --- | -------- | ------ | -------------- |
|
||||||
|
| `/notification/email` | HTTP/S | POST | |
|
||||||
|
|
||||||
|
#### Request parameters
|
||||||
|
|
||||||
|
| Parameter | Type | Method | Description | Default | Required? |
|
||||||
|
| --------- | ---- | ------ | ----------- | ------- | --------- |
|
||||||
|
| to | []string | POST | Email addresses or Crust user IDs | N/A | YES |
|
||||||
|
| cc | []string | POST | Email addresses or Crust user IDs | N/A | NO |
|
||||||
|
| replyTo | string | POST | Crust user ID or email address in reply-to field | N/A | NO |
|
||||||
|
| subject | string | POST | Email subject | N/A | NO |
|
||||||
|
| content | sqlxTypes.JSONText | POST | Message content | N/A | YES |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
# Pages
|
||||||
|
|
||||||
|
Compose module pages
|
||||||
|
|
||||||
|
| Method | Endpoint | Purpose |
|
||||||
|
| ------ | -------- | ------- |
|
||||||
|
| `GET` | `/page/` | List available pages |
|
||||||
|
| `POST` | `/page/` | Create page |
|
||||||
|
| `GET` | `/page/{pageID}` | Get page details |
|
||||||
|
| `GET` | `/page/tree` | Get page all (non-record) pages, hierarchically |
|
||||||
|
| `POST` | `/page/{pageID}` | Update page |
|
||||||
|
| `POST` | `/page/{selfID}/reorder` | Reorder pages |
|
||||||
|
| `Delete` | `/page/{pageID}` | Delete page |
|
||||||
|
| `POST` | `/page/{pageID}/attachment` | Uploads attachment to page |
|
||||||
|
|
||||||
|
## List available pages
|
||||||
|
|
||||||
|
#### Method
|
||||||
|
|
||||||
|
| URI | Protocol | Method | Authentication |
|
||||||
|
| --- | -------- | ------ | -------------- |
|
||||||
|
| `/page/` | HTTP/S | GET | |
|
||||||
|
|
||||||
|
#### Request parameters
|
||||||
|
|
||||||
|
| Parameter | Type | Method | Description | Default | Required? |
|
||||||
|
| --------- | ---- | ------ | ----------- | ------- | --------- |
|
||||||
|
| selfID | uint64 | GET | Parent page ID | N/A | NO |
|
||||||
|
|
||||||
|
## Create page
|
||||||
|
|
||||||
|
#### Method
|
||||||
|
|
||||||
|
| URI | Protocol | Method | Authentication |
|
||||||
|
| --- | -------- | ------ | -------------- |
|
||||||
|
| `/page/` | HTTP/S | POST | |
|
||||||
|
|
||||||
|
#### Request parameters
|
||||||
|
|
||||||
|
| Parameter | Type | Method | Description | Default | Required? |
|
||||||
|
| --------- | ---- | ------ | ----------- | ------- | --------- |
|
||||||
|
| selfID | uint64 | POST | Parent Page ID | N/A | NO |
|
||||||
|
| moduleID | uint64 | POST | Module ID | N/A | NO |
|
||||||
|
| title | string | POST | Title | N/A | YES |
|
||||||
|
| description | string | POST | Description | N/A | NO |
|
||||||
|
| visible | bool | POST | Visible in navigation | N/A | NO |
|
||||||
|
| blocks | sqlxTypes.JSONText | POST | Blocks JSON | N/A | YES |
|
||||||
|
|
||||||
|
## Get page details
|
||||||
|
|
||||||
|
#### Method
|
||||||
|
|
||||||
|
| URI | Protocol | Method | Authentication |
|
||||||
|
| --- | -------- | ------ | -------------- |
|
||||||
|
| `/page/{pageID}` | HTTP/S | GET | |
|
||||||
|
|
||||||
|
#### Request parameters
|
||||||
|
|
||||||
|
| Parameter | Type | Method | Description | Default | Required? |
|
||||||
|
| --------- | ---- | ------ | ----------- | ------- | --------- |
|
||||||
|
| pageID | uint64 | PATH | Page ID | N/A | YES |
|
||||||
|
|
||||||
|
## Get page all (non-record) pages, hierarchically
|
||||||
|
|
||||||
|
#### Method
|
||||||
|
|
||||||
|
| URI | Protocol | Method | Authentication |
|
||||||
|
| --- | -------- | ------ | -------------- |
|
||||||
|
| `/page/tree` | HTTP/S | GET | |
|
||||||
|
|
||||||
|
#### Request parameters
|
||||||
|
|
||||||
|
| Parameter | Type | Method | Description | Default | Required? |
|
||||||
|
| --------- | ---- | ------ | ----------- | ------- | --------- |
|
||||||
|
|
||||||
|
## Update page
|
||||||
|
|
||||||
|
#### Method
|
||||||
|
|
||||||
|
| URI | Protocol | Method | Authentication |
|
||||||
|
| --- | -------- | ------ | -------------- |
|
||||||
|
| `/page/{pageID}` | HTTP/S | POST | |
|
||||||
|
|
||||||
|
#### Request parameters
|
||||||
|
|
||||||
|
| Parameter | Type | Method | Description | Default | Required? |
|
||||||
|
| --------- | ---- | ------ | ----------- | ------- | --------- |
|
||||||
|
| pageID | uint64 | PATH | Page ID | N/A | YES |
|
||||||
|
| selfID | uint64 | POST | Parent Page ID | N/A | NO |
|
||||||
|
| moduleID | uint64 | POST | Module ID (optional) | N/A | NO |
|
||||||
|
| title | string | POST | Title | N/A | YES |
|
||||||
|
| description | string | POST | Description | N/A | NO |
|
||||||
|
| visible | bool | POST | Visible in navigation | N/A | NO |
|
||||||
|
| blocks | sqlxTypes.JSONText | POST | Blocks JSON | N/A | YES |
|
||||||
|
|
||||||
|
## Reorder pages
|
||||||
|
|
||||||
|
#### Method
|
||||||
|
|
||||||
|
| URI | Protocol | Method | Authentication |
|
||||||
|
| --- | -------- | ------ | -------------- |
|
||||||
|
| `/page/{selfID}/reorder` | HTTP/S | POST | |
|
||||||
|
|
||||||
|
#### Request parameters
|
||||||
|
|
||||||
|
| Parameter | Type | Method | Description | Default | Required? |
|
||||||
|
| --------- | ---- | ------ | ----------- | ------- | --------- |
|
||||||
|
| selfID | uint64 | PATH | Parent page ID | N/A | YES |
|
||||||
|
| pageIDs | []string | POST | Page ID order | N/A | YES |
|
||||||
|
|
||||||
|
## Delete page
|
||||||
|
|
||||||
|
#### Method
|
||||||
|
|
||||||
|
| URI | Protocol | Method | Authentication |
|
||||||
|
| --- | -------- | ------ | -------------- |
|
||||||
|
| `/page/{pageID}` | HTTP/S | Delete | |
|
||||||
|
|
||||||
|
#### Request parameters
|
||||||
|
|
||||||
|
| Parameter | Type | Method | Description | Default | Required? |
|
||||||
|
| --------- | ---- | ------ | ----------- | ------- | --------- |
|
||||||
|
| pageID | uint64 | PATH | Page ID | N/A | YES |
|
||||||
|
|
||||||
|
## Uploads attachment to page
|
||||||
|
|
||||||
|
#### Method
|
||||||
|
|
||||||
|
| URI | Protocol | Method | Authentication |
|
||||||
|
| --- | -------- | ------ | -------------- |
|
||||||
|
| `/page/{pageID}/attachment` | HTTP/S | POST | |
|
||||||
|
|
||||||
|
#### Request parameters
|
||||||
|
|
||||||
|
| Parameter | Type | Method | Description | Default | Required? |
|
||||||
|
| --------- | ---- | ------ | ----------- | ------- | --------- |
|
||||||
|
| pageID | uint64 | PATH | Page ID | N/A | YES |
|
||||||
|
| upload | *multipart.FileHeader | POST | File to upload | N/A | YES |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
# Permissions
|
||||||
|
|
||||||
|
| Method | Endpoint | Purpose |
|
||||||
|
| ------ | -------- | ------- |
|
||||||
|
| `GET` | `/permissions/effective` | Effective rules for current user |
|
||||||
|
|
||||||
|
## Effective rules for current user
|
||||||
|
|
||||||
|
#### Method
|
||||||
|
|
||||||
|
| URI | Protocol | Method | Authentication |
|
||||||
|
| --- | -------- | ------ | -------------- |
|
||||||
|
| `/permissions/effective` | HTTP/S | GET | |
|
||||||
|
|
||||||
|
#### Request parameters
|
||||||
|
|
||||||
|
| Parameter | Type | Method | Description | Default | Required? |
|
||||||
|
| --------- | ---- | ------ | ----------- | ------- | --------- |
|
||||||
|
| resource | string | GET | Show only rules for a specific resource | N/A | NO |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
# Records
|
||||||
|
|
||||||
|
Compose records
|
||||||
|
|
||||||
|
| Method | Endpoint | Purpose |
|
||||||
|
| ------ | -------- | ------- |
|
||||||
|
| `GET` | `/module/{moduleID}/record/report` | Generates report from module records |
|
||||||
|
| `GET` | `/module/{moduleID}/record/` | List/read records from module section |
|
||||||
|
| `POST` | `/module/{moduleID}/record/` | Create record in module section |
|
||||||
|
| `GET` | `/module/{moduleID}/record/{recordID}` | Read records by ID from module section |
|
||||||
|
| `POST` | `/module/{moduleID}/record/{recordID}` | Update records in module section |
|
||||||
|
| `DELETE` | `/module/{moduleID}/record/{recordID}` | Delete record row from module section |
|
||||||
|
| `POST` | `/module/{moduleID}/record/{recordID}/{fieldName}/attachment` | Uploads attachment and validates it against record field requirements |
|
||||||
|
|
||||||
|
## Generates report from module records
|
||||||
|
|
||||||
|
#### Method
|
||||||
|
|
||||||
|
| URI | Protocol | Method | Authentication |
|
||||||
|
| --- | -------- | ------ | -------------- |
|
||||||
|
| `/module/{moduleID}/record/report` | HTTP/S | GET | |
|
||||||
|
|
||||||
|
#### Request parameters
|
||||||
|
|
||||||
|
| Parameter | Type | Method | Description | Default | Required? |
|
||||||
|
| --------- | ---- | ------ | ----------- | ------- | --------- |
|
||||||
|
| metrics | string | GET | Metrics (eg: 'SUM(money), MAX(calls)') | N/A | NO |
|
||||||
|
| dimensions | string | GET | Dimensions (eg: 'DATE(foo), status') | N/A | YES |
|
||||||
|
| filter | string | GET | Filter (eg: 'DATE(foo) > 2010') | N/A | NO |
|
||||||
|
| moduleID | uint64 | PATH | Module ID | N/A | YES |
|
||||||
|
|
||||||
|
## List/read records from module section
|
||||||
|
|
||||||
|
#### Method
|
||||||
|
|
||||||
|
| URI | Protocol | Method | Authentication |
|
||||||
|
| --- | -------- | ------ | -------------- |
|
||||||
|
| `/module/{moduleID}/record/` | HTTP/S | GET | |
|
||||||
|
|
||||||
|
#### Request parameters
|
||||||
|
|
||||||
|
| Parameter | Type | Method | Description | Default | Required? |
|
||||||
|
| --------- | ---- | ------ | ----------- | ------- | --------- |
|
||||||
|
| filter | string | GET | Filtering condition | N/A | NO |
|
||||||
|
| page | int | GET | Page number (0 based) | N/A | NO |
|
||||||
|
| perPage | int | GET | Returned items per page (default 50) | N/A | NO |
|
||||||
|
| sort | string | GET | Sort field (default id desc) | N/A | NO |
|
||||||
|
| moduleID | uint64 | PATH | Module ID | N/A | YES |
|
||||||
|
|
||||||
|
## Create record in module section
|
||||||
|
|
||||||
|
#### Method
|
||||||
|
|
||||||
|
| URI | Protocol | Method | Authentication |
|
||||||
|
| --- | -------- | ------ | -------------- |
|
||||||
|
| `/module/{moduleID}/record/` | HTTP/S | POST | |
|
||||||
|
|
||||||
|
#### Request parameters
|
||||||
|
|
||||||
|
| Parameter | Type | Method | Description | Default | Required? |
|
||||||
|
| --------- | ---- | ------ | ----------- | ------- | --------- |
|
||||||
|
| values | types.RecordValueSet | POST | Record values | N/A | YES |
|
||||||
|
| moduleID | uint64 | PATH | Module ID | N/A | YES |
|
||||||
|
|
||||||
|
## Read records by ID from module section
|
||||||
|
|
||||||
|
#### Method
|
||||||
|
|
||||||
|
| URI | Protocol | Method | Authentication |
|
||||||
|
| --- | -------- | ------ | -------------- |
|
||||||
|
| `/module/{moduleID}/record/{recordID}` | HTTP/S | GET | |
|
||||||
|
|
||||||
|
#### Request parameters
|
||||||
|
|
||||||
|
| Parameter | Type | Method | Description | Default | Required? |
|
||||||
|
| --------- | ---- | ------ | ----------- | ------- | --------- |
|
||||||
|
| recordID | uint64 | PATH | Record ID | N/A | YES |
|
||||||
|
| moduleID | uint64 | PATH | Module ID | N/A | YES |
|
||||||
|
|
||||||
|
## Update records in module section
|
||||||
|
|
||||||
|
#### Method
|
||||||
|
|
||||||
|
| URI | Protocol | Method | Authentication |
|
||||||
|
| --- | -------- | ------ | -------------- |
|
||||||
|
| `/module/{moduleID}/record/{recordID}` | HTTP/S | POST | |
|
||||||
|
|
||||||
|
#### Request parameters
|
||||||
|
|
||||||
|
| Parameter | Type | Method | Description | Default | Required? |
|
||||||
|
| --------- | ---- | ------ | ----------- | ------- | --------- |
|
||||||
|
| recordID | uint64 | PATH | Record ID | N/A | YES |
|
||||||
|
| moduleID | uint64 | PATH | Module ID | N/A | YES |
|
||||||
|
| values | types.RecordValueSet | POST | Record values | N/A | YES |
|
||||||
|
|
||||||
|
## Delete record row from module section
|
||||||
|
|
||||||
|
#### Method
|
||||||
|
|
||||||
|
| URI | Protocol | Method | Authentication |
|
||||||
|
| --- | -------- | ------ | -------------- |
|
||||||
|
| `/module/{moduleID}/record/{recordID}` | HTTP/S | DELETE | |
|
||||||
|
|
||||||
|
#### Request parameters
|
||||||
|
|
||||||
|
| Parameter | Type | Method | Description | Default | Required? |
|
||||||
|
| --------- | ---- | ------ | ----------- | ------- | --------- |
|
||||||
|
| recordID | uint64 | PATH | Record ID | N/A | YES |
|
||||||
|
| moduleID | uint64 | PATH | Module ID | N/A | YES |
|
||||||
|
|
||||||
|
## Uploads attachment and validates it against record field requirements
|
||||||
|
|
||||||
|
#### Method
|
||||||
|
|
||||||
|
| URI | Protocol | Method | Authentication |
|
||||||
|
| --- | -------- | ------ | -------------- |
|
||||||
|
| `/module/{moduleID}/record/{recordID}/{fieldName}/attachment` | HTTP/S | POST | |
|
||||||
|
|
||||||
|
#### Request parameters
|
||||||
|
|
||||||
|
| Parameter | Type | Method | Description | Default | Required? |
|
||||||
|
| --------- | ---- | ------ | ----------- | ------- | --------- |
|
||||||
|
| recordID | uint64 | PATH | Record ID | N/A | YES |
|
||||||
|
| fieldName | string | PATH | Field name | N/A | YES |
|
||||||
|
| moduleID | uint64 | PATH | Module ID | N/A | YES |
|
||||||
|
| upload | *multipart.FileHeader | POST | File to upload | N/A | YES |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
# Triggers
|
||||||
|
|
||||||
|
Compose Triggers
|
||||||
|
|
||||||
|
| Method | Endpoint | Purpose |
|
||||||
|
| ------ | -------- | ------- |
|
||||||
|
| `GET` | `/trigger/` | List available triggers |
|
||||||
|
| `POST` | `/trigger/` | Create trigger |
|
||||||
|
| `GET` | `/trigger/{triggerID}` | Get trigger details |
|
||||||
|
| `POST` | `/trigger/{triggerID}` | Update trigger |
|
||||||
|
| `Delete` | `/trigger/{triggerID}` | Delete trigger |
|
||||||
|
|
||||||
|
## List available triggers
|
||||||
|
|
||||||
|
#### Method
|
||||||
|
|
||||||
|
| URI | Protocol | Method | Authentication |
|
||||||
|
| --- | -------- | ------ | -------------- |
|
||||||
|
| `/trigger/` | HTTP/S | GET | |
|
||||||
|
|
||||||
|
#### Request parameters
|
||||||
|
|
||||||
|
| Parameter | Type | Method | Description | Default | Required? |
|
||||||
|
| --------- | ---- | ------ | ----------- | ------- | --------- |
|
||||||
|
| moduleID | uint64 | GET | Filter triggers by module | N/A | NO |
|
||||||
|
|
||||||
|
## Create trigger
|
||||||
|
|
||||||
|
#### Method
|
||||||
|
|
||||||
|
| URI | Protocol | Method | Authentication |
|
||||||
|
| --- | -------- | ------ | -------------- |
|
||||||
|
| `/trigger/` | HTTP/S | POST | |
|
||||||
|
|
||||||
|
#### Request parameters
|
||||||
|
|
||||||
|
| Parameter | Type | Method | Description | Default | Required? |
|
||||||
|
| --------- | ---- | ------ | ----------- | ------- | --------- |
|
||||||
|
| moduleID | uint64 | POST | Module ID | N/A | NO |
|
||||||
|
| name | string | POST | Name | N/A | YES |
|
||||||
|
| actions | []string | POST | Actions that trigger this trigger | N/A | NO |
|
||||||
|
| enabled | bool | POST | Enabled | N/A | NO |
|
||||||
|
| source | string | POST | Trigger source code | N/A | NO |
|
||||||
|
|
||||||
|
## Get trigger details
|
||||||
|
|
||||||
|
#### Method
|
||||||
|
|
||||||
|
| URI | Protocol | Method | Authentication |
|
||||||
|
| --- | -------- | ------ | -------------- |
|
||||||
|
| `/trigger/{triggerID}` | HTTP/S | GET | |
|
||||||
|
|
||||||
|
#### Request parameters
|
||||||
|
|
||||||
|
| Parameter | Type | Method | Description | Default | Required? |
|
||||||
|
| --------- | ---- | ------ | ----------- | ------- | --------- |
|
||||||
|
| triggerID | uint64 | PATH | Trigger ID | N/A | YES |
|
||||||
|
|
||||||
|
## Update trigger
|
||||||
|
|
||||||
|
#### Method
|
||||||
|
|
||||||
|
| URI | Protocol | Method | Authentication |
|
||||||
|
| --- | -------- | ------ | -------------- |
|
||||||
|
| `/trigger/{triggerID}` | HTTP/S | POST | |
|
||||||
|
|
||||||
|
#### Request parameters
|
||||||
|
|
||||||
|
| Parameter | Type | Method | Description | Default | Required? |
|
||||||
|
| --------- | ---- | ------ | ----------- | ------- | --------- |
|
||||||
|
| triggerID | uint64 | PATH | Trigger ID | N/A | YES |
|
||||||
|
| moduleID | uint64 | POST | Module ID | N/A | NO |
|
||||||
|
| name | string | POST | Name | N/A | YES |
|
||||||
|
| actions | []string | POST | Actions that trigger this trigger | N/A | NO |
|
||||||
|
| enabled | bool | POST | Enabled | N/A | NO |
|
||||||
|
| source | string | POST | Trigger source code | N/A | NO |
|
||||||
|
|
||||||
|
## Delete trigger
|
||||||
|
|
||||||
|
#### Method
|
||||||
|
|
||||||
|
| URI | Protocol | Method | Authentication |
|
||||||
|
| --- | -------- | ------ | -------------- |
|
||||||
|
| `/trigger/{triggerID}` | HTTP/S | Delete | |
|
||||||
|
|
||||||
|
#### Request parameters
|
||||||
|
|
||||||
|
| Parameter | Type | Method | Description | Default | Required? |
|
||||||
|
| --------- | ---- | ------ | ----------- | ------- | --------- |
|
||||||
|
| triggerID | uint64 | PATH | Trigger ID | N/A | YES |
|
||||||
|
|
||||||
|
---
|
||||||
@@ -304,26 +304,6 @@ A channel is a representation of a sequence of messages. It has meta data like c
|
|||||||
|
|
||||||
# Messages
|
# Messages
|
||||||
|
|
||||||
Messages represent individual messages in the chat system. Messages are typed, indicating the event which triggered the message.
|
|
||||||
|
|
||||||
Currently expected message types are:
|
|
||||||
|
|
||||||
| Name | Description |
|
|
||||||
| ---- | ----------- |
|
|
||||||
| CREATE | The first message when the channel is created |
|
|
||||||
| TOPIC | A member changed the topic of the channel |
|
|
||||||
| RENAME | A member renamed the channel |
|
|
||||||
| MESSAGE | A member posted a message to the channel |
|
|
||||||
| FILE | A member uploaded a file to the channel |
|
|
||||||
|
|
||||||
The following event types may be sent with a message event:
|
|
||||||
|
|
||||||
| Name | Description |
|
|
||||||
| ---- | ----------- |
|
|
||||||
| CREATED | A message has been created on a channel |
|
|
||||||
| EDITED | A message has been edited by the sender |
|
|
||||||
| REMOVED | A message has been removed by the sender |
|
|
||||||
|
|
||||||
| Method | Endpoint | Purpose |
|
| Method | Endpoint | Purpose |
|
||||||
| ------ | -------- | ------- |
|
| ------ | -------- | ------- |
|
||||||
| `POST` | `/channels/{channelID}/messages/` | Post new message to the channel |
|
| `POST` | `/channels/{channelID}/messages/` | Post new message to the channel |
|
||||||
|
|||||||
@@ -70,10 +70,10 @@ func rolesResetCmd(ctx context.Context, db *factory.DB) func(cmd *cobra.Command,
|
|||||||
{2, "compose", "namespace.create", 2},
|
{2, "compose", "namespace.create", 2},
|
||||||
{2, "compose", "access", 2},
|
{2, "compose", "access", 2},
|
||||||
{2, "compose", "grant", 2},
|
{2, "compose", "grant", 2},
|
||||||
{2, "compose:namespace:*", "page.create", 2},
|
|
||||||
{2, "compose:namespace:*", "read", 2},
|
{2, "compose:namespace:*", "read", 2},
|
||||||
{2, "compose:namespace:*", "update", 2},
|
{2, "compose:namespace:*", "update", 2},
|
||||||
{2, "compose:namespace:*", "delete", 2},
|
{2, "compose:namespace:*", "delete", 2},
|
||||||
|
{2, "compose:namespace:*", "page.create", 2},
|
||||||
{2, "compose:namespace:*", "module.create", 2},
|
{2, "compose:namespace:*", "module.create", 2},
|
||||||
{2, "compose:namespace:*", "chart.create", 2},
|
{2, "compose:namespace:*", "chart.create", 2},
|
||||||
{2, "compose:namespace:*", "trigger.create", 2},
|
{2, "compose:namespace:*", "trigger.create", 2},
|
||||||
|
|||||||
@@ -14,5 +14,5 @@ func (e repositoryError) Error() string {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (e repositoryError) String() string {
|
func (e repositoryError) String() string {
|
||||||
return "crust.auth.repository." + string(e)
|
return "crust.system.repository." + string(e)
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user