From da11910ba94b12c430e249fbb744d25fda23a408 Mon Sep 17 00:00:00 2001 From: Marco Ardizzone Date: Thu, 24 Apr 2025 17:12:16 +0200 Subject: [PATCH] feat: Add GetInstance endpoint (#9452) --- internal/api/grpc/instance/v2/converter.go | 41 +++++++++++++++ internal/api/grpc/instance/v2/query.go | 19 +++++++ internal/api/grpc/object/v2/converter.go | 20 +++++++ proto/zitadel/instance/v2/instance.proto | 52 +++++++++++++++++++ .../instance/v2/instance_service.proto | 37 ++++++++++++- 5 files changed, 168 insertions(+), 1 deletion(-) create mode 100644 internal/api/grpc/instance/v2/converter.go create mode 100644 internal/api/grpc/instance/v2/query.go create mode 100644 proto/zitadel/instance/v2/instance.proto diff --git a/internal/api/grpc/instance/v2/converter.go b/internal/api/grpc/instance/v2/converter.go new file mode 100644 index 00000000000..a24d56d3bf8 --- /dev/null +++ b/internal/api/grpc/instance/v2/converter.go @@ -0,0 +1,41 @@ +package instance + +import ( + "github.com/zitadel/zitadel/cmd/build" + "github.com/zitadel/zitadel/internal/api/grpc/object/v2" + "github.com/zitadel/zitadel/internal/query" + "github.com/zitadel/zitadel/pkg/grpc/instance/v2" +) + +func ToProtoObject(inst *query.Instance) *instance.Instance { + return &instance.Instance{ + Id: inst.ID, + Name: inst.Name, + Domains: DomainsToPb(inst.Domains), + Version: build.Version(), + Details: object.ToViewDetailsPb(inst.Sequence, inst.CreationDate, inst.ChangeDate, inst.ID), + } +} + +func DomainsToPb(domains []*query.InstanceDomain) []*instance.Domain { + d := []*instance.Domain{} + for _, dm := range domains { + pbDomain := DomainToPb(dm) + d = append(d, pbDomain) + } + return d +} + +func DomainToPb(d *query.InstanceDomain) *instance.Domain { + return &instance.Domain{ + Domain: d.Domain, + Primary: d.IsPrimary, + Generated: d.IsGenerated, + Details: object.ToViewDetailsPb( + d.Sequence, + d.CreationDate, + d.ChangeDate, + d.InstanceID, + ), + } +} diff --git a/internal/api/grpc/instance/v2/query.go b/internal/api/grpc/instance/v2/query.go new file mode 100644 index 00000000000..5fed8c8edfe --- /dev/null +++ b/internal/api/grpc/instance/v2/query.go @@ -0,0 +1,19 @@ +package instance + +import ( + "context" + + "github.com/zitadel/zitadel/pkg/grpc/instance/v2" + "google.golang.org/protobuf/types/known/emptypb" +) + +func (s *Server) GetInstance(ctx context.Context, _ *emptypb.Empty) (*instance.GetInstanceResponse, error) { + inst, err := s.query.Instance(ctx, true) + if err != nil { + return nil, err + } + + return &instance.GetInstanceResponse{ + Instance: ToProtoObject(inst), + }, nil +} diff --git a/internal/api/grpc/object/v2/converter.go b/internal/api/grpc/object/v2/converter.go index 753b9db0954..83d3ba2927e 100644 --- a/internal/api/grpc/object/v2/converter.go +++ b/internal/api/grpc/object/v2/converter.go @@ -2,6 +2,7 @@ package object import ( "context" + "time" "google.golang.org/protobuf/types/known/timestamppb" @@ -176,3 +177,22 @@ func MFAStateToPb(state domain.MFAState) user_pb.AuthFactorState { return user_pb.AuthFactorState_AUTH_FACTOR_STATE_UNSPECIFIED } } + +func ToViewDetailsPb( + sequence uint64, + creationDate, + changeDate time.Time, + resourceOwner string, +) *object.Details { + details := &object.Details{ + Sequence: sequence, + ResourceOwner: resourceOwner, + } + if !creationDate.IsZero() { + details.CreationDate = timestamppb.New(creationDate) + } + if !changeDate.IsZero() { + details.ChangeDate = timestamppb.New(changeDate) + } + return details +} diff --git a/proto/zitadel/instance/v2/instance.proto b/proto/zitadel/instance/v2/instance.proto new file mode 100644 index 00000000000..acc204b9b5b --- /dev/null +++ b/proto/zitadel/instance/v2/instance.proto @@ -0,0 +1,52 @@ +syntax = "proto3"; + +import "protoc-gen-openapiv2/options/annotations.proto"; +import "zitadel/object/v2/object.proto"; + +package zitadel.instance.v2; + +option go_package = "github.com/zitadel/zitadel/pkg/grpc/instance/v2;instance"; + +message Instance { + string id = 1 [ + (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_field) = { + example: "\"69629023906488334\"" + } + ]; + zitadel.object.v2.Details details = 2; + State state = 3 [ + (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_field) = { + description: "current state of the instance"; + } + ]; + string name = 4 [ + (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_field) = { + example: "\"ZITADEL\""; + } + ]; + string version = 5 [ + (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_field) = { + example: "\"1.0.0\""; + } + ]; + repeated Domain domains = 6; +} + +enum State { + STATE_UNSPECIFIED = 0; + STATE_CREATING = 1; + STATE_RUNNING = 2; + STATE_STOPPING = 3; + STATE_STOPPED = 4; +} + +message Domain { + zitadel.object.v2.Details details = 1; + string domain = 2 [ + (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_field) = { + example: "\"zitadel.com\"" + } + ]; + bool primary = 3; + bool generated = 4; +} \ No newline at end of file diff --git a/proto/zitadel/instance/v2/instance_service.proto b/proto/zitadel/instance/v2/instance_service.proto index 2d53c9f0dd6..eb4eefe7d1c 100644 --- a/proto/zitadel/instance/v2/instance_service.proto +++ b/proto/zitadel/instance/v2/instance_service.proto @@ -4,6 +4,8 @@ package zitadel.instance.v2; import "validate/validate.proto"; import "zitadel/object/v2/object.proto"; +import "zitadel/instance/v2/instance.proto"; +import "google/protobuf/empty.proto"; import "protoc-gen-openapiv2/options/annotations.proto"; import "google/api/annotations.proto"; import "google/api/field_behavior.proto"; @@ -115,6 +117,10 @@ message DeleteInstanceResponse { zitadel.object.v2.Details details = 1; } +message GetInstanceResponse { + zitadel.instance.v2.Instance instance = 1; +} + service InstanceService { // DeleteInstance deletes an instance with the given ID. @@ -148,10 +154,39 @@ service InstanceService { } }; } + + // GetInstance returns the instance for the current context. + rpc GetInstance(google.protobuf.Empty) returns (GetInstanceResponse) { + option (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_operation) = { + description: "Gets the instance associated to the current context"; + tags: "Instance"; + responses: { + key: "200"; + value: { + description: "The instance of the context."; + schema: { + json_schema: { + ref: "#/definitions/GetInstanceResponse"; + } + }; + } + }; + }; + + option (google.api.http) = { + get: "/v2/instances/current" + }; + + option (zitadel.protoc_gen_zitadel.v2.options) = { + auth_option: { + permission: "instance.get" + } + }; + } + } // UpdateInstance -// GetInstance // ListInstances // DeleteInstance