mirror of
https://github.com/elastic/kibana.git
synced 2025-04-23 17:28:26 -04:00
Backports the following commits to 6.x: - Remove dependency on doc versions (#29906)
This commit is contained in:
parent
41eebe6747
commit
f13b696018
60 changed files with 790 additions and 263 deletions
|
@ -61,7 +61,6 @@ describe('plugins/elasticsearch', () => {
|
|||
|
||||
cluster = { callWithInternalUser: sinon.stub() };
|
||||
cluster.callWithInternalUser.withArgs('index', sinon.match.any).returns(Promise.resolve());
|
||||
cluster.callWithInternalUser.withArgs('create', sinon.match.any).returns(Promise.resolve({ _id: '1', _version: 1 }));
|
||||
cluster.callWithInternalUser.withArgs('mget', sinon.match.any).returns(Promise.resolve({ ok: true }));
|
||||
cluster.callWithInternalUser.withArgs('get', sinon.match.any).returns(Promise.resolve({ found: false }));
|
||||
cluster.callWithInternalUser.withArgs('search', sinon.match.any).returns(Promise.resolve({ hits: { hits: [] } }));
|
||||
|
|
|
@ -7,5 +7,5 @@
|
|||
},
|
||||
"id": "apm-*",
|
||||
"type": "index-pattern",
|
||||
"version": 1
|
||||
"version": "1"
|
||||
}
|
||||
|
|
|
@ -11,7 +11,7 @@
|
|||
},
|
||||
"id": "1ffc5e20-7827-11e7-8c47-65b845b5cfb3",
|
||||
"type": "visualization",
|
||||
"version": 1
|
||||
"version": "1"
|
||||
},
|
||||
{
|
||||
"attributes": {
|
||||
|
@ -26,7 +26,7 @@
|
|||
},
|
||||
"id": "1bdca740-7828-11e7-8c47-65b845b5cfb3",
|
||||
"type": "visualization",
|
||||
"version": 2
|
||||
"version": "2"
|
||||
},
|
||||
{
|
||||
"attributes": {
|
||||
|
@ -41,7 +41,7 @@
|
|||
},
|
||||
"id": "804ffc40-7828-11e7-8c47-65b845b5cfb3",
|
||||
"type": "visualization",
|
||||
"version": 1
|
||||
"version": "1"
|
||||
},
|
||||
{
|
||||
"attributes": {
|
||||
|
@ -59,7 +59,7 @@
|
|||
},
|
||||
"id": "8d3ed660-7828-11e7-8c47-65b845b5cfb3",
|
||||
"type": "dashboard",
|
||||
"version": 1
|
||||
"version": "1"
|
||||
},
|
||||
{
|
||||
"attributes": {
|
||||
|
@ -74,7 +74,7 @@
|
|||
},
|
||||
"id": "c618e4e0-7c69-11e7-aa55-3b0d52c71c60",
|
||||
"type": "visualization",
|
||||
"version": 1
|
||||
"version": "1"
|
||||
},
|
||||
{
|
||||
"attributes": {
|
||||
|
@ -100,7 +100,7 @@
|
|||
},
|
||||
"id": "ceefd050-7c6a-11e7-aa55-3b0d52c71c60",
|
||||
"type": "search",
|
||||
"version": 1
|
||||
"version": "1"
|
||||
},
|
||||
{
|
||||
"attributes": {
|
||||
|
@ -118,7 +118,7 @@
|
|||
},
|
||||
"id": "5f08a870-7c6a-11e7-aa55-3b0d52c71c60",
|
||||
"type": "dashboard",
|
||||
"version": 1
|
||||
"version": "1"
|
||||
},
|
||||
{
|
||||
"attributes": {
|
||||
|
@ -133,7 +133,7 @@
|
|||
},
|
||||
"id": "22518e70-7c69-11e7-aa55-3b0d52c71c60",
|
||||
"type": "visualization",
|
||||
"version": 1
|
||||
"version": "1"
|
||||
},
|
||||
{
|
||||
"attributes": {
|
||||
|
@ -151,7 +151,7 @@
|
|||
},
|
||||
"id": "37f6fac0-7c6a-11e7-aa55-3b0d52c71c60",
|
||||
"type": "dashboard",
|
||||
"version": 1
|
||||
"version": "1"
|
||||
},
|
||||
{
|
||||
"attributes": {
|
||||
|
@ -166,7 +166,7 @@
|
|||
},
|
||||
"id": "a2e199b0-7820-11e7-8c47-65b845b5cfb3",
|
||||
"type": "visualization",
|
||||
"version": 1
|
||||
"version": "1"
|
||||
},
|
||||
{
|
||||
"attributes": {
|
||||
|
@ -181,7 +181,7 @@
|
|||
},
|
||||
"id": "09bcf890-7822-11e7-8c47-65b845b5cfb3",
|
||||
"type": "visualization",
|
||||
"version": 2
|
||||
"version": "2"
|
||||
},
|
||||
{
|
||||
"attributes": {
|
||||
|
@ -196,7 +196,7 @@
|
|||
},
|
||||
"id": "55606a60-7823-11e7-8c47-65b845b5cfb3",
|
||||
"type": "visualization",
|
||||
"version": 1
|
||||
"version": "1"
|
||||
},
|
||||
{
|
||||
"attributes": {
|
||||
|
@ -214,7 +214,7 @@
|
|||
},
|
||||
"id": "41b5d920-7821-11e7-8c47-65b845b5cfb3",
|
||||
"type": "dashboard",
|
||||
"version": 1
|
||||
"version": "1"
|
||||
},
|
||||
{
|
||||
"attributes": {
|
||||
|
@ -238,7 +238,7 @@
|
|||
},
|
||||
"id": "d7735b90-7ddf-11e7-b115-df9c90da2df1",
|
||||
"type": "search",
|
||||
"version": 1
|
||||
"version": "1"
|
||||
},
|
||||
{
|
||||
"attributes": {
|
||||
|
@ -256,5 +256,5 @@
|
|||
},
|
||||
"id": "3e3de700-7de0-11e7-b115-df9c90da2df1",
|
||||
"type": "dashboard",
|
||||
"version": 2
|
||||
"version": "2"
|
||||
}]
|
||||
|
|
|
@ -35,7 +35,7 @@ export const createBulkCreateRoute = prereqs => ({
|
|||
type: Joi.string().required(),
|
||||
id: Joi.string(),
|
||||
attributes: Joi.object().required(),
|
||||
version: Joi.number(),
|
||||
version: Joi.string(),
|
||||
migrationVersion: Joi.object().optional(),
|
||||
}).required()
|
||||
),
|
||||
|
|
|
@ -59,7 +59,7 @@ describe('POST /api/saved_objects/_bulk_get', () => {
|
|||
id: 'abc123',
|
||||
type: 'index-pattern',
|
||||
title: 'logstash-*',
|
||||
version: 2
|
||||
version: 'foo',
|
||||
}]
|
||||
};
|
||||
|
||||
|
|
|
@ -32,7 +32,7 @@ export const createUpdateRoute = (prereqs) => {
|
|||
}).required(),
|
||||
payload: Joi.object({
|
||||
attributes: Joi.object().required(),
|
||||
version: Joi.number().min(1)
|
||||
version: Joi.string(),
|
||||
}).required()
|
||||
},
|
||||
handler(request) {
|
||||
|
|
|
@ -66,7 +66,7 @@ describe('PUT /api/saved_objects/{type}/{id?}', () => {
|
|||
|
||||
it('calls upon savedObjectClient.update', async () => {
|
||||
const attributes = { title: 'Testing' };
|
||||
const options = { version: 2 };
|
||||
const options = { version: 'foo' };
|
||||
const request = {
|
||||
method: 'PUT',
|
||||
url: '/api/saved_objects/index-pattern/logstash-*',
|
||||
|
|
|
@ -24,6 +24,7 @@
|
|||
|
||||
import uuid from 'uuid';
|
||||
import { SavedObjectsSchema } from '../schema';
|
||||
import { decodeVersion, encodeVersion } from '../version';
|
||||
|
||||
/**
|
||||
* The root document type. In 7.0, this needs to change to '_doc'.
|
||||
|
@ -37,7 +38,8 @@ export interface RawDoc {
|
|||
_id: string;
|
||||
_source: any;
|
||||
_type?: string;
|
||||
_version?: number;
|
||||
_seq_no?: number;
|
||||
_primary_term?: number;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -60,7 +62,7 @@ export interface SavedObjectDoc {
|
|||
type: string;
|
||||
namespace?: string;
|
||||
migrationVersion?: MigrationVersion;
|
||||
version?: number;
|
||||
version?: string;
|
||||
updated_at?: Date;
|
||||
|
||||
[rootProp: string]: any;
|
||||
|
@ -99,8 +101,14 @@ export class SavedObjectsSerializer {
|
|||
*
|
||||
* @param {RawDoc} rawDoc - The raw ES document to be converted to saved object format.
|
||||
*/
|
||||
public rawToSavedObject({ _id, _source, _version }: RawDoc): SavedObjectDoc {
|
||||
public rawToSavedObject({ _id, _source, _seq_no, _primary_term }: RawDoc): SavedObjectDoc {
|
||||
const { type, namespace } = _source;
|
||||
|
||||
const version =
|
||||
_seq_no != null || _primary_term != null
|
||||
? encodeVersion(_seq_no!, _primary_term!)
|
||||
: undefined;
|
||||
|
||||
return {
|
||||
type,
|
||||
id: this.trimIdPrefix(namespace, type, _id),
|
||||
|
@ -108,7 +116,7 @@ export class SavedObjectsSerializer {
|
|||
attributes: _source[type],
|
||||
...(_source.migrationVersion && { migrationVersion: _source.migrationVersion }),
|
||||
...(_source.updated_at && { updated_at: _source.updated_at }),
|
||||
...(_version != null && { version: _version }),
|
||||
...(version && { version }),
|
||||
};
|
||||
}
|
||||
|
||||
|
@ -131,7 +139,7 @@ export class SavedObjectsSerializer {
|
|||
_id: this.generateRawId(namespace, type, id),
|
||||
_source: source,
|
||||
_type: ROOT_TYPE,
|
||||
...(version != null && { _version: version }),
|
||||
...(version != null && decodeVersion(version)),
|
||||
};
|
||||
}
|
||||
|
||||
|
|
|
@ -20,6 +20,7 @@
|
|||
import _ from 'lodash';
|
||||
import { ROOT_TYPE, SavedObjectsSerializer } from '.';
|
||||
import { SavedObjectsSchema } from '../schema';
|
||||
import { encodeVersion } from '../version';
|
||||
|
||||
describe('saved object conversion', () => {
|
||||
describe('#rawToSavedObject', () => {
|
||||
|
@ -69,7 +70,8 @@ describe('saved object conversion', () => {
|
|||
const actual = serializer.rawToSavedObject({
|
||||
_id: 'hello:world',
|
||||
_type: ROOT_TYPE,
|
||||
_version: 3,
|
||||
_seq_no: 3,
|
||||
_primary_term: 1,
|
||||
_source: {
|
||||
type: 'hello',
|
||||
hello: {
|
||||
|
@ -86,7 +88,7 @@ describe('saved object conversion', () => {
|
|||
const expected = {
|
||||
id: 'world',
|
||||
type: 'hello',
|
||||
version: 3,
|
||||
version: encodeVersion(3, 1),
|
||||
attributes: {
|
||||
a: 'b',
|
||||
c: 'd',
|
||||
|
@ -112,17 +114,46 @@ describe('saved object conversion', () => {
|
|||
expect(actual).not.toHaveProperty('version');
|
||||
});
|
||||
|
||||
test(`if specified it copies _version to version`, () => {
|
||||
test(`if specified it encodes _seq_no and _primary_term to version`, () => {
|
||||
const serializer = new SavedObjectsSerializer(new SavedObjectsSchema());
|
||||
const actual = serializer.rawToSavedObject({
|
||||
_id: 'foo:bar',
|
||||
_version: 4,
|
||||
_seq_no: 4,
|
||||
_primary_term: 1,
|
||||
_source: {
|
||||
type: 'foo',
|
||||
hello: {},
|
||||
},
|
||||
});
|
||||
expect(actual).toHaveProperty('version', 4);
|
||||
expect(actual).toHaveProperty('version', encodeVersion(4, 1));
|
||||
});
|
||||
|
||||
test(`if only _seq_no is specified it throws`, () => {
|
||||
const serializer = new SavedObjectsSerializer(new SavedObjectsSchema());
|
||||
expect(() =>
|
||||
serializer.rawToSavedObject({
|
||||
_id: 'foo:bar',
|
||||
_seq_no: 4,
|
||||
_source: {
|
||||
type: 'foo',
|
||||
hello: {},
|
||||
},
|
||||
})
|
||||
).toThrowErrorMatchingInlineSnapshot(`"_primary_term from elasticsearch must be an integer"`);
|
||||
});
|
||||
|
||||
test(`if only _primary_term is throws`, () => {
|
||||
const serializer = new SavedObjectsSerializer(new SavedObjectsSchema());
|
||||
expect(() =>
|
||||
serializer.rawToSavedObject({
|
||||
_id: 'foo:bar',
|
||||
_primary_term: 1,
|
||||
_source: {
|
||||
type: 'foo',
|
||||
hello: {},
|
||||
},
|
||||
})
|
||||
).toThrowErrorMatchingInlineSnapshot(`"_seq_no from elasticsearch must be an integer"`);
|
||||
});
|
||||
|
||||
test('if specified it copies the _source.updated_at property to updated_at', () => {
|
||||
|
@ -206,7 +237,8 @@ describe('saved object conversion', () => {
|
|||
const raw = {
|
||||
_id: 'foo-namespace:foo:bar',
|
||||
_type: ROOT_TYPE,
|
||||
_version: 24,
|
||||
_primary_term: 24,
|
||||
_seq_no: 42,
|
||||
_source: {
|
||||
type: 'foo',
|
||||
foo: {
|
||||
|
@ -440,25 +472,38 @@ describe('saved object conversion', () => {
|
|||
expect(actual._source).not.toHaveProperty('migrationVersion');
|
||||
});
|
||||
|
||||
test('it copies the version property to _version', () => {
|
||||
test('it decodes the version property to _seq_no and _primary_term', () => {
|
||||
const serializer = new SavedObjectsSerializer(new SavedObjectsSchema());
|
||||
const actual = serializer.savedObjectToRaw({
|
||||
type: '',
|
||||
attributes: {},
|
||||
version: 4,
|
||||
version: encodeVersion(1, 2),
|
||||
} as any);
|
||||
|
||||
expect(actual).toHaveProperty('_version', 4);
|
||||
expect(actual).toHaveProperty('_seq_no', 1);
|
||||
expect(actual).toHaveProperty('_primary_term', 2);
|
||||
});
|
||||
|
||||
test(`if unspecified it doesn't add _version property`, () => {
|
||||
test(`if unspecified it doesn't add _seq_no or _primary_term properties`, () => {
|
||||
const serializer = new SavedObjectsSerializer(new SavedObjectsSchema());
|
||||
const actual = serializer.savedObjectToRaw({
|
||||
type: '',
|
||||
attributes: {},
|
||||
} as any);
|
||||
|
||||
expect(actual).not.toHaveProperty('_version');
|
||||
expect(actual).not.toHaveProperty('_seq_no');
|
||||
expect(actual).not.toHaveProperty('_primary_term');
|
||||
});
|
||||
|
||||
test(`if version invalid it throws`, () => {
|
||||
const serializer = new SavedObjectsSerializer(new SavedObjectsSchema());
|
||||
expect(() =>
|
||||
serializer.savedObjectToRaw({
|
||||
type: '',
|
||||
attributes: {},
|
||||
version: 'foo',
|
||||
} as any)
|
||||
).toThrowErrorMatchingInlineSnapshot(`"Invalid version [foo]"`);
|
||||
});
|
||||
|
||||
test('it copies attributes to _source[type]', () => {
|
||||
|
|
|
@ -25,3 +25,6 @@ export function isNotFoundError(maybeError: any): boolean;
|
|||
export function isConflictError(maybeError: any): boolean;
|
||||
export function isEsUnavailableError(maybeError: any): boolean;
|
||||
export function isEsAutoCreateIndexError(maybeError: any): boolean;
|
||||
|
||||
export function createInvalidVersionError(version: any): Error;
|
||||
export function isInvalidVersionError(maybeError: Error): boolean;
|
||||
|
|
|
@ -50,6 +50,15 @@ export function isBadRequestError(error) {
|
|||
return error && error[code] === CODE_BAD_REQUEST;
|
||||
}
|
||||
|
||||
// 400 - invalid version
|
||||
const CODE_INVALID_VERSION = 'SavedObjectsClient/invalidVersion';
|
||||
export function createInvalidVersionError(versionInput) {
|
||||
return decorate(Boom.badRequest(`Invalid version [${versionInput}]`), CODE_INVALID_VERSION, 400);
|
||||
}
|
||||
export function isInvalidVersionError(error) {
|
||||
return error && error[code] === CODE_INVALID_VERSION;
|
||||
}
|
||||
|
||||
|
||||
// 401 - Not Authorized
|
||||
const CODE_NOT_AUTHORIZED = 'SavedObjectsClient/notAuthorized';
|
||||
|
|
|
@ -23,6 +23,7 @@ import { getSearchDsl } from './search_dsl';
|
|||
import { includedFields } from './included_fields';
|
||||
import { decorateEsError } from './decorate_es_error';
|
||||
import * as errors from './errors';
|
||||
import { decodeRequestVersion, encodeVersion, encodeHitVersion } from '../../version';
|
||||
|
||||
// BEWARE: The SavedObjectClient depends on the implementation details of the SavedObjectsRepository
|
||||
// so any breaking changes to this repository are considered breaking changes to the SavedObjectsClient.
|
||||
|
@ -169,7 +170,8 @@ export class SavedObjectsRepository {
|
|||
const {
|
||||
error,
|
||||
_id: responseId,
|
||||
_version: version,
|
||||
_seq_no: seqNo,
|
||||
_primary_term: primaryTerm,
|
||||
} = Object.values(response)[0];
|
||||
|
||||
const {
|
||||
|
@ -199,8 +201,8 @@ export class SavedObjectsRepository {
|
|||
id,
|
||||
type,
|
||||
updated_at: time,
|
||||
version,
|
||||
attributes
|
||||
version: encodeVersion(seqNo, primaryTerm),
|
||||
attributes,
|
||||
};
|
||||
})
|
||||
};
|
||||
|
@ -252,7 +254,6 @@ export class SavedObjectsRepository {
|
|||
* @returns {promise} - { took, timed_out, total, deleted, batches, version_conflicts, noops, retries, failures }
|
||||
*/
|
||||
async deleteByNamespace(namespace) {
|
||||
|
||||
if (!namespace || typeof namespace !== 'string') {
|
||||
throw new TypeError(`namespace is required, and must be a string`);
|
||||
}
|
||||
|
@ -324,7 +325,7 @@ export class SavedObjectsRepository {
|
|||
ignore: [404],
|
||||
rest_total_hits_as_int: true,
|
||||
body: {
|
||||
version: true,
|
||||
seq_no_primary_term: true,
|
||||
...getSearchDsl(this._mappings, this._schema, {
|
||||
search,
|
||||
searchFields,
|
||||
|
@ -407,7 +408,7 @@ export class SavedObjectsRepository {
|
|||
id,
|
||||
type,
|
||||
...time && { updated_at: time },
|
||||
version: doc._version,
|
||||
version: encodeHitVersion(doc),
|
||||
attributes: doc._source[type],
|
||||
migrationVersion: doc._source.migrationVersion,
|
||||
};
|
||||
|
@ -449,7 +450,7 @@ export class SavedObjectsRepository {
|
|||
id,
|
||||
type,
|
||||
...updatedAt && { updated_at: updatedAt },
|
||||
version: response._version,
|
||||
version: encodeHitVersion(response),
|
||||
attributes: response._source[type],
|
||||
migrationVersion: response._source.migrationVersion,
|
||||
};
|
||||
|
@ -461,7 +462,7 @@ export class SavedObjectsRepository {
|
|||
* @param {string} type
|
||||
* @param {string} id
|
||||
* @param {object} [options={}]
|
||||
* @property {integer} options.version - ensures version matches that of persisted object
|
||||
* @property {string} options.version - ensures version matches that of persisted object
|
||||
* @property {string} [options.namespace]
|
||||
* @returns {promise}
|
||||
*/
|
||||
|
@ -476,7 +477,7 @@ export class SavedObjectsRepository {
|
|||
id: this._serializer.generateRawId(namespace, type, id),
|
||||
type: this._type,
|
||||
index: this._index,
|
||||
version,
|
||||
...(version && decodeRequestVersion(version)),
|
||||
refresh: 'wait_for',
|
||||
ignore: [404],
|
||||
body: {
|
||||
|
@ -496,7 +497,7 @@ export class SavedObjectsRepository {
|
|||
id,
|
||||
type,
|
||||
updated_at: time,
|
||||
version: response._version,
|
||||
version: encodeHitVersion(response),
|
||||
attributes
|
||||
};
|
||||
}
|
||||
|
@ -570,7 +571,7 @@ export class SavedObjectsRepository {
|
|||
id,
|
||||
type,
|
||||
updated_at: time,
|
||||
version: response._version,
|
||||
version: encodeHitVersion(response),
|
||||
attributes: response.get._source[type],
|
||||
};
|
||||
|
||||
|
|
|
@ -26,6 +26,7 @@ import * as errors from './errors';
|
|||
import elasticsearch from 'elasticsearch';
|
||||
import { SavedObjectsSchema } from '../../schema';
|
||||
import { SavedObjectsSerializer } from '../../serialization';
|
||||
import { encodeHitVersion } from '../../version';
|
||||
|
||||
// BEWARE: The SavedObjectClient depends on the implementation details of the SavedObjectsRepository
|
||||
// so any breaking changes to this repository are considered breaking changes to the SavedObjectsClient.
|
||||
|
@ -39,6 +40,8 @@ describe('SavedObjectsRepository', () => {
|
|||
let migrator;
|
||||
const mockTimestamp = '2017-08-14T15:49:14.886Z';
|
||||
const mockTimestampFields = { updated_at: mockTimestamp };
|
||||
const mockVersionProps = { _seq_no: 1, _primary_term: 1 };
|
||||
const mockVersion = encodeHitVersion(mockVersionProps);
|
||||
const noNamespaceSearchResults = {
|
||||
hits: {
|
||||
total: 4,
|
||||
|
@ -47,6 +50,7 @@ describe('SavedObjectsRepository', () => {
|
|||
_type: 'doc',
|
||||
_id: 'index-pattern:logstash-*',
|
||||
_score: 1,
|
||||
...mockVersionProps,
|
||||
_source: {
|
||||
type: 'index-pattern',
|
||||
...mockTimestampFields,
|
||||
|
@ -61,6 +65,7 @@ describe('SavedObjectsRepository', () => {
|
|||
_type: 'doc',
|
||||
_id: 'config:6.0.0-alpha1',
|
||||
_score: 1,
|
||||
...mockVersionProps,
|
||||
_source: {
|
||||
type: 'config',
|
||||
...mockTimestampFields,
|
||||
|
@ -74,6 +79,7 @@ describe('SavedObjectsRepository', () => {
|
|||
_type: 'doc',
|
||||
_id: 'index-pattern:stocks-*',
|
||||
_score: 1,
|
||||
...mockVersionProps,
|
||||
_source: {
|
||||
type: 'index-pattern',
|
||||
...mockTimestampFields,
|
||||
|
@ -88,6 +94,7 @@ describe('SavedObjectsRepository', () => {
|
|||
_type: 'doc',
|
||||
_id: 'globaltype:something',
|
||||
_score: 1,
|
||||
...mockVersionProps,
|
||||
_source: {
|
||||
type: 'globaltype',
|
||||
...mockTimestampFields,
|
||||
|
@ -107,6 +114,7 @@ describe('SavedObjectsRepository', () => {
|
|||
_type: 'doc',
|
||||
_id: 'foo-namespace:index-pattern:logstash-*',
|
||||
_score: 1,
|
||||
...mockVersionProps,
|
||||
_source: {
|
||||
namespace: 'foo-namespace',
|
||||
type: 'index-pattern',
|
||||
|
@ -122,6 +130,7 @@ describe('SavedObjectsRepository', () => {
|
|||
_type: 'doc',
|
||||
_id: 'foo-namespace:config:6.0.0-alpha1',
|
||||
_score: 1,
|
||||
...mockVersionProps,
|
||||
_source: {
|
||||
namespace: 'foo-namespace',
|
||||
type: 'config',
|
||||
|
@ -136,6 +145,7 @@ describe('SavedObjectsRepository', () => {
|
|||
_type: 'doc',
|
||||
_id: 'foo-namespace:index-pattern:stocks-*',
|
||||
_score: 1,
|
||||
...mockVersionProps,
|
||||
_source: {
|
||||
namespace: 'foo-namespace',
|
||||
type: 'index-pattern',
|
||||
|
@ -151,6 +161,7 @@ describe('SavedObjectsRepository', () => {
|
|||
_type: 'doc',
|
||||
_id: 'globaltype:something',
|
||||
_score: 1,
|
||||
...mockVersionProps,
|
||||
_source: {
|
||||
type: 'globaltype',
|
||||
...mockTimestampFields,
|
||||
|
@ -239,7 +250,7 @@ describe('SavedObjectsRepository', () => {
|
|||
callAdminCluster.callsFake((method, params) => ({
|
||||
_type: 'doc',
|
||||
_id: params.id,
|
||||
_version: 2
|
||||
...mockVersionProps,
|
||||
}));
|
||||
});
|
||||
|
||||
|
@ -267,7 +278,7 @@ describe('SavedObjectsRepository', () => {
|
|||
type: 'index-pattern',
|
||||
id: 'logstash-*',
|
||||
...mockTimestampFields,
|
||||
version: 2,
|
||||
version: mockVersion,
|
||||
attributes: {
|
||||
title: 'Logstash',
|
||||
}
|
||||
|
@ -518,7 +529,7 @@ describe('SavedObjectsRepository', () => {
|
|||
create: {
|
||||
_type: 'doc',
|
||||
_id: 'index-pattern:two',
|
||||
_version: 2
|
||||
...mockVersionProps,
|
||||
}
|
||||
}]
|
||||
}));
|
||||
|
@ -537,7 +548,7 @@ describe('SavedObjectsRepository', () => {
|
|||
}, {
|
||||
id: 'two',
|
||||
type: 'index-pattern',
|
||||
version: 2,
|
||||
version: mockVersion,
|
||||
...mockTimestampFields,
|
||||
attributes: { title: 'Test Two' },
|
||||
}
|
||||
|
@ -552,13 +563,13 @@ describe('SavedObjectsRepository', () => {
|
|||
create: {
|
||||
_type: 'doc',
|
||||
_id: 'config:one',
|
||||
_version: 2
|
||||
...mockVersionProps
|
||||
}
|
||||
}, {
|
||||
create: {
|
||||
_type: 'doc',
|
||||
_id: 'index-pattern:two',
|
||||
_version: 2
|
||||
...mockVersionProps
|
||||
}
|
||||
}]
|
||||
}));
|
||||
|
@ -575,13 +586,13 @@ describe('SavedObjectsRepository', () => {
|
|||
{
|
||||
id: 'one',
|
||||
type: 'config',
|
||||
version: 2,
|
||||
version: mockVersion,
|
||||
...mockTimestampFields,
|
||||
attributes: { title: 'Test One' },
|
||||
}, {
|
||||
id: 'two',
|
||||
type: 'index-pattern',
|
||||
version: 2,
|
||||
version: mockVersion,
|
||||
...mockTimestampFields,
|
||||
attributes: { title: 'Test Two' },
|
||||
}
|
||||
|
@ -858,8 +869,8 @@ describe('SavedObjectsRepository', () => {
|
|||
id: doc._id.replace(/(index-pattern|config|globaltype)\:/, ''),
|
||||
type: doc._source.type,
|
||||
...mockTimestampFields,
|
||||
version: doc._version,
|
||||
attributes: doc._source[doc._source.type]
|
||||
version: mockVersion,
|
||||
attributes: doc._source[doc._source.type],
|
||||
});
|
||||
});
|
||||
});
|
||||
|
@ -881,8 +892,8 @@ describe('SavedObjectsRepository', () => {
|
|||
id: doc._id.replace(/(foo-namespace\:)?(index-pattern|config|globaltype)\:/, ''),
|
||||
type: doc._source.type,
|
||||
...mockTimestampFields,
|
||||
version: doc._version,
|
||||
attributes: doc._source[doc._source.type]
|
||||
version: mockVersion,
|
||||
attributes: doc._source[doc._source.type],
|
||||
});
|
||||
});
|
||||
});
|
||||
|
@ -926,7 +937,7 @@ describe('SavedObjectsRepository', () => {
|
|||
const noNamespaceResult = {
|
||||
_id: 'index-pattern:logstash-*',
|
||||
_type: 'doc',
|
||||
_version: 2,
|
||||
...mockVersionProps,
|
||||
_source: {
|
||||
type: 'index-pattern',
|
||||
specialProperty: 'specialValue',
|
||||
|
@ -939,7 +950,7 @@ describe('SavedObjectsRepository', () => {
|
|||
const namespacedResult = {
|
||||
_id: 'foo-namespace:index-pattern:logstash-*',
|
||||
_type: 'doc',
|
||||
_version: 2,
|
||||
...mockVersionProps,
|
||||
_source: {
|
||||
namespace: 'foo-namespace',
|
||||
type: 'index-pattern',
|
||||
|
@ -968,7 +979,7 @@ describe('SavedObjectsRepository', () => {
|
|||
id: 'logstash-*',
|
||||
type: 'index-pattern',
|
||||
updated_at: mockTimestamp,
|
||||
version: 2,
|
||||
version: mockVersion,
|
||||
attributes: {
|
||||
title: 'Testing'
|
||||
}
|
||||
|
@ -983,7 +994,7 @@ describe('SavedObjectsRepository', () => {
|
|||
id: 'logstash-*',
|
||||
type: 'index-pattern',
|
||||
updated_at: mockTimestamp,
|
||||
version: 2,
|
||||
version: mockVersion,
|
||||
attributes: {
|
||||
title: 'Testing'
|
||||
}
|
||||
|
@ -1111,7 +1122,7 @@ describe('SavedObjectsRepository', () => {
|
|||
_type: 'doc',
|
||||
_id: 'config:good',
|
||||
found: true,
|
||||
_version: 2,
|
||||
...mockVersionProps,
|
||||
_source: { ...mockTimestampFields, config: { title: 'Test' } }
|
||||
}, {
|
||||
_type: 'doc',
|
||||
|
@ -1132,8 +1143,8 @@ describe('SavedObjectsRepository', () => {
|
|||
id: 'good',
|
||||
type: 'config',
|
||||
...mockTimestampFields,
|
||||
version: 2,
|
||||
attributes: { title: 'Test' }
|
||||
version: mockVersion,
|
||||
attributes: { title: 'Test' },
|
||||
});
|
||||
expect(savedObjects[1]).toEqual({
|
||||
id: 'bad',
|
||||
|
@ -1146,14 +1157,13 @@ describe('SavedObjectsRepository', () => {
|
|||
describe('#update', () => {
|
||||
const id = 'logstash-*';
|
||||
const type = 'index-pattern';
|
||||
const newVersion = 2;
|
||||
const attributes = { title: 'Testing' };
|
||||
|
||||
beforeEach(() => {
|
||||
callAdminCluster.returns(Promise.resolve({
|
||||
_id: `${type}:${id}`,
|
||||
_type: 'doc',
|
||||
_version: newVersion,
|
||||
...mockVersionProps,
|
||||
result: 'updated'
|
||||
}));
|
||||
});
|
||||
|
@ -1168,14 +1178,16 @@ describe('SavedObjectsRepository', () => {
|
|||
sinon.assert.calledOnce(migrator.awaitMigration);
|
||||
});
|
||||
|
||||
it('returns current ES document version', async () => {
|
||||
const response = await savedObjectsRepository.update('index-pattern', 'logstash-*', attributes, { namespace: 'foo-namespace' });
|
||||
it('returns current ES document _seq_no and _primary_term encoded as version', async () => {
|
||||
const response = await savedObjectsRepository.update('index-pattern', 'logstash-*', attributes, {
|
||||
namespace: 'foo-namespace',
|
||||
});
|
||||
expect(response).toEqual({
|
||||
id,
|
||||
type,
|
||||
...mockTimestampFields,
|
||||
version: newVersion,
|
||||
attributes
|
||||
version: mockVersion,
|
||||
attributes,
|
||||
});
|
||||
});
|
||||
|
||||
|
@ -1184,12 +1196,18 @@ describe('SavedObjectsRepository', () => {
|
|||
type,
|
||||
id,
|
||||
{ title: 'Testing' },
|
||||
{ version: newVersion - 1 }
|
||||
{
|
||||
version: encodeHitVersion({
|
||||
_seq_no: 100,
|
||||
_primary_term: 200
|
||||
})
|
||||
}
|
||||
);
|
||||
|
||||
sinon.assert.calledOnce(callAdminCluster);
|
||||
sinon.assert.calledWithExactly(callAdminCluster, sinon.match.string, sinon.match({
|
||||
version: newVersion - 1
|
||||
if_seq_no: 100,
|
||||
if_primary_term: 200,
|
||||
}));
|
||||
});
|
||||
|
||||
|
@ -1204,7 +1222,6 @@ describe('SavedObjectsRepository', () => {
|
|||
sinon.assert.calledWithExactly(callAdminCluster, 'update', {
|
||||
type: 'doc',
|
||||
id: 'foo-namespace:index-pattern:logstash-*',
|
||||
version: undefined,
|
||||
body: {
|
||||
doc: { updated_at: mockTimestamp, 'index-pattern': { title: 'Testing' } }
|
||||
},
|
||||
|
@ -1223,7 +1240,6 @@ describe('SavedObjectsRepository', () => {
|
|||
sinon.assert.calledWithExactly(callAdminCluster, 'update', {
|
||||
type: 'doc',
|
||||
id: 'index-pattern:logstash-*',
|
||||
version: undefined,
|
||||
body: {
|
||||
doc: { updated_at: mockTimestamp, 'index-pattern': { title: 'Testing' } }
|
||||
},
|
||||
|
@ -1246,7 +1262,6 @@ describe('SavedObjectsRepository', () => {
|
|||
sinon.assert.calledWithExactly(callAdminCluster, 'update', {
|
||||
type: 'doc',
|
||||
id: 'globaltype:foo',
|
||||
version: undefined,
|
||||
body: {
|
||||
doc: { updated_at: mockTimestamp, 'globaltype': { name: 'bar' } }
|
||||
},
|
||||
|
@ -1264,7 +1279,7 @@ describe('SavedObjectsRepository', () => {
|
|||
callAdminCluster.callsFake((method, params) => ({
|
||||
_type: 'doc',
|
||||
_id: params.id,
|
||||
_version: 2,
|
||||
...mockVersionProps,
|
||||
_index: '.kibana',
|
||||
get: {
|
||||
found: true,
|
||||
|
@ -1284,7 +1299,7 @@ describe('SavedObjectsRepository', () => {
|
|||
callAdminCluster.callsFake((method, params) => ({
|
||||
_type: 'doc',
|
||||
_id: params.id,
|
||||
_version: 2,
|
||||
...mockVersionProps,
|
||||
_index: '.kibana',
|
||||
get: {
|
||||
found: true,
|
||||
|
@ -1313,7 +1328,7 @@ describe('SavedObjectsRepository', () => {
|
|||
type: 'config',
|
||||
id: '6.0.0-alpha1',
|
||||
...mockTimestampFields,
|
||||
version: 2,
|
||||
version: mockVersion,
|
||||
attributes: {
|
||||
buildNum: 8468,
|
||||
defaultIndex: 'logstash-*'
|
||||
|
@ -1384,7 +1399,7 @@ describe('SavedObjectsRepository', () => {
|
|||
callAdminCluster.callsFake((method, params) => ({
|
||||
_type: 'doc',
|
||||
_id: params.id,
|
||||
_version: 2,
|
||||
...mockVersionProps,
|
||||
_index: '.kibana',
|
||||
get: {
|
||||
found: true,
|
||||
|
|
|
@ -58,7 +58,7 @@ export interface FindResponse<T extends SavedObjectAttributes = any> {
|
|||
}
|
||||
|
||||
export interface UpdateOptions extends BaseOptions {
|
||||
version?: number;
|
||||
version?: string;
|
||||
}
|
||||
|
||||
export interface BulkGetObject {
|
||||
|
@ -78,7 +78,7 @@ export interface SavedObjectAttributes {
|
|||
export interface SavedObject<T extends SavedObjectAttributes = any> {
|
||||
id: string;
|
||||
type: string;
|
||||
version?: number;
|
||||
version?: string;
|
||||
updated_at?: string;
|
||||
error?: {
|
||||
message: string;
|
||||
|
|
21
src/server/saved_objects/version/base64.ts
Normal file
21
src/server/saved_objects/version/base64.ts
Normal file
|
@ -0,0 +1,21 @@
|
|||
/*
|
||||
* Licensed to Elasticsearch B.V. under one or more contributor
|
||||
* license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright
|
||||
* ownership. Elasticsearch B.V. 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.
|
||||
*/
|
||||
|
||||
export const decodeBase64 = (base64: string) => Buffer.from(base64, 'base64').toString('utf8');
|
||||
export const encodeBase64 = (utf8: string) => Buffer.from(utf8, 'utf8').toString('base64');
|
|
@ -0,0 +1,35 @@
|
|||
/*
|
||||
* Licensed to Elasticsearch B.V. under one or more contributor
|
||||
* license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright
|
||||
* ownership. Elasticsearch B.V. 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.
|
||||
*/
|
||||
|
||||
jest.mock('./decode_version', () => ({
|
||||
decodeVersion: jest.fn().mockReturnValue({ _seq_no: 1, _primary_term: 2 }),
|
||||
}));
|
||||
|
||||
import { decodeRequestVersion } from './decode_request_version';
|
||||
import { decodeVersion } from './decode_version';
|
||||
|
||||
it('renames decodeVersion() return value to use if_seq_no and if_primary_term', () => {
|
||||
expect(decodeRequestVersion('foobar')).toMatchInlineSnapshot(`
|
||||
Object {
|
||||
"if_primary_term": 2,
|
||||
"if_seq_no": 1,
|
||||
}
|
||||
`);
|
||||
expect(decodeVersion).toHaveBeenCalledWith('foobar');
|
||||
});
|
32
src/server/saved_objects/version/decode_request_version.ts
Normal file
32
src/server/saved_objects/version/decode_request_version.ts
Normal file
|
@ -0,0 +1,32 @@
|
|||
/*
|
||||
* Licensed to Elasticsearch B.V. under one or more contributor
|
||||
* license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright
|
||||
* ownership. Elasticsearch B.V. 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.
|
||||
*/
|
||||
|
||||
import { decodeVersion } from './decode_version';
|
||||
|
||||
/**
|
||||
* Helper for decoding version to request params that are driven
|
||||
* by the version info
|
||||
*/
|
||||
export function decodeRequestVersion(version?: string) {
|
||||
const decoded = decodeVersion(version);
|
||||
return {
|
||||
if_seq_no: decoded._seq_no,
|
||||
if_primary_term: decoded._primary_term,
|
||||
};
|
||||
}
|
102
src/server/saved_objects/version/decode_version.test.ts
Normal file
102
src/server/saved_objects/version/decode_version.test.ts
Normal file
|
@ -0,0 +1,102 @@
|
|||
/*
|
||||
* Licensed to Elasticsearch B.V. under one or more contributor
|
||||
* license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright
|
||||
* ownership. Elasticsearch B.V. 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.
|
||||
*/
|
||||
|
||||
import Boom from 'boom';
|
||||
|
||||
import { decodeVersion } from './decode_version';
|
||||
|
||||
describe('decodeVersion', () => {
|
||||
it('parses version back into {_seq_no,_primary_term} object', () => {
|
||||
expect(decodeVersion('WzQsMV0=')).toMatchInlineSnapshot(`
|
||||
Object {
|
||||
"_primary_term": 1,
|
||||
"_seq_no": 4,
|
||||
}
|
||||
`);
|
||||
});
|
||||
|
||||
it('throws Boom error if not in base64', () => {
|
||||
let error;
|
||||
try {
|
||||
decodeVersion('[1,4]');
|
||||
} catch (err) {
|
||||
error = err;
|
||||
}
|
||||
|
||||
expect(error.message).toMatchInlineSnapshot(`"Invalid version [[1,4]]"`);
|
||||
expect(Boom.isBoom(error)).toBe(true);
|
||||
expect(error.output).toMatchInlineSnapshot(`
|
||||
Object {
|
||||
"headers": Object {},
|
||||
"payload": Object {
|
||||
"error": "Bad Request",
|
||||
"message": "Invalid version [[1,4]]",
|
||||
"statusCode": 400,
|
||||
},
|
||||
"statusCode": 400,
|
||||
}
|
||||
`);
|
||||
});
|
||||
|
||||
it('throws if not JSON encoded', () => {
|
||||
let error;
|
||||
try {
|
||||
decodeVersion('MSwy');
|
||||
} catch (err) {
|
||||
error = err;
|
||||
}
|
||||
|
||||
expect(error.message).toMatchInlineSnapshot(`"Invalid version [MSwy]"`);
|
||||
expect(Boom.isBoom(error)).toBe(true);
|
||||
expect(error.output).toMatchInlineSnapshot(`
|
||||
Object {
|
||||
"headers": Object {},
|
||||
"payload": Object {
|
||||
"error": "Bad Request",
|
||||
"message": "Invalid version [MSwy]",
|
||||
"statusCode": 400,
|
||||
},
|
||||
"statusCode": 400,
|
||||
}
|
||||
`);
|
||||
});
|
||||
|
||||
it('throws if either value is not an integer', () => {
|
||||
let error;
|
||||
try {
|
||||
decodeVersion('WzEsMy41XQ==');
|
||||
} catch (err) {
|
||||
error = err;
|
||||
}
|
||||
|
||||
expect(error.message).toMatchInlineSnapshot(`"Invalid version [WzEsMy41XQ==]"`);
|
||||
expect(Boom.isBoom(error)).toBe(true);
|
||||
expect(error.output).toMatchInlineSnapshot(`
|
||||
Object {
|
||||
"headers": Object {},
|
||||
"payload": Object {
|
||||
"error": "Bad Request",
|
||||
"message": "Invalid version [WzEsMy41XQ==]",
|
||||
"statusCode": 400,
|
||||
},
|
||||
"statusCode": 400,
|
||||
}
|
||||
`);
|
||||
});
|
||||
});
|
51
src/server/saved_objects/version/decode_version.ts
Normal file
51
src/server/saved_objects/version/decode_version.ts
Normal file
|
@ -0,0 +1,51 @@
|
|||
/*
|
||||
* Licensed to Elasticsearch B.V. under one or more contributor
|
||||
* license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright
|
||||
* ownership. Elasticsearch B.V. 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.
|
||||
*/
|
||||
|
||||
import { createInvalidVersionError } from '../service/lib/errors';
|
||||
import { decodeBase64 } from './base64';
|
||||
|
||||
/**
|
||||
* Decode the "opaque" version string to the sequence params we
|
||||
* can use to activate optimistic concurrency in Elasticsearch
|
||||
*/
|
||||
export function decodeVersion(version?: string) {
|
||||
try {
|
||||
if (typeof version !== 'string') {
|
||||
throw new TypeError();
|
||||
}
|
||||
|
||||
const seqParams = JSON.parse(decodeBase64(version)) as [number, number];
|
||||
|
||||
if (
|
||||
!Array.isArray(seqParams) ||
|
||||
seqParams.length !== 2 ||
|
||||
!Number.isInteger(seqParams[0]) ||
|
||||
!Number.isInteger(seqParams[1])
|
||||
) {
|
||||
throw new TypeError();
|
||||
}
|
||||
|
||||
return {
|
||||
_seq_no: seqParams[0],
|
||||
_primary_term: seqParams[1],
|
||||
};
|
||||
} catch (_) {
|
||||
throw createInvalidVersionError(version);
|
||||
}
|
||||
}
|
30
src/server/saved_objects/version/encode_hit_version.test.ts
Normal file
30
src/server/saved_objects/version/encode_hit_version.test.ts
Normal file
|
@ -0,0 +1,30 @@
|
|||
/*
|
||||
* Licensed to Elasticsearch B.V. under one or more contributor
|
||||
* license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright
|
||||
* ownership. Elasticsearch B.V. 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.
|
||||
*/
|
||||
|
||||
jest.mock('./encode_version', () => ({
|
||||
encodeVersion: jest.fn().mockReturnValue('foo'),
|
||||
}));
|
||||
|
||||
import { encodeHitVersion } from './encode_hit_version';
|
||||
import { encodeVersion } from './encode_version';
|
||||
|
||||
it('renames decodeVersion() return value to use if_seq_no and if_primary_term', () => {
|
||||
expect(encodeHitVersion({ _seq_no: 1, _primary_term: 2 })).toMatchInlineSnapshot(`"foo"`);
|
||||
expect(encodeVersion).toHaveBeenCalledWith(1, 2);
|
||||
});
|
28
src/server/saved_objects/version/encode_hit_version.ts
Normal file
28
src/server/saved_objects/version/encode_hit_version.ts
Normal file
|
@ -0,0 +1,28 @@
|
|||
/*
|
||||
* Licensed to Elasticsearch B.V. under one or more contributor
|
||||
* license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright
|
||||
* ownership. Elasticsearch B.V. 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.
|
||||
*/
|
||||
|
||||
import { encodeVersion } from './encode_version';
|
||||
|
||||
/**
|
||||
* Helper for encoding a version from a "hit" (hits.hits[#] from _search) or
|
||||
* "doc" (body from GET, update, etc) object
|
||||
*/
|
||||
export function encodeHitVersion(response: { _seq_no: number; _primary_term: number }) {
|
||||
return encodeVersion(response._seq_no, response._primary_term);
|
||||
}
|
62
src/server/saved_objects/version/encode_version.test.ts
Normal file
62
src/server/saved_objects/version/encode_version.test.ts
Normal file
|
@ -0,0 +1,62 @@
|
|||
/*
|
||||
* Licensed to Elasticsearch B.V. under one or more contributor
|
||||
* license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright
|
||||
* ownership. Elasticsearch B.V. 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.
|
||||
*/
|
||||
|
||||
import { encodeVersion } from './encode_version';
|
||||
|
||||
describe('encodeVersion', () => {
|
||||
it('throws if primaryTerm is not an integer', () => {
|
||||
expect(() => encodeVersion(1, undefined as any)).toThrowErrorMatchingInlineSnapshot(
|
||||
`"_primary_term from elasticsearch must be an integer"`
|
||||
);
|
||||
expect(() => encodeVersion(1, null as any)).toThrowErrorMatchingInlineSnapshot(
|
||||
`"_primary_term from elasticsearch must be an integer"`
|
||||
);
|
||||
expect(() => encodeVersion(1, {} as any)).toThrowErrorMatchingInlineSnapshot(
|
||||
`"_primary_term from elasticsearch must be an integer"`
|
||||
);
|
||||
expect(() => encodeVersion(1, [] as any)).toThrowErrorMatchingInlineSnapshot(
|
||||
`"_primary_term from elasticsearch must be an integer"`
|
||||
);
|
||||
expect(() => encodeVersion(1, 2.5 as any)).toThrowErrorMatchingInlineSnapshot(
|
||||
`"_primary_term from elasticsearch must be an integer"`
|
||||
);
|
||||
});
|
||||
|
||||
it('throws if seqNo is not an integer', () => {
|
||||
expect(() => encodeVersion(undefined as any, 1)).toThrowErrorMatchingInlineSnapshot(
|
||||
`"_seq_no from elasticsearch must be an integer"`
|
||||
);
|
||||
expect(() => encodeVersion(null as any, 1)).toThrowErrorMatchingInlineSnapshot(
|
||||
`"_seq_no from elasticsearch must be an integer"`
|
||||
);
|
||||
expect(() => encodeVersion({} as any, 1)).toThrowErrorMatchingInlineSnapshot(
|
||||
`"_seq_no from elasticsearch must be an integer"`
|
||||
);
|
||||
expect(() => encodeVersion([] as any, 1)).toThrowErrorMatchingInlineSnapshot(
|
||||
`"_seq_no from elasticsearch must be an integer"`
|
||||
);
|
||||
expect(() => encodeVersion(2.5 as any, 1)).toThrowErrorMatchingInlineSnapshot(
|
||||
`"_seq_no from elasticsearch must be an integer"`
|
||||
);
|
||||
});
|
||||
|
||||
it('returns a base64 encoded, JSON string of seqNo and primaryTerm', () => {
|
||||
expect(encodeVersion(123, 456)).toMatchInlineSnapshot(`"WzEyMyw0NTZd"`);
|
||||
});
|
||||
});
|
37
src/server/saved_objects/version/encode_version.ts
Normal file
37
src/server/saved_objects/version/encode_version.ts
Normal file
|
@ -0,0 +1,37 @@
|
|||
/*
|
||||
* Licensed to Elasticsearch B.V. under one or more contributor
|
||||
* license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright
|
||||
* ownership. Elasticsearch B.V. 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.
|
||||
*/
|
||||
|
||||
import { encodeBase64 } from './base64';
|
||||
|
||||
/**
|
||||
* Encode the sequence params into an "opaque" version string
|
||||
* that can be used in the saved object API in place of numeric
|
||||
* version numbers
|
||||
*/
|
||||
export function encodeVersion(seqNo: number, primaryTerm: number) {
|
||||
if (!Number.isInteger(primaryTerm)) {
|
||||
throw new TypeError('_primary_term from elasticsearch must be an integer');
|
||||
}
|
||||
|
||||
if (!Number.isInteger(seqNo)) {
|
||||
throw new TypeError('_seq_no from elasticsearch must be an integer');
|
||||
}
|
||||
|
||||
return encodeBase64(JSON.stringify([seqNo, primaryTerm]));
|
||||
}
|
23
src/server/saved_objects/version/index.ts
Normal file
23
src/server/saved_objects/version/index.ts
Normal file
|
@ -0,0 +1,23 @@
|
|||
/*
|
||||
* Licensed to Elasticsearch B.V. under one or more contributor
|
||||
* license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright
|
||||
* ownership. Elasticsearch B.V. 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.
|
||||
*/
|
||||
|
||||
export * from './encode_version';
|
||||
export * from './encode_hit_version';
|
||||
export * from './decode_version';
|
||||
export * from './decode_request_version';
|
|
@ -42,13 +42,13 @@ describe('Saved Object', function () {
|
|||
* that can be used to stub es calls.
|
||||
* @param indexPatternId
|
||||
* @param additionalOptions - object that will be assigned to the mocked doc response.
|
||||
* @returns {{attributes: {}, type: string, id: *, _version: integer}}
|
||||
* @returns {{attributes: {}, type: string, id: *, _version: string}}
|
||||
*/
|
||||
function getMockedDocResponse(indexPatternId, additionalOptions = {}) {
|
||||
return {
|
||||
type: 'dashboard',
|
||||
id: indexPatternId,
|
||||
_version: 2,
|
||||
_version: 'foo',
|
||||
attributes: {},
|
||||
...additionalOptions
|
||||
};
|
||||
|
@ -242,7 +242,11 @@ describe('Saved Object', function () {
|
|||
return createInitializedSavedObject({ type: 'dashboard' }).then(savedObject => {
|
||||
const mockDocResponse = getMockedDocResponse('myId');
|
||||
sinon.stub(savedObjectsClientStub, 'create').callsFake(() => {
|
||||
return BluebirdPromise.resolve({ type: 'dashboard', id: 'myId', _version: 2 });
|
||||
return BluebirdPromise.resolve({
|
||||
type: 'dashboard',
|
||||
id: 'myId',
|
||||
_version: 'foo'
|
||||
});
|
||||
});
|
||||
|
||||
stubESResponse(mockDocResponse);
|
||||
|
@ -261,7 +265,9 @@ describe('Saved Object', function () {
|
|||
sinon.stub(savedObjectsClientStub, 'create').callsFake(() => {
|
||||
expect(savedObject.isSaving).to.be(true);
|
||||
return BluebirdPromise.resolve({
|
||||
type: 'dashboard', id, version: 2
|
||||
type: 'dashboard',
|
||||
id,
|
||||
version: 'foo'
|
||||
});
|
||||
});
|
||||
expect(savedObject.isSaving).to.be(false);
|
||||
|
@ -451,7 +457,7 @@ describe('Saved Object', function () {
|
|||
attributes: {
|
||||
title: 'testIndexPattern'
|
||||
},
|
||||
_version: 2
|
||||
_version: 'foo'
|
||||
});
|
||||
|
||||
const savedObject = new SavedObject(config);
|
||||
|
|
|
@ -208,7 +208,6 @@ export function SavedObjectProvider(Promise, Private, Notifier, confirmModalProm
|
|||
return savedObjectsClient.get(esType, this.id)
|
||||
.then(resp => {
|
||||
// temporary compatability for savedObjectsClient
|
||||
|
||||
return {
|
||||
_id: resp.id,
|
||||
_type: resp.type,
|
||||
|
|
|
@ -86,7 +86,7 @@ jest.mock('../unsupported_time_patterns', () => ({
|
|||
|
||||
jest.mock('../../saved_objects', () => {
|
||||
const object = {
|
||||
_version: 1,
|
||||
_version: 'foo',
|
||||
_id: 'foo',
|
||||
attributes: {
|
||||
title: 'something'
|
||||
|
@ -106,10 +106,11 @@ jest.mock('../../saved_objects', () => {
|
|||
}
|
||||
|
||||
object.attributes.title = body.title;
|
||||
object._version += 'a';
|
||||
|
||||
return {
|
||||
id: object._id,
|
||||
_version: ++object._version,
|
||||
_version: object._version,
|
||||
};
|
||||
}
|
||||
},
|
||||
|
@ -137,13 +138,13 @@ describe('IndexPattern', () => {
|
|||
const pattern = new IndexPattern('foo');
|
||||
await pattern.init();
|
||||
|
||||
expect(pattern.version).toBe(2);
|
||||
expect(pattern.version).toBe('fooa');
|
||||
|
||||
// Create the same one - we're going to handle concurrency
|
||||
const samePattern = new IndexPattern('foo');
|
||||
await samePattern.init();
|
||||
|
||||
expect(samePattern.version).toBe(3);
|
||||
expect(samePattern.version).toBe('fooaa');
|
||||
|
||||
// This will conflict because samePattern did a save (from refreshFields)
|
||||
// but the resave should work fine
|
||||
|
|
|
@ -47,7 +47,6 @@ describe('SavedObject', () => {
|
|||
|
||||
const client = sinon.stub();
|
||||
const savedObject = new SavedObject(client, { version });
|
||||
|
||||
expect(savedObject._version).to.be(version);
|
||||
});
|
||||
});
|
||||
|
|
|
@ -29,7 +29,7 @@ describe('SavedObjectsClient', () => {
|
|||
id: 'AVwSwFxtcMV38qjDZoQg',
|
||||
type: 'config',
|
||||
attributes: { title: 'Example title' },
|
||||
version: 2
|
||||
version: 'foo'
|
||||
};
|
||||
|
||||
let kfetchStub;
|
||||
|
@ -228,8 +228,8 @@ describe('SavedObjectsClient', () => {
|
|||
|
||||
test('makes HTTP call', () => {
|
||||
const attributes = { foo: 'Foo', bar: 'Bar' };
|
||||
const body = { attributes, version: 2 };
|
||||
const options = { version: 2 };
|
||||
const body = { attributes, version: 'foo' };
|
||||
const options = { version: 'foo' };
|
||||
|
||||
savedObjectsClient.update('index-pattern', 'logstash-*', attributes, options);
|
||||
sinon.assert.calledOnce(kfetchStub);
|
||||
|
|
|
@ -41,7 +41,7 @@ describe('uiSettings/createOrUpgradeSavedConfig', function () {
|
|||
create: sinon.stub().callsFake(async (type, attributes, options = {}) => ({
|
||||
type,
|
||||
id: options.id,
|
||||
version: 1,
|
||||
version: 'foo',
|
||||
}))
|
||||
};
|
||||
|
||||
|
|
|
@ -66,7 +66,7 @@ export default function ({ getService }) {
|
|||
type: 'dashboard',
|
||||
id: 'a01b2f57-fcfd-4864-b735-09e28f0d815e',
|
||||
updated_at: resp.body.saved_objects[1].updated_at,
|
||||
version: 1,
|
||||
version: 'WzgsMV0=',
|
||||
attributes: {
|
||||
title: 'A great new dashboard'
|
||||
}
|
||||
|
@ -98,7 +98,7 @@ export default function ({ getService }) {
|
|||
type: 'visualization',
|
||||
id: 'dd7caf20-9efd-11e7-acb3-3dab96693fab',
|
||||
updated_at: resp.body.saved_objects[0].updated_at,
|
||||
version: 1,
|
||||
version: 'WzAsMV0=',
|
||||
attributes: {
|
||||
title: 'An existing visualization'
|
||||
}
|
||||
|
@ -107,7 +107,7 @@ export default function ({ getService }) {
|
|||
type: 'dashboard',
|
||||
id: 'a01b2f57-fcfd-4864-b735-09e28f0d815e',
|
||||
updated_at: resp.body.saved_objects[1].updated_at,
|
||||
version: 1,
|
||||
version: 'WzEsMV0=',
|
||||
attributes: {
|
||||
title: 'A great new dashboard'
|
||||
}
|
||||
|
|
|
@ -48,7 +48,7 @@ export default function ({ getService }) {
|
|||
id: resp.body.id,
|
||||
type: 'visualization',
|
||||
updated_at: resp.body.updated_at,
|
||||
version: 1,
|
||||
version: 'WzgsMV0=',
|
||||
attributes: {
|
||||
title: 'My favorite vis'
|
||||
}
|
||||
|
@ -86,7 +86,7 @@ export default function ({ getService }) {
|
|||
id: resp.body.id,
|
||||
type: 'visualization',
|
||||
updated_at: resp.body.updated_at,
|
||||
version: 1,
|
||||
version: 'WzAsMV0=',
|
||||
attributes: {
|
||||
title: 'My favorite vis'
|
||||
}
|
||||
|
|
|
@ -42,7 +42,7 @@ export default function ({ getService }) {
|
|||
{
|
||||
type: 'visualization',
|
||||
id: 'dd7caf20-9efd-11e7-acb3-3dab96693fab',
|
||||
version: 1,
|
||||
version: 'WzIsMV0=',
|
||||
attributes: {
|
||||
'title': 'Count of requests'
|
||||
}
|
||||
|
|
|
@ -48,7 +48,7 @@ export default function ({ getService }) {
|
|||
id: resp.body.id,
|
||||
type: 'visualization',
|
||||
updated_at: resp.body.updated_at,
|
||||
version: 2,
|
||||
version: 'WzgsMV0=',
|
||||
attributes: {
|
||||
title: 'My second favorite vis'
|
||||
}
|
||||
|
|
|
@ -101,7 +101,8 @@ export interface DatabaseSearchResponse<T> {
|
|||
_id: string;
|
||||
_score: number;
|
||||
_source: T;
|
||||
_version?: number;
|
||||
_seq_no?: number;
|
||||
_primary_term?: number;
|
||||
_explanation?: DatabaseExplanation;
|
||||
fields?: any;
|
||||
highlight?: any;
|
||||
|
@ -129,7 +130,8 @@ export interface DatabaseGetDocumentResponse<Source> {
|
|||
_index: string;
|
||||
_type: string;
|
||||
_id: string;
|
||||
_version: number;
|
||||
_seq_no: number;
|
||||
_primary_term: number;
|
||||
found: boolean;
|
||||
_source: Source;
|
||||
}
|
||||
|
@ -183,8 +185,8 @@ export interface DatabaseDeleteDocumentParams extends DatabaseGenericParams {
|
|||
refresh?: DatabaseRefresh;
|
||||
routing?: string;
|
||||
timeout?: string;
|
||||
version?: number;
|
||||
versionType?: DatabaseVersionType;
|
||||
ifSeqNo?: number;
|
||||
ifPrimaryTerm?: number;
|
||||
index: string;
|
||||
type: string;
|
||||
id: string;
|
||||
|
@ -195,7 +197,8 @@ export interface DatabaseIndexDocumentResponse {
|
|||
_index: string;
|
||||
_type: string;
|
||||
_id: string;
|
||||
_version: number;
|
||||
_seq_no: number;
|
||||
_primary_term: number;
|
||||
result: string;
|
||||
}
|
||||
|
||||
|
@ -204,7 +207,8 @@ export interface DatabaseUpdateDocumentResponse {
|
|||
_index: string;
|
||||
_type: string;
|
||||
_id: string;
|
||||
_version: number;
|
||||
_seq_no: number;
|
||||
_primary_term: number;
|
||||
result: string;
|
||||
}
|
||||
|
||||
|
@ -213,7 +217,8 @@ export interface DatabaseDeleteDocumentResponse {
|
|||
_index: string;
|
||||
_type: string;
|
||||
_id: string;
|
||||
_version: number;
|
||||
_seq_no: number;
|
||||
_primary_term: number;
|
||||
result: string;
|
||||
}
|
||||
|
||||
|
@ -226,8 +231,8 @@ export interface DatabaseIndexDocumentParams<T> extends DatabaseGenericParams {
|
|||
timeout?: string;
|
||||
timestamp?: Date | number;
|
||||
ttl?: string;
|
||||
version?: number;
|
||||
versionType?: DatabaseVersionType;
|
||||
ifSeqNo?: number;
|
||||
ifPrimaryTerm?: number;
|
||||
pipeline?: string;
|
||||
id?: string;
|
||||
index: string;
|
||||
|
@ -247,8 +252,8 @@ export interface DatabaseCreateDocumentParams extends DatabaseGenericParams {
|
|||
timeout?: string;
|
||||
timestamp?: Date | number;
|
||||
ttl?: string;
|
||||
version?: number;
|
||||
versionType?: DatabaseVersionType;
|
||||
ifSeqNo?: number;
|
||||
ifPrimaryTerm?: number;
|
||||
pipeline?: string;
|
||||
id?: string;
|
||||
index: string;
|
||||
|
@ -266,8 +271,8 @@ export interface DatabaseDeleteDocumentParams extends DatabaseGenericParams {
|
|||
refresh?: DatabaseRefresh;
|
||||
routing?: string;
|
||||
timeout?: string;
|
||||
version?: number;
|
||||
versionType?: DatabaseVersionType;
|
||||
ifSeqNo?: number;
|
||||
ifPrimaryTerm?: number;
|
||||
index: string;
|
||||
type: string;
|
||||
id: string;
|
||||
|
@ -283,8 +288,8 @@ export interface DatabaseGetParams extends DatabaseGenericParams {
|
|||
_source?: DatabaseNameList;
|
||||
_sourceExclude?: DatabaseNameList;
|
||||
_source_includes?: DatabaseNameList;
|
||||
version?: number;
|
||||
versionType?: DatabaseVersionType;
|
||||
ifSeqNo?: number;
|
||||
ifPrimaryTerm?: number;
|
||||
id: string;
|
||||
index: string;
|
||||
type: string;
|
||||
|
@ -292,7 +297,6 @@ export interface DatabaseGetParams extends DatabaseGenericParams {
|
|||
|
||||
export type DatabaseNameList = string | string[] | boolean;
|
||||
export type DatabaseRefresh = boolean | 'true' | 'false' | 'wait_for' | '';
|
||||
export type DatabaseVersionType = 'internal' | 'external' | 'external_gte' | 'force';
|
||||
export type ExpandWildcards = 'open' | 'closed' | 'none' | 'all';
|
||||
export type DefaultOperator = 'AND' | 'OR';
|
||||
export type DatabaseConflicts = 'abort' | 'proceed';
|
||||
|
@ -311,6 +315,7 @@ export interface DatabaseDeleteDocumentResponse {
|
|||
_index: string;
|
||||
_type: string;
|
||||
_id: string;
|
||||
_version: number;
|
||||
_seq_no: number;
|
||||
_primary_term: number;
|
||||
result: string;
|
||||
}
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
* or more contributor license agreements. Licensed under the Elastic License;
|
||||
* you may not use this file except in compliance with the Elastic License.
|
||||
*/
|
||||
import { get } from 'lodash';
|
||||
|
||||
import { FrameworkUser } from '../framework/adapter_types';
|
||||
import { internalAuthData } from './../framework/adapter_types';
|
||||
import {
|
||||
|
@ -114,43 +114,6 @@ export class KibanaDatabaseAdapter implements DatabaseAdapter {
|
|||
return result;
|
||||
}
|
||||
|
||||
private async fetchAllFromScroll<Source>(
|
||||
user: FrameworkUser,
|
||||
response: DatabaseSearchResponse<Source>,
|
||||
hits: DatabaseSearchResponse<Source>['hits']['hits'] = []
|
||||
): Promise<
|
||||
Array<{
|
||||
_index: string;
|
||||
_type: string;
|
||||
_id: string;
|
||||
_score: number;
|
||||
_source: Source;
|
||||
_version?: number;
|
||||
fields?: any;
|
||||
highlight?: any;
|
||||
inner_hits?: any;
|
||||
sort?: string[];
|
||||
}>
|
||||
> {
|
||||
const newHits = get(response, 'hits.hits', []);
|
||||
const scrollId = get(response, '_scroll_id');
|
||||
|
||||
if (newHits.length > 0) {
|
||||
hits.push(...newHits);
|
||||
|
||||
return this.callWithUser(user, 'scroll', {
|
||||
body: {
|
||||
scroll: '30s',
|
||||
scroll_id: scrollId,
|
||||
},
|
||||
}).then((innerResponse: DatabaseSearchResponse<Source>) => {
|
||||
return this.fetchAllFromScroll(user, innerResponse, hits);
|
||||
});
|
||||
}
|
||||
|
||||
return Promise.resolve(hits);
|
||||
}
|
||||
|
||||
private callWithUser(user: FrameworkUser, esMethod: string, options: any = {}): any {
|
||||
if (user.kind === 'authenticated') {
|
||||
return this.es.callWithRequest(
|
||||
|
|
|
@ -89,7 +89,7 @@
|
|||
"name": "version",
|
||||
"description": "The version number the source configuration was last persisted with",
|
||||
"args": [],
|
||||
"type": { "kind": "SCALAR", "name": "Float", "ofType": null },
|
||||
"type": { "kind": "SCALAR", "name": "String", "ofType": null },
|
||||
"isDeprecated": false,
|
||||
"deprecationReason": null
|
||||
},
|
||||
|
@ -430,6 +430,16 @@
|
|||
"enumValues": null,
|
||||
"possibleTypes": null
|
||||
},
|
||||
{
|
||||
"kind": "SCALAR",
|
||||
"name": "String",
|
||||
"description": "The `String` scalar type represents textual data, represented as UTF-8 character sequences. The String type is most often used by GraphQL to represent free-form human-readable text.",
|
||||
"fields": null,
|
||||
"inputFields": null,
|
||||
"interfaces": null,
|
||||
"enumValues": null,
|
||||
"possibleTypes": null
|
||||
},
|
||||
{
|
||||
"kind": "SCALAR",
|
||||
"name": "Float",
|
||||
|
@ -511,16 +521,6 @@
|
|||
"enumValues": null,
|
||||
"possibleTypes": null
|
||||
},
|
||||
{
|
||||
"kind": "SCALAR",
|
||||
"name": "String",
|
||||
"description": "The `String` scalar type represents textual data, represented as UTF-8 character sequences. The String type is most often used by GraphQL to represent free-form human-readable text.",
|
||||
"fields": null,
|
||||
"inputFields": null,
|
||||
"interfaces": null,
|
||||
"enumValues": null,
|
||||
"possibleTypes": null
|
||||
},
|
||||
{
|
||||
"kind": "OBJECT",
|
||||
"name": "InfraSourceFields",
|
||||
|
|
|
@ -19,7 +19,7 @@ export interface InfraSource {
|
|||
/** The id of the source */
|
||||
id: string;
|
||||
/** The version number the source configuration was last persisted with */
|
||||
version?: number | null;
|
||||
version?: string | null;
|
||||
/** The timestamp the source configuration was last persisted at */
|
||||
updatedAt?: number | null;
|
||||
/** The raw configuration of the source */
|
||||
|
|
|
@ -12,7 +12,7 @@ export const sourcesSchema = gql`
|
|||
"The id of the source"
|
||||
id: ID!
|
||||
"The version number the source configuration was last persisted with"
|
||||
version: Float
|
||||
version: String
|
||||
"The timestamp the source configuration was last persisted at"
|
||||
updatedAt: Float
|
||||
"The raw configuration of the source"
|
||||
|
|
|
@ -47,7 +47,7 @@ export interface InfraSource {
|
|||
/** The id of the source */
|
||||
id: string;
|
||||
/** The version number the source configuration was last persisted with */
|
||||
version?: number | null;
|
||||
version?: string | null;
|
||||
/** The timestamp the source configuration was last persisted at */
|
||||
updatedAt?: number | null;
|
||||
/** The raw configuration of the source */
|
||||
|
@ -606,7 +606,7 @@ export namespace InfraSourceResolvers {
|
|||
/** The id of the source */
|
||||
id?: IdResolver<string, TypeParent, Context>;
|
||||
/** The version number the source configuration was last persisted with */
|
||||
version?: VersionResolver<number | null, TypeParent, Context>;
|
||||
version?: VersionResolver<string | null, TypeParent, Context>;
|
||||
/** The timestamp the source configuration was last persisted at */
|
||||
updatedAt?: UpdatedAtResolver<number | null, TypeParent, Context>;
|
||||
/** The raw configuration of the source */
|
||||
|
@ -635,7 +635,7 @@ export namespace InfraSourceResolvers {
|
|||
Context
|
||||
>;
|
||||
export type VersionResolver<
|
||||
R = number | null,
|
||||
R = string | null,
|
||||
Parent = InfraSource,
|
||||
Context = InfraContext
|
||||
> = Resolver<R, Parent, Context>;
|
||||
|
|
|
@ -14,7 +14,7 @@ describe('the InfraSources lib', () => {
|
|||
configuration: createMockStaticConfiguration({}),
|
||||
savedObjects: createMockSavedObjectsService({
|
||||
id: 'TEST_ID',
|
||||
version: 1,
|
||||
version: 'foo',
|
||||
updated_at: '2000-01-01T00:00:00.000Z',
|
||||
attributes: {
|
||||
metricAlias: 'METRIC_ALIAS',
|
||||
|
@ -34,7 +34,7 @@ describe('the InfraSources lib', () => {
|
|||
|
||||
expect(await sourcesLib.getSourceConfiguration(request, 'TEST_ID')).toMatchObject({
|
||||
id: 'TEST_ID',
|
||||
version: 1,
|
||||
version: 'foo',
|
||||
updatedAt: 946684800000,
|
||||
configuration: {
|
||||
metricAlias: 'METRIC_ALIAS',
|
||||
|
@ -66,7 +66,7 @@ describe('the InfraSources lib', () => {
|
|||
}),
|
||||
savedObjects: createMockSavedObjectsService({
|
||||
id: 'TEST_ID',
|
||||
version: 1,
|
||||
version: 'foo',
|
||||
updated_at: '2000-01-01T00:00:00.000Z',
|
||||
attributes: {
|
||||
fields: {
|
||||
|
@ -80,7 +80,7 @@ describe('the InfraSources lib', () => {
|
|||
|
||||
expect(await sourcesLib.getSourceConfiguration(request, 'TEST_ID')).toMatchObject({
|
||||
id: 'TEST_ID',
|
||||
version: 1,
|
||||
version: 'foo',
|
||||
updatedAt: 946684800000,
|
||||
configuration: {
|
||||
metricAlias: 'METRIC_ALIAS',
|
||||
|
@ -101,7 +101,7 @@ describe('the InfraSources lib', () => {
|
|||
configuration: createMockStaticConfiguration({}),
|
||||
savedObjects: createMockSavedObjectsService({
|
||||
id: 'TEST_ID',
|
||||
version: 1,
|
||||
version: 'foo',
|
||||
updated_at: '2000-01-01T00:00:00.000Z',
|
||||
attributes: {},
|
||||
}),
|
||||
|
@ -111,7 +111,7 @@ describe('the InfraSources lib', () => {
|
|||
|
||||
expect(await sourcesLib.getSourceConfiguration(request, 'TEST_ID')).toMatchObject({
|
||||
id: 'TEST_ID',
|
||||
version: 1,
|
||||
version: 'foo',
|
||||
updatedAt: 946684800000,
|
||||
configuration: {
|
||||
metricAlias: expect.any(String),
|
||||
|
|
|
@ -51,7 +51,7 @@ export const InfraSavedSourceConfigurationRuntimeType = runtimeTypes.intersectio
|
|||
attributes: PartialInfraSourceConfigurationRuntimeType,
|
||||
}),
|
||||
runtimeTypes.partial({
|
||||
version: runtimeTypes.number,
|
||||
version: runtimeTypes.string,
|
||||
updated_at: TimestampFromString,
|
||||
}),
|
||||
]);
|
||||
|
|
|
@ -17,7 +17,7 @@ describe('field format map', function () {
|
|||
const indexPatternSavedObject = {
|
||||
id: 'logstash-*',
|
||||
type: 'index-pattern',
|
||||
version: 4,
|
||||
version: 'abc',
|
||||
attributes: {
|
||||
title: 'logstash-*',
|
||||
timeFieldName: '@timestamp',
|
||||
|
|
|
@ -18,7 +18,8 @@ ClientMock.prototype.index = function (params = {}) {
|
|||
_index: params.index || 'index',
|
||||
_type: params.type || constants.DEFAULT_SETTING_DOCTYPE,
|
||||
_id: params.id || uniqueId('testDoc'),
|
||||
_version: 1,
|
||||
_seq_no: 1,
|
||||
_primary_term: 1,
|
||||
_shards: { total: shardCount, successful: shardCount, failed: 0 },
|
||||
created: true
|
||||
});
|
||||
|
@ -53,7 +54,8 @@ ClientMock.prototype.get = function (params = {}, source = {}) {
|
|||
_index: params.index || 'index',
|
||||
_type: params.type || constants.DEFAULT_SETTING_DOCTYPE,
|
||||
_id: params.id || 'AVRPRLnlp7Ur1SZXfT-T',
|
||||
_version: params.version || 1,
|
||||
_seq_no: params._seq_no || 1,
|
||||
_primary_term: params._primary_term || 1,
|
||||
found: true,
|
||||
_source: _source
|
||||
});
|
||||
|
@ -65,7 +67,8 @@ ClientMock.prototype.search = function (params = {}, count = 5, source = {}) {
|
|||
_index: params.index || 'index',
|
||||
_type: params.type || constants.DEFAULT_SETTING_DOCTYPE,
|
||||
_id: uniqueId('documentId'),
|
||||
_version: random(1, 5),
|
||||
_seq_no: random(1, 5),
|
||||
_primar_term: random(1, 5),
|
||||
_score: null,
|
||||
_source: {
|
||||
created_at: new Date().toString(),
|
||||
|
@ -96,7 +99,8 @@ ClientMock.prototype.update = function (params = {}) {
|
|||
_index: params.index || 'index',
|
||||
_type: params.type || constants.DEFAULT_SETTING_DOCTYPE,
|
||||
_id: params.id || uniqueId('testDoc'),
|
||||
_version: params.version + 1 || 2,
|
||||
_seq_no: params.if_seq_no + 1 || 2,
|
||||
_primary_term: params.if_primary_term + 1 || 2,
|
||||
_shards: { total: shardCount, successful: shardCount, failed: 0 },
|
||||
created: true
|
||||
});
|
||||
|
|
|
@ -135,7 +135,8 @@ describe('Job Class', function () {
|
|||
expect(jobDoc).to.have.property('id');
|
||||
expect(jobDoc).to.have.property('index');
|
||||
expect(jobDoc).to.have.property('type');
|
||||
expect(jobDoc).to.have.property('version');
|
||||
expect(jobDoc).to.have.property('_seq_no');
|
||||
expect(jobDoc).to.have.property('_primary_term');
|
||||
done();
|
||||
} catch (e) {
|
||||
done(e);
|
||||
|
@ -383,7 +384,8 @@ describe('Job Class', function () {
|
|||
expect(doc).to.have.property('index', index);
|
||||
expect(doc).to.have.property('type', jobDoc.type);
|
||||
expect(doc).to.have.property('id', jobDoc.id);
|
||||
expect(doc).to.have.property('version', jobDoc.version);
|
||||
expect(doc).to.have.property('_seq_no', jobDoc._seq_no);
|
||||
expect(doc).to.have.property('_primary_term', jobDoc._primary_term);
|
||||
expect(doc).to.have.property('created_by', defaultCreatedBy);
|
||||
|
||||
expect(doc).to.have.property('payload');
|
||||
|
|
|
@ -336,11 +336,6 @@ describe('Worker class', function () {
|
|||
searchStub = sinon.stub(mockQueue.client, 'search').callsFake(() => Promise.resolve({ hits: { hits: [] } }));
|
||||
});
|
||||
|
||||
it('should query with version', function () {
|
||||
const params = getSearchParams();
|
||||
expect(params).to.have.property('version', true);
|
||||
});
|
||||
|
||||
it('should query by default doctype', function () {
|
||||
const params = getSearchParams();
|
||||
expect(params).to.have.property('type', constants.DEFAULT_SETTING_DOCTYPE);
|
||||
|
@ -367,6 +362,11 @@ describe('Worker class', function () {
|
|||
clock.restore();
|
||||
});
|
||||
|
||||
it('should query with seq_no_primary_term', function () {
|
||||
const { body } = getSearchParams(jobtype);
|
||||
expect(body).to.have.property('seq_no_primary_term', true);
|
||||
});
|
||||
|
||||
it('should filter unwanted source data', function () {
|
||||
const excludedFields = [ 'output.content' ];
|
||||
const { body } = getSearchParams(jobtype);
|
||||
|
@ -432,7 +432,6 @@ describe('Worker class', function () {
|
|||
index: 'myIndex',
|
||||
type: 'test',
|
||||
id: 12345,
|
||||
version: 3
|
||||
};
|
||||
return mockQueue.client.get(params)
|
||||
.then((jobDoc) => {
|
||||
|
@ -446,13 +445,14 @@ describe('Worker class', function () {
|
|||
clock.restore();
|
||||
});
|
||||
|
||||
it('should use version on update', function () {
|
||||
it('should use seqNo and primaryTerm on update', function () {
|
||||
worker._claimJob(job);
|
||||
const query = updateSpy.firstCall.args[0];
|
||||
expect(query).to.have.property('index', job._index);
|
||||
expect(query).to.have.property('type', job._type);
|
||||
expect(query).to.have.property('id', job._id);
|
||||
expect(query).to.have.property('version', job._version);
|
||||
expect(query).to.have.property('if_seq_no', job._seq_no);
|
||||
expect(query).to.have.property('if_primary_term', job._primary_term);
|
||||
});
|
||||
|
||||
it('should increment the job attempts', function () {
|
||||
|
@ -500,7 +500,7 @@ describe('Worker class', function () {
|
|||
expect(msg).to.equal(false);
|
||||
});
|
||||
|
||||
it('should reject the promise on version errors', function () {
|
||||
it('should reject the promise on conflict errors', function () {
|
||||
mockQueue.client.update.restore();
|
||||
sinon.stub(mockQueue.client, 'update').returns(Promise.reject({ statusCode: 409 }));
|
||||
return worker._claimJob(job)
|
||||
|
@ -524,7 +524,8 @@ describe('Worker class', function () {
|
|||
_index: 'myIndex',
|
||||
_type: 'test',
|
||||
_id: 12345,
|
||||
_version: 3,
|
||||
_seq_no: 3,
|
||||
_primary_term: 3,
|
||||
found: true,
|
||||
_source: {
|
||||
jobtype: 'jobtype',
|
||||
|
@ -608,13 +609,14 @@ describe('Worker class', function () {
|
|||
clock.restore();
|
||||
});
|
||||
|
||||
it('should use version on update', function () {
|
||||
it('should use _seq_no and _primary_term on update', function () {
|
||||
worker._failJob(job);
|
||||
const query = updateSpy.firstCall.args[0];
|
||||
expect(query).to.have.property('index', job._index);
|
||||
expect(query).to.have.property('type', job._type);
|
||||
expect(query).to.have.property('id', job._id);
|
||||
expect(query).to.have.property('version', job._version);
|
||||
expect(query).to.have.property('if_seq_no', job._seq_no);
|
||||
expect(query).to.have.property('if_primary_term', job._primary_term);
|
||||
});
|
||||
|
||||
it('should set status to failed', function () {
|
||||
|
@ -631,7 +633,7 @@ describe('Worker class', function () {
|
|||
expect(doc.output).to.have.property('content', msg);
|
||||
});
|
||||
|
||||
it('should return true on version mismatch errors', function () {
|
||||
it('should return true on conflict errors', function () {
|
||||
mockQueue.client.update.restore();
|
||||
sinon.stub(mockQueue.client, 'update').returns(Promise.reject({ statusCode: 409 }));
|
||||
return worker._failJob(job)
|
||||
|
@ -735,7 +737,8 @@ describe('Worker class', function () {
|
|||
expect(query).to.have.property('index', job._index);
|
||||
expect(query).to.have.property('type', job._type);
|
||||
expect(query).to.have.property('id', job._id);
|
||||
expect(query).to.have.property('version', job._version);
|
||||
expect(query).to.have.property('if_seq_no', job._seq_no);
|
||||
expect(query).to.have.property('if_primary_term', job._primary_term);
|
||||
expect(query.body.doc).to.have.property('output');
|
||||
expect(query.body.doc.output).to.have.property('content_type', false);
|
||||
expect(query.body.doc.output).to.have.property('content', payload);
|
||||
|
|
|
@ -82,7 +82,8 @@ export class Job extends events.EventEmitter {
|
|||
id: doc._id,
|
||||
type: doc._type,
|
||||
index: doc._index,
|
||||
version: doc._version,
|
||||
_seq_no: doc._seq_no,
|
||||
_primary_term: doc._primary_term,
|
||||
};
|
||||
this.debug(`Job created in index ${this.index}`);
|
||||
|
||||
|
@ -118,7 +119,8 @@ export class Job extends events.EventEmitter {
|
|||
index: doc._index,
|
||||
id: doc._id,
|
||||
type: doc._type,
|
||||
version: doc._version,
|
||||
_seq_no: doc._seq_no,
|
||||
_primary_term: doc._primary_term,
|
||||
});
|
||||
});
|
||||
}
|
||||
|
|
|
@ -130,7 +130,8 @@ export class Worker extends events.EventEmitter {
|
|||
index: job._index,
|
||||
type: job._type,
|
||||
id: job._id,
|
||||
version: job._version,
|
||||
if_seq_no: job._seq_no,
|
||||
if_primary_term: job._primary_term,
|
||||
body: { doc }
|
||||
})
|
||||
.then((response) => {
|
||||
|
@ -167,7 +168,8 @@ export class Worker extends events.EventEmitter {
|
|||
index: job._index,
|
||||
type: job._type,
|
||||
id: job._id,
|
||||
version: job._version,
|
||||
if_seq_no: job._seq_no,
|
||||
if_primary_term: job._primary_term,
|
||||
body: { doc }
|
||||
})
|
||||
.then(() => true)
|
||||
|
@ -244,7 +246,8 @@ export class Worker extends events.EventEmitter {
|
|||
index: job._index,
|
||||
type: job._type,
|
||||
id: job._id,
|
||||
version: job._version,
|
||||
if_seq_no: job._seq_no,
|
||||
if_primary_term: job._primary_term,
|
||||
body: { doc }
|
||||
})
|
||||
.then(() => {
|
||||
|
@ -351,6 +354,7 @@ export class Worker extends events.EventEmitter {
|
|||
_getPendingJobs() {
|
||||
const nowTime = moment().toISOString();
|
||||
const query = {
|
||||
seq_no_primary_term: true,
|
||||
_source: {
|
||||
excludes: [ 'output.content' ]
|
||||
},
|
||||
|
@ -385,7 +389,6 @@ export class Worker extends events.EventEmitter {
|
|||
return this.client.search({
|
||||
index: `${this.queue.index}-*`,
|
||||
type: this.doctype,
|
||||
version: true,
|
||||
body: query
|
||||
})
|
||||
.then((results) => {
|
||||
|
|
|
@ -215,7 +215,7 @@ export class SpacesSavedObjectsClient implements SavedObjectsClient {
|
|||
* @param {string} type
|
||||
* @param {string} id
|
||||
* @param {object} [options={}]
|
||||
* @property {integer} options.version - ensures version matches that of persisted object
|
||||
* @property {string} options.version - ensures version matches that of persisted object
|
||||
* @property {string} [options.namespace]
|
||||
* @returns {promise}
|
||||
*/
|
||||
|
|
|
@ -20,7 +20,8 @@ const getMockTaskInstance = () => ({
|
|||
const getMockConcreteTaskInstance = () => {
|
||||
const concrete: {
|
||||
id: string;
|
||||
version: number;
|
||||
sequenceNumber: number;
|
||||
primaryTerm: number;
|
||||
attempts: number;
|
||||
status: TaskStatus;
|
||||
runAt: Date;
|
||||
|
@ -30,7 +31,8 @@ const getMockConcreteTaskInstance = () => {
|
|||
params: any;
|
||||
} = {
|
||||
id: 'hy8o99o83',
|
||||
version: 1,
|
||||
sequenceNumber: 1,
|
||||
primaryTerm: 1,
|
||||
attempts: 0,
|
||||
status: 'idle',
|
||||
runAt: new Date(moment('2018-09-18T05:33:09.588Z').valueOf()),
|
||||
|
@ -150,12 +152,13 @@ Object {
|
|||
"params": Object {
|
||||
"abc": "def",
|
||||
},
|
||||
"primaryTerm": 1,
|
||||
"runAt": 2018-09-18T05:33:09.588Z,
|
||||
"scheduledAt": 2018-09-18T05:33:09.588Z,
|
||||
"sequenceNumber": 1,
|
||||
"state": Object {},
|
||||
"status": "idle",
|
||||
"taskType": "nice_task",
|
||||
"version": 1,
|
||||
},
|
||||
}
|
||||
`);
|
||||
|
|
|
@ -212,9 +212,14 @@ export interface ConcreteTaskInstance extends TaskInstance {
|
|||
id: string;
|
||||
|
||||
/**
|
||||
* The version of the Elaticsearch document.
|
||||
* The sequence number from the Elaticsearch document.
|
||||
*/
|
||||
version: number;
|
||||
sequenceNumber: number;
|
||||
|
||||
/**
|
||||
* The primary term from the Elaticsearch document.
|
||||
*/
|
||||
primaryTerm: number;
|
||||
|
||||
/**
|
||||
* The date and time that this task was originally scheduled. This is used
|
||||
|
|
|
@ -230,7 +230,8 @@ describe('TaskManagerRunner', () => {
|
|||
{
|
||||
id: 'foo',
|
||||
taskType: 'bar',
|
||||
version: 32,
|
||||
sequenceNumber: 32,
|
||||
primaryTerm: 32,
|
||||
runAt: new Date(),
|
||||
scheduledAt: new Date(),
|
||||
attempts: 0,
|
||||
|
|
|
@ -86,7 +86,8 @@ describe('TaskStore', () => {
|
|||
callCluster.withArgs('index').returns(
|
||||
Promise.resolve({
|
||||
_id: 'testid',
|
||||
_version: 3344,
|
||||
_seq_no: 3344,
|
||||
_primary_term: 3344,
|
||||
})
|
||||
);
|
||||
callCluster.withArgs('indices.getTemplate').returns(Promise.resolve({ tasky: {} }));
|
||||
|
@ -138,7 +139,8 @@ describe('TaskStore', () => {
|
|||
|
||||
expect(result).toMatchObject({
|
||||
...task,
|
||||
version: 3344,
|
||||
sequenceNumber: 3344,
|
||||
primaryTerm: 3344,
|
||||
id: 'testid',
|
||||
});
|
||||
});
|
||||
|
@ -307,7 +309,8 @@ describe('TaskStore', () => {
|
|||
status: 'idle',
|
||||
taskType: 'foo',
|
||||
user: 'jimbo',
|
||||
version: undefined,
|
||||
sequenceNumber: undefined,
|
||||
primaryTerm: undefined,
|
||||
},
|
||||
{
|
||||
attempts: 2,
|
||||
|
@ -320,7 +323,8 @@ describe('TaskStore', () => {
|
|||
status: 'running',
|
||||
taskType: 'bar',
|
||||
user: 'dabo',
|
||||
version: undefined,
|
||||
sequenceNumber: undefined,
|
||||
primaryTerm: undefined,
|
||||
},
|
||||
],
|
||||
searchAfter: ['b', 2],
|
||||
|
@ -401,7 +405,7 @@ describe('TaskStore', () => {
|
|||
},
|
||||
size: 10,
|
||||
sort: { 'task.runAt': { order: 'asc' } },
|
||||
version: true,
|
||||
seq_no_primary_term: true,
|
||||
},
|
||||
index,
|
||||
type: '_doc',
|
||||
|
@ -462,7 +466,8 @@ describe('TaskStore', () => {
|
|||
status: 'idle',
|
||||
taskType: 'foo',
|
||||
user: 'jimbo',
|
||||
version: undefined,
|
||||
sequenceNumber: undefined,
|
||||
primaryTerm: undefined,
|
||||
},
|
||||
{
|
||||
attempts: 2,
|
||||
|
@ -475,7 +480,8 @@ describe('TaskStore', () => {
|
|||
status: 'running',
|
||||
taskType: 'bar',
|
||||
user: 'dabo',
|
||||
version: undefined,
|
||||
sequenceNumber: undefined,
|
||||
primaryTerm: undefined,
|
||||
},
|
||||
]);
|
||||
});
|
||||
|
@ -491,12 +497,17 @@ describe('TaskStore', () => {
|
|||
params: { hello: 'world' },
|
||||
state: { foo: 'bar' },
|
||||
taskType: 'report',
|
||||
version: 2,
|
||||
sequenceNumber: 2,
|
||||
primaryTerm: 2,
|
||||
attempts: 3,
|
||||
status: 'idle' as TaskStatus,
|
||||
};
|
||||
|
||||
const callCluster = sinon.spy(async () => ({ _version: task.version + 1 }));
|
||||
const callCluster = sinon.spy(async () => ({
|
||||
_seq_no: task.sequenceNumber + 1,
|
||||
_primary_term: task.primaryTerm + 1,
|
||||
}));
|
||||
|
||||
const store = new TaskStore({
|
||||
callCluster,
|
||||
getKibanaUuid,
|
||||
|
@ -515,12 +526,13 @@ describe('TaskStore', () => {
|
|||
id: task.id,
|
||||
index: 'tasky',
|
||||
type: '_doc',
|
||||
version: 2,
|
||||
if_seq_no: 2,
|
||||
if_primary_term: 2,
|
||||
refresh: true,
|
||||
body: {
|
||||
doc: {
|
||||
task: {
|
||||
...['id', 'version'].reduce((acc, prop) => _.omit(acc, prop), task),
|
||||
..._.omit(task, ['id', 'sequenceNumber', 'primaryTerm']),
|
||||
params: JSON.stringify(task.params),
|
||||
state: JSON.stringify(task.state),
|
||||
},
|
||||
|
@ -528,7 +540,11 @@ describe('TaskStore', () => {
|
|||
},
|
||||
});
|
||||
|
||||
expect(result).toEqual({ ...task, version: 3 });
|
||||
expect(result).toEqual({
|
||||
...task,
|
||||
sequenceNumber: 3,
|
||||
primaryTerm: 3,
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
|
@ -539,7 +555,8 @@ describe('TaskStore', () => {
|
|||
Promise.resolve({
|
||||
_index: 'myindex',
|
||||
_id: id,
|
||||
_version: 32,
|
||||
_seq_no: 32,
|
||||
_primary_term: 32,
|
||||
result: 'deleted',
|
||||
})
|
||||
);
|
||||
|
@ -559,7 +576,8 @@ describe('TaskStore', () => {
|
|||
expect(result).toEqual({
|
||||
id,
|
||||
index: 'myindex',
|
||||
version: 32,
|
||||
sequenceNumber: 32,
|
||||
primaryTerm: 32,
|
||||
result: 'deleted',
|
||||
});
|
||||
|
||||
|
|
|
@ -40,7 +40,8 @@ export interface FetchResult {
|
|||
export interface RemoveResult {
|
||||
index: string;
|
||||
id: string;
|
||||
version: string;
|
||||
sequenceNumber: number;
|
||||
primaryTerm: number;
|
||||
result: string;
|
||||
}
|
||||
|
||||
|
@ -49,7 +50,8 @@ export interface RawTaskDoc {
|
|||
_id: string;
|
||||
_index: string;
|
||||
_type: string;
|
||||
_version: number;
|
||||
_seq_no: number;
|
||||
_primary_term: number;
|
||||
_source: {
|
||||
type: string;
|
||||
kibana: {
|
||||
|
@ -255,7 +257,8 @@ export class TaskStore {
|
|||
return {
|
||||
...taskInstance,
|
||||
id: result._id,
|
||||
version: result._version,
|
||||
sequenceNumber: result._seq_no,
|
||||
primaryTerm: result._primary_term,
|
||||
attempts: 0,
|
||||
status: task.status,
|
||||
scheduledAt: task.scheduledAt,
|
||||
|
@ -303,7 +306,7 @@ export class TaskStore {
|
|||
},
|
||||
size: 10,
|
||||
sort: { 'task.runAt': { order: 'asc' } },
|
||||
version: true,
|
||||
seq_no_primary_term: true,
|
||||
});
|
||||
|
||||
return docs;
|
||||
|
@ -319,14 +322,15 @@ export class TaskStore {
|
|||
public async update(doc: ConcreteTaskInstance): Promise<ConcreteTaskInstance> {
|
||||
const rawDoc = taskDocToRaw(doc, this);
|
||||
|
||||
const { _version } = await this.callCluster('update', {
|
||||
const result = await this.callCluster('update', {
|
||||
body: {
|
||||
doc: rawDoc._source,
|
||||
},
|
||||
id: doc.id,
|
||||
index: this.index,
|
||||
type: DOC_TYPE,
|
||||
version: doc.version,
|
||||
if_seq_no: doc.sequenceNumber,
|
||||
if_primary_term: doc.primaryTerm,
|
||||
// The refresh is important so that if we immediately look for work,
|
||||
// we don't pick up this task.
|
||||
refresh: true,
|
||||
|
@ -334,7 +338,8 @@ export class TaskStore {
|
|||
|
||||
return {
|
||||
...doc,
|
||||
version: _version,
|
||||
sequenceNumber: result._seq_no,
|
||||
primaryTerm: result._primary_term,
|
||||
};
|
||||
}
|
||||
|
||||
|
@ -357,7 +362,8 @@ export class TaskStore {
|
|||
return {
|
||||
index: result._index,
|
||||
id: result._id,
|
||||
version: result._version,
|
||||
sequenceNumber: result._seq_no,
|
||||
primaryTerm: result._primary_term,
|
||||
result: result.result,
|
||||
};
|
||||
}
|
||||
|
@ -415,7 +421,8 @@ function rawSource(doc: TaskInstance, store: TaskStore) {
|
|||
};
|
||||
|
||||
delete (source as any).id;
|
||||
delete (source as any).version;
|
||||
delete (source as any).sequenceNumber;
|
||||
delete (source as any).primaryTerm;
|
||||
delete (source as any).type;
|
||||
|
||||
return {
|
||||
|
@ -438,7 +445,8 @@ function taskDocToRaw(doc: ConcreteTaskInstance, store: TaskStore): RawTaskDoc {
|
|||
_index: store.index,
|
||||
_source: { type, task, kibana },
|
||||
_type: DOC_TYPE,
|
||||
_version: doc.version,
|
||||
_seq_no: doc.sequenceNumber,
|
||||
_primary_term: doc.primaryTerm,
|
||||
};
|
||||
}
|
||||
|
||||
|
@ -446,7 +454,8 @@ function rawToTaskDoc(doc: RawTaskDoc): ConcreteTaskInstance {
|
|||
return {
|
||||
...doc._source.task,
|
||||
id: doc._id,
|
||||
version: doc._version,
|
||||
sequenceNumber: doc._seq_no,
|
||||
primaryTerm: doc._primary_term,
|
||||
params: parseJSONField(doc._source.task.params, 'params', doc),
|
||||
state: parseJSONField(doc._source.task.state, 'state', doc),
|
||||
};
|
||||
|
|
|
@ -119,7 +119,7 @@ describe('ReindexActions', () => {
|
|||
type: REINDEX_OP_TYPE,
|
||||
id: '9',
|
||||
attributes: { indexName: 'hi', locked: moment().format() },
|
||||
version: 1,
|
||||
version: 'foo',
|
||||
} as ReindexSavedObject,
|
||||
{ newIndexName: 'test' }
|
||||
);
|
||||
|
@ -129,7 +129,7 @@ describe('ReindexActions', () => {
|
|||
expect(args[1]).toEqual('9');
|
||||
expect(args[2].indexName).toEqual('hi');
|
||||
expect(args[2].newIndexName).toEqual('test');
|
||||
expect(args[3]).toEqual({ version: 1 });
|
||||
expect(args[3]).toEqual({ version: 'foo' });
|
||||
});
|
||||
|
||||
it('throws if the reindexOp is not locked', async () => {
|
||||
|
@ -139,7 +139,7 @@ describe('ReindexActions', () => {
|
|||
type: REINDEX_OP_TYPE,
|
||||
id: '10',
|
||||
attributes: { indexName: 'hi', locked: null },
|
||||
version: 1,
|
||||
version: 'foo',
|
||||
} as ReindexSavedObject,
|
||||
{ newIndexName: 'test' }
|
||||
)
|
||||
|
|
|
@ -73,7 +73,7 @@ const sourcesTests: KbnTestProvider = ({ getService }) => {
|
|||
const { version, updatedAt, configuration, status } =
|
||||
response.data && response.data.createSource.source;
|
||||
|
||||
expect(version).to.be.greaterThan(0);
|
||||
expect(version).to.be.a('string');
|
||||
expect(updatedAt).to.be.greaterThan(0);
|
||||
expect(configuration.name).to.be('NAME');
|
||||
expect(configuration.description).to.be('DESCRIPTION');
|
||||
|
@ -102,7 +102,7 @@ const sourcesTests: KbnTestProvider = ({ getService }) => {
|
|||
const { version, updatedAt, configuration, status } =
|
||||
response.data && response.data.createSource.source;
|
||||
|
||||
expect(version).to.be.greaterThan(0);
|
||||
expect(version).to.be.a('string');
|
||||
expect(updatedAt).to.be.greaterThan(0);
|
||||
expect(configuration.name).to.be('NAME');
|
||||
expect(configuration.description).to.be('');
|
||||
|
@ -163,7 +163,7 @@ const sourcesTests: KbnTestProvider = ({ getService }) => {
|
|||
|
||||
const { version } = creationResponse.data && creationResponse.data.createSource.source;
|
||||
|
||||
expect(version).to.be.greaterThan(0);
|
||||
expect(version).to.be.a('string');
|
||||
|
||||
const deletionResponse = await client.mutate<any>({
|
||||
mutation: deleteSourceMutation,
|
||||
|
@ -193,7 +193,7 @@ const sourcesTests: KbnTestProvider = ({ getService }) => {
|
|||
const { version: initialVersion, updatedAt: createdAt } =
|
||||
creationResponse.data && creationResponse.data.createSource.source;
|
||||
|
||||
expect(initialVersion).to.be.greaterThan(0);
|
||||
expect(initialVersion).to.be.a('string');
|
||||
expect(createdAt).to.be.greaterThan(0);
|
||||
|
||||
const updateResponse = await client.mutate<any>({
|
||||
|
@ -233,7 +233,8 @@ const sourcesTests: KbnTestProvider = ({ getService }) => {
|
|||
const { version, updatedAt, configuration, status } =
|
||||
updateResponse.data && updateResponse.data.updateSource.source;
|
||||
|
||||
expect(version).to.be.greaterThan(initialVersion);
|
||||
expect(version).to.be.a('string');
|
||||
expect(version).to.not.be(initialVersion);
|
||||
expect(updatedAt).to.be.greaterThan(createdAt);
|
||||
expect(configuration.name).to.be('UPDATED_NAME');
|
||||
expect(configuration.description).to.be('UPDATED_DESCRIPTION');
|
||||
|
@ -262,7 +263,7 @@ const sourcesTests: KbnTestProvider = ({ getService }) => {
|
|||
const { version: initialVersion, updatedAt: createdAt } =
|
||||
creationResponse.data && creationResponse.data.createSource.source;
|
||||
|
||||
expect(initialVersion).to.be.greaterThan(0);
|
||||
expect(initialVersion).to.be.a('string');
|
||||
expect(createdAt).to.be.greaterThan(0);
|
||||
|
||||
const updateResponse = await client.mutate<any>({
|
||||
|
@ -282,7 +283,8 @@ const sourcesTests: KbnTestProvider = ({ getService }) => {
|
|||
const { version, updatedAt, configuration, status } =
|
||||
updateResponse.data && updateResponse.data.updateSource.source;
|
||||
|
||||
expect(version).to.be.greaterThan(initialVersion);
|
||||
expect(version).to.be.a('string');
|
||||
expect(version).to.not.be(initialVersion);
|
||||
expect(updatedAt).to.be.greaterThan(createdAt);
|
||||
expect(configuration.metricAlias).to.be('metricbeat-**');
|
||||
expect(configuration.logAlias).to.be('filebeat-*,kibana_sample_data_logs*');
|
||||
|
@ -304,7 +306,7 @@ const sourcesTests: KbnTestProvider = ({ getService }) => {
|
|||
const { version: initialVersion, updatedAt: createdAt } =
|
||||
creationResponse.data && creationResponse.data.createSource.source;
|
||||
|
||||
expect(initialVersion).to.be.greaterThan(0);
|
||||
expect(initialVersion).to.be.a('string');
|
||||
expect(createdAt).to.be.greaterThan(0);
|
||||
|
||||
const updateResponse = await client.mutate<any>({
|
||||
|
@ -324,7 +326,8 @@ const sourcesTests: KbnTestProvider = ({ getService }) => {
|
|||
const { version, updatedAt, configuration } =
|
||||
updateResponse.data && updateResponse.data.updateSource.source;
|
||||
|
||||
expect(version).to.be.greaterThan(initialVersion);
|
||||
expect(version).to.be.a('string');
|
||||
expect(version).to.not.be(initialVersion);
|
||||
expect(updatedAt).to.be.greaterThan(createdAt);
|
||||
expect(configuration.fields.container).to.be('UPDATED_CONTAINER');
|
||||
expect(configuration.fields.host).to.be('host.name');
|
||||
|
|
|
@ -93,7 +93,7 @@ export function bulkCreateTestSuiteFactory(es: any, esArchiver: any, supertest:
|
|||
type: 'dashboard',
|
||||
id: `${getIdPrefix(spaceId)}a01b2f57-fcfd-4864-b735-09e28f0d815e`,
|
||||
updated_at: resp.body.saved_objects[1].updated_at,
|
||||
version: 1,
|
||||
version: resp.body.saved_objects[1].version,
|
||||
attributes: {
|
||||
title: 'A great new dashboard',
|
||||
},
|
||||
|
@ -102,7 +102,7 @@ export function bulkCreateTestSuiteFactory(es: any, esArchiver: any, supertest:
|
|||
type: 'globaltype',
|
||||
id: `05976c65-1145-4858-bbf0-d225cc78a06e`,
|
||||
updated_at: resp.body.saved_objects[2].updated_at,
|
||||
version: 1,
|
||||
version: resp.body.saved_objects[2].version,
|
||||
attributes: {
|
||||
name: 'A new globaltype object',
|
||||
},
|
||||
|
|
|
@ -69,7 +69,7 @@ export function createTestSuiteFactory(es: any, esArchiver: any, supertest: Supe
|
|||
id: resp.body.id,
|
||||
type: spaceAwareType,
|
||||
updated_at: resp.body.updated_at,
|
||||
version: 1,
|
||||
version: resp.body.version,
|
||||
attributes: {
|
||||
title: 'My favorite vis',
|
||||
},
|
||||
|
@ -109,7 +109,7 @@ export function createTestSuiteFactory(es: any, esArchiver: any, supertest: Supe
|
|||
id: resp.body.id,
|
||||
type: notSpaceAwareType,
|
||||
updated_at: resp.body.updated_at,
|
||||
version: 1,
|
||||
version: resp.body.version,
|
||||
attributes: {
|
||||
name: `Can't be contained to a space`,
|
||||
},
|
||||
|
|
|
@ -72,7 +72,7 @@ export function findTestSuiteFactory(esArchiver: any, supertest: SuperTest<any>)
|
|||
{
|
||||
type: 'globaltype',
|
||||
id: `8121a00-8efd-21e7-1cb3-34ab966434445`,
|
||||
version: 1,
|
||||
version: resp.body.saved_objects[0].version,
|
||||
attributes: {
|
||||
name: 'My favorite global object',
|
||||
},
|
||||
|
@ -104,7 +104,7 @@ export function findTestSuiteFactory(esArchiver: any, supertest: SuperTest<any>)
|
|||
{
|
||||
type: 'visualization',
|
||||
id: `${getIdPrefix(spaceId)}dd7caf20-9efd-11e7-acb3-3dab96693fab`,
|
||||
version: 1,
|
||||
version: resp.body.saved_objects[0].version,
|
||||
attributes: {
|
||||
title: 'Count of requests',
|
||||
},
|
||||
|
|
|
@ -83,7 +83,7 @@ export function updateTestSuiteFactory(esArchiver: any, supertest: SuperTest<any
|
|||
id: resp.body.id,
|
||||
type: 'globaltype',
|
||||
updated_at: resp.body.updated_at,
|
||||
version: 2,
|
||||
version: resp.body.version,
|
||||
attributes: {
|
||||
name: 'My second favorite',
|
||||
},
|
||||
|
@ -107,7 +107,7 @@ export function updateTestSuiteFactory(esArchiver: any, supertest: SuperTest<any
|
|||
id: resp.body.id,
|
||||
type: 'visualization',
|
||||
updated_at: resp.body.updated_at,
|
||||
version: 2,
|
||||
version: resp.body.version,
|
||||
attributes: {
|
||||
title: 'My second favorite vis',
|
||||
},
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue