feat(v3alpha): read actions (#8357)

# Which Problems Are Solved

The current v3alpha actions APIs don't exactly adhere to the [new
resources API
design](https://zitadel.com/docs/apis/v3#standard-resources).

# How the Problems Are Solved

- **Improved ID access**: The aggregate ID is added to the resource
details object, so accessing resource IDs and constructing proto
messages for resources is easier
- **Explicit Instances**: Optionally, the instance can be explicitly
given in each request
- **Pagination**: A default search limit and a max search limit are
added to the defaults.yaml. They apply to the new v3 APIs (currently
only actions). The search query defaults are changed to ascending by
creation date, because this makes the pagination results the most
deterministic. The creation date is also added to the object details.
The bug with updated creation dates is fixed for executions and targets.
- **Removed Sequences**: Removed Sequence from object details and
ProcessedSequence from search details

# Additional Changes

Object details IDs are checked in unit test only if an empty ID is
expected. Centralizing the details check also makes this internal object
more flexible for future evolutions.

# Additional Context

- Closes #8169 
- Depends on https://github.com/zitadel/zitadel/pull/8225

---------

Co-authored-by: Silvan <silvan.reusser@gmail.com>
Co-authored-by: Stefan Benz <46600784+stebenz@users.noreply.github.com>
This commit is contained in:
Elio Bischof 2024-08-12 22:32:01 +02:00 committed by GitHub
parent 18c3f574a9
commit 042c438813
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
130 changed files with 3253 additions and 605 deletions

View File

@ -547,6 +547,10 @@ SystemDefaults:
PublicKeyLifetime: 30h # ZITADEL_SYSTEMDEFAULTS_KEYCONFIG_PUBLICKEYLIFETIME
# 8766h are 1 year
CertificateLifetime: 8766h # ZITADEL_SYSTEMDEFAULTS_KEYCONFIG_CERTIFICATELIFETIME
# DefaultQueryLimit limits the number of items that can be queried in a single v3 API search request without explicitly passing a limit.
DefaultQueryLimit: 100 # ZITADEL_SYSTEMDEFAULTS_DEFAULTQUERYLIMIT
# MaxQueryLimit limits the number of items that can be queried in a single v3 API search request with explicitly passing a limit.
MaxQueryLimit: 1000 # ZITADEL_SYSTEMDEFAULTS_MAXQUERYLIMIT
Actions:
HTTP:
@ -1056,8 +1060,10 @@ InternalAuthZ:
- "events.read"
- "milestones.read"
- "session.delete"
- "action.target.read"
- "action.target.write"
- "action.target.delete"
- "action.execution.read"
- "action.execution.write"
- "userschema.read"
- "userschema.write"
@ -1092,8 +1098,8 @@ InternalAuthZ:
- "project.grant.member.read"
- "events.read"
- "milestones.read"
- "execution.target.read"
- "execution.read"
- "action.target.read"
- "action.execution.read"
- "userschema.read"
- Role: "IAM_ORG_MANAGER"
Permissions:

View File

@ -436,7 +436,7 @@ func startAPIs(
if err := apis.RegisterService(ctx, feature_v2.CreateServer(commands, queries)); err != nil {
return nil, err
}
if err := apis.RegisterService(ctx, action_v3_alpha.CreateServer(commands, queries, domain.AllFunctions, apis.ListGrpcMethods, apis.ListGrpcServices)); err != nil {
if err := apis.RegisterService(ctx, action_v3_alpha.CreateServer(config.SystemDefaults, commands, queries, domain.AllFunctions, apis.ListGrpcMethods, apis.ListGrpcServices)); err != nil {
return nil, err
}
if err := apis.RegisterService(ctx, user_schema_v3_alpha.CreateServer(commands, queries)); err != nil {

View File

@ -466,7 +466,7 @@ message DeleteTargetResponse {
message SearchTargetsRequest {
// list limitations and ordering.
zitadel.resources.object.v3alpha.ListQuery query = 2;
zitadel.resources.object.v3alpha.SearchQuery query = 2;
// the field the result is sorted.
zitadel.resources.action.v3alpha.TargetFieldName sorting_column = 3 [
(grpc.gateway.protoc_gen_openapiv2.options.openapiv2_field) = {
@ -526,7 +526,7 @@ message DeleteExecutionResponse {
message SearchExecutionsRequest {
// list limitations and ordering.
zitadel.resources.object.v3alpha.ListQuery query = 1;
zitadel.resources.object.v3alpha.SearchQuery query = 1;
// Define the criteria to query for.
repeated zitadel.resources.action.v3alpha.ExecutionSearchFilter filters = 2;
}

View File

@ -292,7 +292,7 @@ message DeleteIDPResponse {
message SearchIDPsRequest {
optional zitadel.object.v3alpha.RequestContext ctx = 1;
// list limitations and ordering.
zitadel.resources.object.v3alpha.ListQuery query = 2;
zitadel.resources.object.v3alpha.SearchQuery query = 2;
// the field the result is sorted.
zitadel.resources.idp.v3alpha.IDPFieldName sorting_column = 3 [
(grpc.gateway.protoc_gen_openapiv2.options.openapiv2_field) = {

View File

@ -29,7 +29,7 @@ type Instance interface {
type InstanceVerifier interface {
InstanceByHost(ctx context.Context, host, publicDomain string) (Instance, error)
InstanceByID(ctx context.Context) (Instance, error)
InstanceByID(ctx context.Context, id string) (Instance, error)
}
type instance struct {

View File

@ -4,7 +4,7 @@ import (
"context"
"github.com/zitadel/zitadel/internal/api/authz"
settings_object "github.com/zitadel/zitadel/internal/api/grpc/settings/object/v3alpha"
resource_object "github.com/zitadel/zitadel/internal/api/grpc/resources/object/v3alpha"
"github.com/zitadel/zitadel/internal/command"
"github.com/zitadel/zitadel/internal/domain"
"github.com/zitadel/zitadel/internal/repository/execution"
@ -14,7 +14,7 @@ import (
)
func (s *Server) SetExecution(ctx context.Context, req *action.SetExecutionRequest) (*action.SetExecutionResponse, error) {
if err := checkExecutionEnabled(ctx); err != nil {
if err := checkActionsEnabled(ctx); err != nil {
return nil, err
}
reqTargets := req.GetExecution().GetTargets()
@ -34,24 +34,21 @@ func (s *Server) SetExecution(ctx context.Context, req *action.SetExecutionReque
set := &command.SetExecution{
Targets: targets,
}
owner := &object.Owner{
Type: object.OwnerType_OWNER_TYPE_INSTANCE,
Id: authz.GetInstance(ctx).InstanceID(),
}
var err error
var details *domain.ObjectDetails
instanceID := authz.GetInstance(ctx).InstanceID()
switch t := req.GetCondition().GetConditionType().(type) {
case *action.Condition_Request:
cond := executionConditionFromRequest(t.Request)
details, err = s.command.SetExecutionRequest(ctx, cond, set, owner.Id)
details, err = s.command.SetExecutionRequest(ctx, cond, set, instanceID)
case *action.Condition_Response:
cond := executionConditionFromResponse(t.Response)
details, err = s.command.SetExecutionResponse(ctx, cond, set, owner.Id)
details, err = s.command.SetExecutionResponse(ctx, cond, set, instanceID)
case *action.Condition_Event:
cond := executionConditionFromEvent(t.Event)
details, err = s.command.SetExecutionEvent(ctx, cond, set, owner.Id)
details, err = s.command.SetExecutionEvent(ctx, cond, set, instanceID)
case *action.Condition_Function:
details, err = s.command.SetExecutionFunction(ctx, command.ExecutionFunctionCondition(t.Function.GetName()), set, owner.Id)
details, err = s.command.SetExecutionFunction(ctx, command.ExecutionFunctionCondition(t.Function.GetName()), set, instanceID)
default:
err = zerrors.ThrowInvalidArgument(nil, "ACTION-5r5Ju", "Errors.Execution.ConditionInvalid")
}
@ -59,7 +56,7 @@ func (s *Server) SetExecution(ctx context.Context, req *action.SetExecutionReque
return nil, err
}
return &action.SetExecutionResponse{
Details: settings_object.DomainToDetailsPb(details, owner),
Details: resource_object.DomainToDetailsPb(details, object.OwnerType_OWNER_TYPE_INSTANCE, instanceID),
}, nil
}

View File

@ -13,7 +13,7 @@ import (
"github.com/zitadel/zitadel/internal/integration"
object "github.com/zitadel/zitadel/pkg/grpc/object/v3alpha"
action "github.com/zitadel/zitadel/pkg/grpc/resources/action/v3alpha"
settings_object "github.com/zitadel/zitadel/pkg/grpc/settings/object/v3alpha"
resource_object "github.com/zitadel/zitadel/pkg/grpc/resources/object/v3alpha"
)
func executionTargetsSingleTarget(id string) []*action.ExecutionTargetType {
@ -25,8 +25,9 @@ func executionTargetsSingleInclude(include *action.Condition) []*action.Executio
}
func TestServer_SetExecution_Request(t *testing.T) {
ensureFeatureEnabled(t)
targetResp := Tester.CreateTarget(CTX, t, "", "https://notexisting", domain.TargetTypeWebhook, false)
_, instanceID, _, isolatedIAMOwnerCTX := Tester.UseIsolatedInstance(t, IAMOwnerCTX, SystemCTX)
ensureFeatureEnabled(t, isolatedIAMOwnerCTX)
targetResp := Tester.CreateTarget(isolatedIAMOwnerCTX, t, "", "https://notexisting", domain.TargetTypeWebhook, false)
tests := []struct {
name string
@ -51,7 +52,7 @@ func TestServer_SetExecution_Request(t *testing.T) {
},
{
name: "no condition, error",
ctx: CTX,
ctx: isolatedIAMOwnerCTX,
req: &action.SetExecutionRequest{
Condition: &action.Condition{
ConditionType: &action.Condition_Request{
@ -66,7 +67,7 @@ func TestServer_SetExecution_Request(t *testing.T) {
},
{
name: "method, not existing",
ctx: CTX,
ctx: isolatedIAMOwnerCTX,
req: &action.SetExecutionRequest{
Condition: &action.Condition{
ConditionType: &action.Condition_Request{
@ -85,7 +86,7 @@ func TestServer_SetExecution_Request(t *testing.T) {
},
{
name: "method, ok",
ctx: CTX,
ctx: isolatedIAMOwnerCTX,
req: &action.SetExecutionRequest{
Condition: &action.Condition{
ConditionType: &action.Condition_Request{
@ -101,18 +102,18 @@ func TestServer_SetExecution_Request(t *testing.T) {
},
},
want: &action.SetExecutionResponse{
Details: &settings_object.Details{
ChangeDate: timestamppb.Now(),
Details: &resource_object.Details{
Changed: timestamppb.Now(),
Owner: &object.Owner{
Type: object.OwnerType_OWNER_TYPE_INSTANCE,
Id: Tester.Instance.InstanceID(),
Id: instanceID,
},
},
},
},
{
name: "service, not existing",
ctx: CTX,
ctx: isolatedIAMOwnerCTX,
req: &action.SetExecutionRequest{
Condition: &action.Condition{
ConditionType: &action.Condition_Request{
@ -131,7 +132,7 @@ func TestServer_SetExecution_Request(t *testing.T) {
},
{
name: "service, ok",
ctx: CTX,
ctx: isolatedIAMOwnerCTX,
req: &action.SetExecutionRequest{
Condition: &action.Condition{
ConditionType: &action.Condition_Request{
@ -147,18 +148,18 @@ func TestServer_SetExecution_Request(t *testing.T) {
},
},
want: &action.SetExecutionResponse{
Details: &settings_object.Details{
ChangeDate: timestamppb.Now(),
Details: &resource_object.Details{
Changed: timestamppb.Now(),
Owner: &object.Owner{
Type: object.OwnerType_OWNER_TYPE_INSTANCE,
Id: Tester.Instance.InstanceID(),
Id: instanceID,
},
},
},
},
{
name: "all, ok",
ctx: CTX,
ctx: isolatedIAMOwnerCTX,
req: &action.SetExecutionRequest{
Condition: &action.Condition{
ConditionType: &action.Condition_Request{
@ -174,11 +175,11 @@ func TestServer_SetExecution_Request(t *testing.T) {
},
},
want: &action.SetExecutionResponse{
Details: &settings_object.Details{
ChangeDate: timestamppb.Now(),
Details: &resource_object.Details{
Changed: timestamppb.Now(),
Owner: &object.Owner{
Type: object.OwnerType_OWNER_TYPE_INSTANCE,
Id: Tester.Instance.InstanceID(),
Id: instanceID,
},
},
},
@ -187,8 +188,8 @@ func TestServer_SetExecution_Request(t *testing.T) {
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
// We want to have the same response no matter how often we call the function
Client.SetExecution(tt.ctx, tt.req)
got, err := Client.SetExecution(tt.ctx, tt.req)
Tester.Client.ActionV3.SetExecution(tt.ctx, tt.req)
got, err := Tester.Client.ActionV3.SetExecution(tt.ctx, tt.req)
if tt.wantErr {
require.Error(t, err)
return
@ -196,7 +197,7 @@ func TestServer_SetExecution_Request(t *testing.T) {
require.NoError(t, err)
integration.AssertSettingsDetails(t, tt.want.Details, got.Details)
integration.AssertResourceDetails(t, tt.want.Details, got.Details)
// cleanup to not impact other requests
Tester.DeleteExecution(tt.ctx, t, tt.req.GetCondition())
@ -205,8 +206,9 @@ func TestServer_SetExecution_Request(t *testing.T) {
}
func TestServer_SetExecution_Request_Include(t *testing.T) {
ensureFeatureEnabled(t)
targetResp := Tester.CreateTarget(CTX, t, "", "https://notexisting", domain.TargetTypeWebhook, false)
_, instanceID, _, isolatedIAMOwnerCTX := Tester.UseIsolatedInstance(t, IAMOwnerCTX, SystemCTX)
ensureFeatureEnabled(t, isolatedIAMOwnerCTX)
targetResp := Tester.CreateTarget(isolatedIAMOwnerCTX, t, "", "https://notexisting", domain.TargetTypeWebhook, false)
executionCond := &action.Condition{
ConditionType: &action.Condition_Request{
Request: &action.RequestExecution{
@ -216,7 +218,7 @@ func TestServer_SetExecution_Request_Include(t *testing.T) {
},
},
}
Tester.SetExecution(CTX, t,
Tester.SetExecution(isolatedIAMOwnerCTX, t,
executionCond,
executionTargetsSingleTarget(targetResp.GetDetails().GetId()),
)
@ -230,7 +232,7 @@ func TestServer_SetExecution_Request_Include(t *testing.T) {
},
},
}
Tester.SetExecution(CTX, t,
Tester.SetExecution(isolatedIAMOwnerCTX, t,
circularExecutionService,
executionTargetsSingleInclude(executionCond),
)
@ -243,7 +245,7 @@ func TestServer_SetExecution_Request_Include(t *testing.T) {
},
},
}
Tester.SetExecution(CTX, t,
Tester.SetExecution(isolatedIAMOwnerCTX, t,
circularExecutionMethod,
executionTargetsSingleInclude(circularExecutionService),
)
@ -257,7 +259,7 @@ func TestServer_SetExecution_Request_Include(t *testing.T) {
}{
{
name: "method, circular error",
ctx: CTX,
ctx: isolatedIAMOwnerCTX,
req: &action.SetExecutionRequest{
Condition: circularExecutionService,
Execution: &action.Execution{
@ -268,7 +270,7 @@ func TestServer_SetExecution_Request_Include(t *testing.T) {
},
{
name: "method, ok",
ctx: CTX,
ctx: isolatedIAMOwnerCTX,
req: &action.SetExecutionRequest{
Condition: &action.Condition{
ConditionType: &action.Condition_Request{
@ -280,23 +282,22 @@ func TestServer_SetExecution_Request_Include(t *testing.T) {
},
},
Execution: &action.Execution{
Targets: executionTargetsSingleInclude(executionCond),
},
},
want: &action.SetExecutionResponse{
Details: &settings_object.Details{
ChangeDate: timestamppb.Now(),
Details: &resource_object.Details{
Changed: timestamppb.Now(),
Owner: &object.Owner{
Type: object.OwnerType_OWNER_TYPE_INSTANCE,
Id: Tester.Instance.InstanceID(),
Id: instanceID,
},
},
},
},
{
name: "service, ok",
ctx: CTX,
ctx: isolatedIAMOwnerCTX,
req: &action.SetExecutionRequest{
Condition: &action.Condition{
ConditionType: &action.Condition_Request{
@ -308,16 +309,15 @@ func TestServer_SetExecution_Request_Include(t *testing.T) {
},
},
Execution: &action.Execution{
Targets: executionTargetsSingleInclude(executionCond),
},
},
want: &action.SetExecutionResponse{
Details: &settings_object.Details{
ChangeDate: timestamppb.Now(),
Details: &resource_object.Details{
Changed: timestamppb.Now(),
Owner: &object.Owner{
Type: object.OwnerType_OWNER_TYPE_INSTANCE,
Id: Tester.Instance.InstanceID(),
Id: instanceID,
},
},
},
@ -326,15 +326,15 @@ func TestServer_SetExecution_Request_Include(t *testing.T) {
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
// We want to have the same response no matter how often we call the function
Client.SetExecution(tt.ctx, tt.req)
got, err := Client.SetExecution(tt.ctx, tt.req)
Tester.Client.ActionV3.SetExecution(tt.ctx, tt.req)
got, err := Tester.Client.ActionV3.SetExecution(tt.ctx, tt.req)
if tt.wantErr {
require.Error(t, err)
return
}
require.NoError(t, err)
integration.AssertSettingsDetails(t, tt.want.Details, got.Details)
integration.AssertResourceDetails(t, tt.want.Details, got.Details)
// cleanup to not impact other requests
Tester.DeleteExecution(tt.ctx, t, tt.req.GetCondition())
@ -343,8 +343,9 @@ func TestServer_SetExecution_Request_Include(t *testing.T) {
}
func TestServer_SetExecution_Response(t *testing.T) {
ensureFeatureEnabled(t)
targetResp := Tester.CreateTarget(CTX, t, "", "https://notexisting", domain.TargetTypeWebhook, false)
_, instanceID, _, isolatedIAMOwnerCTX := Tester.UseIsolatedInstance(t, IAMOwnerCTX, SystemCTX)
ensureFeatureEnabled(t, isolatedIAMOwnerCTX)
targetResp := Tester.CreateTarget(isolatedIAMOwnerCTX, t, "", "https://notexisting", domain.TargetTypeWebhook, false)
tests := []struct {
name string
@ -369,7 +370,7 @@ func TestServer_SetExecution_Response(t *testing.T) {
},
{
name: "no condition, error",
ctx: CTX,
ctx: isolatedIAMOwnerCTX,
req: &action.SetExecutionRequest{
Condition: &action.Condition{
ConditionType: &action.Condition_Response{
@ -384,7 +385,7 @@ func TestServer_SetExecution_Response(t *testing.T) {
},
{
name: "method, not existing",
ctx: CTX,
ctx: isolatedIAMOwnerCTX,
req: &action.SetExecutionRequest{
Condition: &action.Condition{
ConditionType: &action.Condition_Response{
@ -403,7 +404,7 @@ func TestServer_SetExecution_Response(t *testing.T) {
},
{
name: "method, ok",
ctx: CTX,
ctx: isolatedIAMOwnerCTX,
req: &action.SetExecutionRequest{
Condition: &action.Condition{
ConditionType: &action.Condition_Response{
@ -419,18 +420,18 @@ func TestServer_SetExecution_Response(t *testing.T) {
},
},
want: &action.SetExecutionResponse{
Details: &settings_object.Details{
ChangeDate: timestamppb.Now(),
Details: &resource_object.Details{
Changed: timestamppb.Now(),
Owner: &object.Owner{
Type: object.OwnerType_OWNER_TYPE_INSTANCE,
Id: Tester.Instance.InstanceID(),
Id: instanceID,
},
},
},
},
{
name: "service, not existing",
ctx: CTX,
ctx: isolatedIAMOwnerCTX,
req: &action.SetExecutionRequest{
Condition: &action.Condition{
ConditionType: &action.Condition_Response{
@ -449,7 +450,7 @@ func TestServer_SetExecution_Response(t *testing.T) {
},
{
name: "service, ok",
ctx: CTX,
ctx: isolatedIAMOwnerCTX,
req: &action.SetExecutionRequest{
Condition: &action.Condition{
ConditionType: &action.Condition_Response{
@ -465,18 +466,18 @@ func TestServer_SetExecution_Response(t *testing.T) {
},
},
want: &action.SetExecutionResponse{
Details: &settings_object.Details{
ChangeDate: timestamppb.Now(),
Details: &resource_object.Details{
Changed: timestamppb.Now(),
Owner: &object.Owner{
Type: object.OwnerType_OWNER_TYPE_INSTANCE,
Id: Tester.Instance.InstanceID(),
Id: instanceID,
},
},
},
},
{
name: "all, ok",
ctx: CTX,
ctx: isolatedIAMOwnerCTX,
req: &action.SetExecutionRequest{
Condition: &action.Condition{
ConditionType: &action.Condition_Response{
@ -492,11 +493,11 @@ func TestServer_SetExecution_Response(t *testing.T) {
},
},
want: &action.SetExecutionResponse{
Details: &settings_object.Details{
ChangeDate: timestamppb.Now(),
Details: &resource_object.Details{
Changed: timestamppb.Now(),
Owner: &object.Owner{
Type: object.OwnerType_OWNER_TYPE_INSTANCE,
Id: Tester.Instance.InstanceID(),
Id: instanceID,
},
},
},
@ -505,15 +506,15 @@ func TestServer_SetExecution_Response(t *testing.T) {
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
// We want to have the same response no matter how often we call the function
Client.SetExecution(tt.ctx, tt.req)
got, err := Client.SetExecution(tt.ctx, tt.req)
Tester.Client.ActionV3.SetExecution(tt.ctx, tt.req)
got, err := Tester.Client.ActionV3.SetExecution(tt.ctx, tt.req)
if tt.wantErr {
require.Error(t, err)
return
}
require.NoError(t, err)
integration.AssertSettingsDetails(t, tt.want.Details, got.Details)
integration.AssertResourceDetails(t, tt.want.Details, got.Details)
// cleanup to not impact other requests
Tester.DeleteExecution(tt.ctx, t, tt.req.GetCondition())
@ -522,8 +523,9 @@ func TestServer_SetExecution_Response(t *testing.T) {
}
func TestServer_SetExecution_Event(t *testing.T) {
ensureFeatureEnabled(t)
targetResp := Tester.CreateTarget(CTX, t, "", "https://notexisting", domain.TargetTypeWebhook, false)
_, instanceID, _, isolatedIAMOwnerCTX := Tester.UseIsolatedInstance(t, IAMOwnerCTX, SystemCTX)
ensureFeatureEnabled(t, isolatedIAMOwnerCTX)
targetResp := Tester.CreateTarget(isolatedIAMOwnerCTX, t, "", "https://notexisting", domain.TargetTypeWebhook, false)
tests := []struct {
name string
@ -550,7 +552,7 @@ func TestServer_SetExecution_Event(t *testing.T) {
},
{
name: "no condition, error",
ctx: CTX,
ctx: isolatedIAMOwnerCTX,
req: &action.SetExecutionRequest{
Condition: &action.Condition{
ConditionType: &action.Condition_Event{
@ -568,7 +570,7 @@ func TestServer_SetExecution_Event(t *testing.T) {
{
name: "event, not existing",
ctx: CTX,
ctx: isolatedIAMOwnerCTX,
req: &action.SetExecutionRequest{
Condition: &action.Condition{
ConditionType: &action.Condition_Event{
@ -586,7 +588,7 @@ func TestServer_SetExecution_Event(t *testing.T) {
*/
{
name: "event, ok",
ctx: CTX,
ctx: isolatedIAMOwnerCTX,
req: &action.SetExecutionRequest{
Condition: &action.Condition{
ConditionType: &action.Condition_Event{
@ -602,11 +604,11 @@ func TestServer_SetExecution_Event(t *testing.T) {
},
},
want: &action.SetExecutionResponse{
Details: &settings_object.Details{
ChangeDate: timestamppb.Now(),
Details: &resource_object.Details{
Changed: timestamppb.Now(),
Owner: &object.Owner{
Type: object.OwnerType_OWNER_TYPE_INSTANCE,
Id: Tester.Instance.InstanceID(),
Id: instanceID,
},
},
},
@ -616,7 +618,7 @@ func TestServer_SetExecution_Event(t *testing.T) {
{
name: "group, not existing",
ctx: CTX,
ctx: isolatedIAMOwnerCTX,
req: &action.SetExecutionRequest{
Condition: &action.Condition{
ConditionType: &action.Condition_Event{
@ -634,7 +636,7 @@ func TestServer_SetExecution_Event(t *testing.T) {
*/
{
name: "group, ok",
ctx: CTX,
ctx: isolatedIAMOwnerCTX,
req: &action.SetExecutionRequest{
Condition: &action.Condition{
ConditionType: &action.Condition_Event{
@ -650,18 +652,18 @@ func TestServer_SetExecution_Event(t *testing.T) {
},
},
want: &action.SetExecutionResponse{
Details: &settings_object.Details{
ChangeDate: timestamppb.Now(),
Details: &resource_object.Details{
Changed: timestamppb.Now(),
Owner: &object.Owner{
Type: object.OwnerType_OWNER_TYPE_INSTANCE,
Id: Tester.Instance.InstanceID(),
Id: instanceID,
},
},
},
},
{
name: "all, ok",
ctx: CTX,
ctx: isolatedIAMOwnerCTX,
req: &action.SetExecutionRequest{
Condition: &action.Condition{
ConditionType: &action.Condition_Event{
@ -677,11 +679,11 @@ func TestServer_SetExecution_Event(t *testing.T) {
},
},
want: &action.SetExecutionResponse{
Details: &settings_object.Details{
ChangeDate: timestamppb.Now(),
Details: &resource_object.Details{
Changed: timestamppb.Now(),
Owner: &object.Owner{
Type: object.OwnerType_OWNER_TYPE_INSTANCE,
Id: Tester.Instance.InstanceID(),
Id: instanceID,
},
},
},
@ -690,15 +692,15 @@ func TestServer_SetExecution_Event(t *testing.T) {
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
// We want to have the same response no matter how often we call the function
Client.SetExecution(tt.ctx, tt.req)
got, err := Client.SetExecution(tt.ctx, tt.req)
Tester.Client.ActionV3.SetExecution(tt.ctx, tt.req)
got, err := Tester.Client.ActionV3.SetExecution(tt.ctx, tt.req)
if tt.wantErr {
require.Error(t, err)
return
}
require.NoError(t, err)
integration.AssertSettingsDetails(t, tt.want.Details, got.Details)
integration.AssertResourceDetails(t, tt.want.Details, got.Details)
// cleanup to not impact other requests
Tester.DeleteExecution(tt.ctx, t, tt.req.GetCondition())
@ -707,8 +709,9 @@ func TestServer_SetExecution_Event(t *testing.T) {
}
func TestServer_SetExecution_Function(t *testing.T) {
ensureFeatureEnabled(t)
targetResp := Tester.CreateTarget(CTX, t, "", "https://notexisting", domain.TargetTypeWebhook, false)
_, instanceID, _, isolatedIAMOwnerCTX := Tester.UseIsolatedInstance(t, IAMOwnerCTX, SystemCTX)
ensureFeatureEnabled(t, isolatedIAMOwnerCTX)
targetResp := Tester.CreateTarget(isolatedIAMOwnerCTX, t, "", "https://notexisting", domain.TargetTypeWebhook, false)
tests := []struct {
name string
@ -733,7 +736,7 @@ func TestServer_SetExecution_Function(t *testing.T) {
},
{
name: "no condition, error",
ctx: CTX,
ctx: isolatedIAMOwnerCTX,
req: &action.SetExecutionRequest{
Condition: &action.Condition{
ConditionType: &action.Condition_Response{
@ -748,7 +751,7 @@ func TestServer_SetExecution_Function(t *testing.T) {
},
{
name: "function, not existing",
ctx: CTX,
ctx: isolatedIAMOwnerCTX,
req: &action.SetExecutionRequest{
Condition: &action.Condition{
ConditionType: &action.Condition_Function{
@ -763,7 +766,7 @@ func TestServer_SetExecution_Function(t *testing.T) {
},
{
name: "function, ok",
ctx: CTX,
ctx: isolatedIAMOwnerCTX,
req: &action.SetExecutionRequest{
Condition: &action.Condition{
ConditionType: &action.Condition_Function{
@ -775,11 +778,11 @@ func TestServer_SetExecution_Function(t *testing.T) {
},
},
want: &action.SetExecutionResponse{
Details: &settings_object.Details{
ChangeDate: timestamppb.Now(),
Details: &resource_object.Details{
Changed: timestamppb.Now(),
Owner: &object.Owner{
Type: object.OwnerType_OWNER_TYPE_INSTANCE,
Id: Tester.Instance.InstanceID(),
Id: instanceID,
},
},
},
@ -788,15 +791,15 @@ func TestServer_SetExecution_Function(t *testing.T) {
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
// We want to have the same response no matter how often we call the function
Client.SetExecution(tt.ctx, tt.req)
got, err := Client.SetExecution(tt.ctx, tt.req)
Tester.Client.ActionV3.SetExecution(tt.ctx, tt.req)
got, err := Tester.Client.ActionV3.SetExecution(tt.ctx, tt.req)
if tt.wantErr {
require.Error(t, err)
return
}
require.NoError(t, err)
integration.AssertSettingsDetails(t, tt.want.Details, got.Details)
integration.AssertResourceDetails(t, tt.want.Details, got.Details)
// cleanup to not impact other requests
Tester.DeleteExecution(tt.ctx, t, tt.req.GetCondition())

View File

@ -0,0 +1,335 @@
//go:build integration
package action_test
import (
"context"
"encoding/json"
"fmt"
"io"
"net/http"
"net/http/httptest"
"reflect"
"testing"
"time"
"github.com/stretchr/testify/require"
"google.golang.org/protobuf/types/known/durationpb"
"github.com/zitadel/zitadel/internal/api/grpc/server/middleware"
"github.com/zitadel/zitadel/internal/domain"
"github.com/zitadel/zitadel/internal/integration"
object "github.com/zitadel/zitadel/pkg/grpc/object/v3alpha"
action "github.com/zitadel/zitadel/pkg/grpc/resources/action/v3alpha"
resource_object "github.com/zitadel/zitadel/pkg/grpc/resources/object/v3alpha"
)
func TestServer_ExecutionTarget(t *testing.T) {
_, instanceID, _, isolatedIAMOwnerCTX := Tester.UseIsolatedInstance(t, IAMOwnerCTX, SystemCTX)
ensureFeatureEnabled(t, isolatedIAMOwnerCTX)
fullMethod := "/zitadel.resources.action.v3alpha.ZITADELActions/GetTarget"
tests := []struct {
name string
ctx context.Context
dep func(context.Context, *action.GetTargetRequest, *action.GetTargetResponse) (func(), error)
clean func(context.Context)
req *action.GetTargetRequest
want *action.GetTargetResponse
wantErr bool
}{
{
name: "GetTarget, request and response, ok",
ctx: isolatedIAMOwnerCTX,
dep: func(ctx context.Context, request *action.GetTargetRequest, response *action.GetTargetResponse) (func(), error) {
orgID := Tester.Organisation.ID
projectID := ""
userID := Tester.Users.Get(instanceID, integration.IAMOwner).ID
// create target for target changes
targetCreatedName := fmt.Sprint("GetTarget", time.Now().UnixNano()+1)
targetCreatedURL := "https://nonexistent"
targetCreated := Tester.CreateTarget(ctx, t, targetCreatedName, targetCreatedURL, domain.TargetTypeCall, false)
// request received by target
wantRequest := &middleware.ContextInfoRequest{FullMethod: fullMethod, InstanceID: instanceID, OrgID: orgID, ProjectID: projectID, UserID: userID, Request: request}
changedRequest := &action.GetTargetRequest{Id: targetCreated.GetDetails().GetId()}
// replace original request with different targetID
urlRequest, closeRequest := testServerCall(wantRequest, 0, http.StatusOK, changedRequest)
targetRequest := Tester.CreateTarget(ctx, t, "", urlRequest, domain.TargetTypeCall, false)
Tester.SetExecution(ctx, t, conditionRequestFullMethod(fullMethod), executionTargetsSingleTarget(targetRequest.GetDetails().GetId()))
// expected response from the GetTarget
expectedResponse := &action.GetTargetResponse{
Target: &action.GetTarget{
Config: &action.Target{
Name: targetCreatedName,
Endpoint: targetCreatedURL,
TargetType: &action.Target_RestCall{
RestCall: &action.SetRESTCall{
InterruptOnError: false,
},
},
Timeout: durationpb.New(10 * time.Second),
},
Details: targetCreated.GetDetails(),
},
}
// has to be set separately because of the pointers
response.Target = &action.GetTarget{
Details: targetCreated.GetDetails(),
Config: &action.Target{
Name: targetCreatedName,
TargetType: &action.Target_RestCall{
RestCall: &action.SetRESTCall{
InterruptOnError: false,
},
},
Timeout: durationpb.New(10 * time.Second),
Endpoint: targetCreatedURL,
},
}
// content for partial update
changedResponse := &action.GetTargetResponse{
Target: &action.GetTarget{
Details: &resource_object.Details{
Id: targetCreated.GetDetails().GetId(),
},
},
}
// response received by target
wantResponse := &middleware.ContextInfoResponse{
FullMethod: fullMethod,
InstanceID: instanceID,
OrgID: orgID,
ProjectID: projectID,
UserID: userID,
Request: changedRequest,
Response: expectedResponse,
}
// after request with different targetID, return changed response
targetResponseURL, closeResponse := testServerCall(wantResponse, 0, http.StatusOK, changedResponse)
targetResponse := Tester.CreateTarget(ctx, t, "", targetResponseURL, domain.TargetTypeCall, false)
Tester.SetExecution(ctx, t, conditionResponseFullMethod(fullMethod), executionTargetsSingleTarget(targetResponse.GetDetails().GetId()))
return func() {
closeRequest()
closeResponse()
}, nil
},
clean: func(ctx context.Context) {
Tester.DeleteExecution(ctx, t, conditionRequestFullMethod(fullMethod))
Tester.DeleteExecution(ctx, t, conditionResponseFullMethod(fullMethod))
},
req: &action.GetTargetRequest{
Id: "something",
},
want: &action.GetTargetResponse{
Target: &action.GetTarget{
Details: &resource_object.Details{
Id: "changed",
Owner: &object.Owner{
Type: object.OwnerType_OWNER_TYPE_INSTANCE,
Id: instanceID,
},
},
},
},
},
{
name: "GetTarget, request, interrupt",
ctx: isolatedIAMOwnerCTX,
dep: func(ctx context.Context, request *action.GetTargetRequest, response *action.GetTargetResponse) (func(), error) {
fullMethod := "/zitadel.resources.action.v3alpha.ZITADELActions/GetTarget"
orgID := Tester.Organisation.ID
projectID := ""
userID := Tester.Users.Get(instanceID, integration.IAMOwner).ID
// request received by target
wantRequest := &middleware.ContextInfoRequest{FullMethod: fullMethod, InstanceID: instanceID, OrgID: orgID, ProjectID: projectID, UserID: userID, Request: request}
urlRequest, closeRequest := testServerCall(wantRequest, 0, http.StatusInternalServerError, &action.GetTargetRequest{Id: "notchanged"})
targetRequest := Tester.CreateTarget(ctx, t, "", urlRequest, domain.TargetTypeCall, true)
Tester.SetExecution(ctx, t, conditionRequestFullMethod(fullMethod), executionTargetsSingleTarget(targetRequest.GetDetails().GetId()))
// GetTarget with used target
request.Id = targetRequest.GetDetails().GetId()
return func() {
closeRequest()
}, nil
},
clean: func(ctx context.Context) {
Tester.DeleteExecution(ctx, t, conditionRequestFullMethod(fullMethod))
},
req: &action.GetTargetRequest{},
wantErr: true,
},
{
name: "GetTarget, response, interrupt",
ctx: isolatedIAMOwnerCTX,
dep: func(ctx context.Context, request *action.GetTargetRequest, response *action.GetTargetResponse) (func(), error) {
fullMethod := "/zitadel.resources.action.v3alpha.ZITADELActions/GetTarget"
orgID := Tester.Organisation.ID
projectID := ""
userID := Tester.Users.Get(instanceID, integration.IAMOwner).ID
// create target for target changes
targetCreatedName := fmt.Sprint("GetTarget", time.Now().UnixNano()+1)
targetCreatedURL := "https://nonexistent"
targetCreated := Tester.CreateTarget(ctx, t, targetCreatedName, targetCreatedURL, domain.TargetTypeCall, false)
// GetTarget with used target
request.Id = targetCreated.GetDetails().GetId()
// expected response from the GetTarget
expectedResponse := &action.GetTargetResponse{
Target: &action.GetTarget{
Details: targetCreated.GetDetails(),
Config: &action.Target{
Name: targetCreatedName,
Endpoint: targetCreatedURL,
TargetType: &action.Target_RestCall{
RestCall: &action.SetRESTCall{
InterruptOnError: false,
},
},
Timeout: durationpb.New(10 * time.Second),
},
},
}
// content for partial update
changedResponse := &action.GetTargetResponse{
Target: &action.GetTarget{
Details: &resource_object.Details{
Id: "changed",
},
},
}
// response received by target
wantResponse := &middleware.ContextInfoResponse{
FullMethod: fullMethod,
InstanceID: instanceID,
OrgID: orgID,
ProjectID: projectID,
UserID: userID,
Request: request,
Response: expectedResponse,
}
// after request with different targetID, return changed response
targetResponseURL, closeResponse := testServerCall(wantResponse, 0, http.StatusInternalServerError, changedResponse)
targetResponse := Tester.CreateTarget(ctx, t, "", targetResponseURL, domain.TargetTypeCall, true)
Tester.SetExecution(ctx, t, conditionResponseFullMethod(fullMethod), executionTargetsSingleTarget(targetResponse.GetDetails().GetId()))
return func() {
closeResponse()
}, nil
},
clean: func(ctx context.Context) {
Tester.DeleteExecution(ctx, t, conditionResponseFullMethod(fullMethod))
},
req: &action.GetTargetRequest{},
wantErr: true,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
if tt.dep != nil {
close, err := tt.dep(tt.ctx, tt.req, tt.want)
require.NoError(t, err)
defer close()
}
got, err := Tester.Client.ActionV3.GetTarget(tt.ctx, tt.req)
if tt.wantErr {
require.Error(t, err)
return
}
require.NoError(t, err)
integration.AssertResourceDetails(t, tt.want.GetTarget().GetDetails(), got.GetTarget().GetDetails())
require.Equal(t, tt.want.GetTarget().GetConfig(), got.GetTarget().GetConfig())
if tt.clean != nil {
tt.clean(tt.ctx)
}
})
}
}
func conditionRequestFullMethod(fullMethod string) *action.Condition {
return &action.Condition{
ConditionType: &action.Condition_Request{
Request: &action.RequestExecution{
Condition: &action.RequestExecution_Method{
Method: fullMethod,
},
},
},
}
}
func conditionResponseFullMethod(fullMethod string) *action.Condition {
return &action.Condition{
ConditionType: &action.Condition_Response{
Response: &action.ResponseExecution{
Condition: &action.ResponseExecution_Method{
Method: fullMethod,
},
},
},
}
}
func testServerCall(
reqBody interface{},
sleep time.Duration,
statusCode int,
respBody interface{},
) (string, func()) {
handler := func(w http.ResponseWriter, r *http.Request) {
data, err := json.Marshal(reqBody)
if err != nil {
http.Error(w, "error, marshall: "+err.Error(), http.StatusInternalServerError)
return
}
sentBody, err := io.ReadAll(r.Body)
if err != nil {
http.Error(w, "error, read body: "+err.Error(), http.StatusInternalServerError)
return
}
if !reflect.DeepEqual(data, sentBody) {
http.Error(w, "error, equal:\n"+string(data)+"\nsent:\n"+string(sentBody), http.StatusInternalServerError)
return
}
if statusCode != http.StatusOK {
http.Error(w, "error, statusCode", statusCode)
return
}
time.Sleep(sleep)
w.Header().Set("Content-Type", "application/json")
resp, err := json.Marshal(respBody)
if err != nil {
http.Error(w, "error", http.StatusInternalServerError)
return
}
if _, err := io.WriteString(w, string(resp)); err != nil {
http.Error(w, "error", http.StatusInternalServerError)
return
}
}
server := httptest.NewServer(http.HandlerFunc(handler))
return server.URL, server.Close
}

View File

@ -0,0 +1,410 @@
package action
import (
"context"
"strings"
"google.golang.org/protobuf/types/known/durationpb"
resource_object "github.com/zitadel/zitadel/internal/api/grpc/resources/object/v3alpha"
"github.com/zitadel/zitadel/internal/command"
"github.com/zitadel/zitadel/internal/domain"
"github.com/zitadel/zitadel/internal/query"
"github.com/zitadel/zitadel/internal/zerrors"
object "github.com/zitadel/zitadel/pkg/grpc/object/v3alpha"
action "github.com/zitadel/zitadel/pkg/grpc/resources/action/v3alpha"
)
const (
conditionIDAllSegmentCount = 0
conditionIDRequestResponseServiceSegmentCount = 1
conditionIDRequestResponseMethodSegmentCount = 2
conditionIDEventGroupSegmentCount = 1
)
func (s *Server) GetTarget(ctx context.Context, req *action.GetTargetRequest) (*action.GetTargetResponse, error) {
if err := checkActionsEnabled(ctx); err != nil {
return nil, err
}
resp, err := s.query.GetTargetByID(ctx, req.GetId())
if err != nil {
return nil, err
}
return &action.GetTargetResponse{
Target: targetToPb(resp),
}, nil
}
type InstanceContext interface {
GetInstanceId() string
GetInstanceDomain() string
}
type Context interface {
GetOwner() InstanceContext
}
func (s *Server) SearchTargets(ctx context.Context, req *action.SearchTargetsRequest) (*action.SearchTargetsResponse, error) {
if err := checkActionsEnabled(ctx); err != nil {
return nil, err
}
queries, err := s.searchTargetsRequestToModel(req)
if err != nil {
return nil, err
}
resp, err := s.query.SearchTargets(ctx, queries)
if err != nil {
return nil, err
}
return &action.SearchTargetsResponse{
Result: targetsToPb(resp.Targets),
Details: resource_object.ToSearchDetailsPb(queries.SearchRequest, resp.SearchResponse),
}, nil
}
func (s *Server) SearchExecutions(ctx context.Context, req *action.SearchExecutionsRequest) (*action.SearchExecutionsResponse, error) {
if err := checkActionsEnabled(ctx); err != nil {
return nil, err
}
queries, err := s.searchExecutionsRequestToModel(req)
if err != nil {
return nil, err
}
resp, err := s.query.SearchExecutions(ctx, queries)
if err != nil {
return nil, err
}
return &action.SearchExecutionsResponse{
Result: executionsToPb(resp.Executions),
Details: resource_object.ToSearchDetailsPb(queries.SearchRequest, resp.SearchResponse),
}, nil
}
func targetsToPb(targets []*query.Target) []*action.GetTarget {
t := make([]*action.GetTarget, len(targets))
for i, target := range targets {
t[i] = targetToPb(target)
}
return t
}
func targetToPb(t *query.Target) *action.GetTarget {
target := &action.GetTarget{
Details: resource_object.DomainToDetailsPb(&t.ObjectDetails, object.OwnerType_OWNER_TYPE_INSTANCE, t.ResourceOwner),
Config: &action.Target{
Name: t.Name,
Timeout: durationpb.New(t.Timeout),
Endpoint: t.Endpoint,
},
}
switch t.TargetType {
case domain.TargetTypeWebhook:
target.Config.TargetType = &action.Target_RestWebhook{RestWebhook: &action.SetRESTWebhook{InterruptOnError: t.InterruptOnError}}
case domain.TargetTypeCall:
target.Config.TargetType = &action.Target_RestCall{RestCall: &action.SetRESTCall{InterruptOnError: t.InterruptOnError}}
case domain.TargetTypeAsync:
target.Config.TargetType = &action.Target_RestAsync{RestAsync: &action.SetRESTAsync{}}
default:
target.Config.TargetType = nil
}
return target
}
func (s *Server) searchTargetsRequestToModel(req *action.SearchTargetsRequest) (*query.TargetSearchQueries, error) {
offset, limit, asc, err := resource_object.SearchQueryPbToQuery(s.systemDefaults, req.Query)
if err != nil {
return nil, err
}
queries, err := targetQueriesToQuery(req.Filters)
if err != nil {
return nil, err
}
return &query.TargetSearchQueries{
SearchRequest: query.SearchRequest{
Offset: offset,
Limit: limit,
Asc: asc,
SortingColumn: targetFieldNameToSortingColumn(req.SortingColumn),
},
Queries: queries,
}, nil
}
func targetQueriesToQuery(queries []*action.TargetSearchFilter) (_ []query.SearchQuery, err error) {
q := make([]query.SearchQuery, len(queries))
for i, qry := range queries {
q[i], err = targetQueryToQuery(qry)
if err != nil {
return nil, err
}
}
return q, nil
}
func targetQueryToQuery(filter *action.TargetSearchFilter) (query.SearchQuery, error) {
switch q := filter.Filter.(type) {
case *action.TargetSearchFilter_TargetNameFilter:
return targetNameQueryToQuery(q.TargetNameFilter)
case *action.TargetSearchFilter_InTargetIdsFilter:
return targetInTargetIdsQueryToQuery(q.InTargetIdsFilter)
default:
return nil, zerrors.ThrowInvalidArgument(nil, "GRPC-vR9nC", "List.Query.Invalid")
}
}
func targetNameQueryToQuery(q *action.TargetNameFilter) (query.SearchQuery, error) {
return query.NewTargetNameSearchQuery(resource_object.TextMethodPbToQuery(q.Method), q.GetTargetName())
}
func targetInTargetIdsQueryToQuery(q *action.InTargetIDsFilter) (query.SearchQuery, error) {
return query.NewTargetInIDsSearchQuery(q.GetTargetIds())
}
// targetFieldNameToSortingColumn defaults to the creation date because this ensures deterministic pagination
func targetFieldNameToSortingColumn(field *action.TargetFieldName) query.Column {
if field == nil {
return query.TargetColumnCreationDate
}
switch *field {
case action.TargetFieldName_TARGET_FIELD_NAME_UNSPECIFIED:
return query.TargetColumnID
case action.TargetFieldName_TARGET_FIELD_NAME_ID:
return query.TargetColumnID
case action.TargetFieldName_TARGET_FIELD_NAME_CREATED_DATE:
return query.TargetColumnCreationDate
case action.TargetFieldName_TARGET_FIELD_NAME_CHANGED_DATE:
return query.TargetColumnChangeDate
case action.TargetFieldName_TARGET_FIELD_NAME_NAME:
return query.TargetColumnName
case action.TargetFieldName_TARGET_FIELD_NAME_TARGET_TYPE:
return query.TargetColumnTargetType
case action.TargetFieldName_TARGET_FIELD_NAME_URL:
return query.TargetColumnURL
case action.TargetFieldName_TARGET_FIELD_NAME_TIMEOUT:
return query.TargetColumnTimeout
case action.TargetFieldName_TARGET_FIELD_NAME_INTERRUPT_ON_ERROR:
return query.TargetColumnInterruptOnError
default:
return query.TargetColumnCreationDate
}
}
// executionFieldNameToSortingColumn defaults to the creation date because this ensures deterministic pagination
func executionFieldNameToSortingColumn(field *action.ExecutionFieldName) query.Column {
if field == nil {
return query.ExecutionColumnCreationDate
}
switch *field {
case action.ExecutionFieldName_EXECUTION_FIELD_NAME_UNSPECIFIED:
return query.ExecutionColumnID
case action.ExecutionFieldName_EXECUTION_FIELD_NAME_ID:
return query.ExecutionColumnID
case action.ExecutionFieldName_EXECUTION_FIELD_NAME_CREATED_DATE:
return query.ExecutionColumnCreationDate
case action.ExecutionFieldName_EXECUTION_FIELD_NAME_CHANGED_DATE:
return query.ExecutionColumnChangeDate
default:
return query.ExecutionColumnCreationDate
}
}
func (s *Server) searchExecutionsRequestToModel(req *action.SearchExecutionsRequest) (*query.ExecutionSearchQueries, error) {
offset, limit, asc, err := resource_object.SearchQueryPbToQuery(s.systemDefaults, req.Query)
if err != nil {
return nil, err
}
queries, err := executionQueriesToQuery(req.Filters)
if err != nil {
return nil, err
}
return &query.ExecutionSearchQueries{
SearchRequest: query.SearchRequest{
Offset: offset,
Limit: limit,
Asc: asc,
SortingColumn: executionFieldNameToSortingColumn(req.SortingColumn),
},
Queries: queries,
}, nil
}
func executionQueriesToQuery(queries []*action.ExecutionSearchFilter) (_ []query.SearchQuery, err error) {
q := make([]query.SearchQuery, len(queries))
for i, query := range queries {
q[i], err = executionQueryToQuery(query)
if err != nil {
return nil, err
}
}
return q, nil
}
func executionQueryToQuery(searchQuery *action.ExecutionSearchFilter) (query.SearchQuery, error) {
switch q := searchQuery.Filter.(type) {
case *action.ExecutionSearchFilter_InConditionsFilter:
return inConditionsQueryToQuery(q.InConditionsFilter)
case *action.ExecutionSearchFilter_ExecutionTypeFilter:
return executionTypeToQuery(q.ExecutionTypeFilter)
case *action.ExecutionSearchFilter_IncludeFilter:
include, err := conditionToInclude(q.IncludeFilter.GetInclude())
if err != nil {
return nil, err
}
return query.NewIncludeSearchQuery(include)
case *action.ExecutionSearchFilter_TargetFilter:
return query.NewTargetSearchQuery(q.TargetFilter.GetTargetId())
default:
return nil, zerrors.ThrowInvalidArgument(nil, "GRPC-vR9nC", "List.Query.Invalid")
}
}
func executionTypeToQuery(q *action.ExecutionTypeFilter) (query.SearchQuery, error) {
switch q.ExecutionType {
case action.ExecutionType_EXECUTION_TYPE_UNSPECIFIED:
return query.NewExecutionTypeSearchQuery(domain.ExecutionTypeUnspecified)
case action.ExecutionType_EXECUTION_TYPE_REQUEST:
return query.NewExecutionTypeSearchQuery(domain.ExecutionTypeRequest)
case action.ExecutionType_EXECUTION_TYPE_RESPONSE:
return query.NewExecutionTypeSearchQuery(domain.ExecutionTypeResponse)
case action.ExecutionType_EXECUTION_TYPE_EVENT:
return query.NewExecutionTypeSearchQuery(domain.ExecutionTypeEvent)
case action.ExecutionType_EXECUTION_TYPE_FUNCTION:
return query.NewExecutionTypeSearchQuery(domain.ExecutionTypeFunction)
default:
return query.NewExecutionTypeSearchQuery(domain.ExecutionTypeUnspecified)
}
}
func inConditionsQueryToQuery(q *action.InConditionsFilter) (query.SearchQuery, error) {
values := make([]string, len(q.GetConditions()))
for i, condition := range q.GetConditions() {
id, err := conditionToID(condition)
if err != nil {
return nil, err
}
values[i] = id
}
return query.NewExecutionInIDsSearchQuery(values)
}
func conditionToID(q *action.Condition) (string, error) {
switch t := q.GetConditionType().(type) {
case *action.Condition_Request:
cond := &command.ExecutionAPICondition{
Method: t.Request.GetMethod(),
Service: t.Request.GetService(),
All: t.Request.GetAll(),
}
return cond.ID(domain.ExecutionTypeRequest), nil
case *action.Condition_Response:
cond := &command.ExecutionAPICondition{
Method: t.Response.GetMethod(),
Service: t.Response.GetService(),
All: t.Response.GetAll(),
}
return cond.ID(domain.ExecutionTypeResponse), nil
case *action.Condition_Event:
cond := &command.ExecutionEventCondition{
Event: t.Event.GetEvent(),
Group: t.Event.GetGroup(),
All: t.Event.GetAll(),
}
return cond.ID(), nil
case *action.Condition_Function:
return command.ExecutionFunctionCondition(t.Function.GetName()).ID(), nil
default:
return "", zerrors.ThrowInvalidArgument(nil, "GRPC-vR9nC", "List.Query.Invalid")
}
}
func executionsToPb(executions []*query.Execution) []*action.GetExecution {
e := make([]*action.GetExecution, len(executions))
for i, execution := range executions {
e[i] = executionToPb(execution)
}
return e
}
func executionToPb(e *query.Execution) *action.GetExecution {
targets := make([]*action.ExecutionTargetType, len(e.Targets))
for i := range e.Targets {
switch e.Targets[i].Type {
case domain.ExecutionTargetTypeInclude:
targets[i] = &action.ExecutionTargetType{Type: &action.ExecutionTargetType_Include{Include: executionIDToCondition(e.Targets[i].Target)}}
case domain.ExecutionTargetTypeTarget:
targets[i] = &action.ExecutionTargetType{Type: &action.ExecutionTargetType_Target{Target: e.Targets[i].Target}}
case domain.ExecutionTargetTypeUnspecified:
continue
default:
continue
}
}
return &action.GetExecution{
Details: resource_object.DomainToDetailsPb(&e.ObjectDetails, object.OwnerType_OWNER_TYPE_INSTANCE, e.ResourceOwner),
Execution: &action.Execution{
Targets: targets,
},
}
}
func executionIDToCondition(include string) *action.Condition {
if strings.HasPrefix(include, domain.ExecutionTypeRequest.String()) {
return includeRequestToCondition(strings.TrimPrefix(include, domain.ExecutionTypeRequest.String()))
}
if strings.HasPrefix(include, domain.ExecutionTypeResponse.String()) {
return includeResponseToCondition(strings.TrimPrefix(include, domain.ExecutionTypeResponse.String()))
}
if strings.HasPrefix(include, domain.ExecutionTypeEvent.String()) {
return includeEventToCondition(strings.TrimPrefix(include, domain.ExecutionTypeEvent.String()))
}
if strings.HasPrefix(include, domain.ExecutionTypeFunction.String()) {
return includeFunctionToCondition(strings.TrimPrefix(include, domain.ExecutionTypeFunction.String()))
}
return nil
}
func includeRequestToCondition(id string) *action.Condition {
switch strings.Count(id, "/") {
case conditionIDRequestResponseMethodSegmentCount:
return &action.Condition{ConditionType: &action.Condition_Request{Request: &action.RequestExecution{Condition: &action.RequestExecution_Method{Method: id}}}}
case conditionIDRequestResponseServiceSegmentCount:
return &action.Condition{ConditionType: &action.Condition_Request{Request: &action.RequestExecution{Condition: &action.RequestExecution_Service{Service: strings.TrimPrefix(id, "/")}}}}
case conditionIDAllSegmentCount:
return &action.Condition{ConditionType: &action.Condition_Request{Request: &action.RequestExecution{Condition: &action.RequestExecution_All{All: true}}}}
default:
return nil
}
}
func includeResponseToCondition(id string) *action.Condition {
switch strings.Count(id, "/") {
case conditionIDRequestResponseMethodSegmentCount:
return &action.Condition{ConditionType: &action.Condition_Response{Response: &action.ResponseExecution{Condition: &action.ResponseExecution_Method{Method: id}}}}
case conditionIDRequestResponseServiceSegmentCount:
return &action.Condition{ConditionType: &action.Condition_Response{Response: &action.ResponseExecution{Condition: &action.ResponseExecution_Service{Service: strings.TrimPrefix(id, "/")}}}}
case conditionIDAllSegmentCount:
return &action.Condition{ConditionType: &action.Condition_Response{Response: &action.ResponseExecution{Condition: &action.ResponseExecution_All{All: true}}}}
default:
return nil
}
}
func includeEventToCondition(id string) *action.Condition {
switch strings.Count(id, "/") {
case conditionIDEventGroupSegmentCount:
if strings.HasSuffix(id, command.EventGroupSuffix) {
return &action.Condition{ConditionType: &action.Condition_Event{Event: &action.EventExecution{Condition: &action.EventExecution_Group{Group: strings.TrimSuffix(strings.TrimPrefix(id, "/"), command.EventGroupSuffix)}}}}
} else {
return &action.Condition{ConditionType: &action.Condition_Event{Event: &action.EventExecution{Condition: &action.EventExecution_Event{Event: strings.TrimPrefix(id, "/")}}}}
}
case conditionIDAllSegmentCount:
return &action.Condition{ConditionType: &action.Condition_Event{Event: &action.EventExecution{Condition: &action.EventExecution_All{All: true}}}}
default:
return nil
}
}
func includeFunctionToCondition(id string) *action.Condition {
return &action.Condition{ConditionType: &action.Condition_Function{Function: &action.FunctionExecution{Name: strings.TrimPrefix(id, "/")}}}
}

View File

@ -0,0 +1,898 @@
//go:build integration
package action_test
import (
"context"
"fmt"
"reflect"
"testing"
"time"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"google.golang.org/protobuf/types/known/durationpb"
"google.golang.org/protobuf/types/known/timestamppb"
"github.com/zitadel/zitadel/internal/domain"
"github.com/zitadel/zitadel/internal/integration"
object "github.com/zitadel/zitadel/pkg/grpc/object/v3alpha"
action "github.com/zitadel/zitadel/pkg/grpc/resources/action/v3alpha"
resource_object "github.com/zitadel/zitadel/pkg/grpc/resources/object/v3alpha"
)
func TestServer_GetTarget(t *testing.T) {
_, _, _, isolatedIAMOwnerCTX := Tester.UseIsolatedInstance(t, IAMOwnerCTX, SystemCTX)
ensureFeatureEnabled(t, isolatedIAMOwnerCTX)
type args struct {
ctx context.Context
dep func(context.Context, *action.GetTargetRequest, *action.GetTargetResponse) error
req *action.GetTargetRequest
}
tests := []struct {
name string
args args
want *action.GetTargetResponse
wantErr bool
}{
{
name: "missing permission",
args: args{
ctx: Tester.WithAuthorization(context.Background(), integration.OrgOwner),
req: &action.GetTargetRequest{},
},
wantErr: true,
},
{
name: "not found",
args: args{
ctx: isolatedIAMOwnerCTX,
req: &action.GetTargetRequest{Id: "notexisting"},
},
wantErr: true,
},
{
name: "get, ok",
args: args{
ctx: isolatedIAMOwnerCTX,
dep: func(ctx context.Context, request *action.GetTargetRequest, response *action.GetTargetResponse) error {
name := fmt.Sprint(time.Now().UnixNano() + 1)
resp := Tester.CreateTarget(ctx, t, name, "https://example.com", domain.TargetTypeWebhook, false)
request.Id = resp.GetDetails().GetId()
response.Target.Config.Name = name
response.Target.Details = resp.GetDetails()
return nil
},
req: &action.GetTargetRequest{},
},
want: &action.GetTargetResponse{
Target: &action.GetTarget{
Details: &resource_object.Details{
Created: timestamppb.Now(),
Changed: timestamppb.Now(),
},
Config: &action.Target{
Endpoint: "https://example.com",
TargetType: &action.Target_RestWebhook{
RestWebhook: &action.SetRESTWebhook{},
},
Timeout: durationpb.New(10 * time.Second),
},
},
},
},
{
name: "get, async, ok",
args: args{
ctx: isolatedIAMOwnerCTX,
dep: func(ctx context.Context, request *action.GetTargetRequest, response *action.GetTargetResponse) error {
name := fmt.Sprint(time.Now().UnixNano() + 1)
resp := Tester.CreateTarget(ctx, t, name, "https://example.com", domain.TargetTypeAsync, false)
request.Id = resp.GetDetails().GetId()
response.Target.Config.Name = name
response.Target.Details = resp.GetDetails()
return nil
},
req: &action.GetTargetRequest{},
},
want: &action.GetTargetResponse{
Target: &action.GetTarget{
Details: &resource_object.Details{
Created: timestamppb.Now(),
Changed: timestamppb.Now(),
},
Config: &action.Target{
Endpoint: "https://example.com",
TargetType: &action.Target_RestAsync{
RestAsync: &action.SetRESTAsync{},
},
Timeout: durationpb.New(10 * time.Second),
},
},
},
},
{
name: "get, webhook interruptOnError, ok",
args: args{
ctx: isolatedIAMOwnerCTX,
dep: func(ctx context.Context, request *action.GetTargetRequest, response *action.GetTargetResponse) error {
name := fmt.Sprint(time.Now().UnixNano() + 1)
resp := Tester.CreateTarget(ctx, t, name, "https://example.com", domain.TargetTypeWebhook, true)
request.Id = resp.GetDetails().GetId()
response.Target.Config.Name = name
response.Target.Details = resp.GetDetails()
return nil
},
req: &action.GetTargetRequest{},
},
want: &action.GetTargetResponse{
Target: &action.GetTarget{
Details: &resource_object.Details{
Created: timestamppb.Now(),
Changed: timestamppb.Now(),
},
Config: &action.Target{
Endpoint: "https://example.com",
TargetType: &action.Target_RestWebhook{
RestWebhook: &action.SetRESTWebhook{
InterruptOnError: true,
},
},
Timeout: durationpb.New(10 * time.Second),
},
},
},
},
{
name: "get, call, ok",
args: args{
ctx: isolatedIAMOwnerCTX,
dep: func(ctx context.Context, request *action.GetTargetRequest, response *action.GetTargetResponse) error {
name := fmt.Sprint(time.Now().UnixNano() + 1)
resp := Tester.CreateTarget(ctx, t, name, "https://example.com", domain.TargetTypeCall, false)
request.Id = resp.GetDetails().GetId()
response.Target.Config.Name = name
response.Target.Details = resp.GetDetails()
return nil
},
req: &action.GetTargetRequest{},
},
want: &action.GetTargetResponse{
Target: &action.GetTarget{
Details: &resource_object.Details{
Created: timestamppb.Now(),
Changed: timestamppb.Now(),
},
Config: &action.Target{
Endpoint: "https://example.com",
TargetType: &action.Target_RestCall{
RestCall: &action.SetRESTCall{
InterruptOnError: false,
},
},
Timeout: durationpb.New(10 * time.Second),
},
},
},
},
{
name: "get, call interruptOnError, ok",
args: args{
ctx: isolatedIAMOwnerCTX,
dep: func(ctx context.Context, request *action.GetTargetRequest, response *action.GetTargetResponse) error {
name := fmt.Sprint(time.Now().UnixNano() + 1)
resp := Tester.CreateTarget(ctx, t, name, "https://example.com", domain.TargetTypeCall, true)
request.Id = resp.GetDetails().GetId()
response.Target.Config.Name = name
response.Target.Details = resp.GetDetails()
return nil
},
req: &action.GetTargetRequest{},
},
want: &action.GetTargetResponse{
Target: &action.GetTarget{
Details: &resource_object.Details{
Created: timestamppb.Now(),
Changed: timestamppb.Now(),
},
Config: &action.Target{
Endpoint: "https://example.com",
TargetType: &action.Target_RestCall{
RestCall: &action.SetRESTCall{
InterruptOnError: true,
},
},
Timeout: durationpb.New(10 * time.Second),
},
},
},
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
if tt.args.dep != nil {
err := tt.args.dep(tt.args.ctx, tt.args.req, tt.want)
require.NoError(t, err)
}
got, getErr := Tester.Client.ActionV3.GetTarget(tt.args.ctx, tt.args.req)
if tt.wantErr {
assert.Error(t, getErr, "Error: "+getErr.Error())
} else {
assert.NoError(t, getErr)
wantTarget := tt.want.GetTarget()
gotTarget := got.GetTarget()
integration.AssertResourceDetails(t, wantTarget.GetDetails(), gotTarget.GetDetails())
assert.Equal(t, wantTarget.GetConfig(), gotTarget.GetConfig())
}
})
}
}
func TestServer_ListTargets(t *testing.T) {
_, instanceID, _, isolatedIAMOwnerCTX := Tester.UseIsolatedInstance(t, IAMOwnerCTX, SystemCTX)
ensureFeatureEnabled(t, isolatedIAMOwnerCTX)
type args struct {
ctx context.Context
dep func(context.Context, *action.SearchTargetsRequest, *action.SearchTargetsResponse) error
req *action.SearchTargetsRequest
}
tests := []struct {
name string
args args
want *action.SearchTargetsResponse
wantErr bool
}{
{
name: "missing permission",
args: args{
ctx: Tester.WithAuthorization(context.Background(), integration.OrgOwner),
req: &action.SearchTargetsRequest{},
},
wantErr: true,
},
{
name: "list, not found",
args: args{
ctx: isolatedIAMOwnerCTX,
req: &action.SearchTargetsRequest{
Filters: []*action.TargetSearchFilter{
{Filter: &action.TargetSearchFilter_InTargetIdsFilter{
InTargetIdsFilter: &action.InTargetIDsFilter{
TargetIds: []string{"notfound"},
},
},
},
},
},
},
want: &action.SearchTargetsResponse{
Details: &resource_object.ListDetails{
TotalResult: 0,
AppliedLimit: 100,
},
Result: []*action.GetTarget{},
},
},
{
name: "list single id",
args: args{
ctx: isolatedIAMOwnerCTX,
dep: func(ctx context.Context, request *action.SearchTargetsRequest, response *action.SearchTargetsResponse) error {
name := fmt.Sprint(time.Now().UnixNano() + 1)
resp := Tester.CreateTarget(ctx, t, name, "https://example.com", domain.TargetTypeWebhook, false)
request.Filters[0].Filter = &action.TargetSearchFilter_InTargetIdsFilter{
InTargetIdsFilter: &action.InTargetIDsFilter{
TargetIds: []string{resp.GetDetails().GetId()},
},
}
response.Details.Timestamp = resp.GetDetails().GetChanged()
response.Result[0].Details = resp.GetDetails()
response.Result[0].Config.Name = name
return nil
},
req: &action.SearchTargetsRequest{
Filters: []*action.TargetSearchFilter{{}},
},
},
want: &action.SearchTargetsResponse{
Details: &resource_object.ListDetails{
TotalResult: 1,
AppliedLimit: 100,
},
Result: []*action.GetTarget{
{
Details: &resource_object.Details{
Created: timestamppb.Now(),
Changed: timestamppb.Now(),
},
Config: &action.Target{
Endpoint: "https://example.com",
TargetType: &action.Target_RestWebhook{
RestWebhook: &action.SetRESTWebhook{
InterruptOnError: false,
},
},
Timeout: durationpb.New(10 * time.Second),
},
},
},
},
}, {
name: "list single name",
args: args{
ctx: isolatedIAMOwnerCTX,
dep: func(ctx context.Context, request *action.SearchTargetsRequest, response *action.SearchTargetsResponse) error {
name := fmt.Sprint(time.Now().UnixNano() + 1)
resp := Tester.CreateTarget(ctx, t, name, "https://example.com", domain.TargetTypeWebhook, false)
request.Filters[0].Filter = &action.TargetSearchFilter_TargetNameFilter{
TargetNameFilter: &action.TargetNameFilter{
TargetName: name,
},
}
response.Details.Timestamp = resp.GetDetails().GetChanged()
response.Result[0].Details = resp.GetDetails()
response.Result[0].Config.Name = name
return nil
},
req: &action.SearchTargetsRequest{
Filters: []*action.TargetSearchFilter{{}},
},
},
want: &action.SearchTargetsResponse{
Details: &resource_object.ListDetails{
TotalResult: 1,
AppliedLimit: 100,
},
Result: []*action.GetTarget{
{
Details: &resource_object.Details{
Created: timestamppb.Now(),
Changed: timestamppb.Now(),
Owner: &object.Owner{
Type: object.OwnerType_OWNER_TYPE_INSTANCE,
Id: instanceID,
},
},
Config: &action.Target{
Endpoint: "https://example.com",
TargetType: &action.Target_RestWebhook{
RestWebhook: &action.SetRESTWebhook{
InterruptOnError: false,
},
},
Timeout: durationpb.New(10 * time.Second),
},
},
},
},
},
{
name: "list multiple id",
args: args{
ctx: isolatedIAMOwnerCTX,
dep: func(ctx context.Context, request *action.SearchTargetsRequest, response *action.SearchTargetsResponse) error {
name1 := fmt.Sprint(time.Now().UnixNano() + 1)
name2 := fmt.Sprint(time.Now().UnixNano() + 3)
name3 := fmt.Sprint(time.Now().UnixNano() + 5)
resp1 := Tester.CreateTarget(ctx, t, name1, "https://example.com", domain.TargetTypeWebhook, false)
resp2 := Tester.CreateTarget(ctx, t, name2, "https://example.com", domain.TargetTypeCall, true)
resp3 := Tester.CreateTarget(ctx, t, name3, "https://example.com", domain.TargetTypeAsync, false)
request.Filters[0].Filter = &action.TargetSearchFilter_InTargetIdsFilter{
InTargetIdsFilter: &action.InTargetIDsFilter{
TargetIds: []string{resp1.GetDetails().GetId(), resp2.GetDetails().GetId(), resp3.GetDetails().GetId()},
},
}
response.Details.Timestamp = resp3.GetDetails().GetChanged()
response.Result[0].Details = resp1.GetDetails()
response.Result[0].Config.Name = name1
response.Result[1].Details = resp2.GetDetails()
response.Result[1].Config.Name = name2
response.Result[2].Details = resp3.GetDetails()
response.Result[2].Config.Name = name3
return nil
},
req: &action.SearchTargetsRequest{
Filters: []*action.TargetSearchFilter{{}},
},
},
want: &action.SearchTargetsResponse{
Details: &resource_object.ListDetails{
TotalResult: 3,
AppliedLimit: 100,
},
Result: []*action.GetTarget{
{
Details: &resource_object.Details{
Created: timestamppb.Now(),
Changed: timestamppb.Now(),
Owner: &object.Owner{
Type: object.OwnerType_OWNER_TYPE_INSTANCE,
Id: instanceID,
},
},
Config: &action.Target{
Endpoint: "https://example.com",
TargetType: &action.Target_RestWebhook{
RestWebhook: &action.SetRESTWebhook{
InterruptOnError: false,
},
},
Timeout: durationpb.New(10 * time.Second),
},
},
{
Details: &resource_object.Details{
Created: timestamppb.Now(),
Changed: timestamppb.Now(),
Owner: &object.Owner{
Type: object.OwnerType_OWNER_TYPE_INSTANCE,
Id: instanceID,
},
},
Config: &action.Target{
Endpoint: "https://example.com",
TargetType: &action.Target_RestCall{
RestCall: &action.SetRESTCall{
InterruptOnError: true,
},
},
Timeout: durationpb.New(10 * time.Second),
},
},
{
Details: &resource_object.Details{
Created: timestamppb.Now(),
Changed: timestamppb.Now(),
Owner: &object.Owner{
Type: object.OwnerType_OWNER_TYPE_INSTANCE,
Id: instanceID,
},
},
Config: &action.Target{
Endpoint: "https://example.com",
TargetType: &action.Target_RestAsync{
RestAsync: &action.SetRESTAsync{},
},
Timeout: durationpb.New(10 * time.Second),
},
},
},
},
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
if tt.args.dep != nil {
err := tt.args.dep(tt.args.ctx, tt.args.req, tt.want)
require.NoError(t, err)
}
retryDuration := 5 * time.Second
if ctxDeadline, ok := isolatedIAMOwnerCTX.Deadline(); ok {
retryDuration = time.Until(ctxDeadline)
}
require.EventuallyWithT(t, func(ttt *assert.CollectT) {
got, listErr := Tester.Client.ActionV3.SearchTargets(tt.args.ctx, tt.args.req)
if tt.wantErr {
assert.Error(ttt, listErr, "Error: "+listErr.Error())
} else {
assert.NoError(ttt, listErr)
}
if listErr != nil {
return
}
// always first check length, otherwise its failed anyway
assert.Len(ttt, got.Result, len(tt.want.Result))
for i := range tt.want.Result {
integration.AssertResourceDetails(t, tt.want.Result[i].GetDetails(), got.Result[i].GetDetails())
assert.Equal(ttt, tt.want.Result[i].GetConfig(), got.Result[i].GetConfig())
}
integration.AssertResourceListDetails(t, tt.want, got)
}, retryDuration, time.Millisecond*100, "timeout waiting for expected execution result")
})
}
}
func TestServer_SearchExecutions(t *testing.T) {
_, instanceID, _, isolatedIAMOwnerCTX := Tester.UseIsolatedInstance(t, IAMOwnerCTX, SystemCTX)
ensureFeatureEnabled(t, isolatedIAMOwnerCTX)
targetResp := Tester.CreateTarget(isolatedIAMOwnerCTX, t, "", "https://example.com", domain.TargetTypeWebhook, false)
type args struct {
ctx context.Context
dep func(context.Context, *action.SearchExecutionsRequest, *action.SearchExecutionsResponse) error
req *action.SearchExecutionsRequest
}
tests := []struct {
name string
args args
want *action.SearchExecutionsResponse
wantErr bool
}{
{
name: "missing permission",
args: args{
ctx: Tester.WithAuthorization(context.Background(), integration.OrgOwner),
req: &action.SearchExecutionsRequest{},
},
wantErr: true,
},
{
name: "list request single condition",
args: args{
ctx: isolatedIAMOwnerCTX,
dep: func(ctx context.Context, request *action.SearchExecutionsRequest, response *action.SearchExecutionsResponse) error {
cond := request.Filters[0].GetInConditionsFilter().GetConditions()[0]
resp := Tester.SetExecution(ctx, t, cond, executionTargetsSingleTarget(targetResp.GetDetails().GetId()))
response.Details.Timestamp = resp.GetDetails().GetChanged()
// Set expected response with used values for SetExecution
response.Result[0].Details = resp.GetDetails()
response.Result[0].Condition = cond
return nil
},
req: &action.SearchExecutionsRequest{
Filters: []*action.ExecutionSearchFilter{{
Filter: &action.ExecutionSearchFilter_InConditionsFilter{
InConditionsFilter: &action.InConditionsFilter{
Conditions: []*action.Condition{{
ConditionType: &action.Condition_Request{
Request: &action.RequestExecution{
Condition: &action.RequestExecution_Method{
Method: "/zitadel.session.v2.SessionService/GetSession",
},
},
},
}},
},
},
}},
},
},
want: &action.SearchExecutionsResponse{
Details: &resource_object.ListDetails{
TotalResult: 1,
AppliedLimit: 100,
},
Result: []*action.GetExecution{
{
Details: &resource_object.Details{
Created: timestamppb.Now(),
Changed: timestamppb.Now(),
},
Condition: &action.Condition{
ConditionType: &action.Condition_Request{
Request: &action.RequestExecution{
Condition: &action.RequestExecution_Method{
Method: "/zitadel.session.v2.SessionService/GetSession",
},
},
},
},
Execution: &action.Execution{
Targets: executionTargetsSingleTarget(targetResp.GetDetails().GetId()),
},
},
},
},
},
{
name: "list request single target",
args: args{
ctx: isolatedIAMOwnerCTX,
dep: func(ctx context.Context, request *action.SearchExecutionsRequest, response *action.SearchExecutionsResponse) error {
target := Tester.CreateTarget(isolatedIAMOwnerCTX, t, "", "https://example.com", domain.TargetTypeWebhook, false)
// add target as Filter to the request
request.Filters[0] = &action.ExecutionSearchFilter{
Filter: &action.ExecutionSearchFilter_TargetFilter{
TargetFilter: &action.TargetFilter{
TargetId: target.GetDetails().GetId(),
},
},
}
cond := &action.Condition{
ConditionType: &action.Condition_Request{
Request: &action.RequestExecution{
Condition: &action.RequestExecution_Method{
Method: "/zitadel.management.v1.ManagementService/UpdateAction",
},
},
},
}
targets := executionTargetsSingleTarget(target.GetDetails().GetId())
resp := Tester.SetExecution(ctx, t, cond, targets)
response.Details.Timestamp = resp.GetDetails().GetChanged()
response.Result[0].Details = resp.GetDetails()
response.Result[0].Condition = cond
response.Result[0].Execution.Targets = targets
return nil
},
req: &action.SearchExecutionsRequest{
Filters: []*action.ExecutionSearchFilter{{}},
},
},
want: &action.SearchExecutionsResponse{
Details: &resource_object.ListDetails{
TotalResult: 1,
AppliedLimit: 100,
},
Result: []*action.GetExecution{
{
Details: &resource_object.Details{
Created: timestamppb.Now(),
Changed: timestamppb.Now(),
},
Condition: &action.Condition{},
Execution: &action.Execution{
Targets: executionTargetsSingleTarget(""),
},
},
},
},
}, {
name: "list request single include",
args: args{
ctx: isolatedIAMOwnerCTX,
dep: func(ctx context.Context, request *action.SearchExecutionsRequest, response *action.SearchExecutionsResponse) error {
cond := &action.Condition{
ConditionType: &action.Condition_Request{
Request: &action.RequestExecution{
Condition: &action.RequestExecution_Method{
Method: "/zitadel.management.v1.ManagementService/GetAction",
},
},
},
}
Tester.SetExecution(ctx, t, cond, executionTargetsSingleTarget(targetResp.GetDetails().GetId()))
request.Filters[0].GetIncludeFilter().Include = cond
includeCond := &action.Condition{
ConditionType: &action.Condition_Request{
Request: &action.RequestExecution{
Condition: &action.RequestExecution_Method{
Method: "/zitadel.management.v1.ManagementService/ListActions",
},
},
},
}
includeTargets := executionTargetsSingleInclude(cond)
resp2 := Tester.SetExecution(ctx, t, includeCond, includeTargets)
response.Details.Timestamp = resp2.GetDetails().GetChanged()
response.Result[0].Details = resp2.GetDetails()
response.Result[0].Condition = includeCond
response.Result[0].Execution = &action.Execution{
Targets: includeTargets,
}
return nil
},
req: &action.SearchExecutionsRequest{
Filters: []*action.ExecutionSearchFilter{{
Filter: &action.ExecutionSearchFilter_IncludeFilter{
IncludeFilter: &action.IncludeFilter{},
},
}},
},
},
want: &action.SearchExecutionsResponse{
Details: &resource_object.ListDetails{
TotalResult: 1,
AppliedLimit: 100,
},
Result: []*action.GetExecution{
{
Details: &resource_object.Details{
Created: timestamppb.Now(),
Changed: timestamppb.Now(),
},
},
},
},
},
{
name: "list multiple conditions",
args: args{
ctx: isolatedIAMOwnerCTX,
dep: func(ctx context.Context, request *action.SearchExecutionsRequest, response *action.SearchExecutionsResponse) error {
cond1 := request.Filters[0].GetInConditionsFilter().GetConditions()[0]
targets1 := executionTargetsSingleTarget(targetResp.GetDetails().GetId())
resp1 := Tester.SetExecution(ctx, t, cond1, targets1)
response.Result[0].Details = resp1.GetDetails()
response.Result[0].Condition = cond1
response.Result[0].Execution = &action.Execution{
Targets: targets1,
}
cond2 := request.Filters[0].GetInConditionsFilter().GetConditions()[1]
targets2 := executionTargetsSingleTarget(targetResp.GetDetails().GetId())
resp2 := Tester.SetExecution(ctx, t, cond2, targets2)
response.Result[1].Details = resp2.GetDetails()
response.Result[1].Condition = cond2
response.Result[1].Execution = &action.Execution{
Targets: targets2,
}
cond3 := request.Filters[0].GetInConditionsFilter().GetConditions()[2]
targets3 := executionTargetsSingleTarget(targetResp.GetDetails().GetId())
resp3 := Tester.SetExecution(ctx, t, cond3, targets3)
response.Result[2].Details = resp3.GetDetails()
response.Result[2].Condition = cond3
response.Result[2].Execution = &action.Execution{
Targets: targets3,
}
response.Details.Timestamp = resp3.GetDetails().GetChanged()
return nil
},
req: &action.SearchExecutionsRequest{
Filters: []*action.ExecutionSearchFilter{{
Filter: &action.ExecutionSearchFilter_InConditionsFilter{
InConditionsFilter: &action.InConditionsFilter{
Conditions: []*action.Condition{
{
ConditionType: &action.Condition_Request{
Request: &action.RequestExecution{
Condition: &action.RequestExecution_Method{
Method: "/zitadel.session.v2.SessionService/GetSession",
},
},
},
},
{
ConditionType: &action.Condition_Request{
Request: &action.RequestExecution{
Condition: &action.RequestExecution_Method{
Method: "/zitadel.session.v2.SessionService/CreateSession",
},
},
},
},
{
ConditionType: &action.Condition_Request{
Request: &action.RequestExecution{
Condition: &action.RequestExecution_Method{
Method: "/zitadel.session.v2.SessionService/SetSession",
},
},
},
},
},
},
},
}},
},
},
want: &action.SearchExecutionsResponse{
Details: &resource_object.ListDetails{
TotalResult: 3,
AppliedLimit: 100,
},
Result: []*action.GetExecution{
{
Details: &resource_object.Details{
Owner: &object.Owner{Type: object.OwnerType_OWNER_TYPE_INSTANCE, Id: instanceID},
},
}, {
Details: &resource_object.Details{
Owner: &object.Owner{Type: object.OwnerType_OWNER_TYPE_INSTANCE, Id: instanceID},
},
}, {
Details: &resource_object.Details{
Owner: &object.Owner{Type: object.OwnerType_OWNER_TYPE_INSTANCE, Id: instanceID},
},
},
},
},
},
{
name: "list multiple conditions all types",
args: args{
ctx: isolatedIAMOwnerCTX,
dep: func(ctx context.Context, request *action.SearchExecutionsRequest, response *action.SearchExecutionsResponse) error {
targets := executionTargetsSingleTarget(targetResp.GetDetails().GetId())
for i, cond := range request.Filters[0].GetInConditionsFilter().GetConditions() {
resp := Tester.SetExecution(ctx, t, cond, targets)
response.Result[i].Details = resp.GetDetails()
response.Result[i].Condition = cond
response.Result[i].Execution = &action.Execution{
Targets: targets,
}
// filled with info of last sequence
response.Details.Timestamp = resp.GetDetails().GetChanged()
}
return nil
},
req: &action.SearchExecutionsRequest{
Filters: []*action.ExecutionSearchFilter{{
Filter: &action.ExecutionSearchFilter_InConditionsFilter{
InConditionsFilter: &action.InConditionsFilter{
Conditions: []*action.Condition{
{ConditionType: &action.Condition_Request{Request: &action.RequestExecution{Condition: &action.RequestExecution_Method{Method: "/zitadel.session.v2.SessionService/GetSession"}}}},
{ConditionType: &action.Condition_Request{Request: &action.RequestExecution{Condition: &action.RequestExecution_Service{Service: "zitadel.session.v2.SessionService"}}}},
{ConditionType: &action.Condition_Request{Request: &action.RequestExecution{Condition: &action.RequestExecution_All{All: true}}}},
{ConditionType: &action.Condition_Response{Response: &action.ResponseExecution{Condition: &action.ResponseExecution_Method{Method: "/zitadel.session.v2.SessionService/GetSession"}}}},
{ConditionType: &action.Condition_Response{Response: &action.ResponseExecution{Condition: &action.ResponseExecution_Service{Service: "zitadel.session.v2.SessionService"}}}},
{ConditionType: &action.Condition_Response{Response: &action.ResponseExecution{Condition: &action.ResponseExecution_All{All: true}}}},
{ConditionType: &action.Condition_Event{Event: &action.EventExecution{Condition: &action.EventExecution_Event{Event: "user.added"}}}},
{ConditionType: &action.Condition_Event{Event: &action.EventExecution{Condition: &action.EventExecution_Group{Group: "user"}}}},
{ConditionType: &action.Condition_Event{Event: &action.EventExecution{Condition: &action.EventExecution_All{All: true}}}},
{ConditionType: &action.Condition_Function{Function: &action.FunctionExecution{Name: "Action.Flow.Type.ExternalAuthentication.Action.TriggerType.PostAuthentication"}}},
},
},
},
}},
},
},
want: &action.SearchExecutionsResponse{
Details: &resource_object.ListDetails{
TotalResult: 10,
AppliedLimit: 100,
},
Result: []*action.GetExecution{
{Details: &resource_object.Details{Owner: &object.Owner{Type: object.OwnerType_OWNER_TYPE_INSTANCE, Id: instanceID}}},
{Details: &resource_object.Details{Owner: &object.Owner{Type: object.OwnerType_OWNER_TYPE_INSTANCE, Id: instanceID}}},
{Details: &resource_object.Details{Owner: &object.Owner{Type: object.OwnerType_OWNER_TYPE_INSTANCE, Id: instanceID}}},
{Details: &resource_object.Details{Owner: &object.Owner{Type: object.OwnerType_OWNER_TYPE_INSTANCE, Id: instanceID}}},
{Details: &resource_object.Details{Owner: &object.Owner{Type: object.OwnerType_OWNER_TYPE_INSTANCE, Id: instanceID}}},
{Details: &resource_object.Details{Owner: &object.Owner{Type: object.OwnerType_OWNER_TYPE_INSTANCE, Id: instanceID}}},
{Details: &resource_object.Details{Owner: &object.Owner{Type: object.OwnerType_OWNER_TYPE_INSTANCE, Id: instanceID}}},
{Details: &resource_object.Details{Owner: &object.Owner{Type: object.OwnerType_OWNER_TYPE_INSTANCE, Id: instanceID}}},
{Details: &resource_object.Details{Owner: &object.Owner{Type: object.OwnerType_OWNER_TYPE_INSTANCE, Id: instanceID}}},
{Details: &resource_object.Details{Owner: &object.Owner{Type: object.OwnerType_OWNER_TYPE_INSTANCE, Id: instanceID}}},
},
},
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
if tt.args.dep != nil {
err := tt.args.dep(tt.args.ctx, tt.args.req, tt.want)
require.NoError(t, err)
}
retryDuration := 5 * time.Second
if ctxDeadline, ok := isolatedIAMOwnerCTX.Deadline(); ok {
retryDuration = time.Until(ctxDeadline)
}
require.EventuallyWithT(t, func(ttt *assert.CollectT) {
got, listErr := Tester.Client.ActionV3.SearchExecutions(tt.args.ctx, tt.args.req)
if tt.wantErr {
assert.Error(t, listErr, "Error: "+listErr.Error())
} else {
assert.NoError(t, listErr)
}
if listErr != nil {
return
}
// always first check length, otherwise its failed anyway
assert.Len(t, got.Result, len(tt.want.Result))
for i := range tt.want.Result {
// as not sorted, all elements have to be checked
// workaround as oneof elements can only be checked with assert.EqualExportedValues()
if j, found := containExecution(got.Result, tt.want.Result[i]); found {
assert.EqualExportedValues(t, tt.want.Result[i], got.Result[j])
}
}
integration.AssertResourceListDetails(t, tt.want, got)
}, retryDuration, time.Millisecond*100, "timeout waiting for expected execution result")
})
}
}
func containExecution(executionList []*action.GetExecution, execution *action.GetExecution) (int, bool) {
for i, exec := range executionList {
if reflect.DeepEqual(exec.Details, execution.Details) {
return i, true
}
}
return 0, false
}

View File

@ -8,6 +8,7 @@ import (
"github.com/zitadel/zitadel/internal/api/authz"
"github.com/zitadel/zitadel/internal/api/grpc/server"
"github.com/zitadel/zitadel/internal/command"
"github.com/zitadel/zitadel/internal/config/systemdefaults"
"github.com/zitadel/zitadel/internal/query"
"github.com/zitadel/zitadel/internal/zerrors"
action "github.com/zitadel/zitadel/pkg/grpc/resources/action/v3alpha"
@ -17,6 +18,7 @@ var _ action.ZITADELActionsServer = (*Server)(nil)
type Server struct {
action.UnimplementedZITADELActionsServer
systemDefaults systemdefaults.SystemDefaults
command *command.Commands
query *query.Queries
ListActionFunctions func() []string
@ -27,6 +29,7 @@ type Server struct {
type Config struct{}
func CreateServer(
systemDefaults systemdefaults.SystemDefaults,
command *command.Commands,
query *query.Queries,
listActionFunctions func() []string,
@ -34,6 +37,7 @@ func CreateServer(
listGRPCServices func() []string,
) *Server {
return &Server{
systemDefaults: systemDefaults,
command: command,
query: query,
ListActionFunctions: listActionFunctions,
@ -62,7 +66,7 @@ func (s *Server) RegisterGateway() server.RegisterGatewayFunc {
return action.RegisterZITADELActionsHandler
}
func checkExecutionEnabled(ctx context.Context) error {
func checkActionsEnabled(ctx context.Context) error {
if authz.GetInstance(ctx).Features().Actions {
return nil
}

View File

@ -13,49 +13,48 @@ import (
"github.com/stretchr/testify/require"
"github.com/zitadel/zitadel/internal/integration"
feature "github.com/zitadel/zitadel/pkg/grpc/feature/v2"
action "github.com/zitadel/zitadel/pkg/grpc/resources/action/v3alpha"
"github.com/zitadel/zitadel/pkg/grpc/feature/v2"
)
var (
CTX context.Context
Tester *integration.Tester
Client action.ZITADELActionsClient
IAMOwnerCTX, SystemCTX context.Context
Tester *integration.Tester
)
func TestMain(m *testing.M) {
os.Exit(func() int {
ctx, errCtx, cancel := integration.Contexts(5 * time.Minute)
ctx, _, cancel := integration.Contexts(5 * time.Minute)
defer cancel()
Tester = integration.NewTester(ctx)
defer Tester.Done()
Client = Tester.Client.ActionV3
CTX, _ = Tester.WithAuthorization(ctx, integration.IAMOwner), errCtx
IAMOwnerCTX = Tester.WithAuthorization(ctx, integration.IAMOwner)
SystemCTX = Tester.WithAuthorization(ctx, integration.SystemUser)
return m.Run()
}())
}
func ensureFeatureEnabled(t *testing.T) {
f, err := Tester.Client.FeatureV2.GetInstanceFeatures(CTX, &feature.GetInstanceFeaturesRequest{
func ensureFeatureEnabled(t *testing.T, iamOwnerCTX context.Context) {
f, err := Tester.Client.FeatureV2.GetInstanceFeatures(iamOwnerCTX, &feature.GetInstanceFeaturesRequest{
Inheritance: true,
})
require.NoError(t, err)
if f.Actions.GetEnabled() {
return
}
_, err = Tester.Client.FeatureV2.SetInstanceFeatures(CTX, &feature.SetInstanceFeaturesRequest{
_, err = Tester.Client.FeatureV2.SetInstanceFeatures(iamOwnerCTX, &feature.SetInstanceFeaturesRequest{
Actions: gu.Ptr(true),
})
require.NoError(t, err)
retryDuration := time.Minute
if ctxDeadline, ok := CTX.Deadline(); ok {
if ctxDeadline, ok := iamOwnerCTX.Deadline(); ok {
retryDuration = time.Until(ctxDeadline)
}
require.EventuallyWithT(t,
func(ttt *assert.CollectT) {
f, err := Tester.Client.FeatureV2.GetInstanceFeatures(CTX, &feature.GetInstanceFeaturesRequest{
f, err := Tester.Client.FeatureV2.GetInstanceFeatures(iamOwnerCTX, &feature.GetInstanceFeaturesRequest{
Inheritance: true,
})
require.NoError(ttt, err)

View File

@ -15,45 +15,45 @@ import (
)
func (s *Server) CreateTarget(ctx context.Context, req *action.CreateTargetRequest) (*action.CreateTargetResponse, error) {
if err := checkExecutionEnabled(ctx); err != nil {
if err := checkActionsEnabled(ctx); err != nil {
return nil, err
}
add := createTargetToCommand(req)
instance := targetOwnerInstance(ctx)
details, err := s.command.AddTarget(ctx, add, instance.Id)
instanceID := authz.GetInstance(ctx).InstanceID()
details, err := s.command.AddTarget(ctx, add, instanceID)
if err != nil {
return nil, err
}
return &action.CreateTargetResponse{
Details: resource_object.DomainToDetailsPb(details, instance, add.AggregateID),
Details: resource_object.DomainToDetailsPb(details, object.OwnerType_OWNER_TYPE_INSTANCE, instanceID),
}, nil
}
func (s *Server) PatchTarget(ctx context.Context, req *action.PatchTargetRequest) (*action.PatchTargetResponse, error) {
if err := checkExecutionEnabled(ctx); err != nil {
if err := checkActionsEnabled(ctx); err != nil {
return nil, err
}
instance := targetOwnerInstance(ctx)
details, err := s.command.ChangeTarget(ctx, patchTargetToCommand(req), instance.Id)
instanceID := authz.GetInstance(ctx).InstanceID()
details, err := s.command.ChangeTarget(ctx, patchTargetToCommand(req), instanceID)
if err != nil {
return nil, err
}
return &action.PatchTargetResponse{
Details: resource_object.DomainToDetailsPb(details, instance, req.GetId()),
Details: resource_object.DomainToDetailsPb(details, object.OwnerType_OWNER_TYPE_INSTANCE, instanceID),
}, nil
}
func (s *Server) DeleteTarget(ctx context.Context, req *action.DeleteTargetRequest) (*action.DeleteTargetResponse, error) {
if err := checkExecutionEnabled(ctx); err != nil {
if err := checkActionsEnabled(ctx); err != nil {
return nil, err
}
instance := targetOwnerInstance(ctx)
details, err := s.command.DeleteTarget(ctx, req.GetId(), instance.Id)
instanceID := authz.GetInstance(ctx).InstanceID()
details, err := s.command.DeleteTarget(ctx, req.GetId(), instanceID)
if err != nil {
return nil, err
}
return &action.DeleteTargetResponse{
Details: resource_object.DomainToDetailsPb(details, instance, req.GetId()),
Details: resource_object.DomainToDetailsPb(details, object.OwnerType_OWNER_TYPE_INSTANCE, instanceID),
}, nil
}
@ -112,10 +112,3 @@ func patchTargetToCommand(req *action.PatchTargetRequest) *command.ChangeTarget
}
return target
}
func targetOwnerInstance(ctx context.Context) *object.Owner {
return &object.Owner{
Type: object.OwnerType_OWNER_TYPE_INSTANCE,
Id: authz.GetInstance(ctx).InstanceID(),
}
}

View File

@ -5,6 +5,7 @@ package action_test
import (
"context"
"fmt"
object "github.com/zitadel/zitadel/pkg/grpc/object/v3alpha"
"testing"
"time"
@ -15,13 +16,13 @@ import (
"github.com/zitadel/zitadel/internal/domain"
"github.com/zitadel/zitadel/internal/integration"
object "github.com/zitadel/zitadel/pkg/grpc/object/v3alpha"
action "github.com/zitadel/zitadel/pkg/grpc/resources/action/v3alpha"
resource_object "github.com/zitadel/zitadel/pkg/grpc/resources/object/v3alpha"
)
func TestServer_CreateTarget(t *testing.T) {
ensureFeatureEnabled(t)
_, instanceID, _, isolatedIAMOwnerCTX := Tester.UseIsolatedInstance(t, IAMOwnerCTX, SystemCTX)
ensureFeatureEnabled(t, isolatedIAMOwnerCTX)
tests := []struct {
name string
ctx context.Context
@ -39,7 +40,7 @@ func TestServer_CreateTarget(t *testing.T) {
},
{
name: "empty name",
ctx: CTX,
ctx: isolatedIAMOwnerCTX,
req: &action.Target{
Name: "",
},
@ -47,7 +48,7 @@ func TestServer_CreateTarget(t *testing.T) {
},
{
name: "empty type",
ctx: CTX,
ctx: isolatedIAMOwnerCTX,
req: &action.Target{
Name: fmt.Sprint(time.Now().UnixNano() + 1),
TargetType: nil,
@ -56,7 +57,7 @@ func TestServer_CreateTarget(t *testing.T) {
},
{
name: "empty webhook url",
ctx: CTX,
ctx: isolatedIAMOwnerCTX,
req: &action.Target{
Name: fmt.Sprint(time.Now().UnixNano() + 1),
TargetType: &action.Target_RestWebhook{
@ -67,7 +68,7 @@ func TestServer_CreateTarget(t *testing.T) {
},
{
name: "empty request response url",
ctx: CTX,
ctx: isolatedIAMOwnerCTX,
req: &action.Target{
Name: fmt.Sprint(time.Now().UnixNano() + 1),
TargetType: &action.Target_RestCall{
@ -78,7 +79,7 @@ func TestServer_CreateTarget(t *testing.T) {
},
{
name: "empty timeout",
ctx: CTX,
ctx: isolatedIAMOwnerCTX,
req: &action.Target{
Name: fmt.Sprint(time.Now().UnixNano() + 1),
Endpoint: "https://example.com",
@ -91,7 +92,7 @@ func TestServer_CreateTarget(t *testing.T) {
},
{
name: "async, ok",
ctx: CTX,
ctx: isolatedIAMOwnerCTX,
req: &action.Target{
Name: fmt.Sprint(time.Now().UnixNano() + 1),
Endpoint: "https://example.com",
@ -101,16 +102,16 @@ func TestServer_CreateTarget(t *testing.T) {
Timeout: durationpb.New(10 * time.Second),
},
want: &resource_object.Details{
ChangeDate: timestamppb.Now(),
Changed: timestamppb.Now(),
Owner: &object.Owner{
Type: object.OwnerType_OWNER_TYPE_INSTANCE,
Id: Tester.Instance.InstanceID(),
Id: instanceID,
},
},
},
{
name: "webhook, ok",
ctx: CTX,
ctx: isolatedIAMOwnerCTX,
req: &action.Target{
Name: fmt.Sprint(time.Now().UnixNano() + 1),
Endpoint: "https://example.com",
@ -122,16 +123,16 @@ func TestServer_CreateTarget(t *testing.T) {
Timeout: durationpb.New(10 * time.Second),
},
want: &resource_object.Details{
ChangeDate: timestamppb.Now(),
Changed: timestamppb.Now(),
Owner: &object.Owner{
Type: object.OwnerType_OWNER_TYPE_INSTANCE,
Id: Tester.Instance.InstanceID(),
Id: instanceID,
},
},
},
{
name: "webhook, interrupt on error, ok",
ctx: CTX,
ctx: isolatedIAMOwnerCTX,
req: &action.Target{
Name: fmt.Sprint(time.Now().UnixNano() + 1),
Endpoint: "https://example.com",
@ -143,16 +144,16 @@ func TestServer_CreateTarget(t *testing.T) {
Timeout: durationpb.New(10 * time.Second),
},
want: &resource_object.Details{
ChangeDate: timestamppb.Now(),
Changed: timestamppb.Now(),
Owner: &object.Owner{
Type: object.OwnerType_OWNER_TYPE_INSTANCE,
Id: Tester.Instance.InstanceID(),
Id: instanceID,
},
},
},
{
name: "call, ok",
ctx: CTX,
ctx: isolatedIAMOwnerCTX,
req: &action.Target{
Name: fmt.Sprint(time.Now().UnixNano() + 1),
Endpoint: "https://example.com",
@ -164,17 +165,17 @@ func TestServer_CreateTarget(t *testing.T) {
Timeout: durationpb.New(10 * time.Second),
},
want: &resource_object.Details{
ChangeDate: timestamppb.Now(),
Changed: timestamppb.Now(),
Owner: &object.Owner{
Type: object.OwnerType_OWNER_TYPE_INSTANCE,
Id: Tester.Instance.InstanceID(),
Id: instanceID,
},
},
},
{
name: "call, interruptOnError, ok",
ctx: CTX,
ctx: isolatedIAMOwnerCTX,
req: &action.Target{
Name: fmt.Sprint(time.Now().UnixNano() + 1),
Endpoint: "https://example.com",
@ -186,17 +187,17 @@ func TestServer_CreateTarget(t *testing.T) {
Timeout: durationpb.New(10 * time.Second),
},
want: &resource_object.Details{
ChangeDate: timestamppb.Now(),
Changed: timestamppb.Now(),
Owner: &object.Owner{
Type: object.OwnerType_OWNER_TYPE_INSTANCE,
Id: Tester.Instance.InstanceID(),
Id: instanceID,
},
},
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
got, err := Client.CreateTarget(tt.ctx, &action.CreateTargetRequest{Target: tt.req})
got, err := Tester.Client.ActionV3.CreateTarget(tt.ctx, &action.CreateTargetRequest{Target: tt.req})
if tt.wantErr {
require.Error(t, err)
return
@ -208,7 +209,8 @@ func TestServer_CreateTarget(t *testing.T) {
}
func TestServer_PatchTarget(t *testing.T) {
ensureFeatureEnabled(t)
_, instanceID, _, isolatedIAMOwnerCTX := Tester.UseIsolatedInstance(t, IAMOwnerCTX, SystemCTX)
ensureFeatureEnabled(t, isolatedIAMOwnerCTX)
type args struct {
ctx context.Context
req *action.PatchTargetRequest
@ -223,7 +225,7 @@ func TestServer_PatchTarget(t *testing.T) {
{
name: "missing permission",
prepare: func(request *action.PatchTargetRequest) error {
targetID := Tester.CreateTarget(CTX, t, "", "https://example.com", domain.TargetTypeWebhook, false).GetDetails().GetId()
targetID := Tester.CreateTarget(isolatedIAMOwnerCTX, t, "", "https://example.com", domain.TargetTypeWebhook, false).GetDetails().GetId()
request.Id = targetID
return nil
},
@ -244,7 +246,7 @@ func TestServer_PatchTarget(t *testing.T) {
return nil
},
args: args{
ctx: CTX,
ctx: isolatedIAMOwnerCTX,
req: &action.PatchTargetRequest{
Target: &action.PatchTarget{
Name: gu.Ptr(fmt.Sprint(time.Now().UnixNano() + 1)),
@ -256,12 +258,12 @@ func TestServer_PatchTarget(t *testing.T) {
{
name: "change name, ok",
prepare: func(request *action.PatchTargetRequest) error {
targetID := Tester.CreateTarget(CTX, t, "", "https://example.com", domain.TargetTypeWebhook, false).GetDetails().GetId()
targetID := Tester.CreateTarget(isolatedIAMOwnerCTX, t, "", "https://example.com", domain.TargetTypeWebhook, false).GetDetails().GetId()
request.Id = targetID
return nil
},
args: args{
ctx: CTX,
ctx: isolatedIAMOwnerCTX,
req: &action.PatchTargetRequest{
Target: &action.PatchTarget{
Name: gu.Ptr(fmt.Sprint(time.Now().UnixNano() + 1)),
@ -269,22 +271,22 @@ func TestServer_PatchTarget(t *testing.T) {
},
},
want: &resource_object.Details{
ChangeDate: timestamppb.Now(),
Changed: timestamppb.Now(),
Owner: &object.Owner{
Type: object.OwnerType_OWNER_TYPE_INSTANCE,
Id: Tester.Instance.InstanceID(),
Id: instanceID,
},
},
},
{
name: "change type, ok",
prepare: func(request *action.PatchTargetRequest) error {
targetID := Tester.CreateTarget(CTX, t, "", "https://example.com", domain.TargetTypeWebhook, false).GetDetails().GetId()
targetID := Tester.CreateTarget(isolatedIAMOwnerCTX, t, "", "https://example.com", domain.TargetTypeWebhook, false).GetDetails().GetId()
request.Id = targetID
return nil
},
args: args{
ctx: CTX,
ctx: isolatedIAMOwnerCTX,
req: &action.PatchTargetRequest{
Target: &action.PatchTarget{
TargetType: &action.PatchTarget_RestCall{
@ -296,22 +298,22 @@ func TestServer_PatchTarget(t *testing.T) {
},
},
want: &resource_object.Details{
ChangeDate: timestamppb.Now(),
Changed: timestamppb.Now(),
Owner: &object.Owner{
Type: object.OwnerType_OWNER_TYPE_INSTANCE,
Id: Tester.Instance.InstanceID(),
Id: instanceID,
},
},
},
{
name: "change url, ok",
prepare: func(request *action.PatchTargetRequest) error {
targetID := Tester.CreateTarget(CTX, t, "", "https://example.com", domain.TargetTypeWebhook, false).GetDetails().GetId()
targetID := Tester.CreateTarget(isolatedIAMOwnerCTX, t, "", "https://example.com", domain.TargetTypeWebhook, false).GetDetails().GetId()
request.Id = targetID
return nil
},
args: args{
ctx: CTX,
ctx: isolatedIAMOwnerCTX,
req: &action.PatchTargetRequest{
Target: &action.PatchTarget{
Endpoint: gu.Ptr("https://example.com/hooks/new"),
@ -319,22 +321,22 @@ func TestServer_PatchTarget(t *testing.T) {
},
},
want: &resource_object.Details{
ChangeDate: timestamppb.Now(),
Changed: timestamppb.Now(),
Owner: &object.Owner{
Type: object.OwnerType_OWNER_TYPE_INSTANCE,
Id: Tester.Instance.InstanceID(),
Id: instanceID,
},
},
},
{
name: "change timeout, ok",
prepare: func(request *action.PatchTargetRequest) error {
targetID := Tester.CreateTarget(CTX, t, "", "https://example.com", domain.TargetTypeWebhook, false).GetDetails().GetId()
targetID := Tester.CreateTarget(isolatedIAMOwnerCTX, t, "", "https://example.com", domain.TargetTypeWebhook, false).GetDetails().GetId()
request.Id = targetID
return nil
},
args: args{
ctx: CTX,
ctx: isolatedIAMOwnerCTX,
req: &action.PatchTargetRequest{
Target: &action.PatchTarget{
Timeout: durationpb.New(20 * time.Second),
@ -342,22 +344,22 @@ func TestServer_PatchTarget(t *testing.T) {
},
},
want: &resource_object.Details{
ChangeDate: timestamppb.Now(),
Changed: timestamppb.Now(),
Owner: &object.Owner{
Type: object.OwnerType_OWNER_TYPE_INSTANCE,
Id: Tester.Instance.InstanceID(),
Id: instanceID,
},
},
},
{
name: "change type async, ok",
prepare: func(request *action.PatchTargetRequest) error {
targetID := Tester.CreateTarget(CTX, t, "", "https://example.com", domain.TargetTypeAsync, false).GetDetails().GetId()
targetID := Tester.CreateTarget(isolatedIAMOwnerCTX, t, "", "https://example.com", domain.TargetTypeAsync, false).GetDetails().GetId()
request.Id = targetID
return nil
},
args: args{
ctx: CTX,
ctx: isolatedIAMOwnerCTX,
req: &action.PatchTargetRequest{
Target: &action.PatchTarget{
TargetType: &action.PatchTarget_RestAsync{
@ -367,10 +369,10 @@ func TestServer_PatchTarget(t *testing.T) {
},
},
want: &resource_object.Details{
ChangeDate: timestamppb.Now(),
Changed: timestamppb.Now(),
Owner: &object.Owner{
Type: object.OwnerType_OWNER_TYPE_INSTANCE,
Id: Tester.Instance.InstanceID(),
Id: instanceID,
},
},
},
@ -380,8 +382,8 @@ func TestServer_PatchTarget(t *testing.T) {
err := tt.prepare(tt.args.req)
require.NoError(t, err)
// We want to have the same response no matter how often we call the function
Client.PatchTarget(tt.args.ctx, tt.args.req)
got, err := Client.PatchTarget(tt.args.ctx, tt.args.req)
Tester.Client.ActionV3.PatchTarget(tt.args.ctx, tt.args.req)
got, err := Tester.Client.ActionV3.PatchTarget(tt.args.ctx, tt.args.req)
if tt.wantErr {
require.Error(t, err)
return
@ -393,8 +395,9 @@ func TestServer_PatchTarget(t *testing.T) {
}
func TestServer_DeleteTarget(t *testing.T) {
ensureFeatureEnabled(t)
target := Tester.CreateTarget(CTX, t, "", "https://example.com", domain.TargetTypeWebhook, false)
_, instanceID, _, isolatedIAMOwnerCTX := Tester.UseIsolatedInstance(t, IAMOwnerCTX, SystemCTX)
ensureFeatureEnabled(t, isolatedIAMOwnerCTX)
target := Tester.CreateTarget(isolatedIAMOwnerCTX, t, "", "https://example.com", domain.TargetTypeWebhook, false)
tests := []struct {
name string
ctx context.Context
@ -412,7 +415,7 @@ func TestServer_DeleteTarget(t *testing.T) {
},
{
name: "empty id",
ctx: CTX,
ctx: isolatedIAMOwnerCTX,
req: &action.DeleteTargetRequest{
Id: "",
},
@ -420,22 +423,22 @@ func TestServer_DeleteTarget(t *testing.T) {
},
{
name: "delete target",
ctx: CTX,
ctx: isolatedIAMOwnerCTX,
req: &action.DeleteTargetRequest{
Id: target.GetDetails().GetId(),
},
want: &resource_object.Details{
ChangeDate: timestamppb.Now(),
Changed: timestamppb.Now(),
Owner: &object.Owner{
Type: object.OwnerType_OWNER_TYPE_INSTANCE,
Id: Tester.Instance.InstanceID(),
Id: instanceID,
},
},
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
got, err := Client.DeleteTarget(tt.ctx, tt.req)
got, err := Tester.Client.ActionV3.DeleteTarget(tt.ctx, tt.req)
if tt.wantErr {
require.Error(t, err)
return

View File

@ -1,21 +1,77 @@
package object
import (
"fmt"
"google.golang.org/protobuf/types/known/timestamppb"
"github.com/zitadel/zitadel/internal/config/systemdefaults"
"github.com/zitadel/zitadel/internal/domain"
"github.com/zitadel/zitadel/internal/query"
"github.com/zitadel/zitadel/internal/zerrors"
object "github.com/zitadel/zitadel/pkg/grpc/object/v3alpha"
resources_object "github.com/zitadel/zitadel/pkg/grpc/resources/object/v3alpha"
resource_object "github.com/zitadel/zitadel/pkg/grpc/resources/object/v3alpha"
)
func DomainToDetailsPb(objectDetail *domain.ObjectDetails, owner *object.Owner, id string) *resources_object.Details {
details := &resources_object.Details{
Id: id,
Sequence: objectDetail.Sequence,
Owner: owner,
func DomainToDetailsPb(objectDetail *domain.ObjectDetails, ownerType object.OwnerType, ownerId string) *resource_object.Details {
details := &resource_object.Details{
Id: objectDetail.ID,
Owner: &object.Owner{
Type: ownerType,
Id: ownerId,
},
}
if !objectDetail.EventDate.IsZero() {
details.ChangeDate = timestamppb.New(objectDetail.EventDate)
details.Changed = timestamppb.New(objectDetail.EventDate)
}
if !objectDetail.CreationDate.IsZero() {
details.Created = timestamppb.New(objectDetail.CreationDate)
}
return details
}
func ToSearchDetailsPb(request query.SearchRequest, response query.SearchResponse) *resource_object.ListDetails {
details := &resource_object.ListDetails{
AppliedLimit: request.Limit,
TotalResult: response.Count,
Timestamp: timestamppb.New(response.EventCreatedAt),
}
return details
}
func TextMethodPbToQuery(method resource_object.TextFilterMethod) query.TextComparison {
switch method {
case resource_object.TextFilterMethod_TEXT_FILTER_METHOD_EQUALS:
return query.TextEquals
case resource_object.TextFilterMethod_TEXT_FILTER_METHOD_EQUALS_IGNORE_CASE:
return query.TextEqualsIgnoreCase
case resource_object.TextFilterMethod_TEXT_FILTER_METHOD_STARTS_WITH:
return query.TextStartsWith
case resource_object.TextFilterMethod_TEXT_FILTER_METHOD_STARTS_WITH_IGNORE_CASE:
return query.TextStartsWithIgnoreCase
case resource_object.TextFilterMethod_TEXT_FILTER_METHOD_CONTAINS:
return query.TextContains
default:
return -1
}
}
func SearchQueryPbToQuery(defaults systemdefaults.SystemDefaults, query *resource_object.SearchQuery) (offset, limit uint64, asc bool, err error) {
limit = defaults.DefaultQueryLimit
asc = true
if query == nil {
return 0, limit, asc, nil
}
offset = query.Offset
if query.Desc {
asc = false
}
if defaults.MaxQueryLimit > 0 && uint64(query.Limit) > defaults.MaxQueryLimit {
return 0, 0, false, zerrors.ThrowInvalidArgumentf(fmt.Errorf("given: %d, allowed: %d", query.Limit, defaults.MaxQueryLimit), "QUERY-4M0fs", "Errors.Query.LimitExceeded")
}
if query.Limit > 0 {
limit = uint64(query.Limit)
}
return offset, limit, asc, nil
}

View File

@ -17,6 +17,7 @@ import (
"github.com/zitadel/zitadel/internal/i18n"
"github.com/zitadel/zitadel/internal/telemetry/tracing"
"github.com/zitadel/zitadel/internal/zerrors"
object_v3 "github.com/zitadel/zitadel/pkg/grpc/object/v3alpha"
)
const (
@ -34,33 +35,67 @@ func InstanceInterceptor(verifier authz.InstanceVerifier, externalDomain string,
func setInstance(ctx context.Context, req interface{}, info *grpc.UnaryServerInfo, handler grpc.UnaryHandler, verifier authz.InstanceVerifier, externalDomain string, translator *i18n.Translator, idFromRequestsServices ...string) (_ interface{}, err error) {
interceptorCtx, span := tracing.NewServerInterceptorSpan(ctx)
defer func() { span.EndWithError(err) }()
for _, service := range idFromRequestsServices {
if !strings.HasPrefix(service, "/") {
service = "/" + service
}
if strings.HasPrefix(info.FullMethod, service) {
withInstanceIDProperty, ok := req.(interface{ GetInstanceId() string })
withInstanceIDProperty, ok := req.(interface {
GetInstanceId() string
})
if !ok {
return handler(ctx, req)
}
ctx = authz.WithInstanceID(ctx, withInstanceIDProperty.GetInstanceId())
instance, err := verifier.InstanceByID(ctx)
if err != nil {
notFoundErr := new(zerrors.NotFoundError)
if errors.As(err, &notFoundErr) {
notFoundErr.Message = translator.LocalizeFromCtx(ctx, notFoundErr.GetMessage(), nil)
}
return nil, status.Error(codes.NotFound, err.Error())
}
return handler(authz.WithInstance(ctx, instance), req)
return addInstanceByID(interceptorCtx, req, handler, verifier, translator, withInstanceIDProperty.GetInstanceId())
}
}
explicitInstanceRequest, ok := req.(interface {
GetInstance() *object_v3.Instance
})
if ok {
instance := explicitInstanceRequest.GetInstance()
if id := instance.GetId(); id != "" {
return addInstanceByID(interceptorCtx, req, handler, verifier, translator, id)
}
if domain := instance.GetDomain(); domain != "" {
return addInstanceByDomain(interceptorCtx, req, handler, verifier, translator, domain)
}
}
return addInstanceByRequestedHost(interceptorCtx, req, handler, verifier, translator, externalDomain)
}
func addInstanceByID(ctx context.Context, req interface{}, handler grpc.UnaryHandler, verifier authz.InstanceVerifier, translator *i18n.Translator, id string) (interface{}, error) {
instance, err := verifier.InstanceByID(ctx, id)
if err != nil {
notFoundErr := new(zerrors.ZitadelError)
if errors.As(err, &notFoundErr) {
notFoundErr.Message = translator.LocalizeFromCtx(ctx, notFoundErr.GetMessage(), nil)
}
return nil, status.Error(codes.NotFound, fmt.Errorf("unable to set instance using id %s: %w", id, notFoundErr).Error())
}
return handler(authz.WithInstance(ctx, instance), req)
}
func addInstanceByDomain(ctx context.Context, req interface{}, handler grpc.UnaryHandler, verifier authz.InstanceVerifier, translator *i18n.Translator, domain string) (interface{}, error) {
instance, err := verifier.InstanceByHost(ctx, domain, "")
if err != nil {
notFoundErr := new(zerrors.NotFoundError)
if errors.As(err, &notFoundErr) {
notFoundErr.Message = translator.LocalizeFromCtx(ctx, notFoundErr.GetMessage(), nil)
}
return nil, status.Error(codes.NotFound, fmt.Errorf("unable to set instance using domain %s: %w", domain, notFoundErr).Error())
}
return handler(authz.WithInstance(ctx, instance), req)
}
func addInstanceByRequestedHost(ctx context.Context, req interface{}, handler grpc.UnaryHandler, verifier authz.InstanceVerifier, translator *i18n.Translator, externalDomain string) (interface{}, error) {
requestContext := zitadel_http.DomainContext(ctx)
if requestContext.InstanceHost == "" {
logging.WithFields("origin", requestContext.Origin(), "externalDomain", externalDomain).WithError(err).Error("unable to set instance")
logging.WithFields("origin", requestContext.Origin(), "externalDomain", externalDomain).Error("unable to set instance")
return nil, status.Error(codes.NotFound, "no instanceHost specified")
}
instance, err := verifier.InstanceByHost(interceptorCtx, requestContext.InstanceHost, requestContext.PublicHost)
instance, err := verifier.InstanceByHost(ctx, requestContext.InstanceHost, requestContext.PublicHost)
if err != nil {
origin := zitadel_http.DomainContext(ctx)
logging.WithFields("origin", requestContext.Origin(), "externalDomain", externalDomain).WithError(err).Error("unable to set instance")
@ -72,6 +107,5 @@ func setInstance(ctx context.Context, req interface{}, info *grpc.UnaryServerInf
}
return nil, status.Error(codes.NotFound, fmt.Sprintf("unable to set instance using origin %s (ExternalDomain is %s)", origin, externalDomain))
}
span.End()
return handler(authz.WithInstance(ctx, instance), req)
}

View File

@ -13,6 +13,7 @@ import (
"github.com/zitadel/zitadel/internal/api/authz"
http_util "github.com/zitadel/zitadel/internal/api/http"
"github.com/zitadel/zitadel/internal/feature"
object_v3 "github.com/zitadel/zitadel/pkg/grpc/object/v3alpha"
)
func Test_setInstance(t *testing.T) {
@ -69,6 +70,135 @@ func Test_setInstance(t *testing.T) {
err: false,
},
},
{
"explicit instance unset, hostname not found, error",
args{
ctx: http_util.WithDomainContext(context.Background(), &http_util.DomainCtx{InstanceHost: "host2"}),
req: &mockRequestWithExplicitInstance{},
verifier: &mockInstanceVerifier{instanceHost: "host"},
},
res{
want: nil,
err: true,
},
},
{
"explicit instance unset, invalid host, error",
args{
ctx: http_util.WithDomainContext(context.Background(), &http_util.DomainCtx{InstanceHost: "host2"}),
req: &mockRequestWithExplicitInstance{},
verifier: &mockInstanceVerifier{instanceHost: "host"},
},
res{
want: nil,
err: true,
},
},
{
"explicit instance unset, valid host",
args{
ctx: http_util.WithDomainContext(context.Background(), &http_util.DomainCtx{InstanceHost: "host"}),
req: &mockRequestWithExplicitInstance{},
verifier: &mockInstanceVerifier{instanceHost: "host"},
handler: func(ctx context.Context, req interface{}) (interface{}, error) {
return req, nil
},
},
res{
want: &mockRequestWithExplicitInstance{},
err: false,
},
},
{
name: "explicit instance set, id not found, error",
args: args{
ctx: context.Background(),
req: &mockRequestWithExplicitInstance{
instance: object_v3.Instance{
Property: &object_v3.Instance_Id{
Id: "not existing instance id",
},
},
},
verifier: &mockInstanceVerifier{id: "existing instance id"},
},
res: res{
want: nil,
err: true,
},
},
{
name: "explicit instance set, id found, ok",
args: args{
ctx: context.Background(),
req: &mockRequestWithExplicitInstance{
instance: object_v3.Instance{
Property: &object_v3.Instance_Id{
Id: "existing instance id",
},
},
},
verifier: &mockInstanceVerifier{id: "existing instance id"},
handler: func(ctx context.Context, req interface{}) (interface{}, error) {
return req, nil
},
},
res: res{
want: &mockRequestWithExplicitInstance{
instance: object_v3.Instance{
Property: &object_v3.Instance_Id{
Id: "existing instance id",
},
},
},
err: false,
},
},
{
name: "explicit instance set, domain not found, error",
args: args{
ctx: context.Background(),
req: &mockRequestWithExplicitInstance{
instance: object_v3.Instance{
Property: &object_v3.Instance_Domain{
Domain: "not existing instance domain",
},
},
},
verifier: &mockInstanceVerifier{instanceHost: "existing instance domain"},
},
res: res{
want: nil,
err: true,
},
},
{
name: "explicit instance set, domain found, ok",
args: args{
ctx: context.Background(),
req: &mockRequestWithExplicitInstance{
instance: object_v3.Instance{
Property: &object_v3.Instance_Domain{
Domain: "existing instance domain",
},
},
},
verifier: &mockInstanceVerifier{instanceHost: "existing instance domain"},
handler: func(ctx context.Context, req interface{}) (interface{}, error) {
return req, nil
},
},
res: res{
want: &mockRequestWithExplicitInstance{
instance: object_v3.Instance{
Property: &object_v3.Instance_Domain{
Domain: "existing instance domain",
},
},
},
err: false,
},
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
@ -86,7 +216,16 @@ func Test_setInstance(t *testing.T) {
type mockRequest struct{}
type mockRequestWithExplicitInstance struct {
instance object_v3.Instance
}
func (m *mockRequestWithExplicitInstance) GetInstance() *object_v3.Instance {
return &m.instance
}
type mockInstanceVerifier struct {
id string
instanceHost string
publicHost string
}
@ -104,7 +243,12 @@ func (m *mockInstanceVerifier) InstanceByHost(_ context.Context, instanceHost, p
return &mockInstance{}, nil
}
func (m *mockInstanceVerifier) InstanceByID(context.Context) (authz.Instance, error) { return nil, nil }
func (m *mockInstanceVerifier) InstanceByID(_ context.Context, id string) (authz.Instance, error) {
if id != m.id {
return nil, fmt.Errorf("not found")
}
return &mockInstance{}, nil
}
type mockInstance struct{}

View File

@ -2,7 +2,6 @@ package server
import (
"crypto/tls"
grpc_middleware "github.com/grpc-ecosystem/go-grpc-middleware"
"google.golang.org/grpc"
"google.golang.org/grpc/credentials"

View File

@ -3,7 +3,6 @@ package system
import (
"context"
"github.com/zitadel/zitadel/internal/api/authz"
instance_grpc "github.com/zitadel/zitadel/internal/api/grpc/instance"
"github.com/zitadel/zitadel/internal/api/grpc/member"
"github.com/zitadel/zitadel/internal/api/grpc/object"
@ -151,12 +150,6 @@ func (s *Server) ListDomains(ctx context.Context, req *system_pb.ListDomainsRequ
}
func (s *Server) AddDomain(ctx context.Context, req *system_pb.AddDomainRequest) (*system_pb.AddDomainResponse, error) {
instance, err := s.query.InstanceByID(ctx)
if err != nil {
return nil, err
}
ctx = authz.WithInstance(ctx, instance)
details, err := s.command.AddInstanceDomain(ctx, req.Domain)
if err != nil {
return nil, err

View File

@ -242,7 +242,7 @@ func (m *mockInstanceVerifier) InstanceByHost(_ context.Context, instanceHost, p
return &mockInstance{}, nil
}
func (m *mockInstanceVerifier) InstanceByID(context.Context) (authz.Instance, error) {
func (m *mockInstanceVerifier) InstanceByID(context.Context, string) (authz.Instance, error) {
return nil, nil
}

View File

@ -203,6 +203,7 @@ func TestCommands_SetExecutionRequest(t *testing.T) {
res{
details: &domain.ObjectDetails{
ResourceOwner: "instance",
ID: "request/method",
},
},
},
@ -251,6 +252,7 @@ func TestCommands_SetExecutionRequest(t *testing.T) {
res{
details: &domain.ObjectDetails{
ResourceOwner: "instance",
ID: "request/service",
},
},
},
@ -298,6 +300,7 @@ func TestCommands_SetExecutionRequest(t *testing.T) {
res{
details: &domain.ObjectDetails{
ResourceOwner: "instance",
ID: "request",
},
},
},
@ -381,6 +384,7 @@ func TestCommands_SetExecutionRequest(t *testing.T) {
res{
details: &domain.ObjectDetails{
ResourceOwner: "instance",
ID: "request/method",
},
},
},
@ -464,6 +468,7 @@ func TestCommands_SetExecutionRequest(t *testing.T) {
res{
details: &domain.ObjectDetails{
ResourceOwner: "instance",
ID: "request/service",
},
},
},
@ -545,6 +550,86 @@ func TestCommands_SetExecutionRequest(t *testing.T) {
res{
details: &domain.ObjectDetails{
ResourceOwner: "instance",
ID: "request",
},
},
},
{
"push ok, remove all targets",
fields{
eventstore: expectEventstore(
expectFilter( // execution has targets
eventFromEventPusher(
execution.NewSetEventV2(context.Background(),
execution.NewAggregate("request", "instance"),
[]*execution.Target{
{Type: domain.ExecutionTargetTypeTarget, Target: "target"},
},
),
),
),
expectPush(
execution.NewSetEventV2(context.Background(),
execution.NewAggregate("request", "instance"),
[]*execution.Target{},
),
),
),
},
args{
ctx: context.Background(),
cond: &ExecutionAPICondition{
"",
"",
true,
},
set: &SetExecution{
Targets: []*execution.Target{},
},
resourceOwner: "instance",
},
res{
details: &domain.ObjectDetails{
ResourceOwner: "instance",
ID: "request",
},
},
},
{
"push ok, unchanged execution",
fields{
eventstore: expectEventstore(
expectFilter( // execution has targets
eventFromEventPusher(
execution.NewSetEventV2(context.Background(),
execution.NewAggregate("request", "instance"),
[]*execution.Target{
{Type: domain.ExecutionTargetTypeTarget, Target: "target"},
},
),
),
),
),
},
args{
ctx: context.Background(),
cond: &ExecutionAPICondition{
"",
"",
true,
},
set: &SetExecution{
Targets: []*execution.Target{{
Type: domain.ExecutionTargetTypeTarget,
Target: "target",
}},
},
resourceOwner: "instance",
},
res{
details: &domain.ObjectDetails{
ResourceOwner: "instance",
ID: "request",
},
},
},
@ -641,7 +726,7 @@ func TestCommands_SetExecutionRequest(t *testing.T) {
t.Errorf("got wrong err: %v ", err)
}
if tt.res.err == nil {
assert.Equal(t, tt.res.details, details)
assertObjectDetails(t, tt.res.details, details)
}
})
}
@ -876,6 +961,7 @@ func TestCommands_SetExecutionResponse(t *testing.T) {
res{
details: &domain.ObjectDetails{
ResourceOwner: "instance",
ID: "response/method",
},
},
},
@ -915,6 +1001,7 @@ func TestCommands_SetExecutionResponse(t *testing.T) {
res{
details: &domain.ObjectDetails{
ResourceOwner: "instance",
ID: "response/service",
},
},
},
@ -953,6 +1040,86 @@ func TestCommands_SetExecutionResponse(t *testing.T) {
res{
details: &domain.ObjectDetails{
ResourceOwner: "instance",
ID: "response",
},
},
},
{
"push ok, remove all targets",
fields{
eventstore: expectEventstore(
expectFilter( // execution has targets
eventFromEventPusher(
execution.NewSetEventV2(context.Background(),
execution.NewAggregate("response", "instance"),
[]*execution.Target{
{Type: domain.ExecutionTargetTypeTarget, Target: "target"},
},
),
),
),
expectPush(
execution.NewSetEventV2(context.Background(),
execution.NewAggregate("response", "instance"),
[]*execution.Target{},
),
),
),
},
args{
ctx: context.Background(),
cond: &ExecutionAPICondition{
"",
"",
true,
},
set: &SetExecution{
Targets: []*execution.Target{},
},
resourceOwner: "instance",
},
res{
details: &domain.ObjectDetails{
ResourceOwner: "instance",
ID: "response",
},
},
},
{
"push ok, unchanged execution",
fields{
eventstore: expectEventstore(
expectFilter( // execution has targets
eventFromEventPusher(
execution.NewSetEventV2(context.Background(),
execution.NewAggregate("response", "instance"),
[]*execution.Target{
{Type: domain.ExecutionTargetTypeTarget, Target: "target"},
},
),
),
),
),
},
args{
ctx: context.Background(),
cond: &ExecutionAPICondition{
"",
"",
true,
},
set: &SetExecution{
Targets: []*execution.Target{{
Type: domain.ExecutionTargetTypeTarget,
Target: "target",
}},
},
resourceOwner: "instance",
},
res{
details: &domain.ObjectDetails{
ResourceOwner: "instance",
ID: "response",
},
},
},
@ -1049,7 +1216,7 @@ func TestCommands_SetExecutionResponse(t *testing.T) {
t.Errorf("got wrong err: %v ", err)
}
if tt.res.err == nil {
assert.Equal(t, tt.res.details, details)
assertObjectDetails(t, tt.res.details, details)
}
})
}
@ -1288,6 +1455,7 @@ func TestCommands_SetExecutionEvent(t *testing.T) {
res{
details: &domain.ObjectDetails{
ResourceOwner: "instance",
ID: "event/event",
},
},
},
@ -1327,6 +1495,7 @@ func TestCommands_SetExecutionEvent(t *testing.T) {
res{
details: &domain.ObjectDetails{
ResourceOwner: "instance",
ID: "event/group.*",
},
},
},
@ -1365,6 +1534,86 @@ func TestCommands_SetExecutionEvent(t *testing.T) {
res{
details: &domain.ObjectDetails{
ResourceOwner: "instance",
ID: "event",
},
},
},
{
"push ok, remove all targets",
fields{
eventstore: expectEventstore(
expectFilter( // execution has targets
eventFromEventPusher(
execution.NewSetEventV2(context.Background(),
execution.NewAggregate("event", "instance"),
[]*execution.Target{
{Type: domain.ExecutionTargetTypeTarget, Target: "target"},
},
),
),
),
expectPush(
execution.NewSetEventV2(context.Background(),
execution.NewAggregate("event", "instance"),
[]*execution.Target{},
),
),
),
},
args{
ctx: context.Background(),
cond: &ExecutionEventCondition{
"",
"",
true,
},
set: &SetExecution{
Targets: []*execution.Target{},
},
resourceOwner: "instance",
},
res{
details: &domain.ObjectDetails{
ResourceOwner: "instance",
ID: "event",
},
},
},
{
"push ok, unchanged execution",
fields{
eventstore: expectEventstore(
expectFilter( // execution has targets
eventFromEventPusher(
execution.NewSetEventV2(context.Background(),
execution.NewAggregate("event", "instance"),
[]*execution.Target{
{Type: domain.ExecutionTargetTypeTarget, Target: "target"},
},
),
),
),
),
},
args{
ctx: context.Background(),
cond: &ExecutionEventCondition{
"",
"",
true,
},
set: &SetExecution{
Targets: []*execution.Target{{
Type: domain.ExecutionTargetTypeTarget,
Target: "target",
}},
},
resourceOwner: "instance",
},
res{
details: &domain.ObjectDetails{
ResourceOwner: "instance",
ID: "event",
},
},
},
@ -1461,7 +1710,7 @@ func TestCommands_SetExecutionEvent(t *testing.T) {
t.Errorf("got wrong err: %v ", err)
}
if tt.res.err == nil {
assert.Equal(t, tt.res.details, details)
assertObjectDetails(t, tt.res.details, details)
}
})
}
@ -1643,6 +1892,80 @@ func TestCommands_SetExecutionFunction(t *testing.T) {
res{
details: &domain.ObjectDetails{
ResourceOwner: "instance",
ID: "function/function",
},
},
},
{
"push ok, remove all targets",
fields{
actionFunctionExists: existsMock(true),
eventstore: expectEventstore(
expectFilter( // execution has targets
eventFromEventPusher(
execution.NewSetEventV2(context.Background(),
execution.NewAggregate("function/function", "instance"),
[]*execution.Target{
{Type: domain.ExecutionTargetTypeTarget, Target: "target"},
},
),
),
),
expectPush(
execution.NewSetEventV2(context.Background(),
execution.NewAggregate("function/function", "instance"),
[]*execution.Target{},
),
),
),
},
args{
ctx: context.Background(),
cond: "function",
set: &SetExecution{
Targets: []*execution.Target{},
},
resourceOwner: "instance",
},
res{
details: &domain.ObjectDetails{
ResourceOwner: "instance",
ID: "function/function",
},
},
},
{
"push ok, unchanged execution",
fields{
actionFunctionExists: existsMock(true),
eventstore: expectEventstore(
expectFilter( // execution has targets
eventFromEventPusher(
execution.NewSetEventV2(context.Background(),
execution.NewAggregate("function/function", "instance"),
[]*execution.Target{
{Type: domain.ExecutionTargetTypeTarget, Target: "target"},
},
),
),
),
),
},
args{
ctx: context.Background(),
cond: "function",
set: &SetExecution{
Targets: []*execution.Target{{
Type: domain.ExecutionTargetTypeTarget,
Target: "target",
}},
},
resourceOwner: "instance",
},
res{
details: &domain.ObjectDetails{
ResourceOwner: "instance",
ID: "function/function",
},
},
},
@ -1732,7 +2055,7 @@ func TestCommands_SetExecutionFunction(t *testing.T) {
t.Errorf("got wrong err: %v ", err)
}
if tt.res.err == nil {
assert.Equal(t, tt.res.details, details)
assertObjectDetails(t, tt.res.details, details)
}
})
}

View File

@ -113,7 +113,6 @@ func (c *Commands) ChangeTarget(ctx context.Context, change *ChangeTarget, resou
if err := change.IsValid(); err != nil {
return nil, err
}
existing, err := c.getTargetWriteModelByID(ctx, change.AggregateID, resourceOwner)
if err != nil {
return nil, err
@ -121,7 +120,6 @@ func (c *Commands) ChangeTarget(ctx context.Context, change *ChangeTarget, resou
if !existing.State.Exists() {
return nil, zerrors.ThrowNotFound(nil, "COMMAND-xj14f2cccn", "Errors.Target.NotFound")
}
changedEvent := existing.NewChangedEvent(
ctx,
TargetAggregateFromWriteModel(&existing.WriteModel),

View File

@ -202,6 +202,7 @@ func TestCommands_AddTarget(t *testing.T) {
id: "id1",
details: &domain.ObjectDetails{
ResourceOwner: "instance",
ID: "id1",
},
},
},
@ -235,6 +236,7 @@ func TestCommands_AddTarget(t *testing.T) {
id: "id1",
details: &domain.ObjectDetails{
ResourceOwner: "instance",
ID: "id1",
},
},
},
@ -254,7 +256,7 @@ func TestCommands_AddTarget(t *testing.T) {
}
if tt.res.err == nil {
assert.Equal(t, tt.res.id, tt.args.add.AggregateID)
assert.Equal(t, tt.res.details, details)
assertObjectDetails(t, tt.res.details, details)
}
})
}
@ -416,6 +418,7 @@ func TestCommands_ChangeTarget(t *testing.T) {
res{
details: &domain.ObjectDetails{
ResourceOwner: "instance",
ID: "id1",
},
},
},
@ -485,6 +488,7 @@ func TestCommands_ChangeTarget(t *testing.T) {
res{
details: &domain.ObjectDetails{
ResourceOwner: "instance",
ID: "id1",
},
},
},
@ -528,6 +532,7 @@ func TestCommands_ChangeTarget(t *testing.T) {
res{
details: &domain.ObjectDetails{
ResourceOwner: "instance",
ID: "id1",
},
},
},
@ -545,7 +550,7 @@ func TestCommands_ChangeTarget(t *testing.T) {
t.Errorf("got wrong err: %v ", err)
}
if tt.res.err == nil {
assert.Equal(t, tt.res.details, details)
assertObjectDetails(t, tt.res.details, details)
}
})
}
@ -625,6 +630,7 @@ func TestCommands_DeleteTarget(t *testing.T) {
res{
details: &domain.ObjectDetails{
ResourceOwner: "instance",
ID: "id1",
},
},
},
@ -642,7 +648,7 @@ func TestCommands_DeleteTarget(t *testing.T) {
t.Errorf("got wrong err: %v ", err)
}
if tt.res.err == nil {
assert.Equal(t, tt.res.details, details)
assertObjectDetails(t, tt.res.details, details)
}
})
}

View File

@ -629,7 +629,7 @@ func TestCommands_LinkSessionToAuthRequest(t *testing.T) {
}
details, got, err := c.LinkSessionToAuthRequest(tt.args.ctx, tt.args.id, tt.args.sessionID, tt.args.sessionToken, tt.args.checkLoginClient)
require.ErrorIs(t, err, tt.res.wantErr)
assert.Equal(t, tt.res.details, details)
assertObjectDetails(t, tt.res.details, details)
if err == nil {
assert.WithinRange(t, got.AuthTime, testNow, testNow)
got.AuthTime = time.Time{}
@ -739,7 +739,7 @@ func TestCommands_FailAuthRequest(t *testing.T) {
}
details, got, err := c.FailAuthRequest(tt.args.ctx, tt.args.id, tt.args.reason)
require.ErrorIs(t, err, tt.res.wantErr)
assert.Equal(t, tt.res.details, details)
assertObjectDetails(t, tt.res.details, details)
assert.Equal(t, tt.res.authReq, got)
})
}

View File

@ -10,6 +10,7 @@ func writeModelToObjectDetails(writeModel *eventstore.WriteModel) *domain.Object
Sequence: writeModel.ProcessedSequence,
ResourceOwner: writeModel.ResourceOwner,
EventDate: writeModel.ChangeDate,
ID: writeModel.AggregateID,
}
}

View File

@ -115,7 +115,7 @@ func TestCommands_AddDeviceAuth(t *testing.T) {
}
gotDetails, err := c.AddDeviceAuth(tt.args.ctx, tt.args.clientID, tt.args.deviceCode, tt.args.userCode, tt.args.expires, tt.args.scopes, tt.args.audience, tt.args.needRefreshToken)
require.ErrorIs(t, err, tt.wantErr)
assert.Equal(t, tt.wantDetails, gotDetails)
assertObjectDetails(t, tt.wantDetails, gotDetails)
})
}
}
@ -254,7 +254,7 @@ func TestCommands_ApproveDeviceAuth(t *testing.T) {
}
gotDetails, err := c.ApproveDeviceAuth(tt.args.ctx, tt.args.id, tt.args.userID, tt.args.userOrgID, tt.args.authMethods, tt.args.authTime, tt.args.preferredLanguage, tt.args.userAgent)
require.ErrorIs(t, err, tt.wantErr)
assert.Equal(t, gotDetails, tt.wantDetails)
assertObjectDetails(t, tt.wantDetails, gotDetails)
})
}
}
@ -376,7 +376,7 @@ func TestCommands_CancelDeviceAuth(t *testing.T) {
}
gotDetails, err := c.CancelDeviceAuth(tt.args.ctx, tt.args.id, tt.args.reason)
require.ErrorIs(t, err, tt.wantErr)
assert.Equal(t, gotDetails, tt.wantDetails)
assertObjectDetails(t, tt.wantDetails, gotDetails)
})
}
}

View File

@ -333,7 +333,7 @@ func TestCommands_CreateIntent(t *testing.T) {
} else {
assert.Equal(t, tt.res.intentID, "")
}
assert.Equal(t, tt.res.details, details)
assertObjectDetails(t, tt.res.details, details)
})
}
}

View File

@ -6864,7 +6864,7 @@ func TestCommandSide_SetCustomIAMLoginText(t *testing.T) {
t.Errorf("got wrong err: %v ", err)
}
if tt.res.err == nil {
assert.Equal(t, tt.res.want, got)
assertObjectDetails(t, tt.res.want, got)
}
})
}

View File

@ -191,7 +191,7 @@ func TestCommandSide_SetDefaultMessageText(t *testing.T) {
t.Errorf("got wrong err: %v ", err)
t.FailNow()
}
assert.Equal(t, tt.res.want, got)
assertObjectDetails(t, tt.res.want, got)
})
}
}

View File

@ -100,7 +100,7 @@ func TestCommandSide_AddDefaultDebugNotificationProviderFile(t *testing.T) {
t.Errorf("got wrong err: %v ", err)
}
if tt.res.err == nil {
assert.Equal(t, tt.res.want, got)
assertObjectDetails(t, tt.res.want, got)
}
})
}
@ -242,7 +242,7 @@ func TestCommandSide_ChangeDebugNotificationProviderFile(t *testing.T) {
t.Errorf("got wrong err: %v ", err)
}
if tt.res.err == nil {
assert.Equal(t, tt.res.want, got)
assertObjectDetails(t, tt.res.want, got)
}
})
}
@ -323,7 +323,7 @@ func TestCommandSide_RemoveDebugNotificationProviderFile(t *testing.T) {
t.Errorf("got wrong err: %v ", err)
}
if tt.res.err == nil {
assert.Equal(t, tt.res.want, got)
assertObjectDetails(t, tt.res.want, got)
}
})
}

View File

@ -128,7 +128,7 @@ func TestCommandSide_AddDefaultDebugNotificationProviderLog(t *testing.T) {
t.Errorf("got wrong err: %v ", err)
}
if tt.res.err == nil {
assert.Equal(t, tt.res.want, got)
assertObjectDetails(t, tt.res.want, got)
}
})
}
@ -277,7 +277,7 @@ func TestCommandSide_ChangeDebugNotificationProviderLog(t *testing.T) {
t.Errorf("got wrong err: %v ", err)
}
if tt.res.err == nil {
assert.Equal(t, tt.res.want, got)
assertObjectDetails(t, tt.res.want, got)
}
})
}
@ -359,7 +359,7 @@ func TestCommandSide_RemoveDebugNotificationProviderLog(t *testing.T) {
t.Errorf("got wrong err: %v ", err)
}
if tt.res.err == nil {
assert.Equal(t, tt.res.want, got)
assertObjectDetails(t, tt.res.want, got)
}
})
}

View File

@ -198,7 +198,7 @@ func TestCommandSide_AddInstanceDomain(t *testing.T) {
t.Errorf("got wrong err: %v ", err)
return
}
assert.Equal(t, tt.res.want, got)
assertObjectDetails(t, tt.res.want, got)
})
}
}
@ -299,7 +299,7 @@ func TestCommandSide_SetPrimaryInstanceDomain(t *testing.T) {
t.Errorf("got wrong err: %v ", err)
}
if tt.res.err == nil {
assert.Equal(t, tt.res.want, got)
assertObjectDetails(t, tt.res.want, got)
}
})
}
@ -425,7 +425,7 @@ func TestCommandSide_RemoveInstanceDomain(t *testing.T) {
t.Errorf("got wrong err: %v ", err)
}
if tt.res.err == nil {
assert.Equal(t, tt.res.want, got)
assertObjectDetails(t, tt.res.want, got)
}
})
}

View File

@ -363,7 +363,7 @@ func TestCommands_ResetInstanceFeatures(t *testing.T) {
}
got, err := c.ResetInstanceFeatures(ctx)
require.ErrorIs(t, err, tt.wantErr)
assert.Equal(t, tt.want, got)
assertObjectDetails(t, tt.want, got)
})
}
}

View File

@ -301,7 +301,7 @@ func TestCommandSide_AddInstanceGenericOAuthIDP(t *testing.T) {
}
if tt.res.err == nil {
assert.Equal(t, tt.res.id, id)
assert.Equal(t, tt.res.want, got)
assertObjectDetails(t, tt.res.want, got)
}
})
}
@ -619,7 +619,7 @@ func TestCommandSide_UpdateInstanceGenericOAuthIDP(t *testing.T) {
t.Errorf("got wrong err: %v ", err)
}
if tt.res.err == nil {
assert.Equal(t, tt.res.want, got)
assertObjectDetails(t, tt.res.want, got)
}
})
}
@ -829,7 +829,7 @@ func TestCommandSide_AddInstanceGenericOIDCIDP(t *testing.T) {
}
if tt.res.err == nil {
assert.Equal(t, tt.res.id, id)
assert.Equal(t, tt.res.want, got)
assertObjectDetails(t, tt.res.want, got)
}
})
}
@ -1070,7 +1070,7 @@ func TestCommandSide_UpdateInstanceGenericOIDCIDP(t *testing.T) {
t.Errorf("got wrong err: %v ", err)
}
if tt.res.err == nil {
assert.Equal(t, tt.res.want, got)
assertObjectDetails(t, tt.res.want, got)
}
})
}
@ -1312,7 +1312,7 @@ func TestCommandSide_MigrateInstanceGenericOIDCToAzureADProvider(t *testing.T) {
t.Errorf("got wrong err: %v ", err)
}
if tt.res.err == nil {
assert.Equal(t, tt.res.want, got)
assertObjectDetails(t, tt.res.want, got)
}
})
}
@ -1520,7 +1520,7 @@ func TestCommandSide_MigrateInstanceOIDCToGoogleIDP(t *testing.T) {
t.Errorf("got wrong err: %v ", err)
}
if tt.res.err == nil {
assert.Equal(t, tt.res.want, got)
assertObjectDetails(t, tt.res.want, got)
}
})
}
@ -1709,7 +1709,7 @@ func TestCommandSide_AddInstanceAzureADIDP(t *testing.T) {
}
if tt.res.err == nil {
assert.Equal(t, tt.res.id, id)
assert.Equal(t, tt.res.want, got)
assertObjectDetails(t, tt.res.want, got)
}
})
}
@ -1929,7 +1929,7 @@ func TestCommandSide_UpdateInstanceAzureADIDP(t *testing.T) {
t.Errorf("got wrong err: %v ", err)
}
if tt.res.err == nil {
assert.Equal(t, tt.res.want, got)
assertObjectDetails(t, tt.res.want, got)
}
})
}
@ -2092,7 +2092,7 @@ func TestCommandSide_AddInstanceGitHubIDP(t *testing.T) {
}
if tt.res.err == nil {
assert.Equal(t, tt.res.id, id)
assert.Equal(t, tt.res.want, got)
assertObjectDetails(t, tt.res.want, got)
}
})
}
@ -2284,7 +2284,7 @@ func TestCommandSide_UpdateInstanceGitHubIDP(t *testing.T) {
t.Errorf("got wrong err: %v ", err)
}
if tt.res.err == nil {
assert.Equal(t, tt.res.want, got)
assertObjectDetails(t, tt.res.want, got)
}
})
}
@ -2542,7 +2542,7 @@ func TestCommandSide_AddInstanceGitHubEnterpriseIDP(t *testing.T) {
}
if tt.res.err == nil {
assert.Equal(t, tt.res.id, id)
assert.Equal(t, tt.res.want, got)
assertObjectDetails(t, tt.res.want, got)
}
})
}
@ -2832,7 +2832,7 @@ func TestCommandSide_UpdateInstanceGitHubEnterpriseIDP(t *testing.T) {
t.Errorf("got wrong err: %v ", err)
}
if tt.res.err == nil {
assert.Equal(t, tt.res.want, got)
assertObjectDetails(t, tt.res.want, got)
}
})
}
@ -2994,7 +2994,7 @@ func TestCommandSide_AddInstanceGitLabIDP(t *testing.T) {
}
if tt.res.err == nil {
assert.Equal(t, tt.res.id, id)
assert.Equal(t, tt.res.want, got)
assertObjectDetails(t, tt.res.want, got)
}
})
}
@ -3184,7 +3184,7 @@ func TestCommandSide_UpdateInstanceGitLabIDP(t *testing.T) {
t.Errorf("got wrong err: %v ", err)
}
if tt.res.err == nil {
assert.Equal(t, tt.res.want, got)
assertObjectDetails(t, tt.res.want, got)
}
})
}
@ -3391,7 +3391,7 @@ func TestCommandSide_AddInstanceGitLabSelfHostedIDP(t *testing.T) {
}
if tt.res.err == nil {
assert.Equal(t, tt.res.id, id)
assert.Equal(t, tt.res.want, got)
assertObjectDetails(t, tt.res.want, got)
}
})
}
@ -3628,7 +3628,7 @@ func TestCommandSide_UpdateInstanceGitLabSelfHostedIDP(t *testing.T) {
t.Errorf("got wrong err: %v ", err)
}
if tt.res.err == nil {
assert.Equal(t, tt.res.want, got)
assertObjectDetails(t, tt.res.want, got)
}
})
}
@ -3790,7 +3790,7 @@ func TestCommandSide_AddInstanceGoogleIDP(t *testing.T) {
}
if tt.res.err == nil {
assert.Equal(t, tt.res.id, id)
assert.Equal(t, tt.res.want, got)
assertObjectDetails(t, tt.res.want, got)
}
})
}
@ -3980,7 +3980,7 @@ func TestCommandSide_UpdateInstanceGoogleIDP(t *testing.T) {
t.Errorf("got wrong err: %v ", err)
}
if tt.res.err == nil {
assert.Equal(t, tt.res.want, got)
assertObjectDetails(t, tt.res.want, got)
}
})
}
@ -4329,7 +4329,7 @@ func TestCommandSide_AddInstanceLDAPIDP(t *testing.T) {
}
if tt.res.err == nil {
assert.Equal(t, tt.res.id, id)
assert.Equal(t, tt.res.want, got)
assertObjectDetails(t, tt.res.want, got)
}
})
}
@ -4717,7 +4717,7 @@ func TestCommandSide_UpdateInstanceLDAPIDP(t *testing.T) {
t.Errorf("got wrong err: %v ", err)
}
if tt.res.err == nil {
assert.Equal(t, tt.res.want, got)
assertObjectDetails(t, tt.res.want, got)
}
})
}
@ -4926,7 +4926,7 @@ func TestCommandSide_AddInstanceAppleIDP(t *testing.T) {
}
if tt.res.err == nil {
assert.Equal(t, tt.res.id, id)
assert.Equal(t, tt.res.want, got)
assertObjectDetails(t, tt.res.want, got)
}
})
}
@ -5165,7 +5165,7 @@ func TestCommandSide_UpdateInstanceAppleIDP(t *testing.T) {
t.Errorf("got wrong err: %v ", err)
}
if tt.res.err == nil {
assert.Equal(t, tt.res.want, got)
assertObjectDetails(t, tt.res.want, got)
}
})
}
@ -5342,7 +5342,7 @@ func TestCommandSide_AddInstanceSAMLIDP(t *testing.T) {
}
if tt.res.err == nil {
assert.Equal(t, tt.res.id, id)
assert.Equal(t, tt.res.want, got)
assertObjectDetails(t, tt.res.want, got)
}
})
}
@ -5561,7 +5561,7 @@ func TestCommandSide_UpdateInstanceGenericSAMLIDP(t *testing.T) {
t.Errorf("got wrong err: %v ", err)
}
if tt.res.err == nil {
assert.Equal(t, tt.res.want, got)
assertObjectDetails(t, tt.res.want, got)
}
})
}
@ -5687,7 +5687,7 @@ func TestCommandSide_RegenerateInstanceSAMLProviderCertificate(t *testing.T) {
t.Errorf("got wrong err: %v ", err)
}
if tt.res.err == nil {
assert.Equal(t, tt.res.want, got)
assertObjectDetails(t, tt.res.want, got)
}
})
}

View File

@ -529,7 +529,7 @@ func TestCommandSide_RemoveIAMMember(t *testing.T) {
t.Errorf("got wrong err: %v ", err)
}
if tt.res.err == nil {
assert.Equal(t, tt.res.want, got)
assertObjectDetails(t, tt.res.want, got)
}
})
}

View File

@ -190,7 +190,7 @@ func TestCommandSide_AddOIDCConfig(t *testing.T) {
t.Errorf("got wrong err: %v ", err)
}
if tt.res.err == nil {
assert.Equal(t, tt.res.want, got)
assertObjectDetails(t, tt.res.want, got)
}
})
}
@ -403,7 +403,7 @@ func TestCommandSide_ChangeOIDCConfig(t *testing.T) {
t.Errorf("got wrong err: %v ", err)
}
if tt.res.err == nil {
assert.Equal(t, tt.res.want, got)
assertObjectDetails(t, tt.res.want, got)
}
})
}

View File

@ -106,7 +106,7 @@ func TestCommandSide_AddDefaultDomainPolicy(t *testing.T) {
t.Errorf("got wrong err: %v ", err)
}
if tt.res.err == nil {
assert.Equal(t, tt.res.want, got)
assertObjectDetails(t, tt.res.want, got)
}
})
}
@ -340,7 +340,7 @@ func TestCommandSide_ChangeDefaultDomainPolicy(t *testing.T) {
t.Errorf("got wrong err: %v ", err)
}
if tt.res.err == nil {
assert.Equal(t, tt.res.want, got)
assertObjectDetails(t, tt.res.want, got)
}
})
}

View File

@ -291,7 +291,7 @@ func TestCommandSide_ActivateDefaultLabelPolicy(t *testing.T) {
t.Errorf("got wrong err: %v ", err)
}
if tt.res.err == nil {
assert.Equal(t, tt.res.want, got)
assertObjectDetails(t, tt.res.want, got)
}
})
}
@ -446,7 +446,7 @@ func TestCommandSide_AddLogoDefaultLabelPolicy(t *testing.T) {
t.Errorf("got wrong err: %v ", err)
}
if tt.res.err == nil {
assert.Equal(t, tt.res.want, got)
assertObjectDetails(t, tt.res.want, got)
}
})
}
@ -588,7 +588,7 @@ func TestCommandSide_RemoveLogoDefaultLabelPolicy(t *testing.T) {
t.Errorf("got wrong err: %v ", err)
}
if tt.res.err == nil {
assert.Equal(t, tt.res.want, got)
assertObjectDetails(t, tt.res.want, got)
}
})
}
@ -743,7 +743,7 @@ func TestCommandSide_AddIconDefaultLabelPolicy(t *testing.T) {
t.Errorf("got wrong err: %v ", err)
}
if tt.res.err == nil {
assert.Equal(t, tt.res.want, got)
assertObjectDetails(t, tt.res.want, got)
}
})
}
@ -845,7 +845,7 @@ func TestCommandSide_RemoveIconDefaultLabelPolicy(t *testing.T) {
t.Errorf("got wrong err: %v ", err)
}
if tt.res.err == nil {
assert.Equal(t, tt.res.want, got)
assertObjectDetails(t, tt.res.want, got)
}
})
}
@ -1003,7 +1003,7 @@ func TestCommandSide_AddLogoDarkDefaultLabelPolicy(t *testing.T) {
t.Errorf("got wrong err: %v ", err)
}
if tt.res.err == nil {
assert.Equal(t, tt.res.want, got)
assertObjectDetails(t, tt.res.want, got)
}
})
}
@ -1145,7 +1145,7 @@ func TestCommandSide_RemoveLogoDarkDefaultLabelPolicy(t *testing.T) {
t.Errorf("got wrong err: %v ", err)
}
if tt.res.err == nil {
assert.Equal(t, tt.res.want, got)
assertObjectDetails(t, tt.res.want, got)
}
})
}
@ -1300,7 +1300,7 @@ func TestCommandSide_AddIconDarkDefaultLabelPolicy(t *testing.T) {
t.Errorf("got wrong err: %v ", err)
}
if tt.res.err == nil {
assert.Equal(t, tt.res.want, got)
assertObjectDetails(t, tt.res.want, got)
}
})
}
@ -1442,7 +1442,7 @@ func TestCommandSide_RemoveIconDarkDefaultLabelPolicy(t *testing.T) {
t.Errorf("got wrong err: %v ", err)
}
if tt.res.err == nil {
assert.Equal(t, tt.res.want, got)
assertObjectDetails(t, tt.res.want, got)
}
})
}
@ -1597,7 +1597,7 @@ func TestCommandSide_AddFontDefaultLabelPolicy(t *testing.T) {
t.Errorf("got wrong err: %v ", err)
}
if tt.res.err == nil {
assert.Equal(t, tt.res.want, got)
assertObjectDetails(t, tt.res.want, got)
}
})
}
@ -1739,7 +1739,7 @@ func TestCommandSide_RemoveFontDefaultLabelPolicy(t *testing.T) {
t.Errorf("got wrong err: %v ", err)
}
if tt.res.err == nil {
assert.Equal(t, tt.res.want, got)
assertObjectDetails(t, tt.res.want, got)
}
})
}

