Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[WIP] HTM-1076 | HTM-1077: TMFeatureType event handler to propagate changes to Solr indexes #844

Draft
wants to merge 1 commit into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,6 @@ public class SolrAdminController {
private static final Logger logger =
LoggerFactory.getLogger(MethodHandles.lookup().lookupClass());
private final FeatureSourceFactoryHelper featureSourceFactoryHelper;

private final FeatureTypeRepository featureTypeRepository;
private final SearchIndexRepository searchIndexRepository;
private final SolrService solrService;
Expand Down Expand Up @@ -75,7 +74,7 @@ public SolrAdminController(
/**
* Ping solr.
*
* @return the response entity (ok or an error response)
* @return the response entity (OK or an error response)
*/
@Operation(summary = "Ping Solr", description = "Ping Solr to check if it is available")
@ApiResponse(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,11 @@
*/
package org.tailormap.api.repository;

import java.util.List;
import java.util.Optional;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.Query;
import org.springframework.data.repository.query.Param;
import org.springframework.lang.NonNull;
import org.springframework.security.access.prepost.PreAuthorize;
import org.tailormap.api.persistence.Application;
Expand All @@ -21,4 +24,17 @@ public interface ApplicationRepository extends JpaRepository<Application, Long>
@Override
@NonNull
Optional<Application> findById(@NonNull Long aLong);

/**
* Find all applications that have a layer that is linked to a specific (Solr) index.
*
* @param indexId The index id to search for
*/
@NonNull
@PreAuthorize("permitAll()")
@Query(
value =
"select * from application app, lateral jsonb_path_query(app.settings, ('$.layerSettings.**{1}.searchIndexId ? (@ == '||:indexId||')')::jsonpath)",
nativeQuery = true)
List<Application> findByIndexId(@Param("indexId") @NonNull Long indexId);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,139 @@
/*
* Copyright (C) 2024 B3Partners B.V.
*
* SPDX-License-Identifier: MIT
*/
package org.tailormap.api.repository.events;

import java.io.IOException;
import java.lang.invoke.MethodHandles;
import org.apache.solr.client.solrj.SolrServerException;
import org.apache.solr.common.SolrException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.data.rest.core.annotation.HandleAfterDelete;
import org.springframework.data.rest.core.annotation.HandleBeforeSave;
import org.springframework.data.rest.core.annotation.RepositoryEventHandler;
import org.tailormap.api.geotools.featuresources.FeatureSourceFactoryHelper;
import org.tailormap.api.persistence.SearchIndex;
import org.tailormap.api.persistence.TMFeatureType;
import org.tailormap.api.persistence.json.AppLayerSettings;
import org.tailormap.api.repository.ApplicationRepository;
import org.tailormap.api.repository.SearchIndexRepository;
import org.tailormap.api.solr.SolrHelper;
import org.tailormap.api.solr.SolrService;

/** Event handler for Solr indexes when a {@code TMFeatureType} is updated or deleted. */
@RepositoryEventHandler
public class SolrTMFeatureTypeEventHandler {

private static final Logger logger =
LoggerFactory.getLogger(MethodHandles.lookup().lookupClass());

Check warning on line 31 in src/main/java/org/tailormap/api/repository/events/SolrTMFeatureTypeEventHandler.java

View check run for this annotation

Codecov / codecov/patch

src/main/java/org/tailormap/api/repository/events/SolrTMFeatureTypeEventHandler.java#L30-L31

Added lines #L30 - L31 were not covered by tests

private final SearchIndexRepository searchIndexRepository;
private final SolrService solrService;
private final FeatureSourceFactoryHelper featureSourceFactoryHelper;
private final ApplicationRepository applicationRepository;

public SolrTMFeatureTypeEventHandler(
SearchIndexRepository searchIndexRepository,
SolrService solrService,
FeatureSourceFactoryHelper featureSourceFactoryHelper,
ApplicationRepository applicationRepository) {
this.searchIndexRepository = searchIndexRepository;
this.solrService = solrService;
this.featureSourceFactoryHelper = featureSourceFactoryHelper;
this.applicationRepository = applicationRepository;
}

Check warning on line 47 in src/main/java/org/tailormap/api/repository/events/SolrTMFeatureTypeEventHandler.java

View check run for this annotation

Codecov / codecov/patch

src/main/java/org/tailormap/api/repository/events/SolrTMFeatureTypeEventHandler.java#L42-L47

Added lines #L42 - L47 were not covered by tests

/**
* Handle the update of a TMFeatureType.
*
* @param tmFeatureType the TMFeatureType to handle
*/
@HandleBeforeSave
public void handleTMFeatureTypeUpdate(TMFeatureType tmFeatureType) {
logger.debug("Handling TMFeatureType save event for: {}", tmFeatureType);

Check warning on line 56 in src/main/java/org/tailormap/api/repository/events/SolrTMFeatureTypeEventHandler.java

View check run for this annotation

Codecov / codecov/patch

src/main/java/org/tailormap/api/repository/events/SolrTMFeatureTypeEventHandler.java#L56

Added line #L56 was not covered by tests
// determine if it is a new FT or an update
if (null == tmFeatureType.getId()) {
// do nothing as there is no index defined for a new feature type
logger.debug("New TMFeatureType: {}", tmFeatureType);

Check warning on line 60 in src/main/java/org/tailormap/api/repository/events/SolrTMFeatureTypeEventHandler.java

View check run for this annotation

Codecov / codecov/patch

src/main/java/org/tailormap/api/repository/events/SolrTMFeatureTypeEventHandler.java#L60

Added line #L60 was not covered by tests
} else {
logger.debug("Updated TMFeatureType: {}", tmFeatureType);
searchIndexRepository.findByFeatureTypeId(tmFeatureType.getId()).stream()
.findAny()
.ifPresent(

Check warning on line 65 in src/main/java/org/tailormap/api/repository/events/SolrTMFeatureTypeEventHandler.java

View check run for this annotation

Codecov / codecov/patch

src/main/java/org/tailormap/api/repository/events/SolrTMFeatureTypeEventHandler.java#L62-L65

Added lines #L62 - L65 were not covered by tests
searchIndex -> {
logger.debug(

Check warning on line 67 in src/main/java/org/tailormap/api/repository/events/SolrTMFeatureTypeEventHandler.java

View check run for this annotation

Codecov / codecov/patch

src/main/java/org/tailormap/api/repository/events/SolrTMFeatureTypeEventHandler.java#L67

Added line #L67 was not covered by tests
"Updating search index {} for feature type: {}",
searchIndex.getName(),

Check warning on line 69 in src/main/java/org/tailormap/api/repository/events/SolrTMFeatureTypeEventHandler.java

View check run for this annotation

Codecov / codecov/patch

src/main/java/org/tailormap/api/repository/events/SolrTMFeatureTypeEventHandler.java#L69

Added line #L69 was not covered by tests
searchIndex);

try (SolrHelper solrHelper =
new SolrHelper(solrService.getSolrClientForIndexing())) {
solrHelper.addFeatureTypeIndex(

Check warning on line 74 in src/main/java/org/tailormap/api/repository/events/SolrTMFeatureTypeEventHandler.java

View check run for this annotation

Codecov / codecov/patch

src/main/java/org/tailormap/api/repository/events/SolrTMFeatureTypeEventHandler.java#L72-L74

Added lines #L72 - L74 were not covered by tests
searchIndex,
tmFeatureType,
featureSourceFactoryHelper,
searchIndexRepository);
} catch (UnsupportedOperationException

Check warning on line 79 in src/main/java/org/tailormap/api/repository/events/SolrTMFeatureTypeEventHandler.java

View check run for this annotation

Codecov / codecov/patch

src/main/java/org/tailormap/api/repository/events/SolrTMFeatureTypeEventHandler.java#L79

Added line #L79 was not covered by tests
| IOException
| SolrServerException
| SolrException e) {
logger.error("Error re-indexing", e);
searchIndex.setStatus(SearchIndex.Status.ERROR);
searchIndexRepository.save(searchIndex);
}
});

Check warning on line 87 in src/main/java/org/tailormap/api/repository/events/SolrTMFeatureTypeEventHandler.java

View check run for this annotation

Codecov / codecov/patch

src/main/java/org/tailormap/api/repository/events/SolrTMFeatureTypeEventHandler.java#L83-L87

Added lines #L83 - L87 were not covered by tests
}
}

Check warning on line 89 in src/main/java/org/tailormap/api/repository/events/SolrTMFeatureTypeEventHandler.java

View check run for this annotation

Codecov / codecov/patch

src/main/java/org/tailormap/api/repository/events/SolrTMFeatureTypeEventHandler.java#L89

Added line #L89 was not covered by tests

/**
* Handle the deletion of a TMFeatureType.
*
* @param tmFeatureType the TMFeatureType to handle
*/
@HandleAfterDelete
public void handleTMFeatureTypeDeleteForSolr(TMFeatureType tmFeatureType) {
logger.debug("Handling TMFeatureType delete event for: {}", tmFeatureType);
searchIndexRepository.findByFeatureTypeId(tmFeatureType.getId()).stream()
.findAny()
.ifPresent(

Check warning on line 101 in src/main/java/org/tailormap/api/repository/events/SolrTMFeatureTypeEventHandler.java

View check run for this annotation

Codecov / codecov/patch

src/main/java/org/tailormap/api/repository/events/SolrTMFeatureTypeEventHandler.java#L98-L101

Added lines #L98 - L101 were not covered by tests
searchIndex -> {
logger.info(

Check warning on line 103 in src/main/java/org/tailormap/api/repository/events/SolrTMFeatureTypeEventHandler.java

View check run for this annotation

Codecov / codecov/patch

src/main/java/org/tailormap/api/repository/events/SolrTMFeatureTypeEventHandler.java#L103

Added line #L103 was not covered by tests
"Deleting search index {} for feature type: {}",
searchIndex.getName(),

Check warning on line 105 in src/main/java/org/tailormap/api/repository/events/SolrTMFeatureTypeEventHandler.java

View check run for this annotation

Codecov / codecov/patch

src/main/java/org/tailormap/api/repository/events/SolrTMFeatureTypeEventHandler.java#L105

Added line #L105 was not covered by tests
searchIndex);

try (SolrHelper solrHelper = new SolrHelper(solrService.getSolrClientForIndexing())) {
solrHelper.clearIndexForLayer(searchIndex.getId());
searchIndexRepository.delete(searchIndex);

Check warning on line 110 in src/main/java/org/tailormap/api/repository/events/SolrTMFeatureTypeEventHandler.java

View check run for this annotation

Codecov / codecov/patch

src/main/java/org/tailormap/api/repository/events/SolrTMFeatureTypeEventHandler.java#L108-L110

Added lines #L108 - L110 were not covered by tests
// find any application layers that use this index clear the index from them
applicationRepository
.findByIndexId(searchIndex.getId())
.forEach(

Check warning on line 114 in src/main/java/org/tailormap/api/repository/events/SolrTMFeatureTypeEventHandler.java

View check run for this annotation

Codecov / codecov/patch

src/main/java/org/tailormap/api/repository/events/SolrTMFeatureTypeEventHandler.java#L112-L114

Added lines #L112 - L114 were not covered by tests
application -> {
application
.getAllAppTreeLayerNode()
.forEach(

Check warning on line 118 in src/main/java/org/tailormap/api/repository/events/SolrTMFeatureTypeEventHandler.java

View check run for this annotation

Codecov / codecov/patch

src/main/java/org/tailormap/api/repository/events/SolrTMFeatureTypeEventHandler.java#L116-L118

Added lines #L116 - L118 were not covered by tests
appTreeLayerNode -> {
AppLayerSettings appLayerSettings =
application.getAppLayerSettings(appTreeLayerNode);

Check warning on line 121 in src/main/java/org/tailormap/api/repository/events/SolrTMFeatureTypeEventHandler.java

View check run for this annotation

Codecov / codecov/patch

src/main/java/org/tailormap/api/repository/events/SolrTMFeatureTypeEventHandler.java#L120-L121

Added lines #L120 - L121 were not covered by tests
if (null != appLayerSettings.getSearchIndexId()
&& appLayerSettings
.getSearchIndexId()

Check warning on line 124 in src/main/java/org/tailormap/api/repository/events/SolrTMFeatureTypeEventHandler.java

View check run for this annotation

Codecov / codecov/patch

src/main/java/org/tailormap/api/repository/events/SolrTMFeatureTypeEventHandler.java#L124

Added line #L124 was not covered by tests
.equals(searchIndex.getId())) {
appLayerSettings.setSearchIndexId(null);

Check warning on line 126 in src/main/java/org/tailormap/api/repository/events/SolrTMFeatureTypeEventHandler.java

View check run for this annotation

Codecov / codecov/patch

src/main/java/org/tailormap/api/repository/events/SolrTMFeatureTypeEventHandler.java#L126

Added line #L126 was not covered by tests
}
});
applicationRepository.save(application);
});
} catch (UnsupportedOperationException

Check warning on line 131 in src/main/java/org/tailormap/api/repository/events/SolrTMFeatureTypeEventHandler.java

View check run for this annotation

Codecov / codecov/patch

src/main/java/org/tailormap/api/repository/events/SolrTMFeatureTypeEventHandler.java#L128-L131

Added lines #L128 - L131 were not covered by tests
| IOException
| SolrServerException
| SolrException e) {
logger.error("Error deleting index for {}", searchIndex, e);
}
});
}

Check warning on line 138 in src/main/java/org/tailormap/api/repository/events/SolrTMFeatureTypeEventHandler.java

View check run for this annotation

Codecov / codecov/patch

src/main/java/org/tailormap/api/repository/events/SolrTMFeatureTypeEventHandler.java#L135-L138

Added lines #L135 - L138 were not covered by tests
}
2 changes: 1 addition & 1 deletion src/main/java/org/tailormap/api/solr/SolrHelper.java
Original file line number Diff line number Diff line change
Expand Up @@ -107,7 +107,7 @@ public class SolrHelper implements AutoCloseable, Constants {
/**
* Create a configured {@code SolrHelper} object.
*
* @param solrClient the Solr client, this will be closed when this class is closed
* @param solrClient the Solr client, this client will be closed when this class is closed.
*/
public SolrHelper(@NotNull SolrClient solrClient) {
this.solrClient = solrClient;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,9 @@
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertNotNull;
import static org.junit.jupiter.api.Assertions.assertNull;
import static org.junit.jupiter.api.Assertions.assertTrue;

import java.util.List;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.tailormap.api.annotation.PostgresIntegrationTest;
Expand All @@ -30,4 +32,17 @@ void should_not_find_application_by_nonexistent_name() {
Application a = applicationRepository.findByName("does-not-exist");
assertNull(a);
}

@Test
void it_should_find_application_using_findByIndexId_with_valid_ID() {
final Application application = applicationRepository.findByIndexId(2L).get(0);
assertNotNull(application, "application should not be null");
assertEquals("default", application.getName(), "application name is incorrect");
}

@Test
void it_should_not_find_applications_findByIndexId_with_invalid_ID() {
final List<Application> applications = applicationRepository.findByIndexId(-2L);
assertTrue(applications.isEmpty(), "applications should be empty");
}
}
Loading