diff options
| author | Dan Goodliffe <dan@randomdan.homeip.net> | 2016-12-02 21:27:50 +0000 | 
|---|---|---|
| committer | Dan Goodliffe <dan@randomdan.homeip.net> | 2016-12-02 21:27:50 +0000 | 
| commit | 4861233f34f96182df234c35ba6ca9474d29b74a (patch) | |
| tree | e561e24bcb81407bc1cc1fd8b729dcfa9858b81c | |
| parent | Fix use flag ordering to be not locale sensitive and resulting changes in tes... (diff) | |
| download | gentoobrowse-api-4861233f34f96182df234c35ba6ca9474d29b74a.tar.bz2 gentoobrowse-api-4861233f34f96182df234c35ba6ca9474d29b74a.tar.xz gentoobrowse-api-4861233f34f96182df234c35ba6ca9474d29b74a.zip | |
Basic notification service implementation
17 files changed, 645 insertions, 1 deletions
| diff --git a/gentoobrowse-api/service/Jamfile.jam b/gentoobrowse-api/service/Jamfile.jam index a065393..7300b68 100644 --- a/gentoobrowse-api/service/Jamfile.jam +++ b/gentoobrowse-api/service/Jamfile.jam @@ -4,10 +4,11 @@ lib icetray : : : : <include>/usr/include/icetray ;  lib git2 ;  lib gentoobrowse-service : -	[ glob-tree *.cpp : bin ] +	[ glob-tree *.cpp : bin notifications ]  	[ glob-tree *.sql ]  	:  	<library>..//adhocutil +	<library>notifications  	<library>icetray  	<library>git2  	<library>..//dbppcore @@ -15,6 +16,8 @@ lib gentoobrowse-service :  	<library>..//slicer  	<library>..//slicer-db  	<library>../api//gentoobrowse-api +	<library>notifications//gentoobrowse-service-notifications +	<implicit-dependency>notifications//gentoobrowse-service-notifications  	<implicit-dependency>../api//gentoobrowse-api  	<library>..//boost_system  	<library>..//boost_thread @@ -27,6 +30,8 @@ lib gentoobrowse-service :  	: :  	<include>.  	<implicit-dependency>../api//gentoobrowse-api +	<library>notifications//gentoobrowse-service-notifications +	<implicit-dependency>notifications//gentoobrowse-service-notifications  	<library>../api//gentoobrowse-api  	; diff --git a/gentoobrowse-api/service/main.cpp b/gentoobrowse-api/service/main.cpp index bdf6ed5..b7b18a2 100644 --- a/gentoobrowse-api/service/main.cpp +++ b/gentoobrowse-api/service/main.cpp @@ -4,6 +4,8 @@  #include "portageimpl.h"  #include "usersimpl.h"  #include "maintenanceimpl.h" +#include "notificationsimpl.h" +#include "mailserverimpl.h"  namespace Gentoo {  namespace Service { @@ -15,6 +17,8 @@ class Api : public IceTray::Service {  			adp->add(new Portage(db), ic->stringToIdentity("portage"));  			adp->add(new Maintenance(db), ic->stringToIdentity("maintenance"));  			adp->add(new Users(db), ic->stringToIdentity("users")); +			adp->add(new Notifications(), ic->stringToIdentity("notifications")); +			adp->add(new MailServer(), ic->stringToIdentity("mailserver"));  		}  }; diff --git a/gentoobrowse-api/service/notifications/Jamfile.jam b/gentoobrowse-api/service/notifications/Jamfile.jam new file mode 100644 index 0000000..a168649 --- /dev/null +++ b/gentoobrowse-api/service/notifications/Jamfile.jam @@ -0,0 +1,44 @@ +import type : register ; +import generators : register-standard ; + +type.register XSLT : xslt ; + +generators.register-standard xslt.h : XSLT : H ; + +h base-xslt : xslt/base.xslt : <slicer>yes ; +h signup-xslt : xslt/signup.xslt : <slicer>yes ; +h news-xslt : xslt/news.xslt : <slicer>yes ; + +lib exslt ; +lib xslt : : : : <include>/usr/include/libxslt ; +lib xml2 : : : : <include>/usr/include/libxml2 ; +lib esmtp ; +lib slicer-xml ; +lib boost_system ; + +lib gentoobrowse-service-notifications : +	[ glob *.ice *.cpp ] +	: +	<slicer>yes +	<library>xslt +	<library>xml2 +	<library>esmtp +	<library>../../..//libxmlpp +	<library>boost_system +	<library>slicer-xml +	<library>../../domain//gentoobrowse-domain +	<implicit-dependency>../../domain//gentoobrowse-domain +	<dependency>base-xslt +	<dependency>signup-xslt +	<dependency>news-xslt +	: : +	<include>. +	; + +actions xslt.h +{ +	( cd $(2:D) ; xxd -i $(2:B)$(2:S) ) > $(1) +} + +IMPORT $(__name__) : xslt.h : : xslt.h ; + diff --git a/gentoobrowse-api/service/notifications/mailserverimpl.cpp b/gentoobrowse-api/service/notifications/mailserverimpl.cpp new file mode 100644 index 0000000..c3dfdfa --- /dev/null +++ b/gentoobrowse-api/service/notifications/mailserverimpl.cpp @@ -0,0 +1,64 @@ +#include "mailserverimpl.h" +#include <libesmtp.h> +#include <Ice/ObjectAdapter.h> +#include <Ice/Communicator.h> + +namespace Gentoo { +	namespace Service { +		typedef std::vector<std::string> Parts; +		typedef std::pair<Parts::iterator, Parts::const_iterator> PartsProgress; + +		void +		MailServer::sendEmail(const Gentoo::EmailPtr & msg, const Ice::Current & c) +		{ +			auto props = c.adapter->getCommunicator()->getProperties(); +			smtp_session_t session = smtp_create_session(); +			smtp_message_t message = smtp_add_message(session); +			auto server = props->getPropertyWithDefault("GentooBrowseAPI.MailServer", "localhost"); +			smtp_set_server(session, server.c_str()); +			smtp_set_header(message, "To", msg->to.name.c_str(), msg->to.address.c_str()); +			smtp_set_header(message, "From", msg->from.name.c_str(), msg->from.address.c_str()); +			smtp_set_header(message, "Subject", msg->subject.c_str()); +			Parts parts; +			parts.emplace_back("Content-Type: multipart/alternative; boundary=\"<<divider>>\"\r\n"); +			parts.emplace_back("MIME-Version: 1.0\r\n"); +			parts.emplace_back("Content-Transfer-Encoding: binary\r\n"); +			smtp_add_recipient(message, msg->to.address.c_str()); +			for (const auto & p : msg->body) { +				parts.emplace_back("\r\n--<<divider>>\r\nContent-Type: " + p->mimetype  + "; utf-8\r\n\r\n"); +				parts.emplace_back(p->payload); +			} +			parts.emplace_back("\r\n--<<divider>>--\r\n"); +			PartsProgress pp { parts.begin(), parts.end() }; +			smtp_set_messagecb(message, writeBody, &pp); +			if (!smtp_start_session(session)) { +				char buf[BUFSIZ]; +				auto b = smtp_strerror(smtp_errno(), buf, sizeof(buf)); +				assert(b); +				SendEmailFailed e(b); +				smtp_destroy_session(session); +				throw e; +			} +			smtp_destroy_session(session); +		} + +		const char * +		MailServer::writeBody(void **, int * len, void * arg) +		{ +			auto parts = static_cast<PartsProgress *>(arg); +			if (len == NULL || parts->first == parts->second) { +				return NULL; +			} +			const auto & p = *parts->first++; +			*len = p.length(); +			return p.data(); +		} +	} + +	void +	SendEmailFailed::ice_print(std::ostream & buf) const +	{ +		buf << "Failed to send email: " << message; +	} +} + diff --git a/gentoobrowse-api/service/notifications/mailserverimpl.h b/gentoobrowse-api/service/notifications/mailserverimpl.h new file mode 100644 index 0000000..23b17c2 --- /dev/null +++ b/gentoobrowse-api/service/notifications/mailserverimpl.h @@ -0,0 +1,20 @@ +#ifndef MAILSERVERIMPL_H +#define MAILSERVERIMPL_H + +#include <notifications.h> +#include <visibility.h> + +namespace Gentoo { +	namespace Service { +		class DLL_PUBLIC MailServer : public Gentoo::MailServer { +			public: +				void sendEmail(const Gentoo::EmailPtr & msg, const Ice::Current &) override; + +			private: +				static const char * writeBody(void ** buf, int * len, void * arg); +		}; +	} +} + +#endif + diff --git a/gentoobrowse-api/service/notifications/notifications.ice b/gentoobrowse-api/service/notifications/notifications.ice new file mode 100644 index 0000000..a60fa28 --- /dev/null +++ b/gentoobrowse-api/service/notifications/notifications.ice @@ -0,0 +1,49 @@ +#ifndef GENTOO_NOTIFICATIONS +#define GENTOO_NOTIFICATIONS + +#include <user-models.ice> +#include <portage-models.ice> + +module Gentoo { +	class MimePart { +		string mimetype; +		string payload; +	}; + +	sequence<MimePart> MimeParts; + +	struct Address { +		string name; +		string address; +	}; + +	class Email { +		Address to; +		Address from; +		string subject; +		MimeParts body; +	}; + +	["cpp:ice_print"] +	exception SendEmailFailed { +		string message; +	}; + +	struct NewsContent { +		Categories categories; +		Packages packages; +		Ebuilds ebuilds; +	}; + +	interface Notifications { +		Email getSignup(NewUser user); +		Email getNews(User user, NewsContent nc); +	}; + +	interface MailServer { +		idempotent void sendEmail(Email msg) throws SendEmailFailed; +	}; +}; + +#endif + diff --git a/gentoobrowse-api/service/notifications/notificationsimpl.cpp b/gentoobrowse-api/service/notifications/notificationsimpl.cpp new file mode 100644 index 0000000..d7dbee4 --- /dev/null +++ b/gentoobrowse-api/service/notifications/notificationsimpl.cpp @@ -0,0 +1,59 @@ +#include "notificationsimpl.h" +#include <libxml/parser.h> +#include <base-xslt.h> +#include <news-xslt.h> +#include <signup-xslt.h> +#include <string.h> +#include <slicer/slicer.h> +#include "xsltStreamSerializer.h" + +namespace Gentoo { +	namespace Service { +		xmlDocPtr	xsltDocLoaderFunc	(const xmlChar * URI, xmlDictPtr, int, void *, xsltLoadType) +		{ +#define MATCH(name) \ +			if (xmlStrcmp(URI, BAD_CAST #name ".xslt") == 0) { \ +				return xmlParseMemory((char *)name ## _xslt, name ## _xslt_len); \ +			} +			MATCH(base) +			MATCH(news) +			MATCH(signup) +			return NULL; +#undef MATCH +		} + +		Notifications::Notifications() +		{ +			xsltSetLoaderFunc(&xsltDocLoaderFunc); +			news = xsltSSPtr(xsltParseStylesheetFile(BAD_CAST "news.xslt"), xsltFreeStylesheet); +			signup = xsltSSPtr(xsltParseStylesheetFile(BAD_CAST "signup.xslt"), xsltFreeStylesheet); +			xsltSetLoaderFunc(NULL); +		} + +		Gentoo::EmailPtr Notifications::getSignup(const Gentoo::NewUserPtr & u, const Ice::Current &) +		{ +			auto e = basicMail("Welcome", u); +			Slicer::SerializeAny<XsltStreamSerializer>(u, e, signup.get()); +			return e; +		} + +		Gentoo::EmailPtr Notifications::getNews(const Gentoo::UserPtr & u, const Gentoo::NewsContent & c, const Ice::Current &) +		{ +			auto e = basicMail("Latest updates", u); +			Slicer::SerializeAny<XsltStreamSerializer>(c, e, news.get()); +			return e; +		} + +		Gentoo::EmailPtr Notifications::basicMail(const std::string & subject, Gentoo::UserPtr u) +		{ +			Gentoo::EmailPtr e = new Gentoo::Email(); +			e->subject = "Gentoo Browse: " + subject; +			e->from.name = "Gentoo Browse"; +			e->from.address = "noreply@gentoobrowse.randomdan.homeip.net"; +			e->to.name = u->userrealname; +			e->to.address = u->useremail; +			return e; +		} +	} +} + diff --git a/gentoobrowse-api/service/notifications/notificationsimpl.h b/gentoobrowse-api/service/notifications/notificationsimpl.h new file mode 100644 index 0000000..1d3b1fc --- /dev/null +++ b/gentoobrowse-api/service/notifications/notificationsimpl.h @@ -0,0 +1,29 @@ +#ifndef NOTIFICATIONSIMPL_H +#define NOTIFICATIONSIMPL_H + +#include <notifications.h> +#include <visibility.h> +#include <memory> +#include <libxslt/documents.h> + +namespace Gentoo { +	namespace Service { +		class DLL_PUBLIC Notifications : public Gentoo::Notifications { +			public: +				Notifications(); + +				Gentoo::EmailPtr getSignup(const Gentoo::NewUserPtr &, const Ice::Current &) override; +				Gentoo::EmailPtr getNews(const Gentoo::UserPtr &, const Gentoo::NewsContent &, const Ice::Current &) override; + +			private: +				Gentoo::EmailPtr basicMail(const std::string &, Gentoo::UserPtr u); + +				typedef std::shared_ptr<xsltStylesheet> xsltSSPtr; +				xsltSSPtr news; +				xsltSSPtr signup; +		}; +	} +} + +#endif + diff --git a/gentoobrowse-api/service/notifications/xslt/base.xslt b/gentoobrowse-api/service/notifications/xslt/base.xslt new file mode 100644 index 0000000..9db7752 --- /dev/null +++ b/gentoobrowse-api/service/notifications/xslt/base.xslt @@ -0,0 +1,61 @@ +<?xml version="1.0" encoding="UTF-8" ?> +<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" +		xmlns:project2="http://project2.randomdan.homeip.net" exclude-result-prefixes="project2" > +  <xsl:output encoding="utf-8" method="html" media-type="text/html" indent="yes" /> + +  <xsl:template match="/*"> +		<xsl:text disable-output-escaping="yes"><!DOCTYPE html>
</xsl:text> +		<html lang="en"> +			<head> +				<meta charset="utf-8" /> +				<link href="http://gentoobrowse.randomdan.homeip.net/css/bootstrap.min.css" rel="stylesheet" media="screen" /> +				<link href="http://gentoobrowse.randomdan.homeip.net/css/bootstrap-responsive.min.css" rel="stylesheet" media="screen" /> +				<link href="http://gentoobrowse.randomdan.homeip.net/css/general.css" rel="stylesheet" media="screen" /> +				<title><xsl:call-template name="title" /></title> +			</head> +			<body data-spy="scroll" data-target=".bs-docs-sidebar"> +				<!-- nav --> +				<nav class="navbar navbar-inverse navbar-fixed-top"> +					<div class="navbar-inner"> +						<div class="container"> +							<a class="brand" href="http://gentoobrowse.randomdan.homeip.net/">Gentoo Browse</a> +						</div> +					</div> +				</nav> + +				<!-- page content --> +				<header class="jumbotron subhead"> +					<div class="container"> +						<h1> +							<xsl:call-template name="title" /> +						</h1> +					</div> +				</header> +				<div class="container"> +					<article class="row"> +						<xsl:call-template name="content" /> +						<section class="signoff"> +						</section> +					</article> +				</div> + +				<!-- footer --> +				<footer class="footer"> +					<div class="container"> +						<p>Gentoo Browse is not an official Gentoo website. The name "Gentoo" and the "g" logo are trademarks of the Gentoo Foundation, Inc.</p> +					</div> +				</footer> + +				<script src="http://code.jquery.com/jquery-latest.js"></script> +				<script src="http://gentoobrowse.randomdan.homeip.net/js/bootstrap.min.js"></script> +				<script src="http://gentoobrowse.randomdan.homeip.net/js/js.js"></script> +			</body> +		</html> +  </xsl:template> + +  <xsl:template name="head"> +  </xsl:template> + +  <xsl:template name="header"> +  </xsl:template> +</xsl:stylesheet> diff --git a/gentoobrowse-api/service/notifications/xslt/news.xslt b/gentoobrowse-api/service/notifications/xslt/news.xslt new file mode 100644 index 0000000..6b2ea29 --- /dev/null +++ b/gentoobrowse-api/service/notifications/xslt/news.xslt @@ -0,0 +1,57 @@ +<?xml version="1.0" encoding="UTF-8"?> +<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:fn="http://exslt.org/strings" xmlns:project2="http://project2.randomdan.homeip.net" version="1.0" exclude-result-prefixes="project2 fn"> +  <xsl:import href="base.xslt"/> +  <xsl:output encoding="utf-8" method="html" media-type="text/html" indent="yes"/> + +  <xsl:template name="title">Latest news</xsl:template> + +	<xsl:template name="content"> +		<xsl:variable name="packages" select="packages/package[packageid = ../../ebuilds/ebuild/packageid]"/> +		<xsl:variable name="categories" select="categories/category[categoryid = $packages/categoryid]"/> +		<section> +			<h2 class="page-header">New versions of tracked packages</h2> +			<div class="span3 bs-docs-sidebar"> +				<ul class="nav nav-list bs-docs-sidenav"> +					<xsl:for-each select="$categories"> +						<xsl:sort select="name" data-type="text" order="ascending"/> +						<li> +							<a> +								<xsl:attribute name="href">http://gentoobrowse.randomdan.homeip.net/packages/<xsl:value-of select="name" /></xsl:attribute> +								<i class="icon-chevron-right"></i> +								<xsl:value-of select="name" /> +							</a> +						</li> +					</xsl:for-each> +				</ul> +			</div> +			<div class="span9"> +				<xsl:for-each select="$categories"> +					<xsl:sort select="name" data-type="text" order="ascending"/> +					<xsl:variable name="category" select="." /> +					<xsl:for-each select="$packages[categoryid = current()/categoryid]"> +						<xsl:sort select="name" data-type="text" order="ascending"/> +						<xsl:variable name="package" select="." /> +						<p> +							<a> +								<xsl:attribute name="href">http://gentoobrowse.randomdan.homeip.net/packages/<xsl:value-of select="$category/name" /></xsl:attribute> +								<xsl:value-of select="$category/name" /> +							</a> +							/ +							<a> +								<xsl:attribute name="href">http://gentoobrowse.randomdan.homeip.net/packages/<xsl:value-of select="$category/name" />/<xsl:value-of select="name" /></xsl:attribute> +								<xsl:value-of select="name" /> +							</a> +							: +							<xsl:value-of select="description" /> +						</p> +						<xsl:for-each select="../../ebuilds/ebuild[packageid = current()/packageid]"> +							<p> +								v<xsl:value-of select="version" /> +							</p> +						</xsl:for-each> +					</xsl:for-each> +				</xsl:for-each> +			</div> +		</section> +	</xsl:template> +</xsl:stylesheet> diff --git a/gentoobrowse-api/service/notifications/xslt/signup.xslt b/gentoobrowse-api/service/notifications/xslt/signup.xslt new file mode 100644 index 0000000..e579835 --- /dev/null +++ b/gentoobrowse-api/service/notifications/xslt/signup.xslt @@ -0,0 +1,24 @@ +<?xml version="1.0" encoding="UTF-8" ?> +<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"> +	<xsl:import href="base.xslt"/> + +  <xsl:template name="title">Welcome to Gentoo Browse</xsl:template> + +  <xsl:template name="content"> +		<section> +			<h2 class="page-header">Confirmation</h2> +			<p> +				Click <a> +					<xsl:attribute name="href"> +						<xsl:text>http://gentoobrowse.randomdan.homeip.net/user/verify/</xsl:text> +						<xsl:value-of select="verifyguid" /> +					</xsl:attribute> +					<xsl:text>http://gentoobrowse.randomdan.homeip.net/user/verify/</xsl:text> +					<xsl:value-of select="verifyguid" /> +				</a> to confirm your account. +			</p> +			<p>Or alternatively, copy and paste this code into the confirmation page: <xsl:value-of select="verifyguid" /></p> +		</section> +  </xsl:template> +</xsl:stylesheet> + diff --git a/gentoobrowse-api/service/notifications/xsltStreamSerializer.cpp b/gentoobrowse-api/service/notifications/xsltStreamSerializer.cpp new file mode 100644 index 0000000..5a71b35 --- /dev/null +++ b/gentoobrowse-api/service/notifications/xsltStreamSerializer.cpp @@ -0,0 +1,82 @@ +#include "xsltStreamSerializer.h" +#include <libxslt/xsltInternals.h> +#include <libxml/HTMLtree.h> +#include <factory.impl.h> +#include <processPipes.h> +#include <sys/wait.h> + +namespace Gentoo { +	static int xmlstrmclosecallback(void * context) +	{ +		((std::ostream*)context)->flush(); +		return 0; +	} + +	static int xmlstrmwritecallback(void * context, const char * buffer, int len) +	{ +		((std::ostream*)context)->write(buffer, len); +		return len; +	} + +	XsltStreamSerializer::XsltStreamSerializer(Gentoo::EmailPtr e, xsltStylesheet * ss) : +		Slicer::XmlDocumentSerializer(doc), +		mail(e), +		doc(nullptr), +		stylesheet(ss) +	{ +	} + +	XsltStreamSerializer::~XsltStreamSerializer() +	{ +		delete doc; +	} + +	void +	XsltStreamSerializer::Serialize(Slicer::ModelPartForRootPtr mp) +	{ +		Slicer::XmlDocumentSerializer::Serialize(mp); +		auto result = std::shared_ptr<xmlDoc>(xsltApplyStylesheet(stylesheet, doc->cobj(), nullptr), xmlFreeDoc); +		if (!result) { +			throw xmlpp::exception("Failed to apply XSL transform"); +		} +		appendText(result.get()); +		appendHtml(result.get()); +	} + +	void XsltStreamSerializer::appendHtml(xmlDoc * result) const +	{ +		std::stringstream strm; +		xmlOutputBufferPtr buf = xmlOutputBufferCreateIO(xmlstrmwritecallback, xmlstrmclosecallback, &strm, NULL); +		htmlDocContentDumpFormatOutput(buf, result, "utf-8", 0); +		xmlOutputBufferClose(buf); +		mail->body.push_back(new Gentoo::MimePart("text/html", strm.str())); +	} + +	void XsltStreamSerializer::appendText(xmlDoc * result) const +	{ +		std::stringstream strm; +		std::vector<std::string> callLynx; +		callLynx.push_back("/usr/bin/lynx"); +		callLynx.push_back("-dump"); +		callLynx.push_back("-stdin"); +		std::string widthArg = "-width=105"; +		callLynx.push_back(widthArg); +		AdHoc::System::ProcessPipes fds(callLynx, true, true, false); +		FILE * lynxIn = fdopen(fds.fdIn(), "w"); +		// Fixed encoding as we want the result to go back into a ustring +		htmlNodeDumpFileFormat(lynxIn, result, xmlDocGetRootElement(result), "utf-8", 0); +		fclose(lynxIn); +		char buf[1024]; +		int r; +		while ((r = read(fds.fdOut(), buf, sizeof(buf))) > 0) { +			strm.write(buf, r); +		} +		int status; +		waitpid(fds.pid(), &status, 0); +		if (status != 0) { +			throw std::runtime_error("Lynx failed"); +		} +		mail->body.push_back(new Gentoo::MimePart("text/plain", strm.str())); +	} +} + diff --git a/gentoobrowse-api/service/notifications/xsltStreamSerializer.h b/gentoobrowse-api/service/notifications/xsltStreamSerializer.h new file mode 100644 index 0000000..492b954 --- /dev/null +++ b/gentoobrowse-api/service/notifications/xsltStreamSerializer.h @@ -0,0 +1,28 @@ +#ifndef ICESPIDER_CORE_XSLTSTREAMSERIALIZER_H +#define ICESPIDER_CORE_XSLTSTREAMSERIALIZER_H + +#include <slicer/xml/serializer.h> +#include <visibility.h> +#include <libxslt/transform.h> +#include <notifications.h> + +namespace Gentoo { +	class DLL_PUBLIC XsltStreamSerializer : public Slicer::XmlDocumentSerializer { +		public: +			XsltStreamSerializer(Gentoo::EmailPtr, xsltStylesheet *); +			~XsltStreamSerializer(); + +			void Serialize(Slicer::ModelPartForRootPtr mp) override; + +		protected: +			void appendHtml(xmlDoc *) const; +			void appendText(xmlDoc *) const; + +			Gentoo::EmailPtr mail; +			xmlpp::Document * doc; +			xsltStylesheet * stylesheet; +	}; +} + +#endif + diff --git a/gentoobrowse-api/unittests/Jamfile.jam b/gentoobrowse-api/unittests/Jamfile.jam index 92501c0..70fa90f 100644 --- a/gentoobrowse-api/unittests/Jamfile.jam +++ b/gentoobrowse-api/unittests/Jamfile.jam @@ -98,6 +98,14 @@ run  	: testUsers ;  run +	testNotifications.cpp +	: : : +	<dependency>../db/schema.sql +	<define>BOOST_TEST_DYN_LINK +	<library>testCommon +	: testNotifications ; + +run  	testBugs.cpp  	: : :  	<dependency>../db/schema.sql diff --git a/gentoobrowse-api/unittests/mockDefs.cpp b/gentoobrowse-api/unittests/mockDefs.cpp index 70f5b69..eeab5f2 100644 --- a/gentoobrowse-api/unittests/mockDefs.cpp +++ b/gentoobrowse-api/unittests/mockDefs.cpp @@ -2,6 +2,9 @@  #include <definedDirs.h>  Service::Service() : +	IceTray::DryIce({ +			"--GentooBrowseAPI.MailServer=smtp.random.lan:25", +		}),  	PQ::Mock("user=postgres dbname=postgres", "GentooBrowseAPI", {  			rootDir.parent_path() / "db" / "schema.sql",  			rootDir / "data.sql" }) @@ -22,6 +25,8 @@ Maintenance::Maintenance() :  TestClient::TestClient() :  	m(getProxy<Gentoo::MaintenancePrx>("maintenance")),  	p(getProxy<Gentoo::PortagePrx>("portage")), +	n(getProxy<Gentoo::NotificationsPrx>("notifications")), +	ms(getProxy<Gentoo::MailServerPrx>("mailserver")),  	u(getProxy<Gentoo::UsersPrx>("users"))  {  } diff --git a/gentoobrowse-api/unittests/mockDefs.h b/gentoobrowse-api/unittests/mockDefs.h index 9a35c1f..a0c044a 100644 --- a/gentoobrowse-api/unittests/mockDefs.h +++ b/gentoobrowse-api/unittests/mockDefs.h @@ -7,6 +7,7 @@  #include <portage.h>  #include <users.h>  #include <maintenance.h> +#include <notifications.h>  #include <selectcommandUtil.impl.h>  class DLL_PUBLIC Service : public IceTray::DryIce, PQ::Mock { @@ -25,6 +26,8 @@ class DLL_PUBLIC TestClient : public IceTray::DryIceClient {  		Gentoo::MaintenancePrx m;  		Gentoo::PortagePrx p; +		Gentoo::NotificationsPrx n; +		Gentoo::MailServerPrx ms;  		Gentoo::UsersPrx u;  }; diff --git a/gentoobrowse-api/unittests/testNotifications.cpp b/gentoobrowse-api/unittests/testNotifications.cpp new file mode 100644 index 0000000..81b28d0 --- /dev/null +++ b/gentoobrowse-api/unittests/testNotifications.cpp @@ -0,0 +1,102 @@ +#define BOOST_TEST_MODULE TestNotifications +#include <boost/test/unit_test.hpp> + +#include "mockDefs.h" +#include <fstream> + +BOOST_GLOBAL_FIXTURE( Service ); + +BOOST_FIXTURE_TEST_SUITE(tp, TestClient); + +bool +isHtml(const std::string & body) +{ +	return body.find("<html") != std::string::npos; +} + +void +save(const std::string & name, Gentoo::EmailPtr e) +{ +	std::ofstream text("/tmp/" + name + ".txt"); +	text << e->body[0]->payload; +	std::ofstream html("/tmp/" + name + ".html"); +	html << e->body[1]->payload; +} + +void +commonAssert(Gentoo::EmailPtr e) +{ +	BOOST_REQUIRE_EQUAL(e->body.size(), 2); +	BOOST_REQUIRE_EQUAL(e->body[0]->mimetype, "text/plain"); +	BOOST_REQUIRE(!isHtml(e->body[0]->payload)); +	BOOST_REQUIRE_EQUAL(e->body[1]->mimetype, "text/html"); +	BOOST_REQUIRE(isHtml(e->body[1]->payload)); +} + +#if 0 +BOOST_AUTO_TEST_CASE( testSend ) +{ +	Gentoo::NewUserPtr u = new Gentoo::NewUser(1, "testuser", "Test User", "dangoodliffe@gmail.com", "some-guid"); +	auto e = n->getSignup(u); +	BOOST_REQUIRE(e); +	ms->sendEmail(e); +} +#endif + +BOOST_AUTO_TEST_CASE( testSignup ) +{ +	Gentoo::NewUserPtr u = new Gentoo::NewUser(1, "testuser", "Test User", "test@user.com", "some-guid"); +	auto e = n->getSignup(u); +	BOOST_REQUIRE(e); +	BOOST_REQUIRE_EQUAL(e->subject, "Gentoo Browse: Welcome"); +	BOOST_REQUIRE_EQUAL(e->from.name, "Gentoo Browse"); +	BOOST_REQUIRE_EQUAL(e->from.address, "noreply@gentoobrowse.randomdan.homeip.net"); +	BOOST_REQUIRE_EQUAL(e->to.name, "Test User"); +	BOOST_REQUIRE_EQUAL(e->to.address, "test@user.com"); +	commonAssert(e); +	save("signup", e); +	for (auto p : e->body) { +		BOOST_REQUIRE(p->payload.find("Welcome to Gentoo Browse") != std::string::npos); +		BOOST_REQUIRE(p->payload.find("http://gentoobrowse.randomdan.homeip.net/user/verify/some-guid") != std::string::npos); +		BOOST_REQUIRE(p->payload.find("confirmation page: some-guid") != std::string::npos); +	} +} + +BOOST_AUTO_TEST_CASE( testNews ) +{ +	Gentoo::UserPtr u = new Gentoo::User(1, "testuser", "Test User", "test@user.com"); +	Gentoo::NewsContent nc { +		{ +			new Gentoo::Category(1, "app-test", "Test cat1"), +			new Gentoo::Category(2, "sys-test", "Test cat2"), +			new Gentoo::Category(3, "no-show", "Unused cat") +		}, +		{ +			new Gentoo::Package(1, 1, "app1", "first-seen", "Test app desc one", "Test summary 1", IceUtil::None, IceUtil::None, IceUtil::None, IceUtil::None), +			new Gentoo::Package(2, 1, "app2", "first-seen", "Test app desc two", "Test summary 1", IceUtil::None, IceUtil::None, IceUtil::None, IceUtil::None), +			new Gentoo::Package(3, 2, "app3", "first-seen", "Test app desc three", "Test summary 1", IceUtil::None, IceUtil::None, IceUtil::None, IceUtil::None), +			new Gentoo::Package(4, 2, "app4", "first-seen", "Test app no-show", "Test summary 1", IceUtil::None, IceUtil::None, IceUtil::None, IceUtil::None) +		}, +		{ +			new Gentoo::Ebuild(1, 1, 1, "1.0a", "slot", "first-seen", "last-mod", IceUtil::None), +			new Gentoo::Ebuild(2, 1, 1, "1.1a", "slot", "first-seen", "last-mod", IceUtil::None), +			new Gentoo::Ebuild(3, 1, 1, "1.2a", "slot", "first-seen", "last-mod", IceUtil::None), +			new Gentoo::Ebuild(4, 2, 1, "2.0a", "slot", "first-seen", "last-mod", IceUtil::None), +			new Gentoo::Ebuild(5, 3, 1, "3.0a", "slot", "first-seen", "last-mod", IceUtil::None), +			new Gentoo::Ebuild(6, 2, 1, "2.1a", "slot", "first-seen", "last-mod", IceUtil::None), +			new Gentoo::Ebuild(7, 3, 1, "3.1a", "slot", "first-seen", "last-mod", IceUtil::None) +		} +	}; +	auto e = n->getNews(u, nc); +	commonAssert(e); +	save("news", e); +	for (auto p : e->body) { +		BOOST_REQUIRE(p->payload.find("Latest news") != std::string::npos); +		// BOOST_REQUIRE(p->payload.find("http://gentoobrowse.randomdan.homeip.net/packages/app-test") != std::string::npos); +		// BOOST_REQUIRE(p->payload.find("http://gentoobrowse.randomdan.homeip.net/packages/app-test/app") != std::string::npos); +		BOOST_REQUIRE(p->payload.find("no-show") == std::string::npos); +	} +} + +BOOST_AUTO_TEST_SUITE_END() + | 
