piknik

Unnamed repository; edit this file 'description' to name the repository.
Info | Log | Files | Refs | README | LICENSE

commit 52a296abfb5ae51894631b07f7f9f75639175f27
parent 1e3dc968ff9e1ced72ff5c413469a0fb2f6c79da
Author: lash <dev@holbrook.no>
Date:   Wed,  9 Nov 2022 12:08:56 +0000

Add message wrapper hook

Diffstat:
AROADMAP | 13+++++++++++++
AROADMAP.gui | 5+++++
Mpiknik/basket.py | 6++++--
Apiknik/crypto.py | 13+++++++++++++
Mpiknik/msg.py | 8+++++++-
Mrequirements.txt | 1+
Mtests/test_msg.py | 16+++++++++++++++-
7 files changed, 58 insertions(+), 4 deletions(-)

diff --git a/ROADMAP b/ROADMAP @@ -0,0 +1,13 @@ +- 0.4.0 + * HTTP server for making state edits for issues locally (e.g. drag). +- 0.3.0 + * Automatially embed VCS target and source hash to issue and issue messages. + - target: current hash target to merge into (var) + - source: current hash source to merge (var) + * Target alias for issue, e.g. version, revision, may be: + - inherit from other issue + - target hash above + - git tag + - arbitrary string identifier +- 0.2.0 + * GPG signing of issue messages. diff --git a/ROADMAP.gui b/ROADMAP.gui @@ -0,0 +1,5 @@ +GUI is simple browser javascript. + +Initial implementation should use file:/// scheme and be read-only. + +GUI needs to be separate repo. diff --git a/piknik/basket.py b/piknik/basket.py @@ -14,7 +14,7 @@ logg = logging.getLogger(__name__) class Basket: - def __init__(self, state_factory): + def __init__(self, state_factory, message_wrapper=None): self.no_resurrect = True self.state = state_factory.create_states(default_state='proposed', verifier=self.__check_resurrect) self.state.add('backlog') @@ -32,6 +32,7 @@ class Basket: self.__tags.sync(ignore_auto=False) self.__msg = state_factory.create_messages() + self.__msg_wrap = message_wrapper self.issues_rev = {} @@ -142,6 +143,7 @@ class Basket: def __get_msg(self, issue_id): r = self.state.get(issue_id) + print('issue {}'.format(r)) o = Issue.from_str(r) try: v = self.__msg.get(issue_id) @@ -154,7 +156,7 @@ class Basket: def msg(self, issue_id, *args): m = self.__get_msg(issue_id) - m.add(*args) + m.add(*args, wrapper=self.__msg_wrap) ms = m.as_bytes() self.__msg.put(issue_id, ms) return m diff --git a/piknik/crypto.py b/piknik/crypto.py @@ -0,0 +1,13 @@ +# external imports +import gnupg + + +class Signer: + + def __init__(self, home_dir=None, default_key=None): + self.gpg = gnupg.GPG(gnupghome=home_dir) + self.default_key = default_key + + + def sign(self, issue_msg): + pass diff --git a/piknik/msg.py b/piknik/msg.py @@ -3,11 +3,13 @@ import logging import uuid import mimetypes from base64 import b64encode +import time #from email.message import EmailMessage as Message from email.message import Message from email import message_from_string from email.policy import Compat32 +from email.utils import formatdate logg = logging.getLogger(__name__) @@ -20,6 +22,7 @@ class IssueMessage: self.__m.add_header('Subject', issue.title) self.__m.add_header('X-Piknik-Id', issue.id) + self.__m.add_header('Date', formatdate(time.time())) self.__m.set_payload(None) self.__m.set_type('multipart/mixed') self.__m.set_boundary(str(uuid.uuid4())) @@ -62,10 +65,11 @@ class IssueMessage: m.attach(p) - def add(self, *args, related_id=None): + def add(self, *args, related_id=None, wrapper=None): m_id = uuid.uuid4() m = Message() m.add_header('X-Piknik-Msg-Id', str(m_id)) + m.add_header('Date', formatdate(time.time())) if related_id != None: m.add_header('In-Reply-To', related_id) m.set_payload(None) @@ -78,6 +82,8 @@ class IssueMessage: self.add_file(m, v) elif p == 's:': self.add_text(m, v) + if wrapper: + m = wrapper(m) self.__m.attach(m) diff --git a/requirements.txt b/requirements.txt @@ -1,2 +1,3 @@ shep~=0.2.11 leveldir~=0.3.1 +python-gnupg~=0.5.0 diff --git a/tests/test_msg.py b/tests/test_msg.py @@ -2,6 +2,7 @@ import unittest import logging import json +from email.message import Message # local imports from piknik import ( @@ -18,6 +19,13 @@ logging.basicConfig(level=logging.DEBUG) logg = logging.getLogger() +def test_wrapper(p): + m = Message() + m.add_header('Foo', 'bar') + m.set_type('multipart/relative') + m.set_payload(p) + return m + class TestMsg(unittest.TestCase): @@ -56,9 +64,15 @@ class TestMsg(unittest.TestCase): m = self.b.msg(v, 'f:tests/one.png') m = self.b.msg(v, 's:baz') m = self.b.msg(v, 'f:tests/two.bin') - print(m) + def test_wrapper(self): + b = Basket(self.store, message_wrapper=test_wrapper) + o = Issue('bar') + v = b.add(o) + m = b.msg(v, 's:foo') + print(m) + if __name__ == '__main__': unittest.main()