diff --git a/src/main/java/org/caosdb/server/CaosDBServer.java b/src/main/java/org/caosdb/server/CaosDBServer.java index 4b7c0fe11ecb0c2391e5548ed6e37ede416e1343..9f04f75e0dd44bf20e026f57a9766a32e9137492 100644 --- a/src/main/java/org/caosdb/server/CaosDBServer.java +++ b/src/main/java/org/caosdb/server/CaosDBServer.java @@ -80,6 +80,7 @@ import org.caosdb.server.resource.UserResource; import org.caosdb.server.resource.UserRolesResource; import org.caosdb.server.resource.Webinterface; import org.caosdb.server.resource.WebinterfaceBuildNumber; +import org.caosdb.server.resource.transaction.DeleteEntityResource; import org.caosdb.server.resource.transaction.EntityNamesResource; import org.caosdb.server.resource.transaction.EntityResource; import org.caosdb.server.scripting.ScriptingPermissions; @@ -692,14 +693,32 @@ public class CaosDBServer extends Application { authenticator.setNext(authorizer); protectedRouter.attach("/scripting", ScriptingResource.class); + protectedRouter.attach("/delete/entities/", DeleteEntityResource.class); + protectedRouter.attach("/delete/entities", DeleteEntityResource.class); + protectedRouter.attach("/entities/names", EntityNamesResource.class); + protectedRouter.attach("/entities", EntityResource.class); + protectedRouter.attach("/entities/", EntityResource.class); + protectedRouter + .attach("/entities/{specifier}", EntityResource.class) + .getTemplate() + .getDefaultVariable() + .setType(Variable.TYPE_URI_PATH); protectedRouter.attach("/Entities/names", EntityNamesResource.class); protectedRouter.attach("/Entity/names", EntityNamesResource.class); protectedRouter.attach("/Entities", EntityResource.class); protectedRouter.attach("/Entities/", EntityResource.class); - protectedRouter.attach("/Entities/{specifier}", EntityResource.class); + protectedRouter + .attach("/Entities/{specifier}", EntityResource.class) + .getTemplate() + .getDefaultVariable() + .setType(Variable.TYPE_URI_PATH); protectedRouter.attach("/Entity", EntityResource.class); protectedRouter.attach("/Entity/", EntityResource.class); - protectedRouter.attach("/Entity/{specifier}", EntityResource.class); + protectedRouter + .attach("/Entity/{specifier}", EntityResource.class) + .getTemplate() + .getDefaultVariable() + .setType(Variable.TYPE_URI_PATH); protectedRouter.attach("/Users", UserResource.class); protectedRouter.attach("/Users/", UserResource.class); protectedRouter.attach("/Users/{specifier}", UserResource.class); @@ -713,8 +732,16 @@ public class CaosDBServer extends Application { protectedRouter.attach("/UserRoles/{specifier}", UserRolesResource.class); protectedRouter.attach("/EntityPermissions", EntityPermissionsResource.class); protectedRouter.attach("/EntityPermissions/", EntityPermissionsResource.class); - protectedRouter.attach("/EntityPermissions/{specifier}", EntityPermissionsResource.class); - protectedRouter.attach("/Owner/{specifier}", EntityOwnerResource.class); + protectedRouter + .attach("/EntityPermissions/{specifier}", EntityPermissionsResource.class) + .getTemplate() + .getDefaultVariable() + .setType(Variable.TYPE_URI_PATH); + protectedRouter + .attach("/Owner/{specifier}", EntityOwnerResource.class) + .getTemplate() + .getDefaultVariable() + .setType(Variable.TYPE_URI_PATH); protectedRouter.attach( "/FileSystem", new Redirector(getContext(), "/FileSystem/", Redirector.MODE_CLIENT_PERMANENT)); diff --git a/src/main/java/org/caosdb/server/ServerProperties.java b/src/main/java/org/caosdb/server/ServerProperties.java index 93a0c7473be1c31565e4b159ba55e01d557687e7..0885ea75b1c0b64a129e55b471306472c38c5134 100644 --- a/src/main/java/org/caosdb/server/ServerProperties.java +++ b/src/main/java/org/caosdb/server/ServerProperties.java @@ -163,7 +163,9 @@ public class ServerProperties extends Properties implements Observable { final String basepath = System.getProperty("user.dir"); final URL url = CaosDBServer.class.getResource("/build.properties"); - serverProperties.load(url.openStream()); + if (url != null) { + serverProperties.load(url.openStream()); + } // load standard config loadConfigFile(serverProperties, new File(basepath + "/conf/core/server.conf")); diff --git a/src/main/java/org/caosdb/server/entity/StringIds.java b/src/main/java/org/caosdb/server/entity/StringIds.java index 7fd69c2f34cd22376a2d0abf7f04956a2954fbe4..2b07f0d814f4be9f238e45fff3a6109668225052 100644 --- a/src/main/java/org/caosdb/server/entity/StringIds.java +++ b/src/main/java/org/caosdb/server/entity/StringIds.java @@ -31,21 +31,43 @@ import org.caosdb.server.utils.ServerMessages; */ public class StringIds extends EntityIdRegistryStrategy { + private String prefix; + public StringIds(Transaction<?> t) { + this("", t); + } + + public StringIds(String prefix, Transaction<?> t) { super(t); + this.prefix = prefix; + } + + public String getPrefix() { + return prefix; } @Override public String generate() { - return UUID.randomUUID().toString(); + String uuid = UUID.randomUUID().toString(); + if (prefix == null) { + return uuid; + } + StringBuilder sb = new StringBuilder(prefix.length() + uuid.length()); + sb.append(prefix).append(uuid); + return sb.toString(); } @Override public boolean matchIdPattern(String id) { + if (prefix.length() > 0 && !id.startsWith(prefix)) { + // not starting with prefix + return false; + } try { - UUID.fromString(id); + UUID.fromString(id.substring(prefix.length())); return true; } catch (IllegalArgumentException e) { + // could not parse as UUID string return false; } } diff --git a/src/main/java/org/caosdb/server/entity/container/RetrieveContainer.java b/src/main/java/org/caosdb/server/entity/container/RetrieveContainer.java index 99262f56d15d90ba86fb1a9cd8fd76b90c4dbf22..9bce6c1ee1c37fc1e47ee4bc53d0be3e35978484 100644 --- a/src/main/java/org/caosdb/server/entity/container/RetrieveContainer.java +++ b/src/main/java/org/caosdb/server/entity/container/RetrieveContainer.java @@ -21,7 +21,7 @@ */ package org.caosdb.server.entity.container; -import java.util.HashMap; +import java.util.Map; import org.apache.shiro.subject.Subject; import org.caosdb.server.entity.EntityID; import org.caosdb.server.entity.RetrieveEntity; @@ -51,7 +51,7 @@ public class RetrieveContainer extends TransactionContainer { final Subject user, final Long timestamp, final String srid, - final HashMap<String, String> flags) { + final Map<String, String> flags) { super(user, timestamp, srid, flags); } diff --git a/src/main/java/org/caosdb/server/entity/container/TransactionContainer.java b/src/main/java/org/caosdb/server/entity/container/TransactionContainer.java index 049f54cd1ded0ece71c7082ddc960c4246ef0b28..3f7769e06d2f9a331d747a3f38e8bd054c76e37a 100644 --- a/src/main/java/org/caosdb/server/entity/container/TransactionContainer.java +++ b/src/main/java/org/caosdb/server/entity/container/TransactionContainer.java @@ -24,6 +24,7 @@ package org.caosdb.server.entity.container; import java.util.HashMap; import java.util.LinkedList; import java.util.List; +import java.util.Map; import org.apache.shiro.subject.Subject; import org.caosdb.server.CaosDBServer; import org.caosdb.server.database.misc.TransactionBenchmark; @@ -65,7 +66,7 @@ public class TransactionContainer extends Container<EntityInterface> final Subject user, final Long timestamp, final String srid, - final HashMap<String, String> flags) { + final Map<String, String> flags) { this(user, timestamp, srid); setFlags(flags); } @@ -96,9 +97,9 @@ public class TransactionContainer extends Container<EntityInterface> this.messages.add(m); } - private HashMap<String, String> flags = null; + private Map<String, String> flags = null; - public HashMap<String, String> getFlags() { + public Map<String, String> getFlags() { return this.flags; } @@ -167,7 +168,7 @@ public class TransactionContainer extends Container<EntityInterface> return null; } - public void setFlags(final HashMap<String, String> flags) { + public void setFlags(final Map<String, String> flags) { this.flags = flags; } diff --git a/src/main/java/org/caosdb/server/entity/wrapper/Parent.java b/src/main/java/org/caosdb/server/entity/wrapper/Parent.java index a2116ac3d0492e8c12487a5e9d6ddafc82b91df8..f4b395f3b818b2de3f7873bac187776ea7ec0d01 100644 --- a/src/main/java/org/caosdb/server/entity/wrapper/Parent.java +++ b/src/main/java/org/caosdb/server/entity/wrapper/Parent.java @@ -23,7 +23,9 @@ package org.caosdb.server.entity.wrapper; import org.caosdb.server.entity.Affiliation; +import org.caosdb.server.entity.EntityID; import org.caosdb.server.entity.EntityInterface; +import org.caosdb.server.entity.RetrieveEntity; import org.caosdb.server.entity.xml.ParentToElementStrategy; public class Parent extends EntityWrapper { @@ -35,6 +37,10 @@ public class Parent extends EntityWrapper { setToElementStragegy(new ParentToElementStrategy()); } + public Parent(EntityID id) { + this(new RetrieveEntity(id)); + } + public void setEntity(final EntityInterface entity) { super.entity = entity; } diff --git a/src/main/java/org/caosdb/server/grpc/GRPCServer.java b/src/main/java/org/caosdb/server/grpc/GRPCServer.java index 494ce94706eff75415264b175f58cdaf16a2a442..37b91cfc7020507366bbbea6bb8a07376b5f5c28 100644 --- a/src/main/java/org/caosdb/server/grpc/GRPCServer.java +++ b/src/main/java/org/caosdb/server/grpc/GRPCServer.java @@ -199,7 +199,7 @@ public class GRPCServer { boolean started = false; final String port_https_str = getServerProperty(ServerProperties.KEY_GRPC_SERVER_PORT_HTTPS); - if (port_https_str != null && !port_https_str.isEmpty()) { + if (port_https_str != null && !port_https_str.isBlank()) { final Integer port_https = Integer.parseInt(port_https_str); final Server server = instance.buildServer(port_https, true); CaosDBServer.addPreShutdownHook(new ServerStopper(server)); diff --git a/src/main/java/org/caosdb/server/jobs/core/CheckEntityId.java b/src/main/java/org/caosdb/server/jobs/core/CheckEntityId.java index 712b0d0be9da4a50303f7d24d597022934e9c803..3d2694833f0aec147c4bc38f79e40035b01459f7 100644 --- a/src/main/java/org/caosdb/server/jobs/core/CheckEntityId.java +++ b/src/main/java/org/caosdb/server/jobs/core/CheckEntityId.java @@ -18,7 +18,7 @@ public class CheckEntityId extends ContainerJob { private void checkEntityID(EntityInterface e) { try { - if (e.hasId()) { + if (e.hasId() && !e.getId().isTemporary()) { getTransaction().assertMatchIdPattern(e.getId().toString()); } } catch (Message m) { diff --git a/src/main/java/org/caosdb/server/permissions/EntityPermission.java b/src/main/java/org/caosdb/server/permissions/EntityPermission.java index 1cfe9901ad2fdf0067d7cd58712c2caa506ac73b..4b344cde2a6e365129d614252c51294020d9a3f1 100644 --- a/src/main/java/org/caosdb/server/permissions/EntityPermission.java +++ b/src/main/java/org/caosdb/server/permissions/EntityPermission.java @@ -100,7 +100,7 @@ public class EntityPermission extends Permission { return p; } } - throw new IllegalArgumentException("Permission is not defined."); + throw new IllegalArgumentException("Permission is not defined: " + name); } public static EntityPermission getEntityPermission(final int bitNumber) { diff --git a/src/main/java/org/caosdb/server/query/CQLParser.g4 b/src/main/java/org/caosdb/server/query/CQLParser.g4 index 8615bae31fae67eb0bd3315f664bfa708db61618..380c3c74c25869bf580368e7d772c7df39f56563 100644 --- a/src/main/java/org/caosdb/server/query/CQLParser.g4 +++ b/src/main/java/org/caosdb/server/query/CQLParser.g4 @@ -191,10 +191,21 @@ idfilter returns [IDFilter filter] locals [String o, String v, String a] ( OPERATOR {$o = $OPERATOR.text;} WHITE_SPACE? - value {$v = $value.str;} + ( + value {$v = $value.str;} + | pid {$v = $pid.text;} + ) )? ; +/** + * A Handle PID <prefix/suffix> e.g. "21.1234/sdfg" + */ +pid +: + (~(WHITE_SPACE | SLASH ))+ SLASH (~(WHITE_SPACE))+ +; + /** * Transaction filter (e.g INSERTED BY ME). */ diff --git a/src/main/java/org/caosdb/server/query/IDFilter.java b/src/main/java/org/caosdb/server/query/IDFilter.java index 8011139796abff42c0e8109621c849d3e6328137..535d23d982f2709b38de82580fc72a3482e4392d 100644 --- a/src/main/java/org/caosdb/server/query/IDFilter.java +++ b/src/main/java/org/caosdb/server/query/IDFilter.java @@ -134,4 +134,9 @@ public class IDFilter implements EntityFilterInterface { sb.append(")"); return sb.toString(); } + + @Override + public String toString() { + return getCacheKey(); + } } diff --git a/src/main/java/org/caosdb/server/resource/transaction/DeleteEntityResource.java b/src/main/java/org/caosdb/server/resource/transaction/DeleteEntityResource.java new file mode 100644 index 0000000000000000000000000000000000000000..6150730c20f8f6625205b8c0c5a3508295071bd7 --- /dev/null +++ b/src/main/java/org/caosdb/server/resource/transaction/DeleteEntityResource.java @@ -0,0 +1,52 @@ +package org.caosdb.server.resource.transaction; + +import java.util.List; +import org.caosdb.server.entity.DeleteEntity; +import org.caosdb.server.entity.EntityID; +import org.caosdb.server.entity.UpdateEntity; +import org.caosdb.server.entity.container.WritableContainer; +import org.caosdb.server.transaction.WriteTransaction; +import org.caosdb.server.transaction.WriteTransactionInterface; +import org.jdom2.Document; +import org.jdom2.Element; +import org.restlet.data.Status; +import org.restlet.representation.Representation; + +public class DeleteEntityResource extends EntityResource { + + @Override + protected Representation httpDeleteInChildClass() throws Exception { + getResponse().setStatus(Status.CLIENT_ERROR_METHOD_NOT_ALLOWED); + return null; + } + + @Override + protected Representation httpPutInChildClass(Representation entity) { + getResponse().setStatus(Status.CLIENT_ERROR_METHOD_NOT_ALLOWED); + return null; + } + + @Override + protected Representation httpPostInChildClass(Representation entity) + throws xmlNotWellFormedException, Exception { + final WritableContainer entityContainer = + new WritableContainer(getUser(), getTimestamp(), getSRID(), getFlags()); + final Document doc = new Document(); + + List<Element> updateEntities; + updateEntities = parseEntity(getRequest().getEntity()).getRootElement().getChildren(); + + for (final Element e : updateEntities) { + entityContainer.add(new DeleteEntity(new EntityID(new UpdateEntity(e).getId().toString()))); + } + + final WriteTransactionInterface update = new WriteTransaction(entityContainer); + update.execute(); + + final Element rootElem = generateRootElement(); + entityContainer.addToElement(rootElem); + doc.setRootElement(rootElem); + + return ok(doc); + } +} diff --git a/src/test/java/org/caosdb/server/query/TestCQL.java b/src/test/java/org/caosdb/server/query/TestCQL.java index 9ca5904a5b851a4a32de5f2328647b64fa05cfec..7f1461a6e4554167f43ceb1194452fde2232f21c 100644 --- a/src/test/java/org/caosdb/server/query/TestCQL.java +++ b/src/test/java/org/caosdb/server/query/TestCQL.java @@ -284,6 +284,9 @@ public class TestCQL { "FIND ENTITY WHICH HAS A PROPERTY LIKE '*with double*' AND A PROPERTY LIKE '*and single*' AND A PROPERTY LIKE '*what\\'s wrong?*' AND A PROPERTY LIKE '*\\'*' AND A PROPERTY LIKE '*nothin\\'*' AND A PROPERTY LIKE '*\"\\'bla*'"; String issue203a = "FIND WHICH ( HAS pname )"; String issue203b = "FIND WHICH ( HAS pname1 AND REFERENCES pname2 )"; + String pid1 = "FIND ENTITY WITH ID = 21.1234/suffix"; + String pid2 = + "FIND TypeA WHICH IS REFERENCED BY TypeB WITH ID=21.T11965/bd33f91e-bfdd-49f4-b664-8cd6dfe30130"; @Test public void testQuery1() @@ -7130,4 +7133,49 @@ public class TestCQL { no = no + 1; } } + + @Test + public void testPid1() { + CQLLexer lexer; + lexer = new CQLLexer(CharStreams.fromString(this.pid1)); + final CommonTokenStream tokens = new CommonTokenStream(lexer); + + final CQLParser parser = new CQLParser(tokens); + final CqContext sfq = parser.cq(); + + System.out.println(sfq.toStringTree(parser)); + + // 4 children: FIND, ENTITY, WHICHCLAUSE, EOF + assertEquals(4, sfq.getChildCount()); + assertEquals(Query.Role.ENTITY, sfq.r); + assertNull(sfq.e); + assertEquals("WITH ID = 21.1234/suffix", sfq.getChild(2).getText()); + ParseTree which = sfq.getChild(2); + assertEquals("ID = 21.1234/suffix", which.getChild(1).getText()); + + assertTrue(sfq.filter instanceof IDFilter); + assertEquals("ID(,=,21.1234/suffix)", sfq.filter.toString()); + } + + @Test + public void testPid2() { + CQLLexer lexer; + lexer = new CQLLexer(CharStreams.fromString(this.pid2)); + final CommonTokenStream tokens = new CommonTokenStream(lexer); + + final CQLParser parser = new CQLParser(tokens); + final CqContext sfq = parser.cq(); + + System.out.println(sfq.toStringTree(parser)); + + // 4 children: FIND, ENTITY, WHICHCLAUSE, EOF + assertEquals(5, sfq.getChildCount()); + assertNull(sfq.r); + assertEquals("TypeA", sfq.e.str); + assertTrue(sfq.filter instanceof Backreference); + Backreference backRef = (Backreference) sfq.filter; + assertEquals( + "ID(,=,21.T11965/bd33f91e-bfdd-49f4-b664-8cd6dfe30130)", + backRef.getSubProperty().getFilter().toString()); + } }