Skip navigation
All Places > Alfresco Content Services (ECM) > Blog > 2017 > April
2017

It’s been a while since our last release of Community RM, but 2.5.b is now available.

 

What's in 2.5.b

 

The main driver for this release is compatibility with Alfresco Community 201702 (i.e. Alfresco 5.2.x). This involved some work to integrate with the shiny new site creation page as well as a few less visible compatibility fixes. The release also contains a lot of bug fixes, particularly around retention schedules, audit and rules.

 

Create site dialog with RM 2.5.b

 

Internally the main innovation has been to create a build which just tests Community behaviour. We have a lot of tests for our Enterprise artefacts (which include the community code too), but in order to allow us to make Community releases more frequently we have set up a new build to look for regressions in our community code.

 

We have also set up a publicly accessible Travis build to make it easier for anyone to see the status the current code, as well as any pull requests they submit.

 

What’s coming up next?

 

On master you’ll see all our current community development, which includes a large focus on the first release of the governance services v1 REST API. It’s been through a couple of significant refactorings since we started work on it, but we think it’s nearing completion now. The current plan is to release it in 2.6.a, which should be in roughly a month if all goes well.

 

You might also have noticed a new module in the project – rm-benchmark. We’ve recently been investigating our performance at scale, and the first part of that is to benchmark the performance of our core capabilities. See RM-3953 for more details.

 

So please let us know what you think of 2.5.b and what you'd like to see in 2.6.a and beyond.

 

Links

 

Our journey through the v1 REST APIs in 5.2 is nearly at an end, last time we looked at the trashcan API, in this part we're going to look at arguably one of the most important APIs, the discovery API.

 

As there is only one GET endpoint in this API there is no Postman collection for this post as you can follow along with this post using just a browser.

 

The discovery API is available at http://localhost:8080/alfresco/api/discovery and as the name suggests it provides information on the repository, such as version number, feature status and which modules are installed.

 

We recommend that clients call this API as early as possible in their lifecycle so that it's features can be sensitive to the repository's capabilities. A good example is the ability the manage shared links, this feature can be disabled on the server, in this scenario the /shared-links endpoints will return a 501 status code, if this is not handled by the UI it does not provide a very good user experience!

 

Calling this API on my repository (installed using the Community installer) returns the following response:

{
  "entry": {
    "repository": {
      "edition": "Community",
      "version": {
        "major": "5",
        "minor": "2",
        "patch": "0",
        "hotfix": "0",
        "schema": 10005,
        "label": "r135134-b14",
        "display": "5.2.0.0 (r135134-b14) schema 10005"
      },
      "status": {
        "isReadOnly": false,
        "isAuditEnabled": true,
        "isQuickShareEnabled": true,
        "isThumbnailGenerationEnabled": true
      },
      "modules": [
        {
          "id": "alfresco-aos-module",
          "title": "Alfresco Office Services Module",
          "description": "Allows applications that can talk to a SharePoint server to talk to your Alfresco installation",
          "version": "1.1.5",
          "installDate": "2017-02-20T10:52:26.618+0000",
          "installState": "INSTALLED",
          "versionMin": "5.1",
          "versionMax": "999"
        },
        {
          "id": "org.alfresco.integrations.google.docs",
          "title": "Alfresco / Google Docs Integration",
          "description": "The Repository side artifacts of the Alfresco / Google Docs Integration.",
          "version": "3.0.3",
          "installDate": "2017-02-20T10:52:27.288+0000",
          "installState": "INSTALLED",
          "versionMin": "5.0.0",
          "versionMax": "5.99.99"
        },
        {
          "id": "alfresco-share-services",
          "title": "Alfresco Share Services AMP",
          "description": "Module to be applied to alfresco.war, containing APIs for Alfresco Share",
          "version": "5.2.0",
          "installDate": "2017-02-20T10:52:27.779+0000",
          "installState": "INSTALLED",
          "versionMin": "5.1",
          "versionMax": "999"
        },
        {
          "id": "alfresco-trashcan-cleaner",
          "title": "alfresco-trashcan-cleaner project",
          "description": "The Alfresco Trash Can Cleaner (Alfresco Module)",
          "version": "2.2",
          "installState": "UNKNOWN",
          "versionMin": "0",
          "versionMax": "999"
        },
        {
          "id": "fdk-custom-model-module",
          "title": "FDK Custom Model Module",
          "description": "Packages the FDK custom model as a simple module",
          "version": "1.0-SNAPSHOT",
          "installState": "UNKNOWN",
          "versionMin": "0",
          "versionMax": "999"
        }
      ]
    }
  }
}

 

Although this has been the shortest post in this series so far, hopefully you can see the value and importance of this small, simple API.

 

There is one final post in this series to come where we'll cover some topics that apply to all APIs and highlight some capabilities of the API you might not have known were there!

We will now follow Gavin Cornwell  v1 REST API - Part 11 - Trashcan   examples and see how we can achieve the same experience using the SDK

 

To make the exercise more concise we will execute each request in a synchronous way.

Important Notice

Alfresco Java Client is currently in Early Access mode. It evolves as you use them, as you give feedback, and as the developers update and add file. We like to think app & lib development as services that grow and evolve with the involvement of the community.

 

Prerequisites

In order to follow along you'll need an environment to do so, firstly download and install the 5.2.c Early Access Community Release. In our case we will consider Alfresco is available at http://localhost:8080/alfresco and the "admin" user is available and has "admin" as password.

 

Create File 

File file = new File("W:\\test.txt");
RequestBody requestBody = RequestBody.create(MediaType.parse("text/plain"), file);
MultipartBody.Builder multipartBuilder = new MultipartBody.Builder();
multipartBuilder.addFormDataPart("filedata", "content-to-be-deleted.txt", requestBody);
RequestBody fileRequestBody = multipartBuilder.build();
NodeRepresentation nodeToDelete = client.getNodesAPI().createNodeCall(NodesAPI.FOLDER_MY, fileRequestBody).execute().body();

Delete File 

client.getNodesAPI().deleteNodeCall(nodeToDelete.getId()).execute();
        Assert.assertFalse(client.getNodesAPI().getNodeCall(nodeToDelete.getId()).execute().isSuccessful());

 

List deleted nodes  

TrashcanAPI trashcanAPI = client.getTrashcanAPI();
ResultPaging<DeletedNodeRepresentation> deletedNodes = trashcanAPI.listDeletedNodesCall().execute().body();
Assert.assertTrue(deletedNodes.getCount() > 10);

 

Deleted node details 

DeletedNodeRepresentation deletedNodeInfo = trashcanAPI.getDeletedNodeCall(nodeToDelete.getId()).execute().body();
Assert.assertEquals(deletedNodeInfo.getId() , nodeToDelete.getId());

 

Restore node 

NodeRepresentation restoredNode = trashcanAPI.restoreDeletedNodeCall(nodeToDelete.getId(), null).execute().body();
Assert.assertEquals(restoredNode.getId() , nodeToDelete.getId());
Assert.assertTrue(client.getNodesAPI().getNodeCall(nodeToDelete.getId()).execute().isSuccessful());

 

Permanently delete node  

client.getNodesAPI().deleteNodeCall(nodeToDelete.getId()).execute();
Response<Void> purgedNodeResponse = trashcanAPI.purgeDeletedNodeCall(restoredNode.getId()).execute();
Assert.assertTrue(purgedNodeResponse.isSuccessful());
Assert.assertFalse(client.getNodesAPI().getNodeCall(nodeToDelete.getId()).execute().isSuccessful());

 

Alfresco Java Client SDK Series

We will now follow Gavin Cornwell  v1 REST API - Part 10 - People   examples and see how we can achieve the same experience using the SDK

 

To make the exercise more concise we will execute each request in a synchronous way.

