Lab2 Forge API Intro

(This is a continuation from the previous post: Lab1 “Hello Forge World”)

In Lab1, we learned how to obtain an access token and got familiarized ourselves with the basics of REST call. Let’s move on, and create our own storage, upload a model and make it ready for viewing.

To achieve this, we use the following services:

where oss stands for Object Storage Service.

Work Flow

The work flow of our little app is as follows:

  1. Authenticate – we first obtain an access_token. We learned how to do this in the Lab1. access_token is needed to make for subsequent calls.
  2. Create a “bucket” – a bucket is a container where we upload and store a file.
  3. Upload a file – we upload a file to the bucket we created.
  4. Translate a file – we then translate or convert the file for viewing.

As an example, the application may look something like the following at the end of Lab2:

ForgeIntroLab2

and with a viewer launched in a browser:

ForgeIntroLab2FormViewer

Request and Response in the dialog were added for learning purpose.

1 Authenticate

We learned how to do this in the Lab1. access_token is needed to make for subsequent calls.

2 Create Bucket

Create a bucket or a container that holds a file.

2.1 Create Bucket – Request

URL: https://developer.api.autodesk.com/oss/v2/buckets

Methods: POST

Request Headers:

  • Content-Type = application/json
  • Authorization = “Bearer “ + {access_token}

Required Parameters (JSON body):

  • bucketKey
  • policyKey

Doc:
https://developer.autodesk.com/en/docs/data/v2/reference/http/buckets-POST/

Notice here we use a JSON body to pass parameters. access_token is the one we obtained through the earlier authentication request step.

bucketKey — the name of your “bucket” or container where you will upload your files. As naming rules, the name has to be globally unique, characters between 3 and 128, and lower case only.

policyKey — the life length of container, either:

  • “transient” (24 hours)
  • “temporary” (30 days)
  • “persistent” (till removed the owner).

In this lab, let’s use transient.

2.2 Create Bucket – Response

Here is the sample response for oss/v2/buckets:

{
“bucketKey”:”adskbucket_632216″,
“bucketOwner”:”fg6w6gVe8WedWNtlGq2E4zsSkmNAlu4z”,
“createDate”:1425525389481,
“permissions”:
[
{
“authId”:”fg6w6gVe8WedWNtlGq2E4zsSkmNAlu4z”,
“access”:”full”
}
],
“policyKey”:”transient”
}

2.3 Create Bucket – Sample Code

Here is the code sample to create a bucket:

CreateBucket

        public static bool CreateBucket(string accessToken, string bucketKey, string policyKey)
        {
            // (1) Build request
            var client = new RestClient();
            client.BaseUrl = new System.Uri(baseApiUrl);

            // Set resource/end point
            var request = new RestRequest();
            request.Resource = “oss/v2/buckets”;
            request.Method = Method.POST;

            // Request headers  
            request.AddHeader(“Authorization”, “Bearer “ + accessToken);
            request.AddHeader(“Content-Type”, “application/json”); 

            // Add JSON body.  
            request.AddJsonBody(new { bucketKey = bucketKey, policyKey = policyKey });

            // (2) Execute request and get response
            IRestResponse response = client.Execute(request);

            // Save response. This is to see the response for our learning.
            m_lastResponse = response;

            // (3) Parse the response. Return true if succeeds, false otherwise. 
            if (response.StatusCode == HttpStatusCode.OK)
            {
                return true; 
            }

            return false; 
        }

3 Upload File

Upload a file to a bucket.

3.1 Upload File – Request

URL: https://developer.api.autodesk.com/oss/v2/buckets/{bucketKey}/objects/{objectName}

Methods: PUT

Url Segment Parameter:

  • bucketKey (= bucket name)
  • objectName (= file name)

Request Headers:

  • Content-Type = “application/octet-stream”
  • Authorization = “Bearer “ + {access_token}

Required Parameters:

  • requestBody (= byte data)

Doc:

https://developer.autodesk.com/en/docs/data/v2/reference/http/buckets-:bucketKey-objects-:objectName-PUT/

In this call, we are using Url segment parameters; i.e., parameters in { } in the url will be replaced by the value. bucketKey is the name of the bucket that we defined in the previous step.  objectName is the name of the file that we are uploading. Content type is “application/octet-stream”. requestBody is the actual data in byte array.

3.2 Upload File – Response

Here is the example of file upload response:

{
“bucketKey” : “forgeintro”,
“objectId” : “urn:adsk.objects:os.object:forgeintro/sample_project.rvt”,
“objectKey” : “sample_project.rvt”,
“sha1” : “ccac2fa09b9d47f3dgn67jgf890f8157e6b10cfe”,
“size” : 13086720,
“contentType” : “application/octet-stream”,
“location” : “https://developer.api.autodesk.com/oss/v2/buckets/forgeintro/objects/sample_project.rvt”
}

What we are interested in is the value of objectId (highlighted in orange above).

