todosrht: Use GraphQL for ticket CRUD operations

Use GraphQL for the remaining ticket CRUD operations.
This commit is contained in:
Adnan Maolood 2022-04-27 11:35:01 -04:00 committed by Drew DeVault
parent 071d6ca781
commit 037e2bf9fe
2 changed files with 88 additions and 112 deletions

View File

@ -1,8 +1,9 @@
import re
from datetime import datetime
from flask import Blueprint, render_template, request, abort, redirect
from flask import Blueprint, current_app, render_template, request, abort, redirect
from srht.config import cfg
from srht.database import db
from srht.graphql import exec_gql
from srht.oauth import current_user, loginrequired
from srht.validation import Validation
from todosrht.access import get_tracker, get_ticket
@ -16,7 +17,6 @@ from todosrht.types import TicketAccess, TicketResolution, ParticipantType
from todosrht.types import TicketComment, TicketAuthenticity
from todosrht.types import TicketSubscription, User, Participant
from todosrht.urls import ticket_url
from todosrht.webhooks import TrackerWebhook, TicketWebhook
from urllib.parse import quote
@ -158,11 +158,6 @@ def ticket_comment_POST(owner, name, ticket_id):
reopen = valid.optional("reopen")
preview = valid.optional("preview")
valid.expect(not text or 3 <= len(text) <= 16384,
"Comment must be between 3 and 16384 characters.", field="comment")
valid.expect(text or resolve or reopen,
"Comment is required", field="comment")
if (resolve or reopen) and TicketAccess.edit not in access:
abort(403)
@ -186,18 +181,25 @@ def ticket_comment_POST(owner, name, ticket_id):
})
return render_template("ticket.html", **ctx)
participant = get_participant_for_user(current_user)
event = add_comment(participant, ticket,
text=text, resolve=resolve, resolution=resolution, reopen=reopen)
if not event:
return redirect(ticket_url(ticket))
input = {
"text": text,
"status": "REPORTED" if reopen else "RESOLVED" if resolve else None,
"resolution": resolution.name.upper() if resolve else None,
}
TicketWebhook.deliver(TicketWebhook.Events.event_create,
event.to_dict(),
TicketWebhook.Subscription.ticket_id == ticket.id)
TrackerWebhook.deliver(TrackerWebhook.Events.event_create,
event.to_dict(),
TrackerWebhook.Subscription.tracker_id == ticket.tracker_id)
resp = exec_gql(current_app.site, """
mutation SubmitComment($trackerId: Int!, $ticketId: Int!, $input: SubmitCommentInput!) {
submitComment(trackerId: $trackerId, ticketId: $ticketId, input: $input) {
id
}
}
""", trackerId=tracker.id, ticketId=ticket.scoped_id, input=input)
class DummyEvent:
def __init__(self, id):
self.id = id
event = DummyEvent(resp["submitComment"]["id"])
return redirect(ticket_url(ticket, event))
@ticket.route("/<owner>/<name>/<int:ticket_id>/edit/<int:comment_id>")
@ -331,9 +333,19 @@ def ticket_edit_POST(owner, name, ticket_id):
tracker=tracker, ticket=ticket, rendered_preview=preview,
**valid.kwargs)
ticket.title = title
ticket.description = desc
db.session.commit()
input = {
"subject": title,
"body": desc,
}
exec_gql(current_app.site, """
mutation UpdateTicket($trackerId: Int!, $ticketId: Int!, $input: UpdateTicketInput!) {
updateTicket(trackerId: $trackerId, ticketId: $ticketId, input: $input) {
id
}
}
""", trackerId=tracker.id, ticketId=ticket.scoped_id, input=input)
return redirect(ticket_url(ticket))
@ticket.route("/<owner>/<name>/<int:ticket_id>/add_label", methods=["POST"])
@ -365,33 +377,13 @@ def ticket_add_label(owner, name, ticket_id):
if not label:
abort(404)
ticket_label = (TicketLabel.query
.filter(TicketLabel.label_id == label.id)
.filter(TicketLabel.ticket_id == ticket.id)).first()
if not ticket_label:
ticket_label = TicketLabel()
ticket_label.ticket_id = ticket.id
ticket_label.label_id = label.id
ticket_label.user_id = current_user.id
participant = get_participant_for_user(current_user)
event = Event()
event.event_type = EventType.label_added
event.participant_id = participant.id
event.ticket_id = ticket.id
event.label_id = label.id
db.session.add(ticket_label)
db.session.add(event)
db.session.commit()
TicketWebhook.deliver(TicketWebhook.Events.event_create,
event.to_dict(),
TicketWebhook.Subscription.ticket_id == ticket.id)
TrackerWebhook.deliver(TrackerWebhook.Events.event_create,
event.to_dict(),
TrackerWebhook.Subscription.tracker_id == ticket.tracker_id)
exec_gql(current_app.site, """
mutation LabelTicket($trackerId: Int!, $ticketId: Int!, $labelId: Int!) {
labelTicket(trackerId: $trackerId, ticketId: $ticketId, labelId: $labelId) {
id
}
}
""", trackerId=tracker.id, ticketId=ticket.scoped_id, labelId=label_id)
return redirect(ticket_url(ticket))
@ -411,28 +403,13 @@ def ticket_remove_label(owner, name, ticket_id, label_id):
if not label:
abort(404)
ticket_label = (TicketLabel.query
.filter(TicketLabel.label_id == label_id)
.filter(TicketLabel.ticket_id == ticket.id)).first()
if ticket_label:
participant = get_participant_for_user(current_user)
event = Event()
event.event_type = EventType.label_removed
event.participant_id = participant.id
event.ticket_id = ticket.id
event.label_id = label.id
db.session.add(event)
db.session.delete(ticket_label)
db.session.commit()
TicketWebhook.deliver(TicketWebhook.Events.event_create,
event.to_dict(),
TicketWebhook.Subscription.ticket_id == ticket.id)
TrackerWebhook.deliver(TrackerWebhook.Events.event_create,
event.to_dict(),
TrackerWebhook.Subscription.tracker_id == ticket.tracker_id)
exec_gql(current_app.site, """
mutation UnlabelTicket($trackerId: Int!, $ticketId: Int!, $labelId: Int!) {
unlabelTicket(trackerId: $trackerId, ticketId: $ticketId, labelId: $labelId) {
id
}
}
""", trackerId=tracker.id, ticketId=ticket.scoped_id, labelId=label.id)
return redirect(ticket_url(ticket))
@ -447,7 +424,7 @@ def _assignment_get_ticket(owner, name, ticket_id):
if TicketAccess.triage not in access:
abort(401)
return ticket
return (tracker, ticket)
def _assignment_get_user(valid):
if 'myself' in valid:
@ -468,15 +445,20 @@ def _assignment_get_user(valid):
@loginrequired
def ticket_assign(owner, name, ticket_id):
valid = Validation(request)
ticket = _assignment_get_ticket(owner, name, ticket_id)
tracker, ticket = _assignment_get_ticket(owner, name, ticket_id)
user = _assignment_get_user(valid)
if not valid.ok:
_, access = get_ticket(ticket.tracker, ticket_id)
ctx = get_ticket_context(ticket, ticket.tracker, access)
return render_template("ticket.html", **valid.kwargs, **ctx)
assign(ticket, user, current_user)
db.session.commit()
exec_gql(current_app.site, """
mutation AssignUser($trackerId: Int!, $ticketId: Int!, $userId: Int!) {
assignUser(trackerId: $trackerId, ticketId: $ticketId, userId: $userId) {
id
}
}
""", trackerId=tracker.id, ticketId=ticket.scoped_id, userId=user.id)
return redirect(ticket_url(ticket))
@ -484,15 +466,20 @@ def ticket_assign(owner, name, ticket_id):
@loginrequired
def ticket_unassign(owner, name, ticket_id):
valid = Validation(request)
ticket = _assignment_get_ticket(owner, name, ticket_id)
tracker, ticket = _assignment_get_ticket(owner, name, ticket_id)
user = _assignment_get_user(valid)
if not valid.ok:
_, access = get_ticket(ticket.tracker, ticket_id)
ctx = get_ticket_context(ticket, ticket.tracker, access)
return render_template("ticket.html", valid, **ctx)
unassign(ticket, user, current_user)
db.session.commit()
exec_gql(current_app.site, """
mutation UnassignUser($trackerId: Int!, $ticketId: Int!, $userId: Int!) {
unassignUser(trackerId: $trackerId, ticketId: $ticketId, userId: $userId) {
id
}
}
""", trackerId=tracker.id, ticketId=ticket.scoped_id, userId=user.id)
return redirect(ticket_url(ticket))

