Add v2Beta migration methods

This commit is contained in:
Marco Ardizzone
2025-09-23 17:52:40 +02:00
parent 4263f1db56
commit 49c4c0bc4e
5 changed files with 182 additions and 1 deletions

View File

@@ -0,0 +1,5 @@
An objective of Zitadel v5 is to remove the v2beta APIs.
Once this goal is achieved, this package will be useless, as well as the Beta endpoints in `org.go`.
Please do the cleanup.

View File

@@ -0,0 +1,29 @@
package convert
import (
v2_object "github.com/zitadel/zitadel/pkg/grpc/object/v2"
v2beta_object "github.com/zitadel/zitadel/pkg/grpc/object/v2beta"
)
func TextQueryMethodBetaToV2(txtMethod v2beta_object.TextQueryMethod) v2_object.TextQueryMethod {
switch txtMethod {
case v2beta_object.TextQueryMethod_TEXT_QUERY_METHOD_CONTAINS:
return v2_object.TextQueryMethod_TEXT_QUERY_METHOD_CONTAINS
case v2beta_object.TextQueryMethod_TEXT_QUERY_METHOD_CONTAINS_IGNORE_CASE:
return v2_object.TextQueryMethod_TEXT_QUERY_METHOD_CONTAINS_IGNORE_CASE
case v2beta_object.TextQueryMethod_TEXT_QUERY_METHOD_ENDS_WITH:
return v2_object.TextQueryMethod_TEXT_QUERY_METHOD_ENDS_WITH
case v2beta_object.TextQueryMethod_TEXT_QUERY_METHOD_ENDS_WITH_IGNORE_CASE:
return v2_object.TextQueryMethod_TEXT_QUERY_METHOD_ENDS_WITH_IGNORE_CASE
case v2beta_object.TextQueryMethod_TEXT_QUERY_METHOD_EQUALS_IGNORE_CASE:
return v2_object.TextQueryMethod_TEXT_QUERY_METHOD_EQUALS_IGNORE_CASE
case v2beta_object.TextQueryMethod_TEXT_QUERY_METHOD_STARTS_WITH:
return v2_object.TextQueryMethod_TEXT_QUERY_METHOD_STARTS_WITH
case v2beta_object.TextQueryMethod_TEXT_QUERY_METHOD_STARTS_WITH_IGNORE_CASE:
return v2_object.TextQueryMethod_TEXT_QUERY_METHOD_STARTS_WITH_IGNORE_CASE
case v2beta_object.TextQueryMethod_TEXT_QUERY_METHOD_EQUALS:
fallthrough
default:
return v2_object.TextQueryMethod_TEXT_QUERY_METHOD_EQUALS
}
}

View File

