Complete Go Integration Guide

CubeMaster API Integration Guide

Using Go (Golang) net/http

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.

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 applications to communicate over the internet using standard HTTP methods. Here’s a quick breakdown:

  • HTTP Methods:
    • GET: Retrieve data.
    • POST: Send data to create something (used in this guide).
    • PUT: Update data.
    • DELETE: Remove data.
  • Endpoints: URLs like https://api.cubemaster.net/loads define where to send requests.
  • JSON: A common data format for sending and receiving information (used in this API).
  • Headers: Metadata sent with requests, like authentication tokens.
  • Status Codes: Responses like 200 OK (success) or 401 Unauthorized (authentication failed).

In this guide, we’ll use a POST request to send load data to the CubeMaster API and receive a response with loading details.

Ensure you have Go installed and set up:

  1. Download and install Go from golang.org/dl/.
  2. Verify installation: go version (e.g., go1.21.0).
  3. Create a new project directory: mkdir cubemaster-api && cd cubemaster-api.
  4. Initialize a Go module: go mod init cubemaster-api.
  5. No external packages are needed since we’re using the standard net/http library.

Create a file named main.go to write your code.

Assume your customer’s legacy database stores order or shipment data (e.g., items, quantities, dimensions). Here’s how to fetch and map it to the API request:

package main

import (
    "database/sql"
    _ "github.com/go-sql-driver/mysql" // Example: MySQL driver
    "log"
)

type Item struct {
    Name      string
    Length    float64
    Width     float64
    Height    float64
    Weight    float64
    Qty       int
    Color     string
}

func fetchItemsFromDB() ([]Item, error) {
    db, err := sql.Open("mysql", "user:password@tcp(127.0.0.1:3306)/legacy_db")
    if err != nil {
        return nil, err
    }
    defer db.Close()

    rows, err := db.Query("SELECT name, length, width, height, weight, qty, color FROM shipments")
    if err != nil {
        return nil, err
    }
    defer rows.Close()

    var items []Item
    for rows.Next() {
        var item Item
        if err := rows.Scan(&item.Name, &item.Length, &item.Width, &item.Height, &item.Weight, &item.Qty, &item.Color); err != nil {
            return nil, err
        }
        items = append(items, item)
    }
    return items, nil
}

This example uses a MySQL database. Adjust the driver and query based on your legacy system (e.g., PostgreSQL, SQLite). The retrieved data will be used in the API request.

Use net/http to build the POST request with the TokenID in the header and JSON payload:

package main

import (
    "bytes"
    "encoding/json"
    "net/http"
    "os"
)

type Cargo struct {
    Name               string  `json:"Name"`
    Length             float64 `json:"Length"`
    Width              float64 `json:"Width"`
    Height             float64 `json:"Height"`
    Weight             float64 `json:"Weight"`
    OrientationsAllowed string `json:"OrientationsAllowed"`
    TurnAllowedOnFloor bool   `json:"TurnAllowedOnFloor"`
    Qty                int    `json:"Qty"`
    ColorKnownName     string `json:"ColorKnownName"`
}

type Container struct {
    VehicleType    string `json:"VehicleType"`
    Name           string `json:"Name"`
    Length         float64 `json:"Length"`
    Width          float64 `json:"Width"`
    Height         float64 `json:"Height"`
    ColorKnownName string `json:"ColorKnownName"`
}

type Rules struct {
    IsWeightLimited bool   `json:"IsWeightLimited"`
    IsSequenceUsed  bool   `json:"IsSequenceUsed"`
    FillDirection   string `json:"FillDirection"`
    CalculationType string `json:"CalculationType"`
}

type LoadRequest struct {
    Title       string      `json:"Title"`
    Description string      `json:"Description"`
    Cargoes     []Cargo     `json:"Cargoes"`
    Containers  []Container `json:"Containers"`
    Rules       Rules       `json:"Rules"`
}

func buildRequest() (*http.Request, error) {
    // Fetch items from legacy database
    items, err := fetchItemsFromDB()
    if err != nil {
        return nil, err
    }

    // Map database items to Cargoes
    var cargoes []Cargo
    for _, item := range items {
        cargoes = append(cargoes, Cargo{
            Name:               item.Name,
            Length:             item.Length,
            Width:              item.Width,
            Height:             item.Height,
            Weight:             item.Weight,
            OrientationsAllowed: "OrientationsAll",
            TurnAllowedOnFloor: false,
            Qty:                item.Qty,
            ColorKnownName:     item.Color,
        })
    }

    // Define the request payload
    payload := LoadRequest{
        Title:       "New Mixed Truck Load",
        Description: "Hello Web API",
        Cargoes:     cargoes,
        Containers: []Container{
            {
                VehicleType:    "Dry",
                Name:           "53FT-Intermodal",
                Length:         630,
                Width:          98,
                Height:         106,
                ColorKnownName: "Blue",
            },
        },
        Rules: Rules{
            IsWeightLimited: true,
            IsSequenceUsed:  false,
            FillDirection:   "FrontToRear",
            CalculationType: "MixLoad",
        },
    }

    // Marshal to JSON
    jsonData, err := json.Marshal(payload)
    if err != nil {
        return nil, err
    }

    // Create the POST request
    req, err := http.NewRequest("POST", "https://api.cubemaster.net/loads", bytes.NewBuffer(jsonData))
    if err != nil {
        return nil, err
    }

    // Set headers
    req.Header.Set("Content-Type", "application/json")
    req.Header.Set("TokenID", os.Getenv("CUBEMASTER_TOKENID")) // Load TokenID from environment

    return req, nil
}

