A handy Rest client for AgileApps

Ever find yourself in a pickle with AgileApps? of course you have… let me be specific -
Ever find a need to make REST calls to your AgileApps (AAL from now on) tenant to perform various operations? This article is for you. The current version of the rest client specifically that addresses multipart attachments is pretty outdated in the wiki. link for the lazy - Attachments

The packages mentioned in the java class example is outdated and no longer available. However, that does guide one to understand how the request ought to be fashioned.

Therefore, to help you fellow developers, the below class and pom.xml will help.

First of all, you need to have some really basic understanding of maven and pom.xml. My advice is to create a blank archetype of a maven project and simply copy the dependencies from the below:

<?xml version="1.0" encoding="UTF-8"?>

<project xmlns="http://maven.apache.org/POM/4.0.0"
	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
	<modelVersion>4.0.0</modelVersion>

	<groupId>com.sag</groupId>
	<artifactId>RestClientV2</artifactId>
	<version>0.0.1-SNAPSHOT</version>

	<name>RestClientV2</name>
	<!-- FIXME change it to the project's website -->
	<url>http://www.example.com</url>

	<properties>
		<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
		<maven.compiler.source>1.8</maven.compiler.source>
		<maven.compiler.target>1.8</maven.compiler.target>
	</properties>

	<dependencies>
		<dependency>
			<groupId>junit</groupId>
			<artifactId>junit</artifactId>
			<version>4.11</version>
			<scope>test</scope>
		</dependency>
		<!-- https://mvnrepository.com/artifact/org.apache.httpcomponents/httpclient -->
		<dependency>
			<groupId>org.apache.httpcomponents</groupId>
			<artifactId>httpclient</artifactId>
			<version>4.5.13</version>
		</dependency>
		<!-- https://mvnrepository.com/artifact/com.google.code.gson/gson -->
		<dependency>
			<groupId>com.google.code.gson</groupId>
			<artifactId>gson</artifactId>
			<version>2.8.6</version>
		</dependency>
		<dependency>
			<groupId>org.apache.httpcomponents</groupId>
			<artifactId>httpmime</artifactId>
			<version>4.5.13</version>
		</dependency>
	</dependencies>

	<build>
		<pluginManagement><!-- lock down plugins versions to avoid using Maven 
				defaults (may be moved to parent pom) -->
			<plugins>
				<!-- clean lifecycle, see https://maven.apache.org/ref/current/maven-core/lifecycles.html#clean_Lifecycle -->
				<plugin>
					<artifactId>maven-clean-plugin</artifactId>
					<version>3.1.0</version>
				</plugin>
				<!-- default lifecycle, jar packaging: see https://maven.apache.org/ref/current/maven-core/default-bindings.html#Plugin_bindings_for_jar_packaging -->
				<plugin>
					<artifactId>maven-resources-plugin</artifactId>
					<version>3.0.2</version>
				</plugin>
				<plugin>
					<artifactId>maven-compiler-plugin</artifactId>
					<version>3.8.0</version>
				</plugin>
				<plugin>
					<artifactId>maven-surefire-plugin</artifactId>
					<version>2.22.1</version>
				</plugin>
				<plugin>
					<artifactId>maven-jar-plugin</artifactId>
					<version>3.0.2</version>
				</plugin>
				<plugin>
					<artifactId>maven-install-plugin</artifactId>
					<version>2.5.2</version>
				</plugin>
				<plugin>
					<artifactId>maven-deploy-plugin</artifactId>
					<version>2.8.2</version>
				</plugin>
				<!-- site lifecycle, see https://maven.apache.org/ref/current/maven-core/lifecycles.html#site_Lifecycle -->
				<plugin>
					<artifactId>maven-site-plugin</artifactId>
					<version>3.7.1</version>
				</plugin>
				<plugin>
					<artifactId>maven-project-info-reports-plugin</artifactId>
					<version>3.0.0</version>
				</plugin>
			</plugins>
		</pluginManagement>
        <plugins>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-compiler-plugin</artifactId>
                <configuration>
                    <source>9</source>
                    <target>9</target>
                </configuration>
            </plugin>
        </plugins>
    </build>
</project>

org.apache.httpcomponents is the dependency that will help us through this.

For the java execution, create a Main class or reuse the code below, modify this according to your requirement and tenant. Please read through the code and fill in the gaps.

package com.sag.rest;

import java.io.File;
import java.util.List;
import java.util.stream.Collectors;

