Skip to content
Snippets Groups Projects
Commit 904faaf7 authored by Martin Lowe's avatar Martin Lowe :flag_ca: Committed by Christopher Guindon
Browse files

Switch to MongoDB


Added MongoDB support. Added random select, a few filters, BSON codecs,
sorting, secret properties.

Change-Id: I613403d5586f192fd68666df234139353069f8a2
Signed-off-by: Martin Lowe's avatarMartin Lowe <martin.lowe@eclipse-foundation.org>
parent ddb1dbdf
No related branches found
No related tags found
No related merge requests found
Showing
with 806 additions and 455 deletions
......@@ -32,4 +32,11 @@ target/
pom.xml.tag
pom.xml.releaseBackup
pom.xml.versionsBackup
release.properties
\ No newline at end of file
release.properties
# Secrets config
src/main/resources/secret.properties
test/main/resources/secret.properties
#NodeJS
node_modules/
\ No newline at end of file
/*
* Copyright 2007-present the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import java.net.*;
import java.io.*;
import java.nio.channels.*;
import java.util.Properties;
public class MavenWrapperDownloader {
private static final String WRAPPER_VERSION = "0.5.3";
/**
* Default URL to download the maven-wrapper.jar from, if no 'downloadUrl' is provided.
*/
private static final String DEFAULT_DOWNLOAD_URL = "https://repo.maven.apache.org/maven2/io/takari/maven-wrapper/"
+ WRAPPER_VERSION + "/maven-wrapper-" + WRAPPER_VERSION + " .jar";
/**
* Path to the maven-wrapper.properties file, which might contain a downloadUrl property to
* use instead of the default one.
*/
private static final String MAVEN_WRAPPER_PROPERTIES_PATH =
".mvn/wrapper/maven-wrapper.properties";
/**
* Path where the maven-wrapper.jar will be saved to.
*/
private static final String MAVEN_WRAPPER_JAR_PATH =
".mvn/wrapper/maven-wrapper.jar";
/**
* Name of the property which should be used to override the default download url for the wrapper.
*/
private static final String PROPERTY_NAME_WRAPPER_URL = "wrapperUrl";
public static void main(String args[]) {
System.out.println("- Downloader started");
File baseDirectory = new File(args[0]);
System.out.println("- Using base directory: " + baseDirectory.getAbsolutePath());
// If the maven-wrapper.properties exists, read it and check if it contains a custom
// wrapperUrl parameter.
File mavenWrapperPropertyFile = new File(baseDirectory, MAVEN_WRAPPER_PROPERTIES_PATH);
String url = DEFAULT_DOWNLOAD_URL;
if(mavenWrapperPropertyFile.exists()) {
FileInputStream mavenWrapperPropertyFileInputStream = null;
try {
mavenWrapperPropertyFileInputStream = new FileInputStream(mavenWrapperPropertyFile);
Properties mavenWrapperProperties = new Properties();
mavenWrapperProperties.load(mavenWrapperPropertyFileInputStream);
url = mavenWrapperProperties.getProperty(PROPERTY_NAME_WRAPPER_URL, url);
} catch (IOException e) {
System.out.println("- ERROR loading '" + MAVEN_WRAPPER_PROPERTIES_PATH + "'");
} finally {
try {
if(mavenWrapperPropertyFileInputStream != null) {
mavenWrapperPropertyFileInputStream.close();
}
} catch (IOException e) {
// Ignore ...
}
}
}
System.out.println("- Downloading from: " + url);
File outputFile = new File(baseDirectory.getAbsolutePath(), MAVEN_WRAPPER_JAR_PATH);
if(!outputFile.getParentFile().exists()) {
if(!outputFile.getParentFile().mkdirs()) {
System.out.println(
"- ERROR creating output directory '" + outputFile.getParentFile().getAbsolutePath() + "'");
}
}
System.out.println("- Downloading to: " + outputFile.getAbsolutePath());
try {
downloadFileFromURL(url, outputFile);
System.out.println("Done");
System.exit(0);
} catch (Throwable e) {
System.out.println("- Error downloading");
e.printStackTrace();
System.exit(1);
}
}
private static void downloadFileFromURL(String urlString, File destination) throws Exception {
if (System.getenv("MVNW_USERNAME") != null && System.getenv("MVNW_PASSWORD") != null) {
String username = System.getenv("MVNW_USERNAME");
char[] password = System.getenv("MVNW_PASSWORD").toCharArray();
Authenticator.setDefault(new Authenticator() {
@Override
protected PasswordAuthentication getPasswordAuthentication() {
return new PasswordAuthentication(username, password);
}
});
}
URL website = new URL(urlString);
ReadableByteChannel rbc;
rbc = Channels.newChannel(website.openStream());
FileOutputStream fos = new FileOutputStream(destination);
fos.getChannel().transferFrom(rbc, 0, Long.MAX_VALUE);
fos.close();
rbc.close();
}
}
File added
distributionUrl=https://repo.maven.apache.org/maven2/org/apache/maven/apache-maven/3.6.0/apache-maven-3.6.0-bin.zip
wrapperUrl=https://repo.maven.apache.org/maven2/io/takari/maven-wrapper/0.5.3/maven-wrapper-0.5.3.jar
......@@ -4,6 +4,26 @@
Proof of concept project within the Microservice initiative, the Foundation looks to leverage Quarkus to renew their MPC API. The specifications to the API can be found in the `marketplace-rest-api-specs` project within the EclipseFdn group on GitHub.
## Requirements
1. Installed and configured JDK 1.8+
1. Apache Maven 3.5.3+
1. Running instance of MongoDB
### Optional requirements
1. Node JS + NPM (if using sample data script)
## Configuration
This section will outline configuration values that need to be checked and updated to run the API in a local environment. Unless otherwise stated, all values to be updated will be in `./src/main/resources/application.properties`.
1. In order to properly detect MongoDB, a connection string needs to be updated. `quarkus.mongodb.connection-string` designates the location of MongoDB to quarkus in the form of `mongodb://<host>:<port>/`. By default, this value points at `mongodb://localhost:27017`, the default location for local installs of MongoDB.
1. Update `quarkus.mongodb.credentials.username` to be a known user with write permissions to MongoDB instance.
1. Create a copy of `./src/main/resources/secret.sample.properties` named `secret.properties` in the same folder
1. Update `quarkus.mongodb.credentials.password` to be the password for the MongoDB user in the newly created `secret.properties` file.
1. By default, this application binds to port 8090. If port 8090 is occupied by another service, the value of `quarkus.http.port` can be modified to designate a different port.
## Build
* Development
......@@ -24,6 +44,17 @@ Proof of concept project within the Microservice initiative, the Foundation look
See https://quarkus.io for more information.
## Sample data
For ease of use, a script has been created to load sample data into a MongoDB instance using Node JS and a running instance of the API. This script will load a large amount of listings into the running MongoDB using the API for use in testing different queries without having to retrieve real world data.
1. In root of project, run `npm install` to retrieve dependencies for sample data loader script.
1. Run `npm run-script load-listings -- -s <api-url>`, replacing `<api-url>` with the URL of a running instance of this API (e.g. http://localhost:8090). This should take a couple of moments, as by default the loader will load 5000 dummy entries into MongoDB. This can be changed using a `-c` flag followed by the number of entries to be created.
### Additional MongoDB commands needed:
- db.listings.createIndex({body:"text", teaser:"text",title:"text"})
## Copyright
Copyright (c) 2019 Eclipse Foundation and others.
......
{
"name": "loader-script",
"version": "1.0.0",
"lockfileVersion": 1,
"requires": true,
"dependencies": {
"ansi-regex": {
"version": "4.1.0",
"resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.0.tgz",
"integrity": "sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg=="
},
"ansi-styles": {
"version": "3.2.1",
"resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz",
"integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==",
"requires": {
"color-convert": "^1.9.0"
}
},
"axios": {
"version": "0.19.0",
"resolved": "https://registry.npmjs.org/axios/-/axios-0.19.0.tgz",
"integrity": "sha512-1uvKqKQta3KBxIz14F2v06AEHZ/dIoeKfbTRkK1E5oqjDnuEerLmYTgJB5AiQZHJcljpg1TuRzdjDR06qNk0DQ==",
"requires": {
"follow-redirects": "1.5.10",
"is-buffer": "^2.0.2"
}
},
"camelcase": {
"version": "5.3.1",
"resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz",
"integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg=="
},
"cliui": {
"version": "5.0.0",
"resolved": "https://registry.npmjs.org/cliui/-/cliui-5.0.0.tgz",
"integrity": "sha512-PYeGSEmmHM6zvoef2w8TPzlrnNpXIjTipYK780YswmIP9vjxmd6Y2a3CB2Ks6/AU8NHjZugXvo8w3oWM2qnwXA==",
"requires": {
"string-width": "^3.1.0",
"strip-ansi": "^5.2.0",
"wrap-ansi": "^5.1.0"
}
},
"color-convert": {
"version": "1.9.3",
"resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz",
"integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==",
"requires": {
"color-name": "1.1.3"
}
},
"color-name": {
"version": "1.1.3",
"resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz",
"integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU="
},
"debug": {
"version": "3.1.0",
"resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz",
"integrity": "sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==",
"requires": {
"ms": "2.0.0"
}
},
"decamelize": {
"version": "1.2.0",
"resolved": "https://registry.npmjs.org/decamelize/-/decamelize-1.2.0.tgz",
"integrity": "sha1-9lNNFRSCabIDUue+4m9QH5oZEpA="
},
"emoji-regex": {
"version": "7.0.3",
"resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-7.0.3.tgz",
"integrity": "sha512-CwBLREIQ7LvYFB0WyRvwhq5N5qPhc6PMjD6bYggFlI5YyDgl+0vxq5VHbMOFqLg7hfWzmu8T5Z1QofhmTIhItA=="
},
"find-up": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/find-up/-/find-up-3.0.0.tgz",
"integrity": "sha512-1yD6RmLI1XBfxugvORwlck6f75tYL+iR0jqwsOrOxMZyGYqUuDhJ0l4AXdO1iX/FTs9cBAMEk1gWSEx1kSbylg==",
"requires": {
"locate-path": "^3.0.0"
}
},
"follow-redirects": {
"version": "1.5.10",
"resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.5.10.tgz",
"integrity": "sha512-0V5l4Cizzvqt5D44aTXbFZz+FtyXV1vrDN6qrelxtfYQKW0KO0W2T/hkE8xvGa/540LkZlkaUjO4ailYTFtHVQ==",
"requires": {
"debug": "=3.1.0"
}
},
"get-caller-file": {
"version": "2.0.5",
"resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz",
"integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg=="
},
"is-buffer": {
"version": "2.0.3",
"resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-2.0.3.tgz",
"integrity": "sha512-U15Q7MXTuZlrbymiz95PJpZxu8IlipAp4dtS3wOdgPXx3mqBnslrWU14kxfHB+Py/+2PVKSr37dMAgM2A4uArw=="
},
"is-fullwidth-code-point": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz",
"integrity": "sha1-o7MKXE8ZkYMWeqq5O+764937ZU8="
},
"locate-path": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/locate-path/-/locate-path-3.0.0.tgz",
"integrity": "sha512-7AO748wWnIhNqAuaty2ZWHkQHRSNfPVIsPIfwEOWO22AmaoVrWavlOcMR5nzTLNYvp36X220/maaRsrec1G65A==",
"requires": {
"p-locate": "^3.0.0",
"path-exists": "^3.0.0"
}
},
"ms": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
"integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g="
},
"p-limit": {
"version": "2.2.1",
"resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.2.1.tgz",
"integrity": "sha512-85Tk+90UCVWvbDavCLKPOLC9vvY8OwEX/RtKF+/1OADJMVlFfEHOiMTPVyxg7mk/dKa+ipdHm0OUkTvCpMTuwg==",
"requires": {
"p-try": "^2.0.0"
}
},
"p-locate": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/p-locate/-/p-locate-3.0.0.tgz",
"integrity": "sha512-x+12w/To+4GFfgJhBEpiDcLozRJGegY+Ei7/z0tSLkMmxGZNybVMSfWj9aJn8Z5Fc7dBUNJOOVgPv2H7IwulSQ==",
"requires": {
"p-limit": "^2.0.0"
}
},
"p-try": {
"version": "2.2.0",
"resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz",
"integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ=="
},
"path-exists": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/path-exists/-/path-exists-3.0.0.tgz",
"integrity": "sha1-zg6+ql94yxiSXqfYENe1mwEP1RU="
},
"random-words": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/random-words/-/random-words-1.1.0.tgz",
"integrity": "sha512-GyV8PlSmQE08S/RSCjG9Uh0uQaUC7iRpA18PWk9OSnvNCzKQ+B2NxqqN/cYBej4t7dfBWxh10KFBYSiNcg1jlg=="
},
"require-directory": {
"version": "2.1.1",
"resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz",
"integrity": "sha1-jGStX9MNqxyXbiNE/+f3kqam30I="
},
"require-main-filename": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/require-main-filename/-/require-main-filename-2.0.0.tgz",
"integrity": "sha512-NKN5kMDylKuldxYLSUfrbo5Tuzh4hd+2E8NPPX02mZtn1VuREQToYe/ZdlJy+J3uCpfaiGF05e7B8W0iXbQHmg=="
},
"set-blocking": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz",
"integrity": "sha1-BF+XgtARrppoA93TgrJDkrPYkPc="
},
"string-width": {
"version": "3.1.0",
"resolved": "https://registry.npmjs.org/string-width/-/string-width-3.1.0.tgz",
"integrity": "sha512-vafcv6KjVZKSgz06oM/H6GDBrAtz8vdhQakGjFIvNrHA6y3HCF1CInLy+QLq8dTJPQ1b+KDUqDFctkdRW44e1w==",
"requires": {
"emoji-regex": "^7.0.1",
"is-fullwidth-code-point": "^2.0.0",
"strip-ansi": "^5.1.0"
}
},
"strip-ansi": {
"version": "5.2.0",
"resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.2.0.tgz",
"integrity": "sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==",
"requires": {
"ansi-regex": "^4.1.0"
}
},
"which-module": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/which-module/-/which-module-2.0.0.tgz",
"integrity": "sha1-2e8H3Od7mQK4o6j6SzHD4/fm6Ho="
},
"wrap-ansi": {
"version": "5.1.0",
"resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-5.1.0.tgz",
"integrity": "sha512-QC1/iN/2/RPVJ5jYK8BGttj5z83LmSKmvbvrXPNCLZSEb32KKVDJDl/MOt2N01qU2H/FkzEa9PKto1BqDjtd7Q==",
"requires": {
"ansi-styles": "^3.2.0",
"string-width": "^3.0.0",
"strip-ansi": "^5.0.0"
}
},
"y18n": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/y18n/-/y18n-4.0.0.tgz",
"integrity": "sha512-r9S/ZyXu/Xu9q1tYlpsLIsa3EeLXXk0VwlxqTcFRfg9EhMW+17kbt9G0NrgCmhGb5vT2hyhJZLfDGx+7+5Uj/w=="
},
"yargs": {
"version": "14.0.0",
"resolved": "https://registry.npmjs.org/yargs/-/yargs-14.0.0.tgz",
"integrity": "sha512-ssa5JuRjMeZEUjg7bEL99AwpitxU/zWGAGpdj0di41pOEmJti8NR6kyUIJBkR78DTYNPZOU08luUo0GTHuB+ow==",
"requires": {
"cliui": "^5.0.0",
"decamelize": "^1.2.0",
"find-up": "^3.0.0",
"get-caller-file": "^2.0.1",
"require-directory": "^2.1.1",
"require-main-filename": "^2.0.0",
"set-blocking": "^2.0.0",
"string-width": "^3.0.0",
"which-module": "^2.0.0",
"y18n": "^4.0.0",
"yargs-parser": "^13.1.1"
}
},
"yargs-parser": {
"version": "13.1.1",
"resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-13.1.1.tgz",
"integrity": "sha512-oVAVsHz6uFrg3XQheFII8ESO2ssAf9luWuAd6Wexsu4F3OtIW0o8IribPXYrD4WC24LWtPrJlGy87y5udK+dxQ==",
"requires": {
"camelcase": "^5.0.0",
"decamelize": "^1.2.0"
}
}
}
}
{
"name": "loader-script",
"version": "1.0.0",
"description": "",
"main": "src/main/node/index.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1",
"load-listings": "node src/main/node/index.js"
},
"author": "",
"license": "EPL-2.0",
"dependencies": {
"axios": "^0.19.0",
"random-words": "^1.1.0",
"yargs": "^14.0.0"
}
}
......@@ -9,7 +9,7 @@
<version>0.1-ALPHA</version>
<properties>
<surefire-plugin.version>2.22.0</surefire-plugin.version>
<quarkus.version>0.15.0</quarkus.version>
<quarkus.version>0.21.1</quarkus.version>
<maven.compiler.source>1.8</maven.compiler.source>
<maven.compiler.target>1.8</maven.compiler.target>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
......@@ -17,7 +17,7 @@
<sonar.tests>src/test</sonar.tests>
<sonar.java.coveragePlugin>jacoco</sonar.java.coveragePlugin>
<sonar.dynamicAnalysis>reuseReports</sonar.dynamicAnalysis>
<sonar.jacoco.reportPath>${project.build.directory}/jacoco-report</sonar.jacoco.reportPath>
<sonar.jacoco.reportPaths>${project.build.directory}/jacoco-report</sonar.jacoco.reportPaths>
<sonar.junit.reportPath>${project.build.directory}/surefire-reports</sonar.junit.reportPath>
</properties>
<dependencyManagement>
......@@ -39,42 +39,51 @@
<dependency>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-junit5</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>io.rest-assured</groupId>
<artifactId>rest-assured</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.jboss.logmanager</groupId>
<artifactId>jboss-logmanager</artifactId>
<version>2.1.14.Final</version>
</dependency>
<!-- Custom dependencies -->
<dependency>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-resteasy-jsonb-runtime</artifactId>
<version>0.11.0</version>
<version>0.12.0</version>
</dependency>
<dependency>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-smallrye-health</artifactId>
</dependency>
<dependency>
<groupId>org.apache.solr</groupId>
<artifactId>solr-solrj</artifactId>
<version>6.4.0</version>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-mongodb-client</artifactId>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-log4j12</artifactId>
<version>1.7.25</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-lang3</artifactId>
</dependency>
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-text</artifactId>
<version>1.8</version>
</dependency>
<!-- Caching -->
<dependency>
<groupId>com.google.guava</groupId>
<artifactId>guava</artifactId>
<version>18.0</version>
</dependency>
<dependency>
<groupId>com.google.guava</groupId>
<artifactId>guava</artifactId>
</dependency>
</dependencies>
<build>
<plugins>
......@@ -87,27 +96,19 @@
<goals>
<goal>build</goal>
</goals>
<configuration>
<additionalBuildArgs>-H:IncludeResources=.*/.ccf</additionalBuildArgs>
</configuration>
</execution>
</executions>
</plugin>
<plugin>
<artifactId>maven-surefire-plugin</artifactId>
<version>${surefire-plugin.version}</version>
<configuration>
<systemProperties>
<java.util.logging.manager>org.jboss.logmanager.LogManager</java.util.logging.manager>
</systemProperties>
</configuration>
</plugin>
<plugin>
<groupId>org.jacoco</groupId>
<artifactId>jacoco-maven-plugin</artifactId>
<version>0.8.1</version>
<version>0.8.4</version>
<configuration>
<destFile>${sonar.jacoco.reportPath}</destFile>
<destFile>${sonar.jacoco.reportPaths}</destFile>
<append>true</append>
</configuration>
<executions>
......@@ -123,7 +124,7 @@
<goal>report</goal>
</goals>
<configuration>
<outputDirectory>${project.build.directory}/jacoco-report</outputDirectory>
<outputDirectory>${sonar.jacoco.reportPaths}</outputDirectory>
</configuration>
</execution>
</executions>
......
/* Copyright (c) 2019 Eclipse Foundation and others.
* This program and the accompanying materials are made available
* under the terms of the Eclipse Public License 2.0
* which is available at http://www.eclipse.org/legal/epl-v20.html,
* SPDX-License-Identifier: EPL-2.0
*/
package org.eclipsefoundation.marketplace.config;
import javax.json.bind.Jsonb;
import javax.json.bind.JsonbBuilder;
import javax.json.bind.JsonbConfig;
import javax.json.bind.config.PropertyNamingStrategy;
import javax.ws.rs.ext.ContextResolver;
import javax.ws.rs.ext.Provider;
/**
* Updates JSONB config to use a naming convention when interacting with objects
* that match the API best practices set by internal documentation.
*
* @author Martin Lowe
*/
@Provider
public class JsonBConfig implements ContextResolver<Jsonb> {
@Override
public Jsonb getContext(Class<?> type) {
JsonbConfig config = new JsonbConfig();
// following strategy is defined as default by internal API guidelines
config.withPropertyNamingStrategy(PropertyNamingStrategy.LOWER_CASE_WITH_UNDERSCORES);
return JsonbBuilder.create(config);
}
}
/* Copyright (c) 2019 Eclipse Foundation and others.
* This program and the accompanying materials are made available
* under the terms of the Eclipse Public License 2.0
* which is available at http://www.eclipse.org/legal/epl-v20.html,
* SPDX-License-Identifier: EPL-2.0
*/
package org.eclipsefoundation.marketplace.config;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileReader;
import java.io.IOException;
import java.net.URL;
import java.util.HashMap;
import java.util.Map;
import java.util.Properties;
import java.util.ServiceLoader;
import org.eclipse.microprofile.config.spi.ConfigSource;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* Reads in a set of secret configuration values from the secret.properties file
* in the resources folder. These values are only secret in that they are set to
* be ignored by Git and should be set on a per-server basis.
*
* ConfigSource implementation was used to enable the usage of the
* {@link ServiceLoader} to load the configurations.
*
* @author Martin Lowe
*/
public class SecretConfigSource implements ConfigSource {
private static final Logger LOGGER = LoggerFactory.getLogger(SecretConfigSource.class);
private Map<String, String> secrets;
@Override
public Map<String, String> getProperties() {
if (secrets == null) {
this.secrets = new HashMap<>();
// use the class loader to get the secret.properties file path
URL fURL = getClass().getClassLoader().getResource("secret.properties");
if (fURL == null) {
return this.secrets;
}
// load the secrets file in
File f = new File(fURL.getFile());
if (!f.exists() || !f.canRead()) {
return this.secrets;
}
// read each of the lines of secret config that should be added
try (FileReader reader = new FileReader(f); BufferedReader br = new BufferedReader(reader)) {
Properties p = new Properties();
p.load(br);
secrets.putAll((Map) p);
} catch (IOException e) {
LOGGER.error("Error while reading in secrets configuration file.", e);
}
LOGGER.debug("Found secret keys: {}", secrets.keySet());
// add priority ordinal to map if missing. 260 ordinal sets the priority between
// container and environment variable priority.
secrets.computeIfAbsent(ConfigSource.CONFIG_ORDINAL, key -> "260");
}
return secrets;
}
@Override
public String getValue(String propertyName) {
// if secrets not found, retrieve them
if (secrets == null)
this.getProperties();
return secrets.get(propertyName);
}
@Override
public String getName() {
return "secret";
}
}
/* Copyright (c) 2019 Eclipse Foundation and others.
* This program and the accompanying materials are made available
* under the terms of the Eclipse Public License 2.0
* which is available at http://www.eclipse.org/legal/epl-v20.html,
* SPDX-License-Identifier: EPL-2.0
*/
package org.eclipsefoundation.marketplace.dao;
import java.util.List;
import java.util.concurrent.CompletionStage;
import org.eclipsefoundation.marketplace.health.BeanHealth;
import org.eclipsefoundation.marketplace.model.MongoQuery;
import com.mongodb.client.result.DeleteResult;
/**
* Interface for classes communicating with MongoDB. Assumes that reactive
* stream asynchronous calls are used rather than blocking methods.
*
* @author Martin Lowe
*/
public interface MongoDao extends BeanHealth {
/**
* Retrieves a list of typed results given the query passed.
*
* @param q the query object for the current operation
* @return a future result set of objects of type set in query
*/
<T> CompletionStage<List<T>> get(MongoQuery<T> q);
/**
* Adds a list of typed documents to the currently active database and schema,
* using the query object to access the document type.
*
* @param <T> the type of document to post
* @param q the query object for the current operation
* @param documents the list of typed documents to add to the database instance.
* @return a future Void result indicating success on return.
*/
<T> CompletionStage<Void> add(MongoQuery<T> q, List<T> documents);
/**
* Deletes documents that match the given query.
*
* @param <T> the type of document that is being deleted
* @param q the query object for the current operation
* @return a future deletion result indicating whether the operation was
* successful
*/
<T> CompletionStage<DeleteResult> delete(MongoQuery<T> q);
/**
* Counts the number of filtered results of the given document type present.
*
* @param <T> the type of documents beign counted
* @param q the query object for the current operation
* @return a future long result representing the number of results available for
* the given query and docuement type.
*/
<T> CompletionStage<Long> count(MongoQuery<T> q);
}
/* Copyright (c) 2019 Eclipse Foundation and others.
* This program and the accompanying materials are made available
* under the terms of the Eclipse Public License 2.0
* which is available at http://www.eclipse.org/legal/epl-v20.html,
* SPDX-License-Identifier: EPL-2.0
*/
package org.eclipsefoundation.marketplace.dao;
import java.util.List;
import org.apache.solr.client.solrj.SolrQuery;
import org.eclipsefoundation.marketplace.dao.mapper.SolrBeanMapper;
/**
* Interface for DAO objects that interact with Solr instances.
*
* @author Martin Lowe
*/
public interface SolrDao {
/**
* Retrieves data from the current Solr instance using a SolrQuery object.
*
* @param <T> the type of data that should be generated from the Solr results
* @param <M> a mapper object that generates objects of type {@link T}
* @param q the query object
* @param mapper an instance of the mapper that will be used to generate results
* of type {@link T}
* @return a list of objects that map 1:1 for results of the Solr query.
*/
<T, M extends SolrBeanMapper<T>> List<T> get(SolrQuery q, M mapper);
/**
* Persists documents to the Solr instance, using the mapper to convert
* documents back into a form consumable by Solr.
*
* @param <T> the type of data that should be persisted to Solr
* @param <M> a mapper object that generates objects of type {@link T}
* @param documents the objects to be persisted to Solr
* @param mapper an instance of the mapper that will be used to generate
* results of type {@link T}
*/
<T, M extends SolrBeanMapper<T>> void add(List<T> documents, M mapper);
/**
* Removes documents from Solr by query, removing any documents that match the
* below query.
*
* @param q the query identifying documents to be deleted from Solr
* @return the number of documents affected by the deletion request
*/
int delete(SolrQuery q);
/**
* Counts documents available within Solr by query.
*
* @param q the query identifying documents to be deleted from Solr
* @return the number of documents available for a given request
*/
long count(SolrQuery q);
}
/* Copyright (c) 2019 Eclipse Foundation and others.
* This program and the accompanying materials are made available
* under the terms of the Eclipse Public License 2.0
* which is available at http://www.eclipse.org/legal/epl-v20.html,
* SPDX-License-Identifier: EPL-2.0
*/
package org.eclipsefoundation.marketplace.dao.impl;
import java.util.List;
import java.util.concurrent.CompletionStage;
import javax.enterprise.context.ApplicationScoped;
import javax.inject.Inject;
import org.bson.BsonDocument;
import org.eclipse.microprofile.config.inject.ConfigProperty;
import org.eclipse.microprofile.health.HealthCheckResponse;
import org.eclipse.microprofile.health.HealthCheckResponseBuilder;
import org.eclipsefoundation.marketplace.dao.MongoDao;
import org.eclipsefoundation.marketplace.exception.MaintenanceException;
import org.eclipsefoundation.marketplace.model.MongoQuery;
import org.eclipsefoundation.marketplace.namespace.DtoTableNames;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.mongodb.client.result.DeleteResult;
import io.quarkus.mongodb.ReactiveMongoClient;
import io.quarkus.mongodb.ReactiveMongoCollection;
/**
* Default implementation of the MongoDB DAO.
*
* @author Martin Lowe
*/
@ApplicationScoped
public class DefaultMongoDao implements MongoDao {
private static final Logger LOGGER = LoggerFactory.getLogger(DefaultMongoDao.class);
@ConfigProperty(name = "mongodb.database")
String databaseName;
@ConfigProperty(name = "mongodb.default.limit")
int defaultLimit;
@ConfigProperty(name = "mongodb.default.limit.max")
int defaultMax;
@ConfigProperty(name = "mongodb.maintenance", defaultValue = "false")
boolean maintenanceFlag;
@Inject
ReactiveMongoClient mongoClient;
@Override
public <T> CompletionStage<List<T>> get(MongoQuery<T> q) {
if (maintenanceFlag) {
throw new MaintenanceException();
}
if (LOGGER.isDebugEnabled()) {
LOGGER.debug("Querying MongoDB using the following query: {}", q);
}
// when looking for random data, use aggregate pipeline data rather than filter
if (q.isAggregate()) {
LOGGER.debug("Getting aggregate results");
return getCollection(q.getDocType()).aggregate(q.getPipeline(getLimit(q)), q.getDocType()).distinct()
.toList().run();
}
LOGGER.debug("Getting find results");
return getCollection(q.getDocType()).find(q.getFindOptions().limit(getLimit(q))).toList().run();
}
@Override
public <T> CompletionStage<Void> add(MongoQuery<T> q, List<T> documents) {
if (maintenanceFlag) {
throw new MaintenanceException();
}
if (LOGGER.isDebugEnabled()) {
LOGGER.debug("Adding {} documents to MongoDB of type {}", documents.size(), q.getDocType().getSimpleName());
}
return getCollection(q.getDocType()).insertMany(documents);
}
@Override
public <T> CompletionStage<DeleteResult> delete(MongoQuery<T> q) {
if (maintenanceFlag) {
throw new MaintenanceException();
}
if (LOGGER.isDebugEnabled()) {
LOGGER.debug("Removing documents from MongoDB using the following query: {}", q);
}
// TODO we need to figure out how to build this
return getCollection(q.getDocType()).deleteMany(new BsonDocument());
}
@Override
public <T> CompletionStage<Long> count(MongoQuery<T> q) {
if (maintenanceFlag) {
throw new MaintenanceException();
}
if (LOGGER.isDebugEnabled()) {
LOGGER.debug("Counting documents in MongoDB that match the following query: {}", q);
}
return getCollection(q.getDocType()).countDocuments(q.getFilter());
}
@Override
public HealthCheckResponse health() {
HealthCheckResponseBuilder b = HealthCheckResponse.named("MongoDB readiness");
if (maintenanceFlag) {
return b.down().withData("error", "Maintenance flag is set").build();
}
return b.up().build();
}
private <T> int getLimit(MongoQuery<T> q) {
return q.getLimit() > 0 ? Math.min(q.getLimit(), defaultMax) : defaultLimit;
}
private <T> ReactiveMongoCollection<T> getCollection(Class<T> type) {
return mongoClient.getDatabase(databaseName).getCollection(DtoTableNames.getTableName(type), type);
}
}
/*
* Copyright (C) 2019 Eclipse Foundation and others.
*
* This program and the accompanying materials are made
* available under the terms of the Eclipse Public License 2.0
* which is available at https://www.eclipse.org/legal/epl-2.0/
*
* SPDX-License-Identifier: EPL-2.0
*/
package org.eclipsefoundation.marketplace.dao.impl;
import java.io.IOException;
import java.util.List;
import java.util.Objects;
import java.util.stream.Collectors;
import javax.annotation.PostConstruct;
import javax.enterprise.context.ApplicationScoped;
import org.apache.solr.client.solrj.SolrQuery;
import org.apache.solr.client.solrj.SolrServerException;
import org.apache.solr.client.solrj.impl.HttpSolrClient;
import org.apache.solr.client.solrj.impl.XMLResponseParser;
import org.apache.solr.client.solrj.response.QueryResponse;
import org.apache.solr.client.solrj.response.UpdateResponse;
import org.apache.solr.common.SolrDocument;
import org.apache.solr.common.SolrException;
import org.apache.solr.common.SolrInputDocument;
import org.apache.solr.common.util.NamedList;
import org.eclipse.microprofile.config.inject.ConfigProperty;
import org.eclipsefoundation.marketplace.dao.SolrDao;
import org.eclipsefoundation.marketplace.dao.mapper.SolrBeanMapper;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* Default implementation of the Solr DAO interface. Creates a reusable client
* object to maintain sessions and latches to a single Solr core.
*
* @author Martin Lowe
*/
@ApplicationScoped
public class DefaultSolrDao implements SolrDao {
private static final Logger LOGGER = LoggerFactory.getLogger(DefaultSolrDao.class);
@ConfigProperty(name = "solr.url.base")
String baseUrl;
@ConfigProperty(name = "solr.core")
String core;
private HttpSolrClient client;
/**
* Initializes the Solr client object with the injected properties from
* micro-profile. The parser is set manually as by default there is none set.
* This doesn't affect the data returned, only how it is serialized.
*/
@PostConstruct
public void init() {
LOGGER.debug("Creating Solr client for {}/{}", baseUrl, core);
this.client = new HttpSolrClient.Builder(baseUrl + '/' + core).build();
this.client.setParser(new XMLResponseParser());
}
@Override
public <T, M extends SolrBeanMapper<T>> List<T> get(SolrQuery q, M mapper) {
try {
// query solr for documents using the below query
QueryResponse r = client.query(q);
List<SolrDocument> docs = r.getResults();
// log the data to trace, to not burden the logs
if (LOGGER.isTraceEnabled()) {
LOGGER.trace("Found {} results for query {}", docs.size(), q.toQueryString());
}
// convert the solr documents using the bean mappers and return
return docs.stream().map(mapper::toBean).collect(Collectors.toList());
} catch (SolrServerException e) {
throw new SolrException(SolrException.ErrorCode.SERVER_ERROR, "Error communicating with Solr ", e);
} catch (IOException e) {
throw new SolrException(SolrException.ErrorCode.SERVER_ERROR,
"Error while streaming results for current query: " + q.toString(), e);
}
}
@Override
public <T, M extends SolrBeanMapper<T>> void add(List<T> beans, M mapper) {
// create a list of documents to upload to Solr
List<SolrInputDocument> solrDocuments = beans.stream().map(mapper::toDocument).filter(Objects::nonNull)
.collect(Collectors.toList());
try {
// update or add the given records
UpdateResponse response = client.add(solrDocuments);
NamedList<Object> responseFields = response.getResponse();
// if there was an error that didn't cause an exception, throw one
if (responseFields.get("error") != null) {
throw new SolrException(SolrException.ErrorCode.SERVER_ERROR,
"Error encountered while adding documents: " + responseFields.get("message"));
}
// commit the documents to be indexed
client.commit();
// trace time taken + number of entries updated
if (LOGGER.isTraceEnabled()) {
LOGGER.trace("Operation took {}ms to add/update {} record(s)", response.getElapsedTime(), beans.size());
}
} catch (SolrServerException e) {
throw new SolrException(SolrException.ErrorCode.SERVER_ERROR, "Error communicating with Solr ", e);
} catch (IOException e) {
throw new SolrException(SolrException.ErrorCode.SERVER_ERROR, "Error while streaming data to Solr", e);
}
}
@Override
public long count(SolrQuery q) {
try {
// override the row count to 0. Result counts are included as metadata, and shrinks the payload
q.setRows(0);
// query solr for documents using the below query
QueryResponse r = client.query(q);
// return the count of
return r.getResults().getNumFound();
} catch (SolrServerException e) {
throw new SolrException(SolrException.ErrorCode.SERVER_ERROR, "Error communicating with Solr ", e);
} catch (IOException e) {
throw new SolrException(SolrException.ErrorCode.SERVER_ERROR,
"Error while streaming results for current query: " + q.toString(), e);
}
}
@Override
public int delete(SolrQuery q) {
throw new UnsupportedOperationException("Deleting is not yet supported");
}
/**
* @return the baseUrl
*/
public String getBaseUrl() {
return baseUrl;
}
/**
* @param baseUrl the baseUrl to set
*/
public void setBaseUrl(String baseUrl) {
this.baseUrl = baseUrl;
}
/**
* @return the core
*/
public String getCore() {
return core;
}
/**
* @param core the core to set
*/
public void setCore(String core) {
this.core = core;
}
}
/*
* Copyright (C) 2019 Eclipse Foundation and others.
*
* This program and the accompanying materials are made
* available under the terms of the Eclipse Public License 2.0
* which is available at https://www.eclipse.org/legal/epl-2.0/
*
* SPDX-License-Identifier: EPL-2.0
*/
package org.eclipsefoundation.marketplace.dao.mapper;
import static org.eclipsefoundation.marketplace.helper.SolrHelper.addInputField;
import static org.eclipsefoundation.marketplace.helper.SolrHelper.setListField;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
import org.apache.solr.common.SolrDocument;
import org.apache.solr.common.SolrInputDocument;
import org.apache.solr.common.SolrInputField;
import org.eclipsefoundation.marketplace.dto.Listing;
import org.eclipsefoundation.marketplace.namespace.SolrFieldNames;
/**
* Mapping class for handling the transform between the Listing class and
* SolrInputDocuments.
*
* @author Martin Lowe
*/
public class ListingMapper implements SolrBeanMapper<Listing> {
@Override
public Listing toBean(SolrDocument doc) {
Listing out = new Listing();
out.setId((String) doc.getFieldValue(SolrFieldNames.DOCID));
out.setListingId((long) doc.getFieldValue(SolrFieldNames.LISTING_ID));
out.setTitle((String) doc.getFieldValue(SolrFieldNames.LISTING_TITLE));
out.setUrl((String) doc.getFieldValue(SolrFieldNames.LISTING_URL));
out.setSupportUrl((String) doc.getFieldValue(SolrFieldNames.SUPPORT_PAGE_URL));
out.setHomePageUrl((String) doc.getFieldValue(SolrFieldNames.HOME_PAGE_URL));
out.setVersion((String) doc.getFieldValue(SolrFieldNames.LISTING_VERSION));
out.setEclipseVersion((String) doc.getFieldValue(SolrFieldNames.LISTING_ECLIPSE_VERSION));
out.setTeaser((String) doc.getFieldValue(SolrFieldNames.LISTING_TEASER));
out.setBody((String) doc.getFieldValue(SolrFieldNames.LISTING_BODY));
out.setOwner((String) doc.getFieldValue(SolrFieldNames.LISTING_OWNER));
out.setStatus((String) doc.getFieldValue(SolrFieldNames.LISTING_STATUS));
out.setCompanyName((String) doc.getFieldValue(SolrFieldNames.LISTING_COMPANY_NAME));
out.setInstallsRecent((long) doc.getFieldValue(SolrFieldNames.RECENT_NSTALLS));
out.setInstallsTotal((long) doc.getFieldValue(SolrFieldNames.TOTAL_NSTALLS));
// check that it exists first, as non-existant fields throw exceptions
if (doc.containsKey(SolrFieldNames.PLATFORMS)) {
setListField(doc.getFieldValue(SolrFieldNames.PLATFORMS), out::setPlatforms);
}
if (doc.containsKey(SolrFieldNames.LICENSE_TYPE)) {
setListField(doc.getFieldValue(SolrFieldNames.LICENSE_TYPE), out::setLicenseType);
}
if (doc.containsKey(SolrFieldNames.INSTALLABLE_UNITS)) {
setListField(doc.getFieldValue(SolrFieldNames.INSTALLABLE_UNITS), out::setInstallableUnits);
}
if (doc.containsKey(SolrFieldNames.MARKETPLACE_FAVORITES)) {
out.setFavoriteCount((long) doc.getFieldValue(SolrFieldNames.MARKETPLACE_FAVORITES));
}
// convert date to epoch milli
Date creationDate = (Date) doc.getFieldValue(SolrFieldNames.CREATION_DATE);
out.setCreationDate(creationDate.toInstant().toEpochMilli());
Date updateDate = (Date) doc.getFieldValue(SolrFieldNames.UPDATE_DATE);
out.setUpdateDate(updateDate.toInstant().toEpochMilli());
// handle int to boolean conversion, where 1 is true
boolean isFoundationMember = (long) doc.getFieldValue(SolrFieldNames.FOUNDATION_MEMBER_FLAG) == 1;
out.setFoundationMember(isFoundationMember);
return out;
}
@Override
public SolrInputDocument toDocument(Listing document) {
Map<String, SolrInputField> fields = new HashMap<>();
addInputField(SolrFieldNames.DOCID, document.getId(), fields);
addInputField(SolrFieldNames.LISTING_ID, document.getListingId(), fields);
addInputField(SolrFieldNames.LISTING_TITLE, document.getTitle(), fields);
addInputField(SolrFieldNames.LISTING_URL, document.getUrl(), fields);
addInputField(SolrFieldNames.SUPPORT_PAGE_URL, document.getSupportUrl(), fields);
addInputField(SolrFieldNames.HOME_PAGE_URL, document.getHomePageUrl(), fields);
addInputField(SolrFieldNames.LISTING_VERSION, document.getVersion(), fields);
addInputField(SolrFieldNames.LISTING_ECLIPSE_VERSION, document.getEclipseVersion(), fields);
addInputField(SolrFieldNames.LISTING_BODY, document.getTeaser(), fields);
addInputField(SolrFieldNames.LISTING_TEASER, document.getBody(), fields);
addInputField(SolrFieldNames.MARKETPLACE_FAVORITES, document.getFavoriteCount(), fields);
addInputField(SolrFieldNames.RECENT_NSTALLS, document.getInstallsRecent(), fields);
addInputField(SolrFieldNames.TOTAL_NSTALLS, document.getInstallsTotal(), fields);
addInputField(SolrFieldNames.LICENSE_TYPE, document.getLicenseType(), fields);
addInputField(SolrFieldNames.PLATFORMS, document.getPlatforms(), fields);
addInputField(SolrFieldNames.INSTALLABLE_UNITS, document.getInstallableUnits(), fields);
addInputField(SolrFieldNames.LISTING_OWNER, document.getOwner(), fields);
addInputField(SolrFieldNames.LISTING_STATUS, document.getStatus(), fields);
addInputField(SolrFieldNames.LISTING_COMPANY_NAME, document.getCompanyName(), fields);
// convert epoch milli to date
addInputField(SolrFieldNames.UPDATE_DATE, new Date(document.getUpdateDate()), fields);
addInputField(SolrFieldNames.CREATION_DATE, new Date(document.getCreationDate()), fields);
// handle boolean to int conversion, where 1 is true
addInputField(SolrFieldNames.FOUNDATION_MEMBER_FLAG, document.isFoundationMember() ? 1 : 0, fields);
return new SolrInputDocument(fields);
}
}
/*
* Copyright (C) 2019 Eclipse Foundation and others.
*
* This program and the accompanying materials are made
* available under the terms of the Eclipse Public License 2.0
* which is available at https://www.eclipse.org/legal/epl-2.0/
*
* SPDX-License-Identifier: EPL-2.0
*/
package org.eclipsefoundation.marketplace.dao.mapper;
import java.util.Collection;
import java.util.Collections;
import java.util.Map;
import java.util.function.Function;
import java.util.stream.Collectors;
import org.apache.solr.common.SolrDocument;
import org.apache.solr.common.SolrInputDocument;
/**
* Mapping class for handling the transform between the RawSolrResult class and
* SolrInputDocuments.
*
* @author Martin Lowe
*/
public class RawSolrResultMapper implements SolrBeanMapper<Map<String, Collection<Object>>> {
@Override
public Map<String, Collection<Object>> toBean(SolrDocument doc) {
if (doc.getFieldValuesMap().isEmpty()) {
return Collections.emptyMap();
}
// due to Solr implementation of getFieldValuesMap, the values must be iterated
// over using keyset to pick up all fields, otherwise unsupported operation
// exception is thrown.
return doc.getFieldValuesMap().keySet().stream().collect(Collectors.toMap(Function.identity(), doc::getFieldValues));
}
@Override
public SolrInputDocument toDocument(Map<String, Collection<Object>> doc) {
throw new UnsupportedOperationException("Solr documents should never be directly inserted into the index");
}
}
/*
* Copyright (C) 2019 Eclipse Foundation and others.
*
* This program and the accompanying materials are made
* available under the terms of the Eclipse Public License 2.0
* which is available at https://www.eclipse.org/legal/epl-2.0/
*
* SPDX-License-Identifier: EPL-2.0
*/
package org.eclipsefoundation.marketplace.dao.mapper;
import org.apache.solr.common.SolrDocument;
import org.apache.solr.common.SolrInputDocument;
/**
* Interface for transformation vector of domain beans and Solr documents.
*
* @author Martin Lowe
*
* @param <T> the type of domain bean this mapper supports
*/
public interface SolrBeanMapper<T> {
/**
*
* @param doc
* @return
*/
T toBean(SolrDocument doc);
/**
* Transforms the bean of type T into a SolrInputDocument, which is the form
* that Solr accepts for adding documents back to its index.
*
* @param bean the domain bean to transform into an input document.
* @return an input document for Solr consumption
*/
SolrInputDocument toDocument(T bean);
}
/* Copyright (c) 2019 Eclipse Foundation and others.
* This program and the accompanying materials are made available
* under the terms of the Eclipse Public License 2.0
* which is available at http://www.eclipse.org/legal/epl-v20.html,
* SPDX-License-Identifier: EPL-2.0
*/
package org.eclipsefoundation.marketplace.dto;
import io.quarkus.runtime.annotations.RegisterForReflection;
/**
* Represents an author for listings.
*
* @author Martin Lowe
*/
@RegisterForReflection
public class Author {
private String username;
private String fullName;
public Author(String username, String fullName) {
this.username = username;
this.fullName = fullName;
}
public Author() {
// purposefully empty
}
/**
* @return the username
*/
public String getUsername() {
return username;
}
/**
* @param username the username to set
*/
public void setUsername(String username) {
this.username = username;
}
/**
* @return the fullName
*/
public String getFullName() {
return fullName;
}
/**
* @param fullName the fullName to set
*/
public void setFullName(String fullName) {
this.fullName = fullName;
}
}
......@@ -9,6 +9,11 @@
*/
package org.eclipsefoundation.marketplace.dto;
/**
* Represents a listing catalog.
*
* @author Martin Lowe
*/
public class Catalog {
private int id;
private String title;
......
......@@ -11,6 +11,12 @@ package org.eclipsefoundation.marketplace.dto;
import io.quarkus.runtime.annotations.RegisterForReflection;
/**
* Represents a listing category in the marketplace
*
* @author Martin Lowe
*
*/
@RegisterForReflection
public class Category {
private int id;
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment