Add records limit count per namespace
This commit is contained in:
committed by
Jože Fortun
parent
05152a9ef7
commit
ca5604121a
@@ -22,3 +22,4 @@ errors:
|
||||
staleData: stale data
|
||||
unknownBulkOperation: unknown bulk operation {bulkOperation}
|
||||
valueInput: invalid record value input
|
||||
maxRecordsReached: maximum number of records per namespace reached
|
||||
|
||||
+50
-44
@@ -33,7 +33,7 @@
|
||||
|
||||
###############################################################################
|
||||
# Allow insecure (invalid, expired TLS/SSL certificates) connections.
|
||||
#
|
||||
#
|
||||
# [IMPORTANT]
|
||||
# ====
|
||||
# We strongly recommend keeping this value set to false except for local development or demos.
|
||||
@@ -203,7 +203,7 @@
|
||||
|
||||
###############################################################################
|
||||
# Password for the web console endpoint. When running in dev environment, password is not required.
|
||||
#
|
||||
#
|
||||
# Corteza intentionally sets default password to random chars to prevent security incidents.
|
||||
# Type: string
|
||||
# Default: <no value>
|
||||
@@ -290,7 +290,7 @@
|
||||
# Email sending
|
||||
#
|
||||
# Configure your local SMTP server or use one of the available providers.
|
||||
#
|
||||
#
|
||||
# These values are copied to settings when the server starts and can be managed from the administration console.
|
||||
# We recommend you remove these values after they are copied to settings.
|
||||
# If server detects difference between these options and settings, it shows a warning in the log on server start.
|
||||
@@ -430,7 +430,7 @@
|
||||
|
||||
###############################################################################
|
||||
# Password security allows you to disable constraints to which passwords must conform to.
|
||||
#
|
||||
#
|
||||
# [CAUTION]
|
||||
# ====
|
||||
# Disabling password security can be useful for development environments as it removes the need for complex passwords.
|
||||
@@ -442,12 +442,12 @@
|
||||
|
||||
###############################################################################
|
||||
# Algoritm to be use for JWT signature.
|
||||
#
|
||||
#
|
||||
# Supported valus:
|
||||
# - HS256, HS384, HS512
|
||||
# - PS256, PS384, PS512,
|
||||
# - RS256, RS384, RS512
|
||||
#
|
||||
#
|
||||
# Provide shared secret string for HS256, HS384, HS512 and full private key or path to the file PS* and RS* algorithms.
|
||||
# Type: string
|
||||
# Default: HS512
|
||||
@@ -456,7 +456,7 @@
|
||||
###############################################################################
|
||||
# Secret used for signing JWT tokens.
|
||||
# Value is used only when HS256, HS384 or HS512 algorithm is used.
|
||||
#
|
||||
#
|
||||
# [IMPORTANT]
|
||||
# ====
|
||||
# If secret is not set, system auto-generates one from DB_DSN and HOSTNAME environment variables.
|
||||
@@ -480,7 +480,7 @@
|
||||
|
||||
###############################################################################
|
||||
# Lifetime of the refresh token. Should be much longer than lifetime of the access token.
|
||||
#
|
||||
#
|
||||
# Refresh tokens are used to exchange expired access tokens with new ones.
|
||||
# Type: time.Duration
|
||||
# Default: 72h
|
||||
@@ -488,7 +488,7 @@
|
||||
|
||||
###############################################################################
|
||||
# Redirect URL to be sent with OAuth2 authentication request to provider
|
||||
#
|
||||
#
|
||||
# `provider` placeholder is replaced with the actual value when used.
|
||||
# Type: string
|
||||
# Default: <no value>
|
||||
@@ -496,7 +496,7 @@
|
||||
|
||||
###############################################################################
|
||||
# Secret used for securing cookies
|
||||
#
|
||||
#
|
||||
# [IMPORTANT]
|
||||
# ====
|
||||
# If secret is not set, system auto-generates one from DB_DSN and HOSTNAME environment variables.
|
||||
@@ -539,21 +539,21 @@
|
||||
|
||||
###############################################################################
|
||||
# Maximum time user is allowed to stay idle when logged in without "remember-me" option and before session is expired.
|
||||
#
|
||||
#
|
||||
# Recomended value is between an hour and a day.
|
||||
#
|
||||
#
|
||||
# [IMPORTANT]
|
||||
# ====
|
||||
# This affects only profile (/auth) pages. Using applications (admin, compose, ...) does not prolong the session.
|
||||
# ====
|
||||
#
|
||||
#
|
||||
# Type: time.Duration
|
||||
# Default: 24h
|
||||
# AUTH_SESSION_LIFETIME=24h
|
||||
|
||||
###############################################################################
|
||||
# Duration of the session in /auth lasts when user logs-in with "remember-me" option.
|
||||
#
|
||||
#
|
||||
# If set to 0, "remember-me" option is removed.
|
||||
# Type: time.Duration
|
||||
# Default: 8640h
|
||||
@@ -580,7 +580,7 @@
|
||||
|
||||
###############################################################################
|
||||
# Secret used for securing CSRF protection
|
||||
#
|
||||
#
|
||||
# [IMPORTANT]
|
||||
# ====
|
||||
# If secret is not set, system auto-generates one from DB_DSN and HOSTNAME environment variables.
|
||||
@@ -610,19 +610,19 @@
|
||||
|
||||
###############################################################################
|
||||
# Handle for OAuth2 client used for automatic redirect from /auth/oauth2/go endpoint.
|
||||
#
|
||||
#
|
||||
# This simplifies configuration for OAuth2 flow for Corteza Web applications as it removes
|
||||
# the need to suply redirection URL and client ID (oauth2/go endpoint does that internally)
|
||||
#
|
||||
#
|
||||
# Type: string
|
||||
# Default: corteza-webapp
|
||||
# AUTH_DEFAULT_CLIENT=corteza-webapp
|
||||
|
||||
###############################################################################
|
||||
# Path to js, css, images and template source files
|
||||
#
|
||||
#
|
||||
# When corteza starts, if path exists it tries to load template files from it.
|
||||
#
|
||||
#
|
||||
# When empty path is set (default value), embedded files are used.
|
||||
# Type: string
|
||||
# Default: <no value>
|
||||
@@ -631,7 +631,7 @@
|
||||
###############################################################################
|
||||
# When enabled, corteza reloads template before every execution.
|
||||
# Enable this for debugging or when developing auth templates.
|
||||
#
|
||||
#
|
||||
# Should be disabled in production where templates do not change between server restarts.
|
||||
# Type: bool
|
||||
# Default: <no value>
|
||||
@@ -640,7 +640,7 @@
|
||||
###############################################################################
|
||||
# When set, Corteza creates one or more users with the configured values using provided email as a password.
|
||||
# It skips existing (email, handle). All new users are assigned to all bypass roles.
|
||||
#
|
||||
#
|
||||
# When set in production, Corteza stops and reports an error
|
||||
# Type: string
|
||||
# Default: <no value>
|
||||
@@ -810,6 +810,12 @@
|
||||
# Default: <no value>
|
||||
# LIMIT_SYSTEM_USERS=<no value>
|
||||
|
||||
###############################################################################
|
||||
# Maximum number of records per namespace
|
||||
# Type: int
|
||||
# Default: <no value>
|
||||
# LIMIT_RECORD_COUNT_PER_NAMESPACE=<no value>
|
||||
|
||||
###############################################################################
|
||||
###############################################################################
|
||||
# locale
|
||||
@@ -818,16 +824,16 @@
|
||||
###############################################################################
|
||||
# List of compa delimited languages (language tags) to enable.
|
||||
# In case when an enabled language can not be loaded, error is logged.
|
||||
#
|
||||
#
|
||||
# When loading language configurations (config.xml) from the configured path(s).
|
||||
#
|
||||
#
|
||||
# Type: string
|
||||
# Default: en
|
||||
# LOCALE_LANGUAGES=en
|
||||
|
||||
###############################################################################
|
||||
# One or more paths to locale config and translation files, separated by colon
|
||||
#
|
||||
#
|
||||
# When with LOCALE_DEVELOPMENT_MODE=true, default value for path is ../../locale
|
||||
# Type: string
|
||||
# Default: <no value>
|
||||
@@ -837,7 +843,7 @@
|
||||
# Name of the query string parameter used to pass the language tag (it overrides Accept-Language header).
|
||||
# Set it to empty string to disable detection from the query string.
|
||||
# This parameter is ignored if only one language is enabled
|
||||
#
|
||||
#
|
||||
# Type: string
|
||||
# Default: lng
|
||||
# LOCALE_QUERY_STRING_PARAM=lng
|
||||
@@ -868,9 +874,9 @@
|
||||
|
||||
###############################################################################
|
||||
# Disables json format for logging and enables more human-readable output with colors.
|
||||
#
|
||||
#
|
||||
# Disable for production.
|
||||
#
|
||||
#
|
||||
# Type: bool
|
||||
# Default: <no value>
|
||||
# LOG_DEBUG=<no value>
|
||||
@@ -878,11 +884,11 @@
|
||||
###############################################################################
|
||||
# Minimum logging level. If set to "warn",
|
||||
# Levels warn, error, dpanic panic and fatal will be logged.
|
||||
#
|
||||
#
|
||||
# Recommended value for production: warn
|
||||
#
|
||||
#
|
||||
# Possible values: debug, info, warn, error, dpanic, panic, fatal
|
||||
#
|
||||
#
|
||||
# Type: string
|
||||
# Default: warn
|
||||
# LOG_LEVEL=warn
|
||||
@@ -890,25 +896,25 @@
|
||||
###############################################################################
|
||||
# Log filtering rules by level and name (log-level:log-namespace).
|
||||
# Please note that level (LOG_LEVEL) is applied before filter and it affects the final output!
|
||||
#
|
||||
#
|
||||
# Leave unset for production.
|
||||
#
|
||||
#
|
||||
# Example:
|
||||
# `warn+:* *:auth,workflow.*`
|
||||
# Log warnings, errors, panic, fatals. Everything from auth and workflow is logged.
|
||||
#
|
||||
#
|
||||
#
|
||||
#
|
||||
# See more examples and documentation here: https://github.com/moul/zapfilter
|
||||
#
|
||||
#
|
||||
# Type: string
|
||||
# Default: <no value>
|
||||
# LOG_FILTER=<no value>
|
||||
|
||||
###############################################################################
|
||||
# Set to true to see where the logging was called from.
|
||||
#
|
||||
#
|
||||
# Disable for production.
|
||||
#
|
||||
#
|
||||
# Type: bool
|
||||
# Default: <no value>
|
||||
# LOG_INCLUDE_CALLER=<no value>
|
||||
@@ -916,9 +922,9 @@
|
||||
###############################################################################
|
||||
# Include stack-trace when logging at a specified level or below.
|
||||
# Disable for production.
|
||||
#
|
||||
#
|
||||
# Possible values: debug, info, warn, error, dpanic, panic, fatal
|
||||
#
|
||||
#
|
||||
# Type: string
|
||||
# Default: dpanic
|
||||
# LOG_STACKTRACE_LEVEL=dpanic
|
||||
@@ -1013,11 +1019,11 @@
|
||||
#
|
||||
# Provisioning allows you to configure a {PRODUCT_NAME} instance when deployed.
|
||||
# It occurs automatically after the {PRODUCT_NAME} server starts.
|
||||
#
|
||||
#
|
||||
# [IMPORTANT]
|
||||
# ====
|
||||
# We recommend you to keep provisioning enabled as it simplifies version updates by updating the database and updating settings.
|
||||
#
|
||||
#
|
||||
# If you're doing local development or some debugging, you can disable this.
|
||||
# ====
|
||||
#
|
||||
@@ -1043,7 +1049,7 @@
|
||||
# ====
|
||||
# These parameters help in the development and testing process.
|
||||
# When you are deploying to production, these should be disabled to improve performance and reduce storage usage.
|
||||
#
|
||||
#
|
||||
# You should configure external services such as Sentry or ELK to keep track of logs and error reports.
|
||||
# ====
|
||||
#
|
||||
@@ -1149,7 +1155,7 @@
|
||||
# Delay system startup
|
||||
#
|
||||
# You can configure these options to defer API execution until another external (HTTP) service is up and running.
|
||||
#
|
||||
#
|
||||
# [ TIP ]
|
||||
# ====
|
||||
# Delaying API execution can come in handy in complex setups where execution order is important.
|
||||
@@ -1173,7 +1179,7 @@
|
||||
###############################################################################
|
||||
# Space delimited list of hosts and/or URLs to probe.
|
||||
# Host format: `host` or `host:443` (port will default to 80).
|
||||
#
|
||||
#
|
||||
# [NOTE]
|
||||
# ====
|
||||
# Services are probed in parallel.
|
||||
|
||||
+49
-48
@@ -1,53 +1,53 @@
|
||||
package app
|
||||
|
||||
import (
|
||||
"context"
|
||||
"crypto/tls"
|
||||
"fmt"
|
||||
"net/url"
|
||||
"os"
|
||||
"regexp"
|
||||
"strings"
|
||||
"time"
|
||||
"context"
|
||||
"crypto/tls"
|
||||
"fmt"
|
||||
"net/url"
|
||||
"os"
|
||||
"regexp"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
authService "github.com/cortezaproject/corteza/server/auth"
|
||||
"github.com/cortezaproject/corteza/server/auth/saml"
|
||||
authSettings "github.com/cortezaproject/corteza/server/auth/settings"
|
||||
autService "github.com/cortezaproject/corteza/server/automation/service"
|
||||
cmpService "github.com/cortezaproject/corteza/server/compose/service"
|
||||
cmpEvent "github.com/cortezaproject/corteza/server/compose/service/event"
|
||||
discoveryService "github.com/cortezaproject/corteza/server/discovery/service"
|
||||
fedService "github.com/cortezaproject/corteza/server/federation/service"
|
||||
"github.com/cortezaproject/corteza/server/pkg/actionlog"
|
||||
"github.com/cortezaproject/corteza/server/pkg/apigw"
|
||||
apigwTypes "github.com/cortezaproject/corteza/server/pkg/apigw/types"
|
||||
"github.com/cortezaproject/corteza/server/pkg/auth"
|
||||
"github.com/cortezaproject/corteza/server/pkg/corredor"
|
||||
"github.com/cortezaproject/corteza/server/pkg/eventbus"
|
||||
"github.com/cortezaproject/corteza/server/pkg/healthcheck"
|
||||
"github.com/cortezaproject/corteza/server/pkg/http"
|
||||
"github.com/cortezaproject/corteza/server/pkg/id"
|
||||
"github.com/cortezaproject/corteza/server/pkg/locale"
|
||||
"github.com/cortezaproject/corteza/server/pkg/logger"
|
||||
"github.com/cortezaproject/corteza/server/pkg/mail"
|
||||
"github.com/cortezaproject/corteza/server/pkg/messagebus"
|
||||
"github.com/cortezaproject/corteza/server/pkg/monitor"
|
||||
"github.com/cortezaproject/corteza/server/pkg/options"
|
||||
"github.com/cortezaproject/corteza/server/pkg/provision"
|
||||
"github.com/cortezaproject/corteza/server/pkg/rbac"
|
||||
"github.com/cortezaproject/corteza/server/pkg/scheduler"
|
||||
"github.com/cortezaproject/corteza/server/pkg/sentry"
|
||||
"github.com/cortezaproject/corteza/server/pkg/valuestore"
|
||||
"github.com/cortezaproject/corteza/server/pkg/version"
|
||||
"github.com/cortezaproject/corteza/server/pkg/websocket"
|
||||
"github.com/cortezaproject/corteza/server/store"
|
||||
"github.com/cortezaproject/corteza/server/system/service"
|
||||
sysService "github.com/cortezaproject/corteza/server/system/service"
|
||||
sysEvent "github.com/cortezaproject/corteza/server/system/service/event"
|
||||
"github.com/cortezaproject/corteza/server/system/types"
|
||||
"github.com/lestrrat-go/jwx/jwt"
|
||||
"go.uber.org/zap"
|
||||
gomail "gopkg.in/mail.v2"
|
||||
authService "github.com/cortezaproject/corteza/server/auth"
|
||||
"github.com/cortezaproject/corteza/server/auth/saml"
|
||||
authSettings "github.com/cortezaproject/corteza/server/auth/settings"
|
||||
autService "github.com/cortezaproject/corteza/server/automation/service"
|
||||
cmpService "github.com/cortezaproject/corteza/server/compose/service"
|
||||
cmpEvent "github.com/cortezaproject/corteza/server/compose/service/event"
|
||||
discoveryService "github.com/cortezaproject/corteza/server/discovery/service"
|
||||
fedService "github.com/cortezaproject/corteza/server/federation/service"
|
||||
"github.com/cortezaproject/corteza/server/pkg/actionlog"
|
||||
"github.com/cortezaproject/corteza/server/pkg/apigw"
|
||||
apigwTypes "github.com/cortezaproject/corteza/server/pkg/apigw/types"
|
||||
"github.com/cortezaproject/corteza/server/pkg/auth"
|
||||
"github.com/cortezaproject/corteza/server/pkg/corredor"
|
||||
"github.com/cortezaproject/corteza/server/pkg/eventbus"
|
||||
"github.com/cortezaproject/corteza/server/pkg/healthcheck"
|
||||
"github.com/cortezaproject/corteza/server/pkg/http"
|
||||
"github.com/cortezaproject/corteza/server/pkg/id"
|
||||
"github.com/cortezaproject/corteza/server/pkg/locale"
|
||||
"github.com/cortezaproject/corteza/server/pkg/logger"
|
||||
"github.com/cortezaproject/corteza/server/pkg/mail"
|
||||
"github.com/cortezaproject/corteza/server/pkg/messagebus"
|
||||
"github.com/cortezaproject/corteza/server/pkg/monitor"
|
||||
"github.com/cortezaproject/corteza/server/pkg/options"
|
||||
"github.com/cortezaproject/corteza/server/pkg/provision"
|
||||
"github.com/cortezaproject/corteza/server/pkg/rbac"
|
||||
"github.com/cortezaproject/corteza/server/pkg/scheduler"
|
||||
"github.com/cortezaproject/corteza/server/pkg/sentry"
|
||||
"github.com/cortezaproject/corteza/server/pkg/valuestore"
|
||||
"github.com/cortezaproject/corteza/server/pkg/version"
|
||||
"github.com/cortezaproject/corteza/server/pkg/websocket"
|
||||
"github.com/cortezaproject/corteza/server/store"
|
||||
"github.com/cortezaproject/corteza/server/system/service"
|
||||
sysService "github.com/cortezaproject/corteza/server/system/service"
|
||||
sysEvent "github.com/cortezaproject/corteza/server/system/service/event"
|
||||
"github.com/cortezaproject/corteza/server/system/types"
|
||||
"github.com/lestrrat-go/jwx/jwt"
|
||||
"go.uber.org/zap"
|
||||
gomail "gopkg.in/mail.v2"
|
||||
)
|
||||
|
||||
const (
|
||||
@@ -416,6 +416,7 @@ func (app *CortezaApp) InitServices(ctx context.Context) (err error) {
|
||||
ActionLog: app.Opt.ActionLog,
|
||||
Discovery: app.Opt.Discovery,
|
||||
Storage: app.Opt.ObjStore,
|
||||
Limit: app.Opt.Limit,
|
||||
UserFinder: sysService.DefaultUser,
|
||||
SchemaAltManager: sysService.DefaultDalSchemaAlteration,
|
||||
})
|
||||
@@ -562,9 +563,9 @@ func (app *CortezaApp) Activate(ctx context.Context) (err error) {
|
||||
|
||||
app.AuthService.Watch(ctx)
|
||||
|
||||
updateSassInstallSettings(ctx, sysService.DefaultStylesheet.SassInstalled(), app.Log)
|
||||
updateSassInstallSettings(ctx, sysService.DefaultStylesheet.SassInstalled(), app.Log)
|
||||
//Generate CSS for webapps
|
||||
if err = sysService.DefaultStylesheet.GenerateCSS(sysService.CurrentSettings, app.Opt.Webapp.ScssDirPath, app.Log); err != nil {
|
||||
if err = sysService.DefaultStylesheet.GenerateCSS(sysService.CurrentSettings, app.Opt.Webapp.ScssDirPath, app.Log); err != nil {
|
||||
return fmt.Errorf("could not generate css for webapps: %w", err)
|
||||
}
|
||||
|
||||
|
||||
@@ -11,6 +11,10 @@ limit: schema.#optionsGroup & {
|
||||
type: "int"
|
||||
description: "Maximum number of valid (not deleted, not suspended) users"
|
||||
}
|
||||
record_count_per_namespace: {
|
||||
type: "int"
|
||||
description: "Maximum number of records per namespace"
|
||||
}
|
||||
}
|
||||
title: "Limits"
|
||||
}
|
||||
|
||||
@@ -94,7 +94,7 @@ func (app *CortezaApp) mountHttpRoutes(r chi.Router) {
|
||||
}
|
||||
|
||||
if app.Opt.Federation.Enabled {
|
||||
r.Route("/federation", federationRest.MountRoutes())
|
||||
r.Route("/federation", federationRest.MountRoutes(app.Opt.Limit))
|
||||
}
|
||||
|
||||
var fullpathDocs = options.CleanBase(ho.BaseUrl, ho.ApiBaseUrl, "docs")
|
||||
|
||||
@@ -3,7 +3,6 @@ package dalutils
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
|
||||
"github.com/cortezaproject/corteza/server/compose/types"
|
||||
"github.com/cortezaproject/corteza/server/pkg/dal"
|
||||
"github.com/cortezaproject/corteza/server/pkg/filter"
|
||||
@@ -29,6 +28,10 @@ type (
|
||||
deleter interface {
|
||||
Delete(ctx context.Context, m dal.ModelRef, operations dal.OperationSet, pkv ...dal.ValueGetter) (err error)
|
||||
}
|
||||
|
||||
counter interface {
|
||||
Count(ctx context.Context, m dal.ModelRef, operations dal.OperationSet, f filter.Filter) (uint, error)
|
||||
}
|
||||
)
|
||||
|
||||
// ComposeRecordsList iterates over results and collects all available records
|
||||
@@ -65,6 +68,20 @@ func ComposeRecordsFind(ctx context.Context, l lookuper, mod *types.Module, reco
|
||||
return
|
||||
}
|
||||
|
||||
func ComposeRecordsCount(ctx context.Context, c counter, mod *types.Module, filter types.RecordFilter) (cnt uint, err error) {
|
||||
constraints := map[string][]interface{}{
|
||||
"namespaceID": {mod.NamespaceID},
|
||||
}
|
||||
|
||||
if filter.ModuleID != 0 {
|
||||
constraints["moduleID"] = []interface{}{filter.ModuleID}
|
||||
}
|
||||
|
||||
dalFilter := filter.ToConstraintedFilter(constraints)
|
||||
|
||||
return c.Count(ctx, mod.ModelRef(), recLookupOperations(mod), dalFilter)
|
||||
}
|
||||
|
||||
func ComposeRecordCreate(ctx context.Context, c creator, mod *types.Module, records ...*types.Record) (err error) {
|
||||
return c.Create(ctx, mod.ModelRef(), recCreateOperations(mod), recToGetters(records...)...)
|
||||
}
|
||||
|
||||
@@ -12,6 +12,7 @@ type (
|
||||
Create(ctx context.Context, m dal.ModelRef, operations dal.OperationSet, vv ...dal.ValueGetter) error
|
||||
Update(ctx context.Context, m dal.ModelRef, operations dal.OperationSet, rr ...dal.ValueGetter) (err error)
|
||||
Search(ctx context.Context, m dal.ModelRef, operations dal.OperationSet, f filter.Filter) (dal.Iterator, error)
|
||||
Count(ctx context.Context, m dal.ModelRef, operations dal.OperationSet, f filter.Filter) (uint, error)
|
||||
Run(ctx context.Context, pp dal.Pipeline) (dal.Iterator, error)
|
||||
Lookup(ctx context.Context, m dal.ModelRef, operations dal.OperationSet, lookup dal.ValueGetter, dst dal.ValueSetter) (err error)
|
||||
Delete(ctx context.Context, m dal.ModelRef, operations dal.OperationSet, pkv ...dal.ValueGetter) (err error)
|
||||
|
||||
@@ -52,6 +52,8 @@ type (
|
||||
namespace namespaceFinder
|
||||
module moduleFinder
|
||||
|
||||
opt RecordOptions
|
||||
|
||||
revisions *recordRevisions
|
||||
|
||||
formatter recordValuesFormatter
|
||||
@@ -154,6 +156,10 @@ type (
|
||||
Country() string
|
||||
}
|
||||
|
||||
RecordOptions struct {
|
||||
LimitRecords int
|
||||
}
|
||||
|
||||
RecordImportSession struct {
|
||||
Name string `json:"-"`
|
||||
SessionID uint64 `json:"sessionID,string"`
|
||||
@@ -198,7 +204,7 @@ type (
|
||||
recordReportEntry map[string]any
|
||||
)
|
||||
|
||||
func Record() *record {
|
||||
func Record(opts RecordOptions) *record {
|
||||
svc := &record{
|
||||
actionlog: DefaultActionlog,
|
||||
ac: DefaultAccessControl,
|
||||
@@ -209,6 +215,8 @@ func Record() *record {
|
||||
namespace: DefaultNamespace,
|
||||
module: DefaultModule,
|
||||
|
||||
opt: opts,
|
||||
|
||||
revisions: &recordRevisions{revisions.Service(dal.Service())},
|
||||
|
||||
formatter: values.Formatter(),
|
||||
@@ -817,6 +825,11 @@ func (svc record) create(ctx context.Context, new *types.Record) (rec *types.Rec
|
||||
aProps.setNamespace(ns)
|
||||
aProps.setModule(m)
|
||||
|
||||
// check the records limit per namespace
|
||||
if err = svc.checkLimit(ctx, m); err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
if !svc.ac.CanCreateRecordOnModule(ctx, m) {
|
||||
return nil, dd, RecordErrNotAllowedToCreate()
|
||||
}
|
||||
@@ -887,6 +900,27 @@ func (svc record) create(ctx context.Context, new *types.Record) (rec *types.Rec
|
||||
return
|
||||
}
|
||||
|
||||
func (svc record) checkLimit(ctx context.Context, m *types.Module) (err error) {
|
||||
if svc.opt.LimitRecords == 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
// get the number of records count for the namespace
|
||||
recordsCount, err := dalutils.ComposeRecordsCount(ctx, svc.dal, m, types.RecordFilter{
|
||||
NamespaceID: m.NamespaceID,
|
||||
ModuleID: 0,
|
||||
})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if recordsCount >= uint(svc.opt.LimitRecords) {
|
||||
return RecordErrMaxRecordsReached()
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// RecordValueSanitization does basic field and format validation
|
||||
//
|
||||
// Received values must fit the data model: on unknown fields
|
||||
|
||||
+34
-99
@@ -62,7 +62,6 @@ var (
|
||||
// setRecord updates recordActionProps's record
|
||||
//
|
||||
// This function is auto-generated.
|
||||
//
|
||||
func (p *recordActionProps) setRecord(record *types.Record) *recordActionProps {
|
||||
p.record = record
|
||||
return p
|
||||
@@ -71,7 +70,6 @@ func (p *recordActionProps) setRecord(record *types.Record) *recordActionProps {
|
||||
// setChanged updates recordActionProps's changed
|
||||
//
|
||||
// This function is auto-generated.
|
||||
//
|
||||
func (p *recordActionProps) setChanged(changed *types.Record) *recordActionProps {
|
||||
p.changed = changed
|
||||
return p
|
||||
@@ -80,7 +78,6 @@ func (p *recordActionProps) setChanged(changed *types.Record) *recordActionProps
|
||||
// setFilter updates recordActionProps's filter
|
||||
//
|
||||
// This function is auto-generated.
|
||||
//
|
||||
func (p *recordActionProps) setFilter(filter *types.RecordFilter) *recordActionProps {
|
||||
p.filter = filter
|
||||
return p
|
||||
@@ -89,7 +86,6 @@ func (p *recordActionProps) setFilter(filter *types.RecordFilter) *recordActionP
|
||||
// setNamespace updates recordActionProps's namespace
|
||||
//
|
||||
// This function is auto-generated.
|
||||
//
|
||||
func (p *recordActionProps) setNamespace(namespace *types.Namespace) *recordActionProps {
|
||||
p.namespace = namespace
|
||||
return p
|
||||
@@ -98,7 +94,6 @@ func (p *recordActionProps) setNamespace(namespace *types.Namespace) *recordActi
|
||||
// setModule updates recordActionProps's module
|
||||
//
|
||||
// This function is auto-generated.
|
||||
//
|
||||
func (p *recordActionProps) setModule(module *types.Module) *recordActionProps {
|
||||
p.module = module
|
||||
return p
|
||||
@@ -107,7 +102,6 @@ func (p *recordActionProps) setModule(module *types.Module) *recordActionProps {
|
||||
// setBulkOperation updates recordActionProps's bulkOperation
|
||||
//
|
||||
// This function is auto-generated.
|
||||
//
|
||||
func (p *recordActionProps) setBulkOperation(bulkOperation string) *recordActionProps {
|
||||
p.bulkOperation = bulkOperation
|
||||
return p
|
||||
@@ -116,7 +110,6 @@ func (p *recordActionProps) setBulkOperation(bulkOperation string) *recordAction
|
||||
// setField updates recordActionProps's field
|
||||
//
|
||||
// This function is auto-generated.
|
||||
//
|
||||
func (p *recordActionProps) setField(field string) *recordActionProps {
|
||||
p.field = field
|
||||
return p
|
||||
@@ -125,7 +118,6 @@ func (p *recordActionProps) setField(field string) *recordActionProps {
|
||||
// setPositionField updates recordActionProps's positionField
|
||||
//
|
||||
// This function is auto-generated.
|
||||
//
|
||||
func (p *recordActionProps) setPositionField(positionField *types.ModuleField) *recordActionProps {
|
||||
p.positionField = positionField
|
||||
return p
|
||||
@@ -134,7 +126,6 @@ func (p *recordActionProps) setPositionField(positionField *types.ModuleField) *
|
||||
// setGroupField updates recordActionProps's groupField
|
||||
//
|
||||
// This function is auto-generated.
|
||||
//
|
||||
func (p *recordActionProps) setGroupField(groupField *types.ModuleField) *recordActionProps {
|
||||
p.groupField = groupField
|
||||
return p
|
||||
@@ -143,7 +134,6 @@ func (p *recordActionProps) setGroupField(groupField *types.ModuleField) *record
|
||||
// setValue updates recordActionProps's value
|
||||
//
|
||||
// This function is auto-generated.
|
||||
//
|
||||
func (p *recordActionProps) setValue(value string) *recordActionProps {
|
||||
p.value = value
|
||||
return p
|
||||
@@ -152,7 +142,6 @@ func (p *recordActionProps) setValue(value string) *recordActionProps {
|
||||
// setValueErrors updates recordActionProps's valueErrors
|
||||
//
|
||||
// This function is auto-generated.
|
||||
//
|
||||
func (p *recordActionProps) setValueErrors(valueErrors *types.RecordValueErrorSet) *recordActionProps {
|
||||
p.valueErrors = valueErrors
|
||||
return p
|
||||
@@ -161,7 +150,6 @@ func (p *recordActionProps) setValueErrors(valueErrors *types.RecordValueErrorSe
|
||||
// Serialize converts recordActionProps to actionlog.Meta
|
||||
//
|
||||
// This function is auto-generated.
|
||||
//
|
||||
func (p recordActionProps) Serialize() actionlog.Meta {
|
||||
var (
|
||||
m = make(actionlog.Meta)
|
||||
@@ -219,7 +207,6 @@ func (p recordActionProps) Serialize() actionlog.Meta {
|
||||
// tr translates string and replaces meta value placeholder with values
|
||||
//
|
||||
// This function is auto-generated.
|
||||
//
|
||||
func (p recordActionProps) Format(in string, err error) string {
|
||||
var (
|
||||
pairs = []string{"{{err}}"}
|
||||
@@ -385,7 +372,6 @@ func (p recordActionProps) Format(in string, err error) string {
|
||||
// String returns loggable description as string
|
||||
//
|
||||
// This function is auto-generated.
|
||||
//
|
||||
func (a *recordAction) String() string {
|
||||
var props = &recordActionProps{}
|
||||
|
||||
@@ -413,7 +399,6 @@ func (e *recordAction) ToAction() *actionlog.Action {
|
||||
// RecordActionSearch returns "compose:record.search" action
|
||||
//
|
||||
// This function is auto-generated.
|
||||
//
|
||||
func RecordActionSearch(props ...*recordActionProps) *recordAction {
|
||||
a := &recordAction{
|
||||
timestamp: time.Now(),
|
||||
@@ -433,7 +418,6 @@ func RecordActionSearch(props ...*recordActionProps) *recordAction {
|
||||
// RecordActionSearchSensitive returns "compose:record.searchSensitive" action
|
||||
//
|
||||
// This function is auto-generated.
|
||||
//
|
||||
func RecordActionSearchSensitive(props ...*recordActionProps) *recordAction {
|
||||
a := &recordAction{
|
||||
timestamp: time.Now(),
|
||||
@@ -453,7 +437,6 @@ func RecordActionSearchSensitive(props ...*recordActionProps) *recordAction {
|
||||
// RecordActionLookup returns "compose:record.lookup" action
|
||||
//
|
||||
// This function is auto-generated.
|
||||
//
|
||||
func RecordActionLookup(props ...*recordActionProps) *recordAction {
|
||||
a := &recordAction{
|
||||
timestamp: time.Now(),
|
||||
@@ -473,7 +456,6 @@ func RecordActionLookup(props ...*recordActionProps) *recordAction {
|
||||
// RecordActionReport returns "compose:record.report" action
|
||||
//
|
||||
// This function is auto-generated.
|
||||
//
|
||||
func RecordActionReport(props ...*recordActionProps) *recordAction {
|
||||
a := &recordAction{
|
||||
timestamp: time.Now(),
|
||||
@@ -493,7 +475,6 @@ func RecordActionReport(props ...*recordActionProps) *recordAction {
|
||||
// RecordActionBulk returns "compose:record.bulk" action
|
||||
//
|
||||
// This function is auto-generated.
|
||||
//
|
||||
func RecordActionBulk(props ...*recordActionProps) *recordAction {
|
||||
a := &recordAction{
|
||||
timestamp: time.Now(),
|
||||
@@ -513,7 +494,6 @@ func RecordActionBulk(props ...*recordActionProps) *recordAction {
|
||||
// RecordActionCreate returns "compose:record.create" action
|
||||
//
|
||||
// This function is auto-generated.
|
||||
//
|
||||
func RecordActionCreate(props ...*recordActionProps) *recordAction {
|
||||
a := &recordAction{
|
||||
timestamp: time.Now(),
|
||||
@@ -533,7 +513,6 @@ func RecordActionCreate(props ...*recordActionProps) *recordAction {
|
||||
// RecordActionUpdate returns "compose:record.update" action
|
||||
//
|
||||
// This function is auto-generated.
|
||||
//
|
||||
func RecordActionUpdate(props ...*recordActionProps) *recordAction {
|
||||
a := &recordAction{
|
||||
timestamp: time.Now(),
|
||||
@@ -553,7 +532,6 @@ func RecordActionUpdate(props ...*recordActionProps) *recordAction {
|
||||
// RecordActionDelete returns "compose:record.delete" action
|
||||
//
|
||||
// This function is auto-generated.
|
||||
//
|
||||
func RecordActionDelete(props ...*recordActionProps) *recordAction {
|
||||
a := &recordAction{
|
||||
timestamp: time.Now(),
|
||||
@@ -573,7 +551,6 @@ func RecordActionDelete(props ...*recordActionProps) *recordAction {
|
||||
// RecordActionPatch returns "compose:record.patch" action
|
||||
//
|
||||
// This function is auto-generated.
|
||||
//
|
||||
func RecordActionPatch(props ...*recordActionProps) *recordAction {
|
||||
a := &recordAction{
|
||||
timestamp: time.Now(),
|
||||
@@ -593,7 +570,6 @@ func RecordActionPatch(props ...*recordActionProps) *recordAction {
|
||||
// RecordActionUndelete returns "compose:record.undelete" action
|
||||
//
|
||||
// This function is auto-generated.
|
||||
//
|
||||
func RecordActionUndelete(props ...*recordActionProps) *recordAction {
|
||||
a := &recordAction{
|
||||
timestamp: time.Now(),
|
||||
@@ -613,7 +589,6 @@ func RecordActionUndelete(props ...*recordActionProps) *recordAction {
|
||||
// RecordActionImport returns "compose:record.import" action
|
||||
//
|
||||
// This function is auto-generated.
|
||||
//
|
||||
func RecordActionImport(props ...*recordActionProps) *recordAction {
|
||||
a := &recordAction{
|
||||
timestamp: time.Now(),
|
||||
@@ -633,7 +608,6 @@ func RecordActionImport(props ...*recordActionProps) *recordAction {
|
||||
// RecordActionSearchRevisions returns "compose:record.searchRevisions" action
|
||||
//
|
||||
// This function is auto-generated.
|
||||
//
|
||||
func RecordActionSearchRevisions(props ...*recordActionProps) *recordAction {
|
||||
a := &recordAction{
|
||||
timestamp: time.Now(),
|
||||
@@ -653,7 +627,6 @@ func RecordActionSearchRevisions(props ...*recordActionProps) *recordAction {
|
||||
// RecordActionExport returns "compose:record.export" action
|
||||
//
|
||||
// This function is auto-generated.
|
||||
//
|
||||
func RecordActionExport(props ...*recordActionProps) *recordAction {
|
||||
a := &recordAction{
|
||||
timestamp: time.Now(),
|
||||
@@ -673,7 +646,6 @@ func RecordActionExport(props ...*recordActionProps) *recordAction {
|
||||
// RecordActionOrganize returns "compose:record.organize" action
|
||||
//
|
||||
// This function is auto-generated.
|
||||
//
|
||||
func RecordActionOrganize(props ...*recordActionProps) *recordAction {
|
||||
a := &recordAction{
|
||||
timestamp: time.Now(),
|
||||
@@ -693,7 +665,6 @@ func RecordActionOrganize(props ...*recordActionProps) *recordAction {
|
||||
// RecordActionIteratorInvoked returns "compose:record.iteratorInvoked" action
|
||||
//
|
||||
// This function is auto-generated.
|
||||
//
|
||||
func RecordActionIteratorInvoked(props ...*recordActionProps) *recordAction {
|
||||
a := &recordAction{
|
||||
timestamp: time.Now(),
|
||||
@@ -713,7 +684,6 @@ func RecordActionIteratorInvoked(props ...*recordActionProps) *recordAction {
|
||||
// RecordActionIteratorIteration returns "compose:record.iteratorIteration" action
|
||||
//
|
||||
// This function is auto-generated.
|
||||
//
|
||||
func RecordActionIteratorIteration(props ...*recordActionProps) *recordAction {
|
||||
a := &recordAction{
|
||||
timestamp: time.Now(),
|
||||
@@ -733,7 +703,6 @@ func RecordActionIteratorIteration(props ...*recordActionProps) *recordAction {
|
||||
// RecordActionIteratorClone returns "compose:record.iteratorClone" action
|
||||
//
|
||||
// This function is auto-generated.
|
||||
//
|
||||
func RecordActionIteratorClone(props ...*recordActionProps) *recordAction {
|
||||
a := &recordAction{
|
||||
timestamp: time.Now(),
|
||||
@@ -753,7 +722,6 @@ func RecordActionIteratorClone(props ...*recordActionProps) *recordAction {
|
||||
// RecordActionIteratorUpdate returns "compose:record.iteratorUpdate" action
|
||||
//
|
||||
// This function is auto-generated.
|
||||
//
|
||||
func RecordActionIteratorUpdate(props ...*recordActionProps) *recordAction {
|
||||
a := &recordAction{
|
||||
timestamp: time.Now(),
|
||||
@@ -773,7 +741,6 @@ func RecordActionIteratorUpdate(props ...*recordActionProps) *recordAction {
|
||||
// RecordActionIteratorDelete returns "compose:record.iteratorDelete" action
|
||||
//
|
||||
// This function is auto-generated.
|
||||
//
|
||||
func RecordActionIteratorDelete(props ...*recordActionProps) *recordAction {
|
||||
a := &recordAction{
|
||||
timestamp: time.Now(),
|
||||
@@ -793,7 +760,6 @@ func RecordActionIteratorDelete(props ...*recordActionProps) *recordAction {
|
||||
// RecordActionIteratorUndelete returns "compose:record.iteratorUndelete" action
|
||||
//
|
||||
// This function is auto-generated.
|
||||
//
|
||||
func RecordActionIteratorUndelete(props ...*recordActionProps) *recordAction {
|
||||
a := &recordAction{
|
||||
timestamp: time.Now(),
|
||||
@@ -816,9 +782,7 @@ func RecordActionIteratorUndelete(props ...*recordActionProps) *recordAction {
|
||||
|
||||
// RecordErrGeneric returns "compose:record.generic" as *errors.Error
|
||||
//
|
||||
//
|
||||
// This function is auto-generated.
|
||||
//
|
||||
func RecordErrGeneric(mm ...*recordActionProps) *errors.Error {
|
||||
var p = &recordActionProps{}
|
||||
if len(mm) > 0 {
|
||||
@@ -852,9 +816,7 @@ func RecordErrGeneric(mm ...*recordActionProps) *errors.Error {
|
||||
|
||||
// RecordErrNotFound returns "compose:record.notFound" as *errors.Error
|
||||
//
|
||||
//
|
||||
// This function is auto-generated.
|
||||
//
|
||||
func RecordErrNotFound(mm ...*recordActionProps) *errors.Error {
|
||||
var p = &recordActionProps{}
|
||||
if len(mm) > 0 {
|
||||
@@ -886,9 +848,7 @@ func RecordErrNotFound(mm ...*recordActionProps) *errors.Error {
|
||||
|
||||
// RecordErrNamespaceNotFound returns "compose:record.namespaceNotFound" as *errors.Error
|
||||
//
|
||||
//
|
||||
// This function is auto-generated.
|
||||
//
|
||||
func RecordErrNamespaceNotFound(mm ...*recordActionProps) *errors.Error {
|
||||
var p = &recordActionProps{}
|
||||
if len(mm) > 0 {
|
||||
@@ -920,9 +880,7 @@ func RecordErrNamespaceNotFound(mm ...*recordActionProps) *errors.Error {
|
||||
|
||||
// RecordErrModuleNotFoundModule returns "compose:record.moduleNotFoundModule" as *errors.Error
|
||||
//
|
||||
//
|
||||
// This function is auto-generated.
|
||||
//
|
||||
func RecordErrModuleNotFoundModule(mm ...*recordActionProps) *errors.Error {
|
||||
var p = &recordActionProps{}
|
||||
if len(mm) > 0 {
|
||||
@@ -954,9 +912,7 @@ func RecordErrModuleNotFoundModule(mm ...*recordActionProps) *errors.Error {
|
||||
|
||||
// RecordErrInvalidID returns "compose:record.invalidID" as *errors.Error
|
||||
//
|
||||
//
|
||||
// This function is auto-generated.
|
||||
//
|
||||
func RecordErrInvalidID(mm ...*recordActionProps) *errors.Error {
|
||||
var p = &recordActionProps{}
|
||||
if len(mm) > 0 {
|
||||
@@ -988,9 +944,7 @@ func RecordErrInvalidID(mm ...*recordActionProps) *errors.Error {
|
||||
|
||||
// RecordErrInvalidNamespaceID returns "compose:record.invalidNamespaceID" as *errors.Error
|
||||
//
|
||||
//
|
||||
// This function is auto-generated.
|
||||
//
|
||||
func RecordErrInvalidNamespaceID(mm ...*recordActionProps) *errors.Error {
|
||||
var p = &recordActionProps{}
|
||||
if len(mm) > 0 {
|
||||
@@ -1022,9 +976,7 @@ func RecordErrInvalidNamespaceID(mm ...*recordActionProps) *errors.Error {
|
||||
|
||||
// RecordErrInvalidModuleID returns "compose:record.invalidModuleID" as *errors.Error
|
||||
//
|
||||
//
|
||||
// This function is auto-generated.
|
||||
//
|
||||
func RecordErrInvalidModuleID(mm ...*recordActionProps) *errors.Error {
|
||||
var p = &recordActionProps{}
|
||||
if len(mm) > 0 {
|
||||
@@ -1056,9 +1008,7 @@ func RecordErrInvalidModuleID(mm ...*recordActionProps) *errors.Error {
|
||||
|
||||
// RecordErrStaleData returns "compose:record.staleData" as *errors.Error
|
||||
//
|
||||
//
|
||||
// This function is auto-generated.
|
||||
//
|
||||
func RecordErrStaleData(mm ...*recordActionProps) *errors.Error {
|
||||
var p = &recordActionProps{}
|
||||
if len(mm) > 0 {
|
||||
@@ -1090,9 +1040,7 @@ func RecordErrStaleData(mm ...*recordActionProps) *errors.Error {
|
||||
|
||||
// RecordErrNotAllowedToRead returns "compose:record.notAllowedToRead" as *errors.Error
|
||||
//
|
||||
//
|
||||
// This function is auto-generated.
|
||||
//
|
||||
func RecordErrNotAllowedToRead(mm ...*recordActionProps) *errors.Error {
|
||||
var p = &recordActionProps{}
|
||||
if len(mm) > 0 {
|
||||
@@ -1126,9 +1074,7 @@ func RecordErrNotAllowedToRead(mm ...*recordActionProps) *errors.Error {
|
||||
|
||||
// RecordErrNotAllowedToSearch returns "compose:record.notAllowedToSearch" as *errors.Error
|
||||
//
|
||||
//
|
||||
// This function is auto-generated.
|
||||
//
|
||||
func RecordErrNotAllowedToSearch(mm ...*recordActionProps) *errors.Error {
|
||||
var p = &recordActionProps{}
|
||||
if len(mm) > 0 {
|
||||
@@ -1162,9 +1108,7 @@ func RecordErrNotAllowedToSearch(mm ...*recordActionProps) *errors.Error {
|
||||
|
||||
// RecordErrNotAllowedToSearchRevisions returns "compose:record.notAllowedToSearchRevisions" as *errors.Error
|
||||
//
|
||||
//
|
||||
// This function is auto-generated.
|
||||
//
|
||||
func RecordErrNotAllowedToSearchRevisions(mm ...*recordActionProps) *errors.Error {
|
||||
var p = &recordActionProps{}
|
||||
if len(mm) > 0 {
|
||||
@@ -1198,9 +1142,7 @@ func RecordErrNotAllowedToSearchRevisions(mm ...*recordActionProps) *errors.Erro
|
||||
|
||||
// RecordErrRevisionsDisabledOnModule returns "compose:record.revisionsDisabledOnModule" as *errors.Error
|
||||
//
|
||||
//
|
||||
// This function is auto-generated.
|
||||
//
|
||||
func RecordErrRevisionsDisabledOnModule(mm ...*recordActionProps) *errors.Error {
|
||||
var p = &recordActionProps{}
|
||||
if len(mm) > 0 {
|
||||
@@ -1234,9 +1176,7 @@ func RecordErrRevisionsDisabledOnModule(mm ...*recordActionProps) *errors.Error
|
||||
|
||||
// RecordErrNotAllowedToReadNamespace returns "compose:record.notAllowedToReadNamespace" as *errors.Error
|
||||
//
|
||||
//
|
||||
// This function is auto-generated.
|
||||
//
|
||||
func RecordErrNotAllowedToReadNamespace(mm ...*recordActionProps) *errors.Error {
|
||||
var p = &recordActionProps{}
|
||||
if len(mm) > 0 {
|
||||
@@ -1270,9 +1210,7 @@ func RecordErrNotAllowedToReadNamespace(mm ...*recordActionProps) *errors.Error
|
||||
|
||||
// RecordErrNotAllowedToReadModule returns "compose:record.notAllowedToReadModule" as *errors.Error
|
||||
//
|
||||
//
|
||||
// This function is auto-generated.
|
||||
//
|
||||
func RecordErrNotAllowedToReadModule(mm ...*recordActionProps) *errors.Error {
|
||||
var p = &recordActionProps{}
|
||||
if len(mm) > 0 {
|
||||
@@ -1306,9 +1244,7 @@ func RecordErrNotAllowedToReadModule(mm ...*recordActionProps) *errors.Error {
|
||||
|
||||
// RecordErrNotAllowedToListRecords returns "compose:record.notAllowedToListRecords" as *errors.Error
|
||||
//
|
||||
//
|
||||
// This function is auto-generated.
|
||||
//
|
||||
func RecordErrNotAllowedToListRecords(mm ...*recordActionProps) *errors.Error {
|
||||
var p = &recordActionProps{}
|
||||
if len(mm) > 0 {
|
||||
@@ -1342,9 +1278,7 @@ func RecordErrNotAllowedToListRecords(mm ...*recordActionProps) *errors.Error {
|
||||
|
||||
// RecordErrNotAllowedToCreate returns "compose:record.notAllowedToCreate" as *errors.Error
|
||||
//
|
||||
//
|
||||
// This function is auto-generated.
|
||||
//
|
||||
func RecordErrNotAllowedToCreate(mm ...*recordActionProps) *errors.Error {
|
||||
var p = &recordActionProps{}
|
||||
if len(mm) > 0 {
|
||||
@@ -1378,9 +1312,7 @@ func RecordErrNotAllowedToCreate(mm ...*recordActionProps) *errors.Error {
|
||||
|
||||
// RecordErrNotAllowedToUpdate returns "compose:record.notAllowedToUpdate" as *errors.Error
|
||||
//
|
||||
//
|
||||
// This function is auto-generated.
|
||||
//
|
||||
func RecordErrNotAllowedToUpdate(mm ...*recordActionProps) *errors.Error {
|
||||
var p = &recordActionProps{}
|
||||
if len(mm) > 0 {
|
||||
@@ -1414,9 +1346,7 @@ func RecordErrNotAllowedToUpdate(mm ...*recordActionProps) *errors.Error {
|
||||
|
||||
// RecordErrNotAllowedToDelete returns "compose:record.notAllowedToDelete" as *errors.Error
|
||||
//
|
||||
//
|
||||
// This function is auto-generated.
|
||||
//
|
||||
func RecordErrNotAllowedToDelete(mm ...*recordActionProps) *errors.Error {
|
||||
var p = &recordActionProps{}
|
||||
if len(mm) > 0 {
|
||||
@@ -1450,9 +1380,7 @@ func RecordErrNotAllowedToDelete(mm ...*recordActionProps) *errors.Error {
|
||||
|
||||
// RecordErrNotAllowedToUndelete returns "compose:record.notAllowedToUndelete" as *errors.Error
|
||||
//
|
||||
//
|
||||
// This function is auto-generated.
|
||||
//
|
||||
func RecordErrNotAllowedToUndelete(mm ...*recordActionProps) *errors.Error {
|
||||
var p = &recordActionProps{}
|
||||
if len(mm) > 0 {
|
||||
@@ -1486,9 +1414,7 @@ func RecordErrNotAllowedToUndelete(mm ...*recordActionProps) *errors.Error {
|
||||
|
||||
// RecordErrNotAllowedToChangeFieldValue returns "compose:record.notAllowedToChangeFieldValue" as *errors.Error
|
||||
//
|
||||
//
|
||||
// This function is auto-generated.
|
||||
//
|
||||
func RecordErrNotAllowedToChangeFieldValue(mm ...*recordActionProps) *errors.Error {
|
||||
var p = &recordActionProps{}
|
||||
if len(mm) > 0 {
|
||||
@@ -1520,11 +1446,43 @@ func RecordErrNotAllowedToChangeFieldValue(mm ...*recordActionProps) *errors.Err
|
||||
return e
|
||||
}
|
||||
|
||||
// RecordErrMissingPositionField returns "compose:record.missingPositionField" as *errors.Error
|
||||
//
|
||||
// RecordErrMaxRecordsReached returns "compose:record.maxRecordsReached" as *errors.Error
|
||||
//
|
||||
// This function is auto-generated.
|
||||
func RecordErrMaxRecordsReached(mm ...*recordActionProps) *errors.Error {
|
||||
var p = &recordActionProps{}
|
||||
if len(mm) > 0 {
|
||||
p = mm[0]
|
||||
}
|
||||
|
||||
var e = errors.New(
|
||||
errors.KindInternal,
|
||||
|
||||
p.Format("maximum number of records per namespace reached", nil),
|
||||
|
||||
errors.Meta("type", "maxRecordsReached"),
|
||||
errors.Meta("resource", "compose:record"),
|
||||
|
||||
// action log entry; no formatting, it will be applied inside recordAction fn.
|
||||
errors.Meta(recordLogMetaKey{}, "maximum number of records per namespace reached"),
|
||||
errors.Meta(recordPropsMetaKey{}, p),
|
||||
|
||||
// translation namespace & key
|
||||
errors.Meta(locale.ErrorMetaNamespace{}, "compose"),
|
||||
errors.Meta(locale.ErrorMetaKey{}, "record.errors.maxRecordsReached"),
|
||||
|
||||
errors.StackSkip(1),
|
||||
)
|
||||
|
||||
if len(mm) > 0 {
|
||||
}
|
||||
|
||||
return e
|
||||
}
|
||||
|
||||
// RecordErrMissingPositionField returns "compose:record.missingPositionField" as *errors.Error
|
||||
//
|
||||
// This function is auto-generated.
|
||||
func RecordErrMissingPositionField(mm ...*recordActionProps) *errors.Error {
|
||||
var p = &recordActionProps{}
|
||||
if len(mm) > 0 {
|
||||
@@ -1558,9 +1516,7 @@ func RecordErrMissingPositionField(mm ...*recordActionProps) *errors.Error {
|
||||
|
||||
// RecordErrInvalidPositionFieldKind returns "compose:record.invalidPositionFieldKind" as *errors.Error
|
||||
//
|
||||
//
|
||||
// This function is auto-generated.
|
||||
//
|
||||
func RecordErrInvalidPositionFieldKind(mm ...*recordActionProps) *errors.Error {
|
||||
var p = &recordActionProps{}
|
||||
if len(mm) > 0 {
|
||||
@@ -1594,9 +1550,7 @@ func RecordErrInvalidPositionFieldKind(mm ...*recordActionProps) *errors.Error {
|
||||
|
||||
// RecordErrInvalidPositionFieldConfigMultiValue returns "compose:record.invalidPositionFieldConfigMultiValue" as *errors.Error
|
||||
//
|
||||
//
|
||||
// This function is auto-generated.
|
||||
//
|
||||
func RecordErrInvalidPositionFieldConfigMultiValue(mm ...*recordActionProps) *errors.Error {
|
||||
var p = &recordActionProps{}
|
||||
if len(mm) > 0 {
|
||||
@@ -1630,9 +1584,7 @@ func RecordErrInvalidPositionFieldConfigMultiValue(mm ...*recordActionProps) *er
|
||||
|
||||
// RecordErrInvalidPositionValueType returns "compose:record.invalidPositionValueType" as *errors.Error
|
||||
//
|
||||
//
|
||||
// This function is auto-generated.
|
||||
//
|
||||
func RecordErrInvalidPositionValueType(mm ...*recordActionProps) *errors.Error {
|
||||
var p = &recordActionProps{}
|
||||
if len(mm) > 0 {
|
||||
@@ -1666,9 +1618,7 @@ func RecordErrInvalidPositionValueType(mm ...*recordActionProps) *errors.Error {
|
||||
|
||||
// RecordErrMissingGroupField returns "compose:record.missingGroupField" as *errors.Error
|
||||
//
|
||||
//
|
||||
// This function is auto-generated.
|
||||
//
|
||||
func RecordErrMissingGroupField(mm ...*recordActionProps) *errors.Error {
|
||||
var p = &recordActionProps{}
|
||||
if len(mm) > 0 {
|
||||
@@ -1702,9 +1652,7 @@ func RecordErrMissingGroupField(mm ...*recordActionProps) *errors.Error {
|
||||
|
||||
// RecordErrInvalidGroupFieldConfigMultiValue returns "compose:record.invalidGroupFieldConfigMultiValue" as *errors.Error
|
||||
//
|
||||
//
|
||||
// This function is auto-generated.
|
||||
//
|
||||
func RecordErrInvalidGroupFieldConfigMultiValue(mm ...*recordActionProps) *errors.Error {
|
||||
var p = &recordActionProps{}
|
||||
if len(mm) > 0 {
|
||||
@@ -1738,9 +1686,7 @@ func RecordErrInvalidGroupFieldConfigMultiValue(mm ...*recordActionProps) *error
|
||||
|
||||
// RecordErrImportSessionAlreadActive returns "compose:record.importSessionAlreadActive" as *errors.Error
|
||||
//
|
||||
//
|
||||
// This function is auto-generated.
|
||||
//
|
||||
func RecordErrImportSessionAlreadActive(mm ...*recordActionProps) *errors.Error {
|
||||
var p = &recordActionProps{}
|
||||
if len(mm) > 0 {
|
||||
@@ -1774,9 +1720,7 @@ func RecordErrImportSessionAlreadActive(mm ...*recordActionProps) *errors.Error
|
||||
|
||||
// RecordErrFieldNotFound returns "compose:record.fieldNotFound" as *errors.Error
|
||||
//
|
||||
//
|
||||
// This function is auto-generated.
|
||||
//
|
||||
func RecordErrFieldNotFound(mm ...*recordActionProps) *errors.Error {
|
||||
var p = &recordActionProps{}
|
||||
if len(mm) > 0 {
|
||||
@@ -1808,9 +1752,7 @@ func RecordErrFieldNotFound(mm ...*recordActionProps) *errors.Error {
|
||||
|
||||
// RecordErrInvalidValueStructure returns "compose:record.invalidValueStructure" as *errors.Error
|
||||
//
|
||||
//
|
||||
// This function is auto-generated.
|
||||
//
|
||||
func RecordErrInvalidValueStructure(mm ...*recordActionProps) *errors.Error {
|
||||
var p = &recordActionProps{}
|
||||
if len(mm) > 0 {
|
||||
@@ -1842,9 +1784,7 @@ func RecordErrInvalidValueStructure(mm ...*recordActionProps) *errors.Error {
|
||||
|
||||
// RecordErrUnknownBulkOperation returns "compose:record.unknownBulkOperation" as *errors.Error
|
||||
//
|
||||
//
|
||||
// This function is auto-generated.
|
||||
//
|
||||
func RecordErrUnknownBulkOperation(mm ...*recordActionProps) *errors.Error {
|
||||
var p = &recordActionProps{}
|
||||
if len(mm) > 0 {
|
||||
@@ -1876,9 +1816,7 @@ func RecordErrUnknownBulkOperation(mm ...*recordActionProps) *errors.Error {
|
||||
|
||||
// RecordErrInvalidReferenceFormat returns "compose:record.invalidReferenceFormat" as *errors.Error
|
||||
//
|
||||
//
|
||||
// This function is auto-generated.
|
||||
//
|
||||
func RecordErrInvalidReferenceFormat(mm ...*recordActionProps) *errors.Error {
|
||||
var p = &recordActionProps{}
|
||||
if len(mm) > 0 {
|
||||
@@ -1910,9 +1848,7 @@ func RecordErrInvalidReferenceFormat(mm ...*recordActionProps) *errors.Error {
|
||||
|
||||
// RecordErrValueInput returns "compose:record.valueInput" as *errors.Error
|
||||
//
|
||||
//
|
||||
// This function is auto-generated.
|
||||
//
|
||||
func RecordErrValueInput(mm ...*recordActionProps) *errors.Error {
|
||||
var p = &recordActionProps{}
|
||||
if len(mm) > 0 {
|
||||
@@ -1950,7 +1886,6 @@ func RecordErrValueInput(mm ...*recordActionProps) *errors.Error {
|
||||
// It will wrap unrecognized/internal errors with generic errors.
|
||||
//
|
||||
// This function is auto-generated.
|
||||
//
|
||||
func (svc record) recordAction(ctx context.Context, props *recordActionProps, actionFn func(...*recordActionProps) *recordAction, err error) error {
|
||||
if svc.actionlog == nil || actionFn == nil {
|
||||
// action log disabled or no action fn passed, return error as-is
|
||||
|
||||
@@ -183,6 +183,10 @@ errors:
|
||||
message: "not allowed to change value of field {{field}}"
|
||||
log: "failed to change value of field {{field}}; insufficient permissions"
|
||||
|
||||
- error: maxRecordsReached
|
||||
message: "maximum number of records per namespace reached"
|
||||
log: "maximum number of records per namespace reached"
|
||||
|
||||
# Organizer
|
||||
- error: missingPositionField
|
||||
message: "position module field not found"
|
||||
|
||||
@@ -42,6 +42,7 @@ type (
|
||||
ActionLog options.ActionLogOpt
|
||||
Discovery options.DiscoveryOpt
|
||||
Storage options.ObjectStoreOpt
|
||||
Limit options.LimitOpt
|
||||
UserFinder userFinder
|
||||
SchemaAltManager schemaAltManager
|
||||
}
|
||||
@@ -185,7 +186,7 @@ func Initialize(ctx context.Context, log *zap.Logger, s store.Storer, c Config)
|
||||
DefaultModule = Module(c.SchemaAltManager)
|
||||
|
||||
DefaultImportSession = ImportSession()
|
||||
DefaultRecord = Record()
|
||||
DefaultRecord = Record(RecordOptions{LimitRecords: c.Limit.RecordCountPerNamespace})
|
||||
DefaultPage = Page()
|
||||
DefaultPageLayout = PageLayout()
|
||||
DefaultChart = Chart()
|
||||
|
||||
@@ -1,13 +1,14 @@
|
||||
package rest
|
||||
|
||||
import (
|
||||
"github.com/cortezaproject/corteza/server/pkg/options"
|
||||
"github.com/go-chi/chi/v5"
|
||||
|
||||
"github.com/cortezaproject/corteza/server/federation/rest/handlers"
|
||||
"github.com/cortezaproject/corteza/server/pkg/auth"
|
||||
)
|
||||
|
||||
func MountRoutes() func(r chi.Router) {
|
||||
func MountRoutes(opts options.LimitOpt) func(r chi.Router) {
|
||||
return func(r chi.Router) {
|
||||
r.Group(func(r chi.Router) {
|
||||
handlers.NewNodeHandshake(NodeHandshake{}.New()).MountRoutes(r)
|
||||
@@ -22,7 +23,7 @@ func MountRoutes() func(r chi.Router) {
|
||||
handlers.NewNode(Node{}.New()).MountRoutes(r)
|
||||
handlers.NewManageStructure((ManageStructure{}.New())).MountRoutes(r)
|
||||
|
||||
handlers.NewSyncData((SyncData{}.New())).MountRoutes(r)
|
||||
handlers.NewSyncData((SyncData{}.New(opts))).MountRoutes(r)
|
||||
handlers.NewSyncStructure((SyncStructure{}.New())).MountRoutes(r)
|
||||
})
|
||||
}
|
||||
|
||||
@@ -3,6 +3,7 @@ package rest
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"github.com/cortezaproject/corteza/server/pkg/options"
|
||||
"net/http"
|
||||
"strings"
|
||||
"time"
|
||||
@@ -21,7 +22,9 @@ import (
|
||||
)
|
||||
|
||||
type (
|
||||
SyncData struct{}
|
||||
SyncData struct {
|
||||
opts options.LimitOpt
|
||||
}
|
||||
|
||||
recordPayload struct {
|
||||
*ct.Record
|
||||
@@ -50,8 +53,10 @@ type (
|
||||
}
|
||||
)
|
||||
|
||||
func (SyncData) New() *SyncData {
|
||||
return &SyncData{}
|
||||
func (SyncData) New(opts options.LimitOpt) *SyncData {
|
||||
return &SyncData{
|
||||
opts: opts,
|
||||
}
|
||||
}
|
||||
|
||||
func (ctrl SyncData) ReadExposedAll(ctx context.Context, r *request.SyncDataReadExposedAll) (interface{}, error) {
|
||||
@@ -95,7 +100,7 @@ func (ctrl SyncData) ReadExposedAll(ctx context.Context, r *request.SyncDataRead
|
||||
|
||||
// todo - handle error properly
|
||||
// @todo !!!
|
||||
if list, _, err := (cs.Record()).Find(ctx, rf); err != nil || len(list) == 0 {
|
||||
if list, _, err := (cs.Record(cs.RecordOptions{LimitRecords: ctrl.opts.RecordCountPerNamespace})).Find(ctx, rf); err != nil || len(list) == 0 {
|
||||
continue
|
||||
}
|
||||
|
||||
@@ -223,7 +228,7 @@ func (ctrl SyncData) readExposed(ctx context.Context, r *request.SyncDataReadExp
|
||||
}
|
||||
|
||||
// @todo !!!
|
||||
list, f, err := (cs.Record()).Find(ctx, f)
|
||||
list, f, err := (cs.Record(cs.RecordOptions{LimitRecords: ctrl.opts.RecordCountPerNamespace})).Find(ctx, f)
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
|
||||
@@ -49,6 +49,9 @@ type (
|
||||
// Search returns an iterator which can be used to access all if the bits
|
||||
Search(context.Context, *Model, filter.Filter) (Iterator, error)
|
||||
|
||||
// Count returns the total number of rows matching the filter
|
||||
Count(context.Context, *Model, filter.Filter) (uint, error)
|
||||
|
||||
// Analyze returns the operation analysis the connection can perform for the model
|
||||
Analyze(ctx context.Context, m *Model) (map[string]OpAnalysis, error)
|
||||
|
||||
|
||||
@@ -53,6 +53,7 @@ type (
|
||||
Update(ctx context.Context, mf ModelRef, operations OperationSet, rr ...ValueGetter) (err error)
|
||||
Search(ctx context.Context, mf ModelRef, operations OperationSet, f filter.Filter) (iter Iterator, err error)
|
||||
Lookup(ctx context.Context, mf ModelRef, operations OperationSet, lookup ValueGetter, dst ValueSetter) (err error)
|
||||
Count(ctx context.Context, mf ModelRef, operations OperationSet, f filter.Filter) (uint, error)
|
||||
Delete(ctx context.Context, mf ModelRef, operations OperationSet, vv ...ValueGetter) (err error)
|
||||
Truncate(ctx context.Context, mf ModelRef, operations OperationSet) (err error)
|
||||
|
||||
@@ -488,6 +489,21 @@ func (svc *service) Search(ctx context.Context, mf ModelRef, operations Operatio
|
||||
return cw.connection.Search(ctx, model, f)
|
||||
}
|
||||
|
||||
func (svc *service) Count(ctx context.Context, mf ModelRef, operations OperationSet, f filter.Filter) (cnt uint, err error) {
|
||||
if err = svc.canOpData(mf); err != nil {
|
||||
err = fmt.Errorf("cannot count data entry: %w", err)
|
||||
return
|
||||
}
|
||||
|
||||
model, cw, err := svc.storeOpPrep(ctx, mf, operations)
|
||||
if err != nil {
|
||||
err = fmt.Errorf("cannot count data entry: %w", err)
|
||||
return
|
||||
}
|
||||
|
||||
return cw.connection.Count(ctx, model, f)
|
||||
}
|
||||
|
||||
// Run returns an iterator based on the provided Pipeline
|
||||
// @todo consider moving the Search method to utilize this also
|
||||
func (svc *service) Run(ctx context.Context, pp Pipeline) (iter Iterator, err error) {
|
||||
|
||||
Generated
+2
-1
@@ -162,7 +162,8 @@ type (
|
||||
}
|
||||
|
||||
LimitOpt struct {
|
||||
SystemUsers int `env:"LIMIT_SYSTEM_USERS"`
|
||||
SystemUsers int `env:"LIMIT_SYSTEM_USERS"`
|
||||
RecordCountPerNamespace int `env:"LIMIT_RECORD_COUNT_PER_NAMESPACE"`
|
||||
}
|
||||
|
||||
LocaleOpt struct {
|
||||
|
||||
@@ -109,6 +109,13 @@ func (c *connection) Search(ctx context.Context, m *dal.Model, f filter.Filter)
|
||||
})
|
||||
}
|
||||
|
||||
func (c *connection) Count(ctx context.Context, m *dal.Model, f filter.Filter) (i uint, _ error) {
|
||||
return i, c.withModel(m, func(m *model) (err error) {
|
||||
i, err = m.Count(ctx, f)
|
||||
return
|
||||
})
|
||||
}
|
||||
|
||||
func (c *connection) Analyze(ctx context.Context, m *dal.Model) (a map[string]dal.OpAnalysis, err error) {
|
||||
// @todo somehow (probably operations) bring in the info what can be done
|
||||
// for now, since we're quite rigid on the drivers, this will do.
|
||||
|
||||
@@ -268,6 +268,52 @@ func (d *model) Search(f filter.Filter) (i *iterator, err error) {
|
||||
return
|
||||
}
|
||||
|
||||
func (d *model) Count(ctx context.Context, f filter.Filter) (c uint, err error) {
|
||||
var (
|
||||
aux = struct {
|
||||
Count uint `db:"count"`
|
||||
}{}
|
||||
|
||||
rows *sql.Rows
|
||||
exprs []exp.Expression
|
||||
|
||||
q = d.dialect.GOQU().
|
||||
From(d.table.Ident()).Select(goqu.COUNT(goqu.Star()).As("count"))
|
||||
)
|
||||
|
||||
for ident, val := range f.Constraints() {
|
||||
attrExpr, err := d.table.AttributeExpression(ident)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
exprs = append(exprs, exp.NewBooleanExpression(exp.EqOp, attrExpr, val))
|
||||
}
|
||||
|
||||
query, args, err := q.Where(exprs...).Limit(1).ToSQL()
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
rows, err = d.conn.QueryContext(ctx, query, args...)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
defer rows.Close()
|
||||
if !rows.Next() {
|
||||
return 0, errors.NotFound("not found")
|
||||
}
|
||||
|
||||
if err = rows.Scan(&aux.Count); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
c = aux.Count
|
||||
|
||||
return c, nil
|
||||
}
|
||||
|
||||
// Aggregate constructs SELECT sql with group-by and an optional having CLAUSE
|
||||
//
|
||||
// All group-by attributes are prepended to aggregation
|
||||
|
||||
@@ -64,7 +64,7 @@ func InitTestApp() {
|
||||
r = chi.NewRouter()
|
||||
r.Use(server.BaseMiddleware(false, logger.Default())...)
|
||||
helpers.BindAuthMiddleware(r)
|
||||
r.Group(rest.MountRoutes())
|
||||
r.Group(rest.MountRoutes(testApp.Opt.Limit))
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user