Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
*/
package org.apache.solr.client.api.endpoint;

import static org.apache.solr.client.api.util.Constants.ADDTL_FIELDS_PROPERTY;
import static org.apache.solr.client.api.util.Constants.GENERIC_ENTITY_PROPERTY;

import io.swagger.v3.oas.annotations.Operation;
Expand Down Expand Up @@ -147,7 +148,12 @@ SchemaDesignerAddResponse addSchemaObject(
SchemaDesignerUpdateResponse updateSchemaObject(
@PathParam("configSet") String configSet,
@QueryParam("schemaVersion") Integer schemaVersion,
SchemaDesignerUpdateRequestBody requestBody)
@RequestBody(
extensions = {
@Extension(
properties = {@ExtensionProperty(name = ADDTL_FIELDS_PROPERTY, value = "true")})
})
SchemaDesignerUpdateRequestBody requestBody)
throws Exception;

@PUT
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,6 @@
package org.apache.solr.client.api.model;

import com.fasterxml.jackson.annotation.JsonProperty;
import io.swagger.v3.oas.annotations.media.Schema;
import java.util.Map;

/**
Expand All @@ -27,19 +26,15 @@
*/
public class SchemaDesignerAddRequestBody {

@Schema(name = "addField")
@JsonProperty("add-field")
public Map<String, Object> addField;

@Schema(name = "addDynamicField")
@JsonProperty("add-dynamic-field")
public Map<String, Object> addDynamicField;

@Schema(name = "addCopyField")
@JsonProperty("add-copy-field")
public Map<String, Object> addCopyField;

@Schema(name = "addFieldType")
@JsonProperty("add-field-type")
public Map<String, Object> addFieldType;
}
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
import com.fasterxml.jackson.annotation.JsonAnyGetter;
import com.fasterxml.jackson.annotation.JsonAnySetter;
import com.fasterxml.jackson.annotation.JsonProperty;
import io.swagger.v3.oas.annotations.media.Schema;
import java.util.HashMap;
import java.util.Map;

