BookmarkSubscribeRSS Feed

Go Viya – First steps with Go language and SAS Viya

Started ‎12-09-2020 by
Modified ‎03-04-2021 by
Views 5,990

frontpage.png

I usually write Python scripts to access SAS Viya REST APIs but lately I noticed some additions to the developer.sas.com website. In the samples, there is a tab titled Go. Knowing that R&D has been converting micro-services to that language, I thought that I may also like to become a Gopher. And believe me, my kids loved that idea.

 

In this article, I will introduce Go and also demonstrate some code to access SAS Viya REST APIs endpoints. Even though the content will not dive too far in the REST APIs nor it will deep dive into the Go language, having some prior exposure to SAS Viya REST APIs will clearly help reading this article.

What is Go?

I will not start a full review of Go as it might not be your first interest and there are really good articles/tutorials on the web if you want to dive into the language more deeply after reading this article.

To make it simple: Go brings the benefits of compiled and interpreted languages together!

 

This means Go is easy to write (and to read) just like Python or other interpreted languages are. Go runs nearly as fast as compiled code like C or C++. How is it possible? The syntax of Go code is pretty similar to Python or JavaScript in the sense that you write and call packages. The code is then seamlessly compiled before execution. As a result, you have code that is easy to read and performant at execution time.

 

For those who've heard about coding in C, you might think that it is difficult to write Go code due to the need for memory management. Luckily for us, Go brings a garbage collector at the table. This means that as a developer, you don't need to manage memory allocation and deallocation. This already removes some pains of writing code that should be compiled. In terms of performance, Go is also designed with concurrency in mind which is not the case for languages like Python.

 

What many people like about Python is how easy it is to write code. Go is not really different from Python when it comes to code readability. Where Python uses indentation to define code blocks, Go uses curly braces. Go and Python adhere to the same logic when it comes to semicolons: they are useless! A point where Python and Go differ when writing code is that Go is a strongly typed language. It means that you need to define your variables like you would do in Java for example. When assigning a value to a variable, that variable should be declared at the same time (or upfront). This is a tradeoff between easy code writing and performance requirements.

 

You should have by now a better idea of Go's benefits. There is something you should also be aware of when working with Go. As you can imagine, looking for information about Go on the web is not an easy task due to the number of web sites having "go" in their content. To find information about Go, you should look for "golang". With that being said, let's see what needs to be done to move forward with this article.

Building the environment

In order to work with Go, you should install Go and have a code editor like Visual Studio Code. You should also configure your SAS Viya environment to allow REST APIs calls.

 

When you have these, you are done!

Writing your first Go program

Open Visual Studio Code and type the following code in a new file.

 

package main

import (
	"fmt"
)

func main() {
	var text string = "world"
	fmt.Printf("Hello %v!\n", text)
}

 

Create a folder with name helloGo under your Download folder for example and save the file with the .go extension. Something like: hello.go

 

In Visual Studio Code, open a terminal (CTRL+` on Windows or ^+` on Mac) and navigate to the location where you saved the file. From that location, type the following command:

 

go run hello.go

 

If everything goes fine, the code will execute and you should see the following message in the terminal:

 

sbxxab@bizouxmbp helloGo % go run hello.go
Hello world!

 

Behind the scene, Go compiled your code and executed it.

 

If you followed the steps, you have written your first Go program. In this simple example, we can see already a few components of the Go language:

  • Create a package: package main
  • Import a package: fmt
  • Create a function: main
  • Declare a variable of type string: text
  • Assign a value to a variable: world
  • Use a function defined in a package: fmt.Printf
  • Compile your code
  • Execute your code

Basically, you are ready to Go 😉

 

If you want to learn more about Go, you can follow this tutorial.

Accessing SAS Viya REST APIs

Within SAS Viya, your environment should have a client id and client secret defined as indicated on developer.sas.com.

 

The code in this article will return a list of folders that contain a specific string in their name.

 

The first step will be to create a folder for our application. Create a folder named goviya in a folder of your choice. In that folder create three folders:

  • core
  • folders
  • samples

Before I explain how to generate the list of folders, we need to define a package that will be the core of our application. That package will contain two files:

  • callRest.go
  • getToken.go

These files act as wrappers for REST API calls and to get an authentication token in order to access SAS Viya. Here is the content of these files:

callRest.go:

package core

import (
	"crypto/tls"
	"io/ioutil"
	"log"
	"net/http"
	"net/url"
	"strings"
)

