diff --git a/cyclops-ctrl/internal/mapper/helm.go b/cyclops-ctrl/internal/mapper/helm.go
index 47d8b04a..f59bcae2 100644
--- a/cyclops-ctrl/internal/mapper/helm.go
+++ b/cyclops-ctrl/internal/mapper/helm.go
@@ -57,50 +57,58 @@ func HelmSchemaToFields(name string, schema helm.Property, defs map[string]helm.
}
fields = append(fields, models.Field{
- Name: dependency.Name,
- Description: dependency.RootField.Description,
- Type: dependency.RootField.Type,
- DisplayName: mapTitle(dependency.Name, dependency.RootField.DisplayName),
- ManifestKey: dependency.RootField.ManifestKey,
- Value: dependency.RootField.Value,
- Properties: dependency.RootField.Properties,
- Items: dependency.RootField.Items,
- Enum: dependency.RootField.Enum,
- Suggestions: dependency.RootField.Suggestions,
- Required: dependency.RootField.Required,
- FileExtension: dependency.RootField.FileExtension,
- Minimum: dependency.RootField.Minimum,
- Maximum: dependency.RootField.Maximum,
- MultipleOf: dependency.RootField.MultipleOf,
- ExclusiveMinimum: dependency.RootField.ExclusiveMinimum,
- ExclusiveMaximum: dependency.RootField.ExclusiveMaximum,
- MinLength: dependency.RootField.MinLength,
- MaxLength: dependency.RootField.MaxLength,
- Pattern: dependency.RootField.Pattern,
- Immutable: dependency.RootField.Immutable,
+ Name: dependency.Name,
+ Description: dependency.RootField.Description,
+ Type: dependency.RootField.Type,
+ DisplayName: mapTitle(dependency.Name, dependency.RootField.DisplayName),
+ ManifestKey: dependency.RootField.ManifestKey,
+ Value: dependency.RootField.Value,
+ Properties: dependency.RootField.Properties,
+ AdditionalProperties: dependency.RootField.AdditionalProperties,
+ Items: dependency.RootField.Items,
+ Enum: dependency.RootField.Enum,
+ Suggestions: dependency.RootField.Suggestions,
+ Required: dependency.RootField.Required,
+ FileExtension: dependency.RootField.FileExtension,
+ Minimum: dependency.RootField.Minimum,
+ Maximum: dependency.RootField.Maximum,
+ MultipleOf: dependency.RootField.MultipleOf,
+ ExclusiveMinimum: dependency.RootField.ExclusiveMinimum,
+ ExclusiveMaximum: dependency.RootField.ExclusiveMaximum,
+ MinLength: dependency.RootField.MinLength,
+ MaxLength: dependency.RootField.MaxLength,
+ Pattern: dependency.RootField.Pattern,
+ Immutable: dependency.RootField.Immutable,
})
}
+ var additionalProps *models.Field
+ if ap := schema.GetAdditionalProperties(); ap != nil {
+ apField := HelmSchemaToFields("", *ap, defs, nil)
+ additionalProps = &apField
+ }
+
return models.Field{
- Name: name,
- Description: schema.Description,
- Type: mapHelmPropertyTypeToFieldType(schema),
- DisplayName: mapTitle(name, schema.Title),
- ManifestKey: name,
- Properties: fields,
- Enum: schema.Enum,
- Suggestions: schema.Suggestions,
- Required: schema.Required,
- FileExtension: schema.FileExtension,
- Minimum: schema.Minimum,
- Maximum: schema.Maximum,
- ExclusiveMinimum: schema.ExclusiveMinimum,
- ExclusiveMaximum: schema.ExclusiveMaximum,
- MultipleOf: schema.MultipleOf,
- MinLength: schema.MinLength,
- MaxLength: schema.MaxLength,
- Pattern: schema.Pattern,
- Immutable: schema.Immutable,
+ Name: name,
+ Description: schema.Description,
+ Type: mapHelmPropertyTypeToFieldType(schema),
+ DisplayName: mapTitle(name, schema.Title),
+ ManifestKey: name,
+ Properties: fields,
+ AdditionalProperties: additionalProps,
+ Enum: schema.Enum,
+ Suggestions: schema.Suggestions,
+ Required: schema.Required,
+ FileExtension: schema.FileExtension,
+ Minimum: schema.Minimum,
+ Maximum: schema.Maximum,
+ ExclusiveMinimum: schema.ExclusiveMinimum,
+ ExclusiveMaximum: schema.ExclusiveMaximum,
+ MultipleOf: schema.MultipleOf,
+ MinLength: schema.MinLength,
+ MaxLength: schema.MaxLength,
+ Pattern: schema.Pattern,
+ Immutable: schema.Immutable,
}
}
diff --git a/cyclops-ctrl/internal/models/helm/helmschema.go b/cyclops-ctrl/internal/models/helm/helmschema.go
index 85ed8dc3..2cf3df0f 100644
--- a/cyclops-ctrl/internal/models/helm/helmschema.go
+++ b/cyclops-ctrl/internal/models/helm/helmschema.go
@@ -6,20 +6,40 @@ import (
json "github.com/json-iterator/go"
)
+// RawJSON captures any JSON value (object, boolean, string, etc.) without
+// failing on type mismatches.
+type RawJSON []byte
+
+func (r *RawJSON) UnmarshalJSON(data []byte) error {
+ if data == nil {
+ return nil
+ }
+ *r = append((*r)[0:0], data...)
+ return nil
+}
+
+func (r RawJSON) MarshalJSON() ([]byte, error) {
+ if r == nil {
+ return []byte("null"), nil
+ }
+ return r, nil
+}
+
type Property struct {
- Title string `json:"title"`
- Type PropertyType `json:"type"`
- Description string `json:"description"`
- Order []string `json:"order"`
- Properties map[string]Property `json:"properties"`
- Items *Property `json:"items"`
- Enum []interface{} `json:"enum"`
- Suggestions []interface{} `json:"x-suggestions"`
- Required []string `json:"required"`
- FileExtension string `json:"fileExtension"`
- Reference string `json:"$ref"`
- Definitions map[string]Property `json:"$defs"`
- Immutable bool `json:"immutable"`
+ Title string `json:"title"`
+ Type PropertyType `json:"type"`
+ Description string `json:"description"`
+ Order []string `json:"order"`
+ Properties map[string]Property `json:"properties"`
+ AdditionalProperties RawJSON `json:"additionalProperties,omitempty"`
+ Items *Property `json:"items"`
+ Enum []interface{} `json:"enum"`
+ Suggestions []interface{} `json:"x-suggestions"`
+ Required []string `json:"required"`
+ FileExtension string `json:"fileExtension"`
+ Reference string `json:"$ref"`
+ Definitions map[string]Property `json:"$defs"`
+ Immutable bool `json:"immutable"`
// number validation
Minimum *float64 `json:"minimum"`
@@ -37,6 +57,23 @@ type Property struct {
AnyOf []Property `json:"anyOf"`
}
+// GetAdditionalProperties attempts to parse additionalProperties as a schema
+// object. Returns nil when the value is absent, a boolean, or otherwise not a
+// valid Property (e.g. additionalProperties: false).
+func (p Property) GetAdditionalProperties() *Property {
+ if len(p.AdditionalProperties) == 0 {
+ return nil
+ }
+ var prop Property
+ if err := json.Unmarshal(p.AdditionalProperties, &prop); err != nil {
+ return nil
+ }
+ if len(prop.Type) == 0 && len(prop.Properties) == 0 {
+ return nil
+ }
+ return &prop
+}
+
type PropertyType string
func (t *PropertyType) UnmarshalJSON(data []byte) error {
diff --git a/cyclops-ctrl/internal/models/templates.go b/cyclops-ctrl/internal/models/templates.go
index 4d8f0235..da464b42 100644
--- a/cyclops-ctrl/internal/models/templates.go
+++ b/cyclops-ctrl/internal/models/templates.go
@@ -30,19 +30,20 @@ type Template struct {
}
type Field struct {
- Name string `json:"name"`
- Description string `json:"description"`
- Type string `json:"type"`
- DisplayName string `json:"display_name"`
- ManifestKey string `json:"manifest_key"`
- Value string `json:"value"`
- Properties []Field `json:"properties"`
- Items *Field `json:"items"`
- Enum []interface{} `json:"enum"`
- Suggestions []interface{} `json:"x-suggestions"`
- Required []string `json:"required"`
- FileExtension string `json:"fileExtension"`
- Immutable bool `json:"immutable"`
+ Name string `json:"name"`
+ Description string `json:"description"`
+ Type string `json:"type"`
+ DisplayName string `json:"display_name"`
+ ManifestKey string `json:"manifest_key"`
+ Value string `json:"value"`
+ Properties []Field `json:"properties"`
+ AdditionalProperties *Field `json:"additionalProperties,omitempty"`
+ Items *Field `json:"items"`
+ Enum []interface{} `json:"enum"`
+ Suggestions []interface{} `json:"x-suggestions"`
+ Required []string `json:"required"`
+ FileExtension string `json:"fileExtension"`
+ Immutable bool `json:"immutable"`
// number validation
Minimum *float64 `json:"minimum"`
diff --git a/cyclops-ui/src/components/form/TemplateFormFields.tsx b/cyclops-ui/src/components/form/TemplateFormFields.tsx
index 4e8db462..4fb17040 100644
--- a/cyclops-ui/src/components/form/TemplateFormFields.tsx
+++ b/cyclops-ui/src/components/form/TemplateFormFields.tsx
@@ -188,6 +188,9 @@ export function mapFields(
level={level}
formItemName={formItemName}
isRequired={isRequired}
+ initialValues={initialValues}
+ uniqueFieldName={uniqueFieldName}
+ isModuleEdit={isModuleEdit}
/>,
);
}
diff --git a/cyclops-ui/src/components/form/fields/map/MapField.tsx b/cyclops-ui/src/components/form/fields/map/MapField.tsx
index 1dd53779..ce1478b9 100644
--- a/cyclops-ui/src/components/form/fields/map/MapField.tsx
+++ b/cyclops-ui/src/components/form/fields/map/MapField.tsx
@@ -1,7 +1,22 @@
-import React from "react";
-import { Button, Col, Divider, Form, Input, Row } from "antd";
-import { MinusCircleOutlined, PlusOutlined } from "@ant-design/icons";
+import React, { useState } from "react";
+import {
+ Button,
+ Col,
+ Collapse,
+ Divider,
+ Form,
+ Input,
+ Row,
+ Tooltip,
+} from "antd";
+import {
+ InfoCircleOutlined,
+ MinusCircleOutlined,
+ PlusOutlined,
+} from "@ant-design/icons";
import TextArea from "antd/es/input/TextArea";
+import { mapFields } from "../../TemplateFormFields";
+import { collapseColor } from "../utils";
import { useTemplateFormFields } from "../../TemplateFormFieldsContext";
interface Props {
@@ -10,9 +25,12 @@ interface Props {
level: number;
formItemName: string;
isRequired: boolean;
+ initialValues?: any;
+ uniqueFieldName?: string[];
+ isModuleEdit?: boolean;
}
-export const MapField = ({
+const FlatMapField = ({
field,
fieldName,
level,
@@ -110,3 +128,149 @@ export const MapField = ({
);
};
+
+const StructuredMapField = ({
+ field,
+ fieldName,
+ level,
+ formItemName,
+ isRequired,
+ initialValues,
+ uniqueFieldName,
+ isModuleEdit,
+}: Props) => {
+ const { themePalette } = useTemplateFormFields();
+ const [open, setOpen] = useState(false);
+
+ const ap = field.additionalProperties;
+
+ let header =