@@ -0,0 +1,90 @@
package convert
import (
"github.com/zitadel/zitadel/pkg/grpc/object/v2"
v2_org "github.com/zitadel/zitadel/pkg/grpc/org/v2"
v2beta_org "github.com/zitadel/zitadel/pkg/grpc/org/v2beta"
)
func OrganizationBetaRequestToV2Request(in *v2beta_org.ListOrganizationsRequest) *v2_org.ListOrganizationsRequest {
return &v2_org.ListOrganizationsRequest{
Query: &object.ListQuery{
Offset: in.GetPagination().GetOffset(),
Limit: in.GetPagination().GetLimit(),
Asc: in.GetPagination().GetAsc(),
},
SortingColumn: organizationSortingColumnBetaToV2(in.GetSortingColumn()),
Queries: organizationQueriesBetaToV2(in.GetFilter()),
}
}
func organizationSortingColumnBetaToV2(sc v2beta_org.OrgFieldName) v2_org.OrganizationFieldName {
switch sc {
case v2beta_org.OrgFieldName_ORG_FIELD_NAME_NAME:
return v2_org.OrganizationFieldName_ORGANIZATION_FIELD_NAME_NAME
case v2beta_org.OrgFieldName_ORG_FIELD_NAME_CREATION_DATE, v2beta_org.OrgFieldName_ORG_FIELD_NAME_UNSPECIFIED:
fallthrough
default:
return v2_org.OrganizationFieldName_ORGANIZATION_FIELD_NAME_UNSPECIFIED
}
}
func organizationQueriesBetaToV2(queries []*v2beta_org.OrganizationSearchFilter) []*v2_org.SearchQuery {
toReturn := make([]*v2_org.SearchQuery, len(queries))
for i, query := range queries {
toReturn[i] = organizationQueryBetaToV2(query)
}
return toReturn
}
func organizationQueryBetaToV2(query *v2beta_org.OrganizationSearchFilter) *v2_org.SearchQuery {
toReturn := &v2_org.SearchQuery{}
switch assertedType := query.GetFilter().(type) {
case *v2beta_org.OrganizationSearchFilter_DomainFilter:
toReturn.Query = &v2_org.SearchQuery_DomainQuery{
DomainQuery: &v2_org.OrganizationDomainQuery{
Domain: assertedType.DomainFilter.GetDomain(),
Method: TextQueryMethodBetaToV2(assertedType.DomainFilter.GetMethod()),
},
}
case *v2beta_org.OrganizationSearchFilter_IdFilter:
toReturn.Query = &v2_org.SearchQuery_IdQuery{
IdQuery: &v2_org.OrganizationIDQuery{
Id: assertedType.IdFilter.GetId(),
},
}
case *v2beta_org.OrganizationSearchFilter_NameFilter:
toReturn.Query = &v2_org.SearchQuery_NameQuery{
NameQuery: &v2_org.OrganizationNameQuery{
Name: assertedType.NameFilter.GetName(),
Method: TextQueryMethodBetaToV2(assertedType.NameFilter.GetMethod()),
},
}
case *v2beta_org.OrganizationSearchFilter_StateFilter:
toReturn.Query = &v2_org.SearchQuery_StateQuery{
StateQuery: &v2_org.OrganizationStateQuery{
State: organizationStateBetaToV2(assertedType.StateFilter.GetState()),
},
}
default:
return toReturn
}
return toReturn
}
func organizationStateBetaToV2(in v2beta_org.OrgState) v2_org.OrganizationState {
switch in {
case v2beta_org.OrgState_ORG_STATE_ACTIVE:
return v2_org.OrganizationState_ORGANIZATION_STATE_ACTIVE
case v2beta_org.OrgState_ORG_STATE_INACTIVE:
return v2_org.OrganizationState_ORGANIZATION_STATE_INACTIVE
case v2beta_org.OrgState_ORG_STATE_UNSPECIFIED, v2beta_org.OrgState_ORG_STATE_REMOVED:
fallthrough
default:
return v2_org.OrganizationState_ORGANIZATION_STATE_UNSPECIFIED
}
}

View File

