GitHub Metric Microservices Deployment on AWS using ECR - Part 2 - Issue Service & PR Service
Tags: aws, docker, kubernetes, python
Published: 2023-11-10 06:11:22

In Part 1, we looked at setting up the AWS configurations and the implementation of Commit Service. Let's look at how the rest of the microservices are implemented. This is a lengthy part so make sure to read until the very end and move to part 3.

### Issue Service
This service fetches the user’s connected issues and saves them to the DynamoDB table – `deviq_issues` and returns the issues in order – Issues Created by user, issued assigned to the user and issues closed by the user. This service runs on port 5021.

`issueService.py`
```python
import requests
import issueServiceUtil
import boto3

from flask import Flask

issueApp = Flask(__name__)

@issueApp.route('/')
def index():
    return "Hello! from Issue Service"

session = boto3.Session(region_name='<aws_region')
dynamodb = session.resource('dynamodb')
table_name = '<table_name>'
table = dynamodb.Table(table_name)



@issueApp.route('/issues/<username>')
def get_user_issues(username):
    json_array_response = []
    maxId = getPid.getMaxPid()
    token = getPid.get_gh_token()
    # Get issues created by the user
    created_url = f"https://api.github.com/search/issues?q=author:{username}"
    created_response = requests.get(created_url, headers={"Authorization": f"token {token}"})

    if created_response.status_code == 200:
        created_issues = created_response.json()['items']
        print(f"Issues created by {username}:")
        for issue in created_issues:
            table.put_item(Item={
                'pid':maxId,
                'gh_username': username,
                'issue_title': issue['title'],
                'url':issue['html_url'],
                'type': 'CREATED'
                })
            maxId+=1
            print(f"- {issue['title']} ({issue['html_url']})")

        json_array_response.append(created_issues)
        
    else:
        print(f"Error: {created_response.status_code}")

    # Get issues assigned to the user
    assigned_url = f"https://api.github.com/search/issues?q=assignee:{username}"
    assigned_response = requests.get(assigned_url, headers={"Authorization": f"token {token}"})

    if assigned_response.status_code == 200:
        assigned_issues = assigned_response.json()['items']
        print(f"Issues assigned to {username}:")
        for issue in assigned_issues:
            table.put_item(Item={
                'pid':maxId,
                'gh_username': username,
                'issue_title': issue['title'],
                'url':issue['html_url'],
                'type': 'ASSIGNED'
                })
            maxId+=1
            print(f"- {issue['title']} ({issue['html_url']})")
        json_array_response.append(assigned_issues)
    else:
        print(f"Error: {assigned_response.status_code}")

    # Get closed issues by the user
    closed_url = f"https://api.github.com/search/issues?q=author:{username}+is:closed"
    closed_response = requests.get(closed_url, headers={"Authorization": f"token {token}"})

    if closed_response.status_code == 200:
        closed_issues = closed_response.json()['items']
        print(f"Closed issues by {username}:")
        for issue in closed_issues:
            table.put_item(Item={
                'pid':maxId,
                'gh_username': username,
                'issue_title': issue['title'],
                'url':issue['html_url'],
                'type': 'CLOSED'
                })
            maxId+=1
            print(f"- {issue['title']} ({issue['html_url']})")
        json_array_response.append(closed_issues)
    else:
        print(f"Error: {closed_response.status_code}")

    return json_array_response

if __name__ == '__main__':
    issueApp.run(debug=True, host='0.0.0.0', port=5021)
```

Quick peek into `issueServiceUtil.py`

```python
import boto3
import json

# Initialize a DynamoDB client
dynamodb = boto3.resource('dynamodb', region_name='<aws_region>')

# Specify the table name and column (attribute) name
table_name = '<table_name>'
column_name = '<key>'

# Initialize a DynamoDB table resource
table = dynamodb.Table(table_name)

# Perform a scan operation to get all items sorted in descending order by the specified column
response = table.scan()

def getMaxPid():
    # Check if any items were returned
    if 'Items' in response:
        items = response['Items']
        if len(items) > 0:
            max_value = max(item[column_name] for item in items)
            print(f"The maximum value for {column_name} is: {max_value}")
            return max_value + 1
        else:
            print(f"No items found in the table")
            return 1
    else:
        print(f"Error in scanning the table")
        return -1
    

def get_gh_token():
    secret_name = "<SECRET_NAME>"
    region_name = "<aws_region>"
    session = boto3.Session()
    client = session.client(service_name='secretsmanager', region_name=region_name)
    get_s = client.get_secret_value(SecretId = secret_name)

    secret = get_s['SecretString']
    ss = json.loads(secret)
    return ss['github_token']
```

