Thursday, April 7, 2022

My takeaway from SHARE Dallas 2022 Hackathon for IBM Z - Part 1

Hiya! 👋

It's been some time since I wrote on my blog. Recently, I participated in SHARE Dallas 2022 Hackathon that happened between Mar 28, 2022 and Mar 29, 2022 and it was a great experience that I wanted to treasure. Hence this post 😀.

This Hackathon showed the modern ways of working on a Mainframe and it, for sure, fascinates the Mainframers as well as those who think of IBM Mainframes as legacy and old stuff.   

⚠ Before we start, I would like to let you know that this is going to be a long post because the takeaway was pretty huge for me. So, I'll be writing couple of posts to cover the entire experience (this being the first one)

The Hackathon was made available to participate for virtual attendees as well as in-person attendees of SHARE Dallas 2022 Event and no registration cost was involved. I participated virtually from India. 

How things began on the day of event? 

I received an email from the Hackathon's organizers with instructions to install the necessary tools  (given below): 

Discord - Most of the communication with organizers and fellow Hackers took place on Discord. 

VSCode with Zowe Explorer extension - This is an IDE. Zowe Explorer extension on VSCode was used throughout the Hackathon to interact with Mainframe and even with an IoT device. 

Postman - This tool allows to easily test and call REST-based APIs and web-enabled microservices. Though this tool was optional to install, I had it installed on my system and used it to GET and POST some API's. This gave an outlook on the structure of response after posting a request (more about this in the later part of this post) 

OpenVPN - The Hackathon wanted our personal system to be connected to the Montpellier VPN to access the Mainframe. 

In another email from the Organizers, I received my Credentials for the VPN and Z System. There was also a link provided in the same email to access the Challenge documentation. 

I just grabbed a coffee 🥤 and started reading out the 40 page Challenge documentation. 

The Challenge 💪

We were walked through the Catalog Manager Application running inside CICS and written in COBOL. The application had three basic functions:
  • Display all the items in a catalog 
  • Display detailed information of an item
  • Order an item.
There were some screenshots on the documentation which showed the CICS Screen interface to access the Catalog Manager application and its functionalities.

CICS Interface showing the Main Menu of the Catalog Manager Application.

CICS Interface showing the list of items in the Catalog, when '1. List Items' is selected from the Main Menu.

CICS Interface showing the detailed information of an item when an item is selected from the previous screen. 

CICS Interface showing the status of the order at the left side bottom of the screen. 

The overall challenge was to re-build this application using Python on z/OS and REST API's. The transactions still be handled by the CICS Transaction sever as they had enabled a technology (z/OS Connect Enterprise Edition) on the Hackathon LPAR to make the CICS functions available by means of REST API's. Don't fret if all these jargons doesn't make too much sense right now. You'll understand once they are unfolded in the later part of this post. 

Hacking through the tasks for Day 1

Task 1:

After connecting to the Montpellier VPN and setting up the profile for Hackathon in Zowe Explorer on VS Code, I started doing the first task, which was to implement the Traditional "Hello World" application. I used the Terminal to type Unix commands to create a new file (helloWorld.py) on the home directory of Unix System Services. 
There is a UNIX interface within z/OS called UNIX System Services, or USS, so you can log in through ssh and hammer out commands.

For writing the Python code, I used the IBM Z Open Editor instead of Unix terminal. To view/edit the files on IBM Z Open Editor, all you need to do is to locate the file that you just created, from Unix System Services (USS) section under Zowe, and click on it. The contents of the file will be shown on the right side on IBM Z Open Editor. 

Viewing the contents of helloWorld.py file on IBM X Open Editor.

I just wrote a one line command using the print() function in Python to display a message. To finish the Task 1, I used the python3 command on the Terminal of VS Code to run the Python script I had created. 

Output displayed after invoking the Python script using python3 command from USS Terminal.

It is essential that we get inside the directory where our Python code is saved, before running the code with the python3 command. 

This marked the completion of Task 1 ✅ 

Task 2:

To take things to the next level, Task 2 was all about creating our First web application on z/OS. I was introduced to the usage of Flask on Python. 

Flask is a popular web framework and it provides functions on Python for rendering an HTML page and handling incoming HTTP requests. 

I installed Flask on Python using the pip3 command. Post installation, I created a new directory under my Home directory (/u/ztecu85) and named it as webApp. Under webApp directory, two sub-directories namely static and templates were created. 

webApp directory is where all my project's files resided. As the challenge unfolded, we had to create some HTML files and those were stored under the templates sub directory. 

