CMIS Relationship support in AtomPub binding

- getRelationships
- getRelationship
- createRelationship
- unit tests for above

TODO: delete relationship, includeRelationships flag

git-svn-id: https://svn.alfresco.com/repos/alfresco-enterprise/alfresco/HEAD/root@14461 c4b6b30b-aa2e-2d43-bbcb-ca4b014f7261
This commit is contained in:
David Caruana
2009-05-27 22:11:31 +00:00
parent 0e56f7f40c
commit 47da70f4df
23 changed files with 622 additions and 77 deletions

View File

@@ -10,6 +10,19 @@
</entry> </entry>
[/#macro] [/#macro]
[#macro objectCMISProps object propfilter]
<cmis:properties>
[#assign typedef = cmistype(object)]
[#-- TODO: Spec issue: BaseType not a property --]
[@filter propfilter "BaseType"][@propvalue "BaseType" typedef.typeId.baseTypeId.id "STRING"/][/@filter]
[#list typedef.propertyDefinitions?values as propdef]
[@filter propfilter propdef.propertyId.name][@prop propdef.propertyId.name object propdef.dataType/][/@filter]
[/#list]
</cmis:properties>
[/#macro]
[#-- --] [#-- --]
[#-- ATOM Entry for Document --] [#-- ATOM Entry for Document --]
@@ -30,7 +43,7 @@
<title>${node.name}</title> <title>${node.name}</title>
<updated>${xmldate(node.properties.modified)}</updated> <updated>${xmldate(node.properties.modified)}</updated>
<cmis:object> <cmis:object>
[@documentCMISProps node propfilter/] [@objectCMISProps node propfilter/]
[#if includeallowableactions][@allowableactions node/][/#if] [#if includeallowableactions][@allowableactions node/][/#if]
</cmis:object> </cmis:object>
<cmis:terminator/> <cmis:terminator/>
@@ -41,7 +54,7 @@
[#macro documentCMISLinks node] [#macro documentCMISLinks node]
<link rel="allowableactions" href="${absurl(url.serviceContext)}/api/node/${node.nodeRef.storeRef.protocol}/${node.nodeRef.storeRef.identifier}/${node.nodeRef.id}/permissions"/> <link rel="allowableactions" href="${absurl(url.serviceContext)}/api/node/${node.nodeRef.storeRef.protocol}/${node.nodeRef.storeRef.identifier}/${node.nodeRef.id}/permissions"/>
<link rel="relationships" href="${absurl(url.serviceContext)}/api/node/${node.nodeRef.storeRef.protocol}/${node.nodeRef.storeRef.identifier}/${node.nodeRef.id}/associations"/> <link rel="relationships" href="${absurl(url.serviceContext)}/api/node/${node.nodeRef.storeRef.protocol}/${node.nodeRef.storeRef.identifier}/${node.nodeRef.id}/rels"/>
<link rel="parents" href="${absurl(url.serviceContext)}/api/node/${node.nodeRef.storeRef.protocol}/${node.nodeRef.storeRef.identifier}/${node.nodeRef.id}/parents"/> <link rel="parents" href="${absurl(url.serviceContext)}/api/node/${node.nodeRef.storeRef.protocol}/${node.nodeRef.storeRef.identifier}/${node.nodeRef.id}/parents"/>
<link rel="allversions" href="${absurl(url.serviceContext)}/api/node/${node.nodeRef.storeRef.protocol}/${node.nodeRef.storeRef.identifier}/${node.nodeRef.id}/versions"/> <link rel="allversions" href="${absurl(url.serviceContext)}/api/node/${node.nodeRef.storeRef.protocol}/${node.nodeRef.storeRef.identifier}/${node.nodeRef.id}/versions"/>
[@linkstream node "stream"/] [@linkstream node "stream"/]
@@ -49,18 +62,6 @@
<link rel="repository" href="[@serviceuri/]"/> <link rel="repository" href="[@serviceuri/]"/>
[/#macro] [/#macro]
[#macro documentCMISProps node propfilter]
<cmis:properties>
[#-- TODO: Spec issue: BaseType not a property --]
[@filter propfilter "BaseType"][@propvalue "BaseType" "document" "STRING"/][/@filter]
[#assign typedef = cmistype(node)]
[#list typedef.propertyDefinitions?values as propdef]
[@filter propfilter propdef.propertyId.name][@prop propdef.propertyId.name node propdef.dataType/][/@filter]
[/#list]
</cmis:properties>
[/#macro]
[#-- --] [#-- --]
[#-- ATOM Entry for Version --] [#-- ATOM Entry for Version --]
@@ -79,7 +80,7 @@
<title>${node.name}</title> <title>${node.name}</title>
<updated>${xmldate(node.properties.modified)}</updated> <updated>${xmldate(node.properties.modified)}</updated>
<cmis:object> <cmis:object>
[@documentCMISProps node propfilter/] [@objectCMISProps node propfilter/]
</cmis:object> </cmis:object>
<cmis:terminator/> <cmis:terminator/>
<app:edited>${xmldate(node.properties.modified)}</app:edited> <app:edited>${xmldate(node.properties.modified)}</app:edited>
@@ -107,7 +108,7 @@
<title>${node.name}</title> <title>${node.name}</title>
<updated>${xmldate(node.properties.modified)}</updated> <updated>${xmldate(node.properties.modified)}</updated>
<cmis:object> <cmis:object>
[@documentCMISProps node propfilter/] [@objectCMISProps node propfilter/]
[#if includeallowableactions][@allowableactions node/][/#if] [#if includeallowableactions][@allowableactions node/][/#if]
</cmis:object> </cmis:object>
<cmis:terminator/> <cmis:terminator/>
@@ -136,7 +137,7 @@
<updated>${xmldate(node.properties.modified)}</updated> <updated>${xmldate(node.properties.modified)}</updated>
<cmis:object> <cmis:object>
[#-- recurse for depth greater than 1 --] [#-- recurse for depth greater than 1 --]
[@folderCMISProps node propfilter/] [@objectCMISProps node propfilter/]
[#if includeallowableactions][@allowableactions node/][/#if] [#if includeallowableactions][@allowableactions node/][/#if]
</cmis:object> </cmis:object>
[#if depth < maxdepth || depth == -1] [#if depth < maxdepth || depth == -1]
@@ -157,7 +158,7 @@
[#macro folderCMISLinks node] [#macro folderCMISLinks node]
<link rel="allowableactions" href="${absurl(url.serviceContext)}/api/node/${node.nodeRef.storeRef.protocol}/${node.nodeRef.storeRef.identifier}/${node.nodeRef.id}/permissions"/> <link rel="allowableactions" href="${absurl(url.serviceContext)}/api/node/${node.nodeRef.storeRef.protocol}/${node.nodeRef.storeRef.identifier}/${node.nodeRef.id}/permissions"/>
<link rel="relationships" href="${absurl(url.serviceContext)}/api/node/${node.nodeRef.storeRef.protocol}/${node.nodeRef.storeRef.identifier}/${node.nodeRef.id}/associations"/> <link rel="relationships" href="${absurl(url.serviceContext)}/api/node/${node.nodeRef.storeRef.protocol}/${node.nodeRef.storeRef.identifier}/${node.nodeRef.id}/rels"/>
[#if cmisproperty(node, "ParentId")?is_string] [#if cmisproperty(node, "ParentId")?is_string]
<link rel="parents" href="${absurl(url.serviceContext)}/api/node/${node.nodeRef.storeRef.protocol}/${node.nodeRef.storeRef.identifier}/${node.nodeRef.id}/parent"/> <link rel="parents" href="${absurl(url.serviceContext)}/api/node/${node.nodeRef.storeRef.protocol}/${node.nodeRef.storeRef.identifier}/${node.nodeRef.id}/parent"/>
[/#if] [/#if]
@@ -167,16 +168,38 @@
<link rel="repository" href="[@serviceuri/]"/> <link rel="repository" href="[@serviceuri/]"/>
[/#macro] [/#macro]
[#macro folderCMISProps node propfilter]
<cmis:properties>
[#-- TODO: Spec issue: BaseType not a property --]
[@filter propfilter "BaseType"][@propvalue "BaseType" "folder" "STRING"/][/@filter]
[#assign typedef = cmistype(node)] [#-- --]
[#list typedef.propertyDefinitions?values as propdef] [#-- ATOM Entry for Relationship --]
[@filter propfilter propdef.propertyId.name][@prop propdef.propertyId.name node propdef.dataType/][/@filter] [#-- --]
[/#list]
</cmis:properties> [#macro assoc assoc propfilter="*" includeallowableactions=false ns=""]
[@entry ns]
<author><name>${xmldate(date)}</name></author> [#-- TODO: [@namedvalue "CreatedBy" assoc "STRING"/] --]
<content>[@namedvalue "ObjectId" assoc "ID"/]</content> [#-- TODO: spec id, how to map? --]
<id>[@namedvalue "ObjectId" assoc "ID"/]</id> [#-- TODO: id compliant --]
<link rel="self" href="${absurl(url.serviceContext)}[@assocuri assoc/]"/>
<link rel="edit" href="${absurl(url.serviceContext)}[@assocuri assoc/]"/>
[@assocCMISLinks assoc=assoc/]
<published>${xmldate(date)}</published> [#-- TODO: [@namedvalue "CreationDate" assoc "DATETIME"/] --]
<summary>[@namedvalue "ObjectId" assoc "ID"/]</summary> [#-- TODO: spec id, how to map? --]
<title>[@namedvalue "ObjectId" assoc "ID"/]</title> [#-- TODO: spec id, how to map? --]
<updated>${xmldate(date)}</updated> [#-- TODO: [@namedvalue "LastModificationDate" assoc "DATETIME"/] --]
<cmis:object>
[@objectCMISProps assoc propfilter/]
[#-- TODO: [#if includeallowableactions][@allowableactions node/][/#if] --]
</cmis:object>
<cmis:terminator/>
<app:edited>${xmldate(date)}</app:edited> [#-- TODO: [@namedvalue "LastModificationDate" assoc "DATETIME"/] --]
[/@entry]
[/#macro]
[#macro assocCMISLinks assoc]
[#-- TODO: <link rel="allowableactions" href="${absurl(url.serviceContext)}/api/node/${node.nodeRef.storeRef.protocol}/${node.nodeRef.storeRef.identifier}/${node.nodeRef.id}/permissions"/> --]
<link rel="type" href="${absurl(url.serviceContext)}/api/type/${cmistype(assoc).typeId.id!"unknown"}"/>
<link rel="source" href="${absurl(url.serviceContext)}[@nodeuri assoc.source/]"/>
<link rel="target" href="${absurl(url.serviceContext)}[@nodeuri assoc.target/]"/>
<link rel="repository" href="[@serviceuri/]"/>
[/#macro] [/#macro]
@@ -239,8 +262,8 @@
[#if filter == "*" || filter?index_of(value) != -1 || filter?matches(value,'i')][#nested][/#if] [#if filter == "*" || filter?index_of(value) != -1 || filter?matches(value,'i')][#nested][/#if]
[/#macro] [/#macro]
[#macro prop name node type] [#macro prop name object type]
[#assign value=cmisproperty(node, name)/] [#assign value=cmisproperty(object, name)/]
[#if value?is_string || value?is_number || value?is_boolean || value?is_date || value?is_enumerable] [#if value?is_string || value?is_number || value?is_boolean || value?is_date || value?is_enumerable]
[@propvalue name value type/] [@propvalue name value type/]
[#elseif value.class.canonicalName?ends_with("NULL")] [#elseif value.class.canonicalName?ends_with("NULL")]
@@ -250,20 +273,20 @@
[#macro propvalue name value type] [#macro propvalue name value type]
[#if type == "STRING"] [#if type == "STRING"]
<cmis:propertyString cmis:name="${name}">[@values value;v][@stringvalue v/][/@values]</cmis:propertyString> <cmis:propertyString cmis:name="${name}">[@values value;v]<cmis:value>[@stringvalue v/]</cmis:value>[/@values]</cmis:propertyString>
[#elseif type == "INTEGER"] [#elseif type == "INTEGER"]
<cmis:propertyInteger cmis:name="${name}">[@values value;v][@integervalue v/][/@values]</cmis:propertyInteger> <cmis:propertyInteger cmis:name="${name}">[@values value;v]<cmis:value>[@integervalue v/]</cmis:value>[/@values]</cmis:propertyInteger>
[#elseif type == "DECIMAL"] [#elseif type == "DECIMAL"]
<cmis:propertyDecimal cmis:name="${name}">[@values value;v][@decimalvalue v/][/@values]</cmis:propertyDecimal> <cmis:propertyDecimal cmis:name="${name}">[@values value;v]<cmis:value>[@decimalvalue v/]</cmis:value>[/@values]</cmis:propertyDecimal>
[#elseif type == "BOOLEAN"] [#elseif type == "BOOLEAN"]
<cmis:propertyBoolean cmis:name="${name}">[@values value;v][@booleanvalue v/][/@values]</cmis:propertyBoolean> <cmis:propertyBoolean cmis:name="${name}">[@values value;v]<cmis:value>[@booleanvalue v/]</cmis:value>[/@values]</cmis:propertyBoolean>
[#elseif type == "DATETIME"] [#elseif type == "DATETIME"]
<cmis:propertyDateTime cmis:name="${name}">[@values value;v][@datetimevalue v/][/@values]</cmis:propertyDateTime> <cmis:propertyDateTime cmis:name="${name}">[@values value;v]<cmis:value>[@datetimevalue v/]</cmis:value>[/@values]</cmis:propertyDateTime>
[#elseif type == "URI"] [#elseif type == "URI"]
[#-- TODO: check validity of abs url prefix --] [#-- TODO: check validity of abs url prefix --]
<cmis:propertyUri cmis:name="${name}">[@values value;v][@urivalue absurl(url.serviceContext) + v/][/@values]</cmis:propertyUri> <cmis:propertyUri cmis:name="${name}">[@values value;v]<cmis:value>[@urivalue absurl(url.serviceContext) + v/]</cmis:value>[/@values]</cmis:propertyUri>
[#elseif type == "ID"] [#elseif type == "ID"]
<cmis:propertyId cmis:name="${name}">[@values value;v][@idvalue v/][/@values]</cmis:propertyId> <cmis:propertyId cmis:name="${name}">[@values value;v]<cmis:value>[@idvalue v/]</cmis:value>[/@values]</cmis:propertyId>
[#-- TODO: remaining property types --] [#-- TODO: remaining property types --]
[/#if] [/#if]
[/#macro] [/#macro]
@@ -292,15 +315,40 @@
[#-- CMIS Values --] [#-- CMIS Values --]
[#-- --] [#-- --]
[#macro namedvalue name object type]
[#assign value=cmisproperty(object, name)/]
[#if value?is_string || value?is_number || value?is_boolean || value?is_date || value?is_enumerable][@typedvalue value type/][#elseif value.class.canonicalName?ends_with("NULL")][/#if]
[/#macro]
[#macro typedvalue value type]
[#if type == "STRING"]
[@values value;v][@stringvalue v/][/@values]
[#elseif type == "INTEGER"]
[@values value;v][@integervalue v/][/@values]
[#elseif type == "DECIMAL"]
[@values value;v][@decimalvalue v/][/@values]
[#elseif type == "BOOLEAN"]
[@values value;v][@booleanvalue v/][/@values]
[#elseif type == "DATETIME"]
[@values value;v][@datetimevalue v/][/@values]
[#elseif type == "URI"]
[#-- TODO: check validity of abs url prefix --]
[@values value;v][@urivalue absurl(url.serviceContext) + v/][/@values]
[#elseif type == "ID"]
[@values value;v][@idvalue v/][/@values]
[#-- TODO: remaining property types --]
[/#if]
[/#macro]
[#macro values vals][#if vals?is_enumerable][#list vals as val][#nested val][/#list][#else][#nested vals][/#if][/#macro] [#macro values vals][#if vals?is_enumerable][#list vals as val][#nested val][/#list][#else][#nested vals][/#if][/#macro]
[#macro stringvalue value]<cmis:value>${value}</cmis:value>[/#macro] [#macro stringvalue value]${value}[/#macro]
[#macro integervalue value]<cmis:value>${value?c}</cmis:value>[/#macro] [#macro integervalue value]${value?c}[/#macro]
[#macro decimalvalue value]<cmis:value>${value?c}</cmis:value>[/#macro] [#macro decimalvalue value]${value?c}[/#macro]
[#macro booleanvalue value]<cmis:value>${value?string}</cmis:value>[/#macro] [#macro booleanvalue value]${value?string}[/#macro]
[#macro datetimevalue value]<cmis:value>${xmldate(value)}</cmis:value>[/#macro] [#macro datetimevalue value]${xmldate(value)}[/#macro]
[#macro urivalue value]<cmis:value>${value}</cmis:value>[/#macro] [#macro urivalue value]${value}[/#macro]
[#macro idvalue value]<cmis:value>${value}</cmis:value>[/#macro] [#macro idvalue value]${value}[/#macro]
[#-- --] [#-- --]
@@ -527,37 +575,37 @@
[#if type == "STRING"] [#if type == "STRING"]
<cmis:choiceString cmis:key="${choice.name}"> <cmis:choiceString cmis:key="${choice.name}">
[@cmisChoices choice.children type/] [@cmisChoices choice.children type/]
[@stringvalue choice.value/] <cmis:value>[@stringvalue choice.value/]</cmis:value>
</cmis:choiceString> </cmis:choiceString>
[#elseif type == "INTEGER"] [#elseif type == "INTEGER"]
<cmis:choiceInteger cmis:key="${choice.name}"> <cmis:choiceInteger cmis:key="${choice.name}">
[@cmisChoices choice.children type/] [@cmisChoices choice.children type/]
[@stringvalue choice.value/] <cmis:value>[@stringvalue choice.value/]</cmis:value>
</cmis:choiceInteger> </cmis:choiceInteger>
[#elseif type == "DECIMAL"] [#elseif type == "DECIMAL"]
<cmis:choiceDecimal cmis:key="${choice.name}"> <cmis:choiceDecimal cmis:key="${choice.name}">
[@cmisChoices choice.children type/] [@cmisChoices choice.children type/]
[@stringvalue choice.value/] <cmis:value>[@stringvalue choice.value/]</cmis:value>
</cmis:choiceDecimal> </cmis:choiceDecimal>
[#elseif type == "BOOLEAN"] [#elseif type == "BOOLEAN"]
<cmis:choiceBoolean cmis:key="${choice.name}"> <cmis:choiceBoolean cmis:key="${choice.name}">
[@cmisChoices choice.children type/] [@cmisChoices choice.children type/]
[@stringvalue choice.value/] <cmis:value>[@stringvalue choice.value/]</cmis:value>
</cmis:choiceBoolean> </cmis:choiceBoolean>
[#elseif type == "DATETIME"] [#elseif type == "DATETIME"]
<cmis:choiceDateTime cmis:key="${choice.name}"> <cmis:choiceDateTime cmis:key="${choice.name}">
[@cmisChoices choice.children type/] [@cmisChoices choice.children type/]
[@stringvalue choice.value/] <cmis:value>[@stringvalue choice.value/]</cmis:value>
</cmis:choiceDateTime> </cmis:choiceDateTime>
[#elseif type == "URI"] [#elseif type == "URI"]
<cmis:choiceUri cmis:key="${choice.name}"> <cmis:choiceUri cmis:key="${choice.name}">
[@cmisChoices choice.children type/] [@cmisChoices choice.children type/]
[@stringvalue choice.value/] <cmis:value>[@stringvalue choice.value/]</cmis:value>
</cmis:choiceUri> </cmis:choiceUri>
[#elseif type == "ID"] [#elseif type == "ID"]
<cmis:choiceId cmis:key="${choice.name}"> <cmis:choiceId cmis:key="${choice.name}">
[@cmisChoices choice.children type/] [@cmisChoices choice.children type/]
[@stringvalue choice.value/] <cmis:value>[@stringvalue choice.value/]</cmis:value>
</cmis:choiceId> </cmis:choiceId>
[#-- TODO: remaining property types --] [#-- TODO: remaining property types --]
[/#if] [/#if]
@@ -581,4 +629,13 @@
[#macro contenturi node]${absurl(url.serviceContext)}/api/node/${node.nodeRef.storeRef.protocol}/${node.nodeRef.storeRef.identifier}/${node.nodeRef.id}/content[#if node.properties.name?? && node.properties.name?last_index_of(".") != -1]${encodeuri(node.properties.name?substring(node.properties.name?last_index_of(".")))}[/#if][/#macro] [#macro contenturi node]${absurl(url.serviceContext)}/api/node/${node.nodeRef.storeRef.protocol}/${node.nodeRef.storeRef.identifier}/${node.nodeRef.id}/content[#if node.properties.name?? && node.properties.name?last_index_of(".") != -1]${encodeuri(node.properties.name?substring(node.properties.name?last_index_of(".")))}[/#if][/#macro]
[#-- Helper to render Alfresco service document uri --] [#-- Helper to render Alfresco service document uri --]
[#macro serviceuri]${absurl(url.serviceContext)}/api/repository[/#macro] [#macro serviceuri]${absurl(url.serviceContext)}/api/repository[/#macro]
[#-- Helper to render Node Ref --]
[#macro noderef node]${node.nodeRef.storeRef.protocol}/${node.nodeRef.storeRef.identifier}/${node.nodeRef.id}[/#macro]
[#-- Helper to render Alfresco Node uri --]
[#macro nodeuri node]/api/node/[@noderef node/][/#macro]
[#-- Helper to render Alfresco Assoc uri --]
[#macro assocuri assoc]/api/rel/[@noderef assoc.source/]/type/${cmistype(assoc).typeId.id!"undefined"}/target/[@noderef assoc.target/][/#macro]

View File

@@ -1,4 +1,3 @@
// //
// Create Alfresco Node from Atom Entry // Create Alfresco Node from Atom Entry
// //
@@ -199,6 +198,57 @@ function updateNode(node, entry, exclude, validator)
} }
//
// Create Alfresco Association from Atom Entry
//
// @param source source node
// @param entry atom entry
// @return created association (or null, in case of error)
//
function createAssociation(source, entry)
{
var object = entry.getExtension(atom.names.cmis_object);
var typeId = (object !== null) ? object.objectTypeId.nativeValue : null;
// locate relationship type definition
// TODO: check this against spec - default to Relationship, if not specified
var type = cmis.queryType(typeId === null ? "relationship" : typeId);
if (type === null)
{
status.setCode(400, "CMIS object type " + typeId + " not understood");
return null;
}
if (type.typeId.baseTypeId.id != "relationship")
{
status.setCode(400, "CMIS object type " + typeId + " is not a relationship type");
return null;
}
if (!type.creatable)
{
status.setCode(400, "Relationship type " + typeId + " is not creatable");
return null;
}
// locate target
var targetId = (object !== null) ? object.targetId.nativeValue : null;
if (targetId === null)
{
status.setCode(400, "Target Id has not been specified");
return null;
}
var target = search.findNode(targetId);
if (target === null)
{
status.setCode(400, "Target Id " + targetId + " does not refer to known item");
return null;
}
// create association
var assoc = source.createAssociation(target, type.typeId.QName.toString());
return assoc;
}
// callback for validating property update for patch // callback for validating property update for patch
// return null => update not allowed, abort update // return null => update not allowed, abort update
// true => update allowed // true => update allowed

View File

@@ -0,0 +1,6 @@
//
// Constants
//
// CMIS Enums
CMISRelationshipDirectionEnum = Packages.org.alfresco.cmis.CMISRelationshipDirectionEnum;

View File

@@ -0,0 +1,11 @@
[#ftl]
[#import "/org/alfresco/cmis/ns.lib.atom.ftl" as nsLib/]
[#import "/org/alfresco/cmis/atomentry.lib.atom.ftl" as entryLib/]
[#compress]
<?xml version="1.0" encoding="UTF-8"?>
[#assign namespace][@nsLib.entryNS/][/#assign]
[@entryLib.assoc assoc=assoc propfilter=filter includeallowableactions=includeAllowableActions ns=namespace/]
[/#compress]

View File

@@ -0,0 +1,9 @@
<webscript>
<shortname>Retrieve relationship (getProperties)</shortname>
<description>
</description>
<url>/api/rel/{store_type}/{store_id}/{id}/type/{rel_type}/target/{target_store_type}/{target_store_id}/{target_id}</url>
<authentication>guest</authentication>
<format default="atomentry">argument</format>
<family>CMIS</family>
</webscript>

View File

@@ -0,0 +1,28 @@
script:
{
// relationship type
var relType = url.templateArgs.rel_type;
model.relTypeDef = cmis.queryType(relType);
if (model.relTypeDef === null)
{
status.setCode(400, "Relationship type " + relType + " unknown");
break script;
}
if (model.relTypeDef.baseType.typeId != "relationship")
{
status.setCode(400, "Type + " + relType + " is not a relationship type");
break script;
}
// source and target
var source = [url.templateArgs.store_type, url.templateArgs.store_id, url.templateArgs.id];
var target = [url.templateArgs.target_store_type, url.templateArgs.target_store_id, url.templateArgs.target_id];
// locate association
model.assoc = cmis.findRelationship(model.relTypeDef, source, target);
if (model.assoc === null)
{
status.setCode(404, "Assoc " + source.join("/") + "/" + relType + "/" + target.join("/") + " not found");
break script;
}
}

View File

@@ -0,0 +1,24 @@
[#ftl]
[#import "/org/alfresco/cmis/ns.lib.atom.ftl" as nsLib/]
[#import "/org/alfresco/cmis/atomfeed.lib.atom.ftl" as feedLib/]
[#import "/org/alfresco/cmis/atomentry.lib.atom.ftl" as entryLib/]
[#import "/org/alfresco/paging.lib.atom.ftl" as pagingLib/]
[#compress]
<?xml version="1.0" encoding="UTF-8"?>
<feed [@nsLib.feedNS/]>
[@feedLib.node node "relationships"]
[@pagingLib.links cursor/]
[/@feedLib.node]
[#list results as assoc]
[@entryLib.assoc assoc=assoc propfilter=filter includeallowableactions=includeAllowableActions/]
[/#list]
[@feedLib.hasMore cursor/]
[@pagingLib.opensearch cursor/]
</feed>
[/#compress]

View File

@@ -0,0 +1,10 @@
<webscript>
<shortname>Retrieve list of relationships (getRelationships)</shortname>
<description>
</description>
<url>/api/node/{store_type}/{store_id}/{id}/rels?filter={filter?}&amp;relationshipType={relationshipType?}&amp;includeSubRelationshipTypes={includeSubRelationshipTypes?}&amp;direction={direction?}&amp;skipCount={skipCount?}&amp;maxItems={maxItems?}&amp;includeAllowableActions={includeAllowableActions?}</url>
<url>/api/path/{store_type}/{store_id}/{id}/rels?filter={filter?}&amp;relationshipType={relationshipType?}&amp;includeSubRelationshipTypes={includeSubRelationshipTypes?}&amp;direction={direction?}&amp;skipCount={skipCount?}&amp;maxItems={maxItems?}&amp;includeAllowableActions={includeAllowableActions?}</url>
<authentication>guest</authentication>
<format default="atomfeed">argument</format>
<family>CMIS</family>
</webscript>

View File

@@ -0,0 +1,59 @@
<import resource="classpath:alfresco/templates/webscripts/org/alfresco/cmis/constants.lib.js">
script:
{
// locate node
var pathSegments = url.match.split("/");
var reference = [ url.templateArgs.store_type, url.templateArgs.store_id ].concat(url.templateArgs.id.split("/"));
model.node = cmis.findNode(pathSegments[2], reference);
if (model.node === null)
{
status.setCode(404, "Repository " + pathSegments[2] + " " + reference.join("/") + " not found");
break script;
}
// property filter
model.filter = args[cmis.ARG_FILTER];
if (model.filter === null)
{
model.filter = "*";
}
// relationship type
var relType = args[cmis.ARG_RELATIONSHIP_TYPE];
if (relType != null)
{
model.relTypeDef = cmis.queryType(relType);
if (model.relTypeDef === null)
{
status.setCode(400, "Relationship type " + relType + " unknown");
break script;
}
if (model.relTypeDef.baseType.typeId != "relationship")
{
status.setCode(400, "Type + " + relType + " is not a relationship type");
break script;
}
}
// include sub relationship types
model.includeSubRelationshipTypes = args[cmis.ARG_INCLUDE_SUB_RELATIONSHIP_TYPES] == "true" ? true : false;
// direction
var direction = args[cmis.ARG_DIRECTION];
if (direction !== null && !CMISRelationshipDirectionEnum.FACTORY.validLabel(direction))
{
status.setCode(400, "Direction " + direction + " unknown");
break script;
}
model.direction = CMISRelationshipDirectionEnum.FACTORY.toEnum(direction);
// include allowable actions
model.includeAllowableActions = args[cmis.ARG_INCLUDE_ALLOWABLE_ACTIONS] == "true" ? true : false;
// retrieve relationships
var page = paging.createPageOrWindow(args);
var paged = cmis.queryRelationships(model.node, model.relDefType, model.includeSubRelationshipTypes, model.direction, page);
model.results = paged.results;
model.cursor = paged.cursor;
}

View File

@@ -0,0 +1,40 @@
<import resource="classpath:alfresco/templates/webscripts/org/alfresco/cmis/atomentry.lib.js">
script:
{
// ensure atom entry is posted
if (entry === null)
{
status.code = 400;
status.message = "Expected atom entry";
status.redirect = true;
break script;
}
// locate source node
var pathSegments = url.match.split("/");
var reference = [ url.templateArgs.store_type, url.templateArgs.store_id ].concat(url.templateArgs.id.split("/"));
model.source = cmis.findNode(pathSegments[2], reference);
if (model.source === null)
{
status.code = 404;
status.message = "Repository " + pathSegments[2] + " " + reference.join("/") + " not found";
status.redirect = true;
break script;
}
// create
var assoc = createAssociation(model.source, entry);
if (assoc == null)
{
break script;
}
// success
model.assoc = assoc;
// TODO: set Content-Location
status.code = 201;
// TODO: complete url mapping
status.location = url.server + url.serviceContext + "/api/rel/" + model.source.nodeRef.storeRef.protocol + "/" + model.source.nodeRef.storeRef.identifier + "/" + model.source.nodeRef.id;
status.redirect = true;
}

View File

@@ -0,0 +1,11 @@
[#ftl]
[#import "/org/alfresco/cmis/ns.lib.atom.ftl" as nsLib/]
[#import "/org/alfresco/cmis/atomentry.lib.atom.ftl" as entryLib/]
[#compress]
<?xml version="1.0" encoding="UTF-8"?>
[#assign namespace][@nsLib.entryNS/][/#assign]
[@entryLib.assoc assoc=assoc propfilter=filter includeallowableactions=includeAllowableActions ns=namespace/]
[/#compress]

View File

@@ -0,0 +1,10 @@
<webscript>
<shortname>Create relationship (createRelationship)</shortname>
<description>
</description>
<url>/api/node/{store_type}/{store_id}/{id}/rels</url>
<url>/api/path/{store_type}/{store_id}/{id}/rels</url>
<authentication>guest</authentication>
<format default="atomentry">argument</format>
<family>CMIS</family>
</webscript>

View File

@@ -17,13 +17,13 @@ script:
{ {
// query a specific type and its descendants // query a specific type and its descendants
var typedef = cmis.queryType(typeId); var typedef = cmis.queryType(typeId);
if (typedef === null) if (typedef === null)
{ {
status.code = 404; status.code = 404;
status.message = "Type " + typeId + " not found"; status.message = "Type " + typeId + " not found";
status.redirect = true; status.redirect = true;
break script; break script;
} }
var paged = cmis.queryTypeHierarchy(typedef, true, page); var paged = cmis.queryTypeHierarchy(typedef, true, page);
model.results = paged.results; model.results = paged.results;
model.cursor = paged.cursor; model.cursor = paged.cursor;

View File

@@ -27,6 +27,7 @@ package org.alfresco.repo.cmis.rest;
import java.util.List; import java.util.List;
import org.alfresco.cmis.CMISServices; import org.alfresco.cmis.CMISServices;
import org.alfresco.repo.template.TemplateAssociation;
import org.alfresco.repo.template.TemplateNode; import org.alfresco.repo.template.TemplateNode;
import freemarker.ext.beans.BeanModel; import freemarker.ext.beans.BeanModel;
@@ -76,14 +77,6 @@ public final class CMISPropertyValueMethod implements TemplateMethodModelEx
Object arg0 = args.get(0); Object arg0 = args.get(0);
if (arg0 instanceof BeanModel) if (arg0 instanceof BeanModel)
{ {
// extract node
TemplateNode node = null;
Object wrapped = ((BeanModel)arg0).getWrappedObject();
if (wrapped != null && wrapped instanceof TemplateNode)
{
node = (TemplateNode)wrapped;
}
// extract property name // extract property name
String propertyName = null; String propertyName = null;
Object arg1 = args.get(1); Object arg1 = args.get(1);
@@ -92,8 +85,18 @@ public final class CMISPropertyValueMethod implements TemplateMethodModelEx
propertyName = ((TemplateScalarModel)arg1).getAsString(); propertyName = ((TemplateScalarModel)arg1).getAsString();
} }
// retrieve property value // extract node
result = cmisService.getProperty(node.getNodeRef(), propertyName); Object wrapped = ((BeanModel)arg0).getWrappedObject();
if (wrapped != null && wrapped instanceof TemplateNode)
{
// retrieve property value from node
result = cmisService.getProperty(((TemplateNode)wrapped).getNodeRef(), propertyName);
}
else if (wrapped != null && wrapped instanceof TemplateAssociation)
{
// retrieve property value from node
result = cmisService.getProperty(((TemplateAssociation)wrapped).getAssociationRef(), propertyName);
}
} }
} }

View File

@@ -33,11 +33,13 @@ import org.alfresco.cmis.CMISPropertyDefinition;
import org.alfresco.cmis.CMISQueryEnum; import org.alfresco.cmis.CMISQueryEnum;
import org.alfresco.cmis.CMISQueryOptions; import org.alfresco.cmis.CMISQueryOptions;
import org.alfresco.cmis.CMISQueryService; import org.alfresco.cmis.CMISQueryService;
import org.alfresco.cmis.CMISRelationshipDirectionEnum;
import org.alfresco.cmis.CMISResultSet; import org.alfresco.cmis.CMISResultSet;
import org.alfresco.cmis.CMISServices; import org.alfresco.cmis.CMISServices;
import org.alfresco.cmis.CMISTypeDefinition; import org.alfresco.cmis.CMISTypeDefinition;
import org.alfresco.cmis.CMISTypesFilterEnum; import org.alfresco.cmis.CMISTypesFilterEnum;
import org.alfresco.error.AlfrescoRuntimeException; import org.alfresco.error.AlfrescoRuntimeException;
import org.alfresco.repo.jscript.Association;
import org.alfresco.repo.jscript.BaseScopableProcessorExtension; import org.alfresco.repo.jscript.BaseScopableProcessorExtension;
import org.alfresco.repo.jscript.ScriptNode; import org.alfresco.repo.jscript.ScriptNode;
import org.alfresco.repo.model.Repository; import org.alfresco.repo.model.Repository;
@@ -46,6 +48,7 @@ import org.alfresco.repo.web.util.paging.Page;
import org.alfresco.repo.web.util.paging.PagedResults; import org.alfresco.repo.web.util.paging.PagedResults;
import org.alfresco.repo.web.util.paging.Paging; import org.alfresco.repo.web.util.paging.Paging;
import org.alfresco.service.ServiceRegistry; import org.alfresco.service.ServiceRegistry;
import org.alfresco.service.cmr.repository.AssociationRef;
import org.alfresco.service.cmr.repository.NodeRef; import org.alfresco.service.cmr.repository.NodeRef;
import org.alfresco.service.namespace.QName; import org.alfresco.service.namespace.QName;
@@ -73,7 +76,7 @@ public class CMISScript extends BaseScopableProcessorExtension
public static final String ARG_INCLUDE_ALLOWABLE_ACTIONS = "includeAllowableActions"; public static final String ARG_INCLUDE_ALLOWABLE_ACTIONS = "includeAllowableActions";
public static final String ARG_INCLUDE_PROPERTY_DEFINITIONS = "includePropertyDefinitions"; public static final String ARG_INCLUDE_PROPERTY_DEFINITIONS = "includePropertyDefinitions";
public static final String ARG_INCLUDE_RELATIONSHIPS = "includeRelationships"; public static final String ARG_INCLUDE_RELATIONSHIPS = "includeRelationships";
public static final String ARG_INCLUDE_SUB_RELATIONSHIP_TYPES = "includeSubrelationshipTypes"; public static final String ARG_INCLUDE_SUB_RELATIONSHIP_TYPES = "includeSubRelationshipTypes";
public static final String ARG_LENGTH = "length"; public static final String ARG_LENGTH = "length";
public static final String ARG_MAJOR = "major"; public static final String ARG_MAJOR = "major";
public static final String ARG_MAJOR_VERSION = "majorVersion"; public static final String ARG_MAJOR_VERSION = "majorVersion";
@@ -197,7 +200,7 @@ public class CMISScript extends BaseScopableProcessorExtension
*/ */
public String getDefaultTypesFilter() public String getDefaultTypesFilter()
{ {
return CMISTypesFilterEnum.FACTORY.defaultLabel(); return CMISTypesFilterEnum.FACTORY.getDefaultLabel();
} }
/** /**
@@ -242,6 +245,25 @@ public class CMISScript extends BaseScopableProcessorExtension
} }
return node; return node;
} }
/**
* Finds an Association
*
* @param sourceType
* @param source
* @param relDef
* @param targetType
* @param target
*
* @return
*/
public Association findRelationship(CMISTypeDefinition relDef, String[] sourceRef, String[] targetRef)
{
NodeRef source = new NodeRef(sourceRef[0], sourceRef[1], sourceRef[2]);
NodeRef target = new NodeRef(targetRef[0], targetRef[1], targetRef[2]);
AssociationRef assocRef = cmisService.getRelationship(relDef, source, target);
return (assocRef == null) ? null : new Association(services, assocRef);
}
/** /**
* Query for node children * Query for node children
@@ -267,6 +289,31 @@ public class CMISScript extends BaseScopableProcessorExtension
return results; return results;
} }
/**
* Query for node relationships
*
* @param node
* @param relDef
* @param includeSubTypes
* @param direction
* @param page
* @return
*/
public PagedResults queryRelationships(ScriptNode node, CMISTypeDefinition relDef, boolean includeSubTypes, CMISRelationshipDirectionEnum direction, Page page)
{
AssociationRef[] relationships = cmisService.getRelationships(node.getNodeRef(), relDef, includeSubTypes, direction);
Cursor cursor = paging.createCursor(relationships.length, page);
Association[] assocs = new Association[cursor.getRowCount()];
for (int i = cursor.getStartRow(); i <= cursor.getEndRow(); i++)
{
assocs[i - cursor.getStartRow()] = new Association(services, relationships[i], getScope());
}
PagedResults results = paging.createPagedResults(assocs, cursor);
return results;
}
/** /**
* Query for items checked-out to user * Query for items checked-out to user
* *

View File

@@ -27,7 +27,10 @@ package org.alfresco.repo.cmis.rest;
import java.util.List; import java.util.List;
import org.alfresco.cmis.CMISDictionaryService; import org.alfresco.cmis.CMISDictionaryService;
import org.alfresco.cmis.CMISScope;
import org.alfresco.cmis.CMISTypeDefinition; import org.alfresco.cmis.CMISTypeDefinition;
import org.alfresco.cmis.mapping.CMISMapping;
import org.alfresco.repo.template.TemplateAssociation;
import org.alfresco.repo.template.TemplateNode; import org.alfresco.repo.template.TemplateNode;
import org.alfresco.service.namespace.QName; import org.alfresco.service.namespace.QName;
@@ -47,8 +50,10 @@ import freemarker.template.TemplateModelException;
*/ */
public class CMISTypeDefinitionMethod implements TemplateMethodModelEx public class CMISTypeDefinitionMethod implements TemplateMethodModelEx
{ {
private static CMISScope[] EMPTY_SCOPES = new CMISScope[] {};
private CMISDictionaryService dictionaryService; private CMISDictionaryService dictionaryService;
/** /**
* Construct * Construct
*/ */
@@ -71,6 +76,7 @@ public class CMISTypeDefinitionMethod implements TemplateMethodModelEx
if (arg0 instanceof BeanModel) if (arg0 instanceof BeanModel)
{ {
// extract node type qname // extract node type qname
CMISScope[] matchingScopes = EMPTY_SCOPES;
QName nodeType = null; QName nodeType = null;
Object wrapped = ((BeanModel)arg0).getWrappedObject(); Object wrapped = ((BeanModel)arg0).getWrappedObject();
if (wrapped != null) if (wrapped != null)
@@ -79,6 +85,11 @@ public class CMISTypeDefinitionMethod implements TemplateMethodModelEx
{ {
nodeType = ((TemplateNode)wrapped).getType(); nodeType = ((TemplateNode)wrapped).getType();
} }
else if (wrapped instanceof TemplateAssociation)
{
nodeType = ((TemplateAssociation)wrapped).getTypeQName();
matchingScopes = new CMISScope[] { CMISScope.RELATIONSHIP };
}
else if (wrapped instanceof QName) else if (wrapped instanceof QName)
{ {
nodeType = (QName)wrapped; nodeType = (QName)wrapped;
@@ -88,7 +99,7 @@ public class CMISTypeDefinitionMethod implements TemplateMethodModelEx
// convert to CMIS type // convert to CMIS type
if (nodeType != null) if (nodeType != null)
{ {
result = dictionaryService.findTypeForClass(nodeType); result = dictionaryService.findTypeForClass(nodeType, matchingScopes);
} }
} }
} }

View File

@@ -501,6 +501,31 @@ public class BaseCMISWebScriptTest extends BaseWebScriptTest
return entry; return entry;
} }
protected Entry createRelationship(IRI parent, String type, String targetId)
throws Exception
{
return createRelationship(parent, type, targetId, "/org/alfresco/repo/cmis/rest/test/createrelationship.atomentry.xml");
}
protected Entry createRelationship(IRI parent, String type, String targetId, String atomEntryFile)
throws Exception
{
String createFile = loadString(atomEntryFile);
createFile = createFile.replace("${RELTYPE}", type);
createFile = createFile.replace("${TARGETID}", targetId);
Response res = sendRequest(new PostRequest(parent.toString(), createFile, Format.ATOMENTRY.mimetype()), 201, getAtomValidator());
assertNotNull(res);
String xml = res.getContentAsString();
Entry entry = abdera.parseEntry(new StringReader(xml), null);
assertNotNull(entry);
CMISObject object = entry.getExtension(CMISConstants.OBJECT);
assertEquals("relationship", object.getBaseType().getStringValue());
assertEquals(targetId, object.getTargetId().getStringValue());
String testFileHREF = (String)res.getHeader("Location");
assertNotNull(testFileHREF);
return entry;
}
// //
// General Test Helpers // General Test Helpers
// //

View File

@@ -25,12 +25,15 @@
package org.alfresco.repo.cmis.rest.test; package org.alfresco.repo.cmis.rest.test;
import java.io.StringReader; import java.io.StringReader;
import java.util.HashMap;
import java.util.List; import java.util.List;
import java.util.Map;
import org.alfresco.abdera.ext.cmis.CMISConstants; import org.alfresco.abdera.ext.cmis.CMISConstants;
import org.alfresco.abdera.ext.cmis.CMISObject; import org.alfresco.abdera.ext.cmis.CMISObject;
import org.alfresco.abdera.ext.cmis.CMISProperties; import org.alfresco.abdera.ext.cmis.CMISProperties;
import org.alfresco.abdera.ext.cmis.CMISProperty; import org.alfresco.abdera.ext.cmis.CMISProperty;
import org.alfresco.repo.cmis.rest.CMISScript;
import org.alfresco.util.GUID; import org.alfresco.util.GUID;
import org.alfresco.web.scripts.Format; import org.alfresco.web.scripts.Format;
import org.alfresco.web.scripts.TestWebScriptServer.DeleteRequest; import org.alfresco.web.scripts.TestWebScriptServer.DeleteRequest;
@@ -105,11 +108,11 @@ public class CMISCustomTypeTest extends BaseCMISWebScriptTest
Feed children = getFeed(childrenLink.getHref()); Feed children = getFeed(childrenLink.getHref());
assertNotNull(children); assertNotNull(children);
int entriesBefore = children.getEntries().size(); int entriesBefore = children.getEntries().size();
Entry folder = createDocument(children.getSelfLink().getHref(), "testCreateCustomDocument", "/org/alfresco/repo/cmis/rest/test/createcustomdocument.atomentry.xml"); Entry document = createDocument(children.getSelfLink().getHref(), "testCreateCustomDocument", "/org/alfresco/repo/cmis/rest/test/createcustomdocument.atomentry.xml");
Feed feedFolderAfter = getFeed(childrenLink.getHref()); Feed feedFolderAfter = getFeed(childrenLink.getHref());
int entriesAfter = feedFolderAfter.getEntries().size(); int entriesAfter = feedFolderAfter.getEntries().size();
assertEquals(entriesBefore +1, entriesAfter); assertEquals(entriesBefore +1, entriesAfter);
Entry entry = feedFolderAfter.getEntry(folder.getId().toString()); Entry entry = feedFolderAfter.getEntry(document.getId().toString());
CMISObject object = entry.getExtension(CMISConstants.OBJECT); CMISObject object = entry.getExtension(CMISConstants.OBJECT);
assertEquals("D/cmiscustom_document", object.getObjectTypeId().getStringValue()); assertEquals("D/cmiscustom_document", object.getObjectTypeId().getStringValue());
CMISProperty customProp = object.getProperties().find("cmiscustom_docprop_string"); CMISProperty customProp = object.getProperties().find("cmiscustom_docprop_string");
@@ -315,5 +318,91 @@ public class CMISCustomTypeTest extends BaseCMISWebScriptTest
assertEquals(false, result2multiValues.get(1)); assertEquals(false, result2multiValues.get(1));
} }
} }
public void testCreateRelationship()
throws Exception
{
Entry testFolder = createTestFolder("testCreateCustomRelationship");
Link childrenLink = testFolder.getLink(CMISConstants.REL_CHILDREN);
assertNotNull(childrenLink);
Feed children = getFeed(childrenLink.getHref());
assertNotNull(children);
Entry source = createDocument(children.getSelfLink().getHref(), "testSource", "/org/alfresco/repo/cmis/rest/test/createcustomdocument.atomentry.xml");
assertNotNull(source);
Entry target = createDocument(children.getSelfLink().getHref(), "testTarget", "/org/alfresco/repo/cmis/rest/test/createcustomdocument.atomentry.xml");
assertNotNull(target);
// retrieve relationships feed on source
Link relsLink = source.getLink(CMISConstants.REL_RELATIONSHIPS);
assertNotNull(relsLink);
Feed relsBefore = getFeed(relsLink.getHref());
assertNotNull(relsBefore);
assertEquals(0, relsBefore.getEntries().size());
// create relationship between source and target documents
CMISObject targetObject = target.getExtension(CMISConstants.OBJECT);
assertNotNull(targetObject);
String targetId = targetObject.getObjectId().getStringValue();
assertNotNull(targetId);
Entry rel = createRelationship(relsLink.getHref(), "R/cmiscustom_assoc", targetId);
assertNotNull(rel);
// check created relationship
CMISObject sourceObject = source.getExtension(CMISConstants.OBJECT);
assertNotNull(sourceObject);
String sourceId = sourceObject.getObjectId().getStringValue();
assertNotNull(sourceId);
CMISObject relObject = rel.getExtension(CMISConstants.OBJECT);
assertNotNull(relObject);
assertEquals("R/cmiscustom_assoc", relObject.getObjectTypeId().getStringValue());
assertEquals(sourceId, relObject.getSourceId().getStringValue());
assertEquals(targetId, relObject.getTargetId().getStringValue());
assertEquals(source.getSelfLink().getHref(), rel.getLink(CMISConstants.REL_SOURCE).getHref());
assertEquals(target.getSelfLink().getHref(), rel.getLink(CMISConstants.REL_TARGET).getHref());
// check relationships for created item
Map<String, String> args = new HashMap<String, String>();
args.put(CMISScript.ARG_INCLUDE_SUB_RELATIONSHIP_TYPES, "true");
Feed relsAfter = getFeed(relsLink.getHref(), args);
assertNotNull(relsAfter);
assertEquals(1, relsAfter.getEntries().size());
}
public void testGetRelationship()
throws Exception
{
Entry testFolder = createTestFolder("testGetCustomRelationship");
Link childrenLink = testFolder.getLink(CMISConstants.REL_CHILDREN);
assertNotNull(childrenLink);
Feed children = getFeed(childrenLink.getHref());
assertNotNull(children);
Entry source = createDocument(children.getSelfLink().getHref(), "testSource", "/org/alfresco/repo/cmis/rest/test/createcustomdocument.atomentry.xml");
assertNotNull(source);
Entry target = createDocument(children.getSelfLink().getHref(), "testTarget", "/org/alfresco/repo/cmis/rest/test/createcustomdocument.atomentry.xml");
assertNotNull(target);
// retrieve relationships feed on source
Link relsLink = source.getLink(CMISConstants.REL_RELATIONSHIPS);
assertNotNull(relsLink);
// create relationship between source and target documents
CMISObject targetObject = target.getExtension(CMISConstants.OBJECT);
assertNotNull(targetObject);
String targetId = targetObject.getObjectId().getStringValue();
assertNotNull(targetId);
Entry rel = createRelationship(relsLink.getHref(), "R/cmiscustom_assoc", targetId);
assertNotNull(rel);
// get created relationship
Entry relEntry = getEntry(rel.getSelfLink().getHref());
CMISObject relEntryObject = rel.getExtension(CMISConstants.OBJECT);
CMISObject relObject = rel.getExtension(CMISConstants.OBJECT);
assertNotNull(relObject);
assertEquals(relObject.getObjectTypeId().getStringValue(), relEntryObject.getObjectTypeId().getStringValue());
assertEquals(relObject.getSourceId().getStringValue(), relEntryObject.getSourceId().getStringValue());
assertEquals(relObject.getTargetId().getStringValue(), relEntryObject.getTargetId().getStringValue());
assertEquals(source.getSelfLink().getHref(), relEntry.getLink(CMISConstants.REL_SOURCE).getHref());
assertEquals(target.getSelfLink().getHref(), relEntry.getLink(CMISConstants.REL_TARGET).getHref());
}
} }

View File

@@ -4,8 +4,8 @@
<summary>${NAME} (summary)</summary> <summary>${NAME} (summary)</summary>
<content type="text/html">${CONTENT}</content> <content type="text/html">${CONTENT}</content>
<cmis:object> <cmis:object>
<cmis:properties> <cmis:properties>
<cmis:propertyString cmis:name="ObjectTypeId"><cmis:value>document</cmis:value></cmis:propertyString> <cmis:propertyString cmis:name="ObjectTypeId"><cmis:value>document</cmis:value></cmis:propertyString>
</cmis:properties> </cmis:properties>
</cmis:object> </cmis:object>
</entry> </entry>

View File

@@ -0,0 +1,9 @@
<?xml version="1.0" encoding="utf-8"?>
<entry xmlns="http://www.w3.org/2005/Atom" xmlns:cmis="http://docs.oasis-open.org/ns/cmis/core/200901">
<cmis:object>
<cmis:properties>
<cmis:propertyString cmis:name="ObjectTypeId"><cmis:value>${RELTYPE}</cmis:value></cmis:propertyString>
<cmis:propertyId cmis:name="TargetId"><cmis:value>${TARGETID}</cmis:value></cmis:propertyId>
</cmis:properties>
</cmis:object>
</entry>

View File

@@ -208,6 +208,13 @@ public class CMISSchemaTest extends TestCase
assertValidXML(xml, cmisValidator.getCMISAtomValidator()); assertValidXML(xml, cmisValidator.getCMISAtomValidator());
} }
public void testAtomEntry()
throws Exception
{
String xml = getXML("example_atomentry.xml");
assertValidXML(xml, cmisValidator.getCMISAtomValidator());
}
// //
// Missing from v0.61 // Missing from v0.61
// //

View File

@@ -0,0 +1,26 @@
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<ns3:entry xmlns:ns1="http://docs.oasis-open.org/ns/cmis/core/200901" xmlns:ns2="http://docs.oasis-open.org/ns/cmis/messaging/200901" xmlns:ns3="http://www.w3.org/2005/Atom" xmlns:ns4="http://www.w3.org/2007/app">
<ns3:author>
<ns3:name>Al Brown</ns3:name>
<ns3:uri>http://www.ibm.com/</ns3:uri>
<ns3:email>albertcbrown@us.ibm.com</ns3:email>
</ns3:author>
<ns3:content src="http://cmisexample.oasis-open.org/rep1/70cfe57f-5d59-4293-9cbc-842107117d65"/>
<ns3:id>urn:uuid:70cfe57f-5d59-4293-9cbc-842107117d65</ns3:id>
<ns3:link rel="self" href="http://cmisexample.oasis-open.org/rep1/70cfe57f-5d59-4293-9cbc-842107117d65"/>
<ns3:link rel="edit" href="http://cmisexample.oasis-open.org/rep1/70cfe57f-5d59-4293-9cbc-842107117d65"/>
<ns3:link type="application/cmis+xml;type=allowableActions" rel="allowableactions" href="http://cmisexample.oasis-open.org/rep1/70cfe57f-5d59-4293-9cbc-842107117d65/allowableactions"/>
<ns3:link type="application/atom+xml;type=entry" rel="type" href="http://cmisexample.oasis-open.org/rep1/70cfe57f-5d59-4293-9cbc-842107117d65/type"/>
<ns3:link rel="edit-media" href="http://cmisexample.oasis-open.org/rep1/70cfe57f-5d59-4293-9cbc-842107117d65/edit-media"/>
<ns3:link rel="alternate" href="http://cmisexample.oasis-open.org/rep1/70cfe57f-5d59-4293-9cbc-842107117d65/alternate"/>
<ns3:link type="application/atom+xml;type=feed" rel="parents" href="http://cmisexample.oasis-open.org/rep1/70cfe57f-5d59-4293-9cbc-842107117d65/parents"/>
<ns3:link type="application/atom+xml;type=feed" rel="allversions" href="http://cmisexample.oasis-open.org/rep1/70cfe57f-5d59-4293-9cbc-842107117d65/allversions"/>
<ns3:link type="application/atom+xml;type=entry" rel="latestversion" href="http://cmisexample.oasis-open.org/rep1/70cfe57f-5d59-4293-9cbc-842107117d65/latestversions"/>
<ns3:link length="4123" type="text/plain" rel="stream" href="http://cmisexample.oasis-open.org/rep1/70cfe57f-5d59-4293-9cbc-842107117d65media"/>
<ns3:link type="application/atom+xml;type=feed" rel="relationships" href="http://cmisexample.oasis-open.org/rep1/70cfe57f-5d59-4293-9cbc-842107117d65/relationships"/>
<ns3:published>2009-04-17T13:50:58.656-07:00</ns3:published>
<ns3:summary type="html">HTML summary of Entry 70cfe57f-5d59-4293-9cbc-842107117d65</ns3:summary>
<ns3:title type="text">CMIS Example Document</ns3:title>
<ns3:updated>2009-04-17T13:50:58.656-07:00</ns3:updated>
<ns1:terminator xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:nil="true"/>
</ns3:entry>

View File

@@ -42,6 +42,19 @@
<multiple>true</multiple> <multiple>true</multiple>
</property> </property>
</properties> </properties>
<associations>
<association name="cmiscustom:assoc">
<source>
<mandatory>false</mandatory>
<many>false</many>
</source>
<target>
<class>cm:content</class>
<mandatory>false</mandatory>
<many>true</many>
</target>
</association>
</associations>
</type> </type>
</types> </types>