From 9e13b70a3d85c7139157d31f656bf64e7a0a29c9 Mon Sep 17 00:00:00 2001 From: Fabi <38692350+fgerschwiler@users.noreply.github.com> Date: Tue, 22 Mar 2022 08:47:18 +0100 Subject: [PATCH] feat: add system api proto (#3294) * feat: add system api proto * Update proto/zitadel/instance.proto Co-authored-by: Livio Amstutz * Update proto/zitadel/instance.proto Co-authored-by: Livio Amstutz * Update proto/zitadel/instance.proto Co-authored-by: Livio Amstutz * feat: add system api proto * feat: add system api proto * feat: add system api proto * feat: add system api proto * generate grpc * feat: remove region from system proto * fix: remove metrics endpoint * fix: add md file * rebuild proto Co-authored-by: Livio Amstutz --- build/zitadel/generate-grpc.sh | 30 +- docs/docs/apis/proto/instance.md | 108 +++++++ docs/docs/apis/proto/object.md | 10 + docs/docs/apis/proto/settings.md | 153 +++++++++ docs/docs/apis/proto/system.md | 522 ++++++++++++++++++++++++++++++ proto/zitadel/instance.proto | 104 ++++++ proto/zitadel/object.proto | 5 + proto/zitadel/system.proto | 524 +++++++++++++++++++++++++++++++ 8 files changed, 1454 insertions(+), 2 deletions(-) create mode 100644 docs/docs/apis/proto/instance.md create mode 100644 docs/docs/apis/proto/settings.md create mode 100644 docs/docs/apis/proto/system.md create mode 100644 proto/zitadel/instance.proto create mode 100644 proto/zitadel/system.proto diff --git a/build/zitadel/generate-grpc.sh b/build/zitadel/generate-grpc.sh index 771d12f261..47dd542d39 100755 --- a/build/zitadel/generate-grpc.sh +++ b/build/zitadel/generate-grpc.sh @@ -33,6 +33,20 @@ mkdir -p ${DOCS_PATH} # generate additional output +protoc \ + -I=/proto/include \ + --grpc-gateway_out ${GOPATH}/src \ + --grpc-gateway_opt logtostderr=true \ + --openapiv2_out ${OPENAPI_PATH} \ + --openapiv2_opt logtostderr=true \ + --authoption_out ${GRPC_PATH}/system \ + --validate_out=lang=go:${GOPATH}/src \ + ${PROTO_PATH}/system.proto + +# authoptions are generated into the wrong folder +mv ${ZITADEL_PATH}/pkg/grpc/system/zitadel/* ${ZITADEL_PATH}/pkg/grpc/system +rm -r ${ZITADEL_PATH}/pkg/grpc/system/zitadel + protoc \ -I=/proto/include \ --grpc-gateway_out ${GOPATH}/src \ @@ -80,6 +94,10 @@ mv ${ZITADEL_PATH}/pkg/grpc/auth/zitadel/* ${ZITADEL_PATH}/pkg/grpc/auth rm -r ${ZITADEL_PATH}/pkg/grpc/auth/zitadel ## generate docs +protoc \ + -I=/proto/include \ + --doc_out=${DOCS_PATH} --doc_opt=${PROTO_PATH}/docs/zitadel-md.tmpl,system.md \ + ${PROTO_PATH}/system.proto protoc \ -I=/proto/include \ --doc_out=${DOCS_PATH} --doc_opt=${PROTO_PATH}/docs/zitadel-md.tmpl,auth.md \ @@ -112,6 +130,10 @@ protoc \ -I=/proto/include \ --doc_out=${DOCS_PATH} --doc_opt=${PROTO_PATH}/docs/zitadel-md.tmpl,idp.md \ ${PROTO_PATH}/idp.proto +protoc \ + -I=/proto/include \ + --doc_out=${DOCS_PATH} --doc_opt=${PROTO_PATH}/docs/zitadel-md.tmpl,instance.md \ + ${PROTO_PATH}/instance.proto protoc \ -I=/proto/include \ --doc_out=${DOCS_PATH} --doc_opt=${PROTO_PATH}/docs/zitadel-md.tmpl,member.md \ @@ -144,7 +166,11 @@ protoc \ -I=/proto/include \ --doc_out=${DOCS_PATH} --doc_opt=${PROTO_PATH}/docs/zitadel-md.tmpl,project.md \ ${PROTO_PATH}/project.proto - protoc \ +protoc \ + -I=/proto/include \ + --doc_out=${DOCS_PATH} --doc_opt=${PROTO_PATH}/docs/zitadel-md.tmpl,settings.md \ + ${PROTO_PATH}/settings.proto +protoc \ -I=/proto/include \ --doc_out=${DOCS_PATH} --doc_opt=${PROTO_PATH}/docs/zitadel-md.tmpl,text.md \ ${PROTO_PATH}/text.proto @@ -158,4 +184,4 @@ protoc \ ${PROTO_PATH}/settings.proto -echo "done generating grpc" \ No newline at end of file +echo "done generating grpc" diff --git a/docs/docs/apis/proto/instance.md b/docs/docs/apis/proto/instance.md new file mode 100644 index 0000000000..69d7cda721 --- /dev/null +++ b/docs/docs/apis/proto/instance.md @@ -0,0 +1,108 @@ +--- +title: zitadel/instance.proto +--- +> This document reflects the state from API 1.0 (available from 20.04.2021) + + + + +## Messages + + +### DomainsQuery + + + +| Field | Type | Description | Validation | +| ----- | ---- | ----------- | ----------- | +| domains | repeated string | - | string.max_len: 200
| +| method | zitadel.v1.ListQueryMethod | - | enum.defined_only: true
| + + + + +### IdQuery +IdQuery is always equals + + +| Field | Type | Description | Validation | +| ----- | ---- | ----------- | ----------- | +| id | string | - | string.max_len: 200
| + + + + +### Instance + + + +| Field | Type | Description | Validation | +| ----- | ---- | ----------- | ----------- | +| id | string | - | | +| details | zitadel.v1.ObjectDetails | - | | +| state | State | - | | +| generated_domain | string | - | | +| custom_domains | repeated string | - | | +| name | string | - | | +| version | string | - | | + + + + +### Query + + + +| Field | Type | Description | Validation | +| ----- | ---- | ----------- | ----------- | +| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) query.id_query | IdQuery | - | | +| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) query.domains_query | DomainsQuery | - | | +| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) query.state_query | StateQuery | - | | + + + + +### StateQuery +StateQuery is always equals + + +| Field | Type | Description | Validation | +| ----- | ---- | ----------- | ----------- | +| state | State | - | enum.defined_only: true
| + + + + + + +## Enums + + +### FieldName {#fieldname} + + +| Name | Number | Description | +| ---- | ------ | ----------- | +| FIELD_NAME_UNSPECIFIED | 0 | - | +| FIELD_NAME_ID | 1 | - | +| FIELD_NAME_GENERATED_DOMAIN | 2 | - | +| FIELD_NAME_NAME | 3 | - | +| FIELD_NAME_CREATION_DATE | 4 | - | + + + + +### State {#state} + + +| Name | Number | Description | +| ---- | ------ | ----------- | +| STATE_UNSPECIFIED | 0 | - | +| STATE_CREATING | 1 | - | +| STATE_RUNNING | 2 | - | +| STATE_STOPPING | 3 | - | +| STATE_STOPPED | 4 | - | + + + + diff --git a/docs/docs/apis/proto/object.md b/docs/docs/apis/proto/object.md index a99399b475..1f925fe375 100644 --- a/docs/docs/apis/proto/object.md +++ b/docs/docs/apis/proto/object.md @@ -66,6 +66,16 @@ on manipulation: the | | ## Enums +### ListQueryMethod {#listquerymethod} + + +| Name | Number | Description | +| ---- | ------ | ----------- | +| LIST_QUERY_METHOD_IN | 0 | - | + + + + ### TextQueryMethod {#textquerymethod} diff --git a/docs/docs/apis/proto/settings.md b/docs/docs/apis/proto/settings.md new file mode 100644 index 0000000000..393118c453 --- /dev/null +++ b/docs/docs/apis/proto/settings.md @@ -0,0 +1,153 @@ +--- +title: zitadel/settings.proto +--- +> This document reflects the state from API 1.0 (available from 20.04.2021) + + + + +## Messages + + +### DebugNotificationProvider + + + +| Field | Type | Description | Validation | +| ----- | ---- | ----------- | ----------- | +| details | zitadel.v1.ObjectDetails | - | | +| compact | bool | - | | + + + + +### OIDCSettings + + + +| Field | Type | Description | Validation | +| ----- | ---- | ----------- | ----------- | +| details | zitadel.v1.ObjectDetails | - | | +| access_token_lifetime | google.protobuf.Duration | - | | +| id_token_lifetime | google.protobuf.Duration | - | | +| refresh_token_idle_expiration | google.protobuf.Duration | - | | +| refresh_token_expiration | google.protobuf.Duration | - | | + + + + +### SMSProvider + + + +| Field | Type | Description | Validation | +| ----- | ---- | ----------- | ----------- | +| details | zitadel.v1.ObjectDetails | - | | +| id | string | - | | +| state | SMSProviderConfigState | - | | +| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) config.twilio | TwilioConfig | - | | + + + + +### SMTPConfig + + + +| Field | Type | Description | Validation | +| ----- | ---- | ----------- | ----------- | +| details | zitadel.v1.ObjectDetails | - | | +| sender_address | string | - | | +| sender_name | string | - | | +| tls | bool | - | | +| host | string | - | | +| user | string | - | | + + + + +### SecretGenerator + + + +| Field | Type | Description | Validation | +| ----- | ---- | ----------- | ----------- | +| generator_type | SecretGeneratorType | - | | +| details | zitadel.v1.ObjectDetails | - | | +| length | uint32 | - | | +| expiry | google.protobuf.Duration | - | | +| include_lower_letters | bool | - | | +| include_upper_letters | bool | - | | +| include_digits | bool | - | | +| include_symbols | bool | - | | + + + + +### SecretGeneratorQuery + + + +| Field | Type | Description | Validation | +| ----- | ---- | ----------- | ----------- | +| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) query.type_query | SecretGeneratorTypeQuery | - | | + + + + +### SecretGeneratorTypeQuery + + + +| Field | Type | Description | Validation | +| ----- | ---- | ----------- | ----------- | +| generator_type | SecretGeneratorType | - | | + + + + +### TwilioConfig + + + +| Field | Type | Description | Validation | +| ----- | ---- | ----------- | ----------- | +| sid | string | - | | +| sender_number | string | - | | + + + + + + +## Enums + + +### SMSProviderConfigState {#smsproviderconfigstate} + + +| Name | Number | Description | +| ---- | ------ | ----------- | +| SMS_PROVIDER_CONFIG_STATE_UNSPECIFIED | 0 | - | +| SMS_PROVIDER_CONFIG_ACTIVE | 1 | - | +| SMS_PROVIDER_CONFIG_INACTIVE | 2 | - | + + + + +### SecretGeneratorType {#secretgeneratortype} + + +| Name | Number | Description | +| ---- | ------ | ----------- | +| SECRET_GENERATOR_TYPE_UNSPECIFIED | 0 | - | +| SECRET_GENERATOR_TYPE_INIT_CODE | 1 | - | +| SECRET_GENERATOR_TYPE_VERIFY_EMAIL_CODE | 2 | - | +| SECRET_GENERATOR_TYPE_VERIFY_PHONE_CODE | 3 | - | +| SECRET_GENERATOR_TYPE_PASSWORD_RESET_CODE | 4 | - | +| SECRET_GENERATOR_TYPE_PASSWORDLESS_INIT_CODE | 5 | - | +| SECRET_GENERATOR_TYPE_APP_SECRET | 6 | - | + + + + diff --git a/docs/docs/apis/proto/system.md b/docs/docs/apis/proto/system.md new file mode 100644 index 0000000000..1b601750ef --- /dev/null +++ b/docs/docs/apis/proto/system.md @@ -0,0 +1,522 @@ +--- +title: zitadel/system.proto +--- +> This document reflects the state from API 1.0 (available from 20.04.2021) + + +## SystemService {#zitadelsystemv1systemservice} + + +### Healthz + +> **rpc** Healthz([HealthzRequest](#healthzrequest)) +[HealthzResponse](#healthzresponse) + +Indicates if ZITADEL is running. +It respondes as soon as ZITADEL started + + + + GET: /healthz + + +### ListInstances + +> **rpc** ListInstances([ListInstancesRequest](#listinstancesrequest)) +[ListInstancesResponse](#listinstancesresponse) + +Returns a list of ZITADEL instances/tenants + + + + POST: /instances + + +### GetInstance + +> **rpc** GetInstance([GetInstanceRequest](#getinstancerequest)) +[GetInstanceResponse](#getinstanceresponse) + +Returns the detail of an instance + + + + GET: /instances/{id} + + +### AddInstance + +> **rpc** AddInstance([AddInstanceRequest](#addinstancerequest)) +[AddInstanceResponse](#addinstanceresponse) + +Creates a new instance with all needed setup data +This might take some time + + + + POST: /instances + + +### RemoveInstance + +> **rpc** RemoveInstance([RemoveInstanceRequest](#removeinstancerequest)) +[RemoveInstanceResponse](#removeinstanceresponse) + +Removes a instances +This might take some time + + + + DELETE: /instances/{id} + + +### GetUsage + +> **rpc** GetUsage([GetUsageRequest](#getusagerequest)) +[GetUsageResponse](#getusageresponse) + +Returns the usage metrics of an instance + + + + GET: /instances/{id}/usage + + +### GetGeneratedDomain + +> **rpc** GetGeneratedDomain([GetGeneratedDomainRequest](#getgenerateddomainrequest)) +[GetGeneratedDomainResponse](#getgenerateddomainresponse) + +Returns the domain of an instance + + + + GET: /instances/{id}/domains/generated + + +### GetCustomDomains + +> **rpc** GetCustomDomains([GetCustomDomainsRequest](#getcustomdomainsrequest)) +[GetCustomDomainsResponse](#getcustomdomainsresponse) + +Returns the custom domains of an instance + + + + GET: /instances/{id}/domains/custom + + +### AddCustomDomain + +> **rpc** AddCustomDomain([AddCustomDomainRequest](#addcustomdomainrequest)) +[AddCustomDomainResponse](#addcustomdomainresponse) + +Returns the domain of an instance + + + + POST: /instances/{id}/domains/custom + + +### ListViews + +> **rpc** ListViews([ListViewsRequest](#listviewsrequest)) +[ListViewsResponse](#listviewsresponse) + +Returns all stored read models of ZITADEL +views are used for search optimisation and optimise request latencies +they represent the delta of the event happend on the objects + + + + POST: /views/_search + + +### ClearView + +> **rpc** ClearView([ClearViewRequest](#clearviewrequest)) +[ClearViewResponse](#clearviewresponse) + +Truncates the delta of the change stream +be carefull with this function because ZITADEL has to +recompute the deltas after they got cleared. +Search requests will return wrong results until all deltas are recomputed + + + + POST: /views/{database}/{view_name} + + +### ListFailedEvents + +> **rpc** ListFailedEvents([ListFailedEventsRequest](#listfailedeventsrequest)) +[ListFailedEventsResponse](#listfailedeventsresponse) + +Returns event descriptions which cannot be processed. +It's possible that some events need some retries. +For example if the SMTP-API wasn't able to send an email at the first time + + + + POST: /failedevents/_search + + +### RemoveFailedEvent + +> **rpc** RemoveFailedEvent([RemoveFailedEventRequest](#removefailedeventrequest)) +[RemoveFailedEventResponse](#removefailedeventresponse) + +Deletes the event from failed events view. +the event is not removed from the change stream +This call is usefull if the system was able to process the event later. +e.g. if the second try of sending an email was successful. the first try produced a +failed event. You can find out if it worked on the `failure_count` + + + + DELETE: /failedevents/{database}/{view_name}/{failed_sequence} + + + + + + + +## Messages + + +### AddCustomDomainRequest + + + +| Field | Type | Description | Validation | +| ----- | ---- | ----------- | ----------- | +| id | string | - | string.min_len: 1
string.max_len: 200
| +| custom_domain | string | - | string.min_len: 1
string.max_len: 200
| + + + + +### AddCustomDomainResponse + + + +| Field | Type | Description | Validation | +| ----- | ---- | ----------- | ----------- | +| details | zitadel.v1.ObjectDetails | - | | + + + + +### AddInstanceRequest + + + +| Field | Type | Description | Validation | +| ----- | ---- | ----------- | ----------- | +| instance_name | string | - | string.min_len: 1
string.max_len: 200
| +| first_org_name | string | - | string.min_len: 1
string.max_len: 200
| +| custom_domain | string | - | string.max_len: 200
| +| owner_first_name | string | - | string.min_len: 1
string.max_len: 200
| +| owner_last_name | string | - | string.min_len: 1
string.max_len: 200
| +| owner_email | string | - | string.min_len: 1
string.max_len: 200
| +| owner_username | string | - | string.min_len: 1
string.max_len: 200
| +| password | string | - | string.min_len: 1
string.max_len: 200
| +| request_limit | uint64 | - | | +| action_mins_limit | uint64 | - | | + + + + +### AddInstanceResponse + + + +| Field | Type | Description | Validation | +| ----- | ---- | ----------- | ----------- | +| id | string | - | | + + + + +### ChangeSubscriptionRequest + + + +| Field | Type | Description | Validation | +| ----- | ---- | ----------- | ----------- | +| domain | string | - | string.min_len: 1
string.max_len: 200
| +| subscription_name | string | - | string.min_len: 1
string.max_len: 200
| +| request_limit | uint64 | - | | +| action_mins_limit | uint64 | - | | + + + + +### ChangeSubscriptionResponse + + + +| Field | Type | Description | Validation | +| ----- | ---- | ----------- | ----------- | +| details | zitadel.v1.ObjectDetails | - | | + + + + +### ClearViewRequest + + + +| Field | Type | Description | Validation | +| ----- | ---- | ----------- | ----------- | +| database | string | - | string.min_len: 1
string.max_len: 200
| +| view_name | string | - | string.min_len: 1
string.max_len: 200
| + + + + +### ClearViewResponse +This is an empty response + + + + +### FailedEvent + + + +| Field | Type | Description | Validation | +| ----- | ---- | ----------- | ----------- | +| database | string | - | | +| view_name | string | - | | +| failed_sequence | uint64 | - | | +| failure_count | uint64 | - | | +| error_message | string | - | | + + + + +### GetCustomDomainsRequest + + + +| Field | Type | Description | Validation | +| ----- | ---- | ----------- | ----------- | +| id | string | - | string.min_len: 1
string.max_len: 200
| + + + + +### GetCustomDomainsResponse + + + +| Field | Type | Description | Validation | +| ----- | ---- | ----------- | ----------- | +| details | zitadel.v1.ObjectDetails | - | | +| domains | repeated string | - | | + + + + +### GetGeneratedDomainRequest + + + +| Field | Type | Description | Validation | +| ----- | ---- | ----------- | ----------- | +| id | string | - | string.min_len: 1
string.max_len: 200
| + + + + +### GetGeneratedDomainResponse + + + +| Field | Type | Description | Validation | +| ----- | ---- | ----------- | ----------- | +| details | zitadel.v1.ObjectDetails | - | | +| domain | string | - | | + + + + +### GetInstanceRequest + + + +| Field | Type | Description | Validation | +| ----- | ---- | ----------- | ----------- | +| id | string | - | string.min_len: 1
string.max_len: 200
| + + + + +### GetInstanceResponse + + + +| Field | Type | Description | Validation | +| ----- | ---- | ----------- | ----------- | +| instance | zitadel.instance.v1.Instance | - | | + + + + +### GetUsageRequest + + + +| Field | Type | Description | Validation | +| ----- | ---- | ----------- | ----------- | +| id | string | - | string.min_len: 1
string.max_len: 200
| + + + + +### GetUsageResponse + + + +| Field | Type | Description | Validation | +| ----- | ---- | ----------- | ----------- | +| details | zitadel.v1.ObjectDetails | - | | +| executed_requests | uint64 | - | | +| executed_action_mins | uint64 | - | | + + + + +### HealthzRequest +This is an empty request + + + + +### HealthzResponse +This is an empty response + + + + +### ListFailedEventsRequest +This is an empty request + + + + +### ListFailedEventsResponse + + + +| Field | Type | Description | Validation | +| ----- | ---- | ----------- | ----------- | +| result | repeated FailedEvent | TODO: list details | | + + + + +### ListInstancesRequest + + + +| Field | Type | Description | Validation | +| ----- | ---- | ----------- | ----------- | +| query | zitadel.v1.ListQuery | list limitations and ordering | | +| sorting_column | zitadel.instance.v1.FieldName | the field the result is sorted | | +| queries | repeated zitadel.instance.v1.Query | criterias the client is looking for | | + + + + +### ListInstancesResponse + + + +| Field | Type | Description | Validation | +| ----- | ---- | ----------- | ----------- | +| details | zitadel.v1.ListDetails | - | | +| sorting_column | zitadel.instance.v1.FieldName | - | | +| result | repeated zitadel.instance.v1.Instance | - | | + + + + +### ListViewsRequest +This is an empty request + + + + +### ListViewsResponse + + + +| Field | Type | Description | Validation | +| ----- | ---- | ----------- | ----------- | +| result | repeated View | TODO: list details | | + + + + +### RemoveFailedEventRequest + + + +| Field | Type | Description | Validation | +| ----- | ---- | ----------- | ----------- | +| database | string | - | string.min_len: 1
string.max_len: 200
| +| view_name | string | - | string.min_len: 1
string.max_len: 200
| +| failed_sequence | uint64 | - | | + + + + +### RemoveFailedEventResponse +This is an empty response + + + + +### RemoveInstanceRequest + + + +| Field | Type | Description | Validation | +| ----- | ---- | ----------- | ----------- | +| id | string | - | string.min_len: 1
string.max_len: 200
| + + + + +### RemoveInstanceResponse + + + +| Field | Type | Description | Validation | +| ----- | ---- | ----------- | ----------- | +| details | zitadel.v1.ObjectDetails | - | | + + + + +### View + + + +| Field | Type | Description | Validation | +| ----- | ---- | ----------- | ----------- | +| database | string | - | | +| view_name | string | - | | +| processed_sequence | uint64 | - | | +| event_timestamp | google.protobuf.Timestamp | The timestamp the event occured | | +| last_successful_spooler_run | google.protobuf.Timestamp | - | | +| instance | string | - | | + + + + + + diff --git a/proto/zitadel/instance.proto b/proto/zitadel/instance.proto new file mode 100644 index 0000000000..f33a62841a --- /dev/null +++ b/proto/zitadel/instance.proto @@ -0,0 +1,104 @@ +syntax = "proto3"; + +import "zitadel/object.proto"; +import "validate/validate.proto"; +import "protoc-gen-openapiv2/options/annotations.proto"; + +package zitadel.instance.v1; + +option go_package ="github.com/caos/zitadel/pkg/grpc/instance"; + +message Instance { + string id = 1 [ + (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_field) = { + example: "\"69629023906488334\"" + } + ]; + zitadel.v1.ObjectDetails details = 2; + State state = 3 [ + (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_field) = { + description: "current state of the instance"; + } + ]; + string generated_domain = 4 [ + (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_field) = { + example: "\"organization.zitadel.com\""; + } + ]; + repeated string custom_domains = 5 [ + (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_field) = { + example: "[\"zitadel.com\", \"zitadel.cloud\", \"zitadel.ch\"]"; + } + ]; + string name = 6 [ + (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_field) = { + example: "\"ZITADEL\""; + } + ]; + string version = 7 [ + (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_field) = { + example: "\"v1.0.0\""; + } + ]; +} + +enum State { + STATE_UNSPECIFIED = 0; + STATE_CREATING = 1; + STATE_RUNNING = 2; + STATE_STOPPING = 3; + STATE_STOPPED = 4; +} + +message Query { + oneof query { + option (validate.required) = true; + + IdQuery id_query = 1; + DomainsQuery domains_query = 2; + StateQuery state_query = 3; + } +} + +//IdQuery is always equals +message IdQuery { + string id = 1 [ + (validate.rules).string = {max_len: 200}, + (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_field) = { + description: "4820840938402429"; + } + ]; +} + +message DomainsQuery { + repeated string domains = 1 [ + (validate.rules).string = {max_len: 200}, + (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_field) = { + example: "\"caos.ch\""; + } + ]; + zitadel.v1.ListQueryMethod method = 2 [ + (validate.rules).enum.defined_only = true, + (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_field) = { + description: "defines which list equality method is used"; + } + ]; +} + +//StateQuery is always equals +message StateQuery { + State state = 1 [ + (validate.rules).enum.defined_only = true, + (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_field) = { + description: "current state of the instance"; + } + ]; +} + +enum FieldName { + FIELD_NAME_UNSPECIFIED = 0; + FIELD_NAME_ID = 1; + FIELD_NAME_GENERATED_DOMAIN = 2; + FIELD_NAME_NAME = 3; + FIELD_NAME_CREATION_DATE = 4; +} diff --git a/proto/zitadel/object.proto b/proto/zitadel/object.proto index c709b58d33..0a883bee3b 100644 --- a/proto/zitadel/object.proto +++ b/proto/zitadel/object.proto @@ -85,3 +85,8 @@ enum TextQueryMethod { TEXT_QUERY_METHOD_ENDS_WITH = 6; TEXT_QUERY_METHOD_ENDS_WITH_IGNORE_CASE = 7; } + + +enum ListQueryMethod { + LIST_QUERY_METHOD_IN = 0; +} diff --git a/proto/zitadel/system.proto b/proto/zitadel/system.proto new file mode 100644 index 0000000000..e10a3ea0ba --- /dev/null +++ b/proto/zitadel/system.proto @@ -0,0 +1,524 @@ +syntax = "proto3"; + +import "zitadel/object.proto"; +import "zitadel/options.proto"; +import "zitadel/instance.proto"; +import "zitadel/text.proto"; + +import "google/api/annotations.proto"; +import "google/protobuf/timestamp.proto"; +import "google/protobuf/duration.proto"; + +import "protoc-gen-openapiv2/options/annotations.proto"; + +import "validate/validate.proto"; + +package zitadel.system.v1; + +option go_package ="github.com/caos/zitadel/pkg/grpc/system"; + +option (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_swagger) = { + info: { + title: "System API"; + version: "1.0"; + description: "This API is intended to configure and manage the different tenants whithin ZITADEL."; + contact:{ + name: "CAOS developers of ZITADEL" + url: "https://zitadel.ch" + email: "hi@zitadel.ch" + } + license: { + name: "Apache 2.0", + url: "https://github.com/caos/zitadel/blob/main/LICENSE"; + }; + }; + + schemes: HTTPS; + schemes: HTTP; + + consumes: "application/json"; + consumes: "application/grpc"; + + produces: "application/json"; + produces: "application/grpc"; + + consumes: "application/grpc-web+proto"; + produces: "application/grpc-web+proto"; + + host: "api.zitadel.ch"; + base_path: "/system/v1"; + + external_docs: { + description: "Detailed information about ZITADEL", + url: "https://docs.zitadel.ch" + } + + responses: { + key: "403"; + value: { + description: "Returned when the user does not have permission to access the resource."; + schema: { + json_schema: { + ref: "#/definitions/rpcStatus"; + } + } + } + } + responses: { + key: "404"; + value: { + description: "Returned when the resource does not exist."; + schema: { + json_schema: { + ref: "#/definitions/rpcStatus"; + } + } + } + } +}; + +service SystemService { + //Indicates if ZITADEL is running. + // It respondes as soon as ZITADEL started + rpc Healthz(HealthzRequest) returns (HealthzResponse) { + option (google.api.http) = { + get: "/healthz"; + }; + + option (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_operation) = { + tags: "probes"; + responses: { + key: "200"; + value: { + description: "ZITADEL started"; + }; + } + responses: { + key: "default"; + value: { + description: "ZITADEL NOT started yet"; + }; + } + }; + } + + // Returns a list of ZITADEL instances/tenants + rpc ListInstances(ListInstancesRequest) returns (ListInstancesResponse) { + option (google.api.http) = { + post: "/instances" + body: "*" + }; + } + + // Returns the detail of an instance + rpc GetInstance(GetInstanceRequest) returns (GetInstanceResponse) { + option (google.api.http) = { + get: "/instances/{id}"; + }; + } + + // Creates a new instance with all needed setup data + // This might take some time + rpc AddInstance(AddInstanceRequest) returns (AddInstanceResponse) { + option (google.api.http) = { + post: "/instances" + body: "*" + }; + } + + // Removes a instances + // This might take some time + rpc RemoveInstance(RemoveInstanceRequest) returns (RemoveInstanceResponse) { + option (google.api.http) = { + delete: "/instances/{id}" + }; + } + + // Returns the usage metrics of an instance + rpc GetUsage(GetUsageRequest) returns (GetUsageResponse) { + option (google.api.http) = { + get: "/instances/{id}/usage"; + }; + } + + // Returns the domain of an instance + rpc GetGeneratedDomain(GetGeneratedDomainRequest) returns (GetGeneratedDomainResponse) { + option (google.api.http) = { + get: "/instances/{id}/domains/generated"; + }; + } + + // Returns the custom domains of an instance + rpc GetCustomDomains(GetCustomDomainsRequest) returns (GetCustomDomainsResponse) { + option (google.api.http) = { + get: "/instances/{id}/domains/custom"; + }; + } + + // Returns the domain of an instance + rpc AddCustomDomain(AddCustomDomainRequest) returns (AddCustomDomainResponse) { + option (google.api.http) = { + post: "/instances/{id}/domains/custom"; + body: "*" + }; + } + + //Returns all stored read models of ZITADEL + // views are used for search optimisation and optimise request latencies + // they represent the delta of the event happend on the objects + rpc ListViews(ListViewsRequest) returns (ListViewsResponse) { + option (google.api.http) = { + post: "/views/_search"; + }; + + option (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_operation) = { + tags: "views"; + external_docs: { + url: "https://docs.zitadel.ch/concepts#Software_Architecture"; + description: "details of ZITADEL's event driven software concepts"; + }; + responses: { + key: "200"; + value: { + description: "Views for query operations"; + }; + }; + }; + } + + //Truncates the delta of the change stream + // be carefull with this function because ZITADEL has to + // recompute the deltas after they got cleared. + // Search requests will return wrong results until all deltas are recomputed + rpc ClearView(ClearViewRequest) returns (ClearViewResponse) { + option (google.api.http) = { + post: "/views/{database}/{view_name}"; + }; + + option (zitadel.v1.auth_option) = { + permission: "iam.write"; + }; + + option (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_operation) = { + tags: "views"; + external_docs: { + url: "https://docs.zitadel.ch/concepts#Software_Architecture"; + description: "details of ZITADEL's event driven software concepts"; + }; + responses: { + key: "200"; + value: { + description: "View cleared"; + }; + }; + }; + } + + //Returns event descriptions which cannot be processed. + // It's possible that some events need some retries. + // For example if the SMTP-API wasn't able to send an email at the first time + rpc ListFailedEvents(ListFailedEventsRequest) returns (ListFailedEventsResponse) { + option (google.api.http) = { + post: "/failedevents/_search"; + }; + + option (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_operation) = { + tags: "failed events"; + external_docs: { + url: "https://docs.zitadel.ch/concepts#Software_Architecture"; + description: "details of ZITADEL's event driven software concepts"; + }; + responses: { + key: "200"; + value: { + description: "Events which were not processed by the views"; + }; + }; + }; + } + + //Deletes the event from failed events view. + // the event is not removed from the change stream + // This call is usefull if the system was able to process the event later. + // e.g. if the second try of sending an email was successful. the first try produced a + // failed event. You can find out if it worked on the `failure_count` + rpc RemoveFailedEvent(RemoveFailedEventRequest) returns (RemoveFailedEventResponse) { + option (google.api.http) = { + delete: "/failedevents/{database}/{view_name}/{failed_sequence}"; + }; + + option (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_operation) = { + tags: "failed events"; + external_docs: { + url: "https://docs.zitadel.ch/concepts#Software_Architecture"; + description: "details of ZITADEL's event driven software concepts"; + }; + responses: { + key: "200"; + value: { + description: "Events removed from the list"; + }; + }; + responses: { + key: "400"; + value: { + description: "failed event not found"; + schema: { + json_schema: { + ref: "#/definitions/rpcStatus"; + }; + }; + }; + }; + }; + } +} + + +//This is an empty request +message HealthzRequest {} + +//This is an empty response +message HealthzResponse {} + +message ListInstancesRequest { + option (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_schema) = { + json_schema: { + description: "Search query for lists"; + required: ["query"] + }; + }; + + //list limitations and ordering + zitadel.v1.ListQuery query = 1; + // the field the result is sorted + zitadel.instance.v1.FieldName sorting_column = 2; + //criterias the client is looking for + repeated zitadel.instance.v1.Query queries = 3; +} + +message ListInstancesResponse { + zitadel.v1.ListDetails details = 1; + zitadel.instance.v1.FieldName sorting_column = 2; + repeated zitadel.instance.v1.Instance result = 3; +} + +message GetInstanceRequest { + string id = 1 [(validate.rules).string = {min_len: 1, max_len: 200}]; +} + +message GetInstanceResponse { + zitadel.instance.v1.Instance instance = 1; +} + +message AddInstanceRequest { + string instance_name = 1 [(validate.rules).string = {min_len: 1, max_len: 200}]; + string first_org_name = 2 [(validate.rules).string = {min_len: 1, max_len: 200}]; + string custom_domain = 3 [(validate.rules).string = {max_len: 200}]; + string owner_first_name = 4 [(validate.rules).string = {min_len: 1, max_len: 200}]; + string owner_last_name = 5 [(validate.rules).string = {min_len: 1, max_len: 200}]; + string owner_email = 6 [(validate.rules).string = {min_len: 1, max_len: 200}]; + string owner_username = 7 [(validate.rules).string = {min_len: 1, max_len: 200}]; + string password = 8 [(validate.rules).string = {min_len: 1, max_len: 200}]; + uint64 request_limit = 9; + uint64 action_mins_limit = 10; +} + +message AddInstanceResponse { + string id = 1; +} + +message RemoveInstanceRequest { + string id = 1 [(validate.rules).string = {min_len: 1, max_len: 200}]; +} + +message RemoveInstanceResponse { + zitadel.v1.ObjectDetails details = 1; +} + +message GetUsageRequest { + string id = 1 [(validate.rules).string = {min_len: 1, max_len: 200}]; +} + +message GetUsageResponse { + zitadel.v1.ObjectDetails details = 1; + uint64 executed_requests = 2; + uint64 executed_action_mins = 3; +} + +message GetGeneratedDomainRequest { + string id = 1 [(validate.rules).string = {min_len: 1, max_len: 200}]; +} + +message GetGeneratedDomainResponse { + zitadel.v1.ObjectDetails details = 1; + string domain = 2; +} + +message GetCustomDomainsRequest { + string id = 1 [(validate.rules).string = {min_len: 1, max_len: 200}]; +} + +message GetCustomDomainsResponse { + zitadel.v1.ObjectDetails details = 1; + repeated string domains = 2; +} + +message AddCustomDomainRequest { + string id = 1 [(validate.rules).string = {min_len: 1, max_len: 200}]; + string custom_domain = 2 [(validate.rules).string = {min_len: 1, max_len: 200}]; +} + +message AddCustomDomainResponse { + zitadel.v1.ObjectDetails details = 1; +} + +message ChangeSubscriptionRequest { + string domain = 1 [(validate.rules).string = {min_len: 1, max_len: 200}]; + string subscription_name = 2 [(validate.rules).string = {min_len: 1, max_len: 200}]; + uint64 request_limit = 3; + uint64 action_mins_limit = 4; +} + +message ChangeSubscriptionResponse { + zitadel.v1.ObjectDetails details = 1; +} + +//This is an empty request +message ListViewsRequest {} + +message ListViewsResponse { + //TODO: list details + repeated View result = 1; +} + +message ClearViewRequest { + option (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_schema) = { + json_schema: { + required: ["database", "view_name"] + }; + }; + + string database = 1 [ + (validate.rules).string = {min_len: 1, max_len: 200}, + (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_field) = { + example: "\"adminapi\""; + min_length: 1; + max_length: 200; + } + ]; + string view_name = 2 [ + (validate.rules).string = {min_len: 1, max_len: 200}, + (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_field) = { + example: "\"iam_members\""; + min_length: 1; + max_length: 200; + } + ]; +} + +//This is an empty response +message ClearViewResponse {} + +//This is an empty request +message ListFailedEventsRequest {} + +message ListFailedEventsResponse { + //TODO: list details + repeated FailedEvent result = 1; +} + +message RemoveFailedEventRequest { + option (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_schema) = { + json_schema: { + required: ["database", "view_name", "failed_sequence"] + }; + }; + + string database = 1 [ + (validate.rules).string = {min_len: 1, max_len: 200}, + (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_field) = { + example: "\"adminapi\""; + min_length: 1; + max_length: 200; + } + ]; + string view_name = 2 [ + (validate.rules).string = {min_len: 1, max_len: 200}, + (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_field) = { + example: "\"iam_members\""; + min_length: 1; + max_length: 200; + } + ]; + uint64 failed_sequence = 3 [ + (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_field) = { + example: "\"9823758\""; + } + ]; +} + +//This is an empty response +message RemoveFailedEventResponse {} + +message View { + string database = 1 [ + (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_field) = { + example: "\"adminapi\""; + } + ]; + string view_name = 2 [ + (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_field) = { + example: "\"iam_members\""; + } + ]; + uint64 processed_sequence = 3 [ + (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_field) = { + example: "\"9823758\""; + } + ]; + google.protobuf.Timestamp event_timestamp = 4 [ + (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_field) = { + example: "\"2019-04-01T08:45:00.000000Z\""; + description: "The timestamp the event occured"; + } + ]; // The timestamp the event occured + google.protobuf.Timestamp last_successful_spooler_run = 5 [ + (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_field) = { + description: "The timestamp the event occured"; + } + ]; + string instance = 6 [ + (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_field) = { + example: "\"840498034930840\""; + } + ]; +} + +message FailedEvent { + string database = 1 [ + (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_field) = { + example: "\"adminapi\""; + } + ]; + string view_name = 2 [ + (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_field) = { + example: "\"iam_members\""; + } + ]; + uint64 failed_sequence = 3 [ + (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_field) = { + example: "\"9823759\""; + } + ]; + uint64 failure_count = 4 [ + (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_field) = { + example: "\"5\""; + } + ]; + string error_message = 5 [ + (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_field) = { + example: "\"ID=EXAMP-ID3ER Message=Example message\""; + } + ]; +}