diff --git a/docs/sources/developers/kinds/core/serviceaccount/schema-reference.md b/docs/sources/developers/kinds/core/serviceaccount/schema-reference.md
index 7a9b487e6d1..ba92626deac 100644
--- a/docs/sources/developers/kinds/core/serviceaccount/schema-reference.md
+++ b/docs/sources/developers/kinds/core/serviceaccount/schema-reference.md
@@ -13,26 +13,19 @@ title: ServiceAccount kind
## Properties
-| Property | Type | Required | Description |
-|-----------------|--------------------------|----------|-----------------------------------------------------------------------------------------------------------------------------------------|
-| `avatarUrl` | string | **Yes** | AvatarUrl is the service account's avatar URL. It allows the frontend to display a picture in front
of the service account. |
-| `id` | integer | **Yes** | ID is the unique identifier of the service account in the database. |
-| `isDisabled` | boolean | **Yes** | IsDisabled indicates if the service account is disabled. |
-| `login` | string | **Yes** | Login of the service account. |
-| `name` | string | **Yes** | Name of the service account. |
-| `orgId` | integer | **Yes** | OrgId is the ID of an organisation the service account belongs to. |
-| `role` | string | **Yes** | OrgRole is a Grafana Organization Role which can be 'Viewer', 'Editor', 'Admin'. Possible values are: `Admin`, `Editor`, `Viewer`. |
-| `tokens` | integer | **Yes** | Tokens is the number of active tokens for the service account.
Tokens are used to authenticate the service account against Grafana. |
-| `accessControl` | [object](#accesscontrol) | No | AccessControl metadata associated with a given resource. |
-| `created` | integer | No | Created indicates when the service account was created. |
-| `teams` | string[] | No | Teams is a list of teams the service account belongs to. |
-| `updated` | integer | No | Updated indicates when the service account was updated. |
-
-## accessControl
-
-AccessControl metadata associated with a given resource.
-
-| Property | Type | Required | Description |
-|----------|------|----------|-------------|
+| Property | Type | Required | Description |
+|-----------------|--------------------|----------|-----------------------------------------------------------------------------------------------------------------------------------------|
+| `avatarUrl` | string | **Yes** | AvatarUrl is the service account's avatar URL. It allows the frontend to display a picture in front
of the service account. |
+| `id` | integer | **Yes** | ID is the unique identifier of the service account in the database. |
+| `isDisabled` | boolean | **Yes** | IsDisabled indicates if the service account is disabled. |
+| `login` | string | **Yes** | Login of the service account. |
+| `name` | string | **Yes** | Name of the service account. |
+| `orgId` | integer | **Yes** | OrgId is the ID of an organisation the service account belongs to. |
+| `role` | string | **Yes** | OrgRole is a Grafana Organization Role which can be 'Viewer', 'Editor', 'Admin'. Possible values are: `Admin`, `Editor`, `Viewer`. |
+| `tokens` | integer | **Yes** | Tokens is the number of active tokens for the service account.
Tokens are used to authenticate the service account against Grafana. |
+| `accessControl` | map[string]boolean | No | AccessControl metadata associated with a given resource. |
+| `created` | integer | No | Created indicates when the service account was created. |
+| `teams` | string[] | No | Teams is a list of teams the service account belongs to. |
+| `updated` | integer | No | Updated indicates when the service account was updated. |
diff --git a/docs/sources/developers/kinds/core/team/schema-reference.md b/docs/sources/developers/kinds/core/team/schema-reference.md
index e58402f70d4..36a89101065 100644
--- a/docs/sources/developers/kinds/core/team/schema-reference.md
+++ b/docs/sources/developers/kinds/core/team/schema-reference.md
@@ -13,23 +13,16 @@ title: Team kind
## Properties
-| Property | Type | Required | Description |
-|-----------------|--------------------------|----------|----------------------------------------------------------|
-| `created` | integer | **Yes** | Created indicates when the team was created. |
-| `memberCount` | integer | **Yes** | MemberCount is the number of the team members. |
-| `name` | string | **Yes** | Name of the team. |
-| `orgId` | integer | **Yes** | OrgId is the ID of an organisation the team belongs to. |
-| `permission` | integer | **Yes** | Possible values are: `0`, `1`, `2`, `4`. |
-| `updated` | integer | **Yes** | Updated indicates when the team was updated. |
-| `accessControl` | [object](#accesscontrol) | No | AccessControl metadata associated with a given resource. |
-| `avatarUrl` | string | No | AvatarUrl is the team's avatar URL. |
-| `email` | string | No | Email of the team. |
-
-## accessControl
-
-AccessControl metadata associated with a given resource.
-
-| Property | Type | Required | Description |
-|----------|------|----------|-------------|
+| Property | Type | Required | Description |
+|-----------------|--------------------|----------|----------------------------------------------------------|
+| `created` | integer | **Yes** | Created indicates when the team was created. |
+| `memberCount` | integer | **Yes** | MemberCount is the number of the team members. |
+| `name` | string | **Yes** | Name of the team. |
+| `orgId` | integer | **Yes** | OrgId is the ID of an organisation the team belongs to. |
+| `permission` | integer | **Yes** | Possible values are: `0`, `1`, `2`, `4`. |
+| `updated` | integer | **Yes** | Updated indicates when the team was updated. |
+| `accessControl` | map[string]boolean | No | AccessControl metadata associated with a given resource. |
+| `avatarUrl` | string | No | AvatarUrl is the team's avatar URL. |
+| `email` | string | No | Email of the team. |
diff --git a/pkg/codegen/jenny_docs.go b/pkg/codegen/jenny_docs.go
index 6718bd67d77..a802e9dc74e 100644
--- a/pkg/codegen/jenny_docs.go
+++ b/pkg/codegen/jenny_docs.go
@@ -108,18 +108,44 @@ type templateData struct {
// Copied from https://github.com/marcusolsson/json-schema-docs and slightly changed to fit the DocsJenny
type schema struct {
- ID string `json:"$id,omitempty"`
- Ref string `json:"$ref,omitempty"`
- Schema string `json:"$schema,omitempty"`
- Title string `json:"title,omitempty"`
- Description string `json:"description,omitempty"`
- Required []string `json:"required,omitempty"`
- Type PropertyTypes `json:"type,omitempty"`
- Properties map[string]*schema `json:"properties,omitempty"`
- Items *schema `json:"items,omitempty"`
- Definitions map[string]*schema `json:"definitions,omitempty"`
- Enum []Any `json:"enum"`
- Default any `json:"default"`
+ ID string `json:"$id,omitempty"`
+ Ref string `json:"$ref,omitempty"`
+ Schema string `json:"$schema,omitempty"`
+ Title string `json:"title,omitempty"`
+ Description string `json:"description,omitempty"`
+ Required []string `json:"required,omitempty"`
+ Type PropertyTypes `json:"type,omitempty"`
+ Properties map[string]*schema `json:"properties,omitempty"`
+ Items *schema `json:"items,omitempty"`
+ Definitions map[string]*schema `json:"definitions,omitempty"`
+ Enum []Any `json:"enum"`
+ AdditionalProperties *schema `json:"additionalProperties"`
+ Default any `json:"default"`
+}
+
+func renderMapType(props *schema) string {
+ if props == nil {
+ return ""
+ }
+
+ if props.Type.HasType(PropertyTypeObject) {
+ name, anchor := propNameAndAnchor(props.Title, props.Title)
+ return fmt.Sprintf("[%s](#%s)", name, anchor)
+ }
+
+ if props.AdditionalProperties != nil {
+ return "map[string]" + renderMapType(props.AdditionalProperties)
+ }
+
+ if props.Items != nil {
+ return "[]" + renderMapType(props.Items)
+ }
+
+ var types []string
+ for _, t := range props.Type {
+ types = append(types, string(t))
+ }
+ return strings.Join(types, ", ")
}
func jsonToMarkdown(jsonData []byte, tpl string, kindName string) ([]byte, error) {
@@ -189,6 +215,21 @@ func resolveSchema(schem *schema, root *simplejson.Json) (*schema, error) {
*schem.Items = *foo
}
+ if schem.AdditionalProperties != nil {
+ if schem.AdditionalProperties.Ref != "" {
+ tmp, err := resolveReference(schem.AdditionalProperties.Ref, root)
+ if err != nil {
+ return nil, err
+ }
+ *schem.AdditionalProperties = *tmp
+ }
+ foo, err := resolveSchema(schem.AdditionalProperties, root)
+ if err != nil {
+ return nil, err
+ }
+ *schem.AdditionalProperties = *foo
+ }
+
return schem, nil
}
@@ -242,7 +283,7 @@ func (s schema) Markdown(level int) string {
var buf bytes.Buffer
- if s.Title != "" {
+ if s.Title != "" && s.AdditionalProperties == nil {
fmt.Fprintln(&buf, makeHeading(s.Title, level))
fmt.Fprintln(&buf)
}
@@ -289,9 +330,9 @@ func findDefinitions(s *schema) []*schema {
// properties for them recursively.
var objs []*schema
- for k, p := range s.Properties {
- // Use the identifier as the title.
- if p.Type.HasType(PropertyTypeObject) {
+ definition := func(k string, p *schema) {
+ if p.Type.HasType(PropertyTypeObject) && p.AdditionalProperties == nil {
+ // Use the identifier as the title.
if len(p.Title) == 0 {
p.Title = k
}
@@ -312,6 +353,15 @@ func findDefinitions(s *schema) []*schema {
}
}
+ for k, p := range s.Properties {
+ // If a property has AdditionalProperties, then it's a map
+ if p.AdditionalProperties != nil {
+ definition(k, p.AdditionalProperties)
+ }
+
+ definition(k, p)
+ }
+
// Sort the object schemas.
sort.Slice(objs, func(i, j int) bool {
return objs[i].Title < objs[j].Title
@@ -331,67 +381,36 @@ func printProperties(w io.Writer, s *schema) {
// Buffer all property rows so that we can sort them before printing them.
rows := make([][]string, 0, len(s.Properties))
- for k, p := range s.Properties {
- // Generate relative links for objects and arrays of objects.
- propType := make([]string, 0, len(p.Type))
- for _, pt := range p.Type {
- switch pt {
- case PropertyTypeObject:
- name, anchor := propNameAndAnchor(k, p.Title)
- propType = append(propType, fmt.Sprintf("[%s](#%s)", name, anchor))
- case PropertyTypeArray:
- if p.Items != nil {
- for _, pi := range p.Items.Type {
- if pi == PropertyTypeObject {
- name, anchor := propNameAndAnchor(k, p.Items.Title)
- propType = append(propType, fmt.Sprintf("[%s](#%s)[]", name, anchor))
- } else {
- propType = append(propType, fmt.Sprintf("%s[]", pi))
- }
- }
- } else {
- propType = append(propType, string(pt))
- }
- default:
- propType = append(propType, string(pt))
- }
- }
- var propTypeStr string
- if len(propType) == 1 {
- propTypeStr = propType[0]
- } else if len(propType) == 2 {
- propTypeStr = strings.Join(propType, " or ")
- } else if len(propType) > 2 {
- propTypeStr = fmt.Sprintf("%s, or %s", strings.Join(propType[:len(propType)-1], ", "), propType[len(propType)-1])
- }
+ for key, val := range s.Properties {
+ typeStr := propTypeStr(key, val)
// Emphasize required properties.
var required string
- if in(s.Required, k) {
+ if in(s.Required, key) {
required = "**Yes**"
} else {
required = "No"
}
- desc := p.Description
+ desc := val.Description
- if len(p.Enum) > 0 {
- vals := make([]string, 0, len(p.Enum))
- for _, e := range p.Enum {
+ if len(val.Enum) > 0 {
+ vals := make([]string, 0, len(val.Enum))
+ for _, e := range val.Enum {
vals = append(vals, e.String())
}
desc += " Possible values are: `" + strings.Join(vals, "`, `") + "`."
}
- if p.Default != nil {
- desc += fmt.Sprintf(" Default: `%v`.", p.Default)
+ if val.Default != nil {
+ desc += fmt.Sprintf(" Default: `%v`.", val.Default)
}
- rows = append(rows, []string{fmt.Sprintf("`%s`", k), propTypeStr, required, formatForTable(desc)})
+ rows = append(rows, []string{fmt.Sprintf("`%s`", key), typeStr, required, formatForTable(desc)})
}
- // Sort by the required column, then by the name column.
+ // Sort by the required column, then by the key column.
sort.Slice(rows, func(i, j int) bool {
if rows[i][2] < rows[j][2] {
return true
@@ -406,6 +425,53 @@ func printProperties(w io.Writer, s *schema) {
table.Render()
}
+func propTypeStr(propName string, propValue *schema) string {
+ // If the property has AdditionalProperties, it is most likely a map type
+ if propValue.AdditionalProperties != nil {
+ mapValue := renderMapType(propValue.AdditionalProperties)
+ return "map[string]" + mapValue
+ }
+
+ propType := make([]string, 0, len(propValue.Type))
+ // Generate relative links for objects and arrays of objects.
+ for _, pt := range propValue.Type {
+ switch pt {
+ case PropertyTypeObject:
+ name, anchor := propNameAndAnchor(propName, propValue.Title)
+ propType = append(propType, fmt.Sprintf("[%s](#%s)", name, anchor))
+ case PropertyTypeArray:
+ if propValue.Items != nil {
+ for _, pi := range propValue.Items.Type {
+ if pi == PropertyTypeObject {
+ name, anchor := propNameAndAnchor(propName, propValue.Items.Title)
+ propType = append(propType, fmt.Sprintf("[%s](#%s)[]", name, anchor))
+ } else {
+ propType = append(propType, fmt.Sprintf("%s[]", pi))
+ }
+ }
+ } else {
+ propType = append(propType, string(pt))
+ }
+ default:
+ propType = append(propType, string(pt))
+ }
+ }
+
+ if len(propType) == 0 {
+ return ""
+ }
+
+ if len(propType) == 1 {
+ return propType[0]
+ }
+
+ if len(propType) == 2 {
+ return strings.Join(propType, " or ")
+ }
+
+ return fmt.Sprintf("%s, or %s", strings.Join(propType[:len(propType)-1], ", "), propType[len(propType)-1])
+}
+
func propNameAndAnchor(prop, title string) (string, string) {
if len(title) > 0 {
return title, strings.ToLower(title)