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 action specification as SHACL shape #1

Closed danaivach closed 8 months ago

danaivach commented 1 year ago

Feature: read and write a signifier like the following:

@prefix ex: <http://example.org/> .
@prefix hmas: <https://purl.org/hmas/> .
@prefix sh: <http://www.w3.org/ns/shacl#> .
@prefix prov: <http://www.w3.org/ns/prov#> .
@prefix xs: <https://www.w3.org/2001/XMLSchema#> .
@prefix hctl: <https://www.w3.org/2019/wot/hypermedia#> .
@prefix htv: <http://www.w3.org/2011/http#> . 

ex:signifier a hmas:Signifier ;
    hmas:signifies ex:moveGripperSpecification .

ex:moveGripperSpecification a sh:NodeShape;
    sh:class hmas:ActionExecution ;
    sh:property [
        sh:path prov:used ;
                sh:minCount 1;
                sh:maxCount 1 ;
                sh:hasValue ex:form ;
    ] .

ex:form a hctl:Form ;
    hctl:hasTarget <https://api.interactions.ics.unisg.ch/leubot1/v1.3.4/gripper> ;
        hctl:forContentType "application/json" ;
        htv:methodName "PUT" .
danaivach commented 1 year ago

Example test in ArtifactProfileGraphWriterTest:

@Test
public void testWriteArtifactProfileWithSignifier() throws IOException {
    String expectedProfile = PREFIXES +
            ".\n" +
            "<urn:profile> a hmas:ResourceProfile ;\n" +
            " hmas:isProfileOf [ a hmas:Artifact ];\n" +
            " hmas:exposesSignifier [ a hmas:Signifier ;\n" +
            "    hmas:signifies [ 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 <urn:3g6lpq9v> ;\n" +
            "      ]\n" +
            "   ]\n" +
            "] .\n" +
            "<urn:3g6lpq9v> a hctl:Form ;\n" + //here the URN is randomly generated
            "   hctl:hasTarget <https://example.org/resource> .";

    ActionSpecification actionSpec = new ActionSpecification.Builder(BASIC_FORM).build();

    ArtifactProfile profile =
            new ArtifactProfile.Builder(new Artifact.Builder().build())
                    .setIRIAsString("urn:profile")
                    .exposeSignifier(new Signifier.Builder(actionSpec).build())
                    .build();

    assertIsomorphicGraphs(expectedProfile, profile);
  }
danaivach commented 1 year ago

Example test in ArtifactProfileGraphReaderTest:

@Test
  public void testReadActionSpecWithHTTPMethod() {
    String expectedProfile = PREFIXES +
            ".\n" +
            "<urn:profile> a hmas:ResourceProfile ;\n" +
            " hmas:isProfileOf [ a hmas:Artifact ];\n" +
            " hmas:exposesSignifier [ a hmas:Signifier ;\n" +
            "    hmas:signifies [ 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 <urn:3g6lpq9v> ;\n" +
            "      ]\n" +
            "   ]\n" +
            "] .\n" +
            "<urn:3g6lpq9v> a hctl:Form ;\n" + //here the URN is randomly generated
            "   hctl:hasTarget <https://example.org/resource> .";

    ArtifactProfile profile =
            ArtifactProfileGraphReader.readFromString(expectedProfile);

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

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

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

    ActionSpecification actionSpec = (ActionSpecification) signifier.getBehavioralSpecification();
    Set<Form> forms = actionSpec.getForms();
    assertEquals(1, forms.size());
    Form form = new ArrayList<>(forms).get(0);
    assertEquals("https://example.org/resource", form.getTarget());
    assertEquals("urn:3g6lpq9v", form.getIRIAsString());
  }
danaivach commented 1 year ago

Example test in ArtifactProfileGraphWriterTest with multiple forms:

