Skip to content

Commit

Permalink
[Feature/extensions] Add getSettings support for AD (#4519)
Browse files Browse the repository at this point in the history
  • Loading branch information
dbwiddis authored Sep 20, 2022
1 parent edde6a4 commit 749b155
Show file tree
Hide file tree
Showing 14 changed files with 1,098 additions and 45 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -98,6 +98,7 @@ Inspired from [Keep a Changelog](https://keepachangelog.com/en/1.0.0/)
- Pass full RestResponse to user from Extension ([#4356](https:/opensearch-project/OpenSearch/pull/4356))
- Handle named wildcards (REST path parameters) ([#4415](https:/opensearch-project/OpenSearch/pull/4415))
- Adding ActionListener onFailure to ExtensionsOrchestrator ([#61](https:/opensearch-project/opensearch-sdk/issues/61))
- Add getSettings support for AD([#4519](https:/opensearch-project/OpenSearch/pull/4519))

## [2.x]
### Added
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,279 @@
/*
* SPDX-License-Identifier: Apache-2.0
*
* The OpenSearch Contributors require contributions made to
* this file be licensed under the Apache-2.0 license or a
* compatible open source license.
*/

package org.opensearch.common.settings;

import org.opensearch.Version;
import org.opensearch.common.io.stream.StreamInput;
import org.opensearch.common.io.stream.StreamOutput;
import org.opensearch.common.io.stream.Writeable;
import org.opensearch.common.settings.Setting.Property;
import org.opensearch.common.unit.ByteSizeValue;
import org.opensearch.common.unit.TimeValue;
import java.io.IOException;
import java.util.Arrays;
import java.util.EnumSet;
import java.util.concurrent.TimeUnit;

/**
* Wrapper for {@link Setting} with {@link #writeTo(StreamOutput)} implementation dependent on the setting type.
*
* @opensearch.internal
*/
public class WriteableSetting implements Writeable {

/**
* The Generic Types which this class can serialize.
*/
public enum SettingType {
Boolean,
Integer,
Long,
Float,
Double,
String,
TimeValue, // long + TimeUnit
ByteSizeValue, // long + ByteSizeUnit
Version
}

private Setting<?> setting;
private SettingType type;

/**
* Wrap a {@link Setting}. The generic type is determined from the type of the default value.
*
* @param setting The setting to wrap. The default value must not be null.
* @throws IllegalArgumentException if the setting has a null default value.
*/
public WriteableSetting(Setting<?> setting) {
this(setting, getGenericTypeFromDefault(setting));
}

/**
* Wrap a {@link Setting} with a specified generic type.
*
* @param setting The setting to wrap.
* @param type The Generic type of the setting.
*/
public WriteableSetting(Setting<?> setting, SettingType type) {
this.setting = setting;
this.type = type;
}

/**
* Wrap a {@link Setting} read from a stream.
*
* @param in Input to read the value from.
* @throws IOException if there is an error reading the values
*/
public WriteableSetting(StreamInput in) throws IOException {
// Read the type
this.type = in.readEnum(SettingType.class);
// Read the key
String key = in.readString();
// Read the default value
Object defaultValue = readDefaultValue(in);
// Read a boolean specifying whether the fallback settings is null
WriteableSetting fallback = null;
boolean hasFallback = in.readBoolean();
if (hasFallback) {
fallback = new WriteableSetting(in);
}
// We are using known types so don't need the parser
// We are not using validator
// Read properties
EnumSet<Property> propSet = in.readEnumSet(Property.class);
// Put it all in a setting object
this.setting = createSetting(type, key, defaultValue, fallback, propSet.toArray(Property[]::new));
}

/**
* Due to type erasure, it is impossible to determine the generic type of a Setting at runtime.
* All settings have a non-null default, however, so the type of the default can be used to determine the setting type.
*
* @param setting The setting with a generic type.
* @return The corresponding {@link SettingType} for the default value.
*/
private static SettingType getGenericTypeFromDefault(Setting<?> setting) {
String typeStr = null;
try {
// This throws NPE on null default
typeStr = setting.getDefault(Settings.EMPTY).getClass().getSimpleName();
// This throws IAE if not in enum
return SettingType.valueOf(typeStr);
} catch (NullPointerException e) {
throw new IllegalArgumentException("Unable to determine the generic type of this setting with a null default value.");
} catch (IllegalArgumentException e) {
throw new UnsupportedOperationException(
"This class is not yet set up to handle the generic type: "
+ typeStr
+ ". Supported types are "
+ Arrays.toString(SettingType.values())
);
}
}

/**
* Gets the wrapped setting. Use {@link #getType()} to determine its generic type.
*
* @return The wrapped setting.
*/
public Setting<?> getSetting() {
return this.setting;
}

/**
* Gets the generic type of the wrapped setting.
*
* @return The wrapped setting's generic type.
*/
public SettingType getType() {
return this.type;
}

@SuppressWarnings("unchecked")
private Setting<?> createSetting(
SettingType type,
String key,
Object defaultValue,
WriteableSetting fallback,
Property[] propertyArray
) {
switch (type) {
case Boolean:
return fallback == null
? Setting.boolSetting(key, (boolean) defaultValue, propertyArray)
: Setting.boolSetting(key, (Setting<Boolean>) fallback.getSetting(), propertyArray);
case Integer:
return fallback == null
? Setting.intSetting(key, (int) defaultValue, propertyArray)
: Setting.intSetting(key, (Setting<Integer>) fallback.getSetting(), propertyArray);
case Long:
return fallback == null
? Setting.longSetting(key, (long) defaultValue, propertyArray)
: Setting.longSetting(key, (Setting<Long>) fallback.getSetting(), propertyArray);
case Float:
return fallback == null
? Setting.floatSetting(key, (float) defaultValue, propertyArray)
: Setting.floatSetting(key, (Setting<Float>) fallback.getSetting(), propertyArray);
case Double:
return fallback == null
? Setting.doubleSetting(key, (double) defaultValue, propertyArray)
: Setting.doubleSetting(key, (Setting<Double>) fallback.getSetting(), propertyArray);
case String:
return fallback == null
? Setting.simpleString(key, (String) defaultValue, propertyArray)
: Setting.simpleString(key, (Setting<String>) fallback.getSetting(), propertyArray);
case TimeValue:
return fallback == null
? Setting.timeSetting(key, (TimeValue) defaultValue, propertyArray)
: Setting.timeSetting(key, (Setting<TimeValue>) fallback.getSetting(), propertyArray);
case ByteSizeValue:
return fallback == null
? Setting.byteSizeSetting(key, (ByteSizeValue) defaultValue, propertyArray)
: Setting.byteSizeSetting(key, (Setting<ByteSizeValue>) fallback.getSetting(), propertyArray);
case Version:
// No fallback option on this method
return Setting.versionSetting(key, (Version) defaultValue, propertyArray);
default:
// This Should Never Happen (TM)
throw new UnsupportedOperationException("A SettingType has been added to the enum and not handled here.");
}
}

@Override
public void writeTo(StreamOutput out) throws IOException {
// Write the type
out.writeEnum(type);
// Write the key
out.writeString(setting.getKey());
// Write the default value
writeDefaultValue(out, setting.getDefault(Settings.EMPTY));
// Write a boolean specifying whether the fallback settings is null
boolean hasFallback = setting.fallbackSetting != null;
out.writeBoolean(hasFallback);
if (hasFallback) {
new WriteableSetting(setting.fallbackSetting, type).writeTo(out);
}
// We are using known types so don't need the parser
// We are not using validator
// Write properties
out.writeEnumSet(setting.getProperties());
}

private void writeDefaultValue(StreamOutput out, Object defaultValue) throws IOException {
switch (type) {
case Boolean:
out.writeBoolean((boolean) defaultValue);
break;
case Integer:
out.writeInt((int) defaultValue);
break;
case Long:
out.writeLong((long) defaultValue);
break;
case Float:
out.writeFloat((float) defaultValue);
break;
case Double:
out.writeDouble((double) defaultValue);
break;
case String:
out.writeString((String) defaultValue);
break;
case TimeValue:
TimeValue tv = (TimeValue) defaultValue;
out.writeLong(tv.duration());
out.writeString(tv.timeUnit().name());
break;
case ByteSizeValue:
((ByteSizeValue) defaultValue).writeTo(out);
break;
case Version:
Version.writeVersion((Version) defaultValue, out);
break;
default:
// This Should Never Happen (TM)
throw new UnsupportedOperationException("A SettingType has been added to the enum and not handled here.");
}
}

private Object readDefaultValue(StreamInput in) throws IOException {
switch (type) {
case Boolean:
return in.readBoolean();
case Integer:
return in.readInt();
case Long:
return in.readLong();
case Float:
return in.readFloat();
case Double:
return in.readDouble();
case String:
return in.readString();
case TimeValue:
long duration = in.readLong();
TimeUnit unit = TimeUnit.valueOf(in.readString());
return new TimeValue(duration, unit);
case ByteSizeValue:
return new ByteSizeValue(in);
case Version:
return Version.readVersion(in);
default:
// This Should Never Happen (TM)
throw new UnsupportedOperationException("A SettingType has been added to the enum and not handled here.");
}
}

@Override
public String toString() {
return "WriteableSettings{type=Setting<" + type + ">, setting=" + setting + "}";
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
* compatible open source license.
*/

package org.opensearch.extensions.rest;
package org.opensearch.extensions;

import org.opensearch.common.io.stream.StreamInput;
import org.opensearch.common.io.stream.StreamOutput;
Expand All @@ -15,18 +15,18 @@
import java.io.IOException;

/**
* Response to register REST Actions request.
* Generic string response indicating the status of some previous request sent to the SDK
*
* @opensearch.internal
*/
public class RegisterRestActionsResponse extends TransportResponse {
public class ExtensionStringResponse extends TransportResponse {
private String response;

public RegisterRestActionsResponse(String response) {
public ExtensionStringResponse(String response) {
this.response = response;
}

public RegisterRestActionsResponse(StreamInput in) throws IOException {
public ExtensionStringResponse(StreamInput in) throws IOException {
response = in.readString();
}

Expand Down
Loading

0 comments on commit 749b155

Please sign in to comment.