Grails mail plugin delivery to console / log in development mode - email

I am using Grails with the mail plugin. In development mode I would like the mails my applications sends to be rendered (to catch bugs in the templates) but printed to the console.
The only option I found was
grails.mail.disabled=true
But, according to the documentation:
this will treat any call to mailService.sendMail() as a no-op
which is not quite what I want, since I want developers to be able to click on our confirmation links (for which I need the mail output).
Is there a nice way to "deliver" the mails to the console? I am aware that I could override the to-field with another address, but I'd rather not involve our mailserver when testing.

You could create your own dummy implementation of mailService as a class in src/groovy and then put some conditional code in grails-app/conf/spring/resources.groovy to define your dummy service only in development mode. The very simplest I can think of would be
src/groovy/com/example/DummyMailService.groovy
package com.example
import org.springframework.mail.MailMessage
class DummyMailService {
MailMessage sendMail(Closure callable) {
callable.delegate = new CallPrinter()
callable.call()
return null
}
}
class CallPrinter {
def methodMissing(name, args) {
println "${name}: ${args}"
}
}
grails-app/conf/spring/resources.groovy
import grails.util.Environment
beans = {
if(Environment.current == Environment.DEVELOPMENT) {
mailService(com.example.DummyMailService)
}
}
Obviously you could be cleverer, e.g. using a logger rather than println, or switching the dummy service based on a config option rather than just the environment (so you can turn on mail in dev mode later on when you specifically want to test it).
If you want to be able to handle body(view:'...', model:[:]) calls that render templates you could do that using the groovyPageRenderer:
class DummyMailService {
def callPrinter
MailMessage sendMail(Closure callable) {
callable.delegate = callPrinter
callable.call()
return null
}
}
class CallPrinter {
def groovyPageRenderer
def methodMissing(name, args) {
println "${name}: ${args}"
}
void body(Map params) {
methodMissing("body", groovyPageRenderer.render(params))
}
}
and in resources.groovy
mailService(com.example.DummyMailService) {
callPrinter = { com.example.CallPrinter p ->
groovyPageRenderer = ref('groovyPageRenderer')
}
}

GreenMail plugin is the closest to what you'd like that I can think of (see the plugin here). It will render the emails in a web page (the plugin provides a controller), not sure if you could set up Log4j to log the email to the console...

Related

Recommended way to register custom serializer with StateManager

In the pre-GA version of Service Fabric I was registering a custom serializer like this:
protected override IReliableStateManager CreateReliableStateManager()
{
IReliableStateManager result = new ReliableStateManager(
new ReliableStateManagerConfiguration(
onInitializeStateSerializersEvent: InitializeStateSerializers));
return result;
}
private Task InitializeStateSerializers()
{
StateManager.TryAddStateSerializer(new KFOBinarySerializer());
return Task.FromResult(false);
}
However, the CreateReliableStateManager method was removed in the GA version. I've struggled to get something working in its place. Currently I'm calling
StateManager.TryAddStateSerializer(new KFOBinarySerializer());
from within the service's RunAsync method, which appears to work fine.
What is the recommended way to register a custom serializer?
TryAddStateSerializer is deprecated. Anyone know if this is because custom serialization support will go away or if it will simply be supported through some other mechanism?
You can create the state manager in the StatefulService's constructor (full example here):
class MyService : StatefulService
{
public MyService(StatefulServiceContext serviceContext)
: base(serviceContext, CreateReliableStateManager()) { }
private static IReliableStateManager CreateReliableStateManager() { ... }
}
Regarding the deprecated API, Microsoft says it's safe to use, but it will change in the future.

NLog callback possible?