View File

@ -204,7 +204,7 @@ func TestCommandSide_ChangeDefaultLoginPolicy(t *testing.T) {
t.Errorf("got wrong err: %v ", err)
}
if tt.res.err == nil {
assert.Equal(t, tt.res.want, got)
assertObjectDetails(t, tt.res.want, got)
}
})
}
@ -726,7 +726,7 @@ func TestCommandSide_RemoveIDPProviderDefaultLoginPolicy(t *testing.T) {
t.Errorf("got wrong err: %v ", err)
}
if tt.res.err == nil {
assert.Equal(t, tt.res.want, got)
assertObjectDetails(t, tt.res.want, got)
}
})
}
@ -901,7 +901,7 @@ func TestCommandSide_AddSecondFactorDefaultLoginPolicy(t *testing.T) {
t.Errorf("got wrong err: %v ", err)
}
if tt.res.err == nil {
assert.Equal(t, tt.res.want, got)
assertObjectDetails(t, tt.res.want, got)
}
})
}
@ -1170,7 +1170,7 @@ func TestCommandSide_RemoveSecondFactorDefaultLoginPolicy(t *testing.T) {
t.Errorf("got wrong err: %v ", err)
}
if tt.res.err == nil {
assert.Equal(t, tt.res.want, got)
assertObjectDetails(t, tt.res.want, got)
}
})
}
@ -1269,7 +1269,7 @@ func TestCommandSide_AddMultiFactorDefaultLoginPolicy(t *testing.T) {
t.Errorf("got wrong err: %v ", err)
}
if tt.res.err == nil {
assert.Equal(t, tt.res.want, got)
assertObjectDetails(t, tt.res.want, got)
}
})
}
@ -1397,7 +1397,7 @@ func TestCommandSide_RemoveMultiFactorDefaultLoginPolicy(t *testing.T) {
t.Errorf("got wrong err: %v ", err)
}
if tt.res.err == nil {
assert.Equal(t, tt.res.want, got)
assertObjectDetails(t, tt.res.want, got)
}
})
}