Important Notice

Alfresco Java Client is currently in Early Access mode. It evolves as you use them, as you give feedback, and as the developers update and add file. We like to think app & lib development as services that grow and evolve with the involvement of the community.

 

Prerequisites

In order to follow along you'll need an environment to do so, firstly download and install the 5.2.c Early Access Community Release. In our case we will consider Alfresco is available at http://localhost:8080/alfresco and the "admin" user is available and has "admin" as password.

 

Create person 

//Create Person
PeopleAPI peopleAPI = client.getPeopleAPI();

PersonBodyCreate bodyCreate = new PersonBodyCreate("jdoe").firstName("John").lastName("Doe")
          .email("john.doe@example.com").password("jdoe").skypeId("johndoe_skype").jobTitle("Software Engineer");

PersonRepresentation personRepresentation = peopleAPI.createPersonCall(bodyCreate).execute().body();
Assert.assertEquals(personRepresentation.getId(), "jdoe");
Assert.assertEquals(personRepresentation.getFirstName(), "John");
Assert.assertEquals(personRepresentation.getLastName(), "Doe");
Assert.assertEquals(personRepresentation.getEmail(), "john.doe@example.com");
Assert.assertEquals(personRepresentation.getSkypeId(), "johndoe_skype");
Assert.assertEquals(personRepresentation.getJobTitle(), "Software Engineer");

 

List people

ResultPaging<PersonRepresentation> personList = peopleAPI.listPeopleCall().execute().body();
Assert.assertEquals(personList.getCount(), 7);

 

Find people

ResultPaging<PersonRepresentation> searchPersonList = client.getQueriesAPI().findPeopleCall("jdoe").execute().body();
Assert.assertEquals(searchPersonList.getCount(), 1);
Assert.assertEquals(searchPersonList.getList().get(0).getId(), "jdoe");

 

Person Details

PersonRepresentation jdoeDetails = peopleAPI.getPersonCall("jdoe").execute().body();
Assert.assertEquals(jdoeDetails.getId(), "jdoe");
Assert.assertEquals(jdoeDetails.getFirstName(), "John");
Assert.assertEquals(jdoeDetails.getLastName(), "Doe");
Assert.assertEquals(jdoeDetails.getEmail(), "john.doe@example.com");
Assert.assertEquals(jdoeDetails.getSkypeId(), "johndoe_skype");
Assert.assertEquals(jdoeDetails.getJobTitle(), "Software Engineer");

 

Update person details

PersonBodyUpdate bodyUpdate = new PersonBodyUpdate().firstName("Johnathon").mobile("07000 123456");

PersonRepresentation updatedPerson = peopleAPI.updatePersonCall("jdoe", bodyUpdate, null).execute().body();
Assert.assertEquals(updatedPerson.getMobile(), "07000 123456");
Assert.assertEquals(updatedPerson.getFirstName(), "Johnathon");

 

Change password

PersonBodyUpdate changePassword = new PersonBodyUpdate().oldPassword("jdoe").password("my-new-password");

PersonRepresentation updatedPPerson = peopleAPI.updatePersonCall("jdoe", changePassword, null).execute().body();

 

Disable person

PersonBodyUpdate disablePersonBody = new PersonBodyUpdate().enabled(false);

PersonRepresentation disablePerson = peopleAPI.updatePersonCall("jdoe", disablePersonBody, null).execute().body();
Assert.assertEquals(disablePerson.isEnabled(), Boolean.FALSE);

 

 

 

Alfresco Java Client SDK Series

We will now follow Gavin Cornwell  v1 REST API - Part 9 - Queries & Search   examples and see how we can achieve the same experience using the SDK

 

To make the exercise more concise we will execute each request in a synchronous way.

Important Notice

Alfresco Java Client is currently in Early Access mode. It evolves as you use them, as you give feedback, and as the developers update and add file. We like to think app & lib development as services that grow and evolve with the involvement of the community.

 

Prerequisites

In order to follow along you'll need an environment to do so, firstly download and install the 5.2.c Early Access Community Release. In our case we will consider Alfresco is available at http://localhost:8080/alfresco and the "admin" user is available and has "admin" as password.

 

Create Public Site

//Create a public Site
SitesAPI sitesAPI = client.getSitesAPI();

//Create public site
SiteBodyCreate siteBodyCreate = new SiteBodyCreate("queriesSearchSite", "Queries and Search Site", "Site created for queries and search blog post", SiteVisibilityEnum.PUBLIC);
Response<SiteRepresentation> siteRepresentationResponse = sitesAPI.createSiteCall(siteBodyCreate).execute();
SiteRepresentation siteRepresentation = siteRepresentationResponse.body();
Assert.assertEquals(siteRepresentation.getId(), "queriesSearchSite");
Assert.assertEquals(siteRepresentation.getVisibilityEnum(), SiteVisibilityEnum.PUBLIC);

 

Retrieve document library container

Response<SiteContainerRepresentation> doclibContainerResponse = sitesAPI.getSiteContainerCall("queriesSearchSite", "documentLibrary").execute();
SiteContainerRepresentation doclibContainer = doclibContainerResponse.body();
Assert.assertEquals(doclibContainer.getFolderId(), "documentLibrary");

 

Upload File (text)

File file = new File("W:\\test.txt");
RequestBody requestBody = RequestBody.create(MediaType.parse("text/plain"), file);
MultipartBody.Builder multipartBuilder = new MultipartBody.Builder();
multipartBuilder.addFormDataPart("filedata", "test.txt", requestBody);
RequestBody fileRequestBody = multipartBuilder.build();
client.getNodesAPI().createNodeCall(doclibContainer.getId(), fileRequestBody).execute();

 

Upload File (Image)

file = new File("W:\\image.png");
requestBody = RequestBody.create(MediaType.parse("image/png"), file);
multipartBuilder = new MultipartBody.Builder();
multipartBuilder.addFormDataPart("filedata", "image.png", requestBody);
fileRequestBody = multipartBuilder.build();
client.getNodesAPI().createNodeCall(doclibContainer.getId(), fileRequestBody).execute();

 

Nodes Query

ResultPaging<NodeRepresentation> loremResult = client.getQueriesAPI().findNodesCall("lorem").execute().body();
Assert.assertTrue(loremResult.getCount() >= 7);

 

Sites Query

ResultPaging<SiteRepresentation> siteResult = client.getQueriesAPI().findSitesCall("queries").execute().body();
Assert.assertEquals(siteResult.getCount(), 1);
Assert.assertEquals(siteResult.getList().get(0).getId(), "queriesSearchSite");

 

People Query

ResultPaging<PersonRepresentation> personResult = client.getQueriesAPI().findPeopleCall("jackson").execute().body();
Assert.assertEquals(personResult.getCount(), 1);
Assert.assertEquals(personResult.getList().get(0).getId(), "mjackson");

Basic Search

SearchAPI searchAPI = client.getSearchAPI();

QueryBody body = new QueryBody().query(new RequestQuery().query("lorem"));
ResultSetRepresentation<ResultNodeRepresentation> loremSearchResult = searchAPI.searchCall(body).execute().body();
Assert.assertTrue(loremSearchResult.getCount() >= 7);

 

Basic CMIS search

RequestQuery cmisQuery = new RequestQuery().query("select * from cmis:document WHERE cmis:name LIKE 'test.%'")
     .language(RequestQuery.LanguageEnum.CMIS);
QueryBody cmisbody = new QueryBody().query(cmisQuery);
ResultSetRepresentation<ResultNodeRepresentation> cmisResult = searchAPI.searchCall(cmisbody).execute().body();
Assert.assertTrue(cmisResult.getCount() >= 1);

 

Basic Lucene search

