This is an automated email from the ASF dual-hosted git repository.
epugh pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/solr.git
The following commit(s) were added to refs/heads/main by this push:
new 0b1d3f75d5c SOLR-18152: improve body format OpenAPI annotations to
fully communicate SchemaDesigner API (#4819)
0b1d3f75d5c is described below
commit 0b1d3f75d5c1bdacd966584299b47366537395f8
Author: Eric Pugh <[email protected]>
AuthorDate: Fri Sep 4 12:46:43 2026 -0400
SOLR-18152: improve body format OpenAPI annotations to fully communicate
SchemaDesigner API (#4819)
---
.../client/api/endpoint/SchemaDesignerApi.java | 8 +-
.../api/model/SchemaDesignerAddRequestBody.java | 5 -
.../api/model/SchemaDesignerUpdateRequestBody.java | 3 +
.../api/model/UpsertDynamicFieldOperation.java | 3 +
.../client/api/model/UpsertFieldOperation.java | 3 +
.../client/api/model/UpsertFieldTypeOperation.java | 10 +-
.../SchemaChangeOperationSerializationTest.java | 4 +-
.../solr/handler/admin/api/UpdateSchema.java | 2 +-
.../java/org/apache/solr/schema/SchemaManager.java | 4 +-
.../admin/api/V2UpdateSchemaErrorCaseTests.java | 2 +-
.../handler/designer/TestSchemaDesignerSolrJ.java | 4 +-
.../solrj/src/resources/java-template/api.mustache | 11 +-
.../solr/webapp/AdminUiSchemaDesignerTest.java | 46 +--
.../web/js/angular/controllers/schema-designer.js | 460 ++++++++++++---------
solr/webapp/web/js/angular/services.js | 17 +-
solr/webapp/web/partials/schema-designer.html | 4 +-
16 files changed, 341 insertions(+), 245 deletions(-)
diff --git
a/solr/api/src/java/org/apache/solr/client/api/endpoint/SchemaDesignerApi.java
b/solr/api/src/java/org/apache/solr/client/api/endpoint/SchemaDesignerApi.java
index 572891d6ec5..d319471857d 100644
---
a/solr/api/src/java/org/apache/solr/client/api/endpoint/SchemaDesignerApi.java
+++
b/solr/api/src/java/org/apache/solr/client/api/endpoint/SchemaDesignerApi.java
@@ -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;
@@ -147,7 +148,12 @@ public interface SchemaDesignerApi {
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
diff --git
a/solr/api/src/java/org/apache/solr/client/api/model/SchemaDesignerAddRequestBody.java
b/solr/api/src/java/org/apache/solr/client/api/model/SchemaDesignerAddRequestBody.java
index 821cabc4246..25e0da72a79 100644
---
a/solr/api/src/java/org/apache/solr/client/api/model/SchemaDesignerAddRequestBody.java
+++
b/solr/api/src/java/org/apache/solr/client/api/model/SchemaDesignerAddRequestBody.java
@@ -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;
/**
@@ -27,19 +26,15 @@ import java.util.Map;
*/
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;
}
diff --git
a/solr/api/src/java/org/apache/solr/client/api/model/SchemaDesignerUpdateRequestBody.java
b/solr/api/src/java/org/apache/solr/client/api/model/SchemaDesignerUpdateRequestBody.java
index 54b9bb9e56c..e88114462e5 100644
---
a/solr/api/src/java/org/apache/solr/client/api/model/SchemaDesignerUpdateRequestBody.java
+++
b/solr/api/src/java/org/apache/solr/client/api/model/SchemaDesignerUpdateRequestBody.java
@@ -19,6 +19,7 @@ package org.apache.solr.client.api.model;
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;
@@ -28,6 +29,7 @@ import java.util.Map;
* 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;
@@ -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;
diff --git
a/solr/api/src/java/org/apache/solr/client/api/model/UpsertDynamicFieldOperation.java
b/solr/api/src/java/org/apache/solr/client/api/model/UpsertDynamicFieldOperation.java
index db010fbacca..2a0376fafc3 100644
---
a/solr/api/src/java/org/apache/solr/client/api/model/UpsertDynamicFieldOperation.java
+++
b/solr/api/src/java/org/apache/solr/client/api/model/UpsertDynamicFieldOperation.java
@@ -19,9 +19,11 @@ package org.apache.solr.client.api.model;
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;
@@ -29,6 +31,7 @@ public class UpsertDynamicFieldOperation 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;
diff --git
a/solr/api/src/java/org/apache/solr/client/api/model/UpsertFieldOperation.java
b/solr/api/src/java/org/apache/solr/client/api/model/UpsertFieldOperation.java
index c6a7d1c3c32..6946f3536c8 100644
---
a/solr/api/src/java/org/apache/solr/client/api/model/UpsertFieldOperation.java
+++
b/solr/api/src/java/org/apache/solr/client/api/model/UpsertFieldOperation.java
@@ -19,9 +19,11 @@ package org.apache.solr.client.api.model;
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;
@@ -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;
diff --git
a/solr/api/src/java/org/apache/solr/client/api/model/UpsertFieldTypeOperation.java
b/solr/api/src/java/org/apache/solr/client/api/model/UpsertFieldTypeOperation.java
index 6a517830b2e..60ee25122c8 100644
---
a/solr/api/src/java/org/apache/solr/client/api/model/UpsertFieldTypeOperation.java
+++
b/solr/api/src/java/org/apache/solr/client/api/model/UpsertFieldTypeOperation.java
@@ -19,18 +19,26 @@ package org.apache.solr.client.api.model;
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;
diff --git
a/solr/api/src/test/org/apache/solr/client/api/model/SchemaChangeOperationSerializationTest.java
b/solr/api/src/test/org/apache/solr/client/api/model/SchemaChangeOperationSerializationTest.java
index 48afd83e694..a0606bd6d3d 100644
---
a/solr/api/src/test/org/apache/solr/client/api/model/SchemaChangeOperationSerializationTest.java
+++
b/solr/api/src/test/org/apache/solr/client/api/model/SchemaChangeOperationSerializationTest.java
@@ -58,7 +58,7 @@ public class SchemaChangeOperationSerializationTest extends
SolrTestCase {
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));
@@ -272,7 +272,7 @@ public class SchemaChangeOperationSerializationTest extends
SolrTestCase {
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));
diff --git
a/solr/core/src/java/org/apache/solr/handler/admin/api/UpdateSchema.java
b/solr/core/src/java/org/apache/solr/handler/admin/api/UpdateSchema.java
index 1bb3f51c4c3..6bb00ea00f6 100644
--- a/solr/core/src/java/org/apache/solr/handler/admin/api/UpdateSchema.java
+++ b/solr/core/src/java/org/apache/solr/handler/admin/api/UpdateSchema.java
@@ -122,7 +122,7 @@ public class UpdateSchema extends JerseyResource implements
UpdateSchemaApi {
ensureSchemaMutable();
ensureRequiredRequestBodyProvided(requestBody);
ensureRequiredParameterProvided("fieldTypeName", fieldTypeName);
- ensureRequiredParameterProvided("class", requestBody.className);
+ ensureRequiredParameterProvided("class", requestBody.propertyClass);
requestBody.operationType = "add-field-type";
runWithSchemaManager(List.of(requestBody), response);
diff --git a/solr/core/src/java/org/apache/solr/schema/SchemaManager.java
b/solr/core/src/java/org/apache/solr/schema/SchemaManager.java
index a65eb18df6a..05efe512c21 100644
--- a/solr/core/src/java/org/apache/solr/schema/SchemaManager.java
+++ b/solr/core/src/java/org/apache/solr/schema/SchemaManager.java
@@ -240,7 +240,7 @@ public class SchemaManager {
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));
@@ -420,7 +420,7 @@ public class SchemaManager {
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(
diff --git
a/solr/core/src/test/org/apache/solr/handler/admin/api/V2UpdateSchemaErrorCaseTests.java
b/solr/core/src/test/org/apache/solr/handler/admin/api/V2UpdateSchemaErrorCaseTests.java
index 54d063dc31e..d981f572a0b 100644
---
a/solr/core/src/test/org/apache/solr/handler/admin/api/V2UpdateSchemaErrorCaseTests.java
+++
b/solr/core/src/test/org/apache/solr/handler/admin/api/V2UpdateSchemaErrorCaseTests.java
@@ -123,7 +123,7 @@ public class V2UpdateSchemaErrorCaseTests extends
SolrTestCase {
@Test
public void testAddFieldTypeOperationRequiresTypeNameAndClass() {
final var noTypeOp = new UpsertFieldTypeOperation();
- noTypeOp.className = "solr.TextField";
+ noTypeOp.propertyClass = "solr.TextField";
var thrown =
expectThrows(
SolrException.class,
diff --git
a/solr/core/src/test/org/apache/solr/handler/designer/TestSchemaDesignerSolrJ.java
b/solr/core/src/test/org/apache/solr/handler/designer/TestSchemaDesignerSolrJ.java
index d755b6964d9..9d3a4fe1384 100644
---
a/solr/core/src/test/org/apache/solr/handler/designer/TestSchemaDesignerSolrJ.java
+++
b/solr/core/src/test/org/apache/solr/handler/designer/TestSchemaDesignerSolrJ.java
@@ -123,7 +123,9 @@ public class TestSchemaDesignerSolrJ extends
SolrCloudTestCase {
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);
diff --git a/solr/solrj/src/resources/java-template/api.mustache
b/solr/solrj/src/resources/java-template/api.mustache
index 81cdff1b3a8..4aae4828561 100644
--- a/solr/solrj/src/resources/java-template/api.mustache
+++ b/solr/solrj/src/resources/java-template/api.mustache
@@ -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}};
}
{{/vars}}
diff --git
a/solr/webapp/src/test/org/apache/solr/webapp/AdminUiSchemaDesignerTest.java
b/solr/webapp/src/test/org/apache/solr/webapp/AdminUiSchemaDesignerTest.java
index 85a76aec800..739c6312e6e 100644
--- a/solr/webapp/src/test/org/apache/solr/webapp/AdminUiSchemaDesignerTest.java
+++ b/solr/webapp/src/test/org/apache/solr/webapp/AdminUiSchemaDesignerTest.java
@@ -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;
@@ -25,11 +24,9 @@ import org.openqa.selenium.WebElement;
* 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.
*/
[email protected](bugUrl =
"https://issues.apache.org/jira/browse/SOLR-18347")
public class AdminUiSchemaDesignerTest extends AdminUiTestBase {
@Test
@@ -47,34 +44,17 @@ public class AdminUiSchemaDesignerTest extends
AdminUiTestBase {
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();
}
}
diff --git a/solr/webapp/web/js/angular/controllers/schema-designer.js
b/solr/webapp/web/js/angular/controllers/schema-designer.js
index 7ec4282bb05..a8d4c963245 100644
--- a/solr/webapp/web/js/angular/controllers/schema-designer.js
+++ b/solr/webapp/web/js/angular/controllers/schema-designer.js
@@ -15,7 +15,7 @@
limitations under the License.
*/
-solrAdminApp.controller('SchemaDesignerController', function ($scope,
$timeout, $cookies, $window, Constants, SchemaDesigner, ConfigSetFiles, Luke) {
+solrAdminApp.controller('SchemaDesignerController', function ($scope,
$timeout, $cookies, $window, Constants, SchemaDesigner, SchemaDesignerV2,
ConfigSetFiles, Luke) {
$scope.resetMenu("schema-designer", Constants.IS_ROOT_PAGE);
$scope.schemas = [];
@@ -24,6 +24,7 @@ solrAdminApp.controller('SchemaDesignerController', function
($scope, $timeout,
$scope.sortableFields = [];
$scope.hlFields = [];
$scope.types = [];
+ $scope.preparingSchema = false;
$scope.onWarning = function (warnMsg, warnDetails) {
$scope.updateWorking = false;
@@ -34,6 +35,7 @@ solrAdminApp.controller('SchemaDesignerController', function
($scope, $timeout,
$scope.onError = function (errorMsg, errorCode, errorDetails) {
$scope.updateWorking = false;
+ $scope.preparingSchema = false;
delete $scope.updateStatusMessage;
$scope.designerAPIError = errorMsg;
if (errorDetails) {
@@ -68,20 +70,38 @@ solrAdminApp.controller('SchemaDesignerController',
function ($scope, $timeout,
} // else 500 errors get the top-level error message
};
- $scope.errorHandler = function (e) {
- var error = e.data && e.data.error ? e.data.error : null;
+ // Shared by $scope.errorHandler (v1 $http) and $scope.errorHandlerV2 (v2
generated-client) below:
+ // given a structured API error (or null, for a network-level failure like a
timeout), drives the
+ // same local error dialog via $scope.onError.
+ function reportApiFailure(error, errorDetails, fallbackPath, fallbackCode,
extraFailureHint) {
if (error) {
- $scope.onError(error.msg, error.code, e.data.errorDetails);
+ $scope.onError(error.msg, error.code, errorDetails);
} else {
- // when a timeout occurs, the error details are sparse so just give the
user a hint that something was off
- var path = e.config && e.config.url ? e.config.url :
"/api/schema-designer";
- var reloadMsg = "";
- if (path.includes("/analyze")) {
- reloadMsg = " Re-try analyzing your sample docs by clicking on
'Analyze Documents' again."
- }
- $scope.onError("Request to "+path+" failed!", 408,
- {"error":"Most likely the request timed out; check server log for
more details."+reloadMsg});
+ $scope.onError("Request to "+fallbackPath+" failed!", fallbackCode,
+ {"error":"Most likely the request timed out; check server log for
more details."+(extraFailureHint || "")});
}
+ }
+
+ $scope.errorHandler = function (e) {
+ var error = e.data && e.data.error ? e.data.error : null;
+ // when a timeout occurs, the error details are sparse so just give the
user a hint that something was off
+ var path = e.config && e.config.url ? e.config.url :
"/api/schema-designer";
+ var reloadMsg = path.includes("/analyze")
+ ? " Re-try analyzing your sample docs by clicking on 'Analyze
Documents' again."
+ : "";
+ reportApiFailure(error, e.data && e.data.errorDetails, path, 408,
reloadMsg);
+ };
+
+ // Error handler for SchemaDesignerV2 (generated OpenAPI client) callbacks:
response is the raw
+ // superagent response (may be undefined for a network-level failure like a
timeout). This deliberately
+ // does NOT go through the shared ApiErrorHandler service -- app.js's
httpInterceptor already carves
+ // out /api/schema-designer/ from the global 401/403 handling so a failure
here degrades this one
+ // screen instead of forcing a full login redirect mid-design-session; this
mirrors that same intent
+ // for v2 calls by driving the same local error dialog as
$scope.errorHandler.
+ $scope.errorHandlerV2 = function (response) {
+ var data = (response && response.body) || {};
+ var path = (response && response.req && response.req.url) ||
"/api/schema-designer";
+ reportApiFailure(data.error, data.errorDetails, path, (response &&
response.status) || 408);
};
$scope.closeWarnDialog = function () {
@@ -149,36 +169,40 @@ solrAdminApp.controller('SchemaDesignerController',
function ($scope, $timeout,
// query form
$scope.query = {q: '*:*', sortBy: 'score', sortDir: 'desc'};
- SchemaDesigner.get({path: "configs"}, function (data) {
+ SchemaDesignerV2.listDesignerConfigs(function (error, data, response) {
+ $timeout(function () {
+ if (error) {
+ if (response && (response.status === 401 || response.status ===
403)) {
+ $scope.isSchemaDesignerEnabled = false;
+ $scope.hideAll();
+ }
+ return;
+ }
- $scope.schemas = [];
- $scope.publishedSchemas = ["_default"];
+ $scope.schemas = [];
+ $scope.publishedSchemas = ["_default"];
- for (var s in data.configSets) {
- // 1 means published but not editable
- if (data.configSets[s] !== 1) {
- $scope.schemas.push(s);
- }
+ for (var s in data.configSets) {
+ // 1 means published but not editable
+ if (data.configSets[s] !== 1) {
+ $scope.schemas.push(s);
+ }
- // 0 means not published yet (so can't copy from it yet)
- if (data.configSets[s] > 0) {
- $scope.publishedSchemas.push(s);
+ // 0 means not published yet (so can't copy from it yet)
+ if (data.configSets[s] > 0) {
+ $scope.publishedSchemas.push(s);
+ }
}
- }
- $scope.schemas.sort();
- $scope.publishedSchemas.sort();
+ $scope.schemas.sort();
+ $scope.publishedSchemas.sort();
- // if no schemas available to select, open the pop-up immediately
- if ($scope.schemas.length === 0) {
- $scope.firstSchemaMessage = true;
- $scope.showNewSchemaDialog();
- }
- }, function(e) {
- if (e.status === 401 || e.status === 403) {
- $scope.isSchemaDesignerEnabled = false;
- $scope.hideAll();
- }
+ // if no schemas available to select, open the pop-up immediately
+ if ($scope.schemas.length === 0) {
+ $scope.firstSchemaMessage = true;
+ $scope.showNewSchemaDialog();
+ }
+ });
});
};
@@ -239,26 +263,31 @@ solrAdminApp.controller('SchemaDesignerController',
function ($scope, $timeout,
}
$scope.resetSchema();
- var params = {configSet: $scope.currentSchema};
- SchemaDesigner.get(params, function (data) {
- $scope.currentSchema = data.configSet;
- $("#select-schema").trigger("chosen:updated");
-
- $scope.confirmSchema = data.configSet;
- $scope.collectionsForConfig = data.collections;
- $scope.hasDocsOnServer = data.numDocs > 0;
- $scope.published = data.published;
- $scope.initDesignerSettingsFromResponse(data);
- if ($scope.collectionsForConfig && $scope.collectionsForConfig.length >
0) {
- $scope.showConfirmEditSchema = true;
- } else {
- if ($scope.hasDocsOnServer || $scope.published) {
- $scope.doAnalyze();
+ SchemaDesignerV2.getInfo($scope.currentSchema, function (error, data,
response) {
+ $timeout(function () {
+ if (error) {
+ $scope.errorHandlerV2(response);
+ return;
+ }
+ $scope.currentSchema = data.configSet;
+ $("#select-schema").trigger("chosen:updated");
+
+ $scope.confirmSchema = data.configSet;
+ $scope.collectionsForConfig = data.collections;
+ $scope.hasDocsOnServer = data.numDocs > 0;
+ $scope.published = data.published;
+ $scope.initDesignerSettingsFromResponse(data);
+ if ($scope.collectionsForConfig && $scope.collectionsForConfig.length
> 0) {
+ $scope.showConfirmEditSchema = true;
} else {
- $scope.sampleMessage = "Please upload or paste some sample documents
to build the '" + $scope.currentSchema + "' schema.";
+ if ($scope.hasDocsOnServer || $scope.published) {
+ $scope.doAnalyze();
+ } else {
+ $scope.sampleMessage = "Please upload or paste some sample
documents to build the '" + $scope.currentSchema + "' schema.";
+ }
}
- }
- }, $scope.errorHandler);
+ });
+ });
};
$scope.showNewSchemaDialog = function () {
@@ -303,9 +332,17 @@ solrAdminApp.controller('SchemaDesignerController',
function ($scope, $timeout,
$scope.currentSchema = $scope.newSchema;
$scope.sampleMessage = "Please upload or paste some sample documents to
analyze for building the '" + $scope.currentSchema + "' schema.";
- SchemaDesigner.post({path: "prep", configSet: $scope.newSchema, copyFrom:
$scope.copyFrom}, null, function (data) {
- $scope.initDesignerSettingsFromResponse(data);
- }, $scope.errorHandler);
+ $scope.preparingSchema = true;
+ SchemaDesignerV2.prepNewSchema($scope.newSchema, {copyFrom:
$scope.copyFrom}, function (error, data, response) {
+ $timeout(function () {
+ if (error) {
+ $scope.errorHandlerV2(response);
+ return;
+ }
+ $scope.preparingSchema = false;
+ $scope.initDesignerSettingsFromResponse(data);
+ });
+ });
};
$scope.cancelAddSchema = function () {
@@ -451,9 +488,11 @@ solrAdminApp.controller('SchemaDesignerController',
function ($scope, $timeout,
// re-apply the filters on the updated schema
$scope.onTreeFilterOptionChanged();
- // Load the Luke schema
- Luke.schema({core: data.core}, function (schema) {
- Luke.raw({core: data.core}, function (index) {
+ // Load the Luke schema. Route through the temporary collection so the
request reaches its
+ // active replica even when the Admin UI is connected to a different node.
+ var lukeTarget = data.tempCollection || data.core;
+ Luke.schema({core: lukeTarget}, function (schema) {
+ Luke.raw({core: lukeTarget}, function (index) {
$scope.luke = mergeIndexAndSchemaData(index, schema.schema);
$scope.types = Object.keys(schema.schema.types);
$scope.showSchemaActions = true;
@@ -656,31 +695,37 @@ solrAdminApp.controller('SchemaDesignerController',
function ($scope, $timeout,
}
delete $scope.addErrors; // no errors!
- SchemaDesigner.post({
- configSet: $scope.currentSchema,
- schemaVersion: $scope.schemaVersion
- }, addData, function (data) {
- if (data.errors) {
- $scope.addErrors = data.errors[0].errorMessages;
- if (typeof $scope.addErrors === "string") {
- $scope.addErrors = [$scope.addErrors];
+ SchemaDesignerV2.addSchemaObject($scope.currentSchema, {
+ schemaVersion: $scope.schemaVersion,
+ schemaDesignerAddRequestBody: addData
+ }, function (error, data, response) {
+ $timeout(function () {
+ if (error) {
+ $scope.errorHandlerV2(response);
+ return;
}
- } else {
- delete $scope.textAnalysisJson;
- $scope.added = true;
- $timeout(function () {
- $scope.showAddField = false;
- $scope.added = false;
- var nodeId = "/";
- if ("field" === $scope.adding) {
- nodeId = "field/" + ("add-dynamic-field" === command ?
data.dynamicField : data.field);
- } else if ("type" === $scope.adding) {
- nodeId = "type/" + data.fieldType;
+ if (data.errors) {
+ $scope.addErrors = data.errors[0].errorMessages;
+ if (typeof $scope.addErrors === "string") {
+ $scope.addErrors = [$scope.addErrors];
}
- $scope.onSchemaUpdated(data.configSet, data, nodeId);
- }, 500);
- }
- }, $scope.errorHandler);
+ } else {
+ delete $scope.textAnalysisJson;
+ $scope.added = true;
+ $timeout(function () {
+ $scope.showAddField = false;
+ $scope.added = false;
+ var nodeId = "/";
+ if ("field" === $scope.adding) {
+ nodeId = "field/" + ("add-dynamic-field" === command ?
data.dynamicField : data.field);
+ } else if ("type" === $scope.adding) {
+ nodeId = "type/" + data.fieldType;
+ }
+ $scope.onSchemaUpdated(data.configSet, data, nodeId);
+ }, 500);
+ }
+ });
+ });
}
function toSortedNameAndTypeList(fields, typeAttr) {
@@ -721,38 +766,44 @@ solrAdminApp.controller('SchemaDesignerController',
function ($scope, $timeout,
$('#show-diff-dialog').css({left: leftPos});
}
- SchemaDesigner.get({ path: "diff", configSet: $scope.currentSchema },
function (data) {
- var diff = data.diff;
+ SchemaDesignerV2.getSchemaDiff($scope.currentSchema, function (error,
data, response) {
+ $timeout(function () {
+ if (error) {
+ $scope.errorHandlerV2(response);
+ return;
+ }
+ var diff = data.diff;
- var dynamicFields = diff.dynamicFields;
- var enableDynamicFields = data.enableDynamicFields !== null ?
data.enableDynamicFields : true;
- if (!enableDynamicFields) {
- dynamicFields = null;
- }
+ var dynamicFields = diff.dynamicFields;
+ var enableDynamicFields = data.enableDynamicFields !== null ?
data.enableDynamicFields : true;
+ if (!enableDynamicFields) {
+ dynamicFields = null;
+ }
- $scope.diffSource = data["diff-source"];
- $scope.schemaDiff = {
- "fieldsDiff": diff.fields,
- "addedFields": [],
- "removedFields": [],
- "fieldTypesDiff": diff.fieldTypes,
- "removedTypes": [],
- "dynamicFieldsDiff": dynamicFields,
- "copyFieldsDiff": diff.copyFields
- }
- if (diff.fields && diff.fields.added) {
- $scope.schemaDiff.addedFields = toSortedFieldList(diff.fields.added);
- }
- if (diff.fields && diff.fields.removed) {
- $scope.schemaDiff.removedFields =
toSortedNameAndTypeList(diff.fields.removed, "type");
- }
- if (diff.fieldTypes && diff.fieldTypes.removed) {
- $scope.schemaDiff.removedTypes =
toSortedNameAndTypeList(diff.fieldTypes.removed, "class");
- }
+ $scope.diffSource = data["diff-source"];
+ $scope.schemaDiff = {
+ "fieldsDiff": diff.fields,
+ "addedFields": [],
+ "removedFields": [],
+ "fieldTypesDiff": diff.fieldTypes,
+ "removedTypes": [],
+ "dynamicFieldsDiff": dynamicFields,
+ "copyFieldsDiff": diff.copyFields
+ }
+ if (diff.fields && diff.fields.added) {
+ $scope.schemaDiff.addedFields = toSortedFieldList(diff.fields.added);
+ }
+ if (diff.fields && diff.fields.removed) {
+ $scope.schemaDiff.removedFields =
toSortedNameAndTypeList(diff.fields.removed, "type");
+ }
+ if (diff.fieldTypes && diff.fieldTypes.removed) {
+ $scope.schemaDiff.removedTypes =
toSortedNameAndTypeList(diff.fieldTypes.removed, "class");
+ }
- $scope.schemaDiffExists = !(diff.fields == null && diff.fieldTypes ==
null && dynamicFields == null && diff.copyFields == null);
- $scope.showDiff = true;
- }, $scope.errorHandler);
+ $scope.schemaDiffExists = !(diff.fields == null && diff.fieldTypes ==
null && dynamicFields == null && diff.copyFields == null);
+ $scope.showDiff = true;
+ });
+ });
}
$scope.togglePublish = function (event) {
@@ -789,22 +840,28 @@ solrAdminApp.controller('SchemaDesignerController',
function ($scope, $timeout,
}
$scope.addCopyField = function () {
delete $scope.addCopyFieldErrors;
- var data = {"add-copy-field": $scope.copyField};
- SchemaDesigner.post({
- configSet: $scope.currentSchema,
- schemaVersion: $scope.schemaVersion
- }, data, function (data) {
- if (data.errors) {
- $scope.addCopyFieldErrors = data.errors[0].errorMessages;
- if (typeof $scope.addCopyFieldErrors === "string") {
- $scope.addCopyFieldErrors = [$scope.addCopyFieldErrors];
+ var copyFieldData = {"add-copy-field": $scope.copyField};
+ SchemaDesignerV2.addSchemaObject($scope.currentSchema, {
+ schemaVersion: $scope.schemaVersion,
+ schemaDesignerAddRequestBody: copyFieldData
+ }, function (error, data, response) {
+ $timeout(function () {
+ if (error) {
+ $scope.errorHandlerV2(response);
+ return;
}
- } else {
- $scope.showAddCopyField = false;
- // TODO:
- //$timeout($scope.refresh, 1500);
- }
- }, $scope.errorHandler);
+ if (data.errors) {
+ $scope.addCopyFieldErrors = data.errors[0].errorMessages;
+ if (typeof $scope.addCopyFieldErrors === "string") {
+ $scope.addCopyFieldErrors = [$scope.addCopyFieldErrors];
+ }
+ } else {
+ $scope.showAddCopyField = false;
+ // TODO:
+ //$timeout($scope.refresh, 1500);
+ }
+ });
+ });
}
$scope.toggleAnalyzer = function (analyzer) {
@@ -834,22 +891,28 @@ solrAdminApp.controller('SchemaDesignerController',
function ($scope, $timeout,
}
var field = $scope.selectedNode.name;
- var params = {path: "sample"};
- params.configSet = $scope.currentSchema;
- params.uniqueKeyField = $scope.uniqueKeyField;
- params.field = field;
+ var opts = {uniqueKeyField: $scope.uniqueKeyField, field: field};
if ($scope.sampleDocId) {
- params.docId = $scope.sampleDocId;
+ opts.docId = $scope.sampleDocId;
} // else the server will pick the first doc with a non-empty text value
for the desired field
- SchemaDesigner.get(params, function (data) {
- $scope.sampleDocId = data[$scope.uniqueKeyField];
- $scope.indexText = data[field];
- if (data.analysis && data.analysis["field_names"]) {
- $scope.result =
processFieldAnalysisData(data.analysis["field_names"][field]);
- }
- }, $scope.errorHandler);
+ SchemaDesignerV2.getSampleValue($scope.currentSchema, opts, function
(error, data, response) {
+ $timeout(function () {
+ if (error) {
+ $scope.errorHandlerV2(response);
+ return;
+ }
+ // FlexibleSolrJerseyResponse only declares responseHeader/error since
the sample value and
+ // analysis are dynamic per-field data; read the raw parsed body
instead of the typed `data`.
+ var raw = (response && response.body) || {};
+ $scope.sampleDocId = raw[$scope.uniqueKeyField];
+ $scope.indexText = raw[field];
+ if (raw.analysis && raw.analysis["field_names"]) {
+ $scope.result =
processFieldAnalysisData(raw.analysis["field_names"][field]);
+ }
+ });
+ });
};
$scope.changeLanguages = function () {
@@ -881,23 +944,28 @@ solrAdminApp.controller('SchemaDesignerController',
function ($scope, $timeout,
$scope.updateFile = function () {
var nodeId = "files/" + $scope.selectedFile;
- var params = {path: "file", file: $scope.selectedFile, configSet:
$scope.currentSchema};
$scope.updateWorking = true;
$scope.updateStatusMessage = "Updating file ...";
- SchemaDesigner.put(params, $scope.fileNodeText, function (data) {
- if (data.updateFileError) {
- if (data.fileContent) {
- $scope.fileNodeText = data.fileContent;
+ SchemaDesignerV2.updateFileContents($scope.currentSchema,
$scope.fileNodeText, {file: $scope.selectedFile}, function (error, data,
response) {
+ $timeout(function () {
+ if (error) {
+ $scope.errorHandlerV2(response);
+ return;
}
- $scope.updateFileError = data.updateFileError;
- } else {
- delete $scope.updateFileError;
- $scope.updateStatusMessage = "File '"+$scope.selectedFile+"' updated.";
- $scope.onSchemaUpdated(data.configSet, data, nodeId);
- }
- }, $scope.errorHandler);
+ if (data.updateFileError) {
+ if (data.fileContent) {
+ $scope.fileNodeText = data.fileContent;
+ }
+ $scope.updateFileError = data.updateFileError;
+ } else {
+ delete $scope.updateFileError;
+ $scope.updateStatusMessage = "File '"+$scope.selectedFile+"'
updated.";
+ $scope.onSchemaUpdated(data.configSet, data, nodeId);
+ }
+ });
+ });
};
$scope.onSelectFileNode = function (id, doSelectOnTree) {
@@ -1387,31 +1455,37 @@ solrAdminApp.controller('SchemaDesignerController',
function ($scope, $timeout,
$scope.updateWorking = true;
$scope.updateStatusMessage = "Updating " + $scope.selectedType + " ...";
- SchemaDesigner.put({
- configSet: $scope.currentSchema,
- schemaVersion: $scope.schemaVersion
- }, putData, function (data) {
+ SchemaDesignerV2.updateSchemaObject($scope.currentSchema, {
+ schemaVersion: $scope.schemaVersion,
+ schemaDesignerUpdateRequestBody: putData
+ }, function (error, data, response) {
+ $timeout(function () {
+ if (error) {
+ $scope.errorHandlerV2(response);
+ return;
+ }
- var nodeType = data.updateType;
- $scope.schemaVersion = data.schemaVersion;
- $scope.currentSchema = data.configSet;
- $scope.core = data.core;
+ var nodeType = data.updateType;
+ $scope.schemaVersion = data.schemaVersion;
+ $scope.currentSchema = data.configSet;
+ $scope.core = data.core;
- $scope.selectedNode = data[nodeType];
- $scope.selectedNode.href = href;
- $scope.selectedNode.id = id;
+ $scope.selectedNode = data[nodeType];
+ $scope.selectedNode.href = href;
+ $scope.selectedNode.id = id;
- var name = nodeType === "field" ? $scope.selectedNode.type :
$scope.selectedNode.name;
- $scope.initTypeAnalysisInfo(name, "type");
- $scope.showFieldDetails = true;
+ var name = nodeType === "field" ? $scope.selectedNode.type :
$scope.selectedNode.name;
+ $scope.initTypeAnalysisInfo(name, "type");
+ $scope.showFieldDetails = true;
- if (nodeType === "field" && $scope.selectedNode.tokenized) {
- $scope.showAnalysis = true;
- $scope.updateSampleDocId();
- }
+ if (nodeType === "field" && $scope.selectedNode.tokenized) {
+ $scope.showAnalysis = true;
+ $scope.updateSampleDocId();
+ }
- $scope.onSchemaUpdated($scope.currentSchema, data, href);
- }, $scope.errorHandler);
+ $scope.onSchemaUpdated($scope.currentSchema, data, href);
+ });
+ });
};
// TODO: These are copied from analysis.js, so move to a shared location for
both vs. duplicating
@@ -1489,34 +1563,38 @@ solrAdminApp.controller('SchemaDesignerController',
function ($scope, $timeout,
};
$scope.doPublish = function () {
- var params = {
- path: "publish",
- configSet: $scope.currentSchema,
+ var opts = {
schemaVersion: $scope.schemaVersion,
reloadCollections: $scope.reloadOnPublish,
cleanupTemp: true,
disableDesigner: $scope.disableDesigner
};
if ($scope.newCollection && $scope.newCollection.name) {
- params.newCollection = $scope.newCollection.name;
- params.numShards = $scope.newCollection.numShards;
- params.replicationFactor = $scope.newCollection.replicationFactor;
- params.indexToCollection = $scope.newCollection.indexToCollection;
- }
- SchemaDesigner.put(params, null, function (data) {
- $scope.schemaVersion = data.schemaVersion;
- $scope.currentSchema = data.configSet;
+ opts.newCollection = $scope.newCollection.name;
+ opts.numShards = $scope.newCollection.numShards;
+ opts.replicationFactor = $scope.newCollection.replicationFactor;
+ opts.indexToCollection = $scope.newCollection.indexToCollection;
+ }
+ SchemaDesignerV2.publish($scope.currentSchema, opts, function (error,
data, response) {
+ $timeout(function () {
+ if (error) {
+ $scope.errorHandlerV2(response);
+ return;
+ }
+ $scope.schemaVersion = data.schemaVersion;
+ $scope.currentSchema = data.configSet;
- delete $scope.selectedNode;
- $scope.currentSchema = "";
- delete $scope.newSchema;
- $scope.showPublish = false;
- $scope.refresh();
+ delete $scope.selectedNode;
+ $scope.currentSchema = "";
+ delete $scope.newSchema;
+ $scope.showPublish = false;
+ $scope.refresh();
- if (data.newCollection) {
- $window.location.href = "#/" + data.newCollection +
"/collection-overview";
- }
- }, $scope.errorHandler);
+ if (data.newCollection) {
+ $window.location.href = "#/" + data.newCollection +
"/collection-overview";
+ }
+ });
+ });
};
$scope.downloadConfig = function () {
diff --git a/solr/webapp/web/js/angular/services.js
b/solr/webapp/web/js/angular/services.js
index f75a2f9d3c6..1272fceff94 100644
--- a/solr/webapp/web/js/angular/services.js
+++ b/solr/webapp/web/js/angular/services.js
@@ -148,6 +148,12 @@ solrAdminServices.factory('Metrics',
delete solrApi.ApiClient.instance.defaultHeaders["User-Agent"];
return new solrApi.SegmentsApi();
})
+.factory('SchemaDesignerV2',
+ function() {
+ solrApi.ApiClient.instance.basePath = '/api';
+ delete solrApi.ApiClient.instance.defaultHeaders["User-Agent"];
+ return new solrApi.SchemaDesignerApi();
+ })
.factory('Collections',
['$resource', function ($resource) {
// v2 ClusterAPI (/api/cluster) delegates straight through to the same v1
CollectionsHandler
@@ -208,7 +214,7 @@ solrAdminServices.factory('Metrics',
// v2 NodeThreadsAPI (/api/node/threads) still just delegates straight
through to the same v1
// ThreadDumpHandler, so the response shape is byte-identical -- no
generated solrApi client
// class exists for it (it predates the OpenAPI-based v2 API framework),
so this stays a plain
- // $resource, like SchemaDesigner/Security.
+ // $resource, like Security and (partially) SchemaDesigner.
return $resource('/api/node/threads', {'wt':'json', '_':Date.now()});
}])
.factory('Replication',
@@ -364,11 +370,16 @@ solrAdminServices.factory('Metrics',
}])
.factory('SchemaDesigner',
['$resource', function($resource) {
+ // Schema Designer's analyze (sample-doc upload/paste, dynamic
content-type) and query
+ // (arbitrary forwarded Solr query params) endpoints read their request
bodies/params in ways
+ // the OpenAPI-generated SchemaDesignerApi client can't express:
analyze() always sends a null
+ // body (the server deliberately reads the raw content stream, dispatched
by Content-Type,
+ // rather than a formal parameter) and query() takes no query params at
all (the server
+ // forwards arbitrary SolrParams straight through). Both stay on this
plain $resource, like
+ // Threads/Collections/ParamSet. Every other Schema Designer endpoint
uses SchemaDesignerV2.
return $resource('/api/schema-designer/:configSet/:path', {wt: 'json',
path: '@path', configSet: '@configSet', filePath: '@filePath', _:Date.now()}, {
get: {method: "GET"},
post: {method: "POST", timeout: 90000},
- put: {method: "PUT"},
- delete: {method: "DELETE"},
postXml: {headers: {'Content-type': 'text/xml'}, method: "POST",
timeout: 90000},
postCsv: {headers: {'Content-type': 'application/csv'}, method: "POST",
timeout: 90000},
upload: {method: "POST", transformRequest: angular.identity, headers:
{'Content-Type': undefined}, timeout: 90000}
diff --git a/solr/webapp/web/partials/schema-designer.html
b/solr/webapp/web/partials/schema-designer.html
index 4d7fbd5b4b3..c5c0e1b6e7e 100644
--- a/solr/webapp/web/partials/schema-designer.html
+++ b/solr/webapp/web/partials/schema-designer.html
@@ -487,7 +487,9 @@ limitations under the License.
</div>
<div id="analyze-buttons">
<p class="clearfix note" ng-show="sampleMessage && !fileUpload
&& !sampleDocuments"><span>{{sampleMessage}}</span></p>
- <button type="submit" ng-click="doAnalyze()" id="analyze"
ng-show="currentSchema && (hasDocsOnServer || sampleDocuments ||
fileUpload)"><span>Analyze Documents</span></button>
+ <button type="submit" ng-click="doAnalyze()" id="analyze"
+ ng-show="currentSchema && (hasDocsOnServer ||
sampleDocuments || fileUpload)"
+ ng-disabled="preparingSchema ||
updateWorking"><span>Analyze Documents</span></button>
</div>
</form>
</div>