View File

@ -120,7 +120,7 @@ func TestCommandSide_AddDefaultNotificationPolicy(t *testing.T) {
t.Errorf("got wrong err: %v ", err)
}
if tt.res.err == nil {
assert.Equal(t, tt.res.want, got)
assertObjectDetails(t, tt.res.want, got)
}
})
}
@ -231,7 +231,7 @@ func TestCommandSide_ChangeDefaultNotificationPolicy(t *testing.T) {
t.Errorf("got wrong err: %v ", err)
}
if tt.res.err == nil {
assert.Equal(t, tt.res.want, got)
assertObjectDetails(t, tt.res.want, got)
}
})
}

View File

@ -99,7 +99,7 @@ func TestCommandSide_AddDefaultPasswordAgePolicy(t *testing.T) {
t.Errorf("got wrong err: %v ", err)
}
if tt.res.err == nil {
assert.Equal(t, tt.res.want, got)
assertObjectDetails(t, tt.res.want, got)
}
})
}

View File

@ -127,7 +127,7 @@ func TestCommandSide_AddDefaultPasswordComplexityPolicy(t *testing.T) {
t.Errorf("got wrong err: %v ", err)
}
if tt.res.err == nil {
assert.Equal(t, tt.res.want, got)
assertObjectDetails(t, tt.res.want, got)
}
})
}

View File

@ -103,7 +103,7 @@ func TestCommandSide_AddDefaultLockoutPolicy(t *testing.T) {
t.Errorf("got wrong err: %v ", err)
}
if tt.res.err == nil {
assert.Equal(t, tt.res.want, got)
assertObjectDetails(t, tt.res.want, got)
}
})
}

View File

@ -178,7 +178,7 @@ func TestCommandSide_AddDefaultPrivacyPolicy(t *testing.T) {
t.Errorf("got wrong err: %v ", err)
}
if tt.res.err == nil {
assert.Equal(t, tt.res.want, got)
assertObjectDetails(t, tt.res.want, got)
}
})
}

View File

@ -140,7 +140,7 @@ func TestCommandSide_AddSecretGenerator(t *testing.T) {
t.Errorf("got wrong err: %v ", err)
}
if tt.res.err == nil {
assert.Equal(t, tt.res.want, got)
assertObjectDetails(t, tt.res.want, got)
}
})
}
@ -376,7 +376,7 @@ func TestCommandSide_ChangeSecretGenerator(t *testing.T) {
t.Errorf("got wrong err: %v ", err)
}
if tt.res.err == nil {
assert.Equal(t, tt.res.want, got)
assertObjectDetails(t, tt.res.want, got)
}
})
}
@ -518,7 +518,7 @@ func TestCommandSide_RemoveSecretGenerator(t *testing.T) {
t.Errorf("got wrong err: %v ", err)
}
if tt.res.err == nil {
assert.Equal(t, tt.res.want, got)
assertObjectDetails(t, tt.res.want, got)
}
})
}