To complete the task, I created a new file named as webserver.py under webApp directory and wrote the code as shown below. 


Let me walk you through some of the important sections of this code.

from flask import Flask - This line imports Flask module to the project. 

app = Flask(__name__) - __name__ is a special variable in Python which returns the name of the current module (webserver.py).  This line of code helps Flask look for the relevant files needed by the application, such as static and template files. Flask manages to find the root path based on the value stored in __name__

@app.route('/') - This tells the Flask which URL should call the user defined Python function, index()that is defined in the next line. By passing '/' as an argument to the route() function of the Flask Class, we're binding the URL so that when we load the URL on the browser, we will be greeted with a message returned by index() function. 

As we have associated the URL - 10.3.20.138 and Port - 24320 with the index() function defined in Python through app.run() method, this 👇 is what happened when I ran this code from the Unix Terminal. 


Upon the execution of the code, there were several messages shown on the Terminal. One message showed a URL where the application was running. Pressing Ctrl + Clicking on the link took me to the URL where the message returned by the index() function was displayed. 

This task was a nice twist to the traditional 'Hello World' program that we printed the same message on a web application. 

Task 3: 

This task introduced me to the CICS part. As mentioned earlier, the three CICS functions were made available to us by means of three REST API's. The Challenge documentation for this task contained a table with detailed description of the API's. 

Though testing these API's were optional, I used the Postman tool to post the API's with the URL given for each function and receive responses to get familiarized with them. 

Following are the GIF's showing the responses received on Postman after posting the three API's. 

1. API to list all the items in Catalog

HTTP Verb:  GET

URL given to access this service: http://10.3.20.1:50780/catalogManager/v1.0/items?startItemRef={ }

Note that there were 21 items in the Catalog and each item had a unique Item Reference number (itemRef), starting from # 0010 up until 0210, in the increment of +10. The curly braces (highlighted in Red color) at the end of the URL means that it accepts a query parameter, which is an Integer between 10 and 210 (in the increment of +10). In the following GIF, I've assigned 10 to startItemRef at the end of the URL, so that the CICS application use the startItemRef value to get the next 15 items from the Catalog. 



2. API to get the detailed information on one Item:

HTTP Verb: GET

URL given to access this service: http://10.3.20.1:50780/catalogManager/v1.0/items/{ }

Posting this URL fetched the detailed information on one Item with itemRef that we have passed as query parameter at the end of the URL (The curly braces highlighted in Red color at the end of the URL means that it accepts query parameter). 



3. API to Order an Item:

HTTP Verb: POST

URL given to access this service: http://10.3.20.1:50780/catalogManager/v1.0/orders 

This service was different from the other two that while posting this service, we had to send along a Request in JSON format. This Request contained information about the item that we wish to order and the User ID with which we are ordering. CICS Application used this information to update the Catalog and pass the Order's status message. 

Upon posting the URL, following response was received.  


Task 3a:

In this task, we were asked to invoke the API, that list items from the Catalog, using Python. As the response had only 15 items, participants were asked to use the information in the lastItemRef key to create some code in Python that lists all 21 items. FYI, each Item in the response would have a lastItemRef key that holds the value of the last item's reference number. So, the idea was to post the API and get the first response. Then, make use of the lastItemRef key value from the 15th item of the first response and use this value as a query parameter and create the URL to be posted for the second response. 

It was required to combine the two results from the API calls. A clue that was given to the participants was to convert the responses into a Python Dictionary with the json() method; then add two dictionaries together using the extend() method. 

This is where I got stuck 😕 and couldn't get past this task on the Day 1. I slept over this problem and was able to fare better on the Day 2.

Hacking through the tasks for Day 2

Task 3a (continued): 

