diff --git a/extended/src/main/antlr/apoc/custom/Signature.g4 b/extended/src/main/antlr/apoc/custom/Signature.g4 new file mode 100644 index 0000000000..7363f43ac1 --- /dev/null +++ b/extended/src/main/antlr/apoc/custom/Signature.g4 @@ -0,0 +1,30 @@ +grammar Signature; + +procedure: namespace? name '(' (parameter',')*(parameter)? ')' '::' results ; +function: namespace? name '(' (parameter',')*(parameter)? ')' '::' (type | '(' type ')'); +results: empty | '(' (result',')*(result) ')' ; +parameter: name ('=' defaultValue)? '::' type ; +result: name '::' type ; +namespace: (name'.')+ ; +name: IDENTIFIER | QUOTED_IDENTIFIER ; +empty: 'VOID' ; +type: opt_type | list_type ; +defaultValue: value; + +list_type: 'LIST''?'?' OF '+opt_type ; +opt_type: base_type'?'? ; +base_type: 'MAP' | 'ANY' | 'NODE' | 'REL' | 'RELATIONSHIP' | 'EDGE' | 'PATH' | 'NUMBER' | 'LONG' | 'INT' | 'INTEGER' | 'FLOAT' | 'DOUBLE' | 'BOOL' | 'BOOLEAN' | 'DATE' | 'TIME' | 'LOCALTIME' | 'DATETIME' | 'LOCALDATETIME' | 'DURATION' | 'POINT' | 'GEO' | 'GEOMETRY' | 'STRING' | 'TEXT' ; +NEWLINE: [\r\n]+ ; +QUOTED_IDENTIFIER: '`' [^`]+? '`' ; +IDENTIFIER: [a-zA-Z_][a-zA-Z0-9_]+ ; +WS: [ \t\r\n]+ -> skip ; +value: nullValue | INT_VALUE | FLOAT_VALUE | boolValue | mapValue | listValue | stringValue; +INT_VALUE: [0-9]+; +FLOAT_VALUE: ([0-9]+'.'[0-9]+) | 'NaN'; +boolValue: 'true'|'false'; +stringValue: QUOTED_STRING_VALUE | PLAIN_STRING_VALUE; +QUOTED_STRING_VALUE: '"'[^"]+?'"'; +PLAIN_STRING_VALUE: .+?; +nullValue: 'null'; +listValue: '[' ((value',')*value)?']'; +mapValue: '{' (((name ':' value)',')*(name ':' value) | ((name '=' value)',')*(name '=' value))? '}'; diff --git a/extended/src/main/java/apoc/Description.java b/extended/src/main/java/apoc/Description.java new file mode 100644 index 0000000000..47c7e84269 --- /dev/null +++ b/extended/src/main/java/apoc/Description.java @@ -0,0 +1,16 @@ +package apoc; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +/** + * @author mh + * @since 11.04.16 + */ +@Retention(RetentionPolicy.RUNTIME) +@Target({ElementType.METHOD,ElementType.TYPE}) +public @interface Description { + String value(); +} diff --git a/extended/src/main/java/apoc/ExtendedApocConfig.java b/extended/src/main/java/apoc/ExtendedApocConfig.java index 77c12818d9..bb7f298eb5 100644 --- a/extended/src/main/java/apoc/ExtendedApocConfig.java +++ b/extended/src/main/java/apoc/ExtendedApocConfig.java @@ -170,6 +170,14 @@ public Iterator getKeys(String prefix) { return config.getKeys(prefix); } + public boolean containsKey(String key) { + return config.containsKey(key); + } + + public boolean getBoolean(String key, boolean defaultValue) { + return getConfig().getBoolean(key, defaultValue); + } + public > T getEnumProperty(String key, Class cls, T defaultValue) { var value = config.getString(key, defaultValue.toString()).trim(); try { diff --git a/extended/src/main/java/apoc/ExtendedSystemLabels.java b/extended/src/main/java/apoc/ExtendedSystemLabels.java new file mode 100644 index 0000000000..77163ed1c8 --- /dev/null +++ b/extended/src/main/java/apoc/ExtendedSystemLabels.java @@ -0,0 +1,13 @@ +package apoc; + +import org.neo4j.graphdb.Label; + +public enum ExtendedSystemLabels implements Label +{ + ApocCypherProcedures, + ApocCypherProceduresMeta, + Procedure, + Function, + ApocUuid, + DataVirtualizationCatalog +} diff --git a/extended/src/main/java/apoc/ExtendedSystemPropertyKeys.java b/extended/src/main/java/apoc/ExtendedSystemPropertyKeys.java new file mode 100644 index 0000000000..3f69d138ed --- /dev/null +++ b/extended/src/main/java/apoc/ExtendedSystemPropertyKeys.java @@ -0,0 +1,26 @@ +package apoc; + +public enum ExtendedSystemPropertyKeys +{ + // cypher stored procedures/functions + inputs, + description, + mode, + outputs, + output, + forceSingle, + prefix, + + // triggers + selector, + params, + paused, + + // dv + data, + + // uuid handler + label, + addToSetLabel, + propertyName +} diff --git a/extended/src/main/java/apoc/TTLConfig.java b/extended/src/main/java/apoc/TTLConfig.java index f3c34790a7..1c737e7b6d 100644 --- a/extended/src/main/java/apoc/TTLConfig.java +++ b/extended/src/main/java/apoc/TTLConfig.java @@ -23,8 +23,8 @@ public Values configFor(GraphDatabaseAPI db) { String apocTTLEnabledDb = String.format(ExtendedApocConfig.APOC_TTL_ENABLED_DB, db.databaseName()); String apocTTLScheduleDb = String.format(ExtendedApocConfig.APOC_TTL_SCHEDULE_DB, db.databaseName()); String apocTTLLimitDb = String.format(ExtendedApocConfig.APOC_TTL_LIMIT_DB, db.databaseName()); - boolean enabled = apocConfig.getBoolean(ExtendedApocConfig.APOC_TTL_ENABLED, false); - boolean dbEnabled = apocConfig.getBoolean(apocTTLEnabledDb, enabled); + boolean enabled = apocConfig.getConfig().getBoolean(ExtendedApocConfig.APOC_TTL_ENABLED, false); + boolean dbEnabled = apocConfig.getConfig().getBoolean(apocTTLEnabledDb, enabled); if (dbEnabled) { long ttlSchedule = apocConfig.getInt(ExtendedApocConfig.APOC_TTL_SCHEDULE, DEFAULT_SCHEDULE); diff --git a/extended/src/main/java/apoc/config/Config.java b/extended/src/main/java/apoc/config/Config.java index 4a2de4d233..2fe662b4bc 100644 --- a/extended/src/main/java/apoc/config/Config.java +++ b/extended/src/main/java/apoc/config/Config.java @@ -3,12 +3,10 @@ import apoc.ApocConfig; import apoc.Extended; import apoc.result.MapResult; -import apoc.util.Util; import apoc.util.collection.Iterators; import org.apache.commons.configuration2.Configuration; import org.neo4j.common.DependencyResolver; -import org.neo4j.internal.kernel.api.procs.ProcedureCallContext; -import org.neo4j.internal.kernel.api.security.SecurityContext; +import org.neo4j.procedure.Admin; import org.neo4j.procedure.Context; import org.neo4j.procedure.Description; import org.neo4j.procedure.Procedure; @@ -34,27 +32,21 @@ public ConfigResult(String key, Object value) { } } - @Context - public SecurityContext securityContext; - - @Context - public ProcedureCallContext callContext; - @Context public DependencyResolver dependencyResolver; + @Admin @Description("apoc.config.list | Lists the Neo4j configuration as key,value table") @Procedure public Stream list() { - Util.checkAdmin(securityContext, callContext,"apoc.config.list"); Configuration config = dependencyResolver.resolveDependency(ApocConfig.class).getConfig(); return Iterators.stream(config.getKeys()).map(s -> new ConfigResult(s, config.getString(s))); } + @Admin @Description("apoc.config.map | Lists the Neo4j configuration as map") @Procedure public Stream map() { - Util.checkAdmin(securityContext,callContext, "apoc.config.map"); Configuration config = dependencyResolver.resolveDependency(ApocConfig.class).getConfig(); Map configMap = Iterators.stream(config.getKeys()) .collect(Collectors.toMap(s -> s, s -> config.getString(s))); diff --git a/extended/src/main/java/apoc/custom/CypherProceduresHandler.java b/extended/src/main/java/apoc/custom/CypherProceduresHandler.java index d73434ef57..0c0a78c1b2 100644 --- a/extended/src/main/java/apoc/custom/CypherProceduresHandler.java +++ b/extended/src/main/java/apoc/custom/CypherProceduresHandler.java @@ -1,7 +1,8 @@ package apoc.custom; import apoc.ApocConfig; -import apoc.SystemLabels; +import apoc.ExtendedSystemLabels; +import apoc.ExtendedSystemPropertyKeys; import apoc.SystemPropertyKeys; import apoc.util.JsonUtil; import apoc.util.Util; @@ -140,10 +141,10 @@ public Mode mode(String s) { public Stream readSignatures() { List descriptors; try (Transaction tx = systemDb.beginTx()) { - descriptors = tx.findNodes(SystemLabels.ApocCypherProcedures, SystemPropertyKeys.database.name(), api.databaseName()).stream().map(node -> { - if (node.hasLabel(SystemLabels.Procedure)) { + descriptors = tx.findNodes( ExtendedSystemLabels.ApocCypherProcedures, SystemPropertyKeys.database.name(), api.databaseName()).stream().map(node -> { + if (node.hasLabel(ExtendedSystemLabels.Procedure)) { return procedureDescriptor(node); - } else if (node.hasLabel(SystemLabels.Function)) { + } else if (node.hasLabel(ExtendedSystemLabels.Function)) { return userFunctionDescriptor(node); } else { throw new IllegalStateException("don't know what to do with systemdb node " + node); @@ -158,18 +159,18 @@ private ProcedureDescriptor procedureDescriptor(Node node) { String statement = (String) node.getProperty(SystemPropertyKeys.statement.name()); String name = (String) node.getProperty(SystemPropertyKeys.name.name()); - String description = (String) node.getProperty(SystemPropertyKeys.description.name(), null); - String[] prefix = (String[]) node.getProperty(SystemPropertyKeys.prefix.name(), new String[]{PREFIX}); + String description = (String) node.getProperty( ExtendedSystemPropertyKeys.description.name(), null); + String[] prefix = (String[]) node.getProperty(ExtendedSystemPropertyKeys.prefix.name(), new String[]{PREFIX}); - String property = (String) node.getProperty(SystemPropertyKeys.inputs.name()); + String property = (String) node.getProperty(ExtendedSystemPropertyKeys.inputs.name()); List inputs = deserializeSignatures(property); - List outputSignature = deserializeSignatures((String) node.getProperty(SystemPropertyKeys.outputs.name())); + List outputSignature = deserializeSignatures((String) node.getProperty(ExtendedSystemPropertyKeys.outputs.name())); return new ProcedureDescriptor(Signatures.createProcedureSignature( new QualifiedName(prefix, name), inputs, outputSignature, - Mode.valueOf((String) node.getProperty(SystemPropertyKeys.mode.name())), + Mode.valueOf((String) node.getProperty(ExtendedSystemPropertyKeys.mode.name())), false, null, new String[0], @@ -186,17 +187,17 @@ private UserFunctionDescriptor userFunctionDescriptor(Node node) { String statement = (String) node.getProperty(SystemPropertyKeys.statement.name()); String name = (String) node.getProperty(SystemPropertyKeys.name.name()); - String description = (String) node.getProperty(SystemPropertyKeys.description.name(), null); - String[] prefix = (String[]) node.getProperty(SystemPropertyKeys.prefix.name(), new String[]{PREFIX}); + String description = (String) node.getProperty(ExtendedSystemPropertyKeys.description.name(), null); + String[] prefix = (String[]) node.getProperty(ExtendedSystemPropertyKeys.prefix.name(), new String[]{PREFIX}); - String property = (String) node.getProperty(SystemPropertyKeys.inputs.name()); + String property = (String) node.getProperty(ExtendedSystemPropertyKeys.inputs.name()); List inputs = deserializeSignatures(property); - boolean forceSingle = (boolean) node.getProperty(SystemPropertyKeys.forceSingle.name(), false); + boolean forceSingle = (boolean) node.getProperty(ExtendedSystemPropertyKeys.forceSingle.name(), false); return new UserFunctionDescriptor(new UserFunctionSignature( new QualifiedName(prefix, name), inputs, - typeof((String) node.getProperty(SystemPropertyKeys.output.name())), + typeof((String) node.getProperty(ExtendedSystemPropertyKeys.output.name())), null, description, "apoc.custom", @@ -240,16 +241,16 @@ private T withSystemDb(Function action) { public void storeFunction(UserFunctionSignature signature, String statement, boolean forceSingle) { withSystemDb(tx -> { - Node node = Util.mergeNode(tx, SystemLabels.ApocCypherProcedures, SystemLabels.Function, + Node node = Util.mergeNode(tx, ExtendedSystemLabels.ApocCypherProcedures, ExtendedSystemLabels.Function, Pair.of(SystemPropertyKeys.database.name(), api.databaseName()), Pair.of(SystemPropertyKeys.name.name(), signature.name().name()), - Pair.of(SystemPropertyKeys.prefix.name(), signature.name().namespace()) + Pair.of(ExtendedSystemPropertyKeys.prefix.name(), signature.name().namespace()) ); - node.setProperty(SystemPropertyKeys.description.name(), signature.description().orElse(null)); + node.setProperty(ExtendedSystemPropertyKeys.description.name(), signature.description().orElse(null)); node.setProperty(SystemPropertyKeys.statement.name(), statement); - node.setProperty(SystemPropertyKeys.inputs.name(), serializeSignatures(signature.inputSignature())); - node.setProperty(SystemPropertyKeys.output.name(), signature.outputType().toString()); - node.setProperty(SystemPropertyKeys.forceSingle.name(), forceSingle); + node.setProperty(ExtendedSystemPropertyKeys.inputs.name(), serializeSignatures(signature.inputSignature())); + node.setProperty(ExtendedSystemPropertyKeys.output.name(), signature.outputType().toString()); + node.setProperty(ExtendedSystemPropertyKeys.forceSingle.name(), forceSingle); setLastUpdate(tx); registerFunction(signature, statement, forceSingle); @@ -259,16 +260,16 @@ public void storeFunction(UserFunctionSignature signature, String statement, boo public void storeProcedure(ProcedureSignature signature, String statement) { withSystemDb(tx -> { - Node node = Util.mergeNode(tx, SystemLabels.ApocCypherProcedures, SystemLabels.Procedure, + Node node = Util.mergeNode(tx, ExtendedSystemLabels.ApocCypherProcedures, ExtendedSystemLabels.Procedure, Pair.of(SystemPropertyKeys.database.name(), api.databaseName()), Pair.of(SystemPropertyKeys.name.name(), signature.name().name()), - Pair.of(SystemPropertyKeys.prefix.name(), signature.name().namespace()) + Pair.of(ExtendedSystemPropertyKeys.prefix.name(), signature.name().namespace()) ); - node.setProperty(SystemPropertyKeys.description.name(), signature.description().orElse(null)); + node.setProperty(ExtendedSystemPropertyKeys.description.name(), signature.description().orElse(null)); node.setProperty(SystemPropertyKeys.statement.name(), statement); - node.setProperty(SystemPropertyKeys.inputs.name(), serializeSignatures(signature.inputSignature())); - node.setProperty(SystemPropertyKeys.outputs.name(), serializeSignatures(signature.outputSignature())); - node.setProperty(SystemPropertyKeys.mode.name(), signature.mode().name()); + node.setProperty(ExtendedSystemPropertyKeys.inputs.name(), serializeSignatures(signature.inputSignature())); + node.setProperty(ExtendedSystemPropertyKeys.outputs.name(), serializeSignatures(signature.outputSignature())); + node.setProperty(ExtendedSystemPropertyKeys.mode.name(), signature.mode().name()); setLastUpdate(tx); registerProcedure(signature, statement); return null; @@ -305,9 +306,9 @@ public static List deserializeSignatures(String s) { } private void setLastUpdate(Transaction tx) { - Node node = tx.findNode(SystemLabels.ApocCypherProceduresMeta, SystemPropertyKeys.database.name(), api.databaseName()); + Node node = tx.findNode(ExtendedSystemLabels.ApocCypherProceduresMeta, SystemPropertyKeys.database.name(), api.databaseName()); if (node == null) { - node = tx.createNode(SystemLabels.ApocCypherProceduresMeta); + node = tx.createNode(ExtendedSystemLabels.ApocCypherProceduresMeta); node.setProperty(SystemPropertyKeys.database.name(), api.databaseName()); } node.setProperty(SystemPropertyKeys.lastUpdated.name(), System.currentTimeMillis()); @@ -315,7 +316,7 @@ private void setLastUpdate(Transaction tx) { private long getLastUpdate() { return withSystemDb( tx -> { - Node node = tx.findNode(SystemLabels.ApocCypherProceduresMeta, SystemPropertyKeys.database.name(), api.databaseName()); + Node node = tx.findNode(ExtendedSystemLabels.ApocCypherProceduresMeta, SystemPropertyKeys.database.name(), api.databaseName()); return node == null ? 0L : (long) node.getProperty(SystemPropertyKeys.lastUpdated.name()); }); } @@ -600,11 +601,11 @@ public Map params(AnyValue[] input, List fieldSi public void removeProcedure(String name) { withSystemDb(tx -> { QualifiedName qName = qualifiedName(name); - tx.findNodes(SystemLabels.ApocCypherProcedures, + tx.findNodes(ExtendedSystemLabels.ApocCypherProcedures, SystemPropertyKeys.database.name(), api.databaseName(), SystemPropertyKeys.name.name(), qName.name(), - SystemPropertyKeys.prefix.name(), qName.namespace() - ).stream().filter(n -> n.hasLabel(SystemLabels.Procedure)).forEach(node -> { + ExtendedSystemPropertyKeys.prefix.name(), qName.namespace() + ).stream().filter(n -> n.hasLabel(ExtendedSystemLabels.Procedure)).forEach(node -> { ProcedureDescriptor descriptor = procedureDescriptor(node); registerProcedure(descriptor.getSignature(), null); node.delete(); @@ -617,11 +618,11 @@ public void removeProcedure(String name) { public void removeFunction(String name) { withSystemDb(tx -> { QualifiedName qName = qualifiedName(name); - tx.findNodes(SystemLabels.ApocCypherProcedures, + tx.findNodes(ExtendedSystemLabels.ApocCypherProcedures, SystemPropertyKeys.database.name(), api.databaseName(), SystemPropertyKeys.name.name(), qName.name(), - SystemPropertyKeys.prefix.name(), qName.namespace() - ).stream().filter(n -> n.hasLabel(SystemLabels.Function)).forEach(node -> { + ExtendedSystemPropertyKeys.prefix.name(), qName.namespace() + ).stream().filter(n -> n.hasLabel(ExtendedSystemLabels.Function)).forEach(node -> { UserFunctionDescriptor descriptor = userFunctionDescriptor(node); registerFunction(descriptor.getSignature(), null, false); node.delete(); diff --git a/extended/src/main/java/apoc/cypher/CypherExtended.java b/extended/src/main/java/apoc/cypher/CypherExtended.java index 1c25587617..cf6572d9fc 100644 --- a/extended/src/main/java/apoc/cypher/CypherExtended.java +++ b/extended/src/main/java/apoc/cypher/CypherExtended.java @@ -3,6 +3,7 @@ import apoc.Extended; import apoc.Pools; import apoc.result.MapResult; +import apoc.util.CompressionAlgo; import apoc.util.FileUtils; import apoc.util.QueueBasedSpliterator; import apoc.util.Util; @@ -55,8 +56,7 @@ */ @Extended public class CypherExtended { - - public static final String COMPILED_PREFIX = "CYPHER runtime="+ Util.COMPILED; + public static final String COMPILED_PREFIX = "CYPHER runtime=interpreted"; // todo handle enterprise properly public static final int PARTITIONS = 100 * Runtime.getRuntime().availableProcessors(); public static final int MAX_BATCH = 10000; @@ -259,7 +259,7 @@ public RowResult(long row, Map result) { } private Reader readerForFile(@Name("file") String fileName) { try { - return FileUtils.readerFor(fileName); + return FileUtils.readerFor(fileName, CompressionAlgo.NONE.name()); } catch (IOException ioe) { throw new RuntimeException("Error accessing file "+fileName,ioe); } diff --git a/extended/src/main/java/apoc/dv/DataVirtualizationCatalog.java b/extended/src/main/java/apoc/dv/DataVirtualizationCatalog.java index d57ab1a17a..2081660856 100644 --- a/extended/src/main/java/apoc/dv/DataVirtualizationCatalog.java +++ b/extended/src/main/java/apoc/dv/DataVirtualizationCatalog.java @@ -90,5 +90,4 @@ public Stream queryAndLink(@Name("node") Node node, .map(r -> new VirtualPath.Builder(r.getStartNode()).push(r).build()) .map(PathResult::new); } - } diff --git a/extended/src/main/java/apoc/dv/DataVirtualizationCatalogHandler.java b/extended/src/main/java/apoc/dv/DataVirtualizationCatalogHandler.java index 796768cd2e..71b4e71a30 100644 --- a/extended/src/main/java/apoc/dv/DataVirtualizationCatalogHandler.java +++ b/extended/src/main/java/apoc/dv/DataVirtualizationCatalogHandler.java @@ -1,6 +1,7 @@ package apoc.dv; -import apoc.SystemLabels; +import apoc.ExtendedSystemLabels; +import apoc.ExtendedSystemPropertyKeys; import apoc.SystemPropertyKeys; import apoc.util.JsonUtil; import apoc.util.Util; @@ -40,17 +41,17 @@ private T withSystemDb(Function action) { public VirtualizedResource add(VirtualizedResource vr) { return withSystemDb(tx -> { - Node node = Util.mergeNode(tx, SystemLabels.DataVirtualizationCatalog, null, + Node node = Util.mergeNode(tx, ExtendedSystemLabels.DataVirtualizationCatalog, null, Pair.of(SystemPropertyKeys.database.name(), db.databaseName()), Pair.of(SystemPropertyKeys.name.name(), vr.name)); - node.setProperty(SystemPropertyKeys.data.name(), JsonUtil.writeValueAsString(vr)); + node.setProperty( ExtendedSystemPropertyKeys.data.name(), JsonUtil.writeValueAsString(vr)); return vr; }); } public VirtualizedResource get(String name) { return withSystemDb(tx -> { - final List nodes = tx.findNodes(SystemLabels.DataVirtualizationCatalog, + final List nodes = tx.findNodes(ExtendedSystemLabels.DataVirtualizationCatalog, SystemPropertyKeys.database.name(), db.databaseName(), SystemPropertyKeys.name.name(), name) .stream() @@ -60,7 +61,7 @@ public VirtualizedResource get(String name) { } try { Node node = nodes.get(0); - Map map = JsonUtil.OBJECT_MAPPER.readValue(node.getProperty(SystemPropertyKeys.data.name()).toString(), Map.class); + Map map = JsonUtil.OBJECT_MAPPER.readValue(node.getProperty(ExtendedSystemPropertyKeys.data.name()).toString(), Map.class); return VirtualizedResource.from(name, map); } catch (JsonProcessingException e) { throw new RuntimeException(e); @@ -70,7 +71,7 @@ public VirtualizedResource get(String name) { public Stream remove(String name) { withSystemDb(tx -> { - tx.findNodes(SystemLabels.DataVirtualizationCatalog, + tx.findNodes(ExtendedSystemLabels.DataVirtualizationCatalog, SystemPropertyKeys.database.name(), db.databaseName(), SystemPropertyKeys.name.name(), name) .stream() @@ -82,12 +83,12 @@ public Stream remove(String name) { public Stream list() { return withSystemDb(tx -> - tx.findNodes(SystemLabels.DataVirtualizationCatalog, + tx.findNodes(ExtendedSystemLabels.DataVirtualizationCatalog, SystemPropertyKeys.database.name(), db.databaseName()) .stream() .map(node -> { try { - Map map = JsonUtil.OBJECT_MAPPER.readValue(node.getProperty(SystemPropertyKeys.data.name()).toString(), Map.class); + Map map = JsonUtil.OBJECT_MAPPER.readValue(node.getProperty(ExtendedSystemPropertyKeys.data.name()).toString(), Map.class); String name = node.getProperty(SystemPropertyKeys.name.name()).toString(); return VirtualizedResource.from(name, map); } catch (JsonProcessingException e) { diff --git a/extended/src/main/java/apoc/es/ElasticSearch.java b/extended/src/main/java/apoc/es/ElasticSearch.java index c3ff4299b6..c8fe143903 100644 --- a/extended/src/main/java/apoc/es/ElasticSearch.java +++ b/extended/src/main/java/apoc/es/ElasticSearch.java @@ -138,33 +138,33 @@ protected String toQueryParams(Object query) { @Description("apoc.es.stats(host-url-Key) - elastic search statistics") public Stream stats(@Name("host") String hostOrKey) { String url = getElasticSearchUrl(hostOrKey); - return LoadJsonUtils.loadJsonStream(url + "/_stats", null, null); + return loadJsonStream(url + "/_stats", null, null); } @Procedure @Description("apoc.es.get(host-or-port,index-or-null,type-or-null,id-or-null,query-or-null,payload-or-null) yield value - perform a GET operation on elastic search") public Stream get(@Name("host") String hostOrKey, @Name("index") String index, @Name("type") String type, @Name("id") String id, @Name("query") Object query, @Name("payload") Object payload) { - return LoadJsonUtils.loadJsonStream(getQueryUrl(hostOrKey, index, type, id, query), map("content-type",contentType(payload)), toPayload(payload)); + return loadJsonStream(getQueryUrl(hostOrKey, index, type, id, query), map("content-type",contentType(payload)), toPayload(payload)); } @Procedure @Description("apoc.es.query(host-or-port,index-or-null,type-or-null,query-or-null,payload-or-null) yield value - perform a SEARCH operation on elastic search") public Stream query(@Name("host") String hostOrKey, @Name("index") String index, @Name("type") String type, @Name("query") Object query, @Name("payload") Object payload) { - return LoadJsonUtils.loadJsonStream(getSearchQueryUrl(hostOrKey, index, type, query), map("content-type",contentType(payload)), toPayload(payload)); + return loadJsonStream(getSearchQueryUrl(hostOrKey, index, type, query), map("content-type",contentType(payload)), toPayload(payload)); } @Procedure @Description("apoc.es.getRaw(host-or-port,path,payload-or-null) yield value - perform a raw GET operation on elastic search") public Stream getRaw(@Name("host") String hostOrKey, @Name("path") String suffix, @Name("payload") Object payload) { String url = getElasticSearchUrl(hostOrKey); - return LoadJsonUtils.loadJsonStream(url + "/" + suffix, map("content-type",contentType(payload)), toPayload(payload)); + return loadJsonStream(url + "/" + suffix, map("content-type",contentType(payload)), toPayload(payload)); } @Procedure @Description("apoc.es.postRaw(host-or-port,path,payload-or-null) yield value - perform a raw POST operation on elastic search") public Stream postRaw(@Name("host") String hostOrKey, @Name("path") String suffix, @Name("payload") Object payload) { String url = getElasticSearchUrl(hostOrKey); - return LoadJsonUtils.loadJsonStream(url + "/" + suffix, map("method", "POST","content-type",contentType(payload)), toPayload(payload)); + return loadJsonStream(url + "/" + suffix, map("method", "POST","content-type",contentType(payload)), toPayload(payload)); } @Procedure @@ -174,7 +174,7 @@ public Stream post(@Name("host") String hostOrKey, @Name("index") Str { payload = Collections.emptyMap(); } - return LoadJsonUtils.loadJsonStream(getQueryUrl(hostOrKey, index, type, null, query), map("method", "POST","content-type",contentType(payload)), toPayload(payload)); + return loadJsonStream(getQueryUrl(hostOrKey, index, type, null, query), map("method", "POST","content-type",contentType(payload)), toPayload(payload)); } @Procedure @@ -184,6 +184,10 @@ public Stream put(@Name("host") String hostOrKey, @Name("index") Stri { payload = Collections.emptyMap(); } - return LoadJsonUtils.loadJsonStream(getQueryUrl(hostOrKey, index, type, id, query), map("method", "PUT","content-type",contentType(payload)), toPayload(payload)); + return loadJsonStream(getQueryUrl(hostOrKey, index, type, id, query), map("method", "PUT","content-type",contentType(payload)), toPayload(payload)); + } + + private static Stream loadJsonStream(@Name("url") Object url, @Name("headers") Map headers, @Name("payload") String payload) { + return LoadJsonUtils.loadJsonStream(url, headers, payload, "", true, null, null); } } diff --git a/extended/src/main/java/apoc/gephi/Gephi.java b/extended/src/main/java/apoc/gephi/Gephi.java index 937e9960d3..d881a68507 100644 --- a/extended/src/main/java/apoc/gephi/Gephi.java +++ b/extended/src/main/java/apoc/gephi/Gephi.java @@ -3,6 +3,7 @@ import apoc.Extended; import apoc.graph.GraphsUtils; import apoc.result.ProgressInfo; +import apoc.util.ExtendedUtil; import apoc.util.JsonUtil; import apoc.util.UrlResolver; import apoc.util.Util; @@ -56,7 +57,7 @@ public Stream add(@Name("urlOrKey") String keyOrUrl, @Name("worksp propertyNames.removeAll(RESERVED); if (GraphsUtils.extract(data, nodes, rels)) { String payload = toGephiStreaming(nodes, rels, weightproperty, propertyNames.toArray(new String[propertyNames.size()])); - JsonUtil.loadJson(url,map("method","POST","Content-Type","application/json; charset=utf-8"), payload).count(); + JsonUtil.loadJson(url,map("method","POST","Content-Type","application/json; charset=utf-8"), payload, "", true, null, null).count(); return Stream.of(new ProgressInfo(url,"graph","gephi").update(nodes.size(),rels.size(),nodes.size()).done(start)); } return Stream.empty(); @@ -85,7 +86,7 @@ private Map data(Entity pc, Map> col Relationship r = (Relationship) pc; String type = r.getType().name(); Map attributes = map("label", type, "TYPE", type); - Double weight = Util.doubleValue(r,weightproperty,1.0); + Double weight = ExtendedUtil.doubleValue(r,weightproperty,1.0); attributes.putAll(map("source", idStr(r.getStartNode()), "target", idStr(r.getEndNode()), "directed", true,"weight",weight)); attributes.putAll(color(type, colors)); if (exportproperties.length > 0) attributes.putAll(r.getProperties(exportproperties)); diff --git a/extended/src/main/java/apoc/load/LoadCsv.java b/extended/src/main/java/apoc/load/LoadCsv.java index f514475b67..d736f91d33 100644 --- a/extended/src/main/java/apoc/load/LoadCsv.java +++ b/extended/src/main/java/apoc/load/LoadCsv.java @@ -3,6 +3,7 @@ import apoc.Extended; import apoc.export.util.CountingReader; import apoc.load.util.LoadCsvConfig; +import apoc.util.ExtendedUtil; import apoc.util.FileUtils; import apoc.util.Util; import com.opencsv.CSVParserBuilder; @@ -22,7 +23,7 @@ import java.util.stream.StreamSupport; import apoc.load.util.Results; -import static apoc.util.FileUtils.closeReaderSafely; +import static apoc.util.ExtendedFileUtils.closeReaderSafely; import static apoc.util.Util.cleanUrl; import static java.util.Collections.emptyList; @@ -79,6 +80,8 @@ public Stream streamCsv(@Name("url") String url, LoadCsvConfig config .onClose(() -> closeReaderSafely(reader)); } + private static final Mapping EMPTY = new Mapping("", Collections.emptyMap(), LoadCsvConfig.DEFAULT_ARRAY_SEP, false); + private String[] getHeader(CSVReader csv, LoadCsvConfig config) throws IOException, CsvValidationException { if (!config.isHasHeader()) return null; String[] headers = csv.readNext(); @@ -88,7 +91,7 @@ private String[] getHeader(CSVReader csv, LoadCsvConfig config) throws IOExcepti Map mappings = config.getMappings(); for (int i = 0; i < headers.length; i++) { String header = headers[i]; - if (ignore.contains(header) || mappings.getOrDefault(header, Mapping.EMPTY).ignore) { + if (ignore.contains(header) || mappings.getOrDefault(header, EMPTY).ignore) { headers[i] = null; } } @@ -117,7 +120,7 @@ public CSVSpliterator(CSVReader csv, String[] header, String url, long skip, lon this.nullValues = nullValues; this.results = results; this.ignoreErrors = ignoreErrors; - this.limit = Util.isSumOutOfRange(skip, limit) ? Long.MAX_VALUE : (skip + limit); + this.limit = ExtendedUtil.isSumOutOfRange(skip, limit) ? Long.MAX_VALUE : (skip + limit); lineNo = skip; while (skip-- > 0) { csv.readNext(); diff --git a/extended/src/main/java/apoc/load/LoadDirectory.java b/extended/src/main/java/apoc/load/LoadDirectory.java index 9fd0f02f58..312352c679 100644 --- a/extended/src/main/java/apoc/load/LoadDirectory.java +++ b/extended/src/main/java/apoc/load/LoadDirectory.java @@ -28,7 +28,7 @@ import static apoc.ApocConfig.apocConfig; import static apoc.load.LoadDirectoryHandler.getPathDependingOnUseNeo4jConfig; -import static apoc.util.FileUtils.getPathFromUrlString; +import static apoc.util.ExtendedFileUtils.getPathFromUrlString; import static org.neo4j.graphdb.QueryExecutionType.QueryType.READ_WRITE; import static org.neo4j.graphdb.QueryExecutionType.QueryType.WRITE; diff --git a/extended/src/main/java/apoc/load/LoadDirectoryHandler.java b/extended/src/main/java/apoc/load/LoadDirectoryHandler.java index 37c13206de..6ca6fea6aa 100644 --- a/extended/src/main/java/apoc/load/LoadDirectoryHandler.java +++ b/extended/src/main/java/apoc/load/LoadDirectoryHandler.java @@ -24,7 +24,7 @@ import java.util.concurrent.Future; import java.util.stream.Stream; -import static apoc.util.FileUtils.getPathFromUrlString; +import static apoc.util.ExtendedFileUtils.getPathFromUrlString; import static apoc.util.FileUtils.isImportUsingNeo4jConfig; import static java.nio.file.StandardWatchEventKinds.ENTRY_CREATE; import static java.nio.file.StandardWatchEventKinds.ENTRY_DELETE; diff --git a/extended/src/main/java/apoc/load/LoadXls.java b/extended/src/main/java/apoc/load/LoadXls.java index 0fff0daa1d..444ef3d4b1 100644 --- a/extended/src/main/java/apoc/load/LoadXls.java +++ b/extended/src/main/java/apoc/load/LoadXls.java @@ -27,6 +27,8 @@ import java.util.stream.StreamSupport; import static apoc.util.DateParseUtil.dateParse; +import static apoc.util.ExtendedUtil.dateFormat; +import static apoc.util.ExtendedUtil.durationParse; import static apoc.util.Util.*; import static java.util.Arrays.asList; import static java.util.Collections.emptyList; diff --git a/extended/src/main/java/apoc/load/util/JdbcUtil.java b/extended/src/main/java/apoc/load/util/JdbcUtil.java new file mode 100644 index 0000000000..d31ab168f0 --- /dev/null +++ b/extended/src/main/java/apoc/load/util/JdbcUtil.java @@ -0,0 +1,66 @@ +package apoc.load.util; + +import apoc.util.Util; + +import javax.security.auth.Subject; +import javax.security.auth.callback.Callback; +import javax.security.auth.callback.NameCallback; +import javax.security.auth.callback.PasswordCallback; +import javax.security.auth.login.LoginContext; +import java.net.URI; +import java.security.PrivilegedActionException; +import java.security.PrivilegedExceptionAction; +import java.sql.Connection; +import java.sql.DriverManager; + +public class JdbcUtil { + + private static final String KEY_NOT_FOUND_MESSAGE = "No apoc.jdbc.%s.url url specified"; + private static final String LOAD_TYPE = "jdbc"; + + private JdbcUtil() {} + + public static Connection getConnection(String jdbcUrl, LoadJdbcConfig config) throws Exception { + if(config.hasCredentials()) { + return createConnection(jdbcUrl, config.getCredentials().getUser(), config.getCredentials().getPassword()); + } else { + URI uri = new URI(jdbcUrl.substring("jdbc:".length())); + String userInfo = uri.getUserInfo(); + if (userInfo != null) { + String cleanUrl = jdbcUrl.substring(0, jdbcUrl.indexOf("://") + 3) + jdbcUrl.substring(jdbcUrl.indexOf("@") + 1); + String[] user = userInfo.split(":"); + return createConnection(cleanUrl, user[0], user[1]); + } + return DriverManager.getConnection(jdbcUrl); + } + } + + private static Connection createConnection(String jdbcUrl, String userName, String password) throws Exception { + if (jdbcUrl.contains(";auth=kerberos")) { + String client = System.getProperty("java.security.auth.login.config.client", "KerberosClient"); + LoginContext lc = new LoginContext(client, callbacks -> { + for (Callback cb : callbacks) { + if (cb instanceof NameCallback) ((NameCallback) cb).setName(userName); + if (cb instanceof PasswordCallback) ((PasswordCallback) cb).setPassword(password.toCharArray()); + } + }); + lc.login(); + Subject subject = lc.getSubject(); + try { + return Subject.doAs(subject, (PrivilegedExceptionAction) () -> DriverManager.getConnection(jdbcUrl, userName, password)); + } catch (PrivilegedActionException pae) { + throw pae.getException(); + } + } else { + return DriverManager.getConnection(jdbcUrl, userName, password); + } + } + + public static String getUrlOrKey(String urlOrKey) { + return urlOrKey.contains(":") ? urlOrKey : Util.getLoadUrlByConfigFile(LOAD_TYPE, urlOrKey, "url").orElseThrow(() -> new RuntimeException(String.format(KEY_NOT_FOUND_MESSAGE, urlOrKey))); + } + + public static String getSqlOrKey(String sqlOrKey) { + return sqlOrKey.contains(" ") ? sqlOrKey : Util.getLoadUrlByConfigFile(LOAD_TYPE, sqlOrKey, "sql").orElse("SELECT * FROM " + sqlOrKey); + } +} diff --git a/extended/src/main/java/apoc/load/util/LoadCsvConfig.java b/extended/src/main/java/apoc/load/util/LoadCsvConfig.java new file mode 100644 index 0000000000..f834484db3 --- /dev/null +++ b/extended/src/main/java/apoc/load/util/LoadCsvConfig.java @@ -0,0 +1,130 @@ +package apoc.load.util; + +import apoc.load.Mapping; +import apoc.util.CompressionConfig; +import apoc.util.Util; + +import java.util.*; + +import static apoc.util.Util.parseCharFromConfig; +import static java.util.Arrays.asList; +import static java.util.Collections.emptyList; + +public class LoadCsvConfig extends CompressionConfig { + + public static final char DEFAULT_ARRAY_SEP = ';'; + public static final char DEFAULT_SEP = ','; + public static final char DEFAULT_QUOTE_CHAR = '"'; + // this is the same value as ICSVParser.DEFAULT_ESCAPE_CHARACTER + public static final char DEFAULT_ESCAPE_CHAR = '\\'; + + private final boolean ignoreErrors; + private char separator; + private char arraySep; + private char quoteChar; + private char escapeChar; + private long skip; + private boolean hasHeader; + private long limit; + + private boolean failOnError; + private boolean ignoreQuotations; + + private EnumSet results; + + private List ignore; + private List nullValues; + private Map> mapping; + private Map mappings; + + public LoadCsvConfig(Map config) { + super(config); + if (config == null) { + config = Collections.emptyMap(); + } + ignoreErrors = Util.toBoolean(config.getOrDefault("ignoreErrors", false)); + separator = parseCharFromConfig(config, "sep", DEFAULT_SEP); + arraySep = parseCharFromConfig(config, "arraySep", DEFAULT_ARRAY_SEP); + quoteChar = parseCharFromConfig(config,"quoteChar", DEFAULT_QUOTE_CHAR); + escapeChar = parseCharFromConfig(config,"escapeChar", DEFAULT_ESCAPE_CHAR); + long skip = (long) config.getOrDefault("skip", 0L); + this.skip = skip > -1 ? skip : 0L; + hasHeader = (boolean) config.getOrDefault("header", true); + limit = (long) config.getOrDefault("limit", Long.MAX_VALUE); + failOnError = (boolean) config.getOrDefault("failOnError", true); + ignoreQuotations = (boolean) config.getOrDefault("ignoreQuotations", false); + + results = EnumSet.noneOf(Results.class); + List resultList = (List) config.getOrDefault("results", asList("map","list")); + for (String result : resultList) { + results.add(Results.valueOf(result)); + } + + ignore = (List) config.getOrDefault("ignore", emptyList()); + nullValues = (List) config.getOrDefault("nullValues", emptyList()); + mapping = (Map>) config.getOrDefault("mapping", Collections.emptyMap()); + mappings = createMapping(mapping, arraySep, ignore); + } + + private Map createMapping(Map> mapping, char arraySep, List ignore) { + if (mapping.isEmpty()) return Collections.emptyMap(); + HashMap result = new HashMap<>(mapping.size()); + for (Map.Entry> entry : mapping.entrySet()) { + String name = entry.getKey(); + result.put(name, new Mapping(name, entry.getValue(), arraySep, ignore.contains(name))); + } + return result; + } + + public char getSeparator() { + return separator; + } + + public long getSkip() { + return skip; + } + + public boolean isHasHeader() { + return hasHeader; + } + + public long getLimit() { + return limit; + } + + public boolean isFailOnError() { + return failOnError; + } + + public EnumSet getResults() { + return results; + } + + public List getIgnore() { + return ignore; + } + + public List getNullValues() { + return nullValues; + } + + public Map getMappings() { + return mappings; + } + + public char getQuoteChar() { + return quoteChar; + } + + public char getEscapeChar() { + return escapeChar; + } + + public boolean getIgnoreErrors() { + return ignoreErrors; + } + + public boolean isIgnoreQuotations() { + return ignoreQuotations; + } +} diff --git a/extended/src/main/java/apoc/load/util/LoadJdbcConfig.java b/extended/src/main/java/apoc/load/util/LoadJdbcConfig.java new file mode 100644 index 0000000000..64a7ed5a5f --- /dev/null +++ b/extended/src/main/java/apoc/load/util/LoadJdbcConfig.java @@ -0,0 +1,85 @@ +package apoc.load.util; + +import apoc.util.Util; +import org.apache.commons.lang3.StringUtils; + +import java.time.DateTimeException; +import java.time.ZoneId; +import java.util.Collections; +import java.util.Map; + +/** + * @author ab-Larus + * @since 03-10-18 + */ +public class LoadJdbcConfig { + + private ZoneId zoneId = null; + + private Credentials credentials; + + private final Long fetchSize; + + private final boolean autoCommit; + + public LoadJdbcConfig(Map config) { + config = config != null ? config : Collections.emptyMap(); + try { + this.zoneId = config.containsKey("timezone") ? + ZoneId.of(config.get("timezone").toString()) : null; + } catch (DateTimeException e) { + throw new IllegalArgumentException(String.format("The timezone field contains an error: %s", e.getMessage())); + } + this.credentials = config.containsKey("credentials") ? createCredentials((Map) config.get("credentials")) : null; + this.fetchSize = Util.toLong(config.getOrDefault("fetchSize", 5000L)); + this.autoCommit = Util.toBoolean(config.getOrDefault("autoCommit", false)); + } + + public ZoneId getZoneId(){ + return this.zoneId; + } + + public Credentials getCredentials() { + return this.credentials; + } + + public static Credentials createCredentials(Map credentials) { + if (!credentials.getOrDefault("user", StringUtils.EMPTY).equals(StringUtils.EMPTY) && !credentials.getOrDefault("password", StringUtils.EMPTY).equals(StringUtils.EMPTY)) { + return new Credentials(credentials.get("user"), credentials.get("password")); + } else { + throw new IllegalArgumentException("In config param credentials must be passed both user and password."); + } + } + + public static class Credentials { + private String user; + + private String password; + + public Credentials(String user, String password){ + this.user = user; + + this.password = password; + } + + public String getUser() { + return user; + } + + public String getPassword() { + return password; + } + } + + public boolean hasCredentials() { + return this.credentials != null; + } + + public Long getFetchSize() { + return fetchSize; + } + + public boolean isAutoCommit() { + return autoCommit; + } +} \ No newline at end of file diff --git a/extended/src/main/java/apoc/metrics/Metrics.java b/extended/src/main/java/apoc/metrics/Metrics.java index a854b27bd3..8ed1488486 100644 --- a/extended/src/main/java/apoc/metrics/Metrics.java +++ b/extended/src/main/java/apoc/metrics/Metrics.java @@ -6,6 +6,7 @@ import apoc.load.LoadCsv; import apoc.load.util.LoadCsvConfig; import apoc.util.CompressionAlgo; +import apoc.util.ExtendedFileUtils; import apoc.util.FileUtils; import apoc.util.SupportedProtocols; import apoc.util.Util; @@ -21,7 +22,7 @@ import java.util.stream.Stream; import static apoc.ApocConfig.apocConfig; -import static apoc.util.FileUtils.closeReaderSafely; +import static apoc.util.ExtendedFileUtils.closeReaderSafely; /** * @author moxious @@ -112,7 +113,7 @@ public Neo4jMeasuredMetric(String name, long lastUpdated) { @Procedure(mode=Mode.DBMS) @Description("apoc.metrics.list() - get a list of available metrics") public Stream list() { - File metricsDir = FileUtils.getMetricsDirectory(); + File metricsDir = ExtendedFileUtils.getMetricsDirectory(); final FilenameFilter filter = (dir, name) -> name.toLowerCase().endsWith(".csv"); return Arrays.asList(metricsDir.listFiles(filter)) @@ -163,7 +164,7 @@ public Stream loadCsvForMetric(String metricName, Map storage(@Name("directorySetting") String directoryS // Permit case-insensitive checks. String input = directorySetting == null ? null : directorySetting.toLowerCase(); - boolean validSetting = input == null || FileUtils.NEO4J_DIRECTORY_CONFIGURATION_SETTING_NAMES.contains(input); + boolean validSetting = input == null || ExtendedFileUtils.NEO4J_DIRECTORY_CONFIGURATION_SETTING_NAMES.contains(input); if (!validSetting) { - String validOptions = String.join(", ", FileUtils.NEO4J_DIRECTORY_CONFIGURATION_SETTING_NAMES); + String validOptions = String.join(", ", ExtendedFileUtils.NEO4J_DIRECTORY_CONFIGURATION_SETTING_NAMES); throw new RuntimeException("Invalid directory setting specified. Valid options are one of: " + validOptions); } - return FileUtils.NEO4J_DIRECTORY_CONFIGURATION_SETTING_NAMES.stream() + return ExtendedFileUtils.NEO4J_DIRECTORY_CONFIGURATION_SETTING_NAMES.stream() // If user specified a particular one, immediately cut list to just that one. .filter(dirSetting -> (input == null || input.equals(dirSetting))) .map(StoragePair::fromDirectorySetting) diff --git a/extended/src/main/java/apoc/result/BooleanResult.java b/extended/src/main/java/apoc/result/BooleanResult.java new file mode 100644 index 0000000000..bee285d318 --- /dev/null +++ b/extended/src/main/java/apoc/result/BooleanResult.java @@ -0,0 +1,15 @@ +package apoc.result; + +/** + * @author mh + * @since 15.03.16 + */ +public class BooleanResult { + public static final BooleanResult TRUE = new BooleanResult(true); + public static final BooleanResult FALSE = new BooleanResult(false); + public final Boolean value; + + public BooleanResult(Boolean value) { + this.value = value; + } +} diff --git a/extended/src/main/java/apoc/result/IdsResult.java b/extended/src/main/java/apoc/result/IdsResult.java new file mode 100644 index 0000000000..9315774bf0 --- /dev/null +++ b/extended/src/main/java/apoc/result/IdsResult.java @@ -0,0 +1,20 @@ +package apoc.result; + +public class IdsResult { + + public long nodeIds; + + public long relIds; + + public long propIds; + + public long relTypeIds; + + public IdsResult(long nodeIds, long relIds, long propIds, long relTypeIds) { + this.nodeIds = nodeIds; + this.relIds = relIds; + this.propIds = propIds; + this.relTypeIds = relTypeIds; + } + +} diff --git a/extended/src/main/java/apoc/result/KernelInfoResult.java b/extended/src/main/java/apoc/result/KernelInfoResult.java new file mode 100644 index 0000000000..a982ea0a71 --- /dev/null +++ b/extended/src/main/java/apoc/result/KernelInfoResult.java @@ -0,0 +1,42 @@ +package apoc.result; + +import java.text.SimpleDateFormat; +import java.util.Date; + +public class KernelInfoResult { + + public Boolean readOnly; + + public String kernelVersion; + + public String storeId; + + public String kernelStartTime; + + public String databaseName; + + public String storeLogVersion; + + public String storeCreationDate; + + public KernelInfoResult( + Boolean readOnly, + String kernelVersion, + String storeId, + Date kernelStartTime, + String databaseName, + String storeLogVersion, + Date storeCreationDate) { + + SimpleDateFormat format = new SimpleDateFormat(apoc.date.DateUtils.DEFAULT_FORMAT); + + this.readOnly = readOnly; + this.kernelVersion = kernelVersion; + this.storeId = storeId; + this.kernelStartTime = format.format(kernelStartTime); + this.databaseName = databaseName; + this.storeLogVersion = storeLogVersion; + this.storeCreationDate = format.format(storeCreationDate); + } + +} diff --git a/extended/src/main/java/apoc/result/KeyValueResult.java b/extended/src/main/java/apoc/result/KeyValueResult.java new file mode 100644 index 0000000000..1a110dc9cc --- /dev/null +++ b/extended/src/main/java/apoc/result/KeyValueResult.java @@ -0,0 +1,15 @@ +package apoc.result; + +/** + * @author mh + * @since 26.02.16 + */ +public class KeyValueResult { + public final String key; + public final Object value; + + public KeyValueResult(String key, Object value) { + this.key = key; + this.value = value; + } +} diff --git a/extended/src/main/java/apoc/result/NodeValueErrorMapResult.java b/extended/src/main/java/apoc/result/NodeValueErrorMapResult.java new file mode 100644 index 0000000000..8d2fb1be51 --- /dev/null +++ b/extended/src/main/java/apoc/result/NodeValueErrorMapResult.java @@ -0,0 +1,27 @@ +package apoc.result; + +import org.neo4j.graphdb.Node; + +import java.util.Collections; +import java.util.Map; + +public class NodeValueErrorMapResult { + public final Node node; + public final Map value; + public final Map error; + + public NodeValueErrorMapResult(Node node, Map value, Map error) { + this.node = node; + this.value = value; + this.error = error; + } + + public static NodeValueErrorMapResult withError(Node node, Map error) { + return new NodeValueErrorMapResult(node, Collections.emptyMap(), error); + } + + public static NodeValueErrorMapResult withResult(Node node, Map value) { + return new NodeValueErrorMapResult(node, value, Collections.emptyMap()); + } + +} diff --git a/extended/src/main/java/apoc/result/NodeWithMapResult.java b/extended/src/main/java/apoc/result/NodeWithMapResult.java new file mode 100644 index 0000000000..01e7e609f8 --- /dev/null +++ b/extended/src/main/java/apoc/result/NodeWithMapResult.java @@ -0,0 +1,27 @@ +package apoc.result; + +import org.neo4j.graphdb.Node; + +import java.util.Collections; +import java.util.Map; + +public class NodeWithMapResult { + public final Node node; + public final Map value; + public final Map error; + + public NodeWithMapResult(Node node, Map value, Map error) { + this.node = node; + this.value = value; + this.error = error; + } + + public static NodeWithMapResult withError(Node node, Map error) { + return new NodeWithMapResult(node, Collections.emptyMap(), error); + } + + public static NodeWithMapResult withResult(Node node, Map value) { + return new NodeWithMapResult(node, value, Collections.emptyMap()); + } + +} diff --git a/extended/src/main/java/apoc/result/StoreInfoResult.java b/extended/src/main/java/apoc/result/StoreInfoResult.java new file mode 100644 index 0000000000..d0c107d640 --- /dev/null +++ b/extended/src/main/java/apoc/result/StoreInfoResult.java @@ -0,0 +1,37 @@ +package apoc.result; + +public class StoreInfoResult { + + public long logSize; + + public long stringStoreSize; + + public long arrayStoreSize; + + public long relStoreSize; + + public long propStoreSize; + + public long totalStoreSize; + + public long nodeStoreSize; + + public StoreInfoResult( + long logSize, + long stringStoreSize, + long arrayStoreSize, + long relStoreSize, + long propStoreSize, + long totalStoreSize, + long nodeStoreSize + ) { + this.logSize = logSize; + this.stringStoreSize = stringStoreSize; + this.arrayStoreSize = arrayStoreSize; + this.relStoreSize = relStoreSize; + this.propStoreSize = propStoreSize; + this.totalStoreSize = totalStoreSize; + this.nodeStoreSize = nodeStoreSize; + } + +} diff --git a/extended/src/main/java/apoc/result/StringResult.java b/extended/src/main/java/apoc/result/StringResult.java new file mode 100644 index 0000000000..2d94a6e147 --- /dev/null +++ b/extended/src/main/java/apoc/result/StringResult.java @@ -0,0 +1,15 @@ +package apoc.result; + +/** + * @author mh + * @since 26.02.16 + */ +public class StringResult { + public final static StringResult EMPTY = new StringResult(null); + + public final String value; + + public StringResult(String value) { + this.value = value; + } +} diff --git a/extended/src/main/java/apoc/result/TransactionInfoResult.java b/extended/src/main/java/apoc/result/TransactionInfoResult.java new file mode 100644 index 0000000000..d5ef44aab0 --- /dev/null +++ b/extended/src/main/java/apoc/result/TransactionInfoResult.java @@ -0,0 +1,33 @@ +package apoc.result; + +public class TransactionInfoResult { + + public long rolledBackTx; + + public long peakTx; + + public long lastTxId; + + public long currentOpenedTx; + + public long totalOpenedTx; + + public long totalTx; + + public TransactionInfoResult( + long rolledBackTx, + long peakTx, + long lastTxId, + long currentOpenedTx, + long totalOpenedTx, + long totalTx + ) { + this.rolledBackTx = rolledBackTx; + this.peakTx = peakTx; + this.lastTxId = lastTxId; + this.currentOpenedTx = currentOpenedTx; + this.totalOpenedTx = totalOpenedTx; + this.totalTx = totalTx; + } + +} diff --git a/extended/src/main/java/apoc/systemdb/SystemDb.java b/extended/src/main/java/apoc/systemdb/SystemDb.java index 6a74b32bc5..ea88830aac 100644 --- a/extended/src/main/java/apoc/systemdb/SystemDb.java +++ b/extended/src/main/java/apoc/systemdb/SystemDb.java @@ -5,13 +5,13 @@ import apoc.Extended; import apoc.export.cypher.ExportFileManager; import apoc.export.cypher.FileManagerFactory; +import apoc.export.util.ExportConfig; import apoc.export.util.ProgressReporter; import apoc.result.ProgressInfo; import apoc.result.RowResult; import apoc.result.VirtualNode; import apoc.result.VirtualRelationship; import apoc.systemdb.metadata.ExportMetadata; -import apoc.util.Util; import apoc.util.collection.Iterables; import org.apache.commons.lang3.tuple.Pair; import org.neo4j.graphdb.GraphDatabaseService; @@ -19,8 +19,6 @@ import org.neo4j.graphdb.Node; import org.neo4j.graphdb.Relationship; import org.neo4j.graphdb.Transaction; -import org.neo4j.internal.kernel.api.procs.ProcedureCallContext; -import org.neo4j.internal.kernel.api.security.SecurityContext; import org.neo4j.kernel.impl.coreapi.TransactionImpl; import org.neo4j.procedure.Admin; import org.neo4j.procedure.Context; @@ -45,12 +43,6 @@ public class SystemDb { @Context public ApocConfig apocConfig; - @Context - public SecurityContext securityContext; - - @Context - public ProcedureCallContext callContext; - @Context public GraphDatabaseService db; @@ -74,7 +66,7 @@ public Stream metadata(@Name(value = "config",defaultValue = "{}") ProgressInfo progressInfo = new ProgressInfo(fileName, null, "cypher"); ProgressReporter progressReporter = new ProgressReporter(null, null, progressInfo); - ExportFileManager cypherFileManager = FileManagerFactory.createFileManager(fileName + ".cypher", true); + ExportFileManager cypherFileManager = FileManagerFactory.createFileManager(fileName + ".cypher", true, ExportConfig.EMPTY); withSystemDbTransaction(tx -> { tx.getAllNodes() .stream() @@ -100,9 +92,9 @@ public Stream metadata(@Name(value = "config",defaultValue = "{}") return progressReporter.stream(); } + @Admin @Procedure public Stream graph() { - Util.checkAdmin(securityContext, callContext,"apoc.systemdb.graph"); return withSystemDbTransaction(tx -> { Map virtualNodes = new HashMap<>(); for (Node node: tx.getAllNodes()) { @@ -120,10 +112,9 @@ public Stream graph() { }); } + @Admin @Procedure public Stream execute(@Name("DDL commands, either a string or a list of strings") Object ddlStringOrList, @Name(value="params", defaultValue = "{}") Map params) { - Util.checkAdmin(securityContext, callContext, "apoc.systemdb.execute"); - List commands; if (ddlStringOrList instanceof String) { commands = Collections.singletonList((String)ddlStringOrList); diff --git a/extended/src/main/java/apoc/systemdb/metadata/ExportDataVirtualization.java b/extended/src/main/java/apoc/systemdb/metadata/ExportDataVirtualization.java index 60b7302af1..d26f8a2e64 100644 --- a/extended/src/main/java/apoc/systemdb/metadata/ExportDataVirtualization.java +++ b/extended/src/main/java/apoc/systemdb/metadata/ExportDataVirtualization.java @@ -1,5 +1,6 @@ package apoc.systemdb.metadata; +import apoc.ExtendedSystemPropertyKeys; import apoc.SystemPropertyKeys; import apoc.export.util.ProgressReporter; import apoc.util.JsonUtil; @@ -10,7 +11,7 @@ import java.util.List; import java.util.Map; -import static apoc.util.Util.toCypherMap; +import static apoc.util.ExtendedUtil.toCypherMap; public class ExportDataVirtualization implements ExportMetadata { @@ -18,7 +19,7 @@ public class ExportDataVirtualization implements ExportMetadata { public List> export(Node node, ProgressReporter progressReporter) { final String dvName = (String) node.getProperty(SystemPropertyKeys.name.name()); try { - final String data = toCypherMap(JsonUtil.OBJECT_MAPPER.readValue((String) node.getProperty(SystemPropertyKeys.data.name()), Map.class)); + final String data = toCypherMap(JsonUtil.OBJECT_MAPPER.readValue((String) node.getProperty( ExtendedSystemPropertyKeys.data.name()), Map.class)); final String statement = String.format("CALL apoc.dv.catalog.add('%s', %s)", dvName, data); progressReporter.nextRow(); return List.of(Pair.of(getFileName(node, Type.DataVirtualizationCatalog.name()), statement)); diff --git a/extended/src/main/java/apoc/systemdb/metadata/ExportFunction.java b/extended/src/main/java/apoc/systemdb/metadata/ExportFunction.java index 1735aa3bcd..9bc2642b64 100644 --- a/extended/src/main/java/apoc/systemdb/metadata/ExportFunction.java +++ b/extended/src/main/java/apoc/systemdb/metadata/ExportFunction.java @@ -1,5 +1,6 @@ package apoc.systemdb.metadata; +import apoc.ExtendedSystemPropertyKeys; import apoc.SystemPropertyKeys; import apoc.custom.CypherProceduresHandler; import apoc.export.util.ProgressReporter; @@ -15,18 +16,18 @@ public class ExportFunction implements ExportMetadata { @Override public List> export(Node node, ProgressReporter progressReporter) { - final String inputs = getSignature(node, SystemPropertyKeys.inputs.name()); + final String inputs = getSignature(node, ExtendedSystemPropertyKeys.inputs.name()); - final String outputName = SystemPropertyKeys.output.name(); + final String outputName = ExtendedSystemPropertyKeys.output.name(); final String outputs = node.hasProperty(outputName) ? (String) node.getProperty(outputName) - : getSignature(node, SystemPropertyKeys.outputs.name()); + : getSignature(node, ExtendedSystemPropertyKeys.outputs.name()); String statement = String.format("CALL apoc.custom.declareFunction('%s(%s) :: (%s)', '%s', %s, '%s');", node.getProperty(SystemPropertyKeys.name.name()), inputs, outputs, node.getProperty(SystemPropertyKeys.statement.name()), - node.getProperty(SystemPropertyKeys.forceSingle.name()), - node.getProperty(SystemPropertyKeys.description.name())); + node.getProperty(ExtendedSystemPropertyKeys.forceSingle.name()), + node.getProperty(ExtendedSystemPropertyKeys.description.name())); progressReporter.nextRow(); return List.of(Pair.of(getFileName(node, Type.CypherFunction.name()), statement)); } diff --git a/extended/src/main/java/apoc/systemdb/metadata/ExportMetadata.java b/extended/src/main/java/apoc/systemdb/metadata/ExportMetadata.java index 6b6a2bfb1e..e702d99e87 100644 --- a/extended/src/main/java/apoc/systemdb/metadata/ExportMetadata.java +++ b/extended/src/main/java/apoc/systemdb/metadata/ExportMetadata.java @@ -1,5 +1,6 @@ package apoc.systemdb.metadata; +import apoc.ExtendedSystemLabels; import apoc.SystemLabels; import apoc.SystemPropertyKeys; import apoc.export.util.ProgressReporter; @@ -34,15 +35,15 @@ public List> export(Node node, ProgressReporter progressRep public static Optional from(Label label, SystemDbConfig config) { final String name = label.name(); - if (name.equalsIgnoreCase(SystemLabels.Procedure.name())) { + if (name.equalsIgnoreCase( ExtendedSystemLabels.Procedure.name())) { return get(CypherProcedure, config); - } else if(name.equalsIgnoreCase(SystemLabels.Function.name())) { + } else if(name.equalsIgnoreCase(ExtendedSystemLabels.Function.name())) { return get(CypherFunction, config); } else if(name.equalsIgnoreCase(SystemLabels.ApocTrigger.name())) { return get(Trigger, config); - } else if(name.equalsIgnoreCase(SystemLabels.ApocUuid.name())) { + } else if(name.equalsIgnoreCase(ExtendedSystemLabels.ApocUuid.name())) { return get(Uuid, config); - } else if(name.equalsIgnoreCase(SystemLabels.DataVirtualizationCatalog.name())) { + } else if(name.equalsIgnoreCase(ExtendedSystemLabels.DataVirtualizationCatalog.name())) { return get(DataVirtualizationCatalog, config); } return Optional.empty(); diff --git a/extended/src/main/java/apoc/systemdb/metadata/ExportProcedure.java b/extended/src/main/java/apoc/systemdb/metadata/ExportProcedure.java index d4d3664d2d..ef6679b547 100644 --- a/extended/src/main/java/apoc/systemdb/metadata/ExportProcedure.java +++ b/extended/src/main/java/apoc/systemdb/metadata/ExportProcedure.java @@ -1,5 +1,6 @@ package apoc.systemdb.metadata; +import apoc.ExtendedSystemPropertyKeys; import apoc.SystemPropertyKeys; import apoc.custom.CypherProceduresHandler; import apoc.export.util.ProgressReporter; @@ -14,18 +15,18 @@ public class ExportProcedure implements ExportMetadata { @Override public List> export(Node node, ProgressReporter progressReporter) { - final String inputs = getSignature(node, SystemPropertyKeys.inputs.name()); + final String inputs = getSignature(node, ExtendedSystemPropertyKeys.inputs.name()); - final String outputName = SystemPropertyKeys.output.name(); + final String outputName = ExtendedSystemPropertyKeys.output.name(); final String outputs = node.hasProperty(outputName) ? (String) node.getProperty(outputName) - : getSignature(node, SystemPropertyKeys.outputs.name()); + : getSignature(node, ExtendedSystemPropertyKeys.outputs.name()); String statement = String.format("CALL apoc.custom.declareProcedure('%s(%s) :: (%s)', '%s', '%s', '%s');", node.getProperty(SystemPropertyKeys.name.name()), inputs, outputs, node.getProperty(SystemPropertyKeys.statement.name()), - node.getProperty(SystemPropertyKeys.mode.name()), - node.getProperty(SystemPropertyKeys.description.name())); + node.getProperty(ExtendedSystemPropertyKeys.mode.name()), + node.getProperty(ExtendedSystemPropertyKeys.description.name())); progressReporter.nextRow(); return List.of(Pair.of(getFileName(node, Type.CypherProcedure.name()), statement)); } diff --git a/extended/src/main/java/apoc/systemdb/metadata/ExportTrigger.java b/extended/src/main/java/apoc/systemdb/metadata/ExportTrigger.java index b2f47858ed..cf4b7df153 100644 --- a/extended/src/main/java/apoc/systemdb/metadata/ExportTrigger.java +++ b/extended/src/main/java/apoc/systemdb/metadata/ExportTrigger.java @@ -10,7 +10,7 @@ import java.util.List; import java.util.Map; -import static apoc.util.Util.toCypherMap; +import static apoc.util.ExtendedUtil.toCypherMap; public class ExportTrigger implements ExportMetadata { diff --git a/extended/src/main/java/apoc/systemdb/metadata/ExportUuid.java b/extended/src/main/java/apoc/systemdb/metadata/ExportUuid.java index efa0b2a34e..40631fb2d8 100644 --- a/extended/src/main/java/apoc/systemdb/metadata/ExportUuid.java +++ b/extended/src/main/java/apoc/systemdb/metadata/ExportUuid.java @@ -1,6 +1,6 @@ package apoc.systemdb.metadata; -import apoc.SystemPropertyKeys; +import apoc.ExtendedSystemPropertyKeys; import apoc.export.util.ProgressReporter; import org.apache.commons.lang3.tuple.Pair; import org.neo4j.graphdb.Node; @@ -9,16 +9,16 @@ import java.util.List; import java.util.Map; -import static apoc.util.Util.toCypherMap; +import static apoc.util.ExtendedUtil.toCypherMap; public class ExportUuid implements ExportMetadata { @Override public List> export(Node node, ProgressReporter progressReporter) { Map map = new HashMap<>(); - final String labelName = (String) node.getProperty(SystemPropertyKeys.label.name()); - final String property = (String) node.getProperty(SystemPropertyKeys.propertyName.name()); - map.put("addToSetLabels", node.getProperty(SystemPropertyKeys.addToSetLabel.name(), null)); + final String labelName = (String) node.getProperty( ExtendedSystemPropertyKeys.label.name()); + final String property = (String) node.getProperty(ExtendedSystemPropertyKeys.propertyName.name()); + map.put("addToSetLabels", node.getProperty(ExtendedSystemPropertyKeys.addToSetLabel.name(), null)); map.put("uuidProperty", property); final String uuidConfig = toCypherMap(map); // add constraint - TODO: might be worth add config to export or not this file diff --git a/extended/src/main/java/apoc/util/ExtendedFileUtils.java b/extended/src/main/java/apoc/util/ExtendedFileUtils.java new file mode 100644 index 0000000000..417dc69fea --- /dev/null +++ b/extended/src/main/java/apoc/util/ExtendedFileUtils.java @@ -0,0 +1,62 @@ +package apoc.util; + +import static apoc.ApocConfig.apocConfig; + +import apoc.export.util.CountingReader; +import java.io.File; +import java.io.IOException; +import java.net.URI; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.util.Arrays; +import java.util.List; +import org.neo4j.configuration.GraphDatabaseSettings; + +public class ExtendedFileUtils +{ + /** + * @return a File representing the metrics directory that is listable and readable, or null if metrics don't exist, + * aren't enabled, or aren't readable. + */ + public static File getMetricsDirectory() { + String neo4jHome = apocConfig().getString( GraphDatabaseSettings.neo4j_home.name()); + String metricsSetting = apocConfig().getString("server.directories.metrics", neo4jHome + File.separator + "metrics"); + + File metricsDir = metricsSetting.isEmpty() ? new File(neo4jHome, "metrics") : new File(metricsSetting); + + if (metricsDir.exists() && metricsDir.canRead() && metricsDir.isDirectory() ) { + return metricsDir; + } + + return null; + } + + public static Path getPathFromUrlString(String urlDir) { + return Paths.get( URI.create(urlDir)); + } + + // This is the list of dbms.directories.* valid configuration items for neo4j. + // https://neo4j.com/docs/operations-manual/current/reference/configuration-settings/ + // Usually these reside under the same root but because they're separately configurable, in the worst case + // every one is on a different device. + // + // More likely, they'll be largely similar metrics. + public static final List NEO4J_DIRECTORY_CONFIGURATION_SETTING_NAMES = Arrays.asList( +// "dbms.directories.certificates", // not in 4.x version + "server.directories.data", + "server.directories.import", + "server.directories.lib", + "server.directories.logs", +// "server.directories.metrics", // metrics is only in EE + "server.directories.plugins", + "server.directories.run", + "server.directories.transaction.logs.root", // in Neo4j 5.0 GraphDatabaseSettings.transaction_logs_root_path changed from tx_log to this config + "server.directories.neo4j_home" + ); + + public static void closeReaderSafely( CountingReader reader) { + if (reader != null) { + try { reader.close(); } catch ( IOException ignored) { } + } + } +} diff --git a/extended/src/main/java/apoc/util/ExtendedUtil.java b/extended/src/main/java/apoc/util/ExtendedUtil.java new file mode 100644 index 0000000000..64a6c65a55 --- /dev/null +++ b/extended/src/main/java/apoc/util/ExtendedUtil.java @@ -0,0 +1,46 @@ +package apoc.util; + +import static apoc.export.cypher.formatter.CypherFormatterUtils.formatProperties; +import static apoc.export.cypher.formatter.CypherFormatterUtils.formatToString; + +import java.math.BigInteger; +import java.time.Duration; +import java.time.temporal.TemporalAccessor; +import java.util.Map; +import java.util.stream.LongStream; +import org.neo4j.graphdb.Entity; + +public class ExtendedUtil +{ + public static String dateFormat( TemporalAccessor value, String format){ + return Util.getFormat(format).format(value); + } + + public static double doubleValue( Entity pc, String prop, Number defaultValue) { + return Util.toDouble(pc.getProperty(prop, defaultValue)); + } + + public static Duration durationParse(String value) { + return Duration.parse(value); + } + + public static boolean isSumOutOfRange(long... numbers) { + try { + sumLongs(numbers).longValueExact(); + return false; + } catch (ArithmeticException ae) { + return true; + } + } + + private static BigInteger sumLongs(long... numbers) { + return LongStream.of(numbers) + .mapToObj(BigInteger::valueOf) + .reduce(BigInteger.ZERO, (x, y) -> x.add(y)); + } + + public static String toCypherMap( Map map) { + final StringBuilder builder = formatProperties(map); + return "{" + formatToString(builder) + "}"; + } +} diff --git a/extended/src/main/java/apoc/util/UriResolver.java b/extended/src/main/java/apoc/util/UriResolver.java index 047e97fb2b..25de4806a6 100644 --- a/extended/src/main/java/apoc/util/UriResolver.java +++ b/extended/src/main/java/apoc/util/UriResolver.java @@ -8,6 +8,7 @@ import java.net.URISyntaxException; import static apoc.ApocConfig.apocConfig; +import static apoc.ExtendedApocConfig.extendedApocConfig; /** * @author AgileLARUS @@ -35,9 +36,9 @@ public AuthToken getToken() { private String getConfiguredUri(String key) { String keyUrl = "apoc." + this.prefix + "." + key + ".url"; - if (apocConfig().containsKey("apoc.bolt.url")) { + if (extendedApocConfig().containsKey("apoc.bolt.url")) { key = apocConfig().getString("apoc.bolt.url"); - } else if (apocConfig().containsKey(keyUrl)) { + } else if (extendedApocConfig().containsKey(keyUrl)) { key = apocConfig().getString(keyUrl, key); } return key; diff --git a/extended/src/main/java/apoc/util/UrlResolver.java b/extended/src/main/java/apoc/util/UrlResolver.java new file mode 100644 index 0000000000..ac112f2697 --- /dev/null +++ b/extended/src/main/java/apoc/util/UrlResolver.java @@ -0,0 +1,43 @@ +package apoc.util; + +/** + * @author mh + * @since 29.05.16 + */ +public class UrlResolver { + private final String defaultScheme; + private final int defaultPort; + private final String defaultUrl; + + public UrlResolver(String defaultScheme, String defaultHost, int defaultPort) { + this.defaultScheme = defaultScheme; + this.defaultPort = defaultPort; + this.defaultUrl = defaultScheme + "://" + defaultHost + ":" + defaultPort; + } + + public String getUrl(String prefix, String hostOrKey) { + String url = getConfiguredUrl(prefix, hostOrKey); + if (url != null) return url; + url = getConfiguredUrl(prefix, ""); + if (url != null) return url; + url = resolveHost(hostOrKey); + return url == null ? defaultUrl : url; + } + + public String getConfiguredUrl(String prefix, String key) { + String url = Util.getLoadUrlByConfigFile(prefix, key, "url") + .orElse(Util.getLoadUrlByConfigFile(prefix, key, "host") + .map(this::resolveHost) + .orElse(null)); + return url; + } + + public String resolveHost(String host) { + if (host != null) { + if (host.contains("//")) return host; + if (host.contains(":")) return defaultScheme + "://" + host; + return defaultScheme + "://" + host + ":" + defaultPort; + } + return null; + } +} diff --git a/extended/src/main/java/apoc/uuid/UuidConfig.java b/extended/src/main/java/apoc/uuid/UuidConfig.java new file mode 100644 index 0000000000..393e53c42e --- /dev/null +++ b/extended/src/main/java/apoc/uuid/UuidConfig.java @@ -0,0 +1,41 @@ +package apoc.uuid; + +import java.util.Collections; +import java.util.Map; + +import static apoc.util.Util.toBoolean; + +public class UuidConfig { + + private boolean addToExistingNodes; + private boolean addToSetLabels; + private String uuidProperty; + + private static final String DEFAULT_UUID_PROPERTY = "uuid"; + private static final boolean DEFAULT_ADD_TO_EXISTING_NODES = true; + private static final boolean DEFAULT_ADD_TO_SET_LABELS = false; + + + public UuidConfig(Map config) { + if (config == null) { + config = Collections.emptyMap(); + } + this.addToExistingNodes = toBoolean(config.getOrDefault("addToExistingNodes", DEFAULT_ADD_TO_EXISTING_NODES)); + this.addToSetLabels = toBoolean(config.getOrDefault("addToSetLabels", DEFAULT_ADD_TO_SET_LABELS)); + this.uuidProperty = config.getOrDefault("uuidProperty", DEFAULT_UUID_PROPERTY).toString(); + + } + + public boolean isAddToExistingNodes() { + return addToExistingNodes; + } + + public String getUuidProperty() { + return uuidProperty; + } + + public boolean isAddToSetLabels() { + return addToSetLabels; + } +} + diff --git a/extended/src/main/java/apoc/uuid/UuidHandler.java b/extended/src/main/java/apoc/uuid/UuidHandler.java index 4c734cdf15..8d3b671de4 100644 --- a/extended/src/main/java/apoc/uuid/UuidHandler.java +++ b/extended/src/main/java/apoc/uuid/UuidHandler.java @@ -2,7 +2,8 @@ import apoc.ApocConfig; import apoc.ExtendedApocConfig; -import apoc.SystemLabels; +import apoc.ExtendedSystemLabels; +import apoc.ExtendedSystemPropertyKeys; import apoc.SystemPropertyKeys; import apoc.util.Util; import org.apache.commons.collections4.IterableUtils; @@ -67,7 +68,7 @@ public void start() { private boolean isEnabled() { String apocUUIDEnabledDb = String.format(APOC_UUID_ENABLED_DB, this.db.databaseName()); - final boolean enabled = apocConfig.getBoolean(APOC_UUID_ENABLED, false); + final boolean enabled = apocConfig.getConfig().getBoolean(APOC_UUID_ENABLED, false); return apocConfig.getConfig().getBoolean(apocUUIDEnabledDb, enabled); } @@ -176,12 +177,12 @@ public void add(Transaction tx, String label, UuidConfig config) { configuredLabelAndPropertyNames.put(label, config); try (Transaction sysTx = apocConfig.getSystemDb().beginTx()) { - Node node = Util.mergeNode(sysTx, SystemLabels.ApocUuid, null, + Node node = Util.mergeNode(sysTx, ExtendedSystemLabels.ApocUuid, null, Pair.of(SystemPropertyKeys.database.name(), db.databaseName()), - Pair.of(SystemPropertyKeys.label.name(), label), - Pair.of(SystemPropertyKeys.propertyName.name(), propertyName) + Pair.of(ExtendedSystemPropertyKeys.label.name(), label), + Pair.of(ExtendedSystemPropertyKeys.propertyName.name(), propertyName) ); - node.setProperty(SystemPropertyKeys.addToSetLabel.name(), config.isAddToSetLabels()); + node.setProperty(ExtendedSystemPropertyKeys.addToSetLabel.name(), config.isAddToSetLabels()); sysTx.commit(); } } @@ -194,12 +195,12 @@ public Map list() { public void refresh() { configuredLabelAndPropertyNames.clear(); try (Transaction tx = apocConfig.getSystemDb().beginTx()) { - tx.findNodes(SystemLabels.ApocUuid, SystemPropertyKeys.database.name(), db.databaseName()) + tx.findNodes(ExtendedSystemLabels.ApocUuid, SystemPropertyKeys.database.name(), db.databaseName()) .forEachRemaining(node -> { final UuidConfig config = new UuidConfig(Map.of( - "uuidProperty", node.getProperty(SystemPropertyKeys.propertyName.name()), - "addToSetLabels", node.getProperty(SystemPropertyKeys.addToSetLabel.name(), false))); - configuredLabelAndPropertyNames.put((String)node.getProperty(SystemPropertyKeys.label.name()), config); + "uuidProperty", node.getProperty(ExtendedSystemPropertyKeys.propertyName.name()), + "addToSetLabels", node.getProperty(ExtendedSystemPropertyKeys.addToSetLabel.name(), false))); + configuredLabelAndPropertyNames.put((String)node.getProperty(ExtendedSystemPropertyKeys.label.name()), config); }); tx.commit(); } @@ -207,8 +208,8 @@ public void refresh() { public synchronized UuidConfig remove(String label) { try (Transaction tx = apocConfig.getSystemDb().beginTx()) { - tx.findNodes(SystemLabels.ApocUuid, SystemPropertyKeys.database.name(), db.databaseName(), - SystemPropertyKeys.label.name(), label) + tx.findNodes(ExtendedSystemLabels.ApocUuid, SystemPropertyKeys.database.name(), db.databaseName(), + ExtendedSystemPropertyKeys.label.name(), label) .forEachRemaining(node -> node.delete()); tx.commit(); } @@ -219,7 +220,7 @@ public synchronized Map removeAll() { Map retval = new HashMap<>(configuredLabelAndPropertyNames); configuredLabelAndPropertyNames.clear(); try (Transaction tx = apocConfig.getSystemDb().beginTx()) { - tx.findNodes(SystemLabels.ApocUuid, SystemPropertyKeys.database.name(), db.databaseName() ) + tx.findNodes(ExtendedSystemLabels.ApocUuid, SystemPropertyKeys.database.name(), db.databaseName() ) .forEachRemaining(node -> node.delete()); tx.commit(); } diff --git a/extended/src/test/java/apoc/TTLConfigTest.java b/extended/src/test/java/apoc/TTLConfigTest.java index 8f1b1d3506..26abef13e0 100644 --- a/extended/src/test/java/apoc/TTLConfigTest.java +++ b/extended/src/test/java/apoc/TTLConfigTest.java @@ -7,6 +7,7 @@ import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; +import org.apache.commons.configuration2.Configuration; import org.junit.Test; import org.neo4j.kernel.api.procedure.GlobalProcedures; import org.neo4j.kernel.internal.GraphDatabaseAPI; @@ -16,8 +17,11 @@ public class TTLConfigTest @Test public void ttlDisabled() { ApocConfig apocConfig = mock(ApocConfig.class); - when(apocConfig.getBoolean(ExtendedApocConfig.APOC_TTL_ENABLED)).thenReturn(false); - when(apocConfig.getBoolean("apoc.ttl.enabled.foo")).thenReturn(false); + Configuration config = mock(Configuration.class); + + when(apocConfig.getConfig()).thenReturn(config); + when(config.getBoolean(ExtendedApocConfig.APOC_TTL_ENABLED, false)).thenReturn(false); + when(config.getBoolean("apoc.ttl.enabled.foo", false)).thenReturn(false); GraphDatabaseAPI db = mock(GraphDatabaseAPI.class); when(db.databaseName()).thenReturn("foo"); @@ -30,8 +34,11 @@ public void ttlDisabled() { @Test public void ttlDisabledForOurDatabase() { ApocConfig apocConfig = mock(ApocConfig.class); - when(apocConfig.getBoolean(ExtendedApocConfig.APOC_TTL_ENABLED)).thenReturn(true); - when(apocConfig.getBoolean("apoc.ttl.enabled.foo")).thenReturn(false); + Configuration config = mock(Configuration.class); + + when(apocConfig.getConfig()).thenReturn(config); + when(config.getBoolean(ExtendedApocConfig.APOC_TTL_ENABLED, false)).thenReturn(true); + when(config.getBoolean("apoc.ttl.enabled.foo", false)).thenReturn(false); GraphDatabaseAPI db = mock(GraphDatabaseAPI.class); when(db.databaseName()).thenReturn("foo"); @@ -44,8 +51,11 @@ public void ttlDisabledForOurDatabase() { @Test public void ttlEnabledForOurDatabaseOverridesGlobalSettings() { ApocConfig apocConfig = mock(ApocConfig.class); - when(apocConfig.getBoolean(ExtendedApocConfig.APOC_TTL_ENABLED)).thenReturn(false); - when(apocConfig.getBoolean("apoc.ttl.enabled.foo", false)).thenReturn(true); + Configuration config = mock(Configuration.class); + + when(apocConfig.getConfig()).thenReturn(config); + when(config.getBoolean(ExtendedApocConfig.APOC_TTL_ENABLED, false)).thenReturn(false); + when(config.getBoolean("apoc.ttl.enabled.foo", false)).thenReturn(true); when(apocConfig.getInt(ExtendedApocConfig.APOC_TTL_SCHEDULE, DEFAULT_SCHEDULE)).thenReturn(300); when(apocConfig.getInt("apoc.ttl.schedule.foo", 300)).thenReturn(500); diff --git a/extended/src/test/java/apoc/custom/CypherProceduresClusterTest.java b/extended/src/test/java/apoc/custom/CypherProceduresClusterTest.java index 61fd5eaf38..b32e283cd8 100644 --- a/extended/src/test/java/apoc/custom/CypherProceduresClusterTest.java +++ b/extended/src/test/java/apoc/custom/CypherProceduresClusterTest.java @@ -1,5 +1,6 @@ package apoc.custom; +import apoc.util.ExtendedTestContainerUtil; import apoc.util.TestContainerUtil; import apoc.util.TestContainerUtil.ApocPackage; import apoc.util.TestcontainersCausalCluster; @@ -26,7 +27,7 @@ public class CypherProceduresClusterTest { @BeforeClass public static void setupCluster() { - cluster = TestContainerUtil.createEnterpriseCluster( + cluster = ExtendedTestContainerUtil.createEnterpriseCluster( List.of(ApocPackage.EXTENDED), 3, 1, @@ -58,7 +59,7 @@ public void shouldRecreateCustomFunctionsOnOtherClusterMembers() throws Interrup // then // we use the readTransaction in order to route the execution to the READ_REPLICA try(Session session = cluster.getDriver().session()) { - TestContainerUtil.testCallInReadTransaction(session, "return custom.answer1() as row", (row) -> assertEquals(42L, ((Map)((List)row.get("row")).get(0)).get("answer"))); + ExtendedTestContainerUtil.testCallInReadTransaction(session, "return custom.answer1() as row", (row) -> assertEquals(42L, ((Map)((List)row.get("row")).get(0)).get("answer"))); } } @@ -75,7 +76,7 @@ public void shouldUpdateCustomFunctionsOnOtherClusterMembers() throws Interrupte // then // we use the readTransaction in order to route the execution to the READ_REPLICA - TestContainerUtil.testCallInReadTransaction(cluster.getSession(), "return custom.answer2() as row", (row) -> assertEquals(52L, ((Map)((List)row.get("row")).get(0)).get("answer"))); + ExtendedTestContainerUtil.testCallInReadTransaction(cluster.getSession(), "return custom.answer2() as row", (row) -> assertEquals(52L, ((Map)((List)row.get("row")).get(0)).get("answer"))); } @Test @@ -88,7 +89,7 @@ public void shouldRegisterSimpleStatementOnOtherClusterMembers() throws Interrup TestContainerUtil.testCall(cluster.getSession(), "call custom.answerProcedure1()", (row) -> Assert.assertEquals(33L, row.get("answer"))); Thread.sleep(1000); // then - TestContainerUtil.testCallInReadTransaction(cluster.getSession(), "call custom.answerProcedure1()", (row) -> Assert.assertEquals(33L, row.get("answer"))); + ExtendedTestContainerUtil.testCallInReadTransaction(cluster.getSession(), "call custom.answerProcedure1()", (row) -> Assert.assertEquals(33L, row.get("answer"))); } @Test @@ -103,7 +104,7 @@ public void shouldUpdateSimpleStatementOnOtherClusterMembers() throws Interrupte Thread.sleep(1000); // then - TestContainerUtil.testCallInReadTransaction(cluster.getSession(), "call custom.answerProcedure2()", (row) -> Assert.assertEquals(55L, row.get("answer"))); + ExtendedTestContainerUtil.testCallInReadTransaction(cluster.getSession(), "call custom.answerProcedure2()", (row) -> Assert.assertEquals(55L, row.get("answer"))); } @Test(expected = DatabaseException.class) @@ -113,7 +114,7 @@ public void shouldRemoveProcedureOnOtherClusterMembers() throws InterruptedExcep cluster.getSession().writeTransaction(tx -> tx.run("call apoc.custom.declareProcedure('answerToRemove() :: LONG', 'RETURN 33 as answer')")); // we create a procedure Thread.sleep(1000); try { - TestContainerUtil.testCallInReadTransaction(cluster.getSession(), "call custom.answerToRemove()", (row) -> Assert.assertEquals(33L, row.get("answer"))); + ExtendedTestContainerUtil.testCallInReadTransaction(cluster.getSession(), "call custom.answerToRemove()", (row) -> Assert.assertEquals(33L, row.get("answer"))); } catch (Exception e) { fail("Exception while calling the procedure"); } @@ -125,7 +126,7 @@ public void shouldRemoveProcedureOnOtherClusterMembers() throws InterruptedExcep Thread.sleep(1000); System.out.println("waited 5000ms"); try { - TestContainerUtil.testCallInReadTransaction(cluster.getSession(), "call custom.answerToRemove()", (row) -> fail("Procedure not removed")); + ExtendedTestContainerUtil.testCallInReadTransaction(cluster.getSession(), "call custom.answerToRemove()", (row) -> fail("Procedure not removed")); } catch (DatabaseException e) { String expectedMessage = "There is no procedure with the name `custom.answerToRemove` registered for this database instance. Please ensure you've spelled the procedure name correctly and that the procedure is properly deployed."; assertEquals(expectedMessage, e.getMessage()); @@ -140,7 +141,7 @@ public void shouldRemoveFunctionOnOtherClusterMembers() throws InterruptedExcept cluster.getSession().writeTransaction(tx -> tx.run("call apoc.custom.declareFunction('answerFunctionToRemove()', 'RETURN 42 as answer')")); // we create a function Thread.sleep(1000); try { - TestContainerUtil.testCallInReadTransaction(cluster.getSession(), "return custom.answerFunctionToRemove() as row", (row) -> assertEquals(42L, ((Map)((List)row.get("row")).get(0)).get("answer"))); + ExtendedTestContainerUtil.testCallInReadTransaction(cluster.getSession(), "return custom.answerFunctionToRemove() as row", (row) -> assertEquals(42L, ((Map)((List)row.get("row")).get(0)).get("answer"))); } catch (Exception e) { fail("Exception while calling the function"); } @@ -151,7 +152,7 @@ public void shouldRemoveFunctionOnOtherClusterMembers() throws InterruptedExcept // then Thread.sleep(1000); try { - TestContainerUtil.testCallInReadTransaction(cluster.getSession(), "return custom.answerFunctionToRemove() as row", (row) -> fail("Function not removed")); + ExtendedTestContainerUtil.testCallInReadTransaction(cluster.getSession(), "return custom.answerFunctionToRemove() as row", (row) -> fail("Function not removed")); } catch (DatabaseException e) { String expectedMessage = "Unknown function 'custom.answerFunctionToRemove'"; assertEquals(expectedMessage, e.getMessage()); diff --git a/extended/src/test/java/apoc/custom/CypherProceduresTest.java b/extended/src/test/java/apoc/custom/CypherProceduresTest.java index c20d9fc459..4a9c2daf2d 100644 --- a/extended/src/test/java/apoc/custom/CypherProceduresTest.java +++ b/extended/src/test/java/apoc/custom/CypherProceduresTest.java @@ -1,7 +1,7 @@ package apoc.custom; +import apoc.ExtendedSystemLabels; import apoc.RegisterComponentFactory; -import apoc.SystemLabels; import apoc.SystemPropertyKeys; import apoc.util.StatusCodeMatcher; import apoc.util.TestUtil; @@ -440,7 +440,7 @@ public void shouldRemovalOfFunctionNodeDeactivate() { // remove the node in systemdb GraphDatabaseService systemDb = db.getManagementService().database("system"); try (Transaction tx = systemDb.beginTx()) { - Node node = tx.findNode(SystemLabels.ApocCypherProcedures, SystemPropertyKeys.name.name(), "answer"); + Node node = tx.findNode( ExtendedSystemLabels.ApocCypherProcedures, SystemPropertyKeys.name.name(), "answer"); node.delete(); tx.commit(); } diff --git a/extended/src/test/java/apoc/load/LoadGoogleCloudStorageTest.java b/extended/src/test/java/apoc/load/LoadGoogleCloudStorageTest.java index c5e4c8af92..3b1bea0bc6 100644 --- a/extended/src/test/java/apoc/load/LoadGoogleCloudStorageTest.java +++ b/extended/src/test/java/apoc/load/LoadGoogleCloudStorageTest.java @@ -1,6 +1,6 @@ package apoc.load; -import apoc.util.GoogleCloudStorageContainerExtension; +import apoc.util.s3.GoogleCloudStorageContainerExtension; import apoc.util.TestUtil; import org.junit.*; diff --git a/extended/src/test/java/apoc/load/MySQLJdbcTest.java b/extended/src/test/java/apoc/load/MySQLJdbcTest.java index 0a753046e3..74df785696 100644 --- a/extended/src/test/java/apoc/load/MySQLJdbcTest.java +++ b/extended/src/test/java/apoc/load/MySQLJdbcTest.java @@ -1,6 +1,6 @@ package apoc.load; -import apoc.util.MySQLContainerExtension; +import apoc.util.s3.MySQLContainerExtension; import apoc.util.TestUtil; import apoc.util.Util; import org.junit.AfterClass; diff --git a/extended/src/test/java/apoc/metrics/MetricsTest.java b/extended/src/test/java/apoc/metrics/MetricsTest.java index 2dabbe4e5b..9fb349862d 100644 --- a/extended/src/test/java/apoc/metrics/MetricsTest.java +++ b/extended/src/test/java/apoc/metrics/MetricsTest.java @@ -16,7 +16,7 @@ import java.util.stream.Collectors; import java.util.stream.Stream; -import static apoc.util.FileUtils.NEO4J_DIRECTORY_CONFIGURATION_SETTING_NAMES; +import static apoc.util.ExtendedFileUtils.NEO4J_DIRECTORY_CONFIGURATION_SETTING_NAMES; import static apoc.util.TestContainerUtil.*; import static apoc.util.Util.map; import static org.junit.Assert.assertTrue; diff --git a/extended/src/test/java/apoc/systemdb/SystemDbTest.java b/extended/src/test/java/apoc/systemdb/SystemDbTest.java index 6499731079..1bc52d8307 100644 --- a/extended/src/test/java/apoc/systemdb/SystemDbTest.java +++ b/extended/src/test/java/apoc/systemdb/SystemDbTest.java @@ -13,6 +13,10 @@ import apoc.util.collection.Iterables; import apoc.util.collection.Iterators; import apoc.uuid.Uuid; +import java.io.IOException; +import java.nio.charset.StandardCharsets; +import java.util.HashSet; +import org.apache.commons.io.FileUtils; import org.hamcrest.Matchers; import org.junit.Before; import org.junit.Rule; @@ -35,7 +39,6 @@ import static apoc.ApocConfig.apocConfig; import static apoc.systemdb.SystemDbConfig.FEATURES_KEY; import static apoc.systemdb.SystemDbConfig.FILENAME_KEY; -import static apoc.util.TestUtil.readFileLines; import static org.junit.Assert.assertEquals; public class SystemDbTest { @@ -164,4 +167,13 @@ public void testExportMetadata() { assertEquals(Set.of(constraintForUuid), readFileLines("custom.Uuid.schema.neo4j.cypher", directory)); assertEquals(Set.of(uuidStatement), readFileLines("custom.Uuid.neo4j.cypher", directory)); } + + private static Set readFileLines(String fileName, File directory) { + try { + final List fileLines = FileUtils.readLines(new File(directory, fileName), StandardCharsets.UTF_8); + return new HashSet<>(fileLines); + } catch ( IOException e) { + throw new RuntimeException(e); + } + } } diff --git a/extended/src/test/java/apoc/trigger/TriggerClusterTest.java b/extended/src/test/java/apoc/trigger/TriggerClusterTest.java index ae5f1a3596..2bd1905879 100644 --- a/extended/src/test/java/apoc/trigger/TriggerClusterTest.java +++ b/extended/src/test/java/apoc/trigger/TriggerClusterTest.java @@ -1,5 +1,6 @@ package apoc.trigger; +import apoc.util.ExtendedTestContainerUtil; import apoc.util.TestContainerUtil; import apoc.util.TestContainerUtil.ApocPackage; import apoc.util.TestcontainersCausalCluster; @@ -23,7 +24,7 @@ public class TriggerClusterTest { @BeforeClass public static void setupCluster() { - cluster = TestContainerUtil.createEnterpriseCluster( + cluster = ExtendedTestContainerUtil.createEnterpriseCluster( // TODO [Nacho] We cannot build core from here anymore. This needs rethinking List.of(ApocPackage.CORE, ApocPackage.EXTENDED), 3, @@ -86,7 +87,7 @@ public void testSetLabels() throws Exception { assertEquals(true, ((Node)row.get("f")).hasLabel("Person")); }); - long count = TestContainerUtil.singleResultFirstColumn(cluster.getSession(), "MATCH (f:Man) RETURN count(*) as c"); + long count = ExtendedTestContainerUtil.singleResultFirstColumn(cluster.getSession(), "MATCH (f:Man) RETURN count(*) as c"); assertEquals(1L, count); } @@ -100,7 +101,7 @@ public void testTxIdAfterAsync() throws Exception { "{phase:'afterAsync'})"); cluster.getSession().run("CREATE (:TEST {name:'x', _executed:0})"); cluster.getSession().run("CREATE (:TEST {name:'y', _executed:0})"); - org.neo4j.test.assertion.Assert.assertEventually(() -> TestContainerUtil.singleResultFirstColumn(cluster.getSession(), "MATCH p = ()-[r:GENERATED]->() RETURN count(p) AS count"), + org.neo4j.test.assertion.Assert.assertEventually(() -> ExtendedTestContainerUtil.singleResultFirstColumn(cluster.getSession(), "MATCH p = ()-[r:GENERATED]->() RETURN count(p) AS count"), (value) -> value == 2L, 30, TimeUnit.SECONDS); } } diff --git a/extended/src/test/java/apoc/ttl/TTLTest.java b/extended/src/test/java/apoc/ttl/TTLTest.java index c42fa49820..806b9b3546 100644 --- a/extended/src/test/java/apoc/ttl/TTLTest.java +++ b/extended/src/test/java/apoc/ttl/TTLTest.java @@ -3,6 +3,7 @@ import apoc.periodic.Periodic; import apoc.util.TestUtil; import apoc.util.collection.Iterators; +import java.util.Map; import org.junit.AfterClass; import org.junit.ClassRule; import org.junit.Ignore; @@ -70,7 +71,7 @@ private static boolean isNodeCountConsistent(int foo, int bar) { } private static void restartAndRegister(DbmsRule db) throws Exception { - db.restartDatabase(); + db.restartDatabase(Map.of()); TestUtil.registerProcedure(db, TTL.class, Periodic.class); } } diff --git a/extended/src/test/java/apoc/util/ExtendedTestContainerUtil.java b/extended/src/test/java/apoc/util/ExtendedTestContainerUtil.java new file mode 100644 index 0000000000..74342d2777 --- /dev/null +++ b/extended/src/test/java/apoc/util/ExtendedTestContainerUtil.java @@ -0,0 +1,22 @@ +package apoc.util; + +import java.time.Duration; +import java.util.List; +import java.util.Map; +import java.util.function.Consumer; +import org.neo4j.driver.Session; + +public class ExtendedTestContainerUtil +{ + public static TestcontainersCausalCluster createEnterpriseCluster( List apocPackages, int numOfCoreInstances, int numberOfReadReplica, Map neo4jConfig, Map envSettings) { + return TestcontainersCausalCluster.create(apocPackages, numOfCoreInstances, numberOfReadReplica, Duration.ofMinutes(4), neo4jConfig, envSettings); + } + + public static T singleResultFirstColumn(Session session, String cypher) { + return (T) session.writeTransaction(tx -> tx.run(cypher).single().fields().get(0).value().asObject()); + } + + public static void testCallInReadTransaction(Session session, String call, Consumer> consumer) { + TestContainerUtil.testCallInReadTransaction(session, call, null, consumer); + } +} diff --git a/extended/src/test/java/apoc/util/s3/GoogleCloudStorageContainerExtension.java b/extended/src/test/java/apoc/util/s3/GoogleCloudStorageContainerExtension.java new file mode 100644 index 0000000000..ca6b39a8ba --- /dev/null +++ b/extended/src/test/java/apoc/util/s3/GoogleCloudStorageContainerExtension.java @@ -0,0 +1,27 @@ +package apoc.util.s3; + +import org.testcontainers.containers.BindMode; +import org.testcontainers.containers.GenericContainer; +import org.testcontainers.containers.wait.strategy.HttpWaitStrategy; + +import static java.net.HttpURLConnection.HTTP_OK; + +public class GoogleCloudStorageContainerExtension extends GenericContainer { + + public GoogleCloudStorageContainerExtension() { + super("fsouza/fake-gcs-server:latest"); + this.withCommand("-scheme http"); + + setWaitStrategy(new HttpWaitStrategy() + .forPath("/storage/v1/b") + .forPort(4443) + .forStatusCodeMatching(response -> response == HTTP_OK)); + + addExposedPort(4443); + } + + public GoogleCloudStorageContainerExtension withMountedResourceFile(String resourceFilePath, String gcsPath) { + this.withClasspathResourceMapping(resourceFilePath, "/data" + gcsPath, BindMode.READ_ONLY); + return this; + } +} diff --git a/extended/src/test/java/apoc/util/s3/MySQLContainerExtension.java b/extended/src/test/java/apoc/util/s3/MySQLContainerExtension.java new file mode 100644 index 0000000000..81cbd95577 --- /dev/null +++ b/extended/src/test/java/apoc/util/s3/MySQLContainerExtension.java @@ -0,0 +1,23 @@ +package apoc.util.s3; + +import org.testcontainers.containers.MySQLContainer; +import org.testcontainers.containers.wait.strategy.LogMessageWaitStrategy; + +import java.time.Duration; + +public class MySQLContainerExtension extends MySQLContainer { + + public MySQLContainerExtension() { + super("mysql:5.7"); + this.withInitScript("init_mysql.sql"); + this.withUrlParam("user", "test"); + this.withUrlParam("password", "test"); + this.withUrlParam("useSSL", "false"); + + setWaitStrategy(new LogMessageWaitStrategy() + .withRegEx(".*ready for connections.") + .withStartupTimeout(Duration.ofMinutes(2))); + + addExposedPort(3306); + } +}