Stub out todo.sr.ht support

This commit is contained in:
Drew DeVault 2020-04-01 18:51:14 -04:00
parent 43d493f8ba
commit b98a88e47f
8 changed files with 186 additions and 9 deletions

View File

@ -15,6 +15,7 @@ class HubApp(SrhtFlask):
from hubsrht.blueprints.projects import projects
from hubsrht.blueprints.public import public
from hubsrht.blueprints.sources import sources
from hubsrht.blueprints.trackers import trackers
from hubsrht.blueprints.users import users
from hubsrht.blueprints.webhooks import webhooks
@ -22,6 +23,7 @@ class HubApp(SrhtFlask):
self.register_blueprint(projects)
self.register_blueprint(public)
self.register_blueprint(sources)
self.register_blueprint(trackers)
self.register_blueprint(users)
self.register_blueprint(webhooks)

View File

@ -0,0 +1,21 @@
from flask import Blueprint, render_template, request, redirect, url_for
from hubsrht.projects import ProjectAccess, get_project
from hubsrht.services import todo
from hubsrht.types import Event, EventType, Tracker
from srht.database import db
from srht.flask import paginate_query
from srht.oauth import loginrequired
from srht.search import search_by
from srht.validation import Validation
trackers = Blueprint("trackers", __name__)
@trackers.route("/<owner>/<project_name>/trackers/new")
@loginrequired
def new_GET(owner, project_name):
owner, project = get_project(owner, project_name, ProjectAccess.write)
# TODO: Pagination
trackers = todo.get_trackers(owner)
trackers = sorted(trackers, key=lambda r: r["updated"], reverse=True)
return render_template("tracker-new.html", view="new-resource",
owner=owner, project=project, trackers=trackers)

View File

@ -6,6 +6,7 @@ from srht.config import get_origin
_gitsrht = get_origin("git.sr.ht", external=True, default=None)
_listsrht = get_origin("lists.sr.ht", external=True, default=None)
_todosrht = get_origin("todo.sr.ht", external=True, default=None)
origin = get_origin("hub.sr.ht")
class SrhtService(ABC):
@ -51,7 +52,7 @@ class GitService(SrhtService):
def create_repo(self, user, valid):
name = valid.require("name")
description = valid.require("description")
description = valid.optional("description")
if not valid.ok:
return None
return self.post(user, valid, f"{_gitsrht}/api/repos", {
@ -101,8 +102,7 @@ class ListService(SrhtService):
def create_list(self, user, valid):
name = valid.require("name")
description = valid.require("description")
print(name, description)
description = valid.optional("description")
if not valid.ok:
return None
return self.post(user, valid, f"{_listsrht}/api/lists", {
@ -110,5 +110,41 @@ class ListService(SrhtService):
"description": description,
})
class TodoService(SrhtService):
def get_trackers(self, user):
return get_results(f"{_todosrht}/api/trackers", user)
def get_tracker(self, user, tracker_name):
r = self.session.get(f"{_todosrht}/api/trackers/{tracker_name}",
headers=get_authorization(user))
if r.status_code != 200:
raise Exception(r.json())
return r.json()
def ensure_tracker_webhooks(self, user, tracker_name):
config = {
origin + url_for("webhooks.tracker"): ["ticket:create"]
}
url = f"{_todosrht}/api/user/{user.canonical_name}/trackers/{tracker_name}/webhooks"
ensure_webhooks(user, url, config)
def ensure_ticket_webhooks(self, user, tracker_name, ticket_id):
config = {
origin + url_for("webhooks.tracker_ticket"): ["event:create"]
}
url = f"{_todosrht}/api/user/{user.canonical_name}/trackers/{tracker_name}/tickets/{ticket_id}/webhooks"
ensure_webhooks(user, url, config)
def create_tracker(self, user, valid):
name = valid.require("name")
description = valid.optional("description")
if not valid.ok:
return None
return self.post(user, valid, f"{_todosrht}/api/trackers", {
"name": name,
"description": description,
})
git = GitService()
lists = ListService()
todo = TodoService()

View File

@ -84,16 +84,17 @@
value="{{ name or project.name }}" />
{{ valid.summary("name") }}
<div class="form-group">
<label for="list_description">Description</label>
<label for="description">Description</label>
<textarea
type="text"
name="description"
id="description"
class="form-control {{valid.cls("description")}}"
rows="5"
aria-describedby="descriptionHelpText"
>{{ description or project.description }}</textarea>
<small id="descriptionHelpText" class="form-text text-muted pull-left">
aria-describedby="description-help-text"
placeholder="Introduce this mailing list to users and explain the posting rules."
>{{ description or "" }}</textarea>
<small id="description-help-text" class="form-text text-muted pull-left">
Markdown supported
</small>
{{valid.summary("description")}}

View File

@ -58,7 +58,10 @@
</li>
<li>
{{icon('plus-square', cls='text-info')}}
<a href="#">Add bug trackers&nbsp;{{icon('arrow-right')}}</a>
<a
href="{{url_for("trackers.new_GET",
owner=owner.canonical_name, project_name=project.name)}}"
>Add bug trackers&nbsp;{{icon('arrow-right')}}</a>
<br />
<small class="text-muted">
Bug trackers give you a place to organize your tasks and record

