Add api compatibility header support (#1478)

This commit is contained in:
Tomas Della Vedova
2021-07-13 09:47:45 +02:00
committed by GitHub
parent 101a13f63e
commit 4073d60b97
4 changed files with 75 additions and 4 deletions

View File

@ -84,3 +84,17 @@ const client = new Client({
Serializer: MySerializer Serializer: MySerializer
}) })
---- ----
[discrete]
==== Migrate to v8
The Node.js client can be configured to emit an HTTP header
``Accept: application/vnd.elasticsearch+json; compatible-with=7``
which signals to Elasticsearch that the client is requesting
``7.x`` version of request and response bodies. This allows for
upgrading from 7.x to 8.x version of Elasticsearch without upgrading
everything at once. Elasticsearch should be upgraded first after
the compatibility header is configured and clients should be upgraded
second.
To enable to setting, configure the environment variable
``ELASTIC_CLIENT_APIVERSIONING`` to ``true``.

View File

@ -116,6 +116,10 @@ class Client extends ESAPI {
disablePrototypePoisoningProtection: false disablePrototypePoisoningProtection: false
}, opts) }, opts)
if (process.env.ELASTIC_CLIENT_APIVERSIONING === 'true') {
options.headers = Object.assign({ accept: 'application/vnd.elasticsearch+json; compatible-with=7' }, options.headers)
}
this[kInitialOptions] = options this[kInitialOptions] = options
this[kExtensions] = [] this[kExtensions] = []
this.name = options.name this.name = options.name

View File

@ -38,6 +38,7 @@ const clientVersion = require('../package.json').version
const userAgent = `elasticsearch-js/${clientVersion} (${os.platform()} ${os.release()}-${os.arch()}; Node.js ${process.version})` const userAgent = `elasticsearch-js/${clientVersion} (${os.platform()} ${os.release()}-${os.arch()}; Node.js ${process.version})`
const MAX_BUFFER_LENGTH = buffer.constants.MAX_LENGTH const MAX_BUFFER_LENGTH = buffer.constants.MAX_LENGTH
const MAX_STRING_LENGTH = buffer.constants.MAX_STRING_LENGTH const MAX_STRING_LENGTH = buffer.constants.MAX_STRING_LENGTH
const kApiVersioning = Symbol('api versioning')
class Transport { class Transport {
constructor (opts) { constructor (opts) {
@ -64,6 +65,7 @@ class Transport {
this.generateRequestId = opts.generateRequestId || generateRequestId() this.generateRequestId = opts.generateRequestId || generateRequestId()
this.name = opts.name this.name = opts.name
this.opaqueIdPrefix = opts.opaqueIdPrefix this.opaqueIdPrefix = opts.opaqueIdPrefix
this[kApiVersioning] = process.env.ELASTIC_CLIENT_APIVERSIONING === 'true'
this.nodeFilter = opts.nodeFilter || defaultNodeFilter this.nodeFilter = opts.nodeFilter || defaultNodeFilter
if (typeof opts.nodeSelector === 'function') { if (typeof opts.nodeSelector === 'function') {
@ -295,7 +297,8 @@ class Transport {
// - the request is not a HEAD request // - the request is not a HEAD request
// - the payload is not an empty string // - the payload is not an empty string
if (result.headers['content-type'] !== undefined && if (result.headers['content-type'] !== undefined &&
result.headers['content-type'].indexOf('application/json') > -1 && (result.headers['content-type'].indexOf('application/json') > -1 ||
result.headers['content-type'].indexOf('application/vnd.elasticsearch+json') > -1) &&
isHead === false && isHead === false &&
payload !== '' payload !== ''
) { ) {
@ -369,7 +372,7 @@ class Transport {
} }
if (params.body !== '') { if (params.body !== '') {
headers['content-type'] = headers['content-type'] || 'application/json' headers['content-type'] = headers['content-type'] || (this[kApiVersioning] ? 'application/vnd.elasticsearch+json; compatible-with=7' : 'application/json')
} }
// handle ndjson body // handle ndjson body
@ -386,7 +389,7 @@ class Transport {
params.body = params.bulkBody params.body = params.bulkBody
} }
if (params.body !== '') { if (params.body !== '') {
headers['content-type'] = headers['content-type'] || 'application/x-ndjson' headers['content-type'] = headers['content-type'] || (this[kApiVersioning] ? 'application/vnd.elasticsearch+x-ndjson; compatible-with=7' : 'application/x-ndjson')
} }
} }

View File

@ -1422,6 +1422,56 @@ test('Disable prototype poisoning protection', t => {
}) })
}) })
test('API compatibility header (json)', t => {
t.plan(4)
function handler (req, res) {
t.equal(req.headers.accept, 'application/vnd.elasticsearch+json; compatible-with=7')
t.equal(req.headers['content-type'], 'application/vnd.elasticsearch+json; compatible-with=7')
res.setHeader('Content-Type', 'application/vnd.elasticsearch+json; compatible-with=7')
res.end(JSON.stringify({ hello: 'world' }))
}
buildServer(handler, ({ port }, server) => {
process.env.ELASTIC_CLIENT_APIVERSIONING = 'true'
const client = new Client({
node: `http://localhost:${port}`
})
client.index({ index: 'foo', body: {} }, (err, { body }) => {
t.error(err)
t.same(body, { hello: 'world' })
server.stop()
delete process.env.ELASTIC_CLIENT_APIVERSIONING
})
})
})
test('API compatibility header (x-ndjson)', t => {
t.plan(4)
function handler (req, res) {
t.equal(req.headers.accept, 'application/vnd.elasticsearch+json; compatible-with=7')
t.equal(req.headers['content-type'], 'application/vnd.elasticsearch+x-ndjson; compatible-with=7')
res.setHeader('Content-Type', 'application/vnd.elasticsearch+json; compatible-with=7')
res.end(JSON.stringify({ hello: 'world' }))
}
buildServer(handler, ({ port }, server) => {
process.env.ELASTIC_CLIENT_APIVERSIONING = 'true'
const client = new Client({
node: `http://localhost:${port}`
})
client.bulk({ index: 'foo', body: [{}, {}] }, (err, { body }) => {
t.error(err)
t.same(body, { hello: 'world' })
server.stop()
delete process.env.ELASTIC_CLIENT_APIVERSIONING
})
})
})
test('Bearer auth', t => { test('Bearer auth', t => {
t.plan(3) t.plan(3)