Language

Data Bundles add a thin layer around combinators, useful in 2 ways:

  1. Retrieving data into explicitly named properties from different combinators
  2. Accepts orderBy and limit parameters to control how many data points are returned for a specific bundle property

Using previously covered examples of profile and location data, they are clearly very distinct, but an application may still benefit from having both at the same time. For instance, it may only care for the most recent information on user’s profile and their 5 most recent locations:

curl --request POST \
  --url https://test.hubat.net/api/v2/data-bundle/localprofile \
  --header 'content-type: application/json' \
  --header 'x-auth-token: ACCESS_TOKEN' \
  --data '{"profile":{"endpoints":[{"endpoint":"rumpel/profile"}],"limit":1},"location":{"endpoints":[{"endpoint":"rumpel/locations","mapping":{"longitude":"data.locations.longitude","latitude":"data.locations.latitude"}}],"limit":5}}'
var data = JSON.stringify({
  "profile": {
    "endpoints": [
      {
        "endpoint": "rumpel/profile"
      }
    ],
    "limit": 1
  },
  "location": {
    "endpoints": [
      {
        "endpoint": "rumpel/locations",
        "mapping": {
          "longitude": "data.locations.longitude",
          "latitude": "data.locations.latitude"
        }
      }
    ],
    "limit": 5
  }
});

var xhr = new XMLHttpRequest();
xhr.withCredentials = true;

xhr.addEventListener("readystatechange", function () {
  if (this.readyState === this.DONE) {
    console.log(this.responseText);
  }
});

xhr.open("POST", "https://test.hubat.net/api/v2/data-bundle/localprofile");
xhr.setRequestHeader("x-auth-token", "ACCESS_TOKEN");
xhr.setRequestHeader("content-type", "application/json");

xhr.send(data);
<?php

$curl = curl_init();

curl_setopt_array($curl, array(
  CURLOPT_URL => "https://test.hubat.net/api/v2/data-bundle/localprofile",
  CURLOPT_RETURNTRANSFER => true,
  CURLOPT_ENCODING => "",
  CURLOPT_MAXREDIRS => 10,
  CURLOPT_TIMEOUT => 30,
  CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
  CURLOPT_CUSTOMREQUEST => "POST",
  CURLOPT_POSTFIELDS => "{\"profile\":{\"endpoints\":[{\"endpoint\":\"rumpel/profile\"}],\"limit\":1},\"location\":{\"endpoints\":[{\"endpoint\":\"rumpel/locations\",\"mapping\":{\"longitude\":\"data.locations.longitude\",\"latitude\":\"data.locations.latitude\"}}],\"limit\":5}}",
  CURLOPT_HTTPHEADER => array(
    "content-type: application/json",
    "x-auth-token: ACCESS_TOKEN"
  ),
));

$response = curl_exec($curl);
$err = curl_error($curl);

curl_close($curl);

if ($err) {
  echo "cURL Error #:" . $err;
} else {
  echo $response;
}
import requests

url = "https://test.hubat.net/api/v2/data-bundle/localprofile"

payload = "{\"profile\":{\"endpoints\":[{\"endpoint\":\"rumpel/profile\"}],\"limit\":1},\"location\":{\"endpoints\":[{\"endpoint\":\"rumpel/locations\",\"mapping\":{\"longitude\":\"data.locations.longitude\",\"latitude\":\"data.locations.latitude\"}}],\"limit\":5}}"
headers = {
    'x-auth-token': "ACCESS_TOKEN",
    'content-type': "application/json"
    }

response = requests.request("POST", url, data=payload, headers=headers)

print(response.text)
require 'uri'
require 'net/http'

url = URI("https://test.hubat.net/api/v2/data-bundle/localprofile")

http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true
http.verify_mode = OpenSSL::SSL::VERIFY_NONE