I am in the process of converting a home grown logging system to NLog and am wondering if there is a way to add an event to a Logger or otherwise support a mechanism where when I log a message I can get a callback with the final formatted message and the LogLevel. I currently use something like this to send server messages back to a connected client.
Thx
This is an MCVE of what I was talking about in the comments. Create a target that accepts some callback functions:
[Target("MyFirst")]
public sealed class MyFirstTarget : TargetWithLayout
{
private readonly Action<string>[] _callbacks;
public MyFirstTarget(params Action<string>[] callbacks)
{
_callbacks = callbacks;
}
protected override void Write(LogEventInfo logEvent)
{
foreach (var callback in _callbacks)
{
callback(logEvent.FormattedMessage);
}
}
}
Configure NLog to use the target. I do this programmatically since the callbacks are passed in the constructor. You can also configure the target in the NLog.config, but your target will need to be a singleton then so you can register the callbacks in code.
class Program
{
public static void Main()
{
LogManager.Configuration.AddTarget("MyFirst", new MyFirstTarget(s => Debug.WriteLine(s)));
var logger = LogManager.GetCurrentClassLogger();
logger.Debug("test");
}
}
With no other NLog configuration (copy this code into an empty project and add the NLog nuget package), this will emit a message to your debug window.

Unit Testing MVVMLight Messenger

Is it possible to write a Unit Test that calls the Messenger.Default.Register method and then write an Assertion to be used by the Action?
I would like to determine if my ViewModel is sending the correct message after calling an Execute on one of my Commands.
I have tried writing the Assert.AreEqual as the Action however this doesn't seem to be working correctly.
Sounds like a job for mocking! Assuming you're passing in the messenger interface to your viewmodel (because dependency inversion is a Good Thing, for this very reason), your code should look something like this if I understand you correctly:
public class YourViewModel
{
readonly IMessenger messenger;
public YourViewModel(IMessenger messenger)
{
this.messenger = messenger;
// setup of your delegate command to call Execute
}
void Execute(object parameter)
{
messenger.Send(new YourMessageType());
}
}
Then in your unit test you'd mock the messenger and verify that the right method is called, which in this case is Send. So, using the popular mocking framework Moq:
public class YourViewModelTests
{
[Test]
public void Execute_Always_SendsYourMessageType()
{
// arrange
var mockRepository = new MockRepository(MockBehavior.Loose);
var mockMessenger = mockRepository.Create<IMessenger>();
var systemUnderTest = new YourViewModel(mockMessenger.Object);
// act
systemUnderTest.YourCommand.Execute(null);
// assert
mockMessenger.Verify(p => p.Send<YourMessageType>(
It.Is(m => /* return true if it's the right message */)));
}
}
Usually I'd move the just about all of the "arrange" phase into a test setup method, but you should get the idea.
If you'd still like to do it without mocking the messenger and also use Messenger.Default, you can do the following:
public class YourViewModelTests
{
[Test]
public void Execute_Always_SendsYourMessageType()
{
// arrange
var systemUnderTest = new YourViewModel();
// Set the action to store the message that was sent
YourMessageType actual;
Messenger.Default.Register<YourMessageType>(this, t => actual = t);
// act
systemUnderTest.YourCommand.Execute(null);
// assert
YourMessageType expected = /* set up your expected message */;
Assert.That(actual, Is.EqualTo(expected));
}
}
Alternatively, for each test it is possible to create a separate copy of the Messenger. For the runtime you want to use the Default instance of the Messenger, but for Unit Tests, as I said, create a separate copy for each test:
return new GalaSoft.MvvmLight.Messaging.Messenger(); // Unit Tests
return GalaSoft.MvvmLight.Messaging.Messenger.Default; // Runtime
Otherwise one might end up re-inventing the wheel, since in more complex situations where there is a need to test ViewModel communications, you will have to manage Messenger subscribers, message types an so on. Then probably writing unit tests for the messenger mock making sure it works in the same way as the original messenger. There is nothing in the engine of the Messenger that should be different when comparing Runtime and Test executions.
So for testing a factory returns the same instance of the Messenger. Test method subscribes and waits, ViewModel publishes; then Test accepts and exits. Otherwise Test times out and reports an error. I found this approach more "closer to reality" than mocking the messenger and verifying through the mock that the method was called.

Play! 2.0 Scala - Accessing global object

