Step-by-Step Guide: Consuming CubeMaster API with Azure

Learn how to integrate the CubeMaster API (https://api.cubemaster.net/loads) using Azure with this detailed guide.

To use the CubeMaster API, you need an API key (TokenID) for authentication. Here's how to get started:

  1. Visit the CubeMaster website: https://cubemaster.net.
  2. Locate the "Sign In" option (typically found in the top-right corner).
  3. Fill out the registration form with your details (e.g., name, email, password, company information).
  4. After signing up, log in to your account dashboard.
  5. Navigate to the "Settings" - "Integration" section to generate your API key (TokenID).
  6. Generate an API key. Once generated, you’ll receive a unique TokenID (e.g., abc123xyz789). Copy this key and store it securely, as it will be used in the HTTP headers of your API requests.
  7. Copy the TokenID and store it securely (e.g., in a configuration file or environment variable in your C# project).

Note: The TokenID will be used in the HTTP headers of your POST request for authentication.

A RESTful API (Representational State Transfer) is a way for programs to talk to each other over the internet using standard HTTP methods, like POST, GET, PUT, and DELETE. Here’s a simple breakdown:

  • Endpoint: A specific URL (e.g., https://api.cubemaster.net/loads) where the API listens for requests.
  • HTTP Methods: Actions you can perform:
    • POST: Send data to create something (e.g., building a load in CubeMaster).
    • GET: Retrieve data.
  • Headers: Extra info sent with the request, like the TokenID for authentication.
  • Body: The data you send (e.g., JSON with load details).
  • Response: What the API sends back (e.g., success message or error).

In this guide, we’ll use the POST method to send load data to the CubeMaster API and get a response with the calculated load plan.

We’ll use Azure Functions to call the CubeMaster API. Here’s how to set it up:

  1. Log in to the Azure Portal.
  2. Click the + Create a resource button (top-left corner).
  3. Search for Function App in the search bar, select it, and click Create.
  4. In the creation form:
    • Subscription: Choose your Azure subscription.
    • Resource Group: Create a new one (e.g., CubeMasterIntegration) or use an existing one.
    • Function App Name: Enter a unique name (e.g., CubeMasterFunction).
    • Runtime Stack: Select .NET or Node.js (we’ll use .NET in this guide).
    • Region: Choose a region close to you.
  5. Click Review + Create, then Create. Wait for deployment (takes a few minutes).
  6. Once deployed, go to your Function App in the Azure Portal by searching its name in the top search bar.

Assume your customer’s legacy database stores orders or shipments data (e.g., in SQL Server). We’ll fetch this data to build the API request.

  1. In the Azure Portal, go to your Function App.
  2. Click Functions in the left menu, then + Create to add a new function.
  3. Choose HTTP trigger template, name it (e.g., BuildLoad), and set Authorization level to Function. Click Create.
  4. Connect to your legacy database:
    • Go to Configuration in the left menu.
    • Under Application settings, click + New application setting.
    • Add a connection string (e.g., LegacyDBConnection) with the value: Server=your-server;Database=your-db;User Id=your-user;Password=your-password;.
    • Save the changes.
  5. In the function code editor, fetch data using C# (example):
    
    using System.Data.SqlClient;
    string connString = Environment.GetEnvironmentVariable("LegacyDBConnection");
    using (SqlConnection conn = new SqlConnection(connString)) {
        conn.Open();
        SqlCommand cmd = new SqlCommand("SELECT ItemName, Length, Width, Height, Weight, Quantity FROM Orders", conn);
        SqlDataReader reader = cmd.ExecuteReader();
        // Process data into JSON structure (see Step 5)
    }
                                    
  6. Map fields like ItemName to Name, Quantity to Qty, etc., to match the CubeMaster API request format.

Now, construct the POST request to https://api.cubemaster.net/loads using the data from the legacy database and the provided JSON structure.

  1. In your Azure Function (BuildLoad), add the following C# code to send the request:
    
    using System.Net.Http;
    using System.Text;
    
    [FunctionName("BuildLoad")]
    public static async Task Run(
        [HttpTrigger(AuthorizationLevel.Function, "post", Route = null)] HttpRequest req,
        ILogger log)
    {
        var client = new HttpClient();
        client.BaseAddress = new Uri("https://api.cubemaster.net/");
        client.DefaultRequestHeaders.Add("TokenID", Environment.GetEnvironmentVariable("CubeMasterTokenID"));
    
        string jsonRequest = @"{
            ""Title"": ""New Mixed Truck Load"",
            ""Description"": ""Hello Web API"",
            ""Cargoes"": [
                {
                    ""Name"": ""ITEM001"",
                    ""Length"": 72,
                    ""Width"": 30,
                    ""Height"": 75,
                    ""Weight"": 1002.45,
                    ""OrientationsAllowed"": ""OrientationsAll"",
                    ""TurnAllowedOnFloor"": false,
                    ""Qty"": 16,
                    ""ColorKnownName"": ""Brown""
                },
                {
                    ""Name"": ""ITEM002"",
                    ""Length"": 27.31,
                    ""Width"": 37.5,
                    ""Height"": 76.67,
                    ""Weight"": 521.45,
                    ""OrientationsAllowed"": ""OrientationsAll"",
                    ""TurnAllowedOnFloor"": false,
                    ""Qty"": 28,
                    ""ColorKnownName"": ""Aqua""
                },
                {
                    ""Name"": ""SKU0005"",
                    ""Length"": 27.31,
                    ""Width"": 9.5,
                    ""Height"": 75.67,
                    ""Weight"": 501.45,
                    ""OrientationsAllowed"": ""OrientationsAll"",
                    ""TurnAllowedOnFloor"": true,
                    ""Qty"": 24,
                    ""ColorKnownName"": ""Beige""
                },
                {
                    ""Name"": ""SKU0005"",
                    ""Qty"": 23
                },
                {
                    ""Name"": ""SKU0008"",
                    ""Qty"": 34
                }
            ],
            ""Containers"": [
                {
                    ""VehicleType"": ""Dry"",
                    ""Name"": ""53FT-Intermodal"",
                    ""Length"": 630,
                    ""Width"": 98,
                    ""Height"": 106,
                    ""ColorKnownName"": ""Blue""
                }
            ],
            ""Rules"": {
                ""IsWeightLimited"": true,
                ""IsSequenceUsed"": false,
                ""FillDirection"": ""FrontToRear"",
                ""CalculationType"": ""MixLoad""
            }
        }";
    
        var content = new StringContent(jsonRequest, Encoding.UTF8, "application/json");
        HttpResponseMessage response = await client.PostAsync("loads", content);
        string responseContent = await response.Content.ReadAsStringAsync();
    
        return new OkObjectResult(responseContent);
    }
                                    
  2. Store the TokenID in Application settings:
    • Go to Configuration > Application settings.
    • Add CubeMasterTokenID with your API key from Step 1.
    • Save the changes.
  3. Click Save in the function editor to deploy the code.

Request JSON: The above code uses the exact request JSON provided, which specifies cargo items, a container, and rules for load building.

The CubeMaster API returns a detailed JSON response. Here’s how to process it:

  1. Update your Azure Function to parse the response:
    
    using Newtonsoft.Json;
    
    [FunctionName("BuildLoad")]
    public static async Task Run(
        [HttpTrigger(AuthorizationLevel.Function, "post", Route = null)] HttpRequest req,
        ILogger log)
    {
        var client = new HttpClient();
        client.BaseAddress = new Uri("https://api.cubemaster.net/");
        client.DefaultRequestHeaders.Add("TokenID", Environment.GetEnvironmentVariable("CubeMasterTokenID"));
    
        string jsonRequest = @"..."; // Same as Step 5
        var content = new StringContent(jsonRequest, Encoding.UTF8, "application/json");
        HttpResponseMessage response = await client.PostAsync("loads", content);
        string responseContent = await response.Content.ReadAsStringAsync();
    
        if (response.IsSuccessStatusCode) {
            dynamic result = JsonConvert.DeserializeObject(responseContent);
            string status = result.status;
            string message = result.message;
            int cargoesLoaded = result.loadSummary.cargoesLoaded;
            double volumeLoaded = result.loadSummary.volumeLoaded;
            string diagramLink = result.filledContainers[0].graphics.images.path3DDiagram;
    
            log.LogInformation($"Status: {status}, Message: {message}, Cargoes Loaded: {cargoesLoaded}, Volume: {volumeLoaded}");
            return new OkObjectResult(new { Status = status, Message = message, Diagram = diagramLink });
        } else {
            log.LogError($"API Error: {response.StatusCode} - {responseContent}");
            return new BadRequestObjectResult($"Error: {responseContent}");
        }
    }
                                    
  2. Response JSON Example:
    
    {
        "status": "succeed",
        "message": "Engine created. 5 cargoes. 1 empty containers. Calculation started. Calculation ended. The load built successfully. The load saved to the cloud database.",
        "calculationError": "InvalidCargoSize",
        "document": {
            "title": "New Mixed Truck Load",
            "description": "Hello Web API",
            "isShared": true,
            "isAutoSaved": true,
            "isPending": false,
            "calculationTimeInSeconds": 0.6152743,
            "processId": "",
            "batchId": "",
            "createdBy": "CHANG@LOGEN.CO.KR",
            "createdAt": "2023-02-11T01:17:01.7392204+09:00",
            "updatedAt": "0001-01-01T00:00:00"
        },
        "loadSummary": {
            "cargoesLoaded": 68,
            "piecesLoaded": 68,
            "cargoesLeft": 0,
            "piecesLeft": 57,
            "unitloadsLoaded": 0,
            "volumeLoaded": 5261723.4606,
            "weightLoaded": 42674.59999999999,
            "priceLoaded": 0,
            "containersLoaded": 1
        },
        "filledContainers": [
            {
                "name": "#1 53FT-Intermodal",
                "sequence": 1,
                "loadSummary": {
                    "cargoesLoaded": 68,
                    "piecesLoaded": 68,
                    "unitloadsLoaded": 0,
                    "volumeLoaded": 5261723.4606,
                    "volumeUtilization": 80.39990374424703,
                    "vollumeUtilizationToLoadHeight": 81.03441853085657,
                    "floorLoaded": 57090.75,
                    "floorUtilization": 92.46963070942662,
                    "weightLoaded": 42674.59999999999,
                    "weightTotal": 42674.59999999999,
                    "weightUtilization": 0,
                    "dimWeight": 39424.33734939759,
                    "priceLoaded": 0,
                    "pricetUtilization": 0,
                    "cargoesPerLayer": 14,
                    "layersPerUnitload": 0
                },
                "graphics": {
                    "images": {
                        "path3DDiagram": "https://api.cubemaster.net/runtimes/b28413ca_51ed_44c9_b92e_13147363fd61.PNG",
                        "pathComposite": "https://api.cubemaster.net/runtimes/7eb09974_2f1d_41bc_9371_2002658dce07.PNG"
                    }
                }
                // ... (additional fields truncated for brevity)
            }
        ]
        // ... (additional fields truncated for brevity)
    }
                                    
  3. Extract key data like status, message, cargoesLoaded, and path3DDiagram for further use (e.g., display in a UI or store in a database).

Ensure your integration works smoothly by monitoring and debugging:

  1. In the Azure Portal, go to your Function App.
  2. Click Monitor in the left menu to view invocation logs.
  3. Check the Logs tab in the function editor for real-time output (e.g., log.LogInformation messages).
  4. Enable Application Insights:
    • Go to Application Insights in the left menu.
    • Turn it on and link it to your Function App.
    • Use the Insights dashboard to track request success/failure rates, response times, and errors.
  5. If an error occurs (e.g., InvalidCargoSize in the response), review the calculationError field and adjust the request JSON (e.g., verify cargo dimensions).
  6. Test the function:
    • Click Get function URL in the function editor.
    • Use a tool like Postman to send a POST request to the URL with an empty body to trigger it.

Tip: Regularly check logs for issues like authentication failures (invalid TokenID) or network errors.