Gowhere – array, hash table, regular expression, and foreach loop

Gowhere? Some steps further—append arrays, hash table, regular expression, and for each loop.

After playing around with HTTP, I got the JSON data from the API. I wanted to analyze the data—to display the total hours spent on each work item.

A typical record has this structure

type WorkItem struct {
    FromDateTime time.Time
    ToDateTime   time.Time
    Detail       string
}

And an actual work item looks like

{
    "FromDateTime" : "2019-11-29 02:05:00 +0000 UTC",
    "ToDateTime": "2019-11-29 03:19:00 +0000 UTC",
    "Detail" : "Work Item 12345: Implement a cool feature"
}

The "Work Item 12345" has many records. The detail field are not the same except they contain the number 12345 as the work item ID. I want to display the sum of time spent for Work Item 12345. So the algorithm is pretty simple

  1. For each record, extract the work item id from the detail field
  2. Calculate the difference in hours between FromDateTime and ToDateTime
  3. Sum the difference with the existing value—if not exist, create a new one with the time spent zero

Note: If I am writing in C#, I can finish the implementation quickly with Linq support.

The expected result of a work item is below

{
    "WorkItemId" : "12345",
    "WorkItemName" : "Work Item 12345: Implement a cool feature",
    "TimeSpent" : time_spent_in_hours
}

Stats structure to hold the analysis result of a work item

// Stats ...
type Stats struct {
    WorkItemId    string
    WorkItemName  string
    TimeSpent float64
}

Let’s write some code and explore

package main

import (
    "encoding/json"
    "fmt"
    "net/http"
    "regexp"
    "time"
)

func main() {
    // Assuming that I have a list of IDs for a team.
    // The getWorkItemRecords will an array of WorkItem of a member.
    var records = make([]WorkItem, 0)
    for _, id := range team {
        r := getWorkItemRecords(id)
        // Discussion about appending 2 slices: https://stackoverflow.com/questions/16248241/concatenate-two-slices-in-go

        // This is how to append 2 arrays
        records = append(records, r...)
    }

    // A dictionary (hash table) with
    // key: WorkItemID (or name if cannot find the ID) - simply a string
    // value: total time spent
    // More detail about map here: https://blog.golang.org/go-maps-in-action
    statsMap := make(map[string]Stats)

    // Regular expression to extract ID (all numeric characters)
    workItemIdExp := regexp.MustCompile("[\\d]+")

    var id string

    for _, r := range records {
        timeSpent := r.ToDateTime.Sub(r.FromDateTime).Hours()
        if timeSpent < 0 {
            // The record does not have an end time
            continue
        }

        id = workItemIdExp.FindString(r.Detail)

        if id == "" {
            id = r.Detail
        }

        ts, exist := statsMap[id]

        if !exist {
            ts = Stats{id, r.Detail, 0}
        }
        ts.TimeSpent += timeSpent
        statsMap[id] = ts
    }

    var workingHours float64 = 0
    for key, value := range statsMap {
        workingHours += value.TimeSpent
        fmt.Printf("%s (%s) %f\n", key, value.WorkItemName, value.TimeSpent)
    }

    fmt.Printf("Working hours: %f\n", workingHours)
}

What are my further steps from this exercise?

  1. Append 2 arrays (slices) with "…" syntax—append(records, r...)
  2. Hash table (dictionary like) with mapmap[string]int means a dictionary with key is a string and value is an integer
  3. Regular expression with regexp package—regexp.MustCompile("[\\d]+")
  4. For each loop with the rangefor _, r := range records

A happy weekend!

Gowhere – http

I Go (went) a step further—http and other things to consume an API service. What would it take to call an API which returns a list of records in JSON? In .NET, it takes a few lines of code.

Scenario: Display a list of employees—Id, First Name, Last Name, and Joined Date—from a protected API—of a company. The returned value might contain more fields than necessary.

  1. Id: integer
  2. First Name and Last Name: string
  3. Joined Date: Date Time

First thing first, create a file http.go and write some code. To work with http, Go supplies the net/http package.

package main

import (
    "fmt"
    "net/http"
)