I've declared an object which gets instantiated on application start. I want to access it inside a controller, which is part of a plugin. I want to be able to use that plugin, but I can't seem to get past the first part -- finding the MyWebsocketConnection object. None of the examples show how to do this. I don't want to inject into the controller because I'm writing a plugin (I saw static examples of how to do that somewhere).
Global.scala, plugin application \app\Global.scala
object Global extends GlobalSettings {
object MyWebsocketConnection {
val logger = // return something that gets instantiated once, like websocket object for logging to ui
}
class MyWebsocketConnection {
import MyWebsocketConnection.logger
}
override def onStart(app: Application) {
Logger.info("Application has started...");
}
}
My custom logging plugin controller:
MyLogger.Scala, plugin application \app\controllers\MyLogger.scala
object MyLogger {
def info(message: String) = {
// THIS CAN'T BE FOUND ?
// MyWebsocketConnection.logger.send(message)
}
}
So, from the Play! 2.0 app that references the plugin, I would (probably) do something like below, but I can't even get past the part before this:
MyFutureController.scala, another Play! application \app\controllers\MyFutureController.scala
object MyFutureController extends Controller {
def someRandomMethod = Action {
// Custom logging
MyLogger.info("Here's my log message!");
Ok("This documentation stinks!")
}
}
There is also workaround #3: move your Global class to a package and specify its fully qualified name in application.conf file, like so:
global= my.packaged.Global
The problem is that your Global objects resides in default package. And in Java, classes from default package can't be referenced from other packages, they are accessible only within the same package (default).
I see two workarounds of this problem.
Move MyWebsocketConnection to some named package (say config) so it can be accessible in your application.
object MyLogger {
def info(message: String) = {
config.MyWebsocketConnection.logger.send(message)
}
}
Move your whole application into single package (but it is a lot of pain)
foo
|--controllers
|--models
|--views
|--Global.scala
Then Global object will resides in foo package and will be accessible within application.

Grails: Integration testing the Mail Plugin

I'm trying to integration test a class that uses the Mail Plugin. When I run my test (grails test-app -integration EmailerIntegration) I get the error:
Could not locate mail body layouts/_email. Is it in a plugin? If so you must pass the plugin name in the [plugin] variable
Is there some initialization code I'm missing from the setUp method of my test case?
Here is the code for the test case:
package company
import grails.test.*
class EmailerIntegrationTests extends GrailsUnitTestCase {
protected void setUp() {
super.setUp()
}
protected void tearDown() {
super.tearDown()
}
void testSomething() {
User owner = new User()
owner.displayName = "Bob"
owner.email = "bob#yahoo.com"
Emailer emailer = new Emailer()
emailer.sendReadyEmail(owner)
}
}
Here is the code for the class being tested:
package company
import org.apache.log4j.Logger;
import org.codehaus.groovy.grails.commons.ApplicationHolder;
import org.springframework.context.ApplicationContext;
class Emailer {
private Logger log = Logger.getLogger(this.getClass());
ApplicationContext ctx = (ApplicationContext)ApplicationHolder.getApplication().getMainContext();
def mailService = ctx.getBean("mailService");
def sendReadyEmail = { owner ->
mailService.sendMail {
to owner.email
subject "Ready to go"
body( view:"layouts/_email", model:[ownerInstance:owner])
}
}
}
Thanks,
Everett
After looking at the plugin author's own tests for the mail plugin at https://github.com/gpc/grails-mail/blob/master/test/integration/org/grails/mail/MailServiceTests.groovy I realized that the paths in the values for the view parameter all begin with a '/'. I changed my method to
def sendReadyEmail = { owner ->
mailService.sendMail {
to owner.email
subject "Ready to go"
body( view:"/layouts/_email", model:[ownerInstance:owner])
}
And now it works in integration tests and normal program execution.
The body parameter in the sendMail(..) method is a map with the keys view, model, and plugin. A value for plugin is required, and points to some other, supporting, plugin, for instance, the name "email-confirmation" for that corresponding plugin.
Your error message is thrown in org.grails.mail.MailMessageBuilder.renderMailView(Object, Object, Object). You can find this class in your Grails project's plugin folder.
Unfortunately, I haven't found too much documentation on the Mail plugin. Thus, at the moment, I cannot easily tell about how to use the aforementioned supporting plugins. If you can't get forward, however, I might try to further investigate. Thanks