`deployment.yaml`

```yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: issueservice-deployment
spec:
  replicas: 1
  selector:
    matchLabels:
      app: issueservice
  template:
    metadata:
      labels:
        app: issueservice
    spec:
      containers:
      - name: issueservice
        image: <ecr_image_id:tag>
        ports:
        - containerPort: 5021
```

`service.yaml`

```yaml
apiVersion: v1
kind: Service
metadata:
  name: issueservice-service
spec:
  selector:
    app: issueservice
  ports:
    - protocol: TCP
      port: 5021
      targetPort: 5021
  type: LoadBalancer 
```

### PR (Pull Request) Service
This service fetches the user’s assigned pull requests and saves them to the DynamoDB table – `deviq_prs` and returns those Pull Requests. This service runs on port 5069😉

`prService.yaml`

```python
import requests
from datetime import datetime
import boto3
import getPid

from flask import Flask

prApp = Flask(__name__)

date_format = "%Y-%m-%dT%H:%M:%SZ"

session = boto3.Session(region_name='<aws_region>')
dynamodb = session.resource('dynamodb')
table_name = '<table_name>'
table = dynamodb.Table(table_name)

@prApp.route('/')
def index():
    return "Hello! from PR Service"

@prApp.route('/pr/<username>')
def get_user_pullrequests(username):
    token = getPid.get_gh_token()
    repo_url = f"https://api.github.com/users/{username}/repos?type=owner"
    response = requests.get(repo_url, headers={"Authorization": f"token {token}"})
    prs =[]
    
    maxId = getPid.getMaxPid()
    if response.status_code == 200:
        repositories = response.json()
        for repo in repositories:
            repo_name = repo["name"]
            owner = repo["owner"]["login"]
            
            pr_url = f"https://api.github.com/repos/{owner}/{repo_name}/pulls?state=all"
            
            # Step 2: Get the list of pull requests for each repository
            response = requests.get(pr_url, headers={"Authorization": f"token {token}"})
            pull_requests = response.json()

            for pr in pull_requests:
                pr_created_at = pr["created_at"]
                pr_closed_at = pr["closed_at"] if pr["closed_at"] else pr["updated_at"]
                
                # Step 3: Calculate the resolution time for each pull request
                resolution_time = (datetime.strptime(pr_closed_at,date_format) - datetime.strptime(pr_created_at, date_format)).total_seconds() / 60
                print(f"Pull Request #{pr['number']} in {owner}/{repo_name} resolved in {round(resolution_time, ndigits= 3)} minutes.")

                table.put_item(Item={
                'pid':maxId,
                'gh_username': username,
                'PR No': f"Pull Request # {pr['number']}",
                'repo_name': f"{owner}/{repo_name}",
                'resolving time': int(round(resolution_time, ndigits= 3))
                })

                pr = {
                'PR No': f"Pull Request # {pr['number']}",
                'repo_name': f"{owner}/{repo_name}",
                'resolving time': int(round(resolution_time, ndigits= 3))
                }
                #pr_json = json.dumps(pr)
                
                prs.append(pr)
                maxId+=1

    else:
        return f"Error: {response}"

    return prs
if __name__ == '__main__':
    prApp.run(debug=True,host='0.0.0.0', port=5069)
```

and the `prServiceUtil.py`

```python
import boto3
import json
# Initialize a DynamoDB client
dynamodb = boto3.resource('dynamodb', region_name='<aws_region>')

# Specify the table name and column (attribute) name
table_name = '<table_name>'
column_name = '<key>'

# Initialize a DynamoDB table resource
table = dynamodb.Table(table_name)

# Perform a scan operation to get all items sorted in descending order by the specified column
response = table.scan()

def getMaxPid():
    # Check if any items were returned
    if 'Items' in response:
        items = response['Items']
        if len(items) > 0:
            max_value = max(item[column_name] for item in items)
            print(f"The maximum value for {column_name} is: {max_value}")
            return max_value + 1
        else:
            print(f"No items found in the table")
            return 1
    else:
        print(f"Error in scanning the table")
        return -1
    

def get_gh_token():
    secret_name = "<SECRET_NAME>"
    region_name = "<aws_region>"
    session = boto3.Session()
    client = session.client(service_name='secretsmanager', region_name=region_name)
    get_s = client.get_secret_value(SecretId = secret_name)

    secret = get_s['SecretString']
    ss = json.loads(secret)
    return ss['github_token']

```