Working with a REST API: the GitHub case study

Glamorous Toolkit allows you to work with REST APIs out of the box. Here, we take a look at how we can play with the GitHub REST API. Although the case study is specific to GitHub, the approach is essentially the same when working with other REST APIs. If you prefer a video introduction, have a look at Exploring the GitHub REST API in 7' on YouTube.

A REST API allows you to download information about a software system in some format, typically JSON. The raw data is interesting, but cumbersome to work with. The moldable development approach in GT for turning this data into an “explainable system” is to model the domain by taking the raw data and wrapping it into domain entities. Then, we iteratively add tiny, contextual tools to these entities ( i.e., contextual views, actions, and searches) that make the domain entities into a live, explorable model answering questions about the domain — an explainable system . In the case of the GitHub REST API, we obtain raw JSON data about an organization in GitHub (such as feenk), about its repos , their contributors , their commit events , and so on. We turn each of these into live, explorable domain entities by wrapping the raw data and adding contextual tools. Let's see this in action...

GitHub offers a REST API to explore data about the repositories hosted by any given organization. As a case study, we start from JSON content available at https://api.github.com/orgs/feenkcom, which looks like this:

The first thing we do is retrieve that JSON programmatically:

url := 'https://api.github.com/orgs/feenkcom'.
  
json := ZnClient new get: url.
  

Now that's just a string. We never really want to work with a string. We'd rather parse it:

dictionary := STONJSON fromString: json.
  

Parsing the JSON string produces a dictionary. That's already better, but still not quite what we want. We want a custom experience for that outcome. To this end, we wrap the data as a first-class object:

GhOrganization new rawData: dictionary.
  

Once we have a dedicated object, the inspector gets the opportunity to present contextual views that answer questions we have about the domain. What we see below is an object Inspector view of the organization instance, decorated with a number of tiny, contextual tools. For example, we would like to know which repositories belong to the feenk organization, and we can see that in the first, Repositories view.

Now, try exploring. For example, what happens when you double-click on a repository? Notice how you can navigate from the feenk organization to its repositories , and from there to individual contributors , their GitHub events , and issues . Each of these entities is just a wrapper around data that has been dynamically retrieved from GitHub using its REST API.

The process of wrapping data to create live, domain objects and then adding contextual tools to answer questions is called Moldable Development. The specific development practices can be understood as a number of Moldable Development patterns. In order to create these tiny, contextual tools, you need a Moldable Object, such as a GhOrganization GhEntity << #GhOrganization slots: {}; tag: 'Model'; package: 'GToolkit-Demo-GitHubAPI' instance. One way to create such a moldable object is to create a Moldable Data Wrapper around some existing, raw data, which is exactly what we have done here.

Now, what about the tools? There are many interesting tools that you can create, but the most common kind within GT is a Contextual View, such as the Repositories view above. We call the process molding , because we mold the object inspector to display a special view when we inspect a GitHub organization object, rather than just showing the default Raw view. Click above on the Raw view to see what that looks like. Looks familiar, right? That's pretty much what you see in a standard IDE when you inspect an object within a debugger. Instead we have molded the organization object so the inspector shows us more interesting views.

How is a contextual view defined? It is just a method of the class GhOrganization GhEntity << #GhOrganization slots: {}; tag: 'Model'; package: 'GToolkit-Demo-GitHubAPI' that contains a special pragma (or annotation), namely <gtView>. The Repositories view is defined in GhOrganization>>#gtReposFor: gtReposFor: aView <gtView> ^ aView columnedList title: 'Repositories'; priority: 40; items: [ self repos ]; column: 'Repo' text: #name; column: 'Description' text: #description; column: 'Open issues' text: #numberOfOpenIssues; column: 'License' text: #licenseName

All view methods look pretty similar: there is a method named gt*Something*For: with aView as an argument. The method has the <gtView> pragma so the inspector will find it. Then we send a factory message to the view to create a particular kind of view, in this case a columnedList. See Inspector views for other examples. Then we give the view a title , namely Repositories, we give it a priority , so the inspector knows the order in which to show the views, and we have a bunch of further messages to configure the view. Here we just define the items and columns to display.

NB: You can always find the source code behind a view by secondary-clicking ( i.e. , OPT- or ALT-clicking) on the view's name tab. Try it on the views above!

The next most common contextual tools are contextual actions and searches . Select one of the repositories above, for example, gtoolkit. You will see two additional buttons at the top right. If you hover over them, you will see their descriptions, Open in web browser and possibly Go to repository . (The latter won't be visible unless the repository is loaded in your image.) These buttons are examples of a Contextual Action, an action that is only available in the specific context of the object of interest. Try the web browser button. Now secondary-click on the button to see its source code. Notice how similar it is to a contextual view: it consists of just a few lines of code, and it has a dedicated pragma, <gtAction> instead of <gtView>.

In the organization object you will also find a search tool at the top right, with a magnifying glass icon 🔍. If you click on it, you can perform a Contextual Search restricted to repositories in the feenk organization. Try it.

The source code for this tool is defined in GhOrganization>>#gtSpotterForRepoNames: gtSpotterForRepoNames: aSearch <gtSearch> ^ aSearch list title: 'Repositories'; priority: 5; items: [ self repos ]; itemsLimit: Float infinity; itemName: #name; sendCategory: [ :aCategory :aStep :thisSearch | GhRepos new repos: aCategory items contents ]; filterBySubstring

Just like the other tools, it is a dedicated method of the organization class, and has a pragma, this time <gtSearch>. NB: if you secondary-click on the search button, you will find the code of the search action, not the code of the search itself. To get the latter, click on the search button first, then secondary-click on the Repositories section.