RequestQuery luceneQuery = new RequestQuery().query("+@cm\\:modified:[NOW/DAY-7DAYS TO NOW/DAY+1DAY] +TYPE:\"cm:content\"")
   .language(RequestQuery.LanguageEnum.LUCENE);
QueryBody lucenebody = new QueryBody().query(luceneQuery);
ResultSetRepresentation<ResultNodeRepresentation> luceneResult = searchAPI.searchCall(lucenebody).execute().body();
Assert.assertTrue(luceneResult.getCount() == 100);

 

Search with paging and sorting

RequestQuery pagingQuery = new RequestQuery().query("+TYPE:\"cm:content\"").language(RequestQuery.LanguageEnum.AFTS);

List<RequestSortDefinition> sortDefinitions = Arrays.asList(
          new RequestSortDefinition().type(RequestSortDefinition.TypeEnum.FIELD).field("cm:name").ascending(false));

RequestPagination pagination = new RequestPagination().skipCount(10).maxItems(25);

QueryBody pagingQueryBody = new QueryBody().query(pagingQuery).sort(sortDefinitions).paging(pagination);
ResultSetRepresentation<ResultNodeRepresentation> pagingResult = searchAPI.searchCall(pagingQueryBody).execute().body();
Assert.assertTrue(pagingResult.getCount() == 25);
Assert.assertEquals(pagingResult.getPagination().getMaxItems(), 25);
Assert.assertEquals(pagingResult.getPagination().getSkipCount(), 10);

 

Search with facets

RequestQuery facetQuery = new RequestQuery().query("(name:\"test*\" OR title:\"test*\") AND TYPE:\"cm:content\"");

List<RequestFacetQuery> facetsQuery = Arrays.asList(
          new RequestFacetQuery().query("content.size:[0 TO 10240]").label("Small Files"),
          new RequestFacetQuery().query("content.mimetype:'text/plain'").label("Plain Text"),
          new RequestFacetQuery().query("content.mimetype:'image/jpeg' OR content.mimetype:'image/png' OR content.mimetype:'image/gif'").label("Images"),
          new RequestFacetQuery().query("content.mimetype:'application/msword' OR content.mimetype:'application/vnd.ms-excel'").label("Office")
          );

RequestFacetFields facetFields = new RequestFacetFields().facets(Arrays.asList(new RequestFacetFieldsFacets().field("creator")));

QueryBody facetQueryBody = new QueryBody().query(facetQuery).facetQueries(facetsQuery).facetFields(facetFields);
ResultSetRepresentation<ResultNodeRepresentation> facetResult = searchAPI.searchCall(facetQueryBody).execute().body();
Assert.assertTrue(facetResult.getCount() >= 3);
Assert.assertEquals(facetResult.getContext().getFacetQueries().size(), 4);
Assert.assertEquals(facetResult.getContext().getFacetFields().get(0).getBuckets().size(), 2);
Assert.assertEquals(facetResult.getList().size(), 3);

 

Search with term hightlighting

//Search with term highlighting
RequestQuery highlightQuery = new RequestQuery().query("(name:\"test*\" OR title:\"test*\") AND TYPE:\"cm:content\"");

List<RequestHighlightField> highlightField = Arrays.asList(
          new RequestHighlightField().field("cm:name").prefix("(").postfix(")"),
          new RequestHighlightField().field("{http://www.alfresco.org/model/content/1.0}title")
);

RequestHighlight highlight = new RequestHighlight().fields(highlightField);

QueryBody highlightQueryBody = new QueryBody().query(highlightQuery).highlight(highlight);
ResultSetRepresentation<ResultNodeRepresentation> highlightResult = searchAPI.searchCall(highlightQueryBody).execute().body();
Assert.assertTrue(highlightResult.getCount() >= 3);
Assert.assertEquals(highlightResult.getList().size(), 3);
Assert.assertEquals(highlightResult.getList().get(0).getSearch().getHighlight().size(), 2);

 

 

Alfresco Java Client SDK Series

We will now follow Gavin Cornwell  v1 REST API - Part 8 - Sites   examples and see how we can achieve the same experience using the SDK

 

To make the exercise more concise we will execute each request in a synchronous way.

Important Notice

Alfresco Java Client is currently in Early Access mode. It evolves as you use them, as you give feedback, and as the developers update and add file. We like to think app & lib development as services that grow and evolve with the involvement of the community.

 

Prerequisites

In order to follow along you'll need an environment to do so, firstly download and install the 5.2.c Early Access Community Release. In our case we will consider Alfresco is available at http://localhost:8080/alfresco and the "admin" user is available and has "admin" as password.

 

Create Test user

// Select PeopleAPI
PeopleAPI peopleAPI = client.getPeopleAPI();

//Create Test user
PersonBodyCreate personBodyCreate = new PersonBodyCreate("test", "Test", "User", "test@alfresco.com", "test");
Response<PersonRepresentation> personRepresentationResponse = peopleAPI.createPersonCall(personBodyCreate).execute();
PersonRepresentation personRepresentation = personRepresentationResponse.body();
Assert.assertEquals(personRepresentation.getId(), "test");

 

Create public site

// Create new Client and connect with new user test/test user
client = new AlfrescoClient.Builder().httpLogging(HttpLoggingInterceptor.Level.BODY)
     .connect("http://localhost:8080/alfresco", "test", "test").build();

//Create a public Site
SitesAPI sitesAPI = client.getSitesAPI();

//Create public site
/*SiteBodyCreate siteBodyCreate = new SiteBodyCreate("publicSite", "Public Site", "Public site created for blog post", SiteVisibilityEnum.PUBLIC);
Response<SiteRepresentation> siteRepresentationResponse = sitesAPI.createSiteCall(siteBodyCreate).execute();
SiteRepresentation siteRepresentation = siteRepresentationResponse.body();
Assert.assertEquals(siteRepresentation.getId(), "publicSite");
Assert.assertEquals(siteRepresentation.getVisibilityEnum(), SiteVisibilityEnum.PUBLIC);
Assert.assertEquals(siteRepresentation.getRole(), "SiteManager");

 

Retrieve document library container

//Retrieve document library container
Response<SiteContainerRepresentation> doclibContainerResponse = sitesAPI.getSiteContainerCall("publicSite", "documentLibrary").execute();
SiteContainerRepresentation doclibContainer = doclibContainerResponse.body();
Assert.assertEquals(doclibContainer.getFolderId(), "documentLibrary");

 

Update site description

//Update site description
SiteBodyUpdate update = new SiteBodyUpdate("Public site created for blog post - part 8");
Response<SiteRepresentation> siteRepresentationResponse = sitesAPI.updateSiteCall("publicSite", update).execute();
SiteRepresentation siteRepresentation = siteRepresentationResponse.body();
Assert.assertEquals(siteRepresentation.getId(), "publicSite");
Assert.assertEquals(siteRepresentation.getDescription(), "Public site created for blog post - part 8");

 

Create moderated site

SiteBodyCreate siteBodyCreate = new SiteBodyCreate("moderatedSite", "Moderated Site",
     "Moderated site created for blog post", SiteVisibilityEnum.MODERATED);
Response<SiteRepresentation> siteRepresentationResponse = sitesAPI.createSiteCall(siteBodyCreate).execute();
SiteRepresentation siteRepresentation = siteRepresentationResponse.body();
Assert.assertEquals(siteRepresentation.getId(), "moderatedSite");
Assert.assertEquals(siteRepresentation.getVisibilityEnum(), SiteVisibilityEnum.MODERATED);
Assert.assertEquals(siteRepresentation.getRole(), "SiteManager");

 

Create 2nd test user

PersonBodyCreate personBodyCreate = new PersonBodyCreate("test2", "Test", "User2", "test2@alfresco.com",
          "test2");
Response<PersonRepresentation> personRepresentationResponse = peopleAPI.createPersonCall(personBodyCreate)
          .execute();
PersonRepresentation personRepresentation = personRepresentationResponse.body();
Assert.assertEquals(personRepresentation.getId(), "test2");

 

Join public site

// Create new Client and connect with new user test2/test2 user
AlfrescoClient client2 = new AlfrescoClient.Builder().httpLogging(HttpLoggingInterceptor.Level.BODY)
          .connect("http://localhost:8080/alfresco", "test2", "test2").build();

// Join public site
SiteMembershipRequestBodyCreate requestBodyCreate = new SiteMembershipRequestBodyCreate("publicSite");
Response<SiteMembershipRequestRepresentation> publicSiteRepresentationResponse = client2.getSitesAPI()
          .createSiteMembershipRequestForPersonCall("test2", requestBodyCreate, null).execute();
SiteMembershipRequestRepresentation requestRepresentation = publicSiteRepresentationResponse.body();
Assert.assertEquals(requestRepresentation.getSite().getRole(), "SiteConsumer");
Assert.assertEquals(requestRepresentation.getSite().getId(), "publicSite");
Assert.assertEquals(requestRepresentation.getId(), "publicSite");

 

Request to join moderated site

SiteMembershipRequestBodyCreate requestModeratedBodyCreate = new SiteMembershipRequestBodyCreate(
          "moderatedSite", "I would like to join this site as it looks interesting", null);
Response<SiteMembershipRequestRepresentation> moderatedSiteRepresentationResponse = client2.getSitesAPI()
          .createSiteMembershipRequestForPersonCall("test2", requestModeratedBodyCreate, null).execute();
SiteMembershipRequestRepresentation moderatedRequestRepresentation = moderatedSiteRepresentationResponse.body();
Assert.assertNull(moderatedRequestRepresentation.getSite().getRole());
Assert.assertEquals(moderatedRequestRepresentation.getSite().getId(), "moderatedSite");
Assert.assertEquals(moderatedRequestRepresentation.getId(), "moderatedSite");

 

Review site membership requests

ResultPaging<SiteMembershipRequestRepresentation> siteMembershipRequestPaging = client2.getSitesAPI()
          .listSiteMembershipRequestsForPersonCall("test2").execute().body();
Assert.assertEquals(siteMembershipRequestPaging.getCount(), 1);
Assert.assertEquals(siteMembershipRequestPaging.getList().get(0).getId(), "moderatedSite");*/

 

