diff --git a/.dockerignore b/.dockerignore
index 1dff951bc..5ae5188a0 100644
--- a/.dockerignore
+++ b/.dockerignore
@@ -1,5 +1,9 @@
+.github
.idea
-src
+LICENSE
+README.ms
+docs
target/**
!target/fdp-spring-boot.jar
!target/classes/application-production.yml
+nb-configuration.xml
diff --git a/.github/dependabot.yml b/.github/dependabot.yml
new file mode 100644
index 000000000..626dd882e
--- /dev/null
+++ b/.github/dependabot.yml
@@ -0,0 +1,7 @@
+version: 2
+updates:
+ - package-ecosystem: "maven"
+ directory: "/"
+ schedule:
+ interval: "daily"
+ target-branch: "develop"
diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml
index 97ce690fd..1c8a6ef68 100644
--- a/.github/workflows/main.yml
+++ b/.github/workflows/main.yml
@@ -14,9 +14,9 @@ jobs:
PRIVATE_IMAGE: ${{ secrets.PRIVATE_REGISTRY_URL }}/fairdatapoint
TAG_DEVELOP: develop
TAG_LATEST: latest
- JDK_VERSION: 11
- JDK_FILE: openjdk-14.0.1_linux-x64_bin.tar.gz
- JDK_URL: https://download.java.net/java/GA/jdk14.0.1/664493ef4a6946b186ff29eb326336a2/7/GPL/openjdk-14.0.1_linux-x64_bin.tar.gz
+ JDK_VERSION: 15
+ JDK_FILE: openjdk-15.0.2_linux-x64_bin.tar.gz
+ JDK_URL: https://download.java.net/java/GA/jdk15.0.2/0d1cfde4252546c6931946de8db48ee2/7/GPL/openjdk-15.0.2_linux-x64_bin.tar.gz
services:
mongo:
@@ -48,7 +48,7 @@ jobs:
key: ${{ env.JDK_FILE }}
# (2) -> Prepare Java
- - name: Download Oracle JDK
+ - name: Download JDK
run: |
if [ ! -f ~/jdk/$JDK_FILE ]; then
wget --quiet $JDK_URL -O ~/jdk/$JDK_FILE
@@ -56,9 +56,9 @@ jobs:
cp ~/jdk/$JDK_FILE .
- name: Setup Java
- uses: actions/setup-java@master
+ uses: actions/setup-java@v1
with:
- version: ${{ env.JDK_VERSION }}
+ java-version: ${{ env.JDK_VERSION }}
jdkFile: ${{ env.JDK_FILE }}
- name: Verify Maven and Java
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 2ea850b0e..80770f2ea 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -2,11 +2,29 @@
All notable changes to this project will be documented in this file.
-The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
-and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
+The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres
+to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
## [Unreleased]
+## [1.8.0]
+
+### Added
+
+- Denylist for FDP Index pings
+- Endpoints for managing Index settings from [Client]
+
+### Changed
+
+- Upgrade Java JDK from 14 to 15
+- Rate limits use forwarded IP by proxy based on config
+- Index settings are moved to the database
+- Admin trigger now accepts the same DTO as ping
+
+### Fixed
+
+- Fix metadata not found error
+
## [1.7.0]
### Added
@@ -155,4 +173,7 @@ The first release of reference FAIR Data Point implementation.
[1.4.0]: /../../tree/v1.4.0
[1.5.0]: /../../tree/v1.5.0
[1.6.0]: /../../tree/v1.6.0
+
[1.7.0]: /../../tree/v1.7.0
+
+[1.8.0]: /../../tree/v1.8.0
diff --git a/Dockerfile b/Dockerfile
index 7f4f71564..863d920fd 100644
--- a/Dockerfile
+++ b/Dockerfile
@@ -21,7 +21,7 @@
# THE SOFTWARE.
#
-FROM openjdk:14-jdk-slim
+FROM openjdk:15-jdk-slim
WORKDIR /fdp
diff --git a/Dockerfile.build b/Dockerfile.build
new file mode 100644
index 000000000..5b58330de
--- /dev/null
+++ b/Dockerfile.build
@@ -0,0 +1,42 @@
+#
+# The MIT License
+# Copyright © 2017 DTL
+#
+# Permission is hereby granted, free of charge, to any person obtaining a copy
+# of this software and associated documentation files (the "Software"), to deal
+# in the Software without restriction, including without limitation the rights
+# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+# copies of the Software, and to permit persons to whom the Software is
+# furnished to do so, subject to the following conditions:
+#
+# The above copyright notice and this permission notice shall be included in
+# all copies or substantial portions of the Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+# THE SOFTWARE.
+#
+################################################################################
+# BUILD STAGE
+FROM maven:3-openjdk-15 as builder
+
+WORKDIR /builder
+
+ADD . /builder
+
+RUN mvn --quiet -B -U --fail-fast -DskipTests package
+
+################################################################################
+# RUN STAGE
+FROM openjdk:15-jdk-slim
+
+WORKDIR /fdp
+
+COPY --from=builder /builder/target/fdp-spring-boot.jar /fdp/app.jar
+COPY --from=builder /builder/target/classes/application-production.yml /fdp/application.yml
+
+ENTRYPOINT java -jar app.jar --spring.profiles.active=production --spring.config.location=classpath:/application.yml,classpath:/application-production.yml,file:/fdp/application.yml
diff --git a/README.md b/README.md
index d8349e5a3..338e0556b 100644
--- a/README.md
+++ b/README.md
@@ -20,13 +20,13 @@ More information about FDP and how to deploy can be found at [FDP Deployment Doc
**Stack:**
- - **Java** (minimally JDK 14, or higher)
- - **Maven** (recommended 3.2.5 or higher)
- - **Docker** (recommended 17.09.0-ce or higher) - *for build of production image*
+ - **Java** (minimal: JDK 15)
+ - **Maven** (recommended: 3.2.5 or higher)
+ - **Docker** (recommended: 17.09.0-ce or higher) - *for build of production image*
### Build & Run
-To run the application, a mongodb instance is required to be running. To configure the mongodb address, instruct spring-boot to use the `development` profile. Run these commands from the root of the project.
+To run the application, a MongoDB instance is required to be running. To configure the mongodb address, instruct spring-boot to use the `development` profile. Run these commands from the root of the project.
```bash
$ mvn spring-boot:run -Dspring-boot.run.profiles=development
@@ -40,7 +40,7 @@ $ mvn spring-boot:run
### Run tests
-Run these commands from the root of the project
+Run these commands from the root of the project:
```bash
$ mvn test
@@ -48,7 +48,7 @@ $ mvn test
### Package the application
-Run these commands from the root of the project
+Run these commands from the root of the project:
```bash
$ mvn package
@@ -56,10 +56,18 @@ $ mvn package
### Create a Docker image
-Run these commands from the root of the project
+Run these commands from the root of the project (requires building jar file using `mvn package`):
```bash
-$ docker build -t fairdata/fairdatapoint .
+$ docker build -t fairdatapoint:local .
+```
+
+### Build using Docker
+
+If you do not have Java and Maven locally, you can build the Docker image using Docker (instead of using locally built jar file):
+
+```bash
+$ docker build -f Dockefile.build -t fairdatapoint:local .
```
## Security
diff --git a/pom.xml b/pom.xml
index 42b3f16cc..2fd5585fb 100644
--- a/pom.xml
+++ b/pom.xml
@@ -10,7 +10,7 @@
nl.dtls
fairdatapoint
- 1.7.0
+ 1.8.0
jar
FairDataPoint
@@ -41,8 +41,8 @@
UTF-8
- 14
- 14
+ 15
+ 15
1.1.0.RELEASE
@@ -54,15 +54,15 @@
1.2.3
3.0.0
1.4.9
- 0.10.5
- 1.18.10
+ 0.11.2
+ 1.18.18
3.0
0.7.6.201602180812
4.3.0
2.3.1
- 2.2.4
+ 4.0.3
0.2.0
@@ -315,6 +315,21 @@
false
+
+ org.apache.maven.plugins
+ maven-surefire-plugin
+
+
+ **/*.java
+
+
+ **/*Fixtures.java
+ **/*Config.java
+ **/Common.java
+ **/common/*.java
+
+
+
com.github.kburger
rdf4j-generator-maven-plugin
diff --git a/src/main/java/nl/dtls/fairdatapoint/api/controller/index/AdminController.java b/src/main/java/nl/dtls/fairdatapoint/api/controller/index/AdminController.java
index ebbe66b6c..ad4886a65 100644
--- a/src/main/java/nl/dtls/fairdatapoint/api/controller/index/AdminController.java
+++ b/src/main/java/nl/dtls/fairdatapoint/api/controller/index/AdminController.java
@@ -24,7 +24,9 @@
import io.swagger.annotations.ApiOperation;
import lombok.extern.log4j.Log4j2;
+import nl.dtls.fairdatapoint.api.dto.index.ping.PingDTO;
import nl.dtls.fairdatapoint.entity.index.event.Event;
+import nl.dtls.fairdatapoint.service.UtilityService;
import nl.dtls.fairdatapoint.service.index.event.EventService;
import nl.dtls.fairdatapoint.service.index.webhook.WebhookService;
import org.springframework.beans.factory.annotation.Autowired;
@@ -33,6 +35,7 @@
import org.springframework.web.bind.annotation.*;
import javax.servlet.http.HttpServletRequest;
+import javax.validation.Valid;
import java.util.UUID;
@Log4j2
@@ -40,6 +43,9 @@
@RequestMapping("/index/admin")
public class AdminController {
+ @Autowired
+ private UtilityService utilityService;
+
@Autowired
private EventService eventService;
@@ -50,9 +56,20 @@ public class AdminController {
@PostMapping("/trigger")
@PreAuthorize("hasRole('ADMIN')")
@ResponseStatus(HttpStatus.NO_CONTENT)
- public void triggerMetadataRetrieve(@RequestParam(required = false) String clientUrl, HttpServletRequest request) {
- log.info("Received ping from {}", request.getRemoteAddr());
- final Event event = eventService.acceptAdminTrigger(request, clientUrl);
+ public void triggerMetadataRetrieve(@RequestBody @Valid PingDTO reqDto, HttpServletRequest request) {
+ log.info("Received ping from {}", utilityService.getRemoteAddr(request));
+ final Event event = eventService.acceptAdminTrigger(request, reqDto);
+ webhookService.triggerWebhooks(event);
+ eventService.triggerMetadataRetrieval(event);
+ }
+
+ @ApiOperation(value = "trigger-all", hidden = true)
+ @PostMapping("/trigger-all")
+ @PreAuthorize("hasRole('ADMIN')")
+ @ResponseStatus(HttpStatus.NO_CONTENT)
+ public void triggerMetadataRetrieveAll(HttpServletRequest request) {
+ log.info("Received ping from {}", utilityService.getRemoteAddr(request));
+ final Event event = eventService.acceptAdminTriggerAll(request);
webhookService.triggerWebhooks(event);
eventService.triggerMetadataRetrieval(event);
}
@@ -62,7 +79,7 @@ public void triggerMetadataRetrieve(@RequestParam(required = false) String clien
@PreAuthorize("hasRole('ADMIN')")
@ResponseStatus(HttpStatus.NO_CONTENT)
public void webhookPing(@RequestParam(required = true) UUID webhook, HttpServletRequest request) {
- log.info("Received webhook {} ping trigger from {}", webhook, request.getRemoteAddr());
+ log.info("Received webhook {} ping trigger from {}", webhook, utilityService.getRemoteAddr(request));
final Event event = webhookService.handleWebhookPing(request, webhook);
webhookService.triggerWebhooks(event);
}
diff --git a/src/main/java/nl/dtls/fairdatapoint/api/controller/index/IndexEntryController.java b/src/main/java/nl/dtls/fairdatapoint/api/controller/index/IndexEntryController.java
index 55ea3ac84..555943054 100644
--- a/src/main/java/nl/dtls/fairdatapoint/api/controller/index/IndexEntryController.java
+++ b/src/main/java/nl/dtls/fairdatapoint/api/controller/index/IndexEntryController.java
@@ -25,18 +25,15 @@
import nl.dtls.fairdatapoint.api.dto.index.entry.IndexEntryDTO;
import nl.dtls.fairdatapoint.api.dto.index.entry.IndexEntryDetailDTO;
import nl.dtls.fairdatapoint.api.dto.index.entry.IndexEntryInfoDTO;
-import nl.dtls.fairdatapoint.service.index.entry.IndexEntryMapper;
import nl.dtls.fairdatapoint.service.index.entry.IndexEntryService;
-import nl.dtls.fairdatapoint.service.index.event.EventService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
+import org.springframework.http.HttpStatus;
import org.springframework.web.bind.annotation.*;
import java.util.List;
import java.util.Optional;
-import java.util.stream.Collectors;
-import java.util.stream.StreamSupport;
@RestController
@RequestMapping("/index/entries")
@@ -45,27 +42,26 @@ public class IndexEntryController {
@Autowired
private IndexEntryService service;
- @Autowired
- private EventService eventService;
-
- @Autowired
- private IndexEntryMapper mapper;
-
@GetMapping("")
public Page getEntriesPage(Pageable pageable,
@RequestParam(required = false, defaultValue = "") String state) {
- return service.getEntriesPage(pageable, state).map(mapper::toDTO);
+ return service.getEntriesPageDTOs(pageable, state);
}
@RequestMapping(value = "/{uuid}", method = RequestMethod.GET)
public Optional getEntry(@PathVariable final String uuid) {
- return service.getEntry(uuid).map(entry -> mapper.toDetailDTO(entry,
- eventService.getEvents(entry.getUuid())));
+ return service.getEntryDetailDTO(uuid);
+ }
+
+ @RequestMapping(value = "/{uuid}", method = RequestMethod.DELETE)
+ @ResponseStatus(HttpStatus.NO_CONTENT)
+ public void deleteEntry(@PathVariable final String uuid) {
+ service.deleteEntry(uuid);
}
@GetMapping("/all")
public List getEntriesAll() {
- return StreamSupport.stream(service.getAllEntries().spliterator(), true).map(mapper::toDTO).collect(Collectors.toList());
+ return service.getAllEntriesAsDTOs();
}
@GetMapping("/info")
diff --git a/src/main/java/nl/dtls/fairdatapoint/api/controller/index/PingController.java b/src/main/java/nl/dtls/fairdatapoint/api/controller/index/PingController.java
index 9337d12de..51aa5afea 100644
--- a/src/main/java/nl/dtls/fairdatapoint/api/controller/index/PingController.java
+++ b/src/main/java/nl/dtls/fairdatapoint/api/controller/index/PingController.java
@@ -26,6 +26,7 @@
import nl.dtls.fairdatapoint.api.dto.index.ping.PingDTO;
import nl.dtls.fairdatapoint.database.rdf.repository.exception.MetadataRepositoryException;
import nl.dtls.fairdatapoint.entity.index.event.Event;
+import nl.dtls.fairdatapoint.service.UtilityService;
import nl.dtls.fairdatapoint.service.index.event.EventService;
import nl.dtls.fairdatapoint.service.index.harvester.HarvesterService;
import nl.dtls.fairdatapoint.service.index.webhook.WebhookService;
@@ -33,6 +34,7 @@
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus;
+import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;
import javax.servlet.http.HttpServletRequest;
@@ -51,6 +53,9 @@ public class PingController {
@Autowired
private HarvesterService harvesterService;
+
+ @Autowired
+ private UtilityService utilityService;
@ApiOperation(
value = "Ping payload with FAIR Data Point info",
@@ -59,12 +64,13 @@ public class PingController {
)
@RequestMapping(method = RequestMethod.POST, consumes = "application/json", produces = "application/json")
@ResponseStatus(HttpStatus.NO_CONTENT)
- public void receivePing(@RequestBody @Valid PingDTO reqDto, HttpServletRequest request) throws MetadataRepositoryException {
- logger.info("Received ping from {}", request.getRemoteAddr());
+ public ResponseEntity receivePing(@RequestBody @Valid PingDTO reqDto, HttpServletRequest request) throws MetadataRepositoryException {
+ logger.info("Received ping from {}", utilityService.getRemoteAddr(request));
final Event event = eventService.acceptIncomingPing(reqDto, request);
logger.info("Triggering metadata retrieval for {}", event.getRelatedTo().getClientUrl());
eventService.triggerMetadataRetrieval(event);
harvesterService.harvest(reqDto.getClientUrl());
webhookService.triggerWebhooks(event);
+ return new ResponseEntity<>(HttpStatus.NO_CONTENT);
}
}
diff --git a/src/main/java/nl/dtls/fairdatapoint/api/controller/index/SettingsController.java b/src/main/java/nl/dtls/fairdatapoint/api/controller/index/SettingsController.java
new file mode 100644
index 000000000..1086ae6dd
--- /dev/null
+++ b/src/main/java/nl/dtls/fairdatapoint/api/controller/index/SettingsController.java
@@ -0,0 +1,61 @@
+/**
+ * The MIT License
+ * Copyright © 2017 DTL
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+package nl.dtls.fairdatapoint.api.controller.index;
+
+import nl.dtls.fairdatapoint.api.dto.index.settings.IndexSettingsDTO;
+import nl.dtls.fairdatapoint.api.dto.index.settings.IndexSettingsUpdateDTO;
+import nl.dtls.fairdatapoint.service.index.settings.IndexSettingsService;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.security.access.prepost.PreAuthorize;
+import org.springframework.web.bind.annotation.RequestBody;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RequestMethod;
+import org.springframework.web.bind.annotation.RestController;
+
+import javax.validation.Valid;
+
+@RestController
+@RequestMapping("/index/settings")
+public class SettingsController {
+
+ @Autowired
+ private IndexSettingsService indexSettingsService;
+
+ @RequestMapping(method = RequestMethod.GET)
+ @PreAuthorize("hasRole('ADMIN')")
+ public IndexSettingsDTO getIndexSettings() {
+ return indexSettingsService.getCurrentSettings();
+ }
+
+ @RequestMapping(method = RequestMethod.PUT)
+ @PreAuthorize("hasRole('ADMIN')")
+ public IndexSettingsDTO updateIndexSettings(@RequestBody @Valid IndexSettingsUpdateDTO reqDto) {
+ return indexSettingsService.updateSettings(reqDto);
+ }
+
+ @RequestMapping(method = RequestMethod.DELETE)
+ @PreAuthorize("hasRole('ADMIN')")
+ public IndexSettingsDTO resetIndexSettings() {
+ return indexSettingsService.resetSettings();
+ }
+}
diff --git a/src/main/java/nl/dtls/fairdatapoint/api/controller/metadata/GenericMetaController.java b/src/main/java/nl/dtls/fairdatapoint/api/controller/metadata/GenericMetaController.java
index bcde97da9..332fa18b9 100644
--- a/src/main/java/nl/dtls/fairdatapoint/api/controller/metadata/GenericMetaController.java
+++ b/src/main/java/nl/dtls/fairdatapoint/api/controller/metadata/GenericMetaController.java
@@ -91,7 +91,7 @@ public MetaDTO getMeta(HttpServletRequest request) throws MetadataServiceExcepti
MemberDTO member = oMember.orElse(new MemberDTO(null, null));
// 5. Get state
- MetaStateDTO state = metadataStateService.getState(model, rd);
+ MetaStateDTO state = metadataStateService.getState(entityUri, model, rd);
return new MetaDTO(member, state);
}
diff --git a/src/main/java/nl/dtls/fairdatapoint/api/dto/index/settings/IndexSettingsDTO.java b/src/main/java/nl/dtls/fairdatapoint/api/dto/index/settings/IndexSettingsDTO.java
new file mode 100644
index 000000000..448d4969d
--- /dev/null
+++ b/src/main/java/nl/dtls/fairdatapoint/api/dto/index/settings/IndexSettingsDTO.java
@@ -0,0 +1,45 @@
+/**
+ * The MIT License
+ * Copyright © 2017 DTL
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+package nl.dtls.fairdatapoint.api.dto.index.settings;
+
+import lombok.AllArgsConstructor;
+import lombok.Getter;
+import lombok.NoArgsConstructor;
+import lombok.Setter;
+
+import javax.validation.constraints.NotNull;
+
+@NoArgsConstructor
+@AllArgsConstructor
+@Getter
+@Setter
+public class IndexSettingsDTO {
+ @NotNull
+ private IndexSettingsRetrievalDTO retrieval;
+
+ @NotNull
+ private IndexSettingsPingDTO ping;
+
+ @NotNull
+ private Boolean isDefault;
+}
diff --git a/src/main/java/nl/dtls/fairdatapoint/api/dto/index/settings/IndexSettingsPingDTO.java b/src/main/java/nl/dtls/fairdatapoint/api/dto/index/settings/IndexSettingsPingDTO.java
new file mode 100644
index 000000000..616e6826c
--- /dev/null
+++ b/src/main/java/nl/dtls/fairdatapoint/api/dto/index/settings/IndexSettingsPingDTO.java
@@ -0,0 +1,49 @@
+/**
+ * The MIT License
+ * Copyright © 2017 DTL
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+package nl.dtls.fairdatapoint.api.dto.index.settings;
+
+import lombok.AllArgsConstructor;
+import lombok.Getter;
+import lombok.NoArgsConstructor;
+import lombok.Setter;
+import nl.dtls.fairdatapoint.api.validator.ValidDuration;
+
+import javax.validation.constraints.NotNull;
+import java.util.List;
+
+@NoArgsConstructor
+@AllArgsConstructor
+@Getter
+@Setter
+public class IndexSettingsPingDTO {
+ @NotNull
+ @ValidDuration
+ private String validDuration;
+ @NotNull
+ @ValidDuration
+ private String rateLimitDuration;
+ @NotNull
+ private Integer rateLimitHits;
+ @NotNull
+ private List denyList;
+}
diff --git a/src/main/java/nl/dtls/fairdatapoint/api/dto/index/settings/IndexSettingsRetrievalDTO.java b/src/main/java/nl/dtls/fairdatapoint/api/dto/index/settings/IndexSettingsRetrievalDTO.java
new file mode 100644
index 000000000..d630da343
--- /dev/null
+++ b/src/main/java/nl/dtls/fairdatapoint/api/dto/index/settings/IndexSettingsRetrievalDTO.java
@@ -0,0 +1,45 @@
+/**
+ * The MIT License
+ * Copyright © 2017 DTL
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+package nl.dtls.fairdatapoint.api.dto.index.settings;
+
+import lombok.AllArgsConstructor;
+import lombok.Getter;
+import lombok.NoArgsConstructor;
+import lombok.Setter;
+import nl.dtls.fairdatapoint.api.validator.ValidDuration;
+
+import javax.validation.constraints.NotNull;
+
+@NoArgsConstructor
+@AllArgsConstructor
+@Getter
+@Setter
+public class IndexSettingsRetrievalDTO {
+ @NotNull
+ @ValidDuration
+ private String rateLimitWait;
+
+ @NotNull
+ @ValidDuration
+ private String timeout;
+}
diff --git a/src/main/java/nl/dtls/fairdatapoint/api/dto/index/settings/IndexSettingsUpdateDTO.java b/src/main/java/nl/dtls/fairdatapoint/api/dto/index/settings/IndexSettingsUpdateDTO.java
new file mode 100644
index 000000000..09905c30f
--- /dev/null
+++ b/src/main/java/nl/dtls/fairdatapoint/api/dto/index/settings/IndexSettingsUpdateDTO.java
@@ -0,0 +1,45 @@
+/**
+ * The MIT License
+ * Copyright © 2017 DTL
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+package nl.dtls.fairdatapoint.api.dto.index.settings;
+
+import lombok.AllArgsConstructor;
+import lombok.Getter;
+import lombok.NoArgsConstructor;
+import lombok.Setter;
+
+import javax.validation.Valid;
+import javax.validation.constraints.NotNull;
+
+@NoArgsConstructor
+@AllArgsConstructor
+@Getter
+@Setter
+public class IndexSettingsUpdateDTO {
+ @Valid
+ @NotNull
+ private IndexSettingsRetrievalDTO retrieval;
+
+ @Valid
+ @NotNull
+ private IndexSettingsPingDTO ping;
+}
diff --git a/src/main/java/nl/dtls/fairdatapoint/api/filter/LoggingFilter.java b/src/main/java/nl/dtls/fairdatapoint/api/filter/LoggingFilter.java
index 28801d60c..2840b7109 100644
--- a/src/main/java/nl/dtls/fairdatapoint/api/filter/LoggingFilter.java
+++ b/src/main/java/nl/dtls/fairdatapoint/api/filter/LoggingFilter.java
@@ -27,7 +27,9 @@
*/
package nl.dtls.fairdatapoint.api.filter;
+import nl.dtls.fairdatapoint.service.UtilityService;
import org.apache.logging.log4j.ThreadContext;
+import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpHeaders;
import org.springframework.stereotype.Component;
import org.springframework.web.filter.OncePerRequestFilter;
@@ -41,12 +43,15 @@
@Component
public class LoggingFilter extends OncePerRequestFilter {
+ @Autowired
+ private UtilityService utilityService;
+
@Override
public void doFilterInternal(final HttpServletRequest request,
final HttpServletResponse response, final FilterChain fc)
throws IOException, ServletException {
- ThreadContext.put("ipAddress", request.getRemoteAddr());
+ ThreadContext.put("ipAddress", utilityService.getRemoteAddr(request));
ThreadContext.put("responseStatus", String.valueOf(response.getStatus()));
ThreadContext.put("requestMethod", request.getMethod());
ThreadContext.put("requestURI", request.getRequestURI());
diff --git a/src/main/java/nl/dtls/fairdatapoint/api/validator/DurationValidator.java b/src/main/java/nl/dtls/fairdatapoint/api/validator/DurationValidator.java
new file mode 100644
index 000000000..618dbd846
--- /dev/null
+++ b/src/main/java/nl/dtls/fairdatapoint/api/validator/DurationValidator.java
@@ -0,0 +1,47 @@
+/**
+ * The MIT License
+ * Copyright © 2017 DTL
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+package nl.dtls.fairdatapoint.api.validator;
+
+import javax.validation.ConstraintValidator;
+import javax.validation.ConstraintValidatorContext;
+
+import java.time.Duration;
+
+import static nl.dtls.fairdatapoint.util.ValueFactoryHelper.i;
+
+public class DurationValidator implements ConstraintValidator {
+
+ @Override
+ public void initialize(ValidDuration text) {
+ }
+
+ @Override
+ public boolean isValid(String text, ConstraintValidatorContext cxt) {
+ try {
+ Duration.parse(text);
+ return true;
+ } catch (Exception e) {
+ return false;
+ }
+ }
+}
diff --git a/src/main/java/nl/dtls/fairdatapoint/api/validator/ValidDuration.java b/src/main/java/nl/dtls/fairdatapoint/api/validator/ValidDuration.java
new file mode 100644
index 000000000..f46b8ac4e
--- /dev/null
+++ b/src/main/java/nl/dtls/fairdatapoint/api/validator/ValidDuration.java
@@ -0,0 +1,40 @@
+/**
+ * The MIT License
+ * Copyright © 2017 DTL
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+package nl.dtls.fairdatapoint.api.validator;
+
+import javax.validation.Constraint;
+import javax.validation.Payload;
+import java.lang.annotation.*;
+
+@Documented
+@Constraint(validatedBy = DurationValidator.class)
+@Target({ElementType.METHOD, ElementType.FIELD, ElementType.TYPE_USE})
+@Retention(RetentionPolicy.RUNTIME)
+public @interface ValidDuration {
+
+ String message() default "Invalid ISO-8601 duration";
+
+ Class>[] groups() default {};
+
+ Class extends Payload>[] payload() default {};
+}
diff --git a/src/main/java/nl/dtls/fairdatapoint/config/IndexConfig.java b/src/main/java/nl/dtls/fairdatapoint/config/IndexConfig.java
deleted file mode 100644
index 7a4e43c79..000000000
--- a/src/main/java/nl/dtls/fairdatapoint/config/IndexConfig.java
+++ /dev/null
@@ -1,55 +0,0 @@
-/**
- * The MIT License
- * Copyright © 2017 DTL
- *
- * Permission is hereby granted, free of charge, to any person obtaining a copy
- * of this software and associated documentation files (the "Software"), to deal
- * in the Software without restriction, including without limitation the rights
- * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
- * copies of the Software, and to permit persons to whom the Software is
- * furnished to do so, subject to the following conditions:
- *
- * The above copyright notice and this permission notice shall be included in
- * all copies or substantial portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
- * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
- * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
- * THE SOFTWARE.
- */
-package nl.dtls.fairdatapoint.config;
-
-import nl.dtls.fairdatapoint.entity.index.config.EventsConfig;
-import org.springframework.beans.factory.annotation.Value;
-import org.springframework.beans.factory.config.ConfigurableBeanFactory;
-import org.springframework.context.annotation.Bean;
-import org.springframework.context.annotation.Configuration;
-import org.springframework.context.annotation.Scope;
-
-import java.time.Duration;
-
-@Configuration
-public class IndexConfig {
-
- @Bean
- @Scope(ConfigurableBeanFactory.SCOPE_SINGLETON)
- public EventsConfig eventsConfig(
- @Value("${fdp-index.events.retrieval.rateLimitWait:PT10M}") String cfgRetrievalRateLimitWait,
- @Value("${fdp-index.events.retrieval.timeout:PT1M}") String cfgRetrievalTimeout,
- @Value("${fdp-index.events.ping.validDuration:P7D}") String cfgPingValidDuration,
- @Value("${fdp-index.events.ping.rateLimitDuration:PT6H}") String cfgPingRateLimitDuration,
- @Value("${fdp-index.events.ping.rateLimitHits:10}") int cfgPingRateLimitHits
- ) {
- return EventsConfig.builder()
- .retrievalRateLimitWait(Duration.parse(cfgRetrievalRateLimitWait))
- .retrievalTimeout(Duration.parse(cfgRetrievalTimeout))
- .pingValidDuration(Duration.parse(cfgPingValidDuration))
- .pingRateLimitDuration(Duration.parse(cfgPingRateLimitDuration))
- .pingRateLimitHits(cfgPingRateLimitHits)
- .build();
- }
-
-}
diff --git a/src/main/java/nl/dtls/fairdatapoint/config/SwaggerConfig.java b/src/main/java/nl/dtls/fairdatapoint/config/SwaggerConfig.java
index 159c72a68..7db81a37b 100644
--- a/src/main/java/nl/dtls/fairdatapoint/config/SwaggerConfig.java
+++ b/src/main/java/nl/dtls/fairdatapoint/config/SwaggerConfig.java
@@ -71,7 +71,7 @@ private ApiInfo apiInfo() {
" FAIR Data Point Specification" +
" " +
"
",
- "1.7.0",
+ "1.8.0",
"ATO",
new Contact("Luiz Bonino",
"https://github.com/FAIRDataTeam/FAIRDataPoint",
diff --git a/src/main/java/nl/dtls/fairdatapoint/config/WebConfig.java b/src/main/java/nl/dtls/fairdatapoint/config/WebConfig.java
index fb1c06d73..865b915aa 100644
--- a/src/main/java/nl/dtls/fairdatapoint/config/WebConfig.java
+++ b/src/main/java/nl/dtls/fairdatapoint/config/WebConfig.java
@@ -27,6 +27,7 @@
import nl.dtls.fairdatapoint.api.converter.ErrorConverter;
import nl.dtls.fairdatapoint.api.converter.RdfConverter;
import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;
@@ -88,5 +89,4 @@ public ObjectMapper objectMapper() {
mapper.disable(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES);
return mapper;
}
-
}
diff --git a/src/main/java/nl/dtls/fairdatapoint/database/mongo/repository/IndexSettingsRepository.java b/src/main/java/nl/dtls/fairdatapoint/database/mongo/repository/IndexSettingsRepository.java
new file mode 100644
index 000000000..01b672fb3
--- /dev/null
+++ b/src/main/java/nl/dtls/fairdatapoint/database/mongo/repository/IndexSettingsRepository.java
@@ -0,0 +1,33 @@
+/**
+ * The MIT License
+ * Copyright © 2017 DTL
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+package nl.dtls.fairdatapoint.database.mongo.repository;
+
+import nl.dtls.fairdatapoint.entity.index.settings.IndexSettings;
+import org.springframework.data.mongodb.repository.MongoRepository;
+import org.springframework.stereotype.Component;
+
+import java.util.Optional;
+
+public interface IndexSettingsRepository extends MongoRepository {
+ Optional findFirstBy();
+}
diff --git a/src/main/java/nl/dtls/fairdatapoint/entity/index/config/EventsConfig.java b/src/main/java/nl/dtls/fairdatapoint/entity/index/exception/PingDeniedException.java
similarity index 74%
rename from src/main/java/nl/dtls/fairdatapoint/entity/index/config/EventsConfig.java
rename to src/main/java/nl/dtls/fairdatapoint/entity/index/exception/PingDeniedException.java
index d42ef5eca..3fd6f1d0e 100644
--- a/src/main/java/nl/dtls/fairdatapoint/entity/index/config/EventsConfig.java
+++ b/src/main/java/nl/dtls/fairdatapoint/entity/index/exception/PingDeniedException.java
@@ -20,19 +20,13 @@
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
-package nl.dtls.fairdatapoint.entity.index.config;
+package nl.dtls.fairdatapoint.entity.index.exception;
-import lombok.Builder;
-import lombok.Data;
+import org.springframework.http.HttpStatus;
-import java.time.Duration;
+public class PingDeniedException extends IndexException {
-@Builder
-@Data
-public class EventsConfig {
- private final Duration retrievalRateLimitWait;
- private final Duration retrievalTimeout;
- private final Duration pingValidDuration;
- private final Duration pingRateLimitDuration;
- private final int pingRateLimitHits;
+ public PingDeniedException(String clientUrl) {
+ super("Client URL is denied: " + clientUrl, HttpStatus.FORBIDDEN);
+ }
}
diff --git a/src/main/java/nl/dtls/fairdatapoint/entity/index/settings/IndexSettings.java b/src/main/java/nl/dtls/fairdatapoint/entity/index/settings/IndexSettings.java
new file mode 100644
index 000000000..c3227bce6
--- /dev/null
+++ b/src/main/java/nl/dtls/fairdatapoint/entity/index/settings/IndexSettings.java
@@ -0,0 +1,68 @@
+/**
+ * The MIT License
+ * Copyright © 2017 DTL
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+package nl.dtls.fairdatapoint.entity.index.settings;
+
+import lombok.*;
+import org.bson.types.ObjectId;
+import org.springframework.data.annotation.Id;
+import org.springframework.data.mongodb.core.mapping.Document;
+
+import javax.validation.constraints.NotNull;
+import java.util.Objects;
+
+@Document
+@NoArgsConstructor
+@AllArgsConstructor
+@Getter
+@Setter
+@Builder(toBuilder = true)
+public class IndexSettings {
+ @Id
+ protected ObjectId id;
+
+ @NotNull
+ private IndexSettingsRetrieval retrieval;
+
+ @NotNull
+ private IndexSettingsPing ping;
+
+ public static IndexSettings getDefault() {
+ IndexSettings settings = new IndexSettings();
+ settings.setPing(IndexSettingsPing.getDefault());
+ settings.setRetrieval(IndexSettingsRetrieval.getDefault());
+ return settings;
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) return true;
+ if (o == null || getClass() != o.getClass()) return false;
+ IndexSettings that = (IndexSettings) o;
+ return retrieval.equals(that.retrieval) && ping.equals(that.ping);
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(retrieval, ping);
+ }
+}
diff --git a/src/main/java/nl/dtls/fairdatapoint/entity/index/settings/IndexSettingsPing.java b/src/main/java/nl/dtls/fairdatapoint/entity/index/settings/IndexSettingsPing.java
new file mode 100644
index 000000000..32370ee23
--- /dev/null
+++ b/src/main/java/nl/dtls/fairdatapoint/entity/index/settings/IndexSettingsPing.java
@@ -0,0 +1,59 @@
+/**
+ * The MIT License
+ * Copyright © 2017 DTL
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+package nl.dtls.fairdatapoint.entity.index.settings;
+
+import lombok.*;
+
+import javax.validation.constraints.NotNull;
+import java.time.Duration;
+import java.util.Collections;
+import java.util.List;
+
+@NoArgsConstructor
+@AllArgsConstructor
+@Getter
+@Setter
+@Builder(toBuilder = true)
+@EqualsAndHashCode
+public class IndexSettingsPing {
+ @NotNull
+ private Duration validDuration;
+
+ @NotNull
+ private Duration rateLimitDuration;
+
+ @NotNull
+ private Integer rateLimitHits;
+
+ @NotNull
+ private List denyList;
+
+ public static IndexSettingsPing getDefault() {
+ IndexSettingsPing ping = new IndexSettingsPing();
+ ping.setValidDuration(Duration.ofDays(7));
+ ping.setRateLimitDuration(Duration.ofHours(6));
+ ping.setRateLimitHits(10);
+ ping.setDenyList(Collections.singletonList("^(http|https)://localhost(:[0-9]+){0,1}.*$"));
+ return ping;
+ }
+}
diff --git a/src/main/java/nl/dtls/fairdatapoint/entity/index/settings/IndexSettingsRetrieval.java b/src/main/java/nl/dtls/fairdatapoint/entity/index/settings/IndexSettingsRetrieval.java
new file mode 100644
index 000000000..3aff145ca
--- /dev/null
+++ b/src/main/java/nl/dtls/fairdatapoint/entity/index/settings/IndexSettingsRetrieval.java
@@ -0,0 +1,49 @@
+/**
+ * The MIT License
+ * Copyright © 2017 DTL
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+package nl.dtls.fairdatapoint.entity.index.settings;
+
+import lombok.*;
+
+import javax.validation.constraints.NotNull;
+import java.time.Duration;
+
+@NoArgsConstructor
+@AllArgsConstructor
+@Getter
+@Setter
+@Builder(toBuilder = true)
+@EqualsAndHashCode
+public class IndexSettingsRetrieval {
+ @NotNull
+ private Duration rateLimitWait;
+
+ @NotNull
+ private Duration timeout;
+
+ public static IndexSettingsRetrieval getDefault() {
+ IndexSettingsRetrieval retrieval = new IndexSettingsRetrieval();
+ retrieval.setRateLimitWait(Duration.ofMinutes(10));
+ retrieval.setTimeout(Duration.ofMinutes(1));
+ return retrieval;
+ }
+}
diff --git a/src/main/java/nl/dtls/fairdatapoint/service/UtilityService.java b/src/main/java/nl/dtls/fairdatapoint/service/UtilityService.java
new file mode 100644
index 000000000..c9b59867a
--- /dev/null
+++ b/src/main/java/nl/dtls/fairdatapoint/service/UtilityService.java
@@ -0,0 +1,41 @@
+/**
+ * The MIT License
+ * Copyright © 2017 DTL
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+package nl.dtls.fairdatapoint.service;
+
+import nl.dtls.fairdatapoint.util.HttpUtil;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.stereotype.Service;
+
+import javax.servlet.http.HttpServletRequest;
+
+@Service
+public class UtilityService {
+
+ @Value("${instance.behindProxy:true}")
+ private Boolean behindProxy;
+
+ public String getRemoteAddr(HttpServletRequest request) {
+ return HttpUtil.getClientIpAddress(request, behindProxy);
+ }
+}
diff --git a/src/main/java/nl/dtls/fairdatapoint/service/dashboard/DashboardService.java b/src/main/java/nl/dtls/fairdatapoint/service/dashboard/DashboardService.java
index b1ce54197..84d676260 100644
--- a/src/main/java/nl/dtls/fairdatapoint/service/dashboard/DashboardService.java
+++ b/src/main/java/nl/dtls/fairdatapoint/service/dashboard/DashboardService.java
@@ -72,17 +72,18 @@ public class DashboardService {
public List getDashboard(IRI repositoryUri) throws MetadataServiceException {
ResourceDefinition rd = resourceDefinitionService.getByUrlPrefix("");
Model repository = metadataService.retrieve(repositoryUri);
- return getDashboardItem(repository, rd).getChildren();
+ return getDashboardItem(repositoryUri, repository, rd).getChildren();
}
- private DashboardItemDTO getDashboardItem(Model model, ResourceDefinition rd) throws MetadataServiceException {
- IRI metadataUri = getUri(model);
+ private DashboardItemDTO getDashboardItem(IRI metadataUri, Model model, ResourceDefinition rd) throws MetadataServiceException {
List children = new ArrayList<>();
for (ResourceDefinitionChild rdChild : rd.getChildren()) {
IRI relationUri = i(rdChild.getRelationUri());
for (org.eclipse.rdf4j.model.Value childUri : getObjectsBy(model, metadataUri, relationUri)) {
+ IRI childIri = i(childUri.stringValue());
DashboardItemDTO child = getDashboardItem(
- metadataService.retrieve(i(childUri.stringValue())),
+ childIri,
+ metadataService.retrieve(childIri),
resourceDefinitionCache.getByUuid(rdChild.getResourceDefinitionUuid())
);
children.add(child);
diff --git a/src/main/java/nl/dtls/fairdatapoint/service/index/entry/IndexEntryMapper.java b/src/main/java/nl/dtls/fairdatapoint/service/index/entry/IndexEntryMapper.java
index bb516da87..b8f1b7b16 100644
--- a/src/main/java/nl/dtls/fairdatapoint/service/index/entry/IndexEntryMapper.java
+++ b/src/main/java/nl/dtls/fairdatapoint/service/index/entry/IndexEntryMapper.java
@@ -25,7 +25,6 @@
import nl.dtls.fairdatapoint.api.dto.index.entry.IndexEntryDTO;
import nl.dtls.fairdatapoint.api.dto.index.entry.IndexEntryDetailDTO;
import nl.dtls.fairdatapoint.api.dto.index.entry.IndexEntryStateDTO;
-import nl.dtls.fairdatapoint.entity.index.config.EventsConfig;
import nl.dtls.fairdatapoint.entity.index.entry.IndexEntry;
import nl.dtls.fairdatapoint.entity.index.entry.IndexEntryState;
import nl.dtls.fairdatapoint.entity.index.event.Event;
@@ -40,27 +39,24 @@
@Service
public class IndexEntryMapper {
- @Autowired
- private EventsConfig eventsConfig;
-
@Autowired
private EventMapper eventMapper;
- public IndexEntryDTO toDTO(IndexEntry indexEntry) {
+ public IndexEntryDTO toDTO(IndexEntry indexEntry, Instant validThreshold) {
return new IndexEntryDTO(
indexEntry.getUuid(),
indexEntry.getClientUrl(),
- toStateDTO(indexEntry.getState(), indexEntry.getLastRetrievalTime()),
+ toStateDTO(indexEntry.getState(), indexEntry.getLastRetrievalTime(), validThreshold),
indexEntry.getRegistrationTime().toString(),
indexEntry.getModificationTime().toString()
);
}
- public IndexEntryDetailDTO toDetailDTO(IndexEntry indexEntry, Iterable events) {
+ public IndexEntryDetailDTO toDetailDTO(IndexEntry indexEntry, Iterable events, Instant validThreshold) {
return new IndexEntryDetailDTO(
indexEntry.getUuid(),
indexEntry.getClientUrl(),
- toStateDTO(indexEntry.getState(), indexEntry.getLastRetrievalTime()),
+ toStateDTO(indexEntry.getState(), indexEntry.getLastRetrievalTime(), validThreshold),
indexEntry.getCurrentMetadata(),
StreamSupport.stream(events.spliterator(), false)
.map(eventMapper::toDTO)
@@ -71,10 +67,10 @@ public IndexEntryDetailDTO toDetailDTO(IndexEntry indexEntry, Iterable ev
);
}
- public IndexEntryStateDTO toStateDTO(IndexEntryState state, Instant lastRetrievalTime) {
+ public IndexEntryStateDTO toStateDTO(IndexEntryState state, Instant lastRetrievalTime, Instant validThreshold) {
return switch (state) {
case Unknown -> IndexEntryStateDTO.UNKNOWN;
- case Valid -> lastRetrievalTime.isAfter(Instant.now().minus(eventsConfig.getPingValidDuration()))
+ case Valid -> lastRetrievalTime.isAfter(validThreshold)
? IndexEntryStateDTO.ACTIVE
: IndexEntryStateDTO.INACTIVE;
case Invalid -> IndexEntryStateDTO.INVALID;
diff --git a/src/main/java/nl/dtls/fairdatapoint/service/index/entry/IndexEntryService.java b/src/main/java/nl/dtls/fairdatapoint/service/index/entry/IndexEntryService.java
index 99dcd5bae..151030295 100644
--- a/src/main/java/nl/dtls/fairdatapoint/service/index/entry/IndexEntryService.java
+++ b/src/main/java/nl/dtls/fairdatapoint/service/index/entry/IndexEntryService.java
@@ -23,26 +23,30 @@
package nl.dtls.fairdatapoint.service.index.entry;
import lombok.extern.log4j.Log4j2;
+import nl.dtls.fairdatapoint.api.dto.index.entry.IndexEntryDTO;
+import nl.dtls.fairdatapoint.api.dto.index.entry.IndexEntryDetailDTO;
import nl.dtls.fairdatapoint.api.dto.index.entry.IndexEntryInfoDTO;
import nl.dtls.fairdatapoint.api.dto.index.entry.IndexEntryStateDTO;
import nl.dtls.fairdatapoint.api.dto.index.ping.PingDTO;
import nl.dtls.fairdatapoint.database.mongo.repository.IndexEntryRepository;
-import nl.dtls.fairdatapoint.entity.index.config.EventsConfig;
+import nl.dtls.fairdatapoint.entity.exception.ResourceNotFoundException;
import nl.dtls.fairdatapoint.entity.index.entry.IndexEntry;
import nl.dtls.fairdatapoint.entity.index.entry.IndexEntryState;
import nl.dtls.fairdatapoint.service.index.common.RequiredEnabledIndexFeature;
+import nl.dtls.fairdatapoint.service.index.event.EventService;
+import nl.dtls.fairdatapoint.service.index.settings.IndexSettingsService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
+import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.stereotype.Service;
import org.springframework.validation.annotation.Validated;
import javax.validation.Valid;
import java.time.Instant;
-import java.util.HashMap;
-import java.util.Map;
-import java.util.Optional;
-import java.util.UUID;
+import java.util.*;
+import java.util.stream.Collectors;
+import java.util.stream.StreamSupport;
import static nl.dtls.fairdatapoint.api.dto.index.entry.IndexEntryStateDTO.*;
@@ -55,21 +59,35 @@ public class IndexEntryService {
private IndexEntryRepository repository;
@Autowired
- private EventsConfig eventsConfig;
+ private IndexSettingsService indexSettingsService;
+ @Autowired
+ private EventService eventService;
+
+ @Autowired
+ private IndexEntryMapper mapper;
+
+ @RequiredEnabledIndexFeature
public Iterable getAllEntries() {
return repository.findAll();
}
+ @RequiredEnabledIndexFeature
+ public List getAllEntriesAsDTOs() {
+ Instant validThreshold = getValidThreshold();
+ return StreamSupport.stream(getAllEntries().spliterator(), true).map(it -> mapper.toDTO(it, validThreshold)).collect(Collectors.toList());
+ }
+
@RequiredEnabledIndexFeature
public Page getEntriesPage(Pageable pageable, String state) {
+ Instant validThreshold = getValidThreshold();
if (state.equalsIgnoreCase(ACTIVE.name())) {
return repository.findAllByStateEqualsAndLastRetrievalTimeAfter(pageable, IndexEntryState.Valid,
- getValidThreshold());
+ validThreshold);
}
if (state.equalsIgnoreCase(IndexEntryStateDTO.INACTIVE.name())) {
return repository.findAllByStateEqualsAndLastRetrievalTimeBefore(pageable, IndexEntryState.Valid,
- getValidThreshold());
+ validThreshold);
}
if (state.equalsIgnoreCase(IndexEntryStateDTO.UNREACHABLE.name())) {
return repository.findAllByStateEquals(pageable, IndexEntryState.Unreachable);
@@ -83,20 +101,33 @@ public Page getEntriesPage(Pageable pageable, String state) {
return repository.findAll(pageable);
}
+ @RequiredEnabledIndexFeature
+ public Page getEntriesPageDTOs(Pageable pageable, String state) {
+ Instant validThreshold = getValidThreshold();
+ return getEntriesPage(pageable, state).map(it -> mapper.toDTO(it, validThreshold));
+ }
+
@RequiredEnabledIndexFeature
public Optional getEntry(String uuid) {
return repository.findByUuid(uuid);
}
+ @RequiredEnabledIndexFeature
+ public Optional getEntryDetailDTO(String uuid) {
+ Instant validThreshold = getValidThreshold();
+ return getEntry(uuid).map(entry -> mapper.toDetailDTO(entry, eventService.getEvents(entry.getUuid()), validThreshold));
+ }
+
@RequiredEnabledIndexFeature
public IndexEntryInfoDTO getEntriesInfo() {
+ Instant validThreshold = getValidThreshold();
Map entriesCount = new HashMap<>();
entriesCount.put("ALL", repository.count());
entriesCount.put(UNKNOWN.name(), repository.countAllByStateEquals(IndexEntryState.Unknown));
entriesCount.put(ACTIVE.name(),
- repository.countAllByStateEqualsAndLastRetrievalTimeAfter(IndexEntryState.Valid, getValidThreshold()));
+ repository.countAllByStateEqualsAndLastRetrievalTimeAfter(IndexEntryState.Valid, validThreshold));
entriesCount.put(INACTIVE.name(),
- repository.countAllByStateEqualsAndLastRetrievalTimeBefore(IndexEntryState.Valid, getValidThreshold()));
+ repository.countAllByStateEqualsAndLastRetrievalTimeBefore(IndexEntryState.Valid, validThreshold));
entriesCount.put(UNREACHABLE.name(), repository.countAllByStateEquals(IndexEntryState.Unreachable));
entriesCount.put(INVALID.name(), repository.countAllByStateEquals(IndexEntryState.Invalid));
return new IndexEntryInfoDTO(entriesCount);
@@ -124,8 +155,14 @@ public IndexEntry storeEntry(@Valid PingDTO pingDTO) {
return repository.save(entry);
}
- private Instant getValidThreshold() {
- return Instant.now().minus(eventsConfig.getPingValidDuration());
+ @RequiredEnabledIndexFeature
+ @PreAuthorize("hasRole('ADMIN')")
+ public void deleteEntry(String uuid) {
+ IndexEntry entry = repository.findByUuid(uuid).orElseThrow(() -> new ResourceNotFoundException("Index entry not found"));
+ repository.delete(entry);
}
+ private Instant getValidThreshold() {
+ return Instant.now().minus(indexSettingsService.getOrDefaults().getPing().getValidDuration());
+ }
}
diff --git a/src/main/java/nl/dtls/fairdatapoint/service/index/event/EventMapper.java b/src/main/java/nl/dtls/fairdatapoint/service/index/event/EventMapper.java
index 5866fc66d..c9b03b7be 100644
--- a/src/main/java/nl/dtls/fairdatapoint/service/index/event/EventMapper.java
+++ b/src/main/java/nl/dtls/fairdatapoint/service/index/event/EventMapper.java
@@ -45,9 +45,9 @@ public EventDTO toDTO(Event event) {
);
}
- public Event toAdminTriggerEvent(HttpServletRequest request, Authentication authentication, String clientUrl) {
+ public Event toAdminTriggerEvent(HttpServletRequest request, Authentication authentication, String clientUrl, String remoteAddr) {
var adminTrigger = new AdminTrigger();
- adminTrigger.setRemoteAddr(request.getRemoteAddr());
+ adminTrigger.setRemoteAddr(remoteAddr);
adminTrigger.setTokenName(authentication.getName());
adminTrigger.setClientUrl(clientUrl);
return new Event(VERSION, adminTrigger);
diff --git a/src/main/java/nl/dtls/fairdatapoint/service/index/event/EventService.java b/src/main/java/nl/dtls/fairdatapoint/service/index/event/EventService.java
index ebd78038e..327d767f9 100644
--- a/src/main/java/nl/dtls/fairdatapoint/service/index/event/EventService.java
+++ b/src/main/java/nl/dtls/fairdatapoint/service/index/event/EventService.java
@@ -28,24 +28,30 @@
import nl.dtls.fairdatapoint.database.mongo.repository.EventRepository;
import nl.dtls.fairdatapoint.database.mongo.repository.IndexEntryRepository;
import nl.dtls.fairdatapoint.entity.exception.ResourceNotFoundException;
-import nl.dtls.fairdatapoint.entity.index.config.EventsConfig;
import nl.dtls.fairdatapoint.entity.index.entry.IndexEntry;
import nl.dtls.fairdatapoint.entity.index.entry.IndexEntryState;
import nl.dtls.fairdatapoint.entity.index.event.Event;
import nl.dtls.fairdatapoint.entity.index.event.EventType;
import nl.dtls.fairdatapoint.entity.index.exception.IncorrectPingFormatException;
+import nl.dtls.fairdatapoint.entity.index.exception.PingDeniedException;
import nl.dtls.fairdatapoint.entity.index.exception.RateLimitException;
import nl.dtls.fairdatapoint.entity.index.http.Exchange;
import nl.dtls.fairdatapoint.entity.index.http.ExchangeState;
+import nl.dtls.fairdatapoint.service.UtilityService;
import nl.dtls.fairdatapoint.service.index.common.RequiredEnabledIndexFeature;
import nl.dtls.fairdatapoint.service.index.entry.IndexEntryService;
+import nl.dtls.fairdatapoint.service.index.settings.IndexSettingsService;
import nl.dtls.fairdatapoint.service.index.webhook.WebhookService;
+import nl.dtls.fairdatapoint.util.HttpUtil;
import org.eclipse.rdf4j.util.iterators.EmptyIterator;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.context.annotation.Lazy;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Sort;
+import org.springframework.http.HttpStatus;
+import org.springframework.http.ResponseEntity;
import org.springframework.scheduling.annotation.Async;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
import org.springframework.security.core.Authentication;
@@ -74,20 +80,24 @@ public class EventService {
private IndexEntryRepository indexEntryRepository;
@Autowired
+ @Lazy
private IndexEntryService indexEntryService;
@Autowired
private WebhookService webhookService;
@Autowired
- private EventsConfig eventsConfig;
+ private EventMapper eventMapper;
@Autowired
- private EventMapper eventMapper;
+ private UtilityService utilityService;
@Autowired
private IncomingPingUtils incomingPingUtils;
+ @Autowired
+ private IndexSettingsService indexSettingsService;
+
public Iterable getEvents(IndexEntry indexEntry) {
// TODO: make events pagination in the future
return eventRepository.getAllByRelatedTo(indexEntry, PageRequest.of(0, 10, Sort.by(Sort.Direction.DESC,
@@ -102,19 +112,26 @@ public Iterable getEvents(String indexEntryUuid) {
@RequiredEnabledIndexFeature
@SneakyThrows
public Event acceptIncomingPing(PingDTO reqDto, HttpServletRequest request) {
- var remoteAddr = request.getRemoteAddr();
- var rateLimitSince = Instant.now().minus(eventsConfig.getPingRateLimitDuration());
+ var remoteAddr = utilityService.getRemoteAddr(request);
+ var pingSettings = indexSettingsService.getOrDefaults().getPing();
+
+ if (indexSettingsService.isPingDenied(reqDto)) {
+ logger.info("Received ping is denied");
+ throw new PingDeniedException(reqDto.getClientUrl());
+ }
+
+ var rateLimitSince = Instant.now().minus(pingSettings.getRateLimitDuration());
var previousPings = eventRepository.findAllByIncomingPingExchangeRemoteAddrAndCreatedAfter(remoteAddr,
rateLimitSince);
- if (previousPings.size() > eventsConfig.getPingRateLimitHits()) {
+ if (previousPings.size() > pingSettings.getRateLimitHits()) {
logger.warn("Rate limit for PING reached by {}", remoteAddr);
throw new RateLimitException(String.format(
"Rate limit reached for %s (max. %d per %s) - PING ignored",
- remoteAddr, eventsConfig.getPingRateLimitHits(), eventsConfig.getPingRateLimitDuration().toString())
+ remoteAddr, pingSettings.getRateLimitHits(), pingSettings.getRateLimitDuration().toString())
);
}
- var event = incomingPingUtils.prepareEvent(reqDto, request);
+ var event = incomingPingUtils.prepareEvent(reqDto, request, remoteAddr);
eventRepository.save(event);
event.execute();
try {
@@ -137,14 +154,15 @@ public Event acceptIncomingPing(PingDTO reqDto, HttpServletRequest request) {
}
private void processMetadataRetrieval(Event event) {
- String clientUrl = event.getRelatedTo().getClientUrl();
- if (MetadataRetrievalUtils.shouldRetrieve(event, eventsConfig.getRetrievalRateLimitWait())) {
+ var retrievalSettings = indexSettingsService.getOrDefaults().getRetrieval();
+ var clientUrl = event.getRelatedTo().getClientUrl();
+ if (MetadataRetrievalUtils.shouldRetrieve(event, retrievalSettings.getRateLimitWait())) {
indexEntryRepository.save(event.getRelatedTo());
eventRepository.save(event);
event.execute();
logger.info("Retrieving metadata for {}", clientUrl);
- MetadataRetrievalUtils.retrieveRepositoryMetadata(event, eventsConfig.getRetrievalTimeout());
+ MetadataRetrievalUtils.retrieveRepositoryMetadata(event, retrievalSettings.getTimeout());
Exchange ex = event.getMetadataRetrieval().getExchange();
if (ex.getState() == ExchangeState.Retrieved) {
try {
@@ -224,16 +242,19 @@ public void startResumeUnfinishedEvents() {
}
@RequiredEnabledIndexFeature
- public Event acceptAdminTrigger(HttpServletRequest request, String indexEntryUuid) {
+ public Event acceptAdminTrigger(HttpServletRequest request, PingDTO pingDTO) {
Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
- Event event = eventMapper.toAdminTriggerEvent(request, authentication, indexEntryUuid);
- if (indexEntryUuid != null) {
- Optional entry = indexEntryService.getEntry(indexEntryUuid);
- if (entry.isEmpty()) {
- throw new ResourceNotFoundException("There is no such entry: " + indexEntryUuid);
- }
- event.setRelatedTo(entry.get());
- }
+ Event event = eventMapper.toAdminTriggerEvent(request, authentication, pingDTO.getClientUrl(), utilityService.getRemoteAddr(request));
+ IndexEntry entry = indexEntryService.storeEntry(pingDTO);
+ event.setRelatedTo(entry);
+ event.finish();
+ return eventRepository.save(event);
+ }
+
+ @RequiredEnabledIndexFeature
+ public Event acceptAdminTriggerAll(HttpServletRequest request) {
+ Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
+ Event event = eventMapper.toAdminTriggerEvent(request, authentication, null, utilityService.getRemoteAddr(request));
event.finish();
return eventRepository.save(event);
}
diff --git a/src/main/java/nl/dtls/fairdatapoint/service/index/event/IncomingPingUtils.java b/src/main/java/nl/dtls/fairdatapoint/service/index/event/IncomingPingUtils.java
index 4f9ae523a..762c39f41 100644
--- a/src/main/java/nl/dtls/fairdatapoint/service/index/event/IncomingPingUtils.java
+++ b/src/main/java/nl/dtls/fairdatapoint/service/index/event/IncomingPingUtils.java
@@ -43,9 +43,9 @@ public class IncomingPingUtils {
@Autowired
private ObjectMapper objectMapper;
- public Event prepareEvent(PingDTO reqDto, HttpServletRequest request) {
+ public Event prepareEvent(PingDTO reqDto, HttpServletRequest request, String remoteAddr) {
var incomingPing = new IncomingPing();
- var ex = new Exchange(ExchangeDirection.INCOMING, request.getRemoteAddr());
+ var ex = new Exchange(ExchangeDirection.INCOMING, remoteAddr);
incomingPing.setExchange(ex);
ex.getRequest().setHeaders(getHeaders(request));
diff --git a/src/main/java/nl/dtls/fairdatapoint/service/index/settings/IndexSettingsMapper.java b/src/main/java/nl/dtls/fairdatapoint/service/index/settings/IndexSettingsMapper.java
new file mode 100644
index 000000000..b2a3ed226
--- /dev/null
+++ b/src/main/java/nl/dtls/fairdatapoint/service/index/settings/IndexSettingsMapper.java
@@ -0,0 +1,98 @@
+/**
+ * The MIT License
+ * Copyright © 2017 DTL
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+package nl.dtls.fairdatapoint.service.index.settings;
+
+import nl.dtls.fairdatapoint.api.dto.index.settings.IndexSettingsDTO;
+import nl.dtls.fairdatapoint.api.dto.index.settings.IndexSettingsPingDTO;
+import nl.dtls.fairdatapoint.api.dto.index.settings.IndexSettingsRetrievalDTO;
+import nl.dtls.fairdatapoint.api.dto.index.settings.IndexSettingsUpdateDTO;
+import nl.dtls.fairdatapoint.entity.index.settings.IndexSettings;
+import nl.dtls.fairdatapoint.entity.index.settings.IndexSettingsPing;
+import nl.dtls.fairdatapoint.entity.index.settings.IndexSettingsRetrieval;
+import org.springframework.stereotype.Service;
+
+import java.time.Duration;
+
+@Service
+public class IndexSettingsMapper {
+
+ private IndexSettingsPingDTO toPingDTO(IndexSettingsPing indexSettingsPing) {
+ return new IndexSettingsPingDTO(
+ indexSettingsPing.getValidDuration().toString(),
+ indexSettingsPing.getRateLimitDuration().toString(),
+ indexSettingsPing.getRateLimitHits(),
+ indexSettingsPing.getDenyList()
+ );
+ }
+
+ private IndexSettingsRetrievalDTO toRetrievalDTO(IndexSettingsRetrieval indexSettingsRetrieval) {
+ return new IndexSettingsRetrievalDTO(
+ indexSettingsRetrieval.getRateLimitWait().toString(),
+ indexSettingsRetrieval.getTimeout().toString()
+ );
+ }
+
+ public IndexSettingsDTO toDTO(IndexSettings indexSettings) {
+ return new IndexSettingsDTO(
+ toRetrievalDTO(indexSettings.getRetrieval()),
+ toPingDTO(indexSettings.getPing()),
+ indexSettings.equals(IndexSettings.getDefault())
+ );
+ }
+
+ public IndexSettingsUpdateDTO toUpdateDTO(IndexSettings indexSettings) {
+ return new IndexSettingsUpdateDTO(
+ toRetrievalDTO(indexSettings.getRetrieval()),
+ toPingDTO(indexSettings.getPing())
+ );
+ }
+
+ private IndexSettingsPing fromDTO(IndexSettingsPingDTO dto, IndexSettingsPing ping) {
+ return
+ ping
+ .toBuilder()
+ .validDuration(Duration.parse(dto.getValidDuration()))
+ .rateLimitDuration(Duration.parse(dto.getRateLimitDuration()))
+ .rateLimitHits(dto.getRateLimitHits())
+ .denyList(dto.getDenyList())
+ .build();
+ }
+
+ private IndexSettingsRetrieval fromDTO(IndexSettingsRetrievalDTO dto, IndexSettingsRetrieval retrieval) {
+ return
+ retrieval
+ .toBuilder()
+ .rateLimitWait(Duration.parse(dto.getRateLimitWait()))
+ .timeout(Duration.parse(dto.getTimeout()))
+ .build();
+ }
+
+ public IndexSettings fromUpdateDTO(IndexSettingsUpdateDTO dto, IndexSettings indexSettings) {
+ return
+ indexSettings
+ .toBuilder()
+ .ping(fromDTO(dto.getPing(), indexSettings.getPing()))
+ .retrieval(fromDTO(dto.getRetrieval(), indexSettings.getRetrieval()))
+ .build();
+ }
+}
diff --git a/src/main/java/nl/dtls/fairdatapoint/service/index/settings/IndexSettingsService.java b/src/main/java/nl/dtls/fairdatapoint/service/index/settings/IndexSettingsService.java
new file mode 100644
index 000000000..03c2c9f3f
--- /dev/null
+++ b/src/main/java/nl/dtls/fairdatapoint/service/index/settings/IndexSettingsService.java
@@ -0,0 +1,73 @@
+/**
+ * The MIT License
+ * Copyright © 2017 DTL
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+package nl.dtls.fairdatapoint.service.index.settings;
+
+import nl.dtls.fairdatapoint.api.dto.index.ping.PingDTO;
+import nl.dtls.fairdatapoint.api.dto.index.settings.IndexSettingsDTO;
+import nl.dtls.fairdatapoint.api.dto.index.settings.IndexSettingsUpdateDTO;
+import nl.dtls.fairdatapoint.database.mongo.repository.IndexSettingsRepository;
+import nl.dtls.fairdatapoint.entity.index.settings.IndexSettings;
+import nl.dtls.fairdatapoint.service.index.common.RequiredEnabledIndexFeature;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Service;
+
+import java.util.regex.Pattern;
+
+@Service
+public class IndexSettingsService {
+ private static final Logger logger = LoggerFactory.getLogger(IndexSettingsService.class);
+
+ @Autowired
+ private IndexSettingsRepository repository;
+
+ @Autowired
+ private IndexSettingsMapper mapper;
+
+ @RequiredEnabledIndexFeature
+ public boolean isPingDenied(PingDTO ping) {
+ logger.info("Checking if ping.clientUrl is on deny list: " + ping.getClientUrl());
+ return getOrDefaults().getPing().getDenyList().parallelStream().anyMatch(pattern -> Pattern.matches(pattern, ping.getClientUrl()));
+ }
+
+ @RequiredEnabledIndexFeature
+ public IndexSettings getOrDefaults() {
+ return repository.findFirstBy().orElse(IndexSettings.getDefault());
+ }
+
+ @RequiredEnabledIndexFeature
+ public IndexSettingsDTO getCurrentSettings() {
+ return mapper.toDTO(getOrDefaults());
+ }
+
+ @RequiredEnabledIndexFeature
+ public IndexSettingsDTO updateSettings(IndexSettingsUpdateDTO dto) {
+ return mapper.toDTO(repository.save(mapper.fromUpdateDTO(dto, getOrDefaults())));
+ }
+
+ @RequiredEnabledIndexFeature
+ public IndexSettingsDTO resetSettings() {
+ return updateSettings(mapper.toUpdateDTO(IndexSettings.getDefault()));
+ }
+}
diff --git a/src/main/java/nl/dtls/fairdatapoint/service/index/webhook/WebhookMapper.java b/src/main/java/nl/dtls/fairdatapoint/service/index/webhook/WebhookMapper.java
index b0e26546c..5b888d891 100644
--- a/src/main/java/nl/dtls/fairdatapoint/service/index/webhook/WebhookMapper.java
+++ b/src/main/java/nl/dtls/fairdatapoint/service/index/webhook/WebhookMapper.java
@@ -47,10 +47,10 @@ public Event toTriggerEvent(Webhook webhook, WebhookEvent webhookEvent, Event tr
return new Event(VERSION, webhookTrigger, triggerEvent);
}
- public Event toPingEvent(HttpServletRequest request, Authentication authentication, UUID webhookUuid) {
+ public Event toPingEvent(HttpServletRequest request, Authentication authentication, UUID webhookUuid, String remoteAddr) {
var webhookPing = new WebhookPing();
webhookPing.setWebhookUuid(webhookUuid);
- webhookPing.setRemoteAddr(request.getRemoteAddr());
+ webhookPing.setRemoteAddr(remoteAddr);
webhookPing.setTokenName(authentication.getName());
return new Event(VERSION, webhookPing);
}
diff --git a/src/main/java/nl/dtls/fairdatapoint/service/index/webhook/WebhookService.java b/src/main/java/nl/dtls/fairdatapoint/service/index/webhook/WebhookService.java
index 73ed5a8fb..ab1ea736a 100644
--- a/src/main/java/nl/dtls/fairdatapoint/service/index/webhook/WebhookService.java
+++ b/src/main/java/nl/dtls/fairdatapoint/service/index/webhook/WebhookService.java
@@ -28,11 +28,12 @@
import nl.dtls.fairdatapoint.database.mongo.repository.EventRepository;
import nl.dtls.fairdatapoint.database.mongo.repository.WebhookRepository;
import nl.dtls.fairdatapoint.entity.exception.ResourceNotFoundException;
-import nl.dtls.fairdatapoint.entity.index.config.EventsConfig;
import nl.dtls.fairdatapoint.entity.index.event.Event;
import nl.dtls.fairdatapoint.entity.index.webhook.Webhook;
import nl.dtls.fairdatapoint.entity.index.webhook.WebhookEvent;
+import nl.dtls.fairdatapoint.service.UtilityService;
import nl.dtls.fairdatapoint.service.index.common.RequiredEnabledIndexFeature;
+import nl.dtls.fairdatapoint.service.index.settings.IndexSettingsService;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
@@ -64,12 +65,16 @@ public class WebhookService {
EventRepository eventRepository;
@Autowired
- private EventsConfig eventsConfig;
+ private IndexSettingsService indexSettingsService;
+
+ @Autowired
+ private UtilityService utilityService;
private static final String SECRET_PLACEHOLDER = "*** HIDDEN ***";
@RequiredEnabledIndexFeature
public void processWebhookTrigger(Event event) {
+ var retrievalSettings = indexSettingsService.getOrDefaults().getRetrieval();
event.execute();
eventRepository.save(event);
WebhookPayloadDTO webhookPayload = webhookMapper.toWebhookPayloadDTO(event);
@@ -78,7 +83,7 @@ public void processWebhookTrigger(Event event) {
String signature = WebhookUtils.computeHashSignature(payloadWithSecret);
webhookPayload.setSecret(SECRET_PLACEHOLDER);
String payloadWithoutSecret = objectMapper.writeValueAsString(webhookPayload);
- WebhookUtils.postWebhook(event, eventsConfig.getRetrievalTimeout(), payloadWithoutSecret, signature);
+ WebhookUtils.postWebhook(event, retrievalSettings.getTimeout(), payloadWithoutSecret, signature);
} catch (JsonProcessingException e) {
logger.error("Failed to convert webhook payload to string");
} catch (NoSuchAlgorithmException e) {
@@ -106,7 +111,7 @@ public void triggerWebhooks(WebhookEvent webhookEvent, Event triggerEvent) {
public Event handleWebhookPing(HttpServletRequest request, UUID webhookUuid) {
Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
Optional webhook = webhookRepository.findByUuid(webhookUuid);
- Event event = eventRepository.save(webhookMapper.toPingEvent(request, authentication, webhookUuid));
+ Event event = eventRepository.save(webhookMapper.toPingEvent(request, authentication, webhookUuid, utilityService.getRemoteAddr(request)));
if (webhook.isEmpty()) {
throw new ResourceNotFoundException("There is no such webhook: " + webhookUuid);
}
@@ -128,17 +133,10 @@ public void triggerWebhooks(Event triggerEvent) {
break;
case MetadataRetrieval:
switch (triggerEvent.getRelatedTo().getState()) {
- case Valid:
- triggerWebhooks(WebhookEvent.EntryValid, triggerEvent);
- break;
- case Invalid:
- triggerWebhooks(WebhookEvent.EntryInvalid, triggerEvent);
- break;
- case Unreachable:
- triggerWebhooks(WebhookEvent.EntryUnreachable, triggerEvent);
- break;
- default:
- logger.warn("Invalid state of MetadataRetrieval: {}", triggerEvent.getRelatedTo().getState());
+ case Valid -> triggerWebhooks(WebhookEvent.EntryValid, triggerEvent);
+ case Invalid -> triggerWebhooks(WebhookEvent.EntryInvalid, triggerEvent);
+ case Unreachable -> triggerWebhooks(WebhookEvent.EntryUnreachable, triggerEvent);
+ default -> logger.warn("Invalid state of MetadataRetrieval: {}", triggerEvent.getRelatedTo().getState());
}
break;
case WebhookPing:
diff --git a/src/main/java/nl/dtls/fairdatapoint/service/metadata/state/MetadataStateService.java b/src/main/java/nl/dtls/fairdatapoint/service/metadata/state/MetadataStateService.java
index e18ad2dae..29955f5b1 100644
--- a/src/main/java/nl/dtls/fairdatapoint/service/metadata/state/MetadataStateService.java
+++ b/src/main/java/nl/dtls/fairdatapoint/service/metadata/state/MetadataStateService.java
@@ -68,14 +68,13 @@ public Metadata get(IRI metadataUri) {
return oMetadata.get();
}
- public MetaStateDTO getState(Model model, ResourceDefinition rd) {
+ public MetaStateDTO getState(IRI metadataUri, Model model, ResourceDefinition rd) {
// 1. Return null if user is not log in
if (currentUserService.getCurrentUser().isEmpty()) {
return null;
}
// 2. Get metadata info for current
- IRI metadataUri = getUri(model);
Optional oMetadata = metadataRepository.findByUri(metadataUri.stringValue());
if (oMetadata.isEmpty()) {
throw new ResourceNotFoundException(format("Metadata info '%s' was not found", metadataUri));
diff --git a/src/main/java/nl/dtls/fairdatapoint/service/profile/ProfileService.java b/src/main/java/nl/dtls/fairdatapoint/service/profile/ProfileService.java
index f6b18349b..318f42805 100644
--- a/src/main/java/nl/dtls/fairdatapoint/service/profile/ProfileService.java
+++ b/src/main/java/nl/dtls/fairdatapoint/service/profile/ProfileService.java
@@ -1,3 +1,25 @@
+/**
+ * The MIT License
+ * Copyright © 2017 DTL
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
package nl.dtls.fairdatapoint.service.profile;
import nl.dtls.fairdatapoint.service.shape.ShapeService;
diff --git a/src/main/java/nl/dtls/fairdatapoint/util/HttpUtil.java b/src/main/java/nl/dtls/fairdatapoint/util/HttpUtil.java
index d18cab685..03c81ed80 100644
--- a/src/main/java/nl/dtls/fairdatapoint/util/HttpUtil.java
+++ b/src/main/java/nl/dtls/fairdatapoint/util/HttpUtil.java
@@ -40,16 +40,43 @@
@Slf4j
public class HttpUtil {
+ private static final String[] IP_HEADER_CANDIDATES = {
+ "X-Forwarded-For",
+ "X-Real-IP",
+ "Proxy-Client-IP",
+ "WL-Proxy-Client-IP",
+ "HTTP_X_FORWARDED_FOR",
+ "HTTP_X_FORWARDED",
+ "HTTP_X_CLUSTER_CLIENT_IP",
+ "HTTP_CLIENT_IP",
+ "HTTP_FORWARDED_FOR",
+ "HTTP_FORWARDED",
+ "HTTP_VIA",
+ "REMOTE_ADDR"
+ };
+
+ public static String getClientIpAddress(HttpServletRequest request, Boolean behindProxy) {
+ if (behindProxy) {
+ for (String header : IP_HEADER_CANDIDATES) {
+ String ipList = request.getHeader(header);
+ if (ipList != null && ipList.length() != 0 && !"unknown".equalsIgnoreCase(ipList)) {
+ return ipList.split(",")[0];
+ }
+ }
+ }
+ return request.getRemoteAddr();
+ }
+
public static String getRequestURL(HttpServletRequest request, String persistentUrl) {
String urlS = request.getRequestURL().toString();
- log.info("Original requesed url {}", urlS);
+ log.info("Original requested url {}", urlS);
try {
urlS = removeLastSlash(urlS.replace("/expanded", ""));
persistentUrl = removeLastSlash(persistentUrl);
URL url = new URL(urlS);
String modifiedUrl = persistentUrl + url.getPath();
- log.info("Modified requesed url {}", modifiedUrl);
+ log.info("Modified requested url {}", modifiedUrl);
return modifiedUrl;
diff --git a/src/main/resources/META-INF/additional-spring-configuration-metadata.json b/src/main/resources/META-INF/additional-spring-configuration-metadata.json
index bcdcf50fd..6735ab40b 100644
--- a/src/main/resources/META-INF/additional-spring-configuration-metadata.json
+++ b/src/main/resources/META-INF/additional-spring-configuration-metadata.json
@@ -59,6 +59,11 @@
"name": "fdp-index.api.contactName",
"type": "java.lang.String",
"description": "Contact name/label for OpenAPI docs"
+ },
+ {
+ "name": "instance.behindProxy",
+ "type": "java.lang.String",
+ "description": "If FDP is running behind reverse HTTP proxy"
}
]
-}
\ No newline at end of file
+}
diff --git a/src/main/resources/application.yml b/src/main/resources/application.yml
index 1c5cdd3be..70ffbaaf6 100644
--- a/src/main/resources/application.yml
+++ b/src/main/resources/application.yml
@@ -1,5 +1,6 @@
instance:
clientUrl: http://localhost:8080
+ behindProxy: true
spring:
data:
@@ -59,11 +60,3 @@ metadataMetrics:
fdp-index:
enabled: false
- events:
- retrieval:
- rateLimitWait: PT10M # 10 minutes (ISO 8601)
- timeout: PT1M # 1 minute (ISO 8601)
- ping:
- validDuration: P7D # 7 days (ISO 8601)
- rateLimitDuration: PT6H
- rateLimitHits: 10
diff --git a/src/test/java/nl/dtls/fairdatapoint/acceptance/index/admin/List_TriggerAll_POST.java b/src/test/java/nl/dtls/fairdatapoint/acceptance/index/admin/List_TriggerAll_POST.java
new file mode 100644
index 000000000..307005646
--- /dev/null
+++ b/src/test/java/nl/dtls/fairdatapoint/acceptance/index/admin/List_TriggerAll_POST.java
@@ -0,0 +1,130 @@
+/**
+ * The MIT License
+ * Copyright © 2017 DTL
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+package nl.dtls.fairdatapoint.acceptance.index.admin;
+
+import nl.dtls.fairdatapoint.WebIntegrationTest;
+import nl.dtls.fairdatapoint.api.dto.index.ping.PingDTO;
+import nl.dtls.fairdatapoint.database.mongo.repository.EventRepository;
+import nl.dtls.fairdatapoint.database.mongo.repository.IndexEntryRepository;
+import nl.dtls.fairdatapoint.entity.index.entry.IndexEntry;
+import nl.dtls.fairdatapoint.entity.index.event.Event;
+import nl.dtls.fairdatapoint.entity.index.event.EventType;
+import nl.dtls.fairdatapoint.utils.TestIndexEntryFixtures;
+import org.junit.jupiter.api.DisplayName;
+import org.junit.jupiter.api.Test;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.core.ParameterizedTypeReference;
+import org.springframework.http.HttpHeaders;
+import org.springframework.http.HttpStatus;
+import org.springframework.http.RequestEntity;
+import org.springframework.http.ResponseEntity;
+
+import java.net.URI;
+import java.util.List;
+
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.hamcrest.core.Is.is;
+import static org.hamcrest.core.IsEqual.equalTo;
+
+@DisplayName("POST /index/admin/trigger-all")
+public class List_TriggerAll_POST extends WebIntegrationTest {
+
+ @Autowired
+ private EventRepository eventRepository;
+
+ @Autowired
+ private IndexEntryRepository indexEntryRepository;
+
+ private final ParameterizedTypeReference responseType = new ParameterizedTypeReference<>() {
+ };
+
+ private URI url() {
+ return URI.create("/index/admin/trigger-all");
+ }
+
+ @Test
+ @DisplayName("HTTP 403: no token")
+ public void res403_noToken() {
+ // GIVEN
+ RequestEntity request = RequestEntity
+ .post(url())
+ .build();
+
+ // WHEN
+ ResponseEntity result = client.exchange(request, responseType);
+
+ // THEN:
+ assertThat("Correct response code is received", result.getStatusCode(), is(equalTo(HttpStatus.FORBIDDEN)));
+ }
+
+ @Test
+ @DisplayName("HTTP 403: incorrect token")
+ public void res403_incorrectToken() {
+ // GIVEN
+ RequestEntity request = RequestEntity
+ .post(url())
+ .header(HttpHeaders.AUTHORIZATION, "mySecretToken")
+ .build();
+
+ // WHEN
+ ResponseEntity result = client.exchange(request, responseType);
+
+ // THEN:
+ assertThat("Correct response code is received", result.getStatusCode(), is(equalTo(HttpStatus.FORBIDDEN)));
+ }
+
+ @Test
+ @DisplayName("HTTP 403: non-admin token")
+ public void res403_nonAdminToken() {
+ // GIVEN
+ RequestEntity request = RequestEntity
+ .post(url())
+ .header(HttpHeaders.AUTHORIZATION, ALBERT_TOKEN)
+ .build();
+
+ // WHEN
+ ResponseEntity result = client.exchange(request, responseType);
+
+ // THEN:
+ assertThat("Correct response code is received", result.getStatusCode(), is(equalTo(HttpStatus.FORBIDDEN)));
+ }
+
+ @Test
+ @DisplayName("HTTP 204: trigger all")
+ public void res204_triggerAll() {
+ // GIVEN: prepare request
+ RequestEntity request = RequestEntity
+ .post(url())
+ .header(HttpHeaders.AUTHORIZATION, ADMIN_TOKEN)
+ .build();
+
+ // WHEN
+ ResponseEntity result = client.exchange(request, responseType);
+ List events = eventRepository.getAllByType(EventType.AdminTrigger);
+
+ // THEN:
+ assertThat("Correct response code is received", result.getStatusCode(), is(equalTo(HttpStatus.NO_CONTENT)));
+ assertThat("One AdminTrigger event is created", events.size(), is(equalTo(1)));
+ assertThat("Records correct client URL as null", events.get(0).getAdminTrigger().getClientUrl(), is(equalTo(null)));
+ }
+}
diff --git a/src/test/java/nl/dtls/fairdatapoint/acceptance/index/admin/List_Trigger_POST.java b/src/test/java/nl/dtls/fairdatapoint/acceptance/index/admin/List_Trigger_POST.java
index 11207073a..651036181 100644
--- a/src/test/java/nl/dtls/fairdatapoint/acceptance/index/admin/List_Trigger_POST.java
+++ b/src/test/java/nl/dtls/fairdatapoint/acceptance/index/admin/List_Trigger_POST.java
@@ -23,6 +23,7 @@
package nl.dtls.fairdatapoint.acceptance.index.admin;
import nl.dtls.fairdatapoint.WebIntegrationTest;
+import nl.dtls.fairdatapoint.api.dto.index.ping.PingDTO;
import nl.dtls.fairdatapoint.database.mongo.repository.EventRepository;
import nl.dtls.fairdatapoint.database.mongo.repository.IndexEntryRepository;
import nl.dtls.fairdatapoint.entity.index.entry.IndexEntry;
@@ -32,6 +33,7 @@
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.core.ParameterizedTypeReference;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
@@ -61,8 +63,10 @@ private URI url() {
return URI.create("/index/admin/trigger");
}
- private URI url(String clientUrl) {
- return URI.create("/index/admin/trigger?clientUrl=" + clientUrl);
+ private PingDTO reqDTO(String clientUrl) {
+ PingDTO dto = new PingDTO();
+ dto.setClientUrl(clientUrl);
+ return dto;
}
@Test
@@ -70,11 +74,12 @@ private URI url(String clientUrl) {
public void res403_noToken() {
// GIVEN: prepare data
String clientUrl = "http://example.com";
+ PingDTO reqDTO = reqDTO(clientUrl);
// AND: prepare request
- RequestEntity request = RequestEntity
- .post(url(clientUrl))
- .build();
+ RequestEntity request = RequestEntity
+ .post(url())
+ .body(reqDTO);
// WHEN
ResponseEntity result = client.exchange(request, responseType);
@@ -88,12 +93,13 @@ public void res403_noToken() {
public void res403_incorrectToken() {
// GIVEN: prepare data
String clientUrl = "http://example.com";
+ PingDTO reqDTO = reqDTO(clientUrl);
// AND: prepare request
- RequestEntity request = RequestEntity
- .post(url(clientUrl))
- .header(HttpHeaders.AUTHORIZATION, ALBERT_TOKEN)
- .build();
+ RequestEntity request = RequestEntity
+ .post(url())
+ .header(HttpHeaders.AUTHORIZATION, "mySecretToken")
+ .body(reqDTO);
// WHEN
ResponseEntity result = client.exchange(request, responseType);
@@ -107,12 +113,13 @@ public void res403_incorrectToken() {
public void res403_nonAdminToken() {
// GIVEN: prepare data
String clientUrl = "http://example.com";
+ PingDTO reqDTO = reqDTO(clientUrl);
// AND: prepare request
- RequestEntity request = RequestEntity
- .post(url(clientUrl))
+ RequestEntity request = RequestEntity
+ .post(url())
.header(HttpHeaders.AUTHORIZATION, ALBERT_TOKEN)
- .build();
+ .body(reqDTO);
// WHEN
ResponseEntity result = client.exchange(request, responseType);
@@ -122,37 +129,39 @@ public void res403_nonAdminToken() {
}
@Test
- @DisplayName("HTTP 204: trigger one")
- public void res204_triggerOne() {
+ @DisplayName("HTTP 400: malformed URL")
+ public void res403_malformedUrl() {
// GIVEN: prepare data
- IndexEntry entry = TestIndexEntryFixtures.entryExample();
- indexEntryRepository.save(entry);
+ String clientUrl = "http://example.com";
+ PingDTO reqDTO = reqDTO(clientUrl);
+ reqDTO.setClientUrl("thisIsNot/Url");
// AND: prepare request
- RequestEntity request = RequestEntity
- .post(url(entry.getUuid()))
+ RequestEntity request = RequestEntity
+ .post(url())
.header(HttpHeaders.AUTHORIZATION, ADMIN_TOKEN)
- .build();
+ .body(reqDTO);
// WHEN
ResponseEntity result = client.exchange(request, responseType);
- List events = eventRepository.getAllByType(EventType.AdminTrigger);
- // THEN:
- assertThat("Correct response code is received", result.getStatusCode(), is(equalTo(HttpStatus.NO_CONTENT)));
- assertThat("One AdminTrigger event is created", events.size(), is(equalTo(1)));
- assertThat("Records correct client URL", events.get(0).getAdminTrigger().getClientUrl(),
- is(equalTo(entry.getUuid())));
+ // THEN:
+ assertThat("Correct response code is received", result.getStatusCode(), is(equalTo(HttpStatus.BAD_REQUEST)));
}
@Test
- @DisplayName("HTTP 204: trigger all")
- public void res204_triggerAll() {
- // GIVEN: prepare request
- RequestEntity request = RequestEntity
+ @DisplayName("HTTP 204: trigger one")
+ public void res204_triggerOne() {
+ // GIVEN: prepare data
+ IndexEntry entry = TestIndexEntryFixtures.entryExample();
+ indexEntryRepository.save(entry);
+ PingDTO reqDTO = reqDTO(entry.getClientUrl());
+
+ // AND: prepare request
+ RequestEntity request = RequestEntity
.post(url())
.header(HttpHeaders.AUTHORIZATION, ADMIN_TOKEN)
- .build();
+ .body(reqDTO);
// WHEN
ResponseEntity result = client.exchange(request, responseType);
@@ -161,8 +170,7 @@ public void res204_triggerAll() {
// THEN:
assertThat("Correct response code is received", result.getStatusCode(), is(equalTo(HttpStatus.NO_CONTENT)));
assertThat("One AdminTrigger event is created", events.size(), is(equalTo(1)));
- assertThat("Records correct client URL as null", events.get(0).getAdminTrigger().getClientUrl(),
- is(equalTo(null)));
+ assertThat("Records correct client URL", events.get(0).getAdminTrigger().getClientUrl(), is(equalTo(entry.getClientUrl())));
}
@Test
@@ -170,17 +178,21 @@ public void res204_triggerAll() {
public void res404_triggerOne() {
// GIVEN: prepare data
IndexEntry entry = TestIndexEntryFixtures.entryExample();
+ PingDTO reqDTO = reqDTO(entry.getClientUrl());
// AND: prepare request
- RequestEntity request = RequestEntity
- .post(url(entry.getUuid()))
+ RequestEntity request = RequestEntity
+ .post(url())
.header(HttpHeaders.AUTHORIZATION, ADMIN_TOKEN)
- .build();
+ .body(reqDTO);
// WHEN
ResponseEntity result = client.exchange(request, responseType);
+ List events = eventRepository.getAllByType(EventType.AdminTrigger);
// THEN:
- assertThat("Correct response code is received", result.getStatusCode(), is(equalTo(HttpStatus.NOT_FOUND)));
+ assertThat("Correct response code is received", result.getStatusCode(), is(equalTo(HttpStatus.NO_CONTENT)));
+ assertThat("One AdminTrigger event is created", events.size(), is(equalTo(1)));
+ assertThat("Records correct client URL", events.get(0).getAdminTrigger().getClientUrl(), is(equalTo(entry.getClientUrl())));
}
}
diff --git a/src/test/java/nl/dtls/fairdatapoint/acceptance/index/ping/List_POST.java b/src/test/java/nl/dtls/fairdatapoint/acceptance/index/ping/List_POST.java
index e17336677..c21951442 100644
--- a/src/test/java/nl/dtls/fairdatapoint/acceptance/index/ping/List_POST.java
+++ b/src/test/java/nl/dtls/fairdatapoint/acceptance/index/ping/List_POST.java
@@ -43,7 +43,7 @@
import static org.hamcrest.core.Is.is;
import static org.hamcrest.core.IsEqual.equalTo;
-@DisplayName("POST /index")
+@DisplayName("POST /")
public class List_POST extends WebIntegrationTest {
@Autowired
diff --git a/src/test/java/nl/dtls/fairdatapoint/acceptance/index/settings/List_DELETE.java b/src/test/java/nl/dtls/fairdatapoint/acceptance/index/settings/List_DELETE.java
new file mode 100644
index 000000000..7fd318fa5
--- /dev/null
+++ b/src/test/java/nl/dtls/fairdatapoint/acceptance/index/settings/List_DELETE.java
@@ -0,0 +1,175 @@
+/**
+ * The MIT License
+ * Copyright © 2017 DTL
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+package nl.dtls.fairdatapoint.acceptance.index.settings;
+
+import nl.dtls.fairdatapoint.WebIntegrationTest;
+import nl.dtls.fairdatapoint.api.dto.index.settings.IndexSettingsDTO;
+import nl.dtls.fairdatapoint.database.mongo.repository.IndexSettingsRepository;
+import nl.dtls.fairdatapoint.entity.index.settings.IndexSettings;
+import nl.dtls.fairdatapoint.entity.index.settings.IndexSettingsPing;
+import nl.dtls.fairdatapoint.entity.index.settings.IndexSettingsRetrieval;
+import org.junit.jupiter.api.DisplayName;
+import org.junit.jupiter.api.Test;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.core.ParameterizedTypeReference;
+import org.springframework.http.*;
+
+import java.net.URI;
+import java.time.Duration;
+import java.util.Collections;
+import java.util.Objects;
+
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.hamcrest.core.Is.is;
+import static org.hamcrest.core.IsEqual.equalTo;
+import static org.hamcrest.core.IsNull.notNullValue;
+
+@DisplayName("DELETE /index/settings")
+public class List_DELETE extends WebIntegrationTest {
+
+ @Autowired
+ private IndexSettingsRepository indexSettingsRepository;
+
+ private final ParameterizedTypeReference responseType =
+ new ParameterizedTypeReference<>() {
+ };
+
+ private URI url() {
+ return URI.create("/index/settings");
+ }
+
+ private IndexSettings customSettings() {
+ return new IndexSettings()
+ .toBuilder()
+ .ping(
+ new IndexSettingsPing()
+ .toBuilder()
+ .denyList(Collections.singletonList("http://localhost.*$"))
+ .rateLimitDuration(Duration.ofMinutes(17))
+ .validDuration(Duration.ofDays(5))
+ .rateLimitHits(666)
+ .build()
+ )
+ .retrieval(
+ new IndexSettingsRetrieval()
+ .toBuilder()
+ .rateLimitWait(Duration.ofHours(16))
+ .timeout(Duration.ofSeconds(55))
+ .build()
+ )
+ .build();
+ }
+
+ @Test
+ @DisplayName("HTTP 200: default settings")
+ public void res200_defaultSettings() {
+ // GIVEN: prepare data
+ IndexSettings settings = IndexSettings.getDefault();
+ indexSettingsRepository.deleteAll();
+
+ // AND: prepare request
+ RequestEntity> request = RequestEntity
+ .delete(url())
+ .header(HttpHeaders.AUTHORIZATION, ADMIN_TOKEN)
+ .accept(MediaType.APPLICATION_JSON)
+ .build();
+
+ // WHEN
+ ResponseEntity result = client.exchange(request, responseType);
+
+ // THEN
+ assertThat("Correct response code is received", result.getStatusCode(), is(equalTo(HttpStatus.OK)));
+ assertThat("Response body is not null", result.getBody(), is(notNullValue()));
+ assertThat("Response contains default valid duration", Objects.requireNonNull(result.getBody()).getPing().getValidDuration(), is(equalTo(settings.getPing().getValidDuration().toString())));
+ assertThat("Response contains default rate limit duration", Objects.requireNonNull(result.getBody()).getPing().getRateLimitDuration(), is(equalTo(settings.getPing().getRateLimitDuration().toString())));
+ assertThat("Response contains default rate limit hits", Objects.requireNonNull(result.getBody()).getPing().getRateLimitHits(), is(equalTo(settings.getPing().getRateLimitHits())));
+ assertThat("Response contains default deny list", Objects.requireNonNull(result.getBody()).getPing().getDenyList(), is(equalTo(settings.getPing().getDenyList())));
+ assertThat("Response contains default timeout", Objects.requireNonNull(result.getBody()).getRetrieval().getTimeout(), is(equalTo(settings.getRetrieval().getTimeout().toString())));
+ assertThat("Response contains default rate limit wait", Objects.requireNonNull(result.getBody()).getRetrieval().getRateLimitWait(), is(equalTo(settings.getRetrieval().getRateLimitWait().toString())));
+ assertThat("Response indicated default settings", Objects.requireNonNull(result.getBody()).getIsDefault(), is(Boolean.TRUE));
+ }
+
+ @Test
+ @DisplayName("HTTP 200: custom settings")
+ public void res200_customSettings() {
+ // GIVEN: prepare data
+ IndexSettings settings = IndexSettings.getDefault();
+ IndexSettings customSettings = customSettings();
+ indexSettingsRepository.deleteAll();
+ indexSettingsRepository.insert(customSettings);
+
+ // AND: prepare request
+ RequestEntity> request = RequestEntity
+ .delete(url())
+ .header(HttpHeaders.AUTHORIZATION, ADMIN_TOKEN)
+ .accept(MediaType.APPLICATION_JSON)
+ .build();
+
+ // WHEN
+ ResponseEntity result = client.exchange(request, responseType);
+
+ // THEN
+ assertThat("Correct response code is received", result.getStatusCode(), is(equalTo(HttpStatus.OK)));
+ assertThat("Response body is not null", result.getBody(), is(notNullValue()));
+ assertThat("Response contains default valid duration", Objects.requireNonNull(result.getBody()).getPing().getValidDuration(), is(equalTo(settings.getPing().getValidDuration().toString())));
+ assertThat("Response contains default rate limit duration", Objects.requireNonNull(result.getBody()).getPing().getRateLimitDuration(), is(equalTo(settings.getPing().getRateLimitDuration().toString())));
+ assertThat("Response contains default rate limit hits", Objects.requireNonNull(result.getBody()).getPing().getRateLimitHits(), is(equalTo(settings.getPing().getRateLimitHits())));
+ assertThat("Response contains default deny list", Objects.requireNonNull(result.getBody()).getPing().getDenyList(), is(equalTo(settings.getPing().getDenyList())));
+ assertThat("Response contains default timeout", Objects.requireNonNull(result.getBody()).getRetrieval().getTimeout(), is(equalTo(settings.getRetrieval().getTimeout().toString())));
+ assertThat("Response contains default rate limit wait", Objects.requireNonNull(result.getBody()).getRetrieval().getRateLimitWait(), is(equalTo(settings.getRetrieval().getRateLimitWait().toString())));
+ assertThat("Response indicated default settings", Objects.requireNonNull(result.getBody()).getIsDefault(), is(Boolean.TRUE));
+ }
+
+ @Test
+ @DisplayName("HTTP 403: no token")
+ public void res403_noToken() {
+ // GIVEN
+ RequestEntity> request = RequestEntity
+ .delete(url())
+ .accept(MediaType.APPLICATION_JSON)
+ .build();
+
+ // WHEN
+ ResponseEntity result = client.exchange(request, responseType);
+
+ // THEN
+ assertThat("It is forbidden without auth", result.getStatusCode(), is(equalTo(HttpStatus.FORBIDDEN)));
+ }
+
+ @Test
+ @DisplayName("HTTP 403: not admin")
+ public void res403_notAdmin() {
+ // GIVEN
+ RequestEntity> request = RequestEntity
+ .delete(url())
+ .header(HttpHeaders.AUTHORIZATION, NIKOLA_TOKEN)
+ .accept(MediaType.APPLICATION_JSON)
+ .build();
+
+ // WHEN
+ ResponseEntity result = client.exchange(request, responseType);
+
+ // THEN
+ assertThat("It is forbidden for non-admin users", result.getStatusCode(), is(equalTo(HttpStatus.FORBIDDEN)));
+ }
+}
diff --git a/src/test/java/nl/dtls/fairdatapoint/acceptance/index/settings/List_GET.java b/src/test/java/nl/dtls/fairdatapoint/acceptance/index/settings/List_GET.java
new file mode 100644
index 000000000..574830827
--- /dev/null
+++ b/src/test/java/nl/dtls/fairdatapoint/acceptance/index/settings/List_GET.java
@@ -0,0 +1,176 @@
+/**
+ * The MIT License
+ * Copyright © 2017 DTL
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+package nl.dtls.fairdatapoint.acceptance.index.settings;
+
+import nl.dtls.fairdatapoint.WebIntegrationTest;
+import nl.dtls.fairdatapoint.api.dto.index.settings.IndexSettingsDTO;
+import nl.dtls.fairdatapoint.database.mongo.repository.IndexSettingsRepository;
+import nl.dtls.fairdatapoint.entity.index.settings.IndexSettings;
+import nl.dtls.fairdatapoint.entity.index.settings.IndexSettingsPing;
+import nl.dtls.fairdatapoint.entity.index.settings.IndexSettingsRetrieval;
+import org.junit.jupiter.api.DisplayName;
+import org.junit.jupiter.api.Test;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.core.ParameterizedTypeReference;
+import org.springframework.http.*;
+
+import java.net.URI;
+import java.time.Duration;
+import java.util.Collections;
+import java.util.Objects;
+
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.hamcrest.core.Is.is;
+import static org.hamcrest.core.IsEqual.equalTo;
+import static org.hamcrest.core.IsNull.notNullValue;
+
+@DisplayName("GET /index/settings")
+public class List_GET extends WebIntegrationTest {
+
+ @Autowired
+ private IndexSettingsRepository indexSettingsRepository;
+
+ private final ParameterizedTypeReference responseType =
+ new ParameterizedTypeReference<>() {
+ };
+
+ private URI url() {
+ return URI.create("/index/settings");
+ }
+
+ private IndexSettings customSettings() {
+ return new IndexSettings()
+ .toBuilder()
+ .ping(
+ new IndexSettingsPing()
+ .toBuilder()
+ .denyList(Collections.singletonList("http://localhost.*$"))
+ .rateLimitDuration(Duration.ofMinutes(17))
+ .validDuration(Duration.ofDays(5))
+ .rateLimitHits(666)
+ .build()
+ )
+ .retrieval(
+ new IndexSettingsRetrieval()
+ .toBuilder()
+ .rateLimitWait(Duration.ofHours(16))
+ .timeout(Duration.ofSeconds(55))
+ .build()
+ )
+ .build();
+ }
+
+ @Test
+ @DisplayName("HTTP 200: default settings")
+ public void res200_defaultSettings() {
+ // GIVEN: prepare data
+ IndexSettings settings = IndexSettings.getDefault();
+ indexSettingsRepository.deleteAll();
+
+ // AND: prepare request
+ RequestEntity> request = RequestEntity
+ .get(url())
+ .header(HttpHeaders.AUTHORIZATION, ADMIN_TOKEN)
+ .accept(MediaType.APPLICATION_JSON)
+ .build();
+
+ // WHEN
+ ResponseEntity result = client.exchange(request, responseType);
+
+ // THEN
+ assertThat("No settings are created", indexSettingsRepository.findAll().size(), is(equalTo(0)));
+ assertThat("Correct response code is received", result.getStatusCode(), is(equalTo(HttpStatus.OK)));
+ assertThat("Response body is not null", result.getBody(), is(notNullValue()));
+ assertThat("Response contains default valid duration", Objects.requireNonNull(result.getBody()).getPing().getValidDuration(), is(equalTo(settings.getPing().getValidDuration().toString())));
+ assertThat("Response contains default rate limit duration", Objects.requireNonNull(result.getBody()).getPing().getRateLimitDuration(), is(equalTo(settings.getPing().getRateLimitDuration().toString())));
+ assertThat("Response contains default rate limit hits", Objects.requireNonNull(result.getBody()).getPing().getRateLimitHits(), is(equalTo(settings.getPing().getRateLimitHits())));
+ assertThat("Response contains default deny list", Objects.requireNonNull(result.getBody()).getPing().getDenyList(), is(equalTo(settings.getPing().getDenyList())));
+ assertThat("Response contains default timeout", Objects.requireNonNull(result.getBody()).getRetrieval().getTimeout(), is(equalTo(settings.getRetrieval().getTimeout().toString())));
+ assertThat("Response contains default rate limit wait", Objects.requireNonNull(result.getBody()).getRetrieval().getRateLimitWait(), is(equalTo(settings.getRetrieval().getRateLimitWait().toString())));
+ assertThat("Response indicated default settings", Objects.requireNonNull(result.getBody()).getIsDefault(), is(Boolean.TRUE));
+ }
+
+ @Test
+ @DisplayName("HTTP 200: custom settings")
+ public void res200_customSettings() {
+ // GIVEN: prepare data
+ IndexSettings settings = customSettings();
+ indexSettingsRepository.deleteAll();
+ indexSettingsRepository.insert(settings);
+
+ // AND: prepare request
+ RequestEntity> request = RequestEntity
+ .get(url())
+ .header(HttpHeaders.AUTHORIZATION, ADMIN_TOKEN)
+ .accept(MediaType.APPLICATION_JSON)
+ .build();
+
+ // WHEN
+ ResponseEntity result = client.exchange(request, responseType);
+
+ // THEN
+ assertThat("No settings are created", indexSettingsRepository.findAll().size(), is(equalTo(1)));
+ assertThat("Correct response code is received", result.getStatusCode(), is(equalTo(HttpStatus.OK)));
+ assertThat("Response body is not null", result.getBody(), is(notNullValue()));
+ assertThat("Response contains custom valid duration", Objects.requireNonNull(result.getBody()).getPing().getValidDuration(), is(equalTo(settings.getPing().getValidDuration().toString())));
+ assertThat("Response contains custom rate limit duration", Objects.requireNonNull(result.getBody()).getPing().getRateLimitDuration(), is(equalTo(settings.getPing().getRateLimitDuration().toString())));
+ assertThat("Response contains custom rate limit hits", Objects.requireNonNull(result.getBody()).getPing().getRateLimitHits(), is(equalTo(settings.getPing().getRateLimitHits())));
+ assertThat("Response contains custom deny list", Objects.requireNonNull(result.getBody()).getPing().getDenyList(), is(equalTo(settings.getPing().getDenyList())));
+ assertThat("Response contains custom timeout", Objects.requireNonNull(result.getBody()).getRetrieval().getTimeout(), is(equalTo(settings.getRetrieval().getTimeout().toString())));
+ assertThat("Response contains custom rate limit wait", Objects.requireNonNull(result.getBody()).getRetrieval().getRateLimitWait(), is(equalTo(settings.getRetrieval().getRateLimitWait().toString())));
+ assertThat("Response indicated non-default settings", Objects.requireNonNull(result.getBody()).getIsDefault(), is(Boolean.FALSE));
+ }
+
+ @Test
+ @DisplayName("HTTP 403: no token")
+ public void res403_noToken() {
+ // GIVEN
+ RequestEntity> request = RequestEntity
+ .get(url())
+ .accept(MediaType.APPLICATION_JSON)
+ .build();
+
+ // WHEN
+ ResponseEntity result = client.exchange(request, responseType);
+
+ // THEN
+ assertThat("It is forbidden without auth", result.getStatusCode(), is(equalTo(HttpStatus.FORBIDDEN)));
+ }
+
+ @Test
+ @DisplayName("HTTP 403: not admin")
+ public void res403_notAdmin() {
+ // GIVEN
+ RequestEntity> request = RequestEntity
+ .get(url())
+ .header(HttpHeaders.AUTHORIZATION, NIKOLA_TOKEN)
+ .accept(MediaType.APPLICATION_JSON)
+ .build();
+
+ // WHEN
+ ResponseEntity result = client.exchange(request, responseType);
+
+ // THEN
+ assertThat("It is forbidden for non-admin users", result.getStatusCode(), is(equalTo(HttpStatus.FORBIDDEN)));
+ }
+}
diff --git a/src/test/java/nl/dtls/fairdatapoint/acceptance/index/settings/List_PUT.java b/src/test/java/nl/dtls/fairdatapoint/acceptance/index/settings/List_PUT.java
new file mode 100644
index 000000000..b00ce2255
--- /dev/null
+++ b/src/test/java/nl/dtls/fairdatapoint/acceptance/index/settings/List_PUT.java
@@ -0,0 +1,273 @@
+/**
+ * The MIT License
+ * Copyright © 2017 DTL
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+package nl.dtls.fairdatapoint.acceptance.index.settings;
+
+import nl.dtls.fairdatapoint.WebIntegrationTest;
+import nl.dtls.fairdatapoint.api.dto.index.settings.IndexSettingsDTO;
+import nl.dtls.fairdatapoint.api.dto.index.settings.IndexSettingsPingDTO;
+import nl.dtls.fairdatapoint.api.dto.index.settings.IndexSettingsRetrievalDTO;
+import nl.dtls.fairdatapoint.api.dto.index.settings.IndexSettingsUpdateDTO;
+import nl.dtls.fairdatapoint.database.mongo.repository.IndexSettingsRepository;
+import nl.dtls.fairdatapoint.entity.index.settings.IndexSettings;
+import nl.dtls.fairdatapoint.entity.index.settings.IndexSettingsPing;
+import nl.dtls.fairdatapoint.entity.index.settings.IndexSettingsRetrieval;
+import org.junit.jupiter.api.DisplayName;
+import org.junit.jupiter.api.Test;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.core.ParameterizedTypeReference;
+import org.springframework.http.*;
+
+import java.net.URI;
+import java.time.Duration;
+import java.util.Collections;
+import java.util.Objects;
+
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.hamcrest.core.Is.is;
+import static org.hamcrest.core.IsEqual.equalTo;
+import static org.hamcrest.core.IsNull.notNullValue;
+
+@DisplayName("PUT /index/settings")
+public class List_PUT extends WebIntegrationTest {
+
+ @Autowired
+ private IndexSettingsRepository indexSettingsRepository;
+
+ private final ParameterizedTypeReference responseType =
+ new ParameterizedTypeReference<>() {
+ };
+
+ private URI url() {
+ return URI.create("/index/settings");
+ }
+
+ private IndexSettings customSettings1() {
+ return new IndexSettings()
+ .toBuilder()
+ .ping(
+ new IndexSettingsPing()
+ .toBuilder()
+ .denyList(Collections.singletonList("http://localhost.*$"))
+ .rateLimitDuration(Duration.ofMinutes(17))
+ .validDuration(Duration.ofDays(5))
+ .rateLimitHits(666)
+ .build()
+ )
+ .retrieval(
+ new IndexSettingsRetrieval()
+ .toBuilder()
+ .rateLimitWait(Duration.ofHours(16))
+ .timeout(Duration.ofSeconds(55))
+ .build()
+ )
+ .build();
+ }
+
+ private IndexSettings customSettings2() {
+ IndexSettings settings = customSettings1();
+ settings.getPing().setValidDuration(Duration.ofDays(14));
+ settings.getRetrieval().setTimeout(Duration.ofMinutes(2));
+ return settings;
+ }
+
+ private IndexSettingsUpdateDTO customSettingsUpdateDTO() {
+ IndexSettings customSettings = customSettings1();
+ IndexSettingsUpdateDTO dto = new IndexSettingsUpdateDTO();
+ IndexSettingsPingDTO pingDTO = new IndexSettingsPingDTO();
+ pingDTO.setDenyList(customSettings.getPing().getDenyList());
+ pingDTO.setRateLimitDuration(customSettings.getPing().getRateLimitDuration().toString());
+ pingDTO.setValidDuration(customSettings.getPing().getValidDuration().toString());
+ pingDTO.setRateLimitHits(customSettings.getPing().getRateLimitHits());
+ dto.setPing(pingDTO);
+ IndexSettingsRetrievalDTO retrievalDTO = new IndexSettingsRetrievalDTO();
+ retrievalDTO.setRateLimitWait(customSettings.getRetrieval().getRateLimitWait().toString());
+ retrievalDTO.setTimeout(customSettings.getRetrieval().getTimeout().toString());
+ dto.setRetrieval(retrievalDTO);
+ return dto;
+ }
+
+ private IndexSettingsUpdateDTO invalidUpdateDTO1() {
+ IndexSettingsUpdateDTO dto = customSettingsUpdateDTO();
+ dto.getPing().setValidDuration("666");
+ return dto;
+ }
+
+ private IndexSettingsUpdateDTO invalidUpdateDTO2() {
+ IndexSettingsUpdateDTO dto = customSettingsUpdateDTO();
+ dto.getPing().setDenyList(null);
+ return dto;
+ }
+
+ @Test
+ @DisplayName("HTTP 200: update settings from defaults")
+ public void res200_updateSettingsFromDefaults() {
+ // GIVEN: prepare data
+ IndexSettings settings = customSettings1();
+ indexSettingsRepository.deleteAll();
+
+ // AND: prepare request
+ RequestEntity> request = RequestEntity
+ .put(url())
+ .header(HttpHeaders.AUTHORIZATION, ADMIN_TOKEN)
+ .contentType(MediaType.APPLICATION_JSON)
+ .accept(MediaType.APPLICATION_JSON)
+ .body(customSettingsUpdateDTO());
+
+ // WHEN
+ ResponseEntity result = client.exchange(request, responseType);
+
+ // THEN
+ assertThat("Settings are created", indexSettingsRepository.findAll().size(), is(equalTo(1)));
+ assertThat("Correct response code is received", result.getStatusCode(), is(equalTo(HttpStatus.OK)));
+ assertThat("Response body is not null", result.getBody(), is(notNullValue()));
+ assertThat("Response contains default valid duration", Objects.requireNonNull(result.getBody()).getPing().getValidDuration(), is(equalTo(settings.getPing().getValidDuration().toString())));
+ assertThat("Response contains default rate limit duration", Objects.requireNonNull(result.getBody()).getPing().getRateLimitDuration(), is(equalTo(settings.getPing().getRateLimitDuration().toString())));
+ assertThat("Response contains default rate limit hits", Objects.requireNonNull(result.getBody()).getPing().getRateLimitHits(), is(equalTo(settings.getPing().getRateLimitHits())));
+ assertThat("Response contains default deny list", Objects.requireNonNull(result.getBody()).getPing().getDenyList(), is(equalTo(settings.getPing().getDenyList())));
+ assertThat("Response contains default timeout", Objects.requireNonNull(result.getBody()).getRetrieval().getTimeout(), is(equalTo(settings.getRetrieval().getTimeout().toString())));
+ assertThat("Response contains default rate limit wait", Objects.requireNonNull(result.getBody()).getRetrieval().getRateLimitWait(), is(equalTo(settings.getRetrieval().getRateLimitWait().toString())));
+ }
+
+ @Test
+ @DisplayName("HTTP 200: update settings from custom")
+ public void res200_updateSettingsFromCustom() {
+ // GIVEN: prepare data
+ IndexSettingsUpdateDTO reqDTO = customSettingsUpdateDTO();
+ IndexSettings settings = customSettings1();
+ indexSettingsRepository.deleteAll();
+ indexSettingsRepository.insert(customSettings2());
+
+ // AND: prepare request
+ RequestEntity> request = RequestEntity
+ .put(url())
+ .header(HttpHeaders.AUTHORIZATION, ADMIN_TOKEN)
+ .contentType(MediaType.APPLICATION_JSON)
+ .accept(MediaType.APPLICATION_JSON)
+ .body(reqDTO);
+
+ // WHEN
+ ResponseEntity result = client.exchange(request, responseType);
+
+ // THEN
+ assertThat("Settings are created", indexSettingsRepository.findAll().size(), is(equalTo(1)));
+ assertThat("Correct response code is received", result.getStatusCode(), is(equalTo(HttpStatus.OK)));
+ assertThat("Response body is not null", result.getBody(), is(notNullValue()));
+ assertThat("Response contains default valid duration", Objects.requireNonNull(result.getBody()).getPing().getValidDuration(), is(equalTo(settings.getPing().getValidDuration().toString())));
+ assertThat("Response contains default rate limit duration", Objects.requireNonNull(result.getBody()).getPing().getRateLimitDuration(), is(equalTo(settings.getPing().getRateLimitDuration().toString())));
+ assertThat("Response contains default rate limit hits", Objects.requireNonNull(result.getBody()).getPing().getRateLimitHits(), is(equalTo(settings.getPing().getRateLimitHits())));
+ assertThat("Response contains default deny list", Objects.requireNonNull(result.getBody()).getPing().getDenyList(), is(equalTo(settings.getPing().getDenyList())));
+ assertThat("Response contains default timeout", Objects.requireNonNull(result.getBody()).getRetrieval().getTimeout(), is(equalTo(settings.getRetrieval().getTimeout().toString())));
+ assertThat("Response contains default rate limit wait", Objects.requireNonNull(result.getBody()).getRetrieval().getRateLimitWait(), is(equalTo(settings.getRetrieval().getRateLimitWait().toString())));
+ }
+
+ @Test
+ @DisplayName("HTTP 400: invalid duration format")
+ public void res400_invalidDurationFormat() {
+ // GIVEN: prepare data
+ IndexSettingsUpdateDTO reqDTO = invalidUpdateDTO1();
+ indexSettingsRepository.deleteAll();
+
+ // AND: prepare request
+ RequestEntity> request = RequestEntity
+ .put(url())
+ .header(HttpHeaders.AUTHORIZATION, ADMIN_TOKEN)
+ .contentType(MediaType.APPLICATION_JSON)
+ .accept(MediaType.APPLICATION_JSON)
+ .body(reqDTO);
+
+ // WHEN
+ ResponseEntity result = client.exchange(request, responseType);
+
+ // THEN
+ assertThat("It indicates bad request", result.getStatusCode(), is(equalTo(HttpStatus.BAD_REQUEST)));
+ assertThat("No settings are created", indexSettingsRepository.findAll().size(), is(equalTo(0)));
+ }
+
+ @Test
+ @DisplayName("HTTP 400: invalid list")
+ public void res400_invalidList() {
+ // GIVEN: prepare data
+ IndexSettingsUpdateDTO reqDTO = invalidUpdateDTO2();
+ indexSettingsRepository.deleteAll();
+
+ // AND: prepare request
+ RequestEntity> request = RequestEntity
+ .put(url())
+ .header(HttpHeaders.AUTHORIZATION, ADMIN_TOKEN)
+ .contentType(MediaType.APPLICATION_JSON)
+ .accept(MediaType.APPLICATION_JSON)
+ .body(reqDTO);
+
+ // WHEN
+ ResponseEntity result = client.exchange(request, responseType);
+
+ // THEN
+ assertThat("It indicates bad request", result.getStatusCode(), is(equalTo(HttpStatus.BAD_REQUEST)));
+ assertThat("No settings are created", indexSettingsRepository.findAll().size(), is(equalTo(0)));
+ }
+
+ @Test
+ @DisplayName("HTTP 403: no token")
+ public void res403_noToken() {
+ // GIVEN: prepare data
+ IndexSettingsUpdateDTO reqDTO = customSettingsUpdateDTO();
+ indexSettingsRepository.deleteAll();
+
+ // AND: prepare request
+ RequestEntity> request = RequestEntity
+ .put(url())
+ .contentType(MediaType.APPLICATION_JSON)
+ .accept(MediaType.APPLICATION_JSON)
+ .body(reqDTO);
+
+ // WHEN
+ ResponseEntity result = client.exchange(request, responseType);
+
+ // THEN
+ assertThat("It is forbidden without auth", result.getStatusCode(), is(equalTo(HttpStatus.FORBIDDEN)));
+ assertThat("No settings are created", indexSettingsRepository.findAll().size(), is(equalTo(0)));
+ }
+
+ @Test
+ @DisplayName("HTTP 403: not admin")
+ public void res403_notAdmin() {
+ // GIVEN: prepare data
+ IndexSettingsUpdateDTO reqDTO = customSettingsUpdateDTO();
+ indexSettingsRepository.deleteAll();
+
+ // AND: prepare request
+ RequestEntity> request = RequestEntity
+ .put(url())
+ .header(HttpHeaders.AUTHORIZATION, NIKOLA_TOKEN)
+ .contentType(MediaType.APPLICATION_JSON)
+ .accept(MediaType.APPLICATION_JSON)
+ .body(reqDTO);
+
+ // WHEN
+ ResponseEntity result = client.exchange(request, responseType);
+
+ // THEN
+ assertThat("It is forbidden for non-admin users", result.getStatusCode(), is(equalTo(HttpStatus.FORBIDDEN)));
+ assertThat("No settings are created", indexSettingsRepository.findAll().size(), is(equalTo(0)));
+ }
+}
diff --git a/src/test/resources/application-testing.yml b/src/test/resources/application-testing.yml
index 023d98d40..5a3ca6053 100644
--- a/src/test/resources/application-testing.yml
+++ b/src/test/resources/application-testing.yml
@@ -1,8 +1,8 @@
instance:
- clientUrl: http://localhost:8084
+ clientUrl: http://localhost:8088
server:
- port: 8084
+ port: 8088
spring:
data:
@@ -14,12 +14,5 @@ security:
token:
expiration: 9999
-
fdp-index:
enabled: true
- events:
- retrieval:
- rateLimitWait: PT10M # 10 minutes (ISO 8601)
- timeout: PT1M # 1 minute (ISO 8601)
- ping:
- validDuration: P7D # 7 days (ISO 8601)
\ No newline at end of file