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 2022/07/07 15:49:01 UTC

[solr] branch branch_9x updated: SOLR-15182: Remove remaining apispec files (#932)

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


The following commit(s) were added to refs/heads/branch_9x by this push:
     new 57c33c7c5e4 SOLR-15182: Remove remaining apispec files (#932)
57c33c7c5e4 is described below

commit 57c33c7c5e4d7d77cd875e859b172290bf06d4bf
Author: Jason Gerlowski <ge...@apache.org>
AuthorDate: Thu Jul 7 11:33:03 2022 -0400

    SOLR-15182: Remove remaining apispec files (#932)
    
    Solr's been in the slow process of moving its v2 APIs away from the
    existing apispec/mapping framework towards one that relies on more
    explicit annotations to specify API properties.
    
    This commit finishes this work by removing the remaining apispec files.
    Some remnants of the apispec framework remain, and be removed in
    subsequent PRs, but all explicitly defined v2 APIs now use the
    annotation framework!
---
 solr/core/src/java/org/apache/solr/api/ApiBag.java | 22 +++++---
 .../java/org/apache/solr/handler/BlobHandler.java  | 11 +++-
 .../solr/handler/admin/api/GetBlobInfoAPI.java     | 64 ++++++++++++++++++++++
 .../solr/handler/admin/api/UploadBlobAPI.java      | 48 ++++++++++++++++
 .../solr/handler/admin/V2ApiMappingTest.java       |  3 +-
 .../solr/handler/admin/V2BlobAPIMappingTest.java   | 57 +++++++++++++++++++
 .../src/resources/apispec/core.system.blob.json    | 20 -------
 .../resources/apispec/core.system.blob.upload.json | 12 ----
 solr/solrj/src/resources/apispec/emptySpec.json    | 11 ----
 9 files changed, 195 insertions(+), 53 deletions(-)

diff --git a/solr/core/src/java/org/apache/solr/api/ApiBag.java b/solr/core/src/java/org/apache/solr/api/ApiBag.java
index 53927c68be5..7b55610c6c3 100644
--- a/solr/core/src/java/org/apache/solr/api/ApiBag.java
+++ b/solr/core/src/java/org/apache/solr/api/ApiBag.java
@@ -380,34 +380,42 @@ public class ApiBag {
     return b.build();
   }
 
-  public static final SpecProvider EMPTY_SPEC = () -> ValidatingJsonMap.EMPTY;
   public static final String HANDLER_NAME = "handlerName";
+  public static final SpecProvider EMPTY_SPEC = () -> ValidatingJsonMap.EMPTY;
   public static final Set<String> KNOWN_TYPES =
       ImmutableSet.of("string", "boolean", "list", "int", "double", "object");
 
+  // A Spec template for GET AND POST APIs using the /$handlerName template-variable.
+  public static final SpecProvider HANDLER_NAME_SPEC_PROVIDER =
+      () -> {
+        final ValidatingJsonMap spec = new ValidatingJsonMap();
+        spec.put("methods", Lists.newArrayList("GET", "POST"));
+        final ValidatingJsonMap urlMap = new ValidatingJsonMap();
+        urlMap.put("paths", Collections.singletonList("$" + HANDLER_NAME));
+        spec.put("url", urlMap);
+        return spec;
+      };
+
   public PathTrie<Api> getRegistry(String method) {
     return apis.get(method);
   }
 
   public void registerLazy(PluginBag.PluginHolder<SolrRequestHandler> holder, PluginInfo info) {
-    String specName = info.attributes.get("spec");
-    if (specName == null) specName = "emptySpec";
     register(
-        new LazyLoadedApi(Utils.getSpec(specName), holder),
+        new LazyLoadedApi(HANDLER_NAME_SPEC_PROVIDER, holder),
         Collections.singletonMap(HANDLER_NAME, info.attributes.get(NAME)));
   }
 
   public static SpecProvider constructSpec(PluginInfo info) {
     Object specObj = info == null ? null : info.attributes.get("spec");
-    if (specObj == null) specObj = "emptySpec";
-    if (specObj instanceof Map) {
+    if (specObj != null && specObj instanceof Map) {
       // Value from Map<String,String> can be a Map because in PluginInfo(String, Map) we assign a
       // Map<String, Object>
       // assert false : "got a map when this should only be Strings";
       Map<?, ?> map = (Map<?, ?>) specObj;
       return () -> ValidatingJsonMap.getDeepCopy(map, 4, false);
     } else {
-      return Utils.getSpec((String) specObj);
+      return HANDLER_NAME_SPEC_PROVIDER;
     }
   }
 
diff --git a/solr/core/src/java/org/apache/solr/handler/BlobHandler.java b/solr/core/src/java/org/apache/solr/handler/BlobHandler.java
index d6fc0c5b7f1..3bb14b19ed4 100644
--- a/solr/core/src/java/org/apache/solr/handler/BlobHandler.java
+++ b/solr/core/src/java/org/apache/solr/handler/BlobHandler.java
@@ -22,6 +22,7 @@ import static org.apache.solr.common.params.CommonParams.JSON;
 import static org.apache.solr.common.params.CommonParams.SORT;
 import static org.apache.solr.common.params.CommonParams.VERSION;
 
+import com.google.common.collect.Lists;
 import java.io.IOException;
 import java.io.InputStream;
 import java.io.OutputStream;
@@ -41,8 +42,8 @@ import org.apache.lucene.search.SortField;
 import org.apache.lucene.search.TermQuery;
 import org.apache.lucene.search.TopDocs;
 import org.apache.lucene.search.TopFieldDocs;
+import org.apache.solr.api.AnnotatedApi;
 import org.apache.solr.api.Api;
-import org.apache.solr.api.ApiBag;
 import org.apache.solr.common.SolrException;
 import org.apache.solr.common.SolrInputDocument;
 import org.apache.solr.common.params.CommonParams;
@@ -53,6 +54,8 @@ import org.apache.solr.common.util.NamedList;
 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.GetBlobInfoAPI;
+import org.apache.solr.handler.admin.api.UploadBlobAPI;
 import org.apache.solr.request.LocalSolrQueryRequest;
 import org.apache.solr.request.SolrQueryRequest;
 import org.apache.solr.request.SolrRequestHandler;
@@ -338,7 +341,11 @@ public class BlobHandler extends RequestHandlerBase
 
   @Override
   public Collection<Api> getApis() {
-    return ApiBag.wrapRequestHandlers(this, "core.system.blob", "core.system.blob.upload");
+    final List<Api> apis = Lists.newArrayList();
+    apis.addAll(AnnotatedApi.getApis(new GetBlobInfoAPI(this)));
+    apis.addAll(AnnotatedApi.getApis(new UploadBlobAPI(this)));
+
+    return apis;
   }
 
   @Override
diff --git a/solr/core/src/java/org/apache/solr/handler/admin/api/GetBlobInfoAPI.java b/solr/core/src/java/org/apache/solr/handler/admin/api/GetBlobInfoAPI.java
new file mode 100644
index 00000000000..29345c12b88
--- /dev/null
+++ b/solr/core/src/java/org/apache/solr/handler/admin/api/GetBlobInfoAPI.java
@@ -0,0 +1,64 @@
+/*
+ * 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 static org.apache.solr.security.PermissionNameProvider.Name.READ_PERM;
+
+import org.apache.solr.api.EndPoint;
+import org.apache.solr.handler.BlobHandler;
+import org.apache.solr.request.SolrQueryRequest;
+import org.apache.solr.response.SolrQueryResponse;
+
+/**
+ * V2 APIs for fetching blob(s) and their metadata
+ *
+ * <p>These APIs (GET /v2/collections/.system/blob/*) is analogous to the v1 GET
+ * /solr/.system/blob/* APIs.
+ */
+public class GetBlobInfoAPI {
+  private BlobHandler blobHandler;
+
+  public GetBlobInfoAPI(BlobHandler blobHandler) {
+    this.blobHandler = blobHandler;
+  }
+
+  @EndPoint(
+      path = {"/blob"},
+      method = GET,
+      permission = READ_PERM)
+  public void getAllBlobs(SolrQueryRequest req, SolrQueryResponse rsp) {
+    blobHandler.handleRequest(req, rsp);
+  }
+
+  @EndPoint(
+      path = {"/blob/{blobName}"},
+      method = GET,
+      permission = READ_PERM)
+  public void getBlobByName(SolrQueryRequest req, SolrQueryResponse rsp) {
+    blobHandler.handleRequest(req, rsp);
+  }
+
+  @EndPoint(
+      path = {"/blob/{blobName}/{blobVersion}"},
+      method = GET,
+      permission = READ_PERM)
+  public void getVersionedBlobByName(SolrQueryRequest req, SolrQueryResponse rsp) {
+    blobHandler.handleRequest(req, rsp);
+  }
+}
diff --git a/solr/core/src/java/org/apache/solr/handler/admin/api/UploadBlobAPI.java b/solr/core/src/java/org/apache/solr/handler/admin/api/UploadBlobAPI.java
new file mode 100644
index 00000000000..2488a109648
--- /dev/null
+++ b/solr/core/src/java/org/apache/solr/handler/admin/api/UploadBlobAPI.java
@@ -0,0 +1,48 @@
+/*
+ * 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.POST;
+import static org.apache.solr.security.PermissionNameProvider.Name.UPDATE_PERM;
+
+import org.apache.solr.api.EndPoint;
+import org.apache.solr.handler.BlobHandler;
+import org.apache.solr.request.SolrQueryRequest;
+import org.apache.solr.response.SolrQueryResponse;
+
+/**
+ * V2 API for uploading blobs into Solr's .system blobstore
+ *
+ * <p>This API (POST /v2/collections/.system/blob/blobName) is analogous to the v1 POST
+ * /solr/.system/blob/blobName API.
+ */
+public class UploadBlobAPI {
+  private final BlobHandler blobHandler;
+
+  public UploadBlobAPI(BlobHandler blobHandler) {
+    this.blobHandler = blobHandler;
+  }
+
+  @EndPoint(
+      path = {"/blob/{blobName}"},
+      method = POST,
+      permission = UPDATE_PERM)
+  public void uploadBlob(SolrQueryRequest req, SolrQueryResponse rsp) {
+    blobHandler.handleRequest(req, rsp);
+  }
+}
diff --git a/solr/core/src/test/org/apache/solr/handler/admin/V2ApiMappingTest.java b/solr/core/src/test/org/apache/solr/handler/admin/V2ApiMappingTest.java
index 06b7522ad6e..38f9220344d 100644
--- a/solr/core/src/test/org/apache/solr/handler/admin/V2ApiMappingTest.java
+++ b/solr/core/src/test/org/apache/solr/handler/admin/V2ApiMappingTest.java
@@ -204,11 +204,12 @@ public abstract class V2ApiMappingTest<T extends RequestHandlerBase> extends Sol
     return queryRequestCaptor.getValue().getParams();
   }
 
-  protected void assertAnnotatedApiExistsFor(String method, String path) {
+  protected AnnotatedApi assertAnnotatedApiExistsFor(String method, String path) {
     final AnnotatedApi api = getAnnotatedApiFor(method, path);
     assertTrue(
         "Expected to find API mapping for [" + method + " " + path + "] but none found!",
         api != null);
+    return api;
   }
 
   protected AnnotatedApi getAnnotatedApiFor(String method, String path) {
diff --git a/solr/core/src/test/org/apache/solr/handler/admin/V2BlobAPIMappingTest.java b/solr/core/src/test/org/apache/solr/handler/admin/V2BlobAPIMappingTest.java
new file mode 100644
index 00000000000..9d1d444a012
--- /dev/null
+++ b/solr/core/src/test/org/apache/solr/handler/admin/V2BlobAPIMappingTest.java
@@ -0,0 +1,57 @@
+/*
+ * 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;
+
+import org.apache.solr.api.AnnotatedApi;
+import org.apache.solr.handler.BlobHandler;
+import org.apache.solr.handler.admin.api.GetBlobInfoAPI;
+import org.apache.solr.handler.admin.api.UploadBlobAPI;
+import org.junit.Test;
+
+public class V2BlobAPIMappingTest extends V2ApiMappingTest<BlobHandler> {
+  @Override
+  public void populateApiBag() {
+    apiBag.registerObject(new GetBlobInfoAPI(getRequestHandler()));
+    apiBag.registerObject(new UploadBlobAPI(getRequestHandler()));
+  }
+
+  @Override
+  public BlobHandler createUnderlyingRequestHandler() {
+    return createMock(BlobHandler.class);
+  }
+
+  @Override
+  public boolean isCoreSpecific() {
+    return true;
+  }
+
+  @Test
+  public void testGetBlobApiMappings() {
+    assertAnnotatedApiExistsFor("GET", "/blob");
+    assertAnnotatedApiExistsFor("GET", "/blob/someBlobName");
+    assertAnnotatedApiExistsFor("GET", "/blob/someBlobName/123");
+  }
+
+  @Test
+  public void testUploadBlobApiMapping() {
+    final AnnotatedApi uploadBlobApi = assertAnnotatedApiExistsFor("POST", "/blob/someBlobName");
+    assertEquals(1, uploadBlobApi.getCommands().keySet().size());
+    // Empty-string is the indicator for POST requests that don't use the explicit "command" syntax.
+    assertEquals("", uploadBlobApi.getCommands().keySet().iterator().next());
+  }
+}
diff --git a/solr/solrj/src/resources/apispec/core.system.blob.json b/solr/solrj/src/resources/apispec/core.system.blob.json
deleted file mode 100644
index 9bbb6c87c7b..00000000000
--- a/solr/solrj/src/resources/apispec/core.system.blob.json
+++ /dev/null
@@ -1,20 +0,0 @@
-{
-  "documentation": "https://solr.apache.org/guide/8_11/blob-store-api.html",
-  "description": "Lists blobs in the blob store (the .system collection). The list can be limited by name or name and version.",
-  "methods": [
-    "GET"
-  ],
-  "url": {
-    "paths": [
-      "/blob",
-      "/blob/{name}",
-      "/blob/{name}/{version}"
-    ],
-    "params": {
-      "wt": {
-        "type":"string",
-        "description": "Use the value 'filestream' to get the file content. Use other response writers (such as xml, or json) to fetch only the metadata."
-      }
-    }
-  }
-}
diff --git a/solr/solrj/src/resources/apispec/core.system.blob.upload.json b/solr/solrj/src/resources/apispec/core.system.blob.upload.json
deleted file mode 100644
index 16a8f2c23c9..00000000000
--- a/solr/solrj/src/resources/apispec/core.system.blob.upload.json
+++ /dev/null
@@ -1,12 +0,0 @@
-{
-  "documentation": "https://solr.apache.org/guide/8_11/blob-store-api.html",
-  "description": "Uploads a blob to the blob store. Note that the blob store is a specially named collection (which must be '.system') which must be created before uploading a blob to it.",
-  "methods": [
-    "POST"
-  ],
-  "url": {
-    "paths": [
-      "/blob/{name}"
-    ]
-  }
-}
diff --git a/solr/solrj/src/resources/apispec/emptySpec.json b/solr/solrj/src/resources/apispec/emptySpec.json
deleted file mode 100644
index d95bff9a6be..00000000000
--- a/solr/solrj/src/resources/apispec/emptySpec.json
+++ /dev/null
@@ -1,11 +0,0 @@
-{
-  "methods": [
-    "GET",
-    "POST"
-  ],
-  "url": {
-    "paths": [
-      "$handlerName"
-    ]
-  }
-}