List site members

ResultPaging<SiteMemberRepresentation> siteMembers = client2.getSitesAPI().listSiteMembershipsCall("publicSite").execute().body();
Assert.assertEquals(siteMembers.getCount(), 2);
Assert.assertEquals(siteMembers.getList().size(), 2);

 

List my sites

ResultPaging<SiteRoleRepresentation> mySites = client2.getSitesAPI().listSiteMembershipsForPersonCall("test2").execute().body();
Assert.assertEquals(mySites.getCount(), 1);
Assert.assertEquals(mySites.getList().get(0).getRole(), "SiteConsumer");

 

List all sites

ResultPaging<SiteRepresentation> allSites = client2.getSitesAPI().listSitesCall().execute().body();
Assert.assertEquals(allSites.getCount(), 3);

 

Find site

ResultPaging<SiteRepresentation> findSites = client2.getQueriesAPI().findSitesCall("public").execute().body();
Assert.assertEquals(findSites.getCount(), 1);
Assert.assertEquals(findSites.getList().get(0).getId(), "publicSite");

 

Leave site

Response<Void> leaveSiteResponse = client2.getSitesAPI().deleteSiteMembershipForPersonCall("test2", "publicSite").execute();
Assert.assertEquals(leaveSiteResponse.isSuccessful(), true);
Assert.assertEquals(client2.getSitesAPI().listSiteMembershipsForPersonCall("test2").execute().body().getCount(), 0);

 

Add site member

SiteMembershipBodyCreate siteMembershipBodyCreate = new SiteMembershipBodyCreate("test2", "SiteContributor");
SiteMemberRepresentation siteMember = client.getSitesAPI()
          .createSiteMembershipCall("publicSite", siteMembershipBodyCreate, null).execute().body();
Assert.assertEquals(siteMember.getRole(), "SiteContributor");
Assert.assertEquals(siteMember.getId(), "test2");
Assert.assertEquals(client2.getSitesAPI().listSiteMembershipsForPersonCall("test2").execute().body().getCount(), 1);

 

Change member role

SiteMembershipBodyUpdate siteMembershipBodyUpdate = new SiteMembershipBodyUpdate("SiteManager");
SiteMemberRepresentation siteMemberUpdated = client.getSitesAPI()
     .updateSiteMembershipCall("publicSite", "test2", siteMembershipBodyUpdate, null).execute().body();
Assert.assertEquals(siteMemberUpdated.getRole(), "SiteManager");
Assert.assertEquals(siteMemberUpdated.getId(), "test2");
Assert.assertEquals(client2.getSitesAPI().listSiteMembershipsForPersonCall("test2").execute().body().getCount(), 1);

 

Delete site

Response<Void> deleteSiteResponse = client.getSitesAPI().deleteSiteCall("publicSite").execute();
Assert.assertTrue(deleteSiteResponse.isSuccessful());

 

 

Alfresco Java Client SDK Series

We will now follow Gavin Cornwell  v1 REST API - Part 7 - Collaboration  examples and see how we can achieve the same experience using the SDK

 

To make the exercise more concise we will execute each request in a synchronous way.

Important Notice

Alfresco Java Client is currently in Early Access mode. It evolves as you use them, as you give feedback, and as the developers update and add file. We like to think app & lib development as services that grow and evolve with the involvement of the community.

 

Prerequisites

In order to follow along you'll need an environment to do so, firstly download and install the 5.2.c Early Access Community Release. In our case we will consider Alfresco is available at http://localhost:8080/alfresco and the "admin" user is available and has "admin" as password.

 

Create Content

// Select NodesAPI
NodesAPI nodesAPI = client.getNodesAPI();

//Create Content
NodeBodyCreate emptyFileBody = new NodeBodyCreate("collaboration.txt", ContentModel.TYPE_CONTENT);
Response<NodeRepresentation> emptyNodeResponse = nodesAPI.createNodeCall(NodesAPI.FOLDER_MY, emptyFileBody).execute();
NodeRepresentation emptyNode = emptyNodeResponse.body();
Assert.assertEquals(emptyNode.getContent().getSizeInBytes(), 0);

 

Add a comment

//Setting up Comments API
CommentsAPI commentsAPI = client.getCommentsAPI();

//Add a comment
String commentValue = "This is my comment";
CommentBody commentBody = new CommentBody(commentValue);
Response<CommentRepresentation> commentResponse = commentsAPI.createCommentCall(emptyNode.getId(), commentBody).execute();
CommentRepresentation commentRepresentation = commentResponse.body();
Assert.assertEquals(commentRepresentation.getContent(), commentValue);
Assert.assertEquals(commentRepresentation.getCreatedBy().getId(), "admin");

 

Retrieve comments

//Retrieve Comment
Response<ResultPaging<CommentRepresentation>> commentsListResponse = commentsAPI.listCommentsCall(emptyNode.getId()).execute();
ResultPaging<CommentRepresentation> commentListing = commentsListResponse.body();
Assert.assertEquals(commentListing.getCount(), 1);
Assert.assertEquals(commentListing.getList().get(0), commentRepresentation);
Assert.assertEquals(commentListing.getList().get(0).getCanEdit(), Boolean.TRUE);
Assert.assertEquals(commentListing.getList().get(0).getCanDelete(), Boolean.TRUE);

 

Update comments

