Beginning to update index pattern api resource schema

This commit is contained in:
Matthew Bargar 2015-12-09 17:51:34 -05:00
parent 6f0dcefc69
commit 66b3c1eb97
7 changed files with 168 additions and 100 deletions

View file

@ -0,0 +1,12 @@
const Joi = require('joi');
module.exports = function (primary, included) {
const doc = {data: primary};
if (included) {
doc.included = included;
}
return Joi.object(doc);
};

View file

@ -0,0 +1,15 @@
const Joi = require('joi');
module.exports = function (attributes, relationships) {
const resource = {
type: Joi.string().required(),
id: Joi.string().required(),
attributes: attributes
};
if (relationships) {
resource.relationships = relationships;
}
return Joi.object(resource);
};

View file

@ -1,31 +1,60 @@
var Joi = require('joi');
const Joi = require('joi');
const createApiDocumentSchema = require('./create_api_document_schema');
const createResourceObject = require('./create_resource_object');
const relationshipObjectSchema = require('./relationship_object_schema');
module.exports = {
post: Joi.object({
title: Joi.string().required(),
time_field_name: Joi.string(),
interval_name: Joi.string(),
fields: Joi.array().items(Joi.object({
name: Joi.string().required(),
count: Joi.number().integer(),
scripted: Joi.boolean(),
mapping: Joi.object({
type: Joi.string().required()
}).unknown()
})),
field_format_map: Joi.object()
}),
post: createApiDocumentSchema(
createResourceObject(
Joi.object({
title: Joi.string().required(),
time_field_name: Joi.string(),
interval_name: Joi.string(),
fields: Joi.array().items(
Joi.object({
name: Joi.string().required(),
count: Joi.number().integer(),
scripted: Joi.boolean()
})
),
field_format_map: Joi.object()
}),
Joi.object({
template: relationshipObjectSchema
})
),
Joi.array().items(
createResourceObject(
Joi.object({
template: Joi.string().required(),
order: Joi.number().integer(),
mappings: Joi.object()
})
)
)
),
put: Joi.object({
title: Joi.string(),
time_field_name: Joi.string(),
interval_name: Joi.string(),
fields: Joi.array().items(Joi.object({
name: Joi.string().required(),
count: Joi.number().integer(),
scripted: Joi.boolean(),
mapping: Joi.any().forbidden()
})),
field_format_map: Joi.object()
})
// No attributes are required for an update
// Templates can't be updated in an index_pattern PUT
put: createApiDocumentSchema(
createResourceObject(
Joi.object({
title: Joi.string(),
time_field_name: Joi.string(),
interval_name: Joi.string(),
fields: Joi.array().items(
Joi.object({
name: Joi.string().required(),
count: Joi.number().integer(),
scripted: Joi.boolean()
})
),
field_format_map: Joi.object()
}),
Joi.object({
template: relationshipObjectSchema
})
)
)
};

View file

@ -0,0 +1,9 @@
const Joi = require('joi');
module.exports = Joi.object({
data: Joi.object({
type: Joi.string().required(),
id: Joi.string().required()
})
});

View file

