/*******************************************************************************
 * Copyright 2013-2023 Aerospike 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.
 ******************************************************************************/

#include <cstdint>
#include <node.h>

#include "policy.h"
#include "conversions.h"
#include "expressions.h"
#include "metrics.h"

extern "C" {
#include <aerospike/as_policy.h>
#include <aerospike/as_event.h>
#include <aerospike/as_metrics_writer.h>
}

using namespace v8;

int eventpolicy_from_jsobject(as_policy_event *policy, Local<Object> obj,
							  const LogInfo *log)
{
	if (obj->IsUndefined() || obj->IsNull()) {
		return AS_NODE_PARAM_ERR;
	}
	int rc = 0;
	as_policy_event_init(policy);
	if ((rc = get_optional_int32_property(&policy->max_commands_in_process,
										  NULL, obj, "maxCommandsInProcess",
										  log)) != AS_NODE_PARAM_OK) {
		return rc;
	}
	if ((rc = get_optional_uint32_property(&policy->max_commands_in_queue, NULL,
										   obj, "maxCommandsInQueue", log)) !=
		AS_NODE_PARAM_OK) {
		return rc;
	}
	if ((rc = get_optional_uint32_property(&policy->queue_initial_capacity,
										   NULL, obj, "queueInitialCapacity",
										   log)) != AS_NODE_PARAM_OK) {
		return rc;
	}
	as_v8_detail(log, "Parsing event policy: success");
	return AS_NODE_PARAM_OK;
}

int basepolicy_from_jsobject(as_policy_base *policy, Local<Object> obj,
							 const LogInfo *log)
{
	int rc = 0;
	if ((rc = get_optional_uint32_property(&policy->connect_timeout, NULL, obj,
										   "connectTimeout", log)) !=
		AS_NODE_PARAM_OK) {
		return rc;
	}
	if ((rc = get_optional_uint32_property(&policy->socket_timeout, NULL, obj,
										   "socketTimeout", log)) !=
		AS_NODE_PARAM_OK) {
		return rc;
	}
	if ((rc = get_optional_uint32_property(&policy->total_timeout, NULL, obj,
										   "totalTimeout", log)) !=
		AS_NODE_PARAM_OK) {
		return rc;
	}
	if ((rc = get_optional_uint32_property(&policy->timeout_delay, NULL, obj,
										   "timeoutDelay", log)) !=
		AS_NODE_PARAM_OK) {
		return rc;
	}
	if ((rc = get_optional_uint32_property(&policy->max_retries, NULL, obj,
										   "maxRetries", log)) !=
		AS_NODE_PARAM_OK) {
		return rc;
	}
	
	Local<Value> exp_val =
		Nan::Get(obj, Nan::New("filterExpression").ToLocalChecked())
			.ToLocalChecked();
	if (exp_val->IsArray()) {
		Local<Array> exp_ary = Local<Array>::Cast(exp_val);
		if ((rc = compile_expression(exp_ary, &policy->filter_exp, log)) !=
			AS_NODE_PARAM_OK) {
			return rc;
		}
	}
	else if (exp_val->IsString()) {
		Nan::Utf8String exp_b64(exp_val.As<String>());
		policy->filter_exp = as_exp_from_base64(*exp_b64);
	}
	else if (exp_val->IsNull() || exp_val->IsUndefined()) {
		// no-op
	}
	else {
		as_v8_error(log, "Invalid filter expression value");
		return AS_NODE_PARAM_ERR;
	}

	if ((rc = get_optional_bool_property(&policy->compress, NULL, obj,
										 "compress", log)) !=
		AS_NODE_PARAM_OK) {
		return rc;
	}

	if ((rc = get_optional_transaction_property(&policy->txn, NULL, obj,
										 "txn", log)) !=
		AS_NODE_PARAM_OK) {
		return rc;
	}
	
	return AS_NODE_PARAM_OK;
}

