diff --git a/build.gradle b/build.gradle
index 23c6e1d..67cf692 100644
--- a/build.gradle
+++ b/build.gradle
@@ -20,10 +20,30 @@ dependencies {
compile 'ch.qos.logback:logback-classic:1.0.1'
compile 'ch.qos.logback:logback-core:1.0.1'
- compileOnly "org.projectlombok:lombok:1.16.10"
+ compileOnly 'org.projectlombok:lombok:1.16.10'
testCompile 'org.spockframework:spock-core:1.0-groovy-2.4'
testCompile 'org.codehaus.groovy:groovy:2.4.7'
testCompile 'cglib:cglib:3.2.2'
-
+ testCompile 'org.objenesis:objenesis:2.5'
+ testCompile 'com.google.code.findbugs:annotations:3.0.1'
}
+
+apply plugin: 'pmd'
+apply plugin: 'checkstyle'
+checkstyleMain.source = 'src/main/java/com/softwaremill/java_fp_example/contest'
+checkstyle {
+ toolVersion = '7.6.1'
+}
+
+apply plugin: 'findbugs'
+tasks.withType(FindBugs) {
+ reports {
+ xml.enabled = false
+ html.enabled = true
+ }
+}
+
+apply plugin: 'jacoco'
+test { finalizedBy jacocoTestReport }
+
diff --git a/config/checkstyle/checkstyle.xml b/config/checkstyle/checkstyle.xml
new file mode 100644
index 0000000..221d70b
--- /dev/null
+++ b/config/checkstyle/checkstyle.xml
@@ -0,0 +1,219 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/main/java/com/softwaremill/java_fp_example/contest/adamsiemion/FacebookImage.java b/src/main/java/com/softwaremill/java_fp_example/contest/adamsiemion/FacebookImage.java
new file mode 100644
index 0000000..c57e06a
--- /dev/null
+++ b/src/main/java/com/softwaremill/java_fp_example/contest/adamsiemion/FacebookImage.java
@@ -0,0 +1,27 @@
+package com.softwaremill.java_fp_example.contest.adamsiemion;
+
+import static com.softwaremill.java_fp_example.contest.adamsiemion.OpenGraphImage.DEFAULT_IMAGE;
+import static java.util.Objects.requireNonNull;
+
+import com.softwaremill.java_fp_example.contest.adamsiemion.WebPageAddress.WebPageDownloadException;
+
+public class FacebookImage {
+
+ private final WebPageAddress webPageAddress;
+
+ public FacebookImage(final WebPageAddress webPageAddress) {
+ this.webPageAddress = requireNonNull(webPageAddress);
+ }
+
+ public String extractImageAddress() {
+ return getImage().content();
+ }
+
+ private OpenGraphImage getImage() {
+ try {
+ return webPageAddress.download().openGraphHeadImages().getOrElse(DEFAULT_IMAGE);
+ } catch (WebPageDownloadException e) {
+ return DEFAULT_IMAGE;
+ }
+ }
+}
diff --git a/src/main/java/com/softwaremill/java_fp_example/contest/your_github_login/LICENSE-template.txt b/src/main/java/com/softwaremill/java_fp_example/contest/adamsiemion/LICENSE
similarity index 95%
rename from src/main/java/com/softwaremill/java_fp_example/contest/your_github_login/LICENSE-template.txt
rename to src/main/java/com/softwaremill/java_fp_example/contest/adamsiemion/LICENSE
index df01d55..f61b81c 100644
--- a/src/main/java/com/softwaremill/java_fp_example/contest/your_github_login/LICENSE-template.txt
+++ b/src/main/java/com/softwaremill/java_fp_example/contest/adamsiemion/LICENSE
@@ -1,6 +1,6 @@
MIT License
-Copyright (c) 2017
+Copyright (c) 2017 adamsiemion
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
diff --git a/src/main/java/com/softwaremill/java_fp_example/contest/adamsiemion/OpenGraphImage.java b/src/main/java/com/softwaremill/java_fp_example/contest/adamsiemion/OpenGraphImage.java
new file mode 100644
index 0000000..bd2b345
--- /dev/null
+++ b/src/main/java/com/softwaremill/java_fp_example/contest/adamsiemion/OpenGraphImage.java
@@ -0,0 +1,9 @@
+package com.softwaremill.java_fp_example.contest.adamsiemion;
+
+import com.softwaremill.java_fp_example.DefaultImage;
+
+interface OpenGraphImage {
+ String content();
+
+ OpenGraphImage DEFAULT_IMAGE = () -> DefaultImage.DEFAULT_IMAGE;
+}
diff --git a/src/main/java/com/softwaremill/java_fp_example/contest/adamsiemion/ParsedOpenGraphImage.java b/src/main/java/com/softwaremill/java_fp_example/contest/adamsiemion/ParsedOpenGraphImage.java
new file mode 100644
index 0000000..363037c
--- /dev/null
+++ b/src/main/java/com/softwaremill/java_fp_example/contest/adamsiemion/ParsedOpenGraphImage.java
@@ -0,0 +1,17 @@
+package com.softwaremill.java_fp_example.contest.adamsiemion;
+
+import org.jsoup.nodes.Element;
+
+class ParsedOpenGraphImage implements OpenGraphImage {
+
+ private final Element imageTag;
+
+ ParsedOpenGraphImage(final Element imageTag) {
+ this.imageTag = imageTag;
+ }
+
+ @Override
+ public String content() {
+ return imageTag.attr("content");
+ }
+}
diff --git a/src/main/java/com/softwaremill/java_fp_example/contest/adamsiemion/WebPage.java b/src/main/java/com/softwaremill/java_fp_example/contest/adamsiemion/WebPage.java
new file mode 100644
index 0000000..a992f3f
--- /dev/null
+++ b/src/main/java/com/softwaremill/java_fp_example/contest/adamsiemion/WebPage.java
@@ -0,0 +1,28 @@
+package com.softwaremill.java_fp_example.contest.adamsiemion;
+
+import static java.util.Objects.requireNonNull;
+
+import javaslang.collection.List;
+import org.jsoup.nodes.Document;
+import org.jsoup.nodes.Element;
+
+class WebPage {
+
+ private static final String OPENGRAPH_IMAGE_TAG = "og:image";
+
+ private final Document document;
+
+ WebPage(final Document document) {
+ this.document = requireNonNull(document);
+ }
+
+ List openGraphHeadImages() {
+ return List.ofAll(head().getElementsByTag("meta"))
+ .filter(e -> OPENGRAPH_IMAGE_TAG.equals(e.attr("property")))
+ .map(ParsedOpenGraphImage::new);
+ }
+
+ private Element head() {
+ return document.head();
+ }
+}
diff --git a/src/main/java/com/softwaremill/java_fp_example/contest/adamsiemion/WebPageAddress.java b/src/main/java/com/softwaremill/java_fp_example/contest/adamsiemion/WebPageAddress.java
new file mode 100644
index 0000000..de62c2e
--- /dev/null
+++ b/src/main/java/com/softwaremill/java_fp_example/contest/adamsiemion/WebPageAddress.java
@@ -0,0 +1,33 @@
+package com.softwaremill.java_fp_example.contest.adamsiemion;
+
+import static java.util.Objects.requireNonNull;
+
+import java.io.IOException;
+import java.net.URL;
+import org.jsoup.Jsoup;
+
+class WebPageAddress {
+
+ private static final int DOWNLOAD_TIMEOUT_IN_MS = 10_000;
+
+ private final String url;
+
+ static class WebPageDownloadException extends Exception {
+
+ WebPageDownloadException(final String message, final IOException cause) {
+ super(message, cause);
+ }
+ }
+
+ WebPageAddress(final String url) {
+ this.url = requireNonNull(url);
+ }
+
+ WebPage download() throws WebPageDownloadException {
+ try {
+ return new WebPage(Jsoup.parse(new URL(url), DOWNLOAD_TIMEOUT_IN_MS));
+ } catch (IOException e) {
+ throw new WebPageDownloadException("Could not download page " + url, e);
+ }
+ }
+}
diff --git a/src/main/java/com/softwaremill/java_fp_example/contest/your_github_login/Readme.txt b/src/main/java/com/softwaremill/java_fp_example/contest/your_github_login/Readme.txt
deleted file mode 100644
index cb42b52..0000000
--- a/src/main/java/com/softwaremill/java_fp_example/contest/your_github_login/Readme.txt
+++ /dev/null
@@ -1,12 +0,0 @@
-Przykładowy pakiet z rozwiązaniem
-
-
-
-### WAŻNE ###
-
-Poprosimy o wykonanie następujących kroków:
-1. Skopiuj plik LICENSE-template do swojego pakietu
-2. Zmień mu nazwę na LICENSE
-3. Zapoznaj się z licencją (używamy MIT License) oraz "podpisz" ją zamieniając w zawartości ""
- na swój login na GitHubie.
-4. Tak zmodyfikowany plik LICENSE załącz w swoim Pull Requeście.
\ No newline at end of file
diff --git a/src/test/groovy/com/softwaremill/java_fp_example/contest/adamsiemion/DocumentStub.groovy b/src/test/groovy/com/softwaremill/java_fp_example/contest/adamsiemion/DocumentStub.groovy
new file mode 100644
index 0000000..36a7648
--- /dev/null
+++ b/src/test/groovy/com/softwaremill/java_fp_example/contest/adamsiemion/DocumentStub.groovy
@@ -0,0 +1,31 @@
+package com.softwaremill.java_fp_example.contest.adamsiemion
+
+import org.jsoup.Jsoup
+
+class DocumentStub {
+ def withHead
+ def properties = []
+
+ def withMetaPropertyImage(String value) {
+ withHead = true
+ properties << ""
+ this
+ }
+
+ def withHead() {
+ withHead = true
+ this
+ }
+
+ def build() {
+ String html = ""
+ if (withHead)
+ html += ""
+ html += properties.join()
+ if (withHead)
+ html += ""
+ html += ""
+ Jsoup.parse(html)
+ }
+
+}
diff --git a/src/test/groovy/com/softwaremill/java_fp_example/contest/adamsiemion/FacebookImageIntegrationTest.groovy b/src/test/groovy/com/softwaremill/java_fp_example/contest/adamsiemion/FacebookImageIntegrationTest.groovy
new file mode 100644
index 0000000..6ec3c88
--- /dev/null
+++ b/src/test/groovy/com/softwaremill/java_fp_example/contest/adamsiemion/FacebookImageIntegrationTest.groovy
@@ -0,0 +1,28 @@
+package com.softwaremill.java_fp_example.contest.adamsiemion
+
+import spock.lang.Specification
+import spock.lang.Unroll
+
+import static com.softwaremill.java_fp_example.DefaultImage.DEFAULT_IMAGE
+
+class FacebookImageIntegrationTest extends Specification {
+
+ @Unroll
+ def "should test FacebookImage with url #url"() {
+ given:
+ def facebookImage = new FacebookImage(new WebPageAddress(url))
+
+ when:
+ def imageAddress = facebookImage.extractImageAddress()
+
+ then:
+ imageAddress == expectedImageUrl
+
+ where:
+ url || expectedImageUrl
+ "https://softwaremill.com/the-wrong-abstraction-recap/" || "https://softwaremill.com/images/uploads/2017/02/street-shoe-chewing-gum.0526d557.jpg"
+ "https://softwaremill.com/using-kafka-as-a-message-queue/" || "https://softwaremill.com/images/uploads/2017/02/kmq.93f842cf.png"
+ "https://twitter.com/softwaremill" || DEFAULT_IMAGE
+ "http://i-do-not-exist.pl" || DEFAULT_IMAGE
+ }
+}
diff --git a/src/test/groovy/com/softwaremill/java_fp_example/contest/adamsiemion/FacebookImageTest.groovy b/src/test/groovy/com/softwaremill/java_fp_example/contest/adamsiemion/FacebookImageTest.groovy
new file mode 100644
index 0000000..824278e
--- /dev/null
+++ b/src/test/groovy/com/softwaremill/java_fp_example/contest/adamsiemion/FacebookImageTest.groovy
@@ -0,0 +1,52 @@
+package com.softwaremill.java_fp_example.contest.adamsiemion
+
+import com.softwaremill.java_fp_example.DefaultImage
+import edu.umd.cs.findbugs.annotations.SuppressFBWarnings
+import spock.lang.Specification
+
+class FacebookImageTest extends Specification {
+
+ def "should extract the image address when web page contains one image address"() {
+ given:
+ def document = new DocumentStub().withMetaPropertyImage('1').build()
+ WebPageAddress webPageAddress = Mock()
+ webPageAddress.download() >> new WebPage(document)
+ def facebookImage = new FacebookImage(webPageAddress)
+
+ when:
+ def imageAddress = facebookImage.extractImageAddress()
+
+ then:
+ imageAddress == '1'
+ }
+
+ def "should extract the default image address when web page does not contain any image addresses"() {
+ given:
+ def document = new DocumentStub().build()
+ WebPageAddress webPageAddress = Mock()
+ webPageAddress.download() >> new WebPage(document)
+ def facebookImage = new FacebookImage(webPageAddress)
+
+ when:
+ def imageAddress = facebookImage.extractImageAddress()
+
+ then:
+ imageAddress == DefaultImage.DEFAULT_IMAGE
+ }
+
+ @SuppressFBWarnings("SE_NO_SERIALVERSIONID")
+ def "should extract the default image address when downloading of the web page fails"() {
+ given:
+ WebPageAddress webPageAddress = Mock()
+ webPageAddress.download() >> {
+ throw new WebPageAddress.WebPageDownloadException("download failed", new IOException())
+ }
+ def facebookImage = new FacebookImage(webPageAddress)
+
+ when:
+ def imageAddress = facebookImage.extractImageAddress()
+
+ then:
+ imageAddress == DefaultImage.DEFAULT_IMAGE
+ }
+}
diff --git a/src/test/groovy/com/softwaremill/java_fp_example/contest/adamsiemion/WebPageAddressTest.groovy b/src/test/groovy/com/softwaremill/java_fp_example/contest/adamsiemion/WebPageAddressTest.groovy
new file mode 100644
index 0000000..f088ad1
--- /dev/null
+++ b/src/test/groovy/com/softwaremill/java_fp_example/contest/adamsiemion/WebPageAddressTest.groovy
@@ -0,0 +1,20 @@
+package com.softwaremill.java_fp_example.contest.adamsiemion
+
+import spock.lang.Specification
+
+import static com.softwaremill.java_fp_example.contest.adamsiemion.WebPageAddress.*
+
+class WebPageAddressTest extends Specification {
+
+ def "should throw WebPageDownloadException when an incorrect url is provided"() {
+ given:
+ WebPageAddress webPageAddress = new WebPageAddress("wrong_url")
+
+ when:
+ webPageAddress.download()
+
+ then:
+ def e = thrown(WebPageDownloadException)
+ e.message == "Could not download page wrong_url"
+ }
+}
diff --git a/src/test/groovy/com/softwaremill/java_fp_example/contest/adamsiemion/WebPageTest.groovy b/src/test/groovy/com/softwaremill/java_fp_example/contest/adamsiemion/WebPageTest.groovy
new file mode 100644
index 0000000..0b36862
--- /dev/null
+++ b/src/test/groovy/com/softwaremill/java_fp_example/contest/adamsiemion/WebPageTest.groovy
@@ -0,0 +1,42 @@
+package com.softwaremill.java_fp_example.contest.adamsiemion
+
+import spock.lang.Specification
+
+class WebPageTest extends Specification {
+
+ def "should return an empty list of images when a document without head is provided"() {
+ given:
+ def document = new DocumentStub().build()
+ def webPage = new WebPage(document)
+
+ when:
+ def images = webPage.openGraphHeadImages()
+
+ then:
+ images.empty
+ }
+
+ def "should return an empty list of images when a document with an empty head is provided"() {
+ given:
+ def document = new DocumentStub().withHead().build()
+ def webPage = new WebPage(document)
+
+ when:
+ def images = webPage.openGraphHeadImages()
+
+ then:
+ images.empty
+ }
+
+ def "should return a list with 2 images when a document with 2 image meta properties is provided"() {
+ given:
+ def document = new DocumentStub().withMetaPropertyImage('1').withMetaPropertyImage('2').build()
+ def webPage = new WebPage(document)
+
+ when:
+ def images = webPage.openGraphHeadImages()
+
+ then:
+ images*.content() == ['1', '2']
+ }
+}