Files
zitadel/internal/api/grpc/application/v2/convert/convert.go
Livio Spring 0281670030 feat(api): move application service v2beta to GA (and deprecate v2beta) (#10846)
# Which Problems Are Solved

As part of our efforts to simplify the structure and versions of our
APIs, were moving all existing v2beta endpoints to v2 and deprecate
them. They will be removed in Zitadel V5.

# How the Problems Are Solved

- This PR moves app v2beta service and its endpoints to a corresponding
to application v2 version. The v2beta service and endpoints are
deprecated.
- The comments and have been improved and, where not already done, moved
from swagger annotations to proto.
- All required fields have been marked with (google.api.field_behavior)
= REQUIRED and validation rules have been added where missing.
- Name ID of the application always `application_id`, previously was
also `id` and `app_id`.
- Get rid of all `app` abbreviations and name it `application` including
the service name, `AppState` -> `ApplicationState` and `AppSorting` ->
`ApplicationSorting`
- Updated `CreateApplicationRequest`:
- renamed `creation_request_type` to `application_type` and all its
options to `XY_configuration` instead of `XY_request`
- `RegenerateClientSecret`
  - renamed method to `GenerateClientSecret`
  - removed `app_type` from request
- `ListApplicationRequest`:
  - removed required `project_id` and provided it as a filter
- Type `ApplicationNameQuery` has been renamed to
`ApplicationNameFilter` as its usage in the request
- Renamed all fields and types from `config` to `configuration`
- Updated `DeleteApplicationKeyRequest`
  - removed `organization_id`
- Updated `GetApplicationKeyRequest`:
  - removed `project_id`, `application_id` and `organization_id``
- Updated `ListApplicationKeysRequest`:
  - removed oneOf `resource_id` and moved the options into filters
- Name ID of the application key always `key_id`.
- removed unnecessary package prefixed (`zitadel.application.v2`)
- formatted using `buf`

# Additional Changes

None

# Additional Context

- part of https://github.com/zitadel/zitadel/issues/10772
- requires backport to v4.x
2025-10-17 10:12:20 +02:00

274 lines
9.5 KiB
Go

package convert
import (
"net/url"
"strings"
"github.com/muhlemmer/gu"
"google.golang.org/protobuf/types/known/timestamppb"
"github.com/zitadel/zitadel/internal/api/grpc/filter/v2"
"github.com/zitadel/zitadel/internal/config/systemdefaults"
"github.com/zitadel/zitadel/internal/domain"
"github.com/zitadel/zitadel/internal/eventstore/v1/models"
"github.com/zitadel/zitadel/internal/query"
"github.com/zitadel/zitadel/internal/zerrors"
"github.com/zitadel/zitadel/pkg/grpc/application/v2"
)
func AppToPb(queryApp *query.App) *application.Application {
if queryApp == nil {
return &application.Application{}
}
return &application.Application{
ApplicationId: queryApp.ID,
CreationDate: timestamppb.New(queryApp.CreationDate),
ChangeDate: timestamppb.New(queryApp.ChangeDate),
State: appStateToPb(queryApp.State),
Name: queryApp.Name,
Configuration: appConfigToPb(queryApp),
}
}
func AppsToPb(queryApps []*query.App) []*application.Application {
pbApps := make([]*application.Application, len(queryApps))
for i, queryApp := range queryApps {
pbApps[i] = AppToPb(queryApp)
}
return pbApps
}
func ListApplicationsRequestToModel(sysDefaults systemdefaults.SystemDefaults, req *application.ListApplicationsRequest) (*query.AppSearchQueries, error) {
offset, limit, asc, err := filter.PaginationPbToQuery(sysDefaults, req.GetPagination())
if err != nil {
return nil, err
}
queries, err := appQueriesToModel(req.GetFilters())
if err != nil {
return nil, err
}
return &query.AppSearchQueries{
SearchRequest: query.SearchRequest{
Offset: offset,
Limit: limit,
Asc: asc,
SortingColumn: appSortingToColumn(req.GetSortingColumn()),
},
Queries: queries,
}, nil
}
func appSortingToColumn(sortingCriteria application.ApplicationSorting) query.Column {
switch sortingCriteria {
case application.ApplicationSorting_APPLICATION_SORT_BY_CHANGE_DATE:
return query.AppColumnChangeDate
case application.ApplicationSorting_APPLICATION_SORT_BY_CREATION_DATE:
return query.AppColumnCreationDate
case application.ApplicationSorting_APPLICATION_SORT_BY_NAME:
return query.AppColumnName
case application.ApplicationSorting_APPLICATION_SORT_BY_STATE:
return query.AppColumnState
case application.ApplicationSorting_APPLICATION_SORT_BY_ID:
fallthrough
default:
return query.AppColumnID
}
}
func appStateToPb(state domain.AppState) application.ApplicationState {
switch state {
case domain.AppStateActive:
return application.ApplicationState_APPLICATION_STATE_ACTIVE
case domain.AppStateInactive:
return application.ApplicationState_APPLICATION_STATE_INACTIVE
case domain.AppStateRemoved:
return application.ApplicationState_APPLICATION_STATE_REMOVED
case domain.AppStateUnspecified:
fallthrough
default:
return application.ApplicationState_APPLICATION_STATE_UNSPECIFIED
}
}
func appConfigToPb(app *query.App) application.IsApplicationConfiguration {
if app.OIDCConfig != nil {
return appOIDCConfigToPb(app.OIDCConfig)
}
if app.SAMLConfig != nil {
return appSAMLConfigToPb(app.SAMLConfig)
}
return appAPIConfigToPb(app.APIConfig)
}
func loginVersionToDomain(version *application.LoginVersion) (*domain.LoginVersion, *string, error) {
switch v := version.GetVersion().(type) {
case nil:
return gu.Ptr(domain.LoginVersionUnspecified), gu.Ptr(""), nil
case *application.LoginVersion_LoginV1:
return gu.Ptr(domain.LoginVersion1), gu.Ptr(""), nil
case *application.LoginVersion_LoginV2:
_, err := url.Parse(v.LoginV2.GetBaseUri())
return gu.Ptr(domain.LoginVersion2), gu.Ptr(v.LoginV2.GetBaseUri()), err
default:
return gu.Ptr(domain.LoginVersionUnspecified), gu.Ptr(""), nil
}
}
func loginVersionToPb(version domain.LoginVersion, baseURI *string) *application.LoginVersion {
switch version {
case domain.LoginVersionUnspecified:
return nil
case domain.LoginVersion1:
return &application.LoginVersion{Version: &application.LoginVersion_LoginV1{LoginV1: &application.LoginV1{}}}
case domain.LoginVersion2:
return &application.LoginVersion{Version: &application.LoginVersion_LoginV2{LoginV2: &application.LoginV2{BaseUri: baseURI}}}
default:
return nil
}
}
func appQueriesToModel(filters []*application.ApplicationSearchFilter) (queries []query.SearchQuery, err error) {
queries = make([]query.SearchQuery, len(filters))
for i, f := range filters {
queries[i], err = applicationFilterToQuery(f)
if err != nil {
return nil, err
}
}
return queries, nil
}
func applicationFilterToQuery(applicationFilter *application.ApplicationSearchFilter) (query.SearchQuery, error) {
switch q := applicationFilter.GetFilter().(type) {
case *application.ApplicationSearchFilter_ProjectIdFilter:
return query.NewAppProjectIDSearchQuery(q.ProjectIdFilter.GetProjectId())
case *application.ApplicationSearchFilter_NameFilter:
return query.NewAppNameSearchQuery(filter.TextMethodPbToQuery(q.NameFilter.GetMethod()), q.NameFilter.GetName())
case *application.ApplicationSearchFilter_StateFilter:
return query.NewAppStateSearchQuery(domain.AppState(q.StateFilter))
case *application.ApplicationSearchFilter_TypeFilter:
return applicationTypeFilterToQuery(q.TypeFilter)
default:
return nil, zerrors.ThrowInvalidArgument(nil, "CONV-z2mAGy", "List.Query.Invalid")
}
}
func applicationTypeFilterToQuery(t application.ApplicationType) (*query.NotNullQuery, error) {
switch t {
case application.ApplicationType_APPLICATION_TYPE_OIDC:
return query.NewNotNullQuery(query.AppOIDCConfigColumnAppID)
case application.ApplicationType_APPLICATION_TYPE_API:
return query.NewNotNullQuery(query.AppAPIConfigColumnAppID)
case application.ApplicationType_APPLICATION_TYPE_SAML:
return query.NewNotNullQuery(query.AppSAMLConfigColumnAppID)
case application.ApplicationType_APPLICATION_TYPE_UNSPECIFIED:
return nil, zerrors.ThrowInvalidArgument(nil, "CONV-Jke83s", "List.Query.Invalid")
default:
return nil, zerrors.ThrowInvalidArgument(nil, "CONV-Skj3q", "List.Query.Invalid")
}
}
func CreateAPIClientKeyRequestToDomain(key *application.CreateApplicationKeyRequest) *domain.ApplicationKey {
return &domain.ApplicationKey{
ObjectRoot: models.ObjectRoot{
AggregateID: strings.TrimSpace(key.GetProjectId()),
},
ExpirationDate: key.GetExpirationDate().AsTime(),
Type: domain.AuthNKeyTypeJSON,
ApplicationID: strings.TrimSpace(key.GetApplicationId()),
}
}
func ListApplicationKeysRequestToDomain(sysDefaults systemdefaults.SystemDefaults, req *application.ListApplicationKeysRequest) (*query.AuthNKeySearchQueries, error) {
var queries []query.SearchQuery
queries, err := ApplicationKeySearchQueriesToQuery(req.GetFilters())
if err != nil {
return nil, err
}
offset, limit, asc, err := filter.PaginationPbToQuery(sysDefaults, req.GetPagination())
if err != nil {
return nil, err
}
return &query.AuthNKeySearchQueries{
SearchRequest: query.SearchRequest{
Offset: offset,
Limit: limit,
Asc: asc,
SortingColumn: appKeysSortingToColumn(req.GetSortingColumn()),
},
Queries: queries,
}, nil
}
func ApplicationKeySearchQueriesToQuery(queries []*application.ApplicationKeySearchFilter) (_ []query.SearchQuery, err error) {
searchQueries := make([]query.SearchQuery, len(queries))
for i, query := range queries {
searchQueries[i], err = ApplicationKeySearchFilterToQuery(query)
if err != nil {
return nil, err
}
}
return searchQueries, nil
}
func ApplicationKeySearchFilterToQuery(searchQuery *application.ApplicationKeySearchFilter) (query.SearchQuery, error) {
switch f := searchQuery.GetFilter().(type) {
case *application.ApplicationKeySearchFilter_ApplicationIdFilter:
return query.NewAuthNKeyObjectIDQuery(strings.TrimSpace(f.ApplicationIdFilter.GetApplicationId()))
case *application.ApplicationKeySearchFilter_OrganizationIdFilter:
return query.NewAuthNKeyResourceOwnerQuery(strings.TrimSpace(f.OrganizationIdFilter.GetOrganizationId()))
case *application.ApplicationKeySearchFilter_ProjectIdFilter:
return query.NewAuthNKeyAggregateIDQuery(strings.TrimSpace(f.ProjectIdFilter.GetProjectId()))
default:
return nil, zerrors.ThrowInvalidArgument(nil, "CONV-t3ENme", "List.Query.Invalid")
}
}
func appKeysSortingToColumn(sortingCriteria application.ApplicationKeysSorting) query.Column {
switch sortingCriteria {
case application.ApplicationKeysSorting_APPLICATION_KEYS_SORT_BY_PROJECT_ID:
return query.AuthNKeyColumnAggregateID
case application.ApplicationKeysSorting_APPLICATION_KEYS_SORT_BY_CREATION_DATE:
return query.AuthNKeyColumnCreationDate
case application.ApplicationKeysSorting_APPLICATION_KEYS_SORT_BY_EXPIRATION:
return query.AuthNKeyColumnExpiration
case application.ApplicationKeysSorting_APPLICATION_KEYS_SORT_BY_ORGANIZATION_ID:
return query.AuthNKeyColumnResourceOwner
case application.ApplicationKeysSorting_APPLICATION_KEYS_SORT_BY_TYPE:
return query.AuthNKeyColumnType
case application.ApplicationKeysSorting_APPLICATION_KEYS_SORT_BY_APPLICATION_ID:
return query.AuthNKeyColumnObjectID
case application.ApplicationKeysSorting_APPLICATION_KEYS_SORT_BY_ID:
fallthrough
default:
return query.AuthNKeyColumnID
}
}
func ApplicationKeysToPb(keys []*query.AuthNKey) []*application.ApplicationKey {
pbAppKeys := make([]*application.ApplicationKey, len(keys))
for i, k := range keys {
pbKey := &application.ApplicationKey{
KeyId: k.ID,
ApplicationId: k.ApplicationID,
ProjectId: k.AggregateID,
CreationDate: timestamppb.New(k.CreationDate),
OrganizationId: k.ResourceOwner,
ExpirationDate: timestamppb.New(k.Expiration),
}
pbAppKeys[i] = pbKey
}
return pbAppKeys
}