View File

@ -1370,7 +1370,7 @@ func TestCommandSide_UpdateInstance(t *testing.T) {
t.Errorf("got wrong err: %v ", err)
}
if tt.res.err == nil {
assert.Equal(t, tt.res.want, got)
assertObjectDetails(t, tt.res.want, got)
}
})
}
@ -1507,7 +1507,7 @@ func TestCommandSide_RemoveInstance(t *testing.T) {
t.Errorf("got wrong err: %v ", err)
}
if tt.res.err == nil {
assert.Equal(t, tt.res.want, got)
assertObjectDetails(t, tt.res.want, got)
}
})
}

View File

@ -119,7 +119,7 @@ func TestCommands_AddTrustedDomain(t *testing.T) {
}
got, err := c.AddTrustedDomain(tt.args.ctx, tt.args.trustedDomain)
assert.ErrorIs(t, err, tt.want.err)
assert.Equal(t, tt.want.details, got)
assertObjectDetails(t, tt.want.details, got)
})
}
}
@ -191,7 +191,7 @@ func TestCommands_RemoveTrustedDomain(t *testing.T) {
}
got, err := c.RemoveTrustedDomain(tt.args.ctx, tt.args.trustedDomain)
assert.ErrorIs(t, err, tt.want.err)
assert.Equal(t, tt.want.details, got)
assertObjectDetails(t, tt.want.details, got)
})
}
}