@Test
public void testWriteArtifactProfileMultipleForms() throws IOException {
    String expectedProfile = PREFIXES +
            ".\n" +
            "<urn:profile> a hmas:ResourceProfile ;\n" +
            " hmas:isProfileOf [ a hmas:Artifact ];\n" +
            " hmas:exposesSignifier [ a hmas:Signifier ;\n" +
            "    hmas:signifies [ 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:or (;\n" +
            "             [ sh:hasValue <urn:3g6lpq9v> ]\n" +
            "             [ sh:hasValue <urn:x7aym3hn> ]\n" +
            "         ) ;\n" +
            "      ]\n" +
            "   ]\n" +
            "] .\n" +
            "<urn:3g6lpq9v> a hctl:Form ;\n" + //here the URN is randomly generated
            "   hctl:hasTarget <https://example.org/resource> .\n" +
            "<urn:x7aym3hn> a hctl:Form ;\n" + //here the URN is randomly generated
            "   hctl:hasTarget <coaps://example.org/resource> .";

     Form coapForm = new Form.Builder("coaps://example.org/resource").build();

     Set<Form> forms = new HashSet<>(Arrays.asList(BASIC_FORM, coapForm));

    ActionSpecification actionSpec = new ActionSpecification.Builder(forms).build();

    ArtifactProfile profile =
            new ArtifactProfile.Builder(new Artifact.Builder().build())
                    .setIRIAsString("urn:profile")
                    .exposeSignifier(new Signifier.Builder(actionSpec).build())
                    .build();

    assertIsomorphicGraphs(expectedProfile, profile);
  }
danaivach commented 1 year ago

Example test in ArtifactProfileGraphReaderTest with multiple forms:

@Test
public void testReadArtifactProfileMultipleForms() throws IOException {
    String expectedProfile = PREFIXES +
            ".\n" +
            "<urn:profile> a hmas:ResourceProfile ;\n" +
            " hmas:isProfileOf [ a hmas:Artifact ];\n" +
            " hmas:exposesSignifier [ a hmas:Signifier ;\n" +
            "    hmas:signifies [ 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:or (;\n" +
            "             [ sh:hasValue <urn:3g6lpq9v> ]\n" +
            "             [ sh:hasValue <urn:x7aym3hn> ]\n" +
            "         ) ;\n" +
            "      ]\n" +
            "   ]\n" +
            "] .\n" +
            "<urn:3g6lpq9v> a hctl:Form ;\n" + //here the URN is randomly generated
            "   hctl:hasTarget <https://example.org/resource> .\n" +
            "<urn:x7aym3hn> a hctl:Form ;\n" + //here the URN is randomly generated
            "   hctl:hasTarget <coaps://example.org/resource> .";

     ArtifactProfile profile =
            ArtifactProfileGraphReader.readFromString(expectedProfile);

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

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

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

    ActionSpecification actionSpec = (ActionSpecification) signifier.getBehavioralSpecification();
    Set<Form> forms = actionSpec.getForms();
    assertEquals(2, forms.size());

    assertTrue(forms.stream().anyMatch(form ->
      "https://example.org/resource".equals(form.getTarget()) && "urn:3g6lpq9v".equals(form.getIRIAsString()) ));

   assertTrue(forms.stream().anyMatch(form ->
      "coaps://example.org/resource".equals(form.getTarget()) && "urn:x7aym3hn".equals(form.getIRIAsString()) ));  
  }
danaivach commented 1 year ago

An option (presented in the examples above) is to create a class ActionSpecification (maybe subclass of AbstractResource, with the type attribute set by default to SHACL.TERM.NODESHAPE), that can be initialized with minimum one Form (later, optionally with an input etc.).

When serializing an ActionSpecification (along with its forms, input etc.) the SHACL and PROV vocabularies are used).

@alessandro-marcantoni, let me know of any alternatives you are considering. You can write here a couple of example tests (e.g. one for the reader and one for the writer), like the ones provided above, that use your proposed abstractions!

danaivach commented 1 year ago

@alessandro-marcantoni, please, also test how this hmas:ActionExecution validates against the relevant ActionSpecification: https://github.com/HyperAgents/hmas/issues/116#issuecomment-1609123326