The response after posting the  API to list the items in the Catalog looked like the below:

 {  
   "data": {  
     "returnCode": 0,  
     "responseMessage": "+15 ITEMS RETURNED",  
     "inquireCatalog": {  
       "startItemRef": 10,  
       "lastItemRef": 150,  
       "items": [  
         {  
           "itemRef": 10,  
           "cost": "089.90",  
           "onOrder": 0,  
           "description": "Man's Waterproof Rain Jacket",  
           "department": 10,  
           "stock": 989  
         },  
         {  
           "itemRef": 20,  
           "cost": "089.90",  
           "onOrder": 99,  
           "description": "Women Waterproof Rain Jacket",  
           "department": 10,  
           "stock": 996  
         },  
         {  
           "itemRef": 30,  
           "cost": "040.90",  
           "onOrder": 0,  
           "description": "Sunglasses",  
           "department": 10,  
           "stock": 992  
         },  
         {  
           "itemRef": 40,  
           "cost": "079.90",  
           "onOrder": 0,  
           "description": "Windy Umbrella",  
           "department": 10,  
           "stock": 996  
         },  
         {  
           "itemRef": 50,  
           "cost": "004.99",  
           "onOrder": 0,  
           "description": "Hot Coffee",  
           "department": 10,  
           "stock": 990  
         },  
         {  
           "itemRef": 60,  
           "cost": "004.99",  
           "onOrder": 40,  
           "description": "Hot Chocolate",  
           "department": 10,  
           "stock": 999  
         },  
         {  
           "itemRef": 70,  
           "cost": "049.99",  
           "onOrder": 20,  
           "description": "Woman swim suit with fancy colors",  
           "department": 10,  
           "stock": 992  
         },  
         {  
           "itemRef": 80,  
           "cost": "049.99",  
           "onOrder": 0,  
           "description": "Man's swim suit also with fancy colors",  
           "department": 10,  
           "stock": 998  
         },  
         {  
           "itemRef": 90,  
           "cost": "019.99",  
           "onOrder": 0,  
           "description": "Thermoflask double stainless steel",  
           "department": 10,  
           "stock": 996  
         },  
         {  
           "itemRef": 100,  
           "cost": "009.90",  
           "onOrder": 20,  
           "description": "Icecream Family Pack",  
           "department": 10,  
           "stock": 994  
         },  
         {  
           "itemRef": 110,  
           "cost": "004.40",  
           "onOrder": 0,  
           "description": "Iced Tea",  
           "department": 10,  
           "stock": 995  
         },  
         {  
           "itemRef": 120,  
           "cost": "025.99",  
           "onOrder": 0,  
           "description": "Mineral Sunscreen",  
           "department": 10,  
           "stock": 997  
         },  
         {  
           "itemRef": 130,  
           "cost": "014.85",  
           "onOrder": 0,  
           "description": "Rain Cap Size Small",  
           "department": 10,  
           "stock": 998  
         },  
         {  
           "itemRef": 140,  
           "cost": "014.85",  
           "onOrder": 0,  
           "description": "Rain Cap Size Medium",  
           "department": 10,  
           "stock": 999  
         },  
         {  
           "itemRef": 150,  
           "cost": "014.85",  
           "onOrder": 45,  
           "description": "Rain Cap Size Large",  
           "department": 10,  
           "stock": 996  
         }  
       ]  
     }  
   }  
 }  

Per Python, this data is structured as a nested Dictionary. 

💡 Python Dictionaries store data values in key:value pair and are written with Curly braces. 

"data" is a dictionary with three key:value pairs viz. "returnCode", "responseMessage" and "inquireCatalog". 

"inquireCatalog" itself is a Dictionary with three key:value pairs (again 😐). Make note of the third key, "items" for the value of this key is a Python List of all the 15 items of the response. 

💡 Python Lists are created using square brackets and are used to store multiple items in a single variable. 

Responses from the two API calls resulted in two Python Dictionaries but I was not able to combine these dictionaries together. 

Discord came as savior 🙏. I posted this issue there and the organizers quickly helped me understand that most of the info from the Dictionary won't be used besides to make sure the request went well and the focus should be only on the Python List with the details of the items in the Catalog. 

I created a new file named as webRequestHandler.py under the webApp directory and wrote the following code to get the result needed to mark this task as complete ✅.


I've added comments to each line of the code for your understanding. Still, I would like to highlight few sections of the code. 

Line #13 is used to create a Python dictionary called header which stores the authentication token given by the Organizers of Hackathon. Requests to the back-end systems only with this Auth token are deemed as legal. Without the Auth token, we will not be able to send/post API's and get the responses from the back-end systems. 

This 👇 is what happened after running this Python script. 



The output was a Python List of all the 21 items that were on the Catalog and this marked the task as complete ✅.


In the next task, we will make this response look nicer 👌 using Flask HTML rendering capabilities. 

I'm hitting the pause button on this experience journey for now. Remaining tasks will be covered on the next post titled as "My takeaway from SHARE Dallas 2022 Hackathon for IBM Z - Part 2". Keep watching this space 👀

Thanks for reading! Should you have any questions/feedback, please post them in Comments section below. 


References:
1. Challenge Documentation provided for the Hackathon. 
2. GeeksforGeeks, Programiz and W3Schools for Python related information.

Screenshot/GIF courtesy: Mainframe access obtained for SHARE Dallas 2022 Hackathon for IBM Z.