//Update Comment
String updatedCommentValue = "Updated comment";
CommentBody updatedCommentBody = new CommentBody(updatedCommentValue);
Response<CommentRepresentation> updatedCommentResponse = commentsAPI.updateCommentCall(emptyNode.getId(), commentRepresentation.getId(), updatedCommentBody).execute();
CommentRepresentation updatedCommentRepresentation = updatedCommentResponse.body();
Assert.assertEquals(updatedCommentRepresentation.getContent(), updatedCommentValue);

 

Delete comment

//Delete Comment
Response<Void> deleteResponse = commentsAPI.deleteCommentCall(emptyNode.getId(), commentRepresentation.getId()).execute();
Assert.assertEquals(deleteResponse.isSuccessful(), true);
Assert.assertEquals(commentsAPI.listCommentsCall(emptyNode.getId()).execute().body().getCount(), 0);

 

Like node

//Setting up Ratings API
RatingsAPI ratingsAPI = client.getRatingsAPI();

//Like a node
RatingBody likeBody = new RatingBody(RatingsAPI.LIKES, true);
Response<RatingRepresentation> likeResponse = ratingsAPI.rateNodeCall(emptyNode.getId(), likeBody).execute();
Assert.assertEquals(likeResponse.body().getId(), RatingsAPI.LIKES);
Assert.assertEquals(likeResponse.body().getMyRating(), Boolean.TRUE);
Assert.assertEquals(likeResponse.body().getAggregate().getNumberOfRatings(), (Integer) 1);

 

Retrieve ratings

Response<ResultPaging<RatingRepresentation>> ratingListingResponse = ratingsAPI.listRatingsCall(emptyNode.getId()).execute();
ResultPaging<RatingRepresentation> ratingListing = ratingListingResponse.body();
Assert.assertEquals(ratingListing.getCount(), 2);
Assert.assertEquals(ratingListing.getList().get(0).getId(),RatingsAPI.FIVE_STAR);
Assert.assertEquals(ratingListing.getList().get(1).getId(),RatingsAPI.LIKES);

 

Unlike Nodes

//Unlike a node
Response<Void> unlikeResponse = ratingsAPI.deleteRatingCall(emptyNode.getId(), RatingsAPI.LIKES).execute();
Assert.assertEquals(unlikeResponse.isSuccessful(), true);

 

Retrieve Like ratings

//Retrieve Like Rating
Response<RatingRepresentation> ratingRepresentationResponse = ratingsAPI.getRatingCall(emptyNode.getId(), RatingsAPI.LIKES).execute();
Assert.assertNull(ratingRepresentationResponse.body().getMyRating());
Assert.assertEquals(ratingRepresentationResponse.body().getAggregate().getNumberOfRatings(), (Integer) 0);

 

Add Blog Tag

//Setting up Tagging API
TagsAPI tagsApi = client.getTagsAPI();

//Add Blog Tag
String blog = "blog";
TagBody tagBody = new TagBody(blog);
Response<TagRepresentation> tagRepresentationResponse = tagsApi.createTagForNodeCall(emptyNode.getId(), tagBody).execute();
TagRepresentation tagBlog = tagRepresentationResponse.body();
Assert.assertEquals(tagBlog.getTag(), blog);

Add Post Tag

//Add Post Tag
String post = "post";
TagBody tag2Body = new TagBody(post);
Response<TagRepresentation> tag2RepresentationResponse = tagsApi.createTagForNodeCall(emptyNode.getId(), tag2Body).execute();
Assert.assertEquals(tag2RepresentationResponse.body().getTag(), post);

 

Retrieve node tags

//Retrieve node Tags
Response<ResultPaging<TagRepresentation>> tagListResponse = tagsApi.listTagsForNodeCall(emptyNode.getId()).execute();
ResultPaging<TagRepresentation> tagList = tagListResponse.body();
Assert.assertEquals(tagList.getCount(), 2);
Assert.assertEquals(tagList.getList().get(0).getTag(), blog);
Assert.assertEquals(tagList.getList().get(1).getTag(), post);

 

Retrieve repository tags

//Retrieve Repository Tags
Response<ResultPaging<TagRepresentation>> tagRepoListResponse = tagsApi.listTagsCall().execute();
ResultPaging<TagRepresentation> tagRepoList = tagRepoListResponse.body();
Assert.assertEquals(tagRepoList.getCount(), 2);
Assert.assertEquals(tagRepoList.getList().get(0).getTag(), blog);
Assert.assertEquals(tagRepoList.getList().get(1).getTag(), post);

 

Delete tag from node

//Delete Tag from Node
Response<Void> deleteTagResponse = tagsApi.deleteTagFromNodeCall(emptyNode.getId(), tagBlog.getId()).execute();
Assert.assertEquals(deleteTagResponse.isSuccessful(), true);
Assert.assertEquals(tagsApi.listTagsForNodeCall(emptyNode.getId()).execute().body().getCount(), 1);

 

 

Alfresco Java Client SDK Series

In the last post we discussed the people API, this time I'm going to show you how you can manage deleted nodes via the trashcan API.

 

As usual, there is a Postman collection to go with this post, click the "Run in Postman" button below to import it into your client.

 

 

Before we can start looking at the trashcan API we need to do a little bit of setup by deleting a node. Firstly, make sure your repository has the test user we created back in part 8. The 1st and 2nd request of the Postman collection creates and deletes a file, respectively. 

 

To list the nodes that have been deleted we can use http://localhost:8080/alfresco/api/-default-/public/alfresco/versions/1/deleted-nodes (3rd request in the Postman collection), the response will look similar to the one below:

{
  "list": {
    "pagination": {
      "count": 1,
      "hasMoreItems": false,
      "totalItems": 1,
      "skipCount": 0,
      "maxItems": 100
    },
    "entries": [
      {
        "entry": {
          "createdAt": "2017-04-11T13:53:02.359+0000",
          "archivedAt": "2017-04-11T13:55:56.432+0000",
          "isFolder": false,
          "isFile": true,
          "createdByUser": {
            "id": "test",
            "displayName": "Test User"
          },
          "modifiedAt": "2017-04-11T13:53:02.359+0000",
          "modifiedByUser": {
            "id": "test",
            "displayName": "Test User"
          },
          "name": "content-to-be-deleted.txt",
          "archivedByUser": {
            "id": "test",
            "displayName": "Test User"
          },
          "id": "1860a21b-b6d2-4cde-aadd-e0bd521787cf",
          "nodeType": "cm:content",
          "content": {
            "mimeType": "text/plain",
            "mimeTypeName": "Plain Text",
            "sizeInBytes": 0,
            "encoding": "UTF-8"
          }
        }
      }
    ]
  }
}

 

The response should look familiar, it's based on the response of /nodes/{{nodeId}}/children with a couple of additions. There is an additional archivedAt property and an archivedByUser property providing details of when the node was archived and by whom. By default, the list is ordered by the archivedAt property, with the most recently deleted being first in the list.

 

As with the nodes API some information is omitted for performance reasons, see the API Explorer for the additional information you can request via the include query parameter.

 

Calling this endpoint as a normal user will only return the nodes you have deleted, however, if you are an administrator, all deleted nodes in the system are returned.

 

To get details of an individual deleted node http://localhost:8080/alfresco/api/-default-/public/alfresco/versions/1/deleted-nodes/{{nodeId}} (4th request in the Postman collection) can be used. This endpoint returns a little more information about the deleted node (see below) by default but again there is some optional information that can be added via the include query parameter, see the API Explorer for details.

