Add api compatibility header support (#1478) (#1491)

Co-authored-by: Tomas Della Vedova <delvedor@users.noreply.github.com>
This commit is contained in:
github-actions[bot]
2021-07-13 09:48:30 +02:00
committed by GitHub
parent ea2c8d2d4d
commit 28d8ff799e
4 changed files with 75 additions and 4 deletions

View File

@ -83,4 +83,18 @@ class MySerializer extends Serializer {
const client = new Client({ 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)