diff --git a/rest-api-spec/src/main/resources/rest-api-spec/api/indices.get_field_mapping.json b/rest-api-spec/src/main/resources/rest-api-spec/api/indices.get_field_mapping.json index 3d5a629eff08e..3ce610153b5e6 100644 --- a/rest-api-spec/src/main/resources/rest-api-spec/api/indices.get_field_mapping.json +++ b/rest-api-spec/src/main/resources/rest-api-spec/api/indices.get_field_mapping.json @@ -21,6 +21,10 @@ } }, "params": { + "include_type_name": { + "type" : "boolean", + "description" : "Whether a type should be returned in the body of the mappings." + }, "include_defaults": { "type" : "boolean", "description" : "Whether the default mapping values should be returned as well" diff --git a/rest-api-spec/src/main/resources/rest-api-spec/api/indices.get_template.json b/rest-api-spec/src/main/resources/rest-api-spec/api/indices.get_template.json index e3a97ee5c012a..e2aae3b7444aa 100644 --- a/rest-api-spec/src/main/resources/rest-api-spec/api/indices.get_template.json +++ b/rest-api-spec/src/main/resources/rest-api-spec/api/indices.get_template.json @@ -16,6 +16,10 @@ } }, "params": { + "include_type_name": { + "type" : "boolean", + "description" : "Whether a type should be returned in the body of the mappings." + }, "flat_settings": { "type": "boolean", "description": "Return settings in flat format (default: false)" diff --git a/rest-api-spec/src/main/resources/rest-api-spec/api/indices.put_template.json b/rest-api-spec/src/main/resources/rest-api-spec/api/indices.put_template.json index 5bcb2f8a24346..65aa9506ff9f1 100644 --- a/rest-api-spec/src/main/resources/rest-api-spec/api/indices.put_template.json +++ b/rest-api-spec/src/main/resources/rest-api-spec/api/indices.put_template.json @@ -13,6 +13,10 @@ } }, "params": { + "include_type_name": { + "type" : "boolean", + "description" : "Whether a type should be returned in the body of the mappings." + }, "order": { "type" : "number", "description" : "The order for this template when merging multiple matching ones (higher numbers are merged later, overriding the lower numbers)" diff --git a/rest-api-spec/src/main/resources/rest-api-spec/test/indices.create/10_basic.yml b/rest-api-spec/src/main/resources/rest-api-spec/test/indices.create/10_basic.yml index 637a146ca68ca..6f1b77a36b8b3 100644 --- a/rest-api-spec/src/main/resources/rest-api-spec/test/indices.create/10_basic.yml +++ b/rest-api-spec/src/main/resources/rest-api-spec/test/indices.create/10_basic.yml @@ -2,7 +2,7 @@ "Create index with mappings": - skip: version: " - 6.6.99" - reason: include_type_name was introduced in 6.7 + reason: include_type_name was introduced in 6.7.0 - do: indices.create: include_type_name: false @@ -22,7 +22,7 @@ "Create index with settings": - skip: version: " - 6.6.99" - reason: include_type_name was introduced in 6.7 + reason: include_type_name was introduced in 6.7.0 - do: indices.create: include_type_name: false @@ -41,7 +41,7 @@ "Create index": - skip: version: " - 6.6.99" - reason: include_type_name was introduced in 6.7 + reason: include_type_name was introduced in 6.7.0 - do: indices.create: include_type_name: false @@ -54,7 +54,7 @@ "Create index with wait_for_active_shards set to all": - skip: version: " - 6.6.99" - reason: include_type_name was introduced in 6.7 + reason: include_type_name was introduced in 6.7.0 - do: indices.create: include_type_name: false @@ -71,7 +71,7 @@ "Create index with aliases": - skip: version: " - 6.6.99" - reason: include_type_name was introduced in 6.7 + reason: include_type_name was introduced in 6.7.0 - do: indices.create: include_type_name: false diff --git a/rest-api-spec/src/main/resources/rest-api-spec/test/indices.get_field_mapping/10_basic.yml b/rest-api-spec/src/main/resources/rest-api-spec/test/indices.get_field_mapping/10_basic.yml index 6cf0a0b7cf26c..c2c975a229f52 100644 --- a/rest-api-spec/src/main/resources/rest-api-spec/test/indices.get_field_mapping/10_basic.yml +++ b/rest-api-spec/src/main/resources/rest-api-spec/test/indices.get_field_mapping/10_basic.yml @@ -1,54 +1,48 @@ --- setup: + - skip: + version: " - 6.6.99" + reason: include_type_name was introduced in 6.7.0 - do: indices.create: + include_type_name: false index: test_index body: mappings: - test_type: - properties: - text: - type: text + properties: + text: + type: text --- -"Get field mapping with no index and type": +"Get field mapping with no index": - do: indices.get_field_mapping: + include_type_name: false fields: text - - match: {test_index.mappings.test_type.text.mapping.text.type: text} + - match: {test_index.mappings.text.mapping.text.type: text} --- "Get field mapping by index only": - do: indices.get_field_mapping: + include_type_name: false index: test_index fields: text - - match: {test_index.mappings.test_type.text.mapping.text.type: text} + - match: {test_index.mappings.text.mapping.text.type: text} --- -"Get field mapping by type & field": +"Get field mapping by field, with another field that doesn't exist": - do: indices.get_field_mapping: + include_type_name: false index: test_index - type: test_type - fields: text - - - match: {test_index.mappings.test_type.text.mapping.text.type: text} - ---- -"Get field mapping by type & field, with another field that doesn't exist": - - - do: - indices.get_field_mapping: - index: test_index - type: test_type fields: [ text , text1 ] - - match: {test_index.mappings.test_type.text.mapping.text.type: text} + - match: {test_index.mappings.text.mapping.text.type: text} - is_false: test_index.mappings.test_type.text1 --- @@ -56,21 +50,10 @@ setup: - do: indices.get_field_mapping: + include_type_name: false index: test_index - type: test_type fields: text include_defaults: true - - match: {test_index.mappings.test_type.text.mapping.text.type: text} - - match: {test_index.mappings.test_type.text.mapping.text.analyzer: default} - ---- -"Get field mapping should work without index specifying type and fields": - - - do: - indices.get_field_mapping: - type: test_type - fields: text - - - match: {test_index.mappings.test_type.text.mapping.text.type: text} - + - match: {test_index.mappings.text.mapping.text.type: text} + - match: {test_index.mappings.text.mapping.text.analyzer: default} diff --git a/rest-api-spec/src/main/resources/rest-api-spec/test/indices.get_field_mapping/11_basic_with_types.yml b/rest-api-spec/src/main/resources/rest-api-spec/test/indices.get_field_mapping/11_basic_with_types.yml new file mode 100644 index 0000000000000..6cf0a0b7cf26c --- /dev/null +++ b/rest-api-spec/src/main/resources/rest-api-spec/test/indices.get_field_mapping/11_basic_with_types.yml @@ -0,0 +1,76 @@ +--- +setup: + - do: + indices.create: + index: test_index + body: + mappings: + test_type: + properties: + text: + type: text + +--- +"Get field mapping with no index and type": + + - do: + indices.get_field_mapping: + fields: text + + - match: {test_index.mappings.test_type.text.mapping.text.type: text} + +--- +"Get field mapping by index only": + - do: + indices.get_field_mapping: + index: test_index + fields: text + + - match: {test_index.mappings.test_type.text.mapping.text.type: text} + +--- +"Get field mapping by type & field": + + - do: + indices.get_field_mapping: + index: test_index + type: test_type + fields: text + + - match: {test_index.mappings.test_type.text.mapping.text.type: text} + +--- +"Get field mapping by type & field, with another field that doesn't exist": + + - do: + indices.get_field_mapping: + index: test_index + type: test_type + fields: [ text , text1 ] + + - match: {test_index.mappings.test_type.text.mapping.text.type: text} + - is_false: test_index.mappings.test_type.text1 + +--- +"Get field mapping with include_defaults": + + - do: + indices.get_field_mapping: + index: test_index + type: test_type + fields: text + include_defaults: true + + - match: {test_index.mappings.test_type.text.mapping.text.type: text} + - match: {test_index.mappings.test_type.text.mapping.text.analyzer: default} + +--- +"Get field mapping should work without index specifying type and fields": + + - do: + indices.get_field_mapping: + type: test_type + fields: text + + - match: {test_index.mappings.test_type.text.mapping.text.type: text} + diff --git a/rest-api-spec/src/main/resources/rest-api-spec/test/indices.get_field_mapping/20_missing_field.yml b/rest-api-spec/src/main/resources/rest-api-spec/test/indices.get_field_mapping/20_missing_field.yml index 9b8c3efbce81a..61f1f409d2939 100644 --- a/rest-api-spec/src/main/resources/rest-api-spec/test/indices.get_field_mapping/20_missing_field.yml +++ b/rest-api-spec/src/main/resources/rest-api-spec/test/indices.get_field_mapping/20_missing_field.yml @@ -1,21 +1,22 @@ --- "Return empty object if field doesn't exist, but type and index do": - + - skip: + version: " - 6.99.99" + reason: types are required in requests before 7.0.0 - do: indices.create: + include_type_name: false index: test_index body: - mappings: - test_type: - properties: - text: - type: text - analyzer: whitespace + mappings: + properties: + text: + type: text + analyzer: whitespace - do: indices.get_field_mapping: index: test_index - type: test_type fields: not_existent - - - match: { '': {}} + + - match: { 'test_index.mappings': {}} diff --git a/rest-api-spec/src/main/resources/rest-api-spec/test/indices.get_field_mapping/21_missing_field_with_types.yml b/rest-api-spec/src/main/resources/rest-api-spec/test/indices.get_field_mapping/21_missing_field_with_types.yml new file mode 100644 index 0000000000000..c760561f09282 --- /dev/null +++ b/rest-api-spec/src/main/resources/rest-api-spec/test/indices.get_field_mapping/21_missing_field_with_types.yml @@ -0,0 +1,21 @@ +--- +"Return empty object if field doesn't exist, but type and index do": + + - do: + indices.create: + index: test_index + body: + mappings: + test_type: + properties: + text: + type: text + analyzer: whitespace + + - do: + indices.get_field_mapping: + index: test_index + type: test_type + fields: not_existent + + - match: { '': {}} diff --git a/rest-api-spec/src/main/resources/rest-api-spec/test/indices.get_field_mapping/40_missing_index.yml b/rest-api-spec/src/main/resources/rest-api-spec/test/indices.get_field_mapping/40_missing_index.yml index 7da516e116c3d..7c7b07b587849 100644 --- a/rest-api-spec/src/main/resources/rest-api-spec/test/indices.get_field_mapping/40_missing_index.yml +++ b/rest-api-spec/src/main/resources/rest-api-spec/test/indices.get_field_mapping/40_missing_index.yml @@ -5,7 +5,6 @@ catch: missing indices.get_field_mapping: index: test_index - type: type fields: field diff --git a/rest-api-spec/src/main/resources/rest-api-spec/test/indices.get_field_mapping/50_field_wildcards.yml b/rest-api-spec/src/main/resources/rest-api-spec/test/indices.get_field_mapping/50_field_wildcards.yml index 9d62ab6101fc2..3ffecdcc72618 100644 --- a/rest-api-spec/src/main/resources/rest-api-spec/test/indices.get_field_mapping/50_field_wildcards.yml +++ b/rest-api-spec/src/main/resources/rest-api-spec/test/indices.get_field_mapping/50_field_wildcards.yml @@ -1,135 +1,142 @@ --- setup: + - skip: + version: " - 6.99.99" + reason: types are required in requests before 7.0.0 - do: indices.create: + include_type_name: false index: test_index body: mappings: - test_type: - properties: - t1: - type: text - t2: - type: text - obj: - properties: - t1: - type: text - i_t1: - type: text - i_t3: - type: text + properties: + t1: + type: text + t2: + type: text + obj: + properties: + t1: + type: text + i_t1: + type: text + i_t3: + type: text - do: indices.create: + include_type_name: false index: test_index_2 body: mappings: - test_type_2: - properties: - t1: - type: text - t2: - type: text - obj: - properties: - t1: - type: text - i_t1: - type: text - i_t3: - type: text + properties: + t1: + type: text + t2: + type: text + obj: + properties: + t1: + type: text + i_t1: + type: text + i_t3: + type: text --- "Get field mapping with * for fields": - do: indices.get_field_mapping: + include_type_name: false fields: "*" - - match: {test_index.mappings.test_type.t1.full_name: t1 } - - match: {test_index.mappings.test_type.t2.full_name: t2 } - - match: {test_index.mappings.test_type.obj\.t1.full_name: obj.t1 } - - match: {test_index.mappings.test_type.obj\.i_t1.full_name: obj.i_t1 } - - match: {test_index.mappings.test_type.obj\.i_t3.full_name: obj.i_t3 } + - match: {test_index.mappings.t1.full_name: t1 } + - match: {test_index.mappings.t2.full_name: t2 } + - match: {test_index.mappings.obj\.t1.full_name: obj.t1 } + - match: {test_index.mappings.obj\.i_t1.full_name: obj.i_t1 } + - match: {test_index.mappings.obj\.i_t3.full_name: obj.i_t3 } --- "Get field mapping with t* for fields": - do: indices.get_field_mapping: + include_type_name: false index: test_index fields: "t*" - - match: {test_index.mappings.test_type.t1.full_name: t1 } - - match: {test_index.mappings.test_type.t2.full_name: t2 } - - length: {test_index.mappings.test_type: 2} + - match: {test_index.mappings.t1.full_name: t1 } + - match: {test_index.mappings.t2.full_name: t2 } + - length: {test_index.mappings: 2} --- "Get field mapping with *t1 for fields": - do: indices.get_field_mapping: + include_type_name: false index: test_index fields: "*t1" - - match: {test_index.mappings.test_type.t1.full_name: t1 } - - match: {test_index.mappings.test_type.obj\.t1.full_name: obj.t1 } - - match: {test_index.mappings.test_type.obj\.i_t1.full_name: obj.i_t1 } - - length: {test_index.mappings.test_type: 3} + - match: {test_index.mappings.t1.full_name: t1 } + - match: {test_index.mappings.obj\.t1.full_name: obj.t1 } + - match: {test_index.mappings.obj\.i_t1.full_name: obj.i_t1 } + - length: {test_index.mappings: 3} --- "Get field mapping with wildcarded relative names": - do: indices.get_field_mapping: + include_type_name: false index: test_index fields: "obj.i_*" - - match: {test_index.mappings.test_type.obj\.i_t1.full_name: obj.i_t1 } - - match: {test_index.mappings.test_type.obj\.i_t3.full_name: obj.i_t3 } - - length: {test_index.mappings.test_type: 2} + - match: {test_index.mappings.obj\.i_t1.full_name: obj.i_t1 } + - match: {test_index.mappings.obj\.i_t3.full_name: obj.i_t3 } + - length: {test_index.mappings: 2} --- -"Get field mapping should work using '_all' for indices and types": +"Get field mapping should work using '_all' for index": - do: indices.get_field_mapping: + include_type_name: false index: _all - type: _all fields: "t*" - - match: {test_index.mappings.test_type.t1.full_name: t1 } - - match: {test_index.mappings.test_type.t2.full_name: t2 } - - length: {test_index.mappings.test_type: 2} - - match: {test_index_2.mappings.test_type_2.t1.full_name: t1 } - - match: {test_index_2.mappings.test_type_2.t2.full_name: t2 } - - length: {test_index_2.mappings.test_type_2: 2} + - match: {test_index.mappings.t1.full_name: t1 } + - match: {test_index.mappings.t2.full_name: t2 } + - length: {test_index.mappings: 2} + - match: {test_index_2.mappings.t1.full_name: t1 } + - match: {test_index_2.mappings.t2.full_name: t2 } + - length: {test_index_2.mappings: 2} --- -"Get field mapping should work using '*' for indices and types": +"Get field mapping should work using '*' for index": - do: indices.get_field_mapping: + include_type_name: false index: '*' - type: '*' fields: "t*" - - match: {test_index.mappings.test_type.t1.full_name: t1 } - - match: {test_index.mappings.test_type.t2.full_name: t2 } - - length: {test_index.mappings.test_type: 2} - - match: {test_index_2.mappings.test_type_2.t1.full_name: t1 } - - match: {test_index_2.mappings.test_type_2.t2.full_name: t2 } - - length: {test_index_2.mappings.test_type_2: 2} + - match: {test_index.mappings.t1.full_name: t1 } + - match: {test_index.mappings.t2.full_name: t2 } + - length: {test_index.mappings: 2} + - match: {test_index_2.mappings.t1.full_name: t1 } + - match: {test_index_2.mappings.t2.full_name: t2 } + - length: {test_index_2.mappings: 2} --- -"Get field mapping should work using comma_separated values for indices and types": +"Get field mapping should work using comma_separated values for indices": - do: indices.get_field_mapping: + include_type_name: false index: 'test_index,test_index_2' - type: 'test_type,test_type_2' fields: "t*" - - match: {test_index.mappings.test_type.t1.full_name: t1 } - - match: {test_index.mappings.test_type.t2.full_name: t2 } - - length: {test_index.mappings.test_type: 2} - - match: {test_index_2.mappings.test_type_2.t1.full_name: t1 } - - match: {test_index_2.mappings.test_type_2.t2.full_name: t2 } - - length: {test_index_2.mappings.test_type_2: 2} + - match: {test_index.mappings.t1.full_name: t1 } + - match: {test_index.mappings.t2.full_name: t2 } + - length: {test_index.mappings: 2} + - match: {test_index_2.mappings.t1.full_name: t1 } + - match: {test_index_2.mappings.t2.full_name: t2 } + - length: {test_index_2.mappings: 2} diff --git a/rest-api-spec/src/main/resources/rest-api-spec/test/indices.get_field_mapping/51_field_wildcards_with_types.yml b/rest-api-spec/src/main/resources/rest-api-spec/test/indices.get_field_mapping/51_field_wildcards_with_types.yml new file mode 100644 index 0000000000000..9d62ab6101fc2 --- /dev/null +++ b/rest-api-spec/src/main/resources/rest-api-spec/test/indices.get_field_mapping/51_field_wildcards_with_types.yml @@ -0,0 +1,135 @@ +--- +setup: + - do: + indices.create: + index: test_index + body: + mappings: + test_type: + properties: + t1: + type: text + t2: + type: text + obj: + properties: + t1: + type: text + i_t1: + type: text + i_t3: + type: text + + - do: + indices.create: + index: test_index_2 + body: + mappings: + test_type_2: + properties: + t1: + type: text + t2: + type: text + obj: + properties: + t1: + type: text + i_t1: + type: text + i_t3: + type: text + +--- +"Get field mapping with * for fields": + + - do: + indices.get_field_mapping: + fields: "*" + + - match: {test_index.mappings.test_type.t1.full_name: t1 } + - match: {test_index.mappings.test_type.t2.full_name: t2 } + - match: {test_index.mappings.test_type.obj\.t1.full_name: obj.t1 } + - match: {test_index.mappings.test_type.obj\.i_t1.full_name: obj.i_t1 } + - match: {test_index.mappings.test_type.obj\.i_t3.full_name: obj.i_t3 } + +--- +"Get field mapping with t* for fields": + + - do: + indices.get_field_mapping: + index: test_index + fields: "t*" + + - match: {test_index.mappings.test_type.t1.full_name: t1 } + - match: {test_index.mappings.test_type.t2.full_name: t2 } + - length: {test_index.mappings.test_type: 2} + +--- +"Get field mapping with *t1 for fields": + + - do: + indices.get_field_mapping: + index: test_index + fields: "*t1" + - match: {test_index.mappings.test_type.t1.full_name: t1 } + - match: {test_index.mappings.test_type.obj\.t1.full_name: obj.t1 } + - match: {test_index.mappings.test_type.obj\.i_t1.full_name: obj.i_t1 } + - length: {test_index.mappings.test_type: 3} + +--- +"Get field mapping with wildcarded relative names": + + - do: + indices.get_field_mapping: + index: test_index + fields: "obj.i_*" + - match: {test_index.mappings.test_type.obj\.i_t1.full_name: obj.i_t1 } + - match: {test_index.mappings.test_type.obj\.i_t3.full_name: obj.i_t3 } + - length: {test_index.mappings.test_type: 2} + +--- +"Get field mapping should work using '_all' for indices and types": + + - do: + indices.get_field_mapping: + index: _all + type: _all + fields: "t*" + - match: {test_index.mappings.test_type.t1.full_name: t1 } + - match: {test_index.mappings.test_type.t2.full_name: t2 } + - length: {test_index.mappings.test_type: 2} + - match: {test_index_2.mappings.test_type_2.t1.full_name: t1 } + - match: {test_index_2.mappings.test_type_2.t2.full_name: t2 } + - length: {test_index_2.mappings.test_type_2: 2} + +--- +"Get field mapping should work using '*' for indices and types": + + - do: + indices.get_field_mapping: + index: '*' + type: '*' + fields: "t*" + - match: {test_index.mappings.test_type.t1.full_name: t1 } + - match: {test_index.mappings.test_type.t2.full_name: t2 } + - length: {test_index.mappings.test_type: 2} + - match: {test_index_2.mappings.test_type_2.t1.full_name: t1 } + - match: {test_index_2.mappings.test_type_2.t2.full_name: t2 } + - length: {test_index_2.mappings.test_type_2: 2} + +--- +"Get field mapping should work using comma_separated values for indices and types": + + - do: + indices.get_field_mapping: + index: 'test_index,test_index_2' + type: 'test_type,test_type_2' + fields: "t*" + - match: {test_index.mappings.test_type.t1.full_name: t1 } + - match: {test_index.mappings.test_type.t2.full_name: t2 } + - length: {test_index.mappings.test_type: 2} + - match: {test_index_2.mappings.test_type_2.t1.full_name: t1 } + - match: {test_index_2.mappings.test_type_2.t2.full_name: t2 } + - length: {test_index_2.mappings.test_type_2: 2} + diff --git a/rest-api-spec/src/main/resources/rest-api-spec/test/indices.get_field_mapping/60_mix_typeless_typeful.yml b/rest-api-spec/src/main/resources/rest-api-spec/test/indices.get_field_mapping/60_mix_typeless_typeful.yml new file mode 100644 index 0000000000000..ceead38b099b9 --- /dev/null +++ b/rest-api-spec/src/main/resources/rest-api-spec/test/indices.get_field_mapping/60_mix_typeless_typeful.yml @@ -0,0 +1,24 @@ +--- +"GET mapping with typeless API on an index that has types": + + - skip: + version: " - 6.99.99" + reason: include_type_name was introduced in 6.7.0 + + - do: + indices.create: # not using include_type_name: false on purpose + index: index + body: + mappings: + not_doc: + properties: + foo: + type: "keyword" + + - do: + indices.get_field_mapping: + include_type_name: false + index: index + fields: foo + + - match: { index.mappings.foo.mapping.foo.type: "keyword" } diff --git a/rest-api-spec/src/main/resources/rest-api-spec/test/indices.get_mapping/10_basic.yml b/rest-api-spec/src/main/resources/rest-api-spec/test/indices.get_mapping/10_basic.yml index 643b7441765d1..13179f69a5e1f 100644 --- a/rest-api-spec/src/main/resources/rest-api-spec/test/indices.get_mapping/10_basic.yml +++ b/rest-api-spec/src/main/resources/rest-api-spec/test/indices.get_mapping/10_basic.yml @@ -2,7 +2,7 @@ setup: - skip: version: " - 6.6.99" - reason: include_type_name was introduced in 6.7 + reason: include_type_name was introduced in 6.7.0 - do: indices.create: include_type_name: false diff --git a/rest-api-spec/src/main/resources/rest-api-spec/test/indices.get_template/10_basic.yml b/rest-api-spec/src/main/resources/rest-api-spec/test/indices.get_template/10_basic.yml index 0746c9e805ac3..f74f4f399e85f 100644 --- a/rest-api-spec/src/main/resources/rest-api-spec/test/indices.get_template/10_basic.yml +++ b/rest-api-spec/src/main/resources/rest-api-spec/test/indices.get_template/10_basic.yml @@ -1,4 +1,7 @@ setup: + - skip: + version: " - 6.6.99" + reason: include_type_name was introduced in 6.7 - do: indices.put_template: name: test @@ -7,6 +10,11 @@ setup: settings: number_of_shards: 1 number_of_replicas: 0 + mappings: + _doc: + properties: + field: + type: keyword --- "Get template": @@ -17,10 +25,36 @@ setup: - do: indices.get_template: + include_type_name: false name: test - match: {test.index_patterns: ["test-*"]} - match: {test.settings: {index: {number_of_shards: '1', number_of_replicas: '0'}}} + - match: {test.mappings: {properties: {field: {type: keyword}}}} + +--- +"Get template with no mappings": + - skip: + version: " - 5.99.99" + reason: this uses a new API that has been added in 6.0 + + - do: + indices.put_template: + name: test_no_mappings + body: + index_patterns: test-* + settings: + number_of_shards: 1 + number_of_replicas: 0 + + - do: + indices.get_template: + include_type_name: false + name: test_no_mappings + + - match: {test_no_mappings.index_patterns: ["test-*"]} + - match: {test_no_mappings.settings: {index: {number_of_shards: '1', number_of_replicas: '0'}}} + - match: {test_no_mappings.mappings: {}} --- "Get all templates": diff --git a/rest-api-spec/src/main/resources/rest-api-spec/test/indices.get_template/11_basic_with_types.yml b/rest-api-spec/src/main/resources/rest-api-spec/test/indices.get_template/11_basic_with_types.yml new file mode 100644 index 0000000000000..af31ba248e9ab --- /dev/null +++ b/rest-api-spec/src/main/resources/rest-api-spec/test/indices.get_template/11_basic_with_types.yml @@ -0,0 +1,49 @@ +setup: + - do: + indices.put_template: + name: test + body: + index_patterns: test-* + settings: + number_of_shards: 1 + number_of_replicas: 0 + mappings: + doc: + properties: + field: + type: keyword + +--- +"Get template": + - skip: + version: " - 5.99.99" + reason: this uses a new API that has been added in 6.0 + - do: + indices.get_template: + name: test + + - match: {test.index_patterns: ["test-*"]} + - match: {test.settings: {index: {number_of_shards: '1', number_of_replicas: '0'}}} + - match: {test.mappings: {doc: {properties: {field: {type: keyword}}}}} + +--- +"Get template with no mappings": + - skip: + version: " - 5.99.99" + reason: this uses a new API that has been added in 6.0 + - do: + indices.put_template: + name: test_no_mappings + body: + index_patterns: test-* + settings: + number_of_shards: 1 + number_of_replicas: 0 + + - do: + indices.get_template: + name: test_no_mappings + + - match: {test_no_mappings.index_patterns: ["test-*"]} + - match: {test_no_mappings.settings: {index: {number_of_shards: '1', number_of_replicas: '0'}}} + - match: {test_no_mappings.mappings: {}} diff --git a/rest-api-spec/src/main/resources/rest-api-spec/test/indices.put_mapping/10_basic.yml b/rest-api-spec/src/main/resources/rest-api-spec/test/indices.put_mapping/10_basic.yml index a1146380026ea..eda0a07e734e0 100644 --- a/rest-api-spec/src/main/resources/rest-api-spec/test/indices.put_mapping/10_basic.yml +++ b/rest-api-spec/src/main/resources/rest-api-spec/test/indices.put_mapping/10_basic.yml @@ -2,7 +2,7 @@ "Test Create and update mapping": - skip: version: " - 6.6.99" - reason: include_type_name was introduced in 6.7 + reason: include_type_name was introduced in 6.7.0 - do: indices.create: include_type_name: false @@ -60,7 +60,7 @@ "Create index with invalid mappings": - skip: version: " - 6.6.99" - reason: include_type_name was introduced in 6.7 + reason: include_type_name was introduced in 6.7.0 - do: indices.create: include_type_name: false @@ -79,7 +79,7 @@ "PUT mapping with a type and include_type_name: false": - skip: version: " - 6.6.99" - reason: include_type_name was introduced in 6.7 + reason: include_type_name was introduced in 6.7.0 - do: indices.create: index: index diff --git a/rest-api-spec/src/main/resources/rest-api-spec/test/indices.put_mapping/all_path_options.yml b/rest-api-spec/src/main/resources/rest-api-spec/test/indices.put_mapping/all_path_options.yml index 4d1253a486a83..2aab1ea9d280b 100644 --- a/rest-api-spec/src/main/resources/rest-api-spec/test/indices.put_mapping/all_path_options.yml +++ b/rest-api-spec/src/main/resources/rest-api-spec/test/indices.put_mapping/all_path_options.yml @@ -1,7 +1,7 @@ setup: - skip: version: " - 6.6.99" - reason: include_type_name was introduced in 6.7 + reason: include_type_name was introduced in 6.7.0 - do: indices.create: include_type_name: false diff --git a/rest-api-spec/src/main/resources/rest-api-spec/test/indices.put_template/10_basic.yml b/rest-api-spec/src/main/resources/rest-api-spec/test/indices.put_template/10_basic.yml index 3e8b3db468ea9..b6cada313cc73 100644 --- a/rest-api-spec/src/main/resources/rest-api-spec/test/indices.put_template/10_basic.yml +++ b/rest-api-spec/src/main/resources/rest-api-spec/test/indices.put_template/10_basic.yml @@ -1,50 +1,87 @@ --- "Put template": - - skip: - version: " - 5.99.99" - reason: this uses a new API that has been added in 6.0 + version: " - 6.6.99" + reason: include_type_name was introduced in 6.7 - do: indices.put_template: + include_type_name: false name: test body: index_patterns: test-* settings: number_of_shards: 1 number_of_replicas: 0 + mappings: + properties: + field: + type: keyword - do: indices.get_template: + include_type_name: false name: test flat_settings: true - match: {test.index_patterns: ["test-*"]} - match: {test.settings: {index.number_of_shards: '1', index.number_of_replicas: '0'}} + - match: {test.mappings: {properties: {field: {type: keyword}}}} --- "Put multiple template": - - skip: - version: " - 5.99.99" - reason: this uses a new API that has been added in 6.0 + version: " - 6.6.99" + reason: include_type_name was introduced in 6.7 - do: indices.put_template: + include_type_name: false name: test body: index_patterns: [test-*, test2-*] settings: number_of_shards: 1 number_of_replicas: 0 + mappings: + properties: + field: + type: text - do: indices.get_template: + include_type_name: false name: test flat_settings: true - match: {test.index_patterns: ["test-*", "test2-*"]} - match: {test.settings: {index.number_of_shards: '1', index.number_of_replicas: '0'}} + - match: {test.mappings: {properties: {field: {type: text}}}} + +--- +"Put template with empty mappings": + - skip: + version: " - 6.6.99" + reason: include_type_name was introduced in 6.7 + + - do: + indices.put_template: + include_type_name: false + name: test + body: + index_patterns: test-* + settings: + number_of_shards: 1 + number_of_replicas: 0 + mappings: {} + + - do: + indices.get_template: + include_type_name: false + name: test + flat_settings: true + + - match: {test.mappings: {}} --- "Put template with aliases": diff --git a/rest-api-spec/src/main/resources/rest-api-spec/test/indices.put_template/11_basic_with_types.yml b/rest-api-spec/src/main/resources/rest-api-spec/test/indices.put_template/11_basic_with_types.yml new file mode 100644 index 0000000000000..9f1e3de7b5ebf --- /dev/null +++ b/rest-api-spec/src/main/resources/rest-api-spec/test/indices.put_template/11_basic_with_types.yml @@ -0,0 +1,77 @@ +--- +"Put template": + - skip: + version: " - 5.99.99" + reason: this uses a new API that has been added in 6.0 + - do: + indices.put_template: + name: test + body: + index_patterns: test-* + settings: + number_of_shards: 1 + number_of_replicas: 0 + mappings: + doc: + properties: + field: + type: keyword + + - do: + indices.get_template: + name: test + flat_settings: true + + - match: {test.index_patterns: ["test-*"]} + - match: {test.settings: {index.number_of_shards: '1', index.number_of_replicas: '0'}} + - match: {test.mappings: {doc: {properties: {field: {type: keyword}}}}} + +--- +"Put multiple template": + - skip: + version: " - 5.99.99" + reason: this uses a new API that has been added in 6.0 + - do: + indices.put_template: + name: test + body: + index_patterns: [test-*, test2-*] + settings: + number_of_shards: 1 + number_of_replicas: 0 + mappings: + doc: + properties: + field: + type: text + + - do: + indices.get_template: + name: test + flat_settings: true + + - match: {test.index_patterns: ["test-*", "test2-*"]} + - match: {test.settings: {index.number_of_shards: '1', index.number_of_replicas: '0'}} + - match: {test.mappings: {doc: {properties: {field: {type: text}}}}} + +--- +"Put template with empty mappings": + - skip: + version: " - 5.99.99" + reason: this uses a new API that has been added in 6.0 + - do: + indices.put_template: + name: test + body: + index_patterns: test-* + settings: + number_of_shards: 1 + number_of_replicas: 0 + mappings: {} + + - do: + indices.get_template: + name: test + flat_settings: true + + - match: {test.mappings: {}} diff --git a/server/src/main/java/org/elasticsearch/action/admin/indices/mapping/get/GetFieldMappingsResponse.java b/server/src/main/java/org/elasticsearch/action/admin/indices/mapping/get/GetFieldMappingsResponse.java index 44a66f497c846..2c07ebc68d0ef 100644 --- a/server/src/main/java/org/elasticsearch/action/admin/indices/mapping/get/GetFieldMappingsResponse.java +++ b/server/src/main/java/org/elasticsearch/action/admin/indices/mapping/get/GetFieldMappingsResponse.java @@ -34,6 +34,8 @@ import org.elasticsearch.common.xcontent.XContentParser; import org.elasticsearch.common.xcontent.XContentType; import org.elasticsearch.index.mapper.Mapper; +import org.elasticsearch.index.mapper.MapperService; +import org.elasticsearch.rest.BaseRestHandler; import java.io.IOException; import java.io.InputStream; @@ -112,19 +114,32 @@ public FieldMappingMetaData fieldMappings(String index, String type, String fiel @Override public XContentBuilder toXContent(XContentBuilder builder, Params params) throws IOException { + boolean includeTypeName = params.paramAsBoolean(BaseRestHandler.INCLUDE_TYPE_NAME_PARAMETER, true); + builder.startObject(); for (Map.Entry>> indexEntry : mappings.entrySet()) { builder.startObject(indexEntry.getKey()); builder.startObject(MAPPINGS.getPreferredName()); - for (Map.Entry> typeEntry : indexEntry.getValue().entrySet()) { - builder.startObject(typeEntry.getKey()); - for (Map.Entry fieldEntry : typeEntry.getValue().entrySet()) { - builder.startObject(fieldEntry.getKey()); - fieldEntry.getValue().toXContent(builder, params); + + if (includeTypeName == false) { + Map mappings = null; + for (Map.Entry> typeEntry : indexEntry.getValue().entrySet()) { + if (typeEntry.getKey().equals(MapperService.DEFAULT_MAPPING) == false) { + assert mappings == null; + mappings = typeEntry.getValue(); + } + } + if (mappings != null) { + addFieldMappingsToBuilder(builder, params, mappings); + } + } else { + for (Map.Entry> typeEntry : indexEntry.getValue().entrySet()) { + builder.startObject(typeEntry.getKey()); + addFieldMappingsToBuilder(builder, params, typeEntry.getValue()); builder.endObject(); } - builder.endObject(); } + builder.endObject(); builder.endObject(); } @@ -132,6 +147,16 @@ public XContentBuilder toXContent(XContentBuilder builder, Params params) throws return builder; } + private void addFieldMappingsToBuilder(XContentBuilder builder, + Params params, + Map mappings) throws IOException { + for (Map.Entry fieldEntry : mappings.entrySet()) { + builder.startObject(fieldEntry.getKey()); + fieldEntry.getValue().toXContent(builder, params); + builder.endObject(); + } + } + public static GetFieldMappingsResponse fromXContent(XContentParser parser) throws IOException { ensureExpectedToken(XContentParser.Token.START_OBJECT, parser.nextToken(), parser::getTokenLocation); diff --git a/server/src/main/java/org/elasticsearch/cluster/metadata/IndexTemplateMetaData.java b/server/src/main/java/org/elasticsearch/cluster/metadata/IndexTemplateMetaData.java index 8f5f3e4768d1a..57ca16ec6d4b3 100644 --- a/server/src/main/java/org/elasticsearch/cluster/metadata/IndexTemplateMetaData.java +++ b/server/src/main/java/org/elasticsearch/cluster/metadata/IndexTemplateMetaData.java @@ -20,7 +20,6 @@ import com.carrotsearch.hppc.cursors.ObjectCursor; import com.carrotsearch.hppc.cursors.ObjectObjectCursor; - import org.apache.logging.log4j.LogManager; import org.elasticsearch.ElasticsearchParseException; import org.elasticsearch.Version; @@ -42,6 +41,8 @@ import org.elasticsearch.common.xcontent.XContentFactory; import org.elasticsearch.common.xcontent.XContentHelper; import org.elasticsearch.common.xcontent.XContentParser; +import org.elasticsearch.index.mapper.MapperService; +import org.elasticsearch.rest.BaseRestHandler; import java.io.IOException; import java.util.ArrayList; @@ -360,6 +361,8 @@ public static void toXContent(IndexTemplateMetaData indexTemplateMetaData, XCont public static void toInnerXContent(IndexTemplateMetaData indexTemplateMetaData, XContentBuilder builder, ToXContent.Params params) throws IOException { + boolean includeTypeName = params.paramAsBoolean(BaseRestHandler.INCLUDE_TYPE_NAME_PARAMETER, true); + builder.field("order", indexTemplateMetaData.order()); if (indexTemplateMetaData.version() != null) { builder.field("version", indexTemplateMetaData.version()); @@ -371,18 +374,35 @@ public static void toInnerXContent(IndexTemplateMetaData indexTemplateMetaData, builder.endObject(); if (params.paramAsBoolean("reduce_mappings", false)) { - builder.startObject("mappings"); - for (ObjectObjectCursor cursor : indexTemplateMetaData.mappings()) { - byte[] mappingSource = cursor.value.uncompressed(); - Map mapping = XContentHelper.convertToMap(new BytesArray(mappingSource), true).v2(); - if (mapping.size() == 1 && mapping.containsKey(cursor.key)) { - // the type name is the root value, reduce it - mapping = (Map) mapping.get(cursor.key); + // The parameter include_type_name is only ever used in the REST API, where reduce_mappings is + // always set to true. We therefore only check for include_type_name in this branch. + if (includeTypeName == false) { + Map documentMapping = null; + for (ObjectObjectCursor cursor : indexTemplateMetaData.mappings()) { + if (!cursor.key.equals(MapperService.DEFAULT_MAPPING)) { + assert documentMapping == null; + byte[] mappingSource = cursor.value.uncompressed(); + Map mapping = XContentHelper.convertToMap(new BytesArray(mappingSource), true).v2(); + documentMapping = reduceMapping(cursor.key, mapping); + } + } + + if (documentMapping != null) { + builder.field("mappings", documentMapping); + } else { + builder.startObject("mappings").endObject(); } - builder.field(cursor.key); - builder.map(mapping); + } else { + builder.startObject("mappings"); + for (ObjectObjectCursor cursor : indexTemplateMetaData.mappings()) { + byte[] mappingSource = cursor.value.uncompressed(); + Map mapping = XContentHelper.convertToMap(new BytesArray(mappingSource), true).v2(); + mapping = reduceMapping(cursor.key, mapping); + builder.field(cursor.key); + builder.map(mapping); + } + builder.endObject(); } - builder.endObject(); } else { builder.startArray("mappings"); for (ObjectObjectCursor cursor : indexTemplateMetaData.mappings()) { @@ -399,6 +419,16 @@ public static void toInnerXContent(IndexTemplateMetaData indexTemplateMetaData, builder.endObject(); } + @SuppressWarnings("unchecked") + private static Map reduceMapping(String type, Map mapping) { + if (mapping.size() == 1 && mapping.containsKey(type)) { + // the type name is the root value, reduce it + return (Map) mapping.get(type); + } else { + return mapping; + } + } + public static IndexTemplateMetaData fromXContent(XContentParser parser, String templateName) throws IOException { Builder builder = new Builder(templateName); diff --git a/server/src/main/java/org/elasticsearch/rest/action/admin/indices/RestGetFieldMappingAction.java b/server/src/main/java/org/elasticsearch/rest/action/admin/indices/RestGetFieldMappingAction.java index c43f14dcddf26..f3a73fa29fd98 100644 --- a/server/src/main/java/org/elasticsearch/rest/action/admin/indices/RestGetFieldMappingAction.java +++ b/server/src/main/java/org/elasticsearch/rest/action/admin/indices/RestGetFieldMappingAction.java @@ -62,6 +62,13 @@ public RestChannelConsumer prepareRequest(final RestRequest request, final NodeC final String[] indices = Strings.splitStringByCommaToArray(request.param("index")); final String[] types = request.paramAsStringArrayOrEmptyIfAll("type"); final String[] fields = Strings.splitStringByCommaToArray(request.param("fields")); + + boolean includeTypeName = request.paramAsBoolean(INCLUDE_TYPE_NAME_PARAMETER, true); + if (includeTypeName == false && types.length > 0) { + throw new IllegalArgumentException("Cannot set include_type_name=false and specify" + + " types at the same time."); + } + GetFieldMappingsRequest getMappingsRequest = new GetFieldMappingsRequest(); getMappingsRequest.indices(indices).types(types).fields(fields).includeDefaults(request.paramAsBoolean("include_defaults", false)); getMappingsRequest.indicesOptions(IndicesOptions.fromRequest(request, getMappingsRequest.indicesOptions())); diff --git a/server/src/main/java/org/elasticsearch/rest/action/admin/indices/RestGetIndexTemplateAction.java b/server/src/main/java/org/elasticsearch/rest/action/admin/indices/RestGetIndexTemplateAction.java index 38c1cb76611f4..50370797aa6f2 100644 --- a/server/src/main/java/org/elasticsearch/rest/action/admin/indices/RestGetIndexTemplateAction.java +++ b/server/src/main/java/org/elasticsearch/rest/action/admin/indices/RestGetIndexTemplateAction.java @@ -24,6 +24,7 @@ import org.elasticsearch.client.node.NodeClient; import org.elasticsearch.common.Strings; import org.elasticsearch.common.settings.Settings; +import org.elasticsearch.common.util.set.Sets; import org.elasticsearch.rest.BaseRestHandler; import org.elasticsearch.rest.RestController; import org.elasticsearch.rest.RestRequest; @@ -31,6 +32,7 @@ import org.elasticsearch.rest.action.RestToXContentListener; import java.io.IOException; +import java.util.Collections; import java.util.Set; import static org.elasticsearch.rest.RestRequest.Method.GET; @@ -43,6 +45,9 @@ */ public class RestGetIndexTemplateAction extends BaseRestHandler { + private static final Set RESPONSE_PARAMETERS = Collections.unmodifiableSet(Sets.union( + Collections.singleton(INCLUDE_TYPE_NAME_PARAMETER), Settings.FORMAT_PARAMS)); + public RestGetIndexTemplateAction(final Settings settings, final RestController controller) { super(settings); controller.registerHandler(GET, "/_template", this); @@ -79,7 +84,7 @@ protected RestStatus getStatus(final GetIndexTemplatesResponse response) { @Override protected Set responseParams() { - return Settings.FORMAT_PARAMS; + return RESPONSE_PARAMETERS; } } diff --git a/server/src/main/java/org/elasticsearch/rest/action/admin/indices/RestPutIndexTemplateAction.java b/server/src/main/java/org/elasticsearch/rest/action/admin/indices/RestPutIndexTemplateAction.java index 798a2cbe30fa6..8a292859d6031 100644 --- a/server/src/main/java/org/elasticsearch/rest/action/admin/indices/RestPutIndexTemplateAction.java +++ b/server/src/main/java/org/elasticsearch/rest/action/admin/indices/RestPutIndexTemplateAction.java @@ -25,6 +25,8 @@ import org.elasticsearch.common.Strings; import org.elasticsearch.common.logging.DeprecationLogger; import org.elasticsearch.common.settings.Settings; +import org.elasticsearch.common.xcontent.XContentHelper; +import org.elasticsearch.index.mapper.MapperService; import org.elasticsearch.rest.BaseRestHandler; import org.elasticsearch.rest.RestController; import org.elasticsearch.rest.RestRequest; @@ -33,6 +35,8 @@ import java.io.IOException; import java.util.Arrays; import java.util.Collections; +import java.util.HashMap; +import java.util.Map; public class RestPutIndexTemplateAction extends BaseRestHandler { @@ -63,8 +67,23 @@ public RestChannelConsumer prepareRequest(final RestRequest request, final NodeC putRequest.masterNodeTimeout(request.paramAsTime("master_timeout", putRequest.masterNodeTimeout())); putRequest.create(request.paramAsBoolean("create", false)); putRequest.cause(request.param("cause", "")); - putRequest.source(request.requiredContent(), request.getXContentType()); + + boolean includeTypeName = request.paramAsBoolean(INCLUDE_TYPE_NAME_PARAMETER, true); + Map sourceAsMap = prepareRequestSource(request, includeTypeName); + putRequest.source(sourceAsMap); + return channel -> client.admin().indices().putTemplate(putRequest, new RestToXContentListener<>(channel)); } + Map prepareRequestSource(RestRequest request, boolean includeTypeName) { + Map sourceAsMap = XContentHelper.convertToMap(request.requiredContent(), false, + request.getXContentType()).v2(); + if (includeTypeName == false && sourceAsMap.containsKey("mappings")) { + Map newSourceAsMap = new HashMap<>(sourceAsMap); + newSourceAsMap.put("mappings", Collections.singletonMap(MapperService.SINGLE_MAPPING_NAME, sourceAsMap.get("mappings"))); + return newSourceAsMap; + } else { + return sourceAsMap; + } + } } diff --git a/server/src/test/java/org/elasticsearch/rest/action/admin/indices/RestGetFieldMappingActionTests.java b/server/src/test/java/org/elasticsearch/rest/action/admin/indices/RestGetFieldMappingActionTests.java new file mode 100644 index 0000000000000..dcf4237ae07bf --- /dev/null +++ b/server/src/test/java/org/elasticsearch/rest/action/admin/indices/RestGetFieldMappingActionTests.java @@ -0,0 +1,61 @@ +/* + * Licensed to Elasticsearch under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch licenses this file to you under + * the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package org.elasticsearch.rest.action.admin.indices; + +import org.elasticsearch.common.settings.Settings; +import org.elasticsearch.common.util.concurrent.ThreadContext; +import org.elasticsearch.rest.RestRequest; +import org.elasticsearch.rest.RestStatus; +import org.elasticsearch.test.rest.FakeRestChannel; +import org.elasticsearch.test.rest.FakeRestRequest; +import org.elasticsearch.test.rest.RestActionTestCase; +import org.junit.Before; + +import java.util.HashMap; +import java.util.Map; + +import static org.elasticsearch.rest.BaseRestHandler.INCLUDE_TYPE_NAME_PARAMETER; + +public class RestGetFieldMappingActionTests extends RestActionTestCase { + + @Before + public void setUpAction() { + new RestGetFieldMappingAction(Settings.EMPTY, controller()); + } + + public void testTypeInPath() { + // Test that specifying a type while setting include_type_name to false + // results in an illegal argument exception. + Map params = new HashMap<>(); + params.put(INCLUDE_TYPE_NAME_PARAMETER, "false"); + RestRequest request = new FakeRestRequest.Builder(xContentRegistry()) + .withMethod(RestRequest.Method.GET) + .withPath("some_index/some_type/_mapping/field/some_field") + .withParams(params) + .build(); + + FakeRestChannel channel = new FakeRestChannel(request, false, 1); + ThreadContext threadContext = new ThreadContext(Settings.EMPTY); + controller().dispatchRequest(request, channel, threadContext); + + assertEquals(1, channel.errors().get()); + assertEquals(RestStatus.BAD_REQUEST, channel.capturedResponse().status()); + } +} diff --git a/server/src/test/java/org/elasticsearch/rest/action/admin/indices/RestPutIndexTemplateActionTests.java b/server/src/test/java/org/elasticsearch/rest/action/admin/indices/RestPutIndexTemplateActionTests.java new file mode 100644 index 0000000000000..ac0eb8f0d81a6 --- /dev/null +++ b/server/src/test/java/org/elasticsearch/rest/action/admin/indices/RestPutIndexTemplateActionTests.java @@ -0,0 +1,81 @@ +/* + * Licensed to Elasticsearch under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch licenses this file to you under + * the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package org.elasticsearch.rest.action.admin.indices; + +import org.elasticsearch.common.bytes.BytesReference; +import org.elasticsearch.common.settings.Settings; +import org.elasticsearch.common.xcontent.XContentBuilder; +import org.elasticsearch.common.xcontent.XContentFactory; +import org.elasticsearch.common.xcontent.XContentHelper; +import org.elasticsearch.common.xcontent.XContentType; +import org.elasticsearch.rest.RestRequest; +import org.elasticsearch.test.rest.FakeRestRequest; +import org.elasticsearch.test.rest.RestActionTestCase; +import org.junit.Before; + +import java.io.IOException; +import java.util.Map; + +public class RestPutIndexTemplateActionTests extends RestActionTestCase { + private RestPutIndexTemplateAction action; + + @Before + public void setUpAction() { + action = new RestPutIndexTemplateAction(Settings.EMPTY, controller()); + } + + public void testPrepareTypelessRequest() throws IOException { + XContentBuilder content = XContentFactory.jsonBuilder().startObject() + .startObject("mappings") + .startObject("properties") + .startObject("field").field("type", "keyword").endObject() + .endObject() + .endObject() + .startObject("aliases") + .startObject("read_alias").endObject() + .endObject() + .endObject(); + + RestRequest request = new FakeRestRequest.Builder(xContentRegistry()) + .withMethod(RestRequest.Method.PUT) + .withPath("/_template/_some_template") + .withContent(BytesReference.bytes(content), XContentType.JSON) + .build(); + boolean includeTypeName = false; + Map source = action.prepareRequestSource(request, includeTypeName); + + XContentBuilder expectedContent = XContentFactory.jsonBuilder().startObject() + .startObject("mappings") + .startObject("_doc") + .startObject("properties") + .startObject("field").field("type", "keyword").endObject() + .endObject() + .endObject() + .endObject() + .startObject("aliases") + .startObject("read_alias").endObject() + .endObject() + .endObject(); + Map expectedContentAsMap = XContentHelper.convertToMap( + BytesReference.bytes(expectedContent), true, expectedContent.contentType()).v2(); + + assertEquals(expectedContentAsMap, source); + } +} diff --git a/test/framework/src/main/java/org/elasticsearch/test/rest/RestActionTestCase.java b/test/framework/src/main/java/org/elasticsearch/test/rest/RestActionTestCase.java new file mode 100644 index 0000000000000..f5ab14971b890 --- /dev/null +++ b/test/framework/src/main/java/org/elasticsearch/test/rest/RestActionTestCase.java @@ -0,0 +1,67 @@ +/* + * Licensed to Elasticsearch under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch licenses this file to you under + * the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package org.elasticsearch.test.rest; + +import org.elasticsearch.client.node.NodeClient; +import org.elasticsearch.common.settings.Settings; +import org.elasticsearch.common.util.concurrent.ThreadContext; +import org.elasticsearch.indices.breaker.NoneCircuitBreakerService; +import org.elasticsearch.rest.RestController; +import org.elasticsearch.rest.RestRequest; +import org.elasticsearch.test.ESTestCase; +import org.elasticsearch.usage.UsageService; +import org.junit.Before; + +import java.util.Collections; + +import static org.mockito.Mockito.mock; + +/** + * A common base class for Rest*ActionTests. Provides access to a {@link RestController} + * that can be used to register individual REST actions, and test request handling. + */ +public abstract class RestActionTestCase extends ESTestCase { + private RestController controller; + + @Before + public void setUpController() { + controller = new RestController(Collections.emptySet(), null, + mock(NodeClient.class), + new NoneCircuitBreakerService(), + new UsageService()); + } + + /** + * A test {@link RestController}. This controller can be used to register and delegate + * to handlers, but uses a mock client and cannot carry out the full request. + */ + protected RestController controller() { + return controller; + } + + /** + * Sends the given request to the test controller in {@link #controller()}. + */ + protected void dispatchRequest(RestRequest request) { + FakeRestChannel channel = new FakeRestChannel(request, false, 1); + ThreadContext threadContext = new ThreadContext(Settings.EMPTY); + controller.dispatchRequest(request, channel, threadContext); + } +}