View File

@ -223,7 +223,7 @@ func TestLimits_SetLimits(t *testing.T) {
t.Errorf("got wrong err: %v ", err)
}
if tt.res.err == nil {
assert.Equal(t, tt.res.want, got)
assertObjectDetails(t, tt.res.want, got)
}
})
}
@ -626,8 +626,11 @@ func TestLimits_SetLimitsBulk(t *testing.T) {
t.Errorf("got wrong err: %v ", err)
}
if tt.res.err == nil {
assert.Equal(t, tt.res.want, gotDetails)
assert.Equal(t, tt.res.wantTarget, gotTargetDetails)
assertObjectDetails(t, tt.res.want, gotDetails)
assert.Len(t, gotTargetDetails, len(tt.res.wantTarget))
for i, want := range tt.res.wantTarget {
assertObjectDetails(t, want, gotTargetDetails[i])
}
}
})
}
@ -748,7 +751,7 @@ func TestLimits_ResetLimits(t *testing.T) {
t.Errorf("got wrong err: %v ", err)
}
if tt.res.err == nil {
assert.Equal(t, tt.res.want, got)
assertObjectDetails(t, tt.res.want, got)
}
})
}

View File

@ -129,7 +129,7 @@ func TestCommands_AddAction(t *testing.T) {
}
if tt.res.err == nil {
assert.Equal(t, tt.res.id, id)
assert.Equal(t, tt.res.details, details)
assertObjectDetails(t, tt.res.details, details)
}
})
}
@ -330,7 +330,7 @@ func TestCommands_ChangeAction(t *testing.T) {
t.Errorf("got wrong err: %v ", err)
}
if tt.res.err == nil {
assert.Equal(t, tt.res.details, details)
assertObjectDetails(t, tt.res.details, details)
}
})
}
@ -463,7 +463,7 @@ func TestCommands_DeactivateAction(t *testing.T) {
t.Errorf("got wrong err: %v ", err)
}
if tt.res.err == nil {
assert.Equal(t, tt.res.details, details)
assertObjectDetails(t, tt.res.details, details)
}
})
}
@ -596,7 +596,7 @@ func TestCommands_ReactivateAction(t *testing.T) {
t.Errorf("got wrong err: %v ", err)
}
if tt.res.err == nil {
assert.Equal(t, tt.res.details, details)
assertObjectDetails(t, tt.res.details, details)
}
})
}
@ -742,7 +742,7 @@ func TestCommands_DeleteAction(t *testing.T) {
t.Errorf("got wrong err: %v ", err)
}
if tt.res.err == nil {
assert.Equal(t, tt.res.details, details)
assertObjectDetails(t, tt.res.details, details)
}
})
}

