From f7e4190d72f29fc10529d6a0b6c20fb234871498 Mon Sep 17 00:00:00 2001 From: Richard Adams Date: Mon, 10 Oct 2016 18:55:47 +0100 Subject: [PATCH] initial commit of Search API SearchWrapper classes, derrserialization of search hit subtypes --- .../dataverse/api/v1/SearchConfig.java | 63 +++++++++++++++ .../dataverse/api/v1/SearchOperations.java | 31 ++++++++ .../dataverse/api/v1/SearchType.java | 12 +++ .../dataverse/api/v1/SortBy.java | 9 +++ .../dataverse/api/v1/SortOrder.java | 6 ++ .../dataverse/entities/DatasetSearchHit.java | 21 +++++ .../entities/DataverseSearchHit.java | 21 +++++ .../dataverse/entities/FileSearchHit.java | 27 +++++++ .../dataverse/entities/SearchHit.java | 28 +++++++ .../dataverse/entities/SearchHitList.java | 7 ++ .../dataverse/entities/SearchResults.java | 22 ++++++ .../dataverse/http/AbstractOpsImplV1.java | 78 +++++++++++++++++++ .../http/SearchOperationsImplV1.java | 51 ++++++++++++ .../dataverse/http/SearchOperationsTest.java | 29 +++++++ .../dataverse/search/SearchConfigTest.java | 26 +++++++ zoo.json | 17 ++++ 16 files changed, 448 insertions(+) create mode 100644 src/main/java/com/researchspace/dataverse/api/v1/SearchConfig.java create mode 100644 src/main/java/com/researchspace/dataverse/api/v1/SearchOperations.java create mode 100644 src/main/java/com/researchspace/dataverse/api/v1/SearchType.java create mode 100644 src/main/java/com/researchspace/dataverse/api/v1/SortBy.java create mode 100644 src/main/java/com/researchspace/dataverse/api/v1/SortOrder.java create mode 100644 src/main/java/com/researchspace/dataverse/entities/DatasetSearchHit.java create mode 100644 src/main/java/com/researchspace/dataverse/entities/DataverseSearchHit.java create mode 100644 src/main/java/com/researchspace/dataverse/entities/FileSearchHit.java create mode 100644 src/main/java/com/researchspace/dataverse/entities/SearchHit.java create mode 100644 src/main/java/com/researchspace/dataverse/entities/SearchHitList.java create mode 100644 src/main/java/com/researchspace/dataverse/entities/SearchResults.java create mode 100644 src/main/java/com/researchspace/dataverse/http/AbstractOpsImplV1.java create mode 100644 src/main/java/com/researchspace/dataverse/http/SearchOperationsImplV1.java create mode 100644 src/test/java/com/researchspace/dataverse/http/SearchOperationsTest.java create mode 100644 src/test/java/com/researchspace/dataverse/search/SearchConfigTest.java create mode 100644 zoo.json diff --git a/src/main/java/com/researchspace/dataverse/api/v1/SearchConfig.java b/src/main/java/com/researchspace/dataverse/api/v1/SearchConfig.java new file mode 100644 index 0000000..c45eb8a --- /dev/null +++ b/src/main/java/com/researchspace/dataverse/api/v1/SearchConfig.java @@ -0,0 +1,63 @@ +package com.researchspace.dataverse.api.v1; + +import java.util.EnumSet; + +import org.apache.commons.lang.Validate; +import org.springframework.web.util.UriComponentsBuilder; + +import lombok.Builder; +import lombok.Data; +import lombok.Value; + +@Builder(toBuilder = true) +@Value +public class SearchConfig { + /** + * Builder for configuring search + * @author rspace + * + */ + public static class SearchConfigBuilder { + /** + * Sets results per page. Maximum is 1000 + * + * @param perPage + * if > 1000, will set to 1000 + * @return + * @throws IllegalArgumentException + * if perPage <= 0 + */ + SearchConfigBuilder perPage(int perPage) { + Validate.isTrue(perPage > 0, "Cannot have negative results per page"); + if (perPage > MAX_RESULTS_PER_PAGE) { + perPage = MAX_RESULTS_PER_PAGE; + } + this.perPage = perPage; + return this; + } + + /** + * Sets results per page. Maximum is 1000 + * + * @param perPage + * if > 1000, will set to 1000 + * @return + * @throws IllegalArgumentException + * if perPage <= 0 + */ + SearchConfigBuilder start(int start) { + Validate.isTrue(start > 0, "Cannot have negative starting point"); + this.start = start; + return this; + } + + } + private static final int MAX_RESULTS_PER_PAGE = 1000; + private EnumSet type; + private String q, subtree, filterQuery; + private SortBy sortBy; + private SortOrder sortOrder; + private int perPage, start; + private boolean showRelevance, showFacets; + +} diff --git a/src/main/java/com/researchspace/dataverse/api/v1/SearchOperations.java b/src/main/java/com/researchspace/dataverse/api/v1/SearchOperations.java new file mode 100644 index 0000000..240fc4c --- /dev/null +++ b/src/main/java/com/researchspace/dataverse/api/v1/SearchOperations.java @@ -0,0 +1,31 @@ +package com.researchspace.dataverse.api.v1; + +import com.researchspace.dataverse.api.v1.SearchConfig.SearchConfigBuilder; + +/** + *
+  Copyright 2016 ResearchSpace
+
+   Licensed 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.
+ 
+ Top-level entry point into the Dataverse Level1 Search API + * @author rspace + * + */ +public interface SearchOperations { + + SearchConfigBuilder builder(); + + void search(SearchConfig cfg); + +} diff --git a/src/main/java/com/researchspace/dataverse/api/v1/SearchType.java b/src/main/java/com/researchspace/dataverse/api/v1/SearchType.java new file mode 100644 index 0000000..20f5c41 --- /dev/null +++ b/src/main/java/com/researchspace/dataverse/api/v1/SearchType.java @@ -0,0 +1,12 @@ +package com.researchspace.dataverse.api.v1; +/** + * A value for the 'type' parameter of a search + * @author rspace + * + */ +public enum SearchType { + + dataverse, + dataset, + file; +} diff --git a/src/main/java/com/researchspace/dataverse/api/v1/SortBy.java b/src/main/java/com/researchspace/dataverse/api/v1/SortBy.java new file mode 100644 index 0000000..fb9748b --- /dev/null +++ b/src/main/java/com/researchspace/dataverse/api/v1/SortBy.java @@ -0,0 +1,9 @@ +package com.researchspace.dataverse.api.v1; +/** + * Sorting category + * @author rspace + * + */ +public enum SortBy { + name, date +} diff --git a/src/main/java/com/researchspace/dataverse/api/v1/SortOrder.java b/src/main/java/com/researchspace/dataverse/api/v1/SortOrder.java new file mode 100644 index 0000000..36311d1 --- /dev/null +++ b/src/main/java/com/researchspace/dataverse/api/v1/SortOrder.java @@ -0,0 +1,6 @@ +package com.researchspace.dataverse.api.v1; + +public enum SortOrder { + + asc, desc; +} diff --git a/src/main/java/com/researchspace/dataverse/entities/DatasetSearchHit.java b/src/main/java/com/researchspace/dataverse/entities/DatasetSearchHit.java new file mode 100644 index 0000000..34cad5a --- /dev/null +++ b/src/main/java/com/researchspace/dataverse/entities/DatasetSearchHit.java @@ -0,0 +1,21 @@ +package com.researchspace.dataverse.entities; + +import com.fasterxml.jackson.annotation.JsonProperty; + +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.ToString; + +@Data +@EqualsAndHashCode(callSuper = true) +@ToString(callSuper = true) +public class DatasetSearchHit extends SearchHit { + + private @JsonProperty("global_id") String globalId; + private @JsonProperty("published_at") String publishedAt; + private String description, citation, citationHtml; + + public String getType (){ + return "file"; + } +} diff --git a/src/main/java/com/researchspace/dataverse/entities/DataverseSearchHit.java b/src/main/java/com/researchspace/dataverse/entities/DataverseSearchHit.java new file mode 100644 index 0000000..c64c2ac --- /dev/null +++ b/src/main/java/com/researchspace/dataverse/entities/DataverseSearchHit.java @@ -0,0 +1,21 @@ +package com.researchspace.dataverse.entities; + +import com.fasterxml.jackson.annotation.JsonProperty; + +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.ToString; + +@Data +@EqualsAndHashCode(callSuper=true) +@ToString(callSuper=true) +public class DataverseSearchHit extends SearchHit { + String identifier; + @JsonProperty("published_at") + String publishedAt; + + public String getType (){ + return "dataverse"; + } + +} diff --git a/src/main/java/com/researchspace/dataverse/entities/FileSearchHit.java b/src/main/java/com/researchspace/dataverse/entities/FileSearchHit.java new file mode 100644 index 0000000..e699305 --- /dev/null +++ b/src/main/java/com/researchspace/dataverse/entities/FileSearchHit.java @@ -0,0 +1,27 @@ +package com.researchspace.dataverse.entities; + +import com.fasterxml.jackson.annotation.JsonProperty; + +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.ToString; + + +@EqualsAndHashCode(callSuper=true) +@ToString(callSuper=true) +@Data +public class FileSearchHit extends SearchHit { + + private @JsonProperty("file_id") String fileId; + private @JsonProperty("dataset_citation") String datasetCitation; + private @JsonProperty("file_content_type") String fileContentType; + private String description, md5; + private @JsonProperty("size_in_bytes") int size; + @JsonProperty("published_at") + private String publishedAt; + + public String getType (){ + return "file"; + } + +} diff --git a/src/main/java/com/researchspace/dataverse/entities/SearchHit.java b/src/main/java/com/researchspace/dataverse/entities/SearchHit.java new file mode 100644 index 0000000..82d4d20 --- /dev/null +++ b/src/main/java/com/researchspace/dataverse/entities/SearchHit.java @@ -0,0 +1,28 @@ +package com.researchspace.dataverse.entities; + +import com.fasterxml.jackson.annotation.JsonProperty; +import com.fasterxml.jackson.annotation.JsonSubTypes; +import com.fasterxml.jackson.annotation.JsonSubTypes.Type; +import com.fasterxml.jackson.annotation.JsonTypeInfo; + +import lombok.Data; + + +@JsonTypeInfo( + use = JsonTypeInfo.Id.NAME, + include = JsonTypeInfo.As.PROPERTY, + property = "type" + ) +@JsonSubTypes({ + @Type(value = DataverseSearchHit.class, name = "dataverse"), + @Type(value = DatasetSearchHit.class, name = "dataset"), + @Type(value = FileSearchHit.class, name = "file"), + }) +@Data +public abstract class SearchHit { + private String name, type, url; + + @JsonProperty(value="image_url") + private String imageUrl; + +} diff --git a/src/main/java/com/researchspace/dataverse/entities/SearchHitList.java b/src/main/java/com/researchspace/dataverse/entities/SearchHitList.java new file mode 100644 index 0000000..eacc426 --- /dev/null +++ b/src/main/java/com/researchspace/dataverse/entities/SearchHitList.java @@ -0,0 +1,7 @@ +package com.researchspace.dataverse.entities; + +import java.util.ArrayList; + +public class SearchHitList extends ArrayList{ + +} diff --git a/src/main/java/com/researchspace/dataverse/entities/SearchResults.java b/src/main/java/com/researchspace/dataverse/entities/SearchResults.java new file mode 100644 index 0000000..08fa816 --- /dev/null +++ b/src/main/java/com/researchspace/dataverse/entities/SearchResults.java @@ -0,0 +1,22 @@ +package com.researchspace.dataverse.entities; + +import java.util.List; + +import com.fasterxml.jackson.annotation.JsonProperty; + +import lombok.Data; + +@Data +public class SearchResults { + + String q; + @JsonProperty(value="total_count") + private int totalCount; + @JsonProperty(value="count_in_response") + private int countInResponse; + int start; + + List spellingAlternatives; + List items; + +} diff --git a/src/main/java/com/researchspace/dataverse/http/AbstractOpsImplV1.java b/src/main/java/com/researchspace/dataverse/http/AbstractOpsImplV1.java new file mode 100644 index 0000000..93b8acc --- /dev/null +++ b/src/main/java/com/researchspace/dataverse/http/AbstractOpsImplV1.java @@ -0,0 +1,78 @@ +package com.researchspace.dataverse.http; + +import java.util.Arrays; + +import org.apache.commons.lang.StringUtils; +import org.springframework.http.HttpHeaders; +import org.springframework.http.MediaType; +import org.springframework.http.ResponseEntity; +import org.springframework.web.client.RestClientException; +import org.springframework.web.client.RestTemplate; + +import com.researchspace.dataverse.api.v1.DataverseConfig; +import com.researchspace.dataverse.entities.DataverseResponse; +import com.researchspace.springrest.ext.LoggingResponseErrorHandler; +import com.researchspace.springrest.ext.RestUtil; + +import lombok.extern.slf4j.Slf4j; +@Slf4j +public abstract class AbstractOpsImplV1 { + + String apiKey = ""; + String serverURL = ""; + String serverAPIURL = serverURL +"/api"; + String serverAPIv1URL = serverAPIURL +"/v1"; + + final String apiHeader = "X-Dataverse-key"; + public void setApiKey(String apiKey) { + this.apiKey = apiKey; + } + + public void setServerURL(String serverURL) { + this.serverURL = serverURL; + this.serverAPIURL = serverURL + "/api"; + this.serverAPIv1URL = this.serverAPIURL +"/v1"; + } + + public void configure(DataverseConfig config) { + setApiKey(config.getApiKey()); + setServerURL(config.getServerURL().toString()); + } + + void handleError(ResponseEntity> resp) { + log.debug("{}", resp.getBody()); + if (RestUtil.isError(resp.getStatusCode())) { + String msg = String.format("Error code returned %d with message [%s]", resp.getStatusCodeValue(), + resp.getBody().getMessage()); + log.error(msg); + throw new RestClientException(msg); + } + + } + + RestTemplate createTemplate() { + RestTemplate template = new RestTemplate(); + template.setErrorHandler(new LoggingResponseErrorHandler()); + return template; + } + + String createV1Url(String ... pathComponents) { + String url = serverAPIv1URL + "/" + StringUtils.join(pathComponents, "/") ; + log.info("URL is {}", url); + return url; + } + + String createAdminUrl(String ... pathComponents) { + String url = serverAPIURL + "/" + StringUtils.join(pathComponents, "/") ; + log.info("URL is {}", url); + return url; + } + + HttpHeaders addAPIKeyToHeader() { + HttpHeaders headers = new HttpHeaders(); + headers.setAccept(Arrays.asList(MediaType.APPLICATION_JSON)); + headers.add(apiHeader, apiKey); + return headers; + } + +} diff --git a/src/main/java/com/researchspace/dataverse/http/SearchOperationsImplV1.java b/src/main/java/com/researchspace/dataverse/http/SearchOperationsImplV1.java new file mode 100644 index 0000000..9052103 --- /dev/null +++ b/src/main/java/com/researchspace/dataverse/http/SearchOperationsImplV1.java @@ -0,0 +1,51 @@ +package com.researchspace.dataverse.http; + +import org.springframework.http.HttpEntity; +import org.springframework.http.HttpHeaders; +import org.springframework.http.HttpMethod; +import org.springframework.http.ResponseEntity; +import org.springframework.web.client.RestTemplate; +import org.springframework.web.util.UriComponentsBuilder; + +import com.researchspace.dataverse.api.v1.SearchConfig; +import com.researchspace.dataverse.api.v1.SearchConfig.SearchConfigBuilder; +import com.researchspace.dataverse.api.v1.SearchOperations; +import com.researchspace.dataverse.entities.DataverseResponse; +import com.researchspace.dataverse.entities.DataverseSearchHit; +import com.researchspace.dataverse.entities.SearchHit; +import com.researchspace.dataverse.entities.SearchResults; + +import lombok.extern.slf4j.Slf4j; +import org.springframework.core.ParameterizedTypeReference; +@Slf4j +public class SearchOperationsImplV1 extends AbstractOpsImplV1 implements SearchOperations { + + @Override + public SearchConfigBuilder builder() { + return SearchConfig.builder(); + } + + @Override + public void search(SearchConfig cfg) { + String url = createV1Url("search"); + url = UriComponentsBuilder.fromUriString(url) + .queryParam("q", cfg.getQ()) + .build(true) + .toUriString(); + RestTemplate template = createTemplate(); + HttpHeaders headers = addAPIKeyToHeader(); + HttpEntity entity = new HttpEntity("", headers); + ParameterizedTypeReference> type = new ParameterizedTypeReference>() { + }; + + ResponseEntity> resp = template.exchange(url, HttpMethod.GET, entity, type); + + log.info(resp.getBody().getData().toString()); + + + + } + + + +} diff --git a/src/test/java/com/researchspace/dataverse/http/SearchOperationsTest.java b/src/test/java/com/researchspace/dataverse/http/SearchOperationsTest.java new file mode 100644 index 0000000..c034c5f --- /dev/null +++ b/src/test/java/com/researchspace/dataverse/http/SearchOperationsTest.java @@ -0,0 +1,29 @@ +package com.researchspace.dataverse.http; + +import org.junit.After; +import org.junit.Before; +import org.junit.Test; + +import com.researchspace.dataverse.api.v1.SearchConfig; + +public class SearchOperationsTest extends AbstractIntegrationTest { + + private static final String NEW_TEXT = "Do you want to publish"; + + @Before + public void setUp() throws Exception { + super.setUp(); + } + + @After + public void tearDown() throws Exception { + } + + @Test + public void testBasicSearchByText() { + SearchConfig cfg = searchOps.builder().q("documentSchema.xsd").build(); + searchOps.search(cfg); + + } + +} diff --git a/src/test/java/com/researchspace/dataverse/search/SearchConfigTest.java b/src/test/java/com/researchspace/dataverse/search/SearchConfigTest.java new file mode 100644 index 0000000..060d93e --- /dev/null +++ b/src/test/java/com/researchspace/dataverse/search/SearchConfigTest.java @@ -0,0 +1,26 @@ +package com.researchspace.dataverse.search; + +import org.junit.After; +import org.junit.Before; +import org.junit.Test; + +import com.researchspace.dataverse.api.v1.SearchConfig; + +public class SearchConfigTest { + + SearchConfig cfg; + + @Before + public void setUp() throws Exception { + } + + @After + public void tearDown() throws Exception { + } + + @Test + public void test() { + SearchConfig cfg = SearchConfig.builder().q("term").build(); + } + +} diff --git a/zoo.json b/zoo.json new file mode 100644 index 0000000..ac5f764 --- /dev/null +++ b/zoo.json @@ -0,0 +1,17 @@ +{ + "name" : "Samba Wild Park", + "city" : "Paz", + "animals" : [ { + "@class" : "com.researchspace.dataverse.search.Lion", + "name" : "Simba", + "sound" : "Roar", + "type" : "carnivorous", + "endangered" : true + }, { + "@class" : "com.researchspace.dataverse.search.Elephant", + "name" : "Manny", + "sound" : "trumpet", + "type" : "herbivorous", + "endangered" : false + } ] +} \ No newline at end of file