Skip to content

Commit 21c7181

Browse files
committed
Merge branch 'master' of github.com-s2team:s2team/votesapp
2 parents 8361797 + 8041631 commit 21c7181

File tree

11 files changed

+169
-13
lines changed

11 files changed

+169
-13
lines changed

README.md

Lines changed: 147 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,153 @@ Why Files? See [our diary](http://votesapp.de/10-01-2015/Python_brings_us_back_t
4646

4747
We submit the VotesApp stack to the SpringBoot Content only.
4848

49-
5049
# VotesApp Diary
5150

5251
To keep Readme clean, we moved the diary to: http://votesapp.de
52+
53+
# Highlights
54+
55+
This section describes some highlights of our Backend.
56+
57+
* Diary
58+
* Extensibility with Plugins
59+
* Developer "friendliness"
60+
* @Value(#":}{}$:"#$@}#$!$), - em how was it?
61+
* Environment Variables
62+
* Cough, dude, I need some meds. Really, check my health!
63+
* Profile Configuration
64+
* 1, 2, 3, 4, metric
65+
66+
## Diary
67+
From day zero we tried to put our experience during the contest into a diary.
68+
Today it has more then 10 entries describing the fun we had.
69+
70+
## Extensibility with Plugins
71+
During the contest we had a lot of ideas what else can be done with this.
72+
Okay, ... the most important [Chuck Norris Plugin](https://github.com/s2team/votesapp/blob/master/src/main/java/de/votesapp/commands/plugins/ChuckNorrisCommandPlugin.java) is done.
73+
But there are much more, - like the *Rock-paper-scissors*, *Cinema Moves*,
74+
*Random Cats Videos* (@Kathy, this one is for you <3) or more advanced votings.
75+
76+
For those we provide this interface:
77+
78+
```java
79+
public interface CommandPlugin {
80+
public abstract Optional<Answer> interpret(final GroupMessage message, final Group group);
81+
}
82+
```
83+
84+
In the simplest way it can be implemented like this:
85+
86+
```java
87+
@Service
88+
public class PingCommandPlugin extends TextEqualsWordPlugin {
89+
90+
public static final String[] DEFAULT_RESETS = { "ping", "ping?" };
91+
92+
public PingCommandPlugin() {
93+
super(DEFAULT_RESETS);
94+
}
95+
96+
@Override
97+
public Optional<Answer> matches(final GroupMessage message, final Group group) {
98+
return Optional.of(Answer.intoGroup(group, "Pong!"));
99+
}
100+
}
101+
```
102+
103+
To see more about Plugins, you can checkout [our Diary post](http://votesapp.de/15-01-2015/Make_it_fucking_delightful/) about it.
104+
105+
## Developer "friendliness"
106+
Nobody likes to have tons of configurations to do before start hacking, right?
107+
108+
So we took care that the application can be started without huzzle (excpect lombok...).
109+
Services like MongoDB and Yowsup which aren't usable by default will mocked by "runnable" replacements.
110+
This is done by utilizing the `@Profile` annotations.
111+
When the `default` profile is loaded, [fongo](https://github.com/fakemongo/fongo) is used to replace the real MongoDb
112+
and our `WebClient` replaced all the Yowsup Python stuff.
113+
114+
To use Fongo we only need to import `compile("com.github.fakemongo:fongo:1.5.9")` and provide this neat configuration:
115+
116+
```java
117+
@Profile("!mongo")
118+
@Configuration
119+
static class MongoDbInMemoryConfiguration extends AbstractMongoConfiguration {
120+
121+
@Override
122+
public MappingMongoConverter mappingMongoConverter() throws Exception {
123+
final MappingMongoConverter mappingMongoConverter = super.mappingMongoConverter();
124+
// WhatsApp Keys are containing dots in domains.
125+
mappingMongoConverter.setMapKeyDotReplacement("_");
126+
return mappingMongoConverter;
127+
}
128+
129+
@Override
130+
protected String getDatabaseName() {
131+
return "test";
132+
}
133+
134+
@Override
135+
public Mongo mongo() {
136+
return new Fongo("mongo").getMongo();
137+
}
138+
139+
@Override
140+
protected String getMappingBasePackage() {
141+
return "de.votesapp";
142+
}
143+
}
144+
```
145+
146+
Please give [them](https://github.com/fakemongo/fongo) some fame on Github.
147+
They are doing really great!
148+
149+
The `WebClient` is also activated by the same technique.
150+
There we used the `@Profile("!yowsup")` annotation.
151+
152+
Once you configured both, you can active them with `--spring.profiles.active=yowsup,mongo`, or you just don't :).
153+
154+
## @Value(#":}{}$:"#$@}#$!$), - em how was it?
155+
Okay, if you really use it all the day,
156+
you probably know the syntax (because you understand whats going on).
157+
But if I work with some Students or ppl. new to Spring, they really create every combination of parentheses,
158+
dollars, braces and so on.
159+
160+
With Spring Boot those @ConfigurationProperties` can be used.
161+
162+
![Image of our Solution](diary/highlightConfig.png)
163+
164+
Just create a counterpart of your configuration structure (yml) and it can be autowirred in your application.
165+
That is so nice!
166+
167+
## Environment Variables
168+
Our P-System is deployed using docker.
169+
From there we don't really like to edit property files.
170+
To get rid of them we use environment variables for the mandatory properties.
171+
172+
This makes it also handy to create various Eclipse 8Run Configurations* and an easy deployment.
173+
174+
![Image of our Solution](diary/highlightEnvVars.png)
175+
176+
## *Cough*, dude, I need some meds. Really, check my health!
177+
The most critical (and not by AutoConfiguration monitored) Module in VotesApp is the communication to the Yowsup-Rest Server.
178+
If this services isn't available, VotesApp can't work. Tracking it was really easy with SpringBoot.
179+
180+
Since we Poll for new message all day long, we can use this to detect the health.
181+
182+
We store exceptions, when they occur in an `Optional` field and anounce them over Springs `HealthIndicator`.
183+
184+
![Image of our Solution](diary/highlightHealth.png)
185+
186+
To see the status, you can check `http://localhost:8080/health`.
187+
188+
## Profile Configuration
189+
We really like the feature to have properties per profile.
190+
With this we were able to separate the configuration for mongodb, yowsup and our application.
191+
192+
![Image of our Solution](diary/highlightProperties.png)
193+
194+
## 1, 2, 3, 4, metric
195+
We haven't expand them very much, but there are some metrics about how many messages we processed and how many are answered.
196+
To do this we used Springs `CounterService` from the [GroupMessageListener](https://github.com/s2team/votesapp/blob/master/src%2Fmain%2Fjava%2Fde%2Fvotesapp%2Fgroups%2FGroupMessageListener.java)
197+
198+
As configured by default, they can be checked at `http://localhost:8080/metrics`.

diary/highlightConfig.png

132 KB
Loading

diary/highlightEnvVars.png

136 KB
Loading

diary/highlightHealth.png

165 KB
Loading

diary/highlightProperties.png

11.3 KB
Loading

diary/highlightReactor.png

157 KB
Loading

src/main/java/de/votesapp/api/WhatsAppClientToReactorBridge.java

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,12 @@
11
package de.votesapp.api;
22

3+
import java.util.Optional;
4+
35
import lombok.extern.slf4j.Slf4j;
46

57
import org.springframework.beans.factory.annotation.Autowired;
8+
import org.springframework.boot.actuate.health.Health;
9+
import org.springframework.boot.actuate.health.HealthIndicator;
610
import org.springframework.boot.actuate.metrics.CounterService;
711
import org.springframework.scheduling.annotation.Scheduled;
812

@@ -21,13 +25,14 @@
2125
*/
2226
@Slf4j
2327
@Consumer
24-
public class WhatsAppClientToReactorBridge {
28+
public class WhatsAppClientToReactorBridge implements HealthIndicator {
2529

2630
private final WhatsAppClient whatsAppClient;
2731
private final Reactor rootReactor;
2832
private final CounterService counterService;
2933

3034
private int errorThrottle = 0;
35+
private volatile Optional<WhatsAppConnectionException> whatsAppConnectionException = Optional.empty();
3136

3237
@Autowired
3338
public WhatsAppClientToReactorBridge(final WhatsAppClient whatsAppClient, final Reactor rootReactor, final CounterService counterService) {
@@ -51,6 +56,7 @@ public void pollMessages() throws InterruptedException {
5156
}
5257
}
5358
errorThrottle = 0;
59+
whatsAppConnectionException = Optional.empty();
5460
} catch (final WhatsAppConnectionException e) {
5561
log.error(e.getMessage());
5662
// don't bump the system if it isn't reachable.
@@ -71,4 +77,12 @@ public void sendGroupMessage(final Event<GroupMessage> messageToSend) {
7177
log.info("Send Message: {}", message);
7278
whatsAppClient.sendGroupMessage(message);
7379
}
80+
81+
@Override
82+
public Health health() {
83+
if (whatsAppConnectionException.isPresent()) {
84+
return Health.down(whatsAppConnectionException.get()).build();
85+
}
86+
return Health.up().build();
87+
}
7488
}

src/main/java/de/votesapp/client/YowsupRestClient.java

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,8 +6,6 @@
66
import java.util.HashMap;
77
import java.util.Map;
88

9-
import lombok.extern.slf4j.Slf4j;
10-
119
import org.springframework.beans.factory.annotation.Autowired;
1210
import org.springframework.context.annotation.Profile;
1311
import org.springframework.stereotype.Component;

src/main/java/de/votesapp/commands/plugins/PingCommandPlugin.java

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,6 @@ public PingCommandPlugin() {
1818

1919
@Override
2020
public Optional<Answer> matches(final GroupMessage message, final Group group) {
21-
group.resetVotes();
2221
return Optional.of(Answer.intoGroup(group, "Pong!"));
2322
}
2423

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
spring.data.mongodb:
2+
host: ${MONGO_HOST:localhost}
3+
port: ${MONGO_PORT:27017}
4+
database: ${MONGO_DATABASE:votesapp}
5+
authenticationDatabase: ${MONGO_AUTH_DB:}
6+
username: ${MONGO_USERNAME:}
7+
password: ${MONGO_PASSWORD:}

0 commit comments

Comments
 (0)