View File

@ -15,7 +15,6 @@ from todosrht.types import Event, Label, TicketLabel
from todosrht.types import TicketSubscription, Participant
from todosrht.types import Tracker, Ticket, TicketAccess
from todosrht.urls import tracker_url, ticket_url
from todosrht.webhooks import TrackerWebhook
from urllib.parse import quote
import sqlalchemy as sa
@ -224,10 +223,6 @@ def tracker_submit_POST(owner, name):
ticket, _ = get_ticket(tracker, resp["submitTicket"]["id"])
TrackerWebhook.deliver(TrackerWebhook.Events.ticket_create,
ticket.to_dict(),
TrackerWebhook.Subscription.tracker_id == tracker.id)
if another:
session["another"] = True
return redirect(url_for(".tracker_GET",
@ -285,22 +280,19 @@ def tracker_labels_POST(owner, name):
tracker=tracker, access=access, is_owner=is_owner,
**valid.kwargs), 400
existing_label = Label.query.filter_by(
tracker=tracker, name=data["name"]).one_or_none()
valid.expect(not existing_label,
"A label with this name already exists", field="name")
exec_gql(current_app.site, """
mutation CreateLabel($trackerId: Int!, $name: String!, $foreground: String!, $background: String!) {
createLabel(trackerId: $trackerId, name: $name, foreground: $foreground, background: $background) {
id
}
}
""", valid=valid, trackerId=tracker.id, name=data["name"], foreground=data["text_color"], background=data["color"])
if not valid.ok:
return render_template("tracker-labels.html",
tracker=tracker, access=access, is_owner=is_owner,
**valid.kwargs), 400
label = Label(tracker=tracker, **data)
db.session.add(label)
db.session.commit()
TrackerWebhook.deliver(TrackerWebhook.Events.label_create,
label.to_dict(),
TrackerWebhook.Subscription.tracker_id == tracker.id)
return redirect(url_for(".tracker_labels_GET", owner=owner, name=name))
@tracker.route("/<owner>/<name>/labels/<path:label_name>")
@ -338,19 +330,21 @@ def label_edit_POST(owner, name, label_name):
return render_template("tracker-label-edit.html",
tracker=tracker, access=access, label=label, **valid.kwargs), 400
existing_label = Label.query.filter_by(
tracker=tracker, name=data["name"]).one_or_none()
valid.expect(not existing_label or existing_label == label,
"A label with this name already exists", field="name")
input = {
"name": data["name"],
"foregroundColor": data["text_color"],
"backgroundColor": data["color"],
}
exec_gql(current_app.site, """
mutation UpdateLabel($id: Int!, $input: UpdateLabelInput!) {
updateLabel(id: $id, input: $input) { id }
}
""", valid=valid, id=label.id, input=input)
if not valid.ok:
return render_template("tracker-label-edit.html",
tracker=tracker, access=access, **valid.kwargs), 400
label.name = data["name"]
label.color = data["color"]
label.text_color = data["text_color"]
db.session.commit()
return redirect(url_for(".tracker_labels_GET", owner=owner, name=name))
@tracker.route("/<owner>/<name>/labels/<int:label_id>/delete", methods=["POST"])
@ -369,15 +363,10 @@ def delete_label(owner, name, label_id):
if not label:
abort(404)
# Remove label from any linked tickets and related events
TicketLabel.query.filter(TicketLabel.label_id == label.id).delete()
Event.query.filter(Event.label_id == label.id).delete()
exec_gql(current_app.site, """
mutation DeleteLabel($id: Int!) {
deleteLabel(id: $id) { id }
}
""", id=label.id)
label_id = label.id
db.session.delete(label)
db.session.commit()
TrackerWebhook.deliver(TrackerWebhook.Events.label_delete,
{ "id": label_id },
TrackerWebhook.Subscription.tracker_id == tracker.id)
return redirect(url_for(".tracker_labels_GET", owner=owner, name=name))