// CallRest is the default function for calling SAS Viya REST APIs
func CallRest(baseURL string, endpoint string, headers map[string][]string, method string, data url.Values, query url.Values) []byte {
	tr := &http.Transport{
		TLSClientConfig: &tls.Config{InsecureSkipVerify: true},
	}
	restURL := baseURL + endpoint
	req, err := http.NewRequest(method, restURL, strings.NewReader(data.Encode()))
	req.Header = headers
	req.URL.RawQuery = query.Encode()
	client := &http.Client{Transport: tr}
	resp, err := client.Do(req)
	if err != nil {
		log.Println(err)
	}
	defer resp.Body.Close()
	body, err := ioutil.ReadAll(resp.Body)
	if err != nil {
		log.Println(err)
	}
	return []byte(body)
}

getToken.go:

package core

import (
	"encoding/json"
	"net/url"
)

// AuthInfo type contains the information needed for authentication
type AuthInfo struct {
	Username     string `json:"username"`
	Password     string `json:"password"`
	GrantType    string `json:"grant_type"`
	ClientID     string `json:"client_id"`
	ClientSecret string `json:"client_secret"`
}

// Token type contains the token information of the current session.
type Token struct {
	AccessToken string `json:"access_token"`
	ExpiresIn   int    `json:"expires_in"`
	Scope       string `json:"scope"`
	Jti         string `json:"jti"`
}

// GetToken function returns a token based on the AuthInfo
func (ai AuthInfo) GetToken(baseURL string) Token {
	headers := map[string][]string{
		"Content-Type": []string{"application/x-www-form-urlencoded"},
		"Accept":       []string{"application/json"}}
	endpoint := "/SASLogon/oauth/token#password"
	method := "POST"
	data := url.Values{}
	data.Set("username", ai.Username)
	data.Set("password", ai.Password)
	data.Set("grant_type", ai.GrantType)
	data.Set("client_id", ai.ClientID)
	data.Set("client_secret", ai.ClientSecret)
	resp := CallRest(baseURL, endpoint, headers, method, data, nil)
	var token Token
	json.Unmarshal(resp, &token)
	return token
}

 

As you can see the two files contain a single function but both files share the core package name. This is handy as you can create as many files as you want/need and share the work between developers if needed.

 

Save the files under the core folder.

 

In the code, we are making use of struct. These are equivalent to objects in object oriented language like Java.

 

The next step will be to write a function to extract the folder information from SAS Viya.

 

To achieve this, you can create a file named getFolders.go in the folders directory,with the following content:

getFolders.go

package folders

import (
	"encoding/json"
	"goviya/core"
	"net/url"
)

// FolderList is an object representing a list of folders
type FolderList struct {
	Name    string   `json:"name"`
	Start   int      `json:"start"`
	Limit   int      `json:"limit"`
	Count   int      `json:"count"`
	Accept  string   `json:"accept"`
	Links   []Link   `json:"links"`
	Version int      `json:"version"`
	Items   []Folder `json:"items"`
}

// Folder is a folder object
type Folder struct {
	ID                string `json:"id"`
	Name              string `json:"name"`
	Description       string `json:"description"`
	ParentFolderURI   string `json:"parentFolderUri"`
	CreationTimeStamp string `json:"creationTimeStamp"`
	ModifiedTimeStamp string `json:"modifiedTimeStamp"`
	CreatedBy         string `json:"createdBy"`
	ModifiedBy        string `json:"modifiedBy"`
	Type              string `json:"type"`
	IconURI           string `json:"iconUri"`
	MemberCount       int    `json:"memberCount"`
	Links             []Link `json:"links"`
	Properties        string `json:"properties"`
	Version           int    `json:"version"`
}

// Link is a link object
type Link struct {
	Method           string `json:"method"`
	Rel              string `json:"rel"`
	URI              string `json:"uri"`
	Href             string `json:"href"`
	Title            string `json:"title"`
	Type             string `json:"type"`
	ItemType         string `json:"itemType"`
	ResponseType     string `json:"responseType"`
	ResponseItemType string `json:"responseItemType"`
}

// GetFolders extract a list of folders extra filters can be applied
func GetFolders(baseURL string, token core.Token, query url.Values) FolderList {
	bearer := "Bearer " + token.AccessToken
	headers := map[string][]string{
		"Accept":        []string{"application/vnd.sas.collection+json"},
		"Authorization": []string{bearer}}
	endpoint := "/folders/folders"
	method := "GET"
	resp := core.CallRest(baseURL, endpoint, headers, method, nil, query)
	var result FolderList
	json.Unmarshal(resp, &result)
	return result
}

 