func main() {
    fmt.Println("Connecting to the API ...")
    const url = "https://xxxcompany.com/api/employees"

    const accessToken = "base64 access token"

    fmt.Println("Base Address: ", url)
    fmt.Println("Access Token: ", accessToken)

    // Issue a default request but will not work because of the missing access token
    resp, err := http.Get(url)
    if err != nil {
        fmt.Println("Cannot connect the API: ", err)
        return
    }

    // Close the body at the end of the execution
    defer resp.Body.Close()
}

Nothing’s fancy! I took them from the Go http package. When invoking a HTTP call, Go returns a response with an error if there is a connection problem. Go suggests that we must always check for error before usage—a good practice.

The above code will return a 401 status code—Not Authorized. I need to attach the access token to the request. To manipulate the request, I need to create it by myself and ask Go to send it. It is quite easy.

    // Create a custom request with custom headers
    resq, err := http.NewRequest(http.MethodGet, url, nil)
    resq.Header.Add("x-access-token", accessToken)
    // Send the request using the default client supplied by the http
    resp, err := http.DefaultClient.Do(resq)

    if err != nil {
        fmt.Println("Cannot connect the API: ", err)
        return
    }

What did I get from resp.Body? A binary stream.

binary, error:= ioutil.RealAll(resp.Body)

But I need a list of employees which is in JSON format. Go gives me the encoding/json package to decode from binary, represented data in JSON format, to object—struct in Go. So I define an Employee struct—custom data type—to hold the result. The Employee struct has JoinedDate which is a date time—time package is supplied to deal with time.

import (
    "encoding/json"
    "fmt"
    "net/http"
    "time"
)

// Existing code

// Employee
type Employee struct {
    Id         int
    FirstName  string
    LastName   string
    JoinedDate time.Time
}

And it’s time for gardening—decode the binary stream into list of employees

    // Create a decoder with passing the io reader from resp.Body
    decoder := json.NewDecoder(resp.Body)
    // Prepare an empty array of employees
    employees := make([]Employee, 0)
    // Decode, pass the pointer to the employees
    decoder.Decode(&employees)
    // Print the result
    fmt.Println(employees)

Put them all together, I have a working program. Run go run http.go and feel good.

package main

import (
    "encoding/json"
    "fmt"
    "net/http"
    "time"
)

func main() {
    fmt.Println("Connecting to the API ...")
    const url = "https://xxxcompany.com/api/employees"

    const accessToken = "base64 access token"

    fmt.Println("Base Address: ", url)
    fmt.Println("Access Token: ", accessToken)

    // Create a custom request with custom headers
    resq, err := http.NewRequest(http.MethodGet, url, nil)
    resq.Header.Add("x-access-token", accessToken)
    // Send the request using the default client supplied by the http
    resp, err := http.DefaultClient.Do(resq)

    if err != nil {
        fmt.Println("Cannot connect the API: ", err)
        return
    }

    // Close the body at the end of the execution
    defer resp.Body.Close()

    // Create a decoder with passing the io reader from resp.Body
    decoder := json.NewDecoder(resp.Body)
    // Prepare an empty array of employees
    employees := make([]Employee, 0)
    // Decode, pass the pointer to the employees
    decoder.Decode(&employees)
    // Print the result
    fmt.Println(employees)
}

// Employee
type Employee struct {
    Id         int
    FirstName  string
    LastName   string
    JoinedDate time.Time
}

Go where? One step further.

  1. 3 new packages: net/http, encoding/json, time
  2. Create custom http requests
  3. Define a new type via struct—class in C#
  4. Decode from binary stream—JSON data—to an array of object
  5. Use make method to create an object from a type

It makes my day, especially for the weekend!

Gowhere

I started some Go code. "Go where?", I wondered. For some mysterious reasons, I liked the term Gowhere. Let’s create a repository on GitHub to play with Go (Golang)—Gowhere.

Heading to Golang official site, following the instruction, I started to write code in no time. It is really fast and easy to get started. Let’s start with the infamous "Hello World!" application.