View File

@ -6632,7 +6632,7 @@ func TestCommandSide_SetCustomOrgLoginText(t *testing.T) {
t.Errorf("got wrong err: %v ", err)
}
if tt.res.err == nil {
assert.Equal(t, tt.res.want, got)
assertObjectDetails(t, tt.res.want, got)
}
})
}

View File

@ -356,7 +356,7 @@ func TestCommandSide_SetCustomMessageText(t *testing.T) {
t.Errorf("got wrong err: %v ", err)
}
if tt.res.err == nil {
assert.Equal(t, tt.res.want, got)
assertObjectDetails(t, tt.res.want, got)
}
})
}

View File

@ -396,7 +396,7 @@ func TestCommandSide_AddOrgDomain(t *testing.T) {
t.Errorf("got wrong err: %v ", err)
}
if tt.res.err == nil {
assert.Equal(t, tt.res.want, got)
assertObjectDetails(t, tt.res.want, got)
}
})
}
@ -1102,7 +1102,7 @@ func TestCommandSide_ValidateOrgDomain(t *testing.T) {
t.Errorf("got wrong err: %v ", err)
}
if tt.res.err == nil {
assert.Equal(t, tt.res.want, got)
assertObjectDetails(t, tt.res.want, got)
}
})
}
@ -1287,7 +1287,7 @@ func TestCommandSide_SetPrimaryDomain(t *testing.T) {
t.Errorf("got wrong err: %v ", err)
}
if tt.res.err == nil {
assert.Equal(t, tt.res.want, got)
assertObjectDetails(t, tt.res.want, got)
}
})
}
@ -1526,7 +1526,7 @@ func TestCommandSide_RemoveOrgDomain(t *testing.T) {
t.Errorf("got wrong err: %v ", err)
}
if tt.res.err == nil {
assert.Equal(t, tt.res.want, got)
assertObjectDetails(t, tt.res.want, got)
}
})
}

View File

@ -112,7 +112,7 @@ func TestCommands_ClearFlow(t *testing.T) {
t.Errorf("got wrong err: %v ", err)
}
if tt.res.err == nil {
assert.Equal(t, tt.res.details, details)
assertObjectDetails(t, tt.res.details, details)
}
})
}
@ -276,7 +276,7 @@ func TestCommands_SetTriggerActions(t *testing.T) {
t.Errorf("got wrong err: %v ", err)
}
if tt.res.err == nil {
assert.Equal(t, tt.res.details, details)
assertObjectDetails(t, tt.res.details, details)
}
})
}

View File

@ -652,7 +652,7 @@ func TestCommands_RemoveIDPConfig(t *testing.T) {
t.Errorf("got wrong err: %v ", err)
}
if tt.res.err == nil {
assert.Equal(t, tt.res.want, got)
assertObjectDetails(t, tt.res.want, got)
}
})
}

View File

@ -310,7 +310,7 @@ func TestCommandSide_AddOrgGenericOAuthIDP(t *testing.T) {
}
if tt.res.err == nil {
assert.Equal(t, tt.res.id, id)
assert.Equal(t, tt.res.want, got)
assertObjectDetails(t, tt.res.want, got)
}
})
}
@ -639,7 +639,7 @@ func TestCommandSide_UpdateOrgGenericOAuthIDP(t *testing.T) {
t.Errorf("got wrong err: %v ", err)
}
if tt.res.err == nil {
assert.Equal(t, tt.res.want, got)
assertObjectDetails(t, tt.res.want, got)
}
})
}
@ -856,7 +856,7 @@ func TestCommandSide_AddOrgGenericOIDCIDP(t *testing.T) {
}
if tt.res.err == nil {
assert.Equal(t, tt.res.id, id)
assert.Equal(t, tt.res.want, got)
assertObjectDetails(t, tt.res.want, got)
}
})
}
@ -1105,7 +1105,7 @@ func TestCommandSide_UpdateOrgGenericOIDCIDP(t *testing.T) {
t.Errorf("got wrong err: %v ", err)
}
if tt.res.err == nil {
assert.Equal(t, tt.res.want, got)
assertObjectDetails(t, tt.res.want, got)
}
})
}
@ -1350,7 +1350,7 @@ func TestCommandSide_MigrateOrgGenericOIDCToAzureADProvider(t *testing.T) {
t.Errorf("got wrong err: %v ", err)
}
if tt.res.err == nil {
assert.Equal(t, tt.res.want, got)
assertObjectDetails(t, tt.res.want, got)
}
})
}
@ -1566,7 +1566,7 @@ func TestCommandSide_MigrateOrgOIDCToGoogleIDP(t *testing.T) {
t.Errorf("got wrong err: %v ", err)
}
if tt.res.err == nil {
assert.Equal(t, tt.res.want, got)
assertObjectDetails(t, tt.res.want, got)
}
})
}
@ -1761,7 +1761,7 @@ func TestCommandSide_AddOrgAzureADIDP(t *testing.T) {
}
if tt.res.err == nil {
assert.Equal(t, tt.res.id, id)
assert.Equal(t, tt.res.want, got)
assertObjectDetails(t, tt.res.want, got)
}
})
}
@ -1988,7 +1988,7 @@ func TestCommandSide_UpdateOrgAzureADIDP(t *testing.T) {
t.Errorf("got wrong err: %v ", err)
}
if tt.res.err == nil {
assert.Equal(t, tt.res.want, got)
assertObjectDetails(t, tt.res.want, got)
}
})
}
@ -2156,7 +2156,7 @@ func TestCommandSide_AddOrgGitHubIDP(t *testing.T) {
}
if tt.res.err == nil {
assert.Equal(t, tt.res.id, id)
assert.Equal(t, tt.res.want, got)
assertObjectDetails(t, tt.res.want, got)
}
})
}
@ -2354,7 +2354,7 @@ func TestCommandSide_UpdateOrgGitHubIDP(t *testing.T) {
t.Errorf("got wrong err: %v ", err)
}
if tt.res.err == nil {
assert.Equal(t, tt.res.want, got)
assertObjectDetails(t, tt.res.want, got)
}
})
}
@ -2621,7 +2621,7 @@ func TestCommandSide_AddOrgGitHubEnterpriseIDP(t *testing.T) {
}
if tt.res.err == nil {
assert.Equal(t, tt.res.id, id)
assert.Equal(t, tt.res.want, got)
assertObjectDetails(t, tt.res.want, got)
}
})
}
@ -2921,7 +2921,7 @@ func TestCommandSide_UpdateOrgGitHubEnterpriseIDP(t *testing.T) {
t.Errorf("got wrong err: %v ", err)
}
if tt.res.err == nil {
assert.Equal(t, tt.res.want, got)
assertObjectDetails(t, tt.res.want, got)
}
})
}
@ -3088,7 +3088,7 @@ func TestCommandSide_AddOrgGitLabIDP(t *testing.T) {
}
if tt.res.err == nil {
assert.Equal(t, tt.res.id, id)
assert.Equal(t, tt.res.want, got)
assertObjectDetails(t, tt.res.want, got)
}
})
}
@ -3284,7 +3284,7 @@ func TestCommandSide_UpdateOrgGitLabIDP(t *testing.T) {
t.Errorf("got wrong err: %v ", err)
}
if tt.res.err == nil {
assert.Equal(t, tt.res.want, got)
assertObjectDetails(t, tt.res.want, got)
}
})
}
@ -3498,7 +3498,7 @@ func TestCommandSide_AddOrgGitLabSelfHostedIDP(t *testing.T) {
}
if tt.res.err == nil {
assert.Equal(t, tt.res.id, id)
assert.Equal(t, tt.res.want, got)
assertObjectDetails(t, tt.res.want, got)
}
})
}
@ -3743,7 +3743,7 @@ func TestCommandSide_UpdateOrgGitLabSelfHostedIDP(t *testing.T) {
t.Errorf("got wrong err: %v ", err)
}
if tt.res.err == nil {
assert.Equal(t, tt.res.want, got)
assertObjectDetails(t, tt.res.want, got)
}
})
}
@ -3910,7 +3910,7 @@ func TestCommandSide_AddOrgGoogleIDP(t *testing.T) {
}
if tt.res.err == nil {
assert.Equal(t, tt.res.id, id)
assert.Equal(t, tt.res.want, got)
assertObjectDetails(t, tt.res.want, got)
}
})
}
@ -4106,7 +4106,7 @@ func TestCommandSide_UpdateOrgGoogleIDP(t *testing.T) {
t.Errorf("got wrong err: %v ", err)
}
if tt.res.err == nil {
assert.Equal(t, tt.res.want, got)
assertObjectDetails(t, tt.res.want, got)
}
})
}
@ -4466,7 +4466,7 @@ func TestCommandSide_AddOrgLDAPIDP(t *testing.T) {
}
if tt.res.err == nil {
assert.Equal(t, tt.res.id, id)
assert.Equal(t, tt.res.want, got)
assertObjectDetails(t, tt.res.want, got)
}
})
}
@ -4865,7 +4865,7 @@ func TestCommandSide_UpdateOrgLDAPIDP(t *testing.T) {
t.Errorf("got wrong err: %v ", err)
}
if tt.res.err == nil {
assert.Equal(t, tt.res.want, got)
assertObjectDetails(t, tt.res.want, got)
}
})
}
@ -5081,7 +5081,7 @@ func TestCommandSide_AddOrgAppleIDP(t *testing.T) {
}
if tt.res.err == nil {
assert.Equal(t, tt.res.id, id)
assert.Equal(t, tt.res.want, got)
assertObjectDetails(t, tt.res.want, got)
}
})
}
@ -5328,7 +5328,7 @@ func TestCommandSide_UpdateOrgAppleIDP(t *testing.T) {
t.Errorf("got wrong err: %v ", err)
}
if tt.res.err == nil {
assert.Equal(t, tt.res.want, got)
assertObjectDetails(t, tt.res.want, got)
}
})
}
@ -5513,7 +5513,7 @@ func TestCommandSide_AddOrgSAMLIDP(t *testing.T) {
}
if tt.res.err == nil {
assert.Equal(t, tt.res.id, id)
assert.Equal(t, tt.res.want, got)
assertObjectDetails(t, tt.res.want, got)
}
})
}
@ -5741,7 +5741,7 @@ func TestCommandSide_UpdateOrgSAMLIDP(t *testing.T) {
t.Errorf("got wrong err: %v ", err)
}
if tt.res.err == nil {
assert.Equal(t, tt.res.want, got)
assertObjectDetails(t, tt.res.want, got)
}
})
}
@ -5873,7 +5873,7 @@ func TestCommandSide_RegenerateOrgSAMLProviderCertificate(t *testing.T) {
t.Errorf("got wrong err: %v ", err)
}
if tt.res.err == nil {
assert.Equal(t, tt.res.want, got)
assertObjectDetails(t, tt.res.want, got)
}
})
}

View File

@ -841,7 +841,7 @@ func TestCommandSide_RemoveOrgMember(t *testing.T) {
t.Errorf("got wrong err: %v ", err)
}
if tt.res.err == nil {
assert.Equal(t, tt.res.want, got)
assertObjectDetails(t, tt.res.want, got)
}
})
}

View File

@ -280,7 +280,7 @@ func TestCommandSide_BulkSetOrgMetadata(t *testing.T) {
t.Errorf("got wrong err: %v ", err)
}
if tt.res.err == nil {
assert.Equal(t, tt.res.want, got)
assertObjectDetails(t, tt.res.want, got)
}
})
}
@ -420,7 +420,7 @@ func TestCommandSide_OrgRemoveMetadata(t *testing.T) {
t.Errorf("got wrong err: %v ", err)
}
if tt.res.err == nil {
assert.Equal(t, tt.res.want, got)
assertObjectDetails(t, tt.res.want, got)
}
})
}
@ -618,7 +618,7 @@ func TestCommandSide_BulkRemoveOrgMetadata(t *testing.T) {
t.Errorf("got wrong err: %v ", err)
}
if tt.res.err == nil {
assert.Equal(t, tt.res.want, got)
assertObjectDetails(t, tt.res.want, got)
}
})
}

View File

@ -247,7 +247,7 @@ func TestCommandSide_AddDomainPolicy(t *testing.T) {
t.Errorf("got wrong err: %v ", err)
}
if tt.res.err == nil {
assert.Equal(t, tt.res.want, got)
assertObjectDetails(t, tt.res.want, got)
}
})
}
@ -461,7 +461,7 @@ func TestCommandSide_ChangeDomainPolicy(t *testing.T) {
t.Errorf("got wrong err: %v ", err)
}
if tt.res.err == nil {
assert.Equal(t, tt.res.want, got)
assertObjectDetails(t, tt.res.want, got)
}
})
}
@ -648,7 +648,7 @@ func TestCommandSide_RemoveDomainPolicy(t *testing.T) {
t.Errorf("got wrong err: %v ", err)
}
if tt.res.err == nil {
assert.Equal(t, tt.res.want, got)
assertObjectDetails(t, tt.res.want, got)
}
})
}

View File

@ -773,7 +773,7 @@ func TestCommandSide_AddLogoLabelPolicy(t *testing.T) {
t.Errorf("got wrong err: %v ", err)
}
if tt.res.err == nil {
assert.Equal(t, tt.res.want, got)
assertObjectDetails(t, tt.res.want, got)
}
})
}
@ -896,7 +896,7 @@ func TestCommandSide_RemoveLogoLabelPolicy(t *testing.T) {
t.Errorf("got wrong err: %v ", err)
}
if tt.res.err == nil {
assert.Equal(t, tt.res.want, got)
assertObjectDetails(t, tt.res.want, got)
}
})
}
@ -1078,7 +1078,7 @@ func TestCommandSide_AddIconLabelPolicy(t *testing.T) {
t.Errorf("got wrong err: %v ", err)
}
if tt.res.err == nil {
assert.Equal(t, tt.res.want, got)
assertObjectDetails(t, tt.res.want, got)
}
})
}
@ -1198,7 +1198,7 @@ func TestCommandSide_RemoveIconLabelPolicy(t *testing.T) {
t.Errorf("got wrong err: %v ", err)
}
if tt.res.err == nil {
assert.Equal(t, tt.res.want, got)
assertObjectDetails(t, tt.res.want, got)
}
})
}
@ -1380,7 +1380,7 @@ func TestCommandSide_AddLogoDarkLabelPolicy(t *testing.T) {
t.Errorf("got wrong err: %v ", err)
}
if tt.res.err == nil {
assert.Equal(t, tt.res.want, got)
assertObjectDetails(t, tt.res.want, got)
}
})
}
@ -1503,7 +1503,7 @@ func TestCommandSide_RemoveLogoDarkLabelPolicy(t *testing.T) {
t.Errorf("got wrong err: %v ", err)
}
if tt.res.err == nil {
assert.Equal(t, tt.res.want, got)
assertObjectDetails(t, tt.res.want, got)
}
})
}
@ -1685,7 +1685,7 @@ func TestCommandSide_AddIconDarkLabelPolicy(t *testing.T) {
t.Errorf("got wrong err: %v ", err)
}
if tt.res.err == nil {
assert.Equal(t, tt.res.want, got)
assertObjectDetails(t, tt.res.want, got)
}
})
}
@ -1803,7 +1803,7 @@ func TestCommandSide_RemoveIconDarkLabelPolicy(t *testing.T) {
t.Errorf("got wrong err: %v ", err)
}
if tt.res.err == nil {
assert.Equal(t, tt.res.want, got)
assertObjectDetails(t, tt.res.want, got)
}
})
}
@ -1977,7 +1977,7 @@ func TestCommandSide_AddFontLabelPolicy(t *testing.T) {
t.Errorf("got wrong err: %v ", err)
}
if tt.res.err == nil {
assert.Equal(t, tt.res.want, got)
assertObjectDetails(t, tt.res.want, got)
}
})
}
@ -2095,7 +2095,7 @@ func TestCommandSide_RemoveFontLabelPolicy(t *testing.T) {
t.Errorf("got wrong err: %v ", err)
}
if tt.res.err == nil {
assert.Equal(t, tt.res.want, got)
assertObjectDetails(t, tt.res.want, got)
}
})
}

View File

@ -483,7 +483,7 @@ func TestCommandSide_AddLoginPolicy(t *testing.T) {
t.Errorf("got wrong err: %v ", err)
}
if tt.res.err == nil {
assert.Equal(t, tt.res.want, got)
assertObjectDetails(t, tt.res.want, got)
}
})
}
@ -690,7 +690,7 @@ func TestCommandSide_ChangeLoginPolicy(t *testing.T) {
t.Errorf("got wrong err: %v ", err)
}
if tt.res.err == nil {
assert.Equal(t, tt.res.want, got)
assertObjectDetails(t, tt.res.want, got)
}
})
}
@ -803,7 +803,7 @@ func TestCommandSide_RemoveLoginPolicy(t *testing.T) {
t.Errorf("got wrong err: %v ", err)
}
if tt.res.err == nil {
assert.Equal(t, tt.res.want, got)
assertObjectDetails(t, tt.res.want, got)
}
})
}
@ -1393,7 +1393,7 @@ func TestCommandSide_RemoveIDPProviderLoginPolicy(t *testing.T) {
t.Errorf("got wrong err: %v ", err)
}
if tt.res.err == nil {
assert.Equal(t, tt.res.want, got)
assertObjectDetails(t, tt.res.want, got)
}
})
}

View File

@ -136,7 +136,7 @@ func TestCommandSide_AddNotificationPolicy(t *testing.T) {
t.Errorf("got wrong err: %v ", err)
}
if tt.res.err == nil {
assert.Equal(t, tt.res.want, got)
assertObjectDetails(t, tt.res.want, got)
}
})
}
@ -260,7 +260,7 @@ func TestCommandSide_ChangeNotificationPolicy(t *testing.T) {
t.Errorf("got wrong err: %v ", err)
}
if tt.res.err == nil {
assert.Equal(t, tt.res.want, got)
assertObjectDetails(t, tt.res.want, got)
}
})
}
@ -357,7 +357,7 @@ func TestCommandSide_RemoveNotificationPolicy(t *testing.T) {
t.Errorf("got wrong err: %v ", err)
}
if tt.res.err == nil {
assert.Equal(t, tt.res.want, got)
assertObjectDetails(t, tt.res.want, got)
}
})
}

View File

@ -368,7 +368,7 @@ func TestCommandSide_RemovePasswordAgePolicy(t *testing.T) {
t.Errorf("got wrong err: %v ", err)
}
if tt.res.err == nil {
assert.Equal(t, tt.res.want, got)
assertObjectDetails(t, tt.res.want, got)
}
})
}

View File

@ -395,7 +395,7 @@ func TestCommandSide_RemovePasswordComplexityPolicy(t *testing.T) {
t.Errorf("got wrong err: %v ", err)
}
if tt.res.err == nil {
assert.Equal(t, tt.res.want, got)
assertObjectDetails(t, tt.res.want, got)
}
})
}

View File

@ -582,7 +582,7 @@ func TestCommandSide_RemovePrivacyPolicy(t *testing.T) {
t.Errorf("got wrong err: %v ", err)
}
if tt.res.err == nil {
assert.Equal(t, tt.res.want, got)
assertObjectDetails(t, tt.res.want, got)
}
})
}

View File

@ -7,7 +7,10 @@ import (
"reflect"
"testing"
"github.com/stretchr/testify/assert"
"github.com/zitadel/zitadel/internal/command/preparation"
"github.com/zitadel/zitadel/internal/domain"
"github.com/zitadel/zitadel/internal/eventstore"
)
@ -89,3 +92,17 @@ func (mf *MultiFilter) Filter() preparation.FilterToQueryReducer {
return mf.filters[mf.count-1](ctx, queryFactory)
}
}
func assertObjectDetails(t *testing.T, want, got *domain.ObjectDetails) {
if want == nil {
assert.Nil(t, got)
return
}
assert.Equal(t, got.CreationDate, want.CreationDate)
assert.Equal(t, got.EventDate, want.EventDate)
assert.Equal(t, got.ResourceOwner, want.ResourceOwner)
assert.Equal(t, got.Sequence, want.Sequence)
if want.ID != "" {
assert.Equal(t, got.ID, want.ID)
}
}

View File

@ -190,7 +190,7 @@ func TestCommandSide_ChangeApplication(t *testing.T) {
t.Errorf("got wrong err: %v ", err)
}
if tt.res.err == nil {
assert.Equal(t, tt.res.want, got)
assertObjectDetails(t, tt.res.want, got)
}
})
}
@ -342,7 +342,7 @@ func TestCommandSide_DeactivateApplication(t *testing.T) {
t.Errorf("got wrong err: %v ", err)
}
if tt.res.err == nil {
assert.Equal(t, tt.res.want, got)
assertObjectDetails(t, tt.res.want, got)
}
})
}
@ -494,7 +494,7 @@ func TestCommandSide_ReactivateApplication(t *testing.T) {
t.Errorf("got wrong err: %v ", err)
}
if tt.res.err == nil {
assert.Equal(t, tt.res.want, got)
assertObjectDetails(t, tt.res.want, got)
}
})
}
@ -670,7 +670,7 @@ func TestCommandSide_RemoveApplication(t *testing.T) {
t.Errorf("got wrong err: %v ", err)
}
if tt.res.err == nil {
assert.Equal(t, tt.res.want, got)
assertObjectDetails(t, tt.res.want, got)
}
})
}

View File

@ -575,7 +575,7 @@ func TestCommandSide_RemoveProjectGrantMember(t *testing.T) {
t.Errorf("got wrong err: %v ", err)
}
if tt.res.err == nil {
assert.Equal(t, tt.res.want, got)
assertObjectDetails(t, tt.res.want, got)
}
})
}

View File

@ -926,7 +926,7 @@ func TestCommandSide_DeactivateProjectGrant(t *testing.T) {
t.Errorf("got wrong err: %v ", err)
}
if tt.res.err == nil {
assert.Equal(t, tt.res.want, got)
assertObjectDetails(t, tt.res.want, got)
}
})
}
@ -1123,7 +1123,7 @@ func TestCommandSide_ReactivateProjectGrant(t *testing.T) {
t.Errorf("got wrong err: %v ", err)
}
if tt.res.err == nil {
assert.Equal(t, tt.res.want, got)
assertObjectDetails(t, tt.res.want, got)
}
})
}
@ -1386,7 +1386,7 @@ func TestCommandSide_RemoveProjectGrant(t *testing.T) {
t.Errorf("got wrong err: %v ", err)
}
if tt.res.err == nil {
assert.Equal(t, tt.res.want, got)
assertObjectDetails(t, tt.res.want, got)
}
})
}

View File

@ -607,7 +607,7 @@ func TestCommandSide_RemoveProjectMember(t *testing.T) {
t.Errorf("got wrong err: %v ", err)
}
if tt.res.err == nil {
assert.Equal(t, tt.res.want, got)
assertObjectDetails(t, tt.res.want, got)
}
})
}

View File

@ -423,7 +423,7 @@ func TestCommandSide_BulkAddProjectRole(t *testing.T) {
t.Errorf("got wrong err: %v ", err)
}
if tt.res.err == nil {
assert.Equal(t, tt.res.want, got)
assertObjectDetails(t, tt.res.want, got)
}
})
}
@ -997,7 +997,7 @@ func TestCommandSide_RemoveProjectRole(t *testing.T) {
t.Errorf("got wrong err: %v ", err)
}
if tt.res.err == nil {
assert.Equal(t, tt.res.want, got)
assertObjectDetails(t, tt.res.want, got)
}
})
}

View File

@ -606,7 +606,7 @@ func TestCommandSide_DeactivateProject(t *testing.T) {
t.Errorf("got wrong err: %v ", err)
}
if tt.res.err == nil {
assert.Equal(t, tt.res.want, got)
assertObjectDetails(t, tt.res.want, got)
}
})
}
@ -782,7 +782,7 @@ func TestCommandSide_ReactivateProject(t *testing.T) {
t.Errorf("got wrong err: %v ", err)
}
if tt.res.err == nil {
assert.Equal(t, tt.res.want, got)
assertObjectDetails(t, tt.res.want, got)
}
})
}
@ -1066,7 +1066,7 @@ func TestCommandSide_RemoveProject(t *testing.T) {
t.Errorf("got wrong err: %v ", err)
}
if tt.res.err == nil {
assert.Equal(t, tt.res.want, got)
assertObjectDetails(t, tt.res.want, got)
}
})
}

