mirror of
https://github.com/zitadel/zitadel.git
synced 2025-02-28 21:07:22 +00:00
fix: env.json caching, readiness and unique lockerIDs (#3596)
* fix: readiness check * disable cache for env.json * always generate unique lockerID * fix tests
This commit is contained in:
parent
929ed8745a
commit
94e420bb24
@ -15,7 +15,6 @@ import (
|
||||
"github.com/zitadel/zitadel/internal/api/grpc/server"
|
||||
http_util "github.com/zitadel/zitadel/internal/api/http"
|
||||
"github.com/zitadel/zitadel/internal/authz/repository"
|
||||
"github.com/zitadel/zitadel/internal/domain"
|
||||
"github.com/zitadel/zitadel/internal/errors"
|
||||
"github.com/zitadel/zitadel/internal/query"
|
||||
"github.com/zitadel/zitadel/internal/telemetry/tracing"
|
||||
@ -139,19 +138,6 @@ func (a *API) healthHandler() http.Handler {
|
||||
}
|
||||
return nil
|
||||
},
|
||||
func(ctx context.Context) error {
|
||||
iam, err := a.health.Instance(ctx)
|
||||
if err != nil && !errors.IsNotFound(err) {
|
||||
return errors.ThrowPreconditionFailed(err, "API-dsgT2", "IAM SETUP CHECK FAILED")
|
||||
}
|
||||
if iam == nil || iam.SetupStarted < domain.StepCount-1 {
|
||||
return errors.ThrowPreconditionFailed(nil, "API-HBfs3", "IAM NOT SET UP")
|
||||
}
|
||||
if iam.SetupDone < domain.StepCount-1 {
|
||||
return errors.ThrowPreconditionFailed(nil, "API-DASs2", "IAM SETUP RUNNING")
|
||||
}
|
||||
return nil
|
||||
},
|
||||
}
|
||||
handler := http.NewServeMux()
|
||||
handler.HandleFunc("/healthz", handleHealth)
|
||||
|
@ -73,7 +73,7 @@ func Start(config Config, externalSecure bool, issuer op.IssuerFromRequest, inst
|
||||
security := middleware.SecurityHeaders(csp(), nil)
|
||||
|
||||
handler := mux.NewRouter()
|
||||
handler.Use(cache, security)
|
||||
handler.Use(security)
|
||||
handler.Handle(envRequestPath, middleware.TelemetryHandler()(instanceHandler(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
instance := authz.GetInstance(r.Context())
|
||||
if instance.InstanceID() == "" {
|
||||
@ -89,7 +89,7 @@ func Start(config Config, externalSecure bool, issuer op.IssuerFromRequest, inst
|
||||
_, err = w.Write(environmentJSON)
|
||||
logging.OnError(err).Error("error serving environment.json")
|
||||
}))))
|
||||
handler.SkipClean(true).PathPrefix("").Handler(http.FileServer(&spaHandler{http.FS(fSys)}))
|
||||
handler.SkipClean(true).PathPrefix("").Handler(cache(http.FileServer(&spaHandler{http.FS(fSys)})))
|
||||
return handler, nil
|
||||
}
|
||||
|
||||
|
@ -15,9 +15,6 @@ type InstanceWriteModel struct {
|
||||
State domain.InstanceState
|
||||
GeneratedDomain string
|
||||
|
||||
SetUpStarted domain.Step
|
||||
SetUpDone domain.Step
|
||||
|
||||
GlobalOrgID string
|
||||
ProjectID string
|
||||
DefaultLanguage language.Tag
|
||||
@ -53,12 +50,6 @@ func (wm *InstanceWriteModel) Reduce() error {
|
||||
wm.GlobalOrgID = e.OrgID
|
||||
case *instance.DefaultLanguageSetEvent:
|
||||
wm.DefaultLanguage = e.Language
|
||||
case *instance.SetupStepEvent:
|
||||
if e.Done {
|
||||
wm.SetUpDone = e.Step
|
||||
} else {
|
||||
wm.SetUpStarted = e.Step
|
||||
}
|
||||
}
|
||||
}
|
||||
return nil
|
||||
@ -78,9 +69,7 @@ func (wm *InstanceWriteModel) Query() *eventstore.SearchQueryBuilder {
|
||||
instance.InstanceDomainRemovedEventType,
|
||||
instance.ProjectSetEventType,
|
||||
instance.GlobalOrgSetEventType,
|
||||
instance.DefaultLanguageSetEventType,
|
||||
instance.SetupStartedEventType,
|
||||
instance.SetupDoneEventType).
|
||||
instance.DefaultLanguageSetEventType).
|
||||
Builder()
|
||||
}
|
||||
|
||||
|
@ -4,7 +4,6 @@ import (
|
||||
"context"
|
||||
"database/sql"
|
||||
"fmt"
|
||||
"os"
|
||||
"time"
|
||||
|
||||
"github.com/zitadel/logging"
|
||||
@ -34,11 +33,8 @@ type locker struct {
|
||||
}
|
||||
|
||||
func NewLocker(client *sql.DB, lockTable, projectionName string) Locker {
|
||||
workerName, err := os.Hostname()
|
||||
if err != nil || workerName == "" {
|
||||
workerName, err = id.SonyFlakeGenerator.Next()
|
||||
logging.OnError(err).Panic("unable to generate lockID")
|
||||
}
|
||||
workerName, err := id.SonyFlakeGenerator.Next()
|
||||
logging.OnError(err).Panic("unable to generate lockID")
|
||||
return &locker{
|
||||
client: client,
|
||||
lockStmt: fmt.Sprintf(lockStmtFormat, lockTable),
|
||||
|
@ -2,7 +2,6 @@ package spooler
|
||||
|
||||
import (
|
||||
"math/rand"
|
||||
"os"
|
||||
|
||||
"github.com/zitadel/logging"
|
||||
|
||||
@ -19,11 +18,8 @@ type Config struct {
|
||||
}
|
||||
|
||||
func (c *Config) New() *Spooler {
|
||||
lockID, err := os.Hostname()
|
||||
if err != nil || lockID == "" {
|
||||
lockID, err = id.SonyFlakeGenerator.Next()
|
||||
logging.OnError(err).Panic("unable to generate lockID")
|
||||
}
|
||||
lockID, err := id.SonyFlakeGenerator.Next()
|
||||
logging.OnError(err).Panic("unable to generate lockID")
|
||||
|
||||
//shuffle the handlers for better balance when running multiple pods
|
||||
rand.Shuffle(len(c.ViewHandlers), func(i, j int) {
|
||||
|
@ -11,7 +11,6 @@ import (
|
||||
"golang.org/x/text/language"
|
||||
|
||||
"github.com/zitadel/zitadel/internal/api/authz"
|
||||
"github.com/zitadel/zitadel/internal/domain"
|
||||
"github.com/zitadel/zitadel/internal/errors"
|
||||
"github.com/zitadel/zitadel/internal/query/projection"
|
||||
)
|
||||
@ -56,14 +55,6 @@ var (
|
||||
name: projection.InstanceColumnConsoleAppID,
|
||||
table: instanceTable,
|
||||
}
|
||||
InstanceColumnSetupStarted = Column{
|
||||
name: projection.InstanceColumnSetUpStarted,
|
||||
table: instanceTable,
|
||||
}
|
||||
InstanceColumnSetupDone = Column{
|
||||
name: projection.InstanceColumnSetUpDone,
|
||||
table: instanceTable,
|
||||
}
|
||||
InstanceColumnDefaultLanguage = Column{
|
||||
name: projection.InstanceColumnDefaultLanguage,
|
||||
table: instanceTable,
|
||||
@ -82,8 +73,6 @@ type Instance struct {
|
||||
ConsoleID string
|
||||
ConsoleAppID string
|
||||
DefaultLang language.Tag
|
||||
SetupStarted domain.Step
|
||||
SetupDone domain.Step
|
||||
Domains []*InstanceDomain
|
||||
host string
|
||||
}
|
||||
@ -211,8 +200,6 @@ func prepareInstanceQuery(host string) (sq.SelectBuilder, func(*sql.Row) (*Insta
|
||||
InstanceColumnProjectID.identifier(),
|
||||
InstanceColumnConsoleID.identifier(),
|
||||
InstanceColumnConsoleAppID.identifier(),
|
||||
InstanceColumnSetupStarted.identifier(),
|
||||
InstanceColumnSetupDone.identifier(),
|
||||
InstanceColumnDefaultLanguage.identifier(),
|
||||
).
|
||||
From(instanceTable.identifier()).PlaceholderFormat(sq.Dollar),
|
||||
@ -228,8 +215,6 @@ func prepareInstanceQuery(host string) (sq.SelectBuilder, func(*sql.Row) (*Insta
|
||||
&instance.IAMProjectID,
|
||||
&instance.ConsoleID,
|
||||
&instance.ConsoleAppID,
|
||||
&instance.SetupStarted,
|
||||
&instance.SetupDone,
|
||||
&lang,
|
||||
)
|
||||
if err != nil {
|
||||
@ -254,8 +239,6 @@ func prepareInstancesQuery() (sq.SelectBuilder, func(*sql.Rows) (*Instances, err
|
||||
InstanceColumnProjectID.identifier(),
|
||||
InstanceColumnConsoleID.identifier(),
|
||||
InstanceColumnConsoleAppID.identifier(),
|
||||
InstanceColumnSetupStarted.identifier(),
|
||||
InstanceColumnSetupDone.identifier(),
|
||||
InstanceColumnDefaultLanguage.identifier(),
|
||||
countColumn.identifier(),
|
||||
).From(instanceTable.identifier()).PlaceholderFormat(sq.Dollar),
|
||||
@ -276,8 +259,6 @@ func prepareInstancesQuery() (sq.SelectBuilder, func(*sql.Rows) (*Instances, err
|
||||
&instance.IAMProjectID,
|
||||
&instance.ConsoleID,
|
||||
&instance.ConsoleAppID,
|
||||
&instance.SetupStarted,
|
||||
&instance.SetupDone,
|
||||
&lang,
|
||||
&count,
|
||||
)
|
||||
@ -312,8 +293,6 @@ func prepareInstanceDomainQuery(host string) (sq.SelectBuilder, func(*sql.Rows)
|
||||
InstanceColumnProjectID.identifier(),
|
||||
InstanceColumnConsoleID.identifier(),
|
||||
InstanceColumnConsoleAppID.identifier(),
|
||||
InstanceColumnSetupStarted.identifier(),
|
||||
InstanceColumnSetupDone.identifier(),
|
||||
InstanceColumnDefaultLanguage.identifier(),
|
||||
InstanceDomainDomainCol.identifier(),
|
||||
InstanceDomainIsPrimaryCol.identifier(),
|
||||
@ -350,8 +329,6 @@ func prepareInstanceDomainQuery(host string) (sq.SelectBuilder, func(*sql.Rows)
|
||||
&instance.IAMProjectID,
|
||||
&instance.ConsoleID,
|
||||
&instance.ConsoleAppID,
|
||||
&instance.SetupStarted,
|
||||
&instance.SetupDone,
|
||||
&lang,
|
||||
&domain,
|
||||
&isPrimary,
|
||||
|
@ -11,7 +11,6 @@ import (
|
||||
sq "github.com/Masterminds/squirrel"
|
||||
"golang.org/x/text/language"
|
||||
|
||||
"github.com/zitadel/zitadel/internal/domain"
|
||||
errs "github.com/zitadel/zitadel/internal/errors"
|
||||
)
|
||||
|
||||
@ -41,8 +40,6 @@ func Test_InstancePrepares(t *testing.T) {
|
||||
` projections.instances.iam_project_id,`+
|
||||
` projections.instances.console_client_id,`+
|
||||
` projections.instances.console_app_id,`+
|
||||
` projections.instances.setup_started,`+
|
||||
` projections.instances.setup_done,`+
|
||||
` projections.instances.default_language`+
|
||||
` FROM projections.instances`),
|
||||
nil,
|
||||
@ -72,8 +69,6 @@ func Test_InstancePrepares(t *testing.T) {
|
||||
` projections.instances.iam_project_id,`+
|
||||
` projections.instances.console_client_id,`+
|
||||
` projections.instances.console_app_id,`+
|
||||
` projections.instances.setup_started,`+
|
||||
` projections.instances.setup_done,`+
|
||||
` projections.instances.default_language`+
|
||||
` FROM projections.instances`),
|
||||
[]string{
|
||||
@ -85,8 +80,6 @@ func Test_InstancePrepares(t *testing.T) {
|
||||
"iam_project_id",
|
||||
"console_client_id",
|
||||
"console_app_id",
|
||||
"setup_started",
|
||||
"setup_done",
|
||||
"default_language",
|
||||
},
|
||||
[]driver.Value{
|
||||
@ -98,8 +91,6 @@ func Test_InstancePrepares(t *testing.T) {
|
||||
"project-id",
|
||||
"client-id",
|
||||
"app-id",
|
||||
domain.Step2,
|
||||
domain.Step1,
|
||||
"en",
|
||||
},
|
||||
),
|
||||
@ -113,8 +104,6 @@ func Test_InstancePrepares(t *testing.T) {
|
||||
IAMProjectID: "project-id",
|
||||
ConsoleID: "client-id",
|
||||
ConsoleAppID: "app-id",
|
||||
SetupStarted: domain.Step2,
|
||||
SetupDone: domain.Step1,
|
||||
DefaultLang: language.English,
|
||||
},
|
||||
},
|
||||
@ -133,8 +122,6 @@ func Test_InstancePrepares(t *testing.T) {
|
||||
` projections.instances.iam_project_id,`+
|
||||
` projections.instances.console_client_id,`+
|
||||
` projections.instances.console_app_id,`+
|
||||
` projections.instances.setup_started,`+
|
||||
` projections.instances.setup_done,`+
|
||||
` projections.instances.default_language`+
|
||||
` FROM projections.instances`),
|
||||
sql.ErrConnDone,
|
||||
|
@ -22,8 +22,6 @@ const (
|
||||
InstanceColumnConsoleID = "console_client_id"
|
||||
InstanceColumnConsoleAppID = "console_app_id"
|
||||
InstanceColumnSequence = "sequence"
|
||||
InstanceColumnSetUpStarted = "setup_started"
|
||||
InstanceColumnSetUpDone = "setup_done"
|
||||
InstanceColumnDefaultLanguage = "default_language"
|
||||
)
|
||||
|
||||
@ -46,8 +44,6 @@ func NewInstanceProjection(ctx context.Context, config crdb.StatementHandlerConf
|
||||
crdb.NewColumn(InstanceColumnConsoleID, crdb.ColumnTypeText, crdb.Default("")),
|
||||
crdb.NewColumn(InstanceColumnConsoleAppID, crdb.ColumnTypeText, crdb.Default("")),
|
||||
crdb.NewColumn(InstanceColumnSequence, crdb.ColumnTypeInt64),
|
||||
crdb.NewColumn(InstanceColumnSetUpStarted, crdb.ColumnTypeInt64, crdb.Default(0)),
|
||||
crdb.NewColumn(InstanceColumnSetUpDone, crdb.ColumnTypeInt64, crdb.Default(0)),
|
||||
crdb.NewColumn(InstanceColumnDefaultLanguage, crdb.ColumnTypeText, crdb.Default("")),
|
||||
},
|
||||
crdb.NewPrimaryKey(InstanceColumnID),
|
||||
@ -82,14 +78,6 @@ func (p *InstanceProjection) reducers() []handler.AggregateReducer {
|
||||
Event: instance.DefaultLanguageSetEventType,
|
||||
Reduce: p.reduceDefaultLanguageSet,
|
||||
},
|
||||
{
|
||||
Event: instance.SetupStartedEventType,
|
||||
Reduce: p.reduceSetupEvent,
|
||||
},
|
||||
{
|
||||
Event: instance.SetupDoneEventType,
|
||||
Reduce: p.reduceSetupEvent,
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
@ -184,24 +172,3 @@ func (p *InstanceProjection) reduceDefaultLanguageSet(event eventstore.Event) (*
|
||||
},
|
||||
), nil
|
||||
}
|
||||
|
||||
func (p *InstanceProjection) reduceSetupEvent(event eventstore.Event) (*handler.Statement, error) {
|
||||
e, ok := event.(*instance.SetupStepEvent)
|
||||
if !ok {
|
||||
return nil, errors.ThrowInvalidArgumentf(nil, "HANDL-d9nfw", "reduce.wrong.event.type %v", []eventstore.EventType{instance.SetupDoneEventType, instance.SetupStartedEventType})
|
||||
}
|
||||
columns := []handler.Column{
|
||||
handler.NewCol(InstanceColumnID, e.Aggregate().InstanceID),
|
||||
handler.NewCol(InstanceColumnChangeDate, e.CreationDate()),
|
||||
handler.NewCol(InstanceColumnSequence, e.Sequence()),
|
||||
}
|
||||
if e.EventType == instance.SetupStartedEventType {
|
||||
columns = append(columns, handler.NewCol(InstanceColumnSetUpStarted, e.Step))
|
||||
} else {
|
||||
columns = append(columns, handler.NewCol(InstanceColumnSetUpDone, e.Step))
|
||||
}
|
||||
return crdb.NewUpsertStatement(
|
||||
e,
|
||||
columns,
|
||||
), nil
|
||||
}
|
||||
|
@ -3,7 +3,6 @@ package projection
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/zitadel/zitadel/internal/domain"
|
||||
"github.com/zitadel/zitadel/internal/errors"
|
||||
"github.com/zitadel/zitadel/internal/eventstore"
|
||||
"github.com/zitadel/zitadel/internal/eventstore/handler"
|
||||
@ -141,66 +140,6 @@ func TestInstanceProjection_reduces(t *testing.T) {
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "reduceSetupStarted",
|
||||
args: args{
|
||||
event: getEvent(testEvent(
|
||||
repository.EventType(instance.SetupStartedEventType),
|
||||
instance.AggregateType,
|
||||
[]byte(`{"Step": 1}`),
|
||||
), instance.SetupStepMapper),
|
||||
},
|
||||
reduce: (&InstanceProjection{}).reduceSetupEvent,
|
||||
want: wantReduce{
|
||||
projection: InstanceProjectionTable,
|
||||
aggregateType: eventstore.AggregateType("instance"),
|
||||
sequence: 15,
|
||||
previousSequence: 10,
|
||||
executer: &testExecuter{
|
||||
executions: []execution{
|
||||
{
|
||||
expectedStmt: "UPSERT INTO projections.instances (id, change_date, sequence, setup_started) VALUES ($1, $2, $3, $4)",
|
||||
expectedArgs: []interface{}{
|
||||
"instance-id",
|
||||
anyArg{},
|
||||
uint64(15),
|
||||
domain.Step1,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "reduceSetupDone",
|
||||
args: args{
|
||||
event: getEvent(testEvent(
|
||||
repository.EventType(instance.SetupDoneEventType),
|
||||
instance.AggregateType,
|
||||
[]byte(`{"Step": 1}`),
|
||||
), instance.SetupStepMapper),
|
||||
},
|
||||
reduce: (&InstanceProjection{}).reduceSetupEvent,
|
||||
want: wantReduce{
|
||||
projection: InstanceProjectionTable,
|
||||
aggregateType: eventstore.AggregateType("instance"),
|
||||
sequence: 15,
|
||||
previousSequence: 10,
|
||||
executer: &testExecuter{
|
||||
executions: []execution{
|
||||
{
|
||||
expectedStmt: "UPSERT INTO projections.instances (id, change_date, sequence, setup_done) VALUES ($1, $2, $3, $4)",
|
||||
expectedArgs: []interface{}{
|
||||
"instance-id",
|
||||
anyArg{},
|
||||
uint64(15),
|
||||
domain.Step1,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
|
@ -1,103 +0,0 @@
|
||||
package instance
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"strconv"
|
||||
|
||||
"github.com/zitadel/zitadel/internal/eventstore"
|
||||
|
||||
"github.com/zitadel/zitadel/internal/domain"
|
||||
"github.com/zitadel/zitadel/internal/errors"
|
||||
"github.com/zitadel/zitadel/internal/eventstore/repository"
|
||||
)
|
||||
|
||||
const (
|
||||
UniqueStepStarted = "stepstarted"
|
||||
UniqueStepDone = "stepdone"
|
||||
SetupDoneEventType eventstore.EventType = "iam.setup.done"
|
||||
SetupStartedEventType eventstore.EventType = "iam.setup.started"
|
||||
)
|
||||
|
||||
type SetupStepEvent struct {
|
||||
eventstore.BaseEvent `json:"-"`
|
||||
|
||||
Step domain.Step `json:"Step"`
|
||||
Done bool `json:"-"`
|
||||
}
|
||||
|
||||
func NewAddSetupStepStartedUniqueConstraint(step domain.Step) *eventstore.EventUniqueConstraint {
|
||||
return eventstore.NewAddEventUniqueConstraint(
|
||||
UniqueStepStarted,
|
||||
strconv.Itoa(int(step)),
|
||||
"Errors.Step.Started.AlreadyExists")
|
||||
}
|
||||
|
||||
func NewAddSetupStepDoneUniqueConstraint(step domain.Step) *eventstore.EventUniqueConstraint {
|
||||
return eventstore.NewAddEventUniqueConstraint(
|
||||
UniqueStepDone,
|
||||
strconv.Itoa(int(step)),
|
||||
"Errors.Step.Done.AlreadyExists")
|
||||
}
|
||||
|
||||
func (e *SetupStepEvent) Data() interface{} {
|
||||
return e
|
||||
}
|
||||
|
||||
func (e *SetupStepEvent) UniqueConstraints() []*eventstore.EventUniqueConstraint {
|
||||
if e.Done {
|
||||
return []*eventstore.EventUniqueConstraint{NewAddSetupStepDoneUniqueConstraint(e.Step)}
|
||||
} else {
|
||||
return []*eventstore.EventUniqueConstraint{NewAddSetupStepStartedUniqueConstraint(e.Step)}
|
||||
}
|
||||
}
|
||||
|
||||
func SetupStepMapper(event *repository.Event) (eventstore.Event, error) {
|
||||
step := &SetupStepEvent{
|
||||
BaseEvent: *eventstore.BaseEventFromRepo(event),
|
||||
Done: eventstore.EventType(event.Type) == SetupDoneEventType,
|
||||
Step: domain.Step1,
|
||||
}
|
||||
if len(event.Data) == 0 {
|
||||
return step, nil
|
||||
}
|
||||
err := json.Unmarshal(event.Data, step)
|
||||
if err != nil {
|
||||
return nil, errors.ThrowInternal(err, "IAM-O6rVg", "unable to unmarshal step")
|
||||
}
|
||||
|
||||
return step, nil
|
||||
}
|
||||
|
||||
func NewSetupStepDoneEvent(
|
||||
ctx context.Context,
|
||||
aggregate *eventstore.Aggregate,
|
||||
step domain.Step,
|
||||
) *SetupStepEvent {
|
||||
|
||||
return &SetupStepEvent{
|
||||
BaseEvent: *eventstore.NewBaseEventForPush(
|
||||
ctx,
|
||||
aggregate,
|
||||
SetupDoneEventType,
|
||||
),
|
||||
Step: step,
|
||||
Done: true,
|
||||
}
|
||||
}
|
||||
|
||||
func NewSetupStepStartedEvent(
|
||||
ctx context.Context,
|
||||
aggregate *eventstore.Aggregate,
|
||||
step domain.Step,
|
||||
) *SetupStepEvent {
|
||||
|
||||
return &SetupStepEvent{
|
||||
BaseEvent: *eventstore.NewBaseEventForPush(
|
||||
ctx,
|
||||
aggregate,
|
||||
SetupStartedEventType,
|
||||
),
|
||||
Step: step,
|
||||
}
|
||||
}
|
@ -5,9 +5,7 @@ import (
|
||||
)
|
||||
|
||||
func RegisterEventMappers(es *eventstore.Eventstore) {
|
||||
es.RegisterFilterEventMapper(SetupStartedEventType, SetupStepMapper).
|
||||
RegisterFilterEventMapper(SetupDoneEventType, SetupStepMapper).
|
||||
RegisterFilterEventMapper(GlobalOrgSetEventType, GlobalOrgSetMapper).
|
||||
es.RegisterFilterEventMapper(GlobalOrgSetEventType, GlobalOrgSetMapper).
|
||||
RegisterFilterEventMapper(ProjectSetEventType, ProjectSetMapper).
|
||||
RegisterFilterEventMapper(ConsoleSetEventType, ConsoleSetMapper).
|
||||
RegisterFilterEventMapper(DefaultLanguageSetEventType, DefaultLanguageSetMapper).
|
||||
|
Loading…
x
Reference in New Issue
Block a user