From de8ed3ce892ced897d023568f3b08a3c30433ef2 Mon Sep 17 00:00:00 2001 From: rostkadat Date: Tue, 16 Feb 2021 16:38:41 +0100 Subject: [PATCH 1/7] Add AWS Kendra as a Search Provider. --- jspwiki-kendra-searchprovider/.gitignore | 2 + jspwiki-kendra-searchprovider/Dockerfile | 88 +++ jspwiki-kendra-searchprovider/README.md | 40 ++ .../cloudformation/index-and-datasource.yaml | 123 ++++ .../docker-files/jspwiki-custom.properties | 3 + .../docs/images/JSPWiki_Search.png | Bin 0 -> 61411 bytes jspwiki-kendra-searchprovider/pom.xml | 104 ++++ .../search/kendra/KendraSearchProvider.java | 559 ++++++++++++++++++ .../wiki/search/kendra/content_types.json | 18 + .../kendra/KendraSearchProviderTest.java | 179 ++++++ .../src/test/resources/aaa-diagram.pdf | Bin 0 -> 37465 bytes .../src/test/resources/favicon.png | Bin 0 -> 631 bytes .../test/resources/jspwiki-custom.properties | 93 +++ .../src/test/resources/log4j.properties | 9 + jspwiki-war/pom.xml | 6 + pom.xml | 1 + 16 files changed, 1225 insertions(+) create mode 100644 jspwiki-kendra-searchprovider/.gitignore create mode 100644 jspwiki-kendra-searchprovider/Dockerfile create mode 100644 jspwiki-kendra-searchprovider/README.md create mode 100644 jspwiki-kendra-searchprovider/cloudformation/index-and-datasource.yaml create mode 100644 jspwiki-kendra-searchprovider/docker-files/jspwiki-custom.properties create mode 100644 jspwiki-kendra-searchprovider/docs/images/JSPWiki_Search.png create mode 100644 jspwiki-kendra-searchprovider/pom.xml create mode 100644 jspwiki-kendra-searchprovider/src/main/java/org/apache/wiki/search/kendra/KendraSearchProvider.java create mode 100644 jspwiki-kendra-searchprovider/src/main/resources/org/apache/wiki/search/kendra/content_types.json create mode 100644 jspwiki-kendra-searchprovider/src/test/java/org/apache/wiki/search/kendra/KendraSearchProviderTest.java create mode 100644 jspwiki-kendra-searchprovider/src/test/resources/aaa-diagram.pdf create mode 100644 jspwiki-kendra-searchprovider/src/test/resources/favicon.png create mode 100644 jspwiki-kendra-searchprovider/src/test/resources/jspwiki-custom.properties create mode 100644 jspwiki-kendra-searchprovider/src/test/resources/log4j.properties diff --git a/jspwiki-kendra-searchprovider/.gitignore b/jspwiki-kendra-searchprovider/.gitignore new file mode 100644 index 0000000000..51e923ee9f --- /dev/null +++ b/jspwiki-kendra-searchprovider/.gitignore @@ -0,0 +1,2 @@ +/.aws-sam/ +/samconfig.toml diff --git a/jspwiki-kendra-searchprovider/Dockerfile b/jspwiki-kendra-searchprovider/Dockerfile new file mode 100644 index 0000000000..4627e2a714 --- /dev/null +++ b/jspwiki-kendra-searchprovider/Dockerfile @@ -0,0 +1,88 @@ +# 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. + +#FROM maven:3.6-jdk-8 as package +#WORKDIR /tmp +#COPY . . +#RUN set -x \ +## fastest, minimum build +# && mvn clean package -pl jspwiki-war,jspwiki-kendra-searchprovider,jspwiki-wikipages/en -am -DskipTests + +FROM tomcat:9.0 + +#COPY --from=package /tmp/jspwiki-war/target/JSPWiki.war /tmp +#COPY --from=package /tmp/jspwiki-wikipages/en/target/jspwiki-wikipages-en-*.zip /tmp +#COPY --from=package /tmp/jspwiki-kendra-searchprovider/target/jspwiki-kendra-searchprovider-*.jar /tmp +COPY jspwiki-war/target/JSPWiki.war /tmp +COPY jspwiki-wikipages/en/target/jspwiki-wikipages-en-*.zip /tmp +#COPY jspwiki-kendra-searchprovider/target/jspwiki-kendra-searchprovider-*.jar /tmp + +COPY docker-files/log4j.properties /tmp +COPY docker-files/tomcat-users.xml $CATALINA_HOME/conf/tomcat-users.xml +COPY jspwiki-kendra-searchprovider/docker-files/jspwiki-custom.properties /tmp +# +# set default environment entries to configure jspwiki +ENV CATALINA_OPTS -Djava.security.egd=file:/dev/./urandom +ENV LANG en_US.UTF-8 +ENV jspwiki_basicAttachmentProvider_storageDir /var/jspwiki/pages +ENV jspwiki_fileSystemProvider_pageDir /var/jspwiki/pages +ENV jspwiki_jspwiki_frontPage Main +ENV jspwiki_pageProvider VersioningFileProvider +ENV jspwiki_use_external_logconfig true +ENV jspwiki_workDir /var/jspwiki/work +ENV jspwiki_xmlUserDatabaseFile /var/jspwiki/etc/userdatabase.xml +ENV jspwiki_xmlGroupDatabaseFile /var/jspwiki/etc/groupdatabase.xml + +RUN set -x \ + && export DEBIAN_FRONTEND=noninteractive \ + && apt install --fix-missing --quiet --yes unzip + +# +# install jspwiki +RUN set -x \ + && mkdir /var/jspwiki \ +# remove default tomcat applications, we dont need them to run jspwiki + && cd $CATALINA_HOME/webapps \ + && rm -rf examples host-manager manager docs ROOT \ +# remove other stuff we don't need + && rm -rf /usr/local/tomcat/bin/*.bat \ +# create subdirectories where all jspwiki stuff will live + && cd /var/jspwiki \ + && mkdir pages logs etc work \ +# deploy jspwiki + && mkdir $CATALINA_HOME/webapps/ROOT \ + && unzip -q -d $CATALINA_HOME/webapps/ROOT /tmp/JSPWiki.war \ + && rm /tmp/JSPWiki.war \ +# deploy wiki pages + && cd /tmp/ \ + && unzip -q jspwiki-wikipages-en-*.zip \ + && mv jspwiki-wikipages-en-*/* /var/jspwiki/pages/ \ + && rm -rf jspwiki-wikipages-en-* \ +# move the userdatabase.xml and groupdatabase.xml to /var/jspwiki/etc + && cd $CATALINA_HOME/webapps/ROOT/WEB-INF \ + && mv userdatabase.xml groupdatabase.xml /var/jspwiki/etc \ +# arrange proper logging (jspwiki.use.external.logconfig = true needs to be set) + && mv /tmp/log4j.properties $CATALINA_HOME/lib/log4j.properties \ +# Copy Kendra Search Provider configuration + && cp /tmp/jspwiki-custom.properties $CATALINA_HOME/webapps/ROOT/WEB-INF/classes + +# make port visible in metadata +EXPOSE 8080 + +# +# by default we start the Tomcat container when the docker container is started. +CMD ["/usr/local/tomcat/bin/catalina.sh", "run", ">/usr/local/tomcat/logs/catalina.out"] diff --git a/jspwiki-kendra-searchprovider/README.md b/jspwiki-kendra-searchprovider/README.md new file mode 100644 index 0000000000..f483cbe47b --- /dev/null +++ b/jspwiki-kendra-searchprovider/README.md @@ -0,0 +1,40 @@ +# JSPWiki Kendra Search provider + +## What is AWS Kendra + + Amazon Kendra is an intelligent search service powered by machine learning. + +## How to use Kendra with JSPWiki + +1. AWS Account + +You will need an AWS Account if you have not one already: [create-account](https://aws.amazon.com/resources/create-account/) + +2. Create the Kendra Index and DataSource + +In you can use the [index-and-datasource](cloudformation/index-and-datasource.yaml) Cloudformation stack to create the Kendra Index and DataSource. +This require that you have either the [AWS Cli](https://docs.aws.amazon.com/cli/latest/userguide/install-cliv2.html) or the [SAM Cli](https://docs.aws.amazon.com/serverless-application-model/latest/developerguide/serverless-sam-cli-install.html) installed. +Once installed you can simply run the following command to create your Kendra Index and DataSource: + +```shell +sam build --template cloudformation/index-and-datasource.yaml +sam deploy --guided +``` + +*Note*: it is important that the name for your Index and DataSource match the names setup in your JSPWiki Installation. +Namely make sure the properties `jspwiki.kendra.indexName` and `jspwiki.kendra.dataSourceName` are properly configured. + +4. Testing in Docker + +You can test your search index by running docker (*Note* you'll be using your AWS credentials) + +```shell +mvn package -pl jspwiki-war,jspwiki-wikipages/en -am -DskipTests +docker build -t jspwiki-kendra-searchprovider:latest -f jspwiki-kendra-searchprovider/Dockerfile . +docker run -p 8080:8080 -v ~/.aws:/root/.aws jspwiki-kendra-searchprovider:latest +``` + +Then you can create a Page, upload some PDF, and search for some content in the PDF document + +![JSPWiki Search Results](docs/images/JSPWiki_Search.png) + diff --git a/jspwiki-kendra-searchprovider/cloudformation/index-and-datasource.yaml b/jspwiki-kendra-searchprovider/cloudformation/index-and-datasource.yaml new file mode 100644 index 0000000000..9bc0f02a2e --- /dev/null +++ b/jspwiki-kendra-searchprovider/cloudformation/index-and-datasource.yaml @@ -0,0 +1,123 @@ +AWSTemplateFormatVersion: '2010-09-09' +Transform: AWS::Serverless-2016-10-31 +Description: > + index-and-datasource. Create the Kendra Index and Datasource. +Metadata: + + AWS::CloudFormation::Interface: + ParameterGroups: + - Label: + default: Parameters related to the Kendra Index and DataSource + Parameters: + - IndexName + - DataSourceName + - KendraEdition + ParameterLabels: + IndexName: + default: "The Kendra Index's Name" + DataSourceName: + default: "The Kendra DataSource's Name" + KendraEdition: + default: "The Kendra Edition" + +Parameters: + + IndexName: + Description: "The name of the Kendra Index to create" + Type: String + Default: "JSPWikiIndex" + + DataSourceName: + Description: "The name of the Kendra DataSource to create" + Type: String + Default: "JSPWikiDataSource" + + KendraEdition: + Description: "The name of the Kendra DataSource to create" + Type: String + AllowedValues: [ "DEVELOPER_EDITION", "ENTERPRISE_EDITION" ] + Default: "DEVELOPER_EDITION" + +Resources: + + KendraServiceRole: + Type: AWS::IAM::Role + Properties: + AssumeRolePolicyDocument: + Version: 2012-10-17 + Statement: + - Effect: Allow + Principal: + Service: + - kendra.amazonaws.com + Action: + - sts:AssumeRole + Path: "/" + Policies: + - PolicyName: AllowKendra + PolicyDocument: + Version: 2012-10-17 + Statement: + - Effect: Allow + Action: + - cloudwatch:PutMetricData + Resource: "*" + Condition: + StringEquals: + cloudwatch:namespace: AWS/Kendra + - Effect: Allow + Action: + - logs:DescribeLogGroups + Resource: "*" + - Effect: Allow + Action: + - logs:CreateLogGroup + Resource: + - Fn::Sub: arn:${AWS::Partition}:logs:${AWS::Region}:${AWS::AccountId}:log-group:/aws/kendra/* + - Effect: Allow + Action: + - logs:DescribeLogStreams + - logs:CreateLogStream + - logs:PutLogEvents + Resource: + - Fn::Sub: arn:${AWS::Partition}:logs:${AWS::Region}:${AWS::AccountId}:log-group:/aws/kendra/* + + Index: + Type: AWS::Kendra::Index + Properties: + Description: "Index for JSPWiki KendraSearchProvider" + Edition: + Ref: KendraEdition + Name: + Ref: IndexName + RoleArn: + Fn::GetAtt: KendraServiceRole.Arn + Tags: + - Key: Origin + Value: JSPWIKI + + DataSource: + Type: AWS::Kendra::DataSource + Properties: + Description: "DataSource for JSPWiki KendraSearchProvider" + IndexId: + Ref: Index + Name: + Ref: DataSourceName + Tags: + - Key: Origin + Value: JSPWIKI + Type: CUSTOM + +Outputs: + + Index: + Description: The Index + Value: + Ref: Index + + DataSource: + Description: The DataSource + Value: + Ref: DataSource + diff --git a/jspwiki-kendra-searchprovider/docker-files/jspwiki-custom.properties b/jspwiki-kendra-searchprovider/docker-files/jspwiki-custom.properties new file mode 100644 index 0000000000..b862a793a8 --- /dev/null +++ b/jspwiki-kendra-searchprovider/docker-files/jspwiki-custom.properties @@ -0,0 +1,3 @@ +jspwiki.searchProvider = org.apache.wiki.search.kendra.KendraSearchProvider +jspwiki.kendra.indexName = JSPWikiIndex +jspwiki.kendra.dataSourceName = JSPWikiDataSource diff --git a/jspwiki-kendra-searchprovider/docs/images/JSPWiki_Search.png b/jspwiki-kendra-searchprovider/docs/images/JSPWiki_Search.png new file mode 100644 index 0000000000000000000000000000000000000000..cfa5525378b4a6a181138c6f82e4b5abd6405ccc GIT binary patch literal 61411 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 literal 0 HcmV?d00001 diff --git a/jspwiki-kendra-searchprovider/pom.xml b/jspwiki-kendra-searchprovider/pom.xml new file mode 100644 index 0000000000..f6d295a753 --- /dev/null +++ b/jspwiki-kendra-searchprovider/pom.xml @@ -0,0 +1,104 @@ + + + + + + org.apache.jspwiki + jspwiki-builder + 2.11.0-SNAPSHOT + + + jspwiki-kendra-searchprovider + 4.0.0 + Apache JSPWiki AWS Kendra Search provider + Apache JSPWiki Kendra Search provider + + + + ${project.groupId} + jspwiki-main + ${project.version} + provided + + + + ${project.groupId} + jspwiki-main + test-jar + ${project.version} + test + + + + org.junit.jupiter + junit-jupiter-api + test + + + + org.junit.jupiter + junit-jupiter-params + test + + + + org.junit.jupiter + junit-jupiter-engine + test + + + + org.mockito + mockito-core + test + + + + org.mockito + mockito-junit-jupiter + test + + + + org.awaitility + awaitility + test + + + + javax.servlet + javax.servlet-api + test + + + + net.sourceforge.stripes + stripes + test + + + + com.amazonaws + aws-java-sdk-kendra + 1.11.954 + + + + \ No newline at end of file diff --git a/jspwiki-kendra-searchprovider/src/main/java/org/apache/wiki/search/kendra/KendraSearchProvider.java b/jspwiki-kendra-searchprovider/src/main/java/org/apache/wiki/search/kendra/KendraSearchProvider.java new file mode 100644 index 0000000000..e032e32d3c --- /dev/null +++ b/jspwiki-kendra-searchprovider/src/main/java/org/apache/wiki/search/kendra/KendraSearchProvider.java @@ -0,0 +1,559 @@ +/* + 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.wiki.search.kendra; + +import static java.lang.String.format; + +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.lang.reflect.Type; +import java.nio.ByteBuffer; +import java.nio.charset.StandardCharsets; +import java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Properties; + +import org.apache.commons.io.FilenameUtils; +import org.apache.commons.lang3.StringUtils; +import org.apache.log4j.Logger; +import org.apache.wiki.InternalWikiException; +import org.apache.wiki.WatchDog; +import org.apache.wiki.WikiBackgroundThread; +import org.apache.wiki.api.core.Attachment; +import org.apache.wiki.api.core.Context; +import org.apache.wiki.api.core.Engine; +import org.apache.wiki.api.core.Page; +import org.apache.wiki.api.exceptions.NoRequiredPropertyException; +import org.apache.wiki.api.exceptions.ProviderException; +import org.apache.wiki.api.providers.PageProvider; +import org.apache.wiki.api.search.SearchResult; +import org.apache.wiki.api.spi.Wiki; +import org.apache.wiki.attachment.AttachmentManager; +import org.apache.wiki.auth.AuthorizationManager; +import org.apache.wiki.auth.permissions.PagePermission; +import org.apache.wiki.pages.PageManager; +import org.apache.wiki.search.SearchProvider; +import org.apache.wiki.util.TextUtil; + +import com.amazonaws.services.kendra.AWSkendra; +import com.amazonaws.services.kendra.AWSkendraClientBuilder; +import com.amazonaws.services.kendra.model.BatchDeleteDocumentRequest; +import com.amazonaws.services.kendra.model.BatchDeleteDocumentResult; +import com.amazonaws.services.kendra.model.BatchPutDocumentRequest; +import com.amazonaws.services.kendra.model.BatchPutDocumentResponseFailedDocument; +import com.amazonaws.services.kendra.model.BatchPutDocumentResult; +import com.amazonaws.services.kendra.model.ContentType; +import com.amazonaws.services.kendra.model.DataSourceSummary; +import com.amazonaws.services.kendra.model.Document; +import com.amazonaws.services.kendra.model.DocumentAttribute; +import com.amazonaws.services.kendra.model.DocumentAttributeValue; +import com.amazonaws.services.kendra.model.IndexConfigurationSummary; +import com.amazonaws.services.kendra.model.ListDataSourcesRequest; +import com.amazonaws.services.kendra.model.ListDataSourcesResult; +import com.amazonaws.services.kendra.model.ListIndicesRequest; +import com.amazonaws.services.kendra.model.ListIndicesResult; +import com.amazonaws.services.kendra.model.QueryRequest; +import com.amazonaws.services.kendra.model.QueryResultItem; +import com.amazonaws.services.kendra.model.QueryResultType; +import com.amazonaws.services.kendra.model.ScoreConfidence; +import com.amazonaws.services.kendra.model.StartDataSourceSyncJobRequest; +import com.amazonaws.services.kendra.model.StartDataSourceSyncJobResult; +import com.amazonaws.services.kendra.model.StopDataSourceSyncJobRequest; +import com.amazonaws.services.kendra.model.ThrottlingException; +import com.amazonaws.util.IOUtils; +import com.google.gson.Gson; +import com.google.gson.GsonBuilder; +import com.google.gson.reflect.TypeToken; + +/** + * Search provider that implements {link SearchProvider} using AWS Kendra for + * indexing. Note that we are using a Custom DataSource which limits the + * attributes that can be uploaded / searched for each page (as per + * https://docs.aws.amazon.com/kendra/latest/dg/custom-attributes.html). This + * could be overcome by using an S3 bucket where any custom attributes can be + * added. + * + * @since 2.11.0 + */ +public class KendraSearchProvider implements SearchProvider { + + private static final Logger LOG = Logger.getLogger(KendraSearchProvider.class); + private Engine engine; + private Properties properties; + private Map contentTypes; + private AWSkendra kendra; + private String indexName; + private String indexId; + private String dataSourceName; + private String dataSourceId; + + private List updates = Collections.synchronizedList(new ArrayList<>()); + + private static final String PROP_KENDRA_INDEX_NAME = "jspwiki.kendra.indexName"; + private static final String PROP_KENDRA_DATA_SOURCE_NAME = "jspwiki.kendra.dataSourceName"; + private static final String PROP_KENDRA_INDEXDELAY = "jspwiki.kendra.indexdelay"; + private static final String PROP_KENDRA_INITIALDELAY = "jspwiki.kendra.initialdelay"; + + public KendraSearchProvider() { + } + + /** + * {@inheritDoc} + */ + @Override + public void initialize(Engine engine, Properties properties) throws NoRequiredPropertyException, IOException { + this.engine = engine; + this.properties = properties; + this.contentTypes = getContentTypes(); + + setKendra(buildClient()); + + this.indexName = TextUtil.getRequiredProperty(this.properties, PROP_KENDRA_INDEX_NAME); + this.dataSourceName = TextUtil.getRequiredProperty(this.properties, PROP_KENDRA_DATA_SOURCE_NAME); + int initialDelay = TextUtil.getIntegerProperty(this.properties, PROP_KENDRA_INITIALDELAY, + KendraUpdater.INITIAL_DELAY); + int indexDelay = TextUtil.getIntegerProperty(this.properties, PROP_KENDRA_INDEXDELAY, KendraUpdater.INDEX_DELAY); + + // Start the Kendra update thread, which waits first for a little while + // before starting to go through the "pages that need updating". + KendraUpdater updater = new KendraUpdater(engine, this, initialDelay, indexDelay); + updater.start(); + } + + private Map getContentTypes() { + Gson gson = new GsonBuilder().create(); + try (InputStream in = KendraSearchProvider.class.getResourceAsStream("content_types.json")) { + if (in != null) { + Type collectionType = new TypeToken>(){}.getType(); + return gson.fromJson(new InputStreamReader(in), collectionType); + } + } catch (IOException e) { + LOG.error(format("Unable to load default propertyfile 'content_types.json': %s", e.getMessage()), e); + } + return null; + } + + /** + * {@inheritDoc} + */ + @Override + public String getProviderInfo() { + return "KendraSearchProvider"; + } + + /** + * {@inheritDoc} + */ + @Override + public void pageRemoved(Page page) { + String pageName = page.getName(); + BatchDeleteDocumentRequest request = new BatchDeleteDocumentRequest().withIndexId(indexId) + .withDocumentIdList(pageName); + BatchDeleteDocumentResult result = getKendra().batchDeleteDocument(request); + if (result.getFailedDocuments().size() == 0) { + LOG.debug(format("Page '%s' was removed from index", pageName)); + } else { + LOG.error(format("Failed to remove Page '%s' from index", pageName)); + } + } + + /** + * {@inheritDoc} + */ + @Override + public void reindexPage(Page page) { + if (page != null) { + updates.add(page); + LOG.debug(format("Scheduling page '%s' for indexing ...", page.getName())); + } + } + + /** + * {@inheritDoc} + */ + @Override + public Collection findPages(String query, Context wikiContext) throws ProviderException, IOException { + QueryRequest request = new QueryRequest().withIndexId(indexId).withQueryText(query); + List items = null; + try { + items = getKendra().query(request).getResultItems(); + } catch (ThrottlingException e) { + LOG.error(format("ThrottlingException. Skipping...")); + return new ArrayList<>(); + } + List searchResults = new ArrayList<>(items.size()); + AuthorizationManager mgr = engine.getManager(AuthorizationManager.class); + + for (QueryResultItem item : items) { + switch (QueryResultType.fromValue(item.getType())) { + case DOCUMENT: + String documentId = item.getDocumentId(); + String documentExcerpt = item.getDocumentExcerpt().getText(); + String scoreConfidence = item.getScoreAttributes().getScoreConfidence(); + Page page = this.engine.getManager(PageManager.class).getPage(documentId, PageProvider.LATEST_VERSION); + if (page != null) { + PagePermission pp = new PagePermission(page, PagePermission.VIEW_ACTION); + if (mgr.checkPermission(wikiContext.getWikiSession(), pp)) { + SearchResult searchResult = new SearchResultImpl(page, confidence2score(scoreConfidence), + new String[] { documentExcerpt }); + searchResults.add(searchResult); + } else { + LOG.error(format("Page '%s' is not accessible", documentId)); + } + } else { + LOG.error( + format("Kendra found a result page '%s' that could not be loaded, removing from index", documentId)); + pageRemoved(Wiki.contents().page(this.engine, documentId)); + } + break; + default: + LOG.error(format("Unknown query result type: %s", item.getType())); + } + } + return searchResults; + } + + /** + * This method initialize the AWS Kendra Index and Datasources to be used. + * + * @throws InterruptedException + */ + public void initializeIndexAndDataSource() throws InterruptedException { + this.indexId = getIndexId(indexName); + if (this.indexId == null) { + String message = format("Index '%s' does not exists", indexName); + LOG.error(message); + throw new IllegalArgumentException(message); + } + this.dataSourceId = getDatasourceId(this.indexId, dataSourceName); + if (this.dataSourceId == null) { + String message = format("Datasource '%s' does not exists in index %s", dataSourceName, indexName); + LOG.error(message); + throw new IllegalArgumentException(message); + } + } + + /** + * Given an Kendra's Index name, returns the corresponding Index Id, or + * {@code null} if it does not exists + * + * @param indexName the name of the index to look up + * @return the index id or {@code null} + */ + private String getIndexId(String indexName) { + ListIndicesRequest request = new ListIndicesRequest(); + ListIndicesResult result = getKendra().listIndices(request); + String nextToken = ""; + while (nextToken != null) { + for (IndexConfigurationSummary item : result.getIndexConfigurationSummaryItems()) { + if (StringUtils.equals(item.getName(), indexName)) { + return item.getId(); + } + } + nextToken = result.getNextToken(); + request = new ListIndicesRequest().withNextToken(result.getNextToken()); + result = getKendra().listIndices(request); + } + return null; + } + + /** + * Given an Kendra's Datasource name, returns the corresponding Datasource Id, + * or {@code null} if it does not exists + * + * @param dataSourceName the name of the datasource to look up + * @return the datasource id or {@code null} + */ + private String getDatasourceId(String indexId, String dataSourceName) { + ListDataSourcesRequest request = new ListDataSourcesRequest().withIndexId(indexId); + ListDataSourcesResult result = getKendra().listDataSources(request); + String nextToken = ""; + while (nextToken != null) { + for (DataSourceSummary item : result.getSummaryItems()) { + if (StringUtils.equals(item.getName(), dataSourceName)) { + return item.getId(); + } + } + nextToken = result.getNextToken(); + request = new ListDataSourcesRequest().withNextToken(result.getNextToken()); + result = getKendra().listDataSources(request); + } + return null; + } + + /* + * Converts a SCORE Confidence from Kendra to an "equivalent" integer score + */ + private int confidence2score(String scoreConfidence) { + switch (ScoreConfidence.fromValue(scoreConfidence)) { + case VERY_HIGH: + return 100; + case HIGH: + return 75; + case MEDIUM: + return 50; + case LOW: + return 25; + default: + return 0; + } + } + + /** + * This method re-index all the pages found in the Wiki. It is mainly used at + * startup. + * + * @throws IOException in case some page can not be read + */ + private void doFullReindex() throws IOException { + try { + Collection pages = engine.getManager(PageManager.class).getAllPages(); + if (pages.isEmpty()) { + return; + } + LOG.debug(format("Indexing all %d pages. Please wait ...", pages.size())); + String executionId = startExecution(); + for (Page page : pages) { + // Since I do not want to handle the size limit + // (https://docs.aws.amazon.com/goto/WebAPI/kendra-2019-02-03/BatchPutDocument) + // uploading documents one at a time + indexOnePage(page, executionId); + } + } catch (ProviderException e) { + LOG.error(e.getMessage()); + throw new IOException(e); + } finally { + stopExecution(); + } + } + + /** + * This method re-index all pages marked as updated. It is used to periodically + * index pages that have been modified + */ + private void doPartialReindex() { + if (updates.isEmpty()) { + return; + } + LOG.debug(format("Indexing updated pages. Please wait ...")); + String executionId = startExecution(); + synchronized (updates) { + try { + while (updates.size() > 0) { + indexOnePage(updates.remove(0), executionId); + } + } finally { + stopExecution(); + } + } + } + + /** + * Returns an ExecutiuonId that is required to keep track of the modifed + * documents + * @return The execution id + */ + private String startExecution() { + StartDataSourceSyncJobRequest request = new StartDataSourceSyncJobRequest().withIndexId(indexId) + .withId(dataSourceId); + StartDataSourceSyncJobResult result = getKendra().startDataSourceSyncJob(request); + return result.getExecutionId(); + } + + /** + * Stop the execution for the given index Id and DataSource Id. + */ + private void stopExecution() { + StopDataSourceSyncJobRequest request = new StopDataSourceSyncJobRequest().withIndexId(indexId).withId(dataSourceId); + getKendra().stopDataSourceSyncJob(request); + } + + /** + * Index on single {@link Page} into the Kendra Index + * @param page the {@link Page} to index + * @param executionId The Execution Id + */ + private void indexOnePage(Page page, String executionId) { + String pageName = page.getName(); + try { + Document document = newDocument(page, executionId); + BatchPutDocumentRequest request = new BatchPutDocumentRequest().withIndexId(indexId) + .withDocuments(document); + BatchPutDocumentResult result = getKendra().batchPutDocument(request); + if (result.getFailedDocuments().size() == 0) { + LOG.info(format("Successfully indexed Page '%s' as %s", page.getName(), document.getContentType())); + } else { + for (BatchPutDocumentResponseFailedDocument failedDocument : result.getFailedDocuments()) { + LOG.error(format("Failed to index Page '%s': %s", failedDocument.getId(), failedDocument.getErrorMessage())); + } + } + } catch (IOException e) { + LOG.error(format("Failed to index Page '%s': %s", pageName, e.getMessage())); + } + } + + + /** + * Given a {@link Page}, returns the corresponding Kendra {@link Document}. + * + * @param page the {@link Page} to be indexed + * @param executionId an execution id to identify when the {@link Page} was + * indexed for the last time. + * @return a {@link Document} containing the searchable attributes. + * @throws IOException if the {@link Page}'s {@link Attachment} can not be read. + */ + private Document newDocument(Page page, String executionId) throws IOException { + String pageName = page.getName(); + List attrs = new ArrayList<>(); + // These 2 are required as per + // https://docs.aws.amazon.com/kendra/latest/dg/data-source-custom.html#custom-required-attributes + attrs.add(newAttribute("_data_source_id", dataSourceId)); + attrs.add(newAttribute("_data_source_sync_job_execution_id", executionId)); + + String title = TextUtil.beautifyString(pageName); + ByteBuffer blob = null; + ContentType contentType = ContentType.PLAIN_TEXT; + if (page instanceof Attachment) { + Attachment attachment = (Attachment) page; + InputStream is = null; + try { + String filename = attachment.getFileName(); + contentType = getContentType(filename); + is = engine.getManager(AttachmentManager.class).getAttachmentStream(attachment); + blob = ByteBuffer.wrap(IOUtils.toByteArray(is)); + } catch (ProviderException e) { + throw new IOException(e); + } finally { + IOUtils.closeQuietly(is, null); + } + // contentType should be set to its real value + } else { + String text = engine.getManager(PageManager.class).getPureText(page); + blob = ByteBuffer.wrap(text.getBytes(StandardCharsets.UTF_8)); + } + return new Document().withId(pageName).withTitle(title).withAttributes(attrs).withBlob(blob) + .withContentType(contentType); + } + + private DocumentAttribute newAttribute(String key, String value) { + return new DocumentAttribute().withKey(key).withValue(new DocumentAttributeValue().withStringValue(value)); + } + + @SuppressWarnings("unchecked") + private ContentType getContentType(String filename) { + String extention = FilenameUtils.getExtension(filename); + Map ct = (Map) this.contentTypes.get("ContentTypes"); + return ContentType.fromValue(ct.getOrDefault(extention, ContentType.PLAIN_TEXT.name())); + } + + /** + * Updater thread that updates Kendra indexes. + */ + private static final class KendraUpdater extends WikiBackgroundThread { + protected static final int INDEX_DELAY = 5; + protected static final int INITIAL_DELAY = 10; + private KendraSearchProvider provider; + + private int initialDelay; + + private WatchDog watchdog; + + private KendraUpdater(Engine engine, KendraSearchProvider provider, int initialDelay, int indexDelay) { + super(engine, indexDelay); + this.provider = provider; + this.initialDelay = initialDelay; + setName("JSPWiki Kendra Indexer"); + } + + @Override + public void startupTask() throws Exception { + watchdog = WatchDog.getCurrentWatchDog(getEngine()); + try { + Thread.sleep(initialDelay * 1000L); + } catch (InterruptedException e) { + throw new InternalWikiException("Interrupted while waiting to start.", e); + } + watchdog.enterState("Full reindex"); + provider.initializeIndexAndDataSource(); + provider.doFullReindex(); + watchdog.exitState(); + } + + @Override + public void backgroundTask() { + watchdog.enterState("Reindexing ...", 60); + provider.doPartialReindex(); + watchdog.exitState(); + } + } + + private static class SearchResultImpl implements SearchResult { + + private Page page; + private int score; + private String[] contexts; + + public SearchResultImpl(Page page, int score, String[] contexts) { + this.page = page; + this.score = score; + this.contexts = contexts != null ? contexts.clone() : null; + } + + @Override + public Page getPage() { + return this.page; + } + + @Override + public int getScore() { + return this.score; + } + + @Override + public String[] getContexts() { + return this.contexts; + } + } + + public AWSkendra getKendra() { + return kendra; + } + + public void setKendra(AWSkendra kendra) { + this.kendra = kendra; + } + + protected AWSkendra buildClient() { + return AWSkendraClientBuilder.defaultClient(); + } + + public String getIndexName() { + return indexName; + } + + public String getDataSourceName() { + return dataSourceName; + } + +} \ No newline at end of file diff --git a/jspwiki-kendra-searchprovider/src/main/resources/org/apache/wiki/search/kendra/content_types.json b/jspwiki-kendra-searchprovider/src/main/resources/org/apache/wiki/search/kendra/content_types.json new file mode 100644 index 0000000000..87b9d07638 --- /dev/null +++ b/jspwiki-kendra-searchprovider/src/main/resources/org/apache/wiki/search/kendra/content_types.json @@ -0,0 +1,18 @@ +{ + "ContentTypes": { + "pdf": "PDF", + "html": "HTML", + "htm": "HTML", + "xhtml": "HTML", + "doc": "MS_WORD", + "docx": "MS_WORD", + "txt": "PLAIN_TEXT", + "xml": "PLAIN_TEXT", + "properties": "PLAIN_TEXT", + "java": "PLAIN_TEXT", + "js": "PLAIN_TEXT", + "py": "PLAIN_TEXT", + "md": "PLAIN_TEXT", + "ppt": "PPT" + } +} \ No newline at end of file diff --git a/jspwiki-kendra-searchprovider/src/test/java/org/apache/wiki/search/kendra/KendraSearchProviderTest.java b/jspwiki-kendra-searchprovider/src/test/java/org/apache/wiki/search/kendra/KendraSearchProviderTest.java new file mode 100644 index 0000000000..bc0e6fc87e --- /dev/null +++ b/jspwiki-kendra-searchprovider/src/test/java/org/apache/wiki/search/kendra/KendraSearchProviderTest.java @@ -0,0 +1,179 @@ +/* + * 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.wiki.search.kendra; + +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.Mockito.when; +import static org.mockito.Mockito.lenient; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.Properties; +import java.util.concurrent.Callable; + +import org.apache.log4j.Logger; +import org.apache.wiki.TestEngine; +import org.apache.wiki.api.core.Context; +import org.apache.wiki.api.core.ContextEnum; +import org.apache.wiki.api.core.Engine; +import org.apache.wiki.api.search.SearchResult; +import org.apache.wiki.api.spi.Wiki; +import org.apache.wiki.search.SearchManager; +import org.awaitility.Awaitility; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.Mock; +import org.mockito.invocation.InvocationOnMock; +import org.mockito.junit.jupiter.MockitoExtension; +import org.mockito.stubbing.Answer; + +import com.amazonaws.services.kendra.AWSkendra; +import com.amazonaws.services.kendra.model.BatchPutDocumentRequest; +import com.amazonaws.services.kendra.model.BatchPutDocumentResult; +import com.amazonaws.services.kendra.model.DataSourceSummary; +import com.amazonaws.services.kendra.model.IndexConfigurationSummary; +import com.amazonaws.services.kendra.model.ListDataSourcesRequest; +import com.amazonaws.services.kendra.model.ListDataSourcesResult; +import com.amazonaws.services.kendra.model.ListIndicesRequest; +import com.amazonaws.services.kendra.model.ListIndicesResult; +import com.amazonaws.services.kendra.model.QueryRequest; +import com.amazonaws.services.kendra.model.QueryResult; +import com.amazonaws.services.kendra.model.QueryResultItem; +import com.amazonaws.services.kendra.model.QueryResultType; +import com.amazonaws.services.kendra.model.ScoreAttributes; +import com.amazonaws.services.kendra.model.ScoreConfidence; +import com.amazonaws.services.kendra.model.StartDataSourceSyncJobRequest; +import com.amazonaws.services.kendra.model.StartDataSourceSyncJobResult; +import com.amazonaws.services.kendra.model.TextWithHighlights; + +import net.sf.ehcache.CacheManager; +import net.sourceforge.stripes.mock.MockHttpServletRequest; + +@ExtendWith(MockitoExtension.class) +public class KendraSearchProviderTest { + + private static final Logger LOG = Logger.getLogger(KendraSearchProviderTest.class); + + TestEngine engine; + Properties props; + KendraSearchProvider ksp; + + @Mock + AWSkendra kendraMock; + + @BeforeEach + void setUp() throws Exception { + props = TestEngine.getTestProperties(); + TestEngine.emptyWorkDir(props); + CacheManager.getInstance().removeAllCaches(); + engine = new TestEngine(props); + try { + setupAWSKendra(engine); + } catch (Exception e) { + LOG.error(e.toString()); + } + } + + private void setupAWSKendra(Engine engine) throws InterruptedException { + ksp = (KendraSearchProvider) engine.getManager(SearchManager.class).getSearchEngine(); + when(kendraMock.listIndices(any(ListIndicesRequest.class))).then(new Answer() { + @Override + public ListIndicesResult answer(InvocationOnMock invocation) throws Throwable { + return new ListIndicesResult().withIndexConfigurationSummaryItems( + new IndexConfigurationSummary().withId("IndexId").withName(ksp.getIndexName())); + } + }); + lenient().when(kendraMock.listDataSources(any(ListDataSourcesRequest.class))).then(new Answer() { + @Override + public ListDataSourcesResult answer(InvocationOnMock invocation) throws Throwable { + return new ListDataSourcesResult() + .withSummaryItems(new DataSourceSummary().withId("DataSourceId").withName(ksp.getDataSourceName())); + } + }); + lenient().when(kendraMock.startDataSourceSyncJob(any(StartDataSourceSyncJobRequest.class))).then(new Answer() { + @Override + public StartDataSourceSyncJobResult answer(InvocationOnMock invocation) throws Throwable { + return new StartDataSourceSyncJobResult().withExecutionId("executionId"); + } + }); + lenient().when(kendraMock.batchPutDocument(any(BatchPutDocumentRequest.class))).then(new Answer() { + @Override + public BatchPutDocumentResult answer(InvocationOnMock invocation) throws Throwable { + return new BatchPutDocumentResult().withFailedDocuments(new ArrayList<>()); + } + }); + lenient().when(kendraMock.query(any(QueryRequest.class))).then(new Answer() { + @Override + public QueryResult answer(InvocationOnMock invocation) throws Throwable { + return new QueryResult().withResultItems(new ArrayList<>()); + } + }); + + ksp.setKendra(kendraMock); + ksp.initializeIndexAndDataSource(); + } + + void debugSearchResults(final Collection res) { + res.forEach(next -> { + System.out.println("page: " + next.getPage()); + for (final String s : next.getContexts()) { + System.out.println("snippet: " + s); + } + }); + } + + Callable findsResultsFor(final Collection res, final String text) { + return () -> { + final MockHttpServletRequest request = engine.newHttpRequest(); + final Context ctx = Wiki.context().create(engine, request, ContextEnum.PAGE_EDIT.getRequestContext()); + final Collection searchResults = ksp.findPages(text, ctx); + if (searchResults != null && searchResults.size() > 0) { + debugSearchResults(searchResults); + res.addAll(searchResults); + return true; + } + return false; + }; + } + + @Test + public void testSimpleSearch() throws Exception { + final String txt = "It was the dawn of the third age of mankind, ten years after the Earth-Minbari War."; + engine.saveText("TestPage", txt); + addTestresult("TestPage", "mankind", ScoreConfidence.VERY_HIGH); + final Collection res = new ArrayList<>(); + Awaitility.await("testSimpleSearch").until(findsResultsFor(res, "mankind")); + Assertions.assertEquals(1, res.size(), "no pages"); + Assertions.assertEquals("TestPage", res.iterator().next().getPage().getName(), "page"); + engine.deleteTestPage("TestPage"); + } + + private void addTestresult(String pageName, String pageContent, ScoreConfidence scoreConfidence ) { + when(kendraMock.query(any(QueryRequest.class))).then(new Answer() { + @Override + public QueryResult answer(InvocationOnMock invocation) throws Throwable { + QueryResultItem item = new QueryResultItem().withId(pageName).withType(QueryResultType.DOCUMENT); + item.withDocumentTitle(new TextWithHighlights().withText(pageName)); + item.withDocumentExcerpt(new TextWithHighlights().withText(pageContent)); + item.withScoreAttributes(new ScoreAttributes().withScoreConfidence(scoreConfidence)); + return new QueryResult().withResultItems(item); + } + }); + } +} \ No newline at end of file diff --git a/jspwiki-kendra-searchprovider/src/test/resources/aaa-diagram.pdf b/jspwiki-kendra-searchprovider/src/test/resources/aaa-diagram.pdf new file mode 100644 index 0000000000000000000000000000000000000000..9bb37c7691ff3e76c228292d836f97145fd581c8 GIT binary patch literal 37465 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 literal 0 HcmV?d00001 diff --git a/jspwiki-kendra-searchprovider/src/test/resources/favicon.png b/jspwiki-kendra-searchprovider/src/test/resources/favicon.png new file mode 100644 index 0000000000000000000000000000000000000000..bb6f654ce134932faedd4678c795e189b5d8d8ea GIT binary patch literal 631 zcmV--0*L*IP)9(D#FJ z(=^XBE=~|B9gkai-}j&FOYLp$O29e0)DOekN@-gw4aNyX1OTCBnO6_&AGlXlZl5>I;j~B*LsfSC&rEmD}@C*4fIT;K+ R0gnIx002ovPDHLkV1iez2H*ey literal 0 HcmV?d00001 diff --git a/jspwiki-kendra-searchprovider/src/test/resources/jspwiki-custom.properties b/jspwiki-kendra-searchprovider/src/test/resources/jspwiki-custom.properties new file mode 100644 index 0000000000..d4838fe12a --- /dev/null +++ b/jspwiki-kendra-searchprovider/src/test/resources/jspwiki-custom.properties @@ -0,0 +1,93 @@ +# 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. +# +# Custom configuration file used by most JUnit tests overriding +# certain default values in src/main/resources/ini/jspwiki.properties +# +jspwiki.fileSystemProvider.pageDir = target/test-classes/testrepository +jspwiki.workDir = target/test-classes/testworkdir +jspwiki.searchProvider = org.apache.wiki.search.kendra.KendraSearchProvider +jspwiki.kendra.indexName = JSPWikiPageIndex +jspwiki.kendra.indexRoleArn = arn:aws:iam::123456789012:role/IndexRoleArn +jspwiki.kendra.dataSourceName = JSPWikiPageDataSource +jspwiki.kendra.dataSourceRoleArn = arn:aws:iam::123456789012:role/DataSourceRoleArn +jspwiki.kendra.initialdelay = 1 +jspwiki.kendra.indexdelay = 1 + +jspwiki.translatorReader.camelCaseLinks = true +jspwiki.breakTitleWithSpaces = true +jspwiki.translatorReader.useOutlinkImage = false +jspwiki.basicAttachmentProvider.storageDir = target/test-classes/testrepository +jspwiki.encoding = ISO-8859-1 +jspwiki.filterConfig = /filters.xml +jspwiki.referenceStyle = relative +jspwiki.authorizer=org.apache.wiki.auth.TestAuthorizer + +# log file under ./target +log4j.appender.FileLog.File=./target/logs/jspwiki.log + +# RSS under ./target +jspwiki.rss.fileName=./target/rss.rdf + +# +# Security: use standard providers for user/group auth, user management +# and ACLs. Use a test userdatabase for storing users. +# +jspwiki.xmlGroupDatabaseFile = target/test-classes/groupdatabase.xml +jspwiki.xmlUserDatabaseFile = target/test-classes/userdatabase.xml + +log4j.logger.org.apache.wiki.search=DEBUG,ConsoleAppender + +log4j.logger.SecurityLog=INFO, SecurityAppender +log4j.appender.SecurityAppender = org.apache.log4j.RollingFileAppender +log4j.appender.SecurityAppender.File = ./target/logs/security.log +log4j.appender.SecurityAppender.layout = org.apache.log4j.PatternLayout +log4j.appender.SecurityAppender.layout.ConversionPattern=%d %p - %m%n + +# Used by CommandResolverTest +jspwiki.specialPage.RecentChanges = RecentChanges.jsp +jspwiki.specialPage.Search = Search.jsp + +# Used by JSPWikiMarkupParserTest +jspwiki.translatorReader.inlinePattern.1 = *.jpg +jspwiki.translatorReader.inlinePattern.2 = *.png +jspwiki.translatorReader.inlinePattern.3 = http://images.com/* + +# Used by WorkflowManagerTest +jspwiki.approver.workflow.saveWikiPage= +jspwiki.approver.workflow.foo=janne +jspwiki.approver.workflow.bar=Admin + +# Fields needed in order to run MailUtilTest +#mail.smtp.host = 127.0.0.1 +#mail.smtp.port = 25 +#mail.from = JSPWiki +#mail.smtp.account = +#mail.smtp.password = + +# for JDBC tests +server.port=9321 +server.database.0=file:target/jspwiki.hsqldb +server.dbname.0=jspwiki + +jdbc.admin.id=SA +jdbc.admin.password= +jdbc.driver.class=org.hsqldb.jdbc.JDBCDriver +jdbc.driver.id=hsql +jdbc.driver.url=jdbc\:hsqldb\:hsql\://localhost:9321/jspwiki +jdbc.user.id=jspwiki +jdbc.user.password=password diff --git a/jspwiki-kendra-searchprovider/src/test/resources/log4j.properties b/jspwiki-kendra-searchprovider/src/test/resources/log4j.properties new file mode 100644 index 0000000000..5e79818097 --- /dev/null +++ b/jspwiki-kendra-searchprovider/src/test/resources/log4j.properties @@ -0,0 +1,9 @@ +# Root logger option +log4j.rootLogger=INFO, stdout + +# Direct log messages to stdout +log4j.appender.stdout=org.apache.log4j.ConsoleAppender +log4j.appender.stdout.Target=System.out +log4j.appender.stdout.layout=org.apache.log4j.PatternLayout +log4j.appender.stdout.layout.ConversionPattern=%d{yyyy-MM-dd HH:mm:ss} %-5p %c{1}:%L - %m%n + \ No newline at end of file diff --git a/jspwiki-war/pom.xml b/jspwiki-war/pom.xml index 3a4d4aab2e..a823e6b611 100644 --- a/jspwiki-war/pom.xml +++ b/jspwiki-war/pom.xml @@ -54,6 +54,12 @@ jspwiki-210-adapters ${project.version} + + + ${project.groupId} + jspwiki-kendra-searchprovider + ${project.version} + javax.servlet diff --git a/pom.xml b/pom.xml index 27277e55ff..cbe132673d 100644 --- a/pom.xml +++ b/pom.xml @@ -129,6 +129,7 @@ jspwiki-main jspwiki-markdown jspwiki-tika-searchprovider + jspwiki-kendra-searchprovider jspwiki-210-test-adaptees jspwiki-210-adapters jspwiki-war From 4448982b412cb92667cd94e65707e941d02af349 Mon Sep 17 00:00:00 2001 From: AF090536 Date: Mon, 22 Feb 2021 12:21:11 +0100 Subject: [PATCH 2/7] Implemented par of the suggestion of JSPWIKI-1146 --- LICENSE | 1 + .../cloudformation/index-and-datasource.yaml | 16 ++++++++++++++++ jspwiki-kendra-searchprovider/pom.xml | 1 - pom.xml | 7 +++++++ 4 files changed, 24 insertions(+), 1 deletion(-) diff --git a/LICENSE b/LICENSE index 30f2474a4b..4414c0e766 100644 --- a/LICENSE +++ b/LICENSE @@ -249,6 +249,7 @@ tika-parsers-1.25.jar LICENSE xml-apis-1.4.01 LICENSE xmlrpc-2.0.1.jar LICENSE xpp3-1.1.3.4-RC3 ./jspwiki-war/src/main/config/doc/LICENSE.cc-cod +aws-java-sdk-kendra-1.11.954.jar LICENSE TEST LIBRARY LICENSE FILE ============================================================================================================= diff --git a/jspwiki-kendra-searchprovider/cloudformation/index-and-datasource.yaml b/jspwiki-kendra-searchprovider/cloudformation/index-and-datasource.yaml index 9bc0f02a2e..7715cadc2e 100644 --- a/jspwiki-kendra-searchprovider/cloudformation/index-and-datasource.yaml +++ b/jspwiki-kendra-searchprovider/cloudformation/index-and-datasource.yaml @@ -1,3 +1,19 @@ +# 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. AWSTemplateFormatVersion: '2010-09-09' Transform: AWS::Serverless-2016-10-31 Description: > diff --git a/jspwiki-kendra-searchprovider/pom.xml b/jspwiki-kendra-searchprovider/pom.xml index f6d295a753..7fbfa6cfaf 100644 --- a/jspwiki-kendra-searchprovider/pom.xml +++ b/jspwiki-kendra-searchprovider/pom.xml @@ -97,7 +97,6 @@ com.amazonaws aws-java-sdk-kendra - 1.11.954 diff --git a/pom.xml b/pom.xml index cbe132673d..6fef8544d0 100644 --- a/pom.xml +++ b/pom.xml @@ -46,6 +46,7 @@ 1.02 4.0.3 + 1.11.954 4.4 1.4 3.1 @@ -140,6 +141,12 @@ + + com.amazonaws + aws-java-sdk-kendra + ${aws-java-sdk-kendra.version} + + com.codeborne selenide From da55616bfb953aab6acf29ac91352c73a64f7acb Mon Sep 17 00:00:00 2001 From: AF090536 Date: Mon, 22 Feb 2021 16:40:39 +0100 Subject: [PATCH 3/7] Added some more unit testing --- .../search/kendra/KendraSearchProvider.java | 19 +- .../kendra/KendraSearchProviderTest.java | 248 +++++++++++++----- .../apache/wiki/search/kendra/WithKendra.java | 34 +++ .../apache/wiki/search/kendra/WithPage.java | 38 +++ .../apache/wiki/search/kendra/WithPages.java | 33 +++ .../apache/wiki/search/kendra/WithResult.java | 40 +++ .../wiki/search/kendra/WithResults.java | 33 +++ .../test/resources/jspwiki-custom.properties | 10 +- 8 files changed, 382 insertions(+), 73 deletions(-) create mode 100644 jspwiki-kendra-searchprovider/src/test/java/org/apache/wiki/search/kendra/WithKendra.java create mode 100644 jspwiki-kendra-searchprovider/src/test/java/org/apache/wiki/search/kendra/WithPage.java create mode 100644 jspwiki-kendra-searchprovider/src/test/java/org/apache/wiki/search/kendra/WithPages.java create mode 100644 jspwiki-kendra-searchprovider/src/test/java/org/apache/wiki/search/kendra/WithResult.java create mode 100644 jspwiki-kendra-searchprovider/src/test/java/org/apache/wiki/search/kendra/WithResults.java diff --git a/jspwiki-kendra-searchprovider/src/main/java/org/apache/wiki/search/kendra/KendraSearchProvider.java b/jspwiki-kendra-searchprovider/src/main/java/org/apache/wiki/search/kendra/KendraSearchProvider.java index e032e32d3c..cbf87d364c 100644 --- a/jspwiki-kendra-searchprovider/src/main/java/org/apache/wiki/search/kendra/KendraSearchProvider.java +++ b/jspwiki-kendra-searchprovider/src/main/java/org/apache/wiki/search/kendra/KendraSearchProvider.java @@ -137,8 +137,10 @@ public void initialize(Engine engine, Properties properties) throws NoRequiredPr // Start the Kendra update thread, which waits first for a little while // before starting to go through the "pages that need updating". - KendraUpdater updater = new KendraUpdater(engine, this, initialDelay, indexDelay); - updater.start(); + if (initialDelay >= 0) { + KendraUpdater updater = new KendraUpdater(engine, this, initialDelay, indexDelay); + updater.start(); + } } private Map getContentTypes() { @@ -266,7 +268,11 @@ private String getIndexId(String indexName) { ListIndicesResult result = getKendra().listIndices(request); String nextToken = ""; while (nextToken != null) { - for (IndexConfigurationSummary item : result.getIndexConfigurationSummaryItems()) { + List items = result.getIndexConfigurationSummaryItems(); + if (items == null || items.isEmpty()) { + return null; + } + for (IndexConfigurationSummary item : items) { if (StringUtils.equals(item.getName(), indexName)) { return item.getId(); } @@ -290,7 +296,12 @@ private String getDatasourceId(String indexId, String dataSourceName) { ListDataSourcesResult result = getKendra().listDataSources(request); String nextToken = ""; while (nextToken != null) { - for (DataSourceSummary item : result.getSummaryItems()) { + List items = result.getSummaryItems(); + if (items == null || items.isEmpty()) { + return null; + } + + for (DataSourceSummary item : items) { if (StringUtils.equals(item.getName(), dataSourceName)) { return item.getId(); } diff --git a/jspwiki-kendra-searchprovider/src/test/java/org/apache/wiki/search/kendra/KendraSearchProviderTest.java b/jspwiki-kendra-searchprovider/src/test/java/org/apache/wiki/search/kendra/KendraSearchProviderTest.java index bc0e6fc87e..d4627f4dae 100644 --- a/jspwiki-kendra-searchprovider/src/test/java/org/apache/wiki/search/kendra/KendraSearchProviderTest.java +++ b/jspwiki-kendra-searchprovider/src/test/java/org/apache/wiki/search/kendra/KendraSearchProviderTest.java @@ -17,27 +17,37 @@ package org.apache.wiki.search.kendra; import static org.mockito.ArgumentMatchers.any; -import static org.mockito.Mockito.when; import static org.mockito.Mockito.lenient; +import static org.mockito.Mockito.when; +import java.io.IOException; +import java.lang.reflect.Method; +import java.net.URISyntaxException; +import java.nio.file.Files; +import java.nio.file.Paths; import java.util.ArrayList; import java.util.Collection; +import java.util.Iterator; +import java.util.List; import java.util.Properties; +import java.util.UUID; import java.util.concurrent.Callable; -import org.apache.log4j.Logger; import org.apache.wiki.TestEngine; import org.apache.wiki.api.core.Context; import org.apache.wiki.api.core.ContextEnum; -import org.apache.wiki.api.core.Engine; +import org.apache.wiki.api.exceptions.WikiException; import org.apache.wiki.api.search.SearchResult; import org.apache.wiki.api.spi.Wiki; import org.apache.wiki.search.SearchManager; import org.awaitility.Awaitility; +import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.TestInfo; import org.junit.jupiter.api.extension.ExtendWith; +import org.junit.platform.commons.util.StringUtils; import org.mockito.Mock; import org.mockito.invocation.InvocationOnMock; import org.mockito.junit.jupiter.MockitoExtension; @@ -68,65 +78,66 @@ @ExtendWith(MockitoExtension.class) public class KendraSearchProviderTest { - private static final Logger LOG = Logger.getLogger(KendraSearchProviderTest.class); - TestEngine engine; Properties props; - KendraSearchProvider ksp; + KendraSearchProvider searchProvider; @Mock AWSkendra kendraMock; @BeforeEach - void setUp() throws Exception { + void setUp(TestInfo testInfo) throws Exception { props = TestEngine.getTestProperties(); TestEngine.emptyWorkDir(props); CacheManager.getInstance().removeAllCaches(); engine = new TestEngine(props); - try { - setupAWSKendra(engine); - } catch (Exception e) { - LOG.error(e.toString()); + + // before each test I setup the Kendra Client + searchProvider = (KendraSearchProvider) engine.getManager(SearchManager.class).getSearchEngine(); + Method m = testInfo.getTestMethod().get(); + String indexName = null; + String dataSourceName = null; + if (m.isAnnotationPresent(WithKendra.class)) { + WithKendra withKendra = m.getAnnotation(WithKendra.class); + indexName = withKendra.indexName(); + dataSourceName = withKendra.dataSourceName(); + setUpKendraMock(indexName, dataSourceName); + searchProvider.setKendra(kendraMock); + if (StringUtils.isNotBlank(indexName) && StringUtils.isNotBlank(dataSourceName)) { + searchProvider.initializeIndexAndDataSource(); + } + } else { + setUpKendraMock(null, null); + searchProvider.setKendra(kendraMock); + } + // And possibly the pages that will be present in the wiki + if (m.isAnnotationPresent(WithPages.class)) { + WithPages withPages = m.getAnnotation(WithPages.class); + addPages(withPages.value()); + } + if (m.isAnnotationPresent(WithPage.class)) { + WithPage withPage = m.getAnnotation(WithPage.class); + addPages(withPage); + } + // and the corresponding search results + if (m.isAnnotationPresent(WithResults.class)) { + WithResults withResults = m.getAnnotation(WithResults.class); + addResults(withResults.value()); + } + if(m.isAnnotationPresent(WithResult.class)) { + WithResult withResult = m.getAnnotation(WithResult.class); + addResults(withResult); } } - private void setupAWSKendra(Engine engine) throws InterruptedException { - ksp = (KendraSearchProvider) engine.getManager(SearchManager.class).getSearchEngine(); - when(kendraMock.listIndices(any(ListIndicesRequest.class))).then(new Answer() { - @Override - public ListIndicesResult answer(InvocationOnMock invocation) throws Throwable { - return new ListIndicesResult().withIndexConfigurationSummaryItems( - new IndexConfigurationSummary().withId("IndexId").withName(ksp.getIndexName())); - } - }); - lenient().when(kendraMock.listDataSources(any(ListDataSourcesRequest.class))).then(new Answer() { - @Override - public ListDataSourcesResult answer(InvocationOnMock invocation) throws Throwable { - return new ListDataSourcesResult() - .withSummaryItems(new DataSourceSummary().withId("DataSourceId").withName(ksp.getDataSourceName())); - } - }); - lenient().when(kendraMock.startDataSourceSyncJob(any(StartDataSourceSyncJobRequest.class))).then(new Answer() { - @Override - public StartDataSourceSyncJobResult answer(InvocationOnMock invocation) throws Throwable { - return new StartDataSourceSyncJobResult().withExecutionId("executionId"); - } - }); - lenient().when(kendraMock.batchPutDocument(any(BatchPutDocumentRequest.class))).then(new Answer() { - @Override - public BatchPutDocumentResult answer(InvocationOnMock invocation) throws Throwable { - return new BatchPutDocumentResult().withFailedDocuments(new ArrayList<>()); - } - }); - lenient().when(kendraMock.query(any(QueryRequest.class))).then(new Answer() { - @Override - public QueryResult answer(InvocationOnMock invocation) throws Throwable { - return new QueryResult().withResultItems(new ArrayList<>()); - } - }); - - ksp.setKendra(kendraMock); - ksp.initializeIndexAndDataSource(); + @AfterEach + void tearDown(TestInfo testInfo) throws Exception { + Method m = testInfo.getTestMethod().get(); + // And possibly the pages that will be present in the wiki + if (m.isAnnotationPresent(WithPage.class)) { + WithPage withPage = m.getAnnotation(WithPage.class); + engine.deleteTestPage(withPage.name()); + } } void debugSearchResults(final Collection res) { @@ -142,8 +153,8 @@ Callable findsResultsFor(final Collection res, final Stri return () -> { final MockHttpServletRequest request = engine.newHttpRequest(); final Context ctx = Wiki.context().create(engine, request, ContextEnum.PAGE_EDIT.getRequestContext()); - final Collection searchResults = ksp.findPages(text, ctx); - if (searchResults != null && searchResults.size() > 0) { + final Collection searchResults = searchProvider.findPages(text, ctx); + if (searchResults != null && !searchResults.isEmpty()) { debugSearchResults(searchResults); res.addAll(searchResults); return true; @@ -153,26 +164,137 @@ Callable findsResultsFor(final Collection res, final Stri } @Test + public void testInvalidIndexName() throws Exception { + // IndexName is invalid... + Assertions.assertThrows(IllegalArgumentException.class, () -> { + searchProvider.initializeIndexAndDataSource(); + }); + } + + @Test + @WithKendra(indexName = "JSPWikiIndex", dataSourceName = "") + public void testInvalidDataSourceName() throws Exception { + // IndexName is invalid... + Assertions.assertThrows(IllegalArgumentException.class, () -> { + searchProvider.initializeIndexAndDataSource(); + }); + } + + @Test + @WithKendra(indexName = "JSPWikiIndex", dataSourceName = "JSPWikiDataSource") + @WithPage(name = "TestPage", text = "It was the dawn of the third age of mankind, ten years after the Earth-Minbari War.", attachments = {}) + public void testSearchNoResult() throws Exception { + final Collection res = new ArrayList<>(); + Assertions.assertFalse(findsResultsFor(res, "this text does not exists").call()); + Assertions.assertEquals(0, res.size(), "has result. none were expected"); + } + + @Test + @WithKendra(indexName = "JSPWikiIndex", dataSourceName = "JSPWikiDataSource") + @WithPage(name = "TestPage", text = "It was the dawn of the third age of mankind, ten years after the Earth-Minbari War.", attachments = {}) + @WithResult(name = "TestPage", text = "mankind", scoreConfidence = ScoreConfidence.VERY_HIGH) public void testSimpleSearch() throws Exception { - final String txt = "It was the dawn of the third age of mankind, ten years after the Earth-Minbari War."; - engine.saveText("TestPage", txt); - addTestresult("TestPage", "mankind", ScoreConfidence.VERY_HIGH); final Collection res = new ArrayList<>(); Awaitility.await("testSimpleSearch").until(findsResultsFor(res, "mankind")); - Assertions.assertEquals(1, res.size(), "no pages"); - Assertions.assertEquals("TestPage", res.iterator().next().getPage().getName(), "page"); - engine.deleteTestPage("TestPage"); + Assertions.assertEquals(1, res.size(), "no pages. one was expectd"); + Assertions.assertEquals("TestPage", res.iterator().next().getPage().getName(), "the page TestPage was expected"); + } + + @Test + @WithKendra(indexName = "JSPWikiIndex", dataSourceName = "JSPWikiDataSource") + @WithPage(name = "TestPage", text = "It was the dawn of the third age of mankind, ten years after the Earth-Minbari War.", attachments = {}) + @WithPage(name = "TestPage2", text = "It was the dawn of the third age of mankind, ten years after the Earth-Minbari War.", attachments = {}) + @WithResult(name = "TestPage", text = "mankind", scoreConfidence = ScoreConfidence.VERY_HIGH) + @WithResult(name = "TestPage2", text = "mankind", scoreConfidence = ScoreConfidence.VERY_HIGH) + public void testSimpleSearch2() throws Exception { + final Collection res = new ArrayList<>(); + Awaitility.await("testSimpleSearch2").until(findsResultsFor(res, "mankind")); + Assertions.assertEquals(2, res.size(), "2 pages were expectd"); + Iterator i = res.iterator(); + Assertions.assertEquals("TestPage", i.next().getPage().getName(), "the page TestPage was expected"); + Assertions.assertEquals("TestPage2", i.next().getPage().getName(), "the page TestPage2 was expected"); } - - private void addTestresult(String pageName, String pageContent, ScoreConfidence scoreConfidence ) { + + private void setUpKendraMock(String indexName, String dataSourceName) throws Exception { + final String indexId = UUID.randomUUID().toString(); + final String dataSourceId = UUID.randomUUID().toString(); + when(kendraMock.listIndices(any(ListIndicesRequest.class))).then(new Answer() { + @Override + public ListIndicesResult answer(InvocationOnMock invocation) throws Throwable { + ListIndicesResult result = new ListIndicesResult(); + if (StringUtils.isNotBlank(indexName)) { + result + .withIndexConfigurationSummaryItems(new IndexConfigurationSummary().withId(indexId).withName(indexName)); + } + return result; + } + }); + lenient().when(kendraMock.listDataSources(any(ListDataSourcesRequest.class))) + .then(new Answer() { + @Override + public ListDataSourcesResult answer(InvocationOnMock invocation) throws Throwable { + ListDataSourcesResult result = new ListDataSourcesResult(); + if (StringUtils.isNotBlank(dataSourceName)) { + result.withSummaryItems(new DataSourceSummary().withId(dataSourceId).withName(dataSourceName)); + } + return result; + } + }); + lenient().when(kendraMock.startDataSourceSyncJob(any(StartDataSourceSyncJobRequest.class))) + .then(new Answer() { + @Override + public StartDataSourceSyncJobResult answer(InvocationOnMock invocation) throws Throwable { + return new StartDataSourceSyncJobResult().withExecutionId("executionId"); + } + }); + lenient().when(kendraMock.batchPutDocument(any(BatchPutDocumentRequest.class))) + .then(new Answer() { + @Override + public BatchPutDocumentResult answer(InvocationOnMock invocation) throws Throwable { + BatchPutDocumentResult result = new BatchPutDocumentResult(); + result.withFailedDocuments(new ArrayList<>()); + return result; + } + }); + lenient().when(kendraMock.query(any(QueryRequest.class))).then(new Answer() { + @Override + public QueryResult answer(InvocationOnMock invocation) throws Throwable { + QueryResult result = new QueryResult(); + result.withResultItems(new ArrayList<>()); + return result; + } + }); + } + + private void addPages(final WithPage... withPages) + throws WikiException, IOException, URISyntaxException { + for (WithPage withPage : withPages ) { + String name = withPage.name(); + String text = withPage.text(); + String[] attachements = withPage.attachments(); + engine.saveText(name, text); + ClassLoader classLoader = KendraSearchProviderTest.class.getClassLoader(); + for (String attachement : attachements) { + byte[] content = Files.readAllBytes(Paths.get(classLoader.getResource(attachement).toURI())); + engine.addAttachment(name, attachement, content); + } + } + } + + private void addResults(final WithResult... withResults) { when(kendraMock.query(any(QueryRequest.class))).then(new Answer() { @Override public QueryResult answer(InvocationOnMock invocation) throws Throwable { - QueryResultItem item = new QueryResultItem().withId(pageName).withType(QueryResultType.DOCUMENT); - item.withDocumentTitle(new TextWithHighlights().withText(pageName)); - item.withDocumentExcerpt(new TextWithHighlights().withText(pageContent)); - item.withScoreAttributes(new ScoreAttributes().withScoreConfidence(scoreConfidence)); - return new QueryResult().withResultItems(item); + List items = new ArrayList<>(); + for (WithResult withResult : withResults) { + QueryResultItem item = new QueryResultItem().withType(QueryResultType.DOCUMENT); + item.withDocumentId(withResult.name()); + item.withDocumentTitle(new TextWithHighlights().withText(withResult.name())); + item.withDocumentExcerpt(new TextWithHighlights().withText(withResult.text())); + item.withScoreAttributes(new ScoreAttributes().withScoreConfidence(withResult.scoreConfidence())); + items.add(item); + } + return new QueryResult().withResultItems(items); } }); } diff --git a/jspwiki-kendra-searchprovider/src/test/java/org/apache/wiki/search/kendra/WithKendra.java b/jspwiki-kendra-searchprovider/src/test/java/org/apache/wiki/search/kendra/WithKendra.java new file mode 100644 index 0000000000..39c297aee2 --- /dev/null +++ b/jspwiki-kendra-searchprovider/src/test/java/org/apache/wiki/search/kendra/WithKendra.java @@ -0,0 +1,34 @@ +/* + * 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.wiki.search.kendra; + +import static java.lang.annotation.ElementType.METHOD; +import static java.lang.annotation.RetentionPolicy.RUNTIME; + +import java.lang.annotation.Documented; +import java.lang.annotation.Retention; +import java.lang.annotation.Target; + +@Target({METHOD}) +@Retention(RUNTIME) +@Documented +public @interface WithKendra { + + String indexName(); // JSPWikiIndex + + String dataSourceName(); // JSPWikiDataSource +} diff --git a/jspwiki-kendra-searchprovider/src/test/java/org/apache/wiki/search/kendra/WithPage.java b/jspwiki-kendra-searchprovider/src/test/java/org/apache/wiki/search/kendra/WithPage.java new file mode 100644 index 0000000000..c8b1f8c39e --- /dev/null +++ b/jspwiki-kendra-searchprovider/src/test/java/org/apache/wiki/search/kendra/WithPage.java @@ -0,0 +1,38 @@ +/* + * 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.wiki.search.kendra; + +import static java.lang.annotation.ElementType.METHOD; +import static java.lang.annotation.RetentionPolicy.RUNTIME; + +import java.lang.annotation.Documented; +import java.lang.annotation.Repeatable; +import java.lang.annotation.Retention; +import java.lang.annotation.Target; + +@Target({METHOD}) +@Retention(RUNTIME) +@Documented +@Repeatable(WithPages.class) +public @interface WithPage { + + String name() default "Main"; + + String text() default "Sample Text"; + + String[] attachments() default {}; +} diff --git a/jspwiki-kendra-searchprovider/src/test/java/org/apache/wiki/search/kendra/WithPages.java b/jspwiki-kendra-searchprovider/src/test/java/org/apache/wiki/search/kendra/WithPages.java new file mode 100644 index 0000000000..d467c7ba42 --- /dev/null +++ b/jspwiki-kendra-searchprovider/src/test/java/org/apache/wiki/search/kendra/WithPages.java @@ -0,0 +1,33 @@ +/* + * 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.wiki.search.kendra; + +import static java.lang.annotation.ElementType.METHOD; +import static java.lang.annotation.RetentionPolicy.RUNTIME; + +import java.lang.annotation.Documented; +import java.lang.annotation.Retention; +import java.lang.annotation.Target; + +@Target({METHOD}) +@Retention(RUNTIME) +@Documented +public @interface WithPages { + + WithPage[] value(); + +} diff --git a/jspwiki-kendra-searchprovider/src/test/java/org/apache/wiki/search/kendra/WithResult.java b/jspwiki-kendra-searchprovider/src/test/java/org/apache/wiki/search/kendra/WithResult.java new file mode 100644 index 0000000000..ae9be073ac --- /dev/null +++ b/jspwiki-kendra-searchprovider/src/test/java/org/apache/wiki/search/kendra/WithResult.java @@ -0,0 +1,40 @@ +/* + * 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.wiki.search.kendra; + +import static java.lang.annotation.ElementType.METHOD; +import static java.lang.annotation.RetentionPolicy.RUNTIME; + +import java.lang.annotation.Documented; +import java.lang.annotation.Repeatable; +import java.lang.annotation.Retention; +import java.lang.annotation.Target; + +import com.amazonaws.services.kendra.model.ScoreConfidence; + +@Target({ METHOD }) +@Retention(RUNTIME) +@Documented +@Repeatable(WithResults.class) +public @interface WithResult { + + String name(); + + String text(); + + ScoreConfidence scoreConfidence(); +} diff --git a/jspwiki-kendra-searchprovider/src/test/java/org/apache/wiki/search/kendra/WithResults.java b/jspwiki-kendra-searchprovider/src/test/java/org/apache/wiki/search/kendra/WithResults.java new file mode 100644 index 0000000000..617d40f319 --- /dev/null +++ b/jspwiki-kendra-searchprovider/src/test/java/org/apache/wiki/search/kendra/WithResults.java @@ -0,0 +1,33 @@ +/* + * 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.wiki.search.kendra; + +import static java.lang.annotation.ElementType.METHOD; +import static java.lang.annotation.RetentionPolicy.RUNTIME; + +import java.lang.annotation.Documented; +import java.lang.annotation.Retention; +import java.lang.annotation.Target; + +@Target({METHOD}) +@Retention(RUNTIME) +@Documented +public @interface WithResults { + + WithResult[] value(); + +} diff --git a/jspwiki-kendra-searchprovider/src/test/resources/jspwiki-custom.properties b/jspwiki-kendra-searchprovider/src/test/resources/jspwiki-custom.properties index d4838fe12a..4735511cb4 100644 --- a/jspwiki-kendra-searchprovider/src/test/resources/jspwiki-custom.properties +++ b/jspwiki-kendra-searchprovider/src/test/resources/jspwiki-custom.properties @@ -21,12 +21,10 @@ jspwiki.fileSystemProvider.pageDir = target/test-classes/testrepository jspwiki.workDir = target/test-classes/testworkdir jspwiki.searchProvider = org.apache.wiki.search.kendra.KendraSearchProvider -jspwiki.kendra.indexName = JSPWikiPageIndex -jspwiki.kendra.indexRoleArn = arn:aws:iam::123456789012:role/IndexRoleArn -jspwiki.kendra.dataSourceName = JSPWikiPageDataSource -jspwiki.kendra.dataSourceRoleArn = arn:aws:iam::123456789012:role/DataSourceRoleArn -jspwiki.kendra.initialdelay = 1 -jspwiki.kendra.indexdelay = 1 +jspwiki.kendra.indexName = JSPWikiIndex +jspwiki.kendra.dataSourceName = JSPWikiDataSource +jspwiki.kendra.initialdelay = -1 +jspwiki.kendra.indexdelay = -1 jspwiki.translatorReader.camelCaseLinks = true jspwiki.breakTitleWithSpaces = true From 84d4f8767ab2849dfd9b94728bb3bb776a589365 Mon Sep 17 00:00:00 2001 From: Julien Masnada Date: Mon, 1 Mar 2021 09:19:53 +0100 Subject: [PATCH 4/7] Remove use of Awaitility since it is not necessary --- .../search/kendra/KendraSearchProviderTest.java | 17 ++++++++--------- 1 file changed, 8 insertions(+), 9 deletions(-) diff --git a/jspwiki-kendra-searchprovider/src/test/java/org/apache/wiki/search/kendra/KendraSearchProviderTest.java b/jspwiki-kendra-searchprovider/src/test/java/org/apache/wiki/search/kendra/KendraSearchProviderTest.java index d4627f4dae..6b3e726bae 100644 --- a/jspwiki-kendra-searchprovider/src/test/java/org/apache/wiki/search/kendra/KendraSearchProviderTest.java +++ b/jspwiki-kendra-searchprovider/src/test/java/org/apache/wiki/search/kendra/KendraSearchProviderTest.java @@ -40,7 +40,6 @@ import org.apache.wiki.api.search.SearchResult; import org.apache.wiki.api.spi.Wiki; import org.apache.wiki.search.SearchManager; -import org.awaitility.Awaitility; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.BeforeEach; @@ -194,10 +193,10 @@ public void testSearchNoResult() throws Exception { @WithPage(name = "TestPage", text = "It was the dawn of the third age of mankind, ten years after the Earth-Minbari War.", attachments = {}) @WithResult(name = "TestPage", text = "mankind", scoreConfidence = ScoreConfidence.VERY_HIGH) public void testSimpleSearch() throws Exception { - final Collection res = new ArrayList<>(); - Awaitility.await("testSimpleSearch").until(findsResultsFor(res, "mankind")); - Assertions.assertEquals(1, res.size(), "no pages. one was expectd"); - Assertions.assertEquals("TestPage", res.iterator().next().getPage().getName(), "the page TestPage was expected"); + Collection searchResults = new ArrayList<>(); + Assertions.assertTrue(findsResultsFor(searchResults, "mankind").call()); + Assertions.assertEquals(1, searchResults.size(), "no pages. one was expectd"); + Assertions.assertEquals("TestPage", searchResults.iterator().next().getPage().getName(), "the page TestPage was expected"); } @Test @@ -207,10 +206,10 @@ public void testSimpleSearch() throws Exception { @WithResult(name = "TestPage", text = "mankind", scoreConfidence = ScoreConfidence.VERY_HIGH) @WithResult(name = "TestPage2", text = "mankind", scoreConfidence = ScoreConfidence.VERY_HIGH) public void testSimpleSearch2() throws Exception { - final Collection res = new ArrayList<>(); - Awaitility.await("testSimpleSearch2").until(findsResultsFor(res, "mankind")); - Assertions.assertEquals(2, res.size(), "2 pages were expectd"); - Iterator i = res.iterator(); + Collection searchResults = new ArrayList<>(); + Assertions.assertTrue(findsResultsFor(searchResults, "mankind").call()); + Assertions.assertEquals(2, searchResults.size(), "2 pages were expectd"); + Iterator i = searchResults.iterator(); Assertions.assertEquals("TestPage", i.next().getPage().getName(), "the page TestPage was expected"); Assertions.assertEquals("TestPage2", i.next().getPage().getName(), "the page TestPage2 was expected"); } From 9815cffc974264e4102583c6fb7a6e00101c7911 Mon Sep 17 00:00:00 2001 From: Julien Masnada Date: Mon, 1 Mar 2021 09:20:10 +0100 Subject: [PATCH 5/7] Added exclusion for json properties --- pom.xml | 1 + 1 file changed, 1 insertion(+) diff --git a/pom.xml b/pom.xml index 6fef8544d0..b789f9971e 100644 --- a/pom.xml +++ b/pom.xml @@ -713,6 +713,7 @@ **/src/test/resources/test-repo/*.txt **/src/main/webapp/templates/default/images/SilkIconSet-readme.txt **/PkgInfo + jspwiki-kendra-searchprovider/src/main/resources/org/apache/wiki/search/kendra/content_types.json From 9cbda220ce1538d3f3d87468b9ee2af4509352bb Mon Sep 17 00:00:00 2001 From: Julien Masnada Date: Mon, 1 Mar 2021 09:20:22 +0100 Subject: [PATCH 6/7] Added IAM role stack --- .../cloudformation/jspwiki-iam-role.yaml | 67 +++++++++++++++++++ 1 file changed, 67 insertions(+) create mode 100644 jspwiki-kendra-searchprovider/cloudformation/jspwiki-iam-role.yaml diff --git a/jspwiki-kendra-searchprovider/cloudformation/jspwiki-iam-role.yaml b/jspwiki-kendra-searchprovider/cloudformation/jspwiki-iam-role.yaml new file mode 100644 index 0000000000..1eb60b48a8 --- /dev/null +++ b/jspwiki-kendra-searchprovider/cloudformation/jspwiki-iam-role.yaml @@ -0,0 +1,67 @@ +AWSTemplateFormatVersion: "2010-09-09" +Description: > + Create an IAM Role that allows that can be attached to an EC2 Instance Profile in order to access a Kendra Index +Metadata: + + AWS::CloudFormation::Interface: + ParameterGroups: + - Label: + default: Parameters related to the Kendra Index and DataSource + Parameters: + - IndexName + - DataSourceName + ParameterLabels: + IndexName: + default: "The Kendra Index's Name" + DataSourceName: + default: "The Kendra DataSource's Name" + +Parameters: + + IndexName: + Description: "The name of the Kendra Index to create" + Type: String + Default: "JSPWikiIndex" + + DataSourceName: + Description: "The name of the Kendra DataSource to create" + Type: String + Default: "JSPWikiDataSource" + +Resources: + + JSPWikiRole: + Type: AWS::IAM::Role + Properties: + AssumeRolePolicyDocument: + Version: 2012-10-17 + Statement: + - Effect: "Allow" + Principal: + Service: + - ec2.amazonaws.com + Action: + - sts:AssumeRole + Path: "/" + Policies: + - PolicyName: AllowJSPWikiAccessToKendra + PolicyDocument: + Version: 2012-10-17 + Statement: + - Sid: AllowAccessToListOfIndices + Effect: Allow + Action: + - kendra:ListIndices + Resource: "*" + - Sid: AllowAccessToJSPWikiIndex + Effect: Allow + Action: + - kendra:Query + - kendra:StopDataSourceSyncJob + - kendra:BatchPutDocument + - kendra:StartDataSourceSyncJob + - kendra:ListDataSources + - kendra:BatchDeleteDocument + Resource: + - Fn::Sub: "arn:${AWS::Partition}:kendra:${AWS::Region}:${AWS::AccountId}:index/${IndexName}" + - Fn::Sub: "arn:${AWS::Partition}:kendra:${AWS::Region}:${AWS::AccountId}:index/${IndexName}/data-source/${DataSourceName}" \ No newline at end of file From ffb1502c6d1a6b1ab81225cb03187d4b3c2ec245 Mon Sep 17 00:00:00 2001 From: Julien Masnada Date: Tue, 2 Mar 2021 17:57:23 +0100 Subject: [PATCH 7/7] Moved documentation to jspwiki-wiki.a.o --- jspwiki-kendra-searchprovider/Dockerfile | 88 ----------- jspwiki-kendra-searchprovider/README.md | 40 ----- .../cloudformation/index-and-datasource.yaml | 139 ------------------ .../cloudformation/jspwiki-iam-role.yaml | 67 --------- .../docker-files/jspwiki-custom.properties | 3 - .../docs/images/JSPWiki_Search.png | Bin 61411 -> 0 bytes 6 files changed, 337 deletions(-) delete mode 100644 jspwiki-kendra-searchprovider/Dockerfile delete mode 100644 jspwiki-kendra-searchprovider/README.md delete mode 100644 jspwiki-kendra-searchprovider/cloudformation/index-and-datasource.yaml delete mode 100644 jspwiki-kendra-searchprovider/cloudformation/jspwiki-iam-role.yaml delete mode 100644 jspwiki-kendra-searchprovider/docker-files/jspwiki-custom.properties delete mode 100644 jspwiki-kendra-searchprovider/docs/images/JSPWiki_Search.png diff --git a/jspwiki-kendra-searchprovider/Dockerfile b/jspwiki-kendra-searchprovider/Dockerfile deleted file mode 100644 index 4627e2a714..0000000000 --- a/jspwiki-kendra-searchprovider/Dockerfile +++ /dev/null @@ -1,88 +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. - -#FROM maven:3.6-jdk-8 as package -#WORKDIR /tmp -#COPY . . -#RUN set -x \ -## fastest, minimum build -# && mvn clean package -pl jspwiki-war,jspwiki-kendra-searchprovider,jspwiki-wikipages/en -am -DskipTests - -FROM tomcat:9.0 - -#COPY --from=package /tmp/jspwiki-war/target/JSPWiki.war /tmp -#COPY --from=package /tmp/jspwiki-wikipages/en/target/jspwiki-wikipages-en-*.zip /tmp -#COPY --from=package /tmp/jspwiki-kendra-searchprovider/target/jspwiki-kendra-searchprovider-*.jar /tmp -COPY jspwiki-war/target/JSPWiki.war /tmp -COPY jspwiki-wikipages/en/target/jspwiki-wikipages-en-*.zip /tmp -#COPY jspwiki-kendra-searchprovider/target/jspwiki-kendra-searchprovider-*.jar /tmp - -COPY docker-files/log4j.properties /tmp -COPY docker-files/tomcat-users.xml $CATALINA_HOME/conf/tomcat-users.xml -COPY jspwiki-kendra-searchprovider/docker-files/jspwiki-custom.properties /tmp -# -# set default environment entries to configure jspwiki -ENV CATALINA_OPTS -Djava.security.egd=file:/dev/./urandom -ENV LANG en_US.UTF-8 -ENV jspwiki_basicAttachmentProvider_storageDir /var/jspwiki/pages -ENV jspwiki_fileSystemProvider_pageDir /var/jspwiki/pages -ENV jspwiki_jspwiki_frontPage Main -ENV jspwiki_pageProvider VersioningFileProvider -ENV jspwiki_use_external_logconfig true -ENV jspwiki_workDir /var/jspwiki/work -ENV jspwiki_xmlUserDatabaseFile /var/jspwiki/etc/userdatabase.xml -ENV jspwiki_xmlGroupDatabaseFile /var/jspwiki/etc/groupdatabase.xml - -RUN set -x \ - && export DEBIAN_FRONTEND=noninteractive \ - && apt install --fix-missing --quiet --yes unzip - -# -# install jspwiki -RUN set -x \ - && mkdir /var/jspwiki \ -# remove default tomcat applications, we dont need them to run jspwiki - && cd $CATALINA_HOME/webapps \ - && rm -rf examples host-manager manager docs ROOT \ -# remove other stuff we don't need - && rm -rf /usr/local/tomcat/bin/*.bat \ -# create subdirectories where all jspwiki stuff will live - && cd /var/jspwiki \ - && mkdir pages logs etc work \ -# deploy jspwiki - && mkdir $CATALINA_HOME/webapps/ROOT \ - && unzip -q -d $CATALINA_HOME/webapps/ROOT /tmp/JSPWiki.war \ - && rm /tmp/JSPWiki.war \ -# deploy wiki pages - && cd /tmp/ \ - && unzip -q jspwiki-wikipages-en-*.zip \ - && mv jspwiki-wikipages-en-*/* /var/jspwiki/pages/ \ - && rm -rf jspwiki-wikipages-en-* \ -# move the userdatabase.xml and groupdatabase.xml to /var/jspwiki/etc - && cd $CATALINA_HOME/webapps/ROOT/WEB-INF \ - && mv userdatabase.xml groupdatabase.xml /var/jspwiki/etc \ -# arrange proper logging (jspwiki.use.external.logconfig = true needs to be set) - && mv /tmp/log4j.properties $CATALINA_HOME/lib/log4j.properties \ -# Copy Kendra Search Provider configuration - && cp /tmp/jspwiki-custom.properties $CATALINA_HOME/webapps/ROOT/WEB-INF/classes - -# make port visible in metadata -EXPOSE 8080 - -# -# by default we start the Tomcat container when the docker container is started. -CMD ["/usr/local/tomcat/bin/catalina.sh", "run", ">/usr/local/tomcat/logs/catalina.out"] diff --git a/jspwiki-kendra-searchprovider/README.md b/jspwiki-kendra-searchprovider/README.md deleted file mode 100644 index f483cbe47b..0000000000 --- a/jspwiki-kendra-searchprovider/README.md +++ /dev/null @@ -1,40 +0,0 @@ -# JSPWiki Kendra Search provider - -## What is AWS Kendra - - Amazon Kendra is an intelligent search service powered by machine learning. - -## How to use Kendra with JSPWiki - -1. AWS Account - -You will need an AWS Account if you have not one already: [create-account](https://aws.amazon.com/resources/create-account/) - -2. Create the Kendra Index and DataSource - -In you can use the [index-and-datasource](cloudformation/index-and-datasource.yaml) Cloudformation stack to create the Kendra Index and DataSource. -This require that you have either the [AWS Cli](https://docs.aws.amazon.com/cli/latest/userguide/install-cliv2.html) or the [SAM Cli](https://docs.aws.amazon.com/serverless-application-model/latest/developerguide/serverless-sam-cli-install.html) installed. -Once installed you can simply run the following command to create your Kendra Index and DataSource: - -```shell -sam build --template cloudformation/index-and-datasource.yaml -sam deploy --guided -``` - -*Note*: it is important that the name for your Index and DataSource match the names setup in your JSPWiki Installation. -Namely make sure the properties `jspwiki.kendra.indexName` and `jspwiki.kendra.dataSourceName` are properly configured. - -4. Testing in Docker - -You can test your search index by running docker (*Note* you'll be using your AWS credentials) - -```shell -mvn package -pl jspwiki-war,jspwiki-wikipages/en -am -DskipTests -docker build -t jspwiki-kendra-searchprovider:latest -f jspwiki-kendra-searchprovider/Dockerfile . -docker run -p 8080:8080 -v ~/.aws:/root/.aws jspwiki-kendra-searchprovider:latest -``` - -Then you can create a Page, upload some PDF, and search for some content in the PDF document - -![JSPWiki Search Results](docs/images/JSPWiki_Search.png) - diff --git a/jspwiki-kendra-searchprovider/cloudformation/index-and-datasource.yaml b/jspwiki-kendra-searchprovider/cloudformation/index-and-datasource.yaml deleted file mode 100644 index 7715cadc2e..0000000000 --- a/jspwiki-kendra-searchprovider/cloudformation/index-and-datasource.yaml +++ /dev/null @@ -1,139 +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. -AWSTemplateFormatVersion: '2010-09-09' -Transform: AWS::Serverless-2016-10-31 -Description: > - index-and-datasource. Create the Kendra Index and Datasource. -Metadata: - - AWS::CloudFormation::Interface: - ParameterGroups: - - Label: - default: Parameters related to the Kendra Index and DataSource - Parameters: - - IndexName - - DataSourceName - - KendraEdition - ParameterLabels: - IndexName: - default: "The Kendra Index's Name" - DataSourceName: - default: "The Kendra DataSource's Name" - KendraEdition: - default: "The Kendra Edition" - -Parameters: - - IndexName: - Description: "The name of the Kendra Index to create" - Type: String - Default: "JSPWikiIndex" - - DataSourceName: - Description: "The name of the Kendra DataSource to create" - Type: String - Default: "JSPWikiDataSource" - - KendraEdition: - Description: "The name of the Kendra DataSource to create" - Type: String - AllowedValues: [ "DEVELOPER_EDITION", "ENTERPRISE_EDITION" ] - Default: "DEVELOPER_EDITION" - -Resources: - - KendraServiceRole: - Type: AWS::IAM::Role - Properties: - AssumeRolePolicyDocument: - Version: 2012-10-17 - Statement: - - Effect: Allow - Principal: - Service: - - kendra.amazonaws.com - Action: - - sts:AssumeRole - Path: "/" - Policies: - - PolicyName: AllowKendra - PolicyDocument: - Version: 2012-10-17 - Statement: - - Effect: Allow - Action: - - cloudwatch:PutMetricData - Resource: "*" - Condition: - StringEquals: - cloudwatch:namespace: AWS/Kendra - - Effect: Allow - Action: - - logs:DescribeLogGroups - Resource: "*" - - Effect: Allow - Action: - - logs:CreateLogGroup - Resource: - - Fn::Sub: arn:${AWS::Partition}:logs:${AWS::Region}:${AWS::AccountId}:log-group:/aws/kendra/* - - Effect: Allow - Action: - - logs:DescribeLogStreams - - logs:CreateLogStream - - logs:PutLogEvents - Resource: - - Fn::Sub: arn:${AWS::Partition}:logs:${AWS::Region}:${AWS::AccountId}:log-group:/aws/kendra/* - - Index: - Type: AWS::Kendra::Index - Properties: - Description: "Index for JSPWiki KendraSearchProvider" - Edition: - Ref: KendraEdition - Name: - Ref: IndexName - RoleArn: - Fn::GetAtt: KendraServiceRole.Arn - Tags: - - Key: Origin - Value: JSPWIKI - - DataSource: - Type: AWS::Kendra::DataSource - Properties: - Description: "DataSource for JSPWiki KendraSearchProvider" - IndexId: - Ref: Index - Name: - Ref: DataSourceName - Tags: - - Key: Origin - Value: JSPWIKI - Type: CUSTOM - -Outputs: - - Index: - Description: The Index - Value: - Ref: Index - - DataSource: - Description: The DataSource - Value: - Ref: DataSource - diff --git a/jspwiki-kendra-searchprovider/cloudformation/jspwiki-iam-role.yaml b/jspwiki-kendra-searchprovider/cloudformation/jspwiki-iam-role.yaml deleted file mode 100644 index 1eb60b48a8..0000000000 --- a/jspwiki-kendra-searchprovider/cloudformation/jspwiki-iam-role.yaml +++ /dev/null @@ -1,67 +0,0 @@ -AWSTemplateFormatVersion: "2010-09-09" -Description: > - Create an IAM Role that allows that can be attached to an EC2 Instance Profile in order to access a Kendra Index -Metadata: - - AWS::CloudFormation::Interface: - ParameterGroups: - - Label: - default: Parameters related to the Kendra Index and DataSource - Parameters: - - IndexName - - DataSourceName - ParameterLabels: - IndexName: - default: "The Kendra Index's Name" - DataSourceName: - default: "The Kendra DataSource's Name" - -Parameters: - - IndexName: - Description: "The name of the Kendra Index to create" - Type: String - Default: "JSPWikiIndex" - - DataSourceName: - Description: "The name of the Kendra DataSource to create" - Type: String - Default: "JSPWikiDataSource" - -Resources: - - JSPWikiRole: - Type: AWS::IAM::Role - Properties: - AssumeRolePolicyDocument: - Version: 2012-10-17 - Statement: - - Effect: "Allow" - Principal: - Service: - - ec2.amazonaws.com - Action: - - sts:AssumeRole - Path: "/" - Policies: - - PolicyName: AllowJSPWikiAccessToKendra - PolicyDocument: - Version: 2012-10-17 - Statement: - - Sid: AllowAccessToListOfIndices - Effect: Allow - Action: - - kendra:ListIndices - Resource: "*" - - Sid: AllowAccessToJSPWikiIndex - Effect: Allow - Action: - - kendra:Query - - kendra:StopDataSourceSyncJob - - kendra:BatchPutDocument - - kendra:StartDataSourceSyncJob - - kendra:ListDataSources - - kendra:BatchDeleteDocument - Resource: - - Fn::Sub: "arn:${AWS::Partition}:kendra:${AWS::Region}:${AWS::AccountId}:index/${IndexName}" - - Fn::Sub: "arn:${AWS::Partition}:kendra:${AWS::Region}:${AWS::AccountId}:index/${IndexName}/data-source/${DataSourceName}" \ No newline at end of file diff --git a/jspwiki-kendra-searchprovider/docker-files/jspwiki-custom.properties b/jspwiki-kendra-searchprovider/docker-files/jspwiki-custom.properties deleted file mode 100644 index b862a793a8..0000000000 --- a/jspwiki-kendra-searchprovider/docker-files/jspwiki-custom.properties +++ /dev/null @@ -1,3 +0,0 @@ -jspwiki.searchProvider = org.apache.wiki.search.kendra.KendraSearchProvider -jspwiki.kendra.indexName = JSPWikiIndex -jspwiki.kendra.dataSourceName = JSPWikiDataSource diff --git a/jspwiki-kendra-searchprovider/docs/images/JSPWiki_Search.png b/jspwiki-kendra-searchprovider/docs/images/JSPWiki_Search.png deleted file mode 100644 index cfa5525378b4a6a181138c6f82e4b5abd6405ccc..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 61411 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