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

Refactored and added discarding logic #45

Open
wants to merge 6 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 5 commits
Commits
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
3 changes: 2 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -22,4 +22,5 @@ build/
nbbuild/
dist/
nbdist/
.nb-gradle/
.nb-gradle/
/bin/
6 changes: 3 additions & 3 deletions src/main/java/com/scmspain/MsFcTechTestApplication.java
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
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.Configuration;
import org.springframework.context.annotation.Import;

import com.scmspain.configuration.InfrastructureConfiguration;
import com.scmspain.configuration.TweetConfiguration;

@Configuration
@EnableAutoConfiguration
@Import({TweetConfiguration.class, InfrastructureConfiguration.class})
Expand Down
13 changes: 8 additions & 5 deletions src/main/java/com/scmspain/configuration/TweetConfiguration.java
Original file line number Diff line number Diff line change
@@ -1,22 +1,25 @@
package com.scmspain.configuration;

import com.scmspain.controller.TweetController;
import com.scmspain.services.TweetService;
import org.springframework.boot.actuate.metrics.writer.MetricWriter;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;

import javax.persistence.EntityManager;
import com.scmspain.controller.TweetController;
import com.scmspain.repository.TweetRepository;
import com.scmspain.services.TweetService;

@Configuration
@ComponentScan(basePackages = "com.scmspain.exceptions")
public class TweetConfiguration {
@Bean
public TweetService getTweetService(EntityManager entityManager, MetricWriter metricWriter) {
return new TweetService(entityManager, metricWriter);
public TweetService getTweetService(MetricWriter metricWriter, TweetRepository repository) {
return new TweetService(metricWriter, repository);
}

@Bean
public TweetController getTweetConfiguration(TweetService tweetService) {
return new TweetController(tweetService);
}

}
46 changes: 28 additions & 18 deletions src/main/java/com/scmspain/controller/TweetController.java
Original file line number Diff line number Diff line change
@@ -1,20 +1,27 @@
package com.scmspain.controller;

import com.scmspain.controller.command.PublishTweetCommand;
import com.scmspain.entities.Tweet;
import com.scmspain.services.TweetService;
import org.springframework.web.bind.annotation.*;
import static org.springframework.http.HttpStatus.CREATED;
import static org.springframework.http.HttpStatus.NO_CONTENT;

import java.util.List;

import static org.springframework.http.HttpStatus.BAD_REQUEST;
import static org.springframework.http.HttpStatus.CREATED;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.ResponseStatus;
import org.springframework.web.bind.annotation.RestController;

import com.scmspain.controller.command.DiscardTweetCommand;
import com.scmspain.controller.command.PublishTweetCommand;
import com.scmspain.entities.Tweet;
import com.scmspain.services.TweetService;

@RestController
public class TweetController {
private TweetService tweetService;

private final TweetService tweetService;

public TweetController(TweetService tweetService) {
public TweetController(final TweetService tweetService) {
this.tweetService = tweetService;
}

Expand All @@ -26,16 +33,19 @@ public List<Tweet> listAllTweets() {
@PostMapping("/tweet")
@ResponseStatus(CREATED)
public void publishTweet(@RequestBody PublishTweetCommand publishTweetCommand) {
this.tweetService.publishTweet(publishTweetCommand.getPublisher(), publishTweetCommand.getTweet());
this.tweetService.publishTweet(publishTweetCommand.getPublisher(),
publishTweetCommand.getTweet());
}

@ExceptionHandler(IllegalArgumentException.class)
@ResponseStatus(BAD_REQUEST)
@ResponseBody
public Object invalidArgumentException(IllegalArgumentException ex) {
return new Object() {
public String message = ex.getMessage();
public String exceptionClass = ex.getClass().getSimpleName();
};

@GetMapping("/discarded")
public List<Tweet> listAllDiscardedTweets() {
return this.tweetService.listAllDiscardedTweets();
}

@PostMapping("/discarded")
@ResponseStatus(NO_CONTENT)
public void discardTweet(@RequestBody DiscardTweetCommand discardTweetCommand) {
this.tweetService.discardTweet(discardTweetCommand.getTweetId());
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
package com.scmspain.controller.command;

public class DiscardTweetCommand {

private Long tweetId;

public Long getTweetId() {
return tweetId;
}

public void setTweetId(Long tweetId) {
this.tweetId = tweetId;
}

}
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
package com.scmspain.controller.command;

public class PublishTweetCommand {

private String publisher;
private String tweet;

public String getPublisher() {
public String getPublisher() {
return publisher;
}

Expand All @@ -17,6 +18,7 @@ public String getTweet() {
}

public void setTweet(String tweet) {
this.tweet = tweet;
this.tweet = tweet;
}

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

import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;

@Entity
public class Link {

public static final String REGEX_PATTERN = "\\b(http|https)://\\S+\\b";

@Id
@GeneratedValue
private Long id;

@Column(nullable = false)
private String text;

@Column(nullable = false)
private int position;

public Link() {
}

public Link(String text, int position) {
super();
this.text = text;
this.position = position;
}

public String getLinkText() {
return text;
}

public int getPosition() {
return position;
}

public Long getId() {
return id;
}

public void setId(Long id) {
this.id = id;
}

@Override
public String toString() {
return "Link [linkText=" + text + ", position=" + position + "]";
}

}
91 changes: 89 additions & 2 deletions src/main/java/com/scmspain/entities/Tweet.java
Original file line number Diff line number Diff line change
@@ -1,26 +1,62 @@
package com.scmspain.entities;

import java.time.Instant;
import java.util.List;

import javax.persistence.CascadeType;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;
import javax.persistence.OneToMany;
import javax.validation.constraints.NotNull;
import javax.validation.constraints.Size;

import com.fasterxml.jackson.annotation.JsonIgnore;
import com.fasterxml.jackson.annotation.JsonProperty;

@Entity
public class Tweet {

@Id
@GeneratedValue
private Long id;

@Column(nullable = false)
@NotNull
@Size(min=1)
private String publisher;

@Column(nullable = false, length = 140)
@NotNull
@Size(min=1, max = 140)
private String tweet;

@Column (nullable=true)
private Long pre2015MigrationStatus = 0L;

@Column (nullable=false)
private Instant publicationDateTime;

@Column (nullable=true)
private Instant discardedDateTime;

@Column (nullable=true)
@OneToMany(cascade = {CascadeType.ALL})
private List<Link> links;

public Tweet() {
}

public Long getId() {
public Tweet(String tweet, String publisher, List<Link> links) {
super();
this.tweet = tweet;
this.publisher = publisher;
this.links = links;
this.publicationDateTime = Instant.now();
}

public Long getId() {
return id;
}

Expand All @@ -36,8 +72,9 @@ public void setPublisher(String publisher) {
this.publisher = publisher;
}

@JsonProperty("tweet")
public String getTweet() {
return tweet;
return insertLinksOnTweet(tweet, links);
}

public void setTweet(String tweet) {
Expand All @@ -52,4 +89,54 @@ public void setPre2015MigrationStatus(Long pre2015MigrationStatus) {
this.pre2015MigrationStatus = pre2015MigrationStatus;
}

@JsonIgnore
public Instant getDiscardedDateTime() {
return discardedDateTime;
}

public void setDiscardedDateTime(Instant discardedDateTime) {
this.discardedDateTime = discardedDateTime;
}

@JsonIgnore
public Instant getPublicationDateTime() {
return publicationDateTime;
}

public void setPublicationDateTime(Instant publicationDateTime) {
this.publicationDateTime = publicationDateTime;
}

private String insertLinksOnTweet(String text, List<Link> links) {
if (links.size() == 0)

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This code is a bit complex, is it unit tested? Do you think should be in another class maybe?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Applied same solution than for the PatternExtractor. Created a TextElementInsterter, which is a BiFunction that takes a list of TextElement and a String and returns a String. I have created test cases for TextElementInserter

return text;

char[] letters = text.toCharArray();
StringBuffer sb = new StringBuffer();
int lCounter = 0;
int idxLinks = 0;
int i = 0;
Link link = links.get(idxLinks);
String linkText = link.getLinkText();

while (i < letters.length && idxLinks < links.size()) {
link = links.get(idxLinks);
if (lCounter == link.getPosition()) {
linkText = link.getLinkText();
sb.append(linkText);
lCounter += linkText.length();
++idxLinks;
}
else {
sb.append(letters[i++]);
++lCounter;
}
}

while (i < letters.length)
sb.append(letters[i++]);

return sb.toString();
}

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

import java.util.List;
import java.util.stream.Collectors;

import javax.validation.ConstraintViolationException;
import javax.validation.ValidationException;

import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.context.request.WebRequest;
import org.springframework.web.servlet.config.annotation.EnableWebMvc;

import com.scmspain.controller.TweetController;
import com.scmspain.exceptions.ExceptionResponse;
import com.scmspain.exceptions.ResponseError;

@EnableWebMvc
@ControllerAdvice(assignableTypes = TweetController.class)
public class CustomExceptionHandler {

@ExceptionHandler(Exception.class)
@ResponseBody
public final ResponseEntity<ExceptionResponse> handleAllExceptions(Exception ex, WebRequest request) {
ExceptionResponse exceptionResponse = new ExceptionResponse(ex.getMessage());
return new ResponseEntity<>(exceptionResponse, HttpStatus.INTERNAL_SERVER_ERROR);
}

@ExceptionHandler(ValidationException.class)
@ResponseBody
public final ResponseEntity<ExceptionResponse> invalidValidationException(ValidationException ex) {
if (ex instanceof ConstraintViolationException) {
List<ResponseError> errors = ((ConstraintViolationException) ex).getConstraintViolations().stream()
.map(c -> new ResponseError("" + c.getPropertyPath(), c.getMessage()))
.collect(Collectors.toList());


return new ResponseEntity<>(new ExceptionResponse("Invalid field(s)", errors), HttpStatus.BAD_REQUEST);
}
return new ResponseEntity<>(new ExceptionResponse(ex.getMessage()), HttpStatus.INTERNAL_SERVER_ERROR);
}

@ExceptionHandler(TweetNotFoundException.class)
@ResponseBody
public final ResponseEntity<ExceptionResponse> invalidTweetNotFoundException(TweetNotFoundException ex) {
return new ResponseEntity<>(new ExceptionResponse(ex.getMessage()), HttpStatus.NOT_FOUND);
}

}
Loading