WURFL Microservice for GO

Note: Before getting started, you will need access to a running instance of the WURFL Microservice HTTP Server, either through ScientiaMobile's Docker Registry, the AWS Marketplace, or any other distribution channel.

The Golang API package for WURFL Microservice contains source code for the WURFL Microservice client for GO version 1.8+.


WURFL Capabilities and API


WURFL Capabilities: Device properties represented in WURFL. While users of the WURFL Microservice for Docker have access to the list of licensed capabilities, customers who obtained the product through the AWS Marketplace will have access to the WURFL capability list, predefined for the AMI they have acquired. Please note that there are two types of capabilities supported in WURFL:

  • Static Capabilities: Device properties associated to a WURFL device profile. They can be tracked to an actual <capability> element in the wurfl.xml file.

  • Virtual Capabilities: Introduced in API 1.5, virtual capabilities support an algorithmic computational component which greatly augments the power of WURFL at run-time. They can also rely on certain static capabilities under the hood.

WURFL API: Given an HTTP Request and a capability, the WURFL API will return the property value. WURFL has historically supported APIs for all main programming platforms.


Obtaining, Installing and Running the WURFL API


In order to use the WURFL API that operates with WURFL Microservice, please follow these instructions:

  1. Obtain the package with the API library from your HTTP Server (/download endpoint).

  2. Untar the contents of the package in your GOPATH.

  3. Edit example.go (in the package) by modifying the Create() call to include the public IP address of your AMI instance or Docker container service (i.g. replacing localhost with the IP address of your running AMI in AWS Marketplace, or the address of your Docker container service).

Note: There are multiple ways in which a service deployed through Docker can be exposed to the world around it. Please refer to your DevOps if in doubt of the actual address at which the service is running.*

  1. Get dependencies (go get ./…), compile (go build example.go), and run (./example) the included example.go file. Here's the code for your reference:
package main

import (
    "fmt"
    "log"
    "strings"
    "sort"

    "scientiamobile/wmclient"
)

// Implements sort.Interface for []wmclient.JSONModelMktName
type ByModelName []wmclient.JSONModelMktName

func(c ByModelName) Len() int {
    return len(c)
}

func (c ByModelName) Swap(i, j int) {
    c[i], c[j] = c[j], c[i]
}

func (c ByModelName) Less(i, j int) bool {
    return c[i].ModelName < c[j].ModelName
}

func main() {
    var err error

    // First we need to create a WM client instance, to connect to our WM server API at the specified host and port.
    ClientConn, err := wmclient.Create("http", "localhost", "80", "")
    if err != nil {
        // problems such as network errors  or internal server problems
        log.Fatal("wmclient.Create returned :", err.Error())
    }
    // By setting the cache size we are also activating the caching option in WM client. In order to not use cache, you just to need to omit setCacheSize call
    ClientConn.SetCacheSize(100000)

    // We ask Wm server API for some Wm server info such as server API version and info about WURFL API and file used by WM server.
    info, ierr := ClientConn.GetInfo()
    if ierr != nil {
        fmt.Println("Error getting server info: " + ierr.Error())
    } else {
        fmt.Println("WURFL Microservice information:")
        fmt.Println("Server version: " + info.WmVersion)
        fmt.Println("WURFL API version: " + info.WurflAPIVersion)
        fmt.Printf("WURFL file info: %s \n", info.WurflInfo)
    }

    // set the capabilities we want to receive from WM server
    // Static capabilities
    sCapsList := strings.Fields("model_name brand_name")
    // Virtual capabilities
    vCapsList := strings.Fields("is_smartphone form_factor")
    ClientConn.SetRequestedStaticCapabilities(sCapsList)
    ClientConn.SetRequestedVirtualCapabilities(vCapsList)

    ua := "Mozilla/5.0 (iPhone; CPU iPhone OS 5_0 like Mac OS X) AppleWebKit/534.46 (KHTML, like Gecko) Version/5.1 Mobile/9A334 Safari/7534.48.3"

    // Perform a device detection calling WM server API
    JSONDeviceData, callerr := ClientConn.LookupUserAgent(ua)

    if callerr != nil {
        // Applicative error, ie: invalid input provided
        log.Fatalf("Error getting device data %s\n", callerr.Error())
    }

    // Let's get the device capabilities and print some of them
    fmt.Printf("WURFL device id : %s\n", JSONDeviceData.Capabilities["wurfl_id"])

    // print brand & model (static capabilities)
    fmt.Printf("This device is a : %s %s\n", JSONDeviceData.Capabilities["brand_name"], JSONDeviceData.Capabilities["model_name"])

    // check if device is a smartphone (a virtual capability)
    if JSONDeviceData.Capabilities["is_smartphone"] == "true" {
        fmt.Println("This is a smartphone")
    }
    fmt.Printf("This device form_factor is %s\n", JSONDeviceData.Capabilities["form_factor"])

    // Get all the device manufacturers, and print the first twenty
    deviceMakes, err := ClientConn.GetAllDeviceMakes()
    if err != nil {
        log.Fatalf("Error getting device data %s\n", err.Error())
    }

    var limit = 20
    fmt.Printf("Print the first %d Brand of %d\n", limit, len(deviceMakes))

    // Sort the device manufacturer names
    sort.Strings(deviceMakes)

    for _, deviceMake := range deviceMakes[0:limit] {
        fmt.Printf(" - %s\n", deviceMake)
    }

    // Now call the WM server to get all device model and marketing names produced by Apple
    fmt.Println("Print all Model for the Apple Brand")
    modelMktNames, err := ClientConn.GetAllDevicesForMake("Apple")

    if err != nil {
        log.Fatalf("Error getting device data %s\n", err.Error())
    }

    // Sort modelMktNames objects by their model name
    sort.Sort(ByModelName(modelMktNames))

    for _, modelMktName := range modelMktNames {
        fmt.Printf(" - %s %s\n", modelMktName.ModelName, modelMktName.MarketingName)
    }

    // Now call the WM server to get all operative system names
    fmt.Println("Print the list of OSes")
    oses, err := ClientConn.GetAllOSes()

    if err != nil {
        log.Fatalf("Error getting device data %s\n", err.Error())
    }

    // Sort and print all OS names
    sort.Strings(oses)

    for _, os := range oses {
        fmt.Printf(" - %s\n", os)
    }

    // Let's call the WM server to get all version of the Android OS
    fmt.Println("Print all versions for the Android OS")
    versions, err := ClientConn.GetAllVersionsForOS("Android")

    if err != nil {
        log.Fatalf("Error getting device data %s\n", err.Error())
    }

    // Sort all Android version numbers and print them.
    sort.Strings(versions)

    for _, version := range versions {
        fmt.Printf(" - %s\n", version)
    }
}

