From 8143c7c3c8b6683519c7610269f0487d92359e28 Mon Sep 17 00:00:00 2001 From: Jeremy Date: Wed, 12 Jun 2024 12:45:55 -0500 Subject: [PATCH] adds neo4j option by default it will extract entities from the docs and store them in the neo4j graph --- Makefile | 3 +- build.gradle | 10 ++++ .../net/savantly/mainbot/api/DocumentApi.java | 5 +- .../dom/documents/DocumentsConfiguration.java | 3 +- .../mainbot/dom/neo4j/Neo4JConfig.java | 58 +++++++++++++++++++ .../dom/neo4j/Neo4JDocumentService.java | 36 ++++++++++++ .../dom/neo4j/Neo4JEntityExtractor.java | 36 ++++++++++++ .../dom/neo4j/dto/EntityExtraction.java | 18 ++++++ .../dom/neo4j/dto/Neo4JEntityPersistor.java | 22 +++++++ .../dom/neo4j/dto/Neo4JEntitySearch.java | 26 +++++++++ .../mainbot/dom/neo4j/dto/NodeDTO.java | 36 ++++++++++++ .../mainbot/dom/neo4j/dto/Relationship.java | 25 ++++++++ .../dom/neo4j/dto/query/Neo4JNodeQuery.java | 22 +++++++ .../neo4j/dto/query/Neo4JQueryGenerator.java | 10 ++++ .../mainbot/dom/neo4j/entity/PersonNode.java | 36 ++++++++++++ .../dom/neo4j/entity/PersonRepository.java | 7 +++ .../mainbot/dom/neo4j/entity/PlaceNode.java | 19 ++++++ .../dom/neo4j/entity/PlaceRepository.java | 7 +++ .../mainbot/dom/neo4j/entity/ThingNode.java | 27 +++++++++ .../dom/neo4j/entity/ThingRepository.java | 7 +++ .../mainbot/identity/AnonymousUser.java | 7 ++- .../mainbot/security/SecurityConfig.java | 2 + .../oauth2/OAuth2ClientConfiguration.java | 19 +++--- .../application-dev.properties.example | 3 +- src/main/resources/application-neo4j.yml | 10 ++++ src/main/resources/application.yml | 18 +++++- 26 files changed, 455 insertions(+), 17 deletions(-) create mode 100644 src/main/java/net/savantly/mainbot/dom/neo4j/Neo4JConfig.java create mode 100644 src/main/java/net/savantly/mainbot/dom/neo4j/Neo4JDocumentService.java create mode 100644 src/main/java/net/savantly/mainbot/dom/neo4j/Neo4JEntityExtractor.java create mode 100644 src/main/java/net/savantly/mainbot/dom/neo4j/dto/EntityExtraction.java create mode 100644 src/main/java/net/savantly/mainbot/dom/neo4j/dto/Neo4JEntityPersistor.java create mode 100644 src/main/java/net/savantly/mainbot/dom/neo4j/dto/Neo4JEntitySearch.java create mode 100644 src/main/java/net/savantly/mainbot/dom/neo4j/dto/NodeDTO.java create mode 100644 src/main/java/net/savantly/mainbot/dom/neo4j/dto/Relationship.java create mode 100644 src/main/java/net/savantly/mainbot/dom/neo4j/dto/query/Neo4JNodeQuery.java create mode 100644 src/main/java/net/savantly/mainbot/dom/neo4j/dto/query/Neo4JQueryGenerator.java create mode 100644 src/main/java/net/savantly/mainbot/dom/neo4j/entity/PersonNode.java create mode 100644 src/main/java/net/savantly/mainbot/dom/neo4j/entity/PersonRepository.java create mode 100644 src/main/java/net/savantly/mainbot/dom/neo4j/entity/PlaceNode.java create mode 100644 src/main/java/net/savantly/mainbot/dom/neo4j/entity/PlaceRepository.java create mode 100644 src/main/java/net/savantly/mainbot/dom/neo4j/entity/ThingNode.java create mode 100644 src/main/java/net/savantly/mainbot/dom/neo4j/entity/ThingRepository.java create mode 100644 src/main/resources/application-neo4j.yml diff --git a/Makefile b/Makefile index b99fb2d..c22be71 100644 --- a/Makefile +++ b/Makefile @@ -1,3 +1,4 @@ +SPRING_PROFILES_ACTIVE ?= dev .PHONY dev: dev: @@ -5,7 +6,7 @@ dev: @echo "Make sure you have a .env file in the root directory" @echo "Exporting environment variables..." $(call setup_env, .env) - SPRING_PROFILES_ACTIVE=dev,${SPRING_PROFILES_ACTIVE} ./gradlew bootRun + SPRING_PROFILES_ACTIVE=${SPRING_PROFILES_ACTIVE} ./gradlew bootRun .PHONY dev-docker: dev-docker: diff --git a/build.gradle b/build.gradle index accc216..0ae5c4b 100644 --- a/build.gradle +++ b/build.gradle @@ -24,6 +24,14 @@ test { } } + configurations { + all { + //exclude group: 'org.springframework.boot', module: 'spring-boot-starter-logging' + //exclude group: 'ch.qos.logback', module: 'logback-classic' + exclude group: 'org.apache.logging.log4j', module: 'log4j-to-slf4j' + } +} + dependencies { implementation platform('software.amazon.awssdk:bom:2.21.5') implementation 'software.amazon.awssdk:s3' @@ -66,6 +74,8 @@ dependencies { implementation 'org.opensearch.client:opensearch-java:2.9.1' + implementation 'org.springframework.boot:spring-boot-starter-data-neo4j' + implementation 'org.neo4j:neo4j-cypher-dsl:2023.9.7' runtimeOnly 'com.h2database:h2' diff --git a/src/main/java/net/savantly/mainbot/api/DocumentApi.java b/src/main/java/net/savantly/mainbot/api/DocumentApi.java index 7b49288..f43ad74 100644 --- a/src/main/java/net/savantly/mainbot/api/DocumentApi.java +++ b/src/main/java/net/savantly/mainbot/api/DocumentApi.java @@ -3,6 +3,7 @@ import java.util.List; import org.springframework.http.ResponseEntity; +import org.springframework.security.access.prepost.PreAuthorize; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RequestMapping; @@ -37,11 +38,9 @@ public ResponseEntity> addDocument(@RequestBody DocumentAddRequest return ResponseEntity.ok(documentService.addDocument(document)); } + @PreAuthorize("${authorization.addDocsExpression}") private boolean isUnauthorized(UserDto currentUser, DocumentAddRequest document) { log.info("checking authorization for user: {}", currentUser); - if (currentUser.getGroups().contains(authorizationConfig.getAdminGroup())) { - return false; - } if (currentUser.getGroups().contains(document.getNamespace())) { return false; } diff --git a/src/main/java/net/savantly/mainbot/dom/documents/DocumentsConfiguration.java b/src/main/java/net/savantly/mainbot/dom/documents/DocumentsConfiguration.java index 03763ae..735a478 100644 --- a/src/main/java/net/savantly/mainbot/dom/documents/DocumentsConfiguration.java +++ b/src/main/java/net/savantly/mainbot/dom/documents/DocumentsConfiguration.java @@ -54,7 +54,8 @@ public ChunkingConfiguration chunkingConfiguration() { @JsonFormat(shape = JsonFormat.Shape.STRING) static enum DocumentServiceImplementationType { LC4J, - OPENSEARCH + OPENSEARCH, + NEO4J } } diff --git a/src/main/java/net/savantly/mainbot/dom/neo4j/Neo4JConfig.java b/src/main/java/net/savantly/mainbot/dom/neo4j/Neo4JConfig.java new file mode 100644 index 0000000..0a739e2 --- /dev/null +++ b/src/main/java/net/savantly/mainbot/dom/neo4j/Neo4JConfig.java @@ -0,0 +1,58 @@ +package net.savantly.mainbot.dom.neo4j; + +import org.neo4j.cypherdsl.core.renderer.Dialect; +import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; +import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; +import org.springframework.boot.context.properties.ConfigurationProperties; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; + +import net.savantly.mainbot.dom.documents.DocumentService; +import net.savantly.mainbot.dom.documents.impl.LC4JDocumentService; +import net.savantly.mainbot.dom.documents.processing.DocumentProcessorManager; +import net.savantly.mainbot.dom.embeddingstore.DocumentEmbedder; +import net.savantly.mainbot.dom.embeddingstore.EmbeddingStoreProvider; +import net.savantly.mainbot.dom.neo4j.dto.Neo4JEntityPersistor; +import net.savantly.mainbot.dom.neo4j.entity.PersonRepository; +import net.savantly.mainbot.dom.neo4j.entity.PlaceRepository; +import net.savantly.mainbot.dom.neo4j.entity.ThingRepository; + +@Configuration +@ConfigurationProperties(prefix = "neo4j") +@ConditionalOnProperty(name = "documents.implementation", havingValue = "NEO4J") +public class Neo4JConfig { + + @Bean + org.neo4j.cypherdsl.core.renderer.Configuration cypherDslConfiguration() { + return org.neo4j.cypherdsl.core.renderer.Configuration.newConfig() + .withDialect(Dialect.NEO4J_5).build(); + } + + @Bean + @ConditionalOnMissingBean(Neo4JEntityExtractor.class) + public Neo4JEntityExtractor neo4JEntityExtractor() { + return new Neo4JEntityExtractor() { + }; + } + + @Bean + @ConditionalOnMissingBean(Neo4JEntityPersistor.class) + public Neo4JEntityPersistor neo4JEntityPersistor(PersonRepository personRepository, PlaceRepository placeRepository, + ThingRepository thingRepository) { + return new Neo4JEntityPersistor(personRepository, placeRepository, thingRepository); + } + + @Bean + public DocumentService documentService( + Neo4JEntityExtractor entityExtractor, + Neo4JEntityPersistor entityPersistor, + DocumentEmbedder embedder, + EmbeddingStoreProvider storeProvider, + DocumentProcessorManager processorManager) { + + var lc4jDocumentService = new LC4JDocumentService(embedder, storeProvider, processorManager); + + return new Neo4JDocumentService(entityExtractor, entityPersistor, lc4jDocumentService); + } + +} diff --git a/src/main/java/net/savantly/mainbot/dom/neo4j/Neo4JDocumentService.java b/src/main/java/net/savantly/mainbot/dom/neo4j/Neo4JDocumentService.java new file mode 100644 index 0000000..6188c0a --- /dev/null +++ b/src/main/java/net/savantly/mainbot/dom/neo4j/Neo4JDocumentService.java @@ -0,0 +1,36 @@ +package net.savantly.mainbot.dom.neo4j; + +import java.util.List; + +import net.savantly.mainbot.dom.documents.DocumentAddRequest; +import net.savantly.mainbot.dom.documents.DocumentQuery; +import net.savantly.mainbot.dom.documents.DocumentSearchResult; +import net.savantly.mainbot.dom.documents.DocumentService; +import net.savantly.mainbot.dom.documents.impl.LC4JDocumentService; +import net.savantly.mainbot.dom.neo4j.dto.Neo4JEntityPersistor; + +public class Neo4JDocumentService implements DocumentService { + + private final Neo4JEntityExtractor entityExtractor; + private final Neo4JEntityPersistor entityPersistor; + private final LC4JDocumentService lc4jDocumentService; + + public Neo4JDocumentService(Neo4JEntityExtractor entityExtractor, Neo4JEntityPersistor entityPersistor, LC4JDocumentService lc4jDocumentService) { + this.entityExtractor = entityExtractor; + this.entityPersistor = entityPersistor; + this.lc4jDocumentService = lc4jDocumentService; + } + + @Override + public List addDocument(DocumentAddRequest document) { + var extractedEntities = entityExtractor.extractEntities(document.getText()); + entityPersistor.persist(extractedEntities); + return lc4jDocumentService.addDocument(document); + } + + @Override + public List search(DocumentQuery query, String namespace) { + return lc4jDocumentService.search(query, namespace); + } + +} diff --git a/src/main/java/net/savantly/mainbot/dom/neo4j/Neo4JEntityExtractor.java b/src/main/java/net/savantly/mainbot/dom/neo4j/Neo4JEntityExtractor.java new file mode 100644 index 0000000..8a7b211 --- /dev/null +++ b/src/main/java/net/savantly/mainbot/dom/neo4j/Neo4JEntityExtractor.java @@ -0,0 +1,36 @@ +package net.savantly.mainbot.dom.neo4j; + +import net.savantly.mainbot.dom.neo4j.dto.EntityExtraction; +import net.savantly.mainbot.dom.neo4j.entity.PersonNode; +import net.savantly.mainbot.dom.neo4j.entity.PlaceNode; +import net.savantly.mainbot.dom.neo4j.entity.ThingNode; + +public interface Neo4JEntityExtractor { + + default EntityExtraction extractEntities(String text) { + + var person1 = new PersonNode().setName("John Doe"); + var person2 = new PersonNode().setName("Jane Doe"); + + person1.getKnows().add(person2); + + var place1 = new PlaceNode().setName("New York"); + var place2 = new PlaceNode().setName("Los Angeles"); + + person1.setLivesIn(place1); + person2.setLivesIn(place2); + + person1.setHailsFrom(place2); + + var car = new ThingNode().setName("Car"); + var house = new ThingNode().setName("House"); + person1.setHas(car); + person2.setHas(house); + + var extracted = new EntityExtraction(); + extracted.setPeople(java.util.List.of(person1, person2)); + extracted.setPlaces(java.util.List.of(place1, place2)); + extracted.setThings(java.util.List.of(car, house)); + return extracted; + }; +} diff --git a/src/main/java/net/savantly/mainbot/dom/neo4j/dto/EntityExtraction.java b/src/main/java/net/savantly/mainbot/dom/neo4j/dto/EntityExtraction.java new file mode 100644 index 0000000..de3155f --- /dev/null +++ b/src/main/java/net/savantly/mainbot/dom/neo4j/dto/EntityExtraction.java @@ -0,0 +1,18 @@ +package net.savantly.mainbot.dom.neo4j.dto; + +import java.util.ArrayList; +import java.util.List; + +import lombok.Data; +import net.savantly.mainbot.dom.neo4j.entity.PersonNode; +import net.savantly.mainbot.dom.neo4j.entity.PlaceNode; +import net.savantly.mainbot.dom.neo4j.entity.ThingNode; + +@Data +public class EntityExtraction { + + private List people = new ArrayList<>(); + private List places = new ArrayList<>(); + private List things = new ArrayList<>(); + +} diff --git a/src/main/java/net/savantly/mainbot/dom/neo4j/dto/Neo4JEntityPersistor.java b/src/main/java/net/savantly/mainbot/dom/neo4j/dto/Neo4JEntityPersistor.java new file mode 100644 index 0000000..b3ef43b --- /dev/null +++ b/src/main/java/net/savantly/mainbot/dom/neo4j/dto/Neo4JEntityPersistor.java @@ -0,0 +1,22 @@ +package net.savantly.mainbot.dom.neo4j.dto; + +import lombok.RequiredArgsConstructor; +import net.savantly.mainbot.dom.neo4j.entity.PersonRepository; +import net.savantly.mainbot.dom.neo4j.entity.PlaceRepository; +import net.savantly.mainbot.dom.neo4j.entity.ThingRepository; + +@RequiredArgsConstructor +public class Neo4JEntityPersistor { + + private final PersonRepository personRepository; + private final PlaceRepository placeRepository; + private final ThingRepository thingRepository; + + public void persist(EntityExtraction entityExtraction) { + personRepository.saveAll(entityExtraction.getPeople()); + placeRepository.saveAll(entityExtraction.getPlaces()); + thingRepository.saveAll(entityExtraction.getThings()); + + } + +} diff --git a/src/main/java/net/savantly/mainbot/dom/neo4j/dto/Neo4JEntitySearch.java b/src/main/java/net/savantly/mainbot/dom/neo4j/dto/Neo4JEntitySearch.java new file mode 100644 index 0000000..8151503 --- /dev/null +++ b/src/main/java/net/savantly/mainbot/dom/neo4j/dto/Neo4JEntitySearch.java @@ -0,0 +1,26 @@ +package net.savantly.mainbot.dom.neo4j.dto; + +import java.util.List; + +import lombok.RequiredArgsConstructor; +import net.savantly.mainbot.dom.documents.DocumentQuery; +import net.savantly.mainbot.dom.neo4j.dto.query.Neo4JQueryGenerator; +import net.savantly.mainbot.dom.neo4j.entity.PersonRepository; +import net.savantly.mainbot.dom.neo4j.entity.PlaceRepository; +import net.savantly.mainbot.dom.neo4j.entity.ThingRepository; + +@RequiredArgsConstructor +public class Neo4JEntitySearch { + + private final Neo4JQueryGenerator queryGenerator; + private final PersonRepository personRepository; + private final PlaceRepository placeRepository; + private final ThingRepository thingRepository; + + public List search(DocumentQuery query, String namespace) { + + var nodeQuery = queryGenerator.generateQuery(query.getText()); + + return null; + } +} diff --git a/src/main/java/net/savantly/mainbot/dom/neo4j/dto/NodeDTO.java b/src/main/java/net/savantly/mainbot/dom/neo4j/dto/NodeDTO.java new file mode 100644 index 0000000..99db2e1 --- /dev/null +++ b/src/main/java/net/savantly/mainbot/dom/neo4j/dto/NodeDTO.java @@ -0,0 +1,36 @@ +package net.savantly.mainbot.dom.neo4j.dto; + +import java.util.Map; + +import lombok.Data; +import lombok.experimental.Accessors; + +//** TODO: NOT USED YET */ +@Data +@Accessors(chain = true) +public class NodeDTO { + private String id; + /** + * The label of the node + * Person, Place, Thing, etc. + */ + private String label; + + /** + * The name of the node + * John Doe, New York, etc. + */ + private String name; + + /** + * The description of the node + * A person, a city, etc. + */ + private String description; + + /** + * The properties of the node + * key-value pairs + */ + private Map properties = new java.util.HashMap<>(); +} diff --git a/src/main/java/net/savantly/mainbot/dom/neo4j/dto/Relationship.java b/src/main/java/net/savantly/mainbot/dom/neo4j/dto/Relationship.java new file mode 100644 index 0000000..e15d8f6 --- /dev/null +++ b/src/main/java/net/savantly/mainbot/dom/neo4j/dto/Relationship.java @@ -0,0 +1,25 @@ +package net.savantly.mainbot.dom.neo4j.dto; + +import lombok.Data; +import lombok.experimental.Accessors; + +@Data +@Accessors(chain = true) +public class Relationship { + + /** + * The start node of the relationship + */ + private NodeDTO startNode; + + /** + * The end node of the relationship + */ + private NodeDTO endNode; + + /** + * The type of the relationship + * KNOWS, LIVES_IN, etc. + */ + private String type; +} diff --git a/src/main/java/net/savantly/mainbot/dom/neo4j/dto/query/Neo4JNodeQuery.java b/src/main/java/net/savantly/mainbot/dom/neo4j/dto/query/Neo4JNodeQuery.java new file mode 100644 index 0000000..29533b6 --- /dev/null +++ b/src/main/java/net/savantly/mainbot/dom/neo4j/dto/query/Neo4JNodeQuery.java @@ -0,0 +1,22 @@ +package net.savantly.mainbot.dom.neo4j.dto.query; + +import lombok.Data; + +@Data +public class Neo4JNodeQuery { + + /** + * The label of the node (Person, Place, etc) + */ + private String label; + + /** + * The key to search using the value + */ + private String key; + + /** + * The value to search for + */ + private String value; +} diff --git a/src/main/java/net/savantly/mainbot/dom/neo4j/dto/query/Neo4JQueryGenerator.java b/src/main/java/net/savantly/mainbot/dom/neo4j/dto/query/Neo4JQueryGenerator.java new file mode 100644 index 0000000..8ff20d8 --- /dev/null +++ b/src/main/java/net/savantly/mainbot/dom/neo4j/dto/query/Neo4JQueryGenerator.java @@ -0,0 +1,10 @@ +package net.savantly.mainbot.dom.neo4j.dto.query; + +import dev.langchain4j.service.SystemMessage; + +public interface Neo4JQueryGenerator { + + @SystemMessage("Generate a Cypher query from the given schema and context, for Neo4J database.") + public String generateQuery(String text); + +} diff --git a/src/main/java/net/savantly/mainbot/dom/neo4j/entity/PersonNode.java b/src/main/java/net/savantly/mainbot/dom/neo4j/entity/PersonNode.java new file mode 100644 index 0000000..bfc65a4 --- /dev/null +++ b/src/main/java/net/savantly/mainbot/dom/neo4j/entity/PersonNode.java @@ -0,0 +1,36 @@ +package net.savantly.mainbot.dom.neo4j.entity; + +import java.util.ArrayList; +import java.util.List; + +import org.springframework.data.neo4j.core.schema.Id; +import org.springframework.data.neo4j.core.schema.Node; +import org.springframework.data.neo4j.core.schema.Relationship; + +import lombok.Data; +import lombok.experimental.Accessors; + +@Node("Person") +@Data +@Accessors(chain = true) +public class PersonNode { + + @Id + private String name; + private String description; + + @Relationship(type = "KNOWS", direction = Relationship.Direction.OUTGOING) + private List knows = new ArrayList<>(); + + @Relationship(type = "LIVES_IN", direction = Relationship.Direction.OUTGOING) + private PlaceNode livesIn; + + @Relationship(type = "HAS", direction = Relationship.Direction.OUTGOING) + private ThingNode has; + + @Relationship(type = "IS_A", direction = Relationship.Direction.OUTGOING) + private ThingNode isA; + + @Relationship(type = "HAILS_FROM", direction = Relationship.Direction.OUTGOING) + private PlaceNode hailsFrom; +} diff --git a/src/main/java/net/savantly/mainbot/dom/neo4j/entity/PersonRepository.java b/src/main/java/net/savantly/mainbot/dom/neo4j/entity/PersonRepository.java new file mode 100644 index 0000000..7a5be66 --- /dev/null +++ b/src/main/java/net/savantly/mainbot/dom/neo4j/entity/PersonRepository.java @@ -0,0 +1,7 @@ +package net.savantly.mainbot.dom.neo4j.entity; + +import org.springframework.data.neo4j.repository.Neo4jRepository; + +public interface PersonRepository extends Neo4jRepository{ + +} diff --git a/src/main/java/net/savantly/mainbot/dom/neo4j/entity/PlaceNode.java b/src/main/java/net/savantly/mainbot/dom/neo4j/entity/PlaceNode.java new file mode 100644 index 0000000..20a56e7 --- /dev/null +++ b/src/main/java/net/savantly/mainbot/dom/neo4j/entity/PlaceNode.java @@ -0,0 +1,19 @@ +package net.savantly.mainbot.dom.neo4j.entity; + +import org.springframework.data.neo4j.core.schema.Id; +import org.springframework.data.neo4j.core.schema.Node; + +import lombok.Data; +import lombok.experimental.Accessors; + +@Node("Place") +@Data +@Accessors(chain = true) +public class PlaceNode { + + @Id + private String name; + private String description; + private Float latitude; + private Float longitude; +} diff --git a/src/main/java/net/savantly/mainbot/dom/neo4j/entity/PlaceRepository.java b/src/main/java/net/savantly/mainbot/dom/neo4j/entity/PlaceRepository.java new file mode 100644 index 0000000..9bc66f6 --- /dev/null +++ b/src/main/java/net/savantly/mainbot/dom/neo4j/entity/PlaceRepository.java @@ -0,0 +1,7 @@ +package net.savantly.mainbot.dom.neo4j.entity; + +import org.springframework.data.neo4j.repository.Neo4jRepository; + +public interface PlaceRepository extends Neo4jRepository{ + +} diff --git a/src/main/java/net/savantly/mainbot/dom/neo4j/entity/ThingNode.java b/src/main/java/net/savantly/mainbot/dom/neo4j/entity/ThingNode.java new file mode 100644 index 0000000..a6151ff --- /dev/null +++ b/src/main/java/net/savantly/mainbot/dom/neo4j/entity/ThingNode.java @@ -0,0 +1,27 @@ +package net.savantly.mainbot.dom.neo4j.entity; + +import org.springframework.data.neo4j.core.schema.Id; +import org.springframework.data.neo4j.core.schema.Node; +import org.springframework.data.neo4j.core.schema.Relationship; + +import lombok.Data; +import lombok.experimental.Accessors; + +@Node("Thing") +@Data +@Accessors(chain = true) +public class ThingNode { + + @Id + private String name; + private String description; + + @Relationship(type = "HAS", direction = Relationship.Direction.OUTGOING) + private ThingNode has; + + @Relationship(type = "IS_A", direction = Relationship.Direction.OUTGOING) + private ThingNode isA; + + @Relationship(type = "PART_OF", direction = Relationship.Direction.OUTGOING) + private ThingNode partOf; +} diff --git a/src/main/java/net/savantly/mainbot/dom/neo4j/entity/ThingRepository.java b/src/main/java/net/savantly/mainbot/dom/neo4j/entity/ThingRepository.java new file mode 100644 index 0000000..75c266e --- /dev/null +++ b/src/main/java/net/savantly/mainbot/dom/neo4j/entity/ThingRepository.java @@ -0,0 +1,7 @@ +package net.savantly.mainbot.dom.neo4j.entity; + +import org.springframework.data.neo4j.repository.Neo4jRepository; + +public interface ThingRepository extends Neo4jRepository{ + +} diff --git a/src/main/java/net/savantly/mainbot/identity/AnonymousUser.java b/src/main/java/net/savantly/mainbot/identity/AnonymousUser.java index 78a5fd2..c43bf9a 100644 --- a/src/main/java/net/savantly/mainbot/identity/AnonymousUser.java +++ b/src/main/java/net/savantly/mainbot/identity/AnonymousUser.java @@ -1,5 +1,7 @@ package net.savantly.mainbot.identity; +import java.util.List; + import lombok.Data; import lombok.experimental.Accessors; @@ -13,7 +15,7 @@ public class AnonymousUser { private String givenName = "Anonymous"; private String familyName = "Anonymous"; private String uid = "anonymous"; - private String[] groups = new String[] {"anonymous"}; + private String[] groups = new String[] { "anonymous" }; public UserDto toDto() { return new UserDto() @@ -22,6 +24,7 @@ public UserDto toDto() { .setGivenName(givenName) .setName(name) .setUid(uid) - .setUsername(username); + .setUsername(username) + .setGroups(List.of(groups)); } } diff --git a/src/main/java/net/savantly/mainbot/security/SecurityConfig.java b/src/main/java/net/savantly/mainbot/security/SecurityConfig.java index c13e59d..639016e 100644 --- a/src/main/java/net/savantly/mainbot/security/SecurityConfig.java +++ b/src/main/java/net/savantly/mainbot/security/SecurityConfig.java @@ -1,6 +1,7 @@ package net.savantly.mainbot.security; import org.springframework.beans.BeansException; +import org.springframework.boot.autoconfigure.condition.ConditionalOnBean; import org.springframework.boot.context.properties.ConfigurationProperties; import org.springframework.context.ApplicationContext; import org.springframework.context.ApplicationContextAware; @@ -59,6 +60,7 @@ public class SecurityConfig implements ApplicationContextAware { private ApplicationContext applicationContext; @Bean + @ConditionalOnBean(OAuth2AuthorizedClientService.class) public OAuth2ClientService oAuth2ClientService(OAuth2AuthorizedClientService authorizedClientService) { return new OAuth2ClientService(authorizedClientService); } diff --git a/src/main/java/net/savantly/mainbot/security/oauth2/OAuth2ClientConfiguration.java b/src/main/java/net/savantly/mainbot/security/oauth2/OAuth2ClientConfiguration.java index a857f21..eb48041 100644 --- a/src/main/java/net/savantly/mainbot/security/oauth2/OAuth2ClientConfiguration.java +++ b/src/main/java/net/savantly/mainbot/security/oauth2/OAuth2ClientConfiguration.java @@ -1,5 +1,6 @@ package net.savantly.mainbot.security.oauth2; +import org.springframework.boot.autoconfigure.condition.ConditionalOnBean; import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; import org.springframework.boot.autoconfigure.security.oauth2.client.ClientsConfiguredCondition; import org.springframework.boot.autoconfigure.security.oauth2.client.OAuth2ClientProperties; @@ -33,15 +34,16 @@ public class OAuth2ClientConfiguration { */ // @Bean("authorizedClientManager") // public OAuth2AuthorizedClientManager authorizedClientManager( - // OAuth2AuthorizedClientProvider authorizedClientProvider, - // ClientRegistrationRepository clientRegistrationRepository, - // OAuth2AuthorizedClientRepository authorizedClientRepository) { - // log.info("Creating OAuth2AuthorizedClientManager"); - // DefaultOAuth2AuthorizedClientManager authorizedClientManager = new DefaultOAuth2AuthorizedClientManager( - // clientRegistrationRepository, authorizedClientRepository); - // authorizedClientManager.setAuthorizedClientProvider(authorizedClientProvider); + // OAuth2AuthorizedClientProvider authorizedClientProvider, + // ClientRegistrationRepository clientRegistrationRepository, + // OAuth2AuthorizedClientRepository authorizedClientRepository) { + // log.info("Creating OAuth2AuthorizedClientManager"); + // DefaultOAuth2AuthorizedClientManager authorizedClientManager = new + // DefaultOAuth2AuthorizedClientManager( + // clientRegistrationRepository, authorizedClientRepository); + // authorizedClientManager.setAuthorizedClientProvider(authorizedClientProvider); - // return authorizedClientManager; + // return authorizedClientManager; // } @Bean @@ -78,6 +80,7 @@ public ClientRegistrationRepository clientRegistrationRepository(OAuth2ClientPro } @Bean + @ConditionalOnBean({ ClientRegistrationRepository.class }) public OAuth2AuthorizedClientService authorizedClientService( ClientRegistrationRepository clientRegistrationRepository) { log.info("Creating InMemoryOAuth2AuthorizedClientService"); diff --git a/src/main/resources/application-dev.properties.example b/src/main/resources/application-dev.properties.example index 6c048f7..c9f29d7 100644 --- a/src/main/resources/application-dev.properties.example +++ b/src/main/resources/application-dev.properties.example @@ -1,2 +1,3 @@ openai.apiKey=sk-XYZ -openai.enabled=true \ No newline at end of file +openai.enabled=true +authorization.addDocsExpression=true \ No newline at end of file diff --git a/src/main/resources/application-neo4j.yml b/src/main/resources/application-neo4j.yml new file mode 100644 index 0000000..0e81f49 --- /dev/null +++ b/src/main/resources/application-neo4j.yml @@ -0,0 +1,10 @@ +documents: + implementation: NEO4J + + +spring: + neo4j: + uri: ${NEO4J_URI:bolt://localhost:7687} + authentication: + username: ${NEO4J_USERNAME:neo4j} + password: ${NEO4J_PASSWORD} diff --git a/src/main/resources/application.yml b/src/main/resources/application.yml index 17b94d6..9d2a6c6 100644 --- a/src/main/resources/application.yml +++ b/src/main/resources/application.yml @@ -17,6 +17,19 @@ spring: resources: chain: cache: 'false' + security: + oauth2: + client: + registration: + example: + client-id: ${OAUTH2_CLIENT_ID:client-id} + client-secret: ${OAUTH2_CLIENT_SECRET:client-secret} + authorization-grant-type: client_credentials + scope: ${OAUTH2_SCOPE:openid} + provider: + example: + token-uri: ${OAUTH2_TOKEN_URI:http://localhost:8080/oauth2/token} + logging: level: dev: @@ -87,4 +100,7 @@ pinecone: project-name: ${PINECONE_PROJECT_NAME} seed: - enabled: false \ No newline at end of file + enabled: false + +authorizaton: + addDocsExpression: hasRole('admin') \ No newline at end of file