You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

334 lines
12 KiB
JavaScript

/*
* 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 { DefaultExecutionOptions, proxyExecuteKey } = require('../../execution-options');
const Long = types.Long;
let consistencyNames;
const graphProtocol = Object.freeze({
graphson1: 'graphson-1.0',
graphson2: 'graphson-2.0',
graphson3: 'graphson-3.0'
});
const payloadKeys = Object.freeze({
language :'graph-language',
source: 'graph-source',
name: 'graph-name',
results: 'graph-results',
writeConsistency: 'graph-write-consistency',
readConsistency: 'graph-read-consistency',
timeout: 'request-timeout'
});
/**
* 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} [graphResults] The protocol to use for serializing and deserializing graph results.
* <p>
* Note that this value should rarely be set by users and will otherwise be unset. When unset the server resolves
* the protocol based on the <code>graphLanguage</code> specified.
* </p>
* @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>
*/
/**
* 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: {
[payloadKeys.language]: utils.allocBufferFromString(profileOptions.language || baseOptions.language),
[payloadKeys.source]: utils.allocBufferFromString(profileOptions.source || baseOptions.source)
},
graphLanguage: profileOptions.language || baseOptions.language,
graphResults: profileOptions.results || baseOptions.results,
graphSource: profileOptions.source || baseOptions.source,
graphName: utils.ifUndefined(profileOptions.name, baseOptions.name)
};
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);
}
if (options.graphName) {
options.customPayload[payloadKeys.name] = utils.allocBufferFromString(options.graphName);
}
const graphResults = utils.ifUndefined(profileOptions.results, baseOptions.graphResults);
if (graphResults !== undefined) {
options.customPayload[payloadKeys.results] = utils.allocBufferFromString(graphResults);
}
const readConsistency = utils.ifUndefined(profileOptions.readConsistency, baseOptions.readConsistency);
if (readConsistency !== undefined) {
options.customPayload[payloadKeys.readConsistency] =
utils.allocBufferFromString(getConsistencyName(readConsistency));
}
const writeConsistency = utils.ifUndefined(profileOptions.writeConsistency, baseOptions.writeConsistency);
if (writeConsistency !== undefined) {
options.customPayload[payloadKeys.writeConsistency] =
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[payloadKeys.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 {Object} payload
* @param {QueryOptions} profileOptions
* @param {String} key
* @param {String|Number|null} value
* @param {Function} [converter]
* @private
*/
function setPayloadKey(payload, profileOptions, key, value, converter) {
converter = converter || utils.allocBufferFromString;
if (value === null) {
// Use null to avoid set payload for a key
return;
}
if (value !== undefined) {
payload[key] = converter(value);
return;
}
if (profileOptions.customPayload[key]) {
payload[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 {GraphQueryOptions} 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) {
queryOptions = queryOptions || utils.emptyObject;
super(queryOptions, client, null);
this._defaultGraphOptions = getDefaultGraphOptions(
client.profileManager, graphBaseOptions, defaultProfileRetryPolicy, this.getProfile());
this._preferredHost = null;
this._graphSubProtocol = queryOptions.graphResults || this._defaultGraphOptions.graphResults;
this._graphLanguage = queryOptions.graphLanguage || this._defaultGraphOptions.graphLanguage;
}
setPreferredHost(host) {
this._preferredHost = host;
}
getPreferredHost() {
return this._preferredHost;
}
getGraphSource() {
return this.getRawQueryOptions().graphSource || this._defaultGraphOptions.graphSource;
}
getGraphLanguage() {
return this._graphLanguage;
}
setGraphLanguage(value) {
this._graphLanguage = value;
}
getGraphName() {
return utils.ifUndefined(this.getRawQueryOptions().graphName, this._defaultGraphOptions.graphName);
}
getGraphSubProtocol() {
return this._graphSubProtocol;
}
setGraphSubProtocol(protocol) {
this._graphSubProtocol = protocol;
}
/** Graph executions have a specific default read timeout */
getReadTimeout() {
return this.getRawQueryOptions().readTimeout || this._defaultGraphOptions.readTimeout;
}
/** Graph executions have a specific default retry policy */
getRetryPolicy() {
return this.getRawQueryOptions().retry || this._defaultGraphOptions.retry;
}
getRowParser() {
const factory = this.getRawQueryOptions().rowParserFactory;
if (!factory) {
return null;
}
return factory(this.getGraphSubProtocol());
}
getQueryWriter() {
const factory = this.getRawQueryOptions().queryWriterFactory;
if (!factory) {
return null;
}
return factory(this.getGraphSubProtocol());
}
setGraphPayload() {
const options = this.getRawQueryOptions();
const defaultOptions = this._defaultGraphOptions;
// Clone the existing custom payload (if any)
const payload = Object.assign({}, this.getCustomPayload());
// Override the payload for DSE Graph exclusive options
setPayloadKey(payload, defaultOptions, payloadKeys.language,
this.getGraphLanguage() !== this._defaultGraphOptions.graphLanguage ? this.getGraphLanguage() : undefined);
setPayloadKey(payload, defaultOptions, payloadKeys.source, options.graphSource);
setPayloadKey(payload, defaultOptions, payloadKeys.name, options.graphName);
setPayloadKey(payload, defaultOptions, payloadKeys.readConsistency,
getConsistencyName(options.graphReadConsistency));
setPayloadKey(payload, defaultOptions, payloadKeys.writeConsistency,
getConsistencyName(options.graphWriteConsistency));
// Use the read timeout defined by the user or the one default to graph executions
setPayloadKey(payload, defaultOptions, payloadKeys.timeout,
this.getReadTimeout() > 0 ? this.getReadTimeout() : null, longBuffer);
// Graph result is always set
payload[payloadKeys.results] = defaultOptions.graphResults === this.getGraphSubProtocol()
? defaultOptions.customPayload[payloadKeys.results] : utils.allocBufferFromString(this.getGraphSubProtocol());
this.setCustomPayload(payload);
}
}
module.exports = {
GraphExecutionOptions,
graphProtocol,
payloadKeys
};