Expand All @@ -28,6 +29,7 @@
* indexed}, {@code stored}, {@code analyzer}, {@code copyDest}) are captured via the dynamic {@code
* additionalProperties} map and forwarded to the Schema API.
*/
@Schema(additionalProperties = Schema.AdditionalPropertiesValue.TRUE)
public class SchemaDesignerUpdateRequestBody {

@JsonProperty public String name;
Expand All @@ -36,6 +38,7 @@ public class SchemaDesignerUpdateRequestBody {
// Accessed via @JsonAnyGetter / @JsonAnySetter for JSON (de)serialization.
public Map<String, Object> additionalProperties = new HashMap<>();

@Schema(hidden = true)
@JsonAnyGetter
public Map<String, Object> getAdditionalProperties() {
return additionalProperties;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,16 +19,19 @@
import com.fasterxml.jackson.annotation.JsonAnyGetter;
import com.fasterxml.jackson.annotation.JsonAnySetter;
import com.fasterxml.jackson.annotation.JsonProperty;
import io.swagger.v3.oas.annotations.media.Schema;
import java.util.HashMap;
import java.util.Map;

@Schema(additionalProperties = Schema.AdditionalPropertiesValue.TRUE)
public class UpsertDynamicFieldOperation extends SchemaChange {
@JsonProperty public String name;
@JsonProperty public String type;

// Used for setting index and stored settings, etc.
private Map<String, Object> additionalProperties = new HashMap<>();

@Schema(hidden = true)
@JsonAnyGetter
public Map<String, Object> getAdditionalProperties() {
return additionalProperties;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,9 +19,11 @@
import com.fasterxml.jackson.annotation.JsonAnyGetter;
import com.fasterxml.jackson.annotation.JsonAnySetter;
import com.fasterxml.jackson.annotation.JsonProperty;
import io.swagger.v3.oas.annotations.media.Schema;
import java.util.HashMap;
import java.util.Map;

@Schema(additionalProperties = Schema.AdditionalPropertiesValue.TRUE)
public class UpsertFieldOperation extends SchemaChange {
@JsonProperty public String name;

Expand All @@ -30,6 +32,7 @@ public class UpsertFieldOperation extends SchemaChange {
// Used for setting index and stored settings, etc.
private Map<String, Object> additionalProperties = new HashMap<>();

@Schema(hidden = true)
@JsonAnyGetter
public Map<String, Object> getAdditionalProperties() {
return additionalProperties;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,18 +19,26 @@
import com.fasterxml.jackson.annotation.JsonAnyGetter;
import com.fasterxml.jackson.annotation.JsonAnySetter;
import com.fasterxml.jackson.annotation.JsonProperty;
import io.swagger.v3.oas.annotations.media.Schema;
import java.util.HashMap;
import java.util.Map;

@Schema(additionalProperties = Schema.AdditionalPropertiesValue.TRUE)
public class UpsertFieldTypeOperation extends SchemaChange {
@JsonProperty public String name;

// Field named to match what the OpenAPI-generated SolrJ client derives as a Java-safe
// identifier for the reserved word "class" (see api.mustache's {{name}} usage) -- the
// SolrJ codegen assigns into this field by that exact name, so a hand-picked name here
// (e.g. "className") would compile-fail the generated client the moment this class stops
// being shielded from per-field setter generation (see SchemaChange's oneOf discriminator).
@JsonProperty("class")
public String className;
public String propertyClass;

// Used for setting analyzers, index and stored settings, etc.
private Map<String, Object> additionalProperties = new HashMap<>();

@Schema(hidden = true)
@JsonAnyGetter
public Map<String, Object> getAdditionalProperties() {
return additionalProperties;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,7 @@ public void testAddFieldType() throws Exception {
assertThat(parsedGeneric, instanceOf(UpsertFieldTypeOperation.class));
final var parsedSpecific = (UpsertFieldTypeOperation) parsedGeneric;
assertEquals("my-new-field-type", parsedSpecific.name);
assertEquals("org.apache.my.ClassName", parsedSpecific.className);
assertEquals("org.apache.my.ClassName", parsedSpecific.propertyClass);
// Arbitrary properties are put in a map, and can contain nesting
assertEquals(100, parsedSpecific.getAdditionalProperties().get("positionIncrementGap"));
assertThat(parsedSpecific.getAdditionalProperties().get("analyzer"), instanceOf(Map.class));
Expand Down Expand Up @@ -272,7 +272,7 @@ public void testReplaceFieldType() throws Exception {
assertThat(parsedGeneric, instanceOf(UpsertFieldTypeOperation.class));
final var parsedSpecific = (UpsertFieldTypeOperation) parsedGeneric;
assertEquals("my-new-field-type", parsedSpecific.name);
assertEquals("org.apache.my.ClassName", parsedSpecific.className);
assertEquals("org.apache.my.ClassName", parsedSpecific.propertyClass);
// Arbitrary properties are put in a map, and can contain nesting
assertEquals(100, parsedSpecific.getAdditionalProperties().get("positionIncrementGap"));
assertThat(parsedSpecific.getAdditionalProperties().get("analyzer"), instanceOf(Map.class));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -122,7 +122,7 @@ public SolrJerseyResponse addFieldType(String fieldTypeName, UpsertFieldTypeOper
ensureSchemaMutable();
ensureRequiredRequestBodyProvided(requestBody);
ensureRequiredParameterProvided("fieldTypeName", fieldTypeName);
ensureRequiredParameterProvided("class", requestBody.className);
ensureRequiredParameterProvided("class", requestBody.propertyClass);
requestBody.operationType = "add-field-type";

runWithSchemaManager(List.of(requestBody), response);
Expand Down
4 changes: 2 additions & 2 deletions solr/core/src/java/org/apache/solr/schema/SchemaManager.java
Original file line number Diff line number Diff line change
Expand Up @@ -240,7 +240,7 @@ public enum OpType {
public boolean perform(SchemaChange op, SchemaManager mgr) throws SchemaOperationException {
final var addFieldTypeOp = (UpsertFieldTypeOperation) op;
String name = ensureNotNull("name", addFieldTypeOp.name);
String className = ensureNotNull("class", addFieldTypeOp.className);
String className = ensureNotNull("class", addFieldTypeOp.propertyClass);
try {
FieldType fieldType =
mgr.managedIndexSchema.newFieldType(name, className, convertToMap(addFieldTypeOp));
Expand Down Expand Up @@ -420,7 +420,7 @@ public boolean perform(SchemaChange op, SchemaManager mgr) throws SchemaOperatio
public boolean perform(SchemaChange op, SchemaManager mgr) throws SchemaOperationException {
final var replaceFieldTypeOp = (UpsertFieldTypeOperation) op;
String name = ensureNotNull("name", replaceFieldTypeOp.name);
String className = ensureNotNull("class", replaceFieldTypeOp.className);
String className = ensureNotNull("class", replaceFieldTypeOp.propertyClass);
try {
mgr.managedIndexSchema =
mgr.managedIndexSchema.replaceFieldType(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -123,7 +123,7 @@ public void testDeleteDynamicFieldOperationRequiresFieldName() {
@Test
public void testAddFieldTypeOperationRequiresTypeNameAndClass() {
final var noTypeOp = new UpsertFieldTypeOperation();
noTypeOp.className = "solr.TextField";
noTypeOp.propertyClass = "solr.TextField";
var thrown =
expectThrows(
SolrException.class,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -123,7 +123,9 @@ public void testTypedBodyRoundTrip() throws Exception {
var update = new SchemaDesignerApi.UpdateSchemaObject(configSet);
update.setSchemaVersion(schemaVersion);
update.setName("keywords");
update.setAdditionalProperties(Map.of("type", "string", "stored", true, "multiValued", true));
update.setAdditionalProperty("type", "string");
update.setAdditionalProperty("stored", true);
update.setAdditionalProperty("multiValued", true);
SchemaDesignerUpdateResponse updateResp = update.process(cluster.getSolrClient());
assertNotNull(updateResp.field);
assertEquals("field", updateResp.updateType);
Expand Down
11 changes: 8 additions & 3 deletions solr/solrj/src/resources/java-template/api.mustache
Original file line number Diff line number Diff line change
Expand Up @@ -179,13 +179,18 @@ public class {{classname}} {
{{#bodyParam}}
{{#vars}}
// TODO find a way to add required parameters in the request body to the class constructor
// The setter parameter (and the requestBody.<field> access below) both use the
// "name" var: openapi-generator's Java-safe sanitization of this property's wire
// name (e.g. the reserved word "class" becomes "propertyClass"). The hand-written
// requestBody model class's field MUST be named to match exactly, or the field
// access below fails to compile.
{{#description}}
/**
* @param {{baseName}} {{description}}
* @param {{name}} {{description}}
*/
{{/description}}
public void {{setter}}({{{dataType}}} {{baseName}}) {
this.requestBody.{{baseName}} = {{baseName}};
public void {{setter}}({{{dataType}}} {{name}}) {
this.requestBody.{{name}} = {{name}};
Comment thread
epugh marked this conversation as resolved.
}
{{/vars}}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,6 @@
*/
package org.apache.solr.webapp;

import org.apache.lucene.tests.util.LuceneTestCase;
import org.junit.Test;
import org.openqa.selenium.By;
import org.openqa.selenium.WebElement;
Expand All @@ -25,11 +24,9 @@
* Happy-path test of the Schema Designer screen: create a new schema, paste a sample document and
* let the designer analyze it.
*
* <p>AwaitsFix: the designer backend transiently fails its own prep/analyze calls ("version
* mismatch, retry", "Error loading solr config") when driven at automation speed, making this test
* flaky even with retries.
* <p>The Analyze action remains disabled until creation of the mutable schema has completed, so a
* fast user cannot race the prep and analyze requests.
*/
@LuceneTestCase.AwaitsFix(bugUrl = "https://issues.apache.org/jira/browse/SOLR-18347")
public class AdminUiSchemaDesignerTest extends AdminUiTestBase {

@Test
Expand All @@ -47,34 +44,17 @@ public void testDesignSchemaFromSampleDocument() throws Exception {
WebElement sampleDocs = waitFor(By.cssSelector("#sample-docs textarea#document"));
sampleDocs.clear();
sampleDocs.sendKeys("[{\"id\":\"1\",\"designer_title\":\"Hello Designer\"}]");
click(By.id("analyze"));
click(By.cssSelector("#analyze:not([disabled])"));

// the analyzed schema lists the field derived from the sample doc. The designer
// backend transiently fails its own calls ("version mismatch, retry", "Error
// loading solr config") and surfaces an error dialog - dismiss it and analyze
// again, with a generous budget since each round trips several requests
long deadlineNanos = System.nanoTime() + WAIT_TIMEOUT.multipliedBy(3).toNanos();
boolean analyzed = false;
while (!analyzed && System.nanoTime() < deadlineNanos) {
analyzed = driver.getPageSource().contains("designer_title");
if (!analyzed) {
for (String dismissButton : new String[] {"Reload Schema", "OK"}) {
driver.findElements(By.xpath("//button[contains(., '" + dismissButton + "')]")).stream()
.filter(WebElement::isDisplayed)
.findFirst()
.ifPresent(WebElement::click);
}
driver.findElements(By.id("analyze")).stream()
.filter(WebElement::isDisplayed)
.findFirst()
.ifPresent(WebElement::click);
Thread.sleep(500);
}
}
assertTrue("Analyzed schema should list the sample doc field", analyzed);
// the designer's own API calls (prep/analyze/luke against its temp core) error
// transiently while it persists and reloads the schema - it recovers via its retry
// dialog, so only unrelated console errors fail the test
assertNoSevereConsoleErrors("schema-designer/", "._designer_");
waitForPageContains("designer_title");
assertNoSevereConsoleErrors();

// add a field through the UI
click(By.cssSelector("#addField"));
setText(By.id("add_name"), "extra_test_field");
click(By.xpath("//button[@ng-click='addField()']"));

waitForPageContains("extra_test_field");
assertNoSevereConsoleErrors();
}
}
Loading
Loading