Installing Go is easy and straightforward

  1. Download the installation package from the official website, choose the one for your OS. I installed for both MacOS and Windows
  2. Run the package and follow the instruction—Next and Next and Done
  3. Open the command line, run this command go version

I use VS Code, highly recommended, as my code editor. One I created my first Go file and opened with VS Code, it suggests all the plugins I need to be productive with Go. Nice!

So far, I’ve known Go in a single file—write code in a file, no executable package. To run a Go file, I ran go run filename.go in the terminal—either inside the VS Code or Command Line or PowerShell or MacOs terminal.

Create hello.go and try it out

package main

import (
    "fmt"
)

func main() {
    fmt.Println("Hello World! Greeting from Go")
}

Neat and straightforward. There are 3 parts—package, import, and func.

Package – define the namespace (in C#) or a higher logical abstraction for other functions, methods. Which means that every Go file requires a package definition.

import – import functions from other packages.

func – Define a function. In the above example, main is the entry point function.

What if I changed the names—package name to main1 or function name to main_v1? – Failed. The compose main_main is strictly required.

The bracket ({) must be in the same line with the function name. This code will cause compile error

func main() 
{
    fmt.Println("Hello World! Greeting from Go")
}

All those errors are caught at the compile time—Go is a statical language. With the help from VS Code, I do not have to remember or worry about them. I will remember them when I write more Go code.

Go has a set of data types—just like any other languages. What are new to me are the Defer and Pointer. I knew pointer back in the university. Since then I have not used them. Thank to the C#.

Defer allows developers to specify callback functions to be executed in Last In First Out (LIFO) order once the main function completes its execution. I’ve not understood what it is used for—maybe to clean up resources. But I think it is an important concept in Go. I will find out more when I step further.

"How do I pass the returned value from the host method to deferred methods?", I wondered. I figured one possible solution using pointer.

Pointer holds the address of a variable. Which allows the consumers access the latest value of a variable—I know your address, I will go there and get it. Sometimes, It is a dangerous thing, better use with care.

Defer functions are also functions. Therefore it is fine to write defer in defer as the below example.


func deferCallWorkLikeCallBack() {
    // Is it possible to pass the returned value from calling method to the deferred function?
    var number int = 1
    // This will pass 1 to the deferred method. So not a reference
    defer deferInDefer(number)
    // This will pass the latest number, which is 13, to the deferred function
    // Pass the address (pointer) of the number variable
    defer deferWithPointer(&number)
    fmt.Println("Hi I am a deferred call. You can think of me as a callback in your word")
    number = 13
}

func deferInDefer(number int) {
    fmt.Println("Hi you passed the number:", number)
}

func deferWithPointer(number *int) {
    // This allows the function to access the latest value passed from the calling function
    fmt.Println("Hi you passed the pointer to number:", *number)
}

If I pass a pointer to a deferred function, I should be able to access the latest value of a variable.

package main

import (
    "fmt"
)

func main() {
    defer deferCallWorkLikeCallBack()
    fmt.Println("Hello World! Greeting from Go")
}

Will produce this output

Hello World! Greeting from Go
Hi I am a deferred call. You can think of me as a callback in your word
Hi you passed the pointer to number: 13
Hi you passed the number: 1

That’s a good start. I might go somewhere.

Build a Search Box in Web Application with Bootstrap

What could it take for a C# developer (backend developer) to implement a simple, typical search box in a web application?

This is what I wanted to build

Search Box

Let’s find out. So I created a web application with .NET Core 3.0 using the template provided by the .NET Core CLI.

dotnet new webapp

The command line creates a new Web Application (with Razor Pages, Not MVC template).
And I want to have a typical search box with an input field and a search icon. For many web developers, It is a trivial task. I thought so as well. So I had this code in the Index.cshtml

@page
@model IndexModel
@{
    ViewData["Title"] = "Home page";
}

<div class="text-center">
    <h1 class="display-4">Welcome</h1>
    <p>Learn about <a href="https://docs.microsoft.com/aspnet/core">building Web apps with ASP.NET Core</a>.</p>
</div>
<form method="Get">

<p>I want to have a search box</p>
<br />
<div class="form-group">
    <div class="input-group">
        <input type="search" class="form-control" value="" width="200" />
        <span class="input-group-btn">
            <button class="btn btn-default">
                <i class="glyphicon glyphicon-search"></i>
            </button>
        </span>
    </div>
</div>

</form>

Bootstrap 4 – Remove Glyphicons

For those who are new to CSS/HTML, it is a typical form using Bootstrap. All classes are from Bootstrap.

Press F5 and wait for the nice result. The result? A search box without the search icon. Oh man! what have I done wrong? When things go wrong, ask your friend Google. Looking for Glyphicons on Bootstrap brings me to this removed components in Bootstrap 4. The Glyphicons is on the removed list.

It turns out that the project created in .NET 3.0 comes with the latest Bootstrap version

/*!
 * Bootstrap v4.3.1 (https://getbootstrap.com/)
 * Copyright 2011-2019 The Bootstrap Authors
 * Copyright 2011-2019 Twitter, Inc.
 * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE)
 */

Welcome to Font Awesome

So the suggested alternative is Font Awesome. I was totally new to these stuff. So I went to the Font Awesome Site and followed along.

Font Awesome comes with free and paid collections. I made sure to go with the free one, download them from the download page. I just wanted to play around with CSS, so I chose the Solid style.

To know which icons are available for free, visit solid style explorer

I put them all in wwwroot\lib\font-awesome (same level with bootstrap folder). Let’s use font awesome instead of glyphicon

<div class="form-group">
    <div class="input-group">
        <input type="search" class="form-control" value="" width="200" />
        <span class="input-group-btn">
            <button class="btn btn-default">
                <i class="fas fa-search"></i>
            </button>
        </span>
    </div>
</div>

The difference is at the i tag

<i class="fas fa-search"></i>

Visit font awesome basic use to know about style prefix for different licenses and styles.

And the last pieces, include font awesome css files. In my case, it is in _Layout.cshtml

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="utf-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>@ViewData["Title"] - WebApp</title>
    <link rel="stylesheet" href="~/lib/bootstrap/dist/css/bootstrap.css" />
    <link rel="stylesheet" href="~/css/site.css" />
    <link rel="stylesheet" href="~/lib/font-awesome/css/fontawesome.css" />
    <link rel="stylesheet" href="~/lib/font-awesome/css/solid.css" />
</head>

Require 2 files: fontawesome.css and solid.css

And that how I have my search box with additional learning along the way. I now know a bit or two about CSS and Font Awesome. Definitely I will read more about them and create some simple forms. This kind of learning fits well with the 1-hour time-boxed.

Python – Ways of Handling Exceptions

Another step in my Python journey, this time I learn about exceptions – raising and handling exceptions. The syntax is simple. I can reason about them because they look familiar with C#.

try:
    // Do something
except ValueError as e:
    // Do something
except KeyError:
    // rethrow the original exception
    raise
finally:
    // Clean up. Code that will run regardless of

What interesting is the Python philosophies about exceptions:

  1. Avoid creating your own custom exceptions if possible. Encourage to use the built in ones.
  2. Error codes are silence, exceptions are not. It is more about design/code principle. Instead of returning an error code, let the application throw exceptions or fail.
  3. Handle exceptions that your function are expecting or capable of.

I have seen applications where custom exceptions are defined. If well-designed, they serve good purpose. Otherwise, they cause some trouble – lost in translation

  1. Stack trace not preserved
  2. Consumers catch custom exceptions and it is hard to handle it correctly. Why? How could it know what the actually root cause? – the inner exception part.

While writing this, a retrospective moment came up in my head. I have not seen, I have not documented much, the exceptions in code. Except framework code, where they always document what exceptions might be raised. In business applications, for applications I have seen/written so far of course, we do not have a habit of documenting possible exceptions.

Let weight it as essential habit and practice.

So what have I learn to help me becoming a better developer?

  1. Before writing your own exceptions, search and use the common, well-designed, existing ones.
  2. Document your API with possible exceptions. Eventually, exceptions are also output from your API. Consumers need to be aware and have a plan to handle them properly.
  3. Custom exceptions must be designed with care.