import org.apache.http.HttpEntity;
import org.apache.http.client.CookieStore;
import org.apache.http.client.HttpClient;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.cookie.Cookie;
import org.apache.http.entity.ContentType;
import org.apache.http.entity.StringEntity;
import org.apache.http.entity.mime.MultipartEntityBuilder;
import org.apache.http.entity.mime.content.FileBody;
import org.apache.http.entity.mime.content.StringBody;
import org.apache.http.impl.client.BasicCookieStore;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClientBuilder;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.message.BasicHeader;
import org.apache.http.util.EntityUtils;

/**
 * @author sugu
 */
public class RestMan {

	public static void main(String[] args) throws Exception {
		CloseableHttpClient httpclient = HttpClients.createDefault();
		try {
			// The underlying HTTP connection is still held by the response object
			// to allow the response content to be streamed directly from the network
			// socket.
			// In order to ensure correct deallocation of system resources
			// the user MUST call CloseableHttpResponse#close() from a finally clause.
			// Please note that if response content is not fully consumed the underlying
			// connection cannot be safely re-used and will be shut down and discarded
			// by the connection manager.

			HttpClient http = null;
			CookieStore httpCookieStore = new BasicCookieStore();
			//optional comment line if this does not apply to you
			HttpClientBuilder builder = HttpClientBuilder.create().setDefaultCookieStore(httpCookieStore);
			http = builder.build();

			final String uri = "https://<<TENANT>>/networking/rest/login";
			HttpPost httpPost = new HttpPost(uri);
			httpPost.setHeader(new BasicHeader("Content-Type", "application/json"));

			httpPost.setEntity(new StringEntity("{\r\n" + "    \"platform\": {\r\n" + "        \"login\": {\r\n"
					+ "            \"userName\": \"NAMEY@NAMEYNAMERSON.com\",\r\n"
					+ "            \"password\": \"FANCYPASSWORD\"\r\n" + "        }\r\n" + "    }\r\n" + "}"));
			CloseableHttpResponse response2 = (CloseableHttpResponse) http.execute(httpPost);
			
			// httpCookieStore.getCookies().stream().forEach(System.out::println);
			List<Cookie> l = httpCookieStore.getCookies().stream()
					.filter(x -> x.getName().contains("JSESSION") || x.getName().contains("XSRF-TOKEN"))
					.collect(Collectors.toList());
			System.out.println(l);
			
			CloseableHttpResponse response3 = null;

			try {
				System.out.println(response2.getStatusLine());
				HttpEntity entity2 = response2.getEntity();
				// do something useful with the response body
				// and ensure it is fully consumed
				EntityUtils.consume(entity2);

				// start file upload
				System.out.println("Trying to upload file now to 1515201630");
				// set cookie
				httpCookieStore.addCookie(l.get(0));
				builder = HttpClientBuilder.create().setDefaultCookieStore(httpCookieStore);
				http = builder.build();

				httpPost = new HttpPost("https://<<TENANT>>/networking/rest/record/attachments");

				//Change the file path below
				FileBody bin = new FileBody(new File("C:\\keval\\2134\\sample.csv"),
						ContentType.APPLICATION_OCTET_STREAM);
				StringBody xmldata = new StringBody(
						"<platform><record><title>TextTitle</title><file_field>sample.csv</file_field><related_to type=\"cecc9b79e282401fa2a78c21044c7e7e\">1838694755</related_to></record></platform>",
						ContentType.APPLICATION_XML);

				HttpEntity reqEntity = MultipartEntityBuilder.create().addPart("file_field", bin)
						.addPart("__xml_data__", xmldata).build();

				httpPost.setEntity(reqEntity);
				httpPost.addHeader(new BasicHeader("Accept", "application/xml"));
				String xsrf = l.get(1).getValue();
				httpPost.addHeader("X-XSRF-TOKEN", xsrf);

				response3 = (CloseableHttpResponse) http.execute(httpPost);
				HttpEntity entity3 = response3.getEntity();
				System.out.println(response3.getStatusLine());
				String output = new String(response3.getEntity().getContent().readAllBytes());
				System.out.println(output);
				EntityUtils.consume(entity3);

			} finally {
				response2.close();
				response3.close();
			}
		} finally {
			httpclient.close();
		}
	}
}

Thats it!

if you are still here that means you need more -
The code is built on a basic REST client and performs some AAL specific calls. This can be changed and reused to fit other project needs or other AAL related operations. All code in the first try block is to first login to the tenant with a valid username and password. The nested try block is where the attachment related or multipart request is created. details of this is described in - Attachments

Cheers!