Categories
Code

Step 3: Understanding WET code, DRY code, and Refactoring the low-level REST Adapter

This chapter is a bit of a side-bar conversation, but it’s an important one. If you’re still fairly new to programming, it’s time we introduce the concept of WET and DRY code as well as knowing when to take the time to refactor (clean-up) your code.

In the previous chapter, we wrote a simple low-level REST API Adapter. We hadn’t even passed 30 lines of code when it started to become apparent that our code was getting sloppy.

Here’s all 30 lines of our RestAdapter class so far:

import requests
import requests.packages
from typing import List, Dict

class RestAdapter:
   def init(self, hostname: str, api_key: str = '', ver: str = 'v1', ssl_verify: bool = True):
      self.url = f"https://{hostname}/{ver}"
      self._api_key = api_key
      self._ssl_verify = ssl_verify
      if not ssl_verify:
         # noinspection PyUnresolvedReferences
         requests.packages.urllib3.disable_warnings()

   def get(self, endpoint: str, ep_params: Dict = None) -> List[Dict]:
      full_url = self.url + endpoint
      headers = {'x-api-key': self._api_key}
      response = requests.get(url=full_url, verify=self._ssl_verify, headers=headers, params=ep_params)
      data_out = response.json()
      if response.status_code >= 200 and response.status_code <= 299:     # OK
         return data_out
      raise Exception(data_out["message"])

   def post(self, endpoint: str, ep_params: Dict = None, data: Dict = None):
      full_url = self.url + endpoint
      headers = {'x-api-key': self._api_key}
      response = requests.post(url=full_url, verify=self._ssl_verify, headers=headers, params=ep_params, json=data)
      data_out = response.json()
      if response.status_code >= 200 and response.status_code <= 299:     # OK
         return
      raise Exception(data_out["message"])

In the software engineering world, there is a concept of WET code and DRY code.

DRY stands for “Don’t Repeat Yourself”, while WET stands for “We Enjoy Typing” or “Write Everything Twice” (or Thrice!)

Good code is generally pretty DRY. Never repeating itself.
Sloppy code is often pretty WET. The same lines (or variations of the same lines) are often repeated over and over in different places.

Why is repeating code bad? Imagine having the same block of code repeated in 3 different sections of your program. Then imagine changing the logic to that block of code because you wanted to fix a bug or improve performance. Well, now you have to find all the instances of that code block and update it in all 3 locations.

When you first write out new code, you’ll often have WET code. And that’s both common and fairly normal. What is bad is if you decide not to refactor and leave it WET.

By choosing to Refactor your code, you can clean it up, tighten things in places and DRY it out. 😊 So let’s refactor our get and post methods.


What we’re going to do is create a more generic (and private) method called _do which can do all the same things as the get and post methods. If we look above at both of those methods, we can see that 5 of the 7 lines are exactly the same, so let’s copy those over.

def _do(self, endpoint: str, ep_params: Dict = None, data: Dict = None):
    full_url = self.url + endpoint
    headers = {'x-api-key': self._api_key}
    # some request line here
    data_out = response.json()
    if response.status_code >= 200 and response.status_code <= 299:     # OK
        return #something
    raise Exception(data_out["message"])

The only major difference between get and post are, well, that they’re HTTP GET and HTTP POST commands, so we need to take this into account. And it turns out that even the requests module has support for this. So let’s add a new parameter to _do method called http_method.

def _do(self, http_method: str, endpoint: str, ep_params: Dict = None, data: Dict = None):

And let’s add a new request line:

response = requests.request(method=http_method, url=full_url, verify=self._ssl_verify, headers=headers, params=ep_params, json=data)

So now our _do method should look like this:

def _do(self, http_method: str, endpoint: str, ep_params: Dict = None, data: Dict = None):
    full_url = self.url + endpoint
    headers = {'x-api-key': self._api_key}
    response = requests.request(method=http_method, url=full_url, verify=self._ssl_verify, headers=headers, params=ep_params, json=data)
    data_out = response.json()
    if response.status_code >= 200 and response.status_code <= 299:     # OK
        return data_out
    raise Exception(data_out["message"])

Now that we have a private and generic _do method, we can refactor our get and post methods to:

def get(self, endpoint: str, ep_params: Dict = None) -> List[Dict]:
    return self._do(http_method='GET', endpoint=endpoint, ep_params=ep_params)

def post(self, endpoint: str, ep_params: Dict = None, data: Dict = None):
    return self._do(http_method='POST', endpoint=endpoint, ep_params=ep_params, data=data)

Wow! Super simple and DRY, right? Hey, let’s implement the delete method, too!

def delete(self, endpoint: str, ep_params: Dict = None, data: Dict = None):
    return self._do(http_method='DELETE', endpoint=endpoint, ep_params=ep_params, data=data)

And so here we are now. A basic REST Adapter that has implemented GET, POST, and DELETE in only 30 lines of code!

This leads us to the next topic of Exception handling and Raising our own exceptions in the next chapter… Step 4: Exception Handling and Raising New Exceptions


Source code: https://github.com/PretzelLogix/py-cat-api/tree/03_refactor_rest_adapter

4 replies on “Step 3: Understanding WET code, DRY code, and Refactoring the low-level REST Adapter”

Leave a Reply

Your email address will not be published. Required fields are marked *