epimorphics / elda

Epimorphics implementation of the Linked Data API
Other
53 stars 27 forks source link

Add info about shortnames to the XML #236

Open rwalkerands opened 9 months ago

rwalkerands commented 9 months ago

This is somewhat related to https://github.com/epimorphics/elda/issues/221.

I'm (yes, still) using Elda 1.4.3, but I've just also tested with 2.0.2 and the underlying issue is the same. And I'm (yes, still) using XSLT to generate HTML.

The XML that is fed into the XSLT doesn't contain information about which shortnames are valid, i.e., which ones are explicitly defined for the endpoint using the api:label property. As a result, the generated HTML page may offer icons and links that won't work, and indeed, produce the error screen "Sorry ... That shortname is unrecognised."

Current example that shows the problem: https://test.vocabs.ardc.edu.au/repository/api/lda/autestingorgrole/r29-testing/v1/resource?uri=http://test.ands.org.au/testresolution/eh_tmc/concepts/100394

Click either:

It'd be helpful if the <termBinding> element were enhanced to flag the shortnames that are explicitly defined. Then the XSLT can stop generating those icons/links that won't work.

For example, the XML for the above page has a <termBinding> element that includes this fragment:

...
<item id="_:item-1029">
  <label>issued</label>
  <property href="http://purl.org/dc/terms/issued"/>
  </item>
<item id="_:item-1030">
  <label>label</label>
  <property href="http://www.w3.org/2000/01/rdf-schema#label"/>
</item>
...

From this, it would appear that both issued and label are both "usable" shortnames. But only the second one is.

Random example of a way of addressing this:

...
<item id="_:item-1029">
  <label>issued</label>
  <property href="http://purl.org/dc/terms/issued"/>
  </item>
<item id="_:item-1030">
  <label defined="true">label</label>
  <property href="http://www.w3.org/2000/01/rdf-schema#label"/>
</item>
...
rwalkerands commented 9 months ago

This is also further to https://github.com/epimorphics/elda/issues/171; therefore, see also the fixes made in commits ae1a2fbc108c10d21eb7f4c6d0bcf55fe97300ed and 7ea2e64344b8023069981edb9a1905212924c75b.

rwalkerands commented 9 months ago

OK, so on examination of the code, I quickly figured out how to add an element, but not an attribute.

So here's a quick-and-dirty-hack to see if it's possible to add an element for termBindings for which the shortname is defined:

diff -U10 ./elda-lda/src/main/java/com/epimorphics/lda/core/EndpointMetadata.java-o ./elda-lda/src/main/java/com/epimorphics/lda/core/EndpointMetadata.java
--- ./elda-lda/src/main/java/com/epimorphics/lda/core/EndpointMetadata.java-o   2021-09-22 00:12:09.000000000 +1000
+++ ./elda-lda/src/main/java/com/epimorphics/lda/core/EndpointMetadata.java 2024-01-11 15:43:37.073859061 +1100
@@ -300,31 +300,37 @@
    }

    int bnodeCounter = 1000;

    private Resource createBNode(Model m) {
        Resource b = m.createResource( new AnonId( "bnode-" + bnodeCounter++ ) );
        return b;
    }

    public void addTermBindings( Model toScan, Model meta, Resource exec, CompleteContext cc ) {
+       Map<String, String> termBindingsBeforeIncluding = cc.Do();
+       Set<String> uriSetBeforeIncluding = new HashSet<>();
+       uriSetBeforeIncluding.addAll(termBindingsBeforeIncluding.keySet());
        Map<String, String> termBindings = cc.include(toScan).Do();
        List<String> uriList = new ArrayList<String>( termBindings.keySet() );
        Collections.sort( uriList );
        for (String uri: uriList) {
            Resource term = meta.createResource( uri );
            if (toScan.containsResource( term )) {
                String shorty = termBindings.get( uri );
                Resource tb = createBNode( meta );
                exec.addProperty( API.termBinding, tb );
                tb.addProperty( API.label, shorty );
                tb.addProperty( API.property, term );
+               if (uriSetBeforeIncluding.contains(uri)) {
+                   tb.addProperty( API.definition, "true" );
+               }
            }
        }
    }

    // following the Puelia model.
    public void addExecution( Model meta, Resource anExec ) {
        Resource exec = anExec.inModel(meta), page = thisPage.inModel(meta);
        exec.addProperty( RDF.type, API.Execution );
        Resource P = createBNode( meta );
        ELDA.addEldaMetadata( P );

(Yes, I overloaded the use of the api:definition property.)

Well, it seems to produce the correct answer. The resulting XML output now shows:

         <item id="_:item-1029">
            <label>issued</label>
            <property href="http://purl.org/dc/terms/issued"/>
         </item>
         <item id="_:item-1030">
            <api_definition>true</api_definition>
            <label>label</label>
            <property href="http://www.w3.org/2000/01/rdf-schema#label"/>
         </item>
rwalkerands commented 9 months ago

In my own testing, this seems to be working correctly, with one exception: there's some strangeness with a non-shortname publisher. The XML now has:

         <item id="_:item-1039">
            <label>publisher</label>
            <property href="http://purl.org/dc/terms/publisher"/>
         </item>

i.e., without the new api_definition element. I also checked the /api-config page and drilled down to the endpoint's list of shortnames. It doesn't list publisher as a shortname either. And I checked my endpoint definition, and I definitely don't add an api:label for it.

But publisher still seems to work as a shortname, i.e., adding _properties=publisher to the URL succeeds.

(Edit: temporarily removed a URL showing this as it's not ready to show yet.)

rwalkerands commented 9 months ago

OK, I forgot about auto-generation of shortnames; that explains why publisher "works" as a shortname despite my not giving it an api:label. The issue then is: how to mark them also as "defined" in the termBindings?

rwalkerands commented 9 months ago

I re-did my quick-and-dirty-hack to use the ShortnameService. It now produces the "correct answers".

$ diff -U10 ./elda-lda/src/main/java/com/epimorphics/lda/core/EndpointMetadata.java-o ./elda-lda/src/main/java/com/epimorphics/lda/core/EndpointMetadata.java
--- ./elda-lda/src/main/java/com/epimorphics/lda/core/EndpointMetadata.java-o   2021-09-22 00:12:09.000000000 +1000
+++ ./elda-lda/src/main/java/com/epimorphics/lda/core/EndpointMetadata.java 2024-01-16 15:52:37.407510677 +1100
@@ -10,20 +10,21 @@
 import java.util.*;

 import com.epimorphics.lda.bindings.Bindings;
 import com.epimorphics.lda.core.APIResultSet.MergedModels;
 import com.epimorphics.lda.metadata.MetaConfig;
 import com.epimorphics.lda.core.property.ViewProperty;
 import com.epimorphics.lda.query.QueryParameter;
 import com.epimorphics.lda.query.WantsMetadata;
 import com.epimorphics.lda.renderers.Factories.FormatNameAndType;
 import com.epimorphics.lda.shortnames.CompleteContext;
+import com.epimorphics.lda.shortnames.ShortnameService;
 import com.epimorphics.lda.sources.Source;
 import com.epimorphics.lda.specs.APIEndpointSpec;
 import com.epimorphics.lda.specs.EndpointDetails;
 import com.epimorphics.lda.support.PropertyChain;
 import com.epimorphics.lda.vocabularies.*;
 import com.epimorphics.util.URIUtils;
 import com.hp.hpl.jena.graph.compose.MultiUnion;
 import com.hp.hpl.jena.rdf.model.*;
 import com.hp.hpl.jena.sparql.vocabulary.FOAF;
 import com.hp.hpl.jena.util.ResourceUtils;
@@ -33,32 +34,44 @@
     Class to handle the construction of metadata for API endpoint results.
     Bit of a hotchpotch at the moment.

     @author Chris
 */
 public class EndpointMetadata {

    protected final Bindings bindings;
    protected final Resource thisPage;
    protected final URI thisPageAsURI;
+   protected final ShortnameService sns;

    protected final String pageNumber;
    protected final boolean isListEndpoint;
    protected final boolean isParameterBasedFormat;

    public EndpointMetadata( EndpointDetails ep, Resource thisPage, String pageNumber, Bindings bindings ) {
        this.bindings = bindings;
        this.thisPage = thisPage;
        this.pageNumber = pageNumber;
        this.isListEndpoint = ep.isListEndpoint();
        this.isParameterBasedFormat = ep.hasParameterBasedContentNegotiation();
        this.thisPageAsURI = URIUtils.newURI( thisPage.getURI() );
+   this.sns = null;
+   }
+
+   public EndpointMetadata( EndpointDetails ep, Resource thisPage, String pageNumber, Bindings bindings, ShortnameService sns ) {
+       this.bindings = bindings;
+       this.thisPage = thisPage;
+       this.pageNumber = pageNumber;
+       this.isListEndpoint = ep.isListEndpoint();
+       this.isParameterBasedFormat = ep.hasParameterBasedContentNegotiation();
+       this.thisPageAsURI = URIUtils.newURI( thisPage.getURI() );
+   this.sns = sns;
    }

    public static void addAllMetadata
        ( APIEndpointSpec spec
        , MergedModels mergedModels
        , URI fullURI
        , Resource uriForDefinition
        , Bindings bindings
        , CompleteContext cc
        , boolean suppress_IPTO
@@ -133,21 +146,21 @@
                .addProperty( API.definition, uriForDefinition )
                .addProperty( RDF.type, API.ListEndpoint )
                ;
        } else {
            Resource content = firstOf(resultList).inModel(metaModel);
            thisMetaPage.addProperty( FOAF.primaryTopic, content );
            thisMetaPage.addProperty( RDF.type, API.ItemEndpoint );
            if (suppress_IPTO == false) content.addProperty( FOAF.isPrimaryTopicOf, thisMetaPage );
        }
    //
-       EndpointMetadata em = new EndpointMetadata( details, thisMetaPage, "" + page, bindings);
+       EndpointMetadata em = new EndpointMetadata( details, thisMetaPage, "" + page, bindings, spec.getAPISpec().getShortnameService() );
        Model metaModel1 = mergedModels.getMetaModel();
        Model mergedModels1 = mergedModels.getMergedModel();
    //
        Resource exec = metaModel1.createResource();
        Model versionsModel = ModelFactory.createDefaultModel();
        Model formatsModel = ModelFactory.createDefaultModel();
        Model bindingsModel = ModelFactory.createDefaultModel();
        Model execution = ModelFactory.createDefaultModel();
    //
        em.addVersions( versionsModel, cc, views );
@@ -311,20 +324,23 @@
        List<String> uriList = new ArrayList<String>( termBindings.keySet() );
        Collections.sort( uriList );
        for (String uri: uriList) {
            Resource term = meta.createResource( uri );
            if (toScan.containsResource( term )) {
                String shorty = termBindings.get( uri );
                Resource tb = createBNode( meta );
                exec.addProperty( API.termBinding, tb );
                tb.addProperty( API.label, shorty );
                tb.addProperty( API.property, term );
+               if ( sns != null && sns.expand(shorty) != null ) {
+                   tb.addProperty( API.definition, "true" );
+               }
            }
        }
    }

    // following the Puelia model.
    public void addExecution( Model meta, Resource anExec ) {
        Resource exec = anExec.inModel(meta), page = thisPage.inModel(meta);
        exec.addProperty( RDF.type, API.Execution );
        Resource P = createBNode( meta );
        ELDA.addEldaMetadata( P );