diff --git a/klab.core.api/src/main/java/org/integratedmodelling/klab/api/digitaltwin/DigitalTwin.java b/klab.core.api/src/main/java/org/integratedmodelling/klab/api/digitaltwin/DigitalTwin.java index 696200937..815525954 100644 --- a/klab.core.api/src/main/java/org/integratedmodelling/klab/api/digitaltwin/DigitalTwin.java +++ b/klab.core.api/src/main/java/org/integratedmodelling/klab/api/digitaltwin/DigitalTwin.java @@ -3,36 +3,17 @@ import java.net.URL; import java.util.Collection; import java.util.List; -import java.util.Map; -import java.util.Set; import java.util.concurrent.TimeUnit; -import java.util.concurrent.atomic.AtomicLong; - import org.integratedmodelling.klab.api.authentication.ResourcePrivileges; -import org.integratedmodelling.klab.api.collections.Identifier; import org.integratedmodelling.klab.api.data.*; import org.integratedmodelling.klab.api.digitaltwin.impl.ConfigurationBuilder; -import org.integratedmodelling.klab.api.exceptions.KlabIllegalArgumentException; -import org.integratedmodelling.klab.api.exceptions.KlabIllegalStateException; import org.integratedmodelling.klab.api.exceptions.KlabValidationException; import org.integratedmodelling.klab.api.geometry.Geometry; -import org.integratedmodelling.klab.api.knowledge.Observable; -import org.integratedmodelling.klab.api.knowledge.SemanticType; -import org.integratedmodelling.klab.api.knowledge.Urn; import org.integratedmodelling.klab.api.knowledge.observation.Observation; -import org.integratedmodelling.klab.api.knowledge.observation.impl.ObservationImpl; -import org.integratedmodelling.klab.api.knowledge.observation.scale.time.TimeInstant; -import org.integratedmodelling.klab.api.lang.Quantity; import org.integratedmodelling.klab.api.lang.ServiceCall; -import org.integratedmodelling.klab.api.lang.kim.KimConcept; -import org.integratedmodelling.klab.api.lang.kim.KimModel; -import org.integratedmodelling.klab.api.lang.kim.KimObservable; -import org.integratedmodelling.klab.api.lang.kim.KimSymbolDefinition; import org.integratedmodelling.klab.api.provenance.Activity; import org.integratedmodelling.klab.api.provenance.Provenance; import org.integratedmodelling.klab.api.scope.*; -import org.integratedmodelling.klab.api.services.Reasoner; -import org.integratedmodelling.klab.api.services.RuntimeService; import org.integratedmodelling.klab.api.services.runtime.Dataflow; import org.integratedmodelling.klab.api.services.runtime.Message; import org.integratedmodelling.klab.api.services.runtime.Notification; @@ -116,7 +97,7 @@ interface Configuration { ResourcePrivileges getAccessRights(); /** - * These may be present when the configuration is the return value of a connect call. + * These describe any error, warning or info conditions relative to the digital twin. * * @return */ @@ -146,6 +127,15 @@ interface Configuration { */ String getId(); + /** + * This is used to flag a failure in the creation of a digital twin or the connection. If true, + * the ID may be null and should not be used even if not. Notifications should contain at least + * one error if this returns true. + * + * @return + */ + boolean isEmpty(); + @Deprecated boolean isCreateWhenAbsent(); @@ -173,6 +163,16 @@ static Configuration create(URL url, UserScope scope) { return new ConfigurationBuilder(url, scope).build(); } + static Configuration empty(Notification... notifications) { + var ret = new ConfigurationBuilder().empty(); + if (notifications != null) { + for (var n : notifications) { + ret.withNotification(n); + } + } + return ret.build(); + } + static ConfigurationBuilder builder() { return new ConfigurationBuilder(); } diff --git a/klab.core.api/src/main/java/org/integratedmodelling/klab/api/digitaltwin/impl/ConfigurationBuilder.java b/klab.core.api/src/main/java/org/integratedmodelling/klab/api/digitaltwin/impl/ConfigurationBuilder.java index 928d468ba..ca599c6b4 100644 --- a/klab.core.api/src/main/java/org/integratedmodelling/klab/api/digitaltwin/impl/ConfigurationBuilder.java +++ b/klab.core.api/src/main/java/org/integratedmodelling/klab/api/digitaltwin/impl/ConfigurationBuilder.java @@ -1,21 +1,19 @@ package org.integratedmodelling.klab.api.digitaltwin.impl; +import java.net.URL; +import java.util.ArrayList; +import java.util.List; +import java.util.concurrent.TimeUnit; import org.integratedmodelling.klab.api.Klab; import org.integratedmodelling.klab.api.ServicesAPI; import org.integratedmodelling.klab.api.authentication.ResourcePrivileges; import org.integratedmodelling.klab.api.digitaltwin.DigitalTwin; +import org.integratedmodelling.klab.api.knowledge.observation.Observation; import org.integratedmodelling.klab.api.scope.Persistence; import org.integratedmodelling.klab.api.scope.Scope; import org.integratedmodelling.klab.api.scope.UserScope; import org.integratedmodelling.klab.api.services.runtime.Notification; import org.integratedmodelling.klab.api.utils.Utils; -import org.integratedmodelling.klab.api.view.UIReactor; -import org.integratedmodelling.klab.api.view.modeler.views.controllers.AuthenticationViewController; - -import java.net.URL; -import java.util.ArrayList; -import java.util.List; -import java.util.concurrent.TimeUnit; public class ConfigurationBuilder { private ResourcePrivileges accessRights; @@ -26,11 +24,13 @@ public class ConfigurationBuilder { private TimeUnit timeoutUnit; private URL url; private URL serverUrl; - private List notifications = new ArrayList<>(); + private final List notifications = new ArrayList<>(); private boolean createWhenAbsent; private String serviceId; private String description; private String owner; + private Observation observer; + private boolean empty; public ConfigurationBuilder() {} @@ -46,6 +46,8 @@ public ConfigurationBuilder(DigitalTwin.Configuration configuration) { this.notifications.addAll(configuration.getNotifications()); this.createWhenAbsent = configuration.isCreateWhenAbsent(); this.serviceId = configuration.getServiceId(); + this.observer = configuration.getObserver(); + this.empty = configuration.isEmpty(); } /** @@ -118,6 +120,11 @@ public ConfigurationBuilder owner(String owner) { return this; } + public ConfigurationBuilder empty() { + this.empty = true; + return this; + } + public ConfigurationBuilder createWhenAbsent(boolean b) { this.createWhenAbsent = b; return this; @@ -159,6 +166,11 @@ public ConfigurationBuilder timeout(long timeout, TimeUnit timeoutUnit) { return this; } + public ConfigurationBuilder observer(Observation observer) { + this.observer = observer; + return this; + } + public DigitalTwin.Configuration build() { return new ConfigurationImpl( accessRights, @@ -173,6 +185,8 @@ public DigitalTwin.Configuration build() { this.createWhenAbsent, this.serviceId, this.description, - this.owner); + this.owner, + this.empty, + this.observer); } } diff --git a/klab.core.api/src/main/java/org/integratedmodelling/klab/api/digitaltwin/impl/ConfigurationImpl.java b/klab.core.api/src/main/java/org/integratedmodelling/klab/api/digitaltwin/impl/ConfigurationImpl.java index f0c9b8f04..7b141030e 100644 --- a/klab.core.api/src/main/java/org/integratedmodelling/klab/api/digitaltwin/impl/ConfigurationImpl.java +++ b/klab.core.api/src/main/java/org/integratedmodelling/klab/api/digitaltwin/impl/ConfigurationImpl.java @@ -34,6 +34,7 @@ public class ConfigurationImpl implements DigitalTwin.Configuration { private Observation observer; private Data.ShardingStrategy shardingStrategy = new Data.ShardingStrategy(); private String owner; + private boolean empty; // for the object mapper, do not remove ConfigurationImpl() {} @@ -51,7 +52,9 @@ public class ConfigurationImpl implements DigitalTwin.Configuration { boolean createWhenAbsent, String serviceId, String description, - String owner) { + String owner, + boolean empty, + Observation observer) { this.accessRights = accessRights; this.persistence = persistence; this.name = name; @@ -65,6 +68,8 @@ public class ConfigurationImpl implements DigitalTwin.Configuration { this.serviceId = serviceId; this.description = description; this.owner = owner; + this.empty = empty; + this.observer = observer; } @Override @@ -106,6 +111,15 @@ public String getId() { return id; } + @Override + public boolean isEmpty() { + return empty; + } + + public void setEmpty(boolean empty) { + this.empty = empty; + } + @Override public DigitalTwin.Configuration validate(Scope scope) throws KlabValidationException { diff --git a/klab.core.api/src/main/java/org/integratedmodelling/klab/api/services/KlabService.java b/klab.core.api/src/main/java/org/integratedmodelling/klab/api/services/KlabService.java index 2024f7072..d7ba99109 100644 --- a/klab.core.api/src/main/java/org/integratedmodelling/klab/api/services/KlabService.java +++ b/klab.core.api/src/main/java/org/integratedmodelling/klab/api/services/KlabService.java @@ -15,6 +15,7 @@ import org.integratedmodelling.klab.api.configuration.Setting; import org.integratedmodelling.klab.api.configuration.Settings; import org.integratedmodelling.klab.api.data.Metadata; +import org.integratedmodelling.klab.api.digitaltwin.DigitalTwin; import org.integratedmodelling.klab.api.digitaltwin.Scheduler; import org.integratedmodelling.klab.api.engine.Engine; import org.integratedmodelling.klab.api.exceptions.KlabIllegalArgumentException; @@ -395,18 +396,21 @@ String declareSessionScope( * accessible to the resulting scope. If the service is not a runtime, the request must come from * another service and the scope should be instrumented as necessary for its purposes. * - *

FIXME this must return a {@link - * org.integratedmodelling.klab.api.digitaltwin.DigitalTwin.Configuration} so that we know the ID - * along with any observer geometry and other configuration when the scope already exists. + *

The return value is the {@link + * org.integratedmodelling.klab.api.digitaltwin.DigitalTwin.Configuration} that describes the + * scope. Much of its content will be the same as submitted, but along with the ID (or an empty() + * configuration in case of failure) we can also access the current observer geometry, any + * notifications and advisories, and other configuration of scopes that already existed. * * @param contextScope a client scope that should record the ID for future communication. If the * ID is null, the call has failed. * @param sessionScope used to set up federated behavior * @param userScope used to establish the agent making changes (same as sessionScope's unless * federated) - * @return the ID of the new context scope created at server side, or null in case of failure. + * @return the known configuration for the digital twin, including (if successful) the ID of the + * new context scope created at server side, or an empty configuration in case of failure. */ - String declareContextScope( + DigitalTwin.Configuration declareContextScope( ContextScope contextScope, SessionScope sessionScope, UserScope userScope); /** diff --git a/klab.core.common/src/main/java/org/integratedmodelling/common/services/client/BaseServiceClient.java b/klab.core.common/src/main/java/org/integratedmodelling/common/services/client/BaseServiceClient.java index c204ea173..23f0e3e31 100644 --- a/klab.core.common/src/main/java/org/integratedmodelling/common/services/client/BaseServiceClient.java +++ b/klab.core.common/src/main/java/org/integratedmodelling/common/services/client/BaseServiceClient.java @@ -9,7 +9,6 @@ import java.util.concurrent.CompletableFuture; import java.util.function.BiConsumer; import java.util.function.Predicate; - import org.integratedmodelling.common.authentication.scope.AbstractServiceDelegatingScope; import org.integratedmodelling.common.authentication.scope.MessagingChannelImpl; import org.integratedmodelling.common.logging.Logging; @@ -175,7 +174,7 @@ public String declareSessionScope( } @Override - public String declareContextScope( + public DigitalTwin.Configuration declareContextScope( ContextScope contextScope, SessionScope sessionScope, UserScope userScope) { ScopeRequest request = new ScopeRequest(); @@ -188,18 +187,22 @@ public String declareContextScope( .toList()); try { - var scopeId = - client.withScope(sessionScope).post(ServicesAPI.CREATE_CONTEXT, request, String.class); - if (scopeId != null) { - setupMessaging(contextScope, sessionScope, scopeId); - // give the server some time to set up the context and inform the other services - // Thread.sleep(1000); + var configuration = + client + .withScope(sessionScope) + .post(ServicesAPI.CREATE_CONTEXT, request, DigitalTwin.Configuration.class); + if (configuration != null && !configuration.isEmpty()) { + setupMessaging(contextScope, sessionScope, configuration.getId()); } - return scopeId; + return configuration == null + ? DigitalTwin.Configuration.empty( + Notification.error("No valid return value from server at context creation")) + : configuration; + } catch (Throwable t) { - Logging.INSTANCE.error(this.serviceName() + " failed to declare context scope", t); + return DigitalTwin.Configuration.empty( + Notification.error(this.serviceName() + " failed to declare context scope", t)); } - return null; } private String setupMessaging(SessionScope sessionScope, UserScope userScope, String scopeId) { diff --git a/klab.core.common/src/main/java/org/integratedmodelling/common/services/client/scope/ClientContextScope.java b/klab.core.common/src/main/java/org/integratedmodelling/common/services/client/scope/ClientContextScope.java index e13a64e49..f27c1bcc4 100644 --- a/klab.core.common/src/main/java/org/integratedmodelling/common/services/client/scope/ClientContextScope.java +++ b/klab.core.common/src/main/java/org/integratedmodelling/common/services/client/scope/ClientContextScope.java @@ -7,7 +7,6 @@ import org.integratedmodelling.common.utils.Utils; import org.integratedmodelling.klab.api.data.Data; import org.integratedmodelling.klab.api.data.KnowledgeGraph; -import org.integratedmodelling.klab.api.data.Metadata; import org.integratedmodelling.klab.api.data.RuntimeAsset; import org.integratedmodelling.klab.api.digitaltwin.DigitalTwin; import org.integratedmodelling.klab.api.digitaltwin.GraphModel; @@ -16,7 +15,6 @@ import org.integratedmodelling.klab.api.identities.Federation; import org.integratedmodelling.klab.api.knowledge.Observable; import org.integratedmodelling.klab.api.knowledge.SemanticType; -import org.integratedmodelling.klab.api.knowledge.Semantics; import org.integratedmodelling.klab.api.knowledge.observation.Observation; import org.integratedmodelling.klab.api.knowledge.observation.impl.ObservationBuilderImpl; import org.integratedmodelling.klab.api.knowledge.observation.impl.ObservationImpl; @@ -135,6 +133,7 @@ public void setId(String id) { super.setId(id); if (this.configuration instanceof ConfigurationImpl configurationImpl) { configurationImpl.setId(id); + configurationImpl.setUrl(Utils.URLs.newURL(runtimeService.getUrl() + "/dt/" + id)); } } @@ -470,9 +469,19 @@ public String getTransactionId() { public void resolveDefaultObserver() { + if (getFederation() != null + && getFederation().getId().equals(Federation.LOCAL_FEDERATION_ID)) { + // TODO + } + // TODO + } - - if (getFederation() != null && getFederation().getId().equals(Federation.LOCAL_FEDERATION_ID)) { + public void setFromConfiguration(DigitalTwin.Configuration configuration) { + if (configuration.isEmpty()) { + setEmpty(true); + } else { + setId(configuration.getId()); } + this.configuration.getNotifications().addAll(configuration.getNotifications()); } } diff --git a/klab.core.common/src/main/java/org/integratedmodelling/common/services/client/scope/ClientSessionScope.java b/klab.core.common/src/main/java/org/integratedmodelling/common/services/client/scope/ClientSessionScope.java index 0c7395503..32c1aeb8b 100644 --- a/klab.core.common/src/main/java/org/integratedmodelling/common/services/client/scope/ClientSessionScope.java +++ b/klab.core.common/src/main/java/org/integratedmodelling/common/services/client/scope/ClientSessionScope.java @@ -1,5 +1,8 @@ package org.integratedmodelling.common.services.client.scope; +import java.net.URL; +import java.util.Collection; +import java.util.List; import org.integratedmodelling.klab.api.digitaltwin.DigitalTwin; import org.integratedmodelling.klab.api.exceptions.KlabIllegalStateException; import org.integratedmodelling.klab.api.exceptions.KlabInternalErrorException; @@ -10,11 +13,6 @@ import org.integratedmodelling.klab.api.services.KlabService; import org.integratedmodelling.klab.api.services.RuntimeService; -import java.net.URL; -import java.util.Collection; -import java.util.List; -import java.util.function.Predicate; - /** Client-side session scope */ public class ClientSessionScope extends ClientUserScope implements SessionScope { @@ -48,7 +46,7 @@ public T getService(Class serviceClass) { return super.getService(serviceClass); } - /** + /** * Use to pre-define the ID when necessary. * * @param id @@ -100,9 +98,8 @@ public ContextScope createContext(DigitalTwin.Configuration configuration) { */ var ret = new ClientContextScope(this, runtime, configuration.validate(this)); var id = runtime.declareContextScope(ret, this, userScope); - - if (id != null) { - ret.setId(id); + ret.setFromConfiguration(id); + if (!id.isEmpty()) { ClientScopeManager.INSTANCE.register(ret); } diff --git a/klab.core.services/src/main/java/org/integratedmodelling/klab/services/base/BaseService.java b/klab.core.services/src/main/java/org/integratedmodelling/klab/services/base/BaseService.java index 1e615514f..38bd1ba85 100644 --- a/klab.core.services/src/main/java/org/integratedmodelling/klab/services/base/BaseService.java +++ b/klab.core.services/src/main/java/org/integratedmodelling/klab/services/base/BaseService.java @@ -35,6 +35,7 @@ import org.integratedmodelling.klab.api.configuration.Settings; import org.integratedmodelling.klab.api.data.RuntimeAsset; import org.integratedmodelling.klab.api.data.Version; +import org.integratedmodelling.klab.api.digitaltwin.DigitalTwin; import org.integratedmodelling.klab.api.exceptions.KlabAuthorizationException; import org.integratedmodelling.klab.api.exceptions.KlabIOException; import org.integratedmodelling.klab.api.exceptions.KlabIllegalArgumentException; @@ -638,7 +639,7 @@ public String declareSessionScope( * @return */ @Override - public String declareContextScope( + public DigitalTwin.Configuration declareContextScope( ContextScope contextScope, SessionScope sessionScope, UserScope userScope) { if (contextScope instanceof ServiceContextScope serviceContextScope) { @@ -658,7 +659,7 @@ public String declareContextScope( } getScopeManager().registerScope(serviceContextScope); - return serviceContextScope.getId(); + return serviceContextScope.getConfiguration(); } throw new KlabIllegalArgumentException("unexpected scope class"); diff --git a/klab.services.resolver/src/main/java/org/integratedmodelling/klab/services/resolver/ResolverService.java b/klab.services.resolver/src/main/java/org/integratedmodelling/klab/services/resolver/ResolverService.java index 6912a94ed..c72349f2d 100644 --- a/klab.services.resolver/src/main/java/org/integratedmodelling/klab/services/resolver/ResolverService.java +++ b/klab.services.resolver/src/main/java/org/integratedmodelling/klab/services/resolver/ResolverService.java @@ -16,6 +16,7 @@ import org.integratedmodelling.klab.api.collections.Parameters; import org.integratedmodelling.klab.api.data.Metadata; import org.integratedmodelling.klab.api.data.Version; +import org.integratedmodelling.klab.api.digitaltwin.DigitalTwin; import org.integratedmodelling.klab.api.digitaltwin.Scheduler; import org.integratedmodelling.klab.api.knowledge.*; import org.integratedmodelling.klab.api.knowledge.observation.Observation; @@ -443,7 +444,7 @@ private StringBuffer encodePreamble(Dataflow dataflow) { } @Override - public String declareContextScope( + public DigitalTwin.Configuration declareContextScope( ContextScope contextScope, SessionScope sessionScope, UserScope userScope) { // instrument the scope for resolving observations and keeping resolution results across calls. var resolutionGraph = ResolutionGraph.create(contextScope); @@ -453,7 +454,7 @@ public String declareContextScope( // load up the resource scenario cache if persistent if (contextScope.getConfiguration().getPersistence().survivesShutdown) { - var urnMatch = Resource.resourceRegexFor(this, ret); + var urnMatch = Resource.resourceRegexFor(this, ret.getId()); this.resourcesKbox .getResourcesByUrnMatch(urnMatch) .forEach(resolutionGraph::addLocalResource); diff --git a/klab.services.runtime.server/src/main/java/org/integratedmodelling/klab/services/runtime/server/controllers/RuntimeServerController.java b/klab.services.runtime.server/src/main/java/org/integratedmodelling/klab/services/runtime/server/controllers/RuntimeServerController.java index 1f43dcc1d..d0f81b23c 100644 --- a/klab.services.runtime.server/src/main/java/org/integratedmodelling/klab/services/runtime/server/controllers/RuntimeServerController.java +++ b/klab.services.runtime.server/src/main/java/org/integratedmodelling/klab/services/runtime/server/controllers/RuntimeServerController.java @@ -542,7 +542,7 @@ public String createSession( * @return the ID of the new context scope */ @PostMapping(ServicesAPI.CREATE_CONTEXT) - public String createContext( + public DigitalTwin.Configuration createContext( @RequestBody ScopeRequest request, @RequestParam(name = "id", required = false) String contextId, Principal principal, @@ -600,26 +600,26 @@ public String createContext( var id = runtimeService.klabService().declareContextScope(ret, sessionScope, userScope); if (federation != null) { - var implementedQueues = ret.setupMessaging(federation, id, queuesHeader); + var implementedQueues = ret.setupMessaging(federation, id.getId(), queuesHeader); Logging.INSTANCE.info( - "Queues set up for session " + id + ": " + implementedQueues + " on context scope"); + "Queues set up for context " + id.getId() + ": " + implementedQueues); response.setHeader( ServicesAPI.MESSAGING_QUEUES_HEADER, Utils.Strings.join(implementedQueues, ", ")); } - if (!ret.initializeAgents(id)) { - Logging.INSTANCE.warn("agent initialization failed in context creation"); + if (!ret.initializeAgents(id.getId())) { + id.getNotifications() + .add(Notification.warning("agent initialization failed in context creation")); } - return id; } - } else { - Logging.INSTANCE.error("Context instantiation failed: no valid session scope for request"); } } - return null; + + return DigitalTwin.Configuration.empty( + Notification.error("Context instantiation failed: no valid session scope for request")); } @GetMapping(ServicesAPI.RELEASE_SESSION) diff --git a/klab.services.runtime/src/main/java/org/integratedmodelling/klab/services/runtime/RuntimeService.java b/klab.services.runtime/src/main/java/org/integratedmodelling/klab/services/runtime/RuntimeService.java index 34991bd70..e05145d7a 100644 --- a/klab.services.runtime/src/main/java/org/integratedmodelling/klab/services/runtime/RuntimeService.java +++ b/klab.services.runtime/src/main/java/org/integratedmodelling/klab/services/runtime/RuntimeService.java @@ -371,7 +371,7 @@ public Map getExceptionTestcases(Scope scope, boolean deleteExis * @return */ @Override - public String declareContextScope( + public DigitalTwin.Configuration declareContextScope( ContextScope contextScope, SessionScope sessionScope, UserScope userScope) { if (!serviceId().equals(contextScope.getHostServiceId())) { @@ -401,7 +401,7 @@ public String declareContextScope( new DigitalTwinImpl( this, serviceContextScope, scopeId, userScope, getMainKnowledgeGraph())); - return serviceContextScope.getId(); + return serviceContextScope.getConfiguration(); } throw new KlabIllegalArgumentException("unexpected scope class"); } diff --git a/klab.services.runtime/src/main/java/org/integratedmodelling/klab/services/runtime/neo4j/KnowledgeGraphNeo4JClient.java b/klab.services.runtime/src/main/java/org/integratedmodelling/klab/services/runtime/neo4j/KnowledgeGraphNeo4JClient.java index 14dcb73cd..ccfa7e0ae 100644 --- a/klab.services.runtime/src/main/java/org/integratedmodelling/klab/services/runtime/neo4j/KnowledgeGraphNeo4JClient.java +++ b/klab.services.runtime/src/main/java/org/integratedmodelling/klab/services/runtime/neo4j/KnowledgeGraphNeo4JClient.java @@ -82,13 +82,8 @@ public KnowledgeGraph contextualize( var ret = new KnowledgeGraphNeo4JClient(this, digitalTwinConfig.getId(), userScope.getUser()); - ret.initializeContext( - digitalTwinConfig.getId(), - digitalTwinConfig.getName(), - userScope, - digitalTwinConfig.getAccessRights(), - digitalTwinConfig.getDescription(), - digitalTwinConfig.getPersistence()); + // TODO pass the + ret.initializeContext(digitalTwinConfig, userScope); return ret; } diff --git a/klab.services.runtime/src/main/java/org/integratedmodelling/klab/services/runtime/neo4j/KnowledgeGraphNeo4j.java b/klab.services.runtime/src/main/java/org/integratedmodelling/klab/services/runtime/neo4j/KnowledgeGraphNeo4j.java index 3356c3f81..be10eea86 100644 --- a/klab.services.runtime/src/main/java/org/integratedmodelling/klab/services/runtime/neo4j/KnowledgeGraphNeo4j.java +++ b/klab.services.runtime/src/main/java/org/integratedmodelling/klab/services/runtime/neo4j/KnowledgeGraphNeo4j.java @@ -131,11 +131,12 @@ interface Queries { + "\t(ctx)<-[:CREATED]-(creation),\n" + "(prov)-[:HAS_CHILD]->(creation)" }; - String[] SPATIAL_LAYERS_QUERIES = - new String[] { - "CALL spatial.addLayer('shape_$contextId', 'WKB', 'shape')" // , - // "CALL spatial.addPointLayer('centroid_$contextId', 'latitude', 'longitude')" - }; + // String[] SPATIAL_LAYERS_QUERIES = + // new String[] { + // , + // // "CALL spatial.addPointLayer('centroid_$contextId', 'latitude', + // 'longitude')" + // }; } class TransactionImpl implements Transaction { @@ -344,21 +345,15 @@ protected synchronized Result query( } /** Ensure things are OK re: main agents and the like. Must be called only once */ - protected void initializeContext( - String scopeId, - String name, - UserScope scope, - ResourcePrivileges rights, - String description, - Persistence persistence) { - - this.rootContextId = scopeId; + protected void initializeContext(DigitalTwin.Configuration configuration, UserScope scope) { + + this.rootContextId = configuration.getId(); this.userScope = scope; this.serviceId = scope.getHostServiceId(); this.klab = getOrCreateAgent("k.LAB", "AI"); this.user = getOrCreateAgent(scope.getUser().getUsername(), "USER"); - var result = query(Queries.FIND_CONTEXT, Map.of("contextId", scopeId), scope); + var result = query(Queries.FIND_CONTEXT, Map.of("contextId", configuration.getId()), scope); if (result.records().isEmpty()) { @@ -366,6 +361,7 @@ protected void initializeContext( var activityId = nextKey(); var federation = Klab.INSTANCE.getFederationData(scope.getUser()); + var rights = configuration.getAccessRights(); if (rights == null) { rights = ResourcePrivileges.create(scope); } @@ -375,9 +371,9 @@ protected void initializeContext( query, Map.of( "contextId", - scopeId, + configuration.getId(), "name", - name, + configuration.getName(), "rights", rights.toString(), "timestamp", @@ -385,33 +381,36 @@ protected void initializeContext( "federation", (federation == null ? "" : federation.getId()), "description", - (description == null ? "No description given" : description), + (configuration.getDescription() == null + ? "No description given" + : configuration.getDescription()), "lastUpdate", System.currentTimeMillis(), "username", scope.getUser().getUsername(), "expirationType", - persistence.name(), + configuration.getPersistence().name(), "activityId", activityId), scope); } // create spatial layers only if they don't already exist - for (var query : Queries.SPATIAL_LAYERS_QUERIES) { - String layerName = "shape_" + scopeId; - var layerCheck = - query( - "CALL spatial.layers() YIELD name WHERE name = $layerName RETURN count(name) > 0 AS exists", - Map.of("layerName", layerName), - scope); - boolean layerExists = - layerCheck != null - && !layerCheck.records().isEmpty() - && layerCheck.records().getFirst().get("exists").asBoolean(false); - if (!layerExists) { - query(query.replace("$contextId", scopeId), Map.of(), scope); - } + String layerName = "shape_" + Utils.Paths.getLast(configuration.getId(), '.'); + var layerCheck = + query( + "CALL spatial.layers() YIELD name WHERE name = $layerName RETURN count(name) > 0 AS exists", + Map.of("layerName", layerName), + scope); + boolean layerExists = + layerCheck != null + && !layerCheck.records().isEmpty() + && layerCheck.records().getFirst().get("exists").asBoolean(false); + if (!layerExists) { + query( + "CALL spatial.addLayer('$layerName', 'WKB', 'shape')".replace("$layerName", layerName), + Map.of(), + scope); } } } @@ -437,13 +436,19 @@ protected Agent getOrCreateAgent(String name, String ai) { @Override public void deleteContext() { - query("CALL spatial.removeLayer('shape_" + rootContextId + "')", Map.of(), userScope); + query( + "CALL spatial.removeLayer('shape_" + Utils.Paths.getLast(rootContextId, '.') + "')", + Map.of(), + userScope); query(Queries.REMOVE_CONTEXT, Map.of("contextId", rootContextId), userScope); } @Override public void deleteContext(ContextInfo contextScope, ServiceScope serviceScope) { - query("CALL spatial.removeLayer('shape_" + rootContextId + "')", Map.of(), userScope); + query( + "CALL spatial.removeLayer('shape_" + Utils.Paths.getLast(rootContextId, '.') + "')", + Map.of(), + userScope); query( Queries.REMOVE_CONTEXT, Map.of("contextId", contextScope.getConfiguration().getId()), @@ -886,7 +891,9 @@ protected long store( var query = storeSpatialData - ? Queries.CREATE_WITH_SHAPE.replace("{type}", type).replace("{scopeId}", scope.getId()) + ? Queries.CREATE_WITH_SHAPE + .replace("{type}", type) + .replace("{scopeId}", Utils.Paths.getLast(rootContextId, '.')) : Queries.CREATE_WITH_PROPERTIES.replace("{type}", type); var result = query(transaction, query, Map.of("properties", props), scope); @@ -905,18 +912,6 @@ var record = result.next(); if (geometry != null) { storeGeometry(geometry, asset, transaction); - // if (storeSpatialData) { - // query( - // transaction, - // String.format("CALL spatial.addNode('shape_%s', $node)", scope.getId()), - // Map.of("node", neo4jNode), - // scope); - // query( - // transaction, - // String.format("CALL spatial.addNode('centroid_%s', $node')", scope.getId()), - // Map.of("node", neo4jNode), - // scope); - // } } }