diff --git a/src/main/java/com/softwaremill/java_fp_example/contest/dorzepowski/DefaultImageUrl.java b/src/main/java/com/softwaremill/java_fp_example/contest/dorzepowski/DefaultImageUrl.java new file mode 100644 index 0000000..08deec7 --- /dev/null +++ b/src/main/java/com/softwaremill/java_fp_example/contest/dorzepowski/DefaultImageUrl.java @@ -0,0 +1,9 @@ +package com.softwaremill.java_fp_example.contest.dorzepowski; + +class DefaultImageUrl implements ImageUrl { + + @Override + public String toString() { + return "https://softwaremill.com/images/logo-vertical.023d8496.png"; + } +} diff --git a/src/main/java/com/softwaremill/java_fp_example/contest/dorzepowski/FacebookImage.java b/src/main/java/com/softwaremill/java_fp_example/contest/dorzepowski/FacebookImage.java new file mode 100644 index 0000000..672958e --- /dev/null +++ b/src/main/java/com/softwaremill/java_fp_example/contest/dorzepowski/FacebookImage.java @@ -0,0 +1,40 @@ +package com.softwaremill.java_fp_example.contest.dorzepowski; + +import javaslang.control.Option; + +class FacebookImage implements Image { + + private final static String FACEBOOK_IMAGE_TAG = "og:image"; + + private final ParsedPage parsedPage; + + private FacebookImage(ParsedPage parsedPage) { + this.parsedPage = parsedPage; + } + + static FacebookImage from(ParsedPage parsedPage) { + return new FacebookImage(parsedPage); + } + + @Override + public ImageUrl url() { + return facebookImageUrl().getOrElse(DefaultImageUrl::new); + } + + private Option facebookImageUrl() { + return imageUrlOf(contentFrom(firstImageElement())); + } + + private Option imageUrlOf(Option url) { + return url.map(FacebookImageUrl::new); + } + + private Option contentFrom(Option parsedElements) { + return parsedElements.map(ParsedElement::content); + } + + private Option firstImageElement() { + return parsedPage.elementsByProperty(FACEBOOK_IMAGE_TAG).headOption(); + } + +} diff --git a/src/main/java/com/softwaremill/java_fp_example/contest/dorzepowski/FacebookImageUrl.java b/src/main/java/com/softwaremill/java_fp_example/contest/dorzepowski/FacebookImageUrl.java new file mode 100644 index 0000000..f9f212b --- /dev/null +++ b/src/main/java/com/softwaremill/java_fp_example/contest/dorzepowski/FacebookImageUrl.java @@ -0,0 +1,15 @@ +package com.softwaremill.java_fp_example.contest.dorzepowski; + +class FacebookImageUrl implements ImageUrl { + + private final String url; + + FacebookImageUrl(String url) { + this.url = url; + } + + @Override + public String toString() { + return url; + } +} diff --git a/src/main/java/com/softwaremill/java_fp_example/contest/dorzepowski/Image.java b/src/main/java/com/softwaremill/java_fp_example/contest/dorzepowski/Image.java new file mode 100644 index 0000000..f182a1b --- /dev/null +++ b/src/main/java/com/softwaremill/java_fp_example/contest/dorzepowski/Image.java @@ -0,0 +1,6 @@ +package com.softwaremill.java_fp_example.contest.dorzepowski; + +public interface Image { + + ImageUrl url(); +} diff --git a/src/main/java/com/softwaremill/java_fp_example/contest/dorzepowski/ImageUrl.java b/src/main/java/com/softwaremill/java_fp_example/contest/dorzepowski/ImageUrl.java new file mode 100644 index 0000000..e4f22ec --- /dev/null +++ b/src/main/java/com/softwaremill/java_fp_example/contest/dorzepowski/ImageUrl.java @@ -0,0 +1,5 @@ +package com.softwaremill.java_fp_example.contest.dorzepowski; + +public interface ImageUrl { + +} diff --git a/src/main/java/com/softwaremill/java_fp_example/contest/dorzepowski/LICENSE.txt b/src/main/java/com/softwaremill/java_fp_example/contest/dorzepowski/LICENSE.txt new file mode 100644 index 0000000..87e939d --- /dev/null +++ b/src/main/java/com/softwaremill/java_fp_example/contest/dorzepowski/LICENSE.txt @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2017 dorzepowski + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/src/main/java/com/softwaremill/java_fp_example/contest/dorzepowski/Page.java b/src/main/java/com/softwaremill/java_fp_example/contest/dorzepowski/Page.java new file mode 100644 index 0000000..a85e7c0 --- /dev/null +++ b/src/main/java/com/softwaremill/java_fp_example/contest/dorzepowski/Page.java @@ -0,0 +1,18 @@ +package com.softwaremill.java_fp_example.contest.dorzepowski; + +import java.net.URL; + + +public class Page { + + private final URL pageUrl; + + public Page(URL pageUrl) { + this.pageUrl = pageUrl; + } + + public Image facebookImage() { + return FacebookImage.from(new ParsedPage(pageUrl)); + } + +} diff --git a/src/main/java/com/softwaremill/java_fp_example/contest/dorzepowski/ParsedElement.java b/src/main/java/com/softwaremill/java_fp_example/contest/dorzepowski/ParsedElement.java new file mode 100644 index 0000000..ce18ff8 --- /dev/null +++ b/src/main/java/com/softwaremill/java_fp_example/contest/dorzepowski/ParsedElement.java @@ -0,0 +1,16 @@ +package com.softwaremill.java_fp_example.contest.dorzepowski; + +import org.jsoup.nodes.Element; + +class ParsedElement { + + private final Element element; + + ParsedElement(Element element) { + this.element = element; + } + + String content() { + return element.attr("content"); + } +} diff --git a/src/main/java/com/softwaremill/java_fp_example/contest/dorzepowski/ParsedPage.java b/src/main/java/com/softwaremill/java_fp_example/contest/dorzepowski/ParsedPage.java new file mode 100644 index 0000000..ca377cc --- /dev/null +++ b/src/main/java/com/softwaremill/java_fp_example/contest/dorzepowski/ParsedPage.java @@ -0,0 +1,53 @@ +package com.softwaremill.java_fp_example.contest.dorzepowski; + +import java.io.IOException; +import java.net.URL; +import java.util.function.Function; + +import javaslang.collection.Stream; +import javaslang.control.Try; +import lombok.extern.slf4j.Slf4j; +import org.jsoup.Jsoup; +import org.jsoup.nodes.Document; +import org.jsoup.nodes.Element; +import org.jsoup.select.Elements; + +@Slf4j +class ParsedPage { + + private final static int TEN_SECONDS = 10_000; + + private final URL pageUrl; + + ParsedPage(URL pageUrl) { + this.pageUrl = pageUrl; + } + + Stream elementsByProperty(String property) { + return makeParsedElementsWhen(onPage(foundElementsWithProperty(property))); + } + + private Stream makeParsedElementsWhen(Stream foundElements) { + return foundElements.map(ParsedElement::new); + } + + private Stream onPage(Function toFoundElements) { + return parsePage().flatMap(toFoundElements); + } + + private Function foundElementsWithProperty(String property) { + return document -> document.head().getElementsByAttributeValue("property", property); + } + + private Stream parsePage() { + return Try.of(this::parse).onFailure(this::logParsingProblem).toStream(); + } + + private Document parse() throws IOException { + return Jsoup.parse(pageUrl, TEN_SECONDS); + } + + private void logParsingProblem(Throwable e) { + log.error("Unable to extract og:image from url {}. Problem: {}", pageUrl, e.getMessage()); + } +} diff --git a/src/test/groovy/com/softwaremill/java_fp_example/contest/dorzepowski/FindFacebookImageUrlOnPageSpec.groovy b/src/test/groovy/com/softwaremill/java_fp_example/contest/dorzepowski/FindFacebookImageUrlOnPageSpec.groovy new file mode 100644 index 0000000..22016fb --- /dev/null +++ b/src/test/groovy/com/softwaremill/java_fp_example/contest/dorzepowski/FindFacebookImageUrlOnPageSpec.groovy @@ -0,0 +1,50 @@ +package com.softwaremill.java_fp_example.contest.dorzepowski + +import spock.lang.Specification + +class FindFacebookImageUrlOnPageSpec extends Specification { + + public static + final String DEFAULT_IMAGE = "https://softwaremill.com/images/logo-vertical.023d8496.png" + + + def "Find facebook image url for page that contain such url"() { + given: + def urlWithImage = new URL("https://softwaremill.com/the-wrong-abstraction-recap/") + def page = new Page(urlWithImage) + when: + def facebookImageUrl = page.facebookImage().url() + then: + 'https://softwaremill.com/images/uploads/2017/02/street-shoe-chewing-gum.0526d557.jpg' == facebookImageUrl.toString() + } + + + def "Find default url when retrieving facebook image url from not existing page"() { + given: + def notExistingUrl = new URL("http://i-do-not-exist.pl") + def page = new Page(notExistingUrl) + when: + def facebookImageUrl = page.facebookImage().url() + then: + DEFAULT_IMAGE == facebookImageUrl.toString() + } + + def "Find default url when retrieving facebook image url from page that doesn't contain facebook image"() { + given: + def urlWithoutImage = new URL("https://twitter.com/softwaremill") + def page = new Page(urlWithoutImage) + when: + def facebookImageUrl = page.facebookImage().url() + then: + DEFAULT_IMAGE == facebookImageUrl.toString() + } + + def "Find default url when url is null"() { + given: + def page = new Page(null) + when: + def facebookImageUrl = page.facebookImage().url() + then: + DEFAULT_IMAGE == facebookImageUrl.toString() + } +}