If you are using device detection from code running as part of an HTTP server, it is highly recommended that you pass the complete HTTP request to the LookupRequest method as follows:

JsonDeviceData, err := ClientConn.LookupRequest(*r)

This will provide optimal detection accuracy (see httpserver-example.go). For a complete reference of all static and virtual capabilities, you can refer to this document. Please note that WURFL Microservice for the AWS Marketplace comes with a predefined set of capabilities.

Complete reference of the wmclient API

This is the ouput of the "godoc scientiamobile/wmclient" command for the wmclient package.

PACKAGE DOCUMENTATION

package wmclient
    import "scientiamobile/wmclient"


FUNCTIONS

func GetAPIVersion() string
    GetAPIVersion returns the version number of WM Client API

TYPES

type JSONDeviceData struct {
    APIVersion   string            `json:"apiVersion"`
    Capabilities map[string]string `json:"capabilities"`
    Error        string            `json:"error, omitempty"`
    Mtime        int64             `json:"mtime"` // timestamp of this data structure creation, kpet for compatibility with old wurfl cloud
    Ltime        string            `json:"ltime"` // time of last wurfl.xml file load
}
    JSONDeviceData models a WURFL device data in JSON string only format

type JSONDeviceDataTyped struct {
    APIVersion   string                 `json:"apiVersion"`
    Capabilities map[string]interface{} `json:"capabilities"`
    Error        string                 `json:"error, omitempty"`
    Mtime        int64                  `json:"mtime"`
    Ltime        string                 `json:"ltime"`
}
    JSONDeviceDataTyped models a WURFL device data in JSON typed format

type JSONDeviceOsVersions struct {
    OsName    string `json:"device_os"`
    OsVersion string `json:"device_os_version"`
}
    JSONDeviceOsVersions holds device os name and version

type JSONInfoData struct {
    WurflAPIVersion  string   `json:"wurfl_api_version"`
    WurflInfo        string   `json:"wurfl_info"`
    WmVersion        string   `json:"wm_version"`
    ImportantHeaders []string `json:"important_headers"`
    StaticCaps       []string `json:"static_caps"`
    VirtualCaps      []string `json:"virtual_caps"`
    Ltime            string   `json:"ltime"`
}
    JSONInfoData - server and API informations

type JSONMakeModel struct {
    BrandName     string `json:"brand_name"`
    ModelName     string `json:"model_name"`
    MarketingName string `json:"marketing_name,omitempty"`
}
    JSONMakeModel models simple device "identity" data in JSON format

type JSONModelMktName struct {
    ModelName     string `json:"model_name"`
    MarketingName string `json:"marketing_name,omitempty"`
}
    JSONModelMktName holds model_name and marketing_name

