From b6171b57984d227a9a1845352ffeb786d1076d69 Mon Sep 17 00:00:00 2001 From: Adam Siemion Date: Sun, 23 Apr 2017 10:45:55 +0200 Subject: [PATCH 1/2] Elegant wannabe solution --- build.gradle | 24 +- config/checkstyle/checkstyle-google.xml | 323 ++++++++++++++++++ config/checkstyle/checkstyle.xml | 219 ++++++++++++ .../contest/adamsiemion/FacebookImage.java | 27 ++ .../LICENSE} | 2 +- .../contest/adamsiemion/OpenGraphImage.java | 9 + .../adamsiemion/ParsedOpenGraphImage.java | 17 + .../contest/adamsiemion/WebPage.java | 28 ++ .../contest/adamsiemion/WebPageAddress.java | 33 ++ .../contest/your_github_login/Readme.txt | 12 - .../contest/adamsiemion/DocumentStub.groovy | 31 ++ .../FacebookImageIntegrationTest.groovy | 28 ++ .../adamsiemion/FacebookImageTest.groovy | 52 +++ .../adamsiemion/WebPageAddressTest.groovy | 20 ++ .../contest/adamsiemion/WebPageTest.groovy | 42 +++ 15 files changed, 852 insertions(+), 15 deletions(-) create mode 100644 config/checkstyle/checkstyle-google.xml create mode 100644 config/checkstyle/checkstyle.xml create mode 100644 src/main/java/com/softwaremill/java_fp_example/contest/adamsiemion/FacebookImage.java rename src/main/java/com/softwaremill/java_fp_example/contest/{your_github_login/LICENSE-template.txt => adamsiemion/LICENSE} (95%) create mode 100644 src/main/java/com/softwaremill/java_fp_example/contest/adamsiemion/OpenGraphImage.java create mode 100644 src/main/java/com/softwaremill/java_fp_example/contest/adamsiemion/ParsedOpenGraphImage.java create mode 100644 src/main/java/com/softwaremill/java_fp_example/contest/adamsiemion/WebPage.java create mode 100644 src/main/java/com/softwaremill/java_fp_example/contest/adamsiemion/WebPageAddress.java delete mode 100644 src/main/java/com/softwaremill/java_fp_example/contest/your_github_login/Readme.txt create mode 100644 src/test/groovy/com/softwaremill/java_fp_example/contest/adamsiemion/DocumentStub.groovy create mode 100644 src/test/groovy/com/softwaremill/java_fp_example/contest/adamsiemion/FacebookImageIntegrationTest.groovy create mode 100644 src/test/groovy/com/softwaremill/java_fp_example/contest/adamsiemion/FacebookImageTest.groovy create mode 100644 src/test/groovy/com/softwaremill/java_fp_example/contest/adamsiemion/WebPageAddressTest.groovy create mode 100644 src/test/groovy/com/softwaremill/java_fp_example/contest/adamsiemion/WebPageTest.groovy 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-google.xml b/config/checkstyle/checkstyle-google.xml new file mode 100644 index 0000000..eec1778 --- /dev/null +++ b/config/checkstyle/checkstyle-google.xml @@ -0,0 +1,323 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 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'] + } +} From f11c5792ab2fdc43b763cd88f3f8fded4265c279 Mon Sep 17 00:00:00 2001 From: Adam Siemion Date: Sun, 23 Apr 2017 20:40:31 +0200 Subject: [PATCH 2/2] removed checkstyle-google.xml --- config/checkstyle/checkstyle-google.xml | 323 ------------------------ 1 file changed, 323 deletions(-) delete mode 100644 config/checkstyle/checkstyle-google.xml diff --git a/config/checkstyle/checkstyle-google.xml b/config/checkstyle/checkstyle-google.xml deleted file mode 100644 index eec1778..0000000 --- a/config/checkstyle/checkstyle-google.xml +++ /dev/null @@ -1,323 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -