Components:beforeCreate helpers - bettyblocks/cli GitHub Wiki
beforeCreate
helpers provide convenient functions and Components that make it easier to configure your Prefabs with the beforeCreate
pop-up. For a basic explanation of beforeCreate
check this page.
The following props are available inside the beforeCreate component:
- prefab
- save
- close
-
components
- Box
- Button
- ButtonGroup
- ButtonGroupButton
- cloneStructure
- Content
- DeleteButton
- DestructiveButton
- Field
- Footer
- Header
- Heading
- ModelSelector
- ComponentSelector
- AuthenticationProfileSelector
- PartialSelector
- PropertySelector
- PropertiesSelector
- EndpointSelector
- Text
- TextInput
- TextArea
- CircleQuestion
- BBTooltip
- helpers
An object which contains the original prefab.
A function that accepts a (mutated) prefab as an argument. When called the beforeCreate
pop-up will close and the prefab will be added to the canvas.
A function which closes the pop-up and prevents the prefab from being dropped on the canvas when called.
The B.cloneStructure allows you to clone the root structure of another prefab. You can then modify this clone and re-insert it into your prefab like so:
const prefabName = 'Image`; // an ImagePrefab
urls.forEach(url => {
const imageStructure = cloneStructure(prefabName);
setOption(imageStructure, 'url', (option) => ({
...option,
value: [url]
})
);
prefab.structure.push(imageStructure);
});
prepareAction will create a new action action based on a model and it's properties.
const result = await prepareAction(
componentId, // the component you would like to bind the action to
idProperty, // the id property of the model the user has selected
properties, // all the properties that you would like to create actionInputs for.
'update', // the name of the template for the action that you would like to create.
authProfile // the authentication profile object for creating a login template (optional)
permissions // used to set the permission of the action. 'private' | 'public' | 'inherit':
// - when set to private, the action is set to private with the admin execute permission set to true.
// - public set the action to public which allows everyone to be able to execute it.
// - inherit will act as public or private depending if the page has a pageAuthenticationProfileId.
pageAuthenticationProfileId // used to determine if action should be private or public.
);
The type of result is:
export interface PreparedAction {
action: {
actionId: string;
};
variables: Record<string, [Property, ActionVariable]>; // for each propertyId, the property and the associated input variable
relatedIdProperties: Record<string, string>; // for each propertyId, if the user selects relational properties, this contains the id properties of the related models.
recordInputVariable: ActionVariable | null;
resultVariable: ActionVariable;
}
Once you have a an action and input variables you can then create components based on this action. Here are the steps to do that:
- If you would like to associate a component with the action, for example a form set up the following action.
option | description | value |
---|---|---|
option ( 'ACTION_JS' , { label : 'Action' , value : '' }) |
stores the actionId | action.actionId |
- If you would like components that are connected to the property and the action variables that were created add this.
option | description | value |
---|---|---|
option ( 'ACTION_JS_PROPERTY' , { label : 'Property' , value : '' , }) |
stores the association between property and action variable. When you're component get's deleted, the backend can take care of deleting the associated action input variable for you | actionVariable.id |
setOption allows you to update an option.
setOption(structure, "modelId", (option) => ({
...option, // the original option as configured in the prefab
value: modelId, // the new value for the option
configuration: {
// the new configuration for the option
disabled: true,
},
}));
If the option is not found, it will log to the browser console.
makeBettyInput(
BettyPrefabs.AUTO_COMPLETE,
model,
property,
variable,
result.relatedIdProperties,
result.relatedModelIds
);
This will configure a form input that is bound to a form. You may use your own prefabs as well, or you can use names from the default set BettyPrefabs.*
.
If you do provide your own prefab make sure that at minimum it has the component options, documented under prepareAction().
makeBettyUpdateInput(
BettyPrefabs.AUTO_COMPLETE,
model,
property,
variable,
result.relatedIdProperties,
result.relatedModelIds
);
If you want to have a form input with a prefilled value, you can use this helper.
The parameters makeBettyInput and makeBettyUpdateInput needs are:
- The name of the prefab
- The model of the input
- The property of the input
- The action variable the input needs to fill in
In case of relational properties like a belongs-to, has-many or has-and-belongs-to-many: 5. The ids of the properties of a related model 6. The ids of the related models
Adds a new action input variable to an existing action
addActionVariable(
actionId, // id of an existing action
name, // name of the new variable
kind, // kind of the variable on of "INTEGER", "STRING" or "ARRAY"
options // see below
);
ActionVariables have different options depending on the action variables. By default they all support a value option like so: { value: "some-value" }
.
When using the ARRAY
kind remember to set the kind of array in the option like so: { kind: "INTEGER" }
. To be clear:
addActionVariable('001', 'my integer array', kind: 'ARRAY', options: { value: '', kind: 'INTEGER' });
BettyPrefabs includes: AUTO_COMPLETE ,BOOLEAN ,DATE ,DATE_TIME ,DECIMAL ,EMAIL_ADDRESS ,HIDDEN ,IBAN ,INTEGER ,LIST ,PASSWORD ,PHONE_NUMBER ,PRICE ,RADIO ,SELECT ,STRING ,SUBMIT_BUTTON ,TEXT ,TIME ,UPDATE_FORM ,URL
PropertyKinds includes: AUTO_INCREMENT, BELONGS_TO, BOOLEAN, BOOLEAN_EXPRESSION, DATE, DATE_TIME, DECIMAL, EMAIL_ADDRESS, FILE, FLOAT, HAS_AND_BELONGS_TO_MANY, HAS_MANY, HAS_ONE, IBAN, IMAGE, INTEGER, LIST, PASSWORD, PDF, PHONE_NUMBER, PRICE, SERIAL, STRING, SUM, TEXT, TIME, URL
A collection of components we made available for you to use. The components help to keep to the Betty Blocks style guide and give read access to metadata. Here is a list in alphabetical order containing every component we expose, their API, and an example on how to use them:
Box
is a powerful component of the Grommet framework. Use it to easily set up layouts.
Check Grommet's documentation to see how it can be used and which props are available: https://v2.grommet.io/box
Button
is a component of the Grommet framework.
Check Grommet's documentation to see how it can be used and which props are available: https://v2.grommet.io/button
We recommend only the usage of the following props:
Props | type | default | description |
---|---|---|---|
color | "blue" | "orange" | "purple" | "turquoise" |
"turquoise" |
Color of the button. Also applies to hover, focus and active styling |
disabled | boolean |
false |
Disable all interactions with component |
label | string |
"" |
Button label |
onClick | () => void |
Function executed when the button is clicked | |
plain | boolean |
false |
Property to create an outline button |
primary | boolean |
false |
Property to create a primary button |
size | "small" | "medium" | "large" |
"medium" |
Size of the button |
Examples:
Primary button:
<Button label="Button text" onClick={() => {}} primary />
Screenshot:
Secondary button:
<Button label="Button text" onClick={() => {}} />
Screenshot:
Tertiary button:
<Button label="Button text" onClick={() => {}} plain />
Screenshot:
A button group with the functionality of a radio button group. Use it together with ButtonGroupButton.
Props | type | default | description |
---|---|---|---|
color | "purple" | "turquoise" | "blue" | "orange" |
"turquoise" |
Color of the button group. Also applies to hover, focus and active styling |
fill | boolean |
false |
Fill the component width to its parent. |
onChange* | (event: React.ChangeEvent<HTMLInputElement>) => void |
Function which executes when a button is selected in the button group | |
value | string |
Value of the button group button which should be shown as active | |
size | "small" | "medium" | "large" |
"medium" |
Size of the button group |
Example:
See ButtonGroupButton for a usage example.
Screenshot:
See ButtonGroupButton for a screenshot.
A button group button to use inside a ButtonGroup.
Props | type | default | description |
---|---|---|---|
disabled | boolean |
false |
Disable all interactions with component |
label | string |
Label of the button group button | |
name* | string |
Should be the same string as other button group buttons in the current button group to make the radio button functionality work | |
value | string |
Unique string which is available in ButtonGroup's callback function's argument as event.target.value
|
Example:
({ components: { ButtonGroup, ButtonGroupButton } }) => {
const [state, setState] = React.useState("a");
return (
<ButtonGroup
onChange={({ target: { value } }) => {
setState(value);
}}
value={state}
>
<ButtonGroupButton label="Option a" value="a" name="options" />
<ButtonGroupButton label="Option b" value="b" name="options" />
<ButtonGroupButton label="Option c" value="c" name="options" />
</ButtonGroup>
);
};
Screenshot:
A layout helper component that applies spacing to comply with the Betty Blocks style guide.
Example usage:
({ components: { Content } }) => {
return <Content>Your content here...</Content>;
};
DeleteButton
is a component from the Grommet framework which we extended and changed styles to make it comply with the delete button in the Betty Blocks style guides.
Check Grommet's documentation to see how it can be used and which props are available: https://v2.grommet.io/button
We recommend only the usage of the following props:
Props | type | default | description |
---|---|---|---|
disabled | boolean |
false |
Disable all interactions with component |
label | string |
"" |
Button label |
onClick | () => void |
Function executed when the button is clicked | |
size | "small" | "medium" | "large" |
"medium" |
Size of the button |
Example:
<DeleteButton label="Button text" onClick={() => {}} />
Screenshot:
NOTE: This is the hover state of the delete button. The normal state is the same as the secondary button.
DestructiveButton
is a component from the Grommet framework which we extended and changed styles to make it comply with the destructive button in the Betty Blocks style guides.
Check Grommet's documentation to see how it can be used and which props are available: https://v2.grommet.io/button
We recommend only the usage of the following props:
Props | type | default | description |
---|---|---|---|
disabled | boolean |
false |
Disable all interactions with component |
label | string |
"" |
Button label |
onClick | () => void |
Function executed when the button is clicked | |
size | "small" | "medium" | "large" |
"medium" |
Size of the button |
Example:
<DestructiveButton label="Button text" onClick={() => {}} />
Screenshot:
A layout helper component to wrap around input components to add a label and spacing which both comply with the Betty Blocks style guide.
Props | type | default | description |
---|---|---|---|
label | string |
"" |
Field label |
error | ReactNode |
Error text | |
info | ReactNode |
Help text |
Example usage:
({ components : { Content, Field, ModelSelector } }) => {
return (
<Content>
<Field label="Model">
<ModelSelector ... />
</Field>
</Content>
);
}
Screenshot:
A layout helper component to get a pop-up footer that complies with the Betty Blocks style guide.
Props | type | default | description |
---|---|---|---|
onClose | () => void |
Function which is executed when the cancel button is clicked. | |
onSave | () => void |
Function which is executed when the save button is clicked. |
Example usage:
({ close, components: { Footer }, save }) => {
return (
<Footer
onClose={close}
onSave={() => {
save(newPrefab);
}}
/>
);
};
Screenshot:
A layout helper component to get a pop-up header which complies with the Betty Blocks style guide.
Props | type | default | description |
---|---|---|---|
title | string |
Title of the header. | |
subtitle | string |
Subtitle of the header. | |
onClose | () => void |
Function which is executed when the close icon is clicked. |
Example usage:
({ close, components: { Header } }) => {
return (
<Header
title="Configure component"
subtitle="Message to support the title"
onClose={close}
/>
);
};
Screenshot:
Heading
is a component of the Grommet framework. Use it to display titles that comply with the Betty Blocks style guide.
Check Grommet's documentation to see how it can be used and which props are available: https://v2.grommet.io/heading
Most important props:
Props | type | default | description |
---|---|---|---|
level | "1" | "2" | "3" | "4" | "5" | "6" | "7" |
"1" |
Level for the html heading element to be used. |
size | "small" | "medium" | "large" | "xlarge" |
"medium" |
Size of the text |
Example:
<Heading size="xlarge">Hello world</Heading>
Screenshot:
Use it to select a model from the current application.
Props | type | default | description |
---|---|---|---|
disabled | boolean |
false |
Disable all interactions with component. |
fill | boolean |
false |
Fill the component width to its parent. |
onChange | (id: string) => void |
Function which executes when a model is selected in the model selector. | |
size | "small" | "medium" | "large" |
"large" |
Size of the model selector. |
value | string |
A model id used to show the selected model in the model selector. |
Example usage:
({ components: { ModelSelector } }) => {
const [modelId, setModelId] = React.useState("");
return (
<ModelSelector
onChange={(value) => {
setModelId(value);
}}
value={modelId}
/>
);
};
Screenshot:
The componentSelector is a dropdown helper for selecting a component, the components that it shows can be configured. The componentSelector accepts the following arguments:
Props | type | default | description |
---|---|---|---|
allowedComponents | [stings] |
[] |
An array comprising component names which are allowed to be shown by the dropdown menu. |
placeholder | string |
"No component available" |
Text that is shown when no value is selected in the dropdown menu. |
value | string |
'' |
Id of the component to set as selected in the dropdown. |
onChange | (component: NormalizedComponent) => void |
Function which executes when a component is selected in the component selector. |
Example usage:
<ComponentSelector
onChange={(component) => {
const foundModelId = Object.values(component.options).reduce(
(acc, option) => (option.type === "MODEL" ? option.value : acc),
null
);
setThisPageState((prevState) => ({
...prevState,
modelId: foundModelId,
component,
}));
setModelId(foundModelId);
}}
value={thisPageState.component ? thisPageState.component.id : ""}
placeholder="No components available."
allowedComponents={["DataTable", "DataList"]}
/>
Screenshot:
Use it to select an authentication profile from the current application.
Props | type | default | description |
---|---|---|---|
disabled | boolean |
false |
Disable all interactions with component. |
fill | boolean |
false |
Fill the component width to its parent. |
onChange | (id: string) => void |
Function which executes when an authentication profile is selected in the authentication profile selector. | |
size | "small" | "medium" | "large" |
"large" |
Size of the authentication profile selector. |
value | string |
An authentication profile id used to show the selected profile in the authentication profile selector. |
Example usage:
({ components: { AuthenticationProfileSelector } }) => {
const [authProfileId, setAuthProfileId] = React.useState("");
return (
<AuthenticationProfileSelector
onChange={(id) => {
setAuthProfileId(id);
}}
value={authProfileId}
/>
);
};
Use it to select a partial from the current application.
Props | type | default | description |
---|---|---|---|
disabled | boolean |
false |
Disable all interactions with component. |
fill | boolean |
false |
Fill the component width to its parent. |
onChange | (id: string) => void |
Function which executes when a partial is selected in the partial selector. | |
size | "small" | "medium" | "large" |
"large" |
Size of the partial selector. |
value | string |
value is the id of the selected partial. | |
allowedTypes | string[] |
allowedTypes is used to disable the selection of certain partials. Only the partials with a root component that matches with a type in the array will be selectable | |
preSelected | string |
Allows the auto-generation of partials. When you fill in a name, it will look under prefabs -> partials and when the name matches one of the partial templates (this is case insensitive), an 'auto-generate' button will appear with which u can generate a new partial based on the template. When the user already has a partial with said name, It will automatically select it. When neither is the case, nothing will happen. |
Example usage:
({ components: { PartialSelector } }) => {
const [partialId, setPartialId] = React.useState("");
return (
<PartialSelector
onChange={(id) => {
setPartialId(id);
}}
value={partialId}
allowedTypes={[
"BODY_COMPONENT",
"CONTAINER_COMPONENT",
"CONTENT_COMPONENT",
]}
/>
);
};
Use it to select a property from the current application in the current data scope.
Props | type | default | description |
---|---|---|---|
disabled | boolean |
false |
Disable all interactions with component. |
fill | boolean |
false |
Fill the component width to its parent. |
modelId | string |
Model id of a model to add to the current scope of models to select properties from. | |
onChange | (property: string | Property) => void |
Function which executes when a property is selected in one of the property | |
size | "small" | "medium" | "large" |
"large" |
Size of the property selector. |
value* | string | Property |
A property id used to show the selected property in the property selector. |
Example usage:
({ components: { PropertySelector } }) => {
const [property, setProperty] = React.useState("");
return (
<PropertySelector
onChange={(value) => {
setProperty(value);
}}
value={property}
/>
);
};
Screenshot:
Use it to select properties from the current page scope in the application.
Props | type | default | description |
---|---|---|---|
modelId | string |
Model id of a model to add to the current scope of models to select properties from. | |
onChange* | (property: Property[]) => void |
Function which executes when a property is selected in one of the property selectors. | |
value* | Property[] |
An array of properties used to show the selected properties in the properties selector. |
Example usage:
({ components: { PropertiesSelector } }) => {
const [properties, setProperties] = React.useState([]);
return (
<PropertiesSelector
onChange={(value) => {
setProperties(value);
}}
value={properties}
/>
);
};
Screenshot:
Use it to select an endpoint from the current application.
Props | type | default | description |
---|---|---|---|
fill | boolean |
false |
Fill the component width to its parent. |
onChange | (endpoint: string) => void |
Function which executes when a endpoint is selected in one of the endpoint selectors | |
value* | string |
A endpoint id used to show the selected endpoint in the endpoint selector. |
Example usage:
({ components: { EndpointSelector } }) => {
const [endpoint, setEndpoint] = React.useState("");
return (
<EndpointSelector
onChange={(endpoint) => {
setProperty(endpoint);
}}
value={endpoint}
/>
);
};
Screenshot:
Text
is a component of the Grommet framework. Use it to display text which complies with the Betty Blocks style guide.
Check Grommet's documentation to see how it can be used and which props are available: https://v2.grommet.io/text
Most important props:
Props | type | default | description |
---|---|---|---|
size | "xsmall" | "small" | "medium" | "large" |
"medium" |
Size of the text |
Example:
<Text>Hello world</Text>
Screenshot:
TextInput
is a component of the Grommet framework. Use it to display text inputs that comply with the Betty Blocks style guide.
Check Grommet's documentation to see how it can be used and which props are available: https://v2.grommet.io/textinput
Most important props:
Props | type | default | description |
---|---|---|---|
disabled | boolean |
false |
Disable all interactions with component |
name | string |
The name of the text input for when in a form | |
onChange | (event: React.ChangeEvent<HTMLInputElement>) => void |
Function which executes when text is changed in the text input | |
placeholder | string |
Text input placeholder text | |
size | "medium" | "large" |
"medium" |
Size of the text input |
Example:
({ components: { TextInput } }) => {
const [state, setState] = React.useState("");
return (
<TextInput
placeholder="Placeholder..."
value={state}
onChange={({ target: { value } }) => {
setState(value);
}}
/>
);
};
Screenshot:
Focussed:
TextArea
is a component of the Grommet framework. Use it to display text areas that comply with the Betty Blocks style guide.
Check Grommet's documentation to see how it can be used and which props are available: https://v2.grommet.io/textarea
Most important props:
Props | type | default | description |
---|---|---|---|
disabled | boolean |
false |
Disable all interactions with component |
name | string |
The name of the text input for when in a form | |
onChange | (event: React.ChangeEvent<HTMLInputElement>) => void |
Function which executes when text is changed in the text input | |
placeholder | string |
Text input placeholder text | |
size | "medium" | "large" |
"medium" |
Size of the text input |
resize | boolean |
true |
Determines whether users can resize the textarea |
Example:
({ components: { TextArea } }) => {
const [state, setState] = React.useState("");
return (
<TextArea
resize={false}
onChange={({ target: { value } }) => {
setState(value);
}}
value={state}
/>
);
};
Screenshot:
Focussed:
CircleQuestion
is an Icon. Used to display an Icon in form of question mark. Can be used in combination with BBToltip component: If user hovers on it, the tooltip with the given text(data-tip) appears.
Most important props:
Props | type | description |
---|---|---|
color | string |
Optional, Give the icon the color |
data-tip | string |
The text that is displayed on icon hover |
data-for | string |
Id of bind BBToltip |
a11yTitle | string |
Optional, allignment of the title |
size |
"small" | "medium" | "large" | "xlarge" | string
|
Optional, Size of the icon |
Example:
<CircleQuestion
color="grey500"
size="medium"
data-tip="You can use this action input variable in the action itself."
data-for="variable-tooltip"
/>
BBTooltip
is a component that displays a tooltip on hover, this can be used in combination with CircleQuestion
, see above.
Most important props:
Props | type | description |
---|---|---|
id | string |
Id that corresponds to data-for of component that triggers tooltip show |
place |
'top' | 'right' | 'bottom' | 'left'
|
Placement of tooltip |
type |
'dark' | 'success' | 'warning' | 'error' | 'info' | 'light'
|
Tooltip styling theme |
effect |
'float' | 'solid'
|
Behavior of tooltip |
Example:
<CircleQuestion
color="grey500"
size="medium"
data-tip="You can use this action input variable in the action itself."
data-for="variable-tooltip"
/>
<BBTooltip
id="variable-tooltip"
place="top"
type="dark"
effect="solid"
/>
This is a helper to convert camelCase
to snake_case
this is used for generating variables from the beforeCreate based on models. Will lowercase all characters and replace spaces with underscores.
You can use this helper like this:
camelToSnakeCase("Test String")
// will return: "test_string"
This helper will retrieve the UUID of the current page. If there is no UUID available it will return an empty string. You can use the helper like this:
beforeCreate: ({
// extract other components if needed
helpers: { useCurrentPageId }
}
const pageUuid = useCurrentPageId();
Sometimes you need more information from a model than just the id (e.g. all the properties for a given model), this is where you can use useModelQuery
. It works much in the same way as a useQuery
from apollo does.
You can either use the onCompleted hook in the modelQuery to set your state.
useModelQuery({
variables: { id: modelId },
skip: !modelId,
onCompleted: data => {
// use data here
},
});
Or you can use it more traditionally and destruct the objects from the query call.
const { data, loading, error } = useModelQuery({
variables: { id: modelId },
skip: !modelId,
});
This helper receives an array of white list types and based on that excludes those types from a standard array of allowed types. So the result is an array of unsupported types.
This is the whole list of allowed types:
[ 'AUTO_INCREMENT',
'BELONGS_TO',
'BOOLEAN',
'BOOLEAN_EXPRESSION',
'COUNT',
'DATE',
'DATE_EXPRESSION',
'DATE_TIME',
'DATE_TIME_EXPRESSION',
'DECIMAL',
'DECIMAL_EXPRESSION',
'EMAIL',
'EMAIL_ADDRESS',
'ENUM',
'FILE',
'FLOAT',
'GOOGLE_DOCUMENT',
'HAS_AND_BELONGS_TO_MANY',
'HAS_MANY',
'HAS_ONE',
'IBAN',
'IMAGE',
'INTEGER',
'INTEGER_EXPRESSION',
'LIST',
'LOGIN_TOKEN',
'MINUTES',
'MINUTES_EXPRESSION',
'MULTI_FILE',
'MULTI_IMAGE',
'PASSWORD',
'PDF',
'PERIODIC_COUNT',
'PHONE_NUMBER',
'PRICE',
'PRICE_EXPRESSION',
'RICH_TEXT',
'SERIAL',
'SIGNED_PDF',
'STRING',
'STRING_EXPRESSION',
'SUM',
'TEXT',
'TEXT_EXPRESSION',
'TIME',
'URL',
'ZIPCODE',
}
Example of usage:
const unsupportedKinds = createBlacklist(['TEXT', 'URL', 'IBAN', 'STRING']);
This helper searches through the parent component's tree for the closest component that uses a model, and returns its id. Helpers makes usage of global state. Does not require any input arguments. In material-ui-component-set this helper can be used like that:
const modelId = useModelIdSelector();
When you want to retrieve id of the action that is used in the upper component tree, you can use this selector helper. Does not require any input arguments. In material-ui-component-set this helper can be used like that:
const actionId = useActionIdSelector();
That helper uses the value of the global state. It checks the components' tree and returns the object with the name and id of the closest form where the component was dragged to if such was found.
this function:
export const usePrefabSelector = (): { name: string; id: string } => {
return useSelector((state: State): { name: string; id: string } | null => {
const { components } = state.page;
const formId = state.page.beforeCreate.targetId;
const { prefabName: name, id } = components[formId];
return { name, id };
});
};
In material-ui-component-set this helper can be used like that:
const selectedPrefab = usePrefabSelector();
Creates a new model with the given properties
addModelAndProperties(
modelName, // name op the new model, **has to be unique** (string)
properties // object array containing label and kind
);
modelName has to be unique otherwise you will encounter crashes
The property kind can be any known type of property, to see a full list click here
addModelAndProperties('Customer', [
{
label: 'name',
kind: 'STRING',
},
{
label: 'dateOfBirth',
kind: 'DATE',
},
{
label: 'age',
kind: 'INTEGER',
},
]);
Creates a new schema model with the given name and json object
addSchemaModel(
name, // name of the schema model (string)
jsonSchema // Json object of the schema
)
Name has to be unique otherwise you will encounter crashes
The json schema has to follow a specific structure.
addSchemaModel('MyNewSchemaModel', {
id: "https://bettyblocks.com/question.schema.json",
schema: "https://json-schema.org/draft/2020-12/schema",
description: "BLA BLA BLA",
title: "Question",
type: "object",
properties: {
"score": {
"type": "integer"
},
"answer": {
"type": "string"
},
"uuid": {
"type": "string"
},
"type": {
"type": "string"
},
"title": {
"type": "string"
},
"body": {
"type": "string"
}
}
});