3.3 Upload File – Sample Code

Here is the code sample to upload a file:

Upload

        public static string Upload(string accessToken, string bucketKey, string objectName, byte[] fileData)
        {
            // (1) Build request
            var client = new RestClient();
            client.BaseUrl = new System.Uri(baseApiUrl);

            // Set resource/end point
            var request = new RestRequest();
            request.Resource = “oss/v2/buckets/{bucketKey}/objects/{objectName}”;
            request.Method = Method.PUT;
            
            // Set UrlSegment
            request.AddUrlSegment(“bucketKey”, bucketKey);
            request.AddUrlSegment(“objectName”, objectName);

            // Add headers  
            request.AddHeader(“Authorization”, “Bearer “ + accessToken);
            request.AddHeader(“Content-Type”, “application/octet-stream”);

            // Add request body, byte array
            request.AddParameter(“requestBody”, fileData, ParameterType.RequestBody);

            // (2) Execute request and get response
            IRestResponse response = client.Execute(request);

            // Save response. This is to see the response for our learning.
            m_lastResponse = response;

            // (3) Parse the response. Get objectId  
            string objectId = “”;
            if (response.StatusCode == HttpStatusCode.OK)
            {
                JsonDeserializer deserial = new JsonDeserializer();
                OssBucketsObjectsObjectNameResponse bucketsObjectsResponse =
                    deserial.Deserialize<OssBucketsObjectsObjectNameResponse>(response);
                objectId = bucketsObjectsResponse.objectsId;
            }

            return objectId; // “urn:xxx”
        }

where OssBucketObjectsObjectNameResponse is defined as follows:

OssBucketsObjectsObjectNameResponse

    [Serializable]
    publicclassOssBucketsObjectsObjectNameResponse
    {
        public string bucketKey { get; set; }
        public string objectId { get; set; }
        public string objectKey { get; set; }
        public string sha1 { get; set; }
        public string size { get; set; }
        public string contentType { get; set; }
        public string location { get; set; }
    }

4 Translate File

Translate a file for viewing. There are different file types that you can translate to. For simplicity, we assume we want to translate a single Revit file for viewing in this lab.

4.1 Translate File – Request

URL:

https://developer.api.autodesk.com/modelderivative/v2/designdata/job

Methods: POST

Request Headers:

  • Content-Type = “application/json”
  • Authorization = “Bearer “ + {access_token}

Required Parameters (JSON body):

  • urn *1

*1) This is the main info we need. Additional information (such as 2D or 3D view and a target file type)  are needed to compose a JSON body. We’ll come back to this shortly.

Doc:

https://developer.autodesk.com/en/docs/model-derivative/v2/reference/http/job-POST/

Converting ObjectId to Base64 encoded URN

urn above is the Base64 encoded form of objectId that we obtained after we uploaded a file. Base64 is used ensure the data is intact without modification during the transport. This part is not a specific Forge function. The code below is an example adapted from the code on community forum.

ObjectIdToUrn64() – sample code to convert ObjectId to Base64 encoded urn
  1.         static readonly char[] padding = { ‘=’ };
  2.         public static string ObjectIdToUrn64(string objectId)
  3.         {
  4.             // padded version
  5.             byte[] bytes = Encoding.UTF8.GetBytes(objectId);
  6.             string urn64 = Convert.ToBase64String(bytes);
  7.             // unpadded version
  8.             urn64 = urn64.TrimEnd(padding).Replace(‘+’, ‘-‘).Replace(‘/’, ‘_’);
  9.             return urn64;
  10.         }

4.2 Translate File – Response

Here is the successful response from a translation job:

{
“result”: “created”,
“urn”: “dXJuOmFm9yZ2Vpbn3NhbXBsZV9wcm9qZWN0LnJ2dA…”,
“registerKeys”: [“5e8abec1-7880-48d8-bdcd-6e082bff680b”],
“acceptedJobs”: { “output”: { “formats”: [{ “type”: “svf”, “views”: [“2d”, “3d”] }] } }
}

We need to keep the urn (Base64) to access the file for viewing.

Note: the translation job call is asynchronous. It initiates a job that runs in the background, rather than keeping your program to wait. You need to check whether the job is complete by calling the GET :urn/manifest endpoint, even when the POST job endpoint returns a success response.

4.3 Translate File – Sample Code

Here is the code sample to translate a file:

