diff --git a/client/console_client.go b/client/console_client.go index 9093dc3..4f5a8f6 100644 --- a/client/console_client.go +++ b/client/console_client.go @@ -304,9 +304,12 @@ func (client *Client) Get(kind *schema.Kind, parentPathValue []string, parentQue return result, err } -func (client *Client) Execute(execute schema.Execute, pathValue []string, queryParams map[string]string, body interface{}) ([]byte, error) { +func (client *Client) Run(run schema.Run, pathValue []string, queryParams map[string]string, body interface{}) ([]byte, error) { + if run.BackendType != schema.CONSOLE { + return nil, fmt.Errorf("Only console backend type is supported by console client") + } client.setAuthMethodFromEnvIfNeeded() - path := execute.BuildPath(pathValue) + path := run.BuildPath(pathValue) url := client.baseUrl + path requestBuilder := client.client.R() for k, v := range queryParams { @@ -315,7 +318,7 @@ func (client *Client) Execute(execute schema.Execute, pathValue []string, queryP if body != nil { requestBuilder = requestBuilder.SetBody(body) } - resp, err := requestBuilder.Execute(execute.GetMethod(), url) + resp, err := requestBuilder.Execute(run.Method, url) if err != nil { return nil, err } else if resp.IsError() { diff --git a/client/gateway_client.go b/client/gateway_client.go index b810f97..863c971 100644 --- a/client/gateway_client.go +++ b/client/gateway_client.go @@ -17,7 +17,7 @@ type GatewayClient struct { cdkGatewayPassword string baseUrl string client *resty.Client - kinds schema.KindCatalog + schemaCatalog *schema.Catalog } type GatewayApiParameter struct { @@ -43,19 +43,19 @@ func MakeGateway(apiParameter GatewayApiParameter) (*GatewayClient, error) { cdkGatewayPassword: apiParameter.CdkGatewayPassword, baseUrl: apiParameter.BaseUrl, client: restyClient, - kinds: nil, + schemaCatalog: nil, } result.client.SetTLSClientConfig(&tls.Config{InsecureSkipVerify: true}) result.client.SetDisableWarn(true) result.client.SetBasicAuth(apiParameter.CdkGatewayUser, apiParameter.CdkGatewayPassword) - err := result.initKindFromApi() + err := result.initCatalogFromApi() if err != nil { if apiParameter.Debug { fmt.Fprintf(os.Stderr, "Cannot access the Gateway Conduktor API: %s\nUsing offline defaults.\n", err) } - result.kinds = schema.GatewayDefaultCatalog().Kind + result.schemaCatalog = schema.GatewayDefaultCatalog() } return result, nil @@ -283,6 +283,28 @@ func (client *GatewayClient) ActivateDebug() { client.client.SetDebug(true) } +func (client *GatewayClient) Run(run schema.Run, pathValue []string, queryParams map[string]string, body interface{}) ([]byte, error) { + if run.BackendType != schema.GATEWAY { + return nil, fmt.Errorf("Only console backend type is supported by console client") + } + path := run.BuildPath(pathValue) + url := client.baseUrl + path + requestBuilder := client.client.R() + for k, v := range queryParams { + requestBuilder.SetQueryParam(k, v) + } + if body != nil { + requestBuilder = requestBuilder.SetBody(body) + } + resp, err := requestBuilder.Execute(run.Method, url) + if err != nil { + return nil, err + } else if resp.IsError() { + return nil, fmt.Errorf(extractApiError(resp)) + } + return resp.Body(), nil +} + func (client *GatewayClient) Apply(resource *resource.Resource, dryMode bool) (string, error) { kinds := client.GetKinds() kind, ok := kinds[resource.Kind] @@ -328,7 +350,7 @@ func (client *GatewayClient) GetOpenApi() ([]byte, error) { return resp.Body(), nil } -func (client *GatewayClient) initKindFromApi() error { +func (client *GatewayClient) initCatalogFromApi() error { data, err := client.GetOpenApi() if err != nil { return fmt.Errorf("Cannot get openapi: %s", err) @@ -338,7 +360,7 @@ func (client *GatewayClient) initKindFromApi() error { return fmt.Errorf("Cannot parse openapi: %s", err) } strict := false - client.kinds, err = schema.GetGatewayKinds(strict) + client.schemaCatalog, err = schema.GetGatewayCatalog(strict) if err != nil { fmt.Errorf("Cannot extract schemaCatalog from openapi: %s", err) } @@ -346,12 +368,9 @@ func (client *GatewayClient) initKindFromApi() error { } func (client *GatewayClient) GetKinds() schema.KindCatalog { - return client.kinds + return client.schemaCatalog.Kind } func (client *GatewayClient) GetCatalog() *schema.Catalog { - return &schema.Catalog{ - Kind: client.GetKinds(), - Execute: map[string]schema.Execute{}, - } + return client.schemaCatalog } diff --git a/cmd/consoleMkKind.go b/cmd/consoleMakeCatalog.go similarity index 57% rename from cmd/consoleMkKind.go rename to cmd/consoleMakeCatalog.go index d0603d6..21ecd9e 100644 --- a/cmd/consoleMkKind.go +++ b/cmd/consoleMakeCatalog.go @@ -6,19 +6,19 @@ import ( "github.com/spf13/cobra" ) -func initConsoleMkKind() { +func intConsoleMakeCatalog() { var prettyPrint *bool var nonStrict *bool var makeKind = &cobra.Command{ - Use: "makeKind [file]", - Short: "Make kind json from openapi file if file not given it will read from api", + Use: "makeConsoleCatalog [file]", + Short: "Make catalog json from openapi file if file not given it will read from api", Long: ``, - Aliases: []string{"mkKind", "makeConsoleKind"}, + Aliases: []string{"mkKind", "makeConsoleKind", "makeKind"}, // for backward compatibility Args: cobra.RangeArgs(0, 1), Hidden: !utils.CdkDevMode(), Run: func(cmd *cobra.Command, args []string) { - runMkKind(cmd, args, *prettyPrint, *nonStrict, func() ([]byte, error) { return consoleApiClient().GetOpenApi() }, func(schema *schema.OpenApiParser, strict bool) (*schema.Catalog, error) { + runMakeCatalog(cmd, args, *prettyPrint, *nonStrict, func() ([]byte, error) { return consoleApiClient().GetOpenApi() }, func(schema *schema.OpenApiParser, strict bool) (*schema.Catalog, error) { return schema.GetConsoleCatalog(strict) }) }, diff --git a/cmd/execute.go b/cmd/execute.go deleted file mode 100644 index 7c5e42a..0000000 --- a/cmd/execute.go +++ /dev/null @@ -1,116 +0,0 @@ -package cmd - -import ( - "encoding/json" - "fmt" - "github.com/conduktor/ctl/schema" - "github.com/spf13/cobra" - "os" -) - -var executeCmd = &cobra.Command{ - Use: "execute", - Short: "execute an action", - Args: cobra.NoArgs, - Run: func(cmd *cobra.Command, args []string) { - // Root command does nothing - cmd.Help() - os.Exit(1) - }, -} - -func printExecuteResult(result []byte) { - var stringJson string - err := json.Unmarshal(result, &stringJson) - if err == nil { - fmt.Println(stringJson) - return - } - var jsonResult interface{} - err = json.Unmarshal(result, &jsonResult) - if err == nil { - jsonOutput, err := json.MarshalIndent(jsonResult, "", " ") - if err == nil { - fmt.Println(string(jsonOutput)) - return - } - } - if len(result) > 1 { - fmt.Println(string(result)) - } else { - fmt.Println("Ok") - } -} - -func initExecute(executes schema.ExecuteCatalog) { - rootCmd.AddCommand(executeCmd) - - // Add all kinds to the 'get' command - for name, execute := range executes { - args := cobra.MaximumNArgs(0) - pathFlags := execute.GetPathParameter() - pathFlagValues := make([]*string, len(pathFlags)) - queryFlags := execute.GetQueryParameter() - bodyFlags := execute.GetBodyFields() - queryFlagValues := make(map[string]interface{}, len(queryFlags)) - bodyFlagValues := make(map[string]interface{}, len(bodyFlags)) - subExecuteCmd := &cobra.Command{ - Use: name, - Short: execute.GetDoc(), - Args: args, - Aliases: buildAlias(name), - Run: func(cmd *cobra.Command, args []string) { - pathValues := make([]string, len(pathFlagValues)) - queryParams := buildQueryParams(queryFlagValues) - body := buildQueryParams(bodyFlagValues) - for i, v := range pathFlagValues { - pathValues[i] = *v - } - - var err error - - if len(bodyFlags) == 0 { - body = nil - } - result, err := consoleApiClient().Execute(execute, pathValues, queryParams, body) - if err != nil { - fmt.Fprintf(os.Stderr, "Error fetching resources: %s\n", err) - return - } - printExecuteResult(result) - }, - } - for i, flag := range pathFlags { - pathFlagValues[i] = subExecuteCmd.Flags().String(flag, "", "Parent "+flag) - subExecuteCmd.MarkFlagRequired(flag) - } - for key, flag := range queryFlags { - var isFlagSet bool - if flag.Type == "string" { - isFlagSet = true - queryFlagValues[key] = subExecuteCmd.Flags().String(flag.FlagName, "", "") - } else if flag.Type == "boolean" { - isFlagSet = true - queryFlagValues[key] = subExecuteCmd.Flags().Bool(flag.FlagName, false, "") - } - if isFlagSet && flag.Required { - subExecuteCmd.MarkFlagRequired(flag.FlagName) - } - } - for key, flag := range bodyFlags { - var isFlagSet bool - if flag.Type == "string" { - isFlagSet = true - bodyFlagValues[key] = subExecuteCmd.Flags().String(flag.FlagName, "", "") - } else if flag.Type == "boolean" { - isFlagSet = true - bodyFlagValues[key] = subExecuteCmd.Flags().Bool(flag.FlagName, false, "") - } - if isFlagSet && flag.Required { - subExecuteCmd.MarkFlagRequired(flag.FlagName) - } - } - - executeCmd.AddCommand(subExecuteCmd) - } -} diff --git a/cmd/gatewayMkKind.go b/cmd/gatewayMkKind.go index eb84059..0c41b30 100644 --- a/cmd/gatewayMkKind.go +++ b/cmd/gatewayMkKind.go @@ -6,25 +6,25 @@ import ( "github.com/spf13/cobra" ) -func initGatewayMkKind() { +func initGatewayMakeCatalog() { var prettyPrint *bool var nonStrict *bool - var makeKind = &cobra.Command{ - Use: "gatewayMakeKind [file]", - Short: "Make kind json from openapi file if file not given it will read from api", + var makeCatalog = &cobra.Command{ + Use: "gatewayMakeCatalog [file]", + Short: "Make catalog json from openapi file if file not given it will read from api", Long: ``, - Aliases: []string{"gatewayMkKind", "gwMkKind"}, + Aliases: []string{"gatewayMkKind", "gwMkKind", "gatewayMakeKind"}, // for backward compatibility Args: cobra.RangeArgs(0, 1), Hidden: !utils.CdkDevMode(), Run: func(cmd *cobra.Command, args []string) { - runMkKind(cmd, args, *prettyPrint, *nonStrict, func() ([]byte, error) { return gatewayApiClient().GetOpenApi() }, func(schema *schema.OpenApiParser, strict bool) (*schema.Catalog, error) { + runMakeCatalog(cmd, args, *prettyPrint, *nonStrict, func() ([]byte, error) { return gatewayApiClient().GetOpenApi() }, func(schema *schema.OpenApiParser, strict bool) (*schema.Catalog, error) { return schema.GetGatewayCatalog(strict) }) }, } - rootCmd.AddCommand(makeKind) + rootCmd.AddCommand(makeCatalog) - prettyPrint = makeKind.Flags().BoolP("pretty", "p", false, "Pretty print the output") - nonStrict = makeKind.Flags().BoolP("non-strict", "n", false, "Don't be strict on the parsing of the schema") + prettyPrint = makeCatalog.Flags().BoolP("pretty", "p", false, "Pretty print the output") + nonStrict = makeCatalog.Flags().BoolP("non-strict", "n", false, "Don't be strict on the parsing of the schema") } diff --git a/cmd/genericMkKind.go b/cmd/genericMkKind.go index 40867bf..5137633 100644 --- a/cmd/genericMkKind.go +++ b/cmd/genericMkKind.go @@ -9,7 +9,7 @@ import ( "github.com/spf13/cobra" ) -func runMkKind(cmd *cobra.Command, args []string, prettyPrint bool, nonStrict bool, getOpenApi func() ([]byte, error), getKinds func(*schema.OpenApiParser, bool) (*schema.Catalog, error)) { +func runMakeCatalog(cmd *cobra.Command, args []string, prettyPrint bool, nonStrict bool, getOpenApi func() ([]byte, error), getCatalog func(*schema.OpenApiParser, bool) (*schema.Catalog, error)) { var kinds *schema.Catalog if len(args) == 1 { data, err := os.ReadFile(args[0]) @@ -20,7 +20,7 @@ func runMkKind(cmd *cobra.Command, args []string, prettyPrint bool, nonStrict bo if err != nil { panic(err) } - kinds, err = getKinds(schema, !nonStrict) + kinds, err = getCatalog(schema, !nonStrict) if err != nil { panic(err) } @@ -33,7 +33,7 @@ func runMkKind(cmd *cobra.Command, args []string, prettyPrint bool, nonStrict bo if err != nil { panic(err) } - kinds, err = getKinds(schema, !nonStrict) + kinds, err = getCatalog(schema, !nonStrict) if err != nil { panic(err) } diff --git a/cmd/get.go b/cmd/get.go index 15acc46..93c33d1 100644 --- a/cmd/get.go +++ b/cmd/get.go @@ -49,6 +49,7 @@ func buildQueryParams(params map[string]interface{}) map[string]string { if value != nil { str, strOk := value.(*string) boolValue, boolOk := value.(*bool) + intValue, intOk := value.(*int) if strOk { if *str != "" { @@ -56,6 +57,8 @@ func buildQueryParams(params map[string]interface{}) map[string]string { } } else if boolOk { queryParams[key] = strconv.FormatBool(*boolValue) + } else if intOk { + queryParams[key] = strconv.Itoa(*intValue) } else { panic("Unknown query flag type") } @@ -229,6 +232,7 @@ func initGet(kinds schema.KindCatalog) { for i, flag := range parentQueryFlags { parentQueryFlagValue[i] = kindCmd.Flags().String(flag, "", "Parent "+flag) } + //TODO: factorize with run.go for key, flag := range listFlags { var isFlagSet bool if flag.Type == "string" { @@ -237,6 +241,9 @@ func initGet(kinds schema.KindCatalog) { } else if flag.Type == "boolean" { isFlagSet = true listFlagValue[key] = kindCmd.Flags().Bool(flag.FlagName, false, "") + } else if flag.Type == "integer" { + isFlagSet = true + listFlagValue[key] = kindCmd.Flags().Int(flag.FlagName, 0, "") } if isFlagSet && flag.Required { kindCmd.MarkFlagRequired(flag.FlagName) diff --git a/cmd/printKind.go b/cmd/printCatalog.go similarity index 72% rename from cmd/printKind.go rename to cmd/printCatalog.go index 6c74426..72227f1 100644 --- a/cmd/printKind.go +++ b/cmd/printCatalog.go @@ -9,16 +9,17 @@ import ( "github.com/spf13/cobra" ) -func initPrintKind(kinds schema.KindCatalog) { +func initPrintCatalog(kinds schema.Catalog) { var prettyPrint *bool var printKind = &cobra.Command{ - Use: "printKind", - Short: "Print kind catalog used", - Long: ``, - Args: cobra.NoArgs, - Hidden: !utils.CdkDevMode(), + Use: "printCatalog", + Aliases: []string{"printKind"}, // for backward compatibility + Short: "Print catalog", + Long: ``, + Args: cobra.NoArgs, + Hidden: !utils.CdkDevMode(), Run: func(cmd *cobra.Command, args []string) { var payload []byte var err error diff --git a/cmd/root.go b/cmd/root.go index 2b97e16..9f0195d 100644 --- a/cmd/root.go +++ b/cmd/root.go @@ -81,9 +81,9 @@ func init() { initTemplate(catalog.Kind) initDelete(catalog.Kind, !*permissive) initApply(catalog.Kind, !*permissive) - initConsoleMkKind() - initGatewayMkKind() - initPrintKind(catalog.Kind) + intConsoleMakeCatalog() + initGatewayMakeCatalog() + initPrintCatalog(catalog) initSql(catalog.Kind) - initExecute(catalog.Execute) + initRun(catalog.Run) } diff --git a/cmd/run.go b/cmd/run.go new file mode 100644 index 0000000..29c2349 --- /dev/null +++ b/cmd/run.go @@ -0,0 +1,162 @@ +package cmd + +import ( + "encoding/json" + "fmt" + "github.com/conduktor/ctl/schema" + "github.com/spf13/cobra" + "os" +) + +const unprobableInt = -23871 + +var runCmd = &cobra.Command{ + Use: "run", + Short: "run an action", + Args: cobra.NoArgs, + Run: func(cmd *cobra.Command, args []string) { + // Root command does nothing + cmd.Help() + os.Exit(1) + }, +} + +func printExecuteResult(result []byte) { + var stringJson string + err := json.Unmarshal(result, &stringJson) + if err == nil { + fmt.Println(stringJson) + return + } + var jsonResult interface{} + err = json.Unmarshal(result, &jsonResult) + if err == nil { + jsonOutput, err := json.MarshalIndent(jsonResult, "", " ") + if err == nil { + fmt.Println(string(jsonOutput)) + return + } + } + if len(result) > 1 { + fmt.Println(string(result)) + } else { + fmt.Println("Ok") + } +} + +func initRun(runs schema.RunCatalog) { + rootCmd.AddCommand(runCmd) + + // Add all kinds to the 'get' command + for name, run := range runs { + args := cobra.MaximumNArgs(0) + pathFlags := run.PathParameter + pathFlagValues := make([]*string, len(pathFlags)) + queryFlags := run.QueryParameter + bodyFlags := run.BodyFields + queryFlagValues := make(map[string]interface{}, len(queryFlags)) + bodyFlagValues := make(map[string]interface{}, len(bodyFlags)) + subRunCmd := &cobra.Command{ + Use: name, + Short: run.Doc, + Args: args, + Aliases: buildAlias(name), + Run: func(cmd *cobra.Command, args []string) { + pathValues := make([]string, len(pathFlagValues)) + queryParams := buildQueryParams(queryFlagValues) + body := buildBodyParams(bodyFlagValues) + for i, v := range pathFlagValues { + pathValues[i] = *v + } + + var err error + + if len(bodyFlags) == 0 { + body = nil + } + var result []byte + if run.BackendType == schema.CONSOLE { + result, err = consoleApiClient().Run(run, pathValues, queryParams, body) + } else if run.BackendType == schema.GATEWAY { + result, err = gatewayApiClient().Run(run, pathValues, queryParams, body) + } else { + panic("Unknown backend type") + } + if err != nil { + fmt.Fprintf(os.Stderr, "Error fetching resources: %s\n", err) + return + } + printExecuteResult(result) + }, + } + for i, flag := range pathFlags { + pathFlagValues[i] = subRunCmd.Flags().String(flag, "", "Parent "+flag) + err := subRunCmd.MarkFlagRequired(flag) + if err != nil { + panic(err) + } + } + for key, flag := range queryFlags { + var isFlagSet bool + if flag.Type == "string" { + isFlagSet = true + queryFlagValues[key] = subRunCmd.Flags().String(flag.FlagName, "", "") + } else if flag.Type == "boolean" { + isFlagSet = true + queryFlagValues[key] = subRunCmd.Flags().Bool(flag.FlagName, false, "") + } else if flag.Type == "integer" { + isFlagSet = true + bodyFlagValues[key] = subRunCmd.Flags().Int(flag.FlagName, 0, "") + } + if isFlagSet && flag.Required { + err := subRunCmd.MarkFlagRequired(flag.FlagName) + if err != nil { + panic(err) + } + } + } + for key, flag := range bodyFlags { + var isFlagSet bool + if flag.Type == "string" { + isFlagSet = true + bodyFlagValues[key] = subRunCmd.Flags().String(flag.FlagName, "", "") + } else if flag.Type == "boolean" { + isFlagSet = true + bodyFlagValues[key] = subRunCmd.Flags().Bool(flag.FlagName, false, "") + } else if flag.Type == "integer" { + isFlagSet = true + bodyFlagValues[key] = subRunCmd.Flags().Int(flag.FlagName, unprobableInt, "") + } + if isFlagSet && flag.Required { + err := subRunCmd.MarkFlagRequired(flag.FlagName) + if err != nil { + panic(err) + } + } + } + + runCmd.AddCommand(subRunCmd) + } +} +func buildBodyParams(params map[string]interface{}) map[string]interface{} { + body := make(map[string]interface{}) + for key, value := range params { + if value != nil { + str, strOk := value.(*string) + intValue, intOk := value.(*int) + + if strOk { + if *str != "" { + body[key] = str + } + } else if intOk { + if *intValue != unprobableInt { + body[key] = intValue + } + } else { + body[key] = value + } + } + } + return body +} diff --git a/docker/initializer.json b/docker/initializer.json index 90a14b2..4d7aae8 100644 --- a/docker/initializer.json +++ b/docker/initializer.json @@ -27,7 +27,13 @@ }, "httpResponse": { "statusCode": 200, - "body": [{"metadata": {"name": "a"}}], + "body": [ + { + "metadata": { + "name": "a" + } + } + ], "headers": { "Content-Type": [ "application/json" @@ -45,7 +51,11 @@ }, "httpResponse": { "statusCode": 200, - "body": {"metadata": {"name": "a"}}, + "body": { + "metadata": { + "name": "a" + } + }, "headers": { "Content-Type": [ "application/json" @@ -241,7 +251,11 @@ }, "httpResponse": { "statusCode": 200, - "body": {"metadata": {"name": "a"}}, + "body": { + "metadata": { + "name": "a" + } + }, "headers": { "Content-Type": [ "application/json" @@ -254,7 +268,9 @@ "method": "POST", "path": "/api/public/sql/v1/execute", "queryStringParameters": { - "maxLine": ["100"] + "maxLine": [ + "100" + ] }, "headers": { "Authorization": "Bearer yo" @@ -263,7 +279,28 @@ }, "httpResponse": { "statusCode": 200, - "body": {"header": [], "row": []}, + "body": { + "header": [], + "row": [] + }, + "headers": { + "Content-Type": [ + "application/json" + ] + } + } + }, + { + "httpRequest": { + "method": "POST", + "path": "/api/public/partner-zone/v2/yo/generate-credentials", + "headers": { + "Authorization": "Bearer yo" + } + }, + "httpResponse": { + "statusCode": 200, + "body": "\"yolo_le_token\"", "headers": { "Content-Type": [ "application/json" @@ -271,5 +308,4 @@ } } } - ] \ No newline at end of file diff --git a/docker/initializerGw.json b/docker/initializerGw.json index f9a636a..8dfa34e 100644 --- a/docker/initializerGw.json +++ b/docker/initializerGw.json @@ -400,5 +400,28 @@ ] } } + }, + { + "httpRequest": { + "method": "POST", + "path": "/gateway/v2/token", + "headers": { + "Authorization": "Basic YWRtaW46Y29uZHVrdG9y" + }, + "body": { + "lifeTimeSeconds": 3600, + "username": "yo", + "vCluster": "foyer" + } + }, + "httpResponse": { + "statusCode": 200, + "body": "yolo_le_token", + "headers": { + "Content-Type": [ + "application/json" + ] + } + } } ] \ No newline at end of file diff --git a/schema/backend_type.go b/schema/backend_type.go new file mode 100644 index 0000000..4c2f2f1 --- /dev/null +++ b/schema/backend_type.go @@ -0,0 +1,8 @@ +package schema + +type BackendType int + +const ( + CONSOLE BackendType = iota + GATEWAY +) diff --git a/schema/catalog.go b/schema/catalog.go index 490064b..c9cddc1 100644 --- a/schema/catalog.go +++ b/schema/catalog.go @@ -1,69 +1,76 @@ package schema -import "encoding/json" +import ( + "encoding/json" +) type Catalog struct { - Kind KindCatalog - Execute ExecuteCatalog + Kind KindCatalog + Run RunCatalog } type KindCatalog = map[string]Kind -type ExecuteCatalog = map[string]Execute +type RunCatalog = map[string]Run func ConsoleDefaultCatalog() *Catalog { - return &Catalog{ - Kind: buildKindCatalogFromByteSchema[*ConsoleKindVersion](consoleDefaultByteSchema), - Execute: map[string]Execute{}, - } + return buildCatalogFromByteSchema[*ConsoleKindVersion](consoleDefaultByteSchema, CONSOLE) } func GatewayDefaultCatalog() *Catalog { - return &Catalog{ - Kind: buildKindCatalogFromByteSchema[*GatewayKindVersion](gatewayDefaultByteSchema), - Execute: map[string]Execute{}, - } + return buildCatalogFromByteSchema[*GatewayKindVersion](gatewayDefaultByteSchema, GATEWAY) } func (catalog *Catalog) Merge(other *Catalog) Catalog { result := Catalog{ - Kind: make(map[string]Kind), - Execute: make(map[string]Execute), + Kind: make(map[string]Kind), + Run: make(map[string]Run), } - for kindName, kind := range catalog.Kind { result.Kind[kindName] = kind } for kindName, kind := range other.Kind { result.Kind[kindName] = kind } - for executeName, execute := range catalog.Execute { - result.Execute[executeName] = execute + for runName, run := range catalog.Run { + result.Run[runName] = run } - for executeName, execute := range other.Execute { - result.Execute[executeName] = execute + for runName, run := range other.Run { + result.Run[runName] = run } return result } -func buildKindCatalogFromByteSchema[T KindVersion](byteSchema []byte) KindCatalog { - var jsonResult map[string]kindGeneric[T] +func buildCatalogFromByteSchema[T KindVersion](byteSchema []byte, backendType BackendType) *Catalog { + var jsonResult CatalogGeneric[T] err := json.Unmarshal(byteSchema, &jsonResult) if err != nil { panic(err) } - var result = make(map[string]Kind) - for kindName, kindGeneric := range jsonResult { + var result = Catalog{ + Kind: KindCatalog{}, + Run: RunCatalog{}, + } + for kindName, kindGeneric := range jsonResult.Kind { kind := Kind{ Versions: make(map[int]KindVersion), } for version, kindVersion := range kindGeneric.Versions { kind.Versions[version] = kindVersion } - result[kindName] = kind + result.Kind[kindName] = kind } - return result + for runName, run := range jsonResult.Run { + run.BackendType = backendType + result.Run[runName] = run + } + return &result } type kindGeneric[T KindVersion] struct { Versions map[int]T } + +type CatalogGeneric[T KindVersion] struct { + Kind map[string]kindGeneric[T] + Run RunCatalog +} diff --git a/schema/default_schema/console.json b/schema/default_schema/console.json index c0bec30..88bb870 100644 --- a/schema/default_schema/console.json +++ b/schema/default_schema/console.json @@ -1 +1 @@ -{"Alert":{"Versions":{"2":{"ListPath":"/public/monitoring/v2/cluster/{cluster}/alert","Name":"Alert","ParentPathParam":["cluster"],"ParentQueryParam":null,"ListQueryParamter":{"alertType":{"FlagName":"alert-type","Required":false,"Type":"string"},"connect":{"FlagName":"connect","Required":false,"Type":"string"},"connector":{"FlagName":"connector","Required":false,"Type":"string"},"consumerGroup":{"FlagName":"consumer-group","Required":false,"Type":"string"},"topic":{"FlagName":"topic","Required":false,"Type":"string"}},"ApplyExample":"apiVersion: v2\nkind: Alert\nmetadata:\n name: alert\n cluster: cluster\nspec:\n connectName: connect\n connectorName: connector\n threshold: 1\n operator: GreaterThan\n metric: FailedTaskCount\n type: KafkaConnectAlert\n","Order":15},"3":{"ListPath":"/public/monitoring/v3/alert","Name":"Alert","ParentPathParam":[],"ParentQueryParam":["appInstance","group","user"],"ListQueryParamter":{"alertType":{"FlagName":"alert-type","Required":false,"Type":"string"},"appInstance":{"FlagName":"application-instance","Required":false,"Type":"string"},"cluster":{"FlagName":"cluster","Required":false,"Type":"string"},"connect":{"FlagName":"connect","Required":false,"Type":"string"},"connector":{"FlagName":"connector","Required":false,"Type":"string"},"consumerGroup":{"FlagName":"consumer-group","Required":false,"Type":"string"},"group":{"FlagName":"group","Required":false,"Type":"string"},"topic":{"FlagName":"topic","Required":false,"Type":"string"},"user":{"FlagName":"user","Required":false,"Type":"string"}},"ApplyExample":"apiVersion: v3\nkind: Alert\nmetadata:\n name: alert\n appInstance: my-app\nspec:\n cluster: cluster\n connectName: connect\n connectorName: connector\n threshold: 1\n operator: GreaterThan\n metric: FailedTaskCount\n type: KafkaConnectAlert\n","Order":15}}},"Application":{"Versions":{"1":{"ListPath":"/public/self-serve/v1/application","Name":"Application","ParentPathParam":[],"ParentQueryParam":null,"ListQueryParamter":{},"ApplyExample":"apiVersion: v1\nkind: Application\nmetadata:\n name: my-application\nspec:\n title: My Application\n owner: me\n","Order":6}}},"ApplicationGroup":{"Versions":{"1":{"ListPath":"/public/self-serve/v1/application-group","Name":"ApplicationGroup","ParentPathParam":[],"ParentQueryParam":null,"ListQueryParamter":{},"ApplyExample":"apiVersion: v1\nkind: ApplicationGroup\nmetadata:\n application: clickstream-app\n name: clickstream-support\nspec:\n displayName: Support Clickstream\n description: |-\n Members of the Support Group are allowed:\n Read access on all the resources\n Can restart owned connectors\n Can reset offsets\n permissions:\n - appInstance: clickstream-app-dev\n patternType: LITERAL\n name: '*'\n permissions:\n - topicConsume\n - topicViewConfig\n resourceType: TOPIC\n members:\n - user1@company.org\n - user2@company.org\n externalGroups:\n - support\n","Order":9}}},"ApplicationInstance":{"Versions":{"1":{"ListPath":"/public/self-serve/v1/application-instance","Name":"ApplicationInstance","ParentPathParam":[],"ParentQueryParam":null,"ListQueryParamter":{"application":{"FlagName":"application","Required":false,"Type":"string"}},"ApplyExample":"apiVersion: v1\nkind: ApplicationInstance\nmetadata:\n name: my-app-instance-prod\n application: my-app\nspec:\n cluster: prod-cluster\n topicPolicyRef:\n - my-topic-policy\n resources:\n - type: TOPIC\n name: my-topic\n patternType: LITERAL\n - type: CONSUMER_GROUP\n name: my-consumer-group\n patternType: LITERAL\n serviceAccount: my-service-account\n defaultCatalogVisibility: PUBLIC\n","Order":7}}},"ApplicationInstancePermission":{"Versions":{"1":{"ListPath":"/public/self-serve/v1/application-instance-permission","Name":"ApplicationInstancePermission","ParentPathParam":[],"ParentQueryParam":null,"ListQueryParamter":{"filterByApplication":{"FlagName":"application","Required":false,"Type":"string"},"filterByApplicationInstance":{"FlagName":"application-instance","Required":false,"Type":"string"},"filterByGrantedTo":{"FlagName":"granted-to","Required":false,"Type":"string"}},"ApplyExample":"apiVersion: v1\nkind: ApplicationInstancePermission\nmetadata:\n application: test\n appInstance: test\n name: test\nspec:\n resource:\n type: TOPIC\n name: test\n patternType: LITERAL\n permission: READ\n grantedTo: test\n","Order":8}}},"Connector":{"Versions":{"2":{"ListPath":"/public/kafka/v2/cluster/{cluster}/connect/{connectCluster}/connector","Name":"Connector","ParentPathParam":["cluster","connectCluster"],"ParentQueryParam":null,"ListQueryParamter":{},"ApplyExample":"apiVersion: v2\nkind: Connector\nmetadata:\n name: my-connector\n cluster: my-cluster\n connectCluster: my-connect\n autoRestart:\n enabled: true\n frequencySeconds: 600\n description: My connector\nspec:\n config: {}\n","Order":13}}},"Group":{"Versions":{"2":{"ListPath":"/public/iam/v2/group","Name":"Group","ParentPathParam":[],"ParentQueryParam":null,"ListQueryParamter":{},"ApplyExample":"apiVersion: v2\nkind: Group\nmetadata:\n name: group\nspec:\n displayName: test\n description: description\n externalGroups:\n - test\n members:\n - user@conduktor.io\n membersFromExternalGroups: []\n permissions:\n - resourceType: TOPIC\n cluster: '*'\n name: test\n patternType: LITERAL\n permissions:\n - topicConsume\n - resourceType: TOPIC\n cluster: '*'\n name: test2\n patternType: PREFIXED\n permissions:\n - topicConsume\n","Order":1}}},"IndexedTopic":{"Versions":{"1":{"ListPath":"/public/sql/v1/cluster/{cluster}/indexed_topic","Name":"IndexedTopic","ParentPathParam":["cluster"],"ParentQueryParam":null,"ListQueryParamter":{},"ApplyExample":"apiVersion: v1\nkind: IndexedTopic\nmetadata:\n cluster: my-cluster\n name: my-topic\nspec:\n retentionTimeInSecond: 3600\n","Order":14}}},"KafkaCluster":{"Versions":{"2":{"ListPath":"/public/console/v2/kafka-cluster","Name":"KafkaCluster","ParentPathParam":[],"ParentQueryParam":null,"ListQueryParamter":{},"ApplyExample":"apiVersion: v2\nkind: KafkaCluster\nmetadata:\n name: my-kafka-cluster\n labels:\n env: prod\nspec:\n displayName: yo\n bootstrapServers: localhost:9092\n properties:\n sasl.jaas.config: org.apache.kafka.common.security.plain.PlainLoginModule required username=\"admin\" password=\"admin-secret\";\n color: '#FF0000'\n icon: icon\n schemaRegistry:\n url: https://my-schema-registry:8081\n security:\n username: admin\n password: admin-secret\n type: BasicAuth\n ignoreUntrustedCertificate: false\n type: ConfluentLike\n","Order":2}}},"KafkaConnectCluster":{"Versions":{"2":{"ListPath":"/public/console/v2/cluster/{cluster}/kafka-connect","Name":"KafkaConnectCluster","ParentPathParam":["cluster"],"ParentQueryParam":null,"ListQueryParamter":{},"ApplyExample":"apiVersion: v2\nkind: KafkaConnectCluster\nmetadata:\n name: connect-1\n cluster: my-cloud\n labels:\n user-labels: I am a user label\nspec:\n displayName: My kafka connect\n urls: http://localhost:8083\n headers:\n a: b\n c: d\n ignoreUntrustedCertificate: true\n security:\n username: user\n password: password\n type: BasicAuth\n","Order":3}}},"KsqlDBCluster":{"Versions":{"2":{"ListPath":"/public/console/v2/cluster/{cluster}/ksqldb","Name":"KsqlDBCluster","ParentPathParam":["cluster"],"ParentQueryParam":null,"ListQueryParamter":{},"ApplyExample":"apiVersion: v2\nkind: KsqlDBCluster\nmetadata:\n name: connect-1\n cluster: my-cloud\nspec:\n displayName: My kafka connect\n url: http://localhost:8083\n headers:\n a: b\n c: d\n ignoreUntrustedCertificate: true\n security:\n username: user\n password: password\n type: BasicAuth\n","Order":4}}},"ServiceAccount":{"Versions":{"1":{"ListPath":"/public/self-serve/v1/cluster/{cluster}/service-account","Name":"ServiceAccount","ParentPathParam":["cluster"],"ParentQueryParam":null,"ListQueryParamter":{},"ApplyExample":"apiVersion: v1\nkind: ServiceAccount\nmetadata:\n appInstance: my-app-instance-dev\n cluster: my-kafka-cluster\n labels:\n conduktor.io/application: application-a\n conduktor.io/application-instance: dev\n user-labels: I am a user label\n name: sa-clicko-dev\nspec:\n authorization:\n acls:\n - type: TOPIC\n name: click.\n patternType: Prefixed\n operations:\n - Write\n host: '*'\n permission: Allow\n","Order":10}}},"Subject":{"Versions":{"2":{"ListPath":"/public/kafka/v2/cluster/{cluster}/subject","Name":"Subject","ParentPathParam":["cluster"],"ParentQueryParam":null,"ListQueryParamter":{},"ApplyExample":"apiVersion: v2\nkind: Subject\nmetadata:\n name: my-subject\n cluster: my-cluster\n labels:\n conduktor.io/application: application-a\n conduktor.io/application-instance: staging\nspec:\n format: AVRO\n compatibility: BACKWARD_TRANSITIVE\n schema: '{\"type\": \"long\"}'\n","Order":12}}},"Topic":{"Versions":{"2":{"ListPath":"/public/kafka/v2/cluster/{cluster}/topic","Name":"Topic","ParentPathParam":["cluster"],"ParentQueryParam":null,"ListQueryParamter":{},"ApplyExample":"apiVersion: v2\nkind: Topic\nmetadata:\n name: my-topic\n cluster: my-cluster\n labels:\n conduktor.io/application: application-a\n conduktor.io/application-instance: staging\n user-labels: I am a user label\n catalogVisibility: PUBLIC\n descriptionIsEditable: true\n description: This is a topic description\n sqlStorage:\n retentionTimeInSecond: 42\nspec:\n partitions: 1\n replicationFactor: 1\n configs:\n cleanup.policy: delete\n retention.ms: '86400000'\n","Order":11}}},"TopicPolicy":{"Versions":{"1":{"ListPath":"/public/self-serve/v1/topic-policy","Name":"TopicPolicy","ParentPathParam":[],"ParentQueryParam":null,"ListQueryParamter":{"app-instance":{"FlagName":"application-instance","Required":false,"Type":"string"}},"ApplyExample":"apiVersion: v1\nkind: TopicPolicy\nmetadata:\n name: my-app-instance-prod\nspec:\n policies:\n my-policy:\n constraint: OneOf\n optional: true\n values:\n - value1\n - value2\n","Order":5}}},"User":{"Versions":{"2":{"ListPath":"/public/iam/v2/user","Name":"User","ParentPathParam":[],"ParentQueryParam":null,"ListQueryParamter":{},"ApplyExample":"apiVersion: v2\nkind: User\nmetadata:\n name: user@conduktor.io\nspec:\n firstName: description\n lastName: test\n permissions: []\n","Order":0}}}} \ No newline at end of file +{"Kind":{"Alert":{"Versions":{"2":{"ListPath":"/public/monitoring/v2/cluster/{cluster}/alert","Name":"Alert","ParentPathParam":["cluster"],"ParentQueryParam":null,"ListQueryParameter":{"alertType":{"FlagName":"alert-type","Required":false,"Type":"string"},"connect":{"FlagName":"connect","Required":false,"Type":"string"},"connector":{"FlagName":"connector","Required":false,"Type":"string"},"consumerGroup":{"FlagName":"consumer-group","Required":false,"Type":"string"},"topic":{"FlagName":"topic","Required":false,"Type":"string"}},"ApplyExample":"apiVersion: v2\nkind: Alert\nmetadata:\n name: alert\n cluster: cluster\nspec:\n connectName: connect\n connectorName: connector\n threshold: 1\n operator: GreaterThan\n metric: FailedTaskCount\n type: KafkaConnectAlert\n","Order":15},"3":{"ListPath":"/public/monitoring/v3/alert","Name":"Alert","ParentPathParam":[],"ParentQueryParam":["appInstance","group","user"],"ListQueryParameter":{"alertType":{"FlagName":"alert-type","Required":false,"Type":"string"},"appInstance":{"FlagName":"application-instance","Required":false,"Type":"string"},"cluster":{"FlagName":"cluster","Required":false,"Type":"string"},"connect":{"FlagName":"connect","Required":false,"Type":"string"},"connector":{"FlagName":"connector","Required":false,"Type":"string"},"consumerGroup":{"FlagName":"consumer-group","Required":false,"Type":"string"},"group":{"FlagName":"group","Required":false,"Type":"string"},"topic":{"FlagName":"topic","Required":false,"Type":"string"},"user":{"FlagName":"user","Required":false,"Type":"string"}},"ApplyExample":"apiVersion: v3\nkind: Alert\nmetadata:\n name: alert\n appInstance: my-app\nspec:\n cluster: cluster\n connectName: connect\n connectorName: connector\n threshold: 1\n operator: GreaterThan\n metric: FailedTaskCount\n destination:\n channel: test\n type: Slack\n type: KafkaConnectAlert\n","Order":15}}},"Application":{"Versions":{"1":{"ListPath":"/public/self-serve/v1/application","Name":"Application","ParentPathParam":[],"ParentQueryParam":null,"ListQueryParameter":{},"ApplyExample":"apiVersion: v1\nkind: Application\nmetadata:\n name: my-application\nspec:\n title: My Application\n owner: me\n","Order":6}}},"ApplicationGroup":{"Versions":{"1":{"ListPath":"/public/self-serve/v1/application-group","Name":"ApplicationGroup","ParentPathParam":[],"ParentQueryParam":null,"ListQueryParameter":{"application":{"FlagName":"application","Required":false,"Type":"string"}},"ApplyExample":"apiVersion: v1\nkind: ApplicationGroup\nmetadata:\n application: clickstream-app\n name: clickstream-support\nspec:\n displayName: Support Clickstream\n description: |-\n Members of the Support Group are allowed:\n Read access on all the resources\n Can restart owned connectors\n Can reset offsets\n permissions:\n - appInstance: clickstream-app-dev\n patternType: LITERAL\n name: '*'\n permissions:\n - topicConsume\n - topicViewConfig\n resourceType: TOPIC\n members:\n - user1@company.org\n - user2@company.org\n externalGroups:\n - support\n","Order":9}}},"ApplicationInstance":{"Versions":{"1":{"ListPath":"/public/self-serve/v1/application-instance","Name":"ApplicationInstance","ParentPathParam":[],"ParentQueryParam":null,"ListQueryParameter":{"application":{"FlagName":"application","Required":false,"Type":"string"}},"ApplyExample":"apiVersion: v1\nkind: ApplicationInstance\nmetadata:\n name: my-app-instance-prod\n application: my-app\nspec:\n cluster: prod-cluster\n topicPolicyRef:\n - my-topic-policy\n resources:\n - type: TOPIC\n name: my-topic\n patternType: LITERAL\n - type: CONSUMER_GROUP\n name: my-consumer-group\n patternType: LITERAL\n serviceAccount: my-service-account\n defaultCatalogVisibility: PUBLIC\n","Order":7}}},"ApplicationInstancePermission":{"Versions":{"1":{"ListPath":"/public/self-serve/v1/application-instance-permission","Name":"ApplicationInstancePermission","ParentPathParam":[],"ParentQueryParam":null,"ListQueryParameter":{"filterByApplication":{"FlagName":"application","Required":false,"Type":"string"},"filterByApplicationInstance":{"FlagName":"application-instance","Required":false,"Type":"string"},"filterByGrantedTo":{"FlagName":"granted-to","Required":false,"Type":"string"}},"ApplyExample":"apiVersion: v1\nkind: ApplicationInstancePermission\nmetadata:\n application: test\n appInstance: test\n name: test\nspec:\n resource:\n type: TOPIC\n name: test\n patternType: LITERAL\n permission: READ\n grantedTo: test\n","Order":8}}},"Connector":{"Versions":{"2":{"ListPath":"/public/kafka/v2/cluster/{cluster}/connect/{connectCluster}/connector","Name":"Connector","ParentPathParam":["cluster","connectCluster"],"ParentQueryParam":null,"ListQueryParameter":{},"ApplyExample":"apiVersion: v2\nkind: Connector\nmetadata:\n name: my-connector\n cluster: my-cluster\n connectCluster: my-connect\n autoRestart:\n enabled: true\n frequencySeconds: 600\n description: My connector\nspec:\n config: {}\n","Order":13}}},"Group":{"Versions":{"2":{"ListPath":"/public/iam/v2/group","Name":"Group","ParentPathParam":[],"ParentQueryParam":null,"ListQueryParameter":{},"ApplyExample":"apiVersion: v2\nkind: Group\nmetadata:\n name: group\nspec:\n displayName: test\n description: description\n externalGroups:\n - test\n members:\n - user@conduktor.io\n membersFromExternalGroups: []\n permissions:\n - resourceType: TOPIC\n cluster: '*'\n name: test\n patternType: LITERAL\n permissions:\n - topicConsume\n - resourceType: TOPIC\n cluster: '*'\n name: test2\n patternType: PREFIXED\n permissions:\n - topicConsume\n","Order":1}}},"IndexedTopic":{"Versions":{"1":{"ListPath":"/public/sql/v1/cluster/{cluster}/indexed_topic","Name":"IndexedTopic","ParentPathParam":["cluster"],"ParentQueryParam":null,"ListQueryParameter":{},"ApplyExample":"apiVersion: v1\nkind: IndexedTopic\nmetadata:\n cluster: my-cluster\n name: my-topic\nspec:\n retentionTimeInSecond: 3600\n","Order":14}}},"KafkaCluster":{"Versions":{"2":{"ListPath":"/public/console/v2/kafka-cluster","Name":"KafkaCluster","ParentPathParam":[],"ParentQueryParam":null,"ListQueryParameter":{},"ApplyExample":"apiVersion: v2\nkind: KafkaCluster\nmetadata:\n name: my-kafka-cluster\n labels:\n env: prod\nspec:\n displayName: yo\n bootstrapServers: localhost:9092\n properties:\n sasl.jaas.config: org.apache.kafka.common.security.plain.PlainLoginModule required username=\"admin\" password=\"admin-secret\";\n color: '#FF0000'\n icon: icon\n schemaRegistry:\n url: https://my-schema-registry:8081\n security:\n username: admin\n password: admin-secret\n type: BasicAuth\n ignoreUntrustedCertificate: false\n type: ConfluentLike\n","Order":2}}},"KafkaConnectCluster":{"Versions":{"2":{"ListPath":"/public/console/v2/cluster/{cluster}/kafka-connect","Name":"KafkaConnectCluster","ParentPathParam":["cluster"],"ParentQueryParam":null,"ListQueryParameter":{},"ApplyExample":"apiVersion: v2\nkind: KafkaConnectCluster\nmetadata:\n name: connect-1\n cluster: my-cloud\n labels:\n user-labels: I am a user label\nspec:\n displayName: My kafka connect\n urls: http://localhost:8083\n headers:\n a: b\n c: d\n ignoreUntrustedCertificate: true\n security:\n username: user\n password: password\n type: BasicAuth\n","Order":3}}},"KsqlDBCluster":{"Versions":{"2":{"ListPath":"/public/console/v2/cluster/{cluster}/ksqldb","Name":"KsqlDBCluster","ParentPathParam":["cluster"],"ParentQueryParam":null,"ListQueryParameter":{},"ApplyExample":"apiVersion: v2\nkind: KsqlDBCluster\nmetadata:\n name: connect-1\n cluster: my-cloud\nspec:\n displayName: My kafka connect\n url: http://localhost:8083\n headers:\n a: b\n c: d\n ignoreUntrustedCertificate: true\n security:\n username: user\n password: password\n type: BasicAuth\n","Order":4}}},"PartnerZone":{"Versions":{"2":{"ListPath":"/public/console/v2/partner-zone","Name":"PartnerZone","ParentPathParam":[],"ParentQueryParam":null,"ListQueryParameter":{},"ApplyExample":"apiVersion: v2\nkind: PartnerZone\nmetadata:\n name: john-partner-zone\nspec:\n cluster: my-cluster\n displayName: John's partner zone\n url: http://conduktor.io\n serviceAccount: johndoe\n topics:\n - name: topic-a\n backingTopic: kafka-topic-a\n permission: WRITE\n partner:\n name: John Doe\n role: Data analyst\n email: johndoe@company.io\n phone: 07827 837 177\n","Order":16}}},"ServiceAccount":{"Versions":{"1":{"ListPath":"/public/self-serve/v1/cluster/{cluster}/service-account","Name":"ServiceAccount","ParentPathParam":["cluster"],"ParentQueryParam":null,"ListQueryParameter":{},"ApplyExample":"apiVersion: v1\nkind: ServiceAccount\nmetadata:\n appInstance: my-app-instance-dev\n cluster: my-kafka-cluster\n labels:\n conduktor.io/application: application-a\n conduktor.io/application-instance: dev\n user-labels: I am a user label\n name: sa-clicko-dev\nspec:\n authorization:\n type: KAFKA_ACL\n acls:\n - type: TOPIC\n name: click.\n patternType: PREFIXED\n operations:\n - Write\n host: '*'\n permission: Allow\n","Order":10}}},"Subject":{"Versions":{"2":{"ListPath":"/public/kafka/v2/cluster/{cluster}/subject","Name":"Subject","ParentPathParam":["cluster"],"ParentQueryParam":null,"ListQueryParameter":{},"ApplyExample":"apiVersion: v2\nkind: Subject\nmetadata:\n name: my-subject\n cluster: my-cluster\n labels:\n conduktor.io/application: application-a\n conduktor.io/application-instance: staging\nspec:\n format: AVRO\n compatibility: BACKWARD_TRANSITIVE\n schema: '{\"type\": \"long\"}'\n","Order":12}}},"Topic":{"Versions":{"2":{"ListPath":"/public/kafka/v2/cluster/{cluster}/topic","Name":"Topic","ParentPathParam":["cluster"],"ParentQueryParam":null,"ListQueryParameter":{},"ApplyExample":"apiVersion: v2\nkind: Topic\nmetadata:\n name: my-topic\n cluster: my-cluster\n labels:\n conduktor.io/application: application-a\n conduktor.io/application-instance: staging\n user-labels: I am a user label\n catalogVisibility: PUBLIC\n descriptionIsEditable: true\n description: This is a topic description\n sqlStorage:\n retentionTimeInSecond: 42\nspec:\n partitions: 1\n replicationFactor: 1\n configs:\n cleanup.policy: delete\n retention.ms: '86400000'\n","Order":11}}},"TopicPolicy":{"Versions":{"1":{"ListPath":"/public/self-serve/v1/topic-policy","Name":"TopicPolicy","ParentPathParam":[],"ParentQueryParam":null,"ListQueryParameter":{"app-instance":{"FlagName":"application-instance","Required":false,"Type":"string"}},"ApplyExample":"apiVersion: v1\nkind: TopicPolicy\nmetadata:\n name: my-app-instance-prod\nspec:\n policies:\n my-policy:\n constraint: OneOf\n optional: true\n values:\n - value1\n - value2\n","Order":5}}},"User":{"Versions":{"2":{"ListPath":"/public/iam/v2/user","Name":"User","ParentPathParam":[],"ParentQueryParam":null,"ListQueryParameter":{},"ApplyExample":"apiVersion: v2\nkind: User\nmetadata:\n name: user@conduktor.io\nspec:\n firstName: description\n lastName: test\n permissions: []\n","Order":0}}}},"Run":{"partnerZoneGenerateCredentials":{"Path":"/public/partner-zone/v2/{partner-zone-name}/generate-credentials","Name":"partnerZoneGenerateCredentials","Doc":"generate a token for a partner zone service account","QueryParameter":{},"PathParameter":["partner-zone-name"],"BodyFields":{},"Method":"POST"}}} \ No newline at end of file diff --git a/schema/default_schema/gateway.json b/schema/default_schema/gateway.json index aa6c557..be09e0c 100644 --- a/schema/default_schema/gateway.json +++ b/schema/default_schema/gateway.json @@ -1 +1 @@ -{"AliasTopic":{"Versions":{"2":{"ListPath":"/gateway/v2/alias-topic","Name":"AliasTopic","ParentPathParam":[],"ParentQueryParam":null,"ListQueryParameter":{"name":{"FlagName":"name","Required":false,"Type":"string"},"showDefaults":{"FlagName":"show-defaults","Required":false,"Type":"boolean"},"vcluster":{"FlagName":"vcluster","Required":false,"Type":"string"}},"GetAvailable":false,"ApplyExample":"kind: AliasTopic\napiVersion: gateway/v2\nmetadata:\n name: name1\n vCluster: vCluster1\nspec:\n physicalName: physicalName1\n","Order":8}}},"ConcentrationRule":{"Versions":{"2":{"ListPath":"/gateway/v2/concentration-rule","Name":"ConcentrationRule","ParentPathParam":[],"ParentQueryParam":null,"ListQueryParameter":{"name":{"FlagName":"name","Required":false,"Type":"string"},"showDefaults":{"FlagName":"show-defaults","Required":false,"Type":"boolean"},"vcluster":{"FlagName":"vcluster","Required":false,"Type":"string"}},"GetAvailable":false,"ApplyExample":"kind: ConcentrationRule\napiVersion: gateway/v2\nmetadata:\n name: concentrationRule1\n vCluster: vCluster1\nspec:\n pattern: topic.*\n physicalTopics:\n delete: topic\n compact: compact_topic\n deleteCompact: compact_delete_topic\n autoManaged: false\n offsetCorrectness: false\n","Order":9}}},"GatewayGroup":{"Versions":{"2":{"ListPath":"/gateway/v2/group","Name":"GatewayGroup","ParentPathParam":[],"ParentQueryParam":null,"ListQueryParameter":{"showDefaults":{"FlagName":"show-defaults","Required":false,"Type":"boolean"}},"GetAvailable":true,"ApplyExample":"kind: GatewayGroup\napiVersion: gateway/v2\nmetadata:\n name: group1\nspec:\n members:\n - vCluster: vCluster1\n name: serviceAccount1\n - vCluster: vCluster2\n name: serviceAccount2\n - vCluster: vCluster3\n name: serviceAccount3\n externalGroups:\n - GROUP_READER\n - GROUP_WRITER\n","Order":11}}},"GatewayServiceAccount":{"Versions":{"2":{"ListPath":"/gateway/v2/service-account","Name":"GatewayServiceAccount","ParentPathParam":[],"ParentQueryParam":null,"ListQueryParameter":{"name":{"FlagName":"name","Required":false,"Type":"string"},"showDefaults":{"FlagName":"show-defaults","Required":false,"Type":"boolean"},"type":{"FlagName":"type","Required":false,"Type":"string"},"vcluster":{"FlagName":"vcluster","Required":false,"Type":"string"}},"GetAvailable":false,"ApplyExample":"kind: GatewayServiceAccount\napiVersion: gateway/v2\nmetadata:\n name: user1\n vCluster: vcluster1\nspec:\n type: EXTERNAL\n externalNames:\n - externalName\n","Order":10}}},"Interceptor":{"Versions":{"2":{"ListPath":"/gateway/v2/interceptor","Name":"Interceptor","ParentPathParam":[],"ParentQueryParam":null,"ListQueryParameter":{"global":{"FlagName":"global","Required":false,"Type":"boolean"},"group":{"FlagName":"group","Required":false,"Type":"string"},"name":{"FlagName":"name","Required":false,"Type":"string"},"username":{"FlagName":"username","Required":false,"Type":"string"},"vcluster":{"FlagName":"vcluster","Required":false,"Type":"string"}},"GetAvailable":false,"ApplyExample":"kind: Interceptor\napiVersion: gateway/v2\nmetadata:\n name: yellow_cars_filter\n scope:\n vCluster: vCluster1\nspec:\n comment: Filter yellow cars\n pluginClass: io.conduktor.gateway.interceptor.VirtualSqlTopicPlugin\n priority: 1\n config:\n virtualTopic: yellow_cars\n statement: SELECT '$.type' as type, '$.price' as price FROM cars WHERE '$.color' = 'yellow'\n","Order":12}}},"VirtualCluster":{"Versions":{"2":{"ListPath":"/gateway/v2/virtual-cluster","Name":"VirtualCluster","ParentPathParam":[],"ParentQueryParam":null,"ListQueryParameter":{},"GetAvailable":true,"ApplyExample":"kind: VirtualCluster\napiVersion: gateway/v2\nmetadata:\n name: vcluster1\nspec:\n aclEnabled: false\n superUsers:\n - username1\n - username2\n type: Standard\n","Order":7}}}} \ No newline at end of file +{"Kind":{"AliasTopic":{"Versions":{"2":{"ListPath":"/gateway/v2/alias-topic","Name":"AliasTopic","ParentPathParam":[],"ParentQueryParam":null,"ListQueryParameter":{"name":{"FlagName":"name","Required":false,"Type":"string"},"showDefaults":{"FlagName":"show-defaults","Required":false,"Type":"boolean"},"vcluster":{"FlagName":"vcluster","Required":false,"Type":"string"}},"GetAvailable":false,"ApplyExample":"kind: AliasTopic\napiVersion: gateway/v2\nmetadata:\n name: name1\n vCluster: vCluster1\nspec:\n physicalName: physicalName1\n","Order":8}}},"ConcentrationRule":{"Versions":{"2":{"ListPath":"/gateway/v2/concentration-rule","Name":"ConcentrationRule","ParentPathParam":[],"ParentQueryParam":null,"ListQueryParameter":{"name":{"FlagName":"name","Required":false,"Type":"string"},"showDefaults":{"FlagName":"show-defaults","Required":false,"Type":"boolean"},"vcluster":{"FlagName":"vcluster","Required":false,"Type":"string"}},"GetAvailable":false,"ApplyExample":"kind: ConcentrationRule\napiVersion: gateway/v2\nmetadata:\n name: concentrationRule1\n vCluster: vCluster1\nspec:\n pattern: topic.*\n physicalTopics:\n delete: topic\n compact: compact_topic\n deleteCompact: compact_delete_topic\n autoManaged: false\n offsetCorrectness: false\n","Order":9}}},"GatewayGroup":{"Versions":{"2":{"ListPath":"/gateway/v2/group","Name":"GatewayGroup","ParentPathParam":[],"ParentQueryParam":null,"ListQueryParameter":{"showDefaults":{"FlagName":"show-defaults","Required":false,"Type":"boolean"}},"GetAvailable":true,"ApplyExample":"kind: GatewayGroup\napiVersion: gateway/v2\nmetadata:\n name: group1\nspec:\n members:\n - vCluster: vCluster1\n name: serviceAccount1\n - vCluster: vCluster2\n name: serviceAccount2\n - vCluster: vCluster3\n name: serviceAccount3\n externalGroups:\n - GROUP_READER\n - GROUP_WRITER\n","Order":11}}},"GatewayServiceAccount":{"Versions":{"2":{"ListPath":"/gateway/v2/service-account","Name":"GatewayServiceAccount","ParentPathParam":[],"ParentQueryParam":null,"ListQueryParameter":{"name":{"FlagName":"name","Required":false,"Type":"string"},"showDefaults":{"FlagName":"show-defaults","Required":false,"Type":"boolean"},"type":{"FlagName":"type","Required":false,"Type":"string"},"vcluster":{"FlagName":"vcluster","Required":false,"Type":"string"}},"GetAvailable":false,"ApplyExample":"kind: GatewayServiceAccount\napiVersion: gateway/v2\nmetadata:\n name: user1\n vCluster: vcluster1\nspec:\n type: EXTERNAL\n externalNames:\n - externalName\n","Order":10}}},"Interceptor":{"Versions":{"2":{"ListPath":"/gateway/v2/interceptor","Name":"Interceptor","ParentPathParam":[],"ParentQueryParam":null,"ListQueryParameter":{"global":{"FlagName":"global","Required":false,"Type":"boolean"},"group":{"FlagName":"group","Required":false,"Type":"string"},"name":{"FlagName":"name","Required":false,"Type":"string"},"username":{"FlagName":"username","Required":false,"Type":"string"},"vcluster":{"FlagName":"vcluster","Required":false,"Type":"string"}},"GetAvailable":false,"ApplyExample":"kind: Interceptor\napiVersion: gateway/v2\nmetadata:\n name: yellow_cars_filter\n scope:\n vCluster: vCluster1\nspec:\n comment: Filter yellow cars\n pluginClass: io.conduktor.gateway.interceptor.VirtualSqlTopicPlugin\n priority: 1\n config:\n virtualTopic: yellow_cars\n statement: SELECT '$.type' as type, '$.price' as price FROM cars WHERE '$.color' = 'yellow'\n","Order":12}}},"VirtualCluster":{"Versions":{"2":{"ListPath":"/gateway/v2/virtual-cluster","Name":"VirtualCluster","ParentPathParam":[],"ParentQueryParam":null,"ListQueryParameter":{},"GetAvailable":true,"ApplyExample":"kind: VirtualCluster\napiVersion: gateway/v2\nmetadata:\n name: vcluster1\nspec:\n aclEnabled: false\n superUsers:\n - username1\n - username2\n type: Standard\n bootstrapServers: kafka:9092\n clientProperties:\n security.protocol: SASL_PLAINTEXT\n sasl.mechanism: PLAIN\n sasl.jaas.config: org.apache.kafka.common.security.plain.PlainLoginModule required username={{username}} password={{password}};\n","Order":7}}}},"Run":{"generateServiceAccountToken":{"Path":"/gateway/v2/token","Name":"generateServiceAccountToken","Doc":"Generate a token for a service account on a virtual cluster","QueryParameter":{},"PathParameter":[],"BodyFields":{"lifeTimeSeconds":{"FlagName":"life-time-seconds","Required":true,"Type":"integer"},"username":{"FlagName":"username","Required":true,"Type":"string"},"vCluster":{"FlagName":"v-cluster","Required":false,"Type":"string"}},"Method":"POST"}}} \ No newline at end of file diff --git a/schema/execute.go b/schema/execute.go deleted file mode 100644 index 3288ebd..0000000 --- a/schema/execute.go +++ /dev/null @@ -1,67 +0,0 @@ -package schema - -import ( - "fmt" - "strings" -) - -type Execute interface { - GetName() string - GetDoc() string - GetPath() string - BuildPath(pathValue []string) string - GetQueryParameter() map[string]QueryParameterOption - GetPathParameter() []string - GetBodyFields() map[string]BodyField - GetMethod() string -} - -type ConsoleExecute struct { - Path string - Name string - Doc string - ListQueryParameter map[string]QueryParameterOption - PathParameter []string - BodyFields map[string]BodyField - Method string -} - -func (c *ConsoleExecute) GetBodyFields() map[string]BodyField { - return c.BodyFields -} - -func (c *ConsoleExecute) GetName() string { - return c.Name -} - -func (c *ConsoleExecute) GetPath() string { - return c.Path -} - -func (c *ConsoleExecute) GetDoc() string { - return c.Doc -} - -func (c *ConsoleExecute) GetMethod() string { - return c.Method -} - -// TODO: test -func (c *ConsoleExecute) BuildPath(pathValue []string) string { - path := c.GetPath() - if len(pathValue) != len(c.PathParameter) { - panic(fmt.Sprintf("BuildPath: pathValue lentgth (%d) does not match execution path parameter length (%d) for %s", len(pathValue), len(c.PathParameter), c.Name)) - } - for i, pathValue := range pathValue { - path = strings.Replace(path, fmt.Sprintf("{%s}", c.PathParameter[i]), pathValue, 1) - } - return path -} - -func (c *ConsoleExecute) GetQueryParameter() map[string]QueryParameterOption { - return c.ListQueryParameter -} - -func (c *ConsoleExecute) GetPathParameter() []string { - return c.PathParameter -} diff --git a/schema/openapi_parser.go b/schema/openapi_parser.go index 6d8116e..c9eaae5 100644 --- a/schema/openapi_parser.go +++ b/schema/openapi_parser.go @@ -64,22 +64,22 @@ func getKinds[T KindVersion](s *OpenApiParser, strict bool, buildKindVersion fun return result, nil } -func (s *OpenApiParser) getExecutes() (ExecuteCatalog, error) { - result := make(ExecuteCatalog, 0) +func (s *OpenApiParser) getRuns(backendType BackendType) (RunCatalog, error) { + result := make(RunCatalog, 0) for path := s.doc.Model.Paths.PathItems.First(); path != nil; path = path.Next() { - err := handleExecuteOperation(path.Key(), path.Value().Get, resty.MethodGet, result) + err := handleExecuteOperation(backendType, path.Key(), path.Value().Get, resty.MethodGet, result) if err != nil { return nil, err } - err = handleExecuteOperation(path.Key(), path.Value().Post, resty.MethodPost, result) + err = handleExecuteOperation(backendType, path.Key(), path.Value().Post, resty.MethodPost, result) if err != nil { return nil, err } - err = handleExecuteOperation(path.Key(), path.Value().Put, resty.MethodPut, result) + err = handleExecuteOperation(backendType, path.Key(), path.Value().Put, resty.MethodPut, result) if err != nil { return nil, err } - err = handleExecuteOperation(path.Key(), path.Value().Delete, resty.MethodDelete, result) + err = handleExecuteOperation(backendType, path.Key(), path.Value().Delete, resty.MethodDelete, result) if err != nil { return nil, err } @@ -87,40 +87,41 @@ func (s *OpenApiParser) getExecutes() (ExecuteCatalog, error) { return result, nil } -func handleExecuteOperation(path string, operation *v3high.Operation, method string, result ExecuteCatalog) error { +func handleExecuteOperation(backendType BackendType, path string, operation *v3high.Operation, method string, result RunCatalog) error { if operation == nil { return nil } - nameYaml, present := operation.Extensions.Get("x-cdk-execute-action-name") + nameYaml, present := operation.Extensions.Get("x-cdk-run-name") if !present { return nil } name := nameYaml.Value - docYaml, docPresent := operation.Extensions.Get("x-cdk-execute-action-doc") + docYaml, docPresent := operation.Extensions.Get("x-cdk-run-doc") var doc string if docPresent { doc = docYaml.Value } else { doc = "" } - execute := ConsoleExecute{ - Path: path, - Name: name, - Doc: doc, - ListQueryParameter: make(map[string]QueryParameterOption, len(operation.Parameters)), - PathParameter: make([]string, 0, len(operation.Parameters)), - Method: method, + run := Run{ + BackendType: backendType, + Path: path, + Name: name, + Doc: doc, + QueryParameter: make(map[string]QueryParameterOption, len(operation.Parameters)), + PathParameter: make([]string, 0, len(operation.Parameters)), + Method: method, } for _, parameter := range operation.Parameters { if parameter.In == "path" && *parameter.Required { - execute.PathParameter = append(execute.PathParameter, parameter.Name) + run.PathParameter = append(run.PathParameter, parameter.Name) } - if parameter.In == "query" && parameter.Name != "dryMode" { + if parameter.In == "query" { schemaTypes := parameter.Schema.Schema().Type if len(schemaTypes) == 1 { queryName := parameter.Name - execute.ListQueryParameter[queryName] = QueryParameterOption{ + run.QueryParameter[queryName] = QueryParameterOption{ FlagName: computeFlagName(queryName), Required: *parameter.Required, Type: schemaTypes[0], @@ -128,8 +129,8 @@ func handleExecuteOperation(path string, operation *v3high.Operation, method str } } } - execute.BodyFields = computeBodyFields(operation.RequestBody) - result[name] = &execute + run.BodyFields = computeBodyFields(operation.RequestBody) + result[name] = run return nil } @@ -146,7 +147,7 @@ func computeBodyFields(body *v3high.RequestBody) map[string]BodyField { value := propertiesPair.Value() if value != nil && value.Schema() != nil { valueType := value.Schema().Type[0] - if valueType == "string" || valueType == "boolean" { + if valueType == "string" || valueType == "boolean" || valueType == "integer" { result[key] = BodyField{ FlagName: computeFlagName(key), Type: valueType, @@ -164,13 +165,13 @@ func (s *OpenApiParser) GetConsoleCatalog(strict bool) (*Catalog, error) { if err != nil { return nil, err } - execute, err := s.getExecutes() + runs, err := s.getRuns(CONSOLE) if err != nil { return nil, err } return &Catalog{ - Kind: kinds, - Execute: execute, + Kind: kinds, + Run: runs, }, nil } @@ -186,12 +187,16 @@ func (s *OpenApiParser) GetGatewayCatalog(strict bool) (*Catalog, error) { kinds, err := s.GetGatewayKinds(strict) if err != nil { return nil, err - } else { - return &Catalog{ - Kind: kinds, - Execute: map[string]Execute{}, - }, nil } + runs, err := s.getRuns(GATEWAY) + if err != nil { + return nil, err + } + + return &Catalog{ + Kind: kinds, + Run: runs, + }, nil } func buildConsoleKindVersion(s *OpenApiParser, path, kind string, order int, put *v3high.Operation, get *v3high.Operation, strict bool) (*ConsoleKindVersion, error) { diff --git a/schema/openapi_parser_console_test.go b/schema/openapi_parser_console_test.go index 83f4978..81a4683 100644 --- a/schema/openapi_parser_console_test.go +++ b/schema/openapi_parser_console_test.go @@ -361,7 +361,7 @@ func TestKindNotRequiredMetadataField(t *testing.T) { func TestGetExecutes(t *testing.T) { t.Run("gets execute endpoint from schema", func(t *testing.T) { - schemaContent, err := os.ReadFile("testdata/execute.yaml") + schemaContent, err := os.ReadFile("testdata/console_run.yaml") if err != nil { t.Fatalf("failed reading file: %s", err) } @@ -371,27 +371,29 @@ func TestGetExecutes(t *testing.T) { t.Fatalf("failed creating new schema: %s", err) } - result, err := schema.getExecutes() + result, err := schema.getRuns(CONSOLE) if err != nil { t.Fatalf("failed getting execute: %s", err) } - expected := ExecuteCatalog{ - "partnerZoneGenerateCredentials": &ConsoleExecute{ - Path: "/public/partner-zone/v2/{partner-zone-name}/generate-credentials", - Name: "partnerZoneGenerateCredentials", - PathParameter: []string{"partner-zone-name"}, - Doc: "generate a token for a partner zone service account", - ListQueryParameter: map[string]QueryParameterOption{}, - BodyFields: map[string]BodyField{}, - Method: "POST", + //all the token runs are not present in real life just present in the yaml test file used here + expected := RunCatalog{ + "partnerZoneGenerateCredentials": Run{ + Path: "/public/partner-zone/v2/{partner-zone-name}/generate-credentials", + Name: "partnerZoneGenerateCredentials", + PathParameter: []string{"partner-zone-name"}, + Doc: "generate a token for a partner zone service account", + QueryParameter: map[string]QueryParameterOption{}, + BodyFields: map[string]BodyField{}, + Method: "POST", + BackendType: CONSOLE, }, - "createAdminToken": &ConsoleExecute{ - Path: "/token/v1/admin_tokens", - Name: "createAdminToken", - Doc: "Create an admin token", - ListQueryParameter: map[string]QueryParameterOption{}, - PathParameter: []string{}, + "createAdminToken": Run{ + Path: "/token/v1/admin_tokens", + Name: "createAdminToken", + Doc: "Create an admin token", + QueryParameter: map[string]QueryParameterOption{}, + PathParameter: []string{}, BodyFields: map[string]BodyField{ "name": { FlagName: "name", @@ -399,13 +401,14 @@ func TestGetExecutes(t *testing.T) { Type: "string", }, }, - Method: "POST", + Method: "POST", + BackendType: CONSOLE, }, - "createApplicationInstanceToken": &ConsoleExecute{ - Path: "/token/v1/application_instance_tokens/{application-instance-name}", - Name: "createApplicationInstanceToken", - Doc: "Create an application instance token", - ListQueryParameter: map[string]QueryParameterOption{}, + "createApplicationInstanceToken": Run{ + Path: "/token/v1/application_instance_tokens/{application-instance-name}", + Name: "createApplicationInstanceToken", + Doc: "Create an application instance token", + QueryParameter: map[string]QueryParameterOption{}, PathParameter: []string{ "application-instance-name", }, @@ -416,36 +419,40 @@ func TestGetExecutes(t *testing.T) { Type: "string", }, }, - Method: "POST", + Method: "POST", + BackendType: CONSOLE, }, - "deleteToken": &ConsoleExecute{ - Path: "/token/v1/{token-id}", - Name: "deleteToken", - Doc: "Delete a token", - ListQueryParameter: map[string]QueryParameterOption{}, - PathParameter: []string{"token-id"}, - BodyFields: map[string]BodyField{}, - Method: "DELETE", + "deleteToken": Run{ + Path: "/token/v1/{token-id}", + Name: "deleteToken", + Doc: "Delete a token", + QueryParameter: map[string]QueryParameterOption{}, + PathParameter: []string{"token-id"}, + BodyFields: map[string]BodyField{}, + Method: "DELETE", + BackendType: CONSOLE, }, - "listAdminToken": &ConsoleExecute{ - Path: "/token/v1/admin_tokens", - Name: "listAdminToken", - Doc: "List admin token", - ListQueryParameter: map[string]QueryParameterOption{}, - PathParameter: []string{}, - BodyFields: map[string]BodyField{}, - Method: "GET", + "listAdminToken": Run{ + Path: "/token/v1/admin_tokens", + Name: "listAdminToken", + Doc: "List admin token", + QueryParameter: map[string]QueryParameterOption{}, + PathParameter: []string{}, + BodyFields: map[string]BodyField{}, + Method: "GET", + BackendType: CONSOLE, }, - "listApplicationInstanceToken": &ConsoleExecute{ - Path: "/token/v1/application_instance_tokens/{application-instance-name}", - Name: "listApplicationInstanceToken", - Doc: "List application instance token", - ListQueryParameter: map[string]QueryParameterOption{}, + "listApplicationInstanceToken": Run{ + Path: "/token/v1/application_instance_tokens/{application-instance-name}", + Name: "listApplicationInstanceToken", + Doc: "List application instance token", + QueryParameter: map[string]QueryParameterOption{}, PathParameter: []string{ "application-instance-name", }, - BodyFields: map[string]BodyField{}, - Method: "GET", + BodyFields: map[string]BodyField{}, + Method: "GET", + BackendType: CONSOLE, }, } if !reflect.DeepEqual(result, expected) { diff --git a/schema/openapi_parser_gateway_test.go b/schema/openapi_parser_gateway_test.go index 1a0a0cf..0c8c269 100644 --- a/schema/openapi_parser_gateway_test.go +++ b/schema/openapi_parser_gateway_test.go @@ -262,3 +262,54 @@ spec: } }) } +func TestGetExecutesForGateway(t *testing.T) { + t.Run("gets execute endpoint from schema", func(t *testing.T) { + schemaContent, err := os.ReadFile("testdata/gateway_run.yaml") + if err != nil { + t.Fatalf("failed reading file: %s", err) + } + + schema, err := NewOpenApiParser(schemaContent) + if err != nil { + t.Fatalf("failed creating new schema: %s", err) + } + + result, err := schema.getRuns(GATEWAY) + if err != nil { + t.Fatalf("failed getting execute: %s", err) + } + + //all the token runs are not present in real life just present in the yaml test file used here + expected := RunCatalog{ + "generateServiceAccountToken": Run{ + Path: "/gateway/v2/token", + Name: "generateServiceAccountToken", + Doc: "Generate a token for a service account on a virtual cluster", + QueryParameter: map[string]QueryParameterOption{}, + PathParameter: []string{}, + BodyFields: map[string]BodyField{ + "vCluster": { + FlagName: "v-cluster", + Required: false, + Type: "string", + }, + "lifeTimeSeconds": { + Type: "integer", + Required: true, + FlagName: "life-time-seconds", + }, + "username": { + FlagName: "username", + Required: true, + Type: "string", + }, + }, + Method: "POST", + BackendType: GATEWAY, + }, + } + if !reflect.DeepEqual(result, expected) { + t.Error(spew.Printf("got %v, want %v", result, expected)) + } + }) +} diff --git a/schema/run.go b/schema/run.go new file mode 100644 index 0000000..d2aaf44 --- /dev/null +++ b/schema/run.go @@ -0,0 +1,30 @@ +package schema + +import ( + "fmt" + "strings" +) + +type Run struct { + Path string + Name string + Doc string + QueryParameter map[string]QueryParameterOption + PathParameter []string + BodyFields map[string]BodyField + Method string + + BackendType BackendType `json:"-"` +} + +// TODO: test +func (c *Run) BuildPath(pathValue []string) string { + path := c.Path + if len(pathValue) != len(c.PathParameter) { + panic(fmt.Sprintf("BuildPath: pathValue lentgth (%d) does not match execution path parameter length (%d) for %s", len(pathValue), len(c.PathParameter), c.Name)) + } + for i, pathValue := range pathValue { + path = strings.Replace(path, fmt.Sprintf("{%s}", c.PathParameter[i]), pathValue, 1) + } + return path +} diff --git a/schema/testdata/execute.yaml b/schema/testdata/console_run.yaml similarity index 99% rename from schema/testdata/execute.yaml rename to schema/testdata/console_run.yaml index 4d60d81..9ee58cb 100644 --- a/schema/testdata/execute.yaml +++ b/schema/testdata/console_run.yaml @@ -251,8 +251,9 @@ paths: title: An unexpected error occurred in the server security: - httpAuth: [] - x-cdk-execute-action-name: listAdminToken - x-cdk-execute-action-doc: List admin token + x-cdk-run-version: '1' + x-cdk-run-name: listAdminToken + x-cdk-run-doc: List admin token post: tags: - tokens @@ -313,8 +314,9 @@ paths: title: An unexpected error occurred in the server security: - httpAuth: [] - x-cdk-execute-action-name: createAdminToken - x-cdk-execute-action-doc: Create an admin token + x-cdk-run-version: '1' + x-cdk-run-name: createAdminToken + x-cdk-run-doc: Create an admin token /token/v1/{token-id}: delete: tags: @@ -373,8 +375,9 @@ paths: title: An unexpected error occurred in the server security: - httpAuth: [] - x-cdk-execute-action-name: deleteToken - x-cdk-execute-action-doc: Delete a token + x-cdk-run-version: '1' + x-cdk-run-name: deleteToken + x-cdk-run-doc: Delete a token /token/v1/application_instance_tokens/{application-instance-name}: get: tags: @@ -439,8 +442,9 @@ paths: title: An unexpected error occurred in the server security: - httpAuth: [] - x-cdk-execute-action-name: listApplicationInstanceToken - x-cdk-execute-action-doc: List application instance token + x-cdk-run-version: '1' + x-cdk-run-name: listApplicationInstanceToken + x-cdk-run-doc: List application instance token post: tags: - tokens @@ -509,8 +513,9 @@ paths: title: An unexpected error occurred in the server security: - httpAuth: [] - x-cdk-execute-action-name: createApplicationInstanceToken - x-cdk-execute-action-doc: Create an application instance token + x-cdk-run-version: '1' + x-cdk-run-name: createApplicationInstanceToken + x-cdk-run-doc: Create an application instance token /public/v1/groups: get: tags: @@ -8719,8 +8724,9 @@ paths: title: An unexpected error occurred in the server security: - httpAuth: [] - x-cdk-execute-action-name: partnerZoneGenerateCredentials - x-cdk-execute-action-doc: generate a token for a partner zone service account + x-cdk-run-version: '1' + x-cdk-run-name: partnerZoneGenerateCredentials + x-cdk-run-doc: generate a token for a partner zone service account x-codeSamples: - lang: Shell + Curl source: |- diff --git a/schema/testdata/gateway_run.yaml b/schema/testdata/gateway_run.yaml new file mode 100644 index 0000000..fce101f --- /dev/null +++ b/schema/testdata/gateway_run.yaml @@ -0,0 +1,3356 @@ +openapi: 3.1.0 +info: + title: Conduktor Gateway API + version: v2 + summary: The API to interact with Conduktor Gateway programmatically + contact: + email: contact@conduktor.io + url: https://docs.conduktor.io + x-logo: + url: https://raw.githubusercontent.com/conduktor/conduktor.io-public/main/logo/conduktor-gateway-api.png + backgroundColor: '#FFFFFF' + altText: Conduktor logo +tags: +- name: Introduction + description: | + The Conduktor Gateway REST API 's aim is to help you configure your Gateway. + + The legacy (v1) API is available but deprecated ([V1 documentation](./?apiVersion=v1)). + + Get started with Conduktor Gateway [self-hosted](https://docs.conduktor.io/gateway/installation/) today. Setup takes only a few minutes. +- name: Authentication + description: |- + Authentication to the API requires a basic authentication. + + To get a token and use it you must go through the following steps: + + * Configure the admin password in the Gateway YAML configuration file. + + * Use the password in the **authorization** header of your requests. + + Example: + + ```shell + curl -X GET "https://your.gateway-api.host/gateway/v2/virtual-cluster" \ + -H "accept: application/json" \ + --user "admin:password" + ``` +- name: Kinds + description: | + ### Definition + + Kinds the resource types of the Conduktor gateway. + + ### Conduktor Gateway Kinds + + The following kinds are available in the Conduktor Gateway API: + + * `VirtualCluster` + * `AliasTopic` + * `ConcentratedTopic` + * `ConcentrationRule` + * `Interceptor` + * `Plugin` + * `GatewayServiceAccount` + * `Token` + * `GatewayGroup` +- name: Api Groups + description: |+ + ### Definition + + API groups a set of resources that share the same API path prefix. They are used to organize the API endpoints and the + resources they manage. + The versioning is set at this level, so all the resources in the same group share the same version. + + ### Conduktor Api Groups + + The Gateway API consist of a single API group right now (`gateway`), and it manages the following resources: + + | Api Group | Version | Kinds | + |------------|---------|-----------------------------------------------------------------------------------------------------------------------------------------------------------| + | `/gateway` | `/v2` | `/virtual-cluster`, `/alias-topic`, `/concentrated-topic`, `/concentration-rule`,
`/interceptor`, `/plugin`, `/service-account`, `/group`, `/token` | + + + +- name: Versioning + description: |+ + * __The version is set at the api group level__. It is incremented when a breaking change happens in the schema of an endpoint of the group (that has been marked `stable`). The n-1 version is still available for a while to allow users to migrate. The version is part of the endpoint path. + * The API version (v2) is the global version of the Conduktor Gateway API, it should not change unless there is a complete overhaul of the API. + + + Endpoint also have a status to manage their API lifecycle, following the order below: + * __preview__: this is an early-stage feature, really likely to change + * __beta__: early access feature, breaking change + * __stable__: Production-ready endpoint, no breaking change + * __deprecated__: This endpoint isn't supported anymore and the user should migrate + + +- name: Conventions + description: |+ + ### Path conventions + + The API follows as much as possible the endpoints structure below for each kind of resource: + + * `GET /{api-group}/{version}/{kind}/{name}` to read a resource + * `GET /{api-group}/{version}/{kind}` to list resources of a kind + * `PUT /{api-group}/{version}/{kind}` to update or create a resource + * `DELETE /{api-group}/{version}/{kind}/{name}` to delete a resource (returns 204 No Content) + * `POST` is used for specific operations that don't fit this CRUD model. PUT is the default verb for updates and + creations. + * Important principle: the result of a GET can be reused as the body of a PUT to update the resource. + + __Non-unique names__: + When a `name` is not enough to uniquely identify a resource, the GET and DELETE endpoint are different + The GET by name is replaced by query parameters (returning lists or the searched item if the criteria are the elements + of the key), and the DELETE by name is replaced by a DELETE with a body. + For example, an `alias-topic` is identified by its `name` and the `vCluster` gives the following endpoints: + + * `GET /gateway/v2/alias-topic?name={name}&vcluster={vcluster}` + * `PUT /gateway/v2/alias-topic` + * `DELETE /gateway/v2/alias-topic` with a body containing the `name` and the `vCluster` + + ### Other conventions + + * All requests and responses are in JSON and should have their `content-type` header set to `application/json` + * Every kind has a lower-cased name (e.g. `virtual-cluster` for the `VirtualCluster` kind) that is used in the endpoint + path. + * Errors have a standard format: + * The HTTP status code is used to indicate the type of error. + * The response body contains a common JSON object for every error: + * `title`: a unique error code for the error. + * `message`: a human-readable message for the error. + * `cause`: additional information about the error. + * All timestamps are in ISO 8601 format. + +- name: token + description: |+ + ### Definition + + The token group contains just a utility endpoint to generate a token for a given Local ServiceAccount (on a given + virtual cluster, or `passthrough` if omitted). + + This token can be used to authenticate against the Gateway API in the `sasl_plaintext` authentication mode. + More information on this case here : https://docs.conduktor.io/gateway/concepts/Clients/#sasl_plaintext + + ### Available operations + + * Generate a token + + ### Identity + + N/A. + +- name: cli_virtual-cluster_gateway_v2_7 + description: |+ + ### Definition + + https://docs.conduktor.io/gateway/concepts/Virtual%20Cluster/ + + ### Available operations + + * List virtual clusters + * Get a virtual cluster + * Upsert a virtual cluster + * Delete a virtual cluster + + ### Identity + + A virtual cluster is identified by the `name`. + + ### Schema + + + + + x-displayName: virtual-cluster +- name: cli_alias-topic_gateway_v2_8 + description: |+ + ### Definition + + https://docs.conduktor.io/gateway/concepts/Logical_topics/Alias%20topics/ + + ### Available operations + + * List alias topics + * Upsert an alias topic + * Delete an alias topic + + ### Identity + + An alias topic is identified by the `name` inside a `vCluster` (`passthrough` if omitted). + + ### Schema + + + + + x-displayName: alias-topic +- name: cli_concentrated-topic_gateway_v2_0 + description: |+ + ### Definition + + https://docs.conduktor.io/gateway/concepts/Logical_topics/Concentration/ + + ### Available operations + + * List concentrated topics + + Concentrated topics are created when a concentration rule is applied. They are not created or deleted directly by the API. + + ### Identity + + A concentrated topic is identified by the `name` and the `vCluster` (`passthrough` if omitted). + + ### Schema + + + + + x-displayName: concentrated-topic +- name: cli_concentration-rule_gateway_v2_9 + description: |+ + ### Definition + + https://docs.conduktor.io/gateway/concepts/Logical_topics/Concentration/ + + ### Available operations + + * List concentration rules + * Get a concentration rule + * Upsert a concentration rule + * Delete a concentration rule + + + ### Identity + + A concentration rule is identified by its `name` inside a `vCluster`. + + ### Schema + + + + + x-displayName: concentration-rule +- name: cli_interceptor_gateway_v2_12 + description: |+ + ### Definition + + https://docs.conduktor.io/gateway/concepts/Interceptors-and-plugins/ + + ### Available operations + + * List the interceptors + * Get an interceptor + * Upsert an interceptor + * Delete an interceptor + + ### Identity + + An interceptor is identified by its `name` and its `scope`. + The `scope` is itself composed of 3 optional fields `vCluster`, `group`, `username`. + A __global__ interceptor is an interceptor whose `scope` has its fields empty. + + ### Schema + + + + + x-displayName: interceptor +- name: plugin + description: |+ + ### Definition + + https://docs.conduktor.io/gateway/concepts/Interceptors-and-plugins/ + + ### Available operations + + * List the available plugins of the Gateway + + ### Identity + + A plugin is identified by its `pluginId`. + + The list of plugins is fixed for a given Gateway instance. + The list is fixed and cannot be modified. + + ### Schema + + + + +- name: cli_gateway-service-account_gateway_v2_10 + description: |+ + ### Definition + + https://docs.conduktor.io/gateway/concepts/service-accounts-authentication-authorization/ + + ### Available operations + + * List service accounts + * Upsert a service account + * Delete a service account + + ### Identity + + The service account is identified by the `name` and the `vCluster`. + + The `vCluster` name is not mandatory, but if omitted, the `passthrough` virtual cluster will be used. + Thus, a service account is always associated with one and only one virtual cluster. + + ### Local and external service accounts + + A service account can be `Local` or `External`. + + * A `Local` service account is just a local user that allows to generate Gateway tokens for your Kafka client + applications (SASL). + * An `External` service account is a user that is authenticated by an external system (OIDC). In such a + scenario you will only need to create external service accounts in 2 cases : either to rename the OIDC principal + for you Kafka applications OR to gather service accounts into `Groups`. Gateway Tokens are not issued for external + service accounts since the authentication must be done by the external system. + + To create an external service account you must provide have an `principal` that is the name of the user in the + external system. + It can be equal or not to the service account `name` (up to you), but note that __the `principal` must be unique + across all virtual clusters__. + + ### Schema + + + + + x-displayName: service-account +- name: cli_gateway-group_gateway_v2_11 + description: |+ + ### Definition + + Groups a defined by name a allow to regroup Gateway users in order to apply interceptors rules. + + ### Available operations + + * List groups + * Get a group + * Upsert a group + * Delete a group + + ### Identity + + The group is identified by its `name` (unique across all the virtual clusters). + + A group can be added external groups (coming from LDAP, OIDC claims etc.) which will allow the Gateway to bind them on the Gateway Group. + + ### Schema + + + + + x-displayName: group +paths: + /gateway/v2/token: + post: + tags: + - token + description: |2+ + + + [![Beta](https://img.shields.io/badge/Lifecycle-Beta-orange)](#tag/Versioning) + + Create a new token for given service account and a given virtual cluster. + + If the vcluster is not provided, the token will be created for the passthrough virtual cluster. + + operationId: Generate a token for a service account on a virtual cluster + requestBody: + content: + application/json: + schema: + $ref: '#/components/schemas/TokenRequest' + example: + vCluster: vcluster1 + username: user1 + lifeTimeSeconds: 3600 + required: true + responses: + '200': + description: '' + content: + application/json: + schema: + $ref: '#/components/schemas/TokenResponse' + example: + token: eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ1c2VyMSIsIm5hbWUiOiJ1c2VyMSIsImlhdCI6MTUxNjIzOTAyMn0.1Q2JjNz + '400': + description: 'Invalid value for: body' + content: + text/plain: + schema: + type: string + '401': + description: The given credentials are not valid + content: + application/json: + schema: + $ref: '#/components/schemas/Unauthorized' + example: + title: The given credentials are not valid + '404': + description: The searched entity was not found + content: + application/json: + schema: + $ref: '#/components/schemas/NotFound' + example: + title: The searched entity was not found + '409': + description: Requesting a JWT token when no user pool service is configured + in the Gateway or requesting a token for an external service account. + content: + application/json: + schema: + $ref: '#/components/schemas/Conflict' + example: + title: Requesting a JWT token when no user pool service is configured + in the Gateway or requesting a token for an external service account. + '500': + description: An unexpected error occurred in the server + content: + application/json: + schema: + $ref: '#/components/schemas/ServerError' + example: + title: An unexpected error occurred in the server + security: + - httpAuth: [] + x-cdk-run-version: '1' + x-cdk-run-name: generateServiceAccountToken + x-cdk-run-doc: Generate a token for a service account on a virtual cluster + x-codeSamples: + - lang: Shell + source: |- + curl \ + --request POST \ + --url 'http://localhost:8888/gateway/v2/token' \ + --header 'Authorization: Basic YWRtaW46Y29uZHVrdG9y' \ + --header 'Content-Type: application/json' \ + --data-raw '{"vCluster":"vcluster1","username":"user1","lifeTimeSeconds":3600000}' + /gateway/v2/virtual-cluster: + get: + tags: + - cli_virtual-cluster_gateway_v2_7 + description: |2+ + + [![Beta](https://img.shields.io/badge/Lifecycle-Beta-orange)](#tag/Versioning) + + List the virtual clusters + + operationId: List the virtual clusters + responses: + '200': + description: '' + content: + application/json: + schema: + type: array + uniqueItems: true + items: + $ref: '#/components/schemas/VirtualCluster' + example: + - kind: VirtualCluster + apiVersion: gateway/v2 + metadata: + name: vcluster1 + spec: + aclEnabled: false + superUsers: + - username1 + - username2 + type: Standard + bootstrapServers: kafka:9092 + clientProperties: + security.protocol: SASL_PLAINTEXT + sasl.mechanism: PLAIN + sasl.jaas.config: org.apache.kafka.common.security.plain.PlainLoginModule + required username={{username}} password={{password}}; + '400': + description: Virtual cluster is not supported on security protocol PLAINTEXT + content: + application/json: + schema: + $ref: '#/components/schemas/BadRequest' + example: + title: Virtual cluster is not supported on security protocol PLAINTEXT + '401': + description: The given credentials are not valid + content: + application/json: + schema: + $ref: '#/components/schemas/Unauthorized' + example: + title: The given credentials are not valid + '500': + description: An unexpected error occurred in the server + content: + application/json: + schema: + $ref: '#/components/schemas/ServerError' + example: + title: An unexpected error occurred in the server + security: + - httpAuth: [] + x-codeSamples: + - lang: Shell + source: |- + curl \ + --request GET \ + --url 'http://localhost:8888/gateway/v2/virtual-cluster' \ + --header 'Authorization: Basic YWRtaW46Y29uZHVrdG9y' + put: + tags: + - cli_virtual-cluster_gateway_v2_7 + description: |2+ + + [![Beta](https://img.shields.io/badge/Lifecycle-Beta-orange)](#tag/Versioning) + + Upsert a virtual cluster + + operationId: Upsert a virtual cluster + parameters: + - name: dryMode + in: query + description: If true, the operation will be simulated and no changes will + be made + required: false + schema: + default: false + type: boolean + requestBody: + content: + application/json: + schema: + $ref: '#/components/schemas/VirtualCluster' + example: + kind: VirtualCluster + apiVersion: gateway/v2 + metadata: + name: vcluster1 + spec: + aclEnabled: false + superUsers: + - username1 + - username2 + type: Standard + bootstrapServers: kafka:9092 + clientProperties: + security.protocol: SASL_PLAINTEXT + sasl.mechanism: PLAIN + sasl.jaas.config: org.apache.kafka.common.security.plain.PlainLoginModule + required username={{username}} password={{password}}; + required: true + responses: + '200': + description: '' + content: + application/json: + schema: + $ref: '#/components/schemas/ApplyResult_VirtualCluster' + example: + resource: + kind: VirtualCluster + apiVersion: gateway/v2 + metadata: + name: vcluster1 + spec: + aclEnabled: false + superUsers: + - username1 + - username2 + type: Standard + bootstrapServers: kafka:9092 + clientProperties: + security.protocol: SASL_PLAINTEXT + sasl.mechanism: PLAIN + sasl.jaas.config: org.apache.kafka.common.security.plain.PlainLoginModule + required username={{username}} password={{password}}; + upsertResult: UPDATED + '400': + description: Wrong format or usage of reserved keywords (e.g. passthrough) + content: + application/json: + schema: + $ref: '#/components/schemas/BadRequest' + example: + title: Wrong format or usage of reserved keywords (e.g. passthrough) + '401': + description: The given credentials are not valid + content: + application/json: + schema: + $ref: '#/components/schemas/Unauthorized' + example: + title: The given credentials are not valid + '409': + description: The given prefix is already used by another virtual cluster + content: + application/json: + schema: + $ref: '#/components/schemas/Conflict' + example: + title: The given prefix is already used by another virtual cluster + '500': + description: An unexpected error occurred in the server + content: + application/json: + schema: + $ref: '#/components/schemas/ServerError' + example: + title: An unexpected error occurred in the server + security: + - httpAuth: [] + x-codeSamples: + - lang: Shell + source: |- + curl \ + --request PUT \ + --url 'http://localhost:8888/gateway/v2/virtual-cluster?dryMode=false' \ + --header 'Authorization: Basic YWRtaW46Y29uZHVrdG9y' \ + --header 'Content-Type: application/json' \ + --data-raw '{ + "kind" : "VirtualCluster", + "apiVersion" : "gateway/v2", + "metadata" : { + "name" : "vcluster1" + }, + "spec" : { + "aclEnabled" : false, + "superUsers" : [ + "username1", + "username2" + ], + "type" : "Standard", + "bootstrapServers" : "kafka:9092", + "clientProperties" : { + "security.protocol" : "SASL_PLAINTEXT", + "sasl.mechanism" : "PLAIN", + "sasl.jaas.config" : "org.apache.kafka.common.security.plain.PlainLoginModule required username={{username}} password={{password}};" + } + } + }' + /gateway/v2/virtual-cluster/{vCluster}: + get: + tags: + - cli_virtual-cluster_gateway_v2_7 + description: |2+ + + [![Beta](https://img.shields.io/badge/Lifecycle-Beta-orange)](#tag/Versioning) + + Get a virtual cluster + + operationId: Get a virtual cluster + parameters: + - name: vCluster + in: path + description: The name of the virtual cluster + required: true + schema: + type: string + responses: + '200': + description: '' + content: + application/json: + schema: + $ref: '#/components/schemas/VirtualCluster' + example: + kind: VirtualCluster + apiVersion: gateway/v2 + metadata: + name: vcluster1 + spec: + aclEnabled: false + superUsers: + - username1 + - username2 + type: Standard + bootstrapServers: kafka:9092 + clientProperties: + security.protocol: SASL_PLAINTEXT + sasl.mechanism: PLAIN + sasl.jaas.config: org.apache.kafka.common.security.plain.PlainLoginModule + required username={{username}} password={{password}}; + '400': + description: Virtual cluster is not supported on security protocol PLAINTEXT + content: + application/json: + schema: + $ref: '#/components/schemas/BadRequest' + example: + title: Virtual cluster is not supported on security protocol PLAINTEXT + '401': + description: The given credentials are not valid + content: + application/json: + schema: + $ref: '#/components/schemas/Unauthorized' + example: + title: The given credentials are not valid + '404': + description: The given virtual cluster does not exist + content: + application/json: + schema: + $ref: '#/components/schemas/NotFound' + example: + title: The given virtual cluster does not exist + '500': + description: An unexpected error occurred in the server + content: + application/json: + schema: + $ref: '#/components/schemas/ServerError' + example: + title: An unexpected error occurred in the server + security: + - httpAuth: [] + x-codeSamples: + - lang: Shell + source: |- + curl \ + --request GET \ + --url 'http://localhost:8888/gateway/v2/virtual-cluster/vcluster1' \ + --header 'Authorization: Basic YWRtaW46Y29uZHVrdG9y' + delete: + tags: + - cli_virtual-cluster_gateway_v2_7 + description: |2+ + + [![Beta](https://img.shields.io/badge/Lifecycle-Beta-orange)](#tag/Versioning) + + Delete a virtual cluster + + operationId: Delete a virtual cluster + parameters: + - name: vCluster + in: path + description: The name of the virtual cluster + required: true + schema: + type: string + responses: + '204': + description: '' + '400': + description: Default `passthrough` virtual cluster cannot be deleted + content: + application/json: + schema: + $ref: '#/components/schemas/BadRequest' + example: + title: Default `passthrough` virtual cluster cannot be deleted + '401': + description: The given credentials are not valid + content: + application/json: + schema: + $ref: '#/components/schemas/Unauthorized' + example: + title: The given credentials are not valid + '404': + description: The given virtual cluster does not exist + content: + application/json: + schema: + $ref: '#/components/schemas/NotFound' + example: + title: The given virtual cluster does not exist + '409': + description: The given virtual cluster has references (logical topics, interceptors, + concentration rules, service accounts) and cannot be deleted + content: + application/json: + schema: + $ref: '#/components/schemas/Conflict' + example: + title: The given virtual cluster has references (logical topics, interceptors, + concentration rules, service accounts) and cannot be deleted + '500': + description: An unexpected error occurred in the server + content: + application/json: + schema: + $ref: '#/components/schemas/ServerError' + example: + title: An unexpected error occurred in the server + security: + - httpAuth: [] + x-codeSamples: + - lang: Shell + source: |- + curl \ + --request DELETE \ + --url 'http://localhost:8888/gateway/v2/virtual-cluster/vcluster1' \ + --header 'Authorization: Basic YWRtaW46Y29uZHVrdG9y' + /gateway/v2/alias-topic: + get: + tags: + - cli_alias-topic_gateway_v2_8 + description: |2+ + + [![Beta](https://img.shields.io/badge/Lifecycle-Beta-orange)](#tag/Versioning) + + List the alias topics of a virtual cluster + + operationId: List the alias topics + parameters: + - name: vcluster + in: query + description: The virtual cluster filter + required: false + schema: + type: string + - name: name + in: query + description: The name filter + required: false + schema: + type: string + - name: showDefaults + in: query + description: Whether to show default values or not + required: false + schema: + default: false + type: boolean + example: true + responses: + '200': + description: '' + content: + application/json: + schema: + type: array + items: + $ref: '#/components/schemas/AliasTopic' + example: + - kind: AliasTopic + apiVersion: gateway/v2 + metadata: + name: name1 + vCluster: vCluster1 + spec: + physicalName: physicalName1 + '400': + description: The request is not valid + content: + application/json: + schema: + $ref: '#/components/schemas/BadRequest' + example: + title: The request is not valid + '401': + description: The given credentials are not valid + content: + application/json: + schema: + $ref: '#/components/schemas/Unauthorized' + example: + title: The given credentials are not valid + '404': + description: The given virtual cluster does not exist + content: + application/json: + schema: + $ref: '#/components/schemas/NotFound' + example: + title: The given virtual cluster does not exist + '500': + description: An unexpected error occurred in the server + content: + application/json: + schema: + $ref: '#/components/schemas/ServerError' + example: + title: An unexpected error occurred in the server + security: + - httpAuth: [] + x-codeSamples: + - lang: Shell + source: |- + curl \ + --request GET \ + --url 'http://localhost:8888/gateway/v2/alias-topic?showDefaults=false' \ + --header 'Authorization: Basic YWRtaW46Y29uZHVrdG9y' + put: + tags: + - cli_alias-topic_gateway_v2_8 + description: |2+ + + [![Beta](https://img.shields.io/badge/Lifecycle-Beta-orange)](#tag/Versioning) + + Upsert an alias topic in a virtual cluster + + operationId: Upsert an alias topic + parameters: + - name: dryMode + in: query + description: Whether to simulate the operation or not + required: false + schema: + default: false + type: boolean + example: true + requestBody: + content: + application/json: + schema: + $ref: '#/components/schemas/AliasTopic' + example: + kind: AliasTopic + apiVersion: gateway/v2 + metadata: + name: name1 + vCluster: vCluster1 + spec: + physicalName: physicalName1 + required: true + responses: + '200': + description: '' + content: + application/json: + schema: + $ref: '#/components/schemas/ApplyResult_AliasTopic' + example: + resource: + kind: AliasTopic + apiVersion: gateway/v2 + metadata: + name: name1 + vCluster: vCluster1 + spec: + physicalName: physicalName1 + upsertResult: UPDATED + '400': + description: The request is not valid + content: + application/json: + schema: + $ref: '#/components/schemas/BadRequest' + example: + title: The request is not valid + '401': + description: The given credentials are not valid + content: + application/json: + schema: + $ref: '#/components/schemas/Unauthorized' + example: + title: The given credentials are not valid + '404': + description: The given virtual cluster does not exist + content: + application/json: + schema: + $ref: '#/components/schemas/NotFound' + example: + title: The given virtual cluster does not exist + '409': + description: |2 + + The virtual cluster is of type Standard and does not target main physical cluster + The virtual cluster is of type Partner and does not target a valid physical cluster + The physical cluster of an alias topic cannot be changed + content: + application/json: + schema: + $ref: '#/components/schemas/Conflict' + example: + title: |2 + + The virtual cluster is of type Standard and does not target main physical cluster + The virtual cluster is of type Partner and does not target a valid physical cluster + The physical cluster of an alias topic cannot be changed + '500': + description: An unexpected error occurred in the server + content: + application/json: + schema: + $ref: '#/components/schemas/ServerError' + example: + title: An unexpected error occurred in the server + security: + - httpAuth: [] + x-codeSamples: + - lang: Shell + source: |- + curl \ + --request PUT \ + --url 'http://localhost:8888/gateway/v2/alias-topic?dryMode=false' \ + --header 'Authorization: Basic YWRtaW46Y29uZHVrdG9y' \ + --header 'Content-Type: application/json' \ + --data-raw '{ + "kind" : "AliasTopic", + "apiVersion" : "gateway/v2", + "metadata" : { + "name" : "name1", + "vCluster" : "vCluster1" + }, + "spec" : { + "physicalName" : "physicalName1" + } + }' + delete: + tags: + - cli_alias-topic_gateway_v2_8 + description: |2+ + + [![Beta](https://img.shields.io/badge/Lifecycle-Beta-orange)](#tag/Versioning) + + Delete an alias topic in a virtual cluster + + operationId: Delete an alias topic + requestBody: + content: + application/json: + schema: + $ref: '#/components/schemas/AliasTopicId' + required: true + responses: + '204': + description: '' + '400': + description: The request is not valid + content: + application/json: + schema: + $ref: '#/components/schemas/BadRequest' + example: + title: The request is not valid + '401': + description: The given credentials are not valid + content: + application/json: + schema: + $ref: '#/components/schemas/Unauthorized' + example: + title: The given credentials are not valid + '404': + description: The given name does not exist in the given virtual cluster + content: + application/json: + schema: + $ref: '#/components/schemas/NotFound' + example: + title: The given name does not exist in the given virtual cluster + '500': + description: An unexpected error occurred in the server + content: + application/json: + schema: + $ref: '#/components/schemas/ServerError' + example: + title: An unexpected error occurred in the server + security: + - httpAuth: [] + x-codeSamples: + - lang: Shell + source: |- + curl \ + --request DELETE \ + --url 'http://localhost:8888/gateway/v2/alias-topic' \ + --header 'Authorization: Basic YWRtaW46Y29uZHVrdG9y' \ + --header 'Content-Type: application/json' \ + --data-raw '{ + "name" : "name1", + "vCluster" : "vCluster1" + }' + /gateway/v2/concentrated-topic: + get: + tags: + - cli_concentrated-topic_gateway_v2_0 + description: |2+ + + [![Beta](https://img.shields.io/badge/Lifecycle-Beta-orange)](#tag/Versioning) + + List the concentrated topics of a vcluster + + operationId: List the concentrated topics + parameters: + - name: vcluster + in: query + description: The virtual cluster filter + required: false + schema: + type: string + - name: name + in: query + description: The name filter + required: false + schema: + type: string + - name: showDefaults + in: query + description: Whether to show default values or not + required: false + schema: + default: false + type: boolean + example: true + responses: + '200': + description: '' + content: + application/json: + schema: + type: array + items: + $ref: '#/components/schemas/ConcentratedTopic' + example: + - kind: ConcentratedTopic + apiVersion: gateway/v2 + metadata: + name: name1 + vCluster: vCluster1 + spec: + physicalName: physicalName1 + '400': + description: The request is not valid + content: + application/json: + schema: + $ref: '#/components/schemas/BadRequest' + example: + title: The request is not valid + '401': + description: The given credentials are not valid + content: + application/json: + schema: + $ref: '#/components/schemas/Unauthorized' + example: + title: The given credentials are not valid + '404': + description: The given virtual cluster does not exist + content: + application/json: + schema: + $ref: '#/components/schemas/NotFound' + example: + title: The given virtual cluster does not exist + '500': + description: An unexpected error occurred in the server + content: + application/json: + schema: + $ref: '#/components/schemas/ServerError' + example: + title: An unexpected error occurred in the server + security: + - httpAuth: [] + x-codeSamples: + - lang: Shell + source: |- + curl \ + --request GET \ + --url 'http://localhost:8888/gateway/v2/concentrated-topic?showDefaults=false' \ + --header 'Authorization: Basic YWRtaW46Y29uZHVrdG9y' + /gateway/v2/concentration-rule: + get: + tags: + - cli_concentration-rule_gateway_v2_9 + description: |2+ + + [![Beta](https://img.shields.io/badge/Lifecycle-Beta-orange)](#tag/Versioning) + + List the concentration rules of a vcluster + + operationId: List the concentration rules + parameters: + - name: vcluster + in: query + description: The virtual cluster filter + required: false + schema: + type: string + - name: name + in: query + description: The name filter + required: false + schema: + type: string + - name: showDefaults + in: query + description: Whether to show default values or not + required: false + schema: + default: false + type: boolean + example: true + responses: + '200': + description: '' + content: + application/json: + schema: + type: array + items: + $ref: '#/components/schemas/ConcentrationRule' + example: + - kind: ConcentrationRule + apiVersion: gateway/v2 + metadata: + name: concentrationRule1 + vCluster: vCluster1 + spec: + pattern: topic.* + physicalTopics: + delete: topic + compact: compact_topic + deleteCompact: compact_delete_topic + autoManaged: false + offsetCorrectness: false + '400': + description: The request is not valid + content: + application/json: + schema: + $ref: '#/components/schemas/BadRequest' + example: + title: The request is not valid + '401': + description: The given credentials are not valid + content: + application/json: + schema: + $ref: '#/components/schemas/Unauthorized' + example: + title: The given credentials are not valid + '404': + description: The given virtual cluster does not exist + content: + application/json: + schema: + $ref: '#/components/schemas/NotFound' + example: + title: The given virtual cluster does not exist + '500': + description: An unexpected error occurred in the server + content: + application/json: + schema: + $ref: '#/components/schemas/ServerError' + example: + title: An unexpected error occurred in the server + security: + - httpAuth: [] + x-codeSamples: + - lang: Shell + source: |- + curl \ + --request GET \ + --url 'http://localhost:8888/gateway/v2/concentration-rule?showDefaults=false' \ + --header 'Authorization: Basic YWRtaW46Y29uZHVrdG9y' + put: + tags: + - cli_concentration-rule_gateway_v2_9 + description: |2+ + + [![Beta](https://img.shields.io/badge/Lifecycle-Beta-orange)](#tag/Versioning) + + Upsert a concentration rule in a vcluster + + operationId: Upsert a concentration rule + parameters: + - name: dryMode + in: query + description: Whether to simulate the operation or not + required: false + schema: + default: false + type: boolean + example: true + requestBody: + content: + application/json: + schema: + $ref: '#/components/schemas/ConcentrationRule' + example: + kind: ConcentrationRule + apiVersion: gateway/v2 + metadata: + name: concentrationRule1 + vCluster: vCluster1 + spec: + pattern: topic.* + physicalTopics: + delete: topic + compact: compact_topic + deleteCompact: compact_delete_topic + autoManaged: false + offsetCorrectness: false + required: true + responses: + '200': + description: '' + content: + application/json: + schema: + $ref: '#/components/schemas/ApplyResult_ConcentrationRule' + example: + resource: + kind: ConcentrationRule + apiVersion: gateway/v2 + metadata: + name: concentrationRule1 + vCluster: vCluster1 + spec: + pattern: topic.* + physicalTopics: + delete: topic + compact: compact_topic + deleteCompact: compact_delete_topic + autoManaged: false + offsetCorrectness: false + upsertResult: CREATED + '400': + description: The request is not valid + content: + application/json: + schema: + $ref: '#/components/schemas/BadRequest' + example: + title: The request is not valid + '401': + description: The given credentials are not valid + content: + application/json: + schema: + $ref: '#/components/schemas/Unauthorized' + example: + title: The given credentials are not valid + '404': + description: The given virtual cluster does not exist or one of the physical + topics does not exist + content: + application/json: + schema: + $ref: '#/components/schemas/NotFound' + example: + title: The given virtual cluster does not exist or one of the physical + topics does not exist + '409': + description: One of the physical topics exists with an incompatible cleanup.policy + content: + application/json: + schema: + $ref: '#/components/schemas/Conflict' + example: + title: One of the physical topics exists with an incompatible cleanup.policy + '500': + description: An unexpected error occurred in the server + content: + application/json: + schema: + $ref: '#/components/schemas/ServerError' + example: + title: An unexpected error occurred in the server + security: + - httpAuth: [] + x-codeSamples: + - lang: Shell + source: |- + curl \ + --request PUT \ + --url 'http://localhost:8888/gateway/v2/concentration-rule?dryMode=false' \ + --header 'Authorization: Basic YWRtaW46Y29uZHVrdG9y' \ + --header 'Content-Type: application/json' \ + --data-raw '{ + "kind" : "ConcentrationRule", + "apiVersion" : "gateway/v2", + "metadata" : { + "name" : "concentrationRule1", + "vCluster" : "vCluster1" + }, + "spec" : { + "pattern" : "topic.*", + "physicalTopics" : { + "delete" : "topic", + "compact" : "compact_topic", + "deleteCompact" : "compact_delete_topic" + }, + "autoManaged" : false, + "offsetCorrectness" : false + } + }' + delete: + tags: + - cli_concentration-rule_gateway_v2_9 + description: |2+ + + [![Beta](https://img.shields.io/badge/Lifecycle-Beta-orange)](#tag/Versioning) + + Delete a concentration rule in a vcluster + + operationId: Delete a concentration rule + requestBody: + content: + application/json: + schema: + $ref: '#/components/schemas/ConcentrationRuleId' + example: + name: concentrationRule1 + vCluster: vCluster1 + required: true + responses: + '204': + description: '' + '400': + description: The request is not valid + content: + application/json: + schema: + $ref: '#/components/schemas/BadRequest' + example: + title: The request is not valid + '401': + description: The given credentials are not valid + content: + application/json: + schema: + $ref: '#/components/schemas/Unauthorized' + example: + title: The given credentials are not valid + '404': + description: The given name does not exist in the given virtual cluster + content: + application/json: + schema: + $ref: '#/components/schemas/NotFound' + example: + title: The given name does not exist in the given virtual cluster + '500': + description: An unexpected error occurred in the server + content: + application/json: + schema: + $ref: '#/components/schemas/ServerError' + example: + title: An unexpected error occurred in the server + security: + - httpAuth: [] + x-codeSamples: + - lang: Shell + source: |- + curl \ + --request DELETE \ + --url 'http://localhost:8888/gateway/v2/concentration-rule' \ + --header 'Authorization: Basic YWRtaW46Y29uZHVrdG9y' \ + --header 'Content-Type: application/json' \ + --data-raw '{ + "name" : "concentrationRule1", + "vCluster" : "vCluster1" + }' + /gateway/v2/interceptor: + get: + tags: + - cli_interceptor_gateway_v2_12 + description: |2+ + + [![Beta](https://img.shields.io/badge/Lifecycle-Beta-orange)](#tag/Versioning) + + List the interceptors + + operationId: List the interceptors + parameters: + - name: name + in: query + description: Filter by name + required: false + schema: + type: string + - name: global + in: query + description: Keep only global interceptors + required: false + schema: + type: boolean + - name: vcluster + in: query + description: Filter by virtual cluster + required: false + schema: + type: string + - name: group + in: query + description: Filter by group + required: false + schema: + type: string + - name: username + in: query + description: Filter by service account + required: false + schema: + type: string + responses: + '200': + description: '' + content: + application/json: + schema: + type: array + uniqueItems: true + items: + $ref: '#/components/schemas/Interceptor' + example: + - kind: Interceptor + apiVersion: gateway/v2 + metadata: + name: yellow_cars_filter + scope: + vCluster: vCluster1 + spec: + comment: Filter yellow cars + pluginClass: io.conduktor.gateway.interceptor.VirtualSqlTopicPlugin + priority: 1 + config: + virtualTopic: yellow_cars + statement: SELECT '$.type' as type, '$.price' as price FROM cars + WHERE '$.color' = 'yellow' + '400': + description: The request is not valid + content: + application/json: + schema: + $ref: '#/components/schemas/BadRequest' + example: + title: The request is not valid + '401': + description: The given credentials are not valid + content: + application/json: + schema: + $ref: '#/components/schemas/Unauthorized' + example: + title: The given credentials are not valid + '500': + description: An unexpected error occurred in the server + content: + application/json: + schema: + $ref: '#/components/schemas/ServerError' + example: + title: An unexpected error occurred in the server + security: + - httpAuth: [] + x-codeSamples: + - lang: Shell + source: |- + curl \ + --request GET \ + --url 'http://localhost:8888/gateway/v2/interceptor?name=interceptor-name&global=true&vcluster=passthrough&group=group1&username=user1' \ + --header 'Authorization: Basic YWRtaW46Y29uZHVrdG9y' + put: + tags: + - cli_interceptor_gateway_v2_12 + description: |2+ + + [![Beta](https://img.shields.io/badge/Lifecycle-Beta-orange)](#tag/Versioning) + + Upsert an interceptor + + operationId: Upsert an interceptor + parameters: + - name: dryMode + in: query + description: Whether to simulate the operation or not + required: false + schema: + default: false + type: boolean + example: true + requestBody: + content: + application/json: + schema: + $ref: '#/components/schemas/Interceptor' + example: + kind: Interceptor + apiVersion: gateway/v2 + metadata: + name: yellow_cars_filter + scope: + vCluster: vCluster1 + spec: + comment: Filter yellow cars + pluginClass: io.conduktor.gateway.interceptor.VirtualSqlTopicPlugin + priority: 1 + config: + virtualTopic: yellow_cars + statement: SELECT '$.type' as type, '$.price' as price FROM cars + WHERE '$.color' = 'yellow' + required: true + responses: + '200': + description: '' + content: + application/json: + schema: + $ref: '#/components/schemas/ApplyResult_Interceptor' + example: + resource: + kind: Interceptor + apiVersion: gateway/v2 + metadata: + name: yellow_cars_filter + scope: + vCluster: vCluster1 + spec: + comment: Filter yellow cars + pluginClass: io.conduktor.gateway.interceptor.VirtualSqlTopicPlugin + priority: 1 + config: + virtualTopic: yellow_cars + statement: SELECT '$.type' as type, '$.price' as price FROM + cars WHERE '$.color' = 'yellow' + upsertResult: CREATED + '400': + description: The request is not valid + content: + application/json: + schema: + $ref: '#/components/schemas/BadRequest' + example: + title: The request is not valid + '401': + description: The given credentials are not valid + content: + application/json: + schema: + $ref: '#/components/schemas/Unauthorized' + example: + title: The given credentials are not valid + '404': + description: The virtual cluster or the group specified in the scope does + not exist + content: + application/json: + schema: + $ref: '#/components/schemas/NotFound' + example: + title: The virtual cluster or the group specified in the scope does + not exist + '500': + description: An unexpected error occurred in the server + content: + application/json: + schema: + $ref: '#/components/schemas/ServerError' + example: + title: An unexpected error occurred in the server + security: + - httpAuth: [] + x-codeSamples: + - lang: Shell + source: |- + curl \ + --request PUT \ + --url 'http://localhost:8888/gateway/v2/interceptor?dryMode=false' \ + --header 'Authorization: Basic YWRtaW46Y29uZHVrdG9y' \ + --header 'Content-Type: application/json' \ + --data-raw '{ + "kind" : "Interceptor", + "apiVersion" : "gateway/v2", + "metadata" : { + "name" : "yellow_cars_filter", + "scope" : { + "vCluster" : "vCluster1", + "group" : null, + "username" : null + } + }, + "spec" : { + "comment" : "Filter yellow cars", + "pluginClass" : "io.conduktor.gateway.interceptor.VirtualSqlTopicPlugin", + "priority" : 1, + "config" : { + "virtualTopic" : "yellow_cars", + "statement" : "SELECT \'$.type\' as type, \'$.price\' as price FROM cars WHERE \'$.color\' = \'yellow\'" + } + } + }' + /gateway/v2/interceptor/{name}: + delete: + tags: + - cli_interceptor_gateway_v2_12 + description: |2+ + + [![Beta](https://img.shields.io/badge/Lifecycle-Beta-orange)](#tag/Versioning) + + Delete an interceptor + + operationId: Delete an interceptor + parameters: + - name: name + in: path + required: true + schema: + type: string + requestBody: + content: + application/json: + schema: + $ref: '#/components/schemas/InterceptorScope' + required: true + responses: + '204': + description: '' + '400': + description: The request is not valid + content: + application/json: + schema: + $ref: '#/components/schemas/BadRequest' + example: + title: The request is not valid + '401': + description: The given credentials are not valid + content: + application/json: + schema: + $ref: '#/components/schemas/Unauthorized' + example: + title: The given credentials are not valid + '404': + description: The given interceptor does not exist + content: + application/json: + schema: + $ref: '#/components/schemas/NotFound' + example: + title: The given interceptor does not exist + '500': + description: An unexpected error occurred in the server + content: + application/json: + schema: + $ref: '#/components/schemas/ServerError' + example: + title: An unexpected error occurred in the server + security: + - httpAuth: [] + x-codeSamples: + - lang: Shell + source: |- + curl \ + --request DELETE \ + --url 'http://localhost:8888/gateway/v2/interceptor/yellow_cars_filter' \ + --header 'Authorization: Basic YWRtaW46Y29uZHVrdG9y' \ + --header 'Content-Type: application/json' \ + --data-raw '{ + "vCluster" : "vCluster1", + "group" : null, + "username" : null + }' + /gateway/v2/interceptor/resolve: + post: + tags: + - cli_interceptor_gateway_v2_12 + description: |2+ + + [![Beta](https://img.shields.io/badge/Lifecycle-Beta-orange)](#tag/Versioning) + + A utility endpoint to resolve the interceptors for a given virtual cluster, groups and username. + Helps to understand which interceptors will be applied for a given request. + + operationId: Resolve interceptors + requestBody: + content: + application/json: + schema: + $ref: '#/components/schemas/InterceptorResolverRequest' + required: true + responses: + '200': + description: '' + content: + application/json: + schema: + type: array + uniqueItems: true + items: + $ref: '#/components/schemas/Interceptor' + example: + - kind: Interceptor + apiVersion: gateway/v2 + metadata: + name: yellow_cars_filter + scope: + vCluster: vCluster1 + spec: + comment: Filter yellow cars + pluginClass: io.conduktor.gateway.interceptor.VirtualSqlTopicPlugin + priority: 1 + config: + virtualTopic: yellow_cars + statement: SELECT '$.type' as type, '$.price' as price FROM cars + WHERE '$.color' = 'yellow' + '400': + description: The request is not valid + content: + application/json: + schema: + $ref: '#/components/schemas/BadRequest' + example: + title: The request is not valid + '401': + description: The given credentials are not valid + content: + application/json: + schema: + $ref: '#/components/schemas/Unauthorized' + example: + title: The given credentials are not valid + '500': + description: An unexpected error occurred in the server + content: + application/json: + schema: + $ref: '#/components/schemas/ServerError' + example: + title: An unexpected error occurred in the server + security: + - httpAuth: [] + x-codeSamples: + - lang: Shell + source: |- + curl \ + --request POST \ + --url 'http://localhost:8888/gateway/v2/interceptor/resolve' \ + --header 'Authorization: Basic YWRtaW46Y29uZHVrdG9y' \ + --header 'Content-Type: application/json' \ + --data-raw '{ + "vCluster" : "passthrough", + "groups" : [ + "group1", + "group2" + ], + "username" : "user1" + }' + /gateway/v2/plugin: + get: + tags: + - plugin + description: |2+ + + [![Beta](https://img.shields.io/badge/Lifecycle-Beta-orange)](#tag/Versioning) + + List the available plugins of the gateway + + operationId: List the Plugins of the Gateway + responses: + '200': + description: '' + content: + application/json: + schema: + type: array + uniqueItems: true + items: + $ref: '#/components/schemas/Plugin' + example: + - plugin: io.conduktor.gateway.interceptor.chaos.SimulateSlowProducersConsumersPlugin + pluginId: io.conduktor.gateway.interceptor.chaos.SimulateSlowProducersConsumersPlugin + readme: |2+ + + --- + version: ${project.version} + title: Simulate Slow Producers and Consumers + description: Validate your application behaves correctly when there are delays in responses from the Kafka cluster. + parent: console + license: enterprise + --- + + ## Introduction + + This interceptor slows responses from the brokers. + + It will operate only on a set of topics rather than all traffic. + + This interceptor only works on Produce requests and Fetch requests. + + ## Configuration + + | key | type | default | description | + |:--------------|:--------|:--------|:----------------------------------------------------------------| + | topic | String | `.*` | Topics that match this regex will have the interceptor applied. | + | rateInPercent | int | | The percentage of requests that will apply this interceptor | + | minLatencyMs | int | | Minimum for the random response latency in milliseconds | + | maxLatencyMs | int | | Maximum for the random response latency in milliseconds | + + ## Example + + ```json + { + "name": "mySimulateSlowProducersConsumersInterceptor", + "pluginClass": "io.conduktor.gateway.interceptor.chaos.SimulateSlowProducersConsumersPlugin", + "priority": 100, + "config": { + "rateInPercent": 100, + "minLatencyMs": 50, + "maxLatencyMs": 1200 + } + } + ``` + + parent: Console + license: enterprise + description: Validate your application behaves correctly when broker + errors occur. + title: Broker errors + version: 3.0.1 + '401': + description: The given credentials are not valid + content: + application/json: + schema: + $ref: '#/components/schemas/Unauthorized' + example: + title: The given credentials are not valid + '500': + description: An unexpected error occurred in the server + content: + application/json: + schema: + $ref: '#/components/schemas/ServerError' + example: + title: An unexpected error occurred in the server + security: + - httpAuth: [] + x-codeSamples: + - lang: Shell + source: |- + curl \ + --request GET \ + --url 'http://localhost:8888/gateway/v2/plugin' \ + --header 'Authorization: Basic YWRtaW46Y29uZHVrdG9y' + /gateway/v2/service-account: + get: + tags: + - cli_gateway-service-account_gateway_v2_10 + description: |2+ + + + [![Beta](https://img.shields.io/badge/Lifecycle-Beta-orange)](#tag/Versioning) + + List the service accounts + + operationId: List the service accounts + parameters: + - name: vcluster + in: query + description: Filter by virtual cluster + required: false + schema: + type: string + - name: name + in: query + description: Filter by name + required: false + schema: + type: string + - name: type + in: query + description: Filter by type (External or Local) + required: false + schema: + type: string + - name: showDefaults + in: query + description: Whether to show default values or not + required: false + schema: + default: false + type: boolean + example: true + responses: + '200': + description: '' + content: + application/json: + schema: + type: array + items: + $ref: '#/components/schemas/GatewayServiceAccount' + example: + - kind: GatewayServiceAccount + apiVersion: gateway/v2 + metadata: + name: user1 + vCluster: vcluster1 + spec: + type: EXTERNAL + externalNames: + - externalName + - kind: GatewayServiceAccount + apiVersion: gateway/v2 + metadata: + name: user1 + vCluster: vcluster1 + spec: + type: LOCAL + '400': + description: The request is not valid + content: + application/json: + schema: + $ref: '#/components/schemas/BadRequest' + example: + title: The request is not valid + '401': + description: The given credentials are not valid + content: + application/json: + schema: + $ref: '#/components/schemas/Unauthorized' + example: + title: The given credentials are not valid + '500': + description: An unexpected error occurred in the server + content: + application/json: + schema: + $ref: '#/components/schemas/ServerError' + example: + title: An unexpected error occurred in the server + security: + - httpAuth: [] + x-codeSamples: + - lang: Shell + source: |- + curl \ + --request GET \ + --url 'http://localhost:8888/gateway/v2/service-account?vcluster=vCluster1&name=user1&type=External&showDefaults=false' \ + --header 'Authorization: Basic YWRtaW46Y29uZHVrdG9y' + put: + tags: + - cli_gateway-service-account_gateway_v2_10 + description: |2+ + + + [![Beta](https://img.shields.io/badge/Lifecycle-Beta-orange)](#tag/Versioning) + + Upsert a service account + + operationId: Upsert a service account + parameters: + - name: dryMode + in: query + description: Whether to simulate the operation or not + required: false + schema: + default: false + type: boolean + example: true + requestBody: + content: + application/json: + schema: + $ref: '#/components/schemas/GatewayServiceAccount' + example: + kind: GatewayServiceAccount + apiVersion: gateway/v2 + metadata: + name: user1 + vCluster: vcluster1 + spec: + type: EXTERNAL + externalNames: + - externalName + required: true + responses: + '200': + description: '' + content: + application/json: + schema: + $ref: '#/components/schemas/ApplyResult_GatewayServiceAccount' + example: + resource: + kind: GatewayServiceAccount + apiVersion: gateway/v2 + metadata: + name: user1 + vCluster: vcluster1 + spec: + type: EXTERNAL + externalNames: + - externalName + upsertResult: CREATED + '400': + description: The request is not valid + content: + application/json: + schema: + $ref: '#/components/schemas/BadRequest' + example: + title: The request is not valid + '401': + description: The given credentials are not valid + content: + application/json: + schema: + $ref: '#/components/schemas/Unauthorized' + example: + title: The given credentials are not valid + '404': + description: The given service account references a non-existing virtual + cluster + content: + application/json: + schema: + $ref: '#/components/schemas/NotFound' + example: + title: The given service account references a non-existing virtual + cluster + '409': + description: The service account already exist + content: + application/json: + schema: + $ref: '#/components/schemas/Conflict' + example: + title: The service account already exist + '500': + description: An unexpected error occurred in the server + content: + application/json: + schema: + $ref: '#/components/schemas/ServerError' + example: + title: An unexpected error occurred in the server + security: + - httpAuth: [] + x-codeSamples: + - lang: Shell + source: |- + curl \ + --request PUT \ + --url 'http://localhost:8888/gateway/v2/service-account?dryMode=false' \ + --header 'Authorization: Basic YWRtaW46Y29uZHVrdG9y' \ + --header 'Content-Type: application/json' \ + --data-raw '{ + "kind" : "GatewayServiceAccount", + "apiVersion" : "gateway/v2", + "metadata" : { + "name" : "user1", + "vCluster" : "vcluster1" + }, + "spec" : { + "type" : "EXTERNAL", + "externalNames" : [ + "externalName" + ] + } + }' + delete: + tags: + - cli_gateway-service-account_gateway_v2_10 + description: |2+ + + + [![Beta](https://img.shields.io/badge/Lifecycle-Beta-orange)](#tag/Versioning) + + Delete a service account + + operationId: Delete a service account + requestBody: + content: + application/json: + schema: + $ref: '#/components/schemas/GatewayServiceAccountId' + required: true + responses: + '204': + description: '' + '400': + description: Gateway service account is not supported on security protocol + PLAINTEXT. + content: + application/json: + schema: + $ref: '#/components/schemas/BadRequest' + example: + title: Gateway service account is not supported on security protocol + PLAINTEXT. + '401': + description: The given credentials are not valid + content: + application/json: + schema: + $ref: '#/components/schemas/Unauthorized' + example: + title: The given credentials are not valid + '404': + description: The given service account does not exist + content: + application/json: + schema: + $ref: '#/components/schemas/NotFound' + example: + title: The given service account does not exist + '409': + description: The service account is still used by groups + content: + application/json: + schema: + $ref: '#/components/schemas/Conflict' + example: + title: The service account is still used by groups + '500': + description: An unexpected error occurred in the server + content: + application/json: + schema: + $ref: '#/components/schemas/ServerError' + example: + title: An unexpected error occurred in the server + security: + - httpAuth: [] + x-codeSamples: + - lang: Shell + source: |- + curl \ + --request DELETE \ + --url 'http://localhost:8888/gateway/v2/service-account' \ + --header 'Authorization: Basic YWRtaW46Y29uZHVrdG9y' \ + --header 'Content-Type: application/json' \ + --data-raw '{ + "name" : "user1" + }' + /gateway/v2/group: + get: + tags: + - cli_gateway-group_gateway_v2_11 + description: |2+ + + [![Beta](https://img.shields.io/badge/Lifecycle-Beta-orange)](#tag/Versioning) + + List the groups + + operationId: List the groups + parameters: + - name: showDefaults + in: query + description: Whether to show default values or not + required: false + schema: + default: false + type: boolean + example: true + responses: + '200': + description: '' + content: + application/json: + schema: + type: array + items: + $ref: '#/components/schemas/GatewayGroup' + example: + - kind: GatewayGroup + apiVersion: gateway/v2 + metadata: + name: group1 + spec: + members: + - vCluster: vCluster1 + name: serviceAccount1 + - vCluster: vCluster2 + name: serviceAccount2 + - vCluster: vCluster3 + name: serviceAccount3 + externalGroups: + - GROUP_READER + - GROUP_WRITER + '400': + description: 'Invalid value for: query parameter showDefaults' + content: + text/plain: + schema: + type: string + '401': + description: The given credentials are not valid + content: + application/json: + schema: + $ref: '#/components/schemas/Unauthorized' + example: + title: The given credentials are not valid + '500': + description: An unexpected error occurred in the server + content: + application/json: + schema: + $ref: '#/components/schemas/ServerError' + example: + title: An unexpected error occurred in the server + security: + - httpAuth: [] + x-codeSamples: + - lang: Shell + source: |- + curl \ + --request GET \ + --url 'http://localhost:8888/gateway/v2/group?showDefaults=false' \ + --header 'Authorization: Basic YWRtaW46Y29uZHVrdG9y' + put: + tags: + - cli_gateway-group_gateway_v2_11 + description: |2+ + + [![Beta](https://img.shields.io/badge/Lifecycle-Beta-orange)](#tag/Versioning) + + Upsert a group + + operationId: Upsert a group + parameters: + - name: dryMode + in: query + description: Whether to simulate the operation or not + required: false + schema: + default: false + type: boolean + example: true + requestBody: + content: + application/json: + schema: + $ref: '#/components/schemas/GatewayGroup' + example: + kind: GatewayGroup + apiVersion: gateway/v2 + metadata: + name: group1 + spec: + members: + - vCluster: vCluster1 + name: serviceAccount1 + - vCluster: vCluster2 + name: serviceAccount2 + - vCluster: vCluster3 + name: serviceAccount3 + externalGroups: + - GROUP_READER + - GROUP_WRITER + required: true + responses: + '200': + description: '' + content: + application/json: + schema: + $ref: '#/components/schemas/ApplyResult_GatewayGroup' + example: + resource: + kind: GatewayGroup + apiVersion: gateway/v2 + metadata: + name: group1 + spec: + members: + - vCluster: vCluster1 + name: serviceAccount1 + - vCluster: vCluster2 + name: serviceAccount2 + - vCluster: vCluster3 + name: serviceAccount3 + externalGroups: + - GROUP_READER + - GROUP_WRITER + upsertResult: UPDATED + '400': + description: The request is not valid + content: + application/json: + schema: + $ref: '#/components/schemas/BadRequest' + example: + title: The request is not valid + '401': + description: The given credentials are not valid + content: + application/json: + schema: + $ref: '#/components/schemas/Unauthorized' + example: + title: The given credentials are not valid + '404': + description: The group contains a service account that does not exist + content: + application/json: + schema: + $ref: '#/components/schemas/NotFound' + example: + title: The group contains a service account that does not exist + '500': + description: An unexpected error occurred in the server + content: + application/json: + schema: + $ref: '#/components/schemas/ServerError' + example: + title: An unexpected error occurred in the server + security: + - httpAuth: [] + x-codeSamples: + - lang: Shell + source: |- + curl \ + --request PUT \ + --url 'http://localhost:8888/gateway/v2/group?dryMode=false' \ + --header 'Authorization: Basic YWRtaW46Y29uZHVrdG9y' \ + --header 'Content-Type: application/json' \ + --data-raw '{ + "kind" : "GatewayGroup", + "apiVersion" : "gateway/v2", + "metadata" : { + "name" : "group1" + }, + "spec" : { + "members" : [ + { + "vCluster" : "vCluster1", + "name" : "serviceAccount1" + }, + { + "vCluster" : "vCluster2", + "name" : "serviceAccount2" + }, + { + "vCluster" : "vCluster3", + "name" : "serviceAccount3" + } + ], + "externalGroups" : [ + "GROUP_READER", + "GROUP_WRITER" + ] + } + }' + /gateway/v2/group/{name}: + get: + tags: + - cli_gateway-group_gateway_v2_11 + description: |2+ + + [![Beta](https://img.shields.io/badge/Lifecycle-Beta-orange)](#tag/Versioning) + + Get a group + + operationId: Get a group + parameters: + - name: name + in: path + required: true + schema: + type: string + - name: showDefaults + in: query + description: Whether to show default values or not + required: false + schema: + default: false + type: boolean + example: true + responses: + '200': + description: '' + content: + application/json: + schema: + $ref: '#/components/schemas/GatewayGroup' + example: + kind: GatewayGroup + apiVersion: gateway/v2 + metadata: + name: group1 + spec: + members: + - vCluster: vCluster1 + name: serviceAccount1 + - vCluster: vCluster2 + name: serviceAccount2 + - vCluster: vCluster3 + name: serviceAccount3 + externalGroups: + - GROUP_READER + - GROUP_WRITER + '400': + description: 'Invalid value for: query parameter showDefaults' + content: + text/plain: + schema: + type: string + '401': + description: The given credentials are not valid + content: + application/json: + schema: + $ref: '#/components/schemas/Unauthorized' + example: + title: The given credentials are not valid + '404': + description: The given group does not exist + content: + application/json: + schema: + $ref: '#/components/schemas/NotFound' + example: + title: The given group does not exist + '500': + description: An unexpected error occurred in the server + content: + application/json: + schema: + $ref: '#/components/schemas/ServerError' + example: + title: An unexpected error occurred in the server + security: + - httpAuth: [] + x-codeSamples: + - lang: Shell + source: |- + curl \ + --request GET \ + --url 'http://localhost:8888/gateway/v2/group/group1?showDefaults=false' \ + --header 'Authorization: Basic YWRtaW46Y29uZHVrdG9y' + delete: + tags: + - cli_gateway-group_gateway_v2_11 + description: |2+ + + [![Beta](https://img.shields.io/badge/Lifecycle-Beta-orange)](#tag/Versioning) + + Delete a group + + operationId: Delete a group + parameters: + - name: name + in: path + required: true + schema: + type: string + responses: + '204': + description: '' + '401': + description: The given credentials are not valid + content: + application/json: + schema: + $ref: '#/components/schemas/Unauthorized' + example: + title: The given credentials are not valid + '404': + description: The given group does not exist + content: + application/json: + schema: + $ref: '#/components/schemas/NotFound' + example: + title: The given group does not exist + '409': + description: The group is still referenced by interceptors + content: + application/json: + schema: + $ref: '#/components/schemas/Conflict' + example: + title: The group is still referenced by interceptors + '500': + description: An unexpected error occurred in the server + content: + application/json: + schema: + $ref: '#/components/schemas/ServerError' + example: + title: An unexpected error occurred in the server + security: + - httpAuth: [] + x-codeSamples: + - lang: Shell + source: |- + curl \ + --request DELETE \ + --url 'http://localhost:8888/gateway/v2/group/group1' \ + --header 'Authorization: Basic YWRtaW46Y29uZHVrdG9y' +components: + schemas: + AliasTopic: + title: AliasTopic + type: object + required: + - kind + - apiVersion + - metadata + - spec + properties: + kind: + description: The kind of the alias topic + type: string + apiVersion: + description: The api version of the alias topic + type: string + metadata: + $ref: '#/components/schemas/AliasTopicMetadata' + spec: + $ref: '#/components/schemas/AliasTopicSpec' + AliasTopicId: + title: AliasTopicId + type: object + required: + - name + properties: + name: + description: The name of the alias topic + type: string + vCluster: + description: 'The virtual cluster of the alias topic (default: `passthrough`)' + type: string + AliasTopicMetadata: + title: AliasTopicMetadata + description: The metadata of the alias topic + type: object + required: + - name + properties: + name: + description: The name of the alias topic + type: string + format: ^[a-zA-Z0-9._-]+$ + vCluster: + description: 'The virtual cluster of the alias topic (default: `passthrough`)' + type: string + format: ^[a-zA-Z0-9_-]+$ + AliasTopicSpec: + title: AliasTopicSpec + description: The specification of the alias topic + type: object + required: + - physicalName + properties: + physicalName: + description: The physical name of the alias topic + type: string + format: ^[a-zA-Z0-9._-]+$ + physicalCluster: + description: |2 + + The physical kafka cluster hosting the physical topic. + The field is optional and only useful for alias topics referencing __Partner__ virtual clusters. + For alias topics linked to virtual clusters of type __Standard__, the physical cluster can only be "main" (automatically set to "main" if the field is missing). + The physical cluster must be one of the configured list of physical clusters of the gateway. + type: string + ApplyResult_AliasTopic: + title: ApplyResult_AliasTopic + type: object + required: + - resource + - upsertResult + properties: + resource: + $ref: '#/components/schemas/AliasTopic' + description: The resource that was upserted + upsertResult: + $ref: '#/components/schemas/UpsertResult' + ApplyResult_ConcentrationRule: + title: ApplyResult_ConcentrationRule + type: object + required: + - resource + - upsertResult + properties: + resource: + $ref: '#/components/schemas/ConcentrationRule' + description: The resource that was upserted + upsertResult: + $ref: '#/components/schemas/UpsertResult' + ApplyResult_GatewayGroup: + title: ApplyResult_GatewayGroup + type: object + required: + - resource + - upsertResult + properties: + resource: + $ref: '#/components/schemas/GatewayGroup' + description: The resource that was upserted + upsertResult: + $ref: '#/components/schemas/UpsertResult' + ApplyResult_GatewayServiceAccount: + title: ApplyResult_GatewayServiceAccount + type: object + required: + - resource + - upsertResult + properties: + resource: + $ref: '#/components/schemas/GatewayServiceAccount' + description: The resource that was upserted + upsertResult: + $ref: '#/components/schemas/UpsertResult' + ApplyResult_Interceptor: + title: ApplyResult_Interceptor + type: object + required: + - resource + - upsertResult + properties: + resource: + $ref: '#/components/schemas/Interceptor' + description: The resource that was upserted + upsertResult: + $ref: '#/components/schemas/UpsertResult' + ApplyResult_VirtualCluster: + title: ApplyResult_VirtualCluster + type: object + required: + - resource + - upsertResult + properties: + resource: + $ref: '#/components/schemas/VirtualCluster' + description: The resource that was upserted + upsertResult: + $ref: '#/components/schemas/UpsertResult' + BadRequest: + title: BadRequest + type: object + required: + - title + properties: + title: + type: string + msg: + type: string + cause: + type: string + ConcentratedTopic: + title: ConcentratedTopic + type: object + required: + - kind + - apiVersion + - metadata + - spec + properties: + kind: + description: The kind of the concentrated topic + type: string + apiVersion: + description: The api version of the concentrated topic + type: string + metadata: + $ref: '#/components/schemas/ConcentratedTopicMetadata' + spec: + $ref: '#/components/schemas/ConcentratedTopicSpec' + ConcentratedTopicMetadata: + title: ConcentratedTopicMetadata + description: The metadata of the concentrated topic + type: object + required: + - name + properties: + name: + description: The name of the concentrated topic + type: string + vCluster: + description: The virtual cluster of the concentrated topic. If not provided, + defaulted to passthrough virtual cluster. + type: string + ConcentratedTopicSpec: + title: ConcentratedTopicSpec + description: The specification of the concentrated topic + type: object + required: + - physicalName + properties: + physicalName: + description: The physical name of the concentrated topic + type: string + ConcentrationRule: + title: ConcentrationRule + type: object + required: + - kind + - apiVersion + - metadata + - spec + properties: + kind: + description: The kind of the concentration rule + type: string + apiVersion: + description: The api version of the concentration rule + type: string + metadata: + $ref: '#/components/schemas/ConcentrationRuleMetadata' + spec: + $ref: '#/components/schemas/ConcentrationRuleSpec' + ConcentrationRuleId: + title: ConcentrationRuleId + type: object + required: + - name + properties: + name: + description: The name of the concentration rule (identifier in a vCluster) + type: string + vCluster: + description: The virtual cluster of the concentration rule. Default to `passthrough` + if not provided. + type: string + ConcentrationRuleMetadata: + title: ConcentrationRuleMetadata + description: The metadata of the concentration rule + type: object + required: + - name + properties: + name: + description: The name of the concentration rule (identifier in a vCluster) + type: string + format: ^[a-zA-Z0-9._-]+$ + vCluster: + description: The virtual cluster of the concentration rule. Default to `passthrough` + if not provided. + type: string + ConcentrationRuleSpec: + title: ConcentrationRuleSpec + description: The specification of the concentration rule + type: object + required: + - pattern + - physicalTopics + properties: + pattern: + description: The pattern of the concentration rule + type: string + physicalTopics: + $ref: '#/components/schemas/PhysicalTopics' + autoManaged: + description: Whether the concentration rule is auto managed + type: boolean + offsetCorrectness: + description: |2 + + Set the offset management on/off for the logical topics created with this rule (default to false). + Setting it on can have some impacts on the performances and the memory usage. + type: boolean + Conflict: + title: Conflict + type: object + required: + - title + properties: + title: + type: string + msg: + type: string + cause: + type: string + Created: + title: Created + type: object + External: + title: External + type: object + required: + - kind + - apiVersion + - metadata + - spec + properties: + kind: + description: The kind of the service account + type: string + apiVersion: + description: The api version of the service account + type: string + metadata: + $ref: '#/components/schemas/ExternalMetadata' + spec: + $ref: '#/components/schemas/ExternalSpec' + ExternalMetadata: + title: ExternalMetadata + description: Metadata of the service account + type: object + required: + - name + properties: + name: + description: The name of the service account (identifier) + type: string + format: ^[a-zA-Z0-9_-]{3,64}$ + vCluster: + description: |2 + + The name of the virtual cluster the service account belongs to. + + If not provided, the service account will be created in the default `passthrough` virtual cluster. + type: string + format: ^[a-zA-Z0-9_-]+$ + ExternalSpec: + title: ExternalSpec + description: Spec of the service account + type: object + required: + - type + properties: + externalNames: + description: |2 + + List of the external names of the service account. + Used to keep a fixed reference of the service account in interceptors or groups if the values of the external user DB happen to change + (ex: OIDC principal, key in delegated auth mode with Confluent etc...) + At the moment, an external service account should have exactly one external name (The support for many external names will be available soon) + type: array + items: + type: string + type: + description: 'The type of the service account : `LOCAL` or `EXTERNAL` (case + insensitive)' + type: string + GatewayGroup: + title: GatewayGroup + type: object + required: + - kind + - apiVersion + - metadata + - spec + properties: + kind: + description: The kind of the group + type: string + apiVersion: + description: The api version of the group + type: string + metadata: + $ref: '#/components/schemas/GroupMetadata' + spec: + $ref: '#/components/schemas/GroupSpec' + GatewayServiceAccount: + title: GatewayServiceAccount + oneOf: + - $ref: '#/components/schemas/External' + - $ref: '#/components/schemas/Local' + GatewayServiceAccountId: + title: GatewayServiceAccountId + type: object + required: + - name + properties: + vCluster: + description: |2 + + The name of the virtual cluster the service account belongs to. + + If not provided, the service account will be created in the default `passthrough` virtual cluster. + type: string + name: + description: The name of the service account (identifier) + type: string + GroupMetadata: + title: GroupMetadata + description: The metadata of the group + type: object + required: + - name + properties: + name: + description: The name of the group + type: string + format: ^[a-zA-Z0-9_-]{1,100}$ + GroupSpec: + title: GroupSpec + description: The specification of the group + type: object + properties: + members: + description: The service accounts belonging to the group + type: array + uniqueItems: true + items: + $ref: '#/components/schemas/GatewayServiceAccountId' + externalGroups: + description: The external groups (LDAP, OIDC...) mapped on the group + type: array + uniqueItems: true + items: + type: string + Interceptor: + title: Interceptor + type: object + required: + - kind + - apiVersion + - metadata + - spec + properties: + kind: + description: The kind of the interceptor + type: string + apiVersion: + description: The api version of the interceptor + type: string + metadata: + $ref: '#/components/schemas/InterceptorMetadata' + spec: + $ref: '#/components/schemas/InterceptorSpec' + InterceptorMetadata: + title: InterceptorMetadata + description: Metadata of the interceptor + type: object + required: + - name + properties: + name: + description: The name of the interceptor + type: string + scope: + $ref: '#/components/schemas/InterceptorScope' + description: |2 + + The scope of the interceptor. + It can be applied to a specific virtual cluster or group or username. + If none of them is set, it will be applied Globally to the gateway. + InterceptorResolverRequest: + title: InterceptorResolverRequest + type: object + properties: + vCluster: + description: The virtual cluster to test the interceptors resolution + type: string + groups: + description: The groups to test the interceptors resolution + type: array + items: + type: string + username: + description: The username to test the interceptors resolution + type: string + InterceptorScope: + title: InterceptorScope + type: object + properties: + vCluster: + description: An optional vCluster to filter the interceptors + type: string + group: + description: An optional group to filter the interceptors + type: string + username: + description: An optional username to filter the interceptors + type: string + InterceptorSpec: + title: InterceptorSpec + description: Spec of the interceptor + type: object + required: + - pluginClass + - priority + - config + properties: + comment: + description: An optional comment for the interceptor + type: string + pluginClass: + description: The class of the plugin + type: string + priority: + description: The priority of the interceptor + type: integer + format: int32 + config: + $ref: '#/components/schemas/Map_Json' + Local: + title: Local + type: object + required: + - kind + - apiVersion + - metadata + - spec + properties: + kind: + description: The kind of the service account + type: string + apiVersion: + description: The api version of the service account + type: string + metadata: + $ref: '#/components/schemas/LocalMetadata' + spec: + $ref: '#/components/schemas/LocalSpec' + LocalMetadata: + title: LocalMetadata + description: Metadata of the service account + type: object + required: + - name + properties: + name: + description: The name of the service account (identifier) + type: string + format: ^[a-zA-Z0-9_-]{3,64}$ + vCluster: + description: |2 + + The name of the virtual cluster the service account belongs to. + + If not provided, the service account will be created in the default `passthrough` virtual cluster. + type: string + format: ^[a-zA-Z0-9_-]+$ + LocalSpec: + title: LocalSpec + description: Spec of the service account + type: object + required: + - type + properties: + type: + description: 'The type of the service account : `LOCAL` or `EXTERNAL` (case + insensitive)' + type: string + Map_Json: + title: Map_Json + description: The configuration of the interceptor + type: object + additionalProperties: {} + Map_String: + title: Map_String + description: |2 + + The client properties to help create a connection to the virtual cluster. + This field is automatically managed by the gateway + Contains placeholders to replace with your actual values: {{username}}, {{password}}... + type: object + additionalProperties: + type: string + NotChanged: + title: NotChanged + type: object + NotFound: + title: NotFound + type: object + required: + - title + properties: + title: + type: string + msg: + type: string + cause: + type: string + PhysicalTopics: + title: PhysicalTopics + description: The physical topics backing the concentrated logical topics + type: object + required: + - delete + properties: + delete: + description: |2 + + The name of the physical topic to store the data of the concentrated topics created with a 'delete' policy. + The topic must exist in the physical kafka cluster with a 'delete' cleanup.policy (except if you use the autoManaged mode) + type: string + format: ^[a-zA-Z0-9._-]+$ + compact: + description: |2 + + The name of the physical topic to store the data of the concentrated topics created with a 'compact' policy. + If specified, the topic must exist in the physical kafka cluster with a 'compact' cleanup.policy (except if you use the autoManaged mode) + type: string + format: ^[a-zA-Z0-9._-]+$ + deleteCompact: + description: |2 + + The name of the physical topic to store the data of the concentrated topics created with a 'delete,compact' policy. + If specified, the topic must exist in the physical kafka cluster with a 'delete,compact' cleanup.policy (except if you use the autoManaged mode) + type: string + format: ^[a-zA-Z0-9._-]+$ + Plugin: + title: Plugin + type: object + required: + - plugin + - pluginId + - readme + properties: + plugin: + description: The name of the plugin + type: string + pluginId: + description: The id of the plugin + type: string + readme: + description: The readme of the plugin + type: string + parent: + description: The parent of the plugin + type: string + license: + description: The license of the plugin + type: string + description: + description: The description of the plugin + type: string + title: + description: The title of the plugin + type: string + version: + description: The version of the plugin + type: string + ServerError: + title: ServerError + type: object + required: + - title + properties: + title: + type: string + msg: + type: string + cause: + type: string + TokenRequest: + title: TokenRequest + type: object + required: + - username + - lifeTimeSeconds + properties: + vCluster: + description: 'The name of the virtual cluster to create the token for. "passthrough + if omitted" ' + type: string + username: + description: The username of the local service account to create the token + for. + type: string + lifeTimeSeconds: + description: The life time of the token in seconds. + type: integer + format: int64 + TokenResponse: + title: TokenResponse + type: object + required: + - token + properties: + token: + description: The token created for the given username on the given vcluster. + type: string + Unauthorized: + title: Unauthorized + type: object + required: + - title + properties: + title: + type: string + msg: + type: string + cause: + type: string + Updated: + title: Updated + type: object + UpsertResult: + title: UpsertResult + description: The result of the upsert operation (created, updated, not changed) + oneOf: + - $ref: '#/components/schemas/Created' + - $ref: '#/components/schemas/NotChanged' + - $ref: '#/components/schemas/Updated' + VirtualCluster: + title: VirtualCluster + type: object + required: + - kind + - apiVersion + - metadata + - spec + properties: + kind: + description: The kind of the virtual cluster + type: string + apiVersion: + description: The api version of the virtual cluster + type: string + metadata: + $ref: '#/components/schemas/VirtualClusterMetadata' + spec: + $ref: '#/components/schemas/VirtualClusterSpec' + VirtualClusterMetadata: + title: VirtualClusterMetadata + description: Metadata of the virtual cluster + type: object + required: + - name + properties: + name: + description: The name of the virtual cluster + type: string + format: ^[a-zA-Z0-9_-]+$ + VirtualClusterSpec: + title: VirtualClusterSpec + description: Spec of the virtual cluster + type: object + properties: + aclEnabled: + description: Enable ACL for the virtual cluster + type: boolean + superUsers: + description: List of service account's username that are super users of + the virtual cluster + type: array + items: + type: string + type: + description: 'The type of the virtual cluster : Standard, Partner' + type: string + bootstrapServers: + description: |2 + + The bootstrap servers to create a connection to the virtual cluster. + This field is automatically managed by the gateway + type: string + clientProperties: + $ref: '#/components/schemas/Map_String' + securitySchemes: + httpAuth: + type: http + scheme: basic +x-tagGroups: +- name: 🐺 Conduktor Gateway API + tags: + - Introduction + - Authentication + - Kinds + - Api Groups + - Versioning + - Conventions +- name: 🌉 gateway/v2 + tags: + - cli_virtual-cluster_gateway_v2_7 + - cli_alias-topic_gateway_v2_8 + - cli_concentrated-topic_gateway_v2_0 + - cli_concentration-rule_gateway_v2_9 + - cli_interceptor_gateway_v2_12 + - plugin + - cli_gateway-service-account_gateway_v2_10 + - cli_gateway-group_gateway_v2_11 + - token diff --git a/test_final_exec.sh b/test_final_exec.sh index 67abfef..028f233 100755 --- a/test_final_exec.sh +++ b/test_final_exec.sh @@ -49,6 +49,7 @@ main() { run conduktor delete aliastopic aliastopicname --vcluster=v1 run conduktor delete concentrationrule cr1 --vcluster=v1 run conduktor delete gatewayserviceaccount s1 --vcluster=v1 + run conduktor run partnerZoneGenerateCredentials --partner-zone-name yo } main "$@"