{
  "entry": {
    "isFile": true,
    "createdByUser": {
      "id": "test",
      "displayName": "Test User"
    },
    "modifiedAt": "2017-04-11T13:53:02.359+0000",
    "nodeType": "cm:content",
    "content": {
      "mimeType": "text/plain",
      "mimeTypeName": "Plain Text",
      "sizeInBytes": 0,
      "encoding": "UTF-8"
    },
    "aspectNames": [
      "rn:renditioned",
      "cm:ownable",
      "cm:auditable",
      "cm:thumbnailModification"
    ],
    "createdAt": "2017-04-11T13:53:02.359+0000",
    "archivedAt": "2017-04-11T13:55:56.432+0000",
    "isFolder": false,
    "modifiedByUser": {
      "id": "test",
      "displayName": "Test User"
    },
    "name": "content-to-be-deleted.txt",
    "archivedByUser": {
      "id": "test",
      "displayName": "Test User"
    },
    "id": "1860a21b-b6d2-4cde-aadd-e0bd521787cf",
    "properties": {
      "cm:lastThumbnailModification": [
        "doclib:1491918788304"
      ],
      "cm:owner": {
        "id": "test",
        "displayName": "Test User"
      }
    }
  }
}

 

As you would expect, the API also allows you to restore a deleted node which removes it from the list of deleted nodes and puts it back in the "live" store. You can do this by POSTing to http://localhost:8080/alfresco/api/-default-/public/alfresco/versions/1/deleted-nodes/{{nodeId}}/restore (5th request in the Postman collection). For this endpoint there is no body to send. If the node was successfully restored you will receive a 200 OK response with a representation of the "live" node.  

 

Just to make sure you can list the deleted nodes again using http://localhost:8080/alfresco/api/-default-/public/alfresco/versions/1/deleted-nodes (3rd request in the Postman collection) and you should see an empty list as shown below:

{
  "list": {
    "pagination": {
      "count": 0,
      "hasMoreItems": false,
      "totalItems": 0,
      "skipCount": 0,
      "maxItems": 100
    },
    "entries": []
  }
}

 

The opposite or restoring a node is to permanently delete it, once this is done it's final, there is no way to get the node back! To try this out we need to repeat the setup process so re-run the 1st and 2nd request in the Postman collection.

 

To permanently delete a node send a DELETE request to http://localhost:8080/alfresco/api/-default-/public/alfresco/versions/1/deleted-nodes/{{nodeId}} (6th request in the Postman collection), if the deletion was successful you will receive an empty response with a 204 status code.

 

List the deleted nodes (3rd request in the Postman collection) and you will see an empty list once again.

 

If you want to bypass the trashcan completely you can permanently delete a "live" node by using the permanent query parameter, see the API Explorer for details.

 

Hopefully that's given you a good overview of the trashcan API and what's possible, next time we're going to look at the only remaining API we haven't covered yet, the discovery API.

In the last post we looked at the queries and search APIs, today we're going to look at the people API. A couple of the endpoints have been available since 4.2 and we've added a few new ones in the 5.2 release.

 

To keep with tradition this post has a Postman collection you can use to follow along, click the "Run in Postman" button below to import it into your client.

 

 

Let's start by creating a new person using one of the new endpoints added in the 5.2 release. We can POST the body below to http://localhost:8080/alfresco/api/-default-/public/alfresco/versions/1/people (1st request in the Postman collection) to create a person with a username (id) of "jdoe". Any of the properties defined for the out-of-the-box cm:person type can be provided, for full details please refer to the API Explorer.

{
  "id": "jdoe",
  "firstName": "John",
  "lastName": "Doe",
  "email": "john.doe@example.com",
  "password": "jdoe",
  "skypeId": "johndoe_skype",
  "jobTitle": "Software Engineer"
}

 

Sending the request above results in the following response:

{
  "entry": {
    "firstName": "John",
    "lastName": "Doe",
    "skypeId": "johndoe_skype",
    "jobTitle": "Software Engineer",
    "emailNotificationsEnabled": true,
    "company": {},
    "id": "jdoe",
    "enabled": true,
    "email": "john.doe@example.com"
  }
}

 

Some customers extend the out-of-the-box cm:person object so we have added support for custom properties too, for example to create a person with a custom property called mycompany:employeeId the following body could be used (presuming the property has been defined in the content model):

{
  "id": "jdoe",
  "firstName": "Jane",
  "lastName": "Doe",
  "email": "jane.doe@example.com",
  "password": "jdoe",
  "skypeId": "janedoe_skype",
  "jobTitle": "Software Engineer",
  "properties": {
    "mycompany:employeeId": "abc-123"
  }
}

 

Another capability added in 5.2 is the ability to retrieve a list of people in the repository by using http://localhost:8080/alfresco/api/-default-/public/alfresco/versions/1/people (2nd request in the Postman collection) resulting in a response similar to the one below, which shows the person we just created.

{
  "list": {
    "pagination": {
      "count": 8,
      "hasMoreItems": false,
      "totalItems": 8,
      "skipCount": 0,
      "maxItems": 100
    },
    "entries": [
      {
        "entry": {
          "lastName": "Beecher",
          "userStatus": "Helping to design the look and feel of the new web site",
          "jobTitle": "Graphic Designer",
          "statusUpdatedAt": "2011-02-15T20:20:13.432+0000",
          "mobile": "0112211001100",
          "emailNotificationsEnabled": true,
          "description": "Alice is a demo user for the sample Alfresco Team site.",
          "telephone": "0112211001100",
          "enabled": false,
          "firstName": "Alice",
          "skypeId": "abeecher",
          "avatarId": "198500fc-1e99-4f5f-8926-248cea433366",
          "location": "Tilbury, UK",
          "company": {
            "organization": "Moresby, Garland and Wedge",
            "address1": "200 Butterwick Street",
            "address2": "Tilbury",
            "address3": "UK",
            "postcode": "ALF1 SAM1"
          },
          "id": "abeecher",
          "email": "abeecher@example.com"
        }
      },
      {
        "entry": {
          "firstName": "John",
          "lastName": "Doe",
          "skypeId": "johndoe_skype",
          "jobTitle": "Software Engineer",
          "emailNotificationsEnabled": true,
          "company": {},
          "id": "jdoe",
          "enabled": true,
          "email": "john.doe@example.com"
        }
      },
      ...
    ]
  }
}

 

You may recall way back in part 2 when we discussed the /nodes API, the properties and aspect names are not present by default but can be included via the include query parameter, the same holds true here as well. If you want to see any custom properties or aspects applied you can add include=properties,aspectNames to the URL. The results can also be sorted by id (username), firstName and lastName.

 

Unfortunately, we ran out of time in the 5.2 release to add filtering capabilities to this endpoint, however, as discussed in the last post, the /queries/people endpoint (3rd request in the Postman collection) or the /search endpoint can be used to look for people and achieve the same thing.

 

To retrieve the full details of the person we created earlier we can use http://localhost:8080/alfresco/api/-default-/public/alfresco/versions/1/people/jdoe which returns the following response (4th request in the Postman collection):

{
  "entry": {
    "firstName": "John",
    "lastName": "Doe",
    "skypeId": "johndoe_skype",
    "jobTitle": "Software Engineer",
    "emailNotificationsEnabled": true,
    "company": {},
    "id": "jdoe",
    "enabled": true,
    "email": "john.doe@example.com"
  }
}

 

It's also possible to update the details of a person (non administrator users can only update their own details for obvious reasons). The example below shows how we can update the details of the person we created earlier by PUTting the following body to http://localhost:8080/alfresco/api/-default-/public/alfresco/versions/1/people/jdoe (5th request in the Postman collection):

{
  "firstName": "Johnathon",
  "mobile": "07000 123456"
}

 

