danaivach / hmas-java

A library for handling resources in Hypermedia Multi-Agent Systems based on the HyperAgents ontologies.
1 stars 2 forks source link

Read and write signifiers with recommended context #8

Closed danaivach closed 10 months ago

danaivach commented 1 year ago

A signifier should optionally have a recommended context, that is de-serialized in, or serialized from a Context instance.

A Context instance is of type SHACL.TERM.NODE_SHAPE, but its structure is less contained than the structure of ActionSpecification (e.g., there is no default sh:class like hmas:ActionExecution). Therefore, it can be used to specify any shape that adheres to the SHACL specification. For modelling it, there are (at least) 2 options:

  1. Based on an existing model for SHACL Node Shapes, e.g. by subclassing or having another dependency to a class for SHACL node shapes implemented in a 3rd-party library (e.g. see the last bullet in https://github.com/danaivach/hmas-java/issues/2#issuecomment-1723352602).
  2. By having an attribute model of type org.eclipse.rdf4j.model.Model that subsumes all the triples related to the SHACL Node Shape. This option has been already applied in PR https://github.com/danaivach/hmas-interaction/pull/4

Eventually, no. 1 may be needed for the more principled modelling of Context and its SHACL-based validation. No. 2 can work adequately for an initial implementation if time needs to be saved.

The following test is suggested based on option no. 2 (for the reader; but the same example can be used for the writer too). The unit testing part over the model could be more detailed, but similar:

 @Test
  public void testReadArtifactProfileWithContext() {
    String expectedProfile = PREFIXES +
            ".\n" +
            "@prefix ex: <http://example.org/> .\n" +
            "@prefix xs: <https://www.w3.org/2001/XMLSchema#> .\n" +
            "@prefix htv: <http://www.w3.org/2011/http#> .\n" +
            "<urn:profile> a hmas:ResourceProfile ;\n" +
            "  hmas:isProfileOf [ a hmas:Artifact ];\n" +
            "  hmas:exposesSignifier ex:signifier .\n" +
            "\n" +
            "ex:signifier a hmas:Signifier ;\n" +
            "  hmas:recommendsContext ex:situationShape ;\n" +
            "  hmas:signifies ex:moveGripperSpecification .\n" +
            "\n" +
            "ex:moveGripperSpecification a sh:NodeShape;\n" +
            "  sh:class hmas:ActionExecution ;\n" +
            "  sh:property [\n" +
            "    sh:path prov:used ;\n" +
            "    sh:minCount 1;\n" +
            "    sh:maxCount 1 ;\n" +
            "    sh:hasValue ex:httpForm ;\n" +
            "  ] .\n" +
            "\n" +
            "ex:situationShape a sh:NodeShape ;\n" +
            "  sh:class hmas:ResourceProfile ;\n" +
            "  sh:property [\n" +
            "    sh:path hmas:isProfileOf ;\n" +
            "    sh:qualifiedValueShape ex:agentShape ;\n" +
            "    sh:qualifiedMinCount 1 ;\n" +
            "    sh:qualifiedMaxCount 1 \n" +
            "  ] .\n" +
            "\n" +
            "ex:agentShape a sh:NodeShape ;\n" +
            "  sh:class hmas:Agent ;\n" +
            "  sh:property [\n" +
            "    sh:path ex:hasBelief ;\n" +
            "    sh:minCount 1;\n" +
            "    sh:maxCount 1 ;\n" +
            "    sh:value \"room(empty)\"\n" +
            "  ] .\n" +
            "ex:httpForm a hctl:Form ;\n" +
            "  hctl:hasTarget <https://api.interactions.ics.unisg.ch/leubot1/v1.3.4/gripper> ;\n" +
            "  hctl:forContentType \"application/json\" ;\n" +
            "  htv:methodName \"PUT\" .";

    ArtifactProfile profile = ArtifactProfileGraphReader.readFromString(expectedProfile);

    Artifact artifact = profile.getArtifact();
    assertEquals(ARTIFACT, artifact.getTypeAsIRI());

    assertEquals(1, profile.getExposedSignifiers().size());
    Set<Signifier> signifiers = profile.getExposedSignifiers();

    List<Signifier> signifiersList = new ArrayList<>(signifiers);
    Signifier signifier = signifiersList.get(0);

    ActionSpecification actionSpec = (ActionSpecification) signifier.getResource();
    Set<Form> forms = actionSpec.getForms();
    assertEquals(1, forms.size());
    Form form = new ArrayList<>(forms).get(0);
    assertEquals("https://api.interactions.ics.unisg.ch/leubot1/v1.3.4/gripper", form.getTarget());
    assertEquals("http://example.org/httpForm", form.getIRIAsString().get());

    Set<Context> contexts = signifier.getRecommendedContexts();
    assertEquals(1, contexts.size());
    Context context = signifier.getRecommendedContexts().stream().findFirst().get();

    Model contextModel = context.getModel();
    SimpleValueFactory valueFactory = SimpleValueFactory.getInstance();
    IRI hasBeliefResource = valueFactory.createIRI("http://example.org/hasBelief");

    List<Value> actualBeliefs = contextModel.filter( null, hasBeliefResource, null)
            .stream()
            .map(Statement::getObject)
            .collect(Collectors.toList());

    assertEquals(1, actualBeliefs.size());
    assertEquals("room(empty)", actualBeliefs.get(0).stringValue());
danaivach commented 12 months ago

Update: The 2nd option has been already addressed in https://github.com/danaivach/hmas-interaction/pull/4.

The transition from the 2nd option to the 1st one remains.

danaivach commented 10 months ago

I am closing this as the issue is addressed through the implementation of the 2nd discussed option. This option aligns with the use of SHACL in the remaining parts of the library.