The GetFolders function makes use of the CallRest function which is defined in the core package.

 

As you can see, we are again using struct but this time the object contains information that is mapped from a JSON file. In the example below, we are mapping responseItemType from JSON to a string variable named ResponseItemType.

 

ResponseItemType string `json:"responseItemType"`

 

The next step will be to create the main function of our application. Without that main function, Go will not compile the application.

 

In the samples folder, create a file named getFolders.go with the following content:

 

package main

import (
	"flag"
	"fmt"
	"goviya/core"
	"goviya/folders"
	"net/url"
)

func main() {
	var username, password, hostname, clientid, clientsecret, search, limit string
	flag.StringVar(&username, "u", "", "Please enter a user name")
	flag.StringVar(&password, "p", "", "Please enter a password")
	flag.StringVar(&hostname, "h", "", "Please enter the hostname")
	flag.StringVar(&clientid, "ci", "", "Please enter a ClientID")
	flag.StringVar(&clientsecret, "cs", "", "Please enter a Client Secret")
	flag.StringVar(&search, "s", "", "Please enter a search string")
	flag.StringVar(&limit, "l", "100", "Please enter a search string")
	flag.Parse()
	ai := core.AuthInfo{
		Username:     username,
		Password:     password,
		GrantType:    "password",
		ClientID:     clientid,
		ClientSecret: clientsecret}
	baseURL := hostname
	token := ai.GetToken(baseURL)
	query := url.Values{}
	query.Add("limit", limit)
	if search != "" {
		query.Add("filter", "contains(name, "+search+")")
	}
	fl := folders.GetFolders(baseURL, token, query)
	for _, item := range fl.Items {
		fmt.Printf("Id: %v Name: %v Members: %v\n", item.ID, item.Name, item.MemberCount)
	}
}

 

The main function will call the GetToken and the GetFolders functions to extract the folders information. The GetFolders function requires information that should be passed from the command line. This is done using flag package.When the response is received from SAS Viya, the main function will print the retrieved information into the terminal window using the Printf function that we've seen in the hello.go example.

 

Before we execute the program, let's compile it. In the hello.go example, we've used the go run command to execute the program. We will now execute another command: go build. From the terminal, navigate to the samples folder location under goviya and execute the following command:

 

go build getFolders.go

 

This command will not execute the application but it will compile it in the form of an executable. As a result, there will be no need to specify go run at the beginning of the line when you execute the program.

 

You can now execute the following command:

 

./getFolders -u yourUser -p yourPw -h https://intviya01.race.sas.com -ci appName -cs appSecret -l 2 -s "Folder"

 

The parameters are:

  • u: your user id
  • p: the password for your user
  • h: the URL to SAS Viya
  • ci: the client id that was created for your application
  • cs: the client secret that was defined for your application
  • l: the number of folders to be retrieved from the SAS Viya server
  • s: the text that is used to filter the folders

On my environment, the program returned the following information:

 

Id: b378a1a0-310b-4795-8c6e-5a6c91b9771b Name: My Folder Members: 17
Id: c9731c9c-7125-43df-bc85-15805edcbff1 Name: FolderShortcuts Members: 2

Where to go from here?

You should by now have a better understanding about Go and how to write your first application. You can reuse the code in this article to build your own calls and you can also dive more deeply into the Go language. Go offers a lot of packages to create a web server, but also to manipulate images. I'm sure you already have ideas popping up.

 

This article doesn't make extensive usage of the concurrency that Go has to offer. Using concurrency, you can for example make parallel calls to MAS to score models or to call decisions.

 

There is a lot we can do with Go. I'm still at the early stage in my learning process but I think I will use Go more and more in the future to discover more benefits and I guess you will also: Go for it!

Version history
Last update:
‎03-04-2021 04:07 AM
Updated by:

sas-innovate-2024.png

Available on demand!

Missed SAS Innovate Las Vegas? Watch all the action for free! View the keynotes, general sessions and 22 breakouts on demand.

 

Register now!

Free course: Data Literacy Essentials

Data Literacy is for all, even absolute beginners. Jump on board with this free e-learning  and boost your career prospects.

Get Started

Article Tags