request = Net::HTTP::Post.new(url)
request["x-auth-token"] = 'ACCESS_TOKEN'
request["content-type"] = 'application/json'
request.body = "{\"profile\":{\"endpoints\":[{\"endpoint\":\"rumpel/profile\"}],\"limit\":1},\"location\":{\"endpoints\":[{\"endpoint\":\"rumpel/locations\",\"mapping\":{\"longitude\":\"data.locations.longitude\",\"latitude\":\"data.locations.latitude\"}}],\"limit\":5}}"

response = http.request(request)
puts response.read_body

The response includes the specific data requested:


HTTP/1.1 200 OK
Content-Type: application/json


{
  "profile": [
    {
      "endpoint": "rumpel/profile",
      "recordId": "9b136020-372a-4777-81f9-2c4ce6925aea",
      "data": {
        "profile": {
          "website": {
            "link": "https://example.com",
            "private": "false"
          },
          "nick": {
            "private": "true",
            "name": ""
          },
          "primary_email": {
            "value": "testuser@example.com",
            "private": "false"
          },
          "private": "false",
          "youtube": {
            "link": "",
            "private": "true"
          },
          "address_global": {
            "city": "London",
            "county": "",
            "country": "UK",
            "private": "true"
          },
          "age": {
            "group": "",
            "private": "true"
          },
          "personal": {
            "first_name": "",
            "private": "false",
            "preferred_name": "Test",
            "last_name": "User",
            "middle_name": "",
            "title": ""
          },
          "blog": {
            "link": "",
            "private": "false"
          },
          "facebook": {
            "link": "",
            "private": "false"
          },
          "address_details": {
            "no": "",
            "street": "",
            "private": "false",
            "postcode": ""
          },
          "emergency_contact": {
            "first_name": "",
            "private": "true",
            "relationship": "",
            "last_name": "",
            "mobile": ""
          },
          "alternative_email": {
            "private": "true",
            "value": ""
          },
          "fb_profile_photo": {
            "private": "false"
          },
          "twitter": {
            "link": "",
            "private": "false"
          },
          "about": {
            "body": "A short bio about me shown on my PHATA",
            "private": "false",
            "title": "Me the Test User"
          },
          "mobile": {
            "no": "",
            "private": "true"
          },
          "gender": {
            "type": "",
            "private": "true"
          }
        }
      }
    }
  ],
  "location": [
    {
      "endpoint": "rumpel/locations",
      "recordId": "e965e022-6613-476a-a0cd-1f587a41b148",
      "data": {
        "longitude": "0.101014673709963",
        "latitude": "51.671358277138"
      }
    },
    {
      "endpoint": "rumpel/locations",
      "recordId": "fcf1a26b-e49f-4457-915b-156e14140f38",
      "data": {
        "longitude": "0.100905202634514",
        "latitude": "51.674001392439"
      }
    },
    {
      "endpoint": "rumpel/locations",
      "recordId": "8f7afa92-39e2-48ab-8028-f5aebaa9918e",
      "data": {
        "longitude": "0.080477950927866",
        "latitude": "51.6658257133844"
      }
    },
    {
      "endpoint": "rumpel/locations",
      "recordId": "d3a6f04b-4df6-4888-a7b0-c1d5ca272de9",
      "data": {
        "longitude": "0.0641066288762133",
        "latitude": "51.6641215101037"
      }
    },
    {
      "endpoint": "rumpel/locations",
      "recordId": "6a858d87-899e-4961-b722-0738d07c755e",
      "data": {
        "longitude": "0.0961801595986785",
        "latitude": "51.6712232446779"
      }
    }
  ]
}

To keep the example simple, it does not include complex data combinators covered in the previous step, however you will notice that the endpoints property has exactly the same format as the body of a request for creating a new combinator.


Like Data Combinators, Data Bundles can only be directly used by privileged applications suchas the personal data dashboard, however it leads us to Data Debits for consented data sharing as Bundles is the format used to specify the data requested from the user.