Updating Content Types¶
Existing Content
Type
can be updated either by sending a properly formatted PUT request to the /api/v1/internal/contenttype/{name}
endpoint or through the Content Modeler tool provided with the platform.
Note
You will need to use your Application Read and write API KEY
to perform this action.
Read more about API keys and scoped API keys.
Warning
Changes made to the Content Type Definition may lead to problems with programs and pages already using this CTD if they break compatibility. We strongly suggest only apply non-breaking changes to existing CTDs.
Updating Content Types via API¶
Updating
Content Type is simply a PUT
call with a payload similar to:
{
"name": "blogposts",
"label": "Blog Posts",
"schemaDefinition": {
"type": "object",
"allOf": [
{
"$ref": "#/components/schemas/AbstractContentTypeSchemaDefinition"
},
{
"type": "object",
"properties": {
"title": {
"type": "string",
"minLength": 1
},
"postContent": {
"type": "string",
"minLength": 1
}
}
}
],
"required": [
"title",
"postContent"
],
"additionalProperties": false
},
"metaDefinition": {
"propertiesConfig": {
"title": {
"label": "Title",
"inputType": "text",
"unique": true
},
"postContent": {
"label": "Post content",
"inputType": "richtext",
"unique": false
}
},
"order": [
"title",
"postContent"
]
}
}
You can find description of the schema here
Tip
After every change in Content Definition Schema Flotiq automatically updates your API documentation and Elastic Search index attached to this type. This means your API docs will always stay up to date with your current schema, and so will the results of the full-text search feature.
What happens if you add a new property?¶
When you are adding new properties, objects already in the system will not have that property present until added by you.
What happens if you remove a property?¶
If you remove the property, all objects will be stripped of that property's value after schema would be saved.
What happens if you change a property?¶
When you change existing property, depending on the type of changes, Flotiq will:
- Do nothing with your data if changes are made to
label
,isTitlePart
,unique
,required
,pattern
,options
ordefault
properties - Try to convert data if the changes are made to
type
orinputType
properties if data will not be preserved, and the property is required, update of the CTD will not succeed (more in conversion table) - Remove data attached to old property name when
name
of the property was changed; objects will not have a property with the new name present as in adding a new property
Warning
When updating Content Definition Schema, you must remember that Flotiq will try to update objects of that type to reflect changes in the schema, which can result in data loss, if the old types and new types of the properties are not compatible.
Example
curl --location --request PUT "https://api.flotiq.com/api/v1/internal/contenttype/blogposts" \
--header 'X-AUTH-TOKEN: YOUR_API_KEY' \
--header 'Content-Type: application/json' \
--header 'Accept: */*' \
--data-raw '{
"name":"blogpost",
"label":"Blog Posts",
"schemaDefinition":{
"type":"object",
"allOf":[{
"$ref":"#/components/schemas/AbstractContentTypeSchemaDefinition"
},{
"type":"object",
"properties":{
"title":{"type":"string"},
"postContent":{"type":"string"}}}
],
"required":["title","postContent"],
"additionalProperties":false
},
"metaDefinition":{
"propertiesConfig":{
"title":{
"label":"Title",
"inputType":"text",
"unique":true},
"postContent":{
"label":"Post content",
"inputType":"richtext",
"unique":false}},
"order":["title","postContent"]}
}'
var client = new RestClient("https://api.flotiq.com/api/v1/internal/contenttype/blogposts");
var request = new RestRequest(Method.PUT);
request.AddHeader("content-type", "application/json");
request.AddHeader("X-AUTH-TOKEN", "YOUR_API_KEY");
request.AddParameter("application/json", "{\"name\":\"blogposts\",\"label\":\"Blog Posts\",\"schemaDefinition\":{\"type\":\"object\",\"allOf\":[{\"$ref\":\"#/components/schemas/AbstractContentTypeSchemaDefinition\"},{\"type\":\"object\",\"properties\":{\"title\":{\"type\":\"string\"},\"postContent\":{\"type\":\"string\"}}}],\"required\":[\"title\",\"postContent\"],\"additionalProperties\":false},\"metaDefinition\":{\"propertiesConfig\":{\"title\":{\"label\":\"Title\",\"inputType\":\"text\",\"unique\":true},\"postContent\":{\"label\":\"Post content\",\"inputType\":\"richtext\",\"unique\":false}},\"order\":[\"title\",\"postContent\"]}}", ParameterType.RequestBody);
IRestResponse response = client.Execute(request);
package main
import (
"fmt"
"strings"
"net/http"
"io/ioutil"
)
func main() {
url := "https://api.flotiq.com/api/v1/internal/contenttype/blogposts"
payload := strings.NewReader("{\"name\":\"blogposts\",\"label\":\"Blog Posts\",\"schemaDefinition\":{\"type\":\"object\",\"allOf\":[{\"$ref\":\"#/components/schemas/AbstractContentTypeSchemaDefinition\"},{\"type\":\"object\",\"properties\":{\"title\":{\"type\":\"string\"},\"postContent\":{\"type\":\"string\"}}}],\"required\":[\"title\",\"postContent\"],\"additionalProperties\":false},\"metaDefinition\":{\"propertiesConfig\":{\"title\":{\"label\":\"Title\",\"inputType\":\"text\",\"unique\":true},\"postContent\":{\"label\":\"Post content\",\"inputType\":\"richtext\",\"unique\":false}},\"order\":[\"title\",\"postContent\"]}}")
req, _ := http.NewRequest("PUT", url, payload)
req.Header.Add("content-type", "application/json")
req.Header.Add("X-AUTH-TOKEN", "YOUR_API_KEY")
res, _ := http.DefaultClient.Do(req)
defer res.Body.Close()
body, _ := ioutil.ReadAll(res.Body)
fmt.Println(res)
fmt.Println(string(body))
}
OkHttpClient client = new OkHttpClient();
MediaType mediaType = MediaType.parse("application/json");
RequestBody body = RequestBody.create(mediaType, "{\"name\":\"blogposts\",\"label\":\"Blog Posts\",\"schemaDefinition\":{\"type\":\"object\",\"allOf\":[{\"$ref\":\"#/components/schemas/AbstractContentTypeSchemaDefinition\"},{\"type\":\"object\",\"properties\":{\"title\":{\"type\":\"string\"},\"postContent\":{\"type\":\"string\"}}}],\"required\":[\"title\",\"postContent\"],\"additionalProperties\":false},\"metaDefinition\":{\"propertiesConfig\":{\"title\":{\"label\":\"Title\",\"inputType\":\"text\",\"unique\":true},\"postContent\":{\"label\":\"Post content\",\"inputType\":\"richtext\",\"unique\":false}},\"order\":[\"title\",\"postContent\"]}}");
Request request = new Request.Builder()
.url("https://api.flotiq.com/api/v1/internal/contenttype/blogposts")
.put(body)
.addHeader("content-type", "application/json")
.addHeader("X-AUTH-TOKEN", "YOUR_API_KEY")
.build();
Response response = client.newCall(request).execute();
HttpResponse<String> response = Unirest.put("https://api.flotiq.com/api/v1/internal/contenttype/blogposts")
.header("content-type", "application/json")
.header("X-AUTH-TOKEN", "YOUR_API_KEY")
.body("{\"name\":\"blogposts\",\"label\":\"Blog Posts\",\"schemaDefinition\":{\"type\":\"object\",\"allOf\":[{\"$ref\":\"#/components/schemas/AbstractContentTypeSchemaDefinition\"},{\"type\":\"object\",\"properties\":{\"title\":{\"type\":\"string\"},\"postContent\":{\"type\":\"string\"}}}],\"required\":[\"title\",\"postContent\"],\"additionalProperties\":false},\"metaDefinition\":{\"propertiesConfig\":{\"title\":{\"label\":\"Title\",\"inputType\":\"text\",\"unique\":true},\"postContent\":{\"label\":\"Post content\",\"inputType\":\"richtext\",\"unique\":false}},\"order\":[\"title\",\"postContent\"]}}")
.asString();
const request = require('request');
const options = {
method: 'PUT',
url: 'https://api.flotiq.com/api/v1/internal/contenttype/blogposts',
headers: {'content-type': 'application/json', 'X-AUTH-TOKEN': 'YOUR_API_KEY'},
body: {
"name": "blogposts",
"label": "Blog Posts",
"schemaDefinition": {
"type": "object",
"allOf": [
{
"$ref": "#/components/schemas/AbstractContentTypeSchemaDefinition"
},
{
"type": "object",
"properties": {
"title": {
"type": "string",
"minLength": 1
},
"postContent": {
"type": "string",
"minLength": 1
}
}
}
],
"required": [
"title",
"postContent"
],
"additionalProperties": false
},
"metaDefinition": {
"propertiesConfig": {
"title": {
"label": "Title",
"inputType": "text",
"unique": true
},
"postContent": {
"label": "Post content",
"inputType": "richtext",
"unique": false
}
},
"order": [
"title",
"postContent"
]
}
},
json: true
};
request(options, function (error, response, body) {
if (error) throw new Error(error);
console.log(body);
});
<?php
$curl = curl_init();
curl_setopt_array($curl, [
CURLOPT_URL => "https://api.flotiq.com/api/v1/internal/contenttype/blogposts",
CURLOPT_RETURNTRANSFER => true,
CURLOPT_ENCODING => "",
CURLOPT_MAXREDIRS => 10,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
CURLOPT_CUSTOMREQUEST => "PUT",
CURLOPT_POSTFIELDS => "{\"name\":\"blogposts\",\"label\":\"Blog Posts\",\"schemaDefinition\":{\"type\":\"object\",\"allOf\":[{\"$ref\":\"#/components/schemas/AbstractContentTypeSchemaDefinition\"},{\"type\":\"object\",\"properties\":{\"title\":{\"type\":\"string\"},\"postContent\":{\"type\":\"string\"}}}],\"required\":[\"title\",\"postContent\"],\"additionalProperties\":false},\"metaDefinition\":{\"propertiesConfig\":{\"title\":{\"label\":\"Title\",\"inputType\":\"text\",\"unique\":true},\"postContent\":{\"label\":\"Post content\",\"inputType\":\"richtext\",\"unique\":false}},\"order\":[\"title\",\"postContent\"]}}",
CURLOPT_HTTPHEADER => [
"X-AUTH-TOKEN: YOUR_API_KEY",
"content-type: application/json"
],
]);
$response = curl_exec($curl);
$err = curl_error($curl);
curl_close($curl);
if ($err) {
echo "cURL Error #:" . $err;
} else {
echo $response;
}
Response
Returned when the schema has been correct and was saved
{
"name": "blogposts",
"label": "Blog Posts",
"schemaDefinition": {
"type": "object",
"allOf": [
{
"$ref": "#/components/schemas/AbstractContentTypeSchemaDefinition"
},
{
"type": "object",
"properties": {
"title": {
"type": "string",
"minLength": 1
},
"postContent": {
"type": "string",
"minLength": 1
}
}
}
],
"required": [
"title",
"postContent"
],
"additionalProperties": false
},
"metaDefinition": {
"propertiesConfig": {
"title": {
"label": "Title",
"inputType": "text",
"unique": true
},
"postContent": {
"label": "Post content",
"inputType": "richtext",
"unique": false
}
},
"order": [
"title",
"postContent"
]
}
}
Returned when the schema has not been correct and wasn't saved
{
"name": [
"This value is already used."
],
"label": [
"Must be at least 1 characters long"
],
"schemaDefinition.allOf[1].properties.title.type": [
"Does not have a value in the enumeration [\"array\",\"boolean\",\"integer\",\"null\",\"number\",\"object\",\"string\"]",
"String value found, but an array is required",
"Failed to match at least one schema"
],
"metaDefinition.propertiesConfig.price.label": [
"The property label is required"
],
"metaDefinition.propertiesConfig.price.inputType": [
"Does not have a value in the enumeration [\"text\",\"richtext\",\"textarea\",\"textMarkdown\",\"email\",\"number\",\"radio\",\"checkbox\",\"select\",\"datasource\",\"object\",\"geo\"]"
]
}
Returned when API key was missing or incorrect
{
"code": 401,
"massage": "Unauthorized"
}
Returned when the schema wasn't found
{
"code": 404,
"massage": "Not found"
}
Possible validation errors¶
Possible validation errors resemble those encountered when creating Content Types. You can find the list here.
Validation can also fail when updating the schema by modifying required fields or changing field types.
Updating Schema and Modifying Required Fields¶
When you modify a schema, any associated objects undergo a transformation process. This process ensures that the objects conform to the new schema's structure. However, there are certain considerations to keep in mind:
- Required Fields: If a field is marked as "required" in the schema, the system expects all existing objects to have a value for that field. The transformation will fail during the conversion process if an existing object lacks a value for a newly marked required field.
- Changing Field Types: If you change the data type of a field, particularly from one type to another, that cannot be automatically transformed (e.g., from "Text" to "Relationship"), updating existing objects automatically becomes challenging.
When encountering issues during schema conversion, you might receive error messages like the following:
Response
Returned when the schema has not been correct and wasn't saved
{
"fieldName":["The property fieldName is required"],
"co":{
"coFieldName":["Object: /api/v1/content/_media/media doesn't exists "]
}
"ctd":["Existing objects do not conform to the new schema, and updating them automatically is impossible (e.g., Text -> Relationship). Ensure you're not changing a field type that is also required - if so, disable required for the field you're changing."]
}
How are fields converted from one type to another?¶
Old Type (type/inputType) | New Type (type/inputType) | Conversion |
---|---|---|
string/text | string/textarea | Data are preserved |
string/text | string/markdown | Data are preserved |
string/text | string/richtext | Data are preserved |
string/text | string/email | Data are preserved |
string/text | number/number | All objects gets 0 value |
string/text | string/radio | Data are preserved |
string/text | boolean/checkbox | All objects gets false value |
string/text | string/select | Data are preserved |
string/text | array/object | All objects gets empty array |
string/text | array/datasource | All objects gets empty array |
string/text | object/geo | All objects gets object with value: {lat: 0, lon: 0} |
string/text | array/select | String is converted to array of strings if data are among available options |
string/text | array/simpleList | String is converted to array of strings |
string/textarea | string/text | Data are preserved |
string/textarea | string/markdown | Data are preserved |
string/textarea | string/richtext | Data are preserved |
string/textarea | string/email | Data are preserved |
string/textarea | number/number | All objects gets 0 value |
string/textarea | string/radio | Data are preserved |
string/textarea | boolean/checkbox | All objects gets false value |
string/textarea | string/select | Data are preserved |
string/textarea | array/object | All objects gets empty array |
string/textarea | array/datasource | All objects gets empty array |
string/textarea | object/geo | All objects gets object with value: {lat: 0, lon: 0} |
string/textarea | array/select | String is converted to array of strings if data are among available options |
string/textarea | array/simpleList | String is converted to array of strings |
string/markdown | string/text | Data are preserved |
string/markdown | string/textarea | Data are preserved |
string/markdown | string/richtext | Data are preserved |
string/markdown | string/email | Data are preserved |
string/markdown | number/number | All objects gets 0 value |
string/markdown | string/radio | Data are preserved |
string/markdown | boolean/checkbox | All objects gets false value |
string/markdown | string/select | Data are preserved |
string/markdown | array/object | All objects gets empty array |
string/markdown | array/datasource | All objects gets empty array |
string/markdown | object/geo | All objects gets object with value: {lat: 0, lon: 0} |
string/markdown | array/select | String is converted to array of strings if data are among available options |
string/markdown | array/simpleList | String is converted to array of strings |
string/richtext | string/text | Data are preserved |
string/richtext | string/textarea | Data are preserved |
string/richtext | string/markdown | Data are preserved |
string/richtext | string/email | Data are preserved |
string/richtext | number/number | All objects gets 0 value |
string/richtext | string/radio | Data are preserved |
string/richtext | boolean/checkbox | All objects gets false value |
string/richtext | string/select | Data are preserved |
string/richtext | array/object | All objects gets empty array |
string/richtext | array/datasource | All objects gets empty array |
string/richtext | object/geo | All objects gets object with value: {lat: 0, lon: 0} |
string/richtext | array/select | String is converted to array of strings if data are among available options |
string/richtext | array/simpleList | String is converted to array of strings |
string/email | string/textarea | Data are preserved |
string/email | string/markdown | Data are preserved |
string/email | string/richtext | Data are preserved |
string/email | number/number | All objects gets 0 value |
string/email | string/radio | Data are preserved |
string/email | boolean/checkbox | All objects gets false value |
string/email | string/select | Data are preserved |
string/email | array/object | All objects gets empty array |
string/email | array/datasource | All objects gets empty array |
string/email | object/geo | All objects gets object with value: {lat: 0, lon: 0} |
string/email | array/select | String is converted to array of strings if data are among available options |
string/email | array/simpleList | String is converted to array of strings |
number/number | string/text | Numbers are converted to string counterparts |
number/number | string/textarea | Numbers are converted to string counterparts |
number/number | string/markdown | Numbers are converted to string counterparts |
number/number | string/richtext | Numbers are converted to string counterparts |
number/number | string/email | Numbers are converted to string counterparts |
number/number | string/radio | Numbers are converted to string counterparts |
number/number | boolean/checkbox | All objects gets false value |
number/number | string/select | Numbers are converted to string counterparts |
number/number | array/object | All objects gets empty array |
number/number | array/datasource | All objects gets empty array |
number/number | object/geo | All objects gets object with value: {lat: 0, lon: 0} |
number/number | array/select | Numbers are converted to string counterparts and then converted to array of strings if data are among available options |
number/number | array/simpleList | Numbers are converted to string counterparts and then converted to array of strings |
string/radio | string/text | Data are preserved |
string/radio | string/textarea | Data are preserved |
string/radio | string/markdown | Data are preserved |
string/radio | string/richtext | Data are preserved |
string/radio | string/email | Data are preserved |
string/radio | number/number | All objects gets 0 value |
string/radio | boolean/checkbox | All objects gets false value |
string/radio | string/select | Data are preserved |
string/radio | array/object | All objects gets empty array |
string/radio | array/datasource | All objects gets empty array |
string/radio | object/geo | All objects gets object with value: {lat: 0, lon: 0} |
string/radio | array/select | String is converted to array of strings if data are among available options |
string/radio | array/simpleList | String is converted to array of strings |
boolean/checkbox | string/text | Data are converted to the string counterparts |
boolean/checkbox | string/textarea | Data are converted to the string counterparts |
boolean/checkbox | string/markdown | Data are converted to the string counterparts |
boolean/checkbox | string/richtext | Data are converted to the string counterparts |
boolean/checkbox | string/email | Data are converted to the string counterparts |
boolean/checkbox | number/number | All objects gets 0 value |
boolean/checkbox | string/radio | Data are converted to the string counterparts |
boolean/checkbox | string/select | Data are converted to the string counterparts |
boolean/checkbox | array/object | All object gets empty array |
boolean/checkbox | array/datasource | All object gets empty array |
boolean/checkbox | object/geo | All object gets object with value: {lat: 0, lon: 0} |
boolean/checkbox | array/select | Data are converted to string counterparts and then converted to array of strings if data are among available options |
boolean/checkbox | array/simpleList | Data are converted to string counterparts and then converted to array of strings |
string/select | string/text | Data are preserved |
string/select | string/textarea | Data are preserved |
string/select | string/markdown | Data are preserved |
string/select | string/richtext | Data are preserved |
string/select | string/email | Data are preserved |
string/select | number/number | All objects gets 0 value |
string/select | string/radio | Data are preserved |
string/select | boolean/checkbox | All objects gets false value |
string/select | array/object | All objects gets empty array |
string/select | array/datasource | All objects gets empty array |
string/select | object/geo | All objects gets object with value: {lat: 0, lon: 0} |
string/select | array/select | All objects gets empty array |
string/select | array/simpleList | All objects gets empty array |
array/select | string/text | Data are joined to simple string using , as glue |
array/select | string/textarea | Data are joined to simple string using , as glue |
array/select | string/markdown | Data are joined to simple string using , as glue |
array/select | string/richtext | Data are joined to simple string using , as glue |
array/select | string/email | Data are joined to simple string using , as glue |
array/select | number/number | All objects gets 0 value |
array/select | string/radio | First element of data is preserved |
array/select | boolean/checkbox | All objects gets false value |
array/select | array/object | All objects gets empty array |
array/select | array/datasource | All objects gets empty array |
array/select | object/geo | All objects gets object with value: {lat: 0, lon: 0} |
array/select | string/select | First element of data is preserved |
array/select | array/simpleList | Data are preserved |
array/object | string/text | All objects gets '' value |
array/object | string/textarea | All objects gets '' value |
array/object | string/markdown | All objects gets '' value |
array/object | string/richtext | All objects gets '' value |
array/object | string/email | All objects gets '' value |
array/object | number/number | All objects gets 0 value |
array/object | string/radio | All objects gets '' value |
array/object | boolean/checkbox | All objects gets false value |
array/object | string/select | All objects gets '' value |
array/object | array/datasource | All objects gets empty array |
array/object | object/geo | All objects gets object with value: {lat: 0, lon: 0} |
array/object | array/select | String is converted to array of strings |
array/object | array/simpleList | String is converted to array of strings |
array/datasource | string/text | Data are converted to dataUrls joined with ', ' |
array/datasource | string/textarea | Data are converted to dataUrls joined with ', ' |
array/datasource | string/markdown | Data are converted to dataUrls joined with ', ' |
array/datasource | string/richtext | Data are converted to dataUrls joined with ', ' |
array/datasource | string/email | Data are converted to dataUrls joined with ', ' |
array/datasource | number/number | All objects gets 0 value |
array/datasource | string/radio | Data are converted to dataUrls joined with ', ' |
array/datasource | boolean/checkbox | All objects gets false value |
array/datasource | string/select | Data are converted to dataUrls joined with ', ' |
array/datasource | array/object | All objects gets empty array |
array/datasource | object/geo | All objects gets object with value: {lat: 0, lon: 0} |
array/datasource | array/select | All objects gets empty array |
array/datasource | array/simpleList | All objects gets empty array |
object/geo | string/text | Data are converted to 'lat: {lat value}, lon: {lon value}' |
object/geo | string/textarea | Data are converted to 'lat: {lat value}, lon: {lon value}' |
object/geo | string/markdown | Data are converted to 'lat: {lat value}, lon: {lon value}' |
object/geo | string/richtext | Data are converted to 'lat: {lat value}, lon: {lon value}' |
object/geo | string/email | Data are converted to 'lat: {lat value}, lon: {lon value}' |
object/geo | number/number | All objects gets 0 value |
object/geo | string/radio | Data are converted to 'lat: {lat value}, lon: {lon value}' |
object/geo | boolean/checkbox | All objects gets false value |
object/geo | string/select | Data are converted to 'lat: {lat value}, lon: {lon value}' |
object/geo | array/object | All objects gets empty array |
object/geo | array/datasource | All objects gets empty array |
object/geo | array/select | All objects gets empty array |
object/geo | array/simpleList | All objects gets empty array |
array/simpleList | string/text | Data are joined to simple string using , as glue |
array/simpleList | string/textarea | Data are joined to simple string using , as glue |
array/simpleList | string/markdown | Data are joined to simple string using , as glue |
array/simpleList | string/richtext | Data are joined to simple string using , as glue |
array/simpleList | string/email | Data are joined to simple string using , as glue |
array/simpleList | number/number | All objects gets 0 value |
array/simpleList | string/radio | First element of data is preserved |
array/simpleList | boolean/checkbox | All objects gets false value |
array/simpleList | array/object | All objects gets empty array |
array/simpleList | array/datasource | All objects gets empty array |
array/simpleList | object/geo | All objects gets object with value: {lat: 0, lon: 0} |
array/simpleList | string/select | First element of data is preserved |
array/simpleList | array/select | Data are preserved |
Updating Content Types through the Content modeller¶
If you'd rather use our graphical interface to update your Content Types - read the Content modeller documentation