You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@solr.apache.org by ge...@apache.org on 2023/06/27 19:38:23 UTC

[solr] branch branch_9x updated (e479adee1a3 -> 608a5444c80)

This is an automated email from the ASF dual-hosted git repository.

gerlowskija pushed a change to branch branch_9x
in repository https://gitbox.apache.org/repos/asf/solr.git


    from e479adee1a3 SOLR-16748: Official Dockerfile cleanup Args & GPG/SHA (#1563)
     new 968504d0818 SOLR-16395 JAX-RS conversion for remaining GET /schema/* endpoints (#1682)
     new 608a5444c80 SOLR-16395: Avoid Jersey resource inheritance

The 2 revisions listed above as "new" are entirely new to this
repository and will be described in separate emails.  The revisions
listed as "add" were already present in the repository and have only
been added to this reference.


Summary of changes:
 .../org/apache/solr/handler/SchemaHandler.java     | 172 +++++-------
 .../solr/handler/admin/api/GetSchemaAPI.java       |  22 +-
 .../solr/handler/admin/api/GetSchemaFieldAPI.java  | 301 +++++++++++++++++++++
 .../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}      |  43 +--
 .../solr/handler/admin/api/SchemaNameAPITest.java  |  85 ------
 .../handler/admin/api/V2SchemaAPIMappingTest.java  |  20 --
 21 files changed, 710 insertions(+), 691 deletions(-)
 create mode 100644 solr/core/src/java/org/apache/solr/handler/admin/api/GetSchemaFieldAPI.java
 create mode 100644 solr/core/src/java/org/apache/solr/handler/admin/api/GetSchemaZkVersionAPI.java
 delete mode 100644 solr/core/src/java/org/apache/solr/handler/admin/api/SchemaGetDynamicFieldAPI.java
 delete mode 100644 solr/core/src/java/org/apache/solr/handler/admin/api/SchemaGetFieldAPI.java
 delete mode 100644 solr/core/src/java/org/apache/solr/handler/admin/api/SchemaGetFieldTypeAPI.java
 delete mode 100644 solr/core/src/java/org/apache/solr/handler/admin/api/SchemaListAllCopyFieldsAPI.java
 delete mode 100644 solr/core/src/java/org/apache/solr/handler/admin/api/SchemaListAllDynamicFieldsAPI.java
 delete mode 100644 solr/core/src/java/org/apache/solr/handler/admin/api/SchemaListAllFieldTypesAPI.java
 delete mode 100644 solr/core/src/java/org/apache/solr/handler/admin/api/SchemaListAllFieldsAPI.java
 delete mode 100644 solr/core/src/java/org/apache/solr/handler/admin/api/SchemaNameAPI.java
 delete mode 100644 solr/core/src/java/org/apache/solr/handler/admin/api/SchemaZkVersionAPI.java
 create mode 100644 solr/core/src/test/org/apache/solr/handler/admin/api/GetSchemaFieldsAPITest.java
 copy solr/core/src/test/org/apache/solr/{schema/NotRequiredUniqueKeyTest.java => handler/admin/api/GetSchemaZkVersionAPITest.java} (51%)
 delete mode 100644 solr/core/src/test/org/apache/solr/handler/admin/api/SchemaNameAPITest.java


[solr] 02/02: SOLR-16395: Avoid Jersey resource inheritance

Posted by ge...@apache.org.
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 608a5444c808ffc89b9ba9976a5aaf3c9521b6f4
Author: Jason Gerlowski <ge...@apache.org>
AuthorDate: Tue Jun 27 14:08:47 2023 -0400

    SOLR-16395: Avoid Jersey resource inheritance
    
    Prior to this commit GetSchemaFieldAPI inherited from a parent resource
    class, GetSchemaAPI.
    
    The "strict validation" linting that Jersey offers (see the config
    setting in JerseyApplications.java) complained about this, as it was
    detecting that the endpoints offered by GetSchemaAPI were being
    shadowed.
    
    This commit removes this inheritance, so that Jersey's validation (when
    enabled) can complete successfully on newly created SolrCores.
---
 .../java/org/apache/solr/handler/admin/api/GetSchemaFieldAPI.java  | 7 +++++--
 1 file changed, 5 insertions(+), 2 deletions(-)

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
index b5c3fbbb2a7..2ad7db4d863 100644
--- 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
@@ -28,6 +28,7 @@ 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.api.JerseyResource;
 import org.apache.solr.common.MapWriter;
 import org.apache.solr.common.SolrException;
 import org.apache.solr.common.cloud.SolrClassLoader;
@@ -54,15 +55,17 @@ import org.apache.solr.security.PermissionNameProvider;
  *   <li>/fieldtypes/{fieldTypeName}
  * </ul>
  */
-public class GetSchemaFieldAPI extends GetSchemaAPI {
+@Path("/{a:cores|collections}/{collectionName}/schema")
+public class GetSchemaFieldAPI /*extends GetSchemaAPI*/ extends JerseyResource {
 
+  private final IndexSchema indexSchema;
   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.indexSchema = indexSchema;
     this.params = params;
   }
 


[solr] 01/02: SOLR-16395 JAX-RS conversion for remaining GET /schema/* endpoints (#1682)

Posted by ge...@apache.org.
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 <79...@users.noreply.github.com>
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");