It’s been a few months since my last post. Since then I was able to give a talk a the IBM ELM Users Conference, pretty much taking people through the prior series of blog posts. It was exciting to hear the reception of theses blog posts.
The prior post showed how “easy” it can be to create a POST based on the OSLC discovery process and the resource shape. While my goal is to make the consumption of the ELM APIs as easy as possible, there are some very powerful and complex concepts that I think I need to address at this time.
What is a Resource Shape?
Stated simply, a resource shape is a set of assertions constraints that are applied to a resource. These constrains consist of assertions in the form of triples. A triple is simply a subject, predict and object. This is foundational to how RDF is represented. RDF is key to the semantic web, which is all about linked data. The ability to make any data machine accessible thru this approach allows for applications to have an open approach to collecting, analyzing and reporting any data. The ELM applications are based on this concept.
Wow, that’s a meaty paragraph.
Let’s look deeper a the triple and what it means when trying to interpret the resource shape. I will do it based on our POST example for generating a Test Plan. I am only going to show the Body which is a rdf/xml representation of the Test Plan we wanted to generate.
<rdf:RDF
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:dcterms="http://purl.org/dc/terms/"
xmlns:oslc_qm="http://open-services.net/ns/qm#"
xmlns:oslc_rm="http://open-services.net/ns/rm#"
xmlns:rqm_qm="http://jazz.net/ns/qm/rqm#"
xmlns:foaf="http://xmlns.com/foaf/0.1/"
xmlns:oslc="http://open-services.net/ns/core#"
xmlns:process="http://jazz.net/ns/process#"
xmlns:rqm_process="http://jazz.net/xmlns/prod/jazz/rqm/process/1.0/"
xmlns:calm="http://jazz.net/xmlns/prod/jazz/calm/1.0/"
>
<oslc_qm:TestPlan>
<dcterms:title>Test plan created from API</dcterms:title>
<dcterms:description>Here'\''s a really long description that was created by typing a bunch of words.</dcterms:description>
<oslc:formalReview/>
<oslc:hasChildPlan/>
<rqm_qm:catagory/>
<oslc:hasPriority/>
<foaf:contributor/>
<oslc:template/>
<oslc:relatedChangeRequest/>
<process:iteration/>
<oslc:testSchedule/>
<process:teamArea/>
<oslc:hasWorkflowState/>
<oslc:runsOnTestEnvironment/>
<oslc:usesTestCase/>
<oslc:keyDate/>
<oslc_qm:testsDevelopmentPlan/>
<oslc:attachment/>
<rqm_qm:objectiveStatusGroup/>
<oslc:risk/>
<oslc:containsTestSuite/>
<rqm_qm:executionEffort>42.0</rqm_qm:executionEffort>
<oslc:category_PML_F4kaEeynq4H4YH03kw/>
<oslc:category_PMRep4kaEeynq4H4YH03kw/>
<oslc:category_PL_KwIkaEeynq4H4YH03kw/>
<oslc_rm:validatesRequirementCollection/>
<rqm_qm:planningEffort>42.0</rqm_qm:planningEffort>
</oslc_qm:TestPlan>
</rdf:RDF>
The first thing I’ll do is use the handy “RDF Validator” made available on IBM’s cloud to see what this would look like once validated.
This validator will take a look at all the triples and allow me to covert between rdf/xml and either json or text/turtle. This will also allow us to see how the system will interpret our resource.
Converting the above rdf/xml to a text/turtle provide the following output:
@prefix oslc_qm: <http://open-services.net/ns/qm#> .
@prefix calm: <http://jazz.net/xmlns/prod/jazz/calm/1.0/> .
@prefix process: <http://jazz.net/ns/process#> .
@prefix rdf: <http://www.w3.org/1999/02/22-rdf-syntax-ns#> .
@prefix dcterms: <http://purl.org/dc/terms/> .
@prefix rqm_process: <http://jazz.net/xmlns/prod/jazz/rqm/process/1.0/> .
@prefix oslc_rm: <http://open-services.net/ns/rm#> .
@prefix rqm_qm: <http://jazz.net/ns/qm/rqm#> .
@prefix foaf: <http://xmlns.com/foaf/0.1/> .
@prefix oslc: <http://open-services.net/ns/core#> .
[ a oslc_qm:TestPlan ;
process:iteration "" ;
process:teamArea "" ;
rqm_qm:catagory "" ;
rqm_qm:executionEffort "42.0" ;
rqm_qm:objectiveStatusGroup "" ;
rqm_qm:planningEffort "42.0" ;
oslc:attachment "" ;
oslc:category_PL_KwIkaEeynq4H4YH03kw
"" ;
oslc:category_PML_F4kaEeynq4H4YH03kw
"" ;
oslc:category_PMRep4kaEeynq4H4YH03kw
"" ;
oslc:containsTestSuite "" ;
oslc:formalReview "" ;
oslc:hasChildPlan "" ;
oslc:hasPriority "" ;
oslc:hasWorkflowState "" ;
oslc:keyDate "" ;
oslc:relatedChangeRequest "" ;
oslc:risk "" ;
oslc:runsOnTestEnvironment "" ;
oslc:template "" ;
oslc:testSchedule "" ;
oslc:testsDevelopmentPlan "" ;
oslc_qm:usesTestCase "" ;
oslc_rm:validatesRequirementCollection
"" ;
dcterms:description "Here'\\''s a really long description that was created by typing a bunch of words." ;
dcterms:title "Test plan created from API" ;
foaf:contributor ""
]
The second thing we can do is look at if our resource creation is valid, based on the constraints in the resource shape. Let’s do that:
First off we see that all of the empty tags that were passed in the above API call, are converted from a null value to empty strings. Using the same triple, let’s go back and look at the resource shape defined for the process:iteration from our GET API on the Test Plan’s Resource shape.
As you can see in the valueType should be a iteration shaped resource, not a null string.
[ a oslc:Property ;
oslc:hidden false ;
oslc:isMemberProperty false ;
oslc:name "iteration"^^<http://www.w3.org/2001/XMLSchema#string> ;
oslc:occurs oslc:Zero-or-one ;
oslc:propertyDefinition process:iteration ;
oslc:range process:Iteration ;
oslc:readOnly false ;
oslc:representation oslc:Reference ;
oslc:valueShape <http://jazz.net/ns/process/shapes/Iteration> ;
oslc:valueType oslc:Resource ;
dcterms:description "The development iteration associated with the Test Plan."^^<http://www.w3.org/2001/XMLSchema#string> ;
dcterms:title "Iteration"^^<http://www.w3.org/2001/XMLSchema#string>
] .
Each triple is made up of a subject (URI), a predicate (URI) and an Object (which can be either a simple or complex object). Simple objects are primitive data types, String, Int, etc., while complex types, are further defined as URIs. So any triple will consist of URI, URI, and URI or Primitive data type.
And if we look at few of the triples in the Iteration resource we see the following triples:
- A oslc:Property (subject), oslc:propertyDefinition (predicate), contains a process:iteration (object).
- A oslc:Property (subject), oslc:occurs (predicate), exists oslc:Zero-or-one (attribute).
- A oslc:Property (subject), oslc:valueType (predicate), contains a oslc:Resource (object).
- A oslc:Property (subject), oslc:valueShape (predicate) exists as https://jazz.net/ns/process/shapes/Iteration (attribute).
The last one tells us that the constraints of a this specific property (process:iteration) is defined at https://jazz.net/ns/process/shapes/iteration. The definition is not part of OSLC, but it is defined by the Jazz process implemented on this server. This is a complex object type not a simple XMLLiteral like String, Int, or Date. As such, if we provide a value for this property it will constrained by the resource shape’s assertions.
We also see this because our valueType is an oslc:Resource (which can be found at http://open-services.net/ns/core#Resource) which states it must resolve to a URI.
We can continue to resolve each of the URIs in the triple.
Looking at the second item, we see that the property value type indicates that is exists Zero or more times. If we look at the URI for oslc:Zero-or-one (http://open-services.net/ns/core#Zero-or-many) that it defines the iteration property as optional and multi-valued. If we provide a value, it must be a valid property as defined by the valueType (an OSLC resource, defined as an object0 and it will be constrained further by the resources shape defined by https://jazz.net/ns/process/shapes/Iteration.
Given all this, we can easily see now that our original assertion was incorrect. We cannot just use process:iteration with an empty string.
Let’s look at one other property in the TestPlan resource shape to see a simple object. The rqm_qm:executionEffort is constrained in text/turtle as follows:
[ a oslc:Property ;
oslc:hidden false ;
oslc:isMemberProperty false ;
oslc:name "executionEffort"^^xsd:string ;
oslc:occurs oslc:Zero-or-one ;
oslc:propertyDefinition rqm_qm:executionEffort ;
oslc:readOnly false ;
oslc:valueType xsd:float ;
dcterms:description "The execution effort that the Test Plan defined in person hour."^^xsd:string ;
dcterms:title "Execution Effort"^^xsd:string , "Execution Effort"@en
] .
As we see this is much simpler – showing an optional field that is constrained with the following triple:
- A oslc:Property (subject), oslc:valueType (Predicate), is of type xsd:float (attribute).
- A oslc:Property (subject), oslc:occurs (predicate), exists oslc:Zero-or-one (attribute).
So we assert a simple float, that is an optional value.
What did we learn?
Sometimes a rule of thumb is just that. It is a simple guidance to get you started.
But in OSLC you need to be more precise. Look at the details of the assertions and ensure that you fully compliant. You may or may not generate an error on your POST depending on the assertions.
For example, in the above post, I am using “oslc:usesTestCase”. This is not the correct property, it should be “oslc_qm:usesTestCase”, however, since “oslc:usesTestCase” has no assertions defined for it, it will not cause the POST to fail.