Using the same endpoint it's also possible to change a person's password. For security reasons you can only change your own password (unless you're an administrator) and when you do you have to provide the old password together with the new password. The example below (6th request in the Postman collection) changes the password of the person we created earlier to "my-new-password":

{
  "oldPassword": "jdoe",
  "password": "my-new-password"
}

 

The last capability of the people API we're going to cover today is the ability to disable and enable people, this is obviously something only administrators can do. You may have noticed the enabled flag for each person, this can be toggled to set the state of the person. It's also possible to create a person in the disabled state by setting the enabled property to false when the person is created.

 

The example below demonstrates how we can disable the person we created earlier. Again, we use PUT against http://localhost:8080/alfresco/api/-default-/public/alfresco/versions/1/people/jdoe to achieve this (7th request in the Postman collection):

{
  "enabled": false
}

 

Now try and make a request using "jdoe" (remember to update the credentials to use the newer password), you'll get a 401 error as disabled users are locked out of the repository.

 

At this time there is no way to remove a person from the system via the v1 REST API, there are several nuances that need further discussion, once these discussions have concluded you can expect to see this capability added to a future release.

 

That concludes our coverage of the people API, I hope you'll join me again next time when we look at the trashcan (deleted nodes) API.

In the last post we covered the Sites APIs, this time we're going to take a look at ways to find things in the repository. There are two main APIs to do this, /queries and /search.

 

As always there is a Postman collection to accompany this post, click the "Run in Postman" button below to import it into your client.

 

 

The first request uses one of the APIs we learnt about last time to create a public site named "queriesSearchSite", the second request retrieves the document library container id and stores it in a global variable. The third request can be used to upload documents to the site, to get the most out of this post upload a text file containing some lorem ipsum text, some image files and some Office documents.

 

The /queries endpoints are designed to be very simple to use and usable in "live search" scenarios i.e. they can executed upon each key press so clients can show results as the user types. The actual query used behind the scenes is hard-coded, if complex or custom queries are required the /search API should be used, which we'll look at shortly.

 

Let's first take a look at the endpoint to find nodes. The http://localhost:8080/alfresco/api/-default-/public/alfresco/versions/1/queries/nodes endpoint returns nodes (files and folders) that match a simple term provided via a query parameter. The type of nodes returned can be restricted via the nodeType query parameter, for example passing my:type as the value will only return nodes of that type and any of it's subtypes. The query will look in the name, title and description properties, in the content and in tags for a match. Take a look at the API Explorer for the other options available for this endpoint.

 

The 4th request in the Postman collection shows an example of looking for the term "lorem". The number of results you get will depend on the content in your repository, some of the sample site content contains the word "lorem" so you should get a few results! The response format (shown below) is also consistent with the /nodes API so if you've been following the series it should look familiar.

{
  "list": {
    "pagination": {
      "count": 7,
      "hasMoreItems": false,
      "totalItems": 7,
      "skipCount": 0,
      "maxItems": 100
    },
    "entries": [
      {
        "entry": {
          "createdAt": "2017-04-10T09:12:32.761+0000",
          "isFolder": false,
          "isFile": true,
          "createdByUser": {
            "id": "test",
            "displayName": "Test User"
          },
          "modifiedAt": "2017-04-10T09:12:32.761+0000",
          "modifiedByUser": {
            "id": "test",
            "displayName": "Test User"
          },
          "name": "test-lorem-ipsum.txt",
          "id": "3379e95a-fa24-418e-a1df-7d7ef9192516",
          "nodeType": "cm:content",
          "content": {
            "mimeType": "text/plain",
            "mimeTypeName": "Plain Text",
            "sizeInBytes": 3186,
            "encoding": "ISO-8859-1"
          },
          "parentId": "d32682f0-cfd9-43da-ab74-ba78fc59a01a"
        }
      },
      ...
    ]
  }
}

 

To find sites the http://localhost:8080/alfresco/api/-default-/public/alfresco/versions/1/queries/sites endpoint can be used. The 5th request in the Postman collection shows how to look for sites that have the term "queries" in the site id, title or description. Again, take a look at the API Explorer for other options, including how to order the results.

{
  "list": {
    "pagination": {
      "count": 1,
      "hasMoreItems": false,
      "totalItems": 1,
      "skipCount": 0,
      "maxItems": 100
    },
    "entries": [
      {
        "entry": {
          "role": "SiteManager",
          "visibility": "PUBLIC",
          "guid": "763588b4-9c6f-4b34-af41-c92a6102711f",
          "description": "Site created for queries and search blog post",
          "id": "queriesSearchSite",
          "preset": "site-dashboard",
          "title": "Queries and Search Site"
        }
      }
    ]
  }
}

 

Finally, to find people (users) the http://localhost:8080/alfresco/api/-default-/public/alfresco/versions/1/queries/people endpoint can be used. The 6th request in the Postman collection shows how to look for people that have "jackson" in their username (id), first name or last name. As my repository has the sample site loaded the sample user "Mike Jackson" is returned:

{
  "list": {
    "pagination": {
      "count": 1,
      "hasMoreItems": false,
      "totalItems": 1,
      "skipCount": 0,
      "maxItems": 100
    },
    "entries": [
      {
        "entry": {
          "lastName": "Jackson",
          "userStatus": "Working on a new web design for the corporate site",
          "jobTitle": "Web Site Manager",
          "statusUpdatedAt": "2011-02-15T20:13:09.649+0000",
          "mobile": "012211331100",
          "emailNotificationsEnabled": true,
          "description": "Mike is a demo user for the sample Alfresco Team site.",
          "telephone": "012211331100",
          "enabled": false,
          "firstName": "Mike",
          "skypeId": "mjackson",
          "avatarId": "3fbde500-298b-4e80-ae50-e65a5cbc2c4d",
          "location": "Threepwood, UK",
          "company": {
            "organization": "Green Energy",
            "address1": "100 Cavendish Street",
            "address2": "Threepwood",
            "address3": "UK",
            "postcode": "ALF1 SAM1"
          },
          "id": "mjackson",
          "email": "mjackson@example.com"
        }
      }
    ]
  }
}

 

As with the other queries endpoints the options are intentionally limited, see the API Explorer for details. We will also be looking at the people API in a lot more depth in the next instalment of this series so stay tuned!

 

As mentioned earlier, if the pre-canned queries do not provide what you need you have the option to use the rich and powerful /search API, at the cost of a little more complexity.

 

Due to the number of options and functionality available via the search API, it is a little different than most of the other APIs we've looked at so far in the series. Firstly, the API is defined under the "search" namespace so it's base URL is slightly different. Secondly, the /search endpoint does not accept any query parameters and is therefore completely controlled via the POST body as we'll see in the examples that follow.

 

We'll start by executing a simple search, the 7th request in the Postman collection shows how POSTing the following body to http://localhost:8080/alfresco/api/-default-/public/search/versions/1/search searches for the term "lorem":

{
  "query": {
    "query": "lorem"
  }
}

 

The results should look familiar though, for the most part they are the same as the results from /queries and from /nodes/{id}/children

{
  "list": {
    "pagination": {
      "count": 7,
      "hasMoreItems": false,
      "totalItems": 7,
      "skipCount": 0,
      "maxItems": 100
    },
    "entries": [
      {
        "entry": {
          "isFile": true,
          "createdByUser": {
            "id": "mjackson",
            "displayName": "Mike Jackson"
          },
          "modifiedAt": "2011-03-03T10:31:31.651+0000",
          "nodeType": "cm:content",
          "content": {
            "mimeType": "application/vnd.ms-powerpoint",
            "mimeTypeName": "Microsoft PowerPoint",
            "sizeInBytes": 2898432,
            "encoding": "UTF-8"
          },
          "parentId": "38745585-816a-403f-8005-0a55c0aec813",
          "createdAt": "2011-03-03T10:31:30.596+0000",
          "isFolder": false,
          "search": {
            "score": 1.6137421
          },
          "modifiedByUser": {
            "id": "mjackson",
            "displayName": "Mike Jackson"
          },
          "name": "Project Overview.ppt",
          "location": "nodes",
          "id": "99cb2789-f67e-41ff-bea9-505c138a6b23"
        }
      },
      ...
    ]
  }
}

 

There are a couple of differences though, the search API returns two additional properties, search and location. The search property (line 29) adds extra context for the individual result, in this case, the score.

 

Explaining the full details is beyond the scope of this post but it is possible to search across "live" nodes, deleted nodes and versioned nodes, the location property (line 37) shows from which area the result came from. By default only "live" nodes are included.

 

The example above used the default search language afts (Alfresco Full Text Search), however, cmis and lucene are also supported. The example body below shows how to execute a simple CMIS query (8th request in the Postman collection) to find all content with a name starting with "test.":

{
  "query": {
    "query": "select * from cmis:document WHERE cmis:name LIKE 'test.%'",
    "language": "cmis"
  }
}

 

For completeness, the example body below shows how to execute a simple Lucene query (9th request in the Postman collection) to find all the content modified in the last week:

{
  "query": {
    "query": "+@cm\\:modified:[NOW/DAY-7DAYS TO NOW/DAY+1DAY] +TYPE:\"cm:content\"",
    "language": "lucene"
  }
}

 

As with all the v1 REST APIs paging can also be controlled, it's just done via the body rather than a query parameter. The results can also be sorted. The example body below shows how to execute a search (10th request in the Postman collection) to find all content ordered by the name property, only show 25 results rather than the default of 100 and skip the first 10 results:

{
  "query": {
    "query": "+TYPE:\"cm:content\"",
    "language": "afts"
  },
  "paging": {
    "maxItems": "25",
    "skipCount": "10"
  },
  "sort": [{"type":"FIELD", "field":"cm:name", "ascending":"false"}]
}

 

Now we've covered the basics let's look at a couple of the more interesting features of the search API, faceting and term highlighting.

 

There are two types of facets; queries and fields. A query facet returns the count of results for the given query, you can provide multiple facet queries in one request. A field facet returns a number of "buckets" for a field, providing the count of results that fit into each bucket.

 

It's much easier to understand with an example, the body below shows a search request (11th request in the Postman collection) that will look for content nodes that have a name or title starting with "test". We also specify that we want to know how many of the results are small files, how many are plain text files, how many are images and how many are Office files. Additionally, we are also asking for the creator facet field to be included, which will indicate how many of the results were created by each user:

{
  "query": {
    "query": "(name:\"test*\" OR title:\"test*\") AND TYPE:\"cm:content\""
  },
  "facetQueries": [
    {"query": "content.size:[0 TO 10240]", "label": "Small Files"},
    {"query": "content.mimetype:'text/plain'", "label": "Plain Text"},
    {"query": "content.mimetype:'image/jpeg' OR content.mimetype:'image/png' OR content.mimetype:'image/gif'", "label": "Images"},
    {"query": "content.mimetype:'application/msword' OR content.mimetype:'application/vnd.ms-excel'", "label": "Office"}
  ],
  "facetFields": {"facets": [{"field": "creator"}]}
}

 

The response to this request is shown below:

{
  "list": {
    "pagination": {
      "count": 8,
      "hasMoreItems": false,
      "totalItems": 8,
      "skipCount": 0,
      "maxItems": 100
    },
    "context": {
      "facetQueries": [
        {
          "label": "Office",
          "count": 2
        },
        {
          "label": "Small Files",
          "count": 4
        },
        {
          "label": "Plain Text",
          "count": 1
        },
        {
          "label": "Images",
          "count": 3
        }
      ],
      "facetsFields": [
        {
          "label": "creator",
          "buckets": [
            {
              "label": "test",
              "count": 6,
              "display": "Test User"
            },
            {
              "label": "System",
              "count": 2,
              "display": "System"
            }
          ]
        }
      ]
    },
    "entries": [
      {
        "entry": {
          "isFile": true,
          "createdByUser": {
            "id": "test",
            "displayName": "Test User"
          },
          "modifiedAt": "2017-04-10T09:21:44.499+0000",
          "nodeType": "cm:content",
          "content": {
            "mimeType": "image/gif",
            "mimeTypeName": "GIF Image",
            "sizeInBytes": 3039,
            "encoding": "UTF-8"
          },
          "parentId": "d32682f0-cfd9-43da-ab74-ba78fc59a01a",
          "createdAt": "2017-04-10T09:20:41.665+0000",
          "isFolder": false,
          "search": {
            "score": 2.0050006
          },
          "modifiedByUser": {
            "id": "test",
            "displayName": "Test User"
          },
          "name": "test.gif",
          "location": "nodes",
          "id": "4ba71ad8-8812-4c1a-9d0b-30643dc39c51"
        }
      },
      ...
    ]
  }
}

 

As well as the expected list of nodes, the response also contains a facetQueries and a facetsFields object containing the counts we requested. The facetQueries object has an entry for each query supplied in the result whereas the facetsFields object contains an entry for each requested field which in turn contains the count for each bucket.

 

The last example we're going to look at in this post is term highlighting. The example body below shows a search request (12th request in the Postman collection) that will look for content nodes that have a name or title starting with "test", if the match occurs in either the cm:name or cm:title property the location of the match will be returned in the results. By default, the matched term is highlighted by surrounded by an em tag, to surround the match with something else the prefix and postfix properties can be used as shown in the example below:

{
  "query": {
    "query": "(name:\"test*\" OR title:\"test*\") AND TYPE:\"cm:content\""
  },
  "highlight": {
    "fields": [
      {
        "field": "cm:name",
        "prefix": "(",
        "postfix": ")"
      },
      {
        "field": "{http://www.alfresco.org/model/content/1.0}title"
      }
    ]
  }
}

 

As the highlighting is specific to each individual result the search object we saw earlier is used to return the result as shown below:

{
  "list": {
    "pagination": {
      "count": 8,
      "hasMoreItems": false,
      "totalItems": 8,
      "skipCount": 0,
      "maxItems": 100
    },
    "entries": [
      {
        "entry": {
          "isFile": true,
          "createdByUser": {
            "id": "System",
            "displayName": "System"
          },
          "modifiedAt": "2017-02-20T10:57:28.407+0000",
          "nodeType": "cm:content",
          "content": {
            "mimeType": "application/x-javascript",
            "mimeTypeName": "JavaScript",
            "sizeInBytes": 2271,
            "encoding": "UTF-8"
          },
          "parentId": "a4e9e481-89b5-43da-9389-21314dbb6046",
          "createdAt": "2017-02-20T10:57:28.407+0000",
          "isFolder": false,
          "search": {
            "score": 1.1892484,
            "highlight": [
              {
                "field": "cm:name",
                "snippets": [
                  "example (test) script.js.sample"
                ]
              },
              {
                "field": "{http://www.alfresco.org/model/content/1.0}title",
                "snippets": [
                  "Example <em>Test</em> Script"
                ]
              }
            ]
          },
          "modifiedByUser": {
            "id": "System",
            "displayName": "System"
          },
          "name": "example test script.js.sample",
          "location": "nodes",
          "id": "7e02b810-4bce-4ed6-aff0-3f2f88a5ff82"
        }
      },
      ...
    ]
  }
}

 

As we specified in the request, the match in the name property is surrounded by brackets (line 35) and the em tag surrounds the match in the title property (line 41).

 

We've only just scratched the surface of the capabilities of the search API in this post so I would highly recommend you take a look at the API Explorer and select "Search API" from the drop-down menu to get more details of what's possible.

 

If you're using Community via the installer as instructed in the previous post you will have been using SOLR 4. You may have heard that we also released support for SOLR 6 with 5.2. To learn more please read the SOLR 6 blog posts on this site or visit our documentation site.

 

Next time we're going to take a look at the people API.

Filter Blog

By date: By tag: