This is an automated email from the ASF dual-hosted git repository. gerlowskija pushed a commit to branch branch_9x in repository https://gitbox.apache.org/repos/asf/solr.git
commit 968504d0818b30e162e4c420908438bbdf311760 Author: bszabo97 <[email protected]> AuthorDate: Sat Jun 17 21:44:17 2023 +0200 SOLR-16395 JAX-RS conversion for remaining GET /schema/* endpoints (#1682) Migrates several v2 `GET /schema` APIs from the legacy annotation framework to JAX-RS, including: - `/zkversion` - lookup the version of the schema in ZK - `/schema/fields` - list the (non-dynamic) fields in the schema - `/schema/fields/fName` - information about a particular (non-dynamic) field in the schema - `/schema/dynamicfields` - list the (dynamic) fields in the schema - `/schema/dynamicfields/fName` - information about a particular (dynamic) field in the schema - `/schema/fieldtypes` - list the field types in the schema - `/schema/fieldtypes/tName` - information about a particular fieldtype in the schema - `/schema/copyfields` - list the copyfields in the schema This change doesn't modify the APIs themselves, so users should remain unaffected. --- .../org/apache/solr/handler/SchemaHandler.java | 172 +++++------- .../solr/handler/admin/api/GetSchemaAPI.java | 22 +- .../solr/handler/admin/api/GetSchemaFieldAPI.java | 298 +++++++++++++++++++++ .../handler/admin/api/GetSchemaZkVersionAPI.java | 86 ++++++ .../admin/api/SchemaGetDynamicFieldAPI.java | 48 ---- .../solr/handler/admin/api/SchemaGetFieldAPI.java | 48 ---- .../handler/admin/api/SchemaGetFieldTypeAPI.java | 48 ---- .../admin/api/SchemaListAllCopyFieldsAPI.java | 48 ---- .../admin/api/SchemaListAllDynamicFieldsAPI.java | 48 ---- .../admin/api/SchemaListAllFieldTypesAPI.java | 48 ---- .../handler/admin/api/SchemaListAllFieldsAPI.java | 48 ---- .../solr/handler/admin/api/SchemaNameAPI.java | 70 ----- .../solr/handler/admin/api/SchemaZkVersionAPI.java | 48 ---- .../org/apache/solr/jersey/InjectionFactories.java | 20 ++ .../org/apache/solr/jersey/JerseyApplications.java | 4 + .../org/apache/solr/api/JerseyResourceTest.java | 10 +- .../solr/handler/admin/api/GetSchemaAPITest.java | 36 +++ .../handler/admin/api/GetSchemaFieldsAPITest.java | 148 ++++++++++ .../admin/api/GetSchemaZkVersionAPITest.java | 54 ++++ .../solr/handler/admin/api/SchemaNameAPITest.java | 85 ------ .../handler/admin/api/V2SchemaAPIMappingTest.java | 20 -- 21 files changed, 736 insertions(+), 673 deletions(-) diff --git a/solr/core/src/java/org/apache/solr/handler/SchemaHandler.java b/solr/core/src/java/org/apache/solr/handler/SchemaHandler.java index 89dc0ffd3de..58a2ad1890a 100644 --- a/solr/core/src/java/org/apache/solr/handler/SchemaHandler.java +++ b/solr/core/src/java/org/apache/solr/handler/SchemaHandler.java @@ -36,38 +36,21 @@ import org.apache.solr.api.AnnotatedApi; import org.apache.solr.api.Api; import org.apache.solr.api.ApiBag; import org.apache.solr.api.JerseyResource; -import org.apache.solr.cloud.ZkSolrResourceLoader; -import org.apache.solr.common.MapWriter; import org.apache.solr.common.SolrException; -import org.apache.solr.common.cloud.SolrClassLoader; import org.apache.solr.common.params.MapSolrParams; import org.apache.solr.common.params.SolrParams; -import org.apache.solr.common.util.NamedList; -import org.apache.solr.common.util.SimpleOrderedMap; import org.apache.solr.common.util.StrUtils; -import org.apache.solr.core.PluginInfo; import org.apache.solr.core.SolrCore; import org.apache.solr.handler.admin.api.GetSchemaAPI; +import org.apache.solr.handler.admin.api.GetSchemaFieldAPI; +import org.apache.solr.handler.admin.api.GetSchemaZkVersionAPI; import org.apache.solr.handler.admin.api.SchemaBulkModifyAPI; -import org.apache.solr.handler.admin.api.SchemaGetDynamicFieldAPI; -import org.apache.solr.handler.admin.api.SchemaGetFieldAPI; -import org.apache.solr.handler.admin.api.SchemaGetFieldTypeAPI; -import org.apache.solr.handler.admin.api.SchemaListAllCopyFieldsAPI; -import org.apache.solr.handler.admin.api.SchemaListAllDynamicFieldsAPI; -import org.apache.solr.handler.admin.api.SchemaListAllFieldTypesAPI; -import org.apache.solr.handler.admin.api.SchemaListAllFieldsAPI; -import org.apache.solr.handler.admin.api.SchemaNameAPI; -import org.apache.solr.handler.admin.api.SchemaZkVersionAPI; import org.apache.solr.handler.api.V2ApiUtils; -import org.apache.solr.pkg.PackageListeningClassLoader; import org.apache.solr.request.SolrQueryRequest; import org.apache.solr.request.SolrRequestHandler; import org.apache.solr.response.SolrQueryResponse; import org.apache.solr.rest.RestManager; -import org.apache.solr.schema.IndexSchema; -import org.apache.solr.schema.ManagedIndexSchema; import org.apache.solr.schema.SchemaManager; -import org.apache.solr.schema.ZkIndexSchemaReader; import org.apache.solr.security.AuthorizationContext; import org.apache.solr.security.PermissionNameProvider; import org.apache.solr.util.plugin.SolrCoreAware; @@ -163,31 +146,15 @@ public class SchemaHandler extends RequestHandlerBase case "/schema/name": { V2ApiUtils.squashIntoSolrResponseWithoutHeader( - rsp, new SchemaNameAPI(req.getCore()).getSchemaName()); + rsp, new GetSchemaAPI(req.getCore().getLatestSchema()).getSchemaName()); break; } case "/schema/zkversion": { - int refreshIfBelowVersion = req.getParams().getInt("refreshIfBelowVersion", -1); - int zkVersion = -1; - IndexSchema schema = req.getSchema(); - if (schema instanceof ManagedIndexSchema) { - ManagedIndexSchema managed = (ManagedIndexSchema) schema; - zkVersion = managed.getSchemaZkVersion(); - if (refreshIfBelowVersion != -1 && zkVersion < refreshIfBelowVersion) { - log.info( - "REFRESHING SCHEMA (refreshIfBelowVersion={}, currentVersion={}) before returning version!", - refreshIfBelowVersion, - zkVersion); - ZkSolrResourceLoader zkSolrResourceLoader = - (ZkSolrResourceLoader) req.getCore().getResourceLoader(); - ZkIndexSchemaReader zkIndexSchemaReader = - zkSolrResourceLoader.getZkIndexSchemaReader(); - managed = zkIndexSchemaReader.refreshSchemaFromZk(refreshIfBelowVersion); - zkVersion = managed.getSchemaZkVersion(); - } - } - rsp.add("zkversion", zkVersion); + V2ApiUtils.squashIntoSolrResponseWithoutHeader( + rsp, + new GetSchemaZkVersionAPI(req.getCore()) + .getSchemaZkVersion(req.getParams().getInt("refreshIfBelowVersion", -1))); break; } default: @@ -195,7 +162,6 @@ public class SchemaHandler extends RequestHandlerBase List<String> parts = StrUtils.splitSmart(path, '/', true); if (parts.size() > 1 && level2.containsKey(parts.get(1))) { String realName = parts.get(1); - String fieldName = IndexSchema.nameMapping.get(realName); String pathParam = level2.get(realName); // Might be null if (parts.size() > 2) { @@ -203,32 +169,66 @@ public class SchemaHandler extends RequestHandlerBase SolrParams.wrapDefaults( new MapSolrParams(singletonMap(pathParam, parts.get(2))), req.getParams())); } - Map<String, Object> propertyValues = - req.getSchema().getNamedPropertyValues(realName, req.getParams()); - Object o = propertyValues.get(fieldName); - if (parts.size() > 2) { - String name = parts.get(2); - if (o instanceof List) { - List<?> list = (List<?>) o; - for (Object obj : list) { - if (obj instanceof SimpleOrderedMap) { - SimpleOrderedMap<?> simpleOrderedMap = (SimpleOrderedMap<?>) obj; - if (name.equals(simpleOrderedMap.get("name"))) { - rsp.add(fieldName.substring(0, realName.length() - 1), simpleOrderedMap); - insertPackageInfo(rsp.getValues(), req); - return; - } + switch (realName) { + case "fields": + { + if (parts.size() > 2) { + V2ApiUtils.squashIntoSolrResponseWithoutHeader( + rsp, + new GetSchemaFieldAPI(req.getCore().getLatestSchema(), req.getParams()) + .getFieldInfo(parts.get(2))); + } else { + V2ApiUtils.squashIntoSolrResponseWithoutHeader( + rsp, + new GetSchemaFieldAPI(req.getCore().getLatestSchema(), req.getParams()) + .listSchemaFields()); + } + return; + } + case "copyfields": + { + V2ApiUtils.squashIntoSolrResponseWithoutHeader( + rsp, + new GetSchemaFieldAPI(req.getCore().getLatestSchema(), req.getParams()) + .listCopyFields()); + return; + } + case "dynamicfields": + { + if (parts.size() > 2) { + V2ApiUtils.squashIntoSolrResponseWithoutHeader( + rsp, + new GetSchemaFieldAPI(req.getCore().getLatestSchema(), req.getParams()) + .getDynamicFieldInfo(parts.get(2))); + } else { + V2ApiUtils.squashIntoSolrResponseWithoutHeader( + rsp, + new GetSchemaFieldAPI(req.getCore().getLatestSchema(), req.getParams()) + .listDynamicFields()); + } + return; + } + case "fieldtypes": + { + if (parts.size() > 2) { + V2ApiUtils.squashIntoSolrResponseWithoutHeader( + rsp, + new GetSchemaFieldAPI(req.getCore().getLatestSchema(), req.getParams()) + .getFieldTypeInfo(parts.get(2))); + } else { + V2ApiUtils.squashIntoSolrResponseWithoutHeader( + rsp, + new GetSchemaFieldAPI(req.getCore().getLatestSchema(), req.getParams()) + .listSchemaFieldTypes()); } + return; + } + default: + { + break; } - } - throw new SolrException(SolrException.ErrorCode.NOT_FOUND, "No such path " + path); - } else { - rsp.add(fieldName, o); } - insertPackageInfo(rsp.getValues(), req); - return; } - throw new SolrException(SolrException.ErrorCode.NOT_FOUND, "No such path " + path); } } @@ -238,42 +238,6 @@ public class SchemaHandler extends RequestHandlerBase } } - /** - * If a plugin is loaded from a package, the version of the package being used should be added to - * the response - */ - private void insertPackageInfo(Object o, SolrQueryRequest req) { - if (!req.getParams().getBool("meta", false)) return; - if (o instanceof List) { - List<?> l = (List<?>) o; - for (Object o1 : l) { - if (o1 instanceof NamedList || o1 instanceof List) insertPackageInfo(o1, req); - } - - } else if (o instanceof NamedList) { - @SuppressWarnings("unchecked") - NamedList<Object> nl = (NamedList<Object>) o; - nl.forEach( - (n, v) -> { - if (v instanceof NamedList || v instanceof List) insertPackageInfo(v, req); - }); - Object v = nl.get("class"); - if (v instanceof String) { - String klas = (String) v; - PluginInfo.ClassName parsedClassName = new PluginInfo.ClassName(klas); - if (parsedClassName.pkg != null) { - SolrClassLoader solrClassLoader = req.getCore().getLatestSchema().getSolrClassLoader(); - MapWriter mw = - solrClassLoader instanceof PackageListeningClassLoader - ? ((PackageListeningClassLoader) solrClassLoader) - .getPackageVersion(parsedClassName) - : null; - if (mw != null) nl.add("_packageinfo_", mw); - } - } - } - } - private static final Set<String> subPaths = new HashSet<>( Set.of( @@ -320,14 +284,6 @@ public class SchemaHandler extends RequestHandlerBase public Collection<Api> getApis() { final List<Api> apis = new ArrayList<>(); - apis.addAll(AnnotatedApi.getApis(new SchemaZkVersionAPI(this))); - apis.addAll(AnnotatedApi.getApis(new SchemaListAllFieldsAPI(this))); - apis.addAll(AnnotatedApi.getApis(new SchemaGetFieldAPI(this))); - apis.addAll(AnnotatedApi.getApis(new SchemaListAllCopyFieldsAPI(this))); - apis.addAll(AnnotatedApi.getApis(new SchemaListAllDynamicFieldsAPI(this))); - apis.addAll(AnnotatedApi.getApis(new SchemaGetDynamicFieldAPI(this))); - apis.addAll(AnnotatedApi.getApis(new SchemaListAllFieldTypesAPI(this))); - apis.addAll(AnnotatedApi.getApis(new SchemaGetFieldTypeAPI(this))); apis.addAll(AnnotatedApi.getApis(new SchemaBulkModifyAPI(this))); return apis; @@ -335,7 +291,7 @@ public class SchemaHandler extends RequestHandlerBase @Override public Collection<Class<? extends JerseyResource>> getJerseyResources() { - return List.of(SchemaNameAPI.class, GetSchemaAPI.class); + return List.of(GetSchemaAPI.class, GetSchemaFieldAPI.class, GetSchemaZkVersionAPI.class); } @Override diff --git a/solr/core/src/java/org/apache/solr/handler/admin/api/GetSchemaAPI.java b/solr/core/src/java/org/apache/solr/handler/admin/api/GetSchemaAPI.java index 461120aab15..3a966866727 100644 --- a/solr/core/src/java/org/apache/solr/handler/admin/api/GetSchemaAPI.java +++ b/solr/core/src/java/org/apache/solr/handler/admin/api/GetSchemaAPI.java @@ -27,6 +27,7 @@ import javax.ws.rs.Path; import javax.ws.rs.Produces; import javax.ws.rs.core.MediaType; import org.apache.solr.api.JerseyResource; +import org.apache.solr.common.SolrException; import org.apache.solr.common.util.SimpleOrderedMap; import org.apache.solr.jersey.PermissionName; import org.apache.solr.jersey.SolrJerseyResponse; @@ -36,7 +37,7 @@ import org.apache.solr.security.PermissionNameProvider; @Path("/{a:cores|collections}/{collectionName}/schema") public class GetSchemaAPI extends JerseyResource { - private IndexSchema indexSchema; + protected final IndexSchema indexSchema; @Inject public GetSchemaAPI(IndexSchema indexSchema) { @@ -63,6 +64,25 @@ public class GetSchemaAPI extends JerseyResource { public Map<String, Object> schema; } + @GET + @Path("/name") + @Produces({MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML, BINARY_CONTENT_TYPE_V2}) + @PermissionName(PermissionNameProvider.Name.SCHEMA_READ_PERM) + public SchemaNameResponse getSchemaName() throws Exception { + final SchemaNameResponse response = instantiateJerseyResponse(SchemaNameResponse.class); + if (null == indexSchema.getSchemaName()) { + throw new SolrException(SolrException.ErrorCode.NOT_FOUND, "Schema has no name"); + } + + response.name = indexSchema.getSchemaName(); + return response; + } + + public static class SchemaNameResponse extends SolrJerseyResponse { + @JsonProperty("name") + public String name; + } + @GET @Path("/similarity") @Produces({MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML, BINARY_CONTENT_TYPE_V2}) diff --git a/solr/core/src/java/org/apache/solr/handler/admin/api/GetSchemaFieldAPI.java b/solr/core/src/java/org/apache/solr/handler/admin/api/GetSchemaFieldAPI.java new file mode 100644 index 00000000000..b5c3fbbb2a7 --- /dev/null +++ b/solr/core/src/java/org/apache/solr/handler/admin/api/GetSchemaFieldAPI.java @@ -0,0 +1,298 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.solr.handler.admin.api; + +import static org.apache.solr.client.solrj.impl.BinaryResponseParser.BINARY_CONTENT_TYPE_V2; + +import com.fasterxml.jackson.annotation.JsonProperty; +import java.util.List; +import java.util.Map; +import javax.inject.Inject; +import javax.ws.rs.GET; +import javax.ws.rs.Path; +import javax.ws.rs.PathParam; +import javax.ws.rs.Produces; +import javax.ws.rs.core.MediaType; +import org.apache.solr.common.MapWriter; +import org.apache.solr.common.SolrException; +import org.apache.solr.common.cloud.SolrClassLoader; +import org.apache.solr.common.params.SolrParams; +import org.apache.solr.common.util.NamedList; +import org.apache.solr.common.util.SimpleOrderedMap; +import org.apache.solr.core.PluginInfo; +import org.apache.solr.jersey.PermissionName; +import org.apache.solr.jersey.SolrJerseyResponse; +import org.apache.solr.pkg.PackageListeningClassLoader; +import org.apache.solr.schema.IndexSchema; +import org.apache.solr.security.PermissionNameProvider; + +/** + * <code>GetSchemaFieldAPI</code> contains the V2 APIs for all field related endpoint which are + * + * <ul> + * <li>/fields + * <li>/fields/{fieldName} + * <li>/copyfields + * <li>/dynamicfields + * <li>/dynamicfields/{fieldName} + * <li>/fieldtypes + * <li>/fieldtypes/{fieldTypeName} + * </ul> + */ +public class GetSchemaFieldAPI extends GetSchemaAPI { + + private final SolrParams params; + + // TODO Stop using SolrParams here and instead give API methods parameters representing only those + // query-params that they support + @Inject + public GetSchemaFieldAPI(IndexSchema indexSchema, SolrParams params) { + super(indexSchema); + this.params = params; + } + + @GET + @Path("/fields") + @Produces({MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML, BINARY_CONTENT_TYPE_V2}) + @PermissionName(PermissionNameProvider.Name.SCHEMA_READ_PERM) + public SchemaListFieldsResponse listSchemaFields() { + SchemaListFieldsResponse response = instantiateJerseyResponse(SchemaListFieldsResponse.class); + final String realName = "fields"; + + response.fields = listAllFieldsOfType(realName, params); + + return response; + } + + public static class SchemaListFieldsResponse extends SolrJerseyResponse { + @JsonProperty("fields") + public List<Object> fields; + } + + @GET + @Path("/fields/{fieldName}") + @Produces({MediaType.APPLICATION_JSON, MediaType.APPLICATION_ATOM_XML, BINARY_CONTENT_TYPE_V2}) + @PermissionName(PermissionNameProvider.Name.SCHEMA_READ_PERM) + public SchemaGetFieldInfoResponse getFieldInfo(@PathParam("fieldName") String fieldName) { + if (fieldName == null) { + throw new SolrException(SolrException.ErrorCode.BAD_REQUEST, "Field name must not be null"); + } + SchemaGetFieldInfoResponse response = + instantiateJerseyResponse(SchemaGetFieldInfoResponse.class); + final String realName = "fields"; + + SimpleOrderedMap<Object> fieldInfo = retrieveFieldInfoOfType(realName, fieldName, params); + if (fieldInfo != null) { + response.fieldInfo = fieldInfo; + return response; + } + throw new SolrException(SolrException.ErrorCode.NOT_FOUND, "No such field [" + fieldName + "]"); + } + + public static class SchemaGetFieldInfoResponse extends SolrJerseyResponse { + @JsonProperty("field") + public SimpleOrderedMap<?> fieldInfo; + } + + @GET + @Path("/copyfields") + @Produces({MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML, BINARY_CONTENT_TYPE_V2}) + @PermissionName(PermissionNameProvider.Name.SCHEMA_READ_PERM) + public SchemaListCopyFieldsResponse listCopyFields() { + SchemaListCopyFieldsResponse response = + instantiateJerseyResponse(SchemaListCopyFieldsResponse.class); + final String realName = "copyfields"; + + response.copyFields = listAllFieldsOfType(realName, params); + + return response; + } + + public static class SchemaListCopyFieldsResponse extends SolrJerseyResponse { + @JsonProperty("copyFields") + public List<Object> copyFields; + } + + @GET + @Path("/dynamicfields") + @Produces({MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML, BINARY_CONTENT_TYPE_V2}) + @PermissionName(PermissionNameProvider.Name.SCHEMA_READ_PERM) + public SchemaListDynamicFieldsResponse listDynamicFields() { + SchemaListDynamicFieldsResponse response = + instantiateJerseyResponse(SchemaListDynamicFieldsResponse.class); + final String realName = "dynamicfields"; + + response.dynamicFields = listAllFieldsOfType(realName, params); + + return response; + } + + public static class SchemaListDynamicFieldsResponse extends SolrJerseyResponse { + @JsonProperty("dynamicFields") + public List<Object> dynamicFields; + } + + @GET + @Path("/dynamicfields/{fieldName}") + @Produces({MediaType.APPLICATION_JSON, MediaType.APPLICATION_ATOM_XML, BINARY_CONTENT_TYPE_V2}) + @PermissionName(PermissionNameProvider.Name.SCHEMA_READ_PERM) + public SchemaGetDynamicFieldInfoResponse getDynamicFieldInfo( + @PathParam("fieldName") String fieldName) { + if (fieldName == null) { + throw new SolrException( + SolrException.ErrorCode.BAD_REQUEST, "Dynamic field name must not be null"); + } + SchemaGetDynamicFieldInfoResponse response = + instantiateJerseyResponse(SchemaGetDynamicFieldInfoResponse.class); + final String realName = "dynamicfields"; + + SimpleOrderedMap<Object> dynamicFieldInfo = + retrieveFieldInfoOfType(realName, fieldName, params); + if (dynamicFieldInfo != null) { + response.dynamicFieldInfo = dynamicFieldInfo; + return response; + } + throw new SolrException( + SolrException.ErrorCode.NOT_FOUND, "No such dynamic field [" + fieldName + "]"); + } + + public static class SchemaGetDynamicFieldInfoResponse extends SolrJerseyResponse { + @JsonProperty("dynamicField") + public SimpleOrderedMap<?> dynamicFieldInfo; + } + + @GET + @Path("/fieldtypes") + @Produces({MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML, BINARY_CONTENT_TYPE_V2}) + @PermissionName(PermissionNameProvider.Name.SCHEMA_READ_PERM) + public SchemaListFieldTypesResponse listSchemaFieldTypes() { + SchemaListFieldTypesResponse response = + instantiateJerseyResponse(SchemaListFieldTypesResponse.class); + final String realName = "fieldtypes"; + + response.fieldTypes = listAllFieldsOfType(realName, params); + + return response; + } + + public static class SchemaListFieldTypesResponse extends SolrJerseyResponse { + @JsonProperty("fieldTypes") + public List<Object> fieldTypes; + } + + @GET + @Path("/fieldtypes/{fieldTypeName}") + @Produces({MediaType.APPLICATION_JSON, MediaType.APPLICATION_ATOM_XML, BINARY_CONTENT_TYPE_V2}) + @PermissionName(PermissionNameProvider.Name.SCHEMA_READ_PERM) + public SchemaGetFieldTypeInfoResponse getFieldTypeInfo( + @PathParam("fieldTypeName") String fieldTypeName) { + if (fieldTypeName == null) { + throw new SolrException( + SolrException.ErrorCode.BAD_REQUEST, "Field type name must not be null"); + } + SchemaGetFieldTypeInfoResponse response = + instantiateJerseyResponse(SchemaGetFieldTypeInfoResponse.class); + + final String realName = "fieldtypes"; + + SimpleOrderedMap<Object> fieldTypeInfo = + retrieveFieldInfoOfType(realName, fieldTypeName, params); + if (fieldTypeInfo != null) { + response.fieldTypeInfo = fieldTypeInfo; + return response; + } + throw new SolrException( + SolrException.ErrorCode.NOT_FOUND, "No such field type [" + fieldTypeName + "]"); + } + + public static class SchemaGetFieldTypeInfoResponse extends SolrJerseyResponse { + @JsonProperty("fieldType") + public SimpleOrderedMap<?> fieldTypeInfo; + } + + private List<Object> listAllFieldsOfType(String realName, SolrParams params) { + String camelCaseRealName = IndexSchema.nameMapping.get(realName); + Map<String, Object> propertyValues = indexSchema.getNamedPropertyValues(realName, params); + @SuppressWarnings("unchecked") + List<Object> list = (List<Object>) propertyValues.get(camelCaseRealName); + if (params.getBool("meta", false)) { + insertPackageInfo(list); + } + return list; + } + + @SuppressWarnings("unchecked") + private SimpleOrderedMap<Object> retrieveFieldInfoOfType( + String realName, String fieldName, SolrParams params) { + SimpleOrderedMap<Object> returnFieldInfo = null; + String camelCaseRealName = IndexSchema.nameMapping.get(realName); + Map<String, Object> propertyValues = indexSchema.getNamedPropertyValues(realName, params); + Object o = propertyValues.get(camelCaseRealName); + if (o instanceof List) { + List<?> list = (List<?>) o; + for (Object obj : list) { + if (obj instanceof SimpleOrderedMap) { + SimpleOrderedMap<Object> fieldInfo = (SimpleOrderedMap<Object>) obj; + if (fieldName.equals(fieldInfo.get("name"))) { + returnFieldInfo = fieldInfo; + if (params.getBool("meta", false)) { + insertPackageInfo(returnFieldInfo); + } + break; + } + } + } + } + return returnFieldInfo; + } + + /** + * If a plugin is loaded from a package, the version of the package being used should be added to + * the response + */ + private void insertPackageInfo(Object o) { + if (o instanceof List) { + List<?> l = (List<?>) o; + for (Object o1 : l) { + if (o1 instanceof NamedList || o1 instanceof List) insertPackageInfo(o1); + } + + } else if (o instanceof NamedList) { + @SuppressWarnings("unchecked") + NamedList<Object> nl = (NamedList<Object>) o; + nl.forEach( + (n, v) -> { + if (v instanceof NamedList || v instanceof List) insertPackageInfo(v); + }); + Object v = nl.get("class"); + if (v instanceof String) { + String klas = (String) v; + PluginInfo.ClassName parsedClassName = new PluginInfo.ClassName(klas); + if (parsedClassName.pkg != null) { + SolrClassLoader solrClassLoader = indexSchema.getSolrClassLoader(); + MapWriter mw = + solrClassLoader instanceof PackageListeningClassLoader + ? ((PackageListeningClassLoader) solrClassLoader) + .getPackageVersion(parsedClassName) + : null; + if (mw != null) nl.add("_packageinfo_", mw); + } + } + } + } +} diff --git a/solr/core/src/java/org/apache/solr/handler/admin/api/GetSchemaZkVersionAPI.java b/solr/core/src/java/org/apache/solr/handler/admin/api/GetSchemaZkVersionAPI.java new file mode 100644 index 00000000000..56a22f77783 --- /dev/null +++ b/solr/core/src/java/org/apache/solr/handler/admin/api/GetSchemaZkVersionAPI.java @@ -0,0 +1,86 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.solr.handler.admin.api; + +import static org.apache.solr.client.solrj.impl.BinaryResponseParser.BINARY_CONTENT_TYPE_V2; + +import com.fasterxml.jackson.annotation.JsonProperty; +import java.lang.invoke.MethodHandles; +import javax.inject.Inject; +import javax.ws.rs.DefaultValue; +import javax.ws.rs.GET; +import javax.ws.rs.Path; +import javax.ws.rs.Produces; +import javax.ws.rs.QueryParam; +import javax.ws.rs.core.MediaType; +import org.apache.solr.api.JerseyResource; +import org.apache.solr.cloud.ZkSolrResourceLoader; +import org.apache.solr.core.SolrCore; +import org.apache.solr.jersey.PermissionName; +import org.apache.solr.jersey.SolrJerseyResponse; +import org.apache.solr.schema.ManagedIndexSchema; +import org.apache.solr.schema.ZkIndexSchemaReader; +import org.apache.solr.security.PermissionNameProvider; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +@Path("/{a:cores|collections}/{collectionName}/schema") +public class GetSchemaZkVersionAPI extends JerseyResource { + + private static final Logger log = LoggerFactory.getLogger(MethodHandles.lookup().lookupClass()); + private SolrCore solrCore; + + @Inject + public GetSchemaZkVersionAPI(SolrCore solrCore) { + this.solrCore = solrCore; + } + + @GET + @Path("/zkversion") + @Produces({MediaType.APPLICATION_JSON, MediaType.APPLICATION_ATOM_XML, BINARY_CONTENT_TYPE_V2}) + @PermissionName(PermissionNameProvider.Name.SCHEMA_READ_PERM) + public SchemaZkVersionResponse getSchemaZkVersion( + @DefaultValue("-1") @QueryParam("refreshIfBelowVersion") Integer refreshIfBelowVersion) + throws Exception { + final SchemaZkVersionResponse response = + instantiateJerseyResponse(SchemaZkVersionResponse.class); + int zkVersion = -1; + if (solrCore.getLatestSchema() instanceof ManagedIndexSchema) { + ManagedIndexSchema managed = (ManagedIndexSchema) solrCore.getLatestSchema(); + zkVersion = managed.getSchemaZkVersion(); + if (refreshIfBelowVersion != -1 && zkVersion < refreshIfBelowVersion) { + log.info( + "REFRESHING SCHEMA (refreshIfBelowVersion={}, currentVersion={}) before returning version!", + refreshIfBelowVersion, + zkVersion); + ZkSolrResourceLoader zkSolrResourceLoader = + (ZkSolrResourceLoader) solrCore.getResourceLoader(); + ZkIndexSchemaReader zkIndexSchemaReader = zkSolrResourceLoader.getZkIndexSchemaReader(); + managed = zkIndexSchemaReader.refreshSchemaFromZk(refreshIfBelowVersion); + zkVersion = managed.getSchemaZkVersion(); + } + } + response.zkversion = zkVersion; + return response; + } + + public static class SchemaZkVersionResponse extends SolrJerseyResponse { + @JsonProperty("zkversion") + public int zkversion; + } +} diff --git a/solr/core/src/java/org/apache/solr/handler/admin/api/SchemaGetDynamicFieldAPI.java b/solr/core/src/java/org/apache/solr/handler/admin/api/SchemaGetDynamicFieldAPI.java deleted file mode 100644 index 646540c4d67..00000000000 --- a/solr/core/src/java/org/apache/solr/handler/admin/api/SchemaGetDynamicFieldAPI.java +++ /dev/null @@ -1,48 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.apache.solr.handler.admin.api; - -import static org.apache.solr.client.solrj.SolrRequest.METHOD.GET; - -import org.apache.solr.api.EndPoint; -import org.apache.solr.handler.SchemaHandler; -import org.apache.solr.request.SolrQueryRequest; -import org.apache.solr.response.SolrQueryResponse; -import org.apache.solr.security.PermissionNameProvider; - -/** - * V2 API for getting information about a single dynamic field definition from an in-use schema. - * - * <p>This API (GET /v2/collections/collectionName/schema/dynamicfields/fieldName) is analogous to - * the v1 /solr/collectionName/schema/dynamicfields/fieldName API. - */ -public class SchemaGetDynamicFieldAPI { - private final SchemaHandler schemaHandler; - - public SchemaGetDynamicFieldAPI(SchemaHandler schemaHandler) { - this.schemaHandler = schemaHandler; - } - - @EndPoint( - path = {"/schema/dynamicfields/{name}"}, - method = GET, - permission = PermissionNameProvider.Name.SCHEMA_READ_PERM) - public void getSingleDynamicField(SolrQueryRequest req, SolrQueryResponse rsp) throws Exception { - schemaHandler.handleRequestBody(req, rsp); - } -} diff --git a/solr/core/src/java/org/apache/solr/handler/admin/api/SchemaGetFieldAPI.java b/solr/core/src/java/org/apache/solr/handler/admin/api/SchemaGetFieldAPI.java deleted file mode 100644 index a8a0798f1b6..00000000000 --- a/solr/core/src/java/org/apache/solr/handler/admin/api/SchemaGetFieldAPI.java +++ /dev/null @@ -1,48 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.apache.solr.handler.admin.api; - -import static org.apache.solr.client.solrj.SolrRequest.METHOD.GET; - -import org.apache.solr.api.EndPoint; -import org.apache.solr.handler.SchemaHandler; -import org.apache.solr.request.SolrQueryRequest; -import org.apache.solr.response.SolrQueryResponse; -import org.apache.solr.security.PermissionNameProvider; - -/** - * V2 API to retrieve information about a single field from an in-use schema. - * - * <p>This API (GET /v2/collections/collectionName/schema/field/fieldName) is analogous to the v1 - * /solr/collectionName/schema/fields/fieldName API. - */ -public class SchemaGetFieldAPI { - private final SchemaHandler schemaHandler; - - public SchemaGetFieldAPI(SchemaHandler schemaHandler) { - this.schemaHandler = schemaHandler; - } - - @EndPoint( - path = {"/schema/fields/{field}"}, - method = GET, - permission = PermissionNameProvider.Name.SCHEMA_READ_PERM) - public void retrieveFieldInfo(SolrQueryRequest req, SolrQueryResponse rsp) throws Exception { - schemaHandler.handleRequestBody(req, rsp); - } -} diff --git a/solr/core/src/java/org/apache/solr/handler/admin/api/SchemaGetFieldTypeAPI.java b/solr/core/src/java/org/apache/solr/handler/admin/api/SchemaGetFieldTypeAPI.java deleted file mode 100644 index 1a4b182dae9..00000000000 --- a/solr/core/src/java/org/apache/solr/handler/admin/api/SchemaGetFieldTypeAPI.java +++ /dev/null @@ -1,48 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.apache.solr.handler.admin.api; - -import static org.apache.solr.client.solrj.SolrRequest.METHOD.GET; - -import org.apache.solr.api.EndPoint; -import org.apache.solr.handler.SchemaHandler; -import org.apache.solr.request.SolrQueryRequest; -import org.apache.solr.response.SolrQueryResponse; -import org.apache.solr.security.PermissionNameProvider; - -/** - * V2 API for getting information about a single field-type definition from an in-use schema. - * - * <p>This API (GET /v2/collections/collectionName/schema/fieldtypes/typeName) is analogous to the - * v1 /solr/collectionName/schema/fieldtypes/typeName API. - */ -public class SchemaGetFieldTypeAPI { - private final SchemaHandler schemaHandler; - - public SchemaGetFieldTypeAPI(SchemaHandler schemaHandler) { - this.schemaHandler = schemaHandler; - } - - @EndPoint( - path = {"/schema/fieldtypes/{name}"}, - method = GET, - permission = PermissionNameProvider.Name.SCHEMA_READ_PERM) - public void getSingleFieldType(SolrQueryRequest req, SolrQueryResponse rsp) throws Exception { - schemaHandler.handleRequestBody(req, rsp); - } -} diff --git a/solr/core/src/java/org/apache/solr/handler/admin/api/SchemaListAllCopyFieldsAPI.java b/solr/core/src/java/org/apache/solr/handler/admin/api/SchemaListAllCopyFieldsAPI.java deleted file mode 100644 index ef3b6b4bd7e..00000000000 --- a/solr/core/src/java/org/apache/solr/handler/admin/api/SchemaListAllCopyFieldsAPI.java +++ /dev/null @@ -1,48 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.apache.solr.handler.admin.api; - -import static org.apache.solr.client.solrj.SolrRequest.METHOD.GET; - -import org.apache.solr.api.EndPoint; -import org.apache.solr.handler.SchemaHandler; -import org.apache.solr.request.SolrQueryRequest; -import org.apache.solr.response.SolrQueryResponse; -import org.apache.solr.security.PermissionNameProvider; - -/** - * V2 API for listing all copyfield's in an in-use schema. - * - * <p>This API (GET /v2/collections/collectionName/schema/copyfields) is analogous to the v1 - * /solr/collectionName/schema/copyfields API. - */ -public class SchemaListAllCopyFieldsAPI { - private final SchemaHandler schemaHandler; - - public SchemaListAllCopyFieldsAPI(SchemaHandler schemaHandler) { - this.schemaHandler = schemaHandler; - } - - @EndPoint( - path = {"/schema/copyfields"}, - method = GET, - permission = PermissionNameProvider.Name.SCHEMA_READ_PERM) - public void getCopyFields(SolrQueryRequest req, SolrQueryResponse rsp) throws Exception { - schemaHandler.handleRequestBody(req, rsp); - } -} diff --git a/solr/core/src/java/org/apache/solr/handler/admin/api/SchemaListAllDynamicFieldsAPI.java b/solr/core/src/java/org/apache/solr/handler/admin/api/SchemaListAllDynamicFieldsAPI.java deleted file mode 100644 index bd0f4cbd238..00000000000 --- a/solr/core/src/java/org/apache/solr/handler/admin/api/SchemaListAllDynamicFieldsAPI.java +++ /dev/null @@ -1,48 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.apache.solr.handler.admin.api; - -import static org.apache.solr.client.solrj.SolrRequest.METHOD.GET; - -import org.apache.solr.api.EndPoint; -import org.apache.solr.handler.SchemaHandler; -import org.apache.solr.request.SolrQueryRequest; -import org.apache.solr.response.SolrQueryResponse; -import org.apache.solr.security.PermissionNameProvider; - -/** - * V2 API for listing all dynamic field definitions in an in-use schema. - * - * <p>This API (GET /v2/collections/collectionName/schema/dynamicfields) is analogous to the v1 - * /solr/collectionName/schema/dynamicfields API. - */ -public class SchemaListAllDynamicFieldsAPI { - private final SchemaHandler schemaHandler; - - public SchemaListAllDynamicFieldsAPI(SchemaHandler schemaHandler) { - this.schemaHandler = schemaHandler; - } - - @EndPoint( - path = {"/schema/dynamicfields"}, - method = GET, - permission = PermissionNameProvider.Name.SCHEMA_READ_PERM) - public void listDynamicFields(SolrQueryRequest req, SolrQueryResponse rsp) throws Exception { - schemaHandler.handleRequestBody(req, rsp); - } -} diff --git a/solr/core/src/java/org/apache/solr/handler/admin/api/SchemaListAllFieldTypesAPI.java b/solr/core/src/java/org/apache/solr/handler/admin/api/SchemaListAllFieldTypesAPI.java deleted file mode 100644 index 22b0e1c2900..00000000000 --- a/solr/core/src/java/org/apache/solr/handler/admin/api/SchemaListAllFieldTypesAPI.java +++ /dev/null @@ -1,48 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.apache.solr.handler.admin.api; - -import static org.apache.solr.client.solrj.SolrRequest.METHOD.GET; - -import org.apache.solr.api.EndPoint; -import org.apache.solr.handler.SchemaHandler; -import org.apache.solr.request.SolrQueryRequest; -import org.apache.solr.response.SolrQueryResponse; -import org.apache.solr.security.PermissionNameProvider; - -/** - * V2 API for listing all field-type definitions in an in-use schema. - * - * <p>This API (GET /v2/collections/collectionName/schema/fieldtypes) is analogous to the v1 - * /solr/collectionName/schema/fieldtypes API. - */ -public class SchemaListAllFieldTypesAPI { - private final SchemaHandler schemaHandler; - - public SchemaListAllFieldTypesAPI(SchemaHandler schemaHandler) { - this.schemaHandler = schemaHandler; - } - - @EndPoint( - path = {"/schema/fieldtypes"}, - method = GET, - permission = PermissionNameProvider.Name.SCHEMA_READ_PERM) - public void listFieldTypes(SolrQueryRequest req, SolrQueryResponse rsp) throws Exception { - schemaHandler.handleRequestBody(req, rsp); - } -} diff --git a/solr/core/src/java/org/apache/solr/handler/admin/api/SchemaListAllFieldsAPI.java b/solr/core/src/java/org/apache/solr/handler/admin/api/SchemaListAllFieldsAPI.java deleted file mode 100644 index 070c02cafe7..00000000000 --- a/solr/core/src/java/org/apache/solr/handler/admin/api/SchemaListAllFieldsAPI.java +++ /dev/null @@ -1,48 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.apache.solr.handler.admin.api; - -import static org.apache.solr.client.solrj.SolrRequest.METHOD.GET; - -import org.apache.solr.api.EndPoint; -import org.apache.solr.handler.SchemaHandler; -import org.apache.solr.request.SolrQueryRequest; -import org.apache.solr.response.SolrQueryResponse; -import org.apache.solr.security.PermissionNameProvider; - -/** - * V2 API listing all fields defined in an in-use schema. - * - * <p>This API (GET /v2/collections/collectionName/schema/fields) is analogous to the v1 - * /solr/collectionName/schema/fields API. - */ -public class SchemaListAllFieldsAPI { - private final SchemaHandler schemaHandler; - - public SchemaListAllFieldsAPI(SchemaHandler schemaHandler) { - this.schemaHandler = schemaHandler; - } - - @EndPoint( - path = {"/schema/fields"}, - method = GET, - permission = PermissionNameProvider.Name.SCHEMA_READ_PERM) - public void listSchemaFields(SolrQueryRequest req, SolrQueryResponse rsp) throws Exception { - schemaHandler.handleRequestBody(req, rsp); - } -} diff --git a/solr/core/src/java/org/apache/solr/handler/admin/api/SchemaNameAPI.java b/solr/core/src/java/org/apache/solr/handler/admin/api/SchemaNameAPI.java deleted file mode 100644 index 021d362fa3f..00000000000 --- a/solr/core/src/java/org/apache/solr/handler/admin/api/SchemaNameAPI.java +++ /dev/null @@ -1,70 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.apache.solr.handler.admin.api; - -import static org.apache.solr.client.solrj.impl.BinaryResponseParser.BINARY_CONTENT_TYPE_V2; - -import com.fasterxml.jackson.annotation.JsonProperty; -import javax.inject.Inject; -import javax.ws.rs.GET; -import javax.ws.rs.Path; -import javax.ws.rs.Produces; -import org.apache.solr.api.JerseyResource; -import org.apache.solr.common.SolrException; -import org.apache.solr.core.SolrCore; -import org.apache.solr.jersey.PermissionName; -import org.apache.solr.jersey.SolrJerseyResponse; -import org.apache.solr.schema.IndexSchema; -import org.apache.solr.security.PermissionNameProvider; - -/** - * V2 API for checking the name of an in-use schema. - * - * <p>This API (GET /v2/collections/collectionName/schema/name) is analogous to the v1 - * /solr/collectionName/schema/name API. - */ -@Path("/collections/{collectionName}/schema/name") -public class SchemaNameAPI extends JerseyResource { - - private SolrCore solrCore; - - @Inject - public SchemaNameAPI(SolrCore solrCore) { - this.solrCore = solrCore; - } - - @GET - @Produces({"application/json", "application/xml", BINARY_CONTENT_TYPE_V2}) - @PermissionName(PermissionNameProvider.Name.SCHEMA_READ_PERM) - public GetSchemaNameResponse getSchemaName() throws Exception { - final GetSchemaNameResponse response = instantiateJerseyResponse(GetSchemaNameResponse.class); - final IndexSchema schema = solrCore.getLatestSchema(); - if (null == schema.getSchemaName()) { - throw new SolrException(SolrException.ErrorCode.NOT_FOUND, "Schema has no name"); - } - - response.name = schema.getSchemaName(); - return response; - } - - /** Response for {@link SchemaNameAPI}. */ - public static class GetSchemaNameResponse extends SolrJerseyResponse { - @JsonProperty("name") - public String name; - } -} diff --git a/solr/core/src/java/org/apache/solr/handler/admin/api/SchemaZkVersionAPI.java b/solr/core/src/java/org/apache/solr/handler/admin/api/SchemaZkVersionAPI.java deleted file mode 100644 index 9928b9d8123..00000000000 --- a/solr/core/src/java/org/apache/solr/handler/admin/api/SchemaZkVersionAPI.java +++ /dev/null @@ -1,48 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.apache.solr.handler.admin.api; - -import static org.apache.solr.client.solrj.SolrRequest.METHOD.GET; - -import org.apache.solr.api.EndPoint; -import org.apache.solr.handler.SchemaHandler; -import org.apache.solr.request.SolrQueryRequest; -import org.apache.solr.response.SolrQueryResponse; -import org.apache.solr.security.PermissionNameProvider; - -/** - * V2 API for checking the ZK version of an in-use schema. - * - * <p>This API (GET /v2/collections/collectionName/schema/zkversion) is analogous to the v1 - * /solr/collectionName/schema/zkversion API. - */ -public class SchemaZkVersionAPI { - private final SchemaHandler schemaHandler; - - public SchemaZkVersionAPI(SchemaHandler schemaHandler) { - this.schemaHandler = schemaHandler; - } - - @EndPoint( - path = {"/schema/zkversion"}, - method = GET, - permission = PermissionNameProvider.Name.SCHEMA_READ_PERM) - public void getSchemaZkVersion(SolrQueryRequest req, SolrQueryResponse rsp) throws Exception { - schemaHandler.handleRequestBody(req, rsp); - } -} diff --git a/solr/core/src/java/org/apache/solr/jersey/InjectionFactories.java b/solr/core/src/java/org/apache/solr/jersey/InjectionFactories.java index d6111b15d19..f84090d4250 100644 --- a/solr/core/src/java/org/apache/solr/jersey/InjectionFactories.java +++ b/solr/core/src/java/org/apache/solr/jersey/InjectionFactories.java @@ -18,9 +18,11 @@ package org.apache.solr.jersey; import static org.apache.solr.jersey.RequestContextKeys.SOLR_CORE; +import static org.apache.solr.jersey.RequestContextKeys.SOLR_PARAMS; import javax.inject.Inject; import javax.ws.rs.container.ContainerRequestContext; +import org.apache.solr.common.params.SolrParams; import org.apache.solr.core.SolrCore; import org.apache.solr.request.SolrQueryRequest; import org.apache.solr.response.SolrQueryResponse; @@ -104,6 +106,24 @@ public class InjectionFactories { public void dispose(IndexSchema instance) {} } + public static class ReuseFromContextSolrParamsFactory implements Factory<SolrParams> { + + private final ContainerRequestContext containerRequestContext; + + @Inject + public ReuseFromContextSolrParamsFactory(ContainerRequestContext containerRequestContext) { + this.containerRequestContext = containerRequestContext; + } + + @Override + public SolrParams provide() { + return (SolrParams) containerRequestContext.getProperty(SOLR_PARAMS); + } + + @Override + public void dispose(SolrParams instance) {} + } + public static class SingletonFactory<T> implements Factory<T> { private final T singletonVal; diff --git a/solr/core/src/java/org/apache/solr/jersey/JerseyApplications.java b/solr/core/src/java/org/apache/solr/jersey/JerseyApplications.java index d8e3568e540..0bed6862b49 100644 --- a/solr/core/src/java/org/apache/solr/jersey/JerseyApplications.java +++ b/solr/core/src/java/org/apache/solr/jersey/JerseyApplications.java @@ -21,6 +21,7 @@ import io.swagger.v3.oas.annotations.OpenAPIDefinition; import io.swagger.v3.oas.annotations.info.Info; import io.swagger.v3.oas.annotations.info.License; import java.util.Map; +import org.apache.solr.common.params.SolrParams; import org.apache.solr.core.SolrCore; import org.apache.solr.request.SolrQueryRequest; import org.apache.solr.response.SolrQueryResponse; @@ -120,6 +121,9 @@ public class JerseyApplications { bindFactory(InjectionFactories.ReuseFromContextIndexSchemaFactory.class) .to(IndexSchema.class) .in(RequestScoped.class); + bindFactory(InjectionFactories.ReuseFromContextSolrParamsFactory.class) + .to(SolrParams.class) + .in(RequestScoped.class); } }); } diff --git a/solr/core/src/test/org/apache/solr/api/JerseyResourceTest.java b/solr/core/src/test/org/apache/solr/api/JerseyResourceTest.java index f96e1cb94a2..5e6f193c4e6 100644 --- a/solr/core/src/test/org/apache/solr/api/JerseyResourceTest.java +++ b/solr/core/src/test/org/apache/solr/api/JerseyResourceTest.java @@ -23,7 +23,7 @@ import static org.apache.solr.jersey.container.ContainerRequestUtils.DEFAULT_SEC import java.net.URI; import javax.ws.rs.container.ContainerRequestContext; import org.apache.solr.SolrTestCaseJ4; -import org.apache.solr.handler.admin.api.SchemaNameAPI; +import org.apache.solr.handler.admin.api.GetSchemaAPI; import org.glassfish.jersey.internal.MapPropertiesDelegate; import org.glassfish.jersey.server.ContainerRequest; import org.junit.Test; @@ -38,12 +38,12 @@ public class JerseyResourceTest extends SolrTestCaseJ4 { resource.containerRequestContext = requestContext; assertTrue(requestContext.getPropertyNames().isEmpty()); - final SchemaNameAPI.GetSchemaNameResponse returned = - resource.instantiateJerseyResponse(SchemaNameAPI.GetSchemaNameResponse.class); + final GetSchemaAPI.SchemaNameResponse returned = + resource.instantiateJerseyResponse(GetSchemaAPI.SchemaNameResponse.class); assertTrue(requestContext.getPropertyNames().contains(SOLR_JERSEY_RESPONSE)); - final SchemaNameAPI.GetSchemaNameResponse stashed = - (SchemaNameAPI.GetSchemaNameResponse) requestContext.getProperty(SOLR_JERSEY_RESPONSE); + final GetSchemaAPI.SchemaNameResponse stashed = + (GetSchemaAPI.SchemaNameResponse) requestContext.getProperty(SOLR_JERSEY_RESPONSE); assertEquals(stashed, returned); } diff --git a/solr/core/src/test/org/apache/solr/handler/admin/api/GetSchemaAPITest.java b/solr/core/src/test/org/apache/solr/handler/admin/api/GetSchemaAPITest.java index 5aa3e3b8890..69e8a9ba686 100644 --- a/solr/core/src/test/org/apache/solr/handler/admin/api/GetSchemaAPITest.java +++ b/solr/core/src/test/org/apache/solr/handler/admin/api/GetSchemaAPITest.java @@ -21,7 +21,11 @@ import static org.mockito.Mockito.when; import java.util.Map; import org.apache.solr.SolrTestCaseJ4; +import org.apache.solr.client.solrj.response.schema.SchemaResponse; +import org.apache.solr.common.util.NamedList; import org.apache.solr.common.util.SimpleOrderedMap; +import org.apache.solr.handler.SchemaHandler; +import org.apache.solr.handler.api.V2ApiUtils; import org.apache.solr.schema.IndexSchema; import org.apache.solr.schema.SchemaField; import org.apache.solr.schema.SimilarityFactory; @@ -55,6 +59,38 @@ public class GetSchemaAPITest extends SolrTestCaseJ4 { assertEquals("flagValue", response.schema.get("flagKey")); } + @Test + public void testLooksUpNameFromLatestCoreSchema() throws Exception { + when(mockSchema.getSchemaName()).thenReturn("expectedSchemaName"); + + final GetSchemaAPI.SchemaNameResponse response = api.getSchemaName(); + + assertEquals("expectedSchemaName", response.name); + assertNull(response.error); + } + + /** + * Test the v2 to v1 response mapping for /schema/name + * + * <p>{@link SchemaHandler} uses the v2 {@link GetSchemaAPI} (and its response class {@link + * GetSchemaAPI.SchemaNameResponse}) internally to serve the v1 version of this functionality. So + * it's important to make sure that our response stays compatible with SolrJ - both because that's + * important in its own right and because that ensures we haven't accidentally changed the v1 + * response format. + */ + @Test + public void testResponseCanBeParsedBySolrJ() { + final NamedList<Object> squashedResponse = new NamedList<>(); + final GetSchemaAPI.SchemaNameResponse typedResponse = new GetSchemaAPI.SchemaNameResponse(); + typedResponse.name = "someName"; + + V2ApiUtils.squashIntoNamedList(squashedResponse, typedResponse); + final SchemaResponse.SchemaNameResponse solrjResponse = new SchemaResponse.SchemaNameResponse(); + solrjResponse.setResponse(squashedResponse); + + assertEquals("someName", solrjResponse.getSchemaName()); + } + @Test public void testReliesOnIndexSchemaWhenFetchingSimilarity() { final var map = new SimpleOrderedMap<Object>(); diff --git a/solr/core/src/test/org/apache/solr/handler/admin/api/GetSchemaFieldsAPITest.java b/solr/core/src/test/org/apache/solr/handler/admin/api/GetSchemaFieldsAPITest.java new file mode 100644 index 00000000000..03e134602a0 --- /dev/null +++ b/solr/core/src/test/org/apache/solr/handler/admin/api/GetSchemaFieldsAPITest.java @@ -0,0 +1,148 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.solr.handler.admin.api; + +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +import java.util.ArrayList; +import java.util.List; +import java.util.Map; +import org.apache.solr.SolrTestCaseJ4; +import org.apache.solr.common.params.SolrParams; +import org.apache.solr.common.util.SimpleOrderedMap; +import org.apache.solr.schema.IndexSchema; +import org.junit.Before; +import org.junit.Test; + +/** Unit tests for {@link GetSchemaFieldAPI} */ +@SuppressWarnings("unchecked") +public class GetSchemaFieldsAPITest extends SolrTestCaseJ4 { + + private IndexSchema mockSchema; + private SolrParams mockParams; + private GetSchemaFieldAPI api; + + private SimpleOrderedMap<Object> mockField; + private ArrayList<SimpleOrderedMap<Object>> mockFieldList; + + @Before + public void setUpMocks() { + assumeWorkingMockito(); + + mockSchema = mock(IndexSchema.class); + mockParams = mock(SolrParams.class); + api = new GetSchemaFieldAPI(mockSchema, mockParams); + + mockField = new SimpleOrderedMap<>(); + mockField.add("name", "id"); + mockField.add("type", "string"); + + mockFieldList = new ArrayList<>(); + mockFieldList.add(mockField); + } + + @Test + public void testReliesOnIndexSchemaWhenFetchingAllFields() { + when(mockSchema.getNamedPropertyValues("fields", mockParams)) + .thenReturn(Map.of("fields", mockFieldList)); + + final var response = api.listSchemaFields(); + + assertNotNull(response); + assertCorrectListFields(response.fields); + } + + @Test + public void testReliesOnIndexSchemaWhenFetchingSpecificField() { + when(mockSchema.getNamedPropertyValues("fields", mockParams)) + .thenReturn(Map.of("fields", mockFieldList)); + + final var response = api.getFieldInfo("id"); + + assertNotNull(response); + assertCorrectField(response.fieldInfo); + } + + @Test + public void testReliesOnIndexSchemaWhenFetchingCopyFields() { + when(mockSchema.getNamedPropertyValues("copyfields", mockParams)) + .thenReturn(Map.of("copyFields", mockFieldList)); + + final var response = api.listCopyFields(); + + assertNotNull(response); + assertCorrectListFields(response.copyFields); + } + + @Test + public void testReliesOnIndexSchemaWhenFetchingDynamicFields() { + when(mockSchema.getNamedPropertyValues("dynamicfields", mockParams)) + .thenReturn(Map.of("dynamicFields", mockFieldList)); + + final var response = api.listDynamicFields(); + + assertNotNull(response); + assertCorrectListFields(response.dynamicFields); + } + + @Test + public void testReliesOnIndexSchemaWhenFetchingSpecificDynamicField() { + when(mockSchema.getNamedPropertyValues("dynamicfields", mockParams)) + .thenReturn(Map.of("dynamicFields", mockFieldList)); + + final var response = api.getDynamicFieldInfo("id"); + + assertNotNull(response); + assertCorrectField(response.dynamicFieldInfo); + } + + @Test + public void testReliesOnIndexSchemaWhenFetchingFieldTypes() { + when(mockSchema.getNamedPropertyValues("fieldtypes", mockParams)) + .thenReturn(Map.of("fieldTypes", mockFieldList)); + + final var response = api.listSchemaFieldTypes(); + + assertNotNull(response); + assertCorrectListFields(response.fieldTypes); + } + + @Test + public void testReliesOnIndexSchemaWhenFetchingSpecificFieldType() { + when(mockSchema.getNamedPropertyValues("fieldtypes", mockParams)) + .thenReturn(Map.of("fieldTypes", mockFieldList)); + + final var response = api.getFieldTypeInfo("id"); + + assertNotNull(response); + assertCorrectField(response.fieldTypeInfo); + } + + private void assertCorrectListFields(List<Object> responseFields) { + assertNotNull(responseFields); + + assertEquals(1, responseFields.size()); + assertCorrectField((SimpleOrderedMap<?>) responseFields.get(0)); + } + + private void assertCorrectField(SimpleOrderedMap<?> responseField) { + assertEquals("id", responseField.get("name")); + assertEquals("string", responseField.get("type")); + } +} diff --git a/solr/core/src/test/org/apache/solr/handler/admin/api/GetSchemaZkVersionAPITest.java b/solr/core/src/test/org/apache/solr/handler/admin/api/GetSchemaZkVersionAPITest.java new file mode 100644 index 00000000000..b6476613fd2 --- /dev/null +++ b/solr/core/src/test/org/apache/solr/handler/admin/api/GetSchemaZkVersionAPITest.java @@ -0,0 +1,54 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.solr.handler.admin.api; + +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +import org.apache.solr.SolrTestCaseJ4; +import org.apache.solr.core.SolrCore; +import org.apache.solr.schema.IndexSchema; +import org.junit.Before; +import org.junit.Test; + +/** Unit tests for {@link GetSchemaZkVersionAPI} */ +public class GetSchemaZkVersionAPITest extends SolrTestCaseJ4 { + + private SolrCore mockCore; + private IndexSchema mockSchema; + private GetSchemaZkVersionAPI api; + + @Before + public void setUpMocks() { + assumeWorkingMockito(); + + mockCore = mock(SolrCore.class); + mockSchema = mock(IndexSchema.class); + when(mockCore.getLatestSchema()).thenReturn(mockSchema); + api = new GetSchemaZkVersionAPI(mockCore); + } + + @Test + public void testReturnsInvalidZkVersionWhenNotManagedIndexSchema() throws Exception { + + final var response = api.getSchemaZkVersion(-1); + + assertNotNull(response); + assertEquals(-1, response.zkversion); + } +} diff --git a/solr/core/src/test/org/apache/solr/handler/admin/api/SchemaNameAPITest.java b/solr/core/src/test/org/apache/solr/handler/admin/api/SchemaNameAPITest.java deleted file mode 100644 index 003e626ad1a..00000000000 --- a/solr/core/src/test/org/apache/solr/handler/admin/api/SchemaNameAPITest.java +++ /dev/null @@ -1,85 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.apache.solr.handler.admin.api; - -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.when; - -import org.apache.solr.SolrTestCaseJ4; -import org.apache.solr.client.solrj.response.schema.SchemaResponse; -import org.apache.solr.common.util.NamedList; -import org.apache.solr.core.SolrCore; -import org.apache.solr.handler.SchemaHandler; -import org.apache.solr.handler.api.V2ApiUtils; -import org.apache.solr.schema.IndexSchema; -import org.junit.Before; -import org.junit.BeforeClass; -import org.junit.Test; - -/** Unit tests for {@link SchemaNameAPI} */ -public class SchemaNameAPITest extends SolrTestCaseJ4 { - - private SolrCore solrCore; - private IndexSchema schema; - - @BeforeClass - public static void ensureWorkingMockito() { - assumeWorkingMockito(); - } - - @Before - public void initMocks() { - solrCore = mock(SolrCore.class); - schema = mock(IndexSchema.class); - when(solrCore.getLatestSchema()).thenReturn(schema); - } - - @Test - public void testLooksUpNameFromLatestCoreSchema() throws Exception { - when(schema.getSchemaName()).thenReturn("expectedSchemaName"); - final SchemaNameAPI nameApi = new SchemaNameAPI(solrCore); - - final SchemaNameAPI.GetSchemaNameResponse response = nameApi.getSchemaName(); - - assertEquals("expectedSchemaName", response.name); - assertNull(response.error); - } - - /** - * Test the v2 to v1 response mapping for /schema/name - * - * <p>{@link SchemaHandler} uses the v2 {@link SchemaNameAPI} (and its response class {@link - * SchemaNameAPI.GetSchemaNameResponse}) internally to serve the v1 version of this functionality. - * So it's important to make sure that our response stays compatible with SolrJ - both because - * that's important in its own right and because that ensures we haven't accidentally changed the - * v1 response format. - */ - @Test - public void testResponseCanBeParsedBySolrJ() { - final NamedList<Object> squashedResponse = new NamedList<>(); - final SchemaNameAPI.GetSchemaNameResponse typedResponse = - new SchemaNameAPI.GetSchemaNameResponse(); - typedResponse.name = "someName"; - - V2ApiUtils.squashIntoNamedList(squashedResponse, typedResponse); - final SchemaResponse.SchemaNameResponse solrjResponse = new SchemaResponse.SchemaNameResponse(); - solrjResponse.setResponse(squashedResponse); - - assertEquals("someName", solrjResponse.getSchemaName()); - } -} diff --git a/solr/core/src/test/org/apache/solr/handler/admin/api/V2SchemaAPIMappingTest.java b/solr/core/src/test/org/apache/solr/handler/admin/api/V2SchemaAPIMappingTest.java index cb34f8a482e..3b1cab9b049 100644 --- a/solr/core/src/test/org/apache/solr/handler/admin/api/V2SchemaAPIMappingTest.java +++ b/solr/core/src/test/org/apache/solr/handler/admin/api/V2SchemaAPIMappingTest.java @@ -26,14 +26,6 @@ public class V2SchemaAPIMappingTest extends V2ApiMappingTest<SchemaHandler> { @Override public void populateApiBag() { - apiBag.registerObject(new SchemaZkVersionAPI(getRequestHandler())); - apiBag.registerObject(new SchemaListAllFieldsAPI(getRequestHandler())); - apiBag.registerObject(new SchemaGetFieldAPI(getRequestHandler())); - apiBag.registerObject(new SchemaListAllCopyFieldsAPI(getRequestHandler())); - apiBag.registerObject(new SchemaListAllDynamicFieldsAPI(getRequestHandler())); - apiBag.registerObject(new SchemaGetDynamicFieldAPI(getRequestHandler())); - apiBag.registerObject(new SchemaListAllFieldTypesAPI(getRequestHandler())); - apiBag.registerObject(new SchemaGetFieldTypeAPI(getRequestHandler())); apiBag.registerObject(new SchemaBulkModifyAPI(getRequestHandler())); } @@ -47,18 +39,6 @@ public class V2SchemaAPIMappingTest extends V2ApiMappingTest<SchemaHandler> { return true; } - @Test - public void testGetSchemaInfoApis() { - assertAnnotatedApiExistsFor("GET", "/schema/dynamicfields"); - assertAnnotatedApiExistsFor("GET", "/schema/dynamicfields/someDynamicField"); - assertAnnotatedApiExistsFor("GET", "/schema/fieldtypes"); - assertAnnotatedApiExistsFor("GET", "/schema/fieldtypes/someFieldType"); - assertAnnotatedApiExistsFor("GET", "/schema/fields"); - assertAnnotatedApiExistsFor("GET", "/schema/fields/someField"); - assertAnnotatedApiExistsFor("GET", "/schema/copyfields"); - assertAnnotatedApiExistsFor("GET", "/schema/zkversion"); - } - @Test public void testSchemaBulkModificationApiMapping() { assertAnnotatedApiExistsFor("POST", "/schema");
