baseURL = "https://api.d21s.com/v2"
// Read the environment variables used for authentication
var keyID = os.Getenv("DT_SERVICE_ACCOUNT_KEY_ID")
var secret = os.Getenv("DT_SERVICE_ACCOUNT_SECRET")
// Struct that represents a project fetched from the API
Name string `json:"name"`
DisplayName string `json:"displayName"`
Inventory bool `json:"inventory"`
Organization string `json:"organization"`
OrganizationDisplayName string `json:"organizationDisplayName"`
Sensorcount int `json:"sensorCount"`
CloudConnectorCount int `json:"cloudConnectorCount"`
// Sends a GET request to the Disruptive REST API and returns the response body.
func getRequest(endpointURL string, queryParams map[string]string) ([]byte, error) {
// Create the HTTP GET request
req, err := http.NewRequest("GET", endpointURL, nil)
// Set the query parameters for the request
for key, value := range queryParams {
req.URL.RawQuery = q.Encode()
// Set the Authorization header to use HTTP Basic Auth
req.SetBasicAuth(keyID, secret)
// Send the HTTP request with a 3 second timout in case we
// are unable to reach the server.
client := &http.Client{Timeout: time.Second * 3}
response, err := client.Do(req)
defer response.Body.Close()
return ioutil.ReadAll(response.Body)
// getAllProjects retrieves all projects that are accessible to the
// Service Account, one page at a time.
func paginatedGetProjects() ([]Project, error) {
// Initialize empty output slice.
// Define the structure of each page.
type ProjectsPage struct {
Projects []Project `json:"projects"`
NextPageToken string `json:"nextPageToken"`
// Start with an empty page token to request the first page.
// We'll update this variable to point to the subsequent pages.
// Loop until all pages have been fetched
// Define the query parameters for the request.
// The "page_token" query parameter specifies the page
// we want to get. The "page_size" parameter is included
// just for the sake of completion. The default page size is 100.
params := map[string]string{
// Send GET request to the endpoint and get bytes back
pageBytes, err := getRequest(baseURL+"/projects", params)
// Decode pageBytes into the expected page structure
if err := json.Unmarshal(pageBytes, &page); err != nil {
// Append the retrieved page of projects to our output slice
projects = append(projects, page.Projects...)
fmt.Printf("Got a page of %d projects\n", len(page.Projects))
// Update the page token to point to the next page
pageToken = page.NextPageToken
// If we got an empty "next_page_token" in the response,
// it means there are no more pages to fetch.
// Make paginated requests for all available projects.
projects, err := paginatedGetProjects()
// Print display name of all fetched projects.
for i, project := range projects {
fmt.Printf("%d. %s\n", i, project.DisplayName)