int readpolicy_from_jsobject(as_policy_read *policy, Local<Object> obj,
							 const LogInfo *log)
{
	int rc = 0;
	as_policy_read_init(policy);
	if ((rc = basepolicy_from_jsobject(&policy->base, obj, log)) !=
		AS_NODE_PARAM_OK) {
		return rc;
	}
	if ((rc = get_optional_uint32_property((uint32_t *)&policy->key, NULL, obj,
										   "key", log)) != AS_NODE_PARAM_OK) {
		return rc;
	}
	if ((rc = get_optional_uint32_property((uint32_t *)&policy->replica, NULL,
										   obj, "replica", log)) !=
		AS_NODE_PARAM_OK) {
		return rc;
	}
	if ((rc = get_optional_uint32_property((uint32_t *)&policy->read_mode_ap,
										   NULL, obj, "readModeAP", log)) !=
		AS_NODE_PARAM_OK) {
		return rc;
	}
	if ((rc = get_optional_uint32_property((uint32_t *)&policy->read_mode_sc,
										   NULL, obj, "readModeSC", log)) !=
		AS_NODE_PARAM_OK) {
		return rc;
	}
	if ((rc = get_optional_int_property((int *)&policy->read_touch_ttl_percent,
										   NULL, obj, "readTouchTtlPercent", log)) !=
		AS_NODE_PARAM_OK) {
		return rc;
	}
	if ((rc = get_optional_bool_property(&policy->deserialize, NULL, obj,
										 "deserialize", log)) !=
		AS_NODE_PARAM_OK) {
		return rc;
	}
	as_v8_detail(log, "Parsing read policy: success");
	return AS_NODE_PARAM_OK;
}

int writepolicy_from_jsobject(as_policy_write *policy, Local<Object> obj,
							  const LogInfo *log)
{
	int rc = 0;
	as_policy_write_init(policy);
	if ((rc = basepolicy_from_jsobject(&policy->base, obj, log)) !=
		AS_NODE_PARAM_OK) {
		return rc;
	}
	if ((rc = get_optional_uint32_property((uint32_t *)&policy->key, NULL, obj,
										   "key", log)) != AS_NODE_PARAM_OK) {
		return rc;
	}
	if ((rc = get_optional_uint32_property((uint32_t *)&policy->replica, NULL,
										   obj, "replica", log)) !=
		AS_NODE_PARAM_OK) {
		return rc;
	}
	if ((rc = get_optional_uint32_property((uint32_t *)&policy->commit_level,
										   NULL, obj, "commitLevel", log)) !=
		AS_NODE_PARAM_OK) {
		return rc;
	}
	if ((rc = get_optional_uint32_property((uint32_t *)&policy->gen, NULL, obj,
										   "gen", log)) != AS_NODE_PARAM_OK) {
		return rc;
	}
	if ((rc = get_optional_uint32_property((uint32_t *)&policy->exists, NULL,
										   obj, "exists", log)) !=
		AS_NODE_PARAM_OK) {
		return rc;
	}
	if ((rc = get_optional_uint32_property(&policy->compression_threshold, NULL,
										   obj, "compressionThreshold", log)) !=
		AS_NODE_PARAM_OK) {
		return rc;
	}
	if ((rc = get_optional_bool_property(&policy->durable_delete, NULL, obj,
										 "durableDelete", log)) !=
		AS_NODE_PARAM_OK) {
		return rc;
	}
	if ((rc = get_optional_bool_property(&policy->on_locking_only, NULL, obj,
										 "onLockingOnly", log)) !=
		AS_NODE_PARAM_OK) {
		return rc;
	}
	as_v8_detail(log, "Parsing write policy: success");
	return AS_NODE_PARAM_OK;
}

int applypolicy_from_jsobject(as_policy_apply *policy, Local<Object> obj,
							  const LogInfo *log)
{
	int rc = 0;
	as_policy_apply_init(policy);
	if ((rc = basepolicy_from_jsobject(&policy->base, obj, log)) !=
		AS_NODE_PARAM_OK) {
		return rc;
	}
	if ((rc = get_optional_uint32_property((uint32_t *)&policy->key, NULL, obj,
										   "key", log)) != AS_NODE_PARAM_OK) {
		return rc;
	}
	if ((rc = get_optional_uint32_property((uint32_t *)&policy->replica, NULL,
										   obj, "replica", log)) !=
		AS_NODE_PARAM_OK) {
		return rc;
	}
	if ((rc = get_optional_uint32_property((uint32_t *)&policy->commit_level,
										   NULL, obj, "commitLevel", log)) !=
		AS_NODE_PARAM_OK) {
		return rc;
	}
	if ((rc = get_optional_uint32_property(&policy->ttl, NULL, obj, "ttl",
										   log)) != AS_NODE_PARAM_OK) {
		return rc;
	}
	if ((rc = get_optional_bool_property(&policy->durable_delete, NULL, obj,
										 "durableDelete", log)) !=
		AS_NODE_PARAM_OK) {
		return rc;
	}
	if ((rc = get_optional_bool_property(&policy->on_locking_only, NULL, obj,
										 "onLockingOnly", log)) !=
		AS_NODE_PARAM_OK) {
		return rc;
	}
	as_v8_detail(log, "Parsing apply policy: success");
	return AS_NODE_PARAM_OK;
}

