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.
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.
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!
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:
Basically, you are ready to Go 😉
If you want to learn more about Go, you can follow this tutorial.
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:
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:
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:
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)
}
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:
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:
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
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!
Are you ready for the spotlight? We're accepting content ideas for SAS Innovate 2025 to be held May 6-9 in Orlando, FL. The call is open until September 25. Read more here about why you should contribute and what is in it for you!
Data Literacy is for all, even absolute beginners. Jump on board with this free e-learning and boost your career prospects.