@ -19,17 +19,14 @@ module.exports = function registerPost(server) {
}
const callWithRequest = server.plugins.elasticsearch.callWithRequest;
const indexPattern = _.cloneDeep(req.payload);
const requestDocument = _.cloneDeep(req.payload);
const included = requestDocument.included;
const indexPatternId = requestDocument.data.id;
const indexPattern = requestDocument.data.attributes;
const isWildcard = _.contains(indexPattern.title, '*');
const mappings = _(req.payload.fields)
.indexBy('name')
.mapValues(value => value.mapping)
.omit(_.isUndefined)
.value();
const templateResource = _.isEmpty(included) ? null : included[0];
indexPattern.fields = JSON.stringify(_.map(indexPattern.fields, (field) => {
return _.omit(field, 'mapping');
}));
indexPattern.fields = JSON.stringify(indexPattern.fields);
const patternCreateParams = {
index: '.kibana',
@ -40,7 +37,7 @@ module.exports = function registerPost(server) {
callWithRequest(req, 'create', patternCreateParams)
.then((patternResponse) => {
if (!isWildcard || _.isEmpty(mappings)) {
if (!isWildcard || _.isEmpty(included)) {
return patternResponse;
}
@ -51,17 +48,10 @@ module.exports = function registerPost(server) {
}
const templateParams = {
order: 0,
order: templateResource.attributes.order,
create: true,
name: patternToTemplate(indexPattern.title),
body: {
template: indexPattern.title,
mappings: {
_default_: {
properties: mappings
}
}
}
name: templateResource.id,
body: _.omit(templateResource.attributes, 'order')
};
return callWithRequest(req, 'indices.putTemplate', templateParams);

View file

@ -24,41 +24,29 @@ define(function (require) {
.expect(400),
request.post('/kibana/index_patterns')
.send(_.assign(createTestData().indexPatternWithMappings, {title: false}))
.send(_.set(createTestData().indexPatternWithTemplate, 'data.attributes.title', false))
.expect(400),
request.post('/kibana/index_patterns')
.send(_.assign(createTestData().indexPatternWithMappings, {fields: {}}))
.send(_.set(createTestData().indexPatternWithTemplate, 'data.attributes.fields', {}))
.expect(400),
// Fields must have a name
request.post('/kibana/index_patterns')
.send(_.assign(createTestData().indexPatternWithMappings, {fields: [{count: 0}]}))
.expect(400),
// Mapping requires type
request.post('/kibana/index_patterns')
.send(_.assign(createTestData().indexPatternWithMappings, {
fields: [{
'name': 'geo.coordinates',
'count': 0,
'scripted': false,
'mapping': {'index': 'not_analyzed', 'doc_values': false}
}]
}))
.send(_.set(createTestData().indexPatternWithTemplate, 'data.attributes.fields', [{count: 0}]))
.expect(400)
]);
});
bdd.it('should return 201 when a pattern is successfully created', function createPattern() {
return request.post('/kibana/index_patterns')
.send(createTestData().indexPatternWithMappings)
.send(createTestData().indexPatternWithTemplate)
.expect(201);
});
bdd.it('should create an index template if mappings are provided', function createTemplate() {
return request.post('/kibana/index_patterns')
.send(createTestData().indexPatternWithMappings)
.send(createTestData().indexPatternWithTemplate)
.expect(201)
.then(function () {
return scenarioManager.client.indices.getTemplate({name: 'kibana-logstash-*'});
@ -66,7 +54,7 @@ define(function (require) {
});
bdd.it('should NOT create an index template if mappings are NOT provided', function noMappings() {
var pattern = createTestData().indexPatternWithMappings;
var pattern = createTestData().indexPatternWithTemplate;
pattern.fields = _.map(pattern.fields, function (field) {
return _.omit(field, 'mapping');
});
@ -85,7 +73,7 @@ define(function (require) {
});
bdd.it('should NOT create an index template if pattern does not contain a wildcard', function noWildcard() {
var pattern = createTestData().indexPatternWithMappings;
var pattern = createTestData().indexPatternWithTemplate;
pattern.title = 'notawildcard';
return request.post('/kibana/index_patterns')
@ -103,11 +91,11 @@ define(function (require) {
bdd.it('should return 409 conflict when a pattern with the given ID already exists', function patternConflict() {
return request.post('/kibana/index_patterns')
.send(createTestData().indexPatternWithMappings)
.send(createTestData().indexPatternWithTemplate)
.expect(201)
.then(function () {
return request.post('/kibana/index_patterns')
.send(createTestData().indexPatternWithMappings)
.send(createTestData().indexPatternWithTemplate)
.expect(409);
});
});
@ -120,14 +108,14 @@ define(function (require) {
}
}).then(function () {
return request.post('/kibana/index_patterns')
.send(createTestData().indexPatternWithMappings)
.send(createTestData().indexPatternWithTemplate)
.expect(409);
});
});
bdd.it('should return 409 conflict when mappings are provided with a pattern that matches existing indices',
function existingIndicesConflict() {
var pattern = createTestData().indexPatternWithMappings;
var pattern = createTestData().indexPatternWithTemplate;
pattern.title = '.kib*';
return request.post('/kibana/index_patterns')
@ -137,7 +125,7 @@ define(function (require) {
bdd.it('should return 201 created successfully if a pattern matches existing indices but has NO mappings',
function existingIndicesNoMappings() {
var pattern = createTestData().indexPatternWithMappings;
var pattern = createTestData().indexPatternWithTemplate;
pattern.fields = _.map(pattern.fields, function (field) {
return _.omit(field, 'mapping');
});
@ -150,7 +138,7 @@ define(function (require) {
bdd.it('should enforce snake_case in the request body', function () {
return request.post('/kibana/index_patterns')
.send(_.mapKeys(createTestData().indexPatternWithMappings, function (value, key) {
.send(_.mapKeys(createTestData().indexPatternWithTemplate, function (value, key) {
return _.camelCase(key);
}))
.expect(400);

View file

@ -1,33 +1,58 @@
module.exports = function createTestData() {
return {
indexPatternWithMappings: {
'title': 'logstash-*',
'time_field_name': '@timestamp',
'fields': [{
'name': 'geo.coordinates',
'count': 0,
'scripted': false,
'mapping': {'type': 'geo_point', 'index': 'not_analyzed', 'doc_values': false}
}, {
'name': 'ip',
'count': 2,
'scripted': false,
'mapping': {'type': 'ip', 'index': 'not_analyzed', 'doc_values': true}
}, {
'name': '@timestamp',
'count': 0,
'scripted': false,
'mapping': {'type': 'date', 'index': 'not_analyzed', 'doc_values': true}
}, {
'name': 'agent',
'count': 0,
'scripted': false,
'mapping': {'type': 'string', 'index': 'analyzed', 'doc_values': false}
}, {
'name': 'bytes',
'count': 2,
'scripted': false,
'mapping': {'type': 'number', 'index': 'not_analyzed', 'doc_values': true}
indexPatternWithTemplate: {
'data': {
'type': 'index_patterns',
'id': 'logstash-*',
'attributes': {
'title': 'logstash-*',
'time_field_name': '@timestamp',
'fields': [{
'name': 'geo.coordinates',
'count': 0,
'scripted': false
}, {
'name': 'ip',
'count': 2,
'scripted': false
}, {
'name': '@timestamp',
'count': 0,
'scripted': false
}, {
'name': 'agent',
'count': 0,
'scripted': false
}, {
'name': 'bytes',
'count': 2,
'scripted': false
}]
},
'relationships': {
'template': {
'data': {'type': 'index_templates', 'id': 'kibana-logstash-*'}
}
}
},
'included': [{
'type': 'index_templates',
'id': 'kibana-logstash-*',
'attributes': {
'template': 'logstash-*',
'order': 0,
'mappings': {
'_default_': {
'properties': {
'geo.coordinates': {'type': 'geo_point', 'index': 'not_analyzed', 'doc_values': false},
'ip': {'type': 'ip', 'index': 'not_analyzed', 'doc_values': true},
'@timestamp': {'type': 'date', 'index': 'not_analyzed', 'doc_values': true},
'agent': {'type': 'string', 'index': 'analyzed', 'doc_values': false},
'mapping': {'type': 'number', 'index': 'not_analyzed', 'doc_values': true}
}
}
}
}
}]
}
};