int operatepolicy_from_jsobject(as_policy_operate *policy, Local<Object> obj,
								const LogInfo *log)
{
	int rc = 0;
	as_policy_operate_init(policy);
	if ((rc = basepolicy_from_jsobject(&policy->base, obj, log)) !=
		AS_NODE_PARAM_OK) {
		return rc;
	}
	if ((rc = get_optional_uint32_property((uint32_t *)&policy->key, NULL, obj,
										   "key", log)) != AS_NODE_PARAM_OK) {
		return rc;
	}
	if ((rc = get_optional_uint32_property((uint32_t *)&policy->replica, NULL,
										   obj, "replica", log)) !=
		AS_NODE_PARAM_OK) {
		return rc;
	}
	if ((rc = get_optional_uint32_property((uint32_t *)&policy->read_mode_ap,
										   NULL, obj, "readModeAP", log)) !=
		AS_NODE_PARAM_OK) {
		return rc;
	}
	if ((rc = get_optional_uint32_property((uint32_t *)&policy->read_mode_sc,
										   NULL, obj, "readModeSC", log)) !=
		AS_NODE_PARAM_OK) {
		return rc;
	}
	if ((rc = get_optional_int_property((int *)&policy->read_touch_ttl_percent,
										   NULL, obj, "readTouchTtlPercent", log)) !=
		AS_NODE_PARAM_OK) {
		return rc;
	}
	if ((rc = get_optional_uint32_property((uint32_t *)&policy->commit_level,
										   NULL, obj, "commitLevel", log)) !=
		AS_NODE_PARAM_OK) {
		return rc;
	}
	if ((rc = get_optional_uint32_property((uint32_t *)&policy->gen, NULL, obj,
										   "gen", log)) != AS_NODE_PARAM_OK) {
		return rc;
	}
	if ((rc = get_optional_uint32_property((uint32_t *)&policy->exists, NULL,
										   obj, "exists", log)) !=
		AS_NODE_PARAM_OK) {
		return rc;
	}
	if ((rc = get_optional_bool_property(&policy->deserialize, NULL, obj,
										 "deserialize", log)) !=
		AS_NODE_PARAM_OK) {
		return rc;
	}
	if ((rc = get_optional_bool_property(&policy->durable_delete, NULL, obj,
										 "durableDelete", log)) !=
		AS_NODE_PARAM_OK) {
		return rc;
	}
	as_v8_detail(log, "Parsing operate policy: success");
	return AS_NODE_PARAM_OK;
}

int removepolicy_from_jsobject(as_policy_remove *policy, Local<Object> obj,
							   const LogInfo *log)
{
	int rc = 0;
	as_policy_remove_init(policy);
	if ((rc = basepolicy_from_jsobject(&policy->base, obj, log)) !=
		AS_NODE_PARAM_OK) {
		return rc;
	}
	if ((rc = get_optional_uint32_property((uint32_t *)&policy->key, NULL, obj,
										   "key", log)) != AS_NODE_PARAM_OK) {
		return rc;
	}
	if ((rc = get_optional_uint32_property((uint32_t *)&policy->replica, NULL,
										   obj, "replica", log)) !=
		AS_NODE_PARAM_OK) {
		return rc;
	}
	if ((rc = get_optional_uint32_property((uint32_t *)&policy->commit_level,
										   NULL, obj, "commitLevel", log)) !=
		AS_NODE_PARAM_OK) {
		return rc;
	}
	if ((rc = get_optional_uint32_property((uint32_t *)&policy->gen, NULL, obj,
										   "gen", log)) != AS_NODE_PARAM_OK) {
		return rc;
	}
	if ((rc = get_optional_uint16_property((uint16_t *)&policy->generation,
										   NULL, obj, "generation", log)) !=
		AS_NODE_PARAM_OK) {
		return rc;
	}
	if ((rc = get_optional_bool_property(&policy->durable_delete, NULL, obj,
										 "durableDelete", log)) !=
		AS_NODE_PARAM_OK) {
		return rc;
	}
	as_v8_detail(log, "Parsing remove policy: success");
	return AS_NODE_PARAM_OK;
}