Translate

        public static bool Translate(string accessToken, string urn64)
        {

            // (1) Build request
            var client = new RestClient();
            client.BaseUrl = new System.Uri(baseApiUrl);

            // Set resource/end point
            var request = new RestRequest();
            request.Resource = “modelderivative/v2/designdata/job”;
            request.Method = Method.POST;

            // Add headers
            request.AddHeader(“Authorization”, “Bearer “ + accessToken);
            request.AddHeader(“Content-Type”, “application/json”);

            // Construct a request body.
            var views = new List<string>() { “2d”, “3d” };
            JobBody body = new JobBody(urn64, “svf”, views);
            JsonSerializer
serializer = new JsonSerializer(); 

            string jsonbody = serializer.Serialize(body); 

            // Add request parameter in JSON format  
            request.AddParameter(“application/json”, jsonbody, ParameterType.RequestBody);

            // (2) Execute request and get response
            IRestResponse response = client.Execute(request);

            // Save response. This is to see the response for our learning.
            m_lastResponse = response;

            // (3) Parse the response.
            // The job will start. You will need to check the status
            // using GET :urn manifest after this.

            if (response.StatusCode == HttpStatusCode.OK)
            {
                return true;
            }

            return false;
        }

where JobBody is a helper class to construct a JSON body of a form below:

{
    "input": {
      "urn": "dXJuOmFkc2sub2JqZWN0czpvcy5vYmplY3Q6bW9kZWL0E1LnppcA..."
    },
    "output": {
      "formats": [
        {
          "type": "svf",
          "views": [ "2d", "3d" ]
        }
      ]
    }
}

This says: “translate a file with the given urn for a view-able format in 2D and 3D.”  svf is extension for view-able format.

Below is a helper class that defines JobBody:

Helper Class JobBody
  1.     public class JobBody
  2.     {
  3.         // default constructor
  4.         public JobBody(string urn, string type, List<string> views)
  5.         {
  6.             input = new Input() { urn = urn };
  7.             output = new Output()
  8.             {
  9.                 formats = new List<Format>()
  10.                 {
  11.                     new Format()
  12.                     {
  13.                         type = type,
  14.                         views = views
  15.                     }
  16.                 }
  17.             };
  18.         }
  19.         public Input input { get; set; }
  20.         public Output output { get; set; }
  21.         public class Input
  22.         {
  23.             public string urn { get; set; }
  24.         }
  25.         public class Output
  26.         {
  27.             public List<Format> formats { get; set; }
  28.         }
  29.         public class Format
  30.         {
  31.             public string type { get; set; }
  32.             public List<string> views { get; set; }
  33.         }
  34.     }

Note: As you can see, building a complex body structure can be tedious with C#. You are free to choose a different approach that fits your need and coding style. I used a tool called json2csharp to generate the skeleton of above class in C# from a sample JSON body string. There is also SDK that wraps around the REST calls. Once you have gone through the basics of Forge, you may want to take a look. For now, I’m moving on with the minimum dependency.  

5 Putting Together

Add UI’s to call CreateBucket(), Upload() and Translate(). For example,

buttonBucketCreate_Click, buttonUpload_Click, buttonTranslate_Click

        privatevoid buttonBucketCreate_Click(object sender, EventArgs e)
        {
            string bucketKey = textBoxBucketKey.Text;
            string policyKey = “transient”; // transient(24h)/temporary(30days)/persistent

            // Here is the main part that we call Forge API bucket creation.
            // return value key is the bucket name.  
            bool result = Forge.CreateBucket(m_accessToken, bucketKey, policyKey);
        }

        privatevoid buttonUpload_Click(object sender, EventArgs e)
        {
            string bucketKey = textBoxBucketKey.Text;
            string path = textBoxFile.Text;
            string objectName = m_fileName;
            byte[] fileData = File.ReadAllBytes(path);

            // Here is the main part that we call upload  
            string objectId = Forge.Upload(m_accessToken, bucketKey, objectName, fileData);

            textBoxObjectId.Text = objectId; // “urn:xxx”
        }

        privatevoid buttonTranslate_Click(object sender, EventArgs e)
        {
            // Here is the main part that we call Register  
            bool result = Forge.Translate(m_accessToken, urn64);
              
        }

 

In the sample project, I have also added a button (“To urn64”) to convert objectId to Base64-encoded urn.

When everything goes successfully, keep the access_token and urn. We’ll need them for viewing.

Viewer (Optional)

The viewer component is not supported in Win Forms. We will need to wait till later labs to embed a viewer in your app’s UI. In a mean time, for testing purpose, I included a “canned” html (ViewerTemplate.html) and javascript code (viewerBasics.js) in the accompanying Lab2 project. buttonView_Click() event simply takes the html file, replaces the token and urn values with the given values, and open it in your default browser. Once the translation is done, try running “View” button to display your translated model in a browser.

Note: viewerBasic.js is based on Basic Viewer which does not support displaying .pdf file as it is. If you want to display .pdf, you can use Basic Application instead.

Additional Functions (Optional)

You may have noticed that I have added three more buttons in the image of main UI above:

These are not required to translate a file for viewing. You can use them to check the status. The third one is particularly useful as the translation job takes sometime.

You can get the Lab2 source code from here: Lab2

Next: Lab3 “Forge API Web Intro”

Mikako

Advertisements

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Google+ photo

You are commenting using your Google+ account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s