View File

@ -0,0 +1,84 @@
{% extends "resource-new.html" %}
{% block content %}
<form method="POST">
{{csrf_token()}}
<div class="row">
<div class="col-lg-8">
<h3 style="margin-top: 1rem">
Create a new ticket tracker
</h3>
<div class="form-group">
<label for="{{ typename }}">Name</label>
<input
type="text"
name="name"
id="name"
class="form-control {{ valid.cls("name") }}"
value="{{ name or project.name }}" />
{{ valid.summary("name") }}
<div class="form-group">
<label for="description">Description</label>
<textarea
type="text"
name="description"
id="description"
class="form-control {{valid.cls("description")}}"
rows="5"
aria-describedby="description-help-text"
placeholder="Give users an idea of how to submit a good ticket for this tracker."
>{{ description or "" }}</textarea>
<small id="description-help-text" class="form-text text-muted pull-left">
Markdown supported
</small>
{{valid.summary("description")}}
</div>
</div>
<div class="flex-grow-1 d-flex flex-row justify-content-end">
<button
type="submit"
class="btn btn-primary align-self-end"
name="create"
>Create new tracker {{icon("caret-right")}}</button>
</div>
</div>
</div>
{% if any(trackers) %}
<div class="row">
<div class="col-lg-8">
<h3 style="margin-top: 1rem">
Or add an existing tracker
</h3>
{# TODO: Pagination #}
<div class="form-group">
{# TODO: How exactly should this work #}
<input
name="search"
type="text"
placeholder="Search your ticket trackers"
class="form-control"
value="{{ search if search else "" }}" />
</div>
<div class="event-list select-resource">
{% for tracker in trackers %}
<div class="event">
<h3>
<button
type="submit"
name="existing-{{ tracker["name"] }}"
class="pull-right btn btn-primary btn-lg"
>Select tracker&nbsp;{{ icon("caret-right") }}</button>
<a
href="{{get_origin("todo.sr.ht",
external=True)}}/{{ tracker["owner"]["canonical_name"] }}/{{tracker["name"]}}"
target="_blank"
rel="noopener"
>{{ tracker["name"] }}</a>
</h3>
</div>
{% endfor %}
</div>
</div>
</div>
{% endif %}
</form>
{% endblock %}

View File

@ -11,6 +11,7 @@ class Visibility(Enum):
private = "private"
from hubsrht.types.event import Event, EventType
from hubsrht.types.mailinglist import MailingList
from hubsrht.types.project import Project
from hubsrht.types.sourcerepo import SourceRepo, RepoType
from hubsrht.types.mailinglist import MailingList
from hubsrht.types.tracker import Tracker

29
hubsrht/types/tracker.py Normal file
View File

@ -0,0 +1,29 @@
import sqlalchemy as sa
from srht.config import get_origin
from srht.database import Base
_todosrht = get_origin("todo.sr.ht", external=True, default=None)
class Tracker(Base):
__tablename__ = "tracker"
id = sa.Column(sa.Integer, primary_key=True)
remote_id = sa.Column(sa.Integer, nullable=False)
created = sa.Column(sa.DateTime, nullable=False)
updated = sa.Column(sa.DateTime, nullable=False)
project_id = sa.Column(sa.Integer,
sa.ForeignKey("project.id"), nullable=False)
project = sa.orm.relationship("Project",
backref=sa.orm.backref("trackers"),
foreign_keys=[project_id])
# Note: in theory this may eventually be different from the project owner(?)
owner_id = sa.Column(sa.Integer,
sa.ForeignKey("user.id"), nullable=False)
owner = sa.orm.relationship("User")
name = sa.Column(sa.Unicode(128), nullable=False)
description = sa.Column(sa.Unicode(512), nullable=False)
def url(self):
return f"{_todosrht}/{self.owner.canonical_name}/{self.name}"