int batchpolicy_from_jsobject(as_policy_batch *policy, Local<Object> obj,
							  const LogInfo *log)
{
	int rc = 0;
	as_policy_batch_init(policy);
	if ((rc = basepolicy_from_jsobject(&policy->base, obj, log)) !=
		AS_NODE_PARAM_OK) {
		return rc;
	}
	if ((rc = get_optional_uint32_property((uint32_t *)&policy->replica, NULL,
										   obj, "replica", log)) !=
		AS_NODE_PARAM_OK) {
		return rc;
	}
	if ((rc = get_optional_uint32_property((uint32_t *)&policy->read_mode_ap,
										   NULL, obj, "readModeAP", log)) !=
		AS_NODE_PARAM_OK) {
		return rc;
	}
	if ((rc = get_optional_uint32_property((uint32_t *)&policy->read_mode_sc,
										   NULL, obj, "readModeSC", log)) !=
		AS_NODE_PARAM_OK) {
		return rc;
	}
	if ((rc = get_optional_int_property((int *)&policy->read_touch_ttl_percent,
										   NULL, obj, "readTouchTtlPercent", log)) !=
		AS_NODE_PARAM_OK) {
		return rc;
	}
	if ((rc = get_optional_bool_property(&policy->concurrent, NULL, obj,
										 "concurrent", log)) !=
		AS_NODE_PARAM_OK) {
		return rc;
	}
	if ((rc = get_optional_bool_property(&policy->allow_inline, NULL, obj,
										 "allowInline", log)) !=
		AS_NODE_PARAM_OK) {
		return rc;
	}
	if ((rc = get_optional_bool_property(&policy->allow_inline_ssd, NULL, obj,
										 "allowInlineSSD", log)) !=
		AS_NODE_PARAM_OK) {
		return rc;
	}
	if ((rc = get_optional_bool_property(&policy->respond_all_keys, NULL, obj,
										 "respondAllKeys", log)) !=
		AS_NODE_PARAM_OK) {
		return rc;
	}
	if ((rc = get_optional_bool_property(&policy->send_set_name, NULL, obj,
										 "sendSetName", log)) !=
		AS_NODE_PARAM_OK) {
		return rc;
	}
	if ((rc = get_optional_bool_property(&policy->deserialize, NULL, obj,
										 "deserialize", log)) !=
		AS_NODE_PARAM_OK) {
		return rc;
	}
	as_v8_detail(log, "Parsing batch policy: success");
	return AS_NODE_PARAM_OK;
}

int batchread_policy_from_jsobject(as_policy_batch_read *policy,
								   v8::Local<v8::Object> obj,
								   const LogInfo *log)
{
	int rc = 0;

	as_policy_batch_read_init(policy);

	Local<Value> exp_val =
		Nan::Get(obj, Nan::New("filterExpression").ToLocalChecked())
			.ToLocalChecked();
	if (exp_val->IsArray()) {
		Local<Array> exp_ary = Local<Array>::Cast(exp_val);
		if (compile_expression(exp_ary, &policy->filter_exp, log) !=
			AS_NODE_PARAM_OK) {
			return AS_NODE_PARAM_ERR;
		}
	}
	else if (exp_val->IsString()) {
		Nan::Utf8String exp_b64(exp_val.As<String>());
		policy->filter_exp = as_exp_from_base64(*exp_b64);
	}

	if ((rc = get_optional_uint32_property((uint32_t *)&policy->read_mode_ap,
										   NULL, obj, "readModeAP", log)) !=
		AS_NODE_PARAM_OK) {
		return rc;
	}
	if ((rc = get_optional_uint32_property((uint32_t *)&policy->read_mode_sc,
										   NULL, obj, "readModeSC", log)) !=
		AS_NODE_PARAM_OK) {
		return rc;
	}
	if ((rc = get_optional_int_property((int *)&policy->read_touch_ttl_percent,
										   NULL, obj, "readTouchTtlPercent", log)) !=
		AS_NODE_PARAM_OK) {
		return rc;
	}
	return rc;
}