type Request struct {
    LookupHeaders  map[string]string `json:"lookup_headers"`
    RequestedCaps  []string          `json:"requested_caps"`
    RequestedVCaps []string          `json:"requested_vcaps, omitempty"`
    WurflID        string            `json:"wurfl_id, omitempty"`
    TacCode        string            `json:"tac_code, omitempty"`
}
    Request - data object that is sent to the WM server in POST requests

type WmClient struct {
    StaticCaps  []string
    VirtualCaps []string

    ImportantHeaders []string
    // contains filtered or unexported fields
}
    WmClient holds http connection data to WM server and the list of static
    and virtual capabilities it must return in response.

func Create(Scheme string, Host string, Port string, BaseURI string) (*WmClient, error)
    Create : creates object, checks for server visibility

func (c *WmClient) DestroyConnection()
    DestroyConnection - Disposes resources used in connection to server and
    clears cache and other shared data structures

func (c *WmClient) GetActualCacheSizes() (int, int)
    GetActualCacheSizes return the values of cache size. The first value
    being the device-id based cache, the second value being the size of the
    headers-based one

func (c *WmClient) GetAllDeviceMakes() ([]string, error)
    GetAllDeviceMakes returns a slice of all devices brand_name capabilities
    in WM server

func (c *WmClient) GetAllDevicesForMake(brandName string) ([]JSONModelMktName, error)
    GetAllDevicesForMake returns a slice of an aggregate containing
    model_names and marketing_names for the given brand_name

func (c *WmClient) GetAllMakeModel() ([]JSONMakeModel, error)
    GetAllMakeModel returns identity data for all devices in WM server
    Deprecated since 1.2.0.0 in favour of GetAllDeviceMakes

func (c *WmClient) GetAllOSes() ([]string, error)
    GetAllOSes returns a slice of all devices device_os capabilities in WM
    server

func (c *WmClient) GetAllVersionsForOS(osName string) ([]string, error)
    GetAllVersionsForOS returns a slice of an aggregate containing
    device_os_version for the given os_name

func (c *WmClient) GetInfo() (*JSONInfoData, error)
    GetInfo - Returns information about the running WM server and API

func (c *WmClient) HasStaticCapability(CapName string) bool
    HasStaticCapability - returns true if the given CapName exist in this
    client' static capability set, false otherwise

func (c *WmClient) HasVirtualCapability(CapName string) bool
    HasVirtualCapability - returns true if the given CapName exist in this
    client' virtual capability set, false otherwise

func (c *WmClient) LookupDeviceID(deviceID string) (*JSONDeviceData, error)
    LookupDeviceID - Searches WURFL device data using its wurfl_id value

func (c *WmClient) LookupRequest(request http.Request) (*JSONDeviceData, error)
    LookupRequest - detects a device and returns its data in JSON format

func (c *WmClient) LookupUserAgent(userAgent string) (*JSONDeviceData, error)
    LookupUserAgent - Searches WURFL device data using the given user-agent
    for detection

func (c *WmClient) SetCacheSize(uaMaxEntries int)
    SetCacheSize : set UA cache size

func (c *WmClient) SetHTTPTimeout(connection int, transfer int)
    SetHTTPTimeout sets the connection and transfer timeouts for this client
    in seconds. This function should be called before performing any
    connection to WM server

func (c *WmClient) SetRequestedCapabilities(CapsList []string)
    SetRequestedCapabilities - set the given capability names to the set
    they belong

func (c *WmClient) SetRequestedStaticCapabilities(CapsList []string)
    SetRequestedStaticCapabilities - set list of standard static
    capabilities to return

func (c *WmClient) SetRequestedVirtualCapabilities(CapsList []string)
    SetRequestedVirtualCapabilities - set list of virtual capabilities to
    return

func (c *WmClient) SetupCache(deviceMaxEntries int, uaMaxEntries int)
    SetupCache Deprecated: Use SetCacheSize()

Error in case of Missing Capabilities

As mentioned above, depending on the configuration of your WURFL Microservice, one of the WURFL capabilities may not be available. In this case, the JSONDeviceData.Capabilities map will not contain an entry for that capability and caller will get the zero value for it (nil).

Presence of a capability can be checked with:

value, ok := JSONDeviceData.Capabilities["has_cellular_radio"]

And check for:

ok = true/false

Additional capabilities can always be obtained by upgrading to greater version of the AMIs in the AWS Marketplace or by contacting ScientiaMobile to license additional capabilities for the Docker Image or other products.




© 2018 ScientiaMobile Inc.
All Rights Reserved.

NOTICE: All information contained herein is, and remains the property of ScientiaMobile Incorporated and its suppliers, if any. The intellectual and technical concepts contained herein are proprietary to ScientiaMobile Incorporated and its suppliers and may be covered by U.S. and Foreign Patents, patents in process, and are protected by trade secret or copyright law. Dissemination of this information or reproduction of this material is strictly forbidden unless prior written permission is obtained from ScientiaMobile Incorporated.