diff --git a/docs/pages/reference/operator-resources/resources.teleport.dev_oktaimportrules.mdx b/docs/pages/reference/operator-resources/resources.teleport.dev_oktaimportrules.mdx index f5000508e5202..c28c50efeefc2 100644 --- a/docs/pages/reference/operator-resources/resources.teleport.dev_oktaimportrules.mdx +++ b/docs/pages/reference/operator-resources/resources.teleport.dev_oktaimportrules.mdx @@ -33,8 +33,8 @@ resource, which you can apply after installing the Teleport Kubernetes operator. |Field|Type|Description| |---|---|---| -|add_labels|[object](#specmappings itemsadd_labels)|AddLabels specifies which labels to add if any of the previous matches match.| -|match|[][object](#specmappings itemsmatch-items)|Match is a set of matching rules for this mapping. If any of these match, then the mapping will be applied.| +|add_labels|[object](#specmappings-itemsadd_labels)|AddLabels specifies which labels to add if any of the previous matches match.| +|match|[][object](#specmappings-itemsmatch-items)|Match is a set of matching rules for this mapping. If any of these match, then the mapping will be applied.| ### spec.mappings items.add_labels diff --git a/integrations/operator/crdgen/format.go b/integrations/operator/crdgen/format.go index c7e39a56a0d3d..3cdd0afb6add3 100644 --- a/integrations/operator/crdgen/format.go +++ b/integrations/operator/crdgen/format.go @@ -138,91 +138,92 @@ const statusDescription = "Status defines the observed state of the Teleport res const statusName = "status" func propertyTable(currentFieldName string, props *apiextv1.JSONSchemaProps) ([]PropertyTable, error) { - switch props.Type { - case "object": - tab := PropertyTable{ - Name: currentFieldName, + // Only create a property table for an object field. For other types, we can + // describe the type within a table row. + if props.Type != "object" { + return nil, nil + } + tab := PropertyTable{ + Name: currentFieldName, + } + fields := []PropertyTableField{} + tables := []PropertyTable{} + var i int + for k, v := range props.Properties { + // Don't document the Status field, which is for + // internal use. + if k == statusName && strings.HasPrefix(v.Description, statusDescription) { + continue + } + // Name the table after the hierarchy of + // field names to avoid duplication. + var tableName string + if currentFieldName != "" { + tableName = currentFieldName + "." + k + } else { + tableName = k } - fields := []PropertyTableField{} - tables := []PropertyTable{} - var i int - for k, v := range props.Properties { - // Don't document the Status field, which is for - // internal use. - if k == statusName && strings.HasPrefix(v.Description, statusDescription) { - continue - } - // Name the table after the hierarchy of - // field names to avoid duplication. - var tableName string - if currentFieldName != "" { - tableName = currentFieldName + "." + k - } else { - tableName = k - } - var fieldType string - var fieldDesc string - switch v.Type { - case "object": - fieldType = "object" - if len(v.Properties) == 0 { - break - } + var fieldType string + var fieldDesc string + switch v.Type { + case "object": + fieldType = "object" + if len(v.Properties) == 0 { + break + } + extra, err := propertyTable( + tableName, + &v, + ) + if err != nil { + return nil, err + } + fieldType = fmt.Sprintf("[object](#%v)", strings.ReplaceAll(strings.ReplaceAll(tableName, ".", ""), " ", "-")) + tables = append(tables, extra...) + case "array": + var subtp string + if v.Items.Schema.Type == "object" { extra, err := propertyTable( - tableName, - &v, + fmt.Sprintf("%v items", tableName), + v.Items.Schema, ) if err != nil { return nil, err } - fieldType = fmt.Sprintf("[object](#%v)", strings.ReplaceAll(tableName, ".", "")) tables = append(tables, extra...) - case "array": - var subtp string - if v.Items.Schema.Type == "object" { - extra, err := propertyTable( - fmt.Sprintf("%v items", tableName), - v.Items.Schema, - ) - if err != nil { - return nil, err - } - tables = append(tables, extra...) - subtp = fmt.Sprintf("[object](#%v-items)", strings.ReplaceAll(tableName, ".", "")) - } else { - subtp = v.Items.Schema.Type - } - fieldType = fmt.Sprintf("[]%v", subtp) - case "": - if !v.XIntOrString { - fieldType = v.Type - break - } - fieldType = "string or integer" - fieldDesc = strings.TrimSuffix(v.Description, ".") + ". " + "Can be either the string or the integer representation of each option." - default: - fieldType = v.Type + subtp = fmt.Sprintf("[object](#%v-items)", strings.ReplaceAll(strings.ReplaceAll(tableName, ".", ""), " ", "-")) + } else { + subtp = v.Items.Schema.Type } - - if fieldDesc == "" { - fieldDesc = v.Description + fieldType = fmt.Sprintf("[]%v", subtp) + case "": + if !v.XIntOrString { + fieldType = v.Type + break } + fieldType = "string or integer" + fieldDesc = strings.TrimSuffix(v.Description, ".") + ". " + "Can be either the string or the integer representation of each option." + default: + fieldType = v.Type + } - fields = append(fields, PropertyTableField{ - Name: k, - Type: fieldType, - Description: fieldDesc, - }) - i++ + if fieldDesc == "" { + fieldDesc = v.Description } - tab.Fields = fields - sort.Sort(tab) - tables = append([]PropertyTable{tab}, tables...) - return tables, nil + + fields = append(fields, PropertyTableField{ + Name: k, + Type: fieldType, + Description: fieldDesc, + }) + i++ } - return nil, nil + tab.Fields = fields + sort.Sort(tab) + tables = append([]PropertyTable{tab}, tables...) + return tables, nil } func formatAsDocsPage(crd apiextv1.CustomResourceDefinition) ([]byte, string, error) { diff --git a/integrations/operator/crdgen/handlerequest_test.go b/integrations/operator/crdgen/handlerequest_test.go index ece3da8555255..710567b7ed3a5 100644 --- a/integrations/operator/crdgen/handlerequest_test.go +++ b/integrations/operator/crdgen/handlerequest_test.go @@ -517,6 +517,73 @@ state of this API Resource.\n---\nThis struct is intended for direct use as an a }, }, }, + { + description: "array of objects with object field", + input: apiextv1.JSONSchemaProps{ + Type: "object", + Properties: map[string]apiextv1.JSONSchemaProps{ + "mappings": apiextv1.JSONSchemaProps{ + Type: "array", + Description: "Mappings is a list of matches that will map match conditions to labels.", + Items: &apiextv1.JSONSchemaPropsOrArray{ + Schema: &apiextv1.JSONSchemaProps{ + Type: "object", + Properties: map[string]apiextv1.JSONSchemaProps{ + "add_labels": apiextv1.JSONSchemaProps{ + Type: "object", + Description: "AddLabels specifies which labels to add if any of the previous matches match.", + Nullable: true, + Properties: map[string]apiextv1.JSONSchemaProps{ + "key": apiextv1.JSONSchemaProps{ + Type: "string", + }, + "value": apiextv1.JSONSchemaProps{ + Type: "string", + }, + }, + }, + }, + }, + }, + }, + }, + }, + expected: []PropertyTable{ + { + Name: "", + Fields: []PropertyTableField{ + { + Name: "mappings", + Type: "[][object](#mappings-items)", + Description: "Mappings is a list of matches that will map match conditions to labels.", + }, + }, + }, + { + Name: "mappings items", + Fields: []PropertyTableField{ + { + Name: "add_labels", + Type: "[object](#mappings-itemsadd_labels)", + Description: "AddLabels specifies which labels to add if any of the previous matches match.", + }, + }, + }, + { + Name: "mappings items.add_labels", + Fields: []PropertyTableField{ + { + Name: "key", + Type: "string", + }, + { + Name: "value", + Type: "string", + }, + }, + }, + }, + }, } for _, c := range cases {