diff --git a/DESIGN.md b/DESIGN.md
index 34cddf258..b5958d187 100644
--- a/DESIGN.md
+++ b/DESIGN.md
@@ -74,10 +74,10 @@ The sequence diagram below shows the process of initializing an Extension, regis
The `org.opensearch.sdk.sample` package contains a sample `HelloWorldExtension` implementing the below steps. It is executed following the steps in the [`DEVELOPER_GUIDE`](DEVELOPER_GUIDE.md).
-![](Docs/ExtensionRestActions.svg)
-
#### Extension REST Actions Walk Through
+![](Docs/ExtensionRestActions.svg)
+
##### Extension Startup
(1) Extensions must implement the `Extension` interface which requires them to define their settings (name, host address and port) and a list of `ExtensionRestHandler` implementations they will handle. They are started up using a `main()` method which passes an instance of the extension to the `ExtensionsRunner` using `ExtensionsRunner.run(this)`.
@@ -116,9 +116,35 @@ The `ExtensionsOrchestrator` reads a list of extensions present in `extensions.y
(27) The User receives the response.
+#### Extension Point Implementation Walk Through
+
+An example of a more complex extension point, `getNamedXContent()` is shown below. A similar pattern can be followed for most extension points.
+
+![](Docs/NamedXContent.svg)
+
+##### Extension Startup
+
+(1, 2) Extensions initialize by passing an instance of themselves to the `ExtensionsRunner`. The first step in the constructor is for the `ExtensionsRunner` to pass its own instance back to the Extension via setter.
+
+(3, 4) The `Extension` interface includes extensions points such as `getNamedXContent()` (returning a default empty list). If overridden, the Extension will return a list of `NamedXContentRegistry.Entry` which will be saved as `customNamedXContent`. Other extension points operate in a similar manner.
+
+(5) The `ExtensionsRunner` registers an `ExtensionInitRequestHandler` which will complete the initialization process on OpenSearch startup.
+
+##### OpenSearch Startup, Extension Initialization, and NamedXContent Registration
+
+(6) Upon receipt of an `InitializeExtensionsRequest` (among other actions):
+
+(7, 8) Obtains Environment Settings from OpenSearch, necessary for some core XContent.
+
+(9, 10) Instantiates a new `ExtensionNamedXContentRegistry` which is set on the ExtensionsRunner.
+This uses the OpenSearch environment settings along with NamedXContent from several OpenSearch modules,
+and combines it the custom Extension NamedXContent.
+
+Since the Extension has an instance of the ExtensionsRunner, it can now access the registry via getter and pass it to Extension Rest Handlers as needed.
+
## FAQ
- Will extensions replace plugins?
- Plugins will continue to be supported and extensions are preferred as they will be easier to develop, deploy, and operate.
+ Plugins will continue to be supported in the near term but are on a path to deprecation. New development should consider using extensions, as they will be easier to develop, deploy, and operate.
- How is the latency going to be for extensions?
https://github.com/opensearch-project/OpenSearch/issues/3012#issuecomment-1122682444
diff --git a/Docs/NamedXContent.svg b/Docs/NamedXContent.svg
new file mode 100644
index 000000000..6d4ce533e
--- /dev/null
+++ b/Docs/NamedXContent.svg
@@ -0,0 +1 @@
+%23%20File%20comapible%20with%20https%3A%2F%2Fsequencediagram.org%2F%0Atitle%20NamedXContent%20Registration%0A%0Aentryspacing%200.6%0Aparticipantgroup%20%23lightgreen%20**Extension%20Node**%0Aparticipant%20Extension%0Aparticipant%20ExtensionsRunner%0Aparticipant%20ExtensionNamedXContentRegistry%0Aend%0A%0Aparticipantgroup%20%23lightblue%20**OpenSearch%20Node**%0Aparticipant%20ExtensionsOrchestrator%0Aend%0Aautonumber%201%0AExtension-%3EExtensionsRunner%3Arun(this)%0AExtension%3C-ExtensionsRunner%3AsetExtensionsRunner(this)%0AExtension%3C-ExtensionsRunner%3AgetNamedXContent()%0AExtension-%3EExtensionsRunner%3AcustomNamedXContent%0AExtensionsRunner%3C-ExtensionsRunner%3Aregister%20ExtensionsInitRequestHandler%0A%0AExtensionsRunner%3C-ExtensionsOrchestrator%3AInitializeExtensionsRequest%0Anote%20over%20ExtensionsRunner%2CExtensionNamedXContentRegistry%3A**ExtensionsInitRequestHandler**%5CnCombine%20customNamedXContent%5Cnand%20class-loaded%20XContent%20from%5CnIndicesModule%2C%20NetworkModule%2C%20and%5CnSearchModule%20(with%20Settings%20as%20a%5Cnparameter)%0A%0AOpenSearch%20Environment%20Settings%5Cnto%20create%20a%20combined%20registry%0AExtensionsRunner-%3EExtensionsOrchestrator%3AsendEnvironmentSettingsRequest()%0AExtensionsRunner%3C-ExtensionsOrchestrator%3AEnvironmentSettingsResponse%0AExtensionsRunner-%3E*ExtensionNamedXContentRegistry%3Anew%20instance%0AExtensionsRunner%3C-ExtensionNamedXContentRegistry%3AsetNamedXContentRegistry()%0Adestroysilent%20ExtensionNamedXContentRegistry%0Anote%20over%20Extension%2CExtensionNamedXContentRegistry%3AExtension%20can%20now%20access%20NamedXContentRegistry%0A%0A NamedXContent Registration Extension Node OpenSearch Node Extension ExtensionsRunner ExtensionsOrchestrator run(this) 1 setExtensionsRunner(this) 2 getNamedXContent() 3 customNamedXContent 4 register ExtensionsInitRequestHandler 5 InitializeExtensionsRequest 6 ExtensionsInitRequestHandler Combine customNamedXContent and class-loaded XContent from IndicesModule, NetworkModule, and SearchModule (with Settings as a parameter) sendEnvironmentSettingsRequest() 7 EnvironmentSettingsResponse 8 ExtensionNamedXContentRegistry new instance 9 setNamedXContentRegistry() 10 Extension can now access NamedXContentRegistry
diff --git a/src/main/java/org/opensearch/sdk/BaseExtension.java b/src/main/java/org/opensearch/sdk/BaseExtension.java
index b8d8cb61b..347773d73 100644
--- a/src/main/java/org/opensearch/sdk/BaseExtension.java
+++ b/src/main/java/org/opensearch/sdk/BaseExtension.java
@@ -20,6 +20,11 @@
* An abstract class that provides sample methods required by extensions
*/
public abstract class BaseExtension implements Extension {
+ /**
+ * The {@link ExtensionsRunner} instance running this extension
+ */
+ protected ExtensionsRunner extensionsRunner;
+
/**
* A client to make requests to the system
*/
@@ -66,14 +71,12 @@ public ExtensionSettings getExtensionSettings() {
return this.settings;
}
- /**
- * Returns components added by this extension.
- *
- * @param client A client to make requests to the system
- * @param clusterService A service to allow watching and updating cluster state
- * @param threadPool A service to allow retrieving an executor to run an async action
- * @return A collection of objects
- */
+ @Override
+ public void setExtensionsRunner(ExtensionsRunner extensionsRunner) {
+ this.extensionsRunner = extensionsRunner;
+ }
+
+ @Override
public Collection createComponents(SDKClient client, ClusterService clusterService, ThreadPool threadPool) {
this.client = client;
this.clusterService = clusterService;
diff --git a/src/main/java/org/opensearch/sdk/Extension.java b/src/main/java/org/opensearch/sdk/Extension.java
index a075f2cff..ee049c4d8 100644
--- a/src/main/java/org/opensearch/sdk/Extension.java
+++ b/src/main/java/org/opensearch/sdk/Extension.java
@@ -21,6 +21,7 @@
import org.opensearch.action.ActionResponse;
import org.opensearch.action.support.TransportAction;
import org.opensearch.common.settings.Setting;
+import org.opensearch.common.xcontent.NamedXContentRegistry;
/**
* This interface defines methods which an extension must provide. Extensions
@@ -52,6 +53,22 @@ default List> getSettings() {
return Collections.emptyList();
}
+ /**
+ * Gets an optional list of custom {@link NamedXContentRegistry.Entry} for the extension to combine with OpenSearch NamedXConent.
+ *
+ * @return a list of custom NamedXConent this extension uses.
+ */
+ default List getNamedXContent() {
+ return Collections.emptyList();
+ }
+
+ /**
+ * Set the Extension's instance of its corresponding {@link ExtensionsRunner}.
+ *
+ * @param extensionsRunner The ExtensionsRunner running this extension.
+ */
+ void setExtensionsRunner(ExtensionsRunner extensionsRunner);
+
/**
* Returns components added by this extension.
*
@@ -60,7 +77,7 @@ default List> getSettings() {
* @param threadPool A service to allow retrieving an executor to run an async action
* @return A collection of objects
*/
- public Collection createComponents(SDKClient client, ClusterService clusterService, ThreadPool threadPool);
+ Collection createComponents(SDKClient client, ClusterService clusterService, ThreadPool threadPool);
/**
* Gets an optional list of custom {@link TransportAction} for the extension to register with OpenSearch.
diff --git a/src/main/java/org/opensearch/sdk/ExtensionNamedXContentRegistry.java b/src/main/java/org/opensearch/sdk/ExtensionNamedXContentRegistry.java
new file mode 100644
index 000000000..432f03672
--- /dev/null
+++ b/src/main/java/org/opensearch/sdk/ExtensionNamedXContentRegistry.java
@@ -0,0 +1,59 @@
+/*
+ * Copyright OpenSearch Contributors
+ * 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.sdk;
+
+import static java.util.stream.Collectors.toList;
+
+import java.util.Collections;
+import java.util.List;
+import java.util.function.Function;
+import java.util.stream.Stream;
+
+import org.opensearch.cluster.ClusterModule;
+import org.opensearch.common.network.NetworkModule;
+import org.opensearch.common.settings.Settings;
+import org.opensearch.common.xcontent.NamedXContentRegistry;
+import org.opensearch.indices.IndicesModule;
+import org.opensearch.search.SearchModule;
+
+/**
+ * Combines Extension NamedXContent with core OpenSearch NamedXContent
+ */
+public class ExtensionNamedXContentRegistry {
+ private final NamedXContentRegistry namedXContentRegistry;
+
+ /**
+ * Creates and populates a NamedXContentRegistry with the given NamedXContentRegistry entries for this extension and
+ * locally defined content.
+ *
+ * @param settings OpenSearch environment settings
+ * @param extensionNamedXContent List of NamedXContentRegistry.Entry to be registered
+ */
+ public ExtensionNamedXContentRegistry(Settings settings, List extensionNamedXContent) {
+ this.namedXContentRegistry = new NamedXContentRegistry(
+ Stream.of(
+ extensionNamedXContent.stream(),
+ NetworkModule.getNamedXContents().stream(),
+ IndicesModule.getNamedXContents().stream(),
+ new SearchModule(settings, Collections.emptyList()).getNamedXContents().stream(),
+ ClusterModule.getNamedXWriteables().stream()
+ ).flatMap(Function.identity()).collect(toList())
+ );
+ }
+
+ /**
+ * Gets the NamedXContentRegistry.
+ *
+ * @return The NamedXContentRegistry. Includes both extension-defined XContent and core OpenSearch XContent.
+ */
+ public NamedXContentRegistry getRegistry() {
+ return this.namedXContentRegistry;
+ }
+}
diff --git a/src/main/java/org/opensearch/sdk/ExtensionsRunner.java b/src/main/java/org/opensearch/sdk/ExtensionsRunner.java
index 3df77b5d7..11ff9fcbf 100644
--- a/src/main/java/org/opensearch/sdk/ExtensionsRunner.java
+++ b/src/main/java/org/opensearch/sdk/ExtensionsRunner.java
@@ -20,6 +20,7 @@
import org.opensearch.extensions.settings.RegisterCustomSettingsRequest;
import org.opensearch.common.settings.Setting;
import org.opensearch.common.settings.Settings;
+import org.opensearch.common.xcontent.NamedXContentRegistry;
import org.opensearch.discovery.InitializeExtensionsRequest;
import org.opensearch.extensions.ExtensionActionListenerOnFailureRequest;
import org.opensearch.extensions.DiscoveryExtension;
@@ -50,6 +51,7 @@
import java.io.IOException;
import java.util.ArrayList;
+import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.function.Consumer;
@@ -64,6 +66,9 @@ public class ExtensionsRunner {
private static final Logger logger = LogManager.getLogger(ExtensionsRunner.class);
private static final String NODE_NAME_SETTING = "node.name";
+ // Set when initialization is complete
+ private boolean initialized = false;
+
private String uniqueId;
/**
* This field is initialized by a call from {@link ExtensionsInitRequestHandler}.
@@ -77,16 +82,27 @@ public class ExtensionsRunner {
// The routes and classes which handle the REST requests
private final ExtensionRestPathRegistry extensionRestPathRegistry = new ExtensionRestPathRegistry();
- // Custom settings from the extension's getSettings
/**
- * This field is initialized by a call from {@link ExtensionsInitRequestHandler}.
+ * Custom namedXContent from the extension's getNamedXContent. This field is initialized in the constructor.
+ */
+ private final List customNamedXContent;
+ /**
+ * Custom settings from the extension's getSettings. This field is initialized in the constructor.
*/
private final List> customSettings;
- // Node name, host, and port
/**
- * This field is initialized by a call from {@link ExtensionsInitRequestHandler}.
+ * Environment settings from OpenSearch. This field is initialized by a call from
+ * {@link ExtensionsInitRequestHandler}.
+ */
+ private Settings environmentSettings = Settings.EMPTY;
+ /**
+ * Node name, host, and port. This field is initialized by a call from {@link ExtensionsInitRequestHandler}.
*/
public final Settings settings;
+ private ExtensionNamedXContentRegistry extensionNamedXContentRegistry = new ExtensionNamedXContentRegistry(
+ Settings.EMPTY,
+ Collections.emptyList()
+ );
private ExtensionNamedWriteableRegistry namedWriteableRegistry = new ExtensionNamedWriteableRegistry();
private ExtensionsInitRequestHandler extensionsInitRequestHandler = new ExtensionsInitRequestHandler(this);
private OpensearchRequestHandler opensearchRequestHandler = new OpensearchRequestHandler(namedWriteableRegistry);
@@ -114,10 +130,11 @@ public class ExtensionsRunner {
/**
* Instantiates a new Extensions Runner using the specified extension.
*
- * @param extension The settings with which to start the runner.
+ * @param extension The settings with which to start the runner.
* @throws IOException if the runner failed to read settings or API.
*/
protected ExtensionsRunner(Extension extension) throws IOException {
+ extension.setExtensionsRunner(this);
ExtensionSettings extensionSettings = extension.getExtensionSettings();
this.settings = Settings.builder()
.put(NODE_NAME_SETTING, extensionSettings.getExtensionName())
@@ -132,6 +149,8 @@ protected ExtensionsRunner(Extension extension) throws IOException {
}
// save custom settings
this.customSettings = extension.getSettings();
+ // save custom namedXContent
+ this.customNamedXContent = extension.getNamedXContent();
// save custom transport actions
this.transportActions = new TransportActions(extension.getActions());
@@ -142,15 +161,70 @@ protected ExtensionsRunner(Extension extension) throws IOException {
}
/**
- * This method is call from {@link ExtensionsInitRequestHandler}.
+ * Marks the extension initialized.
+ */
+ public void setInitialized() {
+ this.initialized = true;
+ logger.info("Extension initialization is complete!");
+ }
+
+ /**
+ * Reports if the extension has finished initializing.
+ *
+ * @return true if the extension has been initialized
+ */
+ boolean isInitialized() {
+ return this.initialized;
+ }
+
+ /**
+ * Sets the TransportService. Called from {@link ExtensionsInitRequestHandler}.
+ *
* @param extensionTransportService assign value for extensionTransportService
*/
void setExtensionTransportService(TransportService extensionTransportService) {
this.extensionTransportService = extensionTransportService;
}
+ /**
+ * Sets the Environment Settings. Called from {@link ExtensionsInitRequestHandler}.
+ *
+ * @param settings assign value for environmentSettings
+ */
+ public void setEnvironmentSettings(Settings settings) {
+ this.environmentSettings = settings;
+ }
+
+ /**
+ * Gets the Environment Settings. Only valid if {@link #isInitialized()} returns true.
+ *
+ * @return the environment settings if initialized, an empty settings object otherwise.
+ */
+ public Settings getEnvironmentSettings() {
+ return this.environmentSettings;
+ }
+
+ /**
+ * Sets the NamedXContentRegistry. Called from {@link ExtensionsInitRequestHandler}.
+ *
+ * @param registry assign value for namedXContentRegistry
+ */
+ public void setNamedXContentRegistry(ExtensionNamedXContentRegistry registry) {
+ this.extensionNamedXContentRegistry = registry;
+ }
+
+ /**
+ * Gets the NamedXContentRegistry. Only valid if {@link #isInitialized()} returns true.
+ *
+ * @return the NamedXContentRegistry if initialized, an empty registry otherwise.
+ */
+ public ExtensionNamedXContentRegistry getNamedXContentRegistry() {
+ return this.extensionNamedXContentRegistry;
+ }
+
/**
* Sets the Unique ID, used in REST requests to uniquely identify this extension
+ *
* @param id assign value for id
*/
public void setUniqueId(String id) {
@@ -173,6 +247,10 @@ public DiscoveryNode getOpensearchNode() {
return this.opensearchNode;
}
+ public List getCustomNamedXContent() {
+ return this.customNamedXContent;
+ }
+
/**
* Starts a TransportService.
*
diff --git a/src/main/java/org/opensearch/sdk/handlers/ExtensionsInitRequestHandler.java b/src/main/java/org/opensearch/sdk/handlers/ExtensionsInitRequestHandler.java
index e528f7722..db0867435 100644
--- a/src/main/java/org/opensearch/sdk/handlers/ExtensionsInitRequestHandler.java
+++ b/src/main/java/org/opensearch/sdk/handlers/ExtensionsInitRequestHandler.java
@@ -11,8 +11,10 @@
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
+import org.opensearch.common.settings.Settings;
import org.opensearch.discovery.InitializeExtensionsRequest;
import org.opensearch.discovery.InitializeExtensionsResponse;
+import org.opensearch.sdk.ExtensionNamedXContentRegistry;
import org.opensearch.sdk.ExtensionsRunner;
import org.opensearch.transport.TransportService;
@@ -28,6 +30,7 @@ public class ExtensionsInitRequestHandler {
/**
* Instantiate this object with a reference to the ExtensionsRunner
+ *
* @param extensionsRunner the ExtensionsRunner instance
*/
public ExtensionsInitRequestHandler(ExtensionsRunner extensionsRunner) {
@@ -35,7 +38,7 @@ public ExtensionsInitRequestHandler(ExtensionsRunner extensionsRunner) {
}
/**
- * Handles a extension request from OpenSearch. This is the first request for the transport communication and will initialize the extension and will be a part of OpenSearch bootstrap.
+ * Handles a extension request from OpenSearch. This is the first request for the transport communication and will initialize the extension and will be a part of OpenSearch bootstrap.
*
* @param extensionInitRequest The request to handle.
* @return A response to OpenSearch validating that this is an extension.
@@ -60,6 +63,15 @@ public InitializeExtensionsResponse handleExtensionInitRequest(InitializeExtensi
extensionsRunner.opensearchNode,
extensionsRunner.getUniqueId()
);
+ // Get OpenSearch Settings and set values on ExtensionsRunner
+ Settings settings = extensionsRunner.sendEnvironmentSettingsRequest(extensionTransportService);
+ extensionsRunner.setEnvironmentSettings(settings);
+ extensionsRunner.setNamedXContentRegistry(
+ new ExtensionNamedXContentRegistry(settings, extensionsRunner.getCustomNamedXContent())
+ );
+
+ // Last step of initialization
+ extensionsRunner.setInitialized();
}
}
}
diff --git a/src/test/java/org/opensearch/sdk/TestExtensionNamedXContentRegistry.java b/src/test/java/org/opensearch/sdk/TestExtensionNamedXContentRegistry.java
new file mode 100644
index 000000000..2fd8af938
--- /dev/null
+++ b/src/test/java/org/opensearch/sdk/TestExtensionNamedXContentRegistry.java
@@ -0,0 +1,146 @@
+/*
+ * Copyright OpenSearch Contributors
+ * 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.sdk;
+
+import static org.opensearch.common.xcontent.XContentParserUtils.ensureExpectedToken;
+
+import org.opensearch.cluster.ClusterModule;
+import org.opensearch.common.ParseField;
+import org.opensearch.common.bytes.BytesReference;
+import org.opensearch.common.network.NetworkModule;
+import org.opensearch.common.settings.Settings;
+import org.opensearch.common.xcontent.LoggingDeprecationHandler;
+import org.opensearch.common.xcontent.NamedXContentRegistry;
+import org.opensearch.common.xcontent.ToXContentObject;
+import org.opensearch.common.xcontent.XContentBuilder;
+import org.opensearch.common.xcontent.XContentParser;
+import org.opensearch.common.xcontent.XContentType;
+import org.opensearch.common.xcontent.json.JsonXContent;
+import org.opensearch.indices.IndicesModule;
+import org.opensearch.search.SearchModule;
+import org.opensearch.test.OpenSearchTestCase;
+import java.io.IOException;
+import java.util.Collections;
+import java.util.List;
+import java.util.Objects;
+
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+
+public class TestExtensionNamedXContentRegistry extends OpenSearchTestCase {
+ private ExtensionNamedXContentRegistry extensionNamedXContentRegistry;
+
+ private static class Example implements ToXContentObject {
+ public static final String NAME = "Example";
+ public static final NamedXContentRegistry.Entry XCONTENT_REGISTRY = new NamedXContentRegistry.Entry(
+ Example.class,
+ new ParseField(NAME),
+ it -> parse(it)
+ );
+ public static final String NAME_FIELD = "name";
+
+ private final String name;
+
+ public Example(String name) {
+ this.name = name;
+ }
+
+ public String getName() {
+ return this.name;
+ }
+
+ public static Example parse(XContentParser parser) throws IOException {
+ String name = null;
+
+ ensureExpectedToken(XContentParser.Token.START_OBJECT, parser.currentToken(), parser);
+ while (parser.nextToken() != XContentParser.Token.END_OBJECT) {
+ String fieldName = parser.currentName();
+ parser.nextToken();
+
+ switch (fieldName) {
+ case NAME_FIELD:
+ name = parser.text();
+ break;
+ default:
+ parser.skipChildren();
+ break;
+ }
+ }
+ return new Example(name);
+ }
+
+ @Override
+ public XContentBuilder toXContent(XContentBuilder builder, Params params) throws IOException {
+ XContentBuilder xContentBuilder = builder.startObject().field(NAME_FIELD, name);
+ return xContentBuilder.endObject();
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (o == null || getClass() != o.getClass()) {
+ return false;
+ }
+ Example that = (Example) o;
+ return Objects.equals(name, that.name);
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(name);
+ }
+ }
+
+ @Override
+ @BeforeEach
+ public void setUp() {
+ List namedXContents = Collections.singletonList(Example.XCONTENT_REGISTRY);
+ this.extensionNamedXContentRegistry = new ExtensionNamedXContentRegistry(Settings.EMPTY, namedXContents);
+ }
+
+ @Test
+ public void testNamedXContentRegistryParse() throws IOException {
+ // Tests parsing the custom namedXContent
+ BytesReference bytes = BytesReference.bytes(
+ JsonXContent.contentBuilder().startObject().field(Example.NAME_FIELD, Example.NAME).endObject()
+ );
+
+ NamedXContentRegistry registry = this.extensionNamedXContentRegistry.getRegistry();
+ XContentParser parser = XContentType.JSON.xContent()
+ .createParser(registry, LoggingDeprecationHandler.INSTANCE, bytes.streamInput());
+ ensureExpectedToken(XContentParser.Token.START_OBJECT, parser.nextToken(), parser);
+
+ Example example = registry.parseNamedObject(Example.class, Example.NAME, parser, null);
+ assertEquals(Example.NAME, example.getName());
+ }
+
+ @Test
+ public void testNamedXContentRegistryExceptions() {
+ // Tests that the registry includes module contents and generates conflicts when adding
+ assertThrows(
+ IllegalArgumentException.class,
+ () -> new ExtensionNamedXContentRegistry(Settings.EMPTY, NetworkModule.getNamedXContents())
+ );
+ assertThrows(
+ IllegalArgumentException.class,
+ () -> new ExtensionNamedXContentRegistry(Settings.EMPTY, IndicesModule.getNamedXContents())
+ );
+ assertThrows(
+ IllegalArgumentException.class,
+ () -> new ExtensionNamedXContentRegistry(
+ Settings.EMPTY,
+ new SearchModule(Settings.EMPTY, Collections.emptyList()).getNamedXContents()
+ )
+ );
+ assertThrows(
+ IllegalArgumentException.class,
+ () -> new ExtensionNamedXContentRegistry(Settings.EMPTY, ClusterModule.getNamedXWriteables())
+ );
+ }
+}
diff --git a/src/test/java/org/opensearch/sdk/TestExtensionsRunner.java b/src/test/java/org/opensearch/sdk/TestExtensionsRunner.java
index 505f2778e..131466c07 100644
--- a/src/test/java/org/opensearch/sdk/TestExtensionsRunner.java
+++ b/src/test/java/org/opensearch/sdk/TestExtensionsRunner.java
@@ -20,6 +20,7 @@
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
+import java.io.IOException;
import java.net.InetAddress;
import java.net.UnknownHostException;
import java.nio.charset.StandardCharsets;
@@ -36,6 +37,7 @@
import org.opensearch.common.settings.Settings;
import org.opensearch.common.settings.Setting.Property;
import org.opensearch.common.transport.TransportAddress;
+import org.opensearch.common.xcontent.NamedXContentRegistry;
import org.opensearch.discovery.InitializeExtensionsRequest;
import org.opensearch.discovery.InitializeExtensionsResponse;
import org.opensearch.extensions.DiscoveryExtension;
@@ -241,4 +243,21 @@ public void testRegisterCustomSettingsRequest() {
verify(transportService, times(1)).sendRequest(any(), anyString(), any(), any(ExtensionStringResponseHandler.class));
}
+
+ @Test
+ public void testGettersAndSetters() throws IOException {
+ assertFalse(extensionsRunner.isInitialized());
+ extensionsRunner.setInitialized();
+ assertTrue(extensionsRunner.isInitialized());
+
+ Settings settings = Settings.builder().put("test.key", "test.value").build();
+ assertTrue(extensionsRunner.getEnvironmentSettings().isEmpty());
+ extensionsRunner.setEnvironmentSettings(settings);
+ assertEquals("test.value", extensionsRunner.getEnvironmentSettings().get("test.key"));
+
+ assertTrue(extensionsRunner.getCustomNamedXContent().isEmpty());
+ assertTrue(extensionsRunner.getNamedXContentRegistry().getRegistry() instanceof NamedXContentRegistry);
+ extensionsRunner.setNamedXContentRegistry(null);
+ assertNull(extensionsRunner.getNamedXContentRegistry());
+ }
}