So today someone asked us if it was possible to link/unlink folders via REST api. We were somehow surprised because this is a quite straightforward API, so we took a look at the swagger documentation where we found (surprise!) a “parentLink” resource:
This seems simple enough, right? Well, while trying this on postman first question popped up: How do we specify the target? Umm… weird enough… anyway let’s try the example… great, it doesn’t work because the body is wrong… Let’s check the documentation… great, it doesn’t even mention the controllers because these are obviously “documented” on swagger… So what do we do? Well, let’s decompile the controller:
@RequestMapping(method = {RequestMethod.POST}, produces = {"application/vnd.emc.documentum+json", "application/vnd.emc.documentum+xml", "application/json", "application/xml"})
@ResponseBody
@ResponseStatus(HttpStatus.CREATED)
@ResourceViewBinding({FolderLinkView.class})
public FolderLink link(@PathVariable("repositoryName") String repositoryName, @PathVariable("objectId") String childId, @RequestBody FolderLink folderLink, @RequestUri UriInfo uriInfo) throws DfException {
validateTargetControllerAccessible(ParentFolderLinkController.class);
String parentId = folderLink.getObjectId();
if (parentId == null)
throw new RestClientErrorException("E_OBJECT_ID_NOT_FOUND", null, HttpStatus.BAD_REQUEST, null);
ResourceReferenceValidator.validate(folderLink.getHref(), parentId, RESOURCE_NAMES_TO_VALIDATE);
this.folderLinksManager.link(childId, parentId);
FolderLink newFolderLink = new FolderLink(parentId, childId, false);
Map<String, Object> otherParams = new HashMap<>();
otherParams.put("link_to_parent", Boolean.valueOf(true));
otherParams.put("post_from_collection", Boolean.valueOf(true));
return (FolderLink)getRenderedObject(repositoryName, (Linkable)newFolderLink, true, uriInfo, otherParams);
}
The code seems simple enough, as the POST expects a FolderLink object, but this fails with the example. Why? Let’s check the FolderLink class:
@SerializableType(value = "folder-link", jsonWriteRootAsField = false, fieldVisibility = SerializableType.FieldVisibility.NONE, fieldOrder = {"href", "child-id", "parent-id", "links"}, xmlNS = "http://identifiers.emc.com/vocab/documentum", xmlNSPrefix = "dm")
public class FolderLink extends AbstractLinkable {
@SerializableField(xmlAsAttribute = true)
private String href;
@SerializableField(value = "child-id", xmlAsAttribute = true)
private String childId;
@SerializableField(value = "parent-id", xmlAsAttribute = true)
private String parentId;
private String objectId;
public FolderLink() {
this.href = null;
this.childId = null;
this.parentId = null;
this.objectId = null;
}
Great, no properties element. So that’s why the example miserably fails. So let’s add a child-id and a parent-id attributes and this will work, right? Wrong. Another error stating that the source id can’t be null. What the heck? Let’s take a look at the controller’s line where it calls “getObjectId”:
public String getObjectId() {
if (Strings.isNullOrEmpty(this.objectId) &&
!Strings.isNullOrEmpty(this.href))
this.objectId = IdExtracter.extract(this.href);
return this.objectId;
}
So… The swagger example prompts you to use “properties” as an element in the POST body, while, if you remotely want this to work, you have to provide a href element with the object you want to use as parent:
<?xml version="1.0" encoding="UTF-8"?>
<folder-link>
<href>https://server/dctm-rest/repositories/test_repo/folders/0c0180aa80001107</href>
</folder-link>
As you can see, almost exactly as the swagger example (and very logical approach too, because providing parentid was too complex I guess…)