Skip to content
This repository has been archived by the owner on Dec 11, 2018. It is now read-only.

Iván Pérez #31

Open
wants to merge 43 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
43 commits
Select commit Hold shift + click to select a range
8da3fb7
First review: cleaning and formating code.
Apr 15, 2018
997241e
Removing unnecessary exceptions.
Apr 15, 2018
bb5f429
Conmverting attribute to local variable.
Apr 15, 2018
999042c
Cleaning tweet entity.
Apr 15, 2018
cf7597a
Minor format change.
Apr 15, 2018
43896a6
Updating to final some attributes.
Apr 15, 2018
6ad91e0
Cleaning the publish tweet command.
Apr 15, 2018
e18738d
First refactor to port-adapter design.
Apr 15, 2018
2eea920
First refactor to port-adapter design.
Apr 15, 2018
cbed609
Moving persistence logic to infrastructure package and improving test.
Apr 15, 2018
6570bec
Extracting interface (port) for tweet repository.
Apr 15, 2018
83aca51
Use streams for list all tweets implementation.
Apr 15, 2018
a7d5c38
Removing from application dependency on Tweet entity at infrastructure.
Apr 15, 2018
01e1fb2
Extracting interface (port) for metric service.
Apr 15, 2018
239b248
Extracting interface (port) for tweet service.
Apr 15, 2018
507821f
Refactor of publish tweet method, improve logic and add tests.
Apr 15, 2018
0d65d04
Refactor to use wrapper pattern at tweet service implementation.
Apr 15, 2018
9e0664f
Refactor to replace tweet repository with another wrapped tweet service.
Apr 15, 2018
7fb7c9b
Removing Spring dependence from application layer.
Apr 15, 2018
530ef5b
Moving services interfaces (ports) to domain layer.
Apr 15, 2018
cda1382
Add basic spring command bus.
Apr 15, 2018
8e4db0e
Add publish tweet command handler.
Apr 15, 2018
613b27d
Add lists all tweets command and handler.
Apr 15, 2018
4743de1
Refactor command bus configuration, split between tweet and infrastru…
Apr 15, 2018
609f113
The list must be sorted by publication date in descending order.
Apr 15, 2018
43f0f58
Ignoring URLs at tweet's content.
Apr 15, 2018
b8d638f
Ignoring URLs at tweet's content.
Apr 15, 2018
5f8584e
Renaming.
Apr 15, 2018
61b6a66
Publish tweet test for tweets with URLs.
Apr 15, 2018
f7c6c6e
Removing publication date from tweet response (it's a requirement I f…
Apr 15, 2018
593db90
Refactoring tests to increase execution speed.
Apr 15, 2018
6b2286f
Discard tweet command and handler.
Apr 16, 2018
5a5dc6d
Rename database package to repository.
Apr 16, 2018
9267b4c
Rename test method.
Apr 16, 2018
30911c2
Discarded tweets will not be shown in the published tweet list.
Apr 16, 2018
a5f5495
List all discarded tweets command and handler.
Apr 16, 2018
b040fa0
Ordering methods.
Apr 16, 2018
b536a90
Define a global exception handler.
Apr 16, 2018
0154996
Use Spring magic.
Apr 16, 2018
647f15b
Use HttpStatus constants instead of magic numbers.
Apr 16, 2018
0fda3c9
Model URLs at database.
Apr 16, 2018
9e99e57
The response body format should be identical to the published tweets …
Apr 17, 2018
1eff55b
Adding missing metrics and improving metrics verifications.
Apr 19, 2018
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 4 additions & 5 deletions src/main/java/com/scmspain/MsFcTechTestApplication.java
Original file line number Diff line number Diff line change
@@ -1,18 +1,17 @@
package com.scmspain;

import com.scmspain.configuration.InfrastructureConfiguration;
import com.scmspain.configuration.TweetConfiguration;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;

@Configuration
@EnableAutoConfiguration
@Import({TweetConfiguration.class, InfrastructureConfiguration.class})
@ComponentScan(basePackageClasses = { MsFcTechTestApplication.class })
public class MsFcTechTestApplication {

public static void main(String[] args) {
SpringApplication.run(MsFcTechTestApplication.class, args);
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
package com.scmspain.application.services;

import java.util.List;

import com.scmspain.domain.MetricService;
import com.scmspain.domain.TweetNotFoundException;
import com.scmspain.domain.TweetService;
import com.scmspain.domain.model.TweetResponse;

/**
* Tweet service implementation that adds metric to another wrapped tweet service.
*/
public class TweetMetricService implements TweetService {

private final MetricService metricService;
private final TweetService tweetService;

/**
* Constructor.
*
* @param tweetService Tweet service to wrap.
* @param metricService Metrics service.
*/
public TweetMetricService(final TweetService tweetService, final MetricService metricService) {
this.tweetService = tweetService;
this.metricService = metricService;
}

@Override
public Long publish(String publisher, String text) {
metricService.incrementPublishedTweets();
return tweetService.publish(publisher, text);
}

@Override
public List<TweetResponse> listAll() {
metricService.incrementTimesQueriedTweets();
return tweetService.listAll();
}

@Override
public void discard(Long tweetId) throws TweetNotFoundException {
metricService.incrementDiscardedTweets();
tweetService.discard(tweetId);
}

@Override
public List<TweetResponse> listAllDiscarded() {
metricService.incrementTimesQueriedDiscardedTweets();
return tweetService.listAllDiscarded();
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
package com.scmspain.application.services;

import java.util.List;

import com.scmspain.domain.TweetNotFoundException;
import com.scmspain.domain.TweetService;
import com.scmspain.domain.model.TweetResponse;

/**
* Tweet service implementation that adds validation to another wrapped tweet service.
*/
public class TweetValidationService implements TweetService {

private static final int MAX_CHARACTERS = 140;

private final TweetService tweetService;
private final UrlRemover urlRemover;

/**
* Constructor.
*
* @param tweetService Tweet service to wrap.
*/
public TweetValidationService(final TweetService tweetService) {
this.tweetService = tweetService;
this.urlRemover = new UrlRemover();
}

@Override
public Long publish(final String publisher, final String text) {
if (publisherEmpty(publisher)) {
throw new IllegalArgumentException("Publisher must not be empty");
}
if (textEmpty(text)) {
throw new IllegalArgumentException("Tweet must not be empty");
}
if (textTooLong(text)) {
throw new IllegalArgumentException("Tweet must not be greater than " + MAX_CHARACTERS + " characters");
}
return this.tweetService.publish(publisher, text);
}

@Override
public List<TweetResponse> listAll() {
return tweetService.listAll();
}

@Override
public void discard(Long tweetId) throws TweetNotFoundException {
if (tweetId == null) {
throw new IllegalArgumentException("Tweet identifier must not be empty");
}
this.tweetService.discard(tweetId);
}

@Override
public List<TweetResponse> listAllDiscarded() {
return tweetService.listAllDiscarded();
}

private boolean textEmpty(final String text) {
return text == null || text.isEmpty();
}

private boolean publisherEmpty(final String publisher) {
return publisher == null || publisher.isEmpty();
}

private boolean textTooLong(final String text) {
return urlRemover.removeUrls(text).length() > MAX_CHARACTERS;
}

}
73 changes: 73 additions & 0 deletions src/main/java/com/scmspain/application/services/UrlExtractor.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
package com.scmspain.application.services;

import java.util.ArrayList;
import java.util.List;
import java.util.Optional;

/**
* Extract URLs from a String for some protocols.
*/
public class UrlExtractor {

private static final String WHITE_SPACE = " ";
private static final String HTTP_PROTOCOL = "http://";
private static final String HTTPS_PROTOCOL = "https://";

private String text;
private List<String> urls = new ArrayList<>();

public UrlExtractor(final String text) {
this.text = text;
this.extractUrls();
}

public String getText() {
return text;
}

public List<String> getUrls() {
return urls;
}

private void extractUrls() {
extractUrls(HTTP_PROTOCOL);
extractUrls(HTTPS_PROTOCOL);
}

private void extractUrls(final String protocol) {
Optional<String> url = extractUrl(protocol);
if (url.isPresent()) {
text = text.replace(url.get(), getEscapeIndex(urls.size()));
urls.add(url.get());
extractUrl(protocol);
}
}

private Optional<String> extractUrl(String protocol) {
int urlStart = text.indexOf(protocol);
if (urlStart != -1) {
int urlEnd = text.indexOf(WHITE_SPACE, urlStart);
if (urlEnd != -1) {
return Optional.of(text.substring(urlStart, urlEnd));
} else {
return Optional.of(text.substring(urlStart));
}
}
return Optional.empty();
}

public static String rebuild(String text, List<String> urls) {
for (int i = 0; i < urls.size(); i++) {
String target = getEscapeIndex(i);
while (text.contains(target)) {
text = text.replace(target, urls.get(i));
}
}
return text;
}

private static String getEscapeIndex(int index) {
return "{" + index + "}";
}

}
40 changes: 40 additions & 0 deletions src/main/java/com/scmspain/application/services/UrlRemover.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
package com.scmspain.application.services;

/**
* Remove URLs from a String for some protocols.
*/
class UrlRemover {

private static final String WHITE_SPACE = " ";
private static final String HTTP_PROTOCOL = "http://";
private static final String HTTPS_PROTOCOL = "https://";

/**
* Given a text removes the URLs contained on it.
*
* @param text Text.
* @return Text without URLs.
*/
String removeUrls(final String text) {
String cleanText = removeUrls(text, HTTP_PROTOCOL);
return removeUrls(cleanText, HTTPS_PROTOCOL);
}

private String removeUrls(final String text, final String protocol) {
if (text.contains(protocol)) {
String newText = removeUrl(text, protocol);
return removeUrls(newText, protocol);
}
return text;
}

private String removeUrl(String text, String protocol) {
int urlStart = text.indexOf(protocol);
int urlEnd = text.indexOf(WHITE_SPACE, urlStart);
if (urlEnd == -1) {
return text.substring(0, urlStart);
}
return text.substring(0, urlStart) + text.substring(urlEnd + 1);
}

}
22 changes: 0 additions & 22 deletions src/main/java/com/scmspain/configuration/TweetConfiguration.java

This file was deleted.

41 changes: 0 additions & 41 deletions src/main/java/com/scmspain/controller/TweetController.java

This file was deleted.

This file was deleted.

28 changes: 28 additions & 0 deletions src/main/java/com/scmspain/domain/MetricService.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
package com.scmspain.domain;

/**
* Metric service.
*/
public interface MetricService {

/**
* Increment by one the published tweets metrics.
*/
void incrementPublishedTweets();

/**
* Increment by one the times queried tweets metrics.
*/
void incrementTimesQueriedTweets();

/**
* Increment by one the discarded tweets metrics.
*/
void incrementDiscardedTweets();

/**
* Increment by one the times queried discarded tweets metrics.
*/
void incrementTimesQueriedDiscardedTweets();

}
11 changes: 11 additions & 0 deletions src/main/java/com/scmspain/domain/TweetNotFoundException.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
package com.scmspain.domain;

import com.scmspain.domain.command.CommandException;

public class TweetNotFoundException extends CommandException {

public TweetNotFoundException(Long id) {
super(String.format("Tweet with identifier %s not found", id));
}

}
Loading