diff --git a/FlySpring/edgechain-app/pom.xml b/FlySpring/edgechain-app/pom.xml index a2c82a0f3..45e11f5df 100644 --- a/FlySpring/edgechain-app/pom.xml +++ b/FlySpring/edgechain-app/pom.xml @@ -1,396 +1,396 @@ - - - 4.0.0 - - com.flyspring - edgechain-parent - 0.0.1-SNAPSHOT - - - edgechain-app - 1.0.0 - edgechain - EdgeChains SDK. - jar - - - 4.4.0 - 0.23.0 - 0.4.8 - 6.0.23.Final - 2.3.1 - 0.9.1 - 0.6.1 - 3.1.1 - 2.2.0 - 2.9.0 - 5.2.0 - 2.0.1.Final - 0.5.3 - - - - - org.springframework.boot - spring-boot-starter-data-redis - - - - org.springframework.boot - spring-boot-starter-web - - - - org.springframework.boot - spring-boot-starter-security - - - - org.springframework.boot - spring-boot-starter-webflux - - - - org.springframework.boot - spring-boot-starter-data-jpa - - - - redis.clients - jedis - - - - org.postgresql - postgresql - compile - - - - javax.validation - validation-api - ${validation-api.version} - - - - com.github.f4b6a3 - uuid-creator - ${uuid-creator.version} - - - - org.hibernate.validator - hibernate-validator - ${hibernate-validator} - - - - org.modelmapper - modelmapper - ${modelmapper.version} - - - - io.jsonwebtoken - jjwt - ${jsonwebtoken.version} - - - - javax.xml.bind - jaxb-api - ${jaxb-api.version} - - - - com.squareup.retrofit2 - retrofit - ${retrofit2.version} - - - - com.squareup.retrofit2 - adapter-rxjava3 - ${retrofit2.version} - - - - com.squareup.retrofit2 - converter-jackson - ${retrofit2.version} - - - - org.apache.opennlp - opennlp-tools - ${opennlp.version} - - - - io.reactivex.rxjava3 - rxjava - - - - io.reactivex - rxjava-reactive-streams - ${rxjava-reactive-streams.version} - - - - io.projectreactor.addons - reactor-adapter - - - - me.xuender - unidecode - ${unidecode.version} - - - - org.apache.tika - tika-core - ${apache-tika.version} - - - - org.apache.tika - tika-parsers-standard-package - ${apache-tika.version} - - - xml-apis - xml-apis - - - - - - ai.djl - api - ${djl.version} - - - - ai.djl - basicdataset - ${djl.version} - - - - ai.djl.huggingface - tokenizers - ${djl.version} - - - - ai.djl - model-zoo - ${djl.version} - - - - - - ai.djl.pytorch - pytorch-engine - ${djl.version} - - - - ai.djl.pytorch - pytorch-model-zoo - ${djl.version} - - - - - io.github.jam01 - xtrasonnet - ${xtrasonnet.version} - - - - com.knuddels - jtokkit - ${jtokkit.version} - - - - net.objecthunter - exp4j - ${exp4j.version} - - - - - info.picocli - picocli-spring-boot-starter - ${picocli.version} - - - - net.lingala.zip4j - zip4j - ${zip4j.version} - - - - org.zeroturnaround - zt-exec - ${zeroturnaround.version} - - - - org.testcontainers - testcontainers - - - - org.testcontainers - postgresql - - - - org.springframework.boot - spring-boot-starter-test - - - com.vaadin.external.google - android-json - - - test - - - - ai.djl.onnxruntime - onnxruntime-engine - ${djl.version} - runtime - - - - org.testcontainers - junit-jupiter - test - - - - com.auth0 - java-jwt - ${auth0-jwt.version} - test - - - - - - - org.apache.maven.plugins - maven-shade-plugin - ${maven-shade.version} - - - - org.springframework.boot - spring-boot-maven-plugin - ${spring-boot.version} - - - - - false - - - *:* - - META-INF/*.SF - META-INF/*.DSA - META-INF/*.RSA - - - - - - - - shade-jar-with-dependencies - package - - shade - - - - edgechain - - - META-INF/spring.handlers - META-INF/spring.schemas - - - META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports - - - - - com.edgechain.EdgeChainApplication - - - - - - - - - - org.apache.maven.plugins - maven-antrun-plugin - ${maven-antrun.version} - - - download-and-unpack-jbang - generate-resources - - run - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + 4.0.0 + + com.flyspring + edgechain-parent + 0.0.1-SNAPSHOT + + + edgechain-app + 1.0.0 + edgechain + EdgeChains SDK. + jar + + + 4.4.0 + 0.23.0 + 0.4.8 + 6.0.23.Final + 2.3.1 + 0.9.1 + 0.6.1 + 3.1.1 + 2.2.0 + 2.9.0 + 5.2.0 + 2.0.1.Final + 0.5.3 + + + + + org.springframework.boot + spring-boot-starter-data-redis + + + + org.springframework.boot + spring-boot-starter-web + + + + org.springframework.boot + spring-boot-starter-security + + + + org.springframework.boot + spring-boot-starter-webflux + + + + org.springframework.boot + spring-boot-starter-data-jpa + + + + redis.clients + jedis + + + + org.postgresql + postgresql + compile + + + + javax.validation + validation-api + ${validation-api.version} + + + + com.github.f4b6a3 + uuid-creator + ${uuid-creator.version} + + + + org.hibernate.validator + hibernate-validator + ${hibernate-validator} + + + + org.modelmapper + modelmapper + ${modelmapper.version} + + + + io.jsonwebtoken + jjwt + ${jsonwebtoken.version} + + + + javax.xml.bind + jaxb-api + ${jaxb-api.version} + + + + com.squareup.retrofit2 + retrofit + ${retrofit2.version} + + + + com.squareup.retrofit2 + adapter-rxjava3 + ${retrofit2.version} + + + + com.squareup.retrofit2 + converter-jackson + ${retrofit2.version} + + + + org.apache.opennlp + opennlp-tools + ${opennlp.version} + + + + io.reactivex.rxjava3 + rxjava + + + + io.reactivex + rxjava-reactive-streams + ${rxjava-reactive-streams.version} + + + + io.projectreactor.addons + reactor-adapter + + + + me.xuender + unidecode + ${unidecode.version} + + + + org.apache.tika + tika-core + ${apache-tika.version} + + + + org.apache.tika + tika-parsers-standard-package + ${apache-tika.version} + + + xml-apis + xml-apis + + + + + + ai.djl + api + ${djl.version} + + + + ai.djl + basicdataset + ${djl.version} + + + + ai.djl.huggingface + tokenizers + ${djl.version} + + + + ai.djl + model-zoo + ${djl.version} + + + + + + ai.djl.pytorch + pytorch-engine + ${djl.version} + + + + ai.djl.pytorch + pytorch-model-zoo + ${djl.version} + + + + + io.github.jam01 + xtrasonnet + ${xtrasonnet.version} + + + + com.knuddels + jtokkit + ${jtokkit.version} + + + + net.objecthunter + exp4j + ${exp4j.version} + + + + + info.picocli + picocli-spring-boot-starter + ${picocli.version} + + + + net.lingala.zip4j + zip4j + ${zip4j.version} + + + + org.zeroturnaround + zt-exec + ${zeroturnaround.version} + + + + org.testcontainers + testcontainers + + + + org.testcontainers + postgresql + + + + org.springframework.boot + spring-boot-starter-test + + + com.vaadin.external.google + android-json + + + test + + + + ai.djl.onnxruntime + onnxruntime-engine + ${djl.version} + runtime + + + + org.testcontainers + junit-jupiter + test + + + + com.auth0 + java-jwt + ${auth0-jwt.version} + test + + + + + + + org.apache.maven.plugins + maven-shade-plugin + ${maven-shade.version} + + + + org.springframework.boot + spring-boot-maven-plugin + ${spring-boot.version} + + + + + false + + + *:* + + META-INF/*.SF + META-INF/*.DSA + META-INF/*.RSA + + + + + + + + shade-jar-with-dependencies + package + + shade + + + + edgechain + + + META-INF/spring.handlers + META-INF/spring.schemas + + + META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports + + + + + com.edgechain.EdgeChainApplication + + + + + + + + + + org.apache.maven.plugins + maven-antrun-plugin + ${maven-antrun.version} + + + download-and-unpack-jbang + generate-resources + + run + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/FlySpring/edgechain-app/src/main/java/com/edgechain/lib/configuration/WebConfiguration.java b/FlySpring/edgechain-app/src/main/java/com/edgechain/lib/configuration/WebConfiguration.java index f1f580484..ae60946e5 100644 --- a/FlySpring/edgechain-app/src/main/java/com/edgechain/lib/configuration/WebConfiguration.java +++ b/FlySpring/edgechain-app/src/main/java/com/edgechain/lib/configuration/WebConfiguration.java @@ -1,48 +1,48 @@ -package com.edgechain.lib.configuration; - -import com.edgechain.lib.configuration.domain.AuthFilter; -import com.edgechain.lib.configuration.domain.MethodAuthentication; -import com.edgechain.lib.configuration.domain.SecurityUUID; -import java.util.List; -import java.util.UUID; -import org.modelmapper.ModelMapper; -import org.springframework.context.annotation.Bean; -import org.springframework.context.annotation.Configuration; -import org.springframework.context.annotation.Import; -import org.springframework.context.annotation.Primary; -import org.springframework.web.client.RestTemplate; - -@Configuration("WebConfiguration") -@Import(EdgeChainAutoConfiguration.class) -public class WebConfiguration { - - public static final String CONTEXT_PATH = "/edgechains"; - - @Bean - ModelMapper modelMapper() { - return new ModelMapper(); - } - - @Bean - RestTemplate restTemplate() { - return new RestTemplate(); - } - - @Bean - @Primary - SecurityUUID securityUUID() { - return new SecurityUUID(UUID.randomUUID().toString()); - } - - @Bean - AuthFilter authFilter() { - AuthFilter filter = new AuthFilter(); - filter.setRequestPost(new MethodAuthentication(List.of("**"), "")); - filter.setRequestGet(new MethodAuthentication(List.of("**"), "")); - filter.setRequestDelete(new MethodAuthentication(List.of("**"), "")); - filter.setRequestPatch(new MethodAuthentication(List.of("**"), "")); - filter.setRequestPut(new MethodAuthentication(List.of("**"), "")); - - return filter; - } -} +package com.edgechain.lib.configuration; + +import com.edgechain.lib.configuration.domain.AuthFilter; +import com.edgechain.lib.configuration.domain.MethodAuthentication; +import com.edgechain.lib.configuration.domain.SecurityUUID; +import java.util.List; +import java.util.UUID; +import org.modelmapper.ModelMapper; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.context.annotation.Import; +import org.springframework.context.annotation.Primary; +import org.springframework.web.client.RestTemplate; + +@Configuration("WebConfiguration") +@Import(EdgeChainAutoConfiguration.class) +public class WebConfiguration { + + public static final String CONTEXT_PATH = "/edgechains"; + + @Bean + ModelMapper modelMapper() { + return new ModelMapper(); + } + + @Bean + RestTemplate restTemplate() { + return new RestTemplate(); + } + + @Bean + @Primary + SecurityUUID securityUUID() { + return new SecurityUUID(UUID.randomUUID().toString()); + } + + @Bean + AuthFilter authFilter() { + AuthFilter filter = new AuthFilter(); + filter.setRequestPost(new MethodAuthentication(List.of("**"), "")); + filter.setRequestGet(new MethodAuthentication(List.of("**"), "")); + filter.setRequestDelete(new MethodAuthentication(List.of("**"), "")); + filter.setRequestPatch(new MethodAuthentication(List.of("**"), "")); + filter.setRequestPut(new MethodAuthentication(List.of("**"), "")); + + return filter; + } +} diff --git a/FlySpring/edgechain-app/src/main/java/com/edgechain/lib/index/client/impl/PostgresClient.java b/FlySpring/edgechain-app/src/main/java/com/edgechain/lib/index/client/impl/PostgresClient.java index 87b09e7db..d6be058f5 100644 --- a/FlySpring/edgechain-app/src/main/java/com/edgechain/lib/index/client/impl/PostgresClient.java +++ b/FlySpring/edgechain-app/src/main/java/com/edgechain/lib/index/client/impl/PostgresClient.java @@ -1,404 +1,404 @@ -package com.edgechain.lib.index.client.impl; - -import com.edgechain.lib.configuration.context.ApplicationContextHolder; -import com.edgechain.lib.endpoint.impl.PostgresEndpoint; -import com.edgechain.lib.index.domain.PostgresWordEmbeddings; -import com.edgechain.lib.index.repositories.PostgresClientMetadataRepository; -import com.edgechain.lib.index.repositories.PostgresClientRepository; -import com.edgechain.lib.response.StringResponse; -import com.edgechain.lib.rxjava.transformer.observable.EdgeChain; -import com.fasterxml.jackson.core.type.TypeReference; -import com.fasterxml.jackson.databind.ObjectMapper; -import io.reactivex.rxjava3.core.Observable; -import java.sql.Timestamp; -import java.util.ArrayList; -import java.util.HashMap; -import java.util.HashSet; -import java.util.List; -import java.util.Map; -import java.util.Objects; -import java.util.Set; -import org.postgresql.util.PGobject; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import org.springframework.stereotype.Service; - -@Service -public class PostgresClient { - - private static final Logger logger = LoggerFactory.getLogger(PostgresClient.class); - - private static final TypeReference> FLOAT_TYPE_REF = new TypeReference<>() {}; - - private final PostgresClientRepository repository = - ApplicationContextHolder.getContext().getBean(PostgresClientRepository.class); - private final PostgresClientMetadataRepository metadataRepository = - ApplicationContextHolder.getContext().getBean(PostgresClientMetadataRepository.class); - - private final ObjectMapper objectMapper = new ObjectMapper(); - - public EdgeChain createTable(PostgresEndpoint postgresEndpoint) { - return new EdgeChain<>( - Observable.create( - emitter -> { - try { - this.repository.createTable(postgresEndpoint); - emitter.onNext(new StringResponse("Table: " + postgresEndpoint.getTableName())); - emitter.onComplete(); - } catch (final Exception e) { - emitter.onError(e); - } - })); - } - - public EdgeChain createMetadataTable(PostgresEndpoint postgresEndpoint) { - return new EdgeChain<>( - Observable.create( - emitter -> { - try { - this.metadataRepository.createTable(postgresEndpoint); - emitter.onNext( - new StringResponse( - "Table: " + postgresEndpoint.getMetadataTableNames().get(0))); - emitter.onComplete(); - } catch (final Exception e) { - emitter.onError(e); - } - })); - } - - public EdgeChain> batchUpsert(PostgresEndpoint postgresEndpoint) { - - return new EdgeChain<>( - Observable.create( - emitter -> { - try { - - // Upsert Embeddings - List strings = - this.repository.batchUpsertEmbeddings( - postgresEndpoint.getTableName(), - postgresEndpoint.getWordEmbeddingsList(), - postgresEndpoint.getFilename(), - getNamespace(postgresEndpoint)); - - List stringResponseList = - strings.stream().map(StringResponse::new).toList(); - - emitter.onNext(stringResponseList); - emitter.onComplete(); - - } catch (final Exception e) { - emitter.onError(e); - } - }), - postgresEndpoint); - } - - public EdgeChain upsert(PostgresEndpoint postgresEndpoint) { - - return new EdgeChain<>( - Observable.create( - emitter -> { - try { - - // Upsert Embeddings - String embeddingId = - this.repository.upsertEmbeddings( - postgresEndpoint.getTableName(), - postgresEndpoint.getWordEmbedding(), - postgresEndpoint.getFilename(), - getNamespace(postgresEndpoint)); - - emitter.onNext(new StringResponse(embeddingId)); - emitter.onComplete(); - - } catch (final Exception e) { - emitter.onError(e); - } - }), - postgresEndpoint); - } - - public EdgeChain insertMetadata(PostgresEndpoint postgresEndpoint) { - - return new EdgeChain<>( - Observable.create( - emitter -> { - try { - String metadata = postgresEndpoint.getMetadata(); - String input = metadata.replace("'", ""); - - String metadataId = - this.metadataRepository.insertMetadata( - postgresEndpoint.getMetadataTableNames().get(0), - input, - postgresEndpoint.getDocumentDate()); - - emitter.onNext(new StringResponse(metadataId)); - emitter.onComplete(); - - } catch (final Exception e) { - emitter.onError(e); - } - }), - postgresEndpoint); - } - - public EdgeChain> batchInsertMetadata(PostgresEndpoint postgresEndpoint) { - - return new EdgeChain<>( - Observable.create( - emitter -> { - try { - - // Insert metadata - List strings = - this.metadataRepository.batchInsertMetadata( - postgresEndpoint.getMetadataTableNames().get(0), - postgresEndpoint.getMetadataList()); - - List stringResponseList = - strings.stream().map(StringResponse::new).toList(); - - emitter.onNext(stringResponseList); - emitter.onComplete(); - - } catch (final Exception e) { - emitter.onError(e); - } - }), - postgresEndpoint); - } - - public EdgeChain insertIntoJoinTable(PostgresEndpoint postgresEndpoint) { - return new EdgeChain<>( - Observable.create( - emitter -> { - try { - - this.metadataRepository.insertIntoJoinTable(postgresEndpoint); - - emitter.onNext(new StringResponse("Inserted")); - emitter.onComplete(); - - } catch (final Exception e) { - emitter.onError(e); - } - }), - postgresEndpoint); - } - - public EdgeChain> query(PostgresEndpoint postgresEndpoint) { - - return new EdgeChain<>( - Observable.create( - emitter -> { - try { - List wordEmbeddingsList = new ArrayList<>(); - List> rows = - this.repository.query( - postgresEndpoint.getTableName(), - getNamespace(postgresEndpoint), - postgresEndpoint.getProbes(), - postgresEndpoint.getMetric(), - postgresEndpoint.getWordEmbedding().getValues(), - postgresEndpoint.getTopK()); - - for (Map row : rows) { - - PostgresWordEmbeddings val = new PostgresWordEmbeddings(); - val.setId(row.get("id").toString()); - val.setRawText((String) row.get("raw_text")); - val.setFilename((String) row.get("filename")); - val.setTimestamp(((Timestamp) row.get("timestamp")).toLocalDateTime()); - val.setNamespace((String) row.get("namespace")); - val.setScore((Double) row.get("score")); - - wordEmbeddingsList.add(val); - } - emitter.onNext(wordEmbeddingsList); - emitter.onComplete(); - - } catch (final Exception e) { - emitter.onError(e); - } - }), - postgresEndpoint); - } - - public EdgeChain> queryWithMetadata( - PostgresEndpoint postgresEndpoint) { - - return new EdgeChain<>( - Observable.create( - emitter -> { - try { - List wordEmbeddingsList = new ArrayList<>(); - if (postgresEndpoint.getMetadataTableNames() != null) { - try { - List metadataTableNames = postgresEndpoint.getMetadataTableNames(); - int numberOfMetadataTables = metadataTableNames.size(); - - /* - * This map will store the pairs - * We need to extract the title info from another metadata table. - * So instead of having extra PostgresWordEmbeddings objects for the title info - * We can store the title info in a map corresponding to the id key of the embeddings table - * Then after the loop is over we can inject the title field in the correct PostgresWordEmbeddings object by using the id key. - */ - Map titleMetadataMap = new HashMap<>(); - Map dateMetadataMap = new HashMap<>(); - for (String metadataTableName : metadataTableNames) { - List> rows = - this.metadataRepository.queryWithMetadata( - postgresEndpoint.getTableName(), - metadataTableName, - getNamespace(postgresEndpoint), - postgresEndpoint.getProbes(), - postgresEndpoint.getMetric(), - postgresEndpoint.getWordEmbedding().getValues(), - postgresEndpoint.getTopK()); - - // To filter out duplicate context chunks - Set contextChunkIds = new HashSet<>(); - for (Map row : rows) { - String metadataId = row.get("metadata_id").toString(); - if (!metadataTableName.contains("_title_metadata") - && contextChunkIds.contains(metadataId)) continue; - - PostgresWordEmbeddings val = new PostgresWordEmbeddings(); - final String idStr = row.get("id").toString(); - val.setId(idStr); - val.setRawText((String) row.get("raw_text")); - val.setFilename((String) row.get("filename")); - val.setTimestamp(((Timestamp) row.get("timestamp")).toLocalDateTime()); - val.setNamespace((String) row.get("namespace")); - val.setScore((Double) row.get("score")); - - // Add metadata fields in response - if (metadataTableName.contains("_title_metadata")) { - titleMetadataMap.put(idStr, (String) row.get("metadata")); - dateMetadataMap.put(idStr, (String) row.get("document_date")); - - // For checking if only one metadata table is present which is the title - // table - if (numberOfMetadataTables > 1) continue; - } else { - val.setMetadata((String) row.get("metadata")); - } - contextChunkIds.add(metadataId); - wordEmbeddingsList.add(val); - } - - // Insert the title and date fields into their respective - // PostgresWordEmbeddings - for (PostgresWordEmbeddings wordEmbedding : wordEmbeddingsList) { - String id = wordEmbedding.getId(); - if (titleMetadataMap.containsKey(id)) { - wordEmbedding.setTitleMetadata(titleMetadataMap.get(id)); - } - if (dateMetadataMap.containsKey(id)) { - wordEmbedding.setDocumentDate(dateMetadataMap.get(id)); - } - } - } - } catch (Exception e) { - logger.warn("ignored query error", e); - } - } - - emitter.onNext(wordEmbeddingsList); - emitter.onComplete(); - - } catch (final Exception e) { - emitter.onError(e); - } - }), - postgresEndpoint); - } - - public EdgeChain> getAllChunks(PostgresEndpoint postgresEndpoint) { - return new EdgeChain<>( - Observable.create( - emitter -> { - try { - List wordEmbeddingsList = new ArrayList<>(); - List> rows = this.repository.getAllChunks(postgresEndpoint); - for (Map row : rows) { - PostgresWordEmbeddings val = new PostgresWordEmbeddings(); - val.setId(row.get("id").toString()); - val.setRawText((String) row.get("raw_text")); - val.setFilename((String) row.get("filename")); - PGobject pgObject = (PGobject) row.get("embedding"); - String jsonString = pgObject.getValue(); - List values = objectMapper.readerFor(FLOAT_TYPE_REF).readValue(jsonString); - val.setValues(values); - wordEmbeddingsList.add(val); - } - emitter.onNext(wordEmbeddingsList); - emitter.onComplete(); - } catch (final Exception e) { - emitter.onError(e); - } - }), - postgresEndpoint); - } - - public EdgeChain> getSimilarMetadataChunk( - PostgresEndpoint postgresEndpoint) { - - return new EdgeChain<>( - Observable.create( - emitter -> { - try { - List wordEmbeddingsList = new ArrayList<>(); - List> rows = - this.metadataRepository.getSimilarMetadataChunk( - postgresEndpoint.getMetadataTableNames().get(0), - postgresEndpoint.getEmbeddingChunk()); - for (Map row : rows) { - - PostgresWordEmbeddings val = new PostgresWordEmbeddings(); - val.setMetadataId(row.get("metadata_id").toString()); - val.setMetadata((String) row.get("metadata")); - - wordEmbeddingsList.add(val); - } - - emitter.onNext(wordEmbeddingsList); - emitter.onComplete(); - - } catch (final Exception e) { - emitter.onError(e); - } - }), - postgresEndpoint); - } - - public EdgeChain deleteAll(PostgresEndpoint postgresEndpoint) { - - return new EdgeChain<>( - Observable.create( - emitter -> { - String namespace = getNamespace(postgresEndpoint); - try { - this.repository.deleteAll(postgresEndpoint.getTableName(), namespace); - emitter.onNext( - new StringResponse( - "Word embeddings are successfully deleted for namespace:" + namespace)); - emitter.onComplete(); - } catch (final Exception e) { - emitter.onError(e); - } - }), - postgresEndpoint); - } - - private String getNamespace(PostgresEndpoint postgresEndpoint) { - return (Objects.isNull(postgresEndpoint.getNamespace()) - || postgresEndpoint.getNamespace().isEmpty()) - ? "knowledge" - : postgresEndpoint.getNamespace(); - } -} +package com.edgechain.lib.index.client.impl; + +import com.edgechain.lib.configuration.context.ApplicationContextHolder; +import com.edgechain.lib.endpoint.impl.PostgresEndpoint; +import com.edgechain.lib.index.domain.PostgresWordEmbeddings; +import com.edgechain.lib.index.repositories.PostgresClientMetadataRepository; +import com.edgechain.lib.index.repositories.PostgresClientRepository; +import com.edgechain.lib.response.StringResponse; +import com.edgechain.lib.rxjava.transformer.observable.EdgeChain; +import com.fasterxml.jackson.core.type.TypeReference; +import com.fasterxml.jackson.databind.ObjectMapper; +import io.reactivex.rxjava3.core.Observable; +import java.sql.Timestamp; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Objects; +import java.util.Set; +import org.postgresql.util.PGobject; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.stereotype.Service; + +@Service +public class PostgresClient { + + private static final Logger logger = LoggerFactory.getLogger(PostgresClient.class); + + private static final TypeReference> FLOAT_TYPE_REF = new TypeReference<>() {}; + + private final PostgresClientRepository repository = + ApplicationContextHolder.getContext().getBean(PostgresClientRepository.class); + private final PostgresClientMetadataRepository metadataRepository = + ApplicationContextHolder.getContext().getBean(PostgresClientMetadataRepository.class); + + private final ObjectMapper objectMapper = new ObjectMapper(); + + public EdgeChain createTable(PostgresEndpoint postgresEndpoint) { + return new EdgeChain<>( + Observable.create( + emitter -> { + try { + this.repository.createTable(postgresEndpoint); + emitter.onNext(new StringResponse("Table: " + postgresEndpoint.getTableName())); + emitter.onComplete(); + } catch (final Exception e) { + emitter.onError(e); + } + })); + } + + public EdgeChain createMetadataTable(PostgresEndpoint postgresEndpoint) { + return new EdgeChain<>( + Observable.create( + emitter -> { + try { + this.metadataRepository.createTable(postgresEndpoint); + emitter.onNext( + new StringResponse( + "Table: " + postgresEndpoint.getMetadataTableNames().get(0))); + emitter.onComplete(); + } catch (final Exception e) { + emitter.onError(e); + } + })); + } + + public EdgeChain> batchUpsert(PostgresEndpoint postgresEndpoint) { + + return new EdgeChain<>( + Observable.create( + emitter -> { + try { + + // Upsert Embeddings + List strings = + this.repository.batchUpsertEmbeddings( + postgresEndpoint.getTableName(), + postgresEndpoint.getWordEmbeddingsList(), + postgresEndpoint.getFilename(), + getNamespace(postgresEndpoint)); + + List stringResponseList = + strings.stream().map(StringResponse::new).toList(); + + emitter.onNext(stringResponseList); + emitter.onComplete(); + + } catch (final Exception e) { + emitter.onError(e); + } + }), + postgresEndpoint); + } + + public EdgeChain upsert(PostgresEndpoint postgresEndpoint) { + + return new EdgeChain<>( + Observable.create( + emitter -> { + try { + + // Upsert Embeddings + String embeddingId = + this.repository.upsertEmbeddings( + postgresEndpoint.getTableName(), + postgresEndpoint.getWordEmbedding(), + postgresEndpoint.getFilename(), + getNamespace(postgresEndpoint)); + + emitter.onNext(new StringResponse(embeddingId)); + emitter.onComplete(); + + } catch (final Exception e) { + emitter.onError(e); + } + }), + postgresEndpoint); + } + + public EdgeChain insertMetadata(PostgresEndpoint postgresEndpoint) { + + return new EdgeChain<>( + Observable.create( + emitter -> { + try { + String metadata = postgresEndpoint.getMetadata(); + String input = metadata.replace("'", ""); + + String metadataId = + this.metadataRepository.insertMetadata( + postgresEndpoint.getMetadataTableNames().get(0), + input, + postgresEndpoint.getDocumentDate()); + + emitter.onNext(new StringResponse(metadataId)); + emitter.onComplete(); + + } catch (final Exception e) { + emitter.onError(e); + } + }), + postgresEndpoint); + } + + public EdgeChain> batchInsertMetadata(PostgresEndpoint postgresEndpoint) { + + return new EdgeChain<>( + Observable.create( + emitter -> { + try { + + // Insert metadata + List strings = + this.metadataRepository.batchInsertMetadata( + postgresEndpoint.getMetadataTableNames().get(0), + postgresEndpoint.getMetadataList()); + + List stringResponseList = + strings.stream().map(StringResponse::new).toList(); + + emitter.onNext(stringResponseList); + emitter.onComplete(); + + } catch (final Exception e) { + emitter.onError(e); + } + }), + postgresEndpoint); + } + + public EdgeChain insertIntoJoinTable(PostgresEndpoint postgresEndpoint) { + return new EdgeChain<>( + Observable.create( + emitter -> { + try { + + this.metadataRepository.insertIntoJoinTable(postgresEndpoint); + + emitter.onNext(new StringResponse("Inserted")); + emitter.onComplete(); + + } catch (final Exception e) { + emitter.onError(e); + } + }), + postgresEndpoint); + } + + public EdgeChain> query(PostgresEndpoint postgresEndpoint) { + + return new EdgeChain<>( + Observable.create( + emitter -> { + try { + List wordEmbeddingsList = new ArrayList<>(); + List> rows = + this.repository.query( + postgresEndpoint.getTableName(), + getNamespace(postgresEndpoint), + postgresEndpoint.getProbes(), + postgresEndpoint.getMetric(), + postgresEndpoint.getWordEmbedding().getValues(), + postgresEndpoint.getTopK()); + + for (Map row : rows) { + + PostgresWordEmbeddings val = new PostgresWordEmbeddings(); + val.setId(row.get("id").toString()); + val.setRawText((String) row.get("raw_text")); + val.setFilename((String) row.get("filename")); + val.setTimestamp(((Timestamp) row.get("timestamp")).toLocalDateTime()); + val.setNamespace((String) row.get("namespace")); + val.setScore((Double) row.get("score")); + + wordEmbeddingsList.add(val); + } + emitter.onNext(wordEmbeddingsList); + emitter.onComplete(); + + } catch (final Exception e) { + emitter.onError(e); + } + }), + postgresEndpoint); + } + + public EdgeChain> queryWithMetadata( + PostgresEndpoint postgresEndpoint) { + + return new EdgeChain<>( + Observable.create( + emitter -> { + try { + List wordEmbeddingsList = new ArrayList<>(); + if (postgresEndpoint.getMetadataTableNames() != null) { + try { + List metadataTableNames = postgresEndpoint.getMetadataTableNames(); + int numberOfMetadataTables = metadataTableNames.size(); + + /* + * This map will store the pairs + * We need to extract the title info from another metadata table. + * So instead of having extra PostgresWordEmbeddings objects for the title info + * We can store the title info in a map corresponding to the id key of the embeddings table + * Then after the loop is over we can inject the title field in the correct PostgresWordEmbeddings object by using the id key. + */ + Map titleMetadataMap = new HashMap<>(); + Map dateMetadataMap = new HashMap<>(); + for (String metadataTableName : metadataTableNames) { + List> rows = + this.metadataRepository.queryWithMetadata( + postgresEndpoint.getTableName(), + metadataTableName, + getNamespace(postgresEndpoint), + postgresEndpoint.getProbes(), + postgresEndpoint.getMetric(), + postgresEndpoint.getWordEmbedding().getValues(), + postgresEndpoint.getTopK()); + + // To filter out duplicate context chunks + Set contextChunkIds = new HashSet<>(); + for (Map row : rows) { + String metadataId = row.get("metadata_id").toString(); + if (!metadataTableName.contains("_title_metadata") + && contextChunkIds.contains(metadataId)) continue; + + PostgresWordEmbeddings val = new PostgresWordEmbeddings(); + final String idStr = row.get("id").toString(); + val.setId(idStr); + val.setRawText((String) row.get("raw_text")); + val.setFilename((String) row.get("filename")); + val.setTimestamp(((Timestamp) row.get("timestamp")).toLocalDateTime()); + val.setNamespace((String) row.get("namespace")); + val.setScore((Double) row.get("score")); + + // Add metadata fields in response + if (metadataTableName.contains("_title_metadata")) { + titleMetadataMap.put(idStr, (String) row.get("metadata")); + dateMetadataMap.put(idStr, (String) row.get("document_date")); + + // For checking if only one metadata table is present which is the title + // table + if (numberOfMetadataTables > 1) continue; + } else { + val.setMetadata((String) row.get("metadata")); + } + contextChunkIds.add(metadataId); + wordEmbeddingsList.add(val); + } + + // Insert the title and date fields into their respective + // PostgresWordEmbeddings + for (PostgresWordEmbeddings wordEmbedding : wordEmbeddingsList) { + String id = wordEmbedding.getId(); + if (titleMetadataMap.containsKey(id)) { + wordEmbedding.setTitleMetadata(titleMetadataMap.get(id)); + } + if (dateMetadataMap.containsKey(id)) { + wordEmbedding.setDocumentDate(dateMetadataMap.get(id)); + } + } + } + } catch (Exception e) { + logger.warn("ignored query error", e); + } + } + + emitter.onNext(wordEmbeddingsList); + emitter.onComplete(); + + } catch (final Exception e) { + emitter.onError(e); + } + }), + postgresEndpoint); + } + + public EdgeChain> getAllChunks(PostgresEndpoint postgresEndpoint) { + return new EdgeChain<>( + Observable.create( + emitter -> { + try { + List wordEmbeddingsList = new ArrayList<>(); + List> rows = this.repository.getAllChunks(postgresEndpoint); + for (Map row : rows) { + PostgresWordEmbeddings val = new PostgresWordEmbeddings(); + val.setId(row.get("id").toString()); + val.setRawText((String) row.get("raw_text")); + val.setFilename((String) row.get("filename")); + PGobject pgObject = (PGobject) row.get("embedding"); + String jsonString = pgObject.getValue(); + List values = objectMapper.readerFor(FLOAT_TYPE_REF).readValue(jsonString); + val.setValues(values); + wordEmbeddingsList.add(val); + } + emitter.onNext(wordEmbeddingsList); + emitter.onComplete(); + } catch (final Exception e) { + emitter.onError(e); + } + }), + postgresEndpoint); + } + + public EdgeChain> getSimilarMetadataChunk( + PostgresEndpoint postgresEndpoint) { + + return new EdgeChain<>( + Observable.create( + emitter -> { + try { + List wordEmbeddingsList = new ArrayList<>(); + List> rows = + this.metadataRepository.getSimilarMetadataChunk( + postgresEndpoint.getMetadataTableNames().get(0), + postgresEndpoint.getEmbeddingChunk()); + for (Map row : rows) { + + PostgresWordEmbeddings val = new PostgresWordEmbeddings(); + val.setMetadataId(row.get("metadata_id").toString()); + val.setMetadata((String) row.get("metadata")); + + wordEmbeddingsList.add(val); + } + + emitter.onNext(wordEmbeddingsList); + emitter.onComplete(); + + } catch (final Exception e) { + emitter.onError(e); + } + }), + postgresEndpoint); + } + + public EdgeChain deleteAll(PostgresEndpoint postgresEndpoint) { + + return new EdgeChain<>( + Observable.create( + emitter -> { + String namespace = getNamespace(postgresEndpoint); + try { + this.repository.deleteAll(postgresEndpoint.getTableName(), namespace); + emitter.onNext( + new StringResponse( + "Word embeddings are successfully deleted for namespace:" + namespace)); + emitter.onComplete(); + } catch (final Exception e) { + emitter.onError(e); + } + }), + postgresEndpoint); + } + + private String getNamespace(PostgresEndpoint postgresEndpoint) { + return (Objects.isNull(postgresEndpoint.getNamespace()) + || postgresEndpoint.getNamespace().isEmpty()) + ? "knowledge" + : postgresEndpoint.getNamespace(); + } +} diff --git a/FlySpring/edgechain-app/src/main/java/com/edgechain/lib/index/repositories/PostgresClientMetadataRepository.java b/FlySpring/edgechain-app/src/main/java/com/edgechain/lib/index/repositories/PostgresClientMetadataRepository.java index 7b677467a..490b90aaf 100644 --- a/FlySpring/edgechain-app/src/main/java/com/edgechain/lib/index/repositories/PostgresClientMetadataRepository.java +++ b/FlySpring/edgechain-app/src/main/java/com/edgechain/lib/index/repositories/PostgresClientMetadataRepository.java @@ -1,173 +1,173 @@ -package com.edgechain.lib.index.repositories; - -import com.edgechain.lib.endpoint.impl.PostgresEndpoint; -import com.edgechain.lib.index.enums.PostgresDistanceMetric; -import com.edgechain.lib.utils.FloatUtils; -import com.github.f4b6a3.uuid.UuidCreator; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.jdbc.core.JdbcTemplate; -import org.springframework.stereotype.Repository; -import org.springframework.transaction.annotation.Propagation; -import org.springframework.transaction.annotation.Transactional; - -import java.util.*; - -@Repository -public class PostgresClientMetadataRepository { - - @Autowired private JdbcTemplate jdbcTemplate; - - @Transactional - public void createTable(PostgresEndpoint postgresEndpoint) { - String metadataTable = postgresEndpoint.getMetadataTableNames().get(0); - jdbcTemplate.execute( - String.format( - "CREATE TABLE IF NOT EXISTS %s (metadata_id UUID PRIMARY KEY, metadata TEXT NOT NULL," - + " document_date DATE);", - metadataTable)); - - // Create a JOIN table - jdbcTemplate.execute( - String.format( - "CREATE TABLE IF NOT EXISTS %s (id UUID, metadata_id UUID, " - + "FOREIGN KEY (id) REFERENCES %s(id), " - + "FOREIGN KEY (metadata_id) REFERENCES %s(metadata_id), " - + "PRIMARY KEY (id, metadata_id));", - postgresEndpoint.getTableName() + "_join_" + metadataTable, - postgresEndpoint.getTableName(), - metadataTable)); - } - - @Transactional - public List batchInsertMetadata(String metadataTableName, List metadataList) { - - Set uuidSet = new HashSet<>(); - - for (int i = 0; i < metadataList.size(); i++) { - UUID metadataId = - jdbcTemplate.queryForObject( - String.format( - "INSERT INTO %s (metadata_id, metadata) VALUES ('%s', '%s') RETURNING" - + " metadata_id;", - metadataTableName, UuidCreator.getTimeOrderedEpoch(), metadataList.get(i)), - UUID.class); - - if (metadataId != null) { - uuidSet.add(metadataId.toString()); - } - } - - return new ArrayList<>(uuidSet); - } - - @Transactional - public String insertMetadata(String metadataTableName, String metadata, String documentDate) { - - UUID uuid = UuidCreator.getTimeOrderedEpoch(); - jdbcTemplate.update( - String.format( - "INSERT INTO %s (metadata_id, metadata, document_date) VALUES ('%s', '%s'," - + " TO_DATE(NULLIF('%s', ''), 'Month DD, YYYY'));", - metadataTableName, uuid, metadata, documentDate)); - return uuid.toString(); - } - - @Transactional - public void insertIntoJoinTable(PostgresEndpoint postgresEndpoint) { - String joinTableName = - postgresEndpoint.getTableName() - + "_join_" - + postgresEndpoint.getMetadataTableNames().get(0); - jdbcTemplate.execute( - String.format( - "INSERT INTO %s (id, metadata_id) VALUES ('%s', '%s');", - joinTableName, - UUID.fromString(postgresEndpoint.getId()), - UUID.fromString(postgresEndpoint.getMetadataId()))); - } - - @Transactional(readOnly = true, propagation = Propagation.REQUIRED) - public List> queryWithMetadata( - String tableName, - String metadataTableName, - String namespace, - int probes, - PostgresDistanceMetric metric, - List values, - int topK) { - - String embeddings = Arrays.toString(FloatUtils.toFloatArray(values)); - - jdbcTemplate.execute(String.format("SET LOCAL ivfflat.probes = %s;", probes)); - String joinTable = tableName + "_join_" + metadataTableName; - - if (metric.equals(PostgresDistanceMetric.IP)) { - return jdbcTemplate.queryForList( - String.format( - "SELECT e.id, metadata, TO_CHAR(document_date, 'Month DD, YYYY') as document_date," - + " j.metadata_id, raw_text, namespace, filename, timestamp, ( embedding <#>" - + " '%s') * -1 AS score FROM %s e INNER JOIN %s j ON e.id = j.id INNER JOIN %s m" - + " ON j.metadata_id = m.metadata_id WHERE namespace='%s' ORDER BY embedding %s" - + " '%s' LIMIT %s;", - embeddings, - tableName, - joinTable, - metadataTableName, - namespace, - PostgresDistanceMetric.getDistanceMetric(metric), - embeddings, - topK)); - - } else if (metric.equals(PostgresDistanceMetric.COSINE)) { - return jdbcTemplate.queryForList( - String.format( - "SELECT e.id, metadata, TO_CHAR(document_date, 'Month DD, YYYY') as document_date," - + " j.metadata_id, raw_text, namespace, filename, timestamp, 1 - ( embedding <=>" - + " '%s') AS score FROM %s e INNER JOIN %s j ON e.id = j.id INNER JOIN %s m ON" - + " j.metadata_id = m.metadata_id WHERE namespace='%s' ORDER BY embedding %s '%s'" - + " LIMIT %s;", - embeddings, - tableName, - joinTable, - metadataTableName, - namespace, - PostgresDistanceMetric.getDistanceMetric(metric), - embeddings, - topK)); - } else { - return jdbcTemplate.queryForList( - String.format( - "SELECT e.id, metadata, TO_CHAR(document_date, 'Month DD, YYYY') as document_date," - + " j.metadata_id, raw_text, namespace, filename, timestamp, (embedding <-> '%s')" - + " AS score FROM %s e INNER JOIN %s j ON e.id = j.id INNER JOIN %s m ON" - + " j.metadata_id = m.metadata_id WHERE namespace='%s' ORDER BY embedding %s '%s'" - + " ASC LIMIT %s;", - embeddings, - tableName, - joinTable, - metadataTableName, - namespace, - PostgresDistanceMetric.getDistanceMetric(metric), - embeddings, - topK)); - } - } - - // Full-text search - @Transactional(readOnly = true, propagation = Propagation.REQUIRED) - public List> getSimilarMetadataChunk( - String metadataTableName, String embeddingChunk) { - // Remove special characters and replace with a space - String cleanEmbeddingChunk = - embeddingChunk.replaceAll("[^a-zA-Z0-9\\s]", " ").replaceAll("\\s+", " ").trim(); - - // Split the embeddingChunk into words and join them with the '|' (OR) operator - String tsquery = String.join(" | ", cleanEmbeddingChunk.split("\\s+")); - return jdbcTemplate.queryForList( - String.format( - "SELECT *, ts_rank(to_tsvector(%s.metadata), query) as rank_metadata " - + "FROM %s, to_tsvector(%s.metadata) document, to_tsquery('%s') query " - + "WHERE query @@ document ORDER BY rank_metadata DESC", - metadataTableName, metadataTableName, metadataTableName, tsquery)); - } -} +package com.edgechain.lib.index.repositories; + +import com.edgechain.lib.endpoint.impl.PostgresEndpoint; +import com.edgechain.lib.index.enums.PostgresDistanceMetric; +import com.edgechain.lib.utils.FloatUtils; +import com.github.f4b6a3.uuid.UuidCreator; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.jdbc.core.JdbcTemplate; +import org.springframework.stereotype.Repository; +import org.springframework.transaction.annotation.Propagation; +import org.springframework.transaction.annotation.Transactional; + +import java.util.*; + +@Repository +public class PostgresClientMetadataRepository { + + @Autowired private JdbcTemplate jdbcTemplate; + + @Transactional + public void createTable(PostgresEndpoint postgresEndpoint) { + String metadataTable = postgresEndpoint.getMetadataTableNames().get(0); + jdbcTemplate.execute( + String.format( + "CREATE TABLE IF NOT EXISTS %s (metadata_id UUID PRIMARY KEY, metadata TEXT NOT NULL," + + " document_date DATE);", + metadataTable)); + + // Create a JOIN table + jdbcTemplate.execute( + String.format( + "CREATE TABLE IF NOT EXISTS %s (id UUID, metadata_id UUID, " + + "FOREIGN KEY (id) REFERENCES %s(id), " + + "FOREIGN KEY (metadata_id) REFERENCES %s(metadata_id), " + + "PRIMARY KEY (id, metadata_id));", + postgresEndpoint.getTableName() + "_join_" + metadataTable, + postgresEndpoint.getTableName(), + metadataTable)); + } + + @Transactional + public List batchInsertMetadata(String metadataTableName, List metadataList) { + + Set uuidSet = new HashSet<>(); + + for (int i = 0; i < metadataList.size(); i++) { + UUID metadataId = + jdbcTemplate.queryForObject( + String.format( + "INSERT INTO %s (metadata_id, metadata) VALUES ('%s', '%s') RETURNING" + + " metadata_id;", + metadataTableName, UuidCreator.getTimeOrderedEpoch(), metadataList.get(i)), + UUID.class); + + if (metadataId != null) { + uuidSet.add(metadataId.toString()); + } + } + + return new ArrayList<>(uuidSet); + } + + @Transactional + public String insertMetadata(String metadataTableName, String metadata, String documentDate) { + + UUID uuid = UuidCreator.getTimeOrderedEpoch(); + jdbcTemplate.update( + String.format( + "INSERT INTO %s (metadata_id, metadata, document_date) VALUES ('%s', '%s'," + + " TO_DATE(NULLIF('%s', ''), 'Month DD, YYYY'));", + metadataTableName, uuid, metadata, documentDate)); + return uuid.toString(); + } + + @Transactional + public void insertIntoJoinTable(PostgresEndpoint postgresEndpoint) { + String joinTableName = + postgresEndpoint.getTableName() + + "_join_" + + postgresEndpoint.getMetadataTableNames().get(0); + jdbcTemplate.execute( + String.format( + "INSERT INTO %s (id, metadata_id) VALUES ('%s', '%s');", + joinTableName, + UUID.fromString(postgresEndpoint.getId()), + UUID.fromString(postgresEndpoint.getMetadataId()))); + } + + @Transactional(readOnly = true, propagation = Propagation.REQUIRED) + public List> queryWithMetadata( + String tableName, + String metadataTableName, + String namespace, + int probes, + PostgresDistanceMetric metric, + List values, + int topK) { + + String embeddings = Arrays.toString(FloatUtils.toFloatArray(values)); + + jdbcTemplate.execute(String.format("SET LOCAL ivfflat.probes = %s;", probes)); + String joinTable = tableName + "_join_" + metadataTableName; + + if (metric.equals(PostgresDistanceMetric.IP)) { + return jdbcTemplate.queryForList( + String.format( + "SELECT e.id, metadata, TO_CHAR(document_date, 'Month DD, YYYY') as document_date," + + " j.metadata_id, raw_text, namespace, filename, timestamp, ( embedding <#>" + + " '%s') * -1 AS score FROM %s e INNER JOIN %s j ON e.id = j.id INNER JOIN %s m" + + " ON j.metadata_id = m.metadata_id WHERE namespace='%s' ORDER BY embedding %s" + + " '%s' LIMIT %s;", + embeddings, + tableName, + joinTable, + metadataTableName, + namespace, + PostgresDistanceMetric.getDistanceMetric(metric), + embeddings, + topK)); + + } else if (metric.equals(PostgresDistanceMetric.COSINE)) { + return jdbcTemplate.queryForList( + String.format( + "SELECT e.id, metadata, TO_CHAR(document_date, 'Month DD, YYYY') as document_date," + + " j.metadata_id, raw_text, namespace, filename, timestamp, 1 - ( embedding <=>" + + " '%s') AS score FROM %s e INNER JOIN %s j ON e.id = j.id INNER JOIN %s m ON" + + " j.metadata_id = m.metadata_id WHERE namespace='%s' ORDER BY embedding %s '%s'" + + " LIMIT %s;", + embeddings, + tableName, + joinTable, + metadataTableName, + namespace, + PostgresDistanceMetric.getDistanceMetric(metric), + embeddings, + topK)); + } else { + return jdbcTemplate.queryForList( + String.format( + "SELECT e.id, metadata, TO_CHAR(document_date, 'Month DD, YYYY') as document_date," + + " j.metadata_id, raw_text, namespace, filename, timestamp, (embedding <-> '%s')" + + " AS score FROM %s e INNER JOIN %s j ON e.id = j.id INNER JOIN %s m ON" + + " j.metadata_id = m.metadata_id WHERE namespace='%s' ORDER BY embedding %s '%s'" + + " ASC LIMIT %s;", + embeddings, + tableName, + joinTable, + metadataTableName, + namespace, + PostgresDistanceMetric.getDistanceMetric(metric), + embeddings, + topK)); + } + } + + // Full-text search + @Transactional(readOnly = true, propagation = Propagation.REQUIRED) + public List> getSimilarMetadataChunk( + String metadataTableName, String embeddingChunk) { + // Remove special characters and replace with a space + String cleanEmbeddingChunk = + embeddingChunk.replaceAll("[^a-zA-Z0-9\\s]", " ").replaceAll("\\s+", " ").trim(); + + // Split the embeddingChunk into words and join them with the '|' (OR) operator + String tsquery = String.join(" | ", cleanEmbeddingChunk.split("\\s+")); + return jdbcTemplate.queryForList( + String.format( + "SELECT *, ts_rank(to_tsvector(%s.metadata), query) as rank_metadata " + + "FROM %s, to_tsvector(%s.metadata) document, to_tsquery('%s') query " + + "WHERE query @@ document ORDER BY rank_metadata DESC", + metadataTableName, metadataTableName, metadataTableName, tsquery)); + } +} diff --git a/FlySpring/edgechain-app/src/main/java/com/edgechain/lib/index/repositories/PostgresClientRepository.java b/FlySpring/edgechain-app/src/main/java/com/edgechain/lib/index/repositories/PostgresClientRepository.java index 247c0a3a3..e580784d5 100644 --- a/FlySpring/edgechain-app/src/main/java/com/edgechain/lib/index/repositories/PostgresClientRepository.java +++ b/FlySpring/edgechain-app/src/main/java/com/edgechain/lib/index/repositories/PostgresClientRepository.java @@ -1,209 +1,209 @@ -package com.edgechain.lib.index.repositories; - -import com.edgechain.lib.embeddings.WordEmbeddings; -import com.edgechain.lib.endpoint.impl.PostgresEndpoint; -import com.edgechain.lib.index.enums.PostgresDistanceMetric; -import com.edgechain.lib.utils.FloatUtils; -import com.github.f4b6a3.uuid.UuidCreator; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.jdbc.core.JdbcTemplate; -import org.springframework.stereotype.Repository; -import org.springframework.transaction.annotation.Propagation; -import org.springframework.transaction.annotation.Transactional; - -import java.time.LocalDateTime; -import java.util.*; - -@Repository -public class PostgresClientRepository { - - @Autowired private JdbcTemplate jdbcTemplate; - - @Transactional - public void createTable(PostgresEndpoint postgresEndpoint) { - - jdbcTemplate.execute("CREATE EXTENSION IF NOT EXISTS vector;"); - - String checkTableQuery = - String.format( - "SELECT COUNT(*) FROM information_schema.tables WHERE table_name = '%s'", - postgresEndpoint.getTableName()); - - int tableExists = jdbcTemplate.queryForObject(checkTableQuery, Integer.class); - - String indexName; - String vectorOps; - - if (PostgresDistanceMetric.L2.equals(postgresEndpoint.getMetric())) { - indexName = postgresEndpoint.getTableName().concat("_").concat("l2_idx"); - vectorOps = "vector_l2_ops"; - } else if (PostgresDistanceMetric.COSINE.equals(postgresEndpoint.getMetric())) { - indexName = postgresEndpoint.getTableName().concat("_").concat("cosine_idx"); - vectorOps = "vector_cosine_ops"; - } else { - indexName = postgresEndpoint.getTableName().concat("_").concat("ip_idx"); - vectorOps = "vector_ip_ops"; - } - - String indexQuery = - String.format( - "CREATE INDEX IF NOT EXISTS %s ON %s USING ivfflat (embedding %s) WITH" - + " (lists = %s);", - indexName, postgresEndpoint.getTableName(), vectorOps, postgresEndpoint.getLists()); - - if (tableExists == 0) { - - jdbcTemplate.execute( - String.format( - "CREATE TABLE IF NOT EXISTS %s (id UUID PRIMARY KEY, " - + " raw_text TEXT NOT NULL UNIQUE, embedding vector(%s), timestamp" - + " TIMESTAMP NOT NULL, namespace TEXT, filename VARCHAR(255) );", - postgresEndpoint.getTableName(), postgresEndpoint.getDimensions())); - - jdbcTemplate.execute(indexQuery); - - } else { - - String checkIndexQuery = - String.format( - "SELECT COUNT(*) FROM pg_indexes WHERE tablename = '%s' AND indexname = '%s';", - postgresEndpoint.getTableName(), indexName); - - int indexExists = jdbcTemplate.queryForObject(checkIndexQuery, Integer.class); - - if (indexExists != 1) - throw new RuntimeException( - "No index is specifed therefore use the following SQL:\n" + indexQuery); - } - } - - @Transactional - public List batchUpsertEmbeddings( - String tableName, - List wordEmbeddingsList, - String filename, - String namespace) { - - Set uuidSet = new HashSet<>(); - - for (int i = 0; i < wordEmbeddingsList.size(); i++) { - WordEmbeddings wordEmbeddings = wordEmbeddingsList.get(i); - - if (wordEmbeddings != null && wordEmbeddings.getValues() != null) { - - float[] floatArray = FloatUtils.toFloatArray(wordEmbeddings.getValues()); - - UUID id = - jdbcTemplate.queryForObject( - String.format( - "INSERT INTO %s (id, raw_text, embedding, timestamp, namespace, filename)" - + " VALUES ('%s', '%s', '%s', '%s', '%s', '%s') ON CONFLICT (raw_text) DO" - + " UPDATE SET embedding = EXCLUDED.embedding RETURNING id;", - tableName, - UuidCreator.getTimeOrderedEpoch(), - wordEmbeddings.getId(), - Arrays.toString(floatArray), - LocalDateTime.now(), - namespace, - filename), - UUID.class); - - if (id != null) { - uuidSet.add(id.toString()); - } - } - } - - return new ArrayList<>(uuidSet); - } - - @Transactional - public String upsertEmbeddings( - String tableName, WordEmbeddings wordEmbeddings, String filename, String namespace) { - - UUID uuid = UuidCreator.getTimeOrderedEpoch(); - - jdbcTemplate.update( - String.format( - "INSERT INTO %s (id, raw_text, embedding, timestamp, namespace, filename) VALUES ('%s'," - + " '%s', '%s', '%s', '%s', '%s') ON CONFLICT (raw_text) DO UPDATE SET embedding =" - + " EXCLUDED.embedding;", - tableName, - uuid, - wordEmbeddings.getId(), - Arrays.toString(FloatUtils.toFloatArray(wordEmbeddings.getValues())), - LocalDateTime.now(), - namespace, - filename)); - - return uuid.toString(); - } - - @Transactional(readOnly = true, propagation = Propagation.REQUIRED) - public List> query( - String tableName, - String namespace, - int probes, - PostgresDistanceMetric metric, - List values, - int topK) { - - String embeddings = Arrays.toString(FloatUtils.toFloatArray(values)); - - jdbcTemplate.execute(String.format("SET LOCAL ivfflat.probes = %s;", probes)); - if (metric.equals(PostgresDistanceMetric.IP)) { - - return jdbcTemplate.queryForList( - String.format( - "SELECT id, raw_text, namespace, filename, timestamp, ( embedding <#>" - + " '%s') * -1 AS score FROM %s WHERE namespace='%s' ORDER BY embedding %s '%s'" - + " LIMIT %s;", - embeddings, - tableName, - namespace, - PostgresDistanceMetric.getDistanceMetric(metric), - embeddings, - topK)); - - } else if (metric.equals(PostgresDistanceMetric.COSINE)) { - - return jdbcTemplate.queryForList( - String.format( - "SELECT id, raw_text, namespace, filename, timestamp, 1 - ( embedding" - + " <=> '%s') AS score FROM %s WHERE namespace='%s' ORDER BY embedding %s '%s'" - + " LIMIT %s;", - embeddings, - tableName, - namespace, - PostgresDistanceMetric.getDistanceMetric(metric), - embeddings, - topK)); - } else { - return jdbcTemplate.queryForList( - String.format( - "SELECT id, raw_text, namespace, filename, timestamp, (embedding <->" - + " '%s') AS score FROM %s WHERE namespace='%s' ORDER BY embedding %s '%s' ASC" - + " LIMIT %s;", - embeddings, - tableName, - namespace, - PostgresDistanceMetric.getDistanceMetric(metric), - embeddings, - topK)); - } - } - - @Transactional(readOnly = true) - public List> getAllChunks(PostgresEndpoint endpoint) { - return jdbcTemplate.queryForList( - String.format( - "SELECT id, raw_text, embedding, filename from %s WHERE filename = '%s';", - endpoint.getTableName(), endpoint.getFilename())); - } - - @Transactional - public void deleteAll(String tableName, String namespace) { - jdbcTemplate.execute( - String.format("delete from %s where namespace='%s'", tableName, namespace)); - } -} +package com.edgechain.lib.index.repositories; + +import com.edgechain.lib.embeddings.WordEmbeddings; +import com.edgechain.lib.endpoint.impl.PostgresEndpoint; +import com.edgechain.lib.index.enums.PostgresDistanceMetric; +import com.edgechain.lib.utils.FloatUtils; +import com.github.f4b6a3.uuid.UuidCreator; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.jdbc.core.JdbcTemplate; +import org.springframework.stereotype.Repository; +import org.springframework.transaction.annotation.Propagation; +import org.springframework.transaction.annotation.Transactional; + +import java.time.LocalDateTime; +import java.util.*; + +@Repository +public class PostgresClientRepository { + + @Autowired private JdbcTemplate jdbcTemplate; + + @Transactional + public void createTable(PostgresEndpoint postgresEndpoint) { + + jdbcTemplate.execute("CREATE EXTENSION IF NOT EXISTS vector;"); + + String checkTableQuery = + String.format( + "SELECT COUNT(*) FROM information_schema.tables WHERE table_name = '%s'", + postgresEndpoint.getTableName()); + + int tableExists = jdbcTemplate.queryForObject(checkTableQuery, Integer.class); + + String indexName; + String vectorOps; + + if (PostgresDistanceMetric.L2.equals(postgresEndpoint.getMetric())) { + indexName = postgresEndpoint.getTableName().concat("_").concat("l2_idx"); + vectorOps = "vector_l2_ops"; + } else if (PostgresDistanceMetric.COSINE.equals(postgresEndpoint.getMetric())) { + indexName = postgresEndpoint.getTableName().concat("_").concat("cosine_idx"); + vectorOps = "vector_cosine_ops"; + } else { + indexName = postgresEndpoint.getTableName().concat("_").concat("ip_idx"); + vectorOps = "vector_ip_ops"; + } + + String indexQuery = + String.format( + "CREATE INDEX IF NOT EXISTS %s ON %s USING ivfflat (embedding %s) WITH" + + " (lists = %s);", + indexName, postgresEndpoint.getTableName(), vectorOps, postgresEndpoint.getLists()); + + if (tableExists == 0) { + + jdbcTemplate.execute( + String.format( + "CREATE TABLE IF NOT EXISTS %s (id UUID PRIMARY KEY, " + + " raw_text TEXT NOT NULL UNIQUE, embedding vector(%s), timestamp" + + " TIMESTAMP NOT NULL, namespace TEXT, filename VARCHAR(255) );", + postgresEndpoint.getTableName(), postgresEndpoint.getDimensions())); + + jdbcTemplate.execute(indexQuery); + + } else { + + String checkIndexQuery = + String.format( + "SELECT COUNT(*) FROM pg_indexes WHERE tablename = '%s' AND indexname = '%s';", + postgresEndpoint.getTableName(), indexName); + + int indexExists = jdbcTemplate.queryForObject(checkIndexQuery, Integer.class); + + if (indexExists != 1) + throw new RuntimeException( + "No index is specifed therefore use the following SQL:\n" + indexQuery); + } + } + + @Transactional + public List batchUpsertEmbeddings( + String tableName, + List wordEmbeddingsList, + String filename, + String namespace) { + + Set uuidSet = new HashSet<>(); + + for (int i = 0; i < wordEmbeddingsList.size(); i++) { + WordEmbeddings wordEmbeddings = wordEmbeddingsList.get(i); + + if (wordEmbeddings != null && wordEmbeddings.getValues() != null) { + + float[] floatArray = FloatUtils.toFloatArray(wordEmbeddings.getValues()); + + UUID id = + jdbcTemplate.queryForObject( + String.format( + "INSERT INTO %s (id, raw_text, embedding, timestamp, namespace, filename)" + + " VALUES ('%s', '%s', '%s', '%s', '%s', '%s') ON CONFLICT (raw_text) DO" + + " UPDATE SET embedding = EXCLUDED.embedding RETURNING id;", + tableName, + UuidCreator.getTimeOrderedEpoch(), + wordEmbeddings.getId(), + Arrays.toString(floatArray), + LocalDateTime.now(), + namespace, + filename), + UUID.class); + + if (id != null) { + uuidSet.add(id.toString()); + } + } + } + + return new ArrayList<>(uuidSet); + } + + @Transactional + public String upsertEmbeddings( + String tableName, WordEmbeddings wordEmbeddings, String filename, String namespace) { + + UUID uuid = UuidCreator.getTimeOrderedEpoch(); + + jdbcTemplate.update( + String.format( + "INSERT INTO %s (id, raw_text, embedding, timestamp, namespace, filename) VALUES ('%s'," + + " '%s', '%s', '%s', '%s', '%s') ON CONFLICT (raw_text) DO UPDATE SET embedding =" + + " EXCLUDED.embedding;", + tableName, + uuid, + wordEmbeddings.getId(), + Arrays.toString(FloatUtils.toFloatArray(wordEmbeddings.getValues())), + LocalDateTime.now(), + namespace, + filename)); + + return uuid.toString(); + } + + @Transactional(readOnly = true, propagation = Propagation.REQUIRED) + public List> query( + String tableName, + String namespace, + int probes, + PostgresDistanceMetric metric, + List values, + int topK) { + + String embeddings = Arrays.toString(FloatUtils.toFloatArray(values)); + + jdbcTemplate.execute(String.format("SET LOCAL ivfflat.probes = %s;", probes)); + if (metric.equals(PostgresDistanceMetric.IP)) { + + return jdbcTemplate.queryForList( + String.format( + "SELECT id, raw_text, namespace, filename, timestamp, ( embedding <#>" + + " '%s') * -1 AS score FROM %s WHERE namespace='%s' ORDER BY embedding %s '%s'" + + " LIMIT %s;", + embeddings, + tableName, + namespace, + PostgresDistanceMetric.getDistanceMetric(metric), + embeddings, + topK)); + + } else if (metric.equals(PostgresDistanceMetric.COSINE)) { + + return jdbcTemplate.queryForList( + String.format( + "SELECT id, raw_text, namespace, filename, timestamp, 1 - ( embedding" + + " <=> '%s') AS score FROM %s WHERE namespace='%s' ORDER BY embedding %s '%s'" + + " LIMIT %s;", + embeddings, + tableName, + namespace, + PostgresDistanceMetric.getDistanceMetric(metric), + embeddings, + topK)); + } else { + return jdbcTemplate.queryForList( + String.format( + "SELECT id, raw_text, namespace, filename, timestamp, (embedding <->" + + " '%s') AS score FROM %s WHERE namespace='%s' ORDER BY embedding %s '%s' ASC" + + " LIMIT %s;", + embeddings, + tableName, + namespace, + PostgresDistanceMetric.getDistanceMetric(metric), + embeddings, + topK)); + } + } + + @Transactional(readOnly = true) + public List> getAllChunks(PostgresEndpoint endpoint) { + return jdbcTemplate.queryForList( + String.format( + "SELECT id, raw_text, embedding, filename from %s WHERE filename = '%s';", + endpoint.getTableName(), endpoint.getFilename())); + } + + @Transactional + public void deleteAll(String tableName, String namespace) { + jdbcTemplate.execute( + String.format("delete from %s where namespace='%s'", tableName, namespace)); + } +} diff --git a/FlySpring/edgechain-app/src/main/java/com/edgechain/lib/supabase/security/JwtFilter.java b/FlySpring/edgechain-app/src/main/java/com/edgechain/lib/supabase/security/JwtFilter.java index 480b62fc9..b8a73efae 100644 --- a/FlySpring/edgechain-app/src/main/java/com/edgechain/lib/supabase/security/JwtFilter.java +++ b/FlySpring/edgechain-app/src/main/java/com/edgechain/lib/supabase/security/JwtFilter.java @@ -1,90 +1,90 @@ -package com.edgechain.lib.supabase.security; - -import com.edgechain.lib.configuration.WebConfiguration; -import com.edgechain.lib.configuration.domain.SecurityUUID; -import com.edgechain.lib.exceptions.response.ErrorResponse; -import com.edgechain.lib.supabase.entities.User; -import com.edgechain.lib.supabase.exceptions.FilterException; -import com.edgechain.lib.supabase.utils.AuthUtils; -import com.edgechain.lib.utils.JsonUtils; -import io.jsonwebtoken.*; -import jakarta.servlet.*; -import jakarta.servlet.http.HttpServletRequest; -import jakarta.servlet.http.HttpServletResponse; -import java.io.IOException; -import java.util.Objects; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.core.Ordered; -import org.springframework.core.annotation.Order; -import org.springframework.http.HttpStatus; -import org.springframework.http.MediaType; -import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; -import org.springframework.security.core.context.SecurityContextHolder; -import org.springframework.stereotype.Component; -import org.springframework.web.filter.OncePerRequestFilter; - -@Component -@Order(Ordered.HIGHEST_PRECEDENCE) -public class JwtFilter extends OncePerRequestFilter { - - @Autowired private JwtHelper jwtHelper; - - @Autowired private UserSecurityService userSecurityService; - - @Autowired private SecurityUUID securityUUID; - - @Override - protected void doFilterInternal( - HttpServletRequest request, HttpServletResponse response, FilterChain filter) - throws ServletException, IOException { - - if (request.getRequestURI().startsWith(WebConfiguration.CONTEXT_PATH)) { - String authHeader = request.getHeader("Authorization"); - if (Objects.isNull(authHeader) || !authHeader.equals(securityUUID.getAuthKey())) - throw new FilterException("Access Denied"); - } - - String token = AuthUtils.extractToken(request); - - if (token != null) { - - /** If you are using Supabase, then uncomment this line; * */ - // User user = userSecurityService.loadUserByUsername(token); - - /** - * Because, EdgeChains is a project independent of supabase; therefore, your jwt must contain - * these two fields: a) email: "", b) role: "authenticated, user_create" - */ - Jws claimsJws; - try { - claimsJws = jwtHelper.parseToken(token); - } catch (final Exception e) { - // use Spring Security logger here instead of SLF4J - logger.info("JWT not accepted: %s".formatted(e.getMessage())); - - ErrorResponse errorResponse = new ErrorResponse(e.getMessage()); - response.setContentType(MediaType.APPLICATION_JSON_VALUE); - response.getWriter().print(JsonUtils.convertToString(errorResponse)); - response.setStatus(HttpStatus.UNAUTHORIZED.value()); - return; - } - - String email = (String) claimsJws.getBody().get("email"); - String role = (String) claimsJws.getBody().get("role"); - - // use Spring Security logger here instead of SLF4J - logger.info("JWT email=%s role=%s".formatted(email, role)); - - User user = new User(); - user.setEmail(email); - user.setAccessToken(token); - user.setRole(role); - - UsernamePasswordAuthenticationToken authToken = - new UsernamePasswordAuthenticationToken(user, null, user.getAuthorities()); - SecurityContextHolder.getContext().setAuthentication(authToken); - } - - filter.doFilter(request, response); - } -} +package com.edgechain.lib.supabase.security; + +import com.edgechain.lib.configuration.WebConfiguration; +import com.edgechain.lib.configuration.domain.SecurityUUID; +import com.edgechain.lib.exceptions.response.ErrorResponse; +import com.edgechain.lib.supabase.entities.User; +import com.edgechain.lib.supabase.exceptions.FilterException; +import com.edgechain.lib.supabase.utils.AuthUtils; +import com.edgechain.lib.utils.JsonUtils; +import io.jsonwebtoken.*; +import jakarta.servlet.*; +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletResponse; +import java.io.IOException; +import java.util.Objects; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.core.Ordered; +import org.springframework.core.annotation.Order; +import org.springframework.http.HttpStatus; +import org.springframework.http.MediaType; +import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; +import org.springframework.security.core.context.SecurityContextHolder; +import org.springframework.stereotype.Component; +import org.springframework.web.filter.OncePerRequestFilter; + +@Component +@Order(Ordered.HIGHEST_PRECEDENCE) +public class JwtFilter extends OncePerRequestFilter { + + @Autowired private JwtHelper jwtHelper; + + @Autowired private UserSecurityService userSecurityService; + + @Autowired private SecurityUUID securityUUID; + + @Override + protected void doFilterInternal( + HttpServletRequest request, HttpServletResponse response, FilterChain filter) + throws ServletException, IOException { + + if (request.getRequestURI().startsWith(WebConfiguration.CONTEXT_PATH)) { + String authHeader = request.getHeader("Authorization"); + if (Objects.isNull(authHeader) || !authHeader.equals(securityUUID.getAuthKey())) + throw new FilterException("Access Denied"); + } + + String token = AuthUtils.extractToken(request); + + if (token != null) { + + /** If you are using Supabase, then uncomment this line; * */ + // User user = userSecurityService.loadUserByUsername(token); + + /** + * Because, EdgeChains is a project independent of supabase; therefore, your jwt must contain + * these two fields: a) email: "", b) role: "authenticated, user_create" + */ + Jws claimsJws; + try { + claimsJws = jwtHelper.parseToken(token); + } catch (final Exception e) { + // use Spring Security logger here instead of SLF4J + logger.info("JWT not accepted: %s".formatted(e.getMessage())); + + ErrorResponse errorResponse = new ErrorResponse(e.getMessage()); + response.setContentType(MediaType.APPLICATION_JSON_VALUE); + response.getWriter().print(JsonUtils.convertToString(errorResponse)); + response.setStatus(HttpStatus.UNAUTHORIZED.value()); + return; + } + + String email = (String) claimsJws.getBody().get("email"); + String role = (String) claimsJws.getBody().get("role"); + + // use Spring Security logger here instead of SLF4J + logger.info("JWT email=%s role=%s".formatted(email, role)); + + User user = new User(); + user.setEmail(email); + user.setAccessToken(token); + user.setRole(role); + + UsernamePasswordAuthenticationToken authToken = + new UsernamePasswordAuthenticationToken(user, null, user.getAuthorities()); + SecurityContextHolder.getContext().setAuthentication(authToken); + } + + filter.doFilter(request, response); + } +} diff --git a/FlySpring/edgechain-app/src/main/java/com/edgechain/lib/supabase/security/JwtHelper.java b/FlySpring/edgechain-app/src/main/java/com/edgechain/lib/supabase/security/JwtHelper.java index 2ff8fcf58..c5d9f4edc 100644 --- a/FlySpring/edgechain-app/src/main/java/com/edgechain/lib/supabase/security/JwtHelper.java +++ b/FlySpring/edgechain-app/src/main/java/com/edgechain/lib/supabase/security/JwtHelper.java @@ -1,45 +1,45 @@ -package com.edgechain.lib.supabase.security; - -import io.jsonwebtoken.*; -import java.security.Key; -import java.util.Objects; -import javax.crypto.spec.SecretKeySpec; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.core.env.Environment; -import org.springframework.stereotype.Component; - -@Component -public class JwtHelper { - - @Autowired private Environment env; - - public Jws parseToken(String accessToken) { - try { - final String secret = env.getProperty("jwt.secret"); - Objects.requireNonNull(secret, "JWT secret not set"); - final byte[] bytes = secret.getBytes(); - final Key hmacKey = new SecretKeySpec(bytes, SignatureAlgorithm.HS256.getJcaName()); - return Jwts.parser().setSigningKey(hmacKey).parseClaimsJws(accessToken); - - } catch (MalformedJwtException e) { - throw new JwtException("Token Malformed"); - } catch (UnsupportedJwtException e) { - throw new JwtException("Token Unsupported"); - } catch (ExpiredJwtException e) { - throw new JwtException("Token Expired"); - } catch (IllegalArgumentException e) { - throw new JwtException("Token Empty"); - } catch (SignatureException e) { - throw new JwtException("Token Signature Failed"); - } - } - - public boolean validate(String accessToken) { - try { - parseToken(accessToken); - return true; - } catch (JwtException e) { - return false; - } - } -} +package com.edgechain.lib.supabase.security; + +import io.jsonwebtoken.*; +import java.security.Key; +import java.util.Objects; +import javax.crypto.spec.SecretKeySpec; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.core.env.Environment; +import org.springframework.stereotype.Component; + +@Component +public class JwtHelper { + + @Autowired private Environment env; + + public Jws parseToken(String accessToken) { + try { + final String secret = env.getProperty("jwt.secret"); + Objects.requireNonNull(secret, "JWT secret not set"); + final byte[] bytes = secret.getBytes(); + final Key hmacKey = new SecretKeySpec(bytes, SignatureAlgorithm.HS256.getJcaName()); + return Jwts.parser().setSigningKey(hmacKey).parseClaimsJws(accessToken); + + } catch (MalformedJwtException e) { + throw new JwtException("Token Malformed"); + } catch (UnsupportedJwtException e) { + throw new JwtException("Token Unsupported"); + } catch (ExpiredJwtException e) { + throw new JwtException("Token Expired"); + } catch (IllegalArgumentException e) { + throw new JwtException("Token Empty"); + } catch (SignatureException e) { + throw new JwtException("Token Signature Failed"); + } + } + + public boolean validate(String accessToken) { + try { + parseToken(accessToken); + return true; + } catch (JwtException e) { + return false; + } + } +} diff --git a/FlySpring/edgechain-app/src/main/java/com/edgechain/lib/supabase/security/WebSecurity.java b/FlySpring/edgechain-app/src/main/java/com/edgechain/lib/supabase/security/WebSecurity.java index bc6494253..3409c05bb 100644 --- a/FlySpring/edgechain-app/src/main/java/com/edgechain/lib/supabase/security/WebSecurity.java +++ b/FlySpring/edgechain-app/src/main/java/com/edgechain/lib/supabase/security/WebSecurity.java @@ -1,175 +1,175 @@ -package com.edgechain.lib.supabase.security; - -import com.edgechain.lib.configuration.WebConfiguration; -import com.edgechain.lib.configuration.domain.AuthFilter; -import java.util.Arrays; -import java.util.Objects; -import org.slf4j.LoggerFactory; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.boot.web.servlet.FilterRegistrationBean; -import org.springframework.context.annotation.Bean; -import org.springframework.context.annotation.Configuration; -import org.springframework.core.Ordered; -import org.springframework.core.env.Environment; -import org.springframework.http.HttpMethod; -import org.springframework.security.authentication.AuthenticationManager; -import org.springframework.security.config.Customizer; -import org.springframework.security.config.annotation.authentication.configuration.AuthenticationConfiguration; -import org.springframework.security.config.annotation.method.configuration.EnableMethodSecurity; -import org.springframework.security.config.annotation.web.builders.HttpSecurity; -import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity; -import org.springframework.security.config.annotation.web.configurers.AuthorizeHttpRequestsConfigurer; -import org.springframework.security.config.http.SessionCreationPolicy; -import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder; -import org.springframework.security.crypto.password.PasswordEncoder; -import org.springframework.security.web.SecurityFilterChain; -import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter; -import org.springframework.web.cors.CorsConfiguration; -import org.springframework.web.cors.CorsConfigurationSource; -import org.springframework.web.cors.UrlBasedCorsConfigurationSource; -import org.springframework.web.filter.CorsFilter; - -@EnableWebSecurity -@EnableMethodSecurity -@Configuration -public class WebSecurity { - - @Autowired private Environment env; - @Autowired private AuthFilter authFilter; - @Autowired private JwtFilter jwtFilter; - - @Bean - AuthenticationManager authenticationManager(AuthenticationConfiguration config) throws Exception { - return config.getAuthenticationManager(); - } - - @Bean - PasswordEncoder passwordEncoder() { - return new BCryptPasswordEncoder(); - } - - @Bean - SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception { - - http.cors(cors -> cors.configurationSource(corsConfiguration())) - .csrf(csrf -> csrf.disable()) - .authorizeHttpRequests(auth -> buildAuth(auth)) - .httpBasic(Customizer.withDefaults()) - .addFilterBefore(jwtFilter, UsernamePasswordAuthenticationFilter.class) - .sessionManagement( - management -> management.sessionCreationPolicy(SessionCreationPolicy.STATELESS)) - .exceptionHandling(Customizer.withDefaults()) - .securityContext(c -> c.requireExplicitSave(false)) - .formLogin(login -> login.disable()); - return http.build(); - } - - private AuthorizeHttpRequestsConfigurer.AuthorizationManagerRequestMatcherRegistry - buildAuth( - AuthorizeHttpRequestsConfigurer.AuthorizationManagerRequestMatcherRegistry - auth) { - AuthorizeHttpRequestsConfigurer.AuthorizationManagerRequestMatcherRegistry reg = - auth.requestMatchers("" + WebConfiguration.CONTEXT_PATH + "/**").permitAll(); - - reg = - applyAuth( - reg.requestMatchers( - HttpMethod.POST, safeRequests(authFilter.getRequestPost().getRequests(), "POST")), - authFilter.getRequestPost().getAuthorities()); - reg = - applyAuth( - reg.requestMatchers( - HttpMethod.GET, safeRequests(authFilter.getRequestGet().getRequests(), "GET")), - authFilter.getRequestGet().getAuthorities()); - reg = - applyAuth( - reg.requestMatchers( - HttpMethod.DELETE, - safeRequests(authFilter.getRequestDelete().getRequests(), "DELETE")), - authFilter.getRequestDelete().getAuthorities()); - reg = - applyAuth( - reg.requestMatchers( - HttpMethod.PUT, safeRequests(authFilter.getRequestPut().getRequests(), "PUT")), - authFilter.getRequestPut().getAuthorities()); - reg = - applyAuth( - reg.requestMatchers( - HttpMethod.PATCH, - safeRequests(authFilter.getRequestPatch().getRequests(), "PATCH")), - authFilter.getRequestPatch().getAuthorities()); - - reg = reg.anyRequest().permitAll(); - return reg; - } - - private String[] safeRequests(String[] src, String method) { - if (src == null || src.length == 0 || (src.length == 1 && src[0].isEmpty())) { - LoggerFactory.getLogger(getClass()) - .warn( - "Http {} security request patterns outdated. Fixed to a list with one String \"**\" -" - + " please update your configuration", - method); - return new String[] {"**"}; - } else { - return src; - } - } - - private AuthorizeHttpRequestsConfigurer.AuthorizationManagerRequestMatcherRegistry - applyAuth(AuthorizeHttpRequestsConfigurer.AuthorizedUrl url, String[] auths) { - if (auths == null || auths.length == 0 || (auths.length == 1 && auths[0].isEmpty())) { - return url.permitAll(); - } else { - return url.hasAnyAuthority(auths); - } - } - - @Bean - CorsConfigurationSource corsConfiguration() { - - CorsConfiguration configuration = new CorsConfiguration(); - - String cors = env.getProperty("cors.origins"); - - if (Objects.nonNull(cors) && !cors.isEmpty()) { - configuration.setAllowedOrigins(Arrays.stream(cors.split(",")).toList()); - } - - configuration.setAllowCredentials(true); - configuration.setAllowedMethods( - Arrays.asList("GET", "POST", "OPTIONS", "PUT", "PATCH", "DELETE", "HEAD")); - configuration.setAllowedHeaders( - Arrays.asList( - "Origin", - "Content-Type", - "Accept", - "Access-Control-Allow-Headers", - "Access-Control-Request-Method", - "Access-Control-Request-Headers", - "X-Requested-With", - "Authorization", - "Stream")); - configuration.setMaxAge(3600L); - - UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource(); - source.registerCorsConfiguration("/**", configuration); - - return source; - } - - @Bean - FilterRegistrationBean corsFilter() { - FilterRegistrationBean bean = - new FilterRegistrationBean<>(new CorsFilter(corsConfiguration())); - bean.setOrder(Ordered.HIGHEST_PRECEDENCE); - return bean; - } - - @Bean - FilterRegistrationBean jwtFilterFilterRegistrationBean(JwtFilter jwtFilter) { - FilterRegistrationBean registrationBean = new FilterRegistrationBean<>(jwtFilter); - registrationBean.setEnabled(false); - return registrationBean; - } -} +package com.edgechain.lib.supabase.security; + +import com.edgechain.lib.configuration.WebConfiguration; +import com.edgechain.lib.configuration.domain.AuthFilter; +import java.util.Arrays; +import java.util.Objects; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.web.servlet.FilterRegistrationBean; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.core.Ordered; +import org.springframework.core.env.Environment; +import org.springframework.http.HttpMethod; +import org.springframework.security.authentication.AuthenticationManager; +import org.springframework.security.config.Customizer; +import org.springframework.security.config.annotation.authentication.configuration.AuthenticationConfiguration; +import org.springframework.security.config.annotation.method.configuration.EnableMethodSecurity; +import org.springframework.security.config.annotation.web.builders.HttpSecurity; +import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity; +import org.springframework.security.config.annotation.web.configurers.AuthorizeHttpRequestsConfigurer; +import org.springframework.security.config.http.SessionCreationPolicy; +import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder; +import org.springframework.security.crypto.password.PasswordEncoder; +import org.springframework.security.web.SecurityFilterChain; +import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter; +import org.springframework.web.cors.CorsConfiguration; +import org.springframework.web.cors.CorsConfigurationSource; +import org.springframework.web.cors.UrlBasedCorsConfigurationSource; +import org.springframework.web.filter.CorsFilter; + +@EnableWebSecurity +@EnableMethodSecurity +@Configuration +public class WebSecurity { + + @Autowired private Environment env; + @Autowired private AuthFilter authFilter; + @Autowired private JwtFilter jwtFilter; + + @Bean + AuthenticationManager authenticationManager(AuthenticationConfiguration config) throws Exception { + return config.getAuthenticationManager(); + } + + @Bean + PasswordEncoder passwordEncoder() { + return new BCryptPasswordEncoder(); + } + + @Bean + SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception { + + http.cors(cors -> cors.configurationSource(corsConfiguration())) + .csrf(csrf -> csrf.disable()) + .authorizeHttpRequests(auth -> buildAuth(auth)) + .httpBasic(Customizer.withDefaults()) + .addFilterBefore(jwtFilter, UsernamePasswordAuthenticationFilter.class) + .sessionManagement( + management -> management.sessionCreationPolicy(SessionCreationPolicy.STATELESS)) + .exceptionHandling(Customizer.withDefaults()) + .securityContext(c -> c.requireExplicitSave(false)) + .formLogin(login -> login.disable()); + return http.build(); + } + + private AuthorizeHttpRequestsConfigurer.AuthorizationManagerRequestMatcherRegistry + buildAuth( + AuthorizeHttpRequestsConfigurer.AuthorizationManagerRequestMatcherRegistry + auth) { + AuthorizeHttpRequestsConfigurer.AuthorizationManagerRequestMatcherRegistry reg = + auth.requestMatchers("" + WebConfiguration.CONTEXT_PATH + "/**").permitAll(); + + reg = + applyAuth( + reg.requestMatchers( + HttpMethod.POST, safeRequests(authFilter.getRequestPost().getRequests(), "POST")), + authFilter.getRequestPost().getAuthorities()); + reg = + applyAuth( + reg.requestMatchers( + HttpMethod.GET, safeRequests(authFilter.getRequestGet().getRequests(), "GET")), + authFilter.getRequestGet().getAuthorities()); + reg = + applyAuth( + reg.requestMatchers( + HttpMethod.DELETE, + safeRequests(authFilter.getRequestDelete().getRequests(), "DELETE")), + authFilter.getRequestDelete().getAuthorities()); + reg = + applyAuth( + reg.requestMatchers( + HttpMethod.PUT, safeRequests(authFilter.getRequestPut().getRequests(), "PUT")), + authFilter.getRequestPut().getAuthorities()); + reg = + applyAuth( + reg.requestMatchers( + HttpMethod.PATCH, + safeRequests(authFilter.getRequestPatch().getRequests(), "PATCH")), + authFilter.getRequestPatch().getAuthorities()); + + reg = reg.anyRequest().permitAll(); + return reg; + } + + private String[] safeRequests(String[] src, String method) { + if (src == null || src.length == 0 || (src.length == 1 && src[0].isEmpty())) { + LoggerFactory.getLogger(getClass()) + .warn( + "Http {} security request patterns outdated. Fixed to a list with one String \"**\" -" + + " please update your configuration", + method); + return new String[] {"**"}; + } else { + return src; + } + } + + private AuthorizeHttpRequestsConfigurer.AuthorizationManagerRequestMatcherRegistry + applyAuth(AuthorizeHttpRequestsConfigurer.AuthorizedUrl url, String[] auths) { + if (auths == null || auths.length == 0 || (auths.length == 1 && auths[0].isEmpty())) { + return url.permitAll(); + } else { + return url.hasAnyAuthority(auths); + } + } + + @Bean + CorsConfigurationSource corsConfiguration() { + + CorsConfiguration configuration = new CorsConfiguration(); + + String cors = env.getProperty("cors.origins"); + + if (Objects.nonNull(cors) && !cors.isEmpty()) { + configuration.setAllowedOrigins(Arrays.stream(cors.split(",")).toList()); + } + + configuration.setAllowCredentials(true); + configuration.setAllowedMethods( + Arrays.asList("GET", "POST", "OPTIONS", "PUT", "PATCH", "DELETE", "HEAD")); + configuration.setAllowedHeaders( + Arrays.asList( + "Origin", + "Content-Type", + "Accept", + "Access-Control-Allow-Headers", + "Access-Control-Request-Method", + "Access-Control-Request-Headers", + "X-Requested-With", + "Authorization", + "Stream")); + configuration.setMaxAge(3600L); + + UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource(); + source.registerCorsConfiguration("/**", configuration); + + return source; + } + + @Bean + FilterRegistrationBean corsFilter() { + FilterRegistrationBean bean = + new FilterRegistrationBean<>(new CorsFilter(corsConfiguration())); + bean.setOrder(Ordered.HIGHEST_PRECEDENCE); + return bean; + } + + @Bean + FilterRegistrationBean jwtFilterFilterRegistrationBean(JwtFilter jwtFilter) { + FilterRegistrationBean registrationBean = new FilterRegistrationBean<>(jwtFilter); + registrationBean.setEnabled(false); + return registrationBean; + } +} diff --git a/FlySpring/edgechain-app/src/main/resources/application-test.properties b/FlySpring/edgechain-app/src/main/resources/application-test.properties index 311c86783..81046a047 100644 --- a/FlySpring/edgechain-app/src/main/resources/application-test.properties +++ b/FlySpring/edgechain-app/src/main/resources/application-test.properties @@ -1,5 +1,5 @@ -# this profile will be used only when @ActiveProfiles("test") - -# uncomment these log level lines to follow what Spring is doing for ROLES -# logging.level.org.springframework.security=TRACE -# logging.level.org.springframework.security.web.FilterChainProxy=INFO +# this profile will be used only when @ActiveProfiles("test") + +# uncomment these log level lines to follow what Spring is doing for ROLES +# logging.level.org.springframework.security=TRACE +# logging.level.org.springframework.security.web.FilterChainProxy=INFO diff --git a/FlySpring/edgechain-app/src/main/resources/schema.sql b/FlySpring/edgechain-app/src/main/resources/schema.sql new file mode 100644 index 000000000..199c44239 --- /dev/null +++ b/FlySpring/edgechain-app/src/main/resources/schema.sql @@ -0,0 +1,6 @@ +CREATE TABLE history_context +( + id VARCHAR(255) NOT NULL PRIMARY KEY, + response VARCHAR(1024), + created_at TIMESTAMP +); \ No newline at end of file diff --git a/FlySpring/edgechain-app/src/test/java/com/edgechain/chunker/ChunkerTest.java b/FlySpring/edgechain-app/src/test/java/com/edgechain/chunker/ChunkerTest.java index e6edef4d7..d48969f06 100644 --- a/FlySpring/edgechain-app/src/test/java/com/edgechain/chunker/ChunkerTest.java +++ b/FlySpring/edgechain-app/src/test/java/com/edgechain/chunker/ChunkerTest.java @@ -1,85 +1,146 @@ package com.edgechain.chunker; -import static org.junit.jupiter.api.Assertions.assertArrayEquals; - +import com.edgechain.lib.chunk.Chunker; +import com.edgechain.lib.chunk.enums.LangType; import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.TestInfo; +import org.junit.jupiter.api.Timeout; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.boot.test.context.SpringBootTest; -import com.edgechain.lib.chunk.Chunker; -import com.edgechain.lib.chunk.enums.LangType; +import java.util.Arrays; +import java.util.concurrent.TimeUnit; + +import static org.junit.jupiter.api.Assertions.*; @SpringBootTest public class ChunkerTest { - private final Logger logger = LoggerFactory.getLogger(this.getClass()); + private final Logger logger = LoggerFactory.getLogger(this.getClass()); + + @Test + @DisplayName("Test By Chunk Size") + public void chunker_ByChunkSize_ReturnExpectedValue(TestInfo testInfo) throws Exception { + + logger.info("======== " + testInfo.getDisplayName() + " ========"); + + String input = "This is a test string for chunking."; + logger.info(String.valueOf(input.length())); + Chunker chunker = new Chunker(input); + int chunkSize = 5; + + String[] result = chunker.byChunkSize(chunkSize); + + String[] expected = {"This", "is a", "test", "strin", "g for", "chun", "king."}; + assertArrayEquals(expected, result); + } + + @Test + @DisplayName("Test By Sentence") + public void chunker_BySentence_ReturnExpectedValue(TestInfo testInfo) throws Exception { + + logger.info("======== " + testInfo.getDisplayName() + " ========"); + + String input = "This is sentence one. Sentence two. Sentence three."; + Chunker chunker = new Chunker(input); + LangType langType = LangType.EN; + + String[] result = chunker.bySentence(langType); + + String[] expected = {"This is sentence one.", "Sentence two.", "Sentence three."}; + assertArrayEquals(expected, result); + } - @Test - @DisplayName("Test By Chunk Size") - public void chunker_ByChunkSize_ReturnExpectedValue(TestInfo testInfo) throws Exception { + @Test + @DisplayName("Test By Chunk Size With Empty Input ") + public void chunker_ByChunkSizeWithEmptyInput_ReturnEmptyArray(TestInfo testInfo) + throws Exception { - logger.info("======== " + testInfo.getDisplayName() + " ========"); + logger.info("======== " + testInfo.getDisplayName() + " ========"); - String input = "This is a test string for chunking."; - Chunker chunker = new Chunker(input); - int chunkSize = 5; + String input = ""; + Chunker chunker = new Chunker(input); + int chunkSize = 5; - String[] result = chunker.byChunkSize(chunkSize); + String[] result = chunker.byChunkSize(chunkSize); - String[] expected = {"This", "is a", "test", "strin", "g for", "chun", "king."}; - assertArrayEquals(expected, result); - } + String[] expected = {}; + assertArrayEquals(expected, result); + } - @Test - @DisplayName("Test By Sentence") - public void chunker_BySentence_ReturnExpectedValue(TestInfo testInfo) throws Exception { + @Test + @DisplayName("Test By Sentence With Empty Input ") + public void chunker_BySentenceWithEmptyInput_ReturnEmptyArray(TestInfo testInfo) + throws Exception { - logger.info("======== " + testInfo.getDisplayName() + " ========"); + logger.info("======== " + testInfo.getDisplayName() + " ========"); - String input = "This is sentence one. Sentence two. Sentence three."; - Chunker chunker = new Chunker(input); - LangType langType = LangType.EN; + String input = "This is a test."; + Chunker chunker = new Chunker(input); + int chunkSize = 100; - String[] result = chunker.bySentence(langType); + String[] result = chunker.byChunkSize(chunkSize); - String[] expected = {"This is sentence one.", "Sentence two.", "Sentence three."}; - assertArrayEquals(expected, result); - } + String[] expected = {"This is a test."}; + assertArrayEquals(expected, result); + } - @Test - @DisplayName("Test By Chunk Size With Empty Input ") - public void chunker_ByChunkSizeWithEmptyInput_ReturnEmptyArray(TestInfo testInfo) - throws Exception { + @Test + @DisplayName("Test By Very Small ChunkSize ") + void chunker_ByVerySmallChunkSize_ReturnedExpectedValue() { + String input = "This is Testing"; + Chunker chunker = new Chunker(input); - logger.info("======== " + testInfo.getDisplayName() + " ========"); + String[] result = chunker.byChunkSize(1); - String input = ""; - Chunker chunker = new Chunker(input); - int chunkSize = 5; + String[] expected = {"T", "h", "i", "s", "i", "s", "T", "e", "s", "t", "i", "n", "g"}; + assertNotEquals(expected, result); + } - String[] result = chunker.byChunkSize(chunkSize); + @Test + @DisplayName("Test By ChunkSize - Input Contains Whitespace") + void chunker_ByChunkSize_InputWhiteSpaceCharacter_ReturnedExpectedValue() { + String input = "\n\t\t"; + Chunker chunker = new Chunker(input); + int chunkSize = 5; - String[] expected = {}; - assertArrayEquals(expected, result); - } + String[] result = chunker.byChunkSize(chunkSize); - @Test - @DisplayName("Test By Sentence With Empty Input ") - public void chunker_BySentenceWithEmptyInput_ReturnEmptyArray(TestInfo testInfo) - throws Exception { + String[] expected = {""}; + assertArrayEquals(expected, result); + } - logger.info("======== " + testInfo.getDisplayName() + " ========"); + @Test + @DisplayName("Test By Sentence - Contains Only Spaces") + void chunker_BySentence_InputContainsOnlySpaces_ReturnedExpectedValue() { + String input = " "; + Chunker chunker = new Chunker(input); - String input = "This is a test."; - Chunker chunker = new Chunker(input); - int chunkSize = 100; + String[] result = chunker.bySentence(LangType.EN); + logger.info(Arrays.toString(result)); + String[] expected = {}; + assertArrayEquals(expected, result); + assertEquals(expected.length, result.length); + } - String[] result = chunker.byChunkSize(chunkSize); + @Test + @DisplayName("Performance Test With Large String") + @Timeout(value = 5, unit = TimeUnit.SECONDS) + void chunker_Performance_LargeInputString_ReturnedExpectedValue() { + String input = "E".repeat(10000); + Chunker chunker = new Chunker(input); + int chunkSize = 5; - String[] expected = {"This is a test."}; - assertArrayEquals(expected, result); - } + long startTime = System.currentTimeMillis(); + String[] result = chunker.byChunkSize(chunkSize); + long endTime = System.currentTimeMillis(); + long totalExecutionTime = endTime - startTime; + logger.info(String.valueOf(totalExecutionTime)); + + long maxExecutionTime = 5000; // Execution time in mills + assertEquals(2000, result.length); + assertTrue(totalExecutionTime <= maxExecutionTime); + } } diff --git a/FlySpring/edgechain-app/src/test/java/com/edgechain/codeInterpreter/CodeInterpreterTest.java b/FlySpring/edgechain-app/src/test/java/com/edgechain/codeInterpreter/CodeInterpreterTest.java index e4d0527aa..b4af934b4 100644 --- a/FlySpring/edgechain-app/src/test/java/com/edgechain/codeInterpreter/CodeInterpreterTest.java +++ b/FlySpring/edgechain-app/src/test/java/com/edgechain/codeInterpreter/CodeInterpreterTest.java @@ -1,13 +1,13 @@ package com.edgechain.codeInterpreter; import static org.junit.Assert.assertFalse; -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertNotNull; -import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.junit.Assert.assertNull; +import static org.junit.jupiter.api.Assertions.*; import java.io.ByteArrayInputStream; import java.io.InputStream; +import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; @@ -65,4 +65,14 @@ public void test_empty_example_extraction() throws Exception { assertNotNull(extractedValue); assertFalse(extractedValue.contains(prompt)); } + + @Test + @DisplayName("Test for empty input") + void test_emptyInput_ReturnedExpectedValue(){ + String inputJsonnet = ""; + InputStream inputStream = new ByteArrayInputStream(inputJsonnet.getBytes()); + JsonnetLoader jsonnetLoader = new FileJsonnetLoader(); + + assertThrows(Exception.class, () -> jsonnetLoader.load(inputStream)); + } } diff --git a/FlySpring/edgechain-app/src/test/java/com/edgechain/jsonnet/JsonnetLoaderTest.java b/FlySpring/edgechain-app/src/test/java/com/edgechain/jsonnet/JsonnetLoaderTest.java index 5dcdcbfc8..d303b44c0 100644 --- a/FlySpring/edgechain-app/src/test/java/com/edgechain/jsonnet/JsonnetLoaderTest.java +++ b/FlySpring/edgechain-app/src/test/java/com/edgechain/jsonnet/JsonnetLoaderTest.java @@ -1,15 +1,12 @@ package com.edgechain.jsonnet; -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertNotNull; -import static org.junit.jupiter.api.Assertions.assertTrue; - import java.io.ByteArrayInputStream; import java.io.InputStream; import com.edgechain.lib.jsonnet.JsonnetArgs; import com.edgechain.lib.jsonnet.enums.DataType; import org.json.JSONArray; +import org.json.JSONObject; import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.TestInfo; @@ -20,6 +17,8 @@ import com.edgechain.lib.jsonnet.JsonnetLoader; import com.edgechain.lib.jsonnet.impl.FileJsonnetLoader; +import static org.junit.jupiter.api.Assertions.*; + @SpringBootTest public class JsonnetLoaderTest { @@ -106,4 +105,37 @@ public void test_external_variable_xtrasonnet() throws Exception { assertNotNull(externalVar); assertEquals(externalVar, "5"); } + + @Test + void jsonLoader_LoadJsonnet_WithInvalidJsonnet_ThrowsException(){ + String inputJsonnet = "This is a test sentence."; + InputStream inputStream = new ByteArrayInputStream(inputJsonnet.getBytes()); + JsonnetLoader jsonnetLoader = new FileJsonnetLoader(); + assertThrows(Exception.class, () -> jsonnetLoader.load(inputStream)); + } + + @Test + void jsonLoader_LoadJsonnet_WithEmptyJsonnet_ThrowsExpcetion(){ + String inputJsonnet = "{}"; + InputStream inputStream = new ByteArrayInputStream(inputJsonnet.getBytes()); + JsonnetLoader jsonnetLoader = new FileJsonnetLoader(); + jsonnetLoader.load(inputStream); + assertThrows(Exception.class, () -> jsonnetLoader.get("jsonnet")); + } + + @Test + void jsonLoader_LoadJsonnetWithArrayOfObjects_ReturnExpectedValue(TestInfo testInfo) { + String inputJsonnet = "{ \"objects\": [{ \"key\": \"value1\" }, { \"key\": \"value2\" }] }"; + InputStream inputStream = new ByteArrayInputStream(inputJsonnet.getBytes()); + JsonnetLoader jsonnetLoader = new FileJsonnetLoader(); + + jsonnetLoader.load(inputStream); + JSONArray objects = jsonnetLoader.getArray("objects"); + + assertNotNull(objects); + assertEquals(2, objects.length()); + assertEquals("value1", objects.getJSONObject(0).getString("key")); + assertEquals("value2", objects.getJSONObject(1).getString("key")); + } + } diff --git a/FlySpring/edgechain-app/src/test/java/com/edgechain/lib/context/client/impl/PostgreSQLHistoryContextClientTest.java b/FlySpring/edgechain-app/src/test/java/com/edgechain/lib/context/client/impl/PostgreSQLHistoryContextClientTest.java index 5e4228e33..cf8b7a56b 100644 --- a/FlySpring/edgechain-app/src/test/java/com/edgechain/lib/context/client/impl/PostgreSQLHistoryContextClientTest.java +++ b/FlySpring/edgechain-app/src/test/java/com/edgechain/lib/context/client/impl/PostgreSQLHistoryContextClientTest.java @@ -1,91 +1,91 @@ -package com.edgechain.lib.context.client.impl; - -import com.edgechain.lib.context.domain.HistoryContext; -import com.edgechain.lib.rxjava.transformer.observable.EdgeChain; -import com.edgechain.testutil.PostgresTestContainer; -import com.edgechain.testutil.PostgresTestContainer.PostgresImage; -import com.zaxxer.hikari.HikariConfig; -import org.junit.jupiter.api.AfterAll; -import org.junit.jupiter.api.BeforeAll; -import org.junit.jupiter.api.Test; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.boot.test.context.SpringBootTest; -import org.springframework.boot.test.context.SpringBootTest.WebEnvironment; -import org.springframework.test.annotation.DirtiesContext; -import org.testcontainers.junit.jupiter.Testcontainers; -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertFalse; -import static org.junit.jupiter.api.Assertions.assertNotNull; - -@Testcontainers(disabledWithoutDocker = true) -@SpringBootTest(webEnvironment = WebEnvironment.NONE) -@DirtiesContext -class PostgreSQLHistoryContextClientTest { - - private static final Logger LOGGER = - LoggerFactory.getLogger(PostgreSQLHistoryContextClientTest.class); - - private static PostgresTestContainer instance = new PostgresTestContainer(PostgresImage.PLAIN); - - @BeforeAll - static void baseSetupAll() { - instance.start(); - } - - @AfterAll - static void baseTeardownAll() { - instance.stop(); - } - - @Autowired private HikariConfig hikariConfig; - @Autowired private PostgreSQLHistoryContextClient service; - - @Test - void allMethods() { - // hikari has own copy of properties so set these here - hikariConfig.setJdbcUrl(instance.getJdbcUrl()); - hikariConfig.setUsername(instance.getUsername()); - hikariConfig.setPassword(instance.getPassword()); - - final Data data = new Data(); - - final EdgeChain create = service.create("DAVE", null); - create.toSingle().blockingSubscribe(s -> data.id = s.getId(), e -> data.failed = true); - assertFalse(data.failed); - assertNotNull(data.id); - LOGGER.info("create OK id={}", data.id); - - final EdgeChain put = service.put(data.id, "COW", null); - put.toSingle().blockingSubscribe(s -> {}, e -> data.failed = true); - assertFalse(data.failed); - LOGGER.info("put OK"); - - final EdgeChain get = service.get(data.id, null); - get.toSingle().blockingSubscribe(s -> data.val = s.getResponse(), e -> data.failed = true); - assertFalse(data.failed); - assertEquals("COW", data.val); - LOGGER.info("get OK val={}", data.val); - - EdgeChain delete = service.delete(data.id, null); - delete.toSingle().blockingSubscribe(s -> data.val = s, e -> data.failed = true); - assertFalse(data.failed); - assertEquals("", data.val); - LOGGER.info("delete OK val={}", data.val); - - final EdgeChain getMissing = service.get("not_there", null); - getMissing - .toSingle() - .blockingSubscribe(s -> data.failed = true, e -> data.val = e.getMessage()); - assertFalse(data.failed); - assertEquals("PostgreSQL history_context id isn't found.", data.val); - LOGGER.info("get-NotFound OK val={}", data.val); - } - - private static class Data { - public boolean failed; - public String id; - public String val; - } -} +package com.edgechain.lib.context.client.impl; + +import com.edgechain.lib.context.domain.HistoryContext; +import com.edgechain.lib.rxjava.transformer.observable.EdgeChain; +import com.edgechain.testutil.PostgresTestContainer; +import com.edgechain.testutil.PostgresTestContainer.PostgresImage; +import com.zaxxer.hikari.HikariConfig; +import org.junit.jupiter.api.AfterAll; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Test; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.boot.test.context.SpringBootTest.WebEnvironment; +import org.springframework.test.annotation.DirtiesContext; +import org.testcontainers.junit.jupiter.Testcontainers; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertNotNull; + +@Testcontainers(disabledWithoutDocker = true) +@SpringBootTest(webEnvironment = WebEnvironment.NONE) +@DirtiesContext +class PostgreSQLHistoryContextClientTest { + + private static final Logger LOGGER = + LoggerFactory.getLogger(PostgreSQLHistoryContextClientTest.class); + + private static PostgresTestContainer instance = new PostgresTestContainer(PostgresImage.PLAIN); + + @BeforeAll + static void baseSetupAll() { + instance.start(); + } + + @AfterAll + static void baseTeardownAll() { + instance.stop(); + } + + @Autowired private HikariConfig hikariConfig; + @Autowired private PostgreSQLHistoryContextClient service; + + @Test + void allMethods() { + // hikari has own copy of properties so set these here + hikariConfig.setJdbcUrl(instance.getJdbcUrl()); + hikariConfig.setUsername(instance.getUsername()); + hikariConfig.setPassword(instance.getPassword()); + + final Data data = new Data(); + + final EdgeChain create = service.create("DAVE", null); + create.toSingle().blockingSubscribe(s -> data.id = s.getId(), e -> data.failed = true); + assertFalse(data.failed); + assertNotNull(data.id); + LOGGER.info("create OK id={}", data.id); + + final EdgeChain put = service.put(data.id, "COW", null); + put.toSingle().blockingSubscribe(s -> {}, e -> data.failed = true); + assertFalse(data.failed); + LOGGER.info("put OK"); + + final EdgeChain get = service.get(data.id, null); + get.toSingle().blockingSubscribe(s -> data.val = s.getResponse(), e -> data.failed = true); + assertFalse(data.failed); + assertEquals("COW", data.val); + LOGGER.info("get OK val={}", data.val); + + EdgeChain delete = service.delete(data.id, null); + delete.toSingle().blockingSubscribe(s -> data.val = s, e -> data.failed = true); + assertFalse(data.failed); + assertEquals("", data.val); + LOGGER.info("delete OK val={}", data.val); + + final EdgeChain getMissing = service.get("not_there", null); + getMissing + .toSingle() + .blockingSubscribe(s -> data.failed = true, e -> data.val = e.getMessage()); + assertFalse(data.failed); + assertEquals("PostgreSQL history_context id isn't found.", data.val); + LOGGER.info("get-NotFound OK val={}", data.val); + } + + private static class Data { + public boolean failed; + public String id; + public String val; + } +} diff --git a/FlySpring/edgechain-app/src/test/java/com/edgechain/lib/context/client/repositories/PostgreSQLHistoryContextRepositoryTest.java b/FlySpring/edgechain-app/src/test/java/com/edgechain/lib/context/client/repositories/PostgreSQLHistoryContextRepositoryTest.java new file mode 100644 index 000000000..48d36a4bd --- /dev/null +++ b/FlySpring/edgechain-app/src/test/java/com/edgechain/lib/context/client/repositories/PostgreSQLHistoryContextRepositoryTest.java @@ -0,0 +1,90 @@ +package com.edgechain.lib.context.client.repositories; + +import com.edgechain.lib.context.domain.HistoryContext; +import com.edgechain.testutil.PostgresTestContainer; +import org.junit.jupiter.api.*; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.autoconfigure.jdbc.AutoConfigureTestDatabase; +import org.springframework.boot.test.autoconfigure.orm.jpa.DataJpaTest; +import org.springframework.test.context.DynamicPropertyRegistry; +import org.springframework.test.context.DynamicPropertySource; +import org.springframework.test.context.jdbc.Sql; + +import java.time.LocalDateTime; +import java.util.Optional; + +import static org.junit.jupiter.api.Assertions.*; + + +@DataJpaTest +@AutoConfigureTestDatabase(replace = AutoConfigureTestDatabase.Replace.NONE) +@Sql(scripts = {"classpath:schema.sql"}) +class PostgreSQLHistoryContextRepositoryTest { + + Logger logger = LoggerFactory.getLogger(getClass()); + + @Autowired + private PostgreSQLHistoryContextRepository repository; + + private static final PostgresTestContainer instance = new PostgresTestContainer(PostgresTestContainer.PostgresImage.VECTOR); + + @BeforeAll + static void setupAll(){ + instance.start(); + } + + @AfterAll + static void tearAll(){ + instance.stop(); + } + + @BeforeEach + void setUp() { + repository.deleteAll(); + } + + @DynamicPropertySource + static void setProperties(DynamicPropertyRegistry registry){ + registry.add("spring.datasource.url", instance::getJdbcUrl); + registry.add("spring.datasource.username", instance::getUsername); + registry.add("spring.datasource.password", instance::getPassword); + } + + @Test + void test_Save_And_Retrieve_History_Context(){ + HistoryContext historyContext = getHistoryContext(); + repository.save(historyContext); + + Optional result = repository.findById("1"); + logger.info("history context {}", result); + + assertTrue(result.isPresent()); + } + + @Test + void test_Delete_History_Context(){ + HistoryContext historyContext = getHistoryContext(); + repository.save(historyContext); + + repository.deleteById("1"); + Optional result = repository.findById("1"); + + assertTrue(result.isEmpty()); + } + + @Test + void test_Find_By_Non_Exist_Context(){ + Optional result = repository.findById("10"); + assertTrue(result.isEmpty()); + } + + private HistoryContext getHistoryContext() { + return new HistoryContext( + "1", + "testing history context", + LocalDateTime.now() + ); + } +} \ No newline at end of file diff --git a/FlySpring/edgechain-app/src/test/java/com/edgechain/lib/index/client/impl/PostgresClientTest.java b/FlySpring/edgechain-app/src/test/java/com/edgechain/lib/index/client/impl/PostgresClientTest.java index abf8375a4..cea6b333c 100644 --- a/FlySpring/edgechain-app/src/test/java/com/edgechain/lib/index/client/impl/PostgresClientTest.java +++ b/FlySpring/edgechain-app/src/test/java/com/edgechain/lib/index/client/impl/PostgresClientTest.java @@ -1,374 +1,374 @@ -package com.edgechain.lib.index.client.impl; - -import com.edgechain.lib.embeddings.WordEmbeddings; -import com.edgechain.lib.endpoint.impl.PostgresEndpoint; -import com.edgechain.lib.index.domain.PostgresWordEmbeddings; -import com.edgechain.lib.index.enums.PostgresDistanceMetric; -import com.edgechain.lib.response.StringResponse; -import com.edgechain.lib.rxjava.transformer.observable.EdgeChain; -import com.edgechain.testutil.PostgresTestContainer; -import com.edgechain.testutil.PostgresTestContainer.PostgresImage; -import com.zaxxer.hikari.HikariConfig; -import java.util.List; -import java.util.stream.Collectors; -import org.junit.jupiter.api.AfterAll; -import org.junit.jupiter.api.BeforeAll; -import org.junit.jupiter.api.Test; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.boot.test.context.SpringBootTest; -import org.springframework.boot.test.context.SpringBootTest.WebEnvironment; -import org.springframework.test.annotation.DirtiesContext; -import org.testcontainers.junit.jupiter.Testcontainers; -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertTrue; -import static org.junit.jupiter.api.Assertions.fail; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.when; - -@Testcontainers(disabledWithoutDocker = true) -@SpringBootTest(webEnvironment = WebEnvironment.NONE) -@DirtiesContext -class PostgresClientTest { - - private static final Logger LOGGER = LoggerFactory.getLogger(PostgresClientTest.class); - - private static final float FLOAT_ERROR_MARGIN = 0.0001f; - - private static PostgresTestContainer instance = new PostgresTestContainer(PostgresImage.VECTOR); - - @BeforeAll - static void setupAll() { - instance.start(); - } - - @AfterAll - static void teardownAll() { - instance.stop(); - } - - @Autowired private HikariConfig hikariConfig; - - @Autowired private PostgresClient service; - - @Test - void allMethods() { - // hikari has own copy of properties so set these here - hikariConfig.setJdbcUrl(instance.getJdbcUrl()); - hikariConfig.setUsername(instance.getUsername()); - hikariConfig.setPassword(instance.getPassword()); - - createTable(); - createMetadataTable(); - deleteAll(); // check delete before we get foreign keys - - String uuid1 = upsert(); - batchUpsert(); - query_noMeta(); - - String uuid2 = insertMetadata(); - batchInsertMetadata(); - insertIntoJoinTable(uuid1, uuid2); - query_meta(); - - getChunks(); - getSimilarChunks(); - } - - private void createTable() { - createTable_metric(PostgresDistanceMetric.L2, "testtableL2"); - createTable_metric(PostgresDistanceMetric.COSINE, "testtableCOS"); - createTable_metric(null, "testtable"); - - // create table again - createTable_metric(null, "testtable"); - } - - private void createTable_metric(PostgresDistanceMetric metric, String tableName) { - PostgresEndpoint mockPe = mock(PostgresEndpoint.class); - when(mockPe.getTableName()).thenReturn(tableName); - when(mockPe.getLists()).thenReturn(1); - when(mockPe.getDimensions()).thenReturn(2); - when(mockPe.getMetric()).thenReturn(metric); - - final Data data = new Data(); - EdgeChain result = service.createTable(mockPe); - result.toSingle().blockingSubscribe(s -> data.val = s.getResponse(), e -> data.error = e); - if (data.error != null) { - fail("createTable failed", data.error); - } - LOGGER.info("createTable (metric={}) response: '{}'", metric, data.val); - } - - private void createMetadataTable() { - PostgresEndpoint mockPe = mock(PostgresEndpoint.class); - when(mockPe.getTableName()).thenReturn("testtable"); - when(mockPe.getMetadataTableNames()).thenReturn(List.of("dogmeta", "catmeta")); - - final Data data = new Data(); - EdgeChain result = service.createMetadataTable(mockPe); - result.toSingle().blockingSubscribe(s -> data.val = s.getResponse(), e -> data.error = e); - if (data.error != null) { - fail("createMetadataTable failed", data.error); - } - LOGGER.info("createMetadataTable response: '{}'", data.val); - } - - private String upsert() { - WordEmbeddings we = new WordEmbeddings(); - we.setId("WE1"); - we.setScore("101"); - we.setValues(List.of(0.25f, 0.5f)); - - PostgresEndpoint mockPe = mock(PostgresEndpoint.class); - when(mockPe.getTableName()).thenReturn("testtable"); - when(mockPe.getWordEmbedding()).thenReturn(we); - when(mockPe.getFilename()).thenReturn("readme.pdf"); - when(mockPe.getNamespace()).thenReturn("testns"); - - final Data data = new Data(); - EdgeChain result = service.upsert(mockPe); - result.toSingle().blockingSubscribe(s -> data.val = s.getResponse(), e -> data.error = e); - if (data.error != null) { - fail("upsert failed", data.error); - } - LOGGER.info("upsert response: '{}'", data.val); - return data.val; - } - - private String insertMetadata() { - PostgresEndpoint mockPe = mock(PostgresEndpoint.class); - when(mockPe.getMetadataTableNames()).thenReturn(List.of("dogmeta", "catmeta")); - when(mockPe.getMetadata()).thenReturn("''duck''"); - when(mockPe.getDocumentDate()).thenReturn("November 11, 2015"); - - final Data data = new Data(); - EdgeChain result = service.insertMetadata(mockPe); - result.toSingle().blockingSubscribe(s -> data.val = s.getResponse(), e -> data.error = e); - if (data.error != null) { - fail("insertMetadata failed", data.error); - } - LOGGER.info("insertMetadata response: '{}'", data.val); - return data.val; - } - - private void batchUpsert() { - WordEmbeddings we1 = new WordEmbeddings(); - we1.setId("WE1"); - we1.setScore("101"); - we1.setValues(List.of(0.25f, 0.5f)); - - WordEmbeddings we2 = new WordEmbeddings(); - we2.setId("WE2"); - we2.setScore("202"); - we2.setValues(List.of(0.75f, 0.9f)); - - PostgresEndpoint mockPe = mock(PostgresEndpoint.class); - when(mockPe.getTableName()).thenReturn("testtable"); - when(mockPe.getWordEmbeddingsList()).thenReturn(List.of(we1, we2)); - when(mockPe.getFilename()).thenReturn("readme.pdf"); - when(mockPe.getNamespace()).thenReturn("testns"); - - final Data data = new Data(); - EdgeChain> result = service.batchUpsert(mockPe); - result - .toSingle() - .blockingSubscribe( - s -> data.val = s.stream().map(r -> r.getResponse()).collect(Collectors.joining(",")), - e -> data.error = e); - if (data.error != null) { - fail("batchUpsert failed", data.error); - } - LOGGER.info("batchUpsert response: '{}'", data.val); - } - - private void batchInsertMetadata() { - PostgresEndpoint mockPe = mock(PostgresEndpoint.class); - when(mockPe.getMetadataTableNames()).thenReturn(List.of("dogmeta", "catmeta")); - when(mockPe.getMetadataList()).thenReturn(List.of("cow", "horse")); - - final Data data = new Data(); - EdgeChain> result = service.batchInsertMetadata(mockPe); - result - .toSingle() - .blockingSubscribe( - s -> data.val = s.stream().map(r -> r.getResponse()).collect(Collectors.joining(",")), - e -> data.error = e); - if (data.error != null) { - fail("batchInsertMetadata failed", data.error); - } - LOGGER.info("batchInsertMetadata response: '{}'", data.val); - } - - private void insertIntoJoinTable(String uuid1, String uuid2) { - PostgresEndpoint mockPe = mock(PostgresEndpoint.class); - when(mockPe.getTableName()).thenReturn("testtable"); - when(mockPe.getMetadataTableNames()).thenReturn(List.of("dogmeta", "catmeta")); - when(mockPe.getId()).thenReturn(uuid1); - when(mockPe.getMetadataId()).thenReturn(uuid2); - - final Data data = new Data(); - - EdgeChain result = service.insertIntoJoinTable(mockPe); - result.toSingle().blockingSubscribe(s -> data.val = s.getResponse(), e -> data.error = e); - if (data.error != null) { - fail("insertIntoJoinTable failed", data.error); - } - LOGGER.info("insertIntoJoinTable response: '{}'", data.val); - } - - private void deleteAll() { - deleteAll_namespace(null, "knowledge"); - deleteAll_namespace("", "knowledge"); - deleteAll_namespace("testns", "testns"); - } - - private void deleteAll_namespace(String namespace, String expected) { - PostgresEndpoint mockPe = mock(PostgresEndpoint.class); - when(mockPe.getTableName()).thenReturn("testtable"); - when(mockPe.getNamespace()).thenReturn(namespace); - - final Data data = new Data(); - EdgeChain result = service.deleteAll(mockPe); - result.toSingle().blockingSubscribe(s -> data.val = s.getResponse(), e -> data.error = e); - if (data.error != null) { - fail("deleteAll failed", data.error); - } - LOGGER.info("deleteAll (namespace={}) response: '{}'", namespace, data.val); - assertTrue(data.val.endsWith(expected)); - } - - private void query_noMeta() { - query_noMeta_metric(PostgresDistanceMetric.COSINE); - query_noMeta_metric(PostgresDistanceMetric.IP); - query_noMeta_metric(PostgresDistanceMetric.L2); - } - - private void query_noMeta_metric(PostgresDistanceMetric metric) { - WordEmbeddings we1 = new WordEmbeddings(); - we1.setId("WEQUERY"); - we1.setScore("104"); - we1.setValues(List.of(0.25f, 0.5f)); - - PostgresEndpoint mockPe = mock(PostgresEndpoint.class); - when(mockPe.getTableName()).thenReturn("testtable"); - when(mockPe.getNamespace()).thenReturn("testns"); - when(mockPe.getProbes()).thenReturn(5); - when(mockPe.getMetric()).thenReturn(metric); - when(mockPe.getWordEmbedding()).thenReturn(we1); - when(mockPe.getTopK()).thenReturn(1000); - when(mockPe.getMetadataTableNames()).thenReturn(null); - - final Data data = new Data(); - EdgeChain> result = service.query(mockPe); - result - .toSingle() - .blockingSubscribe( - s -> data.val = s.stream().map(r -> r.getRawText()).collect(Collectors.joining(",")), - e -> data.error = e); - if (data.error != null) { - fail("query (no meta) failed", data.error); - } - LOGGER.info("query no meta (metric={}) response: '{}'", metric, data.val); - - // WE1 from single upsert, and WE2 from batch upsert - assertTrue(data.val.contains("WE1") && data.val.contains("WE2")); - } - - private void query_meta() { - query_meta_metric(PostgresDistanceMetric.COSINE); - query_meta_metric(PostgresDistanceMetric.IP); - query_meta_metric(PostgresDistanceMetric.L2); - } - - private void query_meta_metric(PostgresDistanceMetric metric) { - WordEmbeddings we1 = new WordEmbeddings(); - we1.setId("WEQUERY"); - we1.setScore("104"); - we1.setValues(List.of(0.25f, 0.5f)); - - PostgresEndpoint mockPe = mock(PostgresEndpoint.class); - when(mockPe.getTableName()).thenReturn("testtable"); - when(mockPe.getNamespace()).thenReturn("testns"); - when(mockPe.getProbes()).thenReturn(5); - when(mockPe.getMetric()).thenReturn(metric); - when(mockPe.getWordEmbedding()).thenReturn(we1); - when(mockPe.getTopK()).thenReturn(1000); - when(mockPe.getMetadataTableNames()).thenReturn(List.of("dogmeta", "catmeta")); - - final Data data = new Data(); - EdgeChain> result = service.queryWithMetadata(mockPe); - result - .toSingle() - .blockingSubscribe( - s -> data.val = s.stream().map(r -> r.getRawText()).collect(Collectors.joining(",")), - e -> data.error = e); - if (data.error != null) { - fail("query (meta) failed", data.error); - } - LOGGER.info("query with meta (metric={}) response: '{}'", metric, data.val); - - // WE1 from single joined upsert - assertTrue(data.val.contains("WE1")); - } - - private void getChunks() { - PostgresEndpoint mockPe = mock(PostgresEndpoint.class); - when(mockPe.getTableName()).thenReturn("testtable"); - when(mockPe.getFilename()).thenReturn("readme.pdf"); - - final Data data = new Data(); - EdgeChain> result = service.getAllChunks(mockPe); - result - .toSingle() - .blockingSubscribe( - s -> { - data.list = s; - data.val = s.stream().map(r -> r.getRawText()).collect(Collectors.joining(",")); - }, - e -> data.error = e); - if (data.error != null) { - fail("getChunks failed", data.error); - } - LOGGER.info("getChunks response: '{}'", data.val); - - // WE1 from single upsert, and WE2 from batch upsert - assertTrue(data.val.contains("WE1") && data.val.contains("WE2")); - - PostgresWordEmbeddings first = data.list.get(0); - assertEquals(0.25f, first.getValues().get(0), FLOAT_ERROR_MARGIN); - assertEquals(0.5f, first.getValues().get(1), FLOAT_ERROR_MARGIN); - - PostgresWordEmbeddings second = data.list.get(1); - assertEquals(0.75f, second.getValues().get(0), FLOAT_ERROR_MARGIN); - assertEquals(0.9f, second.getValues().get(1), FLOAT_ERROR_MARGIN); - } - - private void getSimilarChunks() { - PostgresEndpoint mockPe = mock(PostgresEndpoint.class); - when(mockPe.getMetadataTableNames()).thenReturn(List.of("dogmeta", "catmeta")); - when(mockPe.getEmbeddingChunk()).thenReturn("how to test this"); - - final Data data = new Data(); - EdgeChain> result = service.getSimilarMetadataChunk(mockPe); - result - .toSingle() - .blockingSubscribe( - s -> { - data.list = s; - data.val = s.stream().map(r -> r.getRawText()).collect(Collectors.joining(",")); - }, - e -> data.error = e); - if (data.error != null) { - fail("getSimilarMetadataChunk failed", data.error); - } - LOGGER.info("getSimilarMetadataChunk response: '{}'", data.val); - } - - private static class Data { - public Throwable error; - public String val; - public List list; - } -} +package com.edgechain.lib.index.client.impl; + +import com.edgechain.lib.embeddings.WordEmbeddings; +import com.edgechain.lib.endpoint.impl.PostgresEndpoint; +import com.edgechain.lib.index.domain.PostgresWordEmbeddings; +import com.edgechain.lib.index.enums.PostgresDistanceMetric; +import com.edgechain.lib.response.StringResponse; +import com.edgechain.lib.rxjava.transformer.observable.EdgeChain; +import com.edgechain.testutil.PostgresTestContainer; +import com.edgechain.testutil.PostgresTestContainer.PostgresImage; +import com.zaxxer.hikari.HikariConfig; +import java.util.List; +import java.util.stream.Collectors; +import org.junit.jupiter.api.AfterAll; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Test; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.boot.test.context.SpringBootTest.WebEnvironment; +import org.springframework.test.annotation.DirtiesContext; +import org.testcontainers.junit.jupiter.Testcontainers; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.junit.jupiter.api.Assertions.fail; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +@Testcontainers(disabledWithoutDocker = true) +@SpringBootTest(webEnvironment = WebEnvironment.NONE) +@DirtiesContext +class PostgresClientTest { + + private static final Logger LOGGER = LoggerFactory.getLogger(PostgresClientTest.class); + + private static final float FLOAT_ERROR_MARGIN = 0.0001f; + + private static PostgresTestContainer instance = new PostgresTestContainer(PostgresImage.VECTOR); + + @BeforeAll + static void setupAll() { + instance.start(); + } + + @AfterAll + static void teardownAll() { + instance.stop(); + } + + @Autowired private HikariConfig hikariConfig; + + @Autowired private PostgresClient service; + + @Test + void allMethods() { + // hikari has own copy of properties so set these here + hikariConfig.setJdbcUrl(instance.getJdbcUrl()); + hikariConfig.setUsername(instance.getUsername()); + hikariConfig.setPassword(instance.getPassword()); + + createTable(); + createMetadataTable(); + deleteAll(); // check delete before we get foreign keys + + String uuid1 = upsert(); + batchUpsert(); + query_noMeta(); + + String uuid2 = insertMetadata(); + batchInsertMetadata(); + insertIntoJoinTable(uuid1, uuid2); + query_meta(); + + getChunks(); + getSimilarChunks(); + } + + private void createTable() { + createTable_metric(PostgresDistanceMetric.L2, "testtableL2"); + createTable_metric(PostgresDistanceMetric.COSINE, "testtableCOS"); + createTable_metric(null, "testtable"); + + // create table again + createTable_metric(null, "testtable"); + } + + private void createTable_metric(PostgresDistanceMetric metric, String tableName) { + PostgresEndpoint mockPe = mock(PostgresEndpoint.class); + when(mockPe.getTableName()).thenReturn(tableName); + when(mockPe.getLists()).thenReturn(1); + when(mockPe.getDimensions()).thenReturn(2); + when(mockPe.getMetric()).thenReturn(metric); + + final Data data = new Data(); + EdgeChain result = service.createTable(mockPe); + result.toSingle().blockingSubscribe(s -> data.val = s.getResponse(), e -> data.error = e); + if (data.error != null) { + fail("createTable failed", data.error); + } + LOGGER.info("createTable (metric={}) response: '{}'", metric, data.val); + } + + private void createMetadataTable() { + PostgresEndpoint mockPe = mock(PostgresEndpoint.class); + when(mockPe.getTableName()).thenReturn("testtable"); + when(mockPe.getMetadataTableNames()).thenReturn(List.of("dogmeta", "catmeta")); + + final Data data = new Data(); + EdgeChain result = service.createMetadataTable(mockPe); + result.toSingle().blockingSubscribe(s -> data.val = s.getResponse(), e -> data.error = e); + if (data.error != null) { + fail("createMetadataTable failed", data.error); + } + LOGGER.info("createMetadataTable response: '{}'", data.val); + } + + private String upsert() { + WordEmbeddings we = new WordEmbeddings(); + we.setId("WE1"); + we.setScore("101"); + we.setValues(List.of(0.25f, 0.5f)); + + PostgresEndpoint mockPe = mock(PostgresEndpoint.class); + when(mockPe.getTableName()).thenReturn("testtable"); + when(mockPe.getWordEmbedding()).thenReturn(we); + when(mockPe.getFilename()).thenReturn("readme.pdf"); + when(mockPe.getNamespace()).thenReturn("testns"); + + final Data data = new Data(); + EdgeChain result = service.upsert(mockPe); + result.toSingle().blockingSubscribe(s -> data.val = s.getResponse(), e -> data.error = e); + if (data.error != null) { + fail("upsert failed", data.error); + } + LOGGER.info("upsert response: '{}'", data.val); + return data.val; + } + + private String insertMetadata() { + PostgresEndpoint mockPe = mock(PostgresEndpoint.class); + when(mockPe.getMetadataTableNames()).thenReturn(List.of("dogmeta", "catmeta")); + when(mockPe.getMetadata()).thenReturn("''duck''"); + when(mockPe.getDocumentDate()).thenReturn("November 11, 2015"); + + final Data data = new Data(); + EdgeChain result = service.insertMetadata(mockPe); + result.toSingle().blockingSubscribe(s -> data.val = s.getResponse(), e -> data.error = e); + if (data.error != null) { + fail("insertMetadata failed", data.error); + } + LOGGER.info("insertMetadata response: '{}'", data.val); + return data.val; + } + + private void batchUpsert() { + WordEmbeddings we1 = new WordEmbeddings(); + we1.setId("WE1"); + we1.setScore("101"); + we1.setValues(List.of(0.25f, 0.5f)); + + WordEmbeddings we2 = new WordEmbeddings(); + we2.setId("WE2"); + we2.setScore("202"); + we2.setValues(List.of(0.75f, 0.9f)); + + PostgresEndpoint mockPe = mock(PostgresEndpoint.class); + when(mockPe.getTableName()).thenReturn("testtable"); + when(mockPe.getWordEmbeddingsList()).thenReturn(List.of(we1, we2)); + when(mockPe.getFilename()).thenReturn("readme.pdf"); + when(mockPe.getNamespace()).thenReturn("testns"); + + final Data data = new Data(); + EdgeChain> result = service.batchUpsert(mockPe); + result + .toSingle() + .blockingSubscribe( + s -> data.val = s.stream().map(r -> r.getResponse()).collect(Collectors.joining(",")), + e -> data.error = e); + if (data.error != null) { + fail("batchUpsert failed", data.error); + } + LOGGER.info("batchUpsert response: '{}'", data.val); + } + + private void batchInsertMetadata() { + PostgresEndpoint mockPe = mock(PostgresEndpoint.class); + when(mockPe.getMetadataTableNames()).thenReturn(List.of("dogmeta", "catmeta")); + when(mockPe.getMetadataList()).thenReturn(List.of("cow", "horse")); + + final Data data = new Data(); + EdgeChain> result = service.batchInsertMetadata(mockPe); + result + .toSingle() + .blockingSubscribe( + s -> data.val = s.stream().map(r -> r.getResponse()).collect(Collectors.joining(",")), + e -> data.error = e); + if (data.error != null) { + fail("batchInsertMetadata failed", data.error); + } + LOGGER.info("batchInsertMetadata response: '{}'", data.val); + } + + private void insertIntoJoinTable(String uuid1, String uuid2) { + PostgresEndpoint mockPe = mock(PostgresEndpoint.class); + when(mockPe.getTableName()).thenReturn("testtable"); + when(mockPe.getMetadataTableNames()).thenReturn(List.of("dogmeta", "catmeta")); + when(mockPe.getId()).thenReturn(uuid1); + when(mockPe.getMetadataId()).thenReturn(uuid2); + + final Data data = new Data(); + + EdgeChain result = service.insertIntoJoinTable(mockPe); + result.toSingle().blockingSubscribe(s -> data.val = s.getResponse(), e -> data.error = e); + if (data.error != null) { + fail("insertIntoJoinTable failed", data.error); + } + LOGGER.info("insertIntoJoinTable response: '{}'", data.val); + } + + private void deleteAll() { + deleteAll_namespace(null, "knowledge"); + deleteAll_namespace("", "knowledge"); + deleteAll_namespace("testns", "testns"); + } + + private void deleteAll_namespace(String namespace, String expected) { + PostgresEndpoint mockPe = mock(PostgresEndpoint.class); + when(mockPe.getTableName()).thenReturn("testtable"); + when(mockPe.getNamespace()).thenReturn(namespace); + + final Data data = new Data(); + EdgeChain result = service.deleteAll(mockPe); + result.toSingle().blockingSubscribe(s -> data.val = s.getResponse(), e -> data.error = e); + if (data.error != null) { + fail("deleteAll failed", data.error); + } + LOGGER.info("deleteAll (namespace={}) response: '{}'", namespace, data.val); + assertTrue(data.val.endsWith(expected)); + } + + private void query_noMeta() { + query_noMeta_metric(PostgresDistanceMetric.COSINE); + query_noMeta_metric(PostgresDistanceMetric.IP); + query_noMeta_metric(PostgresDistanceMetric.L2); + } + + private void query_noMeta_metric(PostgresDistanceMetric metric) { + WordEmbeddings we1 = new WordEmbeddings(); + we1.setId("WEQUERY"); + we1.setScore("104"); + we1.setValues(List.of(0.25f, 0.5f)); + + PostgresEndpoint mockPe = mock(PostgresEndpoint.class); + when(mockPe.getTableName()).thenReturn("testtable"); + when(mockPe.getNamespace()).thenReturn("testns"); + when(mockPe.getProbes()).thenReturn(5); + when(mockPe.getMetric()).thenReturn(metric); + when(mockPe.getWordEmbedding()).thenReturn(we1); + when(mockPe.getTopK()).thenReturn(1000); + when(mockPe.getMetadataTableNames()).thenReturn(null); + + final Data data = new Data(); + EdgeChain> result = service.query(mockPe); + result + .toSingle() + .blockingSubscribe( + s -> data.val = s.stream().map(r -> r.getRawText()).collect(Collectors.joining(",")), + e -> data.error = e); + if (data.error != null) { + fail("query (no meta) failed", data.error); + } + LOGGER.info("query no meta (metric={}) response: '{}'", metric, data.val); + + // WE1 from single upsert, and WE2 from batch upsert + assertTrue(data.val.contains("WE1") && data.val.contains("WE2")); + } + + private void query_meta() { + query_meta_metric(PostgresDistanceMetric.COSINE); + query_meta_metric(PostgresDistanceMetric.IP); + query_meta_metric(PostgresDistanceMetric.L2); + } + + private void query_meta_metric(PostgresDistanceMetric metric) { + WordEmbeddings we1 = new WordEmbeddings(); + we1.setId("WEQUERY"); + we1.setScore("104"); + we1.setValues(List.of(0.25f, 0.5f)); + + PostgresEndpoint mockPe = mock(PostgresEndpoint.class); + when(mockPe.getTableName()).thenReturn("testtable"); + when(mockPe.getNamespace()).thenReturn("testns"); + when(mockPe.getProbes()).thenReturn(5); + when(mockPe.getMetric()).thenReturn(metric); + when(mockPe.getWordEmbedding()).thenReturn(we1); + when(mockPe.getTopK()).thenReturn(1000); + when(mockPe.getMetadataTableNames()).thenReturn(List.of("dogmeta", "catmeta")); + + final Data data = new Data(); + EdgeChain> result = service.queryWithMetadata(mockPe); + result + .toSingle() + .blockingSubscribe( + s -> data.val = s.stream().map(r -> r.getRawText()).collect(Collectors.joining(",")), + e -> data.error = e); + if (data.error != null) { + fail("query (meta) failed", data.error); + } + LOGGER.info("query with meta (metric={}) response: '{}'", metric, data.val); + + // WE1 from single joined upsert + assertTrue(data.val.contains("WE1")); + } + + private void getChunks() { + PostgresEndpoint mockPe = mock(PostgresEndpoint.class); + when(mockPe.getTableName()).thenReturn("testtable"); + when(mockPe.getFilename()).thenReturn("readme.pdf"); + + final Data data = new Data(); + EdgeChain> result = service.getAllChunks(mockPe); + result + .toSingle() + .blockingSubscribe( + s -> { + data.list = s; + data.val = s.stream().map(r -> r.getRawText()).collect(Collectors.joining(",")); + }, + e -> data.error = e); + if (data.error != null) { + fail("getChunks failed", data.error); + } + LOGGER.info("getChunks response: '{}'", data.val); + + // WE1 from single upsert, and WE2 from batch upsert + assertTrue(data.val.contains("WE1") && data.val.contains("WE2")); + + PostgresWordEmbeddings first = data.list.get(0); + assertEquals(0.25f, first.getValues().get(0), FLOAT_ERROR_MARGIN); + assertEquals(0.5f, first.getValues().get(1), FLOAT_ERROR_MARGIN); + + PostgresWordEmbeddings second = data.list.get(1); + assertEquals(0.75f, second.getValues().get(0), FLOAT_ERROR_MARGIN); + assertEquals(0.9f, second.getValues().get(1), FLOAT_ERROR_MARGIN); + } + + private void getSimilarChunks() { + PostgresEndpoint mockPe = mock(PostgresEndpoint.class); + when(mockPe.getMetadataTableNames()).thenReturn(List.of("dogmeta", "catmeta")); + when(mockPe.getEmbeddingChunk()).thenReturn("how to test this"); + + final Data data = new Data(); + EdgeChain> result = service.getSimilarMetadataChunk(mockPe); + result + .toSingle() + .blockingSubscribe( + s -> { + data.list = s; + data.val = s.stream().map(r -> r.getRawText()).collect(Collectors.joining(",")); + }, + e -> data.error = e); + if (data.error != null) { + fail("getSimilarMetadataChunk failed", data.error); + } + LOGGER.info("getSimilarMetadataChunk response: '{}'", data.val); + } + + private static class Data { + public Throwable error; + public String val; + public List list; + } +} diff --git a/FlySpring/edgechain-app/src/test/java/com/edgechain/lib/supabase/security/WebSecurityConfigFixTest.java b/FlySpring/edgechain-app/src/test/java/com/edgechain/lib/supabase/security/WebSecurityConfigFixTest.java index 01d0ba63d..881793676 100644 --- a/FlySpring/edgechain-app/src/test/java/com/edgechain/lib/supabase/security/WebSecurityConfigFixTest.java +++ b/FlySpring/edgechain-app/src/test/java/com/edgechain/lib/supabase/security/WebSecurityConfigFixTest.java @@ -1,119 +1,119 @@ -package com.edgechain.lib.supabase.security; - -import com.edgechain.lib.configuration.domain.AuthFilter; -import com.edgechain.lib.configuration.domain.MethodAuthentication; -import com.edgechain.testutil.TestJwtCreator; -import java.util.List; -import org.junit.jupiter.api.BeforeAll; -import org.junit.jupiter.api.Test; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc; -import org.springframework.boot.test.context.SpringBootTest; -import org.springframework.boot.test.context.SpringBootTest.WebEnvironment; -import org.springframework.boot.test.context.TestConfiguration; -import org.springframework.context.annotation.Bean; -import org.springframework.http.HttpHeaders; -import org.springframework.http.MediaType; -import org.springframework.test.context.ContextConfiguration; -import org.springframework.test.web.servlet.MockMvc; -import static org.junit.jupiter.api.Assertions.assertTrue; -import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.delete; -import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get; -import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.patch; -import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post; -import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.put; -import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; - -@SpringBootTest(webEnvironment = WebEnvironment.RANDOM_PORT) -@AutoConfigureMockMvc -@ContextConfiguration(name = "contextWithEmptyPatternsAuthFilter") -class WebSecurityConfigFixTest { - - // ====== TEST JWT-BASED SECURITY WITH NO ROLES (bearer token must be in header) ====== - - private static final String FULL_NONCONTEXT_PATH = "/v0/endpoint/"; - - @BeforeAll - static void setupAll() { - System.setProperty("jwt.secret", "edge-chain-unit-test-jwt-secret"); - } - - @TestConfiguration - public static class AuthFilterTestConfig { - @Bean - AuthFilter authFilter() { - // provide an AuthFilter with an empty string in the patterns list. - // the security class should fix this to ** - AuthFilter auth = new AuthFilter(); - auth.setRequestGet(new MethodAuthentication(List.of(""), "")); - auth.setRequestDelete(new MethodAuthentication(List.of(""), "")); - auth.setRequestPatch(new MethodAuthentication(List.of(""), "")); - auth.setRequestPost(new MethodAuthentication(List.of(""), "")); - auth.setRequestPut(new MethodAuthentication(List.of(""), "")); - return auth; - } - } - - @Autowired private MockMvc mvc; - - @Autowired private JwtHelper jwtHelper; - - @Test - void validateJwt() { - String jwt = TestJwtCreator.generate("ROLE_IGNORED"); - assertTrue(jwtHelper.validate(jwt)); - } - - @Test - void getEndpoint() throws Exception { - String jwt = TestJwtCreator.generate("ROLE_IGNORED"); - mvc.perform( - get(FULL_NONCONTEXT_PATH) - .header(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE) - .header("Authorization", "Bearer " + jwt)) - .andExpect(status().isNotFound()); - } - - @Test - void postEndpoint() throws Exception { - String jwt = TestJwtCreator.generate("ROLE_IGNORED"); - mvc.perform( - post(FULL_NONCONTEXT_PATH) - .content("{}") - .header(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE) - .header("Authorization", "Bearer " + jwt)) - .andExpect(status().isNotFound()); - } - - @Test - void deleteEndpoint() throws Exception { - String jwt = TestJwtCreator.generate("ROLE_IGNORED"); - mvc.perform( - delete(FULL_NONCONTEXT_PATH) - .header(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE) - .header("Authorization", "Bearer " + jwt)) - .andExpect(status().isNotFound()); - } - - @Test - void patchEndpoint() throws Exception { - String jwt = TestJwtCreator.generate("ROLE_IGNORED"); - mvc.perform( - patch(FULL_NONCONTEXT_PATH) - .content("{}") - .header(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE) - .header("Authorization", "Bearer " + jwt)) - .andExpect(status().isNotFound()); - } - - @Test - void putEndpoint() throws Exception { - String jwt = TestJwtCreator.generate("ROLE_IGNORED"); - mvc.perform( - put(FULL_NONCONTEXT_PATH) - .content("{}") - .header(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE) - .header("Authorization", "Bearer " + jwt)) - .andExpect(status().isNotFound()); - } -} +package com.edgechain.lib.supabase.security; + +import com.edgechain.lib.configuration.domain.AuthFilter; +import com.edgechain.lib.configuration.domain.MethodAuthentication; +import com.edgechain.testutil.TestJwtCreator; +import java.util.List; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.boot.test.context.SpringBootTest.WebEnvironment; +import org.springframework.boot.test.context.TestConfiguration; +import org.springframework.context.annotation.Bean; +import org.springframework.http.HttpHeaders; +import org.springframework.http.MediaType; +import org.springframework.test.context.ContextConfiguration; +import org.springframework.test.web.servlet.MockMvc; +import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.delete; +import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get; +import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.patch; +import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post; +import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.put; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; + +@SpringBootTest(webEnvironment = WebEnvironment.RANDOM_PORT) +@AutoConfigureMockMvc +@ContextConfiguration(name = "contextWithEmptyPatternsAuthFilter") +class WebSecurityConfigFixTest { + + // ====== TEST JWT-BASED SECURITY WITH NO ROLES (bearer token must be in header) ====== + + private static final String FULL_NONCONTEXT_PATH = "/v0/endpoint/"; + + @BeforeAll + static void setupAll() { + System.setProperty("jwt.secret", "edge-chain-unit-test-jwt-secret"); + } + + @TestConfiguration + public static class AuthFilterTestConfig { + @Bean + AuthFilter authFilter() { + // provide an AuthFilter with an empty string in the patterns list. + // the security class should fix this to ** + AuthFilter auth = new AuthFilter(); + auth.setRequestGet(new MethodAuthentication(List.of(""), "")); + auth.setRequestDelete(new MethodAuthentication(List.of(""), "")); + auth.setRequestPatch(new MethodAuthentication(List.of(""), "")); + auth.setRequestPost(new MethodAuthentication(List.of(""), "")); + auth.setRequestPut(new MethodAuthentication(List.of(""), "")); + return auth; + } + } + + @Autowired private MockMvc mvc; + + @Autowired private JwtHelper jwtHelper; + + @Test + void validateJwt() { + String jwt = TestJwtCreator.generate("ROLE_IGNORED"); + assertTrue(jwtHelper.validate(jwt)); + } + + @Test + void getEndpoint() throws Exception { + String jwt = TestJwtCreator.generate("ROLE_IGNORED"); + mvc.perform( + get(FULL_NONCONTEXT_PATH) + .header(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE) + .header("Authorization", "Bearer " + jwt)) + .andExpect(status().isNotFound()); + } + + @Test + void postEndpoint() throws Exception { + String jwt = TestJwtCreator.generate("ROLE_IGNORED"); + mvc.perform( + post(FULL_NONCONTEXT_PATH) + .content("{}") + .header(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE) + .header("Authorization", "Bearer " + jwt)) + .andExpect(status().isNotFound()); + } + + @Test + void deleteEndpoint() throws Exception { + String jwt = TestJwtCreator.generate("ROLE_IGNORED"); + mvc.perform( + delete(FULL_NONCONTEXT_PATH) + .header(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE) + .header("Authorization", "Bearer " + jwt)) + .andExpect(status().isNotFound()); + } + + @Test + void patchEndpoint() throws Exception { + String jwt = TestJwtCreator.generate("ROLE_IGNORED"); + mvc.perform( + patch(FULL_NONCONTEXT_PATH) + .content("{}") + .header(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE) + .header("Authorization", "Bearer " + jwt)) + .andExpect(status().isNotFound()); + } + + @Test + void putEndpoint() throws Exception { + String jwt = TestJwtCreator.generate("ROLE_IGNORED"); + mvc.perform( + put(FULL_NONCONTEXT_PATH) + .content("{}") + .header(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE) + .header("Authorization", "Bearer " + jwt)) + .andExpect(status().isNotFound()); + } +} diff --git a/FlySpring/edgechain-app/src/test/java/com/edgechain/lib/supabase/security/WebSecurityContextPathTest.java b/FlySpring/edgechain-app/src/test/java/com/edgechain/lib/supabase/security/WebSecurityContextPathTest.java index 9b6482670..b6823c633 100644 --- a/FlySpring/edgechain-app/src/test/java/com/edgechain/lib/supabase/security/WebSecurityContextPathTest.java +++ b/FlySpring/edgechain-app/src/test/java/com/edgechain/lib/supabase/security/WebSecurityContextPathTest.java @@ -1,79 +1,79 @@ -package com.edgechain.lib.supabase.security; - -import com.edgechain.lib.configuration.WebConfiguration; -import com.edgechain.lib.configuration.domain.SecurityUUID; -import org.junit.jupiter.api.Test; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc; -import org.springframework.boot.test.context.SpringBootTest; -import org.springframework.boot.test.context.SpringBootTest.WebEnvironment; -import org.springframework.http.HttpHeaders; -import org.springframework.http.MediaType; -import org.springframework.test.web.servlet.MockMvc; -import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.delete; -import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get; -import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.patch; -import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post; -import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.put; -import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; - -@SpringBootTest(webEnvironment = WebEnvironment.RANDOM_PORT) -@AutoConfigureMockMvc -class WebSecurityContextPathTest { - - // ====== TEST CONTEXT-BASED SECURITY (uuid must be in header) ====== - - private static final String FULL_CONTEXT_PATH = WebConfiguration.CONTEXT_PATH + "/"; - - @Autowired private MockMvc mvc; - - @Autowired private SecurityUUID securityUUID; - - @Test - void getContextEndpoint() throws Exception { - mvc.perform( - get(FULL_CONTEXT_PATH) - .header(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE) - .header("Authorization", securityUUID.getAuthKey())) - .andExpect(status().isNotFound()); - } - - @Test - void postContextEndpoint() throws Exception { - mvc.perform( - post(FULL_CONTEXT_PATH) - .content("{}") - .header(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE) - .header("Authorization", securityUUID.getAuthKey())) - .andExpect(status().isNotFound()); - } - - @Test - void deleteContextEndpoint() throws Exception { - mvc.perform( - delete(FULL_CONTEXT_PATH) - .header(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE) - .header("Authorization", securityUUID.getAuthKey())) - .andExpect(status().isNotFound()); - } - - @Test - void patchContextEndpoint() throws Exception { - mvc.perform( - patch(FULL_CONTEXT_PATH) - .content("{}") - .header(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE) - .header("Authorization", securityUUID.getAuthKey())) - .andExpect(status().isNotFound()); - } - - @Test - void putContextEndpoint() throws Exception { - mvc.perform( - put(FULL_CONTEXT_PATH) - .content("{}") - .header(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE) - .header("Authorization", securityUUID.getAuthKey())) - .andExpect(status().isNotFound()); - } -} +package com.edgechain.lib.supabase.security; + +import com.edgechain.lib.configuration.WebConfiguration; +import com.edgechain.lib.configuration.domain.SecurityUUID; +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.boot.test.context.SpringBootTest.WebEnvironment; +import org.springframework.http.HttpHeaders; +import org.springframework.http.MediaType; +import org.springframework.test.web.servlet.MockMvc; +import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.delete; +import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get; +import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.patch; +import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post; +import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.put; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; + +@SpringBootTest(webEnvironment = WebEnvironment.RANDOM_PORT) +@AutoConfigureMockMvc +class WebSecurityContextPathTest { + + // ====== TEST CONTEXT-BASED SECURITY (uuid must be in header) ====== + + private static final String FULL_CONTEXT_PATH = WebConfiguration.CONTEXT_PATH + "/"; + + @Autowired private MockMvc mvc; + + @Autowired private SecurityUUID securityUUID; + + @Test + void getContextEndpoint() throws Exception { + mvc.perform( + get(FULL_CONTEXT_PATH) + .header(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE) + .header("Authorization", securityUUID.getAuthKey())) + .andExpect(status().isNotFound()); + } + + @Test + void postContextEndpoint() throws Exception { + mvc.perform( + post(FULL_CONTEXT_PATH) + .content("{}") + .header(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE) + .header("Authorization", securityUUID.getAuthKey())) + .andExpect(status().isNotFound()); + } + + @Test + void deleteContextEndpoint() throws Exception { + mvc.perform( + delete(FULL_CONTEXT_PATH) + .header(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE) + .header("Authorization", securityUUID.getAuthKey())) + .andExpect(status().isNotFound()); + } + + @Test + void patchContextEndpoint() throws Exception { + mvc.perform( + patch(FULL_CONTEXT_PATH) + .content("{}") + .header(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE) + .header("Authorization", securityUUID.getAuthKey())) + .andExpect(status().isNotFound()); + } + + @Test + void putContextEndpoint() throws Exception { + mvc.perform( + put(FULL_CONTEXT_PATH) + .content("{}") + .header(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE) + .header("Authorization", securityUUID.getAuthKey())) + .andExpect(status().isNotFound()); + } +} diff --git a/FlySpring/edgechain-app/src/test/java/com/edgechain/lib/supabase/security/WebSecurityJwtNoRoleTest.java b/FlySpring/edgechain-app/src/test/java/com/edgechain/lib/supabase/security/WebSecurityJwtNoRoleTest.java index 6d401409e..3731b2a05 100644 --- a/FlySpring/edgechain-app/src/test/java/com/edgechain/lib/supabase/security/WebSecurityJwtNoRoleTest.java +++ b/FlySpring/edgechain-app/src/test/java/com/edgechain/lib/supabase/security/WebSecurityJwtNoRoleTest.java @@ -1,96 +1,96 @@ -package com.edgechain.lib.supabase.security; - -import com.edgechain.testutil.TestJwtCreator; -import org.junit.jupiter.api.BeforeAll; -import org.junit.jupiter.api.Test; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc; -import org.springframework.boot.test.context.SpringBootTest; -import org.springframework.boot.test.context.SpringBootTest.WebEnvironment; -import org.springframework.http.HttpHeaders; -import org.springframework.http.MediaType; -import org.springframework.test.web.servlet.MockMvc; -import static org.junit.jupiter.api.Assertions.assertTrue; -import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.delete; -import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get; -import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.patch; -import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post; -import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.put; -import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; - -@SpringBootTest(webEnvironment = WebEnvironment.RANDOM_PORT) -@AutoConfigureMockMvc -class WebSecurityJwtNoRoleTest { - - // ====== TEST JWT-BASED SECURITY WITH NO ROLES (bearer token must be in header) ====== - - private static final String FULL_NONCONTEXT_PATH = "/v0/endpoint/"; - - @BeforeAll - static void setupAll() { - System.setProperty("jwt.secret", "edge-chain-unit-test-jwt-secret"); - } - - @Autowired private MockMvc mvc; - - @Autowired private JwtHelper jwtHelper; - - @Test - void validateJwt() { - String jwt = TestJwtCreator.generate("ROLE_IGNORED"); - assertTrue(jwtHelper.validate(jwt)); - } - - @Test - void getEndpoint() throws Exception { - String jwt = TestJwtCreator.generate("ROLE_IGNORED"); - mvc.perform( - get(FULL_NONCONTEXT_PATH) - .header(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE) - .header("Authorization", "Bearer " + jwt)) - .andExpect(status().isNotFound()); - } - - @Test - void postEndpoint() throws Exception { - String jwt = TestJwtCreator.generate("ROLE_IGNORED"); - mvc.perform( - post(FULL_NONCONTEXT_PATH) - .content("{}") - .header(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE) - .header("Authorization", "Bearer " + jwt)) - .andExpect(status().isNotFound()); - } - - @Test - void deleteEndpoint() throws Exception { - String jwt = TestJwtCreator.generate("ROLE_IGNORED"); - mvc.perform( - delete(FULL_NONCONTEXT_PATH) - .header(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE) - .header("Authorization", "Bearer " + jwt)) - .andExpect(status().isNotFound()); - } - - @Test - void patchEndpoint() throws Exception { - String jwt = TestJwtCreator.generate("ROLE_IGNORED"); - mvc.perform( - patch(FULL_NONCONTEXT_PATH) - .content("{}") - .header(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE) - .header("Authorization", "Bearer " + jwt)) - .andExpect(status().isNotFound()); - } - - @Test - void putEndpoint() throws Exception { - String jwt = TestJwtCreator.generate("ROLE_IGNORED"); - mvc.perform( - put(FULL_NONCONTEXT_PATH) - .content("{}") - .header(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE) - .header("Authorization", "Bearer " + jwt)) - .andExpect(status().isNotFound()); - } -} +package com.edgechain.lib.supabase.security; + +import com.edgechain.testutil.TestJwtCreator; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.boot.test.context.SpringBootTest.WebEnvironment; +import org.springframework.http.HttpHeaders; +import org.springframework.http.MediaType; +import org.springframework.test.web.servlet.MockMvc; +import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.delete; +import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get; +import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.patch; +import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post; +import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.put; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; + +@SpringBootTest(webEnvironment = WebEnvironment.RANDOM_PORT) +@AutoConfigureMockMvc +class WebSecurityJwtNoRoleTest { + + // ====== TEST JWT-BASED SECURITY WITH NO ROLES (bearer token must be in header) ====== + + private static final String FULL_NONCONTEXT_PATH = "/v0/endpoint/"; + + @BeforeAll + static void setupAll() { + System.setProperty("jwt.secret", "edge-chain-unit-test-jwt-secret"); + } + + @Autowired private MockMvc mvc; + + @Autowired private JwtHelper jwtHelper; + + @Test + void validateJwt() { + String jwt = TestJwtCreator.generate("ROLE_IGNORED"); + assertTrue(jwtHelper.validate(jwt)); + } + + @Test + void getEndpoint() throws Exception { + String jwt = TestJwtCreator.generate("ROLE_IGNORED"); + mvc.perform( + get(FULL_NONCONTEXT_PATH) + .header(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE) + .header("Authorization", "Bearer " + jwt)) + .andExpect(status().isNotFound()); + } + + @Test + void postEndpoint() throws Exception { + String jwt = TestJwtCreator.generate("ROLE_IGNORED"); + mvc.perform( + post(FULL_NONCONTEXT_PATH) + .content("{}") + .header(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE) + .header("Authorization", "Bearer " + jwt)) + .andExpect(status().isNotFound()); + } + + @Test + void deleteEndpoint() throws Exception { + String jwt = TestJwtCreator.generate("ROLE_IGNORED"); + mvc.perform( + delete(FULL_NONCONTEXT_PATH) + .header(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE) + .header("Authorization", "Bearer " + jwt)) + .andExpect(status().isNotFound()); + } + + @Test + void patchEndpoint() throws Exception { + String jwt = TestJwtCreator.generate("ROLE_IGNORED"); + mvc.perform( + patch(FULL_NONCONTEXT_PATH) + .content("{}") + .header(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE) + .header("Authorization", "Bearer " + jwt)) + .andExpect(status().isNotFound()); + } + + @Test + void putEndpoint() throws Exception { + String jwt = TestJwtCreator.generate("ROLE_IGNORED"); + mvc.perform( + put(FULL_NONCONTEXT_PATH) + .content("{}") + .header(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE) + .header("Authorization", "Bearer " + jwt)) + .andExpect(status().isNotFound()); + } +} diff --git a/FlySpring/edgechain-app/src/test/java/com/edgechain/lib/supabase/security/WebSecurityJwtWithRoleTest.java b/FlySpring/edgechain-app/src/test/java/com/edgechain/lib/supabase/security/WebSecurityJwtWithRoleTest.java index 81da385eb..82be25f28 100644 --- a/FlySpring/edgechain-app/src/test/java/com/edgechain/lib/supabase/security/WebSecurityJwtWithRoleTest.java +++ b/FlySpring/edgechain-app/src/test/java/com/edgechain/lib/supabase/security/WebSecurityJwtWithRoleTest.java @@ -1,173 +1,173 @@ -package com.edgechain.lib.supabase.security; - -import com.edgechain.lib.configuration.domain.AuthFilter; -import com.edgechain.lib.configuration.domain.MethodAuthentication; -import com.edgechain.testutil.TestJwtCreator; -import java.util.List; -import org.junit.jupiter.api.BeforeAll; -import org.junit.jupiter.api.Test; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc; -import org.springframework.boot.test.context.SpringBootTest; -import org.springframework.boot.test.context.SpringBootTest.WebEnvironment; -import org.springframework.boot.test.context.TestConfiguration; -import org.springframework.context.annotation.Bean; -import org.springframework.http.HttpHeaders; -import org.springframework.http.MediaType; -import org.springframework.test.context.ActiveProfiles; -import org.springframework.test.context.ContextConfiguration; -import org.springframework.test.web.servlet.MockMvc; -import static org.junit.jupiter.api.Assertions.assertTrue; -import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.delete; -import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get; -import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.patch; -import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post; -import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.put; -import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; - -@ActiveProfiles("test") -@SpringBootTest(webEnvironment = WebEnvironment.RANDOM_PORT) -@AutoConfigureMockMvc -@ContextConfiguration(name = "contextWithTestRolesAuthFilter") -class WebSecurityJwtWithRoleTest { - - // ====== TEST JWT-BASED SECURITY WITH ROLES (bearer token must be in header) ====== - - private static final String FULL_NONCONTEXT_PATH = "/v0/endpoint/"; - - @TestConfiguration - public static class AuthFilterTestConfig { - @Bean - AuthFilter authFilter() { - // provide an AuthFilter with roles to check we test the correct role for each method - AuthFilter auth = new AuthFilter(); - auth.setRequestGet(new MethodAuthentication(List.of("**"), "ROLE_ADMIN1", "ROLE_AI1")); - auth.setRequestDelete(new MethodAuthentication(List.of("**"), "ROLE_ADMIN2", "ROLE_AI2")); - auth.setRequestPatch(new MethodAuthentication(List.of("**"), "ROLE_ADMIN3", "ROLE_AI3")); - auth.setRequestPost(new MethodAuthentication(List.of("**"), "ROLE_ADMIN4", "ROLE_AI4")); - auth.setRequestPut(new MethodAuthentication(List.of("**"), "ROLE_ADMIN5", "ROLE_AI5")); - return auth; - } - } - - @BeforeAll - static void setupAll() { - System.setProperty("jwt.secret", "edge-chain-unit-test-jwt-secret"); - } - - @Autowired private MockMvc mvc; - - @Autowired private JwtHelper jwtHelper; - - @Test - void validateJwt() { - String jwt = TestJwtCreator.generate("ROLE_TEST"); - assertTrue(jwtHelper.validate(jwt)); - } - - @Test - void getEndpoint() throws Exception { - String jwt = TestJwtCreator.generate("ROLE_ADMIN1"); - mvc.perform( - get(FULL_NONCONTEXT_PATH) - .header(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE) - .header("Authorization", "Bearer " + jwt)) - .andExpect(status().isNotFound()); - } - - @Test - void getEndpoint_notAuth() throws Exception { - String jwt = TestJwtCreator.generate("ROLE_NO_ACCESS"); - mvc.perform( - get(FULL_NONCONTEXT_PATH) - .header(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE) - .header("Authorization", "Bearer " + jwt)) - .andExpect(status().isForbidden()); - } - - @Test - void postEndpoint() throws Exception { - String jwt = TestJwtCreator.generate("ROLE_ADMIN4"); - mvc.perform( - post(FULL_NONCONTEXT_PATH) - .content("{}") - .header(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE) - .header("Authorization", "Bearer " + jwt)) - .andExpect(status().isNotFound()); - } - - @Test - void postEndpoint_notAuth() throws Exception { - String jwt = TestJwtCreator.generate("ROLE_NO_ACCESS"); - mvc.perform( - post(FULL_NONCONTEXT_PATH) - .content("{}") - .header(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE) - .header("Authorization", "Bearer " + jwt)) - .andExpect(status().isForbidden()); - } - - @Test - void deleteEndpoint() throws Exception { - String jwt = TestJwtCreator.generate("ROLE_ADMIN2"); - mvc.perform( - delete(FULL_NONCONTEXT_PATH) - .header(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE) - .header("Authorization", "Bearer " + jwt)) - .andExpect(status().isNotFound()); - } - - @Test - void deleteEndpoint_notAuth() throws Exception { - String jwt = TestJwtCreator.generate("ROLE_NO_ACCESS"); - mvc.perform( - delete(FULL_NONCONTEXT_PATH) - .header(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE) - .header("Authorization", "Bearer " + jwt)) - .andExpect(status().isForbidden()); - } - - @Test - void patchEndpoint() throws Exception { - String jwt = TestJwtCreator.generate("ROLE_ADMIN3"); - mvc.perform( - patch(FULL_NONCONTEXT_PATH) - .content("{}") - .header(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE) - .header("Authorization", "Bearer " + jwt)) - .andExpect(status().isNotFound()); - } - - @Test - void patchEndpoint_notAuth() throws Exception { - String jwt = TestJwtCreator.generate("ROLE_NO_ACCESS"); - mvc.perform( - patch(FULL_NONCONTEXT_PATH) - .content("{}") - .header(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE) - .header("Authorization", "Bearer " + jwt)) - .andExpect(status().isForbidden()); - } - - @Test - void putEndpoint() throws Exception { - String jwt = TestJwtCreator.generate("ROLE_ADMIN5"); - mvc.perform( - put(FULL_NONCONTEXT_PATH) - .content("{}") - .header(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE) - .header("Authorization", "Bearer " + jwt)) - .andExpect(status().isNotFound()); - } - - @Test - void putEndpoint_notAuth() throws Exception { - String jwt = TestJwtCreator.generate("ROLE_NO_ACCESS"); - mvc.perform( - put(FULL_NONCONTEXT_PATH) - .content("{}") - .header(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE) - .header("Authorization", "Bearer " + jwt)) - .andExpect(status().isForbidden()); - } -} +package com.edgechain.lib.supabase.security; + +import com.edgechain.lib.configuration.domain.AuthFilter; +import com.edgechain.lib.configuration.domain.MethodAuthentication; +import com.edgechain.testutil.TestJwtCreator; +import java.util.List; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.boot.test.context.SpringBootTest.WebEnvironment; +import org.springframework.boot.test.context.TestConfiguration; +import org.springframework.context.annotation.Bean; +import org.springframework.http.HttpHeaders; +import org.springframework.http.MediaType; +import org.springframework.test.context.ActiveProfiles; +import org.springframework.test.context.ContextConfiguration; +import org.springframework.test.web.servlet.MockMvc; +import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.delete; +import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get; +import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.patch; +import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post; +import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.put; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; + +@ActiveProfiles("test") +@SpringBootTest(webEnvironment = WebEnvironment.RANDOM_PORT) +@AutoConfigureMockMvc +@ContextConfiguration(name = "contextWithTestRolesAuthFilter") +class WebSecurityJwtWithRoleTest { + + // ====== TEST JWT-BASED SECURITY WITH ROLES (bearer token must be in header) ====== + + private static final String FULL_NONCONTEXT_PATH = "/v0/endpoint/"; + + @TestConfiguration + public static class AuthFilterTestConfig { + @Bean + AuthFilter authFilter() { + // provide an AuthFilter with roles to check we test the correct role for each method + AuthFilter auth = new AuthFilter(); + auth.setRequestGet(new MethodAuthentication(List.of("**"), "ROLE_ADMIN1", "ROLE_AI1")); + auth.setRequestDelete(new MethodAuthentication(List.of("**"), "ROLE_ADMIN2", "ROLE_AI2")); + auth.setRequestPatch(new MethodAuthentication(List.of("**"), "ROLE_ADMIN3", "ROLE_AI3")); + auth.setRequestPost(new MethodAuthentication(List.of("**"), "ROLE_ADMIN4", "ROLE_AI4")); + auth.setRequestPut(new MethodAuthentication(List.of("**"), "ROLE_ADMIN5", "ROLE_AI5")); + return auth; + } + } + + @BeforeAll + static void setupAll() { + System.setProperty("jwt.secret", "edge-chain-unit-test-jwt-secret"); + } + + @Autowired private MockMvc mvc; + + @Autowired private JwtHelper jwtHelper; + + @Test + void validateJwt() { + String jwt = TestJwtCreator.generate("ROLE_TEST"); + assertTrue(jwtHelper.validate(jwt)); + } + + @Test + void getEndpoint() throws Exception { + String jwt = TestJwtCreator.generate("ROLE_ADMIN1"); + mvc.perform( + get(FULL_NONCONTEXT_PATH) + .header(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE) + .header("Authorization", "Bearer " + jwt)) + .andExpect(status().isNotFound()); + } + + @Test + void getEndpoint_notAuth() throws Exception { + String jwt = TestJwtCreator.generate("ROLE_NO_ACCESS"); + mvc.perform( + get(FULL_NONCONTEXT_PATH) + .header(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE) + .header("Authorization", "Bearer " + jwt)) + .andExpect(status().isForbidden()); + } + + @Test + void postEndpoint() throws Exception { + String jwt = TestJwtCreator.generate("ROLE_ADMIN4"); + mvc.perform( + post(FULL_NONCONTEXT_PATH) + .content("{}") + .header(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE) + .header("Authorization", "Bearer " + jwt)) + .andExpect(status().isNotFound()); + } + + @Test + void postEndpoint_notAuth() throws Exception { + String jwt = TestJwtCreator.generate("ROLE_NO_ACCESS"); + mvc.perform( + post(FULL_NONCONTEXT_PATH) + .content("{}") + .header(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE) + .header("Authorization", "Bearer " + jwt)) + .andExpect(status().isForbidden()); + } + + @Test + void deleteEndpoint() throws Exception { + String jwt = TestJwtCreator.generate("ROLE_ADMIN2"); + mvc.perform( + delete(FULL_NONCONTEXT_PATH) + .header(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE) + .header("Authorization", "Bearer " + jwt)) + .andExpect(status().isNotFound()); + } + + @Test + void deleteEndpoint_notAuth() throws Exception { + String jwt = TestJwtCreator.generate("ROLE_NO_ACCESS"); + mvc.perform( + delete(FULL_NONCONTEXT_PATH) + .header(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE) + .header("Authorization", "Bearer " + jwt)) + .andExpect(status().isForbidden()); + } + + @Test + void patchEndpoint() throws Exception { + String jwt = TestJwtCreator.generate("ROLE_ADMIN3"); + mvc.perform( + patch(FULL_NONCONTEXT_PATH) + .content("{}") + .header(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE) + .header("Authorization", "Bearer " + jwt)) + .andExpect(status().isNotFound()); + } + + @Test + void patchEndpoint_notAuth() throws Exception { + String jwt = TestJwtCreator.generate("ROLE_NO_ACCESS"); + mvc.perform( + patch(FULL_NONCONTEXT_PATH) + .content("{}") + .header(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE) + .header("Authorization", "Bearer " + jwt)) + .andExpect(status().isForbidden()); + } + + @Test + void putEndpoint() throws Exception { + String jwt = TestJwtCreator.generate("ROLE_ADMIN5"); + mvc.perform( + put(FULL_NONCONTEXT_PATH) + .content("{}") + .header(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE) + .header("Authorization", "Bearer " + jwt)) + .andExpect(status().isNotFound()); + } + + @Test + void putEndpoint_notAuth() throws Exception { + String jwt = TestJwtCreator.generate("ROLE_NO_ACCESS"); + mvc.perform( + put(FULL_NONCONTEXT_PATH) + .content("{}") + .header(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE) + .header("Authorization", "Bearer " + jwt)) + .andExpect(status().isForbidden()); + } +} diff --git a/FlySpring/edgechain-app/src/test/java/com/edgechain/pinecone/PineconeClientTest.java b/FlySpring/edgechain-app/src/test/java/com/edgechain/pinecone/PineconeClientTest.java index 0f49fb8d6..60ca66f8f 100644 --- a/FlySpring/edgechain-app/src/test/java/com/edgechain/pinecone/PineconeClientTest.java +++ b/FlySpring/edgechain-app/src/test/java/com/edgechain/pinecone/PineconeClientTest.java @@ -1,6 +1,52 @@ package com.edgechain.pinecone; +import com.edgechain.lib.embeddings.WordEmbeddings; +import com.edgechain.lib.endpoint.impl.PineconeEndpoint; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.boot.test.web.server.LocalServerPort; -@SpringBootTest -public class PineconeClientTest {} +import java.util.Collections; +import java.util.List; + +import static org.junit.jupiter.api.Assertions.*; + +@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT) +public class PineconeClientTest { + Logger logger = LoggerFactory.getLogger(getClass()); + + @LocalServerPort private int port; + + @BeforeEach + void setUp() { + System.setProperty("server.port", String.valueOf(port)); + } + + @Test + @DisplayName("Test Pineconee namespace") + void pineconeControllerTest_TestPineconeNamespace_ReturnedExpecctedValue(){ + PineconeEndpoint pineconeEndpoint = new PineconeEndpoint(); + pineconeEndpoint.setNamespace("machine-learning"); + String namespace = pineconeEndpoint.getNamespace(); + logger.info(namespace); + + assertNotNull(namespace); + assertEquals("machine-learning", namespace); + } + + @Test + void test_pineconeEndpoint(){ + + PineconeEndpoint pineconeEndpoint = new PineconeEndpoint(); + List wordEmbeddingsList = Collections.singletonList(new WordEmbeddings("word", String.valueOf(0.9f))); + pineconeEndpoint.setTopK(5); + pineconeEndpoint.setWordEmbeddings(wordEmbeddingsList.get(0)); + logger.info("pineconeEndpoint {}", pineconeEndpoint); + String endpointUrl = pineconeEndpoint.getUrl(); + assertNull(endpointUrl); + } +} diff --git a/FlySpring/edgechain-app/src/test/java/com/edgechain/reactChain/ReactChainTest.java b/FlySpring/edgechain-app/src/test/java/com/edgechain/reactChain/ReactChainTest.java index fc8de64ab..e351d92d0 100644 --- a/FlySpring/edgechain-app/src/test/java/com/edgechain/reactChain/ReactChainTest.java +++ b/FlySpring/edgechain-app/src/test/java/com/edgechain/reactChain/ReactChainTest.java @@ -1,6 +1,7 @@ package com.edgechain.reactChain; import com.edgechain.lib.jsonnet.JsonnetLoader; +import com.edgechain.lib.jsonnet.exceptions.JsonnetLoaderException; import com.edgechain.lib.jsonnet.impl.FileJsonnetLoader; import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; @@ -9,53 +10,53 @@ import java.io.ByteArrayInputStream; import java.io.InputStream; -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.Assert.assertNull; +import static org.junit.jupiter.api.Assertions.*; @SpringBootTest public class ReactChainTest { - @Test - @DisplayName("Test extractAction method for Reach Chain") - public void test_extractAction_fromJsonnet() throws Exception { - String inputJsonnet = - "local extractAction(str) =\n" - + " local action = xtr.strings.substringBefore(xtr.strings.substringAfter(str," - + " \"[\"), \"]\");\n" - + " action;\n" - + "{ \"action\": extractAction(\"Thought 1: I need to search ABC and XYZ, and find" - + " which was started first.Action 1: Search[ABC]\") }"; - InputStream inputStream = new ByteArrayInputStream(inputJsonnet.getBytes()); - JsonnetLoader jsonnetLoader = new FileJsonnetLoader(); - jsonnetLoader.load(inputStream); - String action = jsonnetLoader.get("action"); - assertNotNull(action); - assertEquals(action, "ABC"); - } + @Test + @DisplayName("Test extractAction method for Reach Chain") + public void test_extractAction_fromJsonnet() throws Exception { + String inputJsonnet = + "local extractAction(str) =\n" + + " local action = xtr.strings.substringBefore(xtr.strings.substringAfter(str," + + " \"[\"), \"]\");\n" + + " action;\n" + + "{ \"action\": extractAction(\"Thought 1: I need to search ABC and XYZ, and find" + + " which was started first.Action 1: Search[ABC]\") }"; + InputStream inputStream = new ByteArrayInputStream(inputJsonnet.getBytes()); + JsonnetLoader jsonnetLoader = new FileJsonnetLoader(); + jsonnetLoader.load(inputStream); + String action = jsonnetLoader.get("action"); + assertNotNull(action); + assertEquals(action, "ABC"); + } - @Test - @DisplayName("Test extractThought method for Reach Chain") - public void test_extractThought_fromJsonnet() throws Exception { - String inputJsonnet = - "local extractThought(str) =\n" - + " local thought = xtr.strings.substringAfter(xtr.strings.substringBefore(str," - + " \"Action\"), \":\");\n" - + " thought;\n" - + "{ \"thought\": extractThought(\"Thought 1:I need to search ABC and XYZ, and find" - + " which was started first.Action 1: Search[ABC]\") }"; - InputStream inputStream = new ByteArrayInputStream(inputJsonnet.getBytes()); - JsonnetLoader jsonnetLoader = new FileJsonnetLoader(); - jsonnetLoader.load(inputStream); - String thought = jsonnetLoader.get("thought"); - assertNotNull(thought); - assertEquals(thought, "I need to search ABC and XYZ, and find which was started first."); - } + @Test + @DisplayName("Test extractThought method for Reach Chain") + public void test_extractThought_fromJsonnet() throws Exception { + String inputJsonnet = + "local extractThought(str) =\n" + + " local thought = xtr.strings.substringAfter(xtr.strings.substringBefore(str," + + " \"Action\"), \":\");\n" + + " thought;\n" + + "{ \"thought\": extractThought(\"Thought 1:I need to search ABC and XYZ, and find" + + " which was started first.Action 1: Search[ABC]\") }"; + InputStream inputStream = new ByteArrayInputStream(inputJsonnet.getBytes()); + JsonnetLoader jsonnetLoader = new FileJsonnetLoader(); + jsonnetLoader.load(inputStream); + String thought = jsonnetLoader.get("thought"); + assertNotNull(thought); + assertEquals(thought, "I need to search ABC and XYZ, and find which was started first."); + } - @Test - @DisplayName("Mapper search function test") - public void test_mapper() { - String inputJsonnet = - """ + @Test + @DisplayName("Mapper search function test") + public void test_mapper() { + String inputJsonnet = + """ local config = { "edgechains.config": { "mapper": { @@ -69,11 +70,55 @@ local callFunction(funcName) = local searchFunction = callFunction("search"); { "searchFunction": searchFunction } """; - InputStream inputStream = new ByteArrayInputStream(inputJsonnet.getBytes()); - JsonnetLoader jsonnetLoader = new FileJsonnetLoader(); - jsonnetLoader.load(inputStream); - String searchFunction = jsonnetLoader.get("searchFunction"); - assertNotNull(searchFunction); - assertEquals(searchFunction, "udf.fn"); - } -} + InputStream inputStream = new ByteArrayInputStream(inputJsonnet.getBytes()); + JsonnetLoader jsonnetLoader = new FileJsonnetLoader(); + jsonnetLoader.load(inputStream); + String searchFunction = jsonnetLoader.get("searchFunction"); + assertNotNull(searchFunction); + assertEquals(searchFunction, "udf.fn"); + } + + @Test + @DisplayName("Test extractAction with invalid input") + void test_extractAction_WithInvalidJsonnet() throws Exception { + String inputJsonnet = "This is invalid jsonnet."; + InputStream inputStream = new ByteArrayInputStream(inputJsonnet.getBytes()); + JsonnetLoader jsonnetLoader = new FileJsonnetLoader(); + assertThrows(Exception.class, () -> jsonnetLoader.load(inputStream)); + } + + @Test + @DisplayName("Test extractAction with empty input") + void test_extractAction_withEmptyJsonnet() throws Exception { + String inputJsonnet = ""; + InputStream inputStream = new ByteArrayInputStream(inputJsonnet.getBytes()); + JsonnetLoader jsonnetLoader = new FileJsonnetLoader(); + assertThrows(Exception.class, () -> jsonnetLoader.get("action")); + assertThrows(Exception.class, () -> jsonnetLoader.load(inputStream)); + } + + @Test + @DisplayName("Test extractThought - invalid input") + void test_extractThought_WithInvalidInput() { + String inputJsonnet = "This is not a valid jsonnet pattern"; + InputStream inputStream = new ByteArrayInputStream(inputJsonnet.getBytes()); + JsonnetLoader jsonnetLoader = new FileJsonnetLoader(); + assertThrows(Exception.class, () -> jsonnetLoader.load(inputStream)); + } + + @Test + @DisplayName("Test Mapper - Missing function") + public void test_mapper_MissingFunction_ReturnedExpectedResult() { + String inputJsonnet = + """ + local config = { + "edgechains.config": { + "mapper": {}, + }, + }; + """; + InputStream inputStream = new ByteArrayInputStream(inputJsonnet.getBytes()); + JsonnetLoader jsonnetLoader = new FileJsonnetLoader(); + assertThrows(JsonnetLoaderException.class, () -> jsonnetLoader.load(inputStream)); + } +} \ No newline at end of file diff --git a/FlySpring/edgechain-app/src/test/java/com/edgechain/testutil/PostgresTestContainer.java b/FlySpring/edgechain-app/src/test/java/com/edgechain/testutil/PostgresTestContainer.java index 6feab846b..b87cc2458 100644 --- a/FlySpring/edgechain-app/src/test/java/com/edgechain/testutil/PostgresTestContainer.java +++ b/FlySpring/edgechain-app/src/test/java/com/edgechain/testutil/PostgresTestContainer.java @@ -1,41 +1,41 @@ -package com.edgechain.testutil; - -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import org.testcontainers.containers.PostgreSQLContainer; -import org.testcontainers.utility.DockerImageName; - -public class PostgresTestContainer extends PostgreSQLContainer { - - public enum PostgresImage { - PLAIN, - VECTOR - }; - - private static final Logger LOGGER = LoggerFactory.getLogger(PostgresTestContainer.class); - - // private static final String DOCKER_IMAGE = PostgreSQLContainer.IMAGE + ":" + - // PostgreSQLContainer.DEFAULT_TAG; - - private static final DockerImageName IMAGE = DockerImageName.parse("postgres").withTag("14.5"); - - private static final DockerImageName VECTOR_IMAGE = - DockerImageName.parse("ankane/pgvector").asCompatibleSubstituteFor("postgres"); - - public PostgresTestContainer(PostgresImage img) { - super(img == PostgresImage.VECTOR ? VECTOR_IMAGE : IMAGE); - } - - @Override - public void start() { - LOGGER.info("starting container"); - super.start(); - LOGGER.info("TEST with Docker PostgreSQL url={}", getJdbcUrl()); - } - - @Override - public void stop() { - LOGGER.info("stopping container"); - super.stop(); - } -} +package com.edgechain.testutil; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.testcontainers.containers.PostgreSQLContainer; +import org.testcontainers.utility.DockerImageName; + +public class PostgresTestContainer extends PostgreSQLContainer { + + public enum PostgresImage { + PLAIN, + VECTOR + }; + + private static final Logger LOGGER = LoggerFactory.getLogger(PostgresTestContainer.class); + + // private static final String DOCKER_IMAGE = PostgreSQLContainer.IMAGE + ":" + + // PostgreSQLContainer.DEFAULT_TAG; + + private static final DockerImageName IMAGE = DockerImageName.parse("postgres").withTag("14.5"); + + private static final DockerImageName VECTOR_IMAGE = + DockerImageName.parse("ankane/pgvector").asCompatibleSubstituteFor("postgres"); + + public PostgresTestContainer(PostgresImage img) { + super(img == PostgresImage.VECTOR ? VECTOR_IMAGE : IMAGE); + } + + @Override + public void start() { + LOGGER.info("starting container"); + super.start(); + LOGGER.info("TEST with Docker PostgreSQL url={}", getJdbcUrl()); + } + + @Override + public void stop() { + LOGGER.info("stopping container"); + super.stop(); + } +} diff --git a/FlySpring/edgechain-app/src/test/java/com/edgechain/testutil/TestJwtCreator.java b/FlySpring/edgechain-app/src/test/java/com/edgechain/testutil/TestJwtCreator.java index 07a633fcf..7436ada3e 100644 --- a/FlySpring/edgechain-app/src/test/java/com/edgechain/testutil/TestJwtCreator.java +++ b/FlySpring/edgechain-app/src/test/java/com/edgechain/testutil/TestJwtCreator.java @@ -1,25 +1,25 @@ -package com.edgechain.testutil; - -import com.auth0.jwt.JWT; -import com.auth0.jwt.algorithms.Algorithm; -import java.util.Date; - -public final class TestJwtCreator { - - private TestJwtCreator() { - // no - } - - public static String generate(String role) { - Algorithm algo = Algorithm.HMAC256(System.getProperty("jwt.secret").getBytes()); - Date d = new Date(); - return JWT.create() - .withSubject("example JWT for testing") - .withClaim("email", "admin@machine.local") - .withClaim("role", role) - .withIssuer("edgechain-tester") - .withIssuedAt(d) - .withExpiresAt(new Date(d.getTime() + 25000)) - .sign(algo); - } -} +package com.edgechain.testutil; + +import com.auth0.jwt.JWT; +import com.auth0.jwt.algorithms.Algorithm; +import java.util.Date; + +public final class TestJwtCreator { + + private TestJwtCreator() { + // no + } + + public static String generate(String role) { + Algorithm algo = Algorithm.HMAC256(System.getProperty("jwt.secret").getBytes()); + Date d = new Date(); + return JWT.create() + .withSubject("example JWT for testing") + .withClaim("email", "admin@machine.local") + .withClaim("role", role) + .withIssuer("edgechain-tester") + .withIssuedAt(d) + .withExpiresAt(new Date(d.getTime() + 25000)) + .sign(algo); + } +} diff --git a/FlySpring/edgechain-app/src/test/resources/logback-test.xml b/FlySpring/edgechain-app/src/test/resources/logback-test.xml index c050220c4..70f8d6632 100644 --- a/FlySpring/edgechain-app/src/test/resources/logback-test.xml +++ b/FlySpring/edgechain-app/src/test/resources/logback-test.xml @@ -1,18 +1,18 @@ - - - - %d{HH:mm:ss.SSS} [%thread] %-5level %logger - %msg%n - - - - - - - - - - - - - - + + + + %d{HH:mm:ss.SSS} [%thread] %-5level %logger - %msg%n + + + + + + + + + + + + + + diff --git a/FlySpring/flyfly/src/main/resources/autoroute.jar b/FlySpring/flyfly/src/main/resources/autoroute.jar new file mode 100644 index 000000000..df972c625 Binary files /dev/null and b/FlySpring/flyfly/src/main/resources/autoroute.jar differ diff --git a/FlySpring/pom.xml b/FlySpring/pom.xml index 46befc706..5fd4ab880 100644 --- a/FlySpring/pom.xml +++ b/FlySpring/pom.xml @@ -1,102 +1,102 @@ - - - 4.0.0 - - com.flyspring - edgechain-parent - pom - 0.0.1-SNAPSHOT - EdgeChain parent - Parent POM for EdgeChain - - - 17 - 17 - 17 - - UTF-8 - UTF-8 - - 3.4.1 - 3.1.0 - - 3.1.3 - - 2.7.0 - 2.11.0 - 4.7.0 - 1.2.1 - 1.19.0 - 0.0.7 - 1.12 - 2.11.3 - - - - - - org.springframework.boot - spring-boot-dependencies - ${spring-boot.version} - pom - import - - - - commons-io - commons-io - ${commons-io.version} - - - - org.testcontainers - testcontainers-bom - ${testcontainers.version} - pom - import - - - - org.testcontainers - testcontainers - ${testcontainers.version} - - - - org.testcontainers - junit-jupiter - ${testcontainers.version} - - - - org.testcontainers - mysql - ${testcontainers.version} - - - - org.testcontainers - postgresql - ${testcontainers.version} - - - - org.testcontainers - mariadb - ${testcontainers.version} - - - - - - autoroute - flyfly - edgechain-app - - - - clean install - - + + + 4.0.0 + + com.flyspring + edgechain-parent + pom + 0.0.1-SNAPSHOT + EdgeChain parent + Parent POM for EdgeChain + + + 17 + 17 + 17 + + UTF-8 + UTF-8 + + 3.4.1 + 3.1.0 + + 3.1.3 + + 2.7.0 + 2.11.0 + 4.7.0 + 1.2.1 + 1.19.0 + 0.0.7 + 1.12 + 2.11.3 + + + + + + org.springframework.boot + spring-boot-dependencies + ${spring-boot.version} + pom + import + + + + commons-io + commons-io + ${commons-io.version} + + + + org.testcontainers + testcontainers-bom + ${testcontainers.version} + pom + import + + + + org.testcontainers + testcontainers + ${testcontainers.version} + + + + org.testcontainers + junit-jupiter + ${testcontainers.version} + + + + org.testcontainers + mysql + ${testcontainers.version} + + + + org.testcontainers + postgresql + ${testcontainers.version} + + + + org.testcontainers + mariadb + ${testcontainers.version} + + + + + + autoroute + flyfly + edgechain-app + + + + clean install + + diff --git a/FlySpring/readme.md b/FlySpring/readme.md index 02d5c69bb..3a2104965 100644 --- a/FlySpring/readme.md +++ b/FlySpring/readme.md @@ -1,35 +1,35 @@ -# flyfly CLI - -## Installation & Usage - -cd to autoroute directory - -```bash -mvn clean package -``` - -cd to flyfly directory - -```bash -mvn clean package -``` - -Now cd into Examples/starter :- flyfly is ready to roll! - -```bash -java -jar flyfly.jar -``` - -## Commands - -### run - -Runs the Spring Boot application. if the project has jpa and a database driver(connector) in the build file and there is not 'spring.datasource.url' in the application.properties. Then the CLI will start a TestContainers database and add temporary values to application.properties to allow the application to run successfully. That's if the driver is supported by the CLI and Docker is installed. -Currently supported DBs are: MySQL, Postgres, and MariaDB. - - -### format - -Format the code using Spotless. - +# flyfly CLI + +## Installation & Usage + +cd to autoroute directory + +```bash +mvn clean package +``` + +cd to flyfly directory + +```bash +mvn clean package +``` + +Now cd into Examples/starter :- flyfly is ready to roll! + +```bash +java -jar flyfly.jar +``` + +## Commands + +### run + +Runs the Spring Boot application. if the project has jpa and a database driver(connector) in the build file and there is not 'spring.datasource.url' in the application.properties. Then the CLI will start a TestContainers database and add temporary values to application.properties to allow the application to run successfully. That's if the driver is supported by the CLI and Docker is installed. +Currently supported DBs are: MySQL, Postgres, and MariaDB. + + +### format + +Format the code using Spotless. + P.S. - New examples will be added soon! \ No newline at end of file diff --git a/Script/flyfly.jar b/Script/flyfly.jar new file mode 100644 index 000000000..4675c0f1c Binary files /dev/null and b/Script/flyfly.jar differ