Client POC

Hi Hydranians

I've just comitted a working demo of an angularJS client talking to the .net ReST API, both from my Ultimate ResT API (URSA) framework.
The demo is available at http://www.szczepanscy.priv.pl/ursa/
Please treat it as a proof-of-concept and an experiment. Indeed I plan to evolve it, but I wasn't sure of the effects. Please also do not try to hookup your own APIs as my current implementation may bot be fully Hydra compliant (read for more details below).
I've tested it with Edge, IE11 and Chrome, but errors may still appear here and there (I hope not). I've set that domain today thus DNS may bot be populated yet.

This hydro-magneto-whateverlator in general talks to the given entry point, obtains an API Documentation, builds necessary views programmatically and puts the machinery in motion. You're free to click it through - just bear in mind that this is persistentless server side code running. All changes are stored in memory, thus if some reason server will stop the process all changes are gone. Anyway, it should give a hint on what's going on.

Summarizing, few facts:
- only API documentation is used, but for operations like POST a Location header is provided and for lists a Content-Range header is provided as an HATOAS elements; I think that when it comes to business applications and UI real HATOAS state-by-state approach won't work as the application cannot suprise users with unpredicted outcomes - some knowledge ahead is necessary
- class description in general uses OWL restrictions on properties - not very convinient
- operations returns or expects anonymous sub classes of their respective classes to provide additional contextual details, like how a given class functions as an expected instance
- custom predicate is introduced to denote that a single value is expected/returned; if not set multiple instances are assumed
- custom predicate is introduced to denote acceptable media types
- custom predicates are introduced for limit/offset mappings
- I'm using owl:InverseFunctionalProperty to mark classe's property as a primary key one; it's not elegant as for datatypes this moves whole RDF to OWL Full, but for the time beeing let it be that way
- whole Hydra may be a little bit outdated - I'm unable to test it with the Hydra Console as Markus broke the PHP's JSON-LD processor ;-)
- I'm not using few Hydra's predicates like hydra:description in favour to their generics like rdfs:comment as I see no use for them
- currently client doesn't interprete readable/writeable/required properties correctly
- server produces an API documentation on the fly from the code (reflection and few other mechanisms), thus every change in that should be reflected and adopted by the client

Let's me give several areas that I struggled a lot during the implementation.

1. API Documentation model gives a lot of freedom. I think it's too much of it. I already had that conversation with Ruben that sticking to RDFS/OWL we put a lot of burdain on the clients. This is indeed the case. Example: in order to express that an operation returns single instance of a given type I used Markus' suggestion to use a subclass of that type with few extension predicates to denote cardinality details. Hydra doesn't give any way of expressing that (or no obvious way), and as an alternative to what I've used would be using a hydra:Collection as a returns but we'd loose information on what type of the members are.
Same issue goes for describing the classes. I've used OWL cardinality constraints on properties, but this opens doors to much complicated constructs I don't want a Javascript client to touch.
I'd stick to similar approach guys from SHACL used - OWL model of SHACL almost doesn't exist - just few classes and properties, everything is defined using ... SHACL itself which seems to be more fitting the Closed World Assumption and it's classic data structures than Open World Assumption and RDF. Client understanding SHACL doesn't need to bother of what an OWL reasoner may find as it won't find anything - properties has neither ranges nor domains, thus when attaching a property to an instance won't give any unexpected discovery.

2. Another issue I had was with pagination. Again, API Documentation is silent in this area. In order to make the client aware of pagination I had to use yet another custom predicate used for IriTemplate mappings (skip and take), which then are fed by the client with values from the UI.
In order to cover full support for both RDF (by means of JSON-LD) and classic JSON serializations, I used custom Content-Range unit named "members", as I wouldn't be able to inject hypermedia into a JSON payload (or this extra would break lots of things). This is also more generic.

3. Hydra's model also doesn't fit to ... datatypes (in terms of RDF/XSD datatypes). In order to denote that an operation returns values of type xsd:string I had to elevate it to hydra:Class, which I think is not very elegant. I think this moves whole RDF content to OWL Full as only in this profile datatypes are not disjoint from classes. Correct me if I'm wrong, but if it's not the case, still datatype shouldn't be considered a class - at least I think these shouldn't be.

4. We had also several duscussions on how to expand Hydra's capabilities to non-RDF formats and I always statet that not that much of an effort is needed to make it happen. In general, I describe JSON classes same way as those RDF ones, only difference is that those classes/properties IRIs are not HTTP referencable. I used 'javascript': scheme instead - this doesn't break RDF and still Javascript client may be capable of analyzing these data. I had to use a custom predicate on operation to denote allowed media types, which still may be useful for RDF as not all serialization formats are always available. I think in case of business applications sometimes content negotiations won't work nicely and few details described ahead comes in handy.

Ok, that's what has been implemented. As for further investigation I plan to cover somehow these:
- ordered lists support
- key-value pair map/dictionary like structures support
- sorting lists - there is nothing about that in Hydra for now and I see no reasonable external solution
- currently, all properties are literals; if having properties with relations to other resources, question araises of how to tell the client on where to get the values from; if no explicit pointer is set client may search the API documentation for a given supported class and try to find a suitable method, but something explicitely defined may disambiguate it (i.e. custom search-as-you-type operation accepting an user input that will be use as a free text query). Something like OWLs allValuesFrom but pointing to an operation might come in handy.

Feel free to make suggestions. I’m also opened for criticism.

Best

Karol Szczepański

Received on Saturday, 7 November 2015 18:16:20 UTC