Request JSON Example:

{
    "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"
    }
}

Execute the HTTP request using http.DefaultClient.Do and unmarshal the JSON response into structs:

package main

import (
    "encoding/json"
    "fmt"
    "io"
    "log"
    "net/http"
)

type LoadSummary struct {
    CargoesLoaded   int     `json:"cargoesLoaded"`
    PiecesLoaded    int     `json:"piecesLoaded"`
    VolumeLoaded    float64 `json:"volumeLoaded"`
    WeightLoaded    float64 `json:"weightLoaded"`
    ContainersLoaded int    `json:"containersLoaded"`
}

type Images struct {
    Path3DDiagram string `json:"path3DDiagram"`
    PathComposite string `json:"pathComposite"`
}

type Graphics struct {
    Images Images `json:"images"`
}

type FilledContainer struct {
    Name     string   `json:"name"`
    Sequence int      `json:"sequence"`
    Graphics Graphics `json:"graphics"`
}

type LoadResponse struct {
    Status           string            `json:"status"`
    Message          string            `json:"message"`
    CalculationError string            `json:"calculationError"`
    LoadSummary      LoadSummary       `json:"loadSummary"`
    FilledContainers []FilledContainer `json:"filledContainers"`
}

func main() {
    req, err := buildRequest()
    if err != nil {
        log.Fatalf("Error building request: %v", err)
    }

    client := &http.Client{}
    resp, err := client.Do(req)
    if err != nil {
        log.Fatalf("Error sending request: %v", err)
    }
    defer resp.Body.Close()

    body, err := io.ReadAll(resp.Body)
    if err != nil {
        log.Fatalf("Error reading response: %v", err)
    }

    if resp.StatusCode != http.StatusOK {
        log.Fatalf("API request failed with status: %s, body: %s", resp.Status, string(body))
    }

    var loadResp LoadResponse
    if err := json.Unmarshal(body, &loadResp); err != nil {
        log.Fatalf("Error unmarshaling response: %v", err)
    }

    fmt.Printf("Status: %s\n", loadResp.Status)
    fmt.Printf("Message: %s\n", loadResp.Message)
    fmt.Printf("Cargoes Loaded: %d\n", loadResp.LoadSummary.CargoesLoaded)
    fmt.Printf("Volume Loaded: %.2f\n", loadResp.LoadSummary.VolumeLoaded)
    fmt.Printf("Weight Loaded: %.2f\n", loadResp.LoadSummary.WeightLoaded)
    if len(loadResp.FilledContainers) > 0 {
        fmt.Printf("3D Diagram: %s\n", loadResp.FilledContainers[0].Graphics.Images.Path3DDiagram)
    }
}

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",
        "calculationTimeInSeconds": 0.6152743,
        "createdBy": "CHANG@LOGEN.CO.KR"
    },
    "loadSummary": {
        "cargoesLoaded": 68,
        "piecesLoaded": 68,
        "volumeLoaded": 5261723.4606,
        "weightLoaded": 42674.59999999999,
        "containersLoaded": 1
    },
    "filledContainers": [
        {
            "name": "#1 53FT-Intermodal",
            "sequence": 1,
            "graphics": {
                "images": {
                    "path3DDiagram": "https://api.cubemaster.net/runtimes/b28413ca_51ed_44c9_b92e_13147363fd61.PNG"
                }
            }
        }
    ]
}

Techniques for monitoring and debugging your API integration in Go:

  • Standard Log Package: Use Go's built-in log package (e.g., log.Printf) to print request and response details.
  • Structured Logging (slog): In Go 1.21+, use log/slog for JSON log output:
  • import "log/slog"
    
    slog.Info("Sending API request", "url", req.URL.String())
    slog.Info("Response received", "status", resp.Status)
    
  • Dump HTTP Requests/Responses: Use net/http/httputil to inspect raw HTTP traffic:
  • import "net/http/httputil"
    
    dump, _ := httputil.DumpRequestOut(req, true)
    fmt.Println(string(dump))
    
  • Testing with Postman: Verify API authentication and JSON payload independently using Postman before executing Go binaries.