Purpose
A temporary page to collect scenarios, arguments, and information needed to define states and state transitions of CM resources.
Introduction
Probably the most important property of a Change Request is the status property. "Status" specifies the location of a Change Request in a workflow. In queries the status property is used to filter change request (e.g. all change requests that are "fixed") and it is used to perform state transitions on a change request, e.g. closing a change request as "fixed".
The problem is that different CM systems use different properties (or even a set of properties, like RTC's "state" and "resolution" attributes) and different values to represent the change request's state. Even providing access to meta data does not help because knowing all possible state values does not reveal the semantics of a state.
In addition customization makes it possible to modify state value sets and workflows even further. This makes it almost impossible for another application to query or manipulate the state of a change request in a robust way.
In the following proposal we try to find a way to introduce state semantics for Change Requests. Based on this, applications can manipulate Change Requests in a "robust" way independent from the underlying CM system or any customizations that took place.
Please note that the proposal is not meant to help with creating a generic UI for manipulating the status of a Change Request.
Proposal
To address the problems from above in OSLC-CM v2 we propose to use the concept of "predicates" and "actions" instead of manipulating state properties directly via GET and PUT.
A "predicate" is basically a synthetic property representing a semantic aspect of a change request. E.g. whether a change request is "fixed" is a useful Boolean predicate for change requests. A predicate has the characteristic of a "facade" because it hides the details of where (which attribute) and how (which value) the state is represented. E.g. for RTC a "fixed" predicate would be implemented as the condition "state = resolved AND resolution = fixed". But clients would not need to know this implementation detail.
Predicates can be used like real read-only properties of a change request, so they can be specified in GET operations and queries. They can not be used in PUT or POST operations because in many cases a state transition is more involved than just changing the value of an property. E.g. depending on the process used a transition to "fixed" could enforce a verification step which would introduce additional intermediate states and require a signature. That's the reason why state transitions are performed through "actions".
Corresponding to a predicate is a set of "actions" that allow to perform a state transition on the Change Request. So for the predicate "fixed" it would make sense to provide an action "resolve to fixed" and maybe an "reopen as unfixed" action. Actions can take additional arguments. So a "resolve as duplicate" action would require a reference (URI) of another Change Request.
Since a predicate should only cover a specific aspect of a change request, we envision that OSLC-CM v2 defines a small set of predicates for different use cases. E.g. one predicate covers a fundamental but abstract state like Open and another predicate can be used to determine whether a Change Request is fixed.
Representing Predicates and Actions in a RESTful way
To keep things simple, we propose that predicates are directly mapped to a property. So in a GET operation on the Change Request the predicate's value is returned for the property.
For representing actions I see these choices:
- Posting the action as part of a "command" resource to a specific URL (TODO: find reference to "command" approach). The command resource contains the action, the change resource, and any additional properties that are required for the action, e.g. another change request for a "Resolve as Duplicate" action.
- Specifying the action as a value for an additional query-argument "action" used in a PUT (or POST) request. So if a PUT updates a changes resource, the additional action argument triggers that the specified action is performed.
- Using a specific URL for every action. The action URLs are available through the predicate property and the list of actions contains only those actions that are available in the current state of the Change Resource. Doing a PUT on one of these action URLs updates the resource and performs the action. (This is the HATEOAS principle. See "Level 3" in Martin Fowler's article Richardson Maturity Model: steps toward the glory of REST)
- Adding a new property "action" to the Change Resource. The value is the action to be performed on PUT. On GET the property "action" would not be returned.
The advantage of methods 2-4 is that any additional properties that are required for the action, can be passed quite naturally because they are already part of the resource definition, whereas in the first method, a newly introduced command resource needs to include the required properties explicitly.
Methods 2 and 4 both use additional non-obvious properties or arguments to encode "verbs". This does not align well with a RESTful approach.
The third method has the advantage over the second that no "URL path-math" is necessary and that the structure of a URL is not constrained in any way.
Since we do not see any advantage in the additional complexity of the first method, we propose that we use the third method.
Predicates
Predicates are exposed as single-value read-only properties on a Change Request resource. An attempt to update them explicitly in a partial update request SHOULD be answered with a 409 Conflict HTTP status code. Their presence in a resource representation used for an update via PUT MUST be silently ignored. Predicates MUST be queryable. The table below lists the proposed predicates. The authoritative list will be found in
CmSpecificationV2.
Property |
Type |
Occurs |
Title |
Description |
oslc_cm:open |
boolean |
at-most-one, readOnly |
Open |
Whether or not the resource is in an open state |
oslc_cm:inprogress |
boolean |
at-most-one, readOnly |
InProgress? |
Whether or not the resource in a state indicating that active work is occurring |
oslc_cm:fixed |
boolean |
at-most-one, readOnly |
Fixed |
Whether or not the resource has been fixed |
oslc_cm:closed |
boolean |
at-most-one, readOnly |
Closed |
Whether or not the resource is completely done, meaning no further work is occurring |
oslc_cm:approved |
boolean |
at-most-one, readOnly |
Approved |
Whether or not the resource has been approved |
Dependencies: If oslc_cm:inprogress is true, oslc_cm:open must also be true
Actions
Actions are exposed as single-value read-only properties of type Resource in a Change Request resource. The URI of such a reference property ("Action URI") points to the resource that handles the state transition. An attempt to update an action property explicitly in a partial update request SHOULD be answered with a 409 Conflict HTTP status code. Their presence in a resource representation used for an update via PUT MUST be silently ignored.
An Action URI has the same update semantics as the canonical resource URI. A resource can be updated by a PUT to the Action URI. It can also be updated by a partial update request via PATCH (
OSLCCorePartialUpdateDRAFT) to the Action URI. Along with the update, the state transition is performed. HTTP requests other than PUT and PATCH (or a POST used with X-HTTP-Method-Override) SHOULD be answered with a 405 Method Not Allowed HTTP status code.
The CR resource representation SHOULD only include the actions that are applicable to the current state of the resource. If an action is performed and the precondition for a state transition is not met, the request MUST be answered with a 409 Conflict status code.
An action typically leads to a change in a predicate property. The spec doesn't guarantee any specific dependencies, however.
The table below lists the proposed actions. The authoritative list will be found in
CmSpecificationV2.
Property |
Type |
Occurs |
Description |
oslc_cm:actionResolve |
Resource |
at-most-one, readOnly |
Mark the resource as Resolved. Typically, the predicate oslc_cm:fixed becomes true |
oslc_cm:actionClose |
Resource |
at-most-one, readOnly |
Mark the resource as completely done, meaning no further work is occurring. Typically, the predicate oslc_cm:closed becomes true |
oslc_cm:actionStartWorking |
Resource |
at-most-one, readOnly |
Mark the resource as 'In Progress', meaning that the resource is actively being worked on. Typically, the predicate oslc_cm:inprogress becomes true |
oslc_cm:actionReopen |
Resource |
at-most-one, readOnly |
Mark that the resource is falsely in a resolved state. Typically, the predicate oslc_cm:open becomes true |
Basic Example
A CM resource representation with actions and predicates:
<oslc_cm:ChangeRequest
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:dc="http://purl.org/dc/terms/"
xmlns:oslc_cm="http://open-services.net/xmlns/cm/2.0#"
rdf:about="http://example.com/bugs/2314">
<dc:title> Provide import </dc:title>
<dc:identifier> 2314 </dc:identifier>
<oslc_cm:open>true</oslc_cm:open>
<oslc_cm:inprogress>false</oslc_cm:inprogress>
<oslc_cm:fixed>false</oslc_cm:fixed>
<oslc_cm:closed>false</oslc_cm:closed>
<oslc_cm:approved>false</oslc_cm:approved>
<oslc_cm:actionResolve rdf:resource="http://example.com/bugs/2314/resolve"/>
<oslc_cm:actionStartWorking rdf:resource="http://example.com/bugs/2314/start"/>
</oslc_cm:ChangeRequest>
To change the CR's state to 'In Progress', you would update the CR using the
oslc_cm:actionStartWorking
action URL:
PUT /bugs/2314/start HTTP/1.1
<oslc_cm:ChangeRequest ...
rdf:about="http://example.com/bugs/2314">
...
</oslc_cm:ChangeRequest>
After the update, the CR resource representation will look like
<oslc_cm:ChangeRequest
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:dc="http://purl.org/dc/terms/"
xmlns:oslc_cm="http://open-services.net/xmlns/cm/2.0#"
rdf:about="http://example.com/bugs/2314">
<dc:title> Provide import </dc:title>
<dc:identifier> 2314 </dc:identifier>
<oslc_cm:open>true</oslc_cm:open>
<oslc_cm:inprogress>true</oslc_cm:inprogress>
<oslc_cm:fixed>false</oslc_cm:fixed>
<oslc_cm:closed>false</oslc_cm:closed>
<oslc_cm:approved>false</oslc_cm:approved>
<oslc_cm:actionResolve rdf:resource="http://example.com/bugs/2314/resolve"/>
</oslc_cm:ChangeRequest>
Example with parameters
Actions can rely on additional properties being set in order to succeed. An example (not proposed for the OSLC-CM 2.0 spec):
Predicate:
oslc_cm:duplicate |
boolean |
at-most-one, readOnly |
Open |
Whether or not the resource is a duplicate of another resource |
Action:
oslc_cm:actionResolveAsDuplicate |
Resource |
at-most-one, readOnly |
Mark the resource as a duplicate of another resource. |
Requires exactly one oslc_cm:duplicateOf property |
A CM resource representation to start with:
<oslc_cm:ChangeRequest
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:dc="http://purl.org/dc/terms/"
xmlns:oslc_cm="http://open-services.net/xmlns/cm/2.0#"
rdf:about="http://example.com/bugs/2314">
<dc:title> Provide import </dc:title>
<dc:identifier> 2314 </dc:identifier>
<oslc_cm:duplicate>false</oslc_cm:duplicate>
<oslc_cm:actionResolveAsDuplicate rdf:resource="http://example.com/bugs/2314/dupe"/>
</oslc_cm:ChangeRequest>
To resolve this CR as a duplicate of another CR, you would
PUT /bugs/2314/dupe HTTP/1.1
<oslc_cm:ChangeRequest ...
rdf:about="http://example.com/bugs/2314">
...
<oslc_cm:duplicateOf rdf:resource="http://example.com/bugs/2352"/>
...
</oslc_cm:ChangeRequest>
After the update, the CR resource representation looks like
<oslc_cm:ChangeRequest
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:dc="http://purl.org/dc/terms/"
xmlns:oslc_cm="http://open-services.net/xmlns/cm/2.0#"
rdf:about="http://example.com/bugs/2314">
<dc:title> Provide import </dc:title>
<dc:identifier> 2314 </dc:identifier>
<oslc_cm:duplicate>true</oslc_cm:duplicate>
<oslc_cm:duplicateOf rdf:resource="http://example.com/bugs/2352"/>
<oslc_cm:actionResolveAsDuplicate rdf:resource="http://example.com/bugs/2314/dupe"/>
</oslc_cm:ChangeRequest>
Notes
- Should priority and severity (currently having a type of 'any' in the spec) also be exposed as predicates with fixed values like 'high', 'medium', 'low', 'unassigned'?
- Partial update should support a no-op in order to efficiently trigger an action without modifying anything else in the resource
--
PatrickStreule - 13 May 2010