int batchwrite_policy_from_jsobject(as_policy_batch_write *policy,
									v8::Local<v8::Object> obj,
									const LogInfo *log)
{
	int rc = 0;

	as_policy_batch_write_init(policy);

	Local<Value> exp_val =
		Nan::Get(obj, Nan::New("filterExpression").ToLocalChecked())
			.ToLocalChecked();
	if (exp_val->IsArray()) {
		Local<Array> exp_ary = Local<Array>::Cast(exp_val);
		if (compile_expression(exp_ary, &policy->filter_exp, log) !=
			AS_NODE_PARAM_OK) {
			return AS_NODE_PARAM_ERR;
		}
	}
	else if (exp_val->IsString()) {
		Nan::Utf8String exp_b64(exp_val.As<String>());
		policy->filter_exp = as_exp_from_base64(*exp_b64);
	}

	if ((rc = get_optional_uint32_property((uint32_t *)&policy->key, NULL, obj,
										   "key", log)) != AS_NODE_PARAM_OK) {
		return rc;
	}
	if ((rc = get_optional_uint32_property((uint32_t *)&policy->commit_level,
										   NULL, obj, "commitLevel", log)) !=
		AS_NODE_PARAM_OK) {
		return rc;
	}
	if ((rc = get_optional_uint32_property((uint32_t *)&policy->ttl,
										   NULL, obj, "ttl", log)) !=
		AS_NODE_PARAM_OK) {
		return rc;
	}
	if ((rc = get_optional_uint32_property((uint32_t *)&policy->gen, NULL, obj,
										   "gen", log)) != AS_NODE_PARAM_OK) {
		return rc;
	}
	if ((rc = get_optional_uint32_property((uint32_t *)&policy->exists, NULL,
										   obj, "exists", log)) !=
		AS_NODE_PARAM_OK) {
		return rc;
	}
	if ((rc = get_optional_uint32_property((uint32_t *)&policy->ttl, NULL, obj,
										 "ttl", log)) !=
		AS_NODE_PARAM_OK) {
		return rc;
	}
	if ((rc = get_optional_bool_property(&policy->durable_delete, NULL, obj,
										 "durableDelete", log)) !=
		AS_NODE_PARAM_OK) {
		return rc;
	}
	if ((rc = get_optional_bool_property(&policy->on_locking_only, NULL, obj,
										 "onLockingOnly", log)) !=
		AS_NODE_PARAM_OK) {
		return rc;
	}
	return rc;
}

int batchapply_policy_from_jsobject(as_policy_batch_apply *policy,
									v8::Local<v8::Object> obj,
									const LogInfo *log)
{
	int rc = 0;

	as_policy_batch_apply_init(policy);

	Local<Value> exp_val =
		Nan::Get(obj, Nan::New("filterExpression").ToLocalChecked())
			.ToLocalChecked();
	if (exp_val->IsArray()) {
		Local<Array> exp_ary = Local<Array>::Cast(exp_val);
		if (compile_expression(exp_ary, &policy->filter_exp, log) !=
			AS_NODE_PARAM_OK) {
			return AS_NODE_PARAM_ERR;
		}
	}
	else if (exp_val->IsString()) {
		Nan::Utf8String exp_b64(exp_val.As<String>());
		policy->filter_exp = as_exp_from_base64(*exp_b64);
	}
	if ((rc = get_optional_uint32_property((uint32_t *)&policy->key, NULL, obj,
										   "key", log)) != AS_NODE_PARAM_OK) {
		return rc;
	}
	if ((rc = get_optional_uint32_property((uint32_t *)&policy->commit_level,
										   NULL, obj, "commitLevel", log)) !=
		AS_NODE_PARAM_OK) {
		return rc;
	}
	if ((rc = get_optional_uint32_property((uint32_t *)&policy->ttl, NULL, obj,
										   "ttl", log)) != AS_NODE_PARAM_OK) {
		return rc;
	}
	if ((rc = get_optional_bool_property(&policy->durable_delete, NULL, obj,
										 "durableDelete", log)) !=
		AS_NODE_PARAM_OK) {
		return rc;
	}
	if ((rc = get_optional_bool_property(&policy->on_locking_only, NULL, obj,
										 "onLockingOnly", log)) !=
		AS_NODE_PARAM_OK) {
		return rc;
	}

	return rc;
}