@@ -6,8 +6,11 @@ import (
"connectrpc.com/connect"
"google.golang.org/protobuf/types/known/timestamppb"
"github.com/zitadel/zitadel/backend/v3/api/org/v2/convert"
"github.com/zitadel/zitadel/backend/v3/domain"
"github.com/zitadel/zitadel/backend/v3/storage/database/repository"
filter "github.com/zitadel/zitadel/pkg/grpc/filter/v2beta"
"github.com/zitadel/zitadel/pkg/grpc/object/v2"
v2_org "github.com/zitadel/zitadel/pkg/grpc/org/v2"
v2beta_org "github.com/zitadel/zitadel/pkg/grpc/org/v2beta"
)
@@ -53,6 +56,8 @@ func UpdateOrganization(ctx context.Context, request *connect.Request[v2beta_org
// TODO(IAM-Marco) Check if passing the pointer is actually working to retrieve the domain name and the DomainVerified
domainRemoveCmd := domain.NewRemoveOrgDomainCommand(request.Msg.GetId(), orgUpdtCmd.OldDomainName, orgUpdtCmd.IsOldDomainVerified)
// TODO(IAM-Marco): I noticed while debugging that this is calling twice the commands (I think?)
// It's hard to debug, I haven't spent too much into it. Only drawback is pushing events twice.
batchCmd := domain.BatchCommands(orgUpdtCmd, domainAddCmd, domainSetPrimaryCmd, domainRemoveCmd)
err := domain.Invoke(ctx, batchCmd, domain.WithOrganizationRepo(repository.OrganizationRepository))
@@ -62,7 +67,7 @@ func UpdateOrganization(ctx context.Context, request *connect.Request[v2beta_org
return &connect.Response[v2beta_org.UpdateOrganizationResponse]{
Msg: &v2beta_org.UpdateOrganizationResponse{
// TODO(IAM-Marco) Change this with the real update date when OrganizationRepo.Update()
// TODO(IAM-Marco): Change this with the real update date when OrganizationRepo.Update()
// returns the timestamp
ChangeDate: timestamppb.Now(),
},
@@ -77,9 +82,36 @@ func ListOrganizations(ctx context.Context, request *connect.Request[v2_org.List
return nil, err
}
orgs := orgListCmd.ResultToGRPC()
return &connect.Response[v2_org.ListOrganizationsResponse]{
Msg: &v2_org.ListOrganizationsResponse{
Result: orgListCmd.ResultToGRPC(),
Details: &object.ListDetails{
// TODO(IAM-Marco): Return correct result once permissions are in place
TotalResult: uint64(len(orgs)),
},
SortingColumn: request.Msg.GetSortingColumn(),
},
}, nil
}
// TODO(IAM-Marco): Remove in V5
func ListOrganizationsBeta(ctx context.Context, request *connect.Request[v2beta_org.ListOrganizationsRequest]) (*connect.Response[v2beta_org.ListOrganizationsResponse], error) {
orgListCmd := domain.NewListOrgsCommand(convert.OrganizationBetaRequestToV2Request(request.Msg))
err := domain.Invoke(ctx, orgListCmd)
if err != nil {
return nil, err
}
orgs := orgListCmd.ResultToGRPCBeta()
return &connect.Response[v2beta_org.ListOrganizationsResponse]{
Msg: &v2beta_org.ListOrganizationsResponse{
Organizations: orgs,
Pagination: &filter.PaginationResponse{
TotalResult: uint64(len(orgs)),
AppliedLimit: uint64(request.Msg.GetPagination().GetLimit()),
},
},
}, nil
}

View File

@@ -12,6 +12,7 @@ import (
"github.com/zitadel/zitadel/pkg/grpc/object/v2"
"github.com/zitadel/zitadel/pkg/grpc/org/v2"
v2_org "github.com/zitadel/zitadel/pkg/grpc/org/v2"
v2beta_org "github.com/zitadel/zitadel/pkg/grpc/org/v2beta"
)
type ListOrgsCommand struct {
@@ -180,3 +181,27 @@ func (l *ListOrgsCommand) orgToGRPC(org *Organization) *v2_org.Organization {
PrimaryDomain: org.PrimaryDomain(),
}
}
// TODO(IAM-Marco): Remove in V5
func (l *ListOrgsCommand) ResultToGRPCBeta() []*v2beta_org.Organization {
toReturn := make([]*v2beta_org.Organization, len(l.Result))
for i, org := range l.Result {
toReturn[i] = l.orgToGRPCBeta(org)
}
return toReturn
}
// TODO(IAM-Marco): Remove in V5
func (l *ListOrgsCommand) orgToGRPCBeta(org *Organization) *v2beta_org.Organization {
return &v2beta_org.Organization{
Id: org.ID,
ChangedDate: timestamppb.New(org.UpdatedAt),
CreationDate: timestamppb.New(org.CreatedAt),
State: v2beta_org.OrgState(org.State),
Name: org.Name,
PrimaryDomain: org.PrimaryDomain(),
}
}