diff --git a/app/javascript/components/automate-method-form/codeMirror.js b/app/javascript/components/automate-method-form/automate-method-code-mirror/index.jsx
similarity index 50%
rename from app/javascript/components/automate-method-form/codeMirror.js
rename to app/javascript/components/automate-method-form/automate-method-code-mirror/index.jsx
index 17d0aaeff89..ff20cc2b04d 100644
--- a/app/javascript/components/automate-method-form/codeMirror.js
+++ b/app/javascript/components/automate-method-form/automate-method-code-mirror/index.jsx
@@ -3,7 +3,7 @@ import CodeMirror from 'codemirror';
import 'codemirror/lib/codemirror.css';
import 'codemirror/mode/javascript/javascript';
-const ReactCodeMirror = ({ code, onChange }) => {
+const AutomateMethodCodeMirror = () => {
const codeMirrorRef = useRef(null);
useEffect(() => {
@@ -14,22 +14,33 @@ const ReactCodeMirror = ({ code, onChange }) => {
});
editor.on('change', (instance) => {
- if (onChange) {
- onChange(instance.getValue());
- }
+ console.log(instance)
});
- editor.setValue(code);
+
+ editor.setValue('test');
return () => {
editor.toTextArea();
};
- }, [code, onChange]);
+ }, []);
return (
-
+
+
);
};
-export default ReactCodeMirror;
+export default AutomateMethodCodeMirror;
diff --git a/app/javascript/components/automate-method-form/automate-method-input-parameter/helper.js b/app/javascript/components/automate-method-form/automate-method-input-parameter/helper.js
new file mode 100644
index 00000000000..a268ef6ac47
--- /dev/null
+++ b/app/javascript/components/automate-method-form/automate-method-input-parameter/helper.js
@@ -0,0 +1,31 @@
+export const InputParameterActions = {
+ EDIT: 'editInputParameter',
+ DELETE: 'deleteInputParameter',
+}
+const editInputParameterButton = () => ({
+ is_button: true,
+ title: __('Edit'),
+ text: __('Edit'),
+ alt: __('Edit'),
+ kind: 'ghost',
+ callback: InputParameterActions.EDIT,
+});
+
+const deleteInputParameterButton = () => ({
+ is_button: true,
+ title: __('Delete'),
+ text: __('Delete'),
+ alt: __('Delete'),
+ kind: 'ghost',
+ callback: InputParameterActions.DELETE,
+});
+
+export const inputParametersList = (items) => {
+ const rows = items.map((item, index) => ({
+ ...item,
+ id: index.toString(),
+ edit: editInputParameterButton(item, index),
+ delete: deleteInputParameterButton(item, index),
+ }));
+ return rows;
+}
diff --git a/app/javascript/components/automate-method-form/automate-method-input-parameter/index.jsx b/app/javascript/components/automate-method-form/automate-method-input-parameter/index.jsx
new file mode 100644
index 00000000000..eb2ae6f7003
--- /dev/null
+++ b/app/javascript/components/automate-method-form/automate-method-input-parameter/index.jsx
@@ -0,0 +1,131 @@
+import React, { useState } from 'react';
+import { inputParameterSchema } from './schema';
+import {Button, Modal} from 'carbon-components-react';
+import MiqFormRenderer from '@@ddf';
+import { inputParametersList, InputParameterActions } from './helper';
+import MiqDataTable from '../../miq-data-table';
+import NotificationMessage from '../../notification-message';
+
+const AutomateMethodInputParameter = () => {
+
+ const headers = [
+ { key: 'inputName', header: __('Input Name') },
+ { key: 'dataType', header: __('Data Type') },
+ { key: 'defaultValue', header: __('Default Value') },
+ { key: 'edit', header: __('Edit')},
+ { key: 'delete', header: __('Delete')},
+ ]
+
+ const [data, setData] = useState({
+ isModalOpen: false,
+ initialValues: [],
+ rows: [],
+ selectedItemId: undefined,
+ });
+
+ const addInputParameter = (values) => {
+ console.log('actionType=', values);
+ const newList = [...data.initialValues, values];
+ console.log(newList);
+ setData({
+ ...data,
+ initialValues: newList,
+ rows: inputParametersList(newList),
+ selectedItemId: undefined,
+ isModalOpen: false,
+ })
+ };
+
+ const updateInputParameter = (values) => {
+ data.initialValues[data.selectedItemId] = values;
+ setData({
+ ...data,
+ initialValues: data.initialValues,
+ rows: inputParametersList(data.initialValues),
+ selectedItemId: undefined,
+ isModalOpen: false,
+ });
+ }
+
+ const addOrUpdateInputParameter = (values) => {
+ data.selectedItemId
+ ? updateInputParameter(values)
+ : addInputParameter(values);
+ }
+
+ const editInputParameter = (id) => {
+ console.log('Edit', id);
+
+ console.log(data.initialValues);
+ setData({
+ ...data,
+ isModalOpen: true,
+ selectedItemId: id,
+ });
+ }
+
+ const deleteInputParameter = (id) => {
+ console.log('Delete', id);
+ data.initialValues.splice(id, 1);
+ setData({
+ ...data,
+ initialValues: data.initialValues,
+ rows: inputParametersList(data.initialValues),
+ })
+ }
+
+ const onSelect = (item) => {
+ console.log(item);
+ if (item && item.callbackAction) {
+ switch(item.callbackAction) {
+ case InputParameterActions.EDIT:
+ return editInputParameter(item.id);
+ case InputParameterActions.DELETE:
+ return deleteInputParameter(item.id);
+ }
+ }
+ }
+
+ console.log("data====", data);
+
+ return (
+
+
+
+ {
+ data.rows.length > 0
+ ? onSelect(selectedRow)}
+ mode="button-group-list"
+ />
+ :
+ }
+
+ {
+ data.isModalOpen &&
+ setData({...data, isModalOpen: false})}
+ passiveModal
+ >
+ addOrUpdateInputParameter(values)} />
+
+ }
+
+
+ )
+}
+
+export default AutomateMethodInputParameter
diff --git a/app/javascript/components/automate-method-form/method-input-parameters/helper.js b/app/javascript/components/automate-method-form/automate-method-input-parameter/schema.js
similarity index 58%
rename from app/javascript/components/automate-method-form/method-input-parameters/helper.js
rename to app/javascript/components/automate-method-form/automate-method-input-parameter/schema.js
index a67888d3ba1..62dc4996247 100644
--- a/app/javascript/components/automate-method-form/method-input-parameters/helper.js
+++ b/app/javascript/components/automate-method-form/automate-method-input-parameter/schema.js
@@ -1,4 +1,4 @@
-import { componentTypes } from '@@ddf';
+import { componentTypes, validatorTypes } from '@@ddf';
const options = [
{ label: 'Option 1', value: 'option1' },
@@ -10,22 +10,28 @@ export const inputParameterSchema = {
fields: [
{
component: componentTypes.TEXT_FIELD,
- id: 'type',
- name: 'type',
+ id: 'inputName',
+ name: 'inputName',
label: __('Input Name'),
+ isRequired: true,
+ validate: [{ type: validatorTypes.REQUIRED }],
},
{
component: componentTypes.SELECT,
- id: 'selectInput',
- name: 'selectInput',
+ id: 'dataType',
+ name: 'dataType',
label: __('Choose'),
options: options,
+ isRequired: true,
+ validate: [{ type: validatorTypes.REQUIRED }],
},
{
component: componentTypes.TEXT_FIELD,
id: 'defaultValue',
name: 'defaultValue',
label: __('Default Value'),
+ isRequired: true,
+ validate: [{ type: validatorTypes.REQUIRED }],
},
],
};
diff --git a/app/javascript/components/automate-method-form/automateModal.jsx b/app/javascript/components/automate-method-form/automateModal.jsx
deleted file mode 100644
index cd7d9b6ea0a..00000000000
--- a/app/javascript/components/automate-method-form/automateModal.jsx
+++ /dev/null
@@ -1,25 +0,0 @@
-import React from 'react';
-import { Modal } from 'carbon-components-react';
-import MiqFormRenderer from '@@ddf';
-
-const AutomateModal = ({ isOpen, onClose, modalLabel, schema, callBack }) => {
-
- const onSubmit = (values) => {
- const formData = {
- type:values.type,
- selectInput: values.selectInput,
- defaultValue: values.defaultValue,
- }
- callBack(formData);
- onClose();
- }
-
- return (
-
-
-
- );
-};
-
-export default AutomateModal;
diff --git a/app/javascript/components/automate-method-form/combinedSchema.js b/app/javascript/components/automate-method-form/combinedSchema.js
deleted file mode 100644
index 9a35113cad2..00000000000
--- a/app/javascript/components/automate-method-form/combinedSchema.js
+++ /dev/null
@@ -1,203 +0,0 @@
-import { componentTypes } from '@@ddf';
-
-const combinedSchema = (options, selectedOption) => {
- /** Dummy data */
- const repositoryOptions = [
- { label: 'Repository 1', value: 'repo1' },
- { label: 'Repository 2', value: 'repo2' },
- { label: 'Repository 3', value: 'repo3' },
- ];
- const commonFields = [
- {
- component: componentTypes.TEXT_FIELD,
- id: 'type',
- name: 'type',
- label: __('Type'),
- },
- {
- component: componentTypes.TEXT_FIELD,
- id: 'fully-qualified-name',
- name: 'fully-qualified-name',
- label: __('Fully Qualified Name'),
- initialValue: '',
- },
- {
- component: componentTypes.TEXT_FIELD,
- id: 'name',
- name: 'name',
- label: __('Name'),
- initialValue: '',
- },
- {
- component: componentTypes.TEXT_FIELD,
- id: 'displayName',
- name: 'displayname',
- label: __('Display Name'),
- initialValue: '',
- },
- ];
-
- const builtInFields = [
- {
- component: componentTypes.TEXT_FIELD,
- id: 'built-in',
- name: 'built-in',
- label: __('Builtin name'),
- helperText: 'Optional, if not specified, method name is used',
- },
- ];
-
- const expressionFields = [
- {
- component: componentTypes.SELECT,
- id: 'expressionObject',
- name: 'expressionObject',
- label: __('Expression Object'),
- options: options,
- },
- {
- component: componentTypes.TEXTAREA,
- id: 'editexpression',
- name: 'editexpression',
- label: __('Placeholder For Edit Expression'),
- },
- {
- component: componentTypes.TEXTAREA,
- id: 'editselected',
- name: 'editselected',
- label: __('Placeholder For Edit Selected Element'),
- }
- ];
-
- const playBookFields = [
- {
- component: componentTypes.SELECT,
- id: 'repository',
- name: 'repository',
- label: __('Repository'),
- placeholder: __(''),
- options: repositoryOptions,
- },
- {
- component: componentTypes.SELECT,
- id: 'playbook',
- name: 'playbook',
- label: __('PlayBook'),
- condition: {
- when: "repository",
- isNotEmpty: true
- }
- },
- {
- component: componentTypes.SELECT,
- id: 'machineCredential',
- name: 'machineCredential',
- label: __('Machine Credential'),
- condition: {
- when: "repository",
- isNotEmpty: true
- }
- },
- {
- component: componentTypes.SELECT,
- id: 'vaultCredential',
- name: 'vaultCredential',
- label: __('Vault Credential'),
- condition: {
- when: "repository",
- isNotEmpty: true
- }
- },
- {
- component: componentTypes.SELECT,
- id: 'cloudType',
- name: 'cloudType',
- label: __('Cloud Type'),
- condition: {
- when: "repository",
- isNotEmpty: true
- }
- }
- ];
-
- const ansibleFields = [
- {
- component: componentTypes.SELECT,
- id: 'provider',
- name: 'provider',
- label: __('Provider'),
- options: options,
- },
- {
- component: componentTypes.SELECT,
- id: 'workflowTemplate',
- name: 'workflowTemplate',
- label:__('Workflow Template'),
- options: options,
- },
- ]
-
- const additionalFields = [
- {
- id: 'hostValue',
- component: componentTypes.RADIO,
- label: __('Hosts'),
- name: 'hostValue',
- options: [
- { value: 'localhost', label: 'Localhost' },
- { value: 'specify', label: 'Specify host values' },
- ],
- },
- {
- component: componentTypes.TEXTAREA,
- name: 'specify-details',
- label: __('Specify details'),
- condition: {
- and: [{ when: 'hostValue', is: 'specify' }],
- },
- },
- ]
-
- const ansibleFieldsCommon = [
- {
- component: componentTypes.TEXT_FIELD,
- id: 'maxttl',
- name: 'maxttl',
- label: __('Max TTL(mins)'),
- },
- {
- component: componentTypes.SELECT,
- id: 'loggingOutput',
- name: 'loggingOutput',
- label: __('Logging Output'),
- },
- ]
-
- const verbosityField = [
- {
- component: componentTypes.SELECT,
- id: 'verbosity',
- name: 'verbosity',
- label: __('Verbosity'),
- options: options,
- }
- ]
-
- return {
- fields: selectedOption && selectedOption.id === 'Ansible Tower Job Template'
- ?[...commonFields, ...ansibleFields, ...additionalFields, ...ansibleFieldsCommon]
- :selectedOption && selectedOption.id === 'Ansible Tower Workflow Template'
- ?[...commonFields, ...ansibleFields, ...ansibleFieldsCommon]
- :selectedOption && selectedOption.id === 'Built-in'
- ?[...commonFields, ...builtInFields]
- :selectedOption && selectedOption.id === 'Expression'
- ?[...commonFields, ...expressionFields]
- :selectedOption && selectedOption.id === 'Inline'
- ?[...commonFields]
- :selectedOption && selectedOption.id === 'Playbook'
- ?[...commonFields, ...playBookFields, ...additionalFields, ...ansibleFieldsCommon, ...verbosityField]
- :[]
- }
-};
-
-export default combinedSchema;
diff --git a/app/javascript/components/automate-method-form/index.jsx b/app/javascript/components/automate-method-form/index.jsx
index a5d87b7bf0e..2ee2e99f98a 100644
--- a/app/javascript/components/automate-method-form/index.jsx
+++ b/app/javascript/components/automate-method-form/index.jsx
@@ -1,127 +1,55 @@
import React, { useState } from 'react';
-import { Dropdown, Button } from 'carbon-components-react';
+import { Dropdown } from 'carbon-components-react';
import MiqFormRenderer from '@@ddf';
-import combinedSchema from './combinedSchema';
-import AutomateModal from './automateModal';
-import { inputParameterSchema } from './method-input-parameters/helper';
-import ReactCodeMirror from './codeMirror';
-import MiqDataTable from '../miq-data-table';
-
-const MainInfo = ({ options, selectedOption, handleSelect, ansibleSchema }) => (
-
-
Main Info
- (item ? item.label : '')}
- onChange={({ selectedItem }) => handleSelect(selectedItem)}
- titleText={selectedOption ? selectedOption.label : ''}
- />
-
-);
+import {createSchema} from './schema';
+import componentMapper from '../../forms/mappers/componentMapper';
+import AutomateMethodInputParameter from './automate-method-input-parameter';
+import AutomateMethodCodeMirror from './automate-method-code-mirror';
const AutomateMethodForm = (props) => {
- console.log('Initialprops',props)
- const [selectedOption, setSelectedOption] = useState(null);
- const [isModalOpen, setIsModalOpen] = useState(undefined);
- const [formData, setFormData] = useState([]);
-
- const handleFormDataSubmit = (newFormData) => {
- setFormData((prevFormData) => [...prevFormData, newFormData]);
- };
- const handleSelect = (selectedItem) => {
- setSelectedOption(selectedItem);
+ const mapper = {
+ ...componentMapper,
+ 'automate-method-inline': AutomateMethodCodeMirror,
+ 'automate-method-input-parameter': AutomateMethodInputParameter,
};
- const openModal = (type) => {
- setIsModalOpen(type);
- };
-
- const closeModal = () => {
- setIsModalOpen(undefined);
- };
+ console.log('Initial props',props)
+ const [selectedOption, setSelectedOption] = useState(null);
- const options = props.availableLocations.map(([id, label]) => ({ id, label }));
- const ansibleSchema = combinedSchema(options, selectedOption);
- let modalSchema;
- modalSchema = isModalOpen === 'inputParameter' ? inputParameterSchema : undefined;
- console.log('selectedOption: ',selectedOption)
- const headers = [
- { key: 'inputName', header: 'Input Name' },
- { key: 'dataType', header: 'Data Type' },
- { key: 'defaultValue', header: 'Default Value' },
- { key: 'action', header: 'Action' },
- ];
+ const options = props.availableLocations.map((item) => ({ id: item[1], label: item[0] }));
+ console.log('options=', options);
+
+ const renderMainInfo = () => {
+ console.log('renderMainInfo'. options);
+ return
+
Main Info
+ (item ? item.label : '')}
+ onChange={({ selectedItem }) => setSelectedOption(selectedItem)}
+ titleText={selectedOption ? selectedOption.label : ''}
+ />
+
+ }
- const rows = formData.map((data) => ({
- id: 'one',
- inputName: data.type,
- dataType: data.selectInput,
- defaultValue: data.defaultValue,
- }));
+ const renderInputParameters = () => ()
return (
<>
-
- {selectedOption && shouldRenderForm(selectedOption) && (
+ {renderMainInfo()}
+ {selectedOption && (
)}
- {selectedOption && shouldRenderForm(selectedOption) && (
- <>
- {selectedOption.id === 'Inline' && (
- <>
-
- console.log(newCode)} />
-
-
- >
- )}
- {selectedOption && shouldRenderForm(selectedOption) && (
- <>
-
-
- >
- )}
-
- {formData && formData.length > 0 && (
- onSelect(selectedRow)}
- mode="button-group-list"
- />
- )}
- >
- )}
- {
- handleFormDataSubmit(newFormData);
- console.log('newFormData', newFormData);
- console.log('setFormData', formData);
- }}
- />
- {console.log('formData.length', formData.length)}
+
+
>
);
};
-const shouldRenderForm = (selectedOption) => {
- const validOptions = ['Ansible Tower Job Template', 'Ansible Tower Workflow Template', 'Playbook', 'Built-in', 'Expression', 'Inline'];
- return validOptions.includes(selectedOption.id);
-};
-
export default AutomateMethodForm;
diff --git a/app/javascript/components/automate-method-form/schema.config.js b/app/javascript/components/automate-method-form/schema.config.js
new file mode 100644
index 00000000000..f84866edf7f
--- /dev/null
+++ b/app/javascript/components/automate-method-form/schema.config.js
@@ -0,0 +1,155 @@
+import { componentTypes } from '@@ddf';
+
+export const ansibleFields = [
+ {
+ component: componentTypes.SELECT,
+ id: 'provider',
+ name: 'provider',
+ label: __('Provider'),
+ // options: options,
+ },
+ {
+ component: componentTypes.SELECT,
+ id: 'workflowTemplate',
+ name: 'workflowTemplate',
+ label:__('Workflow Template'),
+ // options: options,
+ },
+];
+
+const ansibleFieldsCommon = [
+ {
+ component: componentTypes.TEXT_FIELD,
+ id: 'maxttl',
+ name: 'maxttl',
+ label: __('Max TTL(mins)'),
+ },
+ {
+ component: componentTypes.SELECT,
+ id: 'loggingOutput',
+ name: 'loggingOutput',
+ label: __('Logging Output'),
+ },
+];
+
+const additionalFields = [
+ {
+ id: 'hostValue',
+ component: componentTypes.RADIO,
+ label: __('Hosts'),
+ name: 'hostValue',
+ options: [
+ { value: 'localhost', label: 'Localhost' },
+ { value: 'specify', label: 'Specify host values' },
+ ],
+ },
+ {
+ component: componentTypes.TEXTAREA,
+ name: 'specify-details',
+ label: __('Specify details'),
+ condition: {
+ and: [{ when: 'hostValue', is: 'specify' }],
+ },
+ },
+];
+
+const builtInFields = [
+ {
+ component: componentTypes.TEXT_FIELD,
+ id: 'built-in',
+ name: 'built-in',
+ label: __('Builtin name'),
+ helperText: 'Optional, if not specified, method name is used',
+ },
+];
+
+const expressionFields = [
+ {
+ component: componentTypes.SELECT,
+ id: 'expressionObject',
+ name: 'expressionObject',
+ label: __('Expression Object'),
+ options: [],
+ },
+ {
+ component: componentTypes.TEXTAREA,
+ id: 'editexpression',
+ name: 'editexpression',
+ label: __('Placeholder For Edit Expression'),
+ },
+ {
+ component: componentTypes.TEXTAREA,
+ id: 'editselected',
+ name: 'editselected',
+ label: __('Placeholder For Edit Selected Element'),
+ }
+];
+
+const playBookFields = [
+ {
+ component: componentTypes.SELECT,
+ id: 'repository',
+ name: 'repository',
+ label: __('Repository'),
+ placeholder: __(''),
+ options: [],
+ },
+ {
+ component: componentTypes.SELECT,
+ id: 'playbook',
+ name: 'playbook',
+ label: __('PlayBook'),
+ condition: {
+ when: "repository",
+ isNotEmpty: true
+ }
+ },
+ {
+ component: componentTypes.SELECT,
+ id: 'machineCredential',
+ name: 'machineCredential',
+ label: __('Machine Credential'),
+ condition: {
+ when: "repository",
+ isNotEmpty: true
+ }
+ },
+ {
+ component: componentTypes.SELECT,
+ id: 'vaultCredential',
+ name: 'vaultCredential',
+ label: __('Vault Credential'),
+ condition: {
+ when: "repository",
+ isNotEmpty: true
+ }
+ },
+ {
+ component: componentTypes.SELECT,
+ id: 'cloudType',
+ name: 'cloudType',
+ label: __('Cloud Type'),
+ condition: {
+ when: "repository",
+ isNotEmpty: true
+ }
+ }
+];
+
+const verbosityField = [
+ {
+ component: componentTypes.SELECT,
+ id: 'verbosity',
+ name: 'verbosity',
+ label: __('Verbosity'),
+ options: [],
+ }
+];
+
+export const schemaConfig = {
+ ansibleJobTemplate: [...ansibleFields, ...additionalFields, ...ansibleFieldsCommon],
+ ansibleWorkflowTemplate: [...ansibleFields, ...ansibleFieldsCommon],
+ builtIn: [...builtInFields],
+ expression: [...expressionFields],
+ playbook: [...playBookFields, ...additionalFields, ...ansibleFieldsCommon, ...verbosityField],
+}
diff --git a/app/javascript/components/automate-method-form/schema.js b/app/javascript/components/automate-method-form/schema.js
new file mode 100644
index 00000000000..5edfe23b3a9
--- /dev/null
+++ b/app/javascript/components/automate-method-form/schema.js
@@ -0,0 +1,87 @@
+import { componentTypes } from '@@ddf';
+import { schemaConfig } from './schema.config';
+
+/* Updated Schema */
+export const createSchema = (selectedOption) => {
+
+ let selectedFields = [];
+
+ const commonFields = [
+ {
+ component: componentTypes.TEXT_FIELD,
+ id: 'type',
+ name: 'type',
+ label: __('Type'),
+ },
+ {
+ component: componentTypes.TEXT_FIELD,
+ id: 'fully-qualified-name',
+ name: 'fully-qualified-name',
+ label: __('Fully Qualified Name'),
+ initialValue: '',
+ },
+ {
+ component: componentTypes.TEXT_FIELD,
+ id: 'name',
+ name: 'name',
+ label: __('Name'),
+ initialValue: '',
+ },
+ {
+ component: componentTypes.TEXT_FIELD,
+ id: 'displayName',
+ name: 'displayname',
+ label: __('Display Name'),
+ initialValue: '',
+ },
+ ];
+
+ const inputParametersField = [
+ {
+ component: 'automate-method-input-parameter',
+ id: 'inputParameter',
+ name: 'inputParameter',
+ label: __('Input Parameter'),
+ initialValue: 'test',
+ },
+ ]
+
+ const automateFields = (conditionalFields) => [
+ ...commonFields,
+ ...conditionalFields,
+ ...inputParametersField,
+ ]
+
+ switch (selectedOption.id) {
+ case 'ansible_job_template':
+ selectedFields = automateFields(schemaConfig.ansibleJobTemplate);
+ break;
+ case 'ansible_workflow_template':
+ selectedFields = automateFields(schemaConfig.ansibleWorkflowTemplate);
+ break;
+ case 'builtin':
+ selectedFields = automateFields(schemaConfig.builtIn);
+ break;
+ case 'expression':
+ selectedFields = automateFields(schemaConfig.expression);
+ break;
+ case 'inline':
+ selectedFields = [...commonFields, ...inputParametersField];
+ break;
+ case 'playbook':
+ selectedFields = automateFields(schemaConfig.playbook);
+ break;
+ default:
+ selectedFields = [];
+ }
+ return {
+ fields: [
+ {
+ component: componentTypes.SUB_FORM,
+ id: 'name-wrapper',
+ name: 'subform-1',
+ fields: selectedFields,
+ }
+ ]
+ }
+}