Skip to content

Commit

Permalink
Update 3 projects to use new webhooks (#60)
Browse files Browse the repository at this point in the history
Note to reviewers: due to the amount and complexity of changes, it's
better to review the final result instead of side-by-side with the
previous state. Otherwise you're comparing apples to oranges.

- Quickstart
- Google Forms to Jira (rewritten to use events instead of polling)
- Webhook to Jira (nee "create Jira issue")

Also includes readme updates.

Refs: ENG-1509
  • Loading branch information
daabr authored Sep 11, 2024
1 parent a247e29 commit 25c5ec2
Show file tree
Hide file tree
Showing 11 changed files with 250 additions and 202 deletions.
8 changes: 6 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,17 +7,20 @@ projects for:
- Composable templates for interoperability between common services
- Demonstrations of advanced system capabilities and features

Go to the [samples](https://github.com/autokitteh/samples) repository to find
more projects that demonstrate foundational system features, integration API
details, and recommended practices.

| Name | Description | Integrations |
| :------------------------------------------------------------------------------- | :--------------------------------------------------------------------------------------- | :------------------------------------------ |
| 🐍 [AWS Health to Slack](./aws_health_to_slack/) | Announce cloud health events based on a resource ownership mapping | AWS (Health), Google Sheets, Slack |
| 🐍 [Break-glass](./break_glass/) | Manage break-glass requests and approvals for temporary elevated permissions | Slack → AWS (IAM), Jira |
| 🐍 [Categorize emails](./categorize_emails/) | Categorize new emails and notify the appropriate channels based on the content | Gmail → ChatGPT → Slack |
| 🐍 [Confluence to Slack](./confluence_to_slack/) | Notify when a new page with a specific label is created | Confluence → Slack |
| 🐍 [Create Jira issue via webhook](./create_jira_issue/) | Create Jira issues with HTTP GET/POST requests | HTTP → Jira |
| 🐍 [Data pipeline](./data_pipeline/) | Process and store data from new S3 files in a database | AWS (SNS, S3) → SQLite |
| 🐍 [Discord to Spreadsheet](./discord_to_spreadsheet/) | Log Discord message events using AutoKitteh's event system for simple event handling | Discord → Google Sheets |
|[GitHub Copilot seats](./github_copilot/) | Automate daily GitHub Copilot user pruning and report changes | GitHub ↔ Slack |
| 🐍 [Google Forms to Jira](./google_forms_to_jira/) | Poll a form for responses and create an issue for each one | Google Forms → Jira |
| 🐍 [Google Forms to Jira](./google_forms_to_jira/) | Create Jira issues based on Google Forms responses | Google Forms → Jira |
| 🐍 [Jira assignee from schedule](./jira_google_calendar/assignee_from_schedule/) | Assign new Jira issues to the current on-caller based on a schedule in a shared calendar | Jira ↔ Google Calendar |
| 🐍 [Jira deadline to event](./jira_google_calendar/deadline_to_event/) | Create/update calendar events based on the deadlines of Jira issues | Jira ↔ Google Calendar |
|[Pull Request Review Reminder (Purrr)](./purrr/) | Streamline code reviews and cut down turnaround time to merge pull requests | GitHub ↔ Slack |
Expand All @@ -26,6 +29,7 @@ projects for:
| 🐍 [Room reservation](./room_reservation/) | Manage via Slack ad-hoc room reservations in Google Calendar | Slack ↔ Google Calendar, Google Sheets |
| 🐍 [Slack support](./slack_support/) | Categorize Slack support requests using AI, and route them to the appropiate people | Slack ↔ Gemini, Google Sheets |
| 🐍 [Task chain](./task_chain/) | Run a sequence of tasks with fault tolerance | Slack |
| 🐍 [Webhook to Jira](./webhook_to_jira/) | Create Jira issues based on HTTP GET/POST requests | HTTP → Jira |

> [!NOTE]
> 🐍 = Python implementation, ⭐ = Starlark implementation.
41 changes: 0 additions & 41 deletions create_jira_issue/README.md

This file was deleted.

22 changes: 0 additions & 22 deletions create_jira_issue/autokitteh.yaml

This file was deleted.

102 changes: 49 additions & 53 deletions google_forms_to_jira/README.md
Original file line number Diff line number Diff line change
@@ -1,85 +1,81 @@
# Google Forms to Jira

# Google Forms to Jira Workflow

This project automates the process of creating Jira issues based on new responses to a Google Form. The workflow periodically polls a Google Form for new responses and creates a Jira issue for each new response.

## Benefits

- **Automated Issue Creation**: Automatically creates Jira issues for each new Google Form response.
- **Seamless Integration**: Integrates Google Forms and Jira without additional manual steps.
This project creates Jira issues based on Google Forms responses.

## How It Works

1. **Trigger**: The workflow is triggered by an HTTP GET request.
2. **Poll Forms**: The program polls the specified Google Form for new responses.
3. **Create Jira Issue**: For each new response, the program creates a Jira issue with the response data.

## Installation and Usage

- [Install AutoKitteh](https://docs.autokitteh.com/get_started/install)
1. The workflow is triggered by Google Forms response events for a predefined
form ID

### Configure integrations
2. The workflow extracts the answers from the response, and matches them with
the form's questions, to construct a human-readable summary

> [!IMPORTANT]
> The `autokitteh.yaml` file includes environment variables for the Google Forms and Jira connections that need to be configured.
3. The workflow checks if there's already an existing Jira issue for the
response's ID:

Ensure you have set up the required integrations:
- No (new response): it creates a new Jira issue
- Yes (edited response): it updates the existing Jira issue's description
with the new response

- [Atlassian Jira](https://docs.autokitteh.com/integrations/atlassian)
- [Google Forms](https://docs.autokitteh.com/integrations/google)
## API Documentation

### Clone the Repository
Atlassian Jira:

```shell
git clone https://github.com/autokitteh/kittehub.git
cd kittehub/google_forms_to_jira
```
Alternatively, you can copy the individual files in this directory.
- https://docs.autokitteh.com/integrations/atlassian/jira/python

### Run the AutoKitteh Server
Google Forms:

Simply run this command:
- https://docs.autokitteh.com/integrations/google/forms/events

```shell
ak up --mode dev
```
## Setup Instructions

### Apply Manifest and Deploy Project
1. Install and start a
[self-hosted AutoKitteh server](https://docs.autokitteh.com/get_started/quickstart),
or use AutoKitteh Cloud

1. Navigate to the `google_forms_to_jira` directory:
2. Optional for self-hosted servers (preconfigured in AutoKitteh Cloud):

```shell
cd google_forms_to_jira
```
- [Enable Google connections to use OAuth 2.0](https://docs.autokitteh.com/integrations/google/config)
- [Enable Atlassian connections to use an OAuth 2.0 (3LO) app](https://docs.autokitteh.com/integrations/atlassian/config)

2. Apply manifest and deploy the project by running the following command:
3. Run this command to clone the Kittehub repository, which contains this
project:

```shell
ak deploy --manifest autokitteh.yaml
git clone https://github.com/autokitteh/kittehub.git
```

The output of this command will be important for initializing connections in the following step if you're using the CLI.
4. Set the `JIRA_PROJECT_KEY` variable in this project's
[autokitteh.yaml](./autokitteh.yaml) manifest file

For example, for each configured connection, you will see a line that looks similar to the one below:
5. Run this command to deploy this project's manifest file:

```shell
[exec] create_connection "google_forms_to_jira/jira_connection": con_01j36p9gj6e2nt87p9vap6rbmz created
ak deploy --manifest kittehub/google_forms_to_jira/autokitteh.yaml
```

`con_01j36p9gj6e2nt87p9vap6rbmz` is the connection ID.
6. Initialize this project's connections:

### Initiliaze Connections
- Atlassian Jira: with an OAuth 2.0 (3LO) app (based on step 2), or with
user impersonation using a token
- Google Forms: with user impersonation using OAuth 2.0 (based on step 2),
or a GCP service account's JSON key

> [!NOTE]
> `my_http` does not need to initialized
> [!TIP]
> The exact CLI commands to do so (`ak connection init ...`) will appear in
> the output of the `ak deploy` command from step 3 when you create the
> project on the server, i.e. when you run that command for the first time.
> [!IMPORTANT]
> Specify the ID of a form that you own, to receive notifications about it.
Using the connection IDs from the previous step, run these commands:
## Usage Instructions

```shell
ak connection init jira_connection <connection ID>
ak connection init google_forms_connection <connection ID>
```
1. Submit a response to the Google Form that you specified in step 6 above

### Trigger the Workflow
2. See the Jira issue which was auto-created in the Jira project that you
specified in the [autokitteh.yaml](./autokitteh.yaml) manifest file

The workflow is triggered by an HTTP GET request to http://localhost:9980/http/google_forms_to_jira/, which starts the polling process for new Google Form responses.
3. If the Google Form is configured to allow editing responses instead of
submitting new ones, edit your response, and see the update in the
previously-created Jira issue
37 changes: 10 additions & 27 deletions google_forms_to_jira/autokitteh.yaml
Original file line number Diff line number Diff line change
@@ -1,37 +1,20 @@
# This YAML file is a declarative manifest that describes the setup of
# an AutoKitteh sample project that demonstrates integration with
# Google Forms and Jira.
#
# Before deploying this AutoKitteh project:
# - Set the "GOOGLE_FORM_ID" in the project's vars
# - Set the "JIRA_PROJECT_KEY" in the project's vars
# - Set the "POLL_INTERVAL" in the project's vars
# (in seconds, e.g. 60 for 1 minute)
#
# After applying this file, initialize this AutoKitteh project's
# Google Forms and Jira connections.
# This YAML file is a declarative manifest that describes the setup of an
# AutoKitteh project that creates Jira issues based on Google Forms responses.

version: v1

project:
name: google_forms_to_jira
vars:
- name: GOOGLE_FORM_ID
value: ""
- name: JIRA_PROJECT_KEY
value: ""
- name: POLL_INTERVAL
value: "10"
value:
connections:
- name: jira_connection
integration: jira
- name: google_forms_connection
- name: forms_conn
integration: googleforms
- name: http_connection
integration: http
- name: jira_conn
integration: jira
triggers:
- name: http_request
connection: http_connection
event_type: get
# Triggered by GET request to http://localhost:9980/http/google_forms_to_jira/
call: program.py:on_http_get
- name: google_forms_response
connection: forms_conn
event_type: responses
call: program.py:on_form_response
88 changes: 49 additions & 39 deletions google_forms_to_jira/program.py
Original file line number Diff line number Diff line change
@@ -1,59 +1,69 @@
"""This program polls a Google Form for new responses and creates a Jira issue for each new response.
"""Create Jira issues based on Google Forms responses.
Workflow:
1. Trigger: HTTP GET request.
2. Poll Forms: Poll the Google Form for new responses.
3. Create Jira Issue: For each new response, create a Jira issue with the response data.
Atlassian Jira API documentation:
- https://docs.autokitteh.com/integrations/atlassian/jira/python
Google Forms API documentation:
- https://docs.autokitteh.com/integrations/google/forms/events
"""

import json
import os
import time

import autokitteh
from autokitteh import google
from autokitteh.atlassian import jira_client


POLL_INTERVAL = os.getenv("POLL_INTERVAL")
JIRA_PROJECT_KEY = os.getenv("JIRA_PROJECT_KEY")

jira = jira_client("jira_conn")


def on_form_response(event):
print("Form response submitted:", event.data)
response_id = event.data.response.response_id

forms = google.google_forms_client("forms_conn").forms()
questions = forms.get(formId=event.data.form_id).execute().get("items", [])

answers = _summarize_form_response(event.data.response.answers, questions)

# Check if a Jira issue already exists for this response (i.e. response edited).
query = f"project = {JIRA_PROJECT_KEY} AND description ~ {response_id}"
issues = jira.jql(query + " ORDER BY created DESC")
if issues.get("total", 0) == 0:
_create_jira_issue(answers, response_id)
else:
_update_jira_issue(issues["issues"][0], answers, response_id)


def on_http_get(event):
form_id = os.getenv("GOOGLE_FORM_ID")
form_data = _get_form_data(form_id)
total_responses = None
while True:
total_responses = _poll_forms(form_data, form_id, total_responses)
time.sleep(float(POLL_INTERVAL))
def _summarize_form_response(answers, questions):
"""Extract answers from response, and match with form questions."""
summary = []
for i, question in enumerate(questions, start=1):
question_id = question["questionItem"]["question"]["questionId"]
title = question.get("title", "Untitled question")

if question_id not in answers:
summary.append(f"{i}. {title}:\nNot answered")
else:
answer = answers[question_id]["text_answers"]["answers"][0]["value"]
summary.append(f"{i}. {title}:\n{answer}")

@autokitteh.activity
def _poll_forms(form_data, form_id, prev_total):
google_forms = google.google_forms_client("google_forms_connection")
result = google_forms.forms().responses().list(formId=form_id).execute()
responses = result.get("responses", [])
curr_total = len(responses)
if prev_total and curr_total > prev_total:
new_responses = curr_total - prev_total
for response in responses[-new_responses:]:
_create_jira_issue(form_data["info"]["title"], response)
return curr_total
return summary


def _create_jira_issue(title, response):
jira = jira_client("jira_connection")
answers = json.dumps(response["answers"], indent=2)
def _create_jira_issue(answers, response_id):
fields = {
"project": {"key": os.getenv("JIRA_PROJECT_KEY")},
"summary": "New Google Form Response for form: " + title,
"description": f"{{code:|language=python}} {answers} {{code}}",
"project": {"key": JIRA_PROJECT_KEY},
"issuetype": {"name": "Task"},
"summary": "Response to Google Form",
"description": "\n\n".join(answers) + f"\n\n(Response ID: {response_id})",
}
new_issue = jira.create_issue(fields=fields)
print(f"Created Jira issue: {new_issue["key"]}")
issue = jira.create_issue(fields=fields)
print("Created Jira issue:", issue["key"])


@autokitteh.activity
def _get_form_data(form_id):
google_forms = google.google_forms_client("google_forms_connection")
return google_forms.forms().get(formId=form_id).execute()
def _update_jira_issue(issue, answers, response_id):
description = "\n\n".join(answers) + f"\n\n(Response ID: {response_id})"
jira.update_issue_field(issue["key"], fields={"description": description})
print("Updated Jira issue:", issue["key"])
Loading

0 comments on commit 25c5ec2

Please sign in to comment.