int batchremove_policy_from_jsobject(as_policy_batch_remove *policy,
									 v8::Local<v8::Object> obj,
									 const LogInfo *log)
{
	int rc = 0;

	as_policy_batch_remove_init(policy);

	Local<Value> exp_val =
		Nan::Get(obj, Nan::New("filterExpression").ToLocalChecked())
			.ToLocalChecked();
	if (exp_val->IsArray()) {
		Local<Array> exp_ary = Local<Array>::Cast(exp_val);
		if (compile_expression(exp_ary, &policy->filter_exp, log) !=
			AS_NODE_PARAM_OK) {
			return AS_NODE_PARAM_ERR;
		}
	}
	else if (exp_val->IsString()) {
		Nan::Utf8String exp_b64(exp_val.As<String>());
		policy->filter_exp = as_exp_from_base64(*exp_b64);
	}
	if ((rc = get_optional_uint32_property((uint32_t *)&policy->key, NULL, obj,
										   "key", log)) != AS_NODE_PARAM_OK) {
		return rc;
	}
	if ((rc = get_optional_uint32_property((uint32_t *)&policy->commit_level,
										   NULL, obj, "commitLevel", log)) !=
		AS_NODE_PARAM_OK) {
		return rc;
	}
	if ((rc = get_optional_uint32_property((uint32_t *)&policy->gen, NULL, obj,
										   "gen", log)) != AS_NODE_PARAM_OK) {
		return rc;
	}
	if ((rc = get_optional_uint32_property((uint32_t *)&policy->generation,
										   NULL, obj, "generation", log)) !=
		AS_NODE_PARAM_OK) {
		return rc;
	}
	if ((rc = get_optional_bool_property(&policy->durable_delete, NULL, obj,
										 "durableDelete", log)) !=
		AS_NODE_PARAM_OK) {
		return rc;
	}
	return rc;
}

int querypolicy_from_jsobject(as_policy_query *policy, Local<Object> obj,
							  const LogInfo *log)
{
	int rc = 0;
	as_policy_query_init(policy);
	if ((rc = basepolicy_from_jsobject(&policy->base, obj, log)) !=
		AS_NODE_PARAM_OK) {
		return rc;
	}
	if ((rc = get_optional_uint32_property((uint32_t *)&policy->replica, NULL,
										   obj, "replica", log)) !=
		AS_NODE_PARAM_OK) {
		return rc;
	}	
	if ((rc = get_optional_uint32_property((uint32_t *)&policy->info_timeout,
										   NULL, obj, "infoTimeout", log)) !=
		AS_NODE_PARAM_OK) {
		return rc;
	}
	if ((rc = get_optional_bool_property(&policy->fail_on_cluster_change, NULL,
										 obj, "failOnClusterChange", log)) !=
		AS_NODE_PARAM_OK) {
		return rc;
	}
	if ((rc = get_optional_bool_property(&policy->deserialize, NULL, obj,
										 "deserialize", log)) !=
		AS_NODE_PARAM_OK) {
		return rc;
	}
	if ((rc = get_optional_bool_property(&policy->short_query, NULL, obj,
										 "shortQuery", log)) !=
		AS_NODE_PARAM_OK) {
		return rc;
	}
	if ((rc = get_optional_uint32_property((uint32_t *)&policy->expected_duration, NULL, obj,
										 "expectedDuration", log)) !=
		AS_NODE_PARAM_OK) {
		return rc;
	}
	if ((rc = get_optional_bool_property(&policy->fail_on_cluster_change, NULL, obj, "failOnClusterChange", log)) != AS_NODE_PARAM_OK) {
		return rc;
	}
	if ((rc = get_optional_uint32_property(&policy->info_timeout, NULL, obj, "infoTimeout", log)) != AS_NODE_PARAM_OK) {
		return rc;
	}
	as_v8_detail( log, "Parsing query policy : success");
	return AS_NODE_PARAM_OK;
}

int scanpolicy_from_jsobject(as_policy_scan *policy, Local<Object> obj,
							 const LogInfo *log)
{
	int rc = 0;
	as_policy_scan_init(policy);
	if ((rc = basepolicy_from_jsobject(&policy->base, obj, log)) !=
		AS_NODE_PARAM_OK) {
		return rc;
	}
	if ((rc = get_optional_uint32_property((uint32_t *)&policy->max_records,
										   NULL, obj, "maxRecords", log)) !=
		AS_NODE_PARAM_OK) {
		return rc;
	}
	if ((rc = get_optional_uint32_property(
			 (uint32_t *)&policy->records_per_second, NULL, obj,
			 "recordsPerSecond", log)) != AS_NODE_PARAM_OK) {
		return rc;
	}
	if ((rc = get_optional_bool_property(&policy->durable_delete, NULL, obj,
										 "durableDelete", log)) !=
		AS_NODE_PARAM_OK) {
		return rc;
	}
	if ((rc = get_optional_uint32_property((uint32_t *)&policy->replica, NULL,
										   obj, "replica", log)) !=
		AS_NODE_PARAM_OK) {
		return rc;
	}
	as_v8_detail(log, "Parsing scan policy: success");
	return AS_NODE_PARAM_OK;
}

