From 743d18017dc4eb93f6cfa57b0c5ad2c6a32e91fe Mon Sep 17 00:00:00 2001 From: Maciej Dobrowolski Date: Thu, 6 Apr 2017 13:20:13 +0200 Subject: [PATCH 1/3] just showing my refactoring --- build.gradle | 7 +++ .../maciejdobrowolski/FacebookImage.java | 61 +++++++++++++++++++ .../contest/maciejdobrowolski/LICENSE.txt | 21 +++++++ .../contest/maciejdobrowolski/MetaEntry.java | 20 ++++++ .../contest/maciejdobrowolski/PageModel.java | 11 ++++ .../FacebookImageSpec.groovy | 26 ++++++++ 6 files changed, 146 insertions(+) create mode 100644 src/main/java/com/softwaremill/java_fp_example/contest/maciejdobrowolski/FacebookImage.java create mode 100644 src/main/java/com/softwaremill/java_fp_example/contest/maciejdobrowolski/LICENSE.txt create mode 100644 src/main/java/com/softwaremill/java_fp_example/contest/maciejdobrowolski/MetaEntry.java create mode 100644 src/main/java/com/softwaremill/java_fp_example/contest/maciejdobrowolski/PageModel.java create mode 100644 src/test/groovy/com/softwaremill/java_fp_example/contest/maciejdobrowolski/FacebookImageSpec.groovy diff --git a/build.gradle b/build.gradle index 23c6e1d..2d8e496 100644 --- a/build.gradle +++ b/build.gradle @@ -10,6 +10,7 @@ targetCompatibility = 1.8 repositories { mavenCentral() + jcenter() } dependencies { @@ -20,6 +21,12 @@ dependencies { compile 'ch.qos.logback:logback-classic:1.0.1' compile 'ch.qos.logback:logback-core:1.0.1' + compile 'com.github.florent37:retrojsoup:1.0.3' + compile 'com.github.florent37:rxjsoup:1.0.3' + compileOnly 'com.github.florent37:retrojsoup-compiler:1.0.3' + compile 'org.jsoup:jsoup:1.10.2' + compile group: 'io.reactivex.rxjava2', name: 'rxjava', version: '2.0.8' + compileOnly "org.projectlombok:lombok:1.16.10" testCompile 'org.spockframework:spock-core:1.0-groovy-2.4' diff --git a/src/main/java/com/softwaremill/java_fp_example/contest/maciejdobrowolski/FacebookImage.java b/src/main/java/com/softwaremill/java_fp_example/contest/maciejdobrowolski/FacebookImage.java new file mode 100644 index 0000000..37a817b --- /dev/null +++ b/src/main/java/com/softwaremill/java_fp_example/contest/maciejdobrowolski/FacebookImage.java @@ -0,0 +1,61 @@ +package com.softwaremill.java_fp_example.contest.maciejdobrowolski; + +import static com.softwaremill.java_fp_example.DefaultImage.DEFAULT_IMAGE; +import static java.util.concurrent.TimeUnit.SECONDS; + +import com.github.florent37.retrojsoup.RetroJsoup; +import io.reactivex.Maybe; +import io.reactivex.ObservableTransformer; +import lombok.extern.slf4j.Slf4j; + +@Slf4j +public class FacebookImage { + + private final static String FACEBOOK_IMAGE_TAG = "og:image"; + private final static int TIMEOUT_IN_SECONDS = 10; + + private final String url; + + public FacebookImage(String url) { + this.url = url; + } + + public Maybe imageAddress() { + return pageModel().metaTags() + .compose(defaultOnTimeout()) + .filter(FacebookImage::isFacebookImageTag) + .compose(firstOrDefault()) + .singleElement() + .map(meta -> meta.content); + } + + private PageModel pageModel() { + return new RetroJsoup.Builder() + .url(url) + .build() + .create(PageModel.class); + } + + private ObservableTransformer defaultOnTimeout() { + return upstream -> upstream.timeout(TIMEOUT_IN_SECONDS, SECONDS) + .doOnError(e -> log.error("Unable to extract og:image from url {}. Problem: {}", url, e.getMessage())) + .onErrorReturn(e -> defaultEntry()); + } + + private static MetaEntry defaultEntry() { + return new MetaEntry(FACEBOOK_IMAGE_TAG, DEFAULT_IMAGE); + } + + private static boolean isFacebookImageTag(MetaEntry metaEntry) { + return FACEBOOK_IMAGE_TAG.equals(metaEntry.attr); + } + + private ObservableTransformer firstOrDefault() { + return upstream -> upstream.firstOrError() + .doOnError(e -> log.warn("No {} found for blog post {}", FACEBOOK_IMAGE_TAG, url)) + .onErrorReturn(e -> defaultEntry()) + .toObservable(); + + } + +} diff --git a/src/main/java/com/softwaremill/java_fp_example/contest/maciejdobrowolski/LICENSE.txt b/src/main/java/com/softwaremill/java_fp_example/contest/maciejdobrowolski/LICENSE.txt new file mode 100644 index 0000000..9cb5672 --- /dev/null +++ b/src/main/java/com/softwaremill/java_fp_example/contest/maciejdobrowolski/LICENSE.txt @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2017 MaciejDobrowolski + +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/maciejdobrowolski/MetaEntry.java b/src/main/java/com/softwaremill/java_fp_example/contest/maciejdobrowolski/MetaEntry.java new file mode 100644 index 0000000..e648060 --- /dev/null +++ b/src/main/java/com/softwaremill/java_fp_example/contest/maciejdobrowolski/MetaEntry.java @@ -0,0 +1,20 @@ +package com.softwaremill.java_fp_example.contest.maciejdobrowolski; + +import com.github.florent37.retrojsoup.annotations.JsoupAttr; + +class MetaEntry { + + @JsoupAttr(value = "meta", attr = "property") + String attr; + + @JsoupAttr(value = "meta", attr = "content") + String content; + + MetaEntry() { + } + + MetaEntry(String attr, String content) { + this.attr = attr; + this.content = content; + } +} diff --git a/src/main/java/com/softwaremill/java_fp_example/contest/maciejdobrowolski/PageModel.java b/src/main/java/com/softwaremill/java_fp_example/contest/maciejdobrowolski/PageModel.java new file mode 100644 index 0000000..ce7dc4e --- /dev/null +++ b/src/main/java/com/softwaremill/java_fp_example/contest/maciejdobrowolski/PageModel.java @@ -0,0 +1,11 @@ +package com.softwaremill.java_fp_example.contest.maciejdobrowolski; + +import com.github.florent37.retrojsoup.annotations.Select; +import io.reactivex.Observable; + +interface PageModel { + + @Select("head meta") + Observable metaTags(); + +} diff --git a/src/test/groovy/com/softwaremill/java_fp_example/contest/maciejdobrowolski/FacebookImageSpec.groovy b/src/test/groovy/com/softwaremill/java_fp_example/contest/maciejdobrowolski/FacebookImageSpec.groovy new file mode 100644 index 0000000..e860893 --- /dev/null +++ b/src/test/groovy/com/softwaremill/java_fp_example/contest/maciejdobrowolski/FacebookImageSpec.groovy @@ -0,0 +1,26 @@ +package com.softwaremill.java_fp_example.contest.maciejdobrowolski + +import spock.lang.Specification +import spock.lang.Unroll + +import static com.softwaremill.java_fp_example.DefaultImage.DEFAULT_IMAGE + +class FacebookImageSpec extends Specification { + + @Unroll + def "should test Maciej's version with address #postAddress"() { + when: + FacebookImage facebookImage = new FacebookImage(postAddress) + + then: + facebookImage.imageAddress().blockingGet() == expectedImageUrl + + where: + postAddress || 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 + } + +} From d07a7e9ddd15522196fd567b14effad50619f59c Mon Sep 17 00:00:00 2001 From: Maciej Dobrowolski Date: Thu, 6 Apr 2017 16:13:45 +0200 Subject: [PATCH 2/3] oh my god! --- build.gradle | 4 ++-- .../contest/maciejdobrowolski/FacebookImage.java | 9 +++------ 2 files changed, 5 insertions(+), 8 deletions(-) diff --git a/build.gradle b/build.gradle index 2d8e496..9b60a52 100644 --- a/build.gradle +++ b/build.gradle @@ -23,9 +23,9 @@ dependencies { compile 'com.github.florent37:retrojsoup:1.0.3' compile 'com.github.florent37:rxjsoup:1.0.3' - compileOnly 'com.github.florent37:retrojsoup-compiler:1.0.3' compile 'org.jsoup:jsoup:1.10.2' - compile group: 'io.reactivex.rxjava2', name: 'rxjava', version: '2.0.8' + compile 'io.reactivex.rxjava2:rxjava:2.0.8' + compileOnly 'com.github.florent37:retrojsoup-compiler:1.0.3' compileOnly "org.projectlombok:lombok:1.16.10" diff --git a/src/main/java/com/softwaremill/java_fp_example/contest/maciejdobrowolski/FacebookImage.java b/src/main/java/com/softwaremill/java_fp_example/contest/maciejdobrowolski/FacebookImage.java index 37a817b..8619e68 100644 --- a/src/main/java/com/softwaremill/java_fp_example/contest/maciejdobrowolski/FacebookImage.java +++ b/src/main/java/com/softwaremill/java_fp_example/contest/maciejdobrowolski/FacebookImage.java @@ -12,6 +12,7 @@ public class FacebookImage { private final static String FACEBOOK_IMAGE_TAG = "og:image"; + private final static MetaEntry FALLBACK_META_ENTRY = new MetaEntry(FACEBOOK_IMAGE_TAG, DEFAULT_IMAGE); private final static int TIMEOUT_IN_SECONDS = 10; private final String url; @@ -39,11 +40,7 @@ private PageModel pageModel() { private ObservableTransformer defaultOnTimeout() { return upstream -> upstream.timeout(TIMEOUT_IN_SECONDS, SECONDS) .doOnError(e -> log.error("Unable to extract og:image from url {}. Problem: {}", url, e.getMessage())) - .onErrorReturn(e -> defaultEntry()); - } - - private static MetaEntry defaultEntry() { - return new MetaEntry(FACEBOOK_IMAGE_TAG, DEFAULT_IMAGE); + .onErrorReturnItem(FALLBACK_META_ENTRY); } private static boolean isFacebookImageTag(MetaEntry metaEntry) { @@ -53,7 +50,7 @@ private static boolean isFacebookImageTag(MetaEntry metaEntry) { private ObservableTransformer firstOrDefault() { return upstream -> upstream.firstOrError() .doOnError(e -> log.warn("No {} found for blog post {}", FACEBOOK_IMAGE_TAG, url)) - .onErrorReturn(e -> defaultEntry()) + .onErrorReturnItem(FALLBACK_META_ENTRY) .toObservable(); } From ae4a1046568a7814194e0291e09c2345b82e9933 Mon Sep 17 00:00:00 2001 From: Maciej Dobrowolski Date: Sat, 8 Apr 2017 23:26:19 +0200 Subject: [PATCH 3/3] shouldn't be a commit message descriptive? --- .../contest/maciejdobrowolski/FacebookImage.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/main/java/com/softwaremill/java_fp_example/contest/maciejdobrowolski/FacebookImage.java b/src/main/java/com/softwaremill/java_fp_example/contest/maciejdobrowolski/FacebookImage.java index 8619e68..637c744 100644 --- a/src/main/java/com/softwaremill/java_fp_example/contest/maciejdobrowolski/FacebookImage.java +++ b/src/main/java/com/softwaremill/java_fp_example/contest/maciejdobrowolski/FacebookImage.java @@ -1,6 +1,7 @@ package com.softwaremill.java_fp_example.contest.maciejdobrowolski; import static com.softwaremill.java_fp_example.DefaultImage.DEFAULT_IMAGE; +import static io.reactivex.schedulers.Schedulers.io; import static java.util.concurrent.TimeUnit.SECONDS; import com.github.florent37.retrojsoup.RetroJsoup; @@ -23,6 +24,7 @@ public FacebookImage(String url) { public Maybe imageAddress() { return pageModel().metaTags() + .subscribeOn(io()) .compose(defaultOnTimeout()) .filter(FacebookImage::isFacebookImageTag) .compose(firstOrDefault()) @@ -52,7 +54,6 @@ private ObservableTransformer firstOrDefault() { .doOnError(e -> log.warn("No {} found for blog post {}", FACEBOOK_IMAGE_TAG, url)) .onErrorReturnItem(FALLBACK_META_ENTRY) .toObservable(); - } }