From 56a59f858fe07a4b025444fd749e6fa57e725469 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=81d=C3=A1m=20Imre=20Szab=C3=B3?= Date: Thu, 22 Feb 2024 13:38:53 +0100 Subject: [PATCH] Improve workshop-assignment --- Readme.md | 1 + docker/docker-compose.yml | 2 +- .../service/ChuckNorrisIndexingService.java | 5 ++++- .../service/DadJokeIndexingService.java | 7 +++++-- .../indexingapp/service/IndexingService.java | 14 +++++++++----- .../indexingapp/web/IndexingController.java | 15 +++++++++++---- 6 files changed, 31 insertions(+), 13 deletions(-) diff --git a/Readme.md b/Readme.md index 006734d..d8d2985 100644 --- a/Readme.md +++ b/Readme.md @@ -13,6 +13,7 @@ Some solutions can be done by adding/modifying a few lines of code, we would ask Other, larger solutions don't need implementation, just discuss it like you would discuss it with a colleague. You don't need to check for creating new, useful endpoints or other domain-changes, we are interested in what code or architectural changes would you make in the repository. +Also keep a look out for scalability and stability issues. ## Project description diff --git a/docker/docker-compose.yml b/docker/docker-compose.yml index 0248a4a..e958ce7 100644 --- a/docker/docker-compose.yml +++ b/docker/docker-compose.yml @@ -26,7 +26,7 @@ services: KAFKA_INTER_BROKER_LISTENER_NAME: PLAINTEXT KAFKA_OFFSETS_TOPIC_REPLICATION_FACTOR: 1 KAFKA_CONFLUENT_TOPIC_REPLICATION_FACTOR: 1 - KAFKA_CREATE_TOPICS: "indexing-app:1:1" + KAFKA_CREATE_TOPICS: "match-events:1:1" database: image: postgres:latest diff --git a/src/main/java/com/brandwatch/indexingapp/service/ChuckNorrisIndexingService.java b/src/main/java/com/brandwatch/indexingapp/service/ChuckNorrisIndexingService.java index 63078ff..3eef98a 100644 --- a/src/main/java/com/brandwatch/indexingapp/service/ChuckNorrisIndexingService.java +++ b/src/main/java/com/brandwatch/indexingapp/service/ChuckNorrisIndexingService.java @@ -23,6 +23,7 @@ @Service public class ChuckNorrisIndexingService extends IndexingService { + public static final String NETWORK_NAME = "chucknorris"; private final RestTemplate restTemplate = new RestTemplate(); public ChuckNorrisIndexingService(IndexingAppConfiguration configuration, ContentRepository contentRepository, QueryRepository queryRepository, KafkaTemplate kafkaTemplate, ObjectMapper objectMapper) { super(configuration, contentRepository, queryRepository, kafkaTemplate, objectMapper); @@ -30,11 +31,13 @@ public ChuckNorrisIndexingService(IndexingAppConfiguration configuration, Conten @Override public String getNetwork() { - return "chucknorris"; + return NETWORK_NAME; } @Override protected List loadNewContentsForNetwork() throws URISyntaxException { + // APIDoc: https://api-ninjas.com/api/chucknorris + HttpHeaders requestHeaders = new HttpHeaders(); requestHeaders.set("X-Api-Key", this.getConfiguration().apiKey()); RequestEntity request = new RequestEntity<>(requestHeaders, HttpMethod.GET, new URI("https://api.api-ninjas.com/v1/chucknorris")); diff --git a/src/main/java/com/brandwatch/indexingapp/service/DadJokeIndexingService.java b/src/main/java/com/brandwatch/indexingapp/service/DadJokeIndexingService.java index 6cc49fc..5a1f1a5 100644 --- a/src/main/java/com/brandwatch/indexingapp/service/DadJokeIndexingService.java +++ b/src/main/java/com/brandwatch/indexingapp/service/DadJokeIndexingService.java @@ -26,6 +26,7 @@ @Service public class DadJokeIndexingService extends IndexingService { + public static final String NETWORK_NAME = "dadjoke"; private final RestTemplate restTemplate = new RestTemplate(); public DadJokeIndexingService(IndexingAppConfiguration configuration, ContentRepository contentRepository, QueryRepository queryRepository, KafkaTemplate kafkaTemplate, ObjectMapper objectMapper) { super(configuration, contentRepository, queryRepository, kafkaTemplate, objectMapper); @@ -33,18 +34,20 @@ public DadJokeIndexingService(IndexingAppConfiguration configuration, ContentRep @Override public String getNetwork() { - return "dadjoke"; + return NETWORK_NAME; } @Override protected List loadNewContentsForNetwork() throws URISyntaxException { + // APIdoc: https://api-ninjas.com/api/dadjokes + HttpHeaders requestHeaders = new HttpHeaders(); requestHeaders.set("X-Api-Key", this.getConfiguration().apiKey()); RequestEntity request = new RequestEntity<>(requestHeaders, HttpMethod.GET, new URI("https://api.api-ninjas.com/v1/dadjokes?limit=5")); ResponseEntity response = this.restTemplate.exchange(request, List.class); List responseBody = response.getBody(); return responseBody.stream() - .map(element -> new Content(null, Instant.now(), "dadjoke", String.valueOf(element.get("joke")))) + .map(element -> new Content(null, Instant.now(), NETWORK_NAME, String.valueOf(element.get("joke")))) .collect(Collectors.toList()); } } diff --git a/src/main/java/com/brandwatch/indexingapp/service/IndexingService.java b/src/main/java/com/brandwatch/indexingapp/service/IndexingService.java index e031025..d0292e3 100644 --- a/src/main/java/com/brandwatch/indexingapp/service/IndexingService.java +++ b/src/main/java/com/brandwatch/indexingapp/service/IndexingService.java @@ -35,7 +35,11 @@ public abstract class IndexingService { public void indexAndSendNotifications() { try { List contents = this.loadNewContentsForNetwork(); - contents.forEach(this::saveContentAndSendNotifications); + contents.forEach(content -> { + Content savedContent = this.contentRepository.save(content); + List matchingQueries = getMatchingQueries(savedContent); + sendNotificationsForQueries(savedContent, matchingQueries); + }); } catch (Throwable t) { log.error("Indexing failed with error: ", t); } @@ -45,13 +49,13 @@ public List getContents() { return this.contentRepository.findByNetwork(this.getNetwork()); } - private void saveContentAndSendNotifications(Content content) { - this.contentRepository.saveAndFlush(content); - + private List getMatchingQueries(Content content) { List matchingQueries = this.queryRepository.findByNetwork(this.getNetwork()).stream() .filter(query -> content.getContent().toLowerCase().contains(query.getQueryText())) .collect(Collectors.toList()); - + return matchingQueries; + } + private void sendNotificationsForQueries(Content content, List matchingQueries) { matchingQueries.stream() .map(query -> new MatchedQueryEventDTO(query.getId(), this.getNetwork(), content.getContent())) .forEach(event -> { diff --git a/src/main/java/com/brandwatch/indexingapp/web/IndexingController.java b/src/main/java/com/brandwatch/indexingapp/web/IndexingController.java index 7ea9ba3..53f8c7d 100644 --- a/src/main/java/com/brandwatch/indexingapp/web/IndexingController.java +++ b/src/main/java/com/brandwatch/indexingapp/web/IndexingController.java @@ -12,7 +12,6 @@ import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.RestController; -import java.time.LocalDate; import java.util.List; import java.util.UUID; @@ -27,7 +26,10 @@ public class IndexingController { @GetMapping("/index/{network}/index") // Index data for the given network, and match queries for found new contents public ResponseEntity indexData(@PathVariable String network) { - new Thread(() -> this.indexingServices.stream().filter(service -> service.getNetwork().equalsIgnoreCase(network)).forEach(IndexingService::indexAndSendNotifications)).run(); + new Thread(() -> this.indexingServices.stream() + .filter(service -> service.getNetwork().equalsIgnoreCase(network)) + .forEach(IndexingService::indexAndSendNotifications)) + .run(); return ResponseEntity.ok().build(); } @@ -40,14 +42,19 @@ public ResponseEntity> getContents(@PathVariable String network) { .map(ResponseEntity::ok) .orElseGet(() -> ResponseEntity.notFound().build()); } + @PostMapping("/query/create") // Create a new query for the user - public ResponseEntity createQuery(@RequestParam String network, @RequestParam String matchText, @RequestParam String user) { + public ResponseEntity createQuery(@RequestParam String network, + @RequestParam String matchText, + @RequestParam String user) { return ResponseEntity.ok(this.queryService.create(network, matchText, user)); } + @PostMapping("/query/delete") // Delete queries based on ID or user - public ResponseEntity calculateAverages(@RequestParam(required = false) String user, @RequestParam(required = false) UUID id) { + public ResponseEntity deleteQuery(@RequestParam(required = false) String user, + @RequestParam(required = false) UUID id) { if (user == null) { this.queryService.delete(id); } else {