int infopolicy_from_jsobject(as_policy_info *policy, Local<Object> obj,
							 const LogInfo *log)
{
	if (obj->IsUndefined() || obj->IsNull()) {
		return AS_NODE_PARAM_ERR;
	}
	int rc = 0;
	as_policy_info_init(policy);
	if ((rc = get_optional_uint32_property(&policy->timeout, NULL, obj,
										   "timeout", log)) !=
		AS_NODE_PARAM_OK) {
		return rc;
	}
	if ((rc = get_optional_uint32_property(&policy->timeout_delay, NULL, obj,
										   "timeoutDelay", log)) !=
		AS_NODE_PARAM_OK) {
		return rc;
	}
	if ((rc = get_optional_bool_property(&policy->send_as_is, NULL, obj,
										 "sendAsIs", log)) !=
		AS_NODE_PARAM_OK) {
		return rc;
	}
	if ((rc = get_optional_bool_property(&policy->check_bounds, NULL, obj,
										 "checkBounds", log)) !=
		AS_NODE_PARAM_OK) {
		return rc;
	}
	as_v8_detail(log, "Parsing info policy: success");
	return AS_NODE_PARAM_OK;
}

int adminpolicy_from_jsobject(as_policy_admin *policy, Local<Object> obj,
							 const LogInfo *log)
{
	if (obj->IsUndefined() || obj->IsNull()) {
		return AS_NODE_PARAM_ERR;
	}
	int rc = 0;
	as_policy_admin_init(policy);
	if ((rc = get_optional_uint32_property(&policy->timeout, NULL, obj,
										   "timeout", log)) !=
		AS_NODE_PARAM_OK) {
		return rc;
	}
	as_v8_detail(log, "Parsing info policy: success");
	return AS_NODE_PARAM_OK;
}

int metricspolicy_from_jsobject(as_metrics_policy *policy, Local<Object> obj, char** report_dir, Nan::Persistent<v8::Function>& enable_callback,
								Nan::Persistent<v8::Function>& snapshot_callback, Nan::Persistent<v8::Function>& node_close_callback, Nan::Persistent<v8::Function>& disable_callback,
								bool is_config_policy, const LogInfo *log)
{
	if (obj->IsUndefined() || obj->IsNull()) {
		return AS_NODE_PARAM_ERR;
	}

	int rc = 0;
	bool defined = false;
	int report_dir_size = 256;

	as_metrics_policy_init(policy);

	Local<Value> maybe_metrics_listeners = Nan::Get(obj, Nan::New("metricsListeners").ToLocalChecked()).ToLocalChecked();

	if (maybe_metrics_listeners->IsObject()) {
		Local<Object> v8_metrics_listeners = maybe_metrics_listeners.As<Object>();

		Local<Value> v8_enable_listener = Nan::Get(v8_metrics_listeners, Nan::New("enableListener").ToLocalChecked()).ToLocalChecked();
		Local<Value> v8_snapshot_listener = Nan::Get(v8_metrics_listeners, Nan::New("snapshotListener").ToLocalChecked()).ToLocalChecked();
		Local<Value> v8_node_close_listener = Nan::Get(v8_metrics_listeners, Nan::New("nodeCloseListener").ToLocalChecked()).ToLocalChecked();
		Local<Value> v8_disable_listener = Nan::Get(v8_metrics_listeners, Nan::New("disableListener").ToLocalChecked()).ToLocalChecked();

		if (v8_enable_listener->IsFunction() || v8_snapshot_listener->IsFunction() || v8_node_close_listener->IsFunction() || v8_disable_listener->IsFunction()) {
			if (v8_enable_listener->IsFunction() && v8_snapshot_listener->IsFunction() && v8_node_close_listener->IsFunction() && v8_disable_listener->IsFunction()) {

				enable_callback.Reset(v8_enable_listener.As<Function>());
				snapshot_callback.Reset(v8_snapshot_listener.As<Function>());
				node_close_callback.Reset(v8_node_close_listener.As<Function>());
				disable_callback.Reset(v8_disable_listener.As<Function>());

				if(is_config_policy){
					policy->metrics_listeners.enable_listener = enable_listener_config;
					policy->metrics_listeners.snapshot_listener = snapshot_listener_config;
					policy->metrics_listeners.node_close_listener = node_close_listener_config;
					policy->metrics_listeners.disable_listener = disable_listener_config;
				}
				else{
					policy->metrics_listeners.enable_listener = enable_listener;
					policy->metrics_listeners.snapshot_listener = snapshot_listener;
					policy->metrics_listeners.node_close_listener = node_close_listener;
					policy->metrics_listeners.disable_listener = disable_listener;
				}
			}
			else {
				as_v8_error(log, "If one metrics callback is set, all metrics callbacks must be set");
				return AEROSPIKE_ERR_PARAM;
			}
		}
	}

	Local<Value> v8_labels = Nan::Get(obj, Nan::New("labels").ToLocalChecked()).ToLocalChecked();

	if (v8_labels->IsObject()) {
		Local<Object> labels = Local<Array>::Cast(v8_labels);
		const Local<Array> props =
			Nan::GetOwnPropertyNames(labels).ToLocalChecked();

		for (uint32_t i = 0; i < props->Length(); i++) {

			const Local<Value> name = Nan::Get(props, i).ToLocalChecked();
			const Local<Value> value = Nan::Get(labels, name).ToLocalChecked();

			if(name->IsString() && value->IsString()){
				as_metrics_policy_add_label(policy, *Nan::Utf8String(name.As<String>()), *Nan::Utf8String(value.As<String>()));
			}
			else{
				as_v8_error(log, "labels must be an object with string key pairs.");
				return AS_NODE_PARAM_ERR;
			}

		}
	}
	else if (((!v8_labels->IsNull()) && (!v8_labels->IsUndefined()))) {
		as_v8_error(log, "labels must be an object with string key pairs.");
		return AS_NODE_PARAM_ERR;
	}

	if ((rc = get_optional_report_dir_property(report_dir, &defined, &report_dir_size, obj,
										   "reportDir", log)) !=
		AS_NODE_PARAM_OK) {
		return rc;
	}
	else if (defined) {
		as_metrics_policy_set_report_dir(policy, *report_dir);
	}

	if ((rc = get_optional_uint64_property(&policy->report_size_limit, NULL, obj,
										   "reportSizeLimit", log)) !=
		AS_NODE_PARAM_OK) {
		return rc;
	}
	if ((rc = get_optional_uint32_property(&policy->interval, NULL, obj,
										   "interval", log)) !=
		AS_NODE_PARAM_OK) {
		return rc;
	}
	if ((rc = get_optional_uint8_property(&policy->latency_columns, NULL, obj,
										   "latencyColumns", log)) !=
		AS_NODE_PARAM_OK) {
		return rc;
	}
	if ((rc = get_optional_uint8_property(&policy->latency_shift, NULL, obj,
										   "latencyShift", log)) !=
		AS_NODE_PARAM_OK) {
		return rc;
	}
	as_v8_detail(log, "Parsing info policy: success");
	return AS_NODE_PARAM_OK;
}

int partitions_from_jsobject(as_partition_filter *pf, bool *defined,
							 v8::Local<v8::Object> obj, const LogInfo *log)
{
	int rc = AS_NODE_PARAM_OK;

	*defined = false;
	if ((rc = get_optional_bool_property(defined, NULL, obj, "pfEnabled",
										 log)) != AS_NODE_PARAM_OK) {
		return rc;
	}

	if (!*defined) {
		return AS_NODE_PARAM_OK;
	}

	*defined = true;
	if (Nan::Has(obj, Nan::New("partFilter").ToLocalChecked()).FromJust()) {
		Local<Value> pf_obj =
			Nan::Get(obj, Nan::New("partFilter").ToLocalChecked())
				.ToLocalChecked();
		if (!pf_obj->IsUndefined() && pf_obj->IsObject()) {
			if ((rc = get_optional_uint32_property((uint32_t *)&pf->begin, NULL,
												   pf_obj.As<Object>(), "begin",
												   log)) != AS_NODE_PARAM_OK) {
				return rc;
			}
			if ((rc = get_optional_uint32_property((uint32_t *)&pf->count, NULL,
												   pf_obj.As<Object>(), "count",
												   log)) != AS_NODE_PARAM_OK) {
				return rc;
			}
			int len = AS_DIGEST_VALUE_SIZE;
			bool digest_defined = false;
			if ((rc = get_optional_bytes_property(
					 (uint8_t **)&pf->digest.value, &len, &digest_defined,
					 pf_obj.As<Object>(), "digest", log)) != AS_NODE_PARAM_OK) {
				return rc;
			}
			else {
				if (digest_defined) {
					pf->digest.init = true;
				}
			}
		}
		else {
			return AS_NODE_PARAM_ERR;
		}
	}
	else {
		return AS_NODE_PARAM_ERR;
	}

	as_v8_detail(log, "Parsing scan partition: success");

	return rc;
}