blob: 3a5aef3548834c73757e156c8a3cbf7360a22993 [file]
/*
* Copyright DataStax, Inc.
*
* Licensed 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.
*/
'use strict';
const util = require('util');
const types = require('../../types');
const utils = require('../../utils');
const executionOptionsModule = require('../../execution-options');
const DefaultExecutionOptions = executionOptionsModule.DefaultExecutionOptions;
const proxyExecuteKey = executionOptionsModule.proxyExecuteKey;
const Long = types.Long;
let consistencyNames;
/**
* Graph options that extends {@link QueryOptions}.
* <p>
* Consider using [execution profiles]{@link ExecutionProfile} if you plan to reuse options across different
* query executions.
* </p>
* @typedef {QueryOptions} module:datastax/graph~GraphQueryOptions
* @property {String} [graphLanguage] The graph language to use in graph queries.
* @property {String} [graphName] The graph name to be used in the query. You can use <code>null</code> to clear the
* value from the <code>DseClientOptions</code> and execute a query without a default graph.
* @property {Number} [graphReadConsistency] Specifies the
* [consistency level]{@link module:types~consistencies}
* to be used for the graph read queries in this execution.
* <p>
* When defined, it overrides the consistency level only for the READ part of the graph query.
* </p>
* @property {String} [graphSource] The graph traversal source name to use in graph queries.
* @property {Number} [graphWriteConsistency] Specifies the [consistency level]{@link module:types~consistencies} to
* be used for the graph write queries in this execution.
* <p>
* When defined, it overrides the consistency level only for the WRITE part of the graph query.
* </p>
* @property {RetryPolicy} [retry] Sets the retry policy to be used for the graph query execution.
* <p>
* When not specified in the {@link GraphQueryOptions} or in the {@link ExecutionProfile}, it will use by default
* a retry policy that does not retry graph executions.
* </p>
*/
/**
* @param {ProfileManager} profileManager
* @param baseOptions Graph base options
* @param {RetryPolicy|null} defaultRetryPolicy
* @param {GraphQueryOptions} options
* @return {GraphQueryOptions}
* @ignore
*/
function createQueryOptions(profileManager, baseOptions, defaultRetryPolicy, options) {
const profile = profileManager.getProfile(options && options.executionProfile);
if (!profile) {
// Let the core driver deal with specified profile not been found
return options;
}
const defaultGraphOptions = getDefaultGraphOptions(profileManager, baseOptions, defaultRetryPolicy, profile);
if (!options || typeof options === 'function') {
return defaultGraphOptions;
}
// Check if the user is using a parameter that would make the custom payload different from the
// payload for the profile (ie: the user specified only the profile / or nothing at all)
const noGraphPayloadOptions =
!options.customPayload &&
!options.graphLanguage &&
!options.graphSource &&
!options.executeAs &&
options.graphName === undefined &&
options.graphReadConsistency === undefined &&
options.graphWriteConsistency === undefined &&
options.readTimeout === undefined;
options = utils.extend({
graphLanguage: defaultGraphOptions.graphLanguage,
graphSource: defaultGraphOptions.graphSource,
readTimeout: defaultGraphOptions.readTimeout,
// The default retry policy for graph should not retry
retry: defaultGraphOptions.retry
}, options);
// If there are no changes to custom payload, return.
if (noGraphPayloadOptions) {
// Reuse the same customPayload instance and avoid reconstruct it
options.customPayload = defaultGraphOptions.customPayload;
return options;
}
if (!options.customPayload) {
options.customPayload = {};
}
else {
// Shallow clone the custom payload
options.customPayload = utils.extend({}, options.customPayload);
}
// Set the proxy execute payload to avoid clientOptions from cloning this payload dictionary
setPayloadKey(options, defaultGraphOptions, proxyExecuteKey, options.executeAs);
// Set the payload for DSE graph exclusive options
setPayloadKey(options, defaultGraphOptions, 'graph-language', options.graphLanguage);
setPayloadKey(options, defaultGraphOptions, 'graph-source', options.graphSource);
setPayloadKey(options, defaultGraphOptions, 'graph-name', options.graphName);
setPayloadKey(options, defaultGraphOptions, 'graph-read-consistency',
getConsistencyName(options.graphReadConsistency));
setPayloadKey(options, defaultGraphOptions, 'graph-write-consistency',
getConsistencyName(options.graphWriteConsistency));
setPayloadKey(options, defaultGraphOptions, 'request-timeout',
options.readTimeout > 0 ? options.readTimeout : null, longBuffer);
return options;
}
/**
* Gets the default options with the custom payload for a given profile.
* @param {ProfileManager} profileManager
* @param baseOptions
* @param {RetryPolicy|null} defaultRetryPolicy
* @param {ExecutionProfile} profile
* @returns {DseClientOptions}
* @private
*/
function getDefaultGraphOptions(profileManager, baseOptions, defaultRetryPolicy, profile) {
return profileManager.getOrCreateGraphOptions(profile, function createDefaultOptions() {
const profileOptions = profile.graphOptions || utils.emptyObject;
const defaultProfile = profileManager.getDefault();
const options = {
customPayload: {
'graph-language': utils.allocBufferFromString(profileOptions.language || baseOptions.language),
'graph-source': utils.allocBufferFromString(profileOptions.source || baseOptions.source)
},
graphLanguage: profileOptions.language || baseOptions.language,
graphSource: profileOptions.source || baseOptions.source
};
if (profile !== defaultProfile) {
options.retry = profile.retry || baseOptions.retry;
}
else {
// Based on an implementation detail of the execution profiles, the retry policy for the default profile is
// always loaded (required), but that doesn't mean that it was specified by the user.
// If it wasn't specified by the user, use the default retry policy for graph statements.
options.retry = defaultRetryPolicy || baseOptions.retry;
}
if (baseOptions.executeAs) {
options.customPayload[proxyExecuteKey] = utils.allocBufferFromString(baseOptions.executeAs);
}
const name = utils.ifUndefined(profileOptions.name, baseOptions.name);
if (name) {
options.customPayload['graph-name'] = utils.allocBufferFromString(name);
}
const readConsistency = utils.ifUndefined(profileOptions.readConsistency, baseOptions.readConsistency);
if (readConsistency !== undefined) {
options.customPayload['graph-read-consistency'] =
utils.allocBufferFromString(getConsistencyName(readConsistency));
}
const writeConsistency = utils.ifUndefined(profileOptions.writeConsistency, baseOptions.writeConsistency);
if (writeConsistency !== undefined) {
options.customPayload['graph-write-consistency'] =
utils.allocBufferFromString(getConsistencyName(writeConsistency));
}
options.readTimeout = utils.ifUndefined3(profile.readTimeout, defaultProfile.readTimeout, baseOptions.readTimeout);
if (options.readTimeout > 0) {
// Write the graph read timeout payload
options.customPayload['request-timeout'] = longBuffer(options.readTimeout);
}
return options;
});
}
/**
* Sets the payload key. If the value is not provided, it uses the value from the default profile options.
* @param {QueryOptions} options
* @param {QueryOptions} profileOptions
* @param {String} key
* @param {String|Number|null} value
* @param {Function} [converter]
* @private
*/
function setPayloadKey(options, profileOptions, key, value, converter) {
converter = converter || utils.allocBufferFromString;
if (value === null) {
// Use null to avoid set payload for a key
return;
}
if (value !== undefined) {
options.customPayload[key] = converter(value);
return;
}
if (profileOptions.customPayload[key]) {
options.customPayload[key] = profileOptions.customPayload[key];
}
}
function longBuffer(value) {
value = Long.fromNumber(value);
return Long.toBuffer(value);
}
/**
* Gets the name in upper case of the consistency level.
* @param {Number} consistency
* @private
*/
function getConsistencyName(consistency) {
// eslint-disable-next-line
if (consistency == undefined) {
//null or undefined => undefined
return undefined;
}
loadConsistencyNames();
const name = consistencyNames[consistency];
if (!name) {
throw new Error(util.format(
'Consistency %s not found, use values defined as properties in types.consistencies object', consistency
));
}
return name;
}
function loadConsistencyNames() {
if (consistencyNames) {
return;
}
consistencyNames = {};
const propertyNames = Object.keys(types.consistencies);
for (let i = 0; i < propertyNames.length; i++) {
const name = propertyNames[i];
consistencyNames[types.consistencies[name]] = name.toUpperCase();
}
//Using java constants naming conventions
consistencyNames[types.consistencies.localQuorum] = 'LOCAL_QUORUM';
consistencyNames[types.consistencies.eachQuorum] = 'EACH_QUORUM';
consistencyNames[types.consistencies.localSerial] = 'LOCAL_SERIAL';
consistencyNames[types.consistencies.localOne] = 'LOCAL_ONE';
}
/**
* Represents a wrapper around the options related to a graph execution.
* @internal
* @ignore
*/
class GraphExecutionOptions extends DefaultExecutionOptions {
/**
* Creates a new instance of GraphExecutionOptions.
* @param {QueryOptions} queryOptions The user provided query options.
* @param {Client} client the client instance.
* @param graphBaseOptions The default graph base options.
* @param {RetryPolicy} defaultProfileRetryPolicy
*/
constructor(queryOptions, client, graphBaseOptions, defaultProfileRetryPolicy) {
// We are doing excessive cloning as part of the creation of graph options.
// See NODEJS-482
queryOptions = createQueryOptions(
client.profileManager, graphBaseOptions, defaultProfileRetryPolicy, queryOptions);
super(queryOptions, client, null);
this._preferredHost = null;
}
setPreferredHost(host) {
this._preferredHost = host;
}
getPreferredHost() {
return this._preferredHost;
}
getGraphSource() {
return this.getRawQueryOptions().graphSource;
}
getGraphLanguage() {
return this.getRawQueryOptions().graphLanguage;
}
}
module.exports = {
GraphExecutionOptions
};