View File

@ -275,7 +275,7 @@ func TestQuota_AddQuota(t *testing.T) {
t.Errorf("got wrong err: %v ", err)
}
if tt.res.err == nil {
assert.Equal(t, tt.res.want, got)
assertObjectDetails(t, tt.res.want, got)
}
})
}
@ -541,7 +541,7 @@ func TestQuota_SetQuota(t *testing.T) {
t.Errorf("got wrong err: %v ", err)
}
if tt.res.err == nil {
assert.Equal(t, tt.res.want, got)
assertObjectDetails(t, tt.res.want, got)
}
})
}
@ -678,7 +678,7 @@ func TestQuota_RemoveQuota(t *testing.T) {
t.Errorf("got wrong err: %v ", err)
}
if tt.res.err == nil {
assert.Equal(t, tt.res.want, got)
assertObjectDetails(t, tt.res.want, got)
}
})
}

View File

@ -232,7 +232,7 @@ func TestSetRestrictions(t *testing.T) {
t.Errorf("got wrong err: %v ", err)
}
if tt.res.err == nil {
assert.Equal(t, tt.res.want, got)
assertObjectDetails(t, tt.res.want, got)
}
})
}

View File

@ -1464,7 +1464,7 @@ func TestCommands_TerminateSession(t *testing.T) {
}
got, err := c.TerminateSession(tt.args.ctx, tt.args.sessionID, tt.args.sessionToken)
require.ErrorIs(t, err, tt.res.err)
assert.Equal(t, tt.res.want, got)
assertObjectDetails(t, tt.res.want, got)
})
}
}

View File

@ -94,7 +94,7 @@ func TestCommandSide_AddSMSConfigTwilio(t *testing.T) {
t.Errorf("got wrong err: %v ", err)
}
if tt.res.err == nil {
assert.Equal(t, tt.res.want, got)
assertObjectDetails(t, tt.res.want, got)
}
})
}
@ -253,7 +253,7 @@ func TestCommandSide_ChangeSMSConfigTwilio(t *testing.T) {
t.Errorf("got wrong err: %v ", err)
}
if tt.res.err == nil {
assert.Equal(t, tt.res.want, got)
assertObjectDetails(t, tt.res.want, got)
}
})
}
@ -360,7 +360,7 @@ func TestCommandSide_ActivateSMSConfigTwilio(t *testing.T) {
t.Errorf("got wrong err: %v ", err)
}
if tt.res.err == nil {
assert.Equal(t, tt.res.want, got)
assertObjectDetails(t, tt.res.want, got)
}
})
}
@ -474,7 +474,7 @@ func TestCommandSide_DeactivateSMSConfigTwilio(t *testing.T) {
t.Errorf("got wrong err: %v ", err)
}
if tt.res.err == nil {
assert.Equal(t, tt.res.want, got)
assertObjectDetails(t, tt.res.want, got)
}
})
}
@ -581,7 +581,7 @@ func TestCommandSide_RemoveSMSConfig(t *testing.T) {
t.Errorf("got wrong err: %v ", err)
}
if tt.res.err == nil {
assert.Equal(t, tt.res.want, got)
assertObjectDetails(t, tt.res.want, got)
}
})
}

View File

@ -332,7 +332,7 @@ func TestCommandSide_AddSMTPConfig(t *testing.T) {
t.Errorf("got wrong err: %v ", err)
}
if tt.res.err == nil {
assert.Equal(t, tt.res.want, got)
assertObjectDetails(t, tt.res.want, got)
}
})
}
@ -736,7 +736,7 @@ func TestCommandSide_ChangeSMTPConfig(t *testing.T) {
t.Errorf("got wrong err: %v ", err)
}
if tt.res.err == nil {
assert.Equal(t, tt.res.want, got)
assertObjectDetails(t, tt.res.want, got)
}
})
}
@ -852,7 +852,7 @@ func TestCommandSide_ChangeSMTPConfigPassword(t *testing.T) {
t.Errorf("got wrong err: %v ", err)
}
if tt.res.err == nil {
assert.Equal(t, tt.res.want, got)
assertObjectDetails(t, tt.res.want, got)
}
})
}
@ -968,7 +968,7 @@ func TestCommandSide_ActivateSMTPConfig(t *testing.T) {
t.Errorf("got wrong err: %v ", err)
}
if tt.res.err == nil {
assert.Equal(t, tt.res.want, got)
assertObjectDetails(t, tt.res.want, got)
}
})
}
@ -1091,7 +1091,7 @@ func TestCommandSide_DeactivateSMTPConfig(t *testing.T) {
t.Errorf("got wrong err: %v ", err)
}
if tt.res.err == nil {
assert.Equal(t, tt.res.want, got)
assertObjectDetails(t, tt.res.want, got)
}
})
}
@ -1190,7 +1190,7 @@ func TestCommandSide_RemoveSMTPConfig(t *testing.T) {
t.Errorf("got wrong err: %v ", err)
}
if tt.res.err == nil {
assert.Equal(t, tt.res.want, got)
assertObjectDetails(t, tt.res.want, got)
}
})
}

View File

@ -340,7 +340,7 @@ func TestCommands_ResetSystemFeatures(t *testing.T) {
}
got, err := c.ResetSystemFeatures(context.Background())
require.ErrorIs(t, err, tt.wantErr)
assert.Equal(t, tt.want, got)
assertObjectDetails(t, tt.want, got)
})
}
}

View File

@ -1417,7 +1417,7 @@ func TestCommandSide_DeactivateUserGrant(t *testing.T) {
t.Errorf("got wrong err: %v ", err)
}
if tt.res.err == nil {
assert.Equal(t, tt.res.want, got)
assertObjectDetails(t, tt.res.want, got)
}
})
}
@ -1625,7 +1625,7 @@ func TestCommandSide_ReactivateUserGrant(t *testing.T) {
t.Errorf("got wrong err: %v ", err)
}
if tt.res.err == nil {
assert.Equal(t, tt.res.want, got)
assertObjectDetails(t, tt.res.want, got)
}
})
}
@ -1827,7 +1827,7 @@ func TestCommandSide_RemoveUserGrant(t *testing.T) {
t.Errorf("got wrong err: %v ", err)
}
if tt.res.err == nil {
assert.Equal(t, tt.res.want, got)
assertObjectDetails(t, tt.res.want, got)
}
})
}

View File

@ -192,7 +192,7 @@ func TestCommandSide_AddHumanAvatar(t *testing.T) {
t.Errorf("got wrong err: %v ", err)
}
if tt.res.err == nil {
assert.Equal(t, tt.res.want, got)
assertObjectDetails(t, tt.res.want, got)
}
})
}
@ -355,7 +355,7 @@ func TestCommandSide_RemoveHumanAvatar(t *testing.T) {
t.Errorf("got wrong err: %v ", err)
}
if tt.res.err == nil {
assert.Equal(t, tt.res.want, got)
assertObjectDetails(t, tt.res.want, got)
}
})
}

View File

@ -688,7 +688,7 @@ func TestCommandSide_VerifyHumanEmail(t *testing.T) {
t.Errorf("got wrong err: %v ", err)
}
if tt.res.err == nil {
assert.Equal(t, tt.res.want, got)
assertObjectDetails(t, tt.res.want, got)
}
})
}
@ -949,7 +949,7 @@ func TestCommandSide_CreateVerificationCodeHumanEmail(t *testing.T) {
t.Errorf("got wrong err: %v ", err)
}
if tt.res.err == nil {
assert.Equal(t, tt.res.want, got)
assertObjectDetails(t, tt.res.want, got)
}
})
}

View File

@ -394,7 +394,7 @@ func TestCommandSide_ResendInitialMail(t *testing.T) {
t.Errorf("got wrong err: %v ", err)
}
if tt.res.err == nil {
assert.Equal(t, tt.res.want, got)
assertObjectDetails(t, tt.res.want, got)
}
})
}

View File

@ -962,7 +962,7 @@ func TestCommandSide_RemoveHumanTOTP(t *testing.T) {
t.Errorf("got wrong err: %v ", err)
}
if tt.res.err == nil {
assert.Equal(t, tt.res.want, got)
assertObjectDetails(t, tt.res.want, got)
}
})
}
@ -1179,7 +1179,7 @@ func TestCommandSide_AddHumanOTPSMS(t *testing.T) {
}
got, err := r.AddHumanOTPSMS(tt.args.ctx, tt.args.userID, tt.args.resourceOwner)
assert.ErrorIs(t, err, tt.res.err)
assert.Equal(t, tt.res.want, got)
assertObjectDetails(t, tt.res.want, got)
})
}
}
@ -1306,7 +1306,7 @@ func TestCommandSide_AddHumanOTPSMSWithCheckSucceeded(t *testing.T) {
}
got, err := r.AddHumanOTPSMSWithCheckSucceeded(tt.args.ctx, tt.args.userID, tt.args.resourceOwner, tt.args.authRequest)
assert.ErrorIs(t, err, tt.res.err)
assert.Equal(t, tt.res.want, got)
assertObjectDetails(t, tt.res.want, got)
})
}
}
@ -1421,7 +1421,7 @@ func TestCommandSide_RemoveHumanOTPSMS(t *testing.T) {
}
got, err := r.RemoveHumanOTPSMS(tt.args.ctx, tt.args.userID, tt.args.resourceOwner)
assert.ErrorIs(t, err, tt.res.err)
assert.Equal(t, tt.res.want, got)
assertObjectDetails(t, tt.res.want, got)
})
}
}
@ -2334,7 +2334,7 @@ func TestCommandSide_AddHumanOTPEmail(t *testing.T) {
}
got, err := r.AddHumanOTPEmail(tt.args.ctx, tt.args.userID, tt.args.resourceOwner)
assert.ErrorIs(t, err, tt.res.err)
assert.Equal(t, tt.res.want, got)
assertObjectDetails(t, tt.res.want, got)
})
}
}
@ -2461,7 +2461,7 @@ func TestCommandSide_AddHumanOTPEmailWithCheckSucceeded(t *testing.T) {
}
got, err := r.AddHumanOTPEmailWithCheckSucceeded(tt.args.ctx, tt.args.userID, tt.args.resourceOwner, tt.args.authRequest)
assert.ErrorIs(t, err, tt.res.err)
assert.Equal(t, tt.res.want, got)
assertObjectDetails(t, tt.res.want, got)
})
}
}
@ -2576,7 +2576,7 @@ func TestCommandSide_RemoveHumanOTPEmail(t *testing.T) {
}
got, err := r.RemoveHumanOTPEmail(tt.args.ctx, tt.args.userID, tt.args.resourceOwner)
assert.ErrorIs(t, err, tt.res.err)
assert.Equal(t, tt.res.want, got)
assertObjectDetails(t, tt.res.want, got)
})
}
}

View File

@ -254,7 +254,7 @@ func TestCommandSide_SetOneTimePassword(t *testing.T) {
t.Errorf("got wrong err: %v ", err)
}
if tt.res.err == nil {
assert.Equal(t, tt.res.want, got)
assertObjectDetails(t, tt.res.want, got)
}
})
}
@ -657,7 +657,7 @@ func TestCommandSide_SetPasswordWithVerifyCode(t *testing.T) {
t.Errorf("got wrong err: %v ", err)
}
if tt.res.err == nil {
assert.Equal(t, tt.res.want, got)
assertObjectDetails(t, tt.res.want, got)
}
})
}
@ -1102,7 +1102,7 @@ func TestCommandSide_ChangePassword(t *testing.T) {
t.Errorf("got wrong err: %v ", err)
}
if tt.res.err == nil {
assert.Equal(t, tt.res.want, got)
assertObjectDetails(t, tt.res.want, got)
}
})
}
@ -1336,7 +1336,7 @@ func TestCommandSide_RequestSetPassword(t *testing.T) {
t.Errorf("got wrong err: %v ", err)
}
if tt.res.err == nil {
assert.Equal(t, tt.res.want, got)
assertObjectDetails(t, tt.res.want, got)
}
})
}

View File

@ -609,7 +609,7 @@ func TestCommandSide_VerifyHumanPhone(t *testing.T) {
t.Errorf("got wrong err: %v ", err)
}
if tt.res.err == nil {
assert.Equal(t, tt.res.want, got)
assertObjectDetails(t, tt.res.want, got)
}
})
}
@ -791,7 +791,7 @@ func TestCommandSide_CreateVerificationCodeHumanPhone(t *testing.T) {
t.Errorf("got wrong err: %v ", err)
}
if tt.res.err == nil {
assert.Equal(t, tt.res.want, got)
assertObjectDetails(t, tt.res.want, got)
}
})
}
@ -1047,7 +1047,7 @@ func TestCommandSide_RemoveHumanPhone(t *testing.T) {
t.Errorf("got wrong err: %v ", err)
}
if tt.res.err == nil {
assert.Equal(t, tt.res.want, got)
assertObjectDetails(t, tt.res.want, got)
}
})
}

View File

@ -157,7 +157,7 @@ func TestCommands_RevokeRefreshToken(t *testing.T) {
t.Errorf("got wrong err: %v ", err)
}
if tt.res.err == nil {
assert.Equal(t, tt.res.want, got)
assertObjectDetails(t, tt.res.want, got)
}
})
}

View File

@ -728,7 +728,7 @@ func TestCommandSide_RemoveUserIDPLink(t *testing.T) {
t.Errorf("got wrong err: %v ", err)
}
if tt.res.err == nil {
assert.Equal(t, tt.res.want, got)
assertObjectDetails(t, tt.res.want, got)
}
})
}

View File

@ -267,7 +267,7 @@ func TestCommands_AddMachineKey(t *testing.T) {
t.Errorf("got wrong err: %v ", err)
}
if tt.res.err == nil {
assert.Equal(t, tt.res.want, got)
assertObjectDetails(t, tt.res.want, got)
receivedKey := len(tt.args.key.PrivateKey) > 0
assert.Equal(t, tt.res.key, receivedKey)
}

View File

@ -139,7 +139,7 @@ func TestCommandSide_GenerateMachineSecret(t *testing.T) {
t.Errorf("got wrong err: %v ", err)
}
if tt.res.err == nil {
assert.Equal(t, tt.res.want, got)
assertObjectDetails(t, tt.res.want, got)
assert.Equal(t, tt.args.set.ClientSecret, tt.res.secret.ClientSecret)
}
})
@ -297,7 +297,7 @@ func TestCommandSide_RemoveMachineSecret(t *testing.T) {
t.Errorf("got wrong err: %v ", err)
}
if tt.res.err == nil {
assert.Equal(t, tt.res.want, got)
assertObjectDetails(t, tt.res.want, got)
}
})
}

View File

@ -209,7 +209,7 @@ func TestCommandSide_AddMachine(t *testing.T) {
t.Errorf("got wrong err: %v ", err)
}
if tt.res.err == nil {
assert.Equal(t, tt.res.want, got)
assertObjectDetails(t, tt.res.want, got)
}
})
}
@ -384,7 +384,7 @@ func TestCommandSide_ChangeMachine(t *testing.T) {
t.Errorf("got wrong err: %v ", err)
}
if tt.res.err == nil {
assert.Equal(t, tt.res.want, got)
assertObjectDetails(t, tt.res.want, got)
}
})
}

View File

@ -322,7 +322,7 @@ func TestCommandSide_BulkSetUserMetadata(t *testing.T) {
t.Errorf("got wrong err: %v ", err)
}
if tt.res.err == nil {
assert.Equal(t, tt.res.want, got)
assertObjectDetails(t, tt.res.want, got)
}
})
}
@ -483,7 +483,7 @@ func TestCommandSide_UserRemoveMetadata(t *testing.T) {
t.Errorf("got wrong err: %v ", err)
}
if tt.res.err == nil {
assert.Equal(t, tt.res.want, got)
assertObjectDetails(t, tt.res.want, got)
}
})
}
@ -711,7 +711,7 @@ func TestCommandSide_BulkRemoveUserMetadata(t *testing.T) {
t.Errorf("got wrong err: %v ", err)
}
if tt.res.err == nil {
assert.Equal(t, tt.res.want, got)
assertObjectDetails(t, tt.res.want, got)
}
})
}

View File

@ -270,7 +270,7 @@ func TestCommands_AddPersonalAccessToken(t *testing.T) {
t.Errorf("got wrong err: %v ", err)
}
if tt.res.err == nil {
assert.Equal(t, tt.res.want, got)
assertObjectDetails(t, tt.res.want, got)
assert.Equal(t, tt.res.token, tt.args.pat.Token)
}
})
@ -368,7 +368,7 @@ func TestCommands_RemovePersonalAccessToken(t *testing.T) {
t.Errorf("got wrong err: %v ", err)
}
if tt.res.err == nil {
assert.Equal(t, tt.res.want, got)
assertObjectDetails(t, tt.res.want, got)
}
})
}

View File

@ -281,7 +281,7 @@ func TestCommands_CreateUserSchema(t *testing.T) {
}
gotID, gotDetails, err := c.CreateUserSchema(tt.args.ctx, tt.args.userSchema)
assert.Equal(t, tt.res.id, gotID)
assert.Equal(t, tt.res.details, gotDetails)
assertObjectDetails(t, tt.res.details, gotDetails)
assert.ErrorIs(t, err, tt.res.err)
})
}
@ -620,7 +620,7 @@ func TestCommands_UpdateUserSchema(t *testing.T) {
}
got, err := c.UpdateUserSchema(tt.args.ctx, tt.args.userSchema)
assert.ErrorIs(t, err, tt.res.err)
assert.Equal(t, tt.res.details, got)
assertObjectDetails(t, tt.res.details, got)
})
}
}
@ -713,7 +713,7 @@ func TestCommands_DeactivateUserSchema(t *testing.T) {
}
got, err := c.DeactivateUserSchema(tt.args.ctx, tt.args.id, tt.args.resourceOwner)
assert.ErrorIs(t, err, tt.res.err)
assert.Equal(t, tt.res.details, got)
assertObjectDetails(t, tt.res.details, got)
})
}
}
@ -812,7 +812,7 @@ func TestCommands_ReactivateUserSchema(t *testing.T) {
}
got, err := c.ReactivateUserSchema(tt.args.ctx, tt.args.id, tt.args.resourceOwner)
assert.ErrorIs(t, err, tt.res.err)
assert.Equal(t, tt.res.details, got)
assertObjectDetails(t, tt.res.details, got)
})
}
}
@ -906,7 +906,7 @@ func TestCommands_DeleteUserSchema(t *testing.T) {
}
got, err := c.DeleteUserSchema(tt.args.ctx, tt.args.id, tt.args.resourceOwner)
assert.ErrorIs(t, err, tt.res.err)
assert.Equal(t, tt.res.details, got)
assertObjectDetails(t, tt.res.details, got)
})
}
}

View File

@ -498,7 +498,7 @@ func TestCommandSide_UsernameChange(t *testing.T) {
t.Errorf("got wrong err: %v ", err)
}
if tt.res.err == nil {
assert.Equal(t, tt.res.want, got)
assertObjectDetails(t, tt.res.want, got)
}
})
}
@ -648,7 +648,7 @@ func TestCommandSide_DeactivateUser(t *testing.T) {
t.Errorf("got wrong err: %v ", err)
}
if tt.res.err == nil {
assert.Equal(t, tt.res.want, got)
assertObjectDetails(t, tt.res.want, got)
}
})
}
@ -797,7 +797,7 @@ func TestCommandSide_ReactivateUser(t *testing.T) {
t.Errorf("got wrong err: %v ", err)
}
if tt.res.err == nil {
assert.Equal(t, tt.res.want, got)
assertObjectDetails(t, tt.res.want, got)
}
})
}
@ -947,7 +947,7 @@ func TestCommandSide_LockUser(t *testing.T) {
t.Errorf("got wrong err: %v ", err)
}
if tt.res.err == nil {
assert.Equal(t, tt.res.want, got)
assertObjectDetails(t, tt.res.want, got)
}
})
}
@ -1096,7 +1096,7 @@ func TestCommandSide_UnlockUser(t *testing.T) {
t.Errorf("got wrong err: %v ", err)
}
if tt.res.err == nil {
assert.Equal(t, tt.res.want, got)
assertObjectDetails(t, tt.res.want, got)
}
})
}
@ -1427,7 +1427,7 @@ func TestCommandSide_RemoveUser(t *testing.T) {
t.Errorf("got wrong err: %v ", err)
return
}
assert.Equal(t, tt.res.want, got)
assertObjectDetails(t, tt.res.want, got)
})
}
}
@ -1562,7 +1562,7 @@ func TestCommands_RevokeAccessToken(t *testing.T) {
t.Errorf("got wrong err: %v ", err)
}
if tt.res.err == nil {
assert.Equal(t, tt.res.want, got)
assertObjectDetails(t, tt.res.want, got)
}
})
}

View File

@ -1831,7 +1831,7 @@ func TestCommands_verifyUserEmailWithGenerator(t *testing.T) {
}
got, err := c.verifyUserEmailWithGenerator(context.Background(), tt.args.userID, tt.args.code, GetMockSecretGenerator(t))
require.ErrorIs(t, err, tt.wantErr)
assert.Equal(t, got, tt.want)
assertObjectDetails(t, tt.want, got)
})
}
}

View File

@ -1569,7 +1569,7 @@ func TestCommandSide_AddUserHuman(t *testing.T) {
return
}
if tt.res.err == nil {
assert.Equal(t, tt.res.want, tt.args.human.Details)
assertObjectDetails(t, tt.res.want, tt.args.human.Details)
assert.Equal(t, tt.res.wantID, tt.args.human.ID)
assert.Equal(t, tt.res.wantEmailCode, gu.Value(tt.args.human.EmailCode))
}
@ -2945,7 +2945,7 @@ func TestCommandSide_ChangeUserHuman(t *testing.T) {
return
}
if tt.res.err == nil {
assert.Equal(t, tt.res.want, tt.args.human.Details)
assertObjectDetails(t, tt.res.want, tt.args.human.Details)
assert.Equal(t, tt.res.wantEmailCode, tt.args.human.EmailCode)
assert.Equal(t, tt.res.wantPhoneCode, tt.args.human.PhoneCode)
}

View File

@ -537,7 +537,7 @@ func TestCommands_AddUserPasskeyCode(t *testing.T) {
}
got, err := c.AddUserPasskeyCode(context.Background(), tt.args.userID, tt.args.resourceOwner, alg)
require.ErrorIs(t, err, tt.wantErr)
assert.Equal(t, tt.want, got)
assertObjectDetails(t, tt.want, got)
})
}
}
@ -645,7 +645,7 @@ func TestCommands_AddUserPasskeyCodeURLTemplate(t *testing.T) {
}
got, err := c.AddUserPasskeyCodeURLTemplate(context.Background(), tt.args.userID, tt.args.resourceOwner, alg, tt.args.urlTmpl)
require.ErrorIs(t, err, tt.wantErr)
assert.Equal(t, tt.want, got)
assertObjectDetails(t, tt.want, got)
})
}
}
@ -736,8 +736,13 @@ func TestCommands_AddUserPasskeyCodeReturn(t *testing.T) {
idGenerator: tt.fields.idGenerator,
}
got, err := c.AddUserPasskeyCodeReturn(context.Background(), tt.args.userID, tt.args.resourceOwner, alg)
require.ErrorIs(t, err, tt.wantErr)
assert.Equal(t, tt.want, got)
if tt.wantErr != nil {
require.ErrorIs(t, err, tt.wantErr)
return
}
assert.Equal(t, tt.want.CodeID, got.CodeID)
assert.Equal(t, tt.want.Code, got.Code)
assertObjectDetails(t, tt.want.ObjectDetails, got.ObjectDetails)
})
}
}
@ -896,8 +901,13 @@ func TestCommands_addUserPasskeyCode(t *testing.T) {
idGenerator: tt.fields.idGenerator,
}
got, err := c.addUserPasskeyCode(context.Background(), tt.args.userID, tt.args.resourceOwner, alg, "", false)
require.ErrorIs(t, err, tt.wantErr)
assert.Equal(t, tt.want, got)
if tt.wantErr != nil {
require.ErrorIs(t, err, tt.wantErr)
return
}
assert.Equal(t, tt.want.CodeID, got.CodeID)
assert.Equal(t, tt.want.Code, got.Code)
assertObjectDetails(t, tt.want.ObjectDetails, got.ObjectDetails)
})
}
}

View File

@ -601,7 +601,7 @@ func TestCommands_requestPasswordReset(t *testing.T) {
}
got, gotPlainCode, err := c.requestPasswordReset(tt.args.ctx, tt.args.userID, tt.args.returnCode, tt.args.urlTmpl, tt.args.notificationType)
require.ErrorIs(t, err, tt.res.err)
assert.Equal(t, tt.res.details, got)
assertObjectDetails(t, tt.res.details, got)
assert.Equal(t, tt.res.code, gotPlainCode)
})
}

View File

@ -263,7 +263,7 @@ func TestCommandSide_LockUserV2(t *testing.T) {
t.Errorf("got wrong err: %v ", err)
}
if tt.res.err == nil {
assert.Equal(t, tt.res.want, got)
assertObjectDetails(t, tt.res.want, got)
}
})
}
@ -516,7 +516,7 @@ func TestCommandSide_UnlockUserV2(t *testing.T) {
t.Errorf("got wrong err: %v ", err)
}
if tt.res.err == nil {
assert.Equal(t, tt.res.want, got)
assertObjectDetails(t, tt.res.want, got)
}
})
}
@ -819,7 +819,7 @@ func TestCommandSide_DeactivateUserV2(t *testing.T) {
t.Errorf("got wrong err: %v ", err)
}
if tt.res.err == nil {
assert.Equal(t, tt.res.want, got)
assertObjectDetails(t, tt.res.want, got)
}
})
}
@ -1074,7 +1074,7 @@ func TestCommandSide_ReactivateUserV2(t *testing.T) {
t.Errorf("got wrong err: %v ", err)
}
if tt.res.err == nil {
assert.Equal(t, tt.res.want, got)
assertObjectDetails(t, tt.res.want, got)
}
})
}
@ -1366,7 +1366,7 @@ func TestCommandSide_RemoveUserV2(t *testing.T) {
t.Errorf("got wrong err: %v ", err)
}
if tt.res.err == nil {
assert.Equal(t, tt.res.want, got)
assertObjectDetails(t, tt.res.want, got)
}
})
}

View File

@ -14,6 +14,8 @@ type SystemDefaults struct {
DomainVerification DomainVerification
Notifications Notifications
KeyConfig KeyConfig
DefaultQueryLimit uint64
MaxQueryLimit uint64
}
type SecretGenerators struct {

View File

@ -5,7 +5,12 @@ import (
)
type ObjectDetails struct {
Sequence uint64
EventDate time.Time
Sequence uint64
// EventDate is the date of the last event that changed the object
EventDate time.Time
// CreationDate is the date of the first event that created the object
CreationDate time.Time
ResourceOwner string
// ID is the Aggregate ID of the resource
ID string
}

Some files were not shown because too many files have changed in this diff Show More