From e6d7f8b56b33577ad8bc990f72c9e95986000e5d Mon Sep 17 00:00:00 2001 From: Wellington Moreno Date: Wed, 30 Nov 2016 00:16:08 -0800 Subject: [PATCH 001/168] Adds project pom --- pom.xml | 376 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 376 insertions(+) create mode 100644 pom.xml diff --git a/pom.xml b/pom.xml new file mode 100644 index 0000000..5106907 --- /dev/null +++ b/pom.xml @@ -0,0 +1,376 @@ + + + 4.0.0 + + tech.redroma.yelp + yelp-api + 1.0-SNAPSHOT + YelpAPI + jar + + https://github.com/RedRoma/YelpJavaClient + + A simple Java Client for interfacing with the Yelp API. + + + + RedRoma, Inc. + http://RedRoma.tech/ + + + + + Wellington Moreno + wellington@redroma.tech + SirWellington + https://github.com/RedRoma + + owner + developer + + + + + + + Apache License, Version 2.0 + http://www.apache.org/licenses/LICENSE-2.0.txt + repo + + + + + GitHub Issues + https://github.com/RedRoma/YelpJavaClient/issues + + + + scm:git:git@github.com:RedRoma/YelpJavaClient.git + scm:git:git@github.com:RedRoma/YelpJavaClient.git + git@github.com:RedRoma/YelpJavaClient.git + + + + 2016 + + + 3.0.3 + + + + + + + + ossrh + https://oss.sonatype.org/content/repositories/snapshots + + + + ossrh + https://oss.sonatype.org/service/local/staging/deploy/maven2/ + + + + + + + + ossrh-snapshot + https://oss.sonatype.org/content/repositories/snapshots + + + + + + + + + + + + + + junit + junit + 4.12 + test + + + + org.mockito + mockito-all + 1.10.19 + test + + + + org.hamcrest + hamcrest-library + 1.3 + test + + + + + + + tech.sirwellington.alchemy + alchemy-annotations + 1.6 + + + + tech.sirwellington.alchemy + alchemy-arguments + 1.4 + + + + tech.sirwellington.alchemy + alchemy-collections + 1.1 + + + + tech.sirwellington.alchemy + alchemy-generator + 1.5 + test + + + + tech.sirwellington.alchemy + alchemy-http + 1.2 + + + + tech.sirwellington.alchemy + alchemy-http-mock + 1.0 + test + + + + tech.sirwellington.alchemy + alchemy-test + 1.5 + test + + + + + + + + org.slf4j + slf4j-api + 1.7.21 + + + + org.slf4j + slf4j-simple + 1.7.21 + test + + + + ch.qos.logback + logback-classic + 1.1.7 + + + + + org.slf4j + slf4j-nop + 1.7.21 + test + + + + + + + com.google.guava + guava + 20.0 + + + + + + + + com.google.code.gson + gson + 2.8.0 + + + + + + + + com.google.inject + guice + 4.1.0 + + + + + + + + + + com.googlecode.maven-download-plugin + download-maven-plugin + 1.3.0 + + + download-javadoc-stylesheet + generate-resources + + wget + + + https://raw.githubusercontent.com/SirWellington/alchemy/master/javadoc-style-sheet.css + ${project.build.directory}/resources/css + false + + + + + + + + org.apache.maven.plugins + maven-javadoc-plugin + 2.10.4 + + false + true + + http://docs.oracle.com/javase/8/docs/api/ + + ${project.build.directory}/resources/css/javadoc-style-sheet.css + + + + attach-javadocs + + jar + + + + + + + + org.apache.maven.plugins + maven-source-plugin + 3.0.1 + + + attach-sources + verify + + jar-no-fork + + + + + + + + + org.jacoco + jacoco-maven-plugin + 0.7.7.201606060606 + + + + **/*Exception.* + + + + + + default-prepare-agent + + prepare-agent + + + + + default-report + prepare-package + + report + + + + + default-check + + check + + + + + + + + + + + + + + + + + release + + + + + + + org.apache.maven.plugins + maven-gpg-plugin + 1.6 + + + sign-artifacts + verify + + sign + + + + + + + + org.sonatype.plugins + nexus-staging-maven-plugin + 1.6.7 + true + + ossrh + https://oss.sonatype.org/ + true + + + + + + + + + + From 436dd33da11b87b52fc463805153c5ce0b3dae3a Mon Sep 17 00:00:00 2001 From: Wellington Moreno Date: Wed, 30 Nov 2016 00:24:27 -0800 Subject: [PATCH 002/168] Updates pom with java source version 1.8 --- pom.xml | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 5106907..10a5597 100644 --- a/pom.xml +++ b/pom.xml @@ -12,6 +12,11 @@ A simple Java Client for interfacing with the Yelp API. + + + 1.8 + 1.8 + RedRoma, Inc. @@ -372,5 +377,4 @@ - From b2045350e2bce1f1a8c0fe112fec3ede1465d64d Mon Sep 17 00:00:00 2001 From: Wellington Moreno Date: Wed, 30 Nov 2016 00:27:44 -0800 Subject: [PATCH 003/168] Adds YelpBusiness POJO --- .../java/tech/redroma/yelp/YelpBusiness.java | 334 ++++++++++++++++++ 1 file changed, 334 insertions(+) create mode 100644 src/main/java/tech/redroma/yelp/YelpBusiness.java diff --git a/src/main/java/tech/redroma/yelp/YelpBusiness.java b/src/main/java/tech/redroma/yelp/YelpBusiness.java new file mode 100644 index 0000000..a17082b --- /dev/null +++ b/src/main/java/tech/redroma/yelp/YelpBusiness.java @@ -0,0 +1,334 @@ + +/* + * Copyright 2016 BlackWholeLabs. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package tech.redroma.yelp; + +import com.google.gson.annotations.SerializedName; +import java.util.List; +import java.util.Objects; +import tech.sirwellington.alchemy.annotations.arguments.Optional; +import tech.sirwellington.alchemy.annotations.concurrency.Mutable; +import tech.sirwellington.alchemy.annotations.concurrency.ThreadUnsafe; +import tech.sirwellington.alchemy.annotations.objects.Pojo; + +/** + * Represents a Yelp Business JSON Object, as documented in the Yelp API. + * + * @see https://www.yelp.com/developers/documentation/v3/business_search + * @author SirWellington + */ +@Pojo +@Mutable +@ThreadUnsafe +public class YelpBusiness +{ + + public String id; + + public String name; + + public String url; + + public int rating; + + public String phone; + + public Boolean isClosed; + + public List categories; + + public int reviewCount; + + public Coordinate coordinates; + + @SerializedName("image_url") + public String imageURL; + + @Optional + public Double distance; + + @Override + public int hashCode() + { + int hash = 7; + hash = 37 * hash + Objects.hashCode(this.id); + hash = 37 * hash + Objects.hashCode(this.name); + hash = 37 * hash + Objects.hashCode(this.url); + hash = 37 * hash + this.rating; + hash = 37 * hash + Objects.hashCode(this.phone); + hash = 37 * hash + Objects.hashCode(this.isClosed); + hash = 37 * hash + Objects.hashCode(this.categories); + hash = 37 * hash + this.reviewCount; + hash = 37 * hash + Objects.hashCode(this.coordinates); + hash = 37 * hash + Objects.hashCode(this.imageURL); + hash = 37 * hash + Objects.hashCode(this.distance); + return hash; + } + + @Override + public boolean equals(Object obj) + { + if (this == obj) + { + return true; + } + if (obj == null) + { + return false; + } + if (getClass() != obj.getClass()) + { + return false; + } + final YelpBusiness other = (YelpBusiness) obj; + if (this.rating != other.rating) + { + return false; + } + if (this.reviewCount != other.reviewCount) + { + return false; + } + if (!Objects.equals(this.id, other.id)) + { + return false; + } + if (!Objects.equals(this.name, other.name)) + { + return false; + } + if (!Objects.equals(this.url, other.url)) + { + return false; + } + if (!Objects.equals(this.phone, other.phone)) + { + return false; + } + if (!Objects.equals(this.imageURL, other.imageURL)) + { + return false; + } + if (!Objects.equals(this.isClosed, other.isClosed)) + { + return false; + } + if (!Objects.equals(this.categories, other.categories)) + { + return false; + } + if (!Objects.equals(this.coordinates, other.coordinates)) + { + return false; + } + if (!Objects.equals(this.distance, other.distance)) + { + return false; + } + return true; + } + + @Pojo + @Mutable + @ThreadUnsafe + public static class Category + { + + public String alias; + public String title; + + @Override + public int hashCode() + { + int hash = 5; + hash = 61 * hash + Objects.hashCode(this.alias); + hash = 61 * hash + Objects.hashCode(this.title); + return hash; + } + + @Override + public boolean equals(Object obj) + { + if (this == obj) + { + return true; + } + if (obj == null) + { + return false; + } + if (getClass() != obj.getClass()) + { + return false; + } + final Category other = (Category) obj; + if (!Objects.equals(this.alias, other.alias)) + { + return false; + } + if (!Objects.equals(this.title, other.title)) + { + return false; + } + return true; + } + + @Override + public String toString() + { + return "Category{" + "alias=" + alias + ", title=" + title + '}'; + } + + } + + @Pojo + @Mutable + @ThreadUnsafe + public static class Coordinate + { + + double latitude; + double longitude; + + @Override + public int hashCode() + { + int hash = 7; + hash = 59 * hash + (int) (Double.doubleToLongBits(this.latitude) ^ (Double.doubleToLongBits(this.latitude) >>> 32)); + hash = 59 * hash + (int) (Double.doubleToLongBits(this.longitude) ^ (Double.doubleToLongBits(this.longitude) >>> 32)); + return hash; + } + + @Override + public boolean equals(Object obj) + { + if (this == obj) + { + return true; + } + if (obj == null) + { + return false; + } + if (getClass() != obj.getClass()) + { + return false; + } + final Coordinate other = (Coordinate) obj; + if (Double.doubleToLongBits(this.latitude) != Double.doubleToLongBits(other.latitude)) + { + return false; + } + if (Double.doubleToLongBits(this.longitude) != Double.doubleToLongBits(other.longitude)) + { + return false; + } + return true; + } + + @Override + public String toString() + { + return "Coordinate{" + "latitude=" + latitude + ", longitude=" + longitude + '}'; + } + + } + + @Pojo + @Mutable + @ThreadUnsafe + public static class Address + { + + public String city; + public String state; + public String country; + public String address1; + public String address2; + public String address3; + public String zipCode; + + @Override + public int hashCode() + { + int hash = 5; + hash = 83 * hash + Objects.hashCode(this.city); + hash = 83 * hash + Objects.hashCode(this.state); + hash = 83 * hash + Objects.hashCode(this.country); + hash = 83 * hash + Objects.hashCode(this.address1); + hash = 83 * hash + Objects.hashCode(this.address2); + hash = 83 * hash + Objects.hashCode(this.address3); + hash = 83 * hash + Objects.hashCode(this.zipCode); + return hash; + } + + @Override + public boolean equals(Object obj) + { + if (this == obj) + { + return true; + } + if (obj == null) + { + return false; + } + if (getClass() != obj.getClass()) + { + return false; + } + final Address other = (Address) obj; + if (!Objects.equals(this.city, other.city)) + { + return false; + } + if (!Objects.equals(this.state, other.state)) + { + return false; + } + if (!Objects.equals(this.country, other.country)) + { + return false; + } + if (!Objects.equals(this.address1, other.address1)) + { + return false; + } + if (!Objects.equals(this.address2, other.address2)) + { + return false; + } + if (!Objects.equals(this.address3, other.address3)) + { + return false; + } + if (!Objects.equals(this.zipCode, other.zipCode)) + { + return false; + } + return true; + } + + @Override + public String toString() + { + return "Address{" + "city=" + city + ", state=" + state + ", country=" + country + ", address1=" + address1 + ", address2=" + address2 + ", address3=" + address3 + ", zipCode=" + zipCode + '}'; + } + + } + +} From 52f86eb384526662beca129e3c423c9780cb66ce Mon Sep 17 00:00:00 2001 From: Wellington Moreno Date: Wed, 30 Nov 2016 00:32:10 -0800 Subject: [PATCH 004/168] Adds YelpReview JSON object --- .../java/tech/redroma/yelp/YelpReview.java | 55 +++++++++++++++++++ 1 file changed, 55 insertions(+) create mode 100644 src/main/java/tech/redroma/yelp/YelpReview.java diff --git a/src/main/java/tech/redroma/yelp/YelpReview.java b/src/main/java/tech/redroma/yelp/YelpReview.java new file mode 100644 index 0000000..83d147e --- /dev/null +++ b/src/main/java/tech/redroma/yelp/YelpReview.java @@ -0,0 +1,55 @@ +/* + * Copyright 2016 BlackWholeLabs. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + + +package tech.redroma.yelp; + + +import com.google.gson.annotations.SerializedName; +import java.time.Instant; +import tech.sirwellington.alchemy.annotations.concurrency.Mutable; +import tech.sirwellington.alchemy.annotations.concurrency.ThreadUnsafe; +import tech.sirwellington.alchemy.annotations.objects.Pojo; + + +/** + * Represents a Yelp Review JSON Object, as documented in the Yelp API. + * + * @see https://www.yelp.com/developers/documentation/v3/business_reviews + * @author SirWellington + */ +@Pojo +@Mutable +@ThreadUnsafe +public class YelpReview +{ + public int rating; + public User user; + public String text; + public Instant timeCreated; + public String url; + + @Pojo + @Mutable + @ThreadUnsafe + public static class User + { + public String name; + + @SerializedName("image_url") + public String imageURL; + } +} From abafc2a522b2b67591dc675d9c89082d1a115a69 Mon Sep 17 00:00:00 2001 From: Wellington Moreno Date: Wed, 30 Nov 2016 00:35:49 -0800 Subject: [PATCH 005/168] Adds unit test --- .../tech/redroma/yelp/YelpReviewTest.java | 87 +++++++++++++++++++ 1 file changed, 87 insertions(+) create mode 100644 src/test/java/tech/redroma/yelp/YelpReviewTest.java diff --git a/src/test/java/tech/redroma/yelp/YelpReviewTest.java b/src/test/java/tech/redroma/yelp/YelpReviewTest.java new file mode 100644 index 0000000..da5fbdf --- /dev/null +++ b/src/test/java/tech/redroma/yelp/YelpReviewTest.java @@ -0,0 +1,87 @@ +/* + * Copyright 2016 RedRoma, Inc.. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package tech.redroma.yelp; + +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import tech.sirwellington.alchemy.test.junit.runners.AlchemyTestRunner; +import tech.sirwellington.alchemy.test.junit.runners.DontRepeat; +import tech.sirwellington.alchemy.test.junit.runners.GeneratePojo; +import tech.sirwellington.alchemy.test.junit.runners.Repeat; + +import static org.hamcrest.Matchers.*; +import static org.junit.Assert.*; + +/** + * + * @author SirWellington + */ +@Repeat(10) +@RunWith(AlchemyTestRunner.class) +public class YelpReviewTest +{ + + @GeneratePojo + private YelpReview instance; + + @GeneratePojo + private YelpReview first; + + @GeneratePojo + private YelpReview second; + + @Before + public void setUp() throws Exception + { + } + + @DontRepeat + @Test + public void testInstance() + { + assertThat(instance, notNullValue()); + } + + @Test + public void testInstanceHasAllData() + { + assertThat(instance.text, not(isEmptyOrNullString())); + assertThat(instance.url, not(isEmptyOrNullString())); + assertThat(instance.user, notNullValue()); + assertThat(instance.timeCreated, notNullValue()); + } + + @Test + public void testEqualsWhenNotEqual() + { + assertThat(first, not(second)); + } + + @Test + public void testEqualsWhenEquals() + { + assertThat(first, is(first)); + } + + @Test + public void testHashCode() + { + assertThat(first.hashCode(), is(first.hashCode())); + assertThat(first.hashCode(), not(second.hashCode())); + } +} \ No newline at end of file From bf356f5b748c0b5f3e3880c8caf77d09a83f0749 Mon Sep 17 00:00:00 2001 From: Wellington Moreno Date: Wed, 30 Nov 2016 00:53:03 -0800 Subject: [PATCH 006/168] Updates alchemy-arguments to snapshot --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 10a5597..5efa395 100644 --- a/pom.xml +++ b/pom.xml @@ -128,7 +128,7 @@ tech.sirwellington.alchemy alchemy-arguments - 1.4 + 1.5-SNAPSHOT From 8050d6026a7421012e8514f1f2740b969f27fa9b Mon Sep 17 00:00:00 2001 From: Wellington Moreno Date: Wed, 30 Nov 2016 00:55:24 -0800 Subject: [PATCH 007/168] Adds unit tests for YelpBusiness object --- .../tech/redroma/yelp/YelpBusinessTest.java | 128 ++++++++++++++++++ 1 file changed, 128 insertions(+) create mode 100644 src/test/java/tech/redroma/yelp/YelpBusinessTest.java diff --git a/src/test/java/tech/redroma/yelp/YelpBusinessTest.java b/src/test/java/tech/redroma/yelp/YelpBusinessTest.java new file mode 100644 index 0000000..b80f217 --- /dev/null +++ b/src/test/java/tech/redroma/yelp/YelpBusinessTest.java @@ -0,0 +1,128 @@ +/* + * Copyright 2016 RedRoma, Inc.. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package tech.redroma.yelp; + +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import tech.sirwellington.alchemy.test.junit.runners.AlchemyTestRunner; +import tech.sirwellington.alchemy.test.junit.runners.DontRepeat; +import tech.sirwellington.alchemy.test.junit.runners.GeneratePojo; +import tech.sirwellington.alchemy.test.junit.runners.Repeat; + +import static org.hamcrest.Matchers.is; +import static org.hamcrest.Matchers.isEmptyOrNullString; +import static org.hamcrest.Matchers.not; +import static org.hamcrest.Matchers.notNullValue; +import static org.junit.Assert.assertThat; +import static tech.sirwellington.alchemy.arguments.Arguments.checkThat; +import static tech.sirwellington.alchemy.arguments.assertions.GeolocationAssertions.validLatitude; +import static tech.sirwellington.alchemy.arguments.assertions.GeolocationAssertions.validLongitude; +import static tech.sirwellington.alchemy.generator.AlchemyGenerator.one; +import static tech.sirwellington.alchemy.generator.NumberGenerators.doubles; + +/** + * + * @author SirWellington + */ +@Repeat(10) +@RunWith(AlchemyTestRunner.class) +public class YelpBusinessTest +{ + @GeneratePojo + private YelpBusiness instance; + + @GeneratePojo + private YelpBusiness first; + + @GeneratePojo + private YelpBusiness second; + + @Before + public void setUp() + { + setupData(); + } + + private void setupData() + { + double latitude = one(doubles(-90, 90)); + double longitude = one(doubles(-180, 180)); + instance.coordinates.latitude = latitude; + instance.coordinates.longitude = longitude; + } + + + @DontRepeat + @Test + public void testInstance() + { + assertThat(instance, notNullValue()); + } + + @Test + public void testInstanceHasAllData() + { + assertThat(instance.id, not(isEmptyOrNullString())); + assertThat(instance.imageURL, not(isEmptyOrNullString())); + assertThat(instance.name, not(isEmptyOrNullString())); + assertThat(instance.phone, not(isEmptyOrNullString())); + assertThat(instance.url, not(isEmptyOrNullString())); + assertThat(instance.isClosed, notNullValue()); + assertThat(instance.distance, notNullValue()); + assertThat(instance.categories, notNullValue()); + + instance.categories.forEach(this::testCategory); + testCoordinate(instance.coordinates); + } + + private void testCategory(YelpBusiness.Category category) + { + assertThat(category, notNullValue()); + assertThat(category.alias, not(isEmptyOrNullString())); + assertThat(category.title, not(isEmptyOrNullString())); + } + + private void testCoordinate(YelpBusiness.Coordinate coordinate) + { + assertThat(coordinate, notNullValue()); + checkThat(coordinate.latitude) + .is(validLatitude()); + checkThat(coordinate.longitude) + .is(validLongitude()); + } + + @Test + public void testEqualsWhenNotEqual() + { + assertThat(first, not(second)); + } + + @Test + public void testEqualsWhenEquals() + { + assertThat(first, is(first)); + } + + @Test + public void testHashCode() + { + assertThat(first.hashCode(), is(first.hashCode())); + assertThat(first.hashCode(), not(second.hashCode())); + } + +} \ No newline at end of file From 39d647b3e9e24cc0d8133db47e964b8316708a9b Mon Sep 17 00:00:00 2001 From: Wellington Moreno Date: Wed, 30 Nov 2016 13:31:32 -0800 Subject: [PATCH 008/168] Extracts Business out to a separate class --- .../java/tech/redroma/yelp/Coordinate.java | 79 +++++++++++++++++++ .../java/tech/redroma/yelp/YelpBusiness.java | 52 ------------ .../tech/redroma/yelp/YelpBusinessTest.java | 2 +- 3 files changed, 80 insertions(+), 53 deletions(-) create mode 100644 src/main/java/tech/redroma/yelp/Coordinate.java diff --git a/src/main/java/tech/redroma/yelp/Coordinate.java b/src/main/java/tech/redroma/yelp/Coordinate.java new file mode 100644 index 0000000..637a7d1 --- /dev/null +++ b/src/main/java/tech/redroma/yelp/Coordinate.java @@ -0,0 +1,79 @@ +/* + * Copyright 2016 RedRoma, Inc.. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + + +package tech.redroma.yelp; + + +import tech.sirwellington.alchemy.annotations.concurrency.Mutable; +import tech.sirwellington.alchemy.annotations.concurrency.ThreadUnsafe; +import tech.sirwellington.alchemy.annotations.objects.Pojo; + +/** + * + * @author SirWellington + */ +@Pojo +@Mutable +@ThreadUnsafe +public class Coordinate +{ + double latitude; + double longitude; + + @Override + public int hashCode() + { + int hash = 7; + hash = 59 * hash + (int) (Double.doubleToLongBits(this.latitude) ^ (Double.doubleToLongBits(this.latitude) >>> 32)); + hash = 59 * hash + (int) (Double.doubleToLongBits(this.longitude) ^ (Double.doubleToLongBits(this.longitude) >>> 32)); + return hash; + } + + @Override + public boolean equals(Object obj) + { + if (this == obj) + { + return true; + } + if (obj == null) + { + return false; + } + if (getClass() != obj.getClass()) + { + return false; + } + final Coordinate other = (Coordinate) obj; + if (Double.doubleToLongBits(this.latitude) != Double.doubleToLongBits(other.latitude)) + { + return false; + } + if (Double.doubleToLongBits(this.longitude) != Double.doubleToLongBits(other.longitude)) + { + return false; + } + return true; + } + + @Override + public String toString() + { + return "Coordinate{" + "latitude=" + latitude + ", longitude=" + longitude + '}'; + } + +} diff --git a/src/main/java/tech/redroma/yelp/YelpBusiness.java b/src/main/java/tech/redroma/yelp/YelpBusiness.java index a17082b..9857789 100644 --- a/src/main/java/tech/redroma/yelp/YelpBusiness.java +++ b/src/main/java/tech/redroma/yelp/YelpBusiness.java @@ -195,58 +195,6 @@ public String toString() } - @Pojo - @Mutable - @ThreadUnsafe - public static class Coordinate - { - - double latitude; - double longitude; - - @Override - public int hashCode() - { - int hash = 7; - hash = 59 * hash + (int) (Double.doubleToLongBits(this.latitude) ^ (Double.doubleToLongBits(this.latitude) >>> 32)); - hash = 59 * hash + (int) (Double.doubleToLongBits(this.longitude) ^ (Double.doubleToLongBits(this.longitude) >>> 32)); - return hash; - } - - @Override - public boolean equals(Object obj) - { - if (this == obj) - { - return true; - } - if (obj == null) - { - return false; - } - if (getClass() != obj.getClass()) - { - return false; - } - final Coordinate other = (Coordinate) obj; - if (Double.doubleToLongBits(this.latitude) != Double.doubleToLongBits(other.latitude)) - { - return false; - } - if (Double.doubleToLongBits(this.longitude) != Double.doubleToLongBits(other.longitude)) - { - return false; - } - return true; - } - - @Override - public String toString() - { - return "Coordinate{" + "latitude=" + latitude + ", longitude=" + longitude + '}'; - } - - } @Pojo @Mutable diff --git a/src/test/java/tech/redroma/yelp/YelpBusinessTest.java b/src/test/java/tech/redroma/yelp/YelpBusinessTest.java index b80f217..d21d297 100644 --- a/src/test/java/tech/redroma/yelp/YelpBusinessTest.java +++ b/src/test/java/tech/redroma/yelp/YelpBusinessTest.java @@ -97,7 +97,7 @@ private void testCategory(YelpBusiness.Category category) assertThat(category.title, not(isEmptyOrNullString())); } - private void testCoordinate(YelpBusiness.Coordinate coordinate) + private void testCoordinate(Coordinate coordinate) { assertThat(coordinate, notNullValue()); checkThat(coordinate.latitude) From 1fa0331dbadb26249c12cb0ae5bcf4b7117660d8 Mon Sep 17 00:00:00 2001 From: Wellington Moreno Date: Wed, 30 Nov 2016 13:38:59 -0800 Subject: [PATCH 009/168] Updates YelpBusiness with Address information --- .../java/tech/redroma/yelp/YelpBusiness.java | 123 ++++-------------- 1 file changed, 26 insertions(+), 97 deletions(-) diff --git a/src/main/java/tech/redroma/yelp/YelpBusiness.java b/src/main/java/tech/redroma/yelp/YelpBusiness.java index 9857789..fd67ff2 100644 --- a/src/main/java/tech/redroma/yelp/YelpBusiness.java +++ b/src/main/java/tech/redroma/yelp/YelpBusiness.java @@ -14,7 +14,6 @@ * See the License for the specific language governing permissions and * limitations under the License. */ - package tech.redroma.yelp; import com.google.gson.annotations.SerializedName; @@ -27,8 +26,9 @@ /** * Represents a Yelp Business JSON Object, as documented in the Yelp API. - * - * @see https://www.yelp.com/developers/documentation/v3/business_search + * + * @see + * https://www.yelp.com/developers/documentation/v3/business_search * @author SirWellington */ @Pojo @@ -38,26 +38,28 @@ public class YelpBusiness { public String id; - + public String name; - + public String url; - + public int rating; - + public String phone; - + public Boolean isClosed; - + public List categories; - + public int reviewCount; - + public Coordinate coordinates; - + + public Address location; + @SerializedName("image_url") public String imageURL; - + @Optional public Double distance; @@ -74,6 +76,7 @@ public int hashCode() hash = 37 * hash + Objects.hashCode(this.categories); hash = 37 * hash + this.reviewCount; hash = 37 * hash + Objects.hashCode(this.coordinates); + hash = 37 * hash + Objects.hashCode(this.location); hash = 37 * hash + Objects.hashCode(this.imageURL); hash = 37 * hash + Objects.hashCode(this.distance); return hash; @@ -135,6 +138,10 @@ public boolean equals(Object obj) { return false; } + if (!Objects.equals(this.location, other.location)) + { + return false; + } if (!Objects.equals(this.distance, other.distance)) { return false; @@ -142,6 +149,12 @@ public boolean equals(Object obj) return true; } + @Override + public String toString() + { + return "YelpBusiness{" + "id=" + id + ", name=" + name + ", url=" + url + ", rating=" + rating + ", phone=" + phone + ", isClosed=" + isClosed + ", categories=" + categories + ", reviewCount=" + reviewCount + ", coordinates=" + coordinates + ", location=" + location + ", imageURL=" + imageURL + ", distance=" + distance + '}'; + } + @Pojo @Mutable @ThreadUnsafe @@ -195,88 +208,4 @@ public String toString() } - - @Pojo - @Mutable - @ThreadUnsafe - public static class Address - { - - public String city; - public String state; - public String country; - public String address1; - public String address2; - public String address3; - public String zipCode; - - @Override - public int hashCode() - { - int hash = 5; - hash = 83 * hash + Objects.hashCode(this.city); - hash = 83 * hash + Objects.hashCode(this.state); - hash = 83 * hash + Objects.hashCode(this.country); - hash = 83 * hash + Objects.hashCode(this.address1); - hash = 83 * hash + Objects.hashCode(this.address2); - hash = 83 * hash + Objects.hashCode(this.address3); - hash = 83 * hash + Objects.hashCode(this.zipCode); - return hash; - } - - @Override - public boolean equals(Object obj) - { - if (this == obj) - { - return true; - } - if (obj == null) - { - return false; - } - if (getClass() != obj.getClass()) - { - return false; - } - final Address other = (Address) obj; - if (!Objects.equals(this.city, other.city)) - { - return false; - } - if (!Objects.equals(this.state, other.state)) - { - return false; - } - if (!Objects.equals(this.country, other.country)) - { - return false; - } - if (!Objects.equals(this.address1, other.address1)) - { - return false; - } - if (!Objects.equals(this.address2, other.address2)) - { - return false; - } - if (!Objects.equals(this.address3, other.address3)) - { - return false; - } - if (!Objects.equals(this.zipCode, other.zipCode)) - { - return false; - } - return true; - } - - @Override - public String toString() - { - return "Address{" + "city=" + city + ", state=" + state + ", country=" + country + ", address1=" + address1 + ", address2=" + address2 + ", address3=" + address3 + ", zipCode=" + zipCode + '}'; - } - - } - } From ff43033643e0ec0401f18e8fcf58f7b9a2b10431 Mon Sep 17 00:00:00 2001 From: Wellington Moreno Date: Wed, 30 Nov 2016 13:39:33 -0800 Subject: [PATCH 010/168] Updates unit test --- .../tech/redroma/yelp/YelpBusinessTest.java | 19 +++++++++++++++---- 1 file changed, 15 insertions(+), 4 deletions(-) diff --git a/src/test/java/tech/redroma/yelp/YelpBusinessTest.java b/src/test/java/tech/redroma/yelp/YelpBusinessTest.java index d21d297..546bb09 100644 --- a/src/test/java/tech/redroma/yelp/YelpBusinessTest.java +++ b/src/test/java/tech/redroma/yelp/YelpBusinessTest.java @@ -86,18 +86,19 @@ public void testInstanceHasAllData() assertThat(instance.distance, notNullValue()); assertThat(instance.categories, notNullValue()); - instance.categories.forEach(this::testCategory); - testCoordinate(instance.coordinates); + instance.categories.forEach(this::checkCategory); + checkCoordinate(instance.coordinates); + checkAddress(instance.location); } - private void testCategory(YelpBusiness.Category category) + private void checkCategory(YelpBusiness.Category category) { assertThat(category, notNullValue()); assertThat(category.alias, not(isEmptyOrNullString())); assertThat(category.title, not(isEmptyOrNullString())); } - private void testCoordinate(Coordinate coordinate) + private void checkCoordinate(Coordinate coordinate) { assertThat(coordinate, notNullValue()); checkThat(coordinate.latitude) @@ -106,6 +107,16 @@ private void testCoordinate(Coordinate coordinate) .is(validLongitude()); } + private void checkAddress(Address address) + { + assertThat(address, notNullValue()); + assertThat(address.address1, not(isEmptyOrNullString())); + assertThat(address.city, not(isEmptyOrNullString())); + assertThat(address.country, not(isEmptyOrNullString())); + assertThat(address.state, not(isEmptyOrNullString())); + assertThat(address.zipCode, not(isEmptyOrNullString())); + } + @Test public void testEqualsWhenNotEqual() { From 68eaa51acc5da31885a46a5ad3f7a6b764e07fd0 Mon Sep 17 00:00:00 2001 From: Wellington Moreno Date: Wed, 30 Nov 2016 13:47:14 -0800 Subject: [PATCH 011/168] Separates Address into separate class --- src/main/java/tech/redroma/yelp/Address.java | 112 +++++++++++++++++++ 1 file changed, 112 insertions(+) create mode 100644 src/main/java/tech/redroma/yelp/Address.java diff --git a/src/main/java/tech/redroma/yelp/Address.java b/src/main/java/tech/redroma/yelp/Address.java new file mode 100644 index 0000000..40718de --- /dev/null +++ b/src/main/java/tech/redroma/yelp/Address.java @@ -0,0 +1,112 @@ +/* + * Copyright 2016 RedRoma, Inc.. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + + +package tech.redroma.yelp; + + +import java.util.Objects; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import tech.sirwellington.alchemy.annotations.concurrency.Mutable; +import tech.sirwellington.alchemy.annotations.concurrency.ThreadUnsafe; +import tech.sirwellington.alchemy.annotations.objects.Pojo; + +/** + * + * @author SirWellington + */ +@Pojo +@Mutable +@ThreadUnsafe +public class Address +{ + public String city; + public String state; + public String country; + public String address1; + public String address2; + public String address3; + public String zipCode; + + @Override + public int hashCode() + { + int hash = 5; + hash = 83 * hash + Objects.hashCode(this.city); + hash = 83 * hash + Objects.hashCode(this.state); + hash = 83 * hash + Objects.hashCode(this.country); + hash = 83 * hash + Objects.hashCode(this.address1); + hash = 83 * hash + Objects.hashCode(this.address2); + hash = 83 * hash + Objects.hashCode(this.address3); + hash = 83 * hash + Objects.hashCode(this.zipCode); + return hash; + } + + @Override + public boolean equals(Object obj) + { + if (this == obj) + { + return true; + } + if (obj == null) + { + return false; + } + if (getClass() != obj.getClass()) + { + return false; + } + final Address other = (Address) obj; + if (!Objects.equals(this.city, other.city)) + { + return false; + } + if (!Objects.equals(this.state, other.state)) + { + return false; + } + if (!Objects.equals(this.country, other.country)) + { + return false; + } + if (!Objects.equals(this.address1, other.address1)) + { + return false; + } + if (!Objects.equals(this.address2, other.address2)) + { + return false; + } + if (!Objects.equals(this.address3, other.address3)) + { + return false; + } + if (!Objects.equals(this.zipCode, other.zipCode)) + { + return false; + } + return true; + } + + @Override + public String toString() + { + return "Address{" + "city=" + city + ", state=" + state + ", country=" + country + ", address1=" + address1 + ", address2=" + address2 + ", address3=" + address3 + ", zipCode=" + zipCode + '}'; + } + +} From f337f7356aef5a3c691b0f4162ab8d13b612d3d7 Mon Sep 17 00:00:00 2001 From: Wellington Moreno Date: Wed, 30 Nov 2016 13:51:58 -0800 Subject: [PATCH 012/168] Adds unit test --- .../java/tech/redroma/yelp/AddressTest.java | 89 +++++++++++++++++++ 1 file changed, 89 insertions(+) create mode 100644 src/test/java/tech/redroma/yelp/AddressTest.java diff --git a/src/test/java/tech/redroma/yelp/AddressTest.java b/src/test/java/tech/redroma/yelp/AddressTest.java new file mode 100644 index 0000000..8399f2e --- /dev/null +++ b/src/test/java/tech/redroma/yelp/AddressTest.java @@ -0,0 +1,89 @@ +/* + * Copyright 2016 RedRoma, Inc.. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package tech.redroma.yelp; + +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import tech.sirwellington.alchemy.test.junit.runners.AlchemyTestRunner; +import tech.sirwellington.alchemy.test.junit.runners.GeneratePojo; +import tech.sirwellington.alchemy.test.junit.runners.Repeat; + +import static org.hamcrest.Matchers.is; +import static org.hamcrest.Matchers.isEmptyOrNullString; +import static org.hamcrest.Matchers.not; +import static org.hamcrest.Matchers.notNullValue; +import static org.junit.Assert.assertThat; + +/** + * + * @author SirWellington + */ +@Repeat(10) +@RunWith(AlchemyTestRunner.class) +public class AddressTest +{ + + @GeneratePojo + private Address instance; + + @GeneratePojo + private Address first; + + @GeneratePojo + private Address second; + + @Before + public void setUp() throws Exception + { + + } + + @Test + public void testInstance() + { + assertThat(instance, notNullValue()); + + assertThat(instance.address1, not(isEmptyOrNullString())); + assertThat(instance.address2, not(isEmptyOrNullString())); + assertThat(instance.address3, not(isEmptyOrNullString())); + assertThat(instance.city, not(isEmptyOrNullString())); + assertThat(instance.state, not(isEmptyOrNullString())); + assertThat(instance.country, not(isEmptyOrNullString())); + assertThat(instance.zipCode, not(isEmptyOrNullString())); + } + + @Test + public void testEqualsWhenNotEqual() + { + assertThat(first, not(second)); + } + + @Test + public void testEqualsWhenEquals() + { + assertThat(first, is(first)); + } + + @Test + public void testHashCode() + { + assertThat(first.hashCode(), is(first.hashCode())); + assertThat(first.hashCode(), not(second.hashCode())); + } + +} From 9cac90e9fa663e4189a29a66094e5dbb02267066 Mon Sep 17 00:00:00 2001 From: Wellington Moreno Date: Wed, 30 Nov 2016 13:54:33 -0800 Subject: [PATCH 013/168] Moves Category into a separate class --- src/main/java/tech/redroma/yelp/Category.java | 80 +++++++++++++++++++ .../java/tech/redroma/yelp/YelpBusiness.java | 52 ------------ .../tech/redroma/yelp/YelpBusinessTest.java | 2 +- 3 files changed, 81 insertions(+), 53 deletions(-) create mode 100644 src/main/java/tech/redroma/yelp/Category.java diff --git a/src/main/java/tech/redroma/yelp/Category.java b/src/main/java/tech/redroma/yelp/Category.java new file mode 100644 index 0000000..a6deb38 --- /dev/null +++ b/src/main/java/tech/redroma/yelp/Category.java @@ -0,0 +1,80 @@ +/* + * Copyright 2016 RedRoma, Inc.. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + + +package tech.redroma.yelp; + + +import java.util.Objects; +import tech.sirwellington.alchemy.annotations.concurrency.Mutable; +import tech.sirwellington.alchemy.annotations.concurrency.ThreadUnsafe; +import tech.sirwellington.alchemy.annotations.objects.Pojo; + +/** + * + * @author SirWellington + */ +@Pojo +@Mutable +@ThreadUnsafe +public class Category +{ + public String alias; + public String title; + + @Override + public int hashCode() + { + int hash = 5; + hash = 61 * hash + Objects.hashCode(this.alias); + hash = 61 * hash + Objects.hashCode(this.title); + return hash; + } + + @Override + public boolean equals(Object obj) + { + if (this == obj) + { + return true; + } + if (obj == null) + { + return false; + } + if (getClass() != obj.getClass()) + { + return false; + } + final Category other = (Category) obj; + if (!Objects.equals(this.alias, other.alias)) + { + return false; + } + if (!Objects.equals(this.title, other.title)) + { + return false; + } + return true; + } + + @Override + public String toString() + { + return "Category{" + "alias=" + alias + ", title=" + title + '}'; + } + +} diff --git a/src/main/java/tech/redroma/yelp/YelpBusiness.java b/src/main/java/tech/redroma/yelp/YelpBusiness.java index fd67ff2..78af220 100644 --- a/src/main/java/tech/redroma/yelp/YelpBusiness.java +++ b/src/main/java/tech/redroma/yelp/YelpBusiness.java @@ -155,57 +155,5 @@ public String toString() return "YelpBusiness{" + "id=" + id + ", name=" + name + ", url=" + url + ", rating=" + rating + ", phone=" + phone + ", isClosed=" + isClosed + ", categories=" + categories + ", reviewCount=" + reviewCount + ", coordinates=" + coordinates + ", location=" + location + ", imageURL=" + imageURL + ", distance=" + distance + '}'; } - @Pojo - @Mutable - @ThreadUnsafe - public static class Category - { - - public String alias; - public String title; - - @Override - public int hashCode() - { - int hash = 5; - hash = 61 * hash + Objects.hashCode(this.alias); - hash = 61 * hash + Objects.hashCode(this.title); - return hash; - } - - @Override - public boolean equals(Object obj) - { - if (this == obj) - { - return true; - } - if (obj == null) - { - return false; - } - if (getClass() != obj.getClass()) - { - return false; - } - final Category other = (Category) obj; - if (!Objects.equals(this.alias, other.alias)) - { - return false; - } - if (!Objects.equals(this.title, other.title)) - { - return false; - } - return true; - } - - @Override - public String toString() - { - return "Category{" + "alias=" + alias + ", title=" + title + '}'; - } - - } } diff --git a/src/test/java/tech/redroma/yelp/YelpBusinessTest.java b/src/test/java/tech/redroma/yelp/YelpBusinessTest.java index 546bb09..e86fff0 100644 --- a/src/test/java/tech/redroma/yelp/YelpBusinessTest.java +++ b/src/test/java/tech/redroma/yelp/YelpBusinessTest.java @@ -91,7 +91,7 @@ public void testInstanceHasAllData() checkAddress(instance.location); } - private void checkCategory(YelpBusiness.Category category) + private void checkCategory(Category category) { assertThat(category, notNullValue()); assertThat(category.alias, not(isEmptyOrNullString())); From 9655fba51bc12d1cfc7f080e1e919b1fbba83221 Mon Sep 17 00:00:00 2001 From: Wellington Moreno Date: Wed, 30 Nov 2016 13:54:45 -0800 Subject: [PATCH 014/168] Updates Address --- src/main/java/tech/redroma/yelp/Address.java | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/main/java/tech/redroma/yelp/Address.java b/src/main/java/tech/redroma/yelp/Address.java index 40718de..0ab1120 100644 --- a/src/main/java/tech/redroma/yelp/Address.java +++ b/src/main/java/tech/redroma/yelp/Address.java @@ -19,8 +19,6 @@ import java.util.Objects; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; import tech.sirwellington.alchemy.annotations.concurrency.Mutable; import tech.sirwellington.alchemy.annotations.concurrency.ThreadUnsafe; import tech.sirwellington.alchemy.annotations.objects.Pojo; From 76036a11f783abfe0b847ee11832bf91b948e777 Mon Sep 17 00:00:00 2001 From: Wellington Moreno Date: Wed, 30 Nov 2016 13:57:33 -0800 Subject: [PATCH 015/168] Adds Business JSON object --- .../resources/tech/redroma/yelp/business.json | 31 +++++++++++++++++++ 1 file changed, 31 insertions(+) create mode 100644 src/main/resources/tech/redroma/yelp/business.json diff --git a/src/main/resources/tech/redroma/yelp/business.json b/src/main/resources/tech/redroma/yelp/business.json new file mode 100644 index 0000000..61b5fd8 --- /dev/null +++ b/src/main/resources/tech/redroma/yelp/business.json @@ -0,0 +1,31 @@ +{ + "rating": 4, + "price": "$", + "phone": "+14152520800", + "id": "four-barrel-coffee-san-francisco", + "is_closed": false, + "categories": [ + { + "alias": "coffee", + "title": "Coffee & Tea" + } + ], + "review_count": 1738, + "name": "Four Barrel Coffee", + "url": "https://www.yelp.com/biz/four-barrel-coffee-san-francisco", + "coordinates": { + "latitude": 37.7670169511878, + "longitude": -122.42184275 + }, + "image_url": "http://s3-media2.fl.yelpcdn.com/bphoto/MmgtASP3l_t4tPCL1iAsCg/o.jpg", + "location": { + "city": "San Francisco", + "country": "US", + "address2": "", + "address3": "", + "state": "CA", + "address1": "375 Valencia St", + "zip_code": "94103" + }, + "distance": 1604.23 +} \ No newline at end of file From 6f3b5446c0d47893b1fe4ff87212029c2ac9030e Mon Sep 17 00:00:00 2001 From: Wellington Moreno Date: Wed, 30 Nov 2016 14:01:11 -0800 Subject: [PATCH 016/168] Adds Resources class to load files for testing purposes --- .../java/tech/redroma/yelp/Resources.java | 54 +++++++++++++++++++ 1 file changed, 54 insertions(+) create mode 100644 src/test/java/tech/redroma/yelp/Resources.java diff --git a/src/test/java/tech/redroma/yelp/Resources.java b/src/test/java/tech/redroma/yelp/Resources.java new file mode 100644 index 0000000..5f5998f --- /dev/null +++ b/src/test/java/tech/redroma/yelp/Resources.java @@ -0,0 +1,54 @@ + +/* + * Copyright 2016 RedRoma, Inc.. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package tech.redroma.yelp; + +import java.io.IOException; +import java.net.URL; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import tech.sirwellington.alchemy.annotations.access.Internal; + +import static com.google.common.base.Charsets.UTF_8; + +/** + * + * @author SirWellington + */ +@Internal +public class Resources +{ + + private final static Logger LOG = LoggerFactory.getLogger(Resources.class); + + public static String loadResource(String name) throws IOException + { + URL url; + try + { + url = com.google.common.io.Resources.getResource(Resources.class, name); + } + catch (IllegalArgumentException ex) + { + LOG.error("Failed to {} load resource called [{]]", name, ex); + throw ex; + } + + return com.google.common.io.Resources.toString(url, UTF_8); + + } +} From b46234d3fa2725b96283d8364820e3f115ea03b7 Mon Sep 17 00:00:00 2001 From: Wellington Moreno Date: Wed, 30 Nov 2016 14:01:50 -0800 Subject: [PATCH 017/168] Adds a business-details json sample --- .../tech/redroma/yelp/business-details.json | 87 +++++++++++++++++++ 1 file changed, 87 insertions(+) create mode 100644 src/main/resources/tech/redroma/yelp/business-details.json diff --git a/src/main/resources/tech/redroma/yelp/business-details.json b/src/main/resources/tech/redroma/yelp/business-details.json new file mode 100644 index 0000000..e3e05e8 --- /dev/null +++ b/src/main/resources/tech/redroma/yelp/business-details.json @@ -0,0 +1,87 @@ +{ + "id": "gary-danko-san-francisco", + "name": "Gary Danko", + "image_url": "https://s3-media4.fl.yelpcdn.com/bphoto/--8oiPVp0AsjoWHqaY1rDQ/o.jpg", + "is_claimed": false, + "is_closed": false, + "url": "https://www.yelp.com/biz/gary-danko-san-francisco", + "price": "$$$$", + "rating": 4.5, + "review_count": 4521, + "phone": "+14152520800", + "photos": [ + "http://s3-media3.fl.yelpcdn.com/bphoto/--8oiPVp0AsjoWHqaY1rDQ/o.jpg", + "http://s3-media2.fl.yelpcdn.com/bphoto/ybXbObsm7QGw3SGPA1_WXA/o.jpg", + "http://s3-media3.fl.yelpcdn.com/bphoto/7rZ061Wm4tRZ-iwAhkRSFA/o.jpg" + ], + "hours": [ + { + "hours_type": "REGULAR", + "open": [ + { + "is_overnight": false, + "end": "2200", + "day": 0, + "start": "1730" + }, + { + "is_overnight": false, + "end": "2200", + "day": 1, + "start": "1730" + }, + { + "is_overnight": false, + "end": "2200", + "day": 2, + "start": "1730" + }, + { + "is_overnight": false, + "end": "2200", + "day": 3, + "start": "1730" + }, + { + "is_overnight": false, + "end": "2200", + "day": 4, + "start": "1730" + }, + { + "is_overnight": false, + "end": "2200", + "day": 5, + "start": "1730" + }, + { + "is_overnight": false, + "end": "2200", + "day": 6, + "start": "1730" + } + ], + "is_open_now": false + } + ], + "categories": [ + { + "alias": "newamerican", + "title": "American (New)" + } + ], + "coordinates": { + "latitude": 37.80587, + "longitude": -122.42058 + }, + + "location": { + "city": "San Francisco", + "country": "US", + "address2": "", + "address3": "", + "state": "CA", + "address1": "800 N Point St", + "zip_code": "" + } +} From 8340cb5860a98aa1aece921cc05aebce4501eb7f Mon Sep 17 00:00:00 2001 From: Wellington Moreno Date: Wed, 30 Nov 2016 14:03:21 -0800 Subject: [PATCH 018/168] Improves unit test to allow for greater code reuse --- .../tech/redroma/yelp/YelpBusinessTest.java | 31 ++++++++++++------- 1 file changed, 19 insertions(+), 12 deletions(-) diff --git a/src/test/java/tech/redroma/yelp/YelpBusinessTest.java b/src/test/java/tech/redroma/yelp/YelpBusinessTest.java index e86fff0..c8c1034 100644 --- a/src/test/java/tech/redroma/yelp/YelpBusinessTest.java +++ b/src/test/java/tech/redroma/yelp/YelpBusinessTest.java @@ -77,18 +77,25 @@ public void testInstance() @Test public void testInstanceHasAllData() { - assertThat(instance.id, not(isEmptyOrNullString())); - assertThat(instance.imageURL, not(isEmptyOrNullString())); - assertThat(instance.name, not(isEmptyOrNullString())); - assertThat(instance.phone, not(isEmptyOrNullString())); - assertThat(instance.url, not(isEmptyOrNullString())); - assertThat(instance.isClosed, notNullValue()); - assertThat(instance.distance, notNullValue()); - assertThat(instance.categories, notNullValue()); - - instance.categories.forEach(this::checkCategory); - checkCoordinate(instance.coordinates); - checkAddress(instance.location); + checkBusiness(instance); + checkBusiness(first); + checkBusiness(second); + } + + private void checkBusiness(YelpBusiness business) + { + assertThat(business.id, not(isEmptyOrNullString())); + assertThat(business.imageURL, not(isEmptyOrNullString())); + assertThat(business.name, not(isEmptyOrNullString())); + assertThat(business.phone, not(isEmptyOrNullString())); + assertThat(business.url, not(isEmptyOrNullString())); + assertThat(business.isClosed, notNullValue()); + assertThat(business.distance, notNullValue()); + assertThat(business.categories, notNullValue()); + + business.categories.forEach(this::checkCategory); + checkCoordinate(business.coordinates); + checkAddress(business.location); } private void checkCategory(Category category) From c1d2f5f32510a9ac6fee4476b03ba08472bcbc0e Mon Sep 17 00:00:00 2001 From: Wellington Moreno Date: Wed, 30 Nov 2016 14:03:35 -0800 Subject: [PATCH 019/168] Formats --- .../tech/redroma/yelp/YelpBusinessTest.java | 28 +++++++++---------- 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/src/test/java/tech/redroma/yelp/YelpBusinessTest.java b/src/test/java/tech/redroma/yelp/YelpBusinessTest.java index c8c1034..d4e1382 100644 --- a/src/test/java/tech/redroma/yelp/YelpBusinessTest.java +++ b/src/test/java/tech/redroma/yelp/YelpBusinessTest.java @@ -41,17 +41,18 @@ */ @Repeat(10) @RunWith(AlchemyTestRunner.class) -public class YelpBusinessTest +public class YelpBusinessTest { + @GeneratePojo private YelpBusiness instance; - + @GeneratePojo private YelpBusiness first; - + @GeneratePojo private YelpBusiness second; - + @Before public void setUp() { @@ -65,15 +66,14 @@ private void setupData() instance.coordinates.latitude = latitude; instance.coordinates.longitude = longitude; } - - + @DontRepeat @Test public void testInstance() { assertThat(instance, notNullValue()); } - + @Test public void testInstanceHasAllData() { @@ -81,7 +81,7 @@ public void testInstanceHasAllData() checkBusiness(first); checkBusiness(second); } - + private void checkBusiness(YelpBusiness business) { assertThat(business.id, not(isEmptyOrNullString())); @@ -97,7 +97,7 @@ private void checkBusiness(YelpBusiness business) checkCoordinate(business.coordinates); checkAddress(business.location); } - + private void checkCategory(Category category) { assertThat(category, notNullValue()); @@ -113,7 +113,7 @@ private void checkCoordinate(Coordinate coordinate) checkThat(coordinate.longitude) .is(validLongitude()); } - + private void checkAddress(Address address) { assertThat(address, notNullValue()); @@ -123,19 +123,19 @@ private void checkAddress(Address address) assertThat(address.state, not(isEmptyOrNullString())); assertThat(address.zipCode, not(isEmptyOrNullString())); } - + @Test public void testEqualsWhenNotEqual() { assertThat(first, not(second)); } - + @Test public void testEqualsWhenEquals() { assertThat(first, is(first)); } - + @Test public void testHashCode() { @@ -143,4 +143,4 @@ public void testHashCode() assertThat(first.hashCode(), not(second.hashCode())); } -} \ No newline at end of file +} From 85bd039aeaddaaa5db8280a101209ce1dbd754a8 Mon Sep 17 00:00:00 2001 From: Wellington Moreno Date: Wed, 30 Nov 2016 14:04:59 -0800 Subject: [PATCH 020/168] Adds GSON to resources --- src/test/java/tech/redroma/yelp/Resources.java | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/test/java/tech/redroma/yelp/Resources.java b/src/test/java/tech/redroma/yelp/Resources.java index 5f5998f..c94effb 100644 --- a/src/test/java/tech/redroma/yelp/Resources.java +++ b/src/test/java/tech/redroma/yelp/Resources.java @@ -14,9 +14,10 @@ * See the License for the specific language governing permissions and * limitations under the License. */ - package tech.redroma.yelp; +import com.google.gson.Gson; +import com.google.gson.GsonBuilder; import java.io.IOException; import java.net.URL; import org.slf4j.Logger; @@ -35,6 +36,9 @@ public class Resources private final static Logger LOG = LoggerFactory.getLogger(Resources.class); + static final Gson GSON = new GsonBuilder() + .create(); + public static String loadResource(String name) throws IOException { URL url; From 6e049612ef733554eb63484cf6140dc4c06457a9 Mon Sep 17 00:00:00 2001 From: Wellington Moreno Date: Wed, 30 Nov 2016 14:09:13 -0800 Subject: [PATCH 021/168] Updates alchemy-generator version --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 5efa395..db85c1d 100644 --- a/pom.xml +++ b/pom.xml @@ -140,7 +140,7 @@ tech.sirwellington.alchemy alchemy-generator - 1.5 + 1.6-SNAPSHOT test From a7c6c8636e0515b7d368216b055870a70de5d0c6 Mon Sep 17 00:00:00 2001 From: Wellington Moreno Date: Wed, 30 Nov 2016 14:20:45 -0800 Subject: [PATCH 022/168] Adds unit test for JSON parsing of YelpBusiness --- src/main/java/tech/redroma/yelp/Address.java | 2 + .../java/tech/redroma/yelp/YelpBusiness.java | 1 + .../tech/redroma/yelp/YelpBusinessTest.java | 42 +++++++++++++++---- 3 files changed, 38 insertions(+), 7 deletions(-) diff --git a/src/main/java/tech/redroma/yelp/Address.java b/src/main/java/tech/redroma/yelp/Address.java index 0ab1120..9f7f09b 100644 --- a/src/main/java/tech/redroma/yelp/Address.java +++ b/src/main/java/tech/redroma/yelp/Address.java @@ -18,6 +18,7 @@ package tech.redroma.yelp; +import com.google.gson.annotations.SerializedName; import java.util.Objects; import tech.sirwellington.alchemy.annotations.concurrency.Mutable; import tech.sirwellington.alchemy.annotations.concurrency.ThreadUnsafe; @@ -38,6 +39,7 @@ public class Address public String address1; public String address2; public String address3; + @SerializedName("zip_code") public String zipCode; @Override diff --git a/src/main/java/tech/redroma/yelp/YelpBusiness.java b/src/main/java/tech/redroma/yelp/YelpBusiness.java index 78af220..c2288c3 100644 --- a/src/main/java/tech/redroma/yelp/YelpBusiness.java +++ b/src/main/java/tech/redroma/yelp/YelpBusiness.java @@ -47,6 +47,7 @@ public class YelpBusiness public String phone; + @SerializedName("is_closed") public Boolean isClosed; public List categories; diff --git a/src/test/java/tech/redroma/yelp/YelpBusinessTest.java b/src/test/java/tech/redroma/yelp/YelpBusinessTest.java index d4e1382..08e3c9b 100644 --- a/src/test/java/tech/redroma/yelp/YelpBusinessTest.java +++ b/src/test/java/tech/redroma/yelp/YelpBusinessTest.java @@ -16,6 +16,8 @@ package tech.redroma.yelp; +import com.google.gson.JsonElement; +import java.io.IOException; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; @@ -29,11 +31,13 @@ import static org.hamcrest.Matchers.not; import static org.hamcrest.Matchers.notNullValue; import static org.junit.Assert.assertThat; +import static tech.redroma.yelp.Resources.GSON; import static tech.sirwellington.alchemy.arguments.Arguments.checkThat; import static tech.sirwellington.alchemy.arguments.assertions.GeolocationAssertions.validLatitude; import static tech.sirwellington.alchemy.arguments.assertions.GeolocationAssertions.validLongitude; import static tech.sirwellington.alchemy.generator.AlchemyGenerator.one; -import static tech.sirwellington.alchemy.generator.NumberGenerators.doubles; +import static tech.sirwellington.alchemy.generator.GeolocationGenerators.latitudes; +import static tech.sirwellington.alchemy.generator.GeolocationGenerators.longitudes; /** * @@ -61,19 +65,23 @@ public void setUp() private void setupData() { - double latitude = one(doubles(-90, 90)); - double longitude = one(doubles(-180, 180)); - instance.coordinates.latitude = latitude; - instance.coordinates.longitude = longitude; + instance.coordinates.latitude = one(latitudes()); + instance.coordinates.longitude = one(longitudes()); + + first.coordinates.latitude = one(latitudes()); + first.coordinates.longitude = one(longitudes()); + + second.coordinates.latitude = one(latitudes()); + second.coordinates.longitude = one(longitudes()); } @DontRepeat @Test - public void testInstance() + public void testInstanceIsNotNull() { assertThat(instance, notNullValue()); } - + @Test public void testInstanceHasAllData() { @@ -142,5 +150,25 @@ public void testHashCode() assertThat(first.hashCode(), is(first.hashCode())); assertThat(first.hashCode(), not(second.hashCode())); } + + @Repeat(25) + @Test + public void testSerialization() + { + JsonElement json = GSON.toJsonTree(instance); + assertThat(json, notNullValue()); + assertThat(json.isJsonObject(), is(true)); + } + + @DontRepeat + @Test + public void testDeserialization() throws IOException + { + String json = Resources.loadResource("business.json"); + + YelpBusiness result = GSON.fromJson(json, YelpBusiness.class); + assertThat(result, notNullValue()); + checkBusiness(result); + } } From 0c89a3bf35295310f52ccb7f97b718d6cb743230 Mon Sep 17 00:00:00 2001 From: Wellington Moreno Date: Wed, 30 Nov 2016 14:50:13 -0800 Subject: [PATCH 023/168] Adds Yelp Business Details --- .../redroma/yelp/YelpBusinessDetails.java | 153 ++++++++++++++++++ 1 file changed, 153 insertions(+) create mode 100644 src/main/java/tech/redroma/yelp/YelpBusinessDetails.java diff --git a/src/main/java/tech/redroma/yelp/YelpBusinessDetails.java b/src/main/java/tech/redroma/yelp/YelpBusinessDetails.java new file mode 100644 index 0000000..31d139f --- /dev/null +++ b/src/main/java/tech/redroma/yelp/YelpBusinessDetails.java @@ -0,0 +1,153 @@ +/* + * Copyright 2016 RedRoma, Inc.. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package tech.redroma.yelp; + +import com.google.gson.annotations.SerializedName; +import java.util.List; +import java.util.Objects; +import tech.sirwellington.alchemy.annotations.concurrency.Mutable; +import tech.sirwellington.alchemy.annotations.concurrency.ThreadUnsafe; +import tech.sirwellington.alchemy.annotations.objects.Pojo; + +/** + * + * @author SirWellington + */ +@Pojo +@Mutable +@ThreadUnsafe +public class YelpBusinessDetails +{ + + public String id; + + public String name; + + @SerializedName("image_url") + public String imageURL; + + @SerializedName("is_claimed") + public Boolean isClaimed; + + @SerializedName("is_closed") + public Boolean isClosed; + + public String url; + + public String price; + + public int rating; + + public int reviewCount; + + public String phone; + + @SerializedName("photos") + public List photosURLS; + + public Hours hours; + + public List categories; + + public Coordinate coordinates; + + public Address location; + + + + @Pojo + @ThreadUnsafe + @Mutable + public static class Hours + { + + public enum Type + { + REGULAR + } + + public static class OpenTimes + { + + public int day; + public String start; + public String end; + @SerializedName("is_overnight") + public Boolean isOvernight; + + @Override + public int hashCode() + { + int hash = 7; + hash = 71 * hash + this.day; + hash = 71 * hash + Objects.hashCode(this.start); + hash = 71 * hash + Objects.hashCode(this.end); + hash = 71 * hash + Objects.hashCode(this.isOvernight); + return hash; + } + + @Override + public boolean equals(Object obj) + { + if (this == obj) + { + return true; + } + if (obj == null) + { + return false; + } + if (getClass() != obj.getClass()) + { + return false; + } + final OpenTimes other = (OpenTimes) obj; + if (this.day != other.day) + { + return false; + } + if (!Objects.equals(this.start, other.start)) + { + return false; + } + if (!Objects.equals(this.end, other.end)) + { + return false; + } + if (!Objects.equals(this.isOvernight, other.isOvernight)) + { + return false; + } + return true; + } + + @Override + public String toString() + { + return "OpenTimes{" + "day=" + day + ", start=" + start + ", end=" + end + ", isOvernight=" + isOvernight + '}'; + } + + } + + public Type hoursType; + + @SerializedName("is_open_now") + public Boolean isOpenNow; + + } + +} From 4fece83c0efa6fd0d89914f5bdaf7cd39a276db1 Mon Sep 17 00:00:00 2001 From: Wellington Moreno Date: Wed, 30 Nov 2016 15:29:17 -0800 Subject: [PATCH 024/168] Enhances YelpBusinessDetails with POJO methods --- .../redroma/yelp/YelpBusinessDetails.java | 57 +++++++++++++++++-- 1 file changed, 52 insertions(+), 5 deletions(-) diff --git a/src/main/java/tech/redroma/yelp/YelpBusinessDetails.java b/src/main/java/tech/redroma/yelp/YelpBusinessDetails.java index 31d139f..73660f9 100644 --- a/src/main/java/tech/redroma/yelp/YelpBusinessDetails.java +++ b/src/main/java/tech/redroma/yelp/YelpBusinessDetails.java @@ -67,14 +67,19 @@ public class YelpBusinessDetails public Address location; - - @Pojo @ThreadUnsafe @Mutable public static class Hours { + public Type hoursType; + + @SerializedName("is_open_now") + public Boolean isOpenNow; + + public List open; + public enum Type { REGULAR @@ -143,10 +148,52 @@ public String toString() } - public Type hoursType; + @Override + public int hashCode() + { + int hash = 7; + hash = 97 * hash + Objects.hashCode(this.hoursType); + hash = 97 * hash + Objects.hashCode(this.isOpenNow); + hash = 97 * hash + Objects.hashCode(this.open); + return hash; + } - @SerializedName("is_open_now") - public Boolean isOpenNow; + @Override + public boolean equals(Object obj) + { + if (this == obj) + { + return true; + } + if (obj == null) + { + return false; + } + if (getClass() != obj.getClass()) + { + return false; + } + final Hours other = (Hours) obj; + if (this.hoursType != other.hoursType) + { + return false; + } + if (!Objects.equals(this.isOpenNow, other.isOpenNow)) + { + return false; + } + if (!Objects.equals(this.open, other.open)) + { + return false; + } + return true; + } + + @Override + public String toString() + { + return "Hours{" + "hoursType=" + hoursType + ", isOpenNow=" + isOpenNow + ", open=" + open + '}'; + } } From d051205dbe3f14fec23d977db665af92dcf9abca Mon Sep 17 00:00:00 2001 From: Wellington Moreno Date: Wed, 30 Nov 2016 15:29:55 -0800 Subject: [PATCH 025/168] Enhances YelpBusinessDetails with POJO methods --- .../redroma/yelp/YelpBusinessDetails.java | 108 ++++++++++++++++++ 1 file changed, 108 insertions(+) diff --git a/src/main/java/tech/redroma/yelp/YelpBusinessDetails.java b/src/main/java/tech/redroma/yelp/YelpBusinessDetails.java index 73660f9..65fd022 100644 --- a/src/main/java/tech/redroma/yelp/YelpBusinessDetails.java +++ b/src/main/java/tech/redroma/yelp/YelpBusinessDetails.java @@ -1,3 +1,4 @@ + /* * Copyright 2016 RedRoma, Inc.. * @@ -67,6 +68,113 @@ public class YelpBusinessDetails public Address location; + @Override + public int hashCode() + { + int hash = 7; + hash = 29 * hash + Objects.hashCode(this.id); + hash = 29 * hash + Objects.hashCode(this.name); + hash = 29 * hash + Objects.hashCode(this.imageURL); + hash = 29 * hash + Objects.hashCode(this.isClaimed); + hash = 29 * hash + Objects.hashCode(this.isClosed); + hash = 29 * hash + Objects.hashCode(this.url); + hash = 29 * hash + Objects.hashCode(this.price); + hash = 29 * hash + this.rating; + hash = 29 * hash + this.reviewCount; + hash = 29 * hash + Objects.hashCode(this.phone); + hash = 29 * hash + Objects.hashCode(this.photosURLS); + hash = 29 * hash + Objects.hashCode(this.hours); + hash = 29 * hash + Objects.hashCode(this.categories); + hash = 29 * hash + Objects.hashCode(this.coordinates); + hash = 29 * hash + Objects.hashCode(this.location); + return hash; + } + + @Override + public boolean equals(Object obj) + { + if (this == obj) + { + return true; + } + if (obj == null) + { + return false; + } + if (getClass() != obj.getClass()) + { + return false; + } + final YelpBusinessDetails other = (YelpBusinessDetails) obj; + if (this.rating != other.rating) + { + return false; + } + if (this.reviewCount != other.reviewCount) + { + return false; + } + if (!Objects.equals(this.id, other.id)) + { + return false; + } + if (!Objects.equals(this.name, other.name)) + { + return false; + } + if (!Objects.equals(this.imageURL, other.imageURL)) + { + return false; + } + if (!Objects.equals(this.url, other.url)) + { + return false; + } + if (!Objects.equals(this.price, other.price)) + { + return false; + } + if (!Objects.equals(this.phone, other.phone)) + { + return false; + } + if (!Objects.equals(this.isClaimed, other.isClaimed)) + { + return false; + } + if (!Objects.equals(this.isClosed, other.isClosed)) + { + return false; + } + if (!Objects.equals(this.photosURLS, other.photosURLS)) + { + return false; + } + if (!Objects.equals(this.hours, other.hours)) + { + return false; + } + if (!Objects.equals(this.categories, other.categories)) + { + return false; + } + if (!Objects.equals(this.coordinates, other.coordinates)) + { + return false; + } + if (!Objects.equals(this.location, other.location)) + { + return false; + } + return true; + } + + @Override + public String toString() + { + return "YelpBusinessDetails{" + "id=" + id + ", name=" + name + ", imageURL=" + imageURL + ", isClaimed=" + isClaimed + ", isClosed=" + isClosed + ", url=" + url + ", price=" + price + ", rating=" + rating + ", reviewCount=" + reviewCount + ", phone=" + phone + ", photosURLS=" + photosURLS + ", hours=" + hours + ", categories=" + categories + ", coordinates=" + coordinates + ", location=" + location + '}'; + } + @Pojo @ThreadUnsafe @Mutable From 55b716b6449419a0553db30050d9ffca26e5adde Mon Sep 17 00:00:00 2001 From: Wellington Moreno Date: Wed, 30 Nov 2016 15:39:43 -0800 Subject: [PATCH 026/168] Fixes and updates YelpBusinessDetails Object to match Yelp JSON + Adds unit test --- .../redroma/yelp/YelpBusinessDetails.java | 45 ++-- .../tech/redroma/yelp/business-details.json | 2 +- .../redroma/yelp/YelpBusinessDetailsTest.java | 202 ++++++++++++++++++ 3 files changed, 223 insertions(+), 26 deletions(-) create mode 100644 src/test/java/tech/redroma/yelp/YelpBusinessDetailsTest.java diff --git a/src/main/java/tech/redroma/yelp/YelpBusinessDetails.java b/src/main/java/tech/redroma/yelp/YelpBusinessDetails.java index 65fd022..4cabe9c 100644 --- a/src/main/java/tech/redroma/yelp/YelpBusinessDetails.java +++ b/src/main/java/tech/redroma/yelp/YelpBusinessDetails.java @@ -14,7 +14,6 @@ * See the License for the specific language governing permissions and * limitations under the License. */ - package tech.redroma.yelp; import com.google.gson.annotations.SerializedName; @@ -51,7 +50,7 @@ public class YelpBusinessDetails public String price; - public int rating; + public double rating; public int reviewCount; @@ -60,7 +59,7 @@ public class YelpBusinessDetails @SerializedName("photos") public List photosURLS; - public Hours hours; + public List hours; public List categories; @@ -72,21 +71,21 @@ public class YelpBusinessDetails public int hashCode() { int hash = 7; - hash = 29 * hash + Objects.hashCode(this.id); - hash = 29 * hash + Objects.hashCode(this.name); - hash = 29 * hash + Objects.hashCode(this.imageURL); - hash = 29 * hash + Objects.hashCode(this.isClaimed); - hash = 29 * hash + Objects.hashCode(this.isClosed); - hash = 29 * hash + Objects.hashCode(this.url); - hash = 29 * hash + Objects.hashCode(this.price); - hash = 29 * hash + this.rating; - hash = 29 * hash + this.reviewCount; - hash = 29 * hash + Objects.hashCode(this.phone); - hash = 29 * hash + Objects.hashCode(this.photosURLS); - hash = 29 * hash + Objects.hashCode(this.hours); - hash = 29 * hash + Objects.hashCode(this.categories); - hash = 29 * hash + Objects.hashCode(this.coordinates); - hash = 29 * hash + Objects.hashCode(this.location); + hash = 89 * hash + Objects.hashCode(this.id); + hash = 89 * hash + Objects.hashCode(this.name); + hash = 89 * hash + Objects.hashCode(this.imageURL); + hash = 89 * hash + Objects.hashCode(this.isClaimed); + hash = 89 * hash + Objects.hashCode(this.isClosed); + hash = 89 * hash + Objects.hashCode(this.url); + hash = 89 * hash + Objects.hashCode(this.price); + hash = 89 * hash + (int) (Double.doubleToLongBits(this.rating) ^ (Double.doubleToLongBits(this.rating) >>> 32)); + hash = 89 * hash + this.reviewCount; + hash = 89 * hash + Objects.hashCode(this.phone); + hash = 89 * hash + Objects.hashCode(this.photosURLS); + hash = 89 * hash + Objects.hashCode(this.hours); + hash = 89 * hash + Objects.hashCode(this.categories); + hash = 89 * hash + Objects.hashCode(this.coordinates); + hash = 89 * hash + Objects.hashCode(this.location); return hash; } @@ -181,18 +180,14 @@ public String toString() public static class Hours { - public Type hoursType; + @SerializedName("hours_type") + public String hoursType; @SerializedName("is_open_now") public Boolean isOpenNow; public List open; - public enum Type - { - REGULAR - } - public static class OpenTimes { @@ -282,7 +277,7 @@ public boolean equals(Object obj) return false; } final Hours other = (Hours) obj; - if (this.hoursType != other.hoursType) + if (!Objects.equals(this.hoursType, other.hoursType)) { return false; } diff --git a/src/main/resources/tech/redroma/yelp/business-details.json b/src/main/resources/tech/redroma/yelp/business-details.json index e3e05e8..54e4405 100644 --- a/src/main/resources/tech/redroma/yelp/business-details.json +++ b/src/main/resources/tech/redroma/yelp/business-details.json @@ -82,6 +82,6 @@ "address3": "", "state": "CA", "address1": "800 N Point St", - "zip_code": "" + "zip_code": "94109" } } diff --git a/src/test/java/tech/redroma/yelp/YelpBusinessDetailsTest.java b/src/test/java/tech/redroma/yelp/YelpBusinessDetailsTest.java new file mode 100644 index 0000000..8494f64 --- /dev/null +++ b/src/test/java/tech/redroma/yelp/YelpBusinessDetailsTest.java @@ -0,0 +1,202 @@ +/* + * Copyright 2016 RedRoma, Inc.. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package tech.redroma.yelp; + +import com.google.gson.JsonElement; +import java.io.IOException; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import tech.sirwellington.alchemy.test.junit.runners.AlchemyTestRunner; +import tech.sirwellington.alchemy.test.junit.runners.DontRepeat; +import tech.sirwellington.alchemy.test.junit.runners.GeneratePojo; +import tech.sirwellington.alchemy.test.junit.runners.Repeat; + +import static org.hamcrest.Matchers.empty; +import static org.hamcrest.Matchers.greaterThan; +import static org.hamcrest.Matchers.is; +import static org.hamcrest.Matchers.isEmptyOrNullString; +import static org.hamcrest.Matchers.not; +import static org.hamcrest.Matchers.notNullValue; +import static org.junit.Assert.assertThat; +import static tech.redroma.yelp.Resources.GSON; +import static tech.sirwellington.alchemy.arguments.Arguments.checkThat; +import static tech.sirwellington.alchemy.arguments.assertions.GeolocationAssertions.validLatitude; +import static tech.sirwellington.alchemy.arguments.assertions.GeolocationAssertions.validLongitude; +import static tech.sirwellington.alchemy.arguments.assertions.StringAssertions.nonEmptyString; +import static tech.sirwellington.alchemy.generator.AlchemyGenerator.one; +import static tech.sirwellington.alchemy.generator.GeolocationGenerators.latitudes; +import static tech.sirwellington.alchemy.generator.GeolocationGenerators.longitudes; + +/** + * + * @author SirWellington + */ +@Repeat(10) +@RunWith(AlchemyTestRunner.class) +public class YelpBusinessDetailsTest +{ + + @GeneratePojo + private YelpBusinessDetails instance; + + @GeneratePojo + private YelpBusinessDetails first; + + @GeneratePojo + private YelpBusinessDetails second; + + @Before + public void setUp() + { + setupData(); + } + + private void setupData() + { + instance.coordinates.latitude = one(latitudes()); + instance.coordinates.longitude = one(longitudes()); + + first.coordinates.latitude = one(latitudes()); + first.coordinates.longitude = one(longitudes()); + + second.coordinates.latitude = one(latitudes()); + second.coordinates.longitude = one(longitudes()); + } + + @DontRepeat + @Test + public void testInstanceIsNotNull() + { + assertThat(instance, notNullValue()); + } + + @Test + public void testInstanceHasAllData() + { + checkBusiness(instance); + checkBusiness(first); + checkBusiness(second); + } + + private void checkBusiness(YelpBusinessDetails business) + { + assertThat(business.id, not(isEmptyOrNullString())); + assertThat(business.imageURL, not(isEmptyOrNullString())); + assertThat(business.name, not(isEmptyOrNullString())); + assertThat(business.phone, not(isEmptyOrNullString())); + assertThat(business.price, not(isEmptyOrNullString())); + assertThat(business.url, not(isEmptyOrNullString())); + assertThat(business.isClosed, notNullValue()); + assertThat(business.isClaimed, notNullValue()); + assertThat(business.categories, notNullValue()); + assertThat(business.rating, is(greaterThan(0.0))); + assertThat(business.hours, not(empty())); + + assertThat(business.photosURLS, not(empty())); + business.photosURLS.forEach(p -> checkThat(p).is(nonEmptyString())); + + business.categories.forEach(this::checkCategory); + checkCoordinate(business.coordinates); + checkAddress(business.location); + business.hours.forEach(this::checkHours); + } + + private void checkCategory(Category category) + { + assertThat(category, notNullValue()); + assertThat(category.alias, not(isEmptyOrNullString())); + assertThat(category.title, not(isEmptyOrNullString())); + } + + private void checkCoordinate(Coordinate coordinate) + { + assertThat(coordinate, notNullValue()); + checkThat(coordinate.latitude) + .is(validLatitude()); + checkThat(coordinate.longitude) + .is(validLongitude()); + } + + private void checkAddress(Address address) + { + assertThat(address, notNullValue()); + assertThat(address.address1, not(isEmptyOrNullString())); + assertThat(address.city, not(isEmptyOrNullString())); + assertThat(address.country, not(isEmptyOrNullString())); + assertThat(address.state, not(isEmptyOrNullString())); + assertThat(address.zipCode, not(isEmptyOrNullString())); + } + + private void checkHours(YelpBusinessDetails.Hours hours) + { + assertThat(hours, notNullValue()); + assertThat(hours.hoursType, notNullValue()); + assertThat(hours.isOpenNow, notNullValue()); + assertThat(hours.open, not(empty())); + hours.open.forEach(this::checkHour); + } + + private void checkHour(YelpBusinessDetails.Hours.OpenTimes times) + { + assertThat(times, notNullValue()); + assertThat(times.start, not(isEmptyOrNullString())); + assertThat(times.end, not(isEmptyOrNullString())); + assertThat(times.day, notNullValue()); + assertThat(times.isOvernight, notNullValue()); + } + + @Test + public void testEqualsWhenNotEqual() + { + assertThat(first, not(second)); + } + + @Test + public void testEqualsWhenEquals() + { + assertThat(first, is(first)); + } + + @Test + public void testHashCode() + { + assertThat(first.hashCode(), is(first.hashCode())); + assertThat(first.hashCode(), not(second.hashCode())); + } + + @Repeat(25) + @Test + public void testSerialization() + { + JsonElement json = GSON.toJsonTree(instance); + assertThat(json, notNullValue()); + assertThat(json.isJsonObject(), is(true)); + } + + @DontRepeat + @Test + public void testDeserialization() throws IOException + { + String json = Resources.loadResource("business-details.json"); + + YelpBusinessDetails result = GSON.fromJson(json, YelpBusinessDetails.class); + assertThat(result, notNullValue()); + checkBusiness(result); + } + +} From e2bd06206f74fbeeab587064756562708969784e Mon Sep 17 00:00:00 2001 From: Wellington Moreno Date: Wed, 30 Nov 2016 15:41:12 -0800 Subject: [PATCH 027/168] Updates .gitignore --- .gitignore | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.gitignore b/.gitignore index 32858aa..c82f6d6 100644 --- a/.gitignore +++ b/.gitignore @@ -10,3 +10,5 @@ # virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml hs_err_pid* +/target/ +/nbproject/ \ No newline at end of file From 44b7cf0a27adce994d8497791bd3c34070aca3c4 Mon Sep 17 00:00:00 2001 From: Wellington Moreno Date: Wed, 30 Nov 2016 15:43:02 -0800 Subject: [PATCH 028/168] Adds exceptions for the client library --- .../exceptions/YelpBadArgumentException.java | 45 ++++++++++++++++++ .../redroma/yelp/exceptions/YelpExcetion.java | 46 +++++++++++++++++++ .../YelpOperationFailedException.java | 45 ++++++++++++++++++ 3 files changed, 136 insertions(+) create mode 100644 src/main/java/tech/redroma/yelp/exceptions/YelpBadArgumentException.java create mode 100644 src/main/java/tech/redroma/yelp/exceptions/YelpExcetion.java create mode 100644 src/main/java/tech/redroma/yelp/exceptions/YelpOperationFailedException.java diff --git a/src/main/java/tech/redroma/yelp/exceptions/YelpBadArgumentException.java b/src/main/java/tech/redroma/yelp/exceptions/YelpBadArgumentException.java new file mode 100644 index 0000000..6bec7a3 --- /dev/null +++ b/src/main/java/tech/redroma/yelp/exceptions/YelpBadArgumentException.java @@ -0,0 +1,45 @@ +/* + * Copyright 2016 RedRoma, Inc.. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package tech.redroma.yelp.exceptions; + +/** + * + * @author SirWellington + */ +public class YelpBadArgumentException extends YelpExcetion +{ + + public YelpBadArgumentException() + { + } + + public YelpBadArgumentException(String message) + { + super(message); + } + + public YelpBadArgumentException(String message, Throwable cause) + { + super(message, cause); + } + + public YelpBadArgumentException(Throwable cause) + { + super(cause); + } + +} diff --git a/src/main/java/tech/redroma/yelp/exceptions/YelpExcetion.java b/src/main/java/tech/redroma/yelp/exceptions/YelpExcetion.java new file mode 100644 index 0000000..bae6efe --- /dev/null +++ b/src/main/java/tech/redroma/yelp/exceptions/YelpExcetion.java @@ -0,0 +1,46 @@ +/* + * Copyright 2016 RedRoma, Inc.. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package tech.redroma.yelp.exceptions; + +/** + * The Base Exception type for all YelpAPI Exceptions. + * + * @author SirWellington + */ +public class YelpExcetion extends RuntimeException +{ + + public YelpExcetion() + { + } + + public YelpExcetion(String message) + { + super(message); + } + + public YelpExcetion(String message, Throwable cause) + { + super(message, cause); + } + + public YelpExcetion(Throwable cause) + { + super(cause); + } + +} diff --git a/src/main/java/tech/redroma/yelp/exceptions/YelpOperationFailedException.java b/src/main/java/tech/redroma/yelp/exceptions/YelpOperationFailedException.java new file mode 100644 index 0000000..19dba40 --- /dev/null +++ b/src/main/java/tech/redroma/yelp/exceptions/YelpOperationFailedException.java @@ -0,0 +1,45 @@ +/* + * Copyright 2016 RedRoma, Inc.. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package tech.redroma.yelp.exceptions; + +/** + * + * @author SirWellington + */ +public class YelpOperationFailedException extends YelpExcetion +{ + + public YelpOperationFailedException() + { + } + + public YelpOperationFailedException(String message) + { + super(message); + } + + public YelpOperationFailedException(String message, Throwable cause) + { + super(message, cause); + } + + public YelpOperationFailedException(Throwable cause) + { + super(cause); + } + +} From 151fa6bece6a24003f569f2564972aad4911aba6 Mon Sep 17 00:00:00 2001 From: Wellington Moreno Date: Wed, 30 Nov 2016 16:47:40 -0800 Subject: [PATCH 029/168] Adds YelpAreaTooLargeException exception type --- .../exceptions/YelpAreaTooLargeException.java | 46 +++++++++++++++++++ 1 file changed, 46 insertions(+) create mode 100644 src/main/java/tech/redroma/yelp/exceptions/YelpAreaTooLargeException.java diff --git a/src/main/java/tech/redroma/yelp/exceptions/YelpAreaTooLargeException.java b/src/main/java/tech/redroma/yelp/exceptions/YelpAreaTooLargeException.java new file mode 100644 index 0000000..55c396d --- /dev/null +++ b/src/main/java/tech/redroma/yelp/exceptions/YelpAreaTooLargeException.java @@ -0,0 +1,46 @@ +/* + * Copyright 2016 RedRoma, Inc.. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package tech.redroma.yelp.exceptions; + +/** + * Thrown when making a query with a search radius that is too large. + * + * @author SirWellington + */ +public class YelpAreaTooLargeException extends YelpExcetion +{ + + public YelpAreaTooLargeException() + { + } + + public YelpAreaTooLargeException(String message) + { + super(message); + } + + public YelpAreaTooLargeException(String message, Throwable cause) + { + super(message, cause); + } + + public YelpAreaTooLargeException(Throwable cause) + { + super(cause); + } + +} From 377a9757b708ecb075a98d364ed451d2e6b62815 Mon Sep 17 00:00:00 2001 From: Wellington Moreno Date: Wed, 30 Nov 2016 17:26:16 -0800 Subject: [PATCH 030/168] Adds YelpSearchQuery to capture a Search Query --- .../tech/redroma/yelp/YelpSearchQuery.java | 172 ++++++++++++++++++ 1 file changed, 172 insertions(+) create mode 100644 src/main/java/tech/redroma/yelp/YelpSearchQuery.java diff --git a/src/main/java/tech/redroma/yelp/YelpSearchQuery.java b/src/main/java/tech/redroma/yelp/YelpSearchQuery.java new file mode 100644 index 0000000..0651956 --- /dev/null +++ b/src/main/java/tech/redroma/yelp/YelpSearchQuery.java @@ -0,0 +1,172 @@ +/* + * Copyright 2016 RedRoma, Inc.. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package tech.redroma.yelp; + +import tech.redroma.yelp.exceptions.YelpAreaTooLargeException; +import tech.sirwellington.alchemy.annotations.arguments.Optional; +import tech.sirwellington.alchemy.annotations.concurrency.Immutable; +import tech.sirwellington.alchemy.annotations.concurrency.ThreadSafe; +import tech.sirwellington.alchemy.annotations.objects.Pojo; + +/** + * + * @author SirWellington + */ +@Pojo +@Immutable +@ThreadSafe +public class YelpSearchQuery +{ + + /* + * Optional search term (e.g. "food", "restaurants"). If the term isn't included we search everything. The term keyword also + * accepts business names such as "Starbucks". + */ + @Optional + private String searchTerm; + + /** + * Required if either latitude or longitude is not provided. Specifies the combination of: + Address + Neighborhood + City + + * State + Zip + Country + */ + private String location; + + /** + * The latitude of the location you want to search near by. Required if location is not provided. + */ + private Double latitude; + + /** + * Longitude of the location you want to search near by. required if the location is not provided. + */ + private Double longitude; + + /** + * Search radius, in meters. If the value is too large, a {@link YelpAreaTooLargeException} is thrown. + */ + @Optional + private int radius; + + /** + * categories to filter the serach results with. See the list of supported categories. The category filter can be a ist of + * comma-delimited categories. For example, "bars, french", will filter Bars and French. The category identifier should be + * used (e.g. "discgolf", instead of "Disc Golf"). + */ + @Optional + private String categories; + + /** + * Specify the locale to return the business information in. + * + * @see + * https://www.yelp.com/developers/documentation/v3/supported_locales + */ + @Optional + private String locale; + + /** + * Specify the maximum number of businesses to return. By default, it will return 20. The maximum is 50. + */ + @Optional + private int limit; + + /** + * Offset the list of returned businesses by the amount. For, example, if you have seen results 1-10, specify '10' to see the + * next 11-20. + */ + @Optional + private int offset; + + /** + * Sort the results by one of these modes: + * + */ + @Optional + private SortType sortBy; + + /** + * Pricing levels to filter the search result with. + */ + @Optional + private String price; + + /** + * Defaults to false When set tot rue, only return the businesses open now. Notice that {@link #openNow} and {@link #openAt} + * cannot be used together. + */ + @Optional + private Boolean openNow; + + /** + * An integer representing the Unix timestamp in the same time-zone of the search location. + * + * If specified, it will return businesses that are open at the given time. Note that {@link #openNow} and {@link #openAt} + * cannot be used together. + */ + @Optional + private Boolean openAt; + + public enum SortType + { + BEST_MATCH("best_match"), + RATING("rating"), + REVIEW_COUNT("review_count"), + DISTANCE("distance"); + + public final String text; + + private SortType(String text) + { + this.text = text; + } + } + + public enum PricingLevel + { + $(1), + $$(2), + $$$(3), + $$$$(4); + + final int number; + + private PricingLevel(int number) + { + this.number = number; + } + + } + + public static class Builder + { + + private String term; + private String location; + private Coordinate coordinate; + private double radiusInMeters; + private String categories; + private String locale; + private int offset; + private String sortBy; + private String price; + private boolean isOpenNow; + private int openAt; + private String attributes; + + } + +} From 9a650969678d7016cfe0d813498ac76851bd46a8 Mon Sep 17 00:00:00 2001 From: Wellington Moreno Date: Wed, 30 Nov 2016 17:27:28 -0800 Subject: [PATCH 031/168] Updates documentation --- src/main/java/tech/redroma/yelp/YelpSearchQuery.java | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/src/main/java/tech/redroma/yelp/YelpSearchQuery.java b/src/main/java/tech/redroma/yelp/YelpSearchQuery.java index 0651956..6ca54c6 100644 --- a/src/main/java/tech/redroma/yelp/YelpSearchQuery.java +++ b/src/main/java/tech/redroma/yelp/YelpSearchQuery.java @@ -40,8 +40,15 @@ public class YelpSearchQuery private String searchTerm; /** - * Required if either latitude or longitude is not provided. Specifies the combination of: + Address + Neighborhood + City + - * State + Zip + Country + * Required if either latitude or longitude is not provided. Specifies the combination of: + *
+     * + Address
+     * + Neighborhood
+     * + City
+     * + State
+     * + Zip
+     * + Country
+     * 
*/ private String location; From 7280927b31195ad6004cd108dd4f5f45c83f090e Mon Sep 17 00:00:00 2001 From: Wellington Moreno Date: Wed, 30 Nov 2016 17:28:37 -0800 Subject: [PATCH 032/168] Adds toString(), hashCode(), and equals() to YelpSearchQuery --- .../tech/redroma/yelp/YelpSearchQuery.java | 98 +++++++++++++++++++ 1 file changed, 98 insertions(+) diff --git a/src/main/java/tech/redroma/yelp/YelpSearchQuery.java b/src/main/java/tech/redroma/yelp/YelpSearchQuery.java index 6ca54c6..2679ee8 100644 --- a/src/main/java/tech/redroma/yelp/YelpSearchQuery.java +++ b/src/main/java/tech/redroma/yelp/YelpSearchQuery.java @@ -16,6 +16,7 @@ package tech.redroma.yelp; +import java.util.Objects; import tech.redroma.yelp.exceptions.YelpAreaTooLargeException; import tech.sirwellington.alchemy.annotations.arguments.Optional; import tech.sirwellington.alchemy.annotations.concurrency.Immutable; @@ -127,6 +128,103 @@ public class YelpSearchQuery @Optional private Boolean openAt; + @Override + public int hashCode() + { + int hash = 3; + hash = 79 * hash + Objects.hashCode(this.searchTerm); + hash = 79 * hash + Objects.hashCode(this.location); + hash = 79 * hash + Objects.hashCode(this.latitude); + hash = 79 * hash + Objects.hashCode(this.longitude); + hash = 79 * hash + this.radius; + hash = 79 * hash + Objects.hashCode(this.categories); + hash = 79 * hash + Objects.hashCode(this.locale); + hash = 79 * hash + this.limit; + hash = 79 * hash + this.offset; + hash = 79 * hash + Objects.hashCode(this.sortBy); + hash = 79 * hash + Objects.hashCode(this.price); + hash = 79 * hash + Objects.hashCode(this.openNow); + hash = 79 * hash + Objects.hashCode(this.openAt); + return hash; + } + + @Override + public boolean equals(Object obj) + { + if (this == obj) + { + return true; + } + if (obj == null) + { + return false; + } + if (getClass() != obj.getClass()) + { + return false; + } + final YelpSearchQuery other = (YelpSearchQuery) obj; + if (this.radius != other.radius) + { + return false; + } + if (this.limit != other.limit) + { + return false; + } + if (this.offset != other.offset) + { + return false; + } + if (!Objects.equals(this.searchTerm, other.searchTerm)) + { + return false; + } + if (!Objects.equals(this.location, other.location)) + { + return false; + } + if (!Objects.equals(this.categories, other.categories)) + { + return false; + } + if (!Objects.equals(this.locale, other.locale)) + { + return false; + } + if (!Objects.equals(this.price, other.price)) + { + return false; + } + if (!Objects.equals(this.latitude, other.latitude)) + { + return false; + } + if (!Objects.equals(this.longitude, other.longitude)) + { + return false; + } + if (this.sortBy != other.sortBy) + { + return false; + } + if (!Objects.equals(this.openNow, other.openNow)) + { + return false; + } + if (!Objects.equals(this.openAt, other.openAt)) + { + return false; + } + return true; + } + + @Override + public String toString() + { + return "YelpSearchQuery{" + "searchTerm=" + searchTerm + ", location=" + location + ", latitude=" + latitude + ", longitude=" + longitude + ", radius=" + radius + ", categories=" + categories + ", locale=" + locale + ", limit=" + limit + ", offset=" + offset + ", sortBy=" + sortBy + ", price=" + price + ", openNow=" + openNow + ", openAt=" + openAt + '}'; + } + public enum SortType { BEST_MATCH("best_match"), From dd9f9902fe9607b9bc8df7735eee9538d016dd73 Mon Sep 17 00:00:00 2001 From: Wellington Moreno Date: Wed, 30 Nov 2016 17:31:00 -0800 Subject: [PATCH 033/168] Makes YelpSearchQuery immutable --- .../tech/redroma/yelp/YelpSearchQuery.java | 61 ++++++++++++++----- 1 file changed, 47 insertions(+), 14 deletions(-) diff --git a/src/main/java/tech/redroma/yelp/YelpSearchQuery.java b/src/main/java/tech/redroma/yelp/YelpSearchQuery.java index 2679ee8..6a28dfe 100644 --- a/src/main/java/tech/redroma/yelp/YelpSearchQuery.java +++ b/src/main/java/tech/redroma/yelp/YelpSearchQuery.java @@ -21,8 +21,11 @@ import tech.sirwellington.alchemy.annotations.arguments.Optional; import tech.sirwellington.alchemy.annotations.concurrency.Immutable; import tech.sirwellington.alchemy.annotations.concurrency.ThreadSafe; +import tech.sirwellington.alchemy.annotations.designs.patterns.BuilderPattern; import tech.sirwellington.alchemy.annotations.objects.Pojo; +import static tech.sirwellington.alchemy.annotations.designs.patterns.BuilderPattern.Role.PRODUCT; + /** * * @author SirWellington @@ -30,7 +33,8 @@ @Pojo @Immutable @ThreadSafe -public class YelpSearchQuery +@BuilderPattern(role = PRODUCT) +public final class YelpSearchQuery { /* @@ -38,7 +42,7 @@ public class YelpSearchQuery * accepts business names such as "Starbucks". */ @Optional - private String searchTerm; + private final String searchTerm; /** * Required if either latitude or longitude is not provided. Specifies the combination of: @@ -51,23 +55,23 @@ public class YelpSearchQuery * + Country * */ - private String location; + private final String location; /** * The latitude of the location you want to search near by. Required if location is not provided. */ - private Double latitude; + private final Double latitude; /** * Longitude of the location you want to search near by. required if the location is not provided. */ - private Double longitude; + private final Double longitude; /** * Search radius, in meters. If the value is too large, a {@link YelpAreaTooLargeException} is thrown. */ @Optional - private int radius; + private final int radius; /** * categories to filter the serach results with. See the list of supported categories. The category filter can be a ist of @@ -75,7 +79,7 @@ public class YelpSearchQuery * used (e.g. "discgolf", instead of "Disc Golf"). */ @Optional - private String categories; + private final String categories; /** * Specify the locale to return the business information in. @@ -84,40 +88,40 @@ public class YelpSearchQuery * https://www.yelp.com/developers/documentation/v3/supported_locales */ @Optional - private String locale; + private final String locale; /** * Specify the maximum number of businesses to return. By default, it will return 20. The maximum is 50. */ @Optional - private int limit; + private final int limit; /** * Offset the list of returned businesses by the amount. For, example, if you have seen results 1-10, specify '10' to see the * next 11-20. */ @Optional - private int offset; + private final int offset; /** * Sort the results by one of these modes: * */ @Optional - private SortType sortBy; + private final SortType sortBy; /** * Pricing levels to filter the search result with. */ @Optional - private String price; + private final String price; /** * Defaults to false When set tot rue, only return the businesses open now. Notice that {@link #openNow} and {@link #openAt} * cannot be used together. */ @Optional - private Boolean openNow; + private final Boolean openNow; /** * An integer representing the Unix timestamp in the same time-zone of the search location. @@ -126,7 +130,36 @@ public class YelpSearchQuery * cannot be used together. */ @Optional - private Boolean openAt; + private final Boolean openAt; + + YelpSearchQuery(String searchTerm, + String location, + Double latitude, + Double longitude, + int radius, + String categories, + String locale, + int limit, + int offset, + SortType sortBy, + String price, + Boolean openNow, + Boolean openAt) + { + this.searchTerm = searchTerm; + this.location = location; + this.latitude = latitude; + this.longitude = longitude; + this.radius = radius; + this.categories = categories; + this.locale = locale; + this.limit = limit; + this.offset = offset; + this.sortBy = sortBy; + this.price = price; + this.openNow = openNow; + this.openAt = openAt; + } @Override public int hashCode() From 45faaf7bb6ab4a6e21037520ac51181f063a7c8c Mon Sep 17 00:00:00 2001 From: Wellington Moreno Date: Wed, 30 Nov 2016 18:07:52 -0800 Subject: [PATCH 034/168] Adds functions to Address object to check presence of certain fields --- src/main/java/tech/redroma/yelp/Address.java | 17 ++++++++++++ .../java/tech/redroma/yelp/AddressTest.java | 26 +++++++++++++++++++ 2 files changed, 43 insertions(+) diff --git a/src/main/java/tech/redroma/yelp/Address.java b/src/main/java/tech/redroma/yelp/Address.java index 9f7f09b..f753f5f 100644 --- a/src/main/java/tech/redroma/yelp/Address.java +++ b/src/main/java/tech/redroma/yelp/Address.java @@ -24,6 +24,8 @@ import tech.sirwellington.alchemy.annotations.concurrency.ThreadUnsafe; import tech.sirwellington.alchemy.annotations.objects.Pojo; +import static com.google.common.base.Strings.isNullOrEmpty; + /** * * @author SirWellington @@ -42,6 +44,21 @@ public class Address @SerializedName("zip_code") public String zipCode; + public boolean hasAddress2() + { + return !isNullOrEmpty(address2); + } + + public boolean hasAddress3() + { + return !isNullOrEmpty(address3); + } + + public boolean hasZipCode() + { + return !isNullOrEmpty(zipCode); + } + @Override public int hashCode() { diff --git a/src/test/java/tech/redroma/yelp/AddressTest.java b/src/test/java/tech/redroma/yelp/AddressTest.java index 8399f2e..f48cab7 100644 --- a/src/test/java/tech/redroma/yelp/AddressTest.java +++ b/src/test/java/tech/redroma/yelp/AddressTest.java @@ -27,7 +27,9 @@ import static org.hamcrest.Matchers.isEmptyOrNullString; import static org.hamcrest.Matchers.not; import static org.hamcrest.Matchers.notNullValue; +import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertThat; +import static org.junit.Assert.assertTrue; /** * @@ -86,4 +88,28 @@ public void testHashCode() assertThat(first.hashCode(), not(second.hashCode())); } + @Test + public void testHasAddress2() + { + assertTrue(instance.hasAddress2()); + instance.address2 = null; + assertFalse(instance.hasAddress2()); + } + + @Test + public void testHasAddress3() + { + assertTrue(instance.hasAddress3()); + instance.address3 = null; + assertFalse(instance.hasAddress3()); + } + + @Test + public void testHasZipCode() + { + assertTrue(instance.hasZipCode()); + instance.zipCode = null; + assertFalse(instance.hasZipCode()); + } + } From 6453ed21a151085490ddd7af25d5a1fe2edf7622 Mon Sep 17 00:00:00 2001 From: Wellington Moreno Date: Wed, 30 Nov 2016 18:21:58 -0800 Subject: [PATCH 035/168] Adds Category.with() static factory function --- src/main/java/tech/redroma/yelp/Category.java | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/src/main/java/tech/redroma/yelp/Category.java b/src/main/java/tech/redroma/yelp/Category.java index a6deb38..d236e31 100644 --- a/src/main/java/tech/redroma/yelp/Category.java +++ b/src/main/java/tech/redroma/yelp/Category.java @@ -19,10 +19,14 @@ import java.util.Objects; +import tech.sirwellington.alchemy.annotations.arguments.NonEmpty; import tech.sirwellington.alchemy.annotations.concurrency.Mutable; import tech.sirwellington.alchemy.annotations.concurrency.ThreadUnsafe; import tech.sirwellington.alchemy.annotations.objects.Pojo; +import static tech.sirwellington.alchemy.arguments.Arguments.checkThat; +import static tech.sirwellington.alchemy.arguments.assertions.StringAssertions.nonEmptyString; + /** * * @author SirWellington @@ -35,6 +39,18 @@ public class Category public String alias; public String title; + public Category with(@NonEmpty String alias, @NonEmpty String title) + { + checkThat(alias, title) + .are(nonEmptyString()); + + Category category = new Category(); + category.alias = alias; + category.title = title; + + return category; + } + @Override public int hashCode() { From b8b71b1156291f307660b568dc34493a28d665d9 Mon Sep 17 00:00:00 2001 From: Wellington Moreno Date: Wed, 30 Nov 2016 18:23:18 -0800 Subject: [PATCH 036/168] Adds documentation --- src/main/java/tech/redroma/yelp/Category.java | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/src/main/java/tech/redroma/yelp/Category.java b/src/main/java/tech/redroma/yelp/Category.java index d236e31..56b669a 100644 --- a/src/main/java/tech/redroma/yelp/Category.java +++ b/src/main/java/tech/redroma/yelp/Category.java @@ -39,6 +39,14 @@ public class Category public String alias; public String title; + /** + * Creates a Category object with the given alias and title. + * + * @param alias + * @param title + * + * @return + */ public Category with(@NonEmpty String alias, @NonEmpty String title) { checkThat(alias, title) From 59a10ad92d98a72941270fc66468cfe7ef69f3b6 Mon Sep 17 00:00:00 2001 From: Wellington Moreno Date: Wed, 30 Nov 2016 20:38:00 -0800 Subject: [PATCH 037/168] Adds business logic to create a YelpSearchRequest --- .../tech/redroma/yelp/YelpSearchQuery.java | 314 +++++++++++++++--- 1 file changed, 267 insertions(+), 47 deletions(-) diff --git a/src/main/java/tech/redroma/yelp/YelpSearchQuery.java b/src/main/java/tech/redroma/yelp/YelpSearchQuery.java index 6a28dfe..a01d92b 100644 --- a/src/main/java/tech/redroma/yelp/YelpSearchQuery.java +++ b/src/main/java/tech/redroma/yelp/YelpSearchQuery.java @@ -16,15 +16,31 @@ package tech.redroma.yelp; +import java.util.List; import java.util.Objects; +import java.util.stream.Collectors; +import sir.wellington.alchemy.collections.lists.Lists; import tech.redroma.yelp.exceptions.YelpAreaTooLargeException; +import tech.redroma.yelp.exceptions.YelpBadArgumentException; +import tech.sirwellington.alchemy.annotations.arguments.NonEmpty; import tech.sirwellington.alchemy.annotations.arguments.Optional; +import tech.sirwellington.alchemy.annotations.arguments.Positive; +import tech.sirwellington.alchemy.annotations.arguments.Required; import tech.sirwellington.alchemy.annotations.concurrency.Immutable; import tech.sirwellington.alchemy.annotations.concurrency.ThreadSafe; import tech.sirwellington.alchemy.annotations.designs.patterns.BuilderPattern; import tech.sirwellington.alchemy.annotations.objects.Pojo; +import static tech.sirwellington.alchemy.annotations.designs.patterns.BuilderPattern.Role.BUILDER; import static tech.sirwellington.alchemy.annotations.designs.patterns.BuilderPattern.Role.PRODUCT; +import static tech.sirwellington.alchemy.arguments.Arguments.checkThat; +import static tech.sirwellington.alchemy.arguments.assertions.Assertions.notNull; +import static tech.sirwellington.alchemy.arguments.assertions.CollectionAssertions.nonEmptyList; +import static tech.sirwellington.alchemy.arguments.assertions.GeolocationAssertions.validLatitude; +import static tech.sirwellington.alchemy.arguments.assertions.GeolocationAssertions.validLongitude; +import static tech.sirwellington.alchemy.arguments.assertions.NumberAssertions.lessThanOrEqualTo; +import static tech.sirwellington.alchemy.arguments.assertions.NumberAssertions.positiveInteger; +import static tech.sirwellington.alchemy.arguments.assertions.StringAssertions.nonEmptyString; /** * @@ -71,7 +87,7 @@ public final class YelpSearchQuery * Search radius, in meters. If the value is too large, a {@link YelpAreaTooLargeException} is thrown. */ @Optional - private final int radius; + private final Integer radius; /** * categories to filter the serach results with. See the list of supported categories. The category filter can be a ist of @@ -94,27 +110,27 @@ public final class YelpSearchQuery * Specify the maximum number of businesses to return. By default, it will return 20. The maximum is 50. */ @Optional - private final int limit; + private final Integer limit; /** * Offset the list of returned businesses by the amount. For, example, if you have seen results 1-10, specify '10' to see the * next 11-20. */ @Optional - private final int offset; + private final Integer offset; /** - * Sort the results by one of these modes: + * Sort the results by one of these modes: {@link SortType}. * */ @Optional - private final SortType sortBy; + private final String sortBy; /** * Pricing levels to filter the search result with. */ @Optional - private final String price; + private final String prices; /** * Defaults to false When set tot rue, only return the businesses open now. Notice that {@link #openNow} and {@link #openAt} @@ -130,21 +146,31 @@ public final class YelpSearchQuery * cannot be used together. */ @Optional - private final Boolean openAt; + private final Integer openAt; + + /** + * Additional filters to search businesses. you can use multiple attribute filters at the same time by providing a + * comma-separated string. For example: {@code "attribute1,attribute2"}. + *

+ * Currently the valid values are: {@code "hot_and_new" & "deals"}. + */ + @Optional + private final String attributes; YelpSearchQuery(String searchTerm, String location, Double latitude, Double longitude, - int radius, + Integer radius, String categories, String locale, - int limit, - int offset, - SortType sortBy, + Integer limit, + Integer offset, + String sortBy, String price, Boolean openNow, - Boolean openAt) + Integer openAt, + String attributes) { this.searchTerm = searchTerm; this.location = location; @@ -156,28 +182,30 @@ public final class YelpSearchQuery this.limit = limit; this.offset = offset; this.sortBy = sortBy; - this.price = price; + this.prices = price; this.openNow = openNow; this.openAt = openAt; + this.attributes = attributes; } @Override public int hashCode() { - int hash = 3; - hash = 79 * hash + Objects.hashCode(this.searchTerm); - hash = 79 * hash + Objects.hashCode(this.location); - hash = 79 * hash + Objects.hashCode(this.latitude); - hash = 79 * hash + Objects.hashCode(this.longitude); - hash = 79 * hash + this.radius; - hash = 79 * hash + Objects.hashCode(this.categories); - hash = 79 * hash + Objects.hashCode(this.locale); - hash = 79 * hash + this.limit; - hash = 79 * hash + this.offset; - hash = 79 * hash + Objects.hashCode(this.sortBy); - hash = 79 * hash + Objects.hashCode(this.price); - hash = 79 * hash + Objects.hashCode(this.openNow); - hash = 79 * hash + Objects.hashCode(this.openAt); + int hash = 5; + hash = 17 * hash + Objects.hashCode(this.searchTerm); + hash = 17 * hash + Objects.hashCode(this.location); + hash = 17 * hash + Objects.hashCode(this.latitude); + hash = 17 * hash + Objects.hashCode(this.longitude); + hash = 17 * hash + Objects.hashCode(this.radius); + hash = 17 * hash + Objects.hashCode(this.categories); + hash = 17 * hash + Objects.hashCode(this.locale); + hash = 17 * hash + Objects.hashCode(this.limit); + hash = 17 * hash + Objects.hashCode(this.offset); + hash = 17 * hash + Objects.hashCode(this.sortBy); + hash = 17 * hash + Objects.hashCode(this.prices); + hash = 17 * hash + Objects.hashCode(this.openNow); + hash = 17 * hash + Objects.hashCode(this.openAt); + hash = 17 * hash + Objects.hashCode(this.attributes); return hash; } @@ -197,47 +225,51 @@ public boolean equals(Object obj) return false; } final YelpSearchQuery other = (YelpSearchQuery) obj; - if (this.radius != other.radius) + if (!Objects.equals(this.searchTerm, other.searchTerm)) { return false; } - if (this.limit != other.limit) + if (!Objects.equals(this.location, other.location)) { return false; } - if (this.offset != other.offset) + if (!Objects.equals(this.categories, other.categories)) { return false; } - if (!Objects.equals(this.searchTerm, other.searchTerm)) + if (!Objects.equals(this.locale, other.locale)) { return false; } - if (!Objects.equals(this.location, other.location)) + if (!Objects.equals(this.sortBy, other.sortBy)) { return false; } - if (!Objects.equals(this.categories, other.categories)) + if (!Objects.equals(this.prices, other.prices)) { return false; } - if (!Objects.equals(this.locale, other.locale)) + if (!Objects.equals(this.attributes, other.attributes)) { return false; } - if (!Objects.equals(this.price, other.price)) + if (!Objects.equals(this.latitude, other.latitude)) { return false; } - if (!Objects.equals(this.latitude, other.latitude)) + if (!Objects.equals(this.longitude, other.longitude)) { return false; } - if (!Objects.equals(this.longitude, other.longitude)) + if (!Objects.equals(this.radius, other.radius)) + { + return false; + } + if (!Objects.equals(this.limit, other.limit)) { return false; } - if (this.sortBy != other.sortBy) + if (!Objects.equals(this.offset, other.offset)) { return false; } @@ -255,7 +287,7 @@ public boolean equals(Object obj) @Override public String toString() { - return "YelpSearchQuery{" + "searchTerm=" + searchTerm + ", location=" + location + ", latitude=" + latitude + ", longitude=" + longitude + ", radius=" + radius + ", categories=" + categories + ", locale=" + locale + ", limit=" + limit + ", offset=" + offset + ", sortBy=" + sortBy + ", price=" + price + ", openNow=" + openNow + ", openAt=" + openAt + '}'; + return "YelpSearchQuery{" + "searchTerm=" + searchTerm + ", location=" + location + ", latitude=" + latitude + ", longitude=" + longitude + ", radius=" + radius + ", categories=" + categories + ", locale=" + locale + ", limit=" + limit + ", offset=" + offset + ", sortBy=" + sortBy + ", price=" + prices + ", openNow=" + openNow + ", openAt=" + openAt + '}'; } public enum SortType @@ -289,22 +321,210 @@ private PricingLevel(int number) } - public static class Builder + @BuilderPattern(role = BUILDER) + public final static class Builder { - private String term; + private final static int MAX_LIMIT = 50; + + private String searchTerm; private String location; - private Coordinate coordinate; - private double radiusInMeters; + private Double latitude; + private Double longitude; + private Integer radiusInMeters; private String categories; private String locale; - private int offset; + private Integer limit; + private Integer offset; private String sortBy; - private String price; - private boolean isOpenNow; - private int openAt; + private String prices; + private Boolean isOpenNow; + private Integer openAt; private String attributes; + public static Builder newInstance() + { + return new Builder(); + } + + public static Builder from(@Required YelpSearchQuery query) throws IllegalArgumentException + { + checkThat(query).is(notNull()); + + Builder builder = Builder.newInstance(); + builder.searchTerm = query.searchTerm; + builder.locale = query.locale; + builder.latitude = query.latitude; + builder.longitude = query.longitude; + builder.radiusInMeters = query.radius; + builder.categories = query.categories; + builder.location = query.location; + builder.limit = query.limit; + builder.offset = query.offset; + builder.sortBy = query.sortBy; + builder.prices = query.prices; + builder.isOpenNow = query.openNow; + builder.openAt = query.openAt; + builder.attributes = query.attributes; + + return builder; + } + + public Builder withSearchTerm(@NonEmpty String searchTerm) throws IllegalArgumentException + { + checkThat(searchTerm).is(nonEmptyString()); + + this.searchTerm = searchTerm; + return this; + } + + public Builder withLocation(@Required Address address) throws IllegalArgumentException + { + checkThat(address) + .usingMessage("location cannot be null") + .is(notNull()); + + String text = address.address1; + + if (address.hasAddress2()) + { + text += " " + address.address2; + } + + if (address.hasAddress3()) + { + text += " " + address.address3; + } + + text += " " + address.city; + text += " " + address.state; + text += " " + address.country; + + if (address.hasZipCode()) + { + text += " " + address.zipCode; + } + + this.location = text; + return this; + } + + public Builder withCoordinate(@Required Coordinate coordinate) throws IllegalArgumentException + { + checkThat(coordinate) + .usingMessage("coordinate cannot be null") + .is(notNull()); + + double lat = coordinate.latitude; + double lon = coordinate.longitude; + + checkThat(lat).is(validLatitude()); + checkThat(lon).is(validLongitude()); + + this.latitude = lat; + this.longitude = lon; + return this; + } + + public Builder withRadiusInMeters(@Positive int radius) throws IllegalArgumentException + { + checkThat(radius) + .is(positiveInteger()); + + this.radiusInMeters = radius; + return this; + } + + public Builder withCategories(@Required Category first, Category... rest) throws IllegalArgumentException + { + checkThat(first) + .usingMessage("category is null") + .is(notNull()); + + List categoriesList = Lists.createFrom(first, rest); + return withCategories(categoriesList); + } + + public Builder withCategories(@NonEmpty List categories) throws IllegalArgumentException + { + checkThat(categories) + .is(notNull()) + .is(nonEmptyList()); + + String joined = categories.stream() + .map(c -> c.alias) + .collect(Collectors.joining(",")); + + this.categories = joined; + return this; + } + + public Builder withLocale(@NonEmpty String locale) throws IllegalArgumentException + { + checkThat(locale) + .usingMessage("locale cannot be empty") + .is(nonEmptyString()); + + this.locale = locale; + return this; + } + + public Builder withLimit(@Positive int limit) throws IllegalArgumentException + { + checkThat(limit) + .usingMessage("limit must be > 0") + .is(positiveInteger()) + .usingMessage("limit cannot exceed 50") + .is(lessThanOrEqualTo(MAX_LIMIT)); + + this.limit = limit; + return this; + } + + public Builder withOffset(@Positive int offset) throws IllegalArgumentException + { + checkThat(offset) + .usingMessage("offset must be > 0") + .is(positiveInteger()); + + this.offset = offset; + return this; + } + + public YelpSearchQuery build() throws YelpBadArgumentException + { + checkThatLocationIsSet(); + + return new YelpSearchQuery(searchTerm, + location, + latitude, + longitude, + radiusInMeters, + categories, + locale, + limit, + offset, + sortBy, + prices, + isOpenNow, + openAt, + attributes); + } + + private void checkThatLocationIsSet() + { + if (location != null) + { + return; + } + + if (longitude != null && latitude != null) + { + return; + } + + throw new YelpBadArgumentException("Either location "); + } } } From 472a385d414155772126f0a4ee46974b065801b6 Mon Sep 17 00:00:00 2001 From: Wellington Moreno Date: Wed, 30 Nov 2016 20:38:24 -0800 Subject: [PATCH 038/168] Renames class to YelpSearchRequest --- .../tech/redroma/yelp/YelpSearchRequest.java | 532 ++++++++++++++++++ 1 file changed, 532 insertions(+) create mode 100644 src/main/java/tech/redroma/yelp/YelpSearchRequest.java diff --git a/src/main/java/tech/redroma/yelp/YelpSearchRequest.java b/src/main/java/tech/redroma/yelp/YelpSearchRequest.java new file mode 100644 index 0000000..ba5c107 --- /dev/null +++ b/src/main/java/tech/redroma/yelp/YelpSearchRequest.java @@ -0,0 +1,532 @@ +/* + * Copyright 2016 RedRoma, Inc.. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package tech.redroma.yelp; + +import java.util.List; +import java.util.Objects; +import java.util.stream.Collectors; +import sir.wellington.alchemy.collections.lists.Lists; +import tech.redroma.yelp.exceptions.YelpAreaTooLargeException; +import tech.redroma.yelp.exceptions.YelpBadArgumentException; +import tech.sirwellington.alchemy.annotations.arguments.NonEmpty; +import tech.sirwellington.alchemy.annotations.arguments.Optional; +import tech.sirwellington.alchemy.annotations.arguments.Positive; +import tech.sirwellington.alchemy.annotations.arguments.Required; +import tech.sirwellington.alchemy.annotations.concurrency.Immutable; +import tech.sirwellington.alchemy.annotations.concurrency.ThreadSafe; +import tech.sirwellington.alchemy.annotations.designs.patterns.BuilderPattern; +import tech.sirwellington.alchemy.annotations.objects.Pojo; + +import static tech.sirwellington.alchemy.annotations.designs.patterns.BuilderPattern.Role.BUILDER; +import static tech.sirwellington.alchemy.annotations.designs.patterns.BuilderPattern.Role.PRODUCT; +import static tech.sirwellington.alchemy.arguments.Arguments.checkThat; +import static tech.sirwellington.alchemy.arguments.assertions.Assertions.notNull; +import static tech.sirwellington.alchemy.arguments.assertions.CollectionAssertions.nonEmptyList; +import static tech.sirwellington.alchemy.arguments.assertions.GeolocationAssertions.validLatitude; +import static tech.sirwellington.alchemy.arguments.assertions.GeolocationAssertions.validLongitude; +import static tech.sirwellington.alchemy.arguments.assertions.NumberAssertions.lessThanOrEqualTo; +import static tech.sirwellington.alchemy.arguments.assertions.NumberAssertions.positiveInteger; +import static tech.sirwellington.alchemy.arguments.assertions.StringAssertions.nonEmptyString; +import static tech.sirwellington.alchemy.arguments.Arguments.checkThat; +import static tech.sirwellington.alchemy.arguments.assertions.NumberAssertions.lessThanOrEqualTo; + +/** + * + * @author SirWellington + */ +@Pojo +@Immutable +@ThreadSafe +@BuilderPattern(role = PRODUCT) +public final class YelpSearchRequest +{ + + /* + * Optional search term (e.g. "food", "restaurants"). If the term isn't included we search everything. The term keyword also + * accepts business names such as "Starbucks". + */ + @Optional + private final String searchTerm; + + /** + * Required if either latitude or longitude is not provided. Specifies the combination of: + *

+     * + Address
+     * + Neighborhood
+     * + City
+     * + State
+     * + Zip
+     * + Country
+     * 
+ */ + private final String location; + + /** + * The latitude of the location you want to search near by. Required if location is not provided. + */ + private final Double latitude; + + /** + * Longitude of the location you want to search near by. required if the location is not provided. + */ + private final Double longitude; + + /** + * Search radius, in meters. If the value is too large, a {@link YelpAreaTooLargeException} is thrown. + */ + @Optional + private final Integer radius; + + /** + * categories to filter the serach results with. See the list of supported categories. The category filter can be a ist of + * comma-delimited categories. For example, "bars, french", will filter Bars and French. The category identifier should be + * used (e.g. "discgolf", instead of "Disc Golf"). + */ + @Optional + private final String categories; + + /** + * Specify the locale to return the business information in. + * + * @see + * https://www.yelp.com/developers/documentation/v3/supported_locales + */ + @Optional + private final String locale; + + /** + * Specify the maximum number of businesses to return. By default, it will return 20. The maximum is 50. + */ + @Optional + private final Integer limit; + + /** + * Offset the list of returned businesses by the amount. For, example, if you have seen results 1-10, specify '10' to see the + * next 11-20. + */ + @Optional + private final Integer offset; + + /** + * Sort the results by one of these modes: {@link SortType}. + * + */ + @Optional + private final String sortBy; + + /** + * Pricing levels to filter the search result with. + */ + @Optional + private final String prices; + + /** + * Defaults to false When set tot rue, only return the businesses open now. Notice that {@link #openNow} and {@link #openAt} + * cannot be used together. + */ + @Optional + private final Boolean openNow; + + /** + * An integer representing the Unix timestamp in the same time-zone of the search location. + * + * If specified, it will return businesses that are open at the given time. Note that {@link #openNow} and {@link #openAt} + * cannot be used together. + */ + @Optional + private final Integer openAt; + + /** + * Additional filters to search businesses. you can use multiple attribute filters at the same time by providing a + * comma-separated string. For example: {@code "attribute1,attribute2"}. + *

+ * Currently the valid values are: {@code "hot_and_new" & "deals"}. + */ + @Optional + private final String attributes; + + YelpSearchRequest(String searchTerm, + String location, + Double latitude, + Double longitude, + Integer radius, + String categories, + String locale, + Integer limit, + Integer offset, + String sortBy, + String price, + Boolean openNow, + Integer openAt, + String attributes) + { + this.searchTerm = searchTerm; + this.location = location; + this.latitude = latitude; + this.longitude = longitude; + this.radius = radius; + this.categories = categories; + this.locale = locale; + this.limit = limit; + this.offset = offset; + this.sortBy = sortBy; + this.prices = price; + this.openNow = openNow; + this.openAt = openAt; + this.attributes = attributes; + } + + @Override + public int hashCode() + { + int hash = 5; + hash = 17 * hash + Objects.hashCode(this.searchTerm); + hash = 17 * hash + Objects.hashCode(this.location); + hash = 17 * hash + Objects.hashCode(this.latitude); + hash = 17 * hash + Objects.hashCode(this.longitude); + hash = 17 * hash + Objects.hashCode(this.radius); + hash = 17 * hash + Objects.hashCode(this.categories); + hash = 17 * hash + Objects.hashCode(this.locale); + hash = 17 * hash + Objects.hashCode(this.limit); + hash = 17 * hash + Objects.hashCode(this.offset); + hash = 17 * hash + Objects.hashCode(this.sortBy); + hash = 17 * hash + Objects.hashCode(this.prices); + hash = 17 * hash + Objects.hashCode(this.openNow); + hash = 17 * hash + Objects.hashCode(this.openAt); + hash = 17 * hash + Objects.hashCode(this.attributes); + return hash; + } + + @Override + public boolean equals(Object obj) + { + if (this == obj) + { + return true; + } + if (obj == null) + { + return false; + } + if (getClass() != obj.getClass()) + { + return false; + } + final YelpSearchRequest other = (YelpSearchRequest) obj; + if (!Objects.equals(this.searchTerm, other.searchTerm)) + { + return false; + } + if (!Objects.equals(this.location, other.location)) + { + return false; + } + if (!Objects.equals(this.categories, other.categories)) + { + return false; + } + if (!Objects.equals(this.locale, other.locale)) + { + return false; + } + if (!Objects.equals(this.sortBy, other.sortBy)) + { + return false; + } + if (!Objects.equals(this.prices, other.prices)) + { + return false; + } + if (!Objects.equals(this.attributes, other.attributes)) + { + return false; + } + if (!Objects.equals(this.latitude, other.latitude)) + { + return false; + } + if (!Objects.equals(this.longitude, other.longitude)) + { + return false; + } + if (!Objects.equals(this.radius, other.radius)) + { + return false; + } + if (!Objects.equals(this.limit, other.limit)) + { + return false; + } + if (!Objects.equals(this.offset, other.offset)) + { + return false; + } + if (!Objects.equals(this.openNow, other.openNow)) + { + return false; + } + if (!Objects.equals(this.openAt, other.openAt)) + { + return false; + } + return true; + } + + @Override + public String toString() + { + return "YelpSearchQuery{" + "searchTerm=" + searchTerm + ", location=" + location + ", latitude=" + latitude + ", longitude=" + longitude + ", radius=" + radius + ", categories=" + categories + ", locale=" + locale + ", limit=" + limit + ", offset=" + offset + ", sortBy=" + sortBy + ", price=" + prices + ", openNow=" + openNow + ", openAt=" + openAt + '}'; + } + + public enum SortType + { + BEST_MATCH("best_match"), + RATING("rating"), + REVIEW_COUNT("review_count"), + DISTANCE("distance"); + + public final String text; + + private SortType(String text) + { + this.text = text; + } + } + + public enum PricingLevel + { + $(1), + $$(2), + $$$(3), + $$$$(4); + + final int number; + + private PricingLevel(int number) + { + this.number = number; + } + + } + + @BuilderPattern(role = BUILDER) + public final static class Builder + { + + private final static int MAX_LIMIT = 50; + + private String searchTerm; + private String location; + private Double latitude; + private Double longitude; + private Integer radiusInMeters; + private String categories; + private String locale; + private Integer limit; + private Integer offset; + private String sortBy; + private String prices; + private Boolean isOpenNow; + private Integer openAt; + private String attributes; + + public static Builder newInstance() + { + return new Builder(); + } + + public static Builder from(@Required YelpSearchRequest query) throws IllegalArgumentException + { + checkThat(query).is(notNull()); + + Builder builder = Builder.newInstance(); + builder.searchTerm = query.searchTerm; + builder.locale = query.locale; + builder.latitude = query.latitude; + builder.longitude = query.longitude; + builder.radiusInMeters = query.radius; + builder.categories = query.categories; + builder.location = query.location; + builder.limit = query.limit; + builder.offset = query.offset; + builder.sortBy = query.sortBy; + builder.prices = query.prices; + builder.isOpenNow = query.openNow; + builder.openAt = query.openAt; + builder.attributes = query.attributes; + + return builder; + } + + public Builder withSearchTerm(@NonEmpty String searchTerm) throws IllegalArgumentException + { + checkThat(searchTerm).is(nonEmptyString()); + + this.searchTerm = searchTerm; + return this; + } + + public Builder withLocation(@Required Address address) throws IllegalArgumentException + { + checkThat(address) + .usingMessage("location cannot be null") + .is(notNull()); + + String text = address.address1; + + if (address.hasAddress2()) + { + text += " " + address.address2; + } + + if (address.hasAddress3()) + { + text += " " + address.address3; + } + + text += " " + address.city; + text += " " + address.state; + text += " " + address.country; + + if (address.hasZipCode()) + { + text += " " + address.zipCode; + } + + this.location = text; + return this; + } + + public Builder withCoordinate(@Required Coordinate coordinate) throws IllegalArgumentException + { + checkThat(coordinate) + .usingMessage("coordinate cannot be null") + .is(notNull()); + + double lat = coordinate.latitude; + double lon = coordinate.longitude; + + checkThat(lat).is(validLatitude()); + checkThat(lon).is(validLongitude()); + + this.latitude = lat; + this.longitude = lon; + return this; + } + + public Builder withRadiusInMeters(@Positive int radius) throws IllegalArgumentException + { + checkThat(radius) + .is(positiveInteger()); + + this.radiusInMeters = radius; + return this; + } + + public Builder withCategories(@Required Category first, Category... rest) throws IllegalArgumentException + { + checkThat(first) + .usingMessage("category is null") + .is(notNull()); + + List categoriesList = Lists.createFrom(first, rest); + return withCategories(categoriesList); + } + + public Builder withCategories(@NonEmpty List categories) throws IllegalArgumentException + { + checkThat(categories) + .is(notNull()) + .is(nonEmptyList()); + + String joined = categories.stream() + .map(c -> c.alias) + .collect(Collectors.joining(",")); + + this.categories = joined; + return this; + } + + public Builder withLocale(@NonEmpty String locale) throws IllegalArgumentException + { + checkThat(locale) + .usingMessage("locale cannot be empty") + .is(nonEmptyString()); + + this.locale = locale; + return this; + } + + public Builder withLimit(@Positive int limit) throws IllegalArgumentException + { + checkThat(limit) + .usingMessage("limit must be > 0") + .is(positiveInteger()) + .usingMessage("limit cannot exceed 50") + .is(lessThanOrEqualTo(MAX_LIMIT)); + + this.limit = limit; + return this; + } + + public Builder withOffset(@Positive int offset) throws IllegalArgumentException + { + checkThat(offset) + .usingMessage("offset must be > 0") + .is(positiveInteger()); + + this.offset = offset; + return this; + } + + public YelpSearchRequest build() throws YelpBadArgumentException + { + checkThatLocationIsSet(); + + return new YelpSearchRequest(searchTerm, + location, + latitude, + longitude, + radiusInMeters, + categories, + locale, + limit, + offset, + sortBy, + prices, + isOpenNow, + openAt, + attributes); + } + + private void checkThatLocationIsSet() + { + if (location != null) + { + return; + } + + if (longitude != null && latitude != null) + { + return; + } + + throw new YelpBadArgumentException("Either location "); + } + } + +} From f8a5d15b4f6b2ef4f0933f4ad68776e76b12ac01 Mon Sep 17 00:00:00 2001 From: Wellington Moreno Date: Wed, 30 Nov 2016 20:38:31 -0800 Subject: [PATCH 039/168] Renames class to YelpSearchRequest --- .../tech/redroma/yelp/YelpSearchQuery.java | 530 ------------------ 1 file changed, 530 deletions(-) delete mode 100644 src/main/java/tech/redroma/yelp/YelpSearchQuery.java diff --git a/src/main/java/tech/redroma/yelp/YelpSearchQuery.java b/src/main/java/tech/redroma/yelp/YelpSearchQuery.java deleted file mode 100644 index a01d92b..0000000 --- a/src/main/java/tech/redroma/yelp/YelpSearchQuery.java +++ /dev/null @@ -1,530 +0,0 @@ -/* - * Copyright 2016 RedRoma, Inc.. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package tech.redroma.yelp; - -import java.util.List; -import java.util.Objects; -import java.util.stream.Collectors; -import sir.wellington.alchemy.collections.lists.Lists; -import tech.redroma.yelp.exceptions.YelpAreaTooLargeException; -import tech.redroma.yelp.exceptions.YelpBadArgumentException; -import tech.sirwellington.alchemy.annotations.arguments.NonEmpty; -import tech.sirwellington.alchemy.annotations.arguments.Optional; -import tech.sirwellington.alchemy.annotations.arguments.Positive; -import tech.sirwellington.alchemy.annotations.arguments.Required; -import tech.sirwellington.alchemy.annotations.concurrency.Immutable; -import tech.sirwellington.alchemy.annotations.concurrency.ThreadSafe; -import tech.sirwellington.alchemy.annotations.designs.patterns.BuilderPattern; -import tech.sirwellington.alchemy.annotations.objects.Pojo; - -import static tech.sirwellington.alchemy.annotations.designs.patterns.BuilderPattern.Role.BUILDER; -import static tech.sirwellington.alchemy.annotations.designs.patterns.BuilderPattern.Role.PRODUCT; -import static tech.sirwellington.alchemy.arguments.Arguments.checkThat; -import static tech.sirwellington.alchemy.arguments.assertions.Assertions.notNull; -import static tech.sirwellington.alchemy.arguments.assertions.CollectionAssertions.nonEmptyList; -import static tech.sirwellington.alchemy.arguments.assertions.GeolocationAssertions.validLatitude; -import static tech.sirwellington.alchemy.arguments.assertions.GeolocationAssertions.validLongitude; -import static tech.sirwellington.alchemy.arguments.assertions.NumberAssertions.lessThanOrEqualTo; -import static tech.sirwellington.alchemy.arguments.assertions.NumberAssertions.positiveInteger; -import static tech.sirwellington.alchemy.arguments.assertions.StringAssertions.nonEmptyString; - -/** - * - * @author SirWellington - */ -@Pojo -@Immutable -@ThreadSafe -@BuilderPattern(role = PRODUCT) -public final class YelpSearchQuery -{ - - /* - * Optional search term (e.g. "food", "restaurants"). If the term isn't included we search everything. The term keyword also - * accepts business names such as "Starbucks". - */ - @Optional - private final String searchTerm; - - /** - * Required if either latitude or longitude is not provided. Specifies the combination of: - *

-     * + Address
-     * + Neighborhood
-     * + City
-     * + State
-     * + Zip
-     * + Country
-     * 
- */ - private final String location; - - /** - * The latitude of the location you want to search near by. Required if location is not provided. - */ - private final Double latitude; - - /** - * Longitude of the location you want to search near by. required if the location is not provided. - */ - private final Double longitude; - - /** - * Search radius, in meters. If the value is too large, a {@link YelpAreaTooLargeException} is thrown. - */ - @Optional - private final Integer radius; - - /** - * categories to filter the serach results with. See the list of supported categories. The category filter can be a ist of - * comma-delimited categories. For example, "bars, french", will filter Bars and French. The category identifier should be - * used (e.g. "discgolf", instead of "Disc Golf"). - */ - @Optional - private final String categories; - - /** - * Specify the locale to return the business information in. - * - * @see - * https://www.yelp.com/developers/documentation/v3/supported_locales - */ - @Optional - private final String locale; - - /** - * Specify the maximum number of businesses to return. By default, it will return 20. The maximum is 50. - */ - @Optional - private final Integer limit; - - /** - * Offset the list of returned businesses by the amount. For, example, if you have seen results 1-10, specify '10' to see the - * next 11-20. - */ - @Optional - private final Integer offset; - - /** - * Sort the results by one of these modes: {@link SortType}. - * - */ - @Optional - private final String sortBy; - - /** - * Pricing levels to filter the search result with. - */ - @Optional - private final String prices; - - /** - * Defaults to false When set tot rue, only return the businesses open now. Notice that {@link #openNow} and {@link #openAt} - * cannot be used together. - */ - @Optional - private final Boolean openNow; - - /** - * An integer representing the Unix timestamp in the same time-zone of the search location. - * - * If specified, it will return businesses that are open at the given time. Note that {@link #openNow} and {@link #openAt} - * cannot be used together. - */ - @Optional - private final Integer openAt; - - /** - * Additional filters to search businesses. you can use multiple attribute filters at the same time by providing a - * comma-separated string. For example: {@code "attribute1,attribute2"}. - *

- * Currently the valid values are: {@code "hot_and_new" & "deals"}. - */ - @Optional - private final String attributes; - - YelpSearchQuery(String searchTerm, - String location, - Double latitude, - Double longitude, - Integer radius, - String categories, - String locale, - Integer limit, - Integer offset, - String sortBy, - String price, - Boolean openNow, - Integer openAt, - String attributes) - { - this.searchTerm = searchTerm; - this.location = location; - this.latitude = latitude; - this.longitude = longitude; - this.radius = radius; - this.categories = categories; - this.locale = locale; - this.limit = limit; - this.offset = offset; - this.sortBy = sortBy; - this.prices = price; - this.openNow = openNow; - this.openAt = openAt; - this.attributes = attributes; - } - - @Override - public int hashCode() - { - int hash = 5; - hash = 17 * hash + Objects.hashCode(this.searchTerm); - hash = 17 * hash + Objects.hashCode(this.location); - hash = 17 * hash + Objects.hashCode(this.latitude); - hash = 17 * hash + Objects.hashCode(this.longitude); - hash = 17 * hash + Objects.hashCode(this.radius); - hash = 17 * hash + Objects.hashCode(this.categories); - hash = 17 * hash + Objects.hashCode(this.locale); - hash = 17 * hash + Objects.hashCode(this.limit); - hash = 17 * hash + Objects.hashCode(this.offset); - hash = 17 * hash + Objects.hashCode(this.sortBy); - hash = 17 * hash + Objects.hashCode(this.prices); - hash = 17 * hash + Objects.hashCode(this.openNow); - hash = 17 * hash + Objects.hashCode(this.openAt); - hash = 17 * hash + Objects.hashCode(this.attributes); - return hash; - } - - @Override - public boolean equals(Object obj) - { - if (this == obj) - { - return true; - } - if (obj == null) - { - return false; - } - if (getClass() != obj.getClass()) - { - return false; - } - final YelpSearchQuery other = (YelpSearchQuery) obj; - if (!Objects.equals(this.searchTerm, other.searchTerm)) - { - return false; - } - if (!Objects.equals(this.location, other.location)) - { - return false; - } - if (!Objects.equals(this.categories, other.categories)) - { - return false; - } - if (!Objects.equals(this.locale, other.locale)) - { - return false; - } - if (!Objects.equals(this.sortBy, other.sortBy)) - { - return false; - } - if (!Objects.equals(this.prices, other.prices)) - { - return false; - } - if (!Objects.equals(this.attributes, other.attributes)) - { - return false; - } - if (!Objects.equals(this.latitude, other.latitude)) - { - return false; - } - if (!Objects.equals(this.longitude, other.longitude)) - { - return false; - } - if (!Objects.equals(this.radius, other.radius)) - { - return false; - } - if (!Objects.equals(this.limit, other.limit)) - { - return false; - } - if (!Objects.equals(this.offset, other.offset)) - { - return false; - } - if (!Objects.equals(this.openNow, other.openNow)) - { - return false; - } - if (!Objects.equals(this.openAt, other.openAt)) - { - return false; - } - return true; - } - - @Override - public String toString() - { - return "YelpSearchQuery{" + "searchTerm=" + searchTerm + ", location=" + location + ", latitude=" + latitude + ", longitude=" + longitude + ", radius=" + radius + ", categories=" + categories + ", locale=" + locale + ", limit=" + limit + ", offset=" + offset + ", sortBy=" + sortBy + ", price=" + prices + ", openNow=" + openNow + ", openAt=" + openAt + '}'; - } - - public enum SortType - { - BEST_MATCH("best_match"), - RATING("rating"), - REVIEW_COUNT("review_count"), - DISTANCE("distance"); - - public final String text; - - private SortType(String text) - { - this.text = text; - } - } - - public enum PricingLevel - { - $(1), - $$(2), - $$$(3), - $$$$(4); - - final int number; - - private PricingLevel(int number) - { - this.number = number; - } - - } - - @BuilderPattern(role = BUILDER) - public final static class Builder - { - - private final static int MAX_LIMIT = 50; - - private String searchTerm; - private String location; - private Double latitude; - private Double longitude; - private Integer radiusInMeters; - private String categories; - private String locale; - private Integer limit; - private Integer offset; - private String sortBy; - private String prices; - private Boolean isOpenNow; - private Integer openAt; - private String attributes; - - public static Builder newInstance() - { - return new Builder(); - } - - public static Builder from(@Required YelpSearchQuery query) throws IllegalArgumentException - { - checkThat(query).is(notNull()); - - Builder builder = Builder.newInstance(); - builder.searchTerm = query.searchTerm; - builder.locale = query.locale; - builder.latitude = query.latitude; - builder.longitude = query.longitude; - builder.radiusInMeters = query.radius; - builder.categories = query.categories; - builder.location = query.location; - builder.limit = query.limit; - builder.offset = query.offset; - builder.sortBy = query.sortBy; - builder.prices = query.prices; - builder.isOpenNow = query.openNow; - builder.openAt = query.openAt; - builder.attributes = query.attributes; - - return builder; - } - - public Builder withSearchTerm(@NonEmpty String searchTerm) throws IllegalArgumentException - { - checkThat(searchTerm).is(nonEmptyString()); - - this.searchTerm = searchTerm; - return this; - } - - public Builder withLocation(@Required Address address) throws IllegalArgumentException - { - checkThat(address) - .usingMessage("location cannot be null") - .is(notNull()); - - String text = address.address1; - - if (address.hasAddress2()) - { - text += " " + address.address2; - } - - if (address.hasAddress3()) - { - text += " " + address.address3; - } - - text += " " + address.city; - text += " " + address.state; - text += " " + address.country; - - if (address.hasZipCode()) - { - text += " " + address.zipCode; - } - - this.location = text; - return this; - } - - public Builder withCoordinate(@Required Coordinate coordinate) throws IllegalArgumentException - { - checkThat(coordinate) - .usingMessage("coordinate cannot be null") - .is(notNull()); - - double lat = coordinate.latitude; - double lon = coordinate.longitude; - - checkThat(lat).is(validLatitude()); - checkThat(lon).is(validLongitude()); - - this.latitude = lat; - this.longitude = lon; - return this; - } - - public Builder withRadiusInMeters(@Positive int radius) throws IllegalArgumentException - { - checkThat(radius) - .is(positiveInteger()); - - this.radiusInMeters = radius; - return this; - } - - public Builder withCategories(@Required Category first, Category... rest) throws IllegalArgumentException - { - checkThat(first) - .usingMessage("category is null") - .is(notNull()); - - List categoriesList = Lists.createFrom(first, rest); - return withCategories(categoriesList); - } - - public Builder withCategories(@NonEmpty List categories) throws IllegalArgumentException - { - checkThat(categories) - .is(notNull()) - .is(nonEmptyList()); - - String joined = categories.stream() - .map(c -> c.alias) - .collect(Collectors.joining(",")); - - this.categories = joined; - return this; - } - - public Builder withLocale(@NonEmpty String locale) throws IllegalArgumentException - { - checkThat(locale) - .usingMessage("locale cannot be empty") - .is(nonEmptyString()); - - this.locale = locale; - return this; - } - - public Builder withLimit(@Positive int limit) throws IllegalArgumentException - { - checkThat(limit) - .usingMessage("limit must be > 0") - .is(positiveInteger()) - .usingMessage("limit cannot exceed 50") - .is(lessThanOrEqualTo(MAX_LIMIT)); - - this.limit = limit; - return this; - } - - public Builder withOffset(@Positive int offset) throws IllegalArgumentException - { - checkThat(offset) - .usingMessage("offset must be > 0") - .is(positiveInteger()); - - this.offset = offset; - return this; - } - - public YelpSearchQuery build() throws YelpBadArgumentException - { - checkThatLocationIsSet(); - - return new YelpSearchQuery(searchTerm, - location, - latitude, - longitude, - radiusInMeters, - categories, - locale, - limit, - offset, - sortBy, - prices, - isOpenNow, - openAt, - attributes); - } - - private void checkThatLocationIsSet() - { - if (location != null) - { - return; - } - - if (longitude != null && latitude != null) - { - return; - } - - throw new YelpBadArgumentException("Either location "); - } - } - -} From 294df4e76cd5a2e94cc4b50e08af63fd73ea84c9 Mon Sep 17 00:00:00 2001 From: Wellington Moreno Date: Wed, 30 Nov 2016 21:16:00 -0800 Subject: [PATCH 040/168] Completes the YelpSearchRequest.Builder class --- .../tech/redroma/yelp/YelpSearchRequest.java | 88 ++++++++++++++++++- 1 file changed, 86 insertions(+), 2 deletions(-) diff --git a/src/main/java/tech/redroma/yelp/YelpSearchRequest.java b/src/main/java/tech/redroma/yelp/YelpSearchRequest.java index ba5c107..c7d0469 100644 --- a/src/main/java/tech/redroma/yelp/YelpSearchRequest.java +++ b/src/main/java/tech/redroma/yelp/YelpSearchRequest.java @@ -31,6 +31,7 @@ import tech.sirwellington.alchemy.annotations.designs.patterns.BuilderPattern; import tech.sirwellington.alchemy.annotations.objects.Pojo; +import static java.util.stream.Collectors.joining; import static tech.sirwellington.alchemy.annotations.designs.patterns.BuilderPattern.Role.BUILDER; import static tech.sirwellington.alchemy.annotations.designs.patterns.BuilderPattern.Role.PRODUCT; import static tech.sirwellington.alchemy.arguments.Arguments.checkThat; @@ -38,11 +39,10 @@ import static tech.sirwellington.alchemy.arguments.assertions.CollectionAssertions.nonEmptyList; import static tech.sirwellington.alchemy.arguments.assertions.GeolocationAssertions.validLatitude; import static tech.sirwellington.alchemy.arguments.assertions.GeolocationAssertions.validLongitude; +import static tech.sirwellington.alchemy.arguments.assertions.NumberAssertions.greaterThanOrEqualTo; import static tech.sirwellington.alchemy.arguments.assertions.NumberAssertions.lessThanOrEqualTo; import static tech.sirwellington.alchemy.arguments.assertions.NumberAssertions.positiveInteger; import static tech.sirwellington.alchemy.arguments.assertions.StringAssertions.nonEmptyString; -import static tech.sirwellington.alchemy.arguments.Arguments.checkThat; -import static tech.sirwellington.alchemy.arguments.assertions.NumberAssertions.lessThanOrEqualTo; /** * @@ -320,7 +320,21 @@ private PricingLevel(int number) { this.number = number; } + } + + public enum Attribute + { + HOT_AND_NEW("hot_and_new"), + DEALS("deals") + ; + + private final String text; + private Attribute(String text) + { + this.text = text; + } + } @BuilderPattern(role = BUILDER) @@ -455,6 +469,7 @@ public Builder withCategories(@NonEmpty List categories) throws Illega String joined = categories.stream() .map(c -> c.alias) + .distinct() .collect(Collectors.joining(",")); this.categories = joined; @@ -492,6 +507,75 @@ public Builder withOffset(@Positive int offset) throws IllegalArgumentException this.offset = offset; return this; } + + public Builder withSortBy(@Required SortType sortType) throws IllegalArgumentException + { + checkThat(sortType).is(notNull()); + + this.sortBy = sortType.text; + return this; + } + + public Builder withPrices(@Required PricingLevel first, PricingLevel...others) throws IllegalArgumentException + { + checkThat(first).is(notNull()); + + List pricingLevels = Lists.createFrom(first, others); + return withPrices(pricingLevels); + } + + public Builder withPrices(@NonEmpty List pricingLevels) throws IllegalArgumentException + { + checkThat(pricingLevels) + .is(notNull()) + .is(nonEmptyList()); + + String priceLevels = pricingLevels.stream() + .map(p -> p.number) + .map(String::valueOf) + .distinct() + .collect(Collectors.joining(", ")); + + this.prices = priceLevels; + return this; + } + + public Builder lookingForOpenNow() + { + this.isOpenNow = true; + return this; + } + + public Builder withBusinessesOpenAt(int hour) throws IllegalArgumentException + { + checkThat(hour) + .is(greaterThanOrEqualTo(0)) + .is(lessThanOrEqualTo(24)); + + this.openAt = hour; + return this; + } + + public Builder withAttribute(@Required Attribute attribute) throws IllegalArgumentException + { + checkThat(attribute).is(notNull()); + + this.attributes = attribute.text; + return this; + } + + public Builder withAttributes(@NonEmpty List attributes) + { + checkThat(attributes).is(nonEmptyList()); + + this.attributes = attributes.stream() + .map(a -> a.text) + .distinct() + .collect(joining(",")); + + return this; + } + public YelpSearchRequest build() throws YelpBadArgumentException { From f23b22fc46409a1b7b29109d511cc887383101fe Mon Sep 17 00:00:00 2001 From: Wellington Moreno Date: Wed, 30 Nov 2016 23:44:24 -0800 Subject: [PATCH 041/168] Creates the main interface for interacting with Yelp's API --- src/main/java/tech/redroma/yelp/YelpAPI.java | 78 ++++++++++++++++++++ 1 file changed, 78 insertions(+) create mode 100644 src/main/java/tech/redroma/yelp/YelpAPI.java diff --git a/src/main/java/tech/redroma/yelp/YelpAPI.java b/src/main/java/tech/redroma/yelp/YelpAPI.java new file mode 100644 index 0000000..a735288 --- /dev/null +++ b/src/main/java/tech/redroma/yelp/YelpAPI.java @@ -0,0 +1,78 @@ +/* + * Copyright 2016 RedRoma, Inc.. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + + +package tech.redroma.yelp; + +import java.util.List; +import tech.redroma.yelp.exceptions.YelpBadArgumentException; +import tech.redroma.yelp.exceptions.YelpExcetion; +import tech.sirwellington.alchemy.annotations.arguments.NonEmpty; +import tech.sirwellington.alchemy.annotations.arguments.Required; + +import static tech.sirwellington.alchemy.arguments.Arguments.checkThat; +import static tech.sirwellington.alchemy.arguments.assertions.Assertions.notNull; +import static tech.sirwellington.alchemy.arguments.assertions.StringAssertions.nonEmptyString; + +/** + * The interface used to interact with Yelp's Developer API. + * + * @author SirWellington + * @see Yelp API + */ +public interface YelpAPI +{ + + default YelpBusinessDetails getBusinessDetails(@Required YelpBusiness business) throws YelpExcetion + { + checkThat(business) + .throwing(YelpBadArgumentException.class) + .usingMessage("business cannot be null") + .is(notNull()); + + checkThat(business.id) + .usingMessage("business is missing it's id") + .is(nonEmptyString()); + + return getBusinessDetails(business.id); + } + + @Required + YelpBusinessDetails getBusinessDetails(@NonEmpty String businessId) throws YelpExcetion; + + List searchForBusinesses(@Required YelpSearchRequest request) throws YelpExcetion; + + static YelpAPI newInstance() + { + return Builder.newInstance().build(); + } + + static class Builder + { + private String apiKey = ""; + + public static Builder newInstance() + { + return new Builder(); + } + + public YelpAPI build() + { + return null; + } + + } +} From 0f8f03fc1badfb829a21c13247ec0d8716ee1bef Mon Sep 17 00:00:00 2001 From: Wellington Moreno Date: Thu, 1 Dec 2016 00:18:26 -0800 Subject: [PATCH 042/168] Updates the SearchRequest object to move documentation to publicly accesible methods. --- .../tech/redroma/yelp/YelpSearchRequest.java | 377 ++++++++++++++---- 1 file changed, 293 insertions(+), 84 deletions(-) diff --git a/src/main/java/tech/redroma/yelp/YelpSearchRequest.java b/src/main/java/tech/redroma/yelp/YelpSearchRequest.java index c7d0469..f6abf13 100644 --- a/src/main/java/tech/redroma/yelp/YelpSearchRequest.java +++ b/src/main/java/tech/redroma/yelp/YelpSearchRequest.java @@ -55,24 +55,9 @@ public final class YelpSearchRequest { - /* - * Optional search term (e.g. "food", "restaurants"). If the term isn't included we search everything. The term keyword also - * accepts business names such as "Starbucks". - */ @Optional private final String searchTerm; - /** - * Required if either latitude or longitude is not provided. Specifies the combination of: - *

-     * + Address
-     * + Neighborhood
-     * + City
-     * + State
-     * + Zip
-     * + Country
-     * 
- */ private final String location; /** @@ -85,77 +70,35 @@ public final class YelpSearchRequest */ private final Double longitude; - /** - * Search radius, in meters. If the value is too large, a {@link YelpAreaTooLargeException} is thrown. - */ @Optional private final Integer radius; - /** - * categories to filter the serach results with. See the list of supported categories. The category filter can be a ist of - * comma-delimited categories. For example, "bars, french", will filter Bars and French. The category identifier should be - * used (e.g. "discgolf", instead of "Disc Golf"). - */ @Optional private final String categories; /** - * Specify the locale to return the business information in. * - * @see - * https://www.yelp.com/developers/documentation/v3/supported_locales */ @Optional private final String locale; - /** - * Specify the maximum number of businesses to return. By default, it will return 20. The maximum is 50. - */ @Optional private final Integer limit; - /** - * Offset the list of returned businesses by the amount. For, example, if you have seen results 1-10, specify '10' to see the - * next 11-20. - */ @Optional private final Integer offset; - /** - * Sort the results by one of these modes: {@link SortType}. - * - */ @Optional private final String sortBy; - /** - * Pricing levels to filter the search result with. - */ @Optional private final String prices; - /** - * Defaults to false When set tot rue, only return the businesses open now. Notice that {@link #openNow} and {@link #openAt} - * cannot be used together. - */ @Optional private final Boolean openNow; - - /** - * An integer representing the Unix timestamp in the same time-zone of the search location. - * - * If specified, it will return businesses that are open at the given time. Note that {@link #openNow} and {@link #openAt} - * cannot be used together. - */ @Optional private final Integer openAt; - /** - * Additional filters to search businesses. you can use multiple attribute filters at the same time by providing a - * comma-separated string. For example: {@code "attribute1,attribute2"}. - *

- * Currently the valid values are: {@code "hot_and_new" & "deals"}. - */ @Optional private final String attributes; @@ -190,6 +133,78 @@ public final class YelpSearchRequest this.attributes = attributes; } + public String getSearchTerm() + { + return searchTerm; + } + + public String getLocation() + { + return location; + } + + public Double getLatitude() + { + return latitude; + } + + public Double getLongitude() + { + return longitude; + } + + public Integer getRadius() + { + return radius; + } + + public String getCategories() + { + return categories; + } + + public String getLocale() + { + return locale; + } + + public Integer getLimit() + { + return limit; + } + + public Integer getOffset() + { + return offset; + } + + public String getSortBy() + { + return sortBy; + } + + public String getPrices() + { + return prices; + } + + public Boolean getOpenNow() + { + return openNow; + } + + public Integer getOpenAt() + { + return openAt; + } + + public String getAttributes() + { + return attributes; + } + + + @Override public int hashCode() { @@ -341,7 +356,16 @@ private Attribute(String text) public final static class Builder { - private final static int MAX_LIMIT = 50; + /** + * The maximum limit that can used per query. + */ + public final static int MAX_LIMIT = 50; + + /** + * The widest radius that can be used in a Search. + * Set to 40,000 meters, or 25 miles. + */ + public final static int MAX_RADIUS_IN_METERS = 40_000; private String searchTerm; private String location; @@ -358,34 +382,55 @@ public final static class Builder private Integer openAt; private String attributes; + /** + * Creates a new instance of the Builder. + * @return + */ public static Builder newInstance() { return new Builder(); } - public static Builder from(@Required YelpSearchRequest query) throws IllegalArgumentException + /** + * Creates a new instance of the Builder using the data in the existing {@link YelpSearchRequest}. + * + * @param request Initiates the builder with the data in this variable. It cannot be null. + * @return + * + * @throws IllegalArgumentException If the argument is null + */ + public static Builder from(@Required YelpSearchRequest request) throws IllegalArgumentException { - checkThat(query).is(notNull()); + checkThat(request).is(notNull()); Builder builder = Builder.newInstance(); - builder.searchTerm = query.searchTerm; - builder.locale = query.locale; - builder.latitude = query.latitude; - builder.longitude = query.longitude; - builder.radiusInMeters = query.radius; - builder.categories = query.categories; - builder.location = query.location; - builder.limit = query.limit; - builder.offset = query.offset; - builder.sortBy = query.sortBy; - builder.prices = query.prices; - builder.isOpenNow = query.openNow; - builder.openAt = query.openAt; - builder.attributes = query.attributes; + builder.searchTerm = request.searchTerm; + builder.locale = request.locale; + builder.latitude = request.latitude; + builder.longitude = request.longitude; + builder.radiusInMeters = request.radius; + builder.categories = request.categories; + builder.location = request.location; + builder.limit = request.limit; + builder.offset = request.offset; + builder.sortBy = request.sortBy; + builder.prices = request.prices; + builder.isOpenNow = request.openNow; + builder.openAt = request.openAt; + builder.attributes = request.attributes; return builder; } + /** + * Optional. Search term (e.g. "food", "restaurants"). If the term isn't included we search everything. The term keyword + * also accepts business names such as "Starbucks". + * + * @param searchTerm The search term to use in the request. + * @return + * @throws IllegalArgumentException * + */ + @Optional public Builder withSearchTerm(@NonEmpty String searchTerm) throws IllegalArgumentException { checkThat(searchTerm).is(nonEmptyString()); @@ -393,7 +438,24 @@ public Builder withSearchTerm(@NonEmpty String searchTerm) throws IllegalArgumen this.searchTerm = searchTerm; return this; } - + + /** + * Required if either latitude or longitude is not provided. + * Specifies the combination of: + *

+         * + Address
+         * + Neighborhood
+         * + City
+         * + State
+         * + Zip
+         * + Country
+         * 
+ * + * @param address + * @return + * @throws IllegalArgumentException + */ + @Required public Builder withLocation(@Required Address address) throws IllegalArgumentException { checkThat(address) @@ -425,6 +487,16 @@ public Builder withLocation(@Required Address address) throws IllegalArgumentExc return this; } + /** + * Specifies the latitude and longitude of the location you want to search near by. + * Required if {@linkplain #withLocation(tech.redroma.yelp.Address) Location} is not + * provided. + * + * @param coordinate Cannot be empty, and must contain valid longitude and latitude; + * @return + * @throws IllegalArgumentException + */ + @Required public Builder withCoordinate(@Required Coordinate coordinate) throws IllegalArgumentException { checkThat(coordinate) @@ -442,15 +514,38 @@ public Builder withCoordinate(@Required Coordinate coordinate) throws IllegalArg return this; } - public Builder withRadiusInMeters(@Positive int radius) throws IllegalArgumentException + /** + * Search radius, in meters. If the value is too large, a {@link YelpAreaTooLargeException} is thrown. + * + * @param radius + * @return + * @throws IllegalArgumentException + */ + @Optional + public Builder withRadiusInMeters(@Positive int radius) throws IllegalArgumentException, YelpAreaTooLargeException { checkThat(radius) - .is(positiveInteger()); + .is(positiveInteger()) + .throwing(YelpAreaTooLargeException.class) + .usingMessage("search radius cannot exceed: " + radius) + .is(lessThanOrEqualTo(MAX_RADIUS_IN_METERS)); this.radiusInMeters = radius; return this; } + /** + + * + * @param first The first category (required) + * @param rest A Varargs of the rest. + * @return + * @throws IllegalArgumentException If the first is null. + * + * @see Category + * @see #withCategories(java.util.List) + */ + @Optional public Builder withCategories(@Required Category first, Category... rest) throws IllegalArgumentException { checkThat(first) @@ -461,6 +556,20 @@ public Builder withCategories(@Required Category first, Category... rest) throws return withCategories(categoriesList); } + /** + * Categories to filter the search results with. + * See the {@linkplain Category list of supported categories}. + *

+ * The category filter can be a list of + * comma-delimited categories. For example, "bars, french", will filter Bars and French. The category identifier should be + * used (e.g. "discgolf", instead of "Disc Golf"). + * + * @param categories + * @return + * @throws IllegalArgumentException + * @see #withCategories(tech.redroma.yelp.Category, tech.redroma.yelp.Category...) + */ + @Optional public Builder withCategories(@NonEmpty List categories) throws IllegalArgumentException { checkThat(categories) @@ -476,6 +585,16 @@ public Builder withCategories(@NonEmpty List categories) throws Illega return this; } + /** + * Specify the locale to return the business information in. + * + * @param locale The locale (cannot be empty). + * @return + * @throws IllegalArgumentException + * @see + * https://www.yelp.com/developers/documentation/v3/supported_locales + */ + @Optional public Builder withLocale(@NonEmpty String locale) throws IllegalArgumentException { checkThat(locale) @@ -486,6 +605,15 @@ public Builder withLocale(@NonEmpty String locale) throws IllegalArgumentExcepti return this; } + /** + * Specify the maximum number of businesses to return. By default, it will return 20. + * Cannot exceed {@link #MAX_LIMIT}. + * + * @param limit Must be {@code > 0} and cannot exceed {@link #MAX_LIMIT}. + * @return + * @throws IllegalArgumentException + */ + @Optional public Builder withLimit(@Positive int limit) throws IllegalArgumentException { checkThat(limit) @@ -498,6 +626,17 @@ public Builder withLimit(@Positive int limit) throws IllegalArgumentException return this; } + /** + * Offset the list of returned business by this amount. + *

+ * For example, if you have seen results 1-10, setting this to '10' + * will return the next 11-20. + * + * @param offset Must be {@code > 0} + * @return + * @throws IllegalArgumentException + */ + @Optional public Builder withOffset(@Positive int offset) throws IllegalArgumentException { checkThat(offset) @@ -508,6 +647,14 @@ public Builder withOffset(@Positive int offset) throws IllegalArgumentException return this; } + /** + * Sort the results by one of these modes: {@link SortType}. + * + * @param sortType The type of sorting order desired. Cannot be null. + * @return + * @throws IllegalArgumentException + */ + @Optional public Builder withSortBy(@Required SortType sortType) throws IllegalArgumentException { checkThat(sortType).is(notNull()); @@ -516,6 +663,16 @@ public Builder withSortBy(@Required SortType sortType) throws IllegalArgumentExc return this; } + /** + * Pricing levels to filter the search result with. + * + * @param first The first pricing level, cannot be null. + * @param others Varargs allowing you to specify other pricing levels. + * @return + * @throws IllegalArgumentException + * @see #withPrices(java.util.List) + */ + @Optional public Builder withPrices(@Required PricingLevel first, PricingLevel...others) throws IllegalArgumentException { checkThat(first).is(notNull()); @@ -524,6 +681,15 @@ public Builder withPrices(@Required PricingLevel first, PricingLevel...others) t return withPrices(pricingLevels); } + /** + * Filters results to include businesses at the specified pricing points. + * + * @param pricingLevels The pricing levels to the filter the search results with (cannot be empty). + * @return + * @throws IllegalArgumentException + * @see #withPrices(tech.redroma.yelp.YelpSearchRequest.PricingLevel, tech.redroma.yelp.YelpSearchRequest.PricingLevel...) + */ + @Optional public Builder withPrices(@NonEmpty List pricingLevels) throws IllegalArgumentException { checkThat(pricingLevels) @@ -540,12 +706,30 @@ public Builder withPrices(@NonEmpty List pricingLevels) throws Ill return this; } + /** + * Returns results that only includes businesses that are open now. + *

+ * Notice that {@link #withBusinessesOpenAt(int) } and {@link #lookingForOpenNow() } cannot be used together. + * + * @return + */ + @Optional public Builder lookingForOpenNow() { this.isOpenNow = true; return this; } + /** + * Returns results taht only include businesses that are open at the specified time. + *

+ * Notice that {@link #withBusinessesOpenAt(int) } and {@link #lookingForOpenNow() } cannot be used together. + * + * @param hour The Unix timestamp in the same time-zone of the search location. + * @return + * @throws IllegalArgumentException + */ + @Optional public Builder withBusinessesOpenAt(int hour) throws IllegalArgumentException { checkThat(hour) @@ -556,15 +740,33 @@ public Builder withBusinessesOpenAt(int hour) throws IllegalArgumentException return this; } - public Builder withAttribute(@Required Attribute attribute) throws IllegalArgumentException + /** + * Convenience method for {@link #withAttributes(java.util.List) }. + * + * @param first The first Attribute is required. + * @param rest The rest are optional + * @return + * @throws IllegalArgumentException + */ + public Builder withAttribute(@Required Attribute first, Attribute...rest) throws IllegalArgumentException { - checkThat(attribute).is(notNull()); + checkThat(first).is(notNull()); - this.attributes = attribute.text; - return this; + List attributes = Lists.createFrom(first, rest); + return withAttributes(attributes); } - public Builder withAttributes(@NonEmpty List attributes) + /** + * Additional filters to search businesses. + *

+ * See {@link Attribute} for possible values. + * + * @param attributes The attributes to filter search results with. Cannot be empty. + * @throws IllegalArgumentException + * @return + * @see #withAttribute(tech.redroma.yelp.YelpSearchRequest.Attribute, tech.redroma.yelp.YelpSearchRequest.Attribute...) + */ + public Builder withAttributes(@NonEmpty List attributes) throws IllegalArgumentException { checkThat(attributes).is(nonEmptyList()); @@ -576,7 +778,14 @@ public Builder withAttributes(@NonEmpty List attributes) return this; } - + /** + * Builds the final {@link YelpSearchRequest} from the parameters stored so far. Note that a Search Request requires at + * least a {@linkplain #withLocation(tech.redroma.yelp.Address) Location} or a + * {@linkplain #withCoordinate(tech.redroma.yelp.Coordinate) Coordinate}. + * + * @return + * @throws YelpBadArgumentException + */ public YelpSearchRequest build() throws YelpBadArgumentException { checkThatLocationIsSet(); @@ -609,7 +818,7 @@ private void checkThatLocationIsSet() return; } - throw new YelpBadArgumentException("Either location "); + throw new YelpBadArgumentException("Either a location or a coordinate must be set."); } } From 930a03511dfafe1b383f7bcda8b69831b2a41265 Mon Sep 17 00:00:00 2001 From: Wellington Moreno Date: Thu, 1 Dec 2016 00:19:54 -0800 Subject: [PATCH 043/168] formats --- src/main/java/tech/redroma/yelp/YelpSearchRequest.java | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/main/java/tech/redroma/yelp/YelpSearchRequest.java b/src/main/java/tech/redroma/yelp/YelpSearchRequest.java index f6abf13..7e9d11a 100644 --- a/src/main/java/tech/redroma/yelp/YelpSearchRequest.java +++ b/src/main/java/tech/redroma/yelp/YelpSearchRequest.java @@ -203,8 +203,6 @@ public String getAttributes() return attributes; } - - @Override public int hashCode() { From 54b0a3f66d39eeb42160576a47fbc519e17d8708 Mon Sep 17 00:00:00 2001 From: Wellington Moreno Date: Thu, 1 Dec 2016 08:37:10 -0800 Subject: [PATCH 044/168] Adds documentation to YelpSearchRequest --- .../java/tech/redroma/yelp/YelpSearchRequest.java | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/src/main/java/tech/redroma/yelp/YelpSearchRequest.java b/src/main/java/tech/redroma/yelp/YelpSearchRequest.java index 7e9d11a..995f2ba 100644 --- a/src/main/java/tech/redroma/yelp/YelpSearchRequest.java +++ b/src/main/java/tech/redroma/yelp/YelpSearchRequest.java @@ -45,8 +45,13 @@ import static tech.sirwellington.alchemy.arguments.assertions.StringAssertions.nonEmptyString; /** - * + * Use to make search requests to the Yelp API. Use {@link #newBuilder() } to create a request object. + *

+ * Official Yelp documentation can be found here. + * * @author SirWellington + * @see YelpSearchRequest.Builder + * @see https://www.yelp.com/developers/documentation/v3/business_search */ @Pojo @Immutable @@ -54,6 +59,11 @@ @BuilderPattern(role = PRODUCT) public final class YelpSearchRequest { + + public static YelpSearchRequest.Builder newBuilder() + { + return Builder.newInstance(); + } @Optional private final String searchTerm; From a9d17cb8667d66cdb5f255c6140ca5fd966425c7 Mon Sep 17 00:00:00 2001 From: Wellington Moreno Date: Thu, 1 Dec 2016 09:21:32 -0800 Subject: [PATCH 045/168] Adds native support for Locales --- src/main/java/tech/redroma/yelp/Locale.java | 237 ++++++++++++++++++ .../tech/redroma/yelp/YelpSearchRequest.java | 19 +- 2 files changed, 249 insertions(+), 7 deletions(-) create mode 100644 src/main/java/tech/redroma/yelp/Locale.java diff --git a/src/main/java/tech/redroma/yelp/Locale.java b/src/main/java/tech/redroma/yelp/Locale.java new file mode 100644 index 0000000..21a3659 --- /dev/null +++ b/src/main/java/tech/redroma/yelp/Locale.java @@ -0,0 +1,237 @@ +/* + * Copyright 2016 RedRoma, Inc.. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package tech.redroma.yelp; + +import java.util.Objects; +import tech.sirwellington.alchemy.annotations.arguments.NonEmpty; + +import static tech.sirwellington.alchemy.arguments.Arguments.checkThat; +import static tech.sirwellington.alchemy.arguments.assertions.StringAssertions.nonEmptyString; + +/** + * See {@link Locales} for supported locales. + * + * @author SirWellington + * @see Locales + */ +public interface Locale +{ + + public String code(); + public String country(); + public String language(); + + public enum Locales implements Locale + { + CZECH_REPUBLIC("cs_CZ", "Czech Republic", "Czech"), + DENMARK("da_DK", "Denmark", "Danish"), + + //German + AUSTRIA("de_AT", "Austria", "German"), + SWITZERLAND_GERMAN("de_CH", "Switzerland", "German"), + GERMANY("de_DE", "Germany", "German"), + + //English + AUSTRALIA("en_AU", "Australia", "English"), + BELGIUM_ENGLISH("en_BE", "Belgium", "English"), + CANADA_ENGLISH("en_CA", "Canada", "English"), + SWITZERLAND_ENGLISH("en_CH", "Switzerland", "English"), + UNITED_KINGDOM("en_GB", "United Kingdom", "English"), + HONG_KONG_ENGLISH("en_HK", "Hong Kong", "English"), + IRELAND("en_IR", "Republic of Ireland", "English"), + MALAYSIA_ENGLISH("en_MY", "Malaysia", "English"), + NEW_ZEALAND("en_NZ", "New Zealand", "English"), + PHILIPPINES_ENGLISH("en_PH", "Philippines", "English"), + SINGAPORE("en_SG", "Singapore", "English"), + UNITED_STATES("en_US", "United St ates", "English"), + + //Spanish + ARGENTINA("es_AR", "Argentina", "Spanish"), + CHILE("es_CL", "Chile", "Spanish"), + SPAIN("es_ES", "Spain", "Spanish"), + MEXICO("es_MX", "Mexico", "Spanish"), + FINLAND_FINNISH("fi_FI", "Finland", "Finnish"), + PHILIPPINES_FILIPINO("fil_PH", "Phillippines", "Filipino"), + + //French + BELGIUM_FRENCH("fr_BE", "Belgium", "French"), + CANADA_FRENCH("fr_CA", "Canada", "French"), + SWITZERLAND_FRENCH("fr_CH", "Switzerland", "French"), + FRANCE("fr_FR", "France", "French"), + + //Italian + SWITZERLAND_ITALIAN("it_CH", "Switzerland", "Italian"), + ITALY("it_IT", "Italy", "Italian"), + + JAPAN("ja_JP", "Japan", "Japanese"), + + MALAYSIA_MALAY("ms_MY", "Malaysia", "Malay"), + + NORWAY("nb_NO", "Norway", "Norwegian"), + + //Dutch + BELGIUM_DUTCH("nl_BE", "Belgium", "Dutch"), + NETHERLANDS("nl_NL", "The Netherlands", "Dutch"), + + POLAND("pl_PL", "Poland", "Polish"), + + //Portugese + BRAZIL("pt_BR", "Brazil", "Portugese"), + PORTUGAL("pt_PT", "Portugal", "Portguese"), + + //Swedish + FINLAND_SWEDISH("sv_FI", "Finland", "Swedish"), + SWEDEN_SWEDISH("sv_SE", "Sweden", "Swedish"), + + TURKEY("tr_TR", "Turkey", "Turkish"), + + //Chinese + HONG_KONG_CHINESE("zh_HK", "Hong Kong", "Chinese"), + TAIWAIN("zh_TW", "Taiwan", "Chinese"), + ; + + private final String code; + + private final String country; + private final String language; + + private Locales(String code, String country, String language) + { + checkThat(code, country, language) + .are(nonEmptyString()); + + this.code = code; + this.country = country; + this.language = language; + } + + @Override + public String code() + { + return code; + } + + @Override + public String country() + { + return country; + } + + @Override + public String language() + { + return language; + } + + } + + /** + * Creates a Custom Locale from the specified information. + * + * @param code The country code follows ISO 3166-1 alpha-2 code, for example "en_CA". + * @param country The display name of the country, for example "Canada". + * @param language The Language, for example "English". + * @return + */ + static Locale of(@NonEmpty String code, @NonEmpty String country, @NonEmpty String language) throws IllegalArgumentException + { + checkThat(code, country, language) + .are(nonEmptyString()); + + /** + * Hidden internal class used for custom Locales. + */ + class CustomLocale implements Locale + { + + private final String code; + private final String country; + private final String language; + + CustomLocale(String code, String country, String language) + { + this.code = code; + this.country = country; + this.language = language; + } + + @Override + public String code() + { + return code; + } + + @Override + public String country() + { + return country; + } + + @Override + public String language() + { + return language; + } + + @Override + public int hashCode() + { + int hash = 5; + hash = 53 * hash + Objects.hashCode(this.code); + hash = 53 * hash + Objects.hashCode(this.country); + hash = 53 * hash + Objects.hashCode(this.language); + return hash; + } + + @Override + public boolean equals(Object obj) + { + if (this == obj) + { + return true; + } + if (obj == null) + { + return false; + } + if (getClass() != obj.getClass()) + { + return false; + } + final CustomLocale other = (CustomLocale) obj; + if (!Objects.equals(this.code, other.code)) + { + return false; + } + if (!Objects.equals(this.country, other.country)) + { + return false; + } + if (!Objects.equals(this.language, other.language)) + { + return false; + } + return true; + } + + } + + return new CustomLocale(code, country, language); + } + + +} diff --git a/src/main/java/tech/redroma/yelp/YelpSearchRequest.java b/src/main/java/tech/redroma/yelp/YelpSearchRequest.java index 995f2ba..db3847c 100644 --- a/src/main/java/tech/redroma/yelp/YelpSearchRequest.java +++ b/src/main/java/tech/redroma/yelp/YelpSearchRequest.java @@ -43,6 +43,7 @@ import static tech.sirwellington.alchemy.arguments.assertions.NumberAssertions.lessThanOrEqualTo; import static tech.sirwellington.alchemy.arguments.assertions.NumberAssertions.positiveInteger; import static tech.sirwellington.alchemy.arguments.assertions.StringAssertions.nonEmptyString; +import static tech.sirwellington.alchemy.arguments.assertions.StringAssertions.stringWithLength; /** * Use to make search requests to the Yelp API. Use {@link #newBuilder() } to create a request object. @@ -86,9 +87,6 @@ public static YelpSearchRequest.Builder newBuilder() @Optional private final String categories; - /** - * - */ @Optional private final String locale; @@ -599,17 +597,24 @@ public Builder withCategories(@NonEmpty List categories) throws Illega * @param locale The locale (cannot be empty). * @return * @throws IllegalArgumentException + * @see Locale.Locales * @see * https://www.yelp.com/developers/documentation/v3/supported_locales */ @Optional - public Builder withLocale(@NonEmpty String locale) throws IllegalArgumentException + public Builder withLocale(@Required Locale locale) throws IllegalArgumentException { checkThat(locale) .usingMessage("locale cannot be empty") - .is(nonEmptyString()); - - this.locale = locale; + .is(notNull()); + + String code = locale.code(); + checkThat(code) + .usingMessage("Country Code cannot be empty") + .is(nonEmptyString()) + .usingMessage("Country must take the form: {language code}_{country code}") + .is(stringWithLength(5)); + this.locale = locale.code(); return this; } From 0564f694540f6d2cd7bf09dbd7d054a847c614a7 Mon Sep 17 00:00:00 2001 From: Wellington Moreno Date: Thu, 1 Dec 2016 17:10:49 -0800 Subject: [PATCH 046/168] Adds a OAuthTokenProvider interface and implementations for obtaining a token from Yelp. --- .../tech/redroma/yelp/OAuthTokenProvider.java | 302 ++++++++++++++++++ 1 file changed, 302 insertions(+) create mode 100644 src/main/java/tech/redroma/yelp/OAuthTokenProvider.java diff --git a/src/main/java/tech/redroma/yelp/OAuthTokenProvider.java b/src/main/java/tech/redroma/yelp/OAuthTokenProvider.java new file mode 100644 index 0000000..b51a3d7 --- /dev/null +++ b/src/main/java/tech/redroma/yelp/OAuthTokenProvider.java @@ -0,0 +1,302 @@ +/* + * Copyright 2016 RedRoma, Inc.. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package tech.redroma.yelp; + +import com.google.gson.JsonElement; +import com.google.gson.JsonObject; +import com.google.gson.JsonPrimitive; +import java.net.URL; +import java.util.Objects; +import java.util.concurrent.TimeUnit; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import tech.redroma.yelp.exceptions.YelpBadArgumentException; +import tech.redroma.yelp.exceptions.YelpOperationFailedException; +import tech.sirwellington.alchemy.annotations.arguments.NonEmpty; +import tech.sirwellington.alchemy.annotations.arguments.Required; +import tech.sirwellington.alchemy.http.AlchemyHttp; +import tech.sirwellington.alchemy.http.HttpResponse; +import tech.sirwellington.alchemy.http.exceptions.AlchemyHttpException; + +import static tech.sirwellington.alchemy.arguments.Arguments.checkThat; +import static tech.sirwellington.alchemy.arguments.assertions.Assertions.notNull; +import static tech.sirwellington.alchemy.arguments.assertions.BooleanAssertions.trueStatement; +import static tech.sirwellington.alchemy.arguments.assertions.StringAssertions.nonEmptyString; +import static tech.sirwellington.alchemy.arguments.assertions.StringAssertions.stringWithLengthGreaterThan; + +/** + * The OAuthTokenProvider is responsible for providing an OAuth Token used to make API calls. + *

+ * For more information on Yelp's OAuth mechanism. + * Click here for more information. + * + * @author SirWellington + * @see + * https://www.yelp.com/developers/documentation/v3/get_started + */ +public interface OAuthTokenProvider +{ + + public static OAuthTokenProvider newBasicTokenProvider(@NonEmpty String token) throws IllegalArgumentException + { + checkThat(token) + .is(nonEmptyString()); + + return new Basic(token); + } + + @Required + String getToken(); + + static OAuthTokenProvider basicTokenProviderWithToken(@NonEmpty String token) throws IllegalArgumentException + { + checkThat(token) + .usingMessage("token is required") + .is(nonEmptyString()); + + return new Basic(token); + } + + static final class Basic implements OAuthTokenProvider + { + + private final String token; + + Basic(String token) + { + checkThat(token).is(nonEmptyString()); + this.token = token; + } + + @Override + public String getToken() + { + return token; + } + + @Override + public int hashCode() + { + int hash = 7; + hash = 23 * hash + Objects.hashCode(this.token); + return hash; + } + + @Override + public boolean equals(Object obj) + { + if (this == obj) + { + return true; + } + if (obj == null) + { + return false; + } + if (getClass() != obj.getClass()) + { + return false; + } + final Basic other = (Basic) obj; + if (!Objects.equals(this.token, other.token)) + { + return false; + } + return true; + } + + @Override + public String toString() + { + return "Basic{" + "token=" + token + '}'; + } + + } + + static final class OAuthObtainer implements OAuthTokenProvider + { + + private final AlchemyHttp http; + private final URL authenticationURL; + private final String clientId; + private final String clientSecret; + + private static final int BAD_REQUEST = 400; + + private static final Logger LOG = LoggerFactory.getLogger(OAuthObtainer.class); + + OAuthObtainer(AlchemyHttp http, URL authenticationURL, String clientId, String clientSecret) + { + checkThat(http, authenticationURL) + .are(notNull()); + + checkThat(clientId, clientSecret) + .are(nonEmptyString()) + .are(stringWithLengthGreaterThan(2)); + + this.http = http; + this.authenticationURL = authenticationURL; + this.clientId = clientId; + this.clientSecret = clientSecret; + } + + @Override + public String getToken() + { + JsonObject response = null; + + try + { + response = http.go() + .post() + .nothing() + .usingQueryParam(Keys.GRANT_TYPE, "client_credentials") + .usingQueryParam(Keys.CLIENT_ID, clientId) + .usingQueryParam(Keys.CLIENT_SECRET, clientSecret) + .usingHeader(Keys.CONTENT_TYPE, Keys.URL_ENCODED) + .expecting(JsonObject.class) + .at(authenticationURL); + } + catch (AlchemyHttpException ex) + { + HttpResponse yelpResponse = ex.getResponse(); + int status = yelpResponse.statusCode(); + + if (status == BAD_REQUEST) + { + throw new YelpBadArgumentException("Client ID or Secret are incorrect: " + yelpResponse.body()); + } + + throw new YelpOperationFailedException("Failed to get access token.", ex); + } + + checkResponse(response); + printExpiration(response); + + return tryToGetTokenFrom(response); + } + + @Override + public int hashCode() + { + int hash = 7; + hash = 79 * hash + Objects.hashCode(this.http); + hash = 79 * hash + Objects.hashCode(this.authenticationURL); + return hash; + } + + @Override + public boolean equals(Object obj) + { + if (this == obj) + { + return true; + } + if (obj == null) + { + return false; + } + if (getClass() != obj.getClass()) + { + return false; + } + final OAuthObtainer other = (OAuthObtainer) obj; + if (!Objects.equals(this.http, other.http)) + { + return false; + } + if (!Objects.equals(this.authenticationURL, other.authenticationURL)) + { + return false; + } + return true; + } + + @Override + public String toString() + { + return "OAuthObtainer{" + "http=" + http + ", authenticationURL=" + authenticationURL + '}'; + } + + private void printExpiration(JsonObject response) + { + JsonElement expiration = response.get(Keys.EXPIRATION); + + if (expiration.isJsonPrimitive()) + { + JsonPrimitive expirationValue = expiration.getAsJsonPrimitive(); + + if (expirationValue.isNumber()) + { + int expirationSeconds = expirationValue.getAsInt(); + + long expirationDays = TimeUnit.SECONDS.toDays(expirationSeconds); + long expirationMinutes = TimeUnit.SECONDS.toMinutes(expirationSeconds); + LOG.debug("Yelp Token expires in {} days or {} minutes", expirationDays, expirationMinutes); + } + else + { + LOG.warn("Received unexpected token expiration: " + expirationValue); + } + + } + } + + private void checkResponse(JsonObject response) + { + checkThat(response) + .throwing(YelpOperationFailedException.class) + .usingMessage("Received unexpected null response") + .is(notNull()); + + checkThat(response.has(Keys.EXPIRATION)) + .usingMessage("OAuth response is missing expiration information: " + response) + .is(trueStatement()); + + checkThat(response.has(Keys.TOKEN)) + .usingMessage("OAUth response is missing access token: " + response) + .throwing(YelpOperationFailedException.class) + .is(trueStatement()); + } + + private String tryToGetTokenFrom(JsonObject response) + { + checkThat(response.has(Keys.TOKEN)) + .usingMessage("Yelp AUTH response is missing the token") + .throwing(YelpBadArgumentException.class) + .is(trueStatement()); + + return response.get(Keys.TOKEN).getAsString(); + } + + private static class Keys + { + + static final String GRANT_TYPE = "grant_type"; + static final String CLIENT_ID = "client_id"; + static final String CLIENT_SECRET = "client_secret"; + static final String CONTENT_TYPE = "Content-Type"; + static final String URL_ENCODED = "application/x-www-form-urlencoded"; + + //Response Keys + static final String EXPIRATION = "expires_in"; + static final String TOKEN = "access_token"; + } + + } + +} From 7e4583be354336401c5ce59a0d223c14fde83ea1 Mon Sep 17 00:00:00 2001 From: Wellington Moreno Date: Thu, 1 Dec 2016 19:24:26 -0800 Subject: [PATCH 047/168] Adds documentation to OAuthTokenProvider --- .../tech/redroma/yelp/OAuthTokenProvider.java | 92 ++++++++++++++++++- 1 file changed, 88 insertions(+), 4 deletions(-) diff --git a/src/main/java/tech/redroma/yelp/OAuthTokenProvider.java b/src/main/java/tech/redroma/yelp/OAuthTokenProvider.java index b51a3d7..f9a7e49 100644 --- a/src/main/java/tech/redroma/yelp/OAuthTokenProvider.java +++ b/src/main/java/tech/redroma/yelp/OAuthTokenProvider.java @@ -19,6 +19,7 @@ import com.google.gson.JsonElement; import com.google.gson.JsonObject; import com.google.gson.JsonPrimitive; +import java.net.MalformedURLException; import java.net.URL; import java.util.Objects; import java.util.concurrent.TimeUnit; @@ -40,6 +41,7 @@ /** * The OAuthTokenProvider is responsible for providing an OAuth Token used to make API calls. + * The recommended method is to use the {@linkplain #newRefreshingTokenProvider(java.lang.String, java.lang.String) Refereshing OAUth Provider}. *

* For more information on Yelp's OAuth mechanism. * Click here for more information. @@ -51,14 +53,96 @@ public interface OAuthTokenProvider { + /** + * Creates an {@link OAuthTokenProvider} using the provided key. Use this if you already have an OAuth token that you want to + * reuse. + * + * @param token The OAuth token to use when making API calls. + * @return + * @throws IllegalArgumentException + */ public static OAuthTokenProvider newBasicTokenProvider(@NonEmpty String token) throws IllegalArgumentException { - checkThat(token) - .is(nonEmptyString()); - + checkThat(token).is(nonEmptyString()); + return new Basic(token); } - + + /** + * Creates a refreshing {@link OAuthTokenProvider} that obtains OAuth Tokens from Yelp and periodically renews the token + * before it expires. This is the recommended method of authentication. + * + * @param clientId The Client ID obtained from the Yelp Developer Console. + * @param clientSecret The Client Secret obtained from the Yelp Developer Console. + * @return + * @throws IllegalArgumentException + */ + public static OAuthTokenProvider newRefreshingTokenProvider(@NonEmpty String clientId, @NonEmpty String clientSecret) throws IllegalArgumentException + { + final String DEFAULT_AUTH_URL = "https://api.yelp.com/oauth2/token"; + + try + { + URL authURL = new URL(DEFAULT_AUTH_URL); + return newRefeshingTokenProvider(clientId, clientSecret, authURL); + } + catch (MalformedURLException ex) + { + throw new IllegalArgumentException("Could not form Auth URL.", ex); + } + } + + /** + * @param clientId The Client ID obtained from the Yelp Developer Console. + * @param clientSecret The Client Secret obtained from the Yelp Developer Console. + * @param authURL + * @return + * @throws IllegalArgumentException + */ + public static OAuthTokenProvider newRefeshingTokenProvider(@NonEmpty String clientId, @NonEmpty String clientSecret, + @Required URL authURL) throws IllegalArgumentException + { + AlchemyHttp http = AlchemyHttp.newDefaultInstance(); + + return newRefreshingTokenProvider(clientId, clientSecret, authURL, http); + } + + /** + * + * @param clientId The Client ID obtained from the Yelp Developer Console. + * @param clientSecret The Client Secret obtained from the Yelp Developer Console. + * @param authURL The Authentication URL to use for authentication. + * @param http The Alchemy HTTP Client to use during requests. + * @return + * @throws IllegalArgumentException + * @see #newRefreshingTokenProvider(java.lang.String, java.lang.String, java.net.URL, + * tech.sirwellington.alchemy.http.AlchemyHttp) + */ + static OAuthTokenProvider newRefreshingTokenProvider(@NonEmpty String clientId, + @NonEmpty String clientSecret, + @Required URL authURL, + @Required AlchemyHttp http) throws IllegalArgumentException + { + checkThat(clientId, clientSecret) + .usingMessage("cliend ID and client secret are required") + .are(nonEmptyString()); + + checkThat(authURL) + .usingMessage("Authorization URL is required") + .is(notNull()); + + checkThat(http) + .usingMessage("Alchemy HTTP cannot be null") + .is(notNull()); + + return new OAuthObtainer(http, authURL, clientId, clientSecret); + } + + /** + * Obtain an OAuth token used for API calls. + * + * @return + */ @Required String getToken(); From 155bb4529589208180d7bd586ffbb3e716cd5533 Mon Sep 17 00:00:00 2001 From: Wellington Moreno Date: Thu, 1 Dec 2016 19:27:04 -0800 Subject: [PATCH 048/168] Breaks out the BasicAuthProvider into a separate class --- .../tech/redroma/yelp/OAuthTokenProvider.java | 59 +----------- .../redroma/yelp/OAuthTokenProviderBasic.java | 91 +++++++++++++++++++ 2 files changed, 93 insertions(+), 57 deletions(-) create mode 100644 src/main/java/tech/redroma/yelp/OAuthTokenProviderBasic.java diff --git a/src/main/java/tech/redroma/yelp/OAuthTokenProvider.java b/src/main/java/tech/redroma/yelp/OAuthTokenProvider.java index f9a7e49..8bf073a 100644 --- a/src/main/java/tech/redroma/yelp/OAuthTokenProvider.java +++ b/src/main/java/tech/redroma/yelp/OAuthTokenProvider.java @@ -65,7 +65,7 @@ public static OAuthTokenProvider newBasicTokenProvider(@NonEmpty String token) t { checkThat(token).is(nonEmptyString()); - return new Basic(token); + return new OAuthTokenProviderBasic(token); } /** @@ -152,64 +152,9 @@ static OAuthTokenProvider basicTokenProviderWithToken(@NonEmpty String token) th .usingMessage("token is required") .is(nonEmptyString()); - return new Basic(token); + return new OAuthTokenProviderBasic(token); } - static final class Basic implements OAuthTokenProvider - { - - private final String token; - - Basic(String token) - { - checkThat(token).is(nonEmptyString()); - this.token = token; - } - - @Override - public String getToken() - { - return token; - } - - @Override - public int hashCode() - { - int hash = 7; - hash = 23 * hash + Objects.hashCode(this.token); - return hash; - } - - @Override - public boolean equals(Object obj) - { - if (this == obj) - { - return true; - } - if (obj == null) - { - return false; - } - if (getClass() != obj.getClass()) - { - return false; - } - final Basic other = (Basic) obj; - if (!Objects.equals(this.token, other.token)) - { - return false; - } - return true; - } - - @Override - public String toString() - { - return "Basic{" + "token=" + token + '}'; - } - - } static final class OAuthObtainer implements OAuthTokenProvider { diff --git a/src/main/java/tech/redroma/yelp/OAuthTokenProviderBasic.java b/src/main/java/tech/redroma/yelp/OAuthTokenProviderBasic.java new file mode 100644 index 0000000..57fce40 --- /dev/null +++ b/src/main/java/tech/redroma/yelp/OAuthTokenProviderBasic.java @@ -0,0 +1,91 @@ +/* + * Copyright 2016 RedRoma, Inc.. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + + +package tech.redroma.yelp; + + +import java.util.Objects; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import tech.sirwellington.alchemy.annotations.access.Internal; +import tech.sirwellington.alchemy.arguments.assertions.StringAssertions; + +import static tech.sirwellington.alchemy.arguments.Arguments.checkThat; + +/** + * Stores a permanent token and returns it on each call. + * + * @author SirWellington + */ +@Internal +final class OAuthTokenProviderBasic implements OAuthTokenProvider +{ + private final static Logger LOG = LoggerFactory.getLogger(OAuthTokenProviderBasic.class); + + private final String token; + + OAuthTokenProviderBasic(String token) + { + checkThat(token).is(StringAssertions.nonEmptyString()); + this.token = token; + } + + @Override + public String getToken() + { + return token; + } + + @Override + public int hashCode() + { + int hash = 7; + hash = 23 * hash + Objects.hashCode(this.token); + return hash; + } + + @Override + public boolean equals(Object obj) + { + if (this == obj) + { + return true; + } + if (obj == null) + { + return false; + } + if (getClass() != obj.getClass()) + { + return false; + } + final OAuthTokenProviderBasic other = (OAuthTokenProviderBasic) obj; + if (!Objects.equals(this.token, other.token)) + { + return false; + } + return true; + } + + @Override + public String toString() + { + return "OAuthTokenProviderBasic{" + "token=" + token + '}'; + } + + +} From 0699451a6fb258c823637bc29975355287245c26 Mon Sep 17 00:00:00 2001 From: Wellington Moreno Date: Thu, 1 Dec 2016 19:31:38 -0800 Subject: [PATCH 049/168] Refactors the renewing OAuth Provider into a separate class --- .../tech/redroma/yelp/OAuthTokenProvider.java | 173 +------------- .../yelp/OAuthTokenProviderRenewing.java | 221 ++++++++++++++++++ 2 files changed, 222 insertions(+), 172 deletions(-) create mode 100644 src/main/java/tech/redroma/yelp/OAuthTokenProviderRenewing.java diff --git a/src/main/java/tech/redroma/yelp/OAuthTokenProvider.java b/src/main/java/tech/redroma/yelp/OAuthTokenProvider.java index 8bf073a..7a2cba2 100644 --- a/src/main/java/tech/redroma/yelp/OAuthTokenProvider.java +++ b/src/main/java/tech/redroma/yelp/OAuthTokenProvider.java @@ -135,7 +135,7 @@ static OAuthTokenProvider newRefreshingTokenProvider(@NonEmpty String clientId, .usingMessage("Alchemy HTTP cannot be null") .is(notNull()); - return new OAuthObtainer(http, authURL, clientId, clientSecret); + return new OAuthTokenProviderRenewing(http, authURL, clientId, clientSecret); } /** @@ -156,176 +156,5 @@ static OAuthTokenProvider basicTokenProviderWithToken(@NonEmpty String token) th } - static final class OAuthObtainer implements OAuthTokenProvider - { - - private final AlchemyHttp http; - private final URL authenticationURL; - private final String clientId; - private final String clientSecret; - - private static final int BAD_REQUEST = 400; - - private static final Logger LOG = LoggerFactory.getLogger(OAuthObtainer.class); - - OAuthObtainer(AlchemyHttp http, URL authenticationURL, String clientId, String clientSecret) - { - checkThat(http, authenticationURL) - .are(notNull()); - - checkThat(clientId, clientSecret) - .are(nonEmptyString()) - .are(stringWithLengthGreaterThan(2)); - - this.http = http; - this.authenticationURL = authenticationURL; - this.clientId = clientId; - this.clientSecret = clientSecret; - } - - @Override - public String getToken() - { - JsonObject response = null; - - try - { - response = http.go() - .post() - .nothing() - .usingQueryParam(Keys.GRANT_TYPE, "client_credentials") - .usingQueryParam(Keys.CLIENT_ID, clientId) - .usingQueryParam(Keys.CLIENT_SECRET, clientSecret) - .usingHeader(Keys.CONTENT_TYPE, Keys.URL_ENCODED) - .expecting(JsonObject.class) - .at(authenticationURL); - } - catch (AlchemyHttpException ex) - { - HttpResponse yelpResponse = ex.getResponse(); - int status = yelpResponse.statusCode(); - - if (status == BAD_REQUEST) - { - throw new YelpBadArgumentException("Client ID or Secret are incorrect: " + yelpResponse.body()); - } - - throw new YelpOperationFailedException("Failed to get access token.", ex); - } - - checkResponse(response); - printExpiration(response); - - return tryToGetTokenFrom(response); - } - - @Override - public int hashCode() - { - int hash = 7; - hash = 79 * hash + Objects.hashCode(this.http); - hash = 79 * hash + Objects.hashCode(this.authenticationURL); - return hash; - } - - @Override - public boolean equals(Object obj) - { - if (this == obj) - { - return true; - } - if (obj == null) - { - return false; - } - if (getClass() != obj.getClass()) - { - return false; - } - final OAuthObtainer other = (OAuthObtainer) obj; - if (!Objects.equals(this.http, other.http)) - { - return false; - } - if (!Objects.equals(this.authenticationURL, other.authenticationURL)) - { - return false; - } - return true; - } - - @Override - public String toString() - { - return "OAuthObtainer{" + "http=" + http + ", authenticationURL=" + authenticationURL + '}'; - } - - private void printExpiration(JsonObject response) - { - JsonElement expiration = response.get(Keys.EXPIRATION); - - if (expiration.isJsonPrimitive()) - { - JsonPrimitive expirationValue = expiration.getAsJsonPrimitive(); - - if (expirationValue.isNumber()) - { - int expirationSeconds = expirationValue.getAsInt(); - - long expirationDays = TimeUnit.SECONDS.toDays(expirationSeconds); - long expirationMinutes = TimeUnit.SECONDS.toMinutes(expirationSeconds); - LOG.debug("Yelp Token expires in {} days or {} minutes", expirationDays, expirationMinutes); - } - else - { - LOG.warn("Received unexpected token expiration: " + expirationValue); - } - - } - } - - private void checkResponse(JsonObject response) - { - checkThat(response) - .throwing(YelpOperationFailedException.class) - .usingMessage("Received unexpected null response") - .is(notNull()); - - checkThat(response.has(Keys.EXPIRATION)) - .usingMessage("OAuth response is missing expiration information: " + response) - .is(trueStatement()); - - checkThat(response.has(Keys.TOKEN)) - .usingMessage("OAUth response is missing access token: " + response) - .throwing(YelpOperationFailedException.class) - .is(trueStatement()); - } - - private String tryToGetTokenFrom(JsonObject response) - { - checkThat(response.has(Keys.TOKEN)) - .usingMessage("Yelp AUTH response is missing the token") - .throwing(YelpBadArgumentException.class) - .is(trueStatement()); - - return response.get(Keys.TOKEN).getAsString(); - } - - private static class Keys - { - - static final String GRANT_TYPE = "grant_type"; - static final String CLIENT_ID = "client_id"; - static final String CLIENT_SECRET = "client_secret"; - static final String CONTENT_TYPE = "Content-Type"; - static final String URL_ENCODED = "application/x-www-form-urlencoded"; - - //Response Keys - static final String EXPIRATION = "expires_in"; - static final String TOKEN = "access_token"; - } - - } } diff --git a/src/main/java/tech/redroma/yelp/OAuthTokenProviderRenewing.java b/src/main/java/tech/redroma/yelp/OAuthTokenProviderRenewing.java new file mode 100644 index 0000000..39d9547 --- /dev/null +++ b/src/main/java/tech/redroma/yelp/OAuthTokenProviderRenewing.java @@ -0,0 +1,221 @@ +/* + * Copyright 2016 RedRoma, Inc.. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package tech.redroma.yelp; + +import com.google.gson.JsonElement; +import com.google.gson.JsonObject; +import com.google.gson.JsonPrimitive; +import java.net.URL; +import java.util.Objects; +import java.util.concurrent.TimeUnit; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import tech.redroma.yelp.exceptions.YelpBadArgumentException; +import tech.redroma.yelp.exceptions.YelpOperationFailedException; +import tech.sirwellington.alchemy.annotations.access.Internal; +import tech.sirwellington.alchemy.http.AlchemyHttp; +import tech.sirwellington.alchemy.http.HttpResponse; +import tech.sirwellington.alchemy.http.exceptions.AlchemyHttpException; + +import static tech.sirwellington.alchemy.arguments.Arguments.checkThat; +import static tech.sirwellington.alchemy.arguments.assertions.Assertions.notNull; +import static tech.sirwellington.alchemy.arguments.assertions.BooleanAssertions.trueStatement; +import static tech.sirwellington.alchemy.arguments.assertions.StringAssertions.nonEmptyString; +import static tech.sirwellington.alchemy.arguments.assertions.StringAssertions.stringWithLengthGreaterThan; + +/** + * + * @author SirWellington + */ +@Internal +final class OAuthTokenProviderRenewing implements OAuthTokenProvider +{ + + private static final Logger LOG = LoggerFactory.getLogger(OAuthTokenProviderRenewing.class); + + private final AlchemyHttp http; + private final URL authenticationURL; + private final String clientId; + private final String clientSecret; + private static final int BAD_REQUEST = 400; + + OAuthTokenProviderRenewing(AlchemyHttp http, URL authenticationURL, String clientId, String clientSecret) + { + checkThat(http, authenticationURL) + .are(notNull()); + + checkThat(clientId, clientSecret) + .are(nonEmptyString()) + .are(stringWithLengthGreaterThan(2)); + + this.http = http; + this.authenticationURL = authenticationURL; + this.clientId = clientId; + this.clientSecret = clientSecret; + } + + @Override + public String getToken() + { + JsonObject response = null; + try + { + response = http + .go() + .post() + .nothing() + .usingQueryParam(Keys.GRANT_TYPE, "client_credentials") + .usingQueryParam(Keys.CLIENT_ID, clientId) + .usingQueryParam(Keys.CLIENT_SECRET, clientSecret) + .usingHeader(Keys.CONTENT_TYPE, Keys.URL_ENCODED) + .expecting(JsonObject.class) + .at(authenticationURL); + } + catch (AlchemyHttpException ex) + { + HttpResponse yelpResponse = ex.getResponse(); + int status = yelpResponse.statusCode(); + + if (status == BAD_REQUEST) + { + throw new YelpBadArgumentException("Client ID or Secret are incorrect: " + yelpResponse.body()); + } + + throw new YelpOperationFailedException("Failed to get access token.", ex); + } + + checkResponse(response); + printExpiration(response); + + return tryToGetTokenFrom(response); + } + + @Override + public int hashCode() + { + int hash = 5; + hash = 47 * hash + Objects.hashCode(this.http); + hash = 47 * hash + Objects.hashCode(this.authenticationURL); + hash = 47 * hash + Objects.hashCode(this.clientId); + hash = 47 * hash + Objects.hashCode(this.clientSecret); + return hash; + } + + @Override + public boolean equals(Object obj) + { + if (this == obj) + { + return true; + } + if (obj == null) + { + return false; + } + if (getClass() != obj.getClass()) + { + return false; + } + final OAuthTokenProviderRenewing other = (OAuthTokenProviderRenewing) obj; + if (!Objects.equals(this.clientId, other.clientId)) + { + return false; + } + if (!Objects.equals(this.clientSecret, other.clientSecret)) + { + return false; + } + if (!Objects.equals(this.http, other.http)) + { + return false; + } + if (!Objects.equals(this.authenticationURL, other.authenticationURL)) + { + return false; + } + return true; + } + + @Override + public String toString() + { + return "OAuthTokenProviderRenewing{" + "http=" + http + ", authenticationURL=" + authenticationURL + ", clientId=" + clientId + ", clientSecret=" + "" + '}'; + } + + private void printExpiration(JsonObject response) + { + JsonElement expiration = response.get(Keys.EXPIRATION); + + if (expiration.isJsonPrimitive()) + { + JsonPrimitive expirationValue = expiration.getAsJsonPrimitive(); + + if (expirationValue.isNumber()) + { + int expirationSeconds = expirationValue.getAsInt(); + long expirationDays = TimeUnit.SECONDS.toDays(expirationSeconds); + long expirationMinutes = TimeUnit.SECONDS.toMinutes(expirationSeconds); + + LOG.debug("Yelp Token expires in {} days or {} minutes", expirationDays, expirationMinutes); + } + else + { + LOG.warn("Received unexpected token expiration: " + expirationValue); + } + } + } + + private void checkResponse(JsonObject response) + { + checkThat(response) + .throwing(YelpOperationFailedException.class) + .usingMessage("Received unexpected null response") + .is(notNull()); + + checkThat(response.has(Keys.EXPIRATION)) + .usingMessage("OAuth response is missing expiration information: " + response) + .is(trueStatement()); + + checkThat(response.has(Keys.TOKEN)).usingMessage("OAUth response is missing access token: " + response) + .throwing(YelpOperationFailedException.class) + .is(trueStatement()); + } + + private String tryToGetTokenFrom(JsonObject response) + { + checkThat(response.has(Keys.TOKEN)) + .usingMessage("Yelp AUTH response is missing the token") + .throwing(YelpBadArgumentException.class) + .is(trueStatement()); + + return response.get(Keys.TOKEN).getAsString(); + } + + private static class Keys + { + static final String GRANT_TYPE = "grant_type"; + static final String CLIENT_ID = "client_id"; + static final String CLIENT_SECRET = "client_secret"; + static final String CONTENT_TYPE = "Content-Type"; + static final String URL_ENCODED = "application/x-www-form-urlencoded"; + + //Response Keys + static final String EXPIRATION = "expires_in"; + static final String TOKEN = "access_token"; + } + +} From f432f9ad709716120d13dd670f7581977bc5fb21 Mon Sep 17 00:00:00 2001 From: Wellington Moreno Date: Thu, 1 Dec 2016 19:32:37 -0800 Subject: [PATCH 050/168] Adds documentation to OAuthTokenProviderRenewing --- .../java/tech/redroma/yelp/OAuthTokenProviderRenewing.java | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/main/java/tech/redroma/yelp/OAuthTokenProviderRenewing.java b/src/main/java/tech/redroma/yelp/OAuthTokenProviderRenewing.java index 39d9547..93ce350 100644 --- a/src/main/java/tech/redroma/yelp/OAuthTokenProviderRenewing.java +++ b/src/main/java/tech/redroma/yelp/OAuthTokenProviderRenewing.java @@ -38,7 +38,9 @@ import static tech.sirwellington.alchemy.arguments.assertions.StringAssertions.stringWithLengthGreaterThan; /** - * + * This {@link OAuthTokenProvider} retrieves an OAuth Token from the Yelp Authentication API + * using the given client_id and client_secret, ensuring it is never expired. + * * @author SirWellington */ @Internal From 269e5bb37fb7da6cf7a17aa662f92d3d0b148879 Mon Sep 17 00:00:00 2001 From: Wellington Moreno Date: Thu, 1 Dec 2016 19:38:54 -0800 Subject: [PATCH 051/168] Adds some documentation --- .../redroma/yelp/OAuthTokenProviderRenewing.java | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/src/main/java/tech/redroma/yelp/OAuthTokenProviderRenewing.java b/src/main/java/tech/redroma/yelp/OAuthTokenProviderRenewing.java index 93ce350..dd02f31 100644 --- a/src/main/java/tech/redroma/yelp/OAuthTokenProviderRenewing.java +++ b/src/main/java/tech/redroma/yelp/OAuthTokenProviderRenewing.java @@ -16,6 +16,7 @@ package tech.redroma.yelp; +import com.google.common.base.Strings; import com.google.gson.JsonElement; import com.google.gson.JsonObject; import com.google.gson.JsonPrimitive; @@ -27,6 +28,7 @@ import tech.redroma.yelp.exceptions.YelpBadArgumentException; import tech.redroma.yelp.exceptions.YelpOperationFailedException; import tech.sirwellington.alchemy.annotations.access.Internal; +import tech.sirwellington.alchemy.annotations.concurrency.ThreadUnsafe; import tech.sirwellington.alchemy.http.AlchemyHttp; import tech.sirwellington.alchemy.http.HttpResponse; import tech.sirwellington.alchemy.http.exceptions.AlchemyHttpException; @@ -54,6 +56,12 @@ final class OAuthTokenProviderRenewing implements OAuthTokenProvider private final String clientId; private final String clientSecret; private static final int BAD_REQUEST = 400; + + /* + * TODO: Make this thread-safe by adding a Lock. + */ + @ThreadUnsafe + private String oauthToken; OAuthTokenProviderRenewing(AlchemyHttp http, URL authenticationURL, String clientId, String clientSecret) { @@ -73,6 +81,11 @@ final class OAuthTokenProviderRenewing implements OAuthTokenProvider @Override public String getToken() { + if (!Strings.isNullOrEmpty(oauthToken)) + { + return oauthToken; + } + JsonObject response = null; try { @@ -103,7 +116,8 @@ public String getToken() checkResponse(response); printExpiration(response); - return tryToGetTokenFrom(response); + this.oauthToken = tryToGetTokenFrom(response); + return this.oauthToken; } @Override From a7448fc9ff229ca98d9614b64bfbe4ff8c85fa36 Mon Sep 17 00:00:00 2001 From: Wellington Moreno Date: Thu, 1 Dec 2016 19:40:03 -0800 Subject: [PATCH 052/168] Adds YelpAuthenticationException --- .../YelpAuthenticationException.java | 46 +++++++++++++++++++ 1 file changed, 46 insertions(+) create mode 100644 src/main/java/tech/redroma/yelp/exceptions/YelpAuthenticationException.java diff --git a/src/main/java/tech/redroma/yelp/exceptions/YelpAuthenticationException.java b/src/main/java/tech/redroma/yelp/exceptions/YelpAuthenticationException.java new file mode 100644 index 0000000..295cc09 --- /dev/null +++ b/src/main/java/tech/redroma/yelp/exceptions/YelpAuthenticationException.java @@ -0,0 +1,46 @@ +/* + * Copyright 2016 RedRoma, Inc.. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package tech.redroma.yelp.exceptions; + +/** + * Thrown when an Authentication error occurs communicating with the Yelp API. + * + * @author SirWellington + */ +public class YelpAuthenticationException extends YelpExcetion +{ + + public YelpAuthenticationException() + { + } + + public YelpAuthenticationException(String message) + { + super(message); + } + + public YelpAuthenticationException(String message, Throwable cause) + { + super(message, cause); + } + + public YelpAuthenticationException(Throwable cause) + { + super(cause); + } + +} From 60ebb1e97dbfbf5a1d258df3ff4db2d2b01b4108 Mon Sep 17 00:00:00 2001 From: Wellington Moreno Date: Thu, 1 Dec 2016 19:42:59 -0800 Subject: [PATCH 053/168] Tweaks OAuthTokenProviderBasic --- .../java/tech/redroma/yelp/OAuthTokenProviderBasic.java | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/main/java/tech/redroma/yelp/OAuthTokenProviderBasic.java b/src/main/java/tech/redroma/yelp/OAuthTokenProviderBasic.java index 57fce40..61ac8f4 100644 --- a/src/main/java/tech/redroma/yelp/OAuthTokenProviderBasic.java +++ b/src/main/java/tech/redroma/yelp/OAuthTokenProviderBasic.java @@ -22,12 +22,12 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; import tech.sirwellington.alchemy.annotations.access.Internal; -import tech.sirwellington.alchemy.arguments.assertions.StringAssertions; import static tech.sirwellington.alchemy.arguments.Arguments.checkThat; +import static tech.sirwellington.alchemy.arguments.assertions.StringAssertions.nonEmptyString; /** - * Stores a permanent token and returns it on each call. + * Stores a permanent token and returns it on each call to {@link #getToken() }. * * @author SirWellington */ @@ -40,7 +40,8 @@ final class OAuthTokenProviderBasic implements OAuthTokenProvider OAuthTokenProviderBasic(String token) { - checkThat(token).is(StringAssertions.nonEmptyString()); + checkThat(token).is(nonEmptyString()); + this.token = token; } From 88339b022fea2839d133ff0f7106e6d55f6e7ff5 Mon Sep 17 00:00:00 2001 From: Wellington Moreno Date: Thu, 1 Dec 2016 19:43:19 -0800 Subject: [PATCH 054/168] Organizes imports --- .../java/tech/redroma/yelp/OAuthTokenProvider.java | 13 ------------- 1 file changed, 13 deletions(-) diff --git a/src/main/java/tech/redroma/yelp/OAuthTokenProvider.java b/src/main/java/tech/redroma/yelp/OAuthTokenProvider.java index 7a2cba2..049e9e8 100644 --- a/src/main/java/tech/redroma/yelp/OAuthTokenProvider.java +++ b/src/main/java/tech/redroma/yelp/OAuthTokenProvider.java @@ -16,28 +16,15 @@ package tech.redroma.yelp; -import com.google.gson.JsonElement; -import com.google.gson.JsonObject; -import com.google.gson.JsonPrimitive; import java.net.MalformedURLException; import java.net.URL; -import java.util.Objects; -import java.util.concurrent.TimeUnit; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import tech.redroma.yelp.exceptions.YelpBadArgumentException; -import tech.redroma.yelp.exceptions.YelpOperationFailedException; import tech.sirwellington.alchemy.annotations.arguments.NonEmpty; import tech.sirwellington.alchemy.annotations.arguments.Required; import tech.sirwellington.alchemy.http.AlchemyHttp; -import tech.sirwellington.alchemy.http.HttpResponse; -import tech.sirwellington.alchemy.http.exceptions.AlchemyHttpException; import static tech.sirwellington.alchemy.arguments.Arguments.checkThat; import static tech.sirwellington.alchemy.arguments.assertions.Assertions.notNull; -import static tech.sirwellington.alchemy.arguments.assertions.BooleanAssertions.trueStatement; import static tech.sirwellington.alchemy.arguments.assertions.StringAssertions.nonEmptyString; -import static tech.sirwellington.alchemy.arguments.assertions.StringAssertions.stringWithLengthGreaterThan; /** * The OAuthTokenProvider is responsible for providing an OAuth Token used to make API calls. From 9e13676ccf8e23c04a5bef56141dd702406901d0 Mon Sep 17 00:00:00 2001 From: Wellington Moreno Date: Thu, 1 Dec 2016 19:44:03 -0800 Subject: [PATCH 055/168] Moves OAuth code into a separate package --- .../java/tech/redroma/yelp/{ => oauth}/OAuthTokenProvider.java | 2 +- .../tech/redroma/yelp/{ => oauth}/OAuthTokenProviderBasic.java | 2 +- .../redroma/yelp/{ => oauth}/OAuthTokenProviderRenewing.java | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) rename src/main/java/tech/redroma/yelp/{ => oauth}/OAuthTokenProvider.java (99%) rename src/main/java/tech/redroma/yelp/{ => oauth}/OAuthTokenProviderBasic.java (98%) rename src/main/java/tech/redroma/yelp/{ => oauth}/OAuthTokenProviderRenewing.java (99%) diff --git a/src/main/java/tech/redroma/yelp/OAuthTokenProvider.java b/src/main/java/tech/redroma/yelp/oauth/OAuthTokenProvider.java similarity index 99% rename from src/main/java/tech/redroma/yelp/OAuthTokenProvider.java rename to src/main/java/tech/redroma/yelp/oauth/OAuthTokenProvider.java index 049e9e8..d28ddf1 100644 --- a/src/main/java/tech/redroma/yelp/OAuthTokenProvider.java +++ b/src/main/java/tech/redroma/yelp/oauth/OAuthTokenProvider.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package tech.redroma.yelp; +package tech.redroma.yelp.oauth; import java.net.MalformedURLException; import java.net.URL; diff --git a/src/main/java/tech/redroma/yelp/OAuthTokenProviderBasic.java b/src/main/java/tech/redroma/yelp/oauth/OAuthTokenProviderBasic.java similarity index 98% rename from src/main/java/tech/redroma/yelp/OAuthTokenProviderBasic.java rename to src/main/java/tech/redroma/yelp/oauth/OAuthTokenProviderBasic.java index 61ac8f4..c2bcf83 100644 --- a/src/main/java/tech/redroma/yelp/OAuthTokenProviderBasic.java +++ b/src/main/java/tech/redroma/yelp/oauth/OAuthTokenProviderBasic.java @@ -15,7 +15,7 @@ */ -package tech.redroma.yelp; +package tech.redroma.yelp.oauth; import java.util.Objects; diff --git a/src/main/java/tech/redroma/yelp/OAuthTokenProviderRenewing.java b/src/main/java/tech/redroma/yelp/oauth/OAuthTokenProviderRenewing.java similarity index 99% rename from src/main/java/tech/redroma/yelp/OAuthTokenProviderRenewing.java rename to src/main/java/tech/redroma/yelp/oauth/OAuthTokenProviderRenewing.java index dd02f31..e0ac262 100644 --- a/src/main/java/tech/redroma/yelp/OAuthTokenProviderRenewing.java +++ b/src/main/java/tech/redroma/yelp/oauth/OAuthTokenProviderRenewing.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package tech.redroma.yelp; +package tech.redroma.yelp.oauth; import com.google.common.base.Strings; import com.google.gson.JsonElement; From 9dea7bf3a9f53773f5c7f08c2f6799497b56e8bf Mon Sep 17 00:00:00 2001 From: Wellington Moreno Date: Thu, 1 Dec 2016 19:44:37 -0800 Subject: [PATCH 056/168] Renames classes --- .../{OAuthTokenProviderBasic.java => BasicProvider.java} | 8 ++++---- .../java/tech/redroma/yelp/oauth/OAuthTokenProvider.java | 6 +++--- ...thTokenProviderRenewing.java => RenewingProvider.java} | 8 ++++---- 3 files changed, 11 insertions(+), 11 deletions(-) rename src/main/java/tech/redroma/yelp/oauth/{OAuthTokenProviderBasic.java => BasicProvider.java} (87%) rename src/main/java/tech/redroma/yelp/oauth/{OAuthTokenProviderRenewing.java => RenewingProvider.java} (95%) diff --git a/src/main/java/tech/redroma/yelp/oauth/OAuthTokenProviderBasic.java b/src/main/java/tech/redroma/yelp/oauth/BasicProvider.java similarity index 87% rename from src/main/java/tech/redroma/yelp/oauth/OAuthTokenProviderBasic.java rename to src/main/java/tech/redroma/yelp/oauth/BasicProvider.java index c2bcf83..edd23cd 100644 --- a/src/main/java/tech/redroma/yelp/oauth/OAuthTokenProviderBasic.java +++ b/src/main/java/tech/redroma/yelp/oauth/BasicProvider.java @@ -32,13 +32,13 @@ * @author SirWellington */ @Internal -final class OAuthTokenProviderBasic implements OAuthTokenProvider +final class BasicProvider implements OAuthTokenProvider { - private final static Logger LOG = LoggerFactory.getLogger(OAuthTokenProviderBasic.class); + private final static Logger LOG = LoggerFactory.getLogger(BasicProvider.class); private final String token; - OAuthTokenProviderBasic(String token) + BasicProvider(String token) { checkThat(token).is(nonEmptyString()); @@ -74,7 +74,7 @@ public boolean equals(Object obj) { return false; } - final OAuthTokenProviderBasic other = (OAuthTokenProviderBasic) obj; + final BasicProvider other = (BasicProvider) obj; if (!Objects.equals(this.token, other.token)) { return false; diff --git a/src/main/java/tech/redroma/yelp/oauth/OAuthTokenProvider.java b/src/main/java/tech/redroma/yelp/oauth/OAuthTokenProvider.java index d28ddf1..26b7992 100644 --- a/src/main/java/tech/redroma/yelp/oauth/OAuthTokenProvider.java +++ b/src/main/java/tech/redroma/yelp/oauth/OAuthTokenProvider.java @@ -52,7 +52,7 @@ public static OAuthTokenProvider newBasicTokenProvider(@NonEmpty String token) t { checkThat(token).is(nonEmptyString()); - return new OAuthTokenProviderBasic(token); + return new BasicProvider(token); } /** @@ -122,7 +122,7 @@ static OAuthTokenProvider newRefreshingTokenProvider(@NonEmpty String clientId, .usingMessage("Alchemy HTTP cannot be null") .is(notNull()); - return new OAuthTokenProviderRenewing(http, authURL, clientId, clientSecret); + return new RenewingProvider(http, authURL, clientId, clientSecret); } /** @@ -139,7 +139,7 @@ static OAuthTokenProvider basicTokenProviderWithToken(@NonEmpty String token) th .usingMessage("token is required") .is(nonEmptyString()); - return new OAuthTokenProviderBasic(token); + return new BasicProvider(token); } diff --git a/src/main/java/tech/redroma/yelp/oauth/OAuthTokenProviderRenewing.java b/src/main/java/tech/redroma/yelp/oauth/RenewingProvider.java similarity index 95% rename from src/main/java/tech/redroma/yelp/oauth/OAuthTokenProviderRenewing.java rename to src/main/java/tech/redroma/yelp/oauth/RenewingProvider.java index e0ac262..d2bcdcb 100644 --- a/src/main/java/tech/redroma/yelp/oauth/OAuthTokenProviderRenewing.java +++ b/src/main/java/tech/redroma/yelp/oauth/RenewingProvider.java @@ -46,10 +46,10 @@ * @author SirWellington */ @Internal -final class OAuthTokenProviderRenewing implements OAuthTokenProvider +final class RenewingProvider implements OAuthTokenProvider { - private static final Logger LOG = LoggerFactory.getLogger(OAuthTokenProviderRenewing.class); + private static final Logger LOG = LoggerFactory.getLogger(RenewingProvider.class); private final AlchemyHttp http; private final URL authenticationURL; @@ -63,7 +63,7 @@ final class OAuthTokenProviderRenewing implements OAuthTokenProvider @ThreadUnsafe private String oauthToken; - OAuthTokenProviderRenewing(AlchemyHttp http, URL authenticationURL, String clientId, String clientSecret) + RenewingProvider(AlchemyHttp http, URL authenticationURL, String clientId, String clientSecret) { checkThat(http, authenticationURL) .are(notNull()); @@ -146,7 +146,7 @@ public boolean equals(Object obj) { return false; } - final OAuthTokenProviderRenewing other = (OAuthTokenProviderRenewing) obj; + final RenewingProvider other = (RenewingProvider) obj; if (!Objects.equals(this.clientId, other.clientId)) { return false; From 64615ba12de207b619a60045a68acbea24042e83 Mon Sep 17 00:00:00 2001 From: Wellington Moreno Date: Thu, 1 Dec 2016 19:45:45 -0800 Subject: [PATCH 057/168] Uopdates documentation --- .../redroma/yelp/oauth/OAuthTokenProvider.java | 15 +++------------ 1 file changed, 3 insertions(+), 12 deletions(-) diff --git a/src/main/java/tech/redroma/yelp/oauth/OAuthTokenProvider.java b/src/main/java/tech/redroma/yelp/oauth/OAuthTokenProvider.java index 26b7992..2720bb1 100644 --- a/src/main/java/tech/redroma/yelp/oauth/OAuthTokenProvider.java +++ b/src/main/java/tech/redroma/yelp/oauth/OAuthTokenProvider.java @@ -50,7 +50,9 @@ public interface OAuthTokenProvider */ public static OAuthTokenProvider newBasicTokenProvider(@NonEmpty String token) throws IllegalArgumentException { - checkThat(token).is(nonEmptyString()); + checkThat(token) + .usingMessage("token is required") + .is(nonEmptyString()); return new BasicProvider(token); } @@ -133,15 +135,4 @@ static OAuthTokenProvider newRefreshingTokenProvider(@NonEmpty String clientId, @Required String getToken(); - static OAuthTokenProvider basicTokenProviderWithToken(@NonEmpty String token) throws IllegalArgumentException - { - checkThat(token) - .usingMessage("token is required") - .is(nonEmptyString()); - - return new BasicProvider(token); - } - - - } From b1a67557b7e0f071e696620261cc1a5c54964f85 Mon Sep 17 00:00:00 2001 From: Wellington Moreno Date: Thu, 1 Dec 2016 19:49:30 -0800 Subject: [PATCH 058/168] Adds unit tests: BasicProviderTest --- .../redroma/yelp/oauth/BasicProviderTest.java | 102 ++++++++++++++++++ 1 file changed, 102 insertions(+) create mode 100644 src/test/java/tech/redroma/yelp/oauth/BasicProviderTest.java diff --git a/src/test/java/tech/redroma/yelp/oauth/BasicProviderTest.java b/src/test/java/tech/redroma/yelp/oauth/BasicProviderTest.java new file mode 100644 index 0000000..4dd5ca9 --- /dev/null +++ b/src/test/java/tech/redroma/yelp/oauth/BasicProviderTest.java @@ -0,0 +1,102 @@ +/* + * Copyright 2016 RedRoma, Inc.. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package tech.redroma.yelp.oauth; + +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import tech.sirwellington.alchemy.test.junit.runners.AlchemyTestRunner; +import tech.sirwellington.alchemy.test.junit.runners.DontRepeat; +import tech.sirwellington.alchemy.test.junit.runners.GenerateString; +import tech.sirwellington.alchemy.test.junit.runners.Repeat; + +import static org.hamcrest.Matchers.is; +import static org.hamcrest.Matchers.not; +import static org.junit.Assert.assertThat; +import static tech.sirwellington.alchemy.generator.AlchemyGenerator.one; +import static tech.sirwellington.alchemy.generator.StringGenerators.hexadecimalString; +import static tech.sirwellington.alchemy.test.junit.ThrowableAssertion.assertThrows; +import static tech.sirwellington.alchemy.test.junit.runners.GenerateString.Type.HEXADECIMAL; + +/** + * + * @author SirWellington + */ +@Repeat(50) +@RunWith(AlchemyTestRunner.class) +public class BasicProviderTest +{ + + private BasicProvider instance; + + @GenerateString(HEXADECIMAL) + private String token; + + @Before + public void setUp() throws Exception + { + instance = new BasicProvider(token); + } + + @DontRepeat + @Test + public void testConstructor() + { + assertThrows(() -> new BasicProvider(null)).isInstanceOf(IllegalArgumentException.class); + assertThrows(() -> new BasicProvider("")).isInstanceOf(IllegalArgumentException.class); + } + + @Test + public void testGetToken() + { + String result = instance.getToken(); + assertThat(result, is(token)); + } + + @Test + public void testHashCode() + { + BasicProvider other = new BasicProvider(token); + assertThat(other.hashCode(), is(instance.hashCode())); + } + + @Test + public void testHashCodeWhenDifferent() + { + String otherToken = one(hexadecimalString(10)); + BasicProvider other = new BasicProvider(otherToken); + + assertThat(other.hashCode(), not(instance.hashCode())); + } + + @Test + public void testEquals() + { + BasicProvider other = new BasicProvider(token); + assertThat(other, is(instance)); + } + + @Test + public void testEqualsWhenDifferent() + { + String otherToken = one(hexadecimalString(10)); + BasicProvider other = new BasicProvider(otherToken); + + assertThat(other, not(instance)); + } + +} From fc2b4ea5ca617270da83a4e5bba35525e98a1d37 Mon Sep 17 00:00:00 2001 From: Wellington Moreno Date: Thu, 1 Dec 2016 20:21:23 -0800 Subject: [PATCH 059/168] Adds unit test for RenewingProvider --- .../redroma/yelp/oauth/RenewingProvider.java | 2 +- .../yelp/oauth/RenewingProviderTest.java | 146 ++++++++++++++++++ 2 files changed, 147 insertions(+), 1 deletion(-) create mode 100644 src/test/java/tech/redroma/yelp/oauth/RenewingProviderTest.java diff --git a/src/main/java/tech/redroma/yelp/oauth/RenewingProvider.java b/src/main/java/tech/redroma/yelp/oauth/RenewingProvider.java index d2bcdcb..b858ee7 100644 --- a/src/main/java/tech/redroma/yelp/oauth/RenewingProvider.java +++ b/src/main/java/tech/redroma/yelp/oauth/RenewingProvider.java @@ -221,7 +221,7 @@ private String tryToGetTokenFrom(JsonObject response) return response.get(Keys.TOKEN).getAsString(); } - private static class Keys + static class Keys { static final String GRANT_TYPE = "grant_type"; static final String CLIENT_ID = "client_id"; diff --git a/src/test/java/tech/redroma/yelp/oauth/RenewingProviderTest.java b/src/test/java/tech/redroma/yelp/oauth/RenewingProviderTest.java new file mode 100644 index 0000000..1fe0744 --- /dev/null +++ b/src/test/java/tech/redroma/yelp/oauth/RenewingProviderTest.java @@ -0,0 +1,146 @@ +/* + * Copyright 2016 RedRoma, Inc.. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package tech.redroma.yelp.oauth; + +import com.google.gson.JsonObject; +import java.net.URL; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.Mock; +import tech.sirwellington.alchemy.http.AlchemyHttp; +import tech.sirwellington.alchemy.http.mock.AlchemyHttpMock; +import tech.sirwellington.alchemy.test.junit.runners.AlchemyTestRunner; +import tech.sirwellington.alchemy.test.junit.runners.GenerateInteger; +import tech.sirwellington.alchemy.test.junit.runners.GenerateString; +import tech.sirwellington.alchemy.test.junit.runners.GenerateURL; +import tech.sirwellington.alchemy.test.junit.runners.Repeat; + +import static org.hamcrest.Matchers.containsString; +import static org.hamcrest.Matchers.is; +import static org.hamcrest.Matchers.not; +import static org.junit.Assert.assertThat; +import static tech.sirwellington.alchemy.test.junit.runners.GenerateInteger.Type.RANGE; +import static tech.sirwellington.alchemy.test.junit.runners.GenerateString.Type.ALPHANUMERIC; +import static tech.sirwellington.alchemy.test.junit.runners.GenerateString.Type.HEXADECIMAL; + +/** + * + * @author SirWellington + */ +@Repeat(50) +@RunWith(AlchemyTestRunner.class) +public class RenewingProviderTest +{ + + @Mock + private AlchemyHttp http; + + @GenerateURL + private URL authURL; + + private RenewingProvider instance; + private RenewingProvider other; + + @GenerateString(HEXADECIMAL) + private String cliendId; + + @GenerateString(ALPHANUMERIC) + private String clientSecret; + + @GenerateString(HEXADECIMAL) + private String otherClientId; + + @GenerateString(ALPHANUMERIC) + private String otherClientSecret; + + private JsonObject authResponse; + + @GenerateInteger(value = RANGE, min = 1_000, max = 100_000) + private Integer expiration; + + @GenerateString + private String token; + + @Before + public void setUp() throws Exception + { + + setupData(); + setupMocks(); + + instance = new RenewingProvider(http, authURL, cliendId, clientSecret); + other = new RenewingProvider(http, authURL, otherClientId, otherClientSecret); + } + + private void setupData() throws Exception + { + authResponse = new JsonObject(); + authResponse.addProperty(RenewingProvider.Keys.CLIENT_ID, cliendId); + authResponse.addProperty(RenewingProvider.Keys.CLIENT_SECRET, clientSecret); + authResponse.addProperty(RenewingProvider.Keys.GRANT_TYPE, "client_credentials"); + } + + private void setupMocks() throws Exception + { + http = AlchemyHttpMock.begin() + .whenPost() + .noBody() + .at(authURL) + .thenReturnJson(authResponse) + .build(); + } + + @Test + public void testGetToken() + { + } + + @Test + public void testHashCode() + { + RenewingProvider copy = new RenewingProvider(http, authURL, cliendId, clientSecret); + assertThat(copy.hashCode(), is(instance.hashCode())); + } + + @Test + public void testHashCodeWhenDifferent() + { + assertThat(instance.hashCode(), not(other.hashCode())); + } + + @Test + public void testEquals() + { + RenewingProvider copy = new RenewingProvider(http, authURL, cliendId, clientSecret); + assertThat(copy, is(instance)); + } + + @Test + public void testEqualsWhenIsDifferent() + { + assertThat(instance, not(other)); + } + + @Test + public void testToString() + { + String string = instance.toString(); + assertThat(string, not(containsString(clientSecret))); + } + +} From c6817eedc99e7c827f38d76dfc37d4b01d3705ba Mon Sep 17 00:00:00 2001 From: Wellington Moreno Date: Thu, 1 Dec 2016 20:27:57 -0800 Subject: [PATCH 060/168] Adds additional Unit Tests --- .../tech/redroma/yelp/oauth/RenewingProviderTest.java | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/src/test/java/tech/redroma/yelp/oauth/RenewingProviderTest.java b/src/test/java/tech/redroma/yelp/oauth/RenewingProviderTest.java index 1fe0744..9657192 100644 --- a/src/test/java/tech/redroma/yelp/oauth/RenewingProviderTest.java +++ b/src/test/java/tech/redroma/yelp/oauth/RenewingProviderTest.java @@ -90,9 +90,8 @@ public void setUp() throws Exception private void setupData() throws Exception { authResponse = new JsonObject(); - authResponse.addProperty(RenewingProvider.Keys.CLIENT_ID, cliendId); - authResponse.addProperty(RenewingProvider.Keys.CLIENT_SECRET, clientSecret); - authResponse.addProperty(RenewingProvider.Keys.GRANT_TYPE, "client_credentials"); + authResponse.addProperty(RenewingProvider.Keys.EXPIRATION, expiration); + authResponse.addProperty(RenewingProvider.Keys.TOKEN, token); } private void setupMocks() throws Exception @@ -108,6 +107,10 @@ private void setupMocks() throws Exception @Test public void testGetToken() { + String result = instance.getToken(); + assertThat(result, is(token)); + + AlchemyHttpMock.verifyAllRequestsMade(http); } @Test From 701ae94da446ca42b7a4a59648053e1abd618626 Mon Sep 17 00:00:00 2001 From: Wellington Moreno Date: Thu, 1 Dec 2016 21:06:35 -0800 Subject: [PATCH 061/168] Updates alchemy-http-mock dependency to latest snapshot --- pom.xml | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) diff --git a/pom.xml b/pom.xml index db85c1d..42e7afa 100644 --- a/pom.xml +++ b/pom.xml @@ -153,7 +153,7 @@ tech.sirwellington.alchemy alchemy-http-mock - 1.0 + 1.1-SNAPSHOT test @@ -181,12 +181,6 @@ test - - ch.qos.logback - logback-classic - 1.1.7 - - org.slf4j From 96101b9e40f3296a9dc877f144edce0867728cb2 Mon Sep 17 00:00:00 2001 From: Wellington Moreno Date: Thu, 1 Dec 2016 21:28:33 -0800 Subject: [PATCH 062/168] Additional unit tests for the RenewingProvider --- pom.xml | 19 ++++++++++- .../redroma/yelp/oauth/RenewingProvider.java | 19 +++++++---- .../yelp/oauth/RenewingProviderTest.java | 33 +++++++++++++++++++ 3 files changed, 64 insertions(+), 7 deletions(-) diff --git a/pom.xml b/pom.xml index 42e7afa..384321b 100644 --- a/pom.xml +++ b/pom.xml @@ -217,7 +217,24 @@ guice 4.1.0 - + + org.seleniumhq.selenium + selenium-java + test + 2.44.0 + + + com.opera + operadriver + test + 1.5 + + + org.seleniumhq.selenium + selenium-remote-driver + + + diff --git a/src/main/java/tech/redroma/yelp/oauth/RenewingProvider.java b/src/main/java/tech/redroma/yelp/oauth/RenewingProvider.java index b858ee7..99f9459 100644 --- a/src/main/java/tech/redroma/yelp/oauth/RenewingProvider.java +++ b/src/main/java/tech/redroma/yelp/oauth/RenewingProvider.java @@ -25,6 +25,7 @@ import java.util.concurrent.TimeUnit; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import tech.redroma.yelp.exceptions.YelpAuthenticationException; import tech.redroma.yelp.exceptions.YelpBadArgumentException; import tech.redroma.yelp.exceptions.YelpOperationFailedException; import tech.sirwellington.alchemy.annotations.access.Internal; @@ -102,14 +103,20 @@ public String getToken() } catch (AlchemyHttpException ex) { - HttpResponse yelpResponse = ex.getResponse(); - int status = yelpResponse.statusCode(); - - if (status == BAD_REQUEST) + LOG.error("Failed to obtain a OAuth Token from Yelp", ex); + + if (ex.hasResponse()) { - throw new YelpBadArgumentException("Client ID or Secret are incorrect: " + yelpResponse.body()); + HttpResponse yelpResponse = ex.getResponse(); + + int status = yelpResponse.statusCode(); + + if (status == BAD_REQUEST) + { + throw new YelpAuthenticationException("Client ID or Secret are incorrect: " + yelpResponse.body()); + } } - + throw new YelpOperationFailedException("Failed to get access token.", ex); } diff --git a/src/test/java/tech/redroma/yelp/oauth/RenewingProviderTest.java b/src/test/java/tech/redroma/yelp/oauth/RenewingProviderTest.java index 9657192..0706532 100644 --- a/src/test/java/tech/redroma/yelp/oauth/RenewingProviderTest.java +++ b/src/test/java/tech/redroma/yelp/oauth/RenewingProviderTest.java @@ -22,9 +22,14 @@ import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.Mock; +import tech.redroma.yelp.exceptions.YelpAuthenticationException; import tech.sirwellington.alchemy.http.AlchemyHttp; +import tech.sirwellington.alchemy.http.HttpRequest; +import tech.sirwellington.alchemy.http.HttpResponse; +import tech.sirwellington.alchemy.http.exceptions.AlchemyHttpException; import tech.sirwellington.alchemy.http.mock.AlchemyHttpMock; import tech.sirwellington.alchemy.test.junit.runners.AlchemyTestRunner; +import tech.sirwellington.alchemy.test.junit.runners.DontRepeat; import tech.sirwellington.alchemy.test.junit.runners.GenerateInteger; import tech.sirwellington.alchemy.test.junit.runners.GenerateString; import tech.sirwellington.alchemy.test.junit.runners.GenerateURL; @@ -34,6 +39,9 @@ import static org.hamcrest.Matchers.is; import static org.hamcrest.Matchers.not; import static org.junit.Assert.assertThat; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; +import static tech.sirwellington.alchemy.test.junit.ThrowableAssertion.assertThrows; import static tech.sirwellington.alchemy.test.junit.runners.GenerateInteger.Type.RANGE; import static tech.sirwellington.alchemy.test.junit.runners.GenerateString.Type.ALPHANUMERIC; import static tech.sirwellington.alchemy.test.junit.runners.GenerateString.Type.HEXADECIMAL; @@ -113,6 +121,31 @@ public void testGetToken() AlchemyHttpMock.verifyAllRequestsMade(http); } + @DontRepeat + @Test + public void testGetTokenWhenAuthenticationError() + { + HttpRequest fakeRequest = mock(HttpRequest.class); + HttpResponse fakeResponse = mock(HttpResponse.class); + + when(fakeResponse.body()).thenReturn(authResponse); + when(fakeResponse.statusCode()).thenReturn(400); + + AlchemyHttpException ex = new AlchemyHttpException(fakeRequest, fakeResponse); + + http = AlchemyHttpMock.begin() + .whenPost() + .anyBody() + .at(authURL) + .thenThrow(ex) + .build(); + + instance = new RenewingProvider(http, authURL, cliendId, clientSecret); + + assertThrows(() -> instance.getToken()) + .isInstanceOf(YelpAuthenticationException.class); + } + @Test public void testHashCode() { From aaaa4d252e00127ff3d5455c7fe20ffdcc8e3356 Mon Sep 17 00:00:00 2001 From: Wellington Moreno Date: Thu, 1 Dec 2016 21:28:58 -0800 Subject: [PATCH 063/168] Removes selenium --- pom.xml | 19 +------------------ 1 file changed, 1 insertion(+), 18 deletions(-) diff --git a/pom.xml b/pom.xml index 384321b..f9180dd 100644 --- a/pom.xml +++ b/pom.xml @@ -217,24 +217,7 @@ guice 4.1.0 - - org.seleniumhq.selenium - selenium-java - test - 2.44.0 - - - com.opera - operadriver - test - 1.5 - - - org.seleniumhq.selenium - selenium-remote-driver - - - + From 9a8dfb4dac5b0c04552b31d6e4f964915c32e86d Mon Sep 17 00:00:00 2001 From: Wellington Moreno Date: Thu, 1 Dec 2016 21:31:54 -0800 Subject: [PATCH 064/168] Adds unit tests: OAuthTokenProviderTest --- .../yelp/oauth/OAuthTokenProviderTest.java | 107 ++++++++++++++++++ 1 file changed, 107 insertions(+) create mode 100644 src/test/java/tech/redroma/yelp/oauth/OAuthTokenProviderTest.java diff --git a/src/test/java/tech/redroma/yelp/oauth/OAuthTokenProviderTest.java b/src/test/java/tech/redroma/yelp/oauth/OAuthTokenProviderTest.java new file mode 100644 index 0000000..4cdea94 --- /dev/null +++ b/src/test/java/tech/redroma/yelp/oauth/OAuthTokenProviderTest.java @@ -0,0 +1,107 @@ +/* + * Copyright 2016 RedRoma, Inc.. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package tech.redroma.yelp.oauth; + +import java.net.URL; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.Mock; +import tech.sirwellington.alchemy.http.AlchemyHttp; +import tech.sirwellington.alchemy.test.junit.runners.AlchemyTestRunner; +import tech.sirwellington.alchemy.test.junit.runners.GenerateString; +import tech.sirwellington.alchemy.test.junit.runners.GenerateURL; +import tech.sirwellington.alchemy.test.junit.runners.Repeat; + +import static org.hamcrest.Matchers.is; +import static org.hamcrest.Matchers.notNullValue; +import static org.junit.Assert.assertThat; +import static tech.sirwellington.alchemy.test.junit.runners.GenerateString.Type.ALPHANUMERIC; +import static tech.sirwellington.alchemy.test.junit.runners.GenerateString.Type.HEXADECIMAL; + +/** + * + * @author SirWellington + */ +@Repeat(10) +@RunWith(AlchemyTestRunner.class) +public class OAuthTokenProviderTest +{ + + @GenerateString(HEXADECIMAL) + private String token; + + @GenerateString(HEXADECIMAL) + private String cliendId; + + @GenerateString(ALPHANUMERIC) + private String cliendSecret; + + @Mock + private AlchemyHttp http; + + @GenerateURL + private URL authURL; + + @Before + public void setUp() throws Exception + { + + setupData(); + setupMocks(); + } + + private void setupData() throws Exception + { + + } + + private void setupMocks() throws Exception + { + + } + + @Test + public void testNewBasicTokenProvider() + { + OAuthTokenProvider result = OAuthTokenProvider.newBasicTokenProvider(token); + assertThat(result, notNullValue()); + assertThat(result.getToken(), is(token)); + } + + @Test + public void testNewRefreshingTokenProvider_String_String() + { + OAuthTokenProvider result = OAuthTokenProvider.newRefeshingTokenProvider(cliendId, cliendSecret, authURL); + assertThat(result, notNullValue()); + } + + @Test + public void testNewRefeshingTokenProvider() + { + OAuthTokenProvider result = OAuthTokenProvider.newRefreshingTokenProvider(cliendId, cliendSecret); + assertThat(result, notNullValue()); + } + + @Test + public void testNewRefreshingTokenProvider_4args() + { + OAuthTokenProvider result = OAuthTokenProvider.newRefreshingTokenProvider(cliendId, cliendSecret, authURL, http); + assertThat(result, notNullValue()); + } + +} From 8710f8eb0795bc86ea1fe12cee76a0b9c7d2e0a5 Mon Sep 17 00:00:00 2001 From: Wellington Moreno Date: Thu, 1 Dec 2016 21:33:59 -0800 Subject: [PATCH 065/168] Create .travis.yml --- .travis.yml | 9 +++++++++ 1 file changed, 9 insertions(+) create mode 100644 .travis.yml diff --git a/.travis.yml b/.travis.yml new file mode 100644 index 0000000..8f2c137 --- /dev/null +++ b/.travis.yml @@ -0,0 +1,9 @@ +sudo: required +language: java +jdk: +- oraclejdk8 + +before_install: + - sudo apt-get update -qq + - sudo apt-get update && sudo apt-get install oracle-java8-installer + - java -version From 2351ebfbce1672583a570e889e33b80e74e378b4 Mon Sep 17 00:00:00 2001 From: Wellington Moreno Date: Thu, 1 Dec 2016 21:37:58 -0800 Subject: [PATCH 066/168] Update README.md --- README.md | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 7a4f5e5..a3ad79d 100644 --- a/README.md +++ b/README.md @@ -1,2 +1,7 @@ -# YelpJavaClient -Provides a convenient Java Client Library for interfacing with Yelp +YelpJavaClient +=================== +[![Build Status](https://travis-ci.org/RedRoma/YelpJavaClient.svg?branch=develop)](https://travis-ci.org/RedRoma/YelpJavaClient) + +Provides a convenient Java Client Library for interfacing with Yelp. + +> Documentation coming soon... From e0ba8f9790078f2162c310c0ac4867223046f536 Mon Sep 17 00:00:00 2001 From: Wellington Moreno Date: Thu, 1 Dec 2016 21:42:27 -0800 Subject: [PATCH 067/168] Adds documentation to the YelpAPI --- src/main/java/tech/redroma/yelp/YelpAPI.java | 35 +++++++++++++++++++- 1 file changed, 34 insertions(+), 1 deletion(-) diff --git a/src/main/java/tech/redroma/yelp/YelpAPI.java b/src/main/java/tech/redroma/yelp/YelpAPI.java index a735288..9f5e5b9 100644 --- a/src/main/java/tech/redroma/yelp/YelpAPI.java +++ b/src/main/java/tech/redroma/yelp/YelpAPI.java @@ -35,7 +35,16 @@ */ public interface YelpAPI { - + /** + * Returns the detail information of a business. Normally, you'll get the business id from the + * {@linkplain #searchForBusinesses(tech.redroma.yelp.YelpSearchRequest) search API}. To get + * review information for a business, refer to ... + * + * @param business The business to get more details of. + * @return + * @throws YelpExcetion + * @see #getBusinessDetails(java.lang.String) + */ default YelpBusinessDetails getBusinessDetails(@Required YelpBusiness business) throws YelpExcetion { checkThat(business) @@ -50,9 +59,33 @@ default YelpBusinessDetails getBusinessDetails(@Required YelpBusiness business) return getBusinessDetails(business.id); } + /** + * Returns the detail information of a business. Normally, you'll get the business id from + * the {@linkplain #searchForBusinesses(tech.redroma.yelp.YelpSearchRequest) search API}. + * TO get review information for a business, refer to ... + * + * @param businessId + * @return + * @throws YelpExcetion + * @see #getBusinessDetails(java.lang.String) + * @see #searchForBusinesses(tech.redroma.yelp.YelpSearchRequest) + */ @Required YelpBusinessDetails getBusinessDetails(@NonEmpty String businessId) throws YelpExcetion; + /** + * Returns up to 1,000 businesses based on the provided search criteria. It has some basic information about the businesses + * that match the search criteria. To get details information, see {@link #getBusinessDetails(java.lang.String) }. + *

+ * To create a search request, see {@link YelpSearchRequest#newBuilder() }. + * + * @param request + * @return + * @throws YelpExcetion + * @see #getBusinessDetails(java.lang.String) + * @see #getBusinessDetails(tech.redroma.yelp.YelpBusiness) + * @see YelpSearchRequest + */ List searchForBusinesses(@Required YelpSearchRequest request) throws YelpExcetion; static YelpAPI newInstance() From 794a9bab11bde4d8922800e754c7cffe31f6d901 Mon Sep 17 00:00:00 2001 From: Wellington Moreno Date: Thu, 1 Dec 2016 22:21:13 -0800 Subject: [PATCH 068/168] Updates alchemy-test dependency --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index f9180dd..1f4375e 100644 --- a/pom.xml +++ b/pom.xml @@ -160,7 +160,7 @@ tech.sirwellington.alchemy alchemy-test - 1.5 + 1.6-SNAPSHOT test From 5c580f8c3b97623ce59f4e88316d1b8e05170a56 Mon Sep 17 00:00:00 2001 From: Wellington Moreno Date: Thu, 1 Dec 2016 22:31:13 -0800 Subject: [PATCH 069/168] Enriches YelpSearchRequest with methods that check its state + Adds missing unit tests --- .../tech/redroma/yelp/YelpSearchRequest.java | 74 ++- .../redroma/yelp/YelpSearchRequestTest.java | 536 ++++++++++++++++++ 2 files changed, 609 insertions(+), 1 deletion(-) create mode 100644 src/test/java/tech/redroma/yelp/YelpSearchRequestTest.java diff --git a/src/main/java/tech/redroma/yelp/YelpSearchRequest.java b/src/main/java/tech/redroma/yelp/YelpSearchRequest.java index db3847c..b067e19 100644 --- a/src/main/java/tech/redroma/yelp/YelpSearchRequest.java +++ b/src/main/java/tech/redroma/yelp/YelpSearchRequest.java @@ -31,6 +31,7 @@ import tech.sirwellington.alchemy.annotations.designs.patterns.BuilderPattern; import tech.sirwellington.alchemy.annotations.objects.Pojo; +import static com.google.common.base.Strings.isNullOrEmpty; import static java.util.stream.Collectors.joining; import static tech.sirwellington.alchemy.annotations.designs.patterns.BuilderPattern.Role.BUILDER; import static tech.sirwellington.alchemy.annotations.designs.patterns.BuilderPattern.Role.PRODUCT; @@ -104,6 +105,7 @@ public static YelpSearchRequest.Builder newBuilder() @Optional private final Boolean openNow; + @Optional private final Integer openAt; @@ -140,7 +142,77 @@ public static YelpSearchRequest.Builder newBuilder() this.openAt = openAt; this.attributes = attributes; } - + + public boolean hasSearchTerm() + { + return !isNullOrEmpty(searchTerm); + } + + public boolean hasLocation() + { + return !isNullOrEmpty(location); + } + + public boolean hasLatitude() + { + return latitude != null; + } + + public boolean hasLongitude() + { + return longitude != null; + } + + public boolean hasRadius() + { + return radius != null; + } + + public boolean hasCategories() + { + return !isNullOrEmpty(categories); + } + + public boolean hasLocale() + { + return !isNullOrEmpty(locale); + } + + public boolean hasLimit() + { + return limit != null && limit > 0; + } + + public boolean hasOffset() + { + return offset != null && offset > 0; + } + + public boolean hasSortBy() + { + return !isNullOrEmpty(sortBy); + } + + public boolean hasPrices() + { + return !isNullOrEmpty(prices); + } + + public boolean hasIsOpenNow() + { + return openNow != null; + } + + public boolean hasOpenAt() + { + return openAt != null; + } + + public boolean hasAttributes() + { + return !isNullOrEmpty(attributes); + } + public String getSearchTerm() { return searchTerm; diff --git a/src/test/java/tech/redroma/yelp/YelpSearchRequestTest.java b/src/test/java/tech/redroma/yelp/YelpSearchRequestTest.java new file mode 100644 index 0000000..ced167e --- /dev/null +++ b/src/test/java/tech/redroma/yelp/YelpSearchRequestTest.java @@ -0,0 +1,536 @@ +/* + * Copyright 2016 RedRoma, Inc.. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package tech.redroma.yelp; + +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import tech.sirwellington.alchemy.test.junit.runners.AlchemyTestRunner; +import tech.sirwellington.alchemy.test.junit.runners.GenerateBoolean; +import tech.sirwellington.alchemy.test.junit.runners.GenerateDouble; +import tech.sirwellington.alchemy.test.junit.runners.GenerateInteger; +import tech.sirwellington.alchemy.test.junit.runners.GenerateString; +import tech.sirwellington.alchemy.test.junit.runners.Repeat; + +import static org.hamcrest.Matchers.is; +import static org.hamcrest.Matchers.notNullValue; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertThat; +import static org.junit.Assert.assertTrue; + +/** + * + * @author SirWellington + */ +@Repeat(10) +@RunWith(AlchemyTestRunner.class) +public class YelpSearchRequestTest +{ + + @GenerateString + private String searchTerm; + + @GenerateString + private String location; + + @GenerateDouble + private Double latitude; + + @GenerateDouble + private Double longitude; + + @GenerateInteger + private Integer radius; + + @GenerateString + private String categories; + + @GenerateString + private String locale; + + @GenerateInteger + private Integer limit; + + @GenerateInteger + private Integer offset; + + @GenerateString + private String sortBy; + + @GenerateString + private String prices; + + @GenerateBoolean + private Boolean openNow; + + @GenerateInteger + private Integer openAt; + + @GenerateString + private String attributes; + + private YelpSearchRequest instance; + + @Before + public void setUp() throws Exception + { + + setupData(); + setupMocks(); + + instance = new YelpSearchRequest(searchTerm, + location, + latitude, + longitude, + radius, + categories, + locale, + limit, + offset, + sortBy, + prices, + openNow, + openAt, + attributes); + } + + private void setupData() throws Exception + { + + } + + private void setupMocks() throws Exception + { + + } + + @Test + public void testNewBuilder() + { + YelpSearchRequest.Builder builder = YelpSearchRequest.newBuilder(); + assertThat(builder, notNullValue()); + } + + @Test + public void testHasSearchTerm() + { + assertTrue(instance.hasSearchTerm()); + + instance = new YelpSearchRequest(null, + location, + latitude, + longitude, + radius, + categories, + locale, + limit, + offset, + sortBy, + prices, + openNow, + openAt, + attributes); + + assertFalse(instance.hasSearchTerm()); + } + + @Test + public void testHasLocation() + { + assertTrue(instance.hasLocation()); + + instance = new YelpSearchRequest(searchTerm, + null, + latitude, + longitude, + radius, + categories, + locale, + limit, + offset, + sortBy, + prices, + openNow, + openAt, + attributes); + + assertFalse(instance.hasLocation()); + } + + @Test + public void testHasLatitude() + { + assertTrue(instance.hasLatitude()); + + instance = new YelpSearchRequest(searchTerm, + location, + null, + longitude, + radius, + categories, + locale, + limit, + offset, + sortBy, + prices, + openNow, + openAt, + attributes); + + assertFalse(instance.hasLatitude()); + } + + @Test + public void testHasLongitude() + { + assertTrue(instance.hasLongitude()); + + instance = new YelpSearchRequest(searchTerm, + location, + latitude, + null, + radius, + categories, + locale, + limit, + offset, + sortBy, + prices, + openNow, + openAt, + attributes); + + assertFalse(instance.hasLongitude()); + } + + @Test + public void testHasRadius() + { + assertTrue(instance.hasRadius()); + + instance = new YelpSearchRequest(searchTerm, + location, + latitude, + longitude, + null, + categories, + locale, + limit, + offset, + sortBy, + prices, + openNow, + openAt, + attributes); + + assertFalse(instance.hasRadius()); + + } + + @Test + public void testHasCategories() + { + assertTrue(instance.hasCategories()); + + instance = new YelpSearchRequest(searchTerm, + location, + latitude, + longitude, + radius, + null, + locale, + limit, + offset, + sortBy, + prices, + openNow, + openAt, + attributes); + + assertFalse(instance.hasCategories()); + } + + @Test + public void testHasLocale() + { + assertTrue(instance.hasLocale()); + + instance = new YelpSearchRequest(searchTerm, + location, + latitude, + longitude, + radius, + categories, + null, + limit, + offset, + sortBy, + prices, + openNow, + openAt, + attributes); + + assertFalse(instance.hasLocale()); + } + + @Test + public void testHasLimit() + { + + assertTrue(instance.hasLimit()); + + instance = new YelpSearchRequest(searchTerm, + location, + latitude, + longitude, + radius, + categories, + locale, + null, + offset, + sortBy, + prices, + openNow, + openAt, + attributes); + + assertFalse(instance.hasLimit()); + } + + @Test + public void testHasOffset() + { + + instance = new YelpSearchRequest(searchTerm, + location, + latitude, + longitude, + radius, + categories, + locale, + limit, + null, + sortBy, + prices, + openNow, + openAt, + attributes); + } + + @Test + public void testHasSortBy() + { + assertTrue(instance.hasSortBy()); + + instance = new YelpSearchRequest(searchTerm, + location, + latitude, + longitude, + radius, + categories, + locale, + limit, + offset, + null, + prices, + openNow, + openAt, + attributes); + + assertFalse(instance.hasSortBy()); + } + + @Test + public void testHasPrices() + { + assertTrue(instance.hasPrices()); + + instance = new YelpSearchRequest(searchTerm, + location, + latitude, + longitude, + radius, + categories, + locale, + limit, + offset, + sortBy, + null, + openNow, + openAt, + attributes); + + assertFalse(instance.hasPrices()); + } + + + @Test + public void testHasIsOpenNow() + { + assertTrue(instance.hasIsOpenNow()); + + instance = new YelpSearchRequest(searchTerm, + location, + latitude, + longitude, + radius, + categories, + locale, + limit, + offset, + sortBy, + prices, + null, + openAt, + attributes); + + assertFalse(instance.hasIsOpenNow()); + } + + @Test + public void testHasOpenAt() + { + assertTrue(instance.hasOpenAt()); + + instance = new YelpSearchRequest(searchTerm, + location, + latitude, + longitude, + radius, + categories, + locale, + limit, + offset, + sortBy, + prices, + openNow, + null, + attributes); + + assertFalse(instance.hasOpenAt()); + } + + @Test + public void testHasAttributes() + { + + assertTrue(instance.hasAttributes()); + + instance = new YelpSearchRequest(searchTerm, + location, + latitude, + longitude, + radius, + categories, + locale, + limit, + offset, + sortBy, + prices, + openNow, + openAt, + null); + + assertFalse(instance.hasAttributes()); + } + + @Test + public void testGetSearchTerm() + { + assertThat(instance.getSearchTerm(), is(searchTerm)); + } + + @Test + public void testGetLocation() + { + assertThat(instance.getLocation(), is(location)); + } + + @Test + public void testGetLatitude() + { + assertThat(instance.getLatitude(), is(latitude)); + } + + @Test + public void testGetLongitude() + { + assertThat(instance.getLongitude(), is(longitude)); + } + + @Test + public void testGetRadius() + { + assertThat(instance.getRadius(), is(radius)); + } + + @Test + public void testGetCategories() + { + assertThat(instance.getCategories(), is(categories)); + } + + @Test + public void testGetLocale() + { + assertThat(instance.getLocale(), is(locale)); + } + + @Test + public void testGetLimit() + { + assertThat(instance.getLimit(), is(limit)); + } + + @Test + public void testGetOffset() + { + assertThat(instance.getOffset(), is(offset)); + } + + @Test + public void testGetSortBy() + { + assertThat(instance.getSortBy(), is(sortBy)); + } + + @Test + public void testGetPrices() + { + assertThat(instance.getPrices(), is(prices)); + } + + @Test + public void testGetOpenNow() + { + assertThat(instance.getOpenNow(), is(openNow)); + } + + @Test + public void testGetOpenAt() + { + assertThat(instance.getOpenAt(), is(openAt)); + } + + @Test + public void testGetAttributes() + { + assertThat(instance.getAttributes(), is(attributes)); + } + + +} From 062b9589a532f5c4430cdd9f901495a4f43bccd4 Mon Sep 17 00:00:00 2001 From: Wellington Moreno Date: Thu, 1 Dec 2016 22:38:25 -0800 Subject: [PATCH 070/168] Adds additional check to YelpSearchRequest.Builder --- src/main/java/tech/redroma/yelp/YelpSearchRequest.java | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/src/main/java/tech/redroma/yelp/YelpSearchRequest.java b/src/main/java/tech/redroma/yelp/YelpSearchRequest.java index b067e19..a1f8402 100644 --- a/src/main/java/tech/redroma/yelp/YelpSearchRequest.java +++ b/src/main/java/tech/redroma/yelp/YelpSearchRequest.java @@ -874,6 +874,7 @@ public Builder withAttributes(@NonEmpty List attributes) throws Illeg public YelpSearchRequest build() throws YelpBadArgumentException { checkThatLocationIsSet(); + checkThatOnlyOneOfOpenNowOrOpenAtIsSet(); return new YelpSearchRequest(searchTerm, location, @@ -905,6 +906,14 @@ private void checkThatLocationIsSet() throw new YelpBadArgumentException("Either a location or a coordinate must be set."); } + + private void checkThatOnlyOneOfOpenNowOrOpenAtIsSet() + { + if (openAt != null && isOpenNow != null) + { + throw new IllegalArgumentException("You can use 'open_now' or 'open_at', but not both."); + } + } } } From 7768c112cae9e87dfd06b36be56e28c562d25681 Mon Sep 17 00:00:00 2001 From: Wellington Moreno Date: Thu, 1 Dec 2016 23:09:32 -0800 Subject: [PATCH 071/168] Adds YelpAPIImpl to implement the Java YelpAPI --- .../java/tech/redroma/yelp/YelpAPIImpl.java | 391 ++++++++++++++++++ 1 file changed, 391 insertions(+) create mode 100644 src/main/java/tech/redroma/yelp/YelpAPIImpl.java diff --git a/src/main/java/tech/redroma/yelp/YelpAPIImpl.java b/src/main/java/tech/redroma/yelp/YelpAPIImpl.java new file mode 100644 index 0000000..93e2acc --- /dev/null +++ b/src/main/java/tech/redroma/yelp/YelpAPIImpl.java @@ -0,0 +1,391 @@ +/* + * Copyright 2016 RedRoma, Inc.. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package tech.redroma.yelp; + +import java.net.MalformedURLException; +import java.util.List; +import java.util.Objects; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import tech.redroma.yelp.exceptions.YelpAuthenticationException; +import tech.redroma.yelp.exceptions.YelpBadArgumentException; +import tech.redroma.yelp.exceptions.YelpExcetion; +import tech.redroma.yelp.exceptions.YelpOperationFailedException; +import tech.redroma.yelp.oauth.OAuthTokenProvider; +import tech.sirwellington.alchemy.annotations.access.Internal; +import tech.sirwellington.alchemy.annotations.access.NonInstantiable; +import tech.sirwellington.alchemy.http.AlchemyHttp; +import tech.sirwellington.alchemy.http.AlchemyRequest; +import tech.sirwellington.alchemy.http.HttpResponse; +import tech.sirwellington.alchemy.http.exceptions.AlchemyHttpException; + +import static java.lang.String.format; +import static tech.sirwellington.alchemy.arguments.Arguments.checkThat; +import static tech.sirwellington.alchemy.arguments.assertions.Assertions.notNull; +import static tech.sirwellington.alchemy.arguments.assertions.NetworkAssertions.validURL; +import static tech.sirwellington.alchemy.arguments.assertions.StringAssertions.nonEmptyString; + +/** + * This internal class is reposisble for implementing the business logic necessary for making Yelp API Queries. It implements all + * of the operations defined in the {@link YelpAPI}. + * + * @author SirWellington + */ +@Internal +final class YelpAPIImpl implements YelpAPI +{ + + private final static Logger LOG = LoggerFactory.getLogger(YelpAPIImpl.class); + + private final AlchemyHttp http; + private final OAuthTokenProvider tokenProvider; + private final String baseURL; + + YelpAPIImpl(AlchemyHttp http, OAuthTokenProvider tokenProvider, String baseURL) + { + checkThat(http, tokenProvider) + .are(notNull()); + + checkThat(baseURL) + .is(nonEmptyString()) + .is(validURL()); + + this.http = http; + this.tokenProvider = tokenProvider; + this.baseURL = baseURL; + } + + @Override + public YelpBusinessDetails getBusinessDetails(String businessId) throws YelpExcetion + { + checkThat(businessId) + .throwing(YelpBadArgumentException.class) + .usingMessage("Business ID cannot be empty") + .is(nonEmptyString()); + + String url = createDetailUrlFor(businessId); + + YelpBusinessDetails details = tryToGetDetailsAt(url); + + return details; + + } + + @Override + public List searchForBusinesses(YelpSearchRequest request) throws YelpExcetion + { + String token = tokenProvider.getToken(); + + checkThat(token) + .throwing(YelpAuthenticationException.class) + .usingMessage("No token available to make API call") + .is(nonEmptyString()); + + AlchemyRequest.Step3 httpRequest = http.go() + .get() + .usingHeader(HeaderParameters.AUTHORIZATION, HeaderParameters.BEARER + " " + token); + + httpRequest = requestFilledWithParametersFrom(httpRequest, request); + + String url = baseURL + URLS.BUSINESS_SEARCH; + + try + { + HttpResponse response = httpRequest.at(url); + + List results = response.bodyAsArrayOf(YelpBusiness.class); + + LOG.debug("Found {} businesses matching search request [{}]", results.size(), request); + return results; + } + catch (AlchemyHttpException ex) + { + if (isBadAuth(ex)) + { + throw new YelpAuthenticationException(ex); + } + + if (isBadRequest(ex)) + { + throw new YelpBadArgumentException(ex); + } + + throw new YelpOperationFailedException(format("Failed to make serach request at %s with [%s]", url, request), ex); + } + catch (Exception ex) + { + LOG.error("Failed to make search request at {}", url, ex); + throw new YelpOperationFailedException("could not search Yelp at: " + url, ex); + } + } + + private String createDetailUrlFor(String businessId) + { + return format("%s%s/%s", baseURL, URLS.BUSINESSES, businessId); + } + + private YelpBusinessDetails tryToGetDetailsAt(String url) + { + checkThat(url) + .is(validURL()); + + String token = tokenProvider.getToken(); + + checkThat(token) + .throwing(YelpAuthenticationException.class) + .usingMessage("missing token") + .is(nonEmptyString()); + + try + { + return http.go() + .get() + .usingHeader(HeaderParameters.AUTHORIZATION, HeaderParameters.BEARER + " " + token) + .expecting(YelpBusinessDetails.class) + .at(url); + } + catch (MalformedURLException ex) + { + LOG.error("Received strange malformed URL for {}", url, ex); + throw new YelpBadArgumentException("Business ID led to invalid URL: " + url); + } + catch (AlchemyHttpException ex) + { + LOG.warn("Operation to make request at {} failed", url, ex); + + if (isBadAuth(ex)) + { + throw new YelpAuthenticationException(ex); + } + else if (isBadRequest(ex)) + { + throw new YelpBadArgumentException(ex); + } + else + { + throw new YelpOperationFailedException(ex); + } + } + catch (Exception ex) + { + LOG.error("Failed to make request at {}", url, ex); + throw new YelpOperationFailedException(ex); + } + } + + private AlchemyRequest.Step3 requestFilledWithParametersFrom(AlchemyRequest.Step3 httpRequest, YelpSearchRequest request) + { + + if (request.hasAttributes()) + { + httpRequest = httpRequest.usingQueryParam(SearchParameters.ATTRIBUTES, request.getAttributes()); + } + + if (request.hasCategories()) + { + httpRequest = httpRequest.usingQueryParam(SearchParameters.CATEGORIES, request.getCategories()); + } + + if (request.hasIsOpenNow()) + { + httpRequest = httpRequest.usingQueryParam(SearchParameters.OPEN_NOW, request.getOpenNow()); + } + + if (request.hasLatitude()) + { + httpRequest = httpRequest.usingQueryParam(SearchParameters.LATITUDE, request.getLatitude()); + } + + if (request.hasLimit()) + { + httpRequest = httpRequest.usingQueryParam(SearchParameters.LIMIT, request.getLimit()); + } + + if (request.hasLocale()) + { + httpRequest = httpRequest.usingHeader(SearchParameters.LOCALE, request.getLocale()); + } + + if (request.hasLocation()) + { + httpRequest = httpRequest.usingQueryParam(SearchParameters.LOCATION, request.getLocation()); + } + + if (request.hasLongitude()) + { + httpRequest = httpRequest.usingQueryParam(SearchParameters.LONGITUDE, request.getLongitude()); + } + + if (request.hasOffset()) + { + httpRequest = httpRequest.usingQueryParam(SearchParameters.OFFSET, request.getOffset()); + } + + if (request.hasOpenAt()) + { + httpRequest = httpRequest.usingQueryParam(SearchParameters.OPEN_AT, request.getOpenAt()); + } + + if (request.hasPrices()) + { + httpRequest = httpRequest.usingQueryParam(SearchParameters.PRICE, request.getPrices()); + } + + if (request.hasRadius()) + { + httpRequest = httpRequest.usingQueryParam(SearchParameters.RADIUS, request.getRadius()); + } + + if (request.hasSearchTerm()) + { + httpRequest = httpRequest.usingQueryParam(SearchParameters.SEARCH_TERM, request.getSearchTerm()); + } + + if (request.hasSortBy()) + { + httpRequest = httpRequest.usingQueryParam(SearchParameters.SORT_BY, request.getSortBy()); + } + + return httpRequest; + } + + private boolean isBadRequest(AlchemyHttpException ex) + { + int expectedStatus = 400; + + if (ex.hasResponse()) + { + return ex.getResponse().statusCode() == expectedStatus; + } + + return false; + } + + private boolean isBadAuth(AlchemyHttpException ex) + { + int expectedStatus = 401; + + if (ex.hasResponse()) + { + return ex.getResponse().statusCode() == expectedStatus; + } + + return false; + } + + @Override + public int hashCode() + { + int hash = 5; + hash = 29 * hash + Objects.hashCode(this.http); + hash = 29 * hash + Objects.hashCode(this.tokenProvider); + hash = 29 * hash + Objects.hashCode(this.baseURL); + return hash; + } + + @Override + public boolean equals(Object obj) + { + if (this == obj) + { + return true; + } + if (obj == null) + { + return false; + } + if (getClass() != obj.getClass()) + { + return false; + } + final YelpAPIImpl other = (YelpAPIImpl) obj; + if (!Objects.equals(this.baseURL, other.baseURL)) + { + return false; + } + if (!Objects.equals(this.http, other.http)) + { + return false; + } + if (!Objects.equals(this.tokenProvider, other.tokenProvider)) + { + return false; + } + return true; + } + + @Override + public String toString() + { + return "YelpAPIImpl{" + "http=" + http + ", tokenProvider=" + tokenProvider + ", baseURL=" + baseURL + '}'; + } + + /** + * URLs used to construct queries. + */ + @NonInstantiable + @Internal + static class URLS + { + + /** + * https://api.yelp.com/v3/businesses + */ + static final String BUSINESSES = "/businesses"; + + /** + * https://api.yelp.com/v3/businesses/search + */ + static final String BUSINESS_SEARCH = "/businesses/search"; + + /** + * https://api.yelp.com/v3/businesses/{id}/reviews + */ + static final String REVIEWS = "/reviews"; + } + + @NonInstantiable + @Internal + static class SearchParameters + { + + static final String SEARCH_TERM = "term"; + static final String LOCATION = "location"; + static final String LATITUDE = "latitude"; + static final String LONGITUDE = "longitude"; + static final String RADIUS = "radius"; + static final String CATEGORIES = "categories"; + static final String LOCALE = "locale"; + static final String LIMIT = "limit"; + static final String OFFSET = "offset"; + static final String SORT_BY = "sort_by"; + static final String PRICE = "price"; + static final String OPEN_NOW = "open_now"; + static final String OPEN_AT = "open_at"; + static final String ATTRIBUTES = "attributes"; + } + + @NonInstantiable + @Internal + static class HeaderParameters + { + + static final String AUTHORIZATION = "Authorization"; + static final String BEARER = "Bearer"; + } + +} From bd9838218d03f806d675f6104ebcebd8788dd7a4 Mon Sep 17 00:00:00 2001 From: Wellington Moreno Date: Thu, 1 Dec 2016 23:09:42 -0800 Subject: [PATCH 072/168] Updates documentation --- src/main/java/tech/redroma/yelp/YelpAPIImpl.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/tech/redroma/yelp/YelpAPIImpl.java b/src/main/java/tech/redroma/yelp/YelpAPIImpl.java index 93e2acc..41a34d2 100644 --- a/src/main/java/tech/redroma/yelp/YelpAPIImpl.java +++ b/src/main/java/tech/redroma/yelp/YelpAPIImpl.java @@ -40,7 +40,7 @@ import static tech.sirwellington.alchemy.arguments.assertions.StringAssertions.nonEmptyString; /** - * This internal class is reposisble for implementing the business logic necessary for making Yelp API Queries. It implements all + * This internal class is responsible for implementing the business logic necessary for making Yelp API Queries. It implements all * of the operations defined in the {@link YelpAPI}. * * @author SirWellington From edf84244318e1b1e5bdd8ae10aaed88b77b00093 Mon Sep 17 00:00:00 2001 From: Wellington Moreno Date: Thu, 1 Dec 2016 23:17:10 -0800 Subject: [PATCH 073/168] Adds additional constructors to Coordinate --- .../java/tech/redroma/yelp/Coordinate.java | 22 +++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/src/main/java/tech/redroma/yelp/Coordinate.java b/src/main/java/tech/redroma/yelp/Coordinate.java index 637a7d1..4804275 100644 --- a/src/main/java/tech/redroma/yelp/Coordinate.java +++ b/src/main/java/tech/redroma/yelp/Coordinate.java @@ -22,6 +22,10 @@ import tech.sirwellington.alchemy.annotations.concurrency.ThreadUnsafe; import tech.sirwellington.alchemy.annotations.objects.Pojo; +import static tech.sirwellington.alchemy.arguments.Arguments.checkThat; +import static tech.sirwellington.alchemy.arguments.assertions.GeolocationAssertions.validLatitude; +import static tech.sirwellington.alchemy.arguments.assertions.GeolocationAssertions.validLongitude; + /** * * @author SirWellington @@ -34,6 +38,24 @@ public class Coordinate double latitude; double longitude; + public Coordinate() + { + } + + public Coordinate(double latitude, double longitude) + { + checkThat(latitude).is(validLatitude()); + checkThat(longitude).is(validLongitude()); + + this.latitude = latitude; + this.longitude = longitude; + } + + public static Coordinate of(double latitude, double longitude) throws IllegalArgumentException + { + return new Coordinate(latitude, longitude); + } + @Override public int hashCode() { From ceb802b4c4fdfa8da745b8ede543512822047199 Mon Sep 17 00:00:00 2001 From: Wellington Moreno Date: Thu, 1 Dec 2016 23:20:41 -0800 Subject: [PATCH 074/168] Adds additional constructors to Coordinate --- .../java/tech/redroma/yelp/Coordinate.java | 21 +++++++++++++++---- 1 file changed, 17 insertions(+), 4 deletions(-) diff --git a/src/main/java/tech/redroma/yelp/Coordinate.java b/src/main/java/tech/redroma/yelp/Coordinate.java index 4804275..a164954 100644 --- a/src/main/java/tech/redroma/yelp/Coordinate.java +++ b/src/main/java/tech/redroma/yelp/Coordinate.java @@ -38,10 +38,6 @@ public class Coordinate double latitude; double longitude; - public Coordinate() - { - } - public Coordinate(double latitude, double longitude) { checkThat(latitude).is(validLatitude()); @@ -51,11 +47,28 @@ public Coordinate(double latitude, double longitude) this.longitude = longitude; } + /** + * + * @param latitude A valid latitude + * @param longitude A valid longitude + * @return + * @throws IllegalArgumentException + */ public static Coordinate of(double latitude, double longitude) throws IllegalArgumentException { return new Coordinate(latitude, longitude); } + public double getLatitude() + { + return latitude; + } + + public double getLongitude() + { + return longitude; + } + @Override public int hashCode() { From 955f04607e4402a977c27534755d4ad326dd1b18 Mon Sep 17 00:00:00 2001 From: Wellington Moreno Date: Thu, 1 Dec 2016 23:34:14 -0800 Subject: [PATCH 075/168] Adds unit tests for YelpAPIImpl --- .../tech/redroma/yelp/YelpAPIImplTest.java | 138 ++++++++++++++++++ 1 file changed, 138 insertions(+) create mode 100644 src/test/java/tech/redroma/yelp/YelpAPIImplTest.java diff --git a/src/test/java/tech/redroma/yelp/YelpAPIImplTest.java b/src/test/java/tech/redroma/yelp/YelpAPIImplTest.java new file mode 100644 index 0000000..1429b43 --- /dev/null +++ b/src/test/java/tech/redroma/yelp/YelpAPIImplTest.java @@ -0,0 +1,138 @@ +/* + * Copyright 2016 RedRoma, Inc.. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package tech.redroma.yelp; + +import java.net.URL; +import java.util.List; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.Mock; +import tech.redroma.yelp.oauth.OAuthTokenProvider; +import tech.sirwellington.alchemy.generator.AlchemyGenerator; +import tech.sirwellington.alchemy.http.AlchemyHttp; +import tech.sirwellington.alchemy.http.HttpResponse; +import tech.sirwellington.alchemy.http.mock.AlchemyHttpMock; +import tech.sirwellington.alchemy.test.junit.runners.AlchemyTestRunner; +import tech.sirwellington.alchemy.test.junit.runners.GenerateString; +import tech.sirwellington.alchemy.test.junit.runners.GenerateURL; +import tech.sirwellington.alchemy.test.junit.runners.Repeat; + +import static org.hamcrest.Matchers.is; +import static org.junit.Assert.assertThat; +import static org.mockito.Mockito.when; +import static tech.sirwellington.alchemy.generator.AlchemyGenerator.one; +import static tech.sirwellington.alchemy.generator.CollectionGenerators.listOf; +import static tech.sirwellington.alchemy.generator.GeolocationGenerators.latitudes; +import static tech.sirwellington.alchemy.generator.GeolocationGenerators.longitudes; +import static tech.sirwellington.alchemy.generator.ObjectGenerators.pojos; + +/** + * + * @author SirWellington + */ +@Repeat(10) +@RunWith(AlchemyTestRunner.class) +public class YelpAPIImplTest +{ + + @Mock + private AlchemyHttp http; + + @Mock + private OAuthTokenProvider tokenProvider; + + @GenerateURL + private URL baseURL; + + @GenerateString + private String token; + + @Mock + private HttpResponse httpResponse; + + private List businesses; + + private YelpSearchRequest request; + + @GenerateString + private String searchTerm; + + private double latitude; + private double longitude; + + private YelpAPIImpl instance; + + @Before + public void setUp() throws Exception + { + + setupData(); + setupMocks(); + + instance = new YelpAPIImpl(http, tokenProvider, baseURL.toString()); + } + + private void setupData() throws Exception + { + AlchemyGenerator pojos = pojos(YelpBusiness.class); + businesses = listOf(pojos); + + longitude = one(longitudes()); + latitude = one(latitudes()); + + request = YelpSearchRequest.newBuilder() + .withSearchTerm(searchTerm) + .withCoordinate(Coordinate.of(latitude, longitude)) + .build(); + } + + private void setupMocks() throws Exception + { + when(tokenProvider.getToken()).thenReturn(token); + + when(httpResponse.bodyAsArrayOf(YelpBusiness.class)) + .thenReturn(businesses); + + } + + @Test + public void testGetBusinessDetails() + { + } + + @Test + public void testSearchForBusinesses() throws Exception + { + //We need a specific URL for each method, so a mock HTTP must be constructed + //For each. + String expectedURL = baseURL + YelpAPIImpl.URLS.BUSINESS_SEARCH; + http = AlchemyHttpMock.begin() + .whenGet() + .anyBody() + .at(expectedURL) + .thenReturnResponse(httpResponse) + .build(); + + instance = new YelpAPIImpl(http, tokenProvider, baseURL.toString()); + + List results = instance.searchForBusinesses(request); + assertThat(results, is(businesses)); + AlchemyHttpMock.verifyAllRequestsMade(http); + } + +} From 2c67ca30d5d2122e9fd1fd24db354ce50c69644d Mon Sep 17 00:00:00 2001 From: Wellington Moreno Date: Fri, 2 Dec 2016 00:40:26 -0800 Subject: [PATCH 076/168] Updates equals and hashCode for YelpBusinessDetails --- .../redroma/yelp/YelpBusinessDetails.java | 32 +++++++++---------- 1 file changed, 16 insertions(+), 16 deletions(-) diff --git a/src/main/java/tech/redroma/yelp/YelpBusinessDetails.java b/src/main/java/tech/redroma/yelp/YelpBusinessDetails.java index 4cabe9c..bd7267b 100644 --- a/src/main/java/tech/redroma/yelp/YelpBusinessDetails.java +++ b/src/main/java/tech/redroma/yelp/YelpBusinessDetails.java @@ -71,21 +71,21 @@ public class YelpBusinessDetails public int hashCode() { int hash = 7; - hash = 89 * hash + Objects.hashCode(this.id); - hash = 89 * hash + Objects.hashCode(this.name); - hash = 89 * hash + Objects.hashCode(this.imageURL); - hash = 89 * hash + Objects.hashCode(this.isClaimed); - hash = 89 * hash + Objects.hashCode(this.isClosed); - hash = 89 * hash + Objects.hashCode(this.url); - hash = 89 * hash + Objects.hashCode(this.price); - hash = 89 * hash + (int) (Double.doubleToLongBits(this.rating) ^ (Double.doubleToLongBits(this.rating) >>> 32)); - hash = 89 * hash + this.reviewCount; - hash = 89 * hash + Objects.hashCode(this.phone); - hash = 89 * hash + Objects.hashCode(this.photosURLS); - hash = 89 * hash + Objects.hashCode(this.hours); - hash = 89 * hash + Objects.hashCode(this.categories); - hash = 89 * hash + Objects.hashCode(this.coordinates); - hash = 89 * hash + Objects.hashCode(this.location); + hash = 37 * hash + Objects.hashCode(this.id); + hash = 37 * hash + Objects.hashCode(this.name); + hash = 37 * hash + Objects.hashCode(this.imageURL); + hash = 37 * hash + Objects.hashCode(this.isClaimed); + hash = 37 * hash + Objects.hashCode(this.isClosed); + hash = 37 * hash + Objects.hashCode(this.url); + hash = 37 * hash + Objects.hashCode(this.price); + hash = 37 * hash + (int) (Double.doubleToLongBits(this.rating) ^ (Double.doubleToLongBits(this.rating) >>> 32)); + hash = 37 * hash + this.reviewCount; + hash = 37 * hash + Objects.hashCode(this.phone); + hash = 37 * hash + Objects.hashCode(this.photosURLS); + hash = 37 * hash + Objects.hashCode(this.hours); + hash = 37 * hash + Objects.hashCode(this.categories); + hash = 37 * hash + Objects.hashCode(this.coordinates); + hash = 37 * hash + Objects.hashCode(this.location); return hash; } @@ -105,7 +105,7 @@ public boolean equals(Object obj) return false; } final YelpBusinessDetails other = (YelpBusinessDetails) obj; - if (this.rating != other.rating) + if (Double.doubleToLongBits(this.rating) != Double.doubleToLongBits(other.rating)) { return false; } From eed4f6f64319a72375514d514803655ff8bb1270 Mon Sep 17 00:00:00 2001 From: Wellington Moreno Date: Fri, 2 Dec 2016 00:50:45 -0800 Subject: [PATCH 077/168] Adds default constructor to Coordinate --- src/main/java/tech/redroma/yelp/Coordinate.java | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/main/java/tech/redroma/yelp/Coordinate.java b/src/main/java/tech/redroma/yelp/Coordinate.java index a164954..6fec7ea 100644 --- a/src/main/java/tech/redroma/yelp/Coordinate.java +++ b/src/main/java/tech/redroma/yelp/Coordinate.java @@ -38,6 +38,10 @@ public class Coordinate double latitude; double longitude; + Coordinate() + { + } + public Coordinate(double latitude, double longitude) { checkThat(latitude).is(validLatitude()); From eeab3eca0aca0c024d2c18b5e5b7467f6b7ba3fe Mon Sep 17 00:00:00 2001 From: Wellington Moreno Date: Fri, 2 Dec 2016 00:59:07 -0800 Subject: [PATCH 078/168] Adds additional tests --- .../tech/redroma/yelp/YelpAPIImplTest.java | 102 +++++++++++------- 1 file changed, 62 insertions(+), 40 deletions(-) diff --git a/src/test/java/tech/redroma/yelp/YelpAPIImplTest.java b/src/test/java/tech/redroma/yelp/YelpAPIImplTest.java index 1429b43..a9eec07 100644 --- a/src/test/java/tech/redroma/yelp/YelpAPIImplTest.java +++ b/src/test/java/tech/redroma/yelp/YelpAPIImplTest.java @@ -1,18 +1,18 @@ -/* - * Copyright 2016 RedRoma, Inc.. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ + /* + * Copyright 2016 RedRoma, Inc.. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ package tech.redroma.yelp; @@ -28,6 +28,7 @@ import tech.sirwellington.alchemy.http.HttpResponse; import tech.sirwellington.alchemy.http.mock.AlchemyHttpMock; import tech.sirwellington.alchemy.test.junit.runners.AlchemyTestRunner; +import tech.sirwellington.alchemy.test.junit.runners.GeneratePojo; import tech.sirwellington.alchemy.test.junit.runners.GenerateString; import tech.sirwellington.alchemy.test.junit.runners.GenerateURL; import tech.sirwellington.alchemy.test.junit.runners.Repeat; @@ -40,6 +41,7 @@ import static tech.sirwellington.alchemy.generator.GeolocationGenerators.latitudes; import static tech.sirwellington.alchemy.generator.GeolocationGenerators.longitudes; import static tech.sirwellington.alchemy.generator.ObjectGenerators.pojos; +import static tech.sirwellington.alchemy.test.junit.runners.GenerateString.Type.ALPHABETIC; /** * @@ -49,72 +51,92 @@ @RunWith(AlchemyTestRunner.class) public class YelpAPIImplTest { - + @Mock private AlchemyHttp http; - + @Mock private OAuthTokenProvider tokenProvider; - + @GenerateURL private URL baseURL; - + @GenerateString private String token; - + @Mock private HttpResponse httpResponse; - + private List businesses; - + private YelpSearchRequest request; - + @GenerateString private String searchTerm; - + private double latitude; private double longitude; - + + @GenerateString(ALPHABETIC) + private String businessID; + + @GeneratePojo + private YelpBusinessDetails businessDetails; + private YelpAPIImpl instance; - + @Before public void setUp() throws Exception { - + setupData(); setupMocks(); - + instance = new YelpAPIImpl(http, tokenProvider, baseURL.toString()); } - + private void setupData() throws Exception { AlchemyGenerator pojos = pojos(YelpBusiness.class); businesses = listOf(pojos); - + longitude = one(longitudes()); latitude = one(latitudes()); - + request = YelpSearchRequest.newBuilder() .withSearchTerm(searchTerm) .withCoordinate(Coordinate.of(latitude, longitude)) .build(); } - + private void setupMocks() throws Exception { when(tokenProvider.getToken()).thenReturn(token); - + when(httpResponse.bodyAsArrayOf(YelpBusiness.class)) .thenReturn(businesses); - + } - + @Test - public void testGetBusinessDetails() + public void testGetBusinessDetails() throws Exception { + String expectedURL = baseURL + YelpAPIImpl.URLS.BUSINESSES + "/" + businessID; + + + http = AlchemyHttpMock.begin() + .whenGet() + .noBody() + .at(expectedURL) + .thenReturnPOJO(businessDetails) + .build(); + + instance = new YelpAPIImpl(http, tokenProvider, baseURL.toString()); + + YelpBusinessDetails result = instance.getBusinessDetails(businessID); + assertThat(result, is(businessDetails)); } - + @Test public void testSearchForBusinesses() throws Exception { @@ -127,12 +149,12 @@ public void testSearchForBusinesses() throws Exception .at(expectedURL) .thenReturnResponse(httpResponse) .build(); - + instance = new YelpAPIImpl(http, tokenProvider, baseURL.toString()); - + List results = instance.searchForBusinesses(request); assertThat(results, is(businesses)); AlchemyHttpMock.verifyAllRequestsMade(http); } - + } From 5ca05ea9b101c7e22529234233c7ee67609042b4 Mon Sep 17 00:00:00 2001 From: Wellington Moreno Date: Fri, 2 Dec 2016 00:59:20 -0800 Subject: [PATCH 079/168] Adds more functionality to the YelpAPI Builder --- src/main/java/tech/redroma/yelp/YelpAPI.java | 75 +++++++++++++++++++- 1 file changed, 72 insertions(+), 3 deletions(-) diff --git a/src/main/java/tech/redroma/yelp/YelpAPI.java b/src/main/java/tech/redroma/yelp/YelpAPI.java index 9f5e5b9..0921f74 100644 --- a/src/main/java/tech/redroma/yelp/YelpAPI.java +++ b/src/main/java/tech/redroma/yelp/YelpAPI.java @@ -18,14 +18,19 @@ package tech.redroma.yelp; import java.util.List; +import java.util.concurrent.TimeUnit; import tech.redroma.yelp.exceptions.YelpBadArgumentException; import tech.redroma.yelp.exceptions.YelpExcetion; +import tech.redroma.yelp.oauth.OAuthTokenProvider; import tech.sirwellington.alchemy.annotations.arguments.NonEmpty; import tech.sirwellington.alchemy.annotations.arguments.Required; +import tech.sirwellington.alchemy.http.AlchemyHttp; import static tech.sirwellington.alchemy.arguments.Arguments.checkThat; import static tech.sirwellington.alchemy.arguments.assertions.Assertions.notNull; +import static tech.sirwellington.alchemy.arguments.assertions.NetworkAssertions.validURL; import static tech.sirwellington.alchemy.arguments.assertions.StringAssertions.nonEmptyString; +import static tech.sirwellington.alchemy.arguments.assertions.StringAssertions.stringWithLengthGreaterThanOrEqualTo; /** * The interface used to interact with Yelp's Developer API. @@ -95,16 +100,80 @@ static YelpAPI newInstance() static class Builder { - private String apiKey = ""; - + private static final String DEFAULT_BASE_URL = "https://api.yelp.com/v3"; + + private OAuthTokenProvider oauthProvider; + + private String baseURL = DEFAULT_BASE_URL; + + private AlchemyHttp http = AlchemyHttp.newBuilder() + .usingTimeout(60, TimeUnit.SECONDS) + .build(); + public static Builder newInstance() { return new Builder(); } + public Builder withBaseURL(@NonEmpty String baseURL) throws IllegalArgumentException + { + checkThat(baseURL) + .is(validURL()); + + this.baseURL = baseURL; + return this; + } + + public Builder withHttpClient(@Required AlchemyHttp http) throws IllegalArgumentException + { + checkThat(http).is(notNull()); + + this.http = http; + return this; + } + + public Builder withOAuthToken(@NonEmpty String oauthToken) throws IllegalArgumentException + { + checkThat(oauthToken) + .is(nonEmptyString()) + .is(stringWithLengthGreaterThanOrEqualTo(2)); + + this.oauthProvider = OAuthTokenProvider.newBasicTokenProvider(oauthToken); + return this; + } + + public Builder withClientCredentials(@NonEmpty String cliendId, @NonEmpty String clientSecret) throws IllegalArgumentException + { + checkThat(cliendId, clientSecret) + .usingMessage("invalid client id and secret") + .are(nonEmptyString()) + .are(stringWithLengthGreaterThanOrEqualTo(3)); + + this.oauthProvider = OAuthTokenProvider.newRefreshingTokenProvider(cliendId, clientSecret); + return this; + } + public YelpAPI build() { - return null; + ensureReadyToBuild(); + + return new YelpAPIImpl(http, oauthProvider, baseURL); + } + + private void ensureReadyToBuild() + { + checkThat(baseURL) + .usingMessage("invalid base URL: " + baseURL) + .is(nonEmptyString()) + .is(validURL()); + + checkThat(oauthProvider) + .usingMessage("OAuth Provider missing") + .is(notNull()); + + checkThat(http) + .usingMessage("missing Alchemy HTTP client") + .is(notNull()); } } From bfaef6f7262e46f8ee5f159813454379996f7f5a Mon Sep 17 00:00:00 2001 From: Wellington Moreno Date: Fri, 2 Dec 2016 01:02:43 -0800 Subject: [PATCH 080/168] Annotates with BuilderPattern --- src/main/java/tech/redroma/yelp/YelpAPI.java | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/main/java/tech/redroma/yelp/YelpAPI.java b/src/main/java/tech/redroma/yelp/YelpAPI.java index 0921f74..be41486 100644 --- a/src/main/java/tech/redroma/yelp/YelpAPI.java +++ b/src/main/java/tech/redroma/yelp/YelpAPI.java @@ -24,8 +24,11 @@ import tech.redroma.yelp.oauth.OAuthTokenProvider; import tech.sirwellington.alchemy.annotations.arguments.NonEmpty; import tech.sirwellington.alchemy.annotations.arguments.Required; +import tech.sirwellington.alchemy.annotations.designs.patterns.BuilderPattern; import tech.sirwellington.alchemy.http.AlchemyHttp; +import static tech.sirwellington.alchemy.annotations.designs.patterns.BuilderPattern.Role.BUILDER; +import static tech.sirwellington.alchemy.annotations.designs.patterns.BuilderPattern.Role.PRODUCT; import static tech.sirwellington.alchemy.arguments.Arguments.checkThat; import static tech.sirwellington.alchemy.arguments.assertions.Assertions.notNull; import static tech.sirwellington.alchemy.arguments.assertions.NetworkAssertions.validURL; @@ -38,6 +41,7 @@ * @author SirWellington * @see Yelp API */ +@BuilderPattern(role = PRODUCT) public interface YelpAPI { /** @@ -98,7 +102,8 @@ static YelpAPI newInstance() return Builder.newInstance().build(); } - static class Builder + @BuilderPattern(role = BUILDER) + static final class Builder { private static final String DEFAULT_BASE_URL = "https://api.yelp.com/v3"; From dfe6fe82eb1ff0f508b260b0209c6641aae9f69d Mon Sep 17 00:00:00 2001 From: Wellington Moreno Date: Fri, 2 Dec 2016 01:08:55 -0800 Subject: [PATCH 081/168] Adds additional unit test cases when things go wrong --- .../tech/redroma/yelp/YelpAPIImplTest.java | 46 ++++++++++++++++++- 1 file changed, 45 insertions(+), 1 deletion(-) diff --git a/src/test/java/tech/redroma/yelp/YelpAPIImplTest.java b/src/test/java/tech/redroma/yelp/YelpAPIImplTest.java index a9eec07..b81be7f 100644 --- a/src/test/java/tech/redroma/yelp/YelpAPIImplTest.java +++ b/src/test/java/tech/redroma/yelp/YelpAPIImplTest.java @@ -22,12 +22,15 @@ import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.Mock; +import tech.redroma.yelp.exceptions.YelpExcetion; import tech.redroma.yelp.oauth.OAuthTokenProvider; import tech.sirwellington.alchemy.generator.AlchemyGenerator; import tech.sirwellington.alchemy.http.AlchemyHttp; import tech.sirwellington.alchemy.http.HttpResponse; +import tech.sirwellington.alchemy.http.exceptions.AlchemyHttpException; import tech.sirwellington.alchemy.http.mock.AlchemyHttpMock; import tech.sirwellington.alchemy.test.junit.runners.AlchemyTestRunner; +import tech.sirwellington.alchemy.test.junit.runners.DontRepeat; import tech.sirwellington.alchemy.test.junit.runners.GeneratePojo; import tech.sirwellington.alchemy.test.junit.runners.GenerateString; import tech.sirwellington.alchemy.test.junit.runners.GenerateURL; @@ -41,6 +44,7 @@ import static tech.sirwellington.alchemy.generator.GeolocationGenerators.latitudes; import static tech.sirwellington.alchemy.generator.GeolocationGenerators.longitudes; import static tech.sirwellington.alchemy.generator.ObjectGenerators.pojos; +import static tech.sirwellington.alchemy.test.junit.ThrowableAssertion.assertThrows; import static tech.sirwellington.alchemy.test.junit.runners.GenerateString.Type.ALPHABETIC; /** @@ -123,7 +127,6 @@ public void testGetBusinessDetails() throws Exception { String expectedURL = baseURL + YelpAPIImpl.URLS.BUSINESSES + "/" + businessID; - http = AlchemyHttpMock.begin() .whenGet() .noBody() @@ -137,6 +140,27 @@ public void testGetBusinessDetails() throws Exception assertThat(result, is(businessDetails)); } + @DontRepeat + @Test + public void testGetBusinessDetailsWhenFails() throws Exception + { + String expectedURL = baseURL + YelpAPIImpl.URLS.BUSINESSES + "/" + businessID; + Exception ex = new AlchemyHttpException(); + + http = AlchemyHttpMock.begin() + .whenGet() + .noBody() + .at(expectedURL) + .thenThrow(ex) + .build(); + + instance = new YelpAPIImpl(http, tokenProvider, baseURL.toString()); + + assertThrows(() -> instance.getBusinessDetails(businessID)) + .isInstanceOf(YelpExcetion.class); + } + + @Test public void testSearchForBusinesses() throws Exception { @@ -157,4 +181,24 @@ public void testSearchForBusinesses() throws Exception AlchemyHttpMock.verifyAllRequestsMade(http); } + @DontRepeat + @Test + public void testSearchForBusinessesWhenFails() throws Exception + { + String expectedURL = baseURL + YelpAPIImpl.URLS.BUSINESS_SEARCH; + + Exception ex = new AlchemyHttpException(); + + http = AlchemyHttpMock.begin() + .whenGet() + .anyBody() + .at(expectedURL) + .thenThrow(ex) + .build(); + instance = new YelpAPIImpl(http, tokenProvider, baseURL.toString()); + + assertThrows(() -> instance.searchForBusinesses(request)) + .isInstanceOf(YelpExcetion.class); + } + } From ac97e7c39c25337bb711fd468f1c771165d69140 Mon Sep 17 00:00:00 2001 From: Wellington Moreno Date: Fri, 2 Dec 2016 01:11:31 -0800 Subject: [PATCH 082/168] Breaks out expected URLs into separate reusable variables --- .../tech/redroma/yelp/YelpAPIImplTest.java | 30 +++++++++++-------- 1 file changed, 18 insertions(+), 12 deletions(-) diff --git a/src/test/java/tech/redroma/yelp/YelpAPIImplTest.java b/src/test/java/tech/redroma/yelp/YelpAPIImplTest.java index b81be7f..230affc 100644 --- a/src/test/java/tech/redroma/yelp/YelpAPIImplTest.java +++ b/src/test/java/tech/redroma/yelp/YelpAPIImplTest.java @@ -89,6 +89,10 @@ public class YelpAPIImplTest private YelpAPIImpl instance; + private String expectedGetBusinessDetailsURL; + + private String expectedSearchURL; + @Before public void setUp() throws Exception { @@ -111,6 +115,9 @@ private void setupData() throws Exception .withSearchTerm(searchTerm) .withCoordinate(Coordinate.of(latitude, longitude)) .build(); + + expectedGetBusinessDetailsURL = baseURL + YelpAPIImpl.URLS.BUSINESSES + "/" + businessID; + expectedSearchURL = baseURL + YelpAPIImpl.URLS.BUSINESS_SEARCH; } private void setupMocks() throws Exception @@ -125,12 +132,10 @@ private void setupMocks() throws Exception @Test public void testGetBusinessDetails() throws Exception { - String expectedURL = baseURL + YelpAPIImpl.URLS.BUSINESSES + "/" + businessID; - http = AlchemyHttpMock.begin() .whenGet() .noBody() - .at(expectedURL) + .at(expectedGetBusinessDetailsURL) .thenReturnPOJO(businessDetails) .build(); @@ -144,13 +149,12 @@ public void testGetBusinessDetails() throws Exception @Test public void testGetBusinessDetailsWhenFails() throws Exception { - String expectedURL = baseURL + YelpAPIImpl.URLS.BUSINESSES + "/" + businessID; Exception ex = new AlchemyHttpException(); http = AlchemyHttpMock.begin() .whenGet() .noBody() - .at(expectedURL) + .at(expectedGetBusinessDetailsURL) .thenThrow(ex) .build(); @@ -160,17 +164,20 @@ public void testGetBusinessDetailsWhenFails() throws Exception .isInstanceOf(YelpExcetion.class); } + @DontRepeat + @Test + public void testGetBusinessDetailsWhenTokenInvalid() throws Exception + { + + } @Test public void testSearchForBusinesses() throws Exception { - //We need a specific URL for each method, so a mock HTTP must be constructed - //For each. - String expectedURL = baseURL + YelpAPIImpl.URLS.BUSINESS_SEARCH; http = AlchemyHttpMock.begin() .whenGet() .anyBody() - .at(expectedURL) + .at(expectedSearchURL) .thenReturnResponse(httpResponse) .build(); @@ -185,16 +192,15 @@ public void testSearchForBusinesses() throws Exception @Test public void testSearchForBusinessesWhenFails() throws Exception { - String expectedURL = baseURL + YelpAPIImpl.URLS.BUSINESS_SEARCH; - Exception ex = new AlchemyHttpException(); http = AlchemyHttpMock.begin() .whenGet() .anyBody() - .at(expectedURL) + .at(expectedSearchURL) .thenThrow(ex) .build(); + instance = new YelpAPIImpl(http, tokenProvider, baseURL.toString()); assertThrows(() -> instance.searchForBusinesses(request)) From f496ac68b5623970c0ab5e0f7b3f5d6a56913be3 Mon Sep 17 00:00:00 2001 From: Wellington Moreno Date: Fri, 2 Dec 2016 01:14:02 -0800 Subject: [PATCH 083/168] Adds additional test case: testGetBusinessDetailsWhenTokenInvalid --- .../java/tech/redroma/yelp/YelpAPIImplTest.java | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/src/test/java/tech/redroma/yelp/YelpAPIImplTest.java b/src/test/java/tech/redroma/yelp/YelpAPIImplTest.java index 230affc..3db0b53 100644 --- a/src/test/java/tech/redroma/yelp/YelpAPIImplTest.java +++ b/src/test/java/tech/redroma/yelp/YelpAPIImplTest.java @@ -22,6 +22,7 @@ import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.Mock; +import tech.redroma.yelp.exceptions.YelpAuthenticationException; import tech.redroma.yelp.exceptions.YelpExcetion; import tech.redroma.yelp.oauth.OAuthTokenProvider; import tech.sirwellington.alchemy.generator.AlchemyGenerator; @@ -38,6 +39,7 @@ import static org.hamcrest.Matchers.is; import static org.junit.Assert.assertThat; +import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; import static tech.sirwellington.alchemy.generator.AlchemyGenerator.one; import static tech.sirwellington.alchemy.generator.CollectionGenerators.listOf; @@ -168,7 +170,21 @@ public void testGetBusinessDetailsWhenFails() throws Exception @Test public void testGetBusinessDetailsWhenTokenInvalid() throws Exception { + HttpResponse fakeResponse = mock(HttpResponse.class); + when(fakeResponse.statusCode()).thenReturn(401); + Exception ex = new AlchemyHttpException(fakeResponse); + http = AlchemyHttpMock.begin() + .whenGet() + .noBody() + .at(expectedGetBusinessDetailsURL) + .thenThrow(ex) + .build(); + + instance = new YelpAPIImpl(http, tokenProvider, baseURL.toString()); + + assertThrows(() -> instance.getBusinessDetails(businessID)) + .isInstanceOf(YelpAuthenticationException.class); } @Test From c77934f61334c4e2c3ccc70fac69cd4a26c0dbc4 Mon Sep 17 00:00:00 2001 From: Wellington Moreno Date: Fri, 2 Dec 2016 01:14:57 -0800 Subject: [PATCH 084/168] Adds additional test case: testSearchForBusinessesWhenTokenInvalid --- .../tech/redroma/yelp/YelpAPIImplTest.java | 21 +++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/src/test/java/tech/redroma/yelp/YelpAPIImplTest.java b/src/test/java/tech/redroma/yelp/YelpAPIImplTest.java index 3db0b53..acaa9d1 100644 --- a/src/test/java/tech/redroma/yelp/YelpAPIImplTest.java +++ b/src/test/java/tech/redroma/yelp/YelpAPIImplTest.java @@ -223,4 +223,25 @@ public void testSearchForBusinessesWhenFails() throws Exception .isInstanceOf(YelpExcetion.class); } + @DontRepeat + @Test + public void testSearchForBusinessesWhenTokenInvalid() throws Exception + { + HttpResponse fakeResponse = mock(HttpResponse.class); + when(fakeResponse.statusCode()).thenReturn(401); + Exception ex = new AlchemyHttpException(fakeResponse); + + http = AlchemyHttpMock.begin() + .whenGet() + .noBody() + .at(expectedSearchURL) + .thenThrow(ex) + .build(); + + instance = new YelpAPIImpl(http, tokenProvider, baseURL.toString()); + + assertThrows(() -> instance.searchForBusinesses(request)) + .isInstanceOf(YelpAuthenticationException.class); + } + } From 8be5e83d0449f96b761e45eaebddb4a3acbdbc28 Mon Sep 17 00:00:00 2001 From: Wellington Moreno Date: Fri, 2 Dec 2016 01:15:20 -0800 Subject: [PATCH 085/168] Adds basic YelpAPITest --- .../java/tech/redroma/yelp/YelpAPITest.java | 96 +++++++++++++++++++ 1 file changed, 96 insertions(+) create mode 100644 src/test/java/tech/redroma/yelp/YelpAPITest.java diff --git a/src/test/java/tech/redroma/yelp/YelpAPITest.java b/src/test/java/tech/redroma/yelp/YelpAPITest.java new file mode 100644 index 0000000..7a8b769 --- /dev/null +++ b/src/test/java/tech/redroma/yelp/YelpAPITest.java @@ -0,0 +1,96 @@ +/* + * Copyright 2016 RedRoma, Inc.. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package tech.redroma.yelp; + +import java.util.List; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.runners.MockitoJUnitRunner; +import org.mockito.Mock; +import tech.redroma.yelp.exceptions.YelpExcetion; +import tech.sirwellington.alchemy.test.junit.runners.AlchemyTestRunner; +import tech.sirwellington.alchemy.test.junit.runners.DontRepeat; +import tech.sirwellington.alchemy.test.junit.runners.Repeat; + +import static org.junit.Assert.*; +import static org.hamcrest.Matchers.*; +import static org.mockito.Mockito.*; + +/** + * + * @author SirWellington + */ +@Repeat(10) +@RunWith(AlchemyTestRunner.class) +public class YelpAPITest +{ + + @Before + public void setUp() throws Exception + { + + setupData(); + setupMocks(); + } + + + private void setupData() throws Exception + { + + } + + private void setupMocks() throws Exception + { + + } + + @Test + public void testGetBusinessDetails_YelpBusiness() + { + } + + @Test + public void testGetBusinessDetails_String() + { + } + + @Test + public void testSearchForBusinesses() + { + } + + @Test + public void testNewInstance() + { + } + + public class YelpAPIImpl implements YelpAPI + { + + public YelpBusinessDetails getBusinessDetails(String businessId) throws YelpExcetion + { + return null; + } + + public List searchForBusinesses(YelpSearchRequest request) throws YelpExcetion + { + return null; + } + } + +} \ No newline at end of file From bb3d1496de860f196c2612212cf9f8dc3922e97a Mon Sep 17 00:00:00 2001 From: Wellington Moreno Date: Fri, 2 Dec 2016 09:13:45 -0800 Subject: [PATCH 086/168] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index a3ad79d..97b2a25 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ YelpJavaClient =================== -[![Build Status](https://travis-ci.org/RedRoma/YelpJavaClient.svg?branch=develop)](https://travis-ci.org/RedRoma/YelpJavaClient) +[![Jenkins](http://jenkins.redroma.tech/job/YelpAPI/badge/icon)](http://jenkins.redroma.tech/job/YelpAPI/) [![Travis](https://travis-ci.org/RedRoma/YelpJavaClient.svg?branch=develop)](https://travis-ci.org/RedRoma/YelpJavaClient) Provides a convenient Java Client Library for interfacing with Yelp. From b651f9611468c74e307ca0022d75d4085204d33a Mon Sep 17 00:00:00 2001 From: Wellington Moreno Date: Fri, 2 Dec 2016 09:14:33 -0800 Subject: [PATCH 087/168] Adds convenience static method to create a YelpAPI Instance --- src/main/java/tech/redroma/yelp/YelpAPI.java | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/src/main/java/tech/redroma/yelp/YelpAPI.java b/src/main/java/tech/redroma/yelp/YelpAPI.java index be41486..37696e0 100644 --- a/src/main/java/tech/redroma/yelp/YelpAPI.java +++ b/src/main/java/tech/redroma/yelp/YelpAPI.java @@ -97,9 +97,14 @@ default YelpBusinessDetails getBusinessDetails(@Required YelpBusiness business) */ List searchForBusinesses(@Required YelpSearchRequest request) throws YelpExcetion; - static YelpAPI newInstance() + static YelpAPI newInstance(@NonEmpty String cliendId, @NonEmpty String clientSecret) { - return Builder.newInstance().build(); + checkThat(cliendId, clientSecret) + .are(nonEmptyString()); + + return Builder.newInstance() + .withClientCredentials(cliendId, clientSecret) + .build(); } @BuilderPattern(role = BUILDER) From f77b060c3a963c88432cfc7f72e122e11ca10fa9 Mon Sep 17 00:00:00 2001 From: Wellington Moreno Date: Fri, 2 Dec 2016 09:19:31 -0800 Subject: [PATCH 088/168] Adds an integration test for testing the entirety of the YelpAPIClient --- .../java/tech/redroma/yelp/YelpAPIIT.java | 77 +++++++++++++++++++ 1 file changed, 77 insertions(+) create mode 100644 src/test/java/tech/redroma/yelp/YelpAPIIT.java diff --git a/src/test/java/tech/redroma/yelp/YelpAPIIT.java b/src/test/java/tech/redroma/yelp/YelpAPIIT.java new file mode 100644 index 0000000..e476bb0 --- /dev/null +++ b/src/test/java/tech/redroma/yelp/YelpAPIIT.java @@ -0,0 +1,77 @@ +/* + * Copyright 2016 RedRoma, Inc.. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package tech.redroma.yelp; + +import java.util.List; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import tech.sirwellington.alchemy.annotations.testing.IntegrationTest; +import tech.sirwellington.alchemy.test.junit.runners.AlchemyTestRunner; + +import static com.google.common.base.Strings.isNullOrEmpty; + +/** + * Manually run this test to make sure your client key and secret work. + * + * @author SirWellington + */ +@IntegrationTest +@RunWith(AlchemyTestRunner.class) +public class YelpAPIIT +{ + + private final Logger LOG = LoggerFactory.getLogger(this.getClass()); + + //Set to your own credentials to enable the test + private final String clientId = ""; + private final String clientSecret = ""; + + private YelpSearchRequest request; + + private YelpAPI yelp; + + @Before + public void setUp() throws Exception + { + if (!isNullOrEmpty(clientId) && !isNullOrEmpty(clientSecret)) + { + yelp = YelpAPI.newInstance(clientId, clientSecret); + } + } + + @Test + public void testSearch() + { + if (yelp == null) + { + return; + } + +// 34.018363, -118.492343 + request = YelpSearchRequest.newBuilder() + .withSearchTerm("Deli") + .withCoordinate(Coordinate.of(34.018363, -118.492343)) + .build(); + + List results = yelp.searchForBusinesses(request); + LOG.info("Found {} results for request: {}", results.size(), request); + } + +} \ No newline at end of file From bc17766943546bec69e64d4fa284cdb2dc0c3871 Mon Sep 17 00:00:00 2001 From: Wellington Moreno Date: Fri, 2 Dec 2016 09:19:58 -0800 Subject: [PATCH 089/168] Updates documentation --- src/test/java/tech/redroma/yelp/YelpAPIIT.java | 1 + 1 file changed, 1 insertion(+) diff --git a/src/test/java/tech/redroma/yelp/YelpAPIIT.java b/src/test/java/tech/redroma/yelp/YelpAPIIT.java index e476bb0..980796c 100644 --- a/src/test/java/tech/redroma/yelp/YelpAPIIT.java +++ b/src/test/java/tech/redroma/yelp/YelpAPIIT.java @@ -40,6 +40,7 @@ public class YelpAPIIT private final Logger LOG = LoggerFactory.getLogger(this.getClass()); //Set to your own credentials to enable the test + //BE SURE NOT TO CHECK THIS INTO A SOURCE REPOSITORY. private final String clientId = ""; private final String clientSecret = ""; From 6f0cda23943cb037a81cb4874325dbdf5878159c Mon Sep 17 00:00:00 2001 From: Wellington Moreno Date: Fri, 2 Dec 2016 09:27:53 -0800 Subject: [PATCH 090/168] Adds a Responses class that models against Yelp's responses. --- .../java/tech/redroma/yelp/Responses.java | 102 ++++++++++++++++++ .../java/tech/redroma/yelp/ResponsesIT.java | 77 +++++++++++++ 2 files changed, 179 insertions(+) create mode 100644 src/main/java/tech/redroma/yelp/Responses.java create mode 100644 src/test/java/tech/redroma/yelp/ResponsesIT.java diff --git a/src/main/java/tech/redroma/yelp/Responses.java b/src/main/java/tech/redroma/yelp/Responses.java new file mode 100644 index 0000000..fb8bde6 --- /dev/null +++ b/src/main/java/tech/redroma/yelp/Responses.java @@ -0,0 +1,102 @@ +/* + * Copyright 2016 RedRoma, Inc.. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + + +package tech.redroma.yelp; + + +import java.util.List; +import java.util.Objects; +import tech.sirwellington.alchemy.annotations.access.Internal; +import tech.sirwellington.alchemy.annotations.access.NonInstantiable; +import tech.sirwellington.alchemy.annotations.objects.Pojo; + +/** + * + * @author SirWellington + */ +@Internal +@NonInstantiable +final class Responses +{ + + Responses() throws IllegalAccessException + { + throw new IllegalAccessException("cannot instantiate"); + } + + + /** + * Designed for the Search API. + * + * @see https://www.yelp.com/developers/documentation/v3/business_search + */ + @Pojo + @Internal + static class SearchResponse + { + + public int total; + public List businesses; + + SearchResponse() + { + } + + @Override + public int hashCode() + { + int hash = 5; + hash = 67 * hash + this.total; + hash = 67 * hash + Objects.hashCode(this.businesses); + return hash; + } + + @Override + public boolean equals(Object obj) + { + if (this == obj) + { + return true; + } + if (obj == null) + { + return false; + } + if (getClass() != obj.getClass()) + { + return false; + } + final SearchResponse other = (SearchResponse) obj; + if (this.total != other.total) + { + return false; + } + if (!Objects.equals(this.businesses, other.businesses)) + { + return false; + } + return true; + } + + @Override + public String toString() + { + return "SearchResponse{" + "total=" + total + ", businesses=" + businesses + '}'; + } + + } +} diff --git a/src/test/java/tech/redroma/yelp/ResponsesIT.java b/src/test/java/tech/redroma/yelp/ResponsesIT.java new file mode 100644 index 0000000..16aab92 --- /dev/null +++ b/src/test/java/tech/redroma/yelp/ResponsesIT.java @@ -0,0 +1,77 @@ +/* + * Copyright 2016 RedRoma, Inc.. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package tech.redroma.yelp; + +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import tech.sirwellington.alchemy.test.junit.runners.AlchemyTestRunner; +import tech.sirwellington.alchemy.test.junit.runners.GeneratePojo; +import tech.sirwellington.alchemy.test.junit.runners.Repeat; + +import static org.hamcrest.Matchers.empty; +import static org.hamcrest.Matchers.greaterThan; +import static org.hamcrest.Matchers.not; +import static org.hamcrest.Matchers.notNullValue; +import static org.junit.Assert.assertThat; + +/** + * + * @author SirWellington + */ +@Repeat(10) +@RunWith(AlchemyTestRunner.class) +public class ResponsesIT +{ + + @GeneratePojo + private Responses.SearchResponse searchResponse; + + @Before + public void setUp() throws Exception + { + + setupData(); + setupMocks(); + } + + private void setupData() throws Exception + { + + } + + private void setupMocks() throws Exception + { + + } + + @Test + public void testSearchResponse() throws Exception + { + assertThat(searchResponse, notNullValue()); + assertThat(searchResponse.total, greaterThan(0)); + assertThat(searchResponse.businesses, not(empty())); + } + + @Test + public void testSearchResponseHashCode() throws Exception + { + int hashCode = searchResponse.hashCode(); + assertThat(hashCode, not(0)); + } + +} From 9174dc7cb7e108cef70e976ece04f1def613bd0f Mon Sep 17 00:00:00 2001 From: Wellington Moreno Date: Fri, 2 Dec 2016 09:29:42 -0800 Subject: [PATCH 091/168] Updates the search request with a MAX_OFFSET --- src/main/java/tech/redroma/yelp/YelpSearchRequest.java | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/src/main/java/tech/redroma/yelp/YelpSearchRequest.java b/src/main/java/tech/redroma/yelp/YelpSearchRequest.java index a1f8402..6f1fe14 100644 --- a/src/main/java/tech/redroma/yelp/YelpSearchRequest.java +++ b/src/main/java/tech/redroma/yelp/YelpSearchRequest.java @@ -439,6 +439,11 @@ public final static class Builder */ public final static int MAX_LIMIT = 50; + /** + * The maximum number of offset that can used in a query. + */ + public final static int MAX_OFFSET = 1000; + /** * The widest radius that can be used in a Search. * Set to 40,000 meters, or 25 miles. @@ -726,7 +731,10 @@ public Builder withOffset(@Positive int offset) throws IllegalArgumentException { checkThat(offset) .usingMessage("offset must be > 0") - .is(positiveInteger()); + .is(positiveInteger()) + .usingMessage("Per Yelp's API rules, the offset cannot exceed: " + MAX_OFFSET) + .is(lessThanOrEqualTo(MAX_OFFSET)); + this.offset = offset; return this; From c3c1b73877aa5e64ac607241b4ba9a4d08a92fd3 Mon Sep 17 00:00:00 2001 From: Wellington Moreno Date: Fri, 2 Dec 2016 09:36:20 -0800 Subject: [PATCH 092/168] Renames class to YelpResponses --- .../tech/redroma/yelp/{Responses.java => YelpResponses.java} | 4 ++-- .../redroma/yelp/{ResponsesIT.java => YelpResponsesTest.java} | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) rename src/main/java/tech/redroma/yelp/{Responses.java => YelpResponses.java} (97%) rename src/test/java/tech/redroma/yelp/{ResponsesIT.java => YelpResponsesTest.java} (95%) diff --git a/src/main/java/tech/redroma/yelp/Responses.java b/src/main/java/tech/redroma/yelp/YelpResponses.java similarity index 97% rename from src/main/java/tech/redroma/yelp/Responses.java rename to src/main/java/tech/redroma/yelp/YelpResponses.java index fb8bde6..ba8b7b7 100644 --- a/src/main/java/tech/redroma/yelp/Responses.java +++ b/src/main/java/tech/redroma/yelp/YelpResponses.java @@ -30,10 +30,10 @@ */ @Internal @NonInstantiable -final class Responses +final class YelpResponses { - Responses() throws IllegalAccessException + YelpResponses() throws IllegalAccessException { throw new IllegalAccessException("cannot instantiate"); } diff --git a/src/test/java/tech/redroma/yelp/ResponsesIT.java b/src/test/java/tech/redroma/yelp/YelpResponsesTest.java similarity index 95% rename from src/test/java/tech/redroma/yelp/ResponsesIT.java rename to src/test/java/tech/redroma/yelp/YelpResponsesTest.java index 16aab92..df07a24 100644 --- a/src/test/java/tech/redroma/yelp/ResponsesIT.java +++ b/src/test/java/tech/redroma/yelp/YelpResponsesTest.java @@ -35,11 +35,11 @@ */ @Repeat(10) @RunWith(AlchemyTestRunner.class) -public class ResponsesIT +public class YelpResponsesTest { @GeneratePojo - private Responses.SearchResponse searchResponse; + private YelpResponses.SearchResponse searchResponse; @Before public void setUp() throws Exception From bac3bbf5b0b5f01f46e81950451978920b7bab03 Mon Sep 17 00:00:00 2001 From: Wellington Moreno Date: Fri, 2 Dec 2016 09:49:05 -0800 Subject: [PATCH 093/168] Updates Locale with documentation --- src/main/java/tech/redroma/yelp/Locale.java | 22 +++++++++++++++++++-- 1 file changed, 20 insertions(+), 2 deletions(-) diff --git a/src/main/java/tech/redroma/yelp/Locale.java b/src/main/java/tech/redroma/yelp/Locale.java index 21a3659..9e416a0 100644 --- a/src/main/java/tech/redroma/yelp/Locale.java +++ b/src/main/java/tech/redroma/yelp/Locale.java @@ -23,18 +23,36 @@ import static tech.sirwellington.alchemy.arguments.assertions.StringAssertions.nonEmptyString; /** - * See {@link Locales} for supported locales. + * A Local consists of a code, country, and language. + *

+ * See {@link Locales} for locales supported by Yelp. * * @author SirWellington * @see Locales */ public interface Locale { - + /** + * For example, {@code en_CA}, for English/Canada. + * @return The 5 digit code consisting of {@code language-code_country-code }. The underscore in the middle is included. + */ public String code(); + + /** + * For example, Italy. + * @return The display name of the country. + */ public String country(); + + /** + * For example, Italian. + * @return The display name of the language. + */ public String language(); + /** + * All of the Locales supported by Yelp. + */ public enum Locales implements Locale { CZECH_REPUBLIC("cs_CZ", "Czech Republic", "Czech"), From 00113dd6e1d22e607763f1c991aa08f1f78fc276 Mon Sep 17 00:00:00 2001 From: Wellington Moreno Date: Fri, 2 Dec 2016 09:49:24 -0800 Subject: [PATCH 094/168] Adds documentation to withLocale() --- src/main/java/tech/redroma/yelp/YelpSearchRequest.java | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/main/java/tech/redroma/yelp/YelpSearchRequest.java b/src/main/java/tech/redroma/yelp/YelpSearchRequest.java index 6f1fe14..338f147 100644 --- a/src/main/java/tech/redroma/yelp/YelpSearchRequest.java +++ b/src/main/java/tech/redroma/yelp/YelpSearchRequest.java @@ -670,6 +670,8 @@ public Builder withCategories(@NonEmpty List categories) throws Illega /** * Specify the locale to return the business information in. + *

+ * To see the possible Locales, refer to {@link Locale.Locales}. * * @param locale The locale (cannot be empty). * @return From 785e57b3cf0b8ff5445fbf0d21beef440302814f Mon Sep 17 00:00:00 2001 From: Wellington Moreno Date: Fri, 2 Dec 2016 09:51:00 -0800 Subject: [PATCH 095/168] Updates searchForBusinesses() operation to properly handle Yelp response --- .../java/tech/redroma/yelp/YelpAPIImpl.java | 21 ++++++++++++------- 1 file changed, 14 insertions(+), 7 deletions(-) diff --git a/src/main/java/tech/redroma/yelp/YelpAPIImpl.java b/src/main/java/tech/redroma/yelp/YelpAPIImpl.java index 41a34d2..8b93156 100644 --- a/src/main/java/tech/redroma/yelp/YelpAPIImpl.java +++ b/src/main/java/tech/redroma/yelp/YelpAPIImpl.java @@ -21,6 +21,7 @@ import java.util.Objects; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import sir.wellington.alchemy.collections.lists.Lists; import tech.redroma.yelp.exceptions.YelpAuthenticationException; import tech.redroma.yelp.exceptions.YelpBadArgumentException; import tech.redroma.yelp.exceptions.YelpExcetion; @@ -30,7 +31,6 @@ import tech.sirwellington.alchemy.annotations.access.NonInstantiable; import tech.sirwellington.alchemy.http.AlchemyHttp; import tech.sirwellington.alchemy.http.AlchemyRequest; -import tech.sirwellington.alchemy.http.HttpResponse; import tech.sirwellington.alchemy.http.exceptions.AlchemyHttpException; import static java.lang.String.format; @@ -103,14 +103,11 @@ public List searchForBusinesses(YelpSearchRequest request) throws String url = baseURL + URLS.BUSINESS_SEARCH; + YelpResponses.SearchResponse response; try { - HttpResponse response = httpRequest.at(url); - - List results = response.bodyAsArrayOf(YelpBusiness.class); - - LOG.debug("Found {} businesses matching search request [{}]", results.size(), request); - return results; + response = httpRequest.expecting(YelpResponses.SearchResponse.class) + .at(url); } catch (AlchemyHttpException ex) { @@ -131,6 +128,16 @@ public List searchForBusinesses(YelpSearchRequest request) throws LOG.error("Failed to make search request at {}", url, ex); throw new YelpOperationFailedException("could not search Yelp at: " + url, ex); } + + if (response == null) + { + LOG.warn("Received null response from Yelp at {} for request {}", url, request); + } + + List results = Lists.nullToEmpty(response.businesses); + + LOG.info("Received {} results out of {} total for search: {}", results.size(), response.total, request); + return results; } private String createDetailUrlFor(String businessId) From 4629e7986c442e77d64187e3761348fa4d11ffd8 Mon Sep 17 00:00:00 2001 From: Wellington Moreno Date: Fri, 2 Dec 2016 09:51:20 -0800 Subject: [PATCH 096/168] Updates YelpAPIImplTest --- .../tech/redroma/yelp/YelpAPIImplTest.java | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/src/test/java/tech/redroma/yelp/YelpAPIImplTest.java b/src/test/java/tech/redroma/yelp/YelpAPIImplTest.java index acaa9d1..4c4b17d 100644 --- a/src/test/java/tech/redroma/yelp/YelpAPIImplTest.java +++ b/src/test/java/tech/redroma/yelp/YelpAPIImplTest.java @@ -25,7 +25,6 @@ import tech.redroma.yelp.exceptions.YelpAuthenticationException; import tech.redroma.yelp.exceptions.YelpExcetion; import tech.redroma.yelp.oauth.OAuthTokenProvider; -import tech.sirwellington.alchemy.generator.AlchemyGenerator; import tech.sirwellington.alchemy.http.AlchemyHttp; import tech.sirwellington.alchemy.http.HttpResponse; import tech.sirwellington.alchemy.http.exceptions.AlchemyHttpException; @@ -42,10 +41,10 @@ import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; import static tech.sirwellington.alchemy.generator.AlchemyGenerator.one; -import static tech.sirwellington.alchemy.generator.CollectionGenerators.listOf; +import static tech.sirwellington.alchemy.generator.EnumGenerators.enumValueOf; import static tech.sirwellington.alchemy.generator.GeolocationGenerators.latitudes; import static tech.sirwellington.alchemy.generator.GeolocationGenerators.longitudes; -import static tech.sirwellington.alchemy.generator.ObjectGenerators.pojos; +import static tech.sirwellington.alchemy.generator.NumberGenerators.integers; import static tech.sirwellington.alchemy.test.junit.ThrowableAssertion.assertThrows; import static tech.sirwellington.alchemy.test.junit.runners.GenerateString.Type.ALPHABETIC; @@ -73,6 +72,9 @@ public class YelpAPIImplTest @Mock private HttpResponse httpResponse; + @GeneratePojo + private YelpResponses.SearchResponse searchResponse; + private List businesses; private YelpSearchRequest request; @@ -107,8 +109,7 @@ public void setUp() throws Exception private void setupData() throws Exception { - AlchemyGenerator pojos = pojos(YelpBusiness.class); - businesses = listOf(pojos); + businesses = searchResponse.businesses; longitude = one(longitudes()); latitude = one(latitudes()); @@ -116,6 +117,8 @@ private void setupData() throws Exception request = YelpSearchRequest.newBuilder() .withSearchTerm(searchTerm) .withCoordinate(Coordinate.of(latitude, longitude)) + .withLimit(one(integers(5, YelpSearchRequest.Builder.MAX_LIMIT))) + .withLocale(enumValueOf(Locale.Locales.class).get()) .build(); expectedGetBusinessDetailsURL = baseURL + YelpAPIImpl.URLS.BUSINESSES + "/" + businessID; @@ -126,9 +129,6 @@ private void setupMocks() throws Exception { when(tokenProvider.getToken()).thenReturn(token); - when(httpResponse.bodyAsArrayOf(YelpBusiness.class)) - .thenReturn(businesses); - } @Test @@ -194,7 +194,7 @@ public void testSearchForBusinesses() throws Exception .whenGet() .anyBody() .at(expectedSearchURL) - .thenReturnResponse(httpResponse) + .thenReturnPOJO(searchResponse) .build(); instance = new YelpAPIImpl(http, tokenProvider, baseURL.toString()); From 684f3daabee06869d4cb848eba45f668d5e08e95 Mon Sep 17 00:00:00 2001 From: Wellington Moreno Date: Fri, 2 Dec 2016 09:51:31 -0800 Subject: [PATCH 097/168] Increases test iteration for YelpAPIImplTest --- src/test/java/tech/redroma/yelp/YelpAPIImplTest.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/test/java/tech/redroma/yelp/YelpAPIImplTest.java b/src/test/java/tech/redroma/yelp/YelpAPIImplTest.java index 4c4b17d..95420f1 100644 --- a/src/test/java/tech/redroma/yelp/YelpAPIImplTest.java +++ b/src/test/java/tech/redroma/yelp/YelpAPIImplTest.java @@ -52,7 +52,7 @@ * * @author SirWellington */ -@Repeat(10) +@Repeat(50) @RunWith(AlchemyTestRunner.class) public class YelpAPIImplTest { From 4fbbb67aa7a99aab408bb5f45a4d55065a7925c9 Mon Sep 17 00:00:00 2001 From: Wellington Moreno Date: Fri, 2 Dec 2016 10:10:41 -0800 Subject: [PATCH 098/168] Updates alchemy-http to latest snapshot --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 1f4375e..f092e08 100644 --- a/pom.xml +++ b/pom.xml @@ -147,7 +147,7 @@ tech.sirwellington.alchemy alchemy-http - 1.2 + 1.3-SNAPSHOT From 5404de2b7dcb0b718f1c837e99956ca86a6aa1fc Mon Sep 17 00:00:00 2001 From: Wellington Moreno Date: Fri, 2 Dec 2016 10:17:30 -0800 Subject: [PATCH 099/168] Adds MAX_LIMIT to IT test --- src/test/java/tech/redroma/yelp/YelpAPIIT.java | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/test/java/tech/redroma/yelp/YelpAPIIT.java b/src/test/java/tech/redroma/yelp/YelpAPIIT.java index 980796c..8a2d991 100644 --- a/src/test/java/tech/redroma/yelp/YelpAPIIT.java +++ b/src/test/java/tech/redroma/yelp/YelpAPIIT.java @@ -26,6 +26,7 @@ import tech.sirwellington.alchemy.test.junit.runners.AlchemyTestRunner; import static com.google.common.base.Strings.isNullOrEmpty; +import static tech.redroma.yelp.YelpSearchRequest.Builder.MAX_LIMIT; /** * Manually run this test to make sure your client key and secret work. @@ -69,6 +70,7 @@ public void testSearch() request = YelpSearchRequest.newBuilder() .withSearchTerm("Deli") .withCoordinate(Coordinate.of(34.018363, -118.492343)) + .withLimit(MAX_LIMIT) .build(); List results = yelp.searchForBusinesses(request); From 15914ef239e3bf259a2fb2392a7f165623dfbdd5 Mon Sep 17 00:00:00 2001 From: Wellington Moreno Date: Fri, 2 Dec 2016 10:36:24 -0800 Subject: [PATCH 100/168] Enhances Yelp IT test with additional case --- .../java/tech/redroma/yelp/YelpAPIIT.java | 23 +++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/src/test/java/tech/redroma/yelp/YelpAPIIT.java b/src/test/java/tech/redroma/yelp/YelpAPIIT.java index 8a2d991..841b848 100644 --- a/src/test/java/tech/redroma/yelp/YelpAPIIT.java +++ b/src/test/java/tech/redroma/yelp/YelpAPIIT.java @@ -22,6 +22,7 @@ import org.junit.runner.RunWith; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import sir.wellington.alchemy.collections.lists.Lists; import tech.sirwellington.alchemy.annotations.testing.IntegrationTest; import tech.sirwellington.alchemy.test.junit.runners.AlchemyTestRunner; @@ -77,4 +78,26 @@ public void testSearch() LOG.info("Found {} results for request: {}", results.size(), request); } + @Test + public void testGetBusinessDetails() throws Exception + { + if (yelp == null) + { + return; + } + + request = YelpSearchRequest.newBuilder() + .withSearchTerm("Grocery") + .withCoordinate(Coordinate.of(34.018363, -118.492343)) + .withLimit(5) + .build(); + + List results = yelp.searchForBusinesses(request); + YelpBusiness business = Lists.oneOf(results); + + YelpBusinessDetails details = yelp.getBusinessDetails(business); + LOG.info("Found details for business: {}", business); + + } + } \ No newline at end of file From 4011ae98b3b6d7f0f6b7869311b3d5daaa594a78 Mon Sep 17 00:00:00 2001 From: Wellington Moreno Date: Fri, 2 Dec 2016 10:38:45 -0800 Subject: [PATCH 101/168] Adds documentation to Address --- src/main/java/tech/redroma/yelp/Address.java | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/main/java/tech/redroma/yelp/Address.java b/src/main/java/tech/redroma/yelp/Address.java index f753f5f..f51c8d1 100644 --- a/src/main/java/tech/redroma/yelp/Address.java +++ b/src/main/java/tech/redroma/yelp/Address.java @@ -27,7 +27,9 @@ import static com.google.common.base.Strings.isNullOrEmpty; /** - * + * A physical location. Can be {@linkplain YelpBusiness#location returned by Yelp} or + * {@linkplain YelpAPI#searchForBusinesses(tech.redroma.yelp.YelpSearchRequest) used in a Search request}. + * * @author SirWellington */ @Pojo From 555e406550e396ce0f7d6362dcc27347a8b78085 Mon Sep 17 00:00:00 2001 From: Wellington Moreno Date: Fri, 2 Dec 2016 10:39:52 -0800 Subject: [PATCH 102/168] Adds documentation to Category --- src/main/java/tech/redroma/yelp/Category.java | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/main/java/tech/redroma/yelp/Category.java b/src/main/java/tech/redroma/yelp/Category.java index 56b669a..649e6d5 100644 --- a/src/main/java/tech/redroma/yelp/Category.java +++ b/src/main/java/tech/redroma/yelp/Category.java @@ -17,7 +17,6 @@ package tech.redroma.yelp; - import java.util.Objects; import tech.sirwellington.alchemy.annotations.arguments.NonEmpty; import tech.sirwellington.alchemy.annotations.concurrency.Mutable; @@ -28,10 +27,14 @@ import static tech.sirwellington.alchemy.arguments.assertions.StringAssertions.nonEmptyString; /** + * The category of a business, for example "Grocery", "Restaurant". + * + * Can be {@linkplain YelpBusiness#categories returned by Yelp} or + * {@linkplain YelpAPI#searchForBusinesses(tech.redroma.yelp.YelpSearchRequest) used in a Search request}. * * @author SirWellington */ -@Pojo +@Pojo @Mutable @ThreadUnsafe public class Category From 6a70d1806a294d26b0c0a5d253f335c6445b4bbc Mon Sep 17 00:00:00 2001 From: Wellington Moreno Date: Fri, 2 Dec 2016 10:47:41 -0800 Subject: [PATCH 103/168] Updates withLocale() to handle the edge case of 'fil_PH' --- src/main/java/tech/redroma/yelp/YelpSearchRequest.java | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/main/java/tech/redroma/yelp/YelpSearchRequest.java b/src/main/java/tech/redroma/yelp/YelpSearchRequest.java index 338f147..2f0450c 100644 --- a/src/main/java/tech/redroma/yelp/YelpSearchRequest.java +++ b/src/main/java/tech/redroma/yelp/YelpSearchRequest.java @@ -44,7 +44,7 @@ import static tech.sirwellington.alchemy.arguments.assertions.NumberAssertions.lessThanOrEqualTo; import static tech.sirwellington.alchemy.arguments.assertions.NumberAssertions.positiveInteger; import static tech.sirwellington.alchemy.arguments.assertions.StringAssertions.nonEmptyString; -import static tech.sirwellington.alchemy.arguments.assertions.StringAssertions.stringWithLength; +import static tech.sirwellington.alchemy.arguments.assertions.StringAssertions.stringWithLengthBetween; /** * Use to make search requests to the Yelp API. Use {@link #newBuilder() } to create a request object. @@ -692,7 +692,8 @@ public Builder withLocale(@Required Locale locale) throws IllegalArgumentExcepti .usingMessage("Country Code cannot be empty") .is(nonEmptyString()) .usingMessage("Country must take the form: {language code}_{country code}") - .is(stringWithLength(5)); + .is(stringWithLengthBetween(5, 6)); + this.locale = locale.code(); return this; } From f5e13c7f64f1cb120e1fa7d0a148d224547df366 Mon Sep 17 00:00:00 2001 From: Wellington Moreno Date: Fri, 2 Dec 2016 10:48:29 -0800 Subject: [PATCH 104/168] Expands withLocale() to check for '_' --- src/main/java/tech/redroma/yelp/YelpSearchRequest.java | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/main/java/tech/redroma/yelp/YelpSearchRequest.java b/src/main/java/tech/redroma/yelp/YelpSearchRequest.java index 2f0450c..33fd4ab 100644 --- a/src/main/java/tech/redroma/yelp/YelpSearchRequest.java +++ b/src/main/java/tech/redroma/yelp/YelpSearchRequest.java @@ -44,6 +44,7 @@ import static tech.sirwellington.alchemy.arguments.assertions.NumberAssertions.lessThanOrEqualTo; import static tech.sirwellington.alchemy.arguments.assertions.NumberAssertions.positiveInteger; import static tech.sirwellington.alchemy.arguments.assertions.StringAssertions.nonEmptyString; +import static tech.sirwellington.alchemy.arguments.assertions.StringAssertions.stringContaining; import static tech.sirwellington.alchemy.arguments.assertions.StringAssertions.stringWithLengthBetween; /** @@ -692,7 +693,8 @@ public Builder withLocale(@Required Locale locale) throws IllegalArgumentExcepti .usingMessage("Country Code cannot be empty") .is(nonEmptyString()) .usingMessage("Country must take the form: {language code}_{country code}") - .is(stringWithLengthBetween(5, 6)); + .is(stringWithLengthBetween(5, 6)) + .is(stringContaining("_")); this.locale = locale.code(); return this; From 1eb8a320c2ff4bae0de6a791d0908acaecc0fab9 Mon Sep 17 00:00:00 2001 From: Wellington Moreno Date: Fri, 2 Dec 2016 11:14:29 -0800 Subject: [PATCH 105/168] Adds the ability to eagerly obtain an oauth token --- src/main/java/tech/redroma/yelp/YelpAPI.java | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/src/main/java/tech/redroma/yelp/YelpAPI.java b/src/main/java/tech/redroma/yelp/YelpAPI.java index 37696e0..0fc9a6e 100644 --- a/src/main/java/tech/redroma/yelp/YelpAPI.java +++ b/src/main/java/tech/redroma/yelp/YelpAPI.java @@ -19,6 +19,8 @@ import java.util.List; import java.util.concurrent.TimeUnit; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; import tech.redroma.yelp.exceptions.YelpBadArgumentException; import tech.redroma.yelp.exceptions.YelpExcetion; import tech.redroma.yelp.oauth.OAuthTokenProvider; @@ -111,6 +113,7 @@ static YelpAPI newInstance(@NonEmpty String cliendId, @NonEmpty String clientSec static final class Builder { private static final String DEFAULT_BASE_URL = "https://api.yelp.com/v3"; + private static final Logger LOG = LoggerFactory.getLogger(Builder.class); private OAuthTokenProvider oauthProvider; @@ -120,6 +123,8 @@ static final class Builder .usingTimeout(60, TimeUnit.SECONDS) .build(); + private boolean requestTokenImmediately = false; + public static Builder newInstance() { return new Builder(); @@ -163,10 +168,22 @@ public Builder withClientCredentials(@NonEmpty String cliendId, @NonEmpty String return this; } + public Builder withEagerAuthentication() + { + requestTokenImmediately = true; + return this; + } + public YelpAPI build() { ensureReadyToBuild(); + if (requestTokenImmediately) + { + LOG.debug("Obtaining OAuth Token in advance."); + oauthProvider.getToken(); + } + return new YelpAPIImpl(http, oauthProvider, baseURL); } From fe75e07523640422fc2da42d087bbd7f38ac05f2 Mon Sep 17 00:00:00 2001 From: Wellington Moreno Date: Fri, 2 Dec 2016 11:15:39 -0800 Subject: [PATCH 106/168] Adds documentation --- src/main/java/tech/redroma/yelp/YelpAPI.java | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/main/java/tech/redroma/yelp/YelpAPI.java b/src/main/java/tech/redroma/yelp/YelpAPI.java index 0fc9a6e..28a5b55 100644 --- a/src/main/java/tech/redroma/yelp/YelpAPI.java +++ b/src/main/java/tech/redroma/yelp/YelpAPI.java @@ -39,7 +39,9 @@ /** * The interface used to interact with Yelp's Developer API. - * + *

+ * To create, see {@link #newInstance(java.lang.String, java.lang.String) } or {@link YelpAPI.Builder}. + * * @author SirWellington * @see Yelp API */ @@ -109,6 +111,9 @@ static YelpAPI newInstance(@NonEmpty String cliendId, @NonEmpty String clientSec .build(); } + /** + * Use to create a more customized {@link YelpAPI}. + */ @BuilderPattern(role = BUILDER) static final class Builder { From 83da728905ec106b392e398e7008322e76a77067 Mon Sep 17 00:00:00 2001 From: Wellington Moreno Date: Fri, 2 Dec 2016 11:21:57 -0800 Subject: [PATCH 107/168] Adds documentation --- .../yelp/exceptions/YelpAuthenticationException.java | 9 ++++++++- .../yelp/exceptions/YelpBadArgumentException.java | 3 ++- .../yelp/exceptions/YelpOperationFailedException.java | 8 +++++++- 3 files changed, 17 insertions(+), 3 deletions(-) diff --git a/src/main/java/tech/redroma/yelp/exceptions/YelpAuthenticationException.java b/src/main/java/tech/redroma/yelp/exceptions/YelpAuthenticationException.java index 295cc09..a9cccb2 100644 --- a/src/main/java/tech/redroma/yelp/exceptions/YelpAuthenticationException.java +++ b/src/main/java/tech/redroma/yelp/exceptions/YelpAuthenticationException.java @@ -17,7 +17,14 @@ package tech.redroma.yelp.exceptions; /** - * Thrown when an Authentication error occurs communicating with the Yelp API. + * Thrown when an Authentication error occurs communicating with the Yelp API. + *

+ * This can happen because: + *

+ * + The client_id or client_secret were invalid
+ * + An OAuth token could not be obtained from Yelp
+ * + The OAuth token being used has expired
+ * 
* * @author SirWellington */ diff --git a/src/main/java/tech/redroma/yelp/exceptions/YelpBadArgumentException.java b/src/main/java/tech/redroma/yelp/exceptions/YelpBadArgumentException.java index 6bec7a3..6a94c13 100644 --- a/src/main/java/tech/redroma/yelp/exceptions/YelpBadArgumentException.java +++ b/src/main/java/tech/redroma/yelp/exceptions/YelpBadArgumentException.java @@ -17,7 +17,8 @@ package tech.redroma.yelp.exceptions; /** - * + * Thrown when a request is made with a bad argument, causing a {@code 400}-class error. + * * @author SirWellington */ public class YelpBadArgumentException extends YelpExcetion diff --git a/src/main/java/tech/redroma/yelp/exceptions/YelpOperationFailedException.java b/src/main/java/tech/redroma/yelp/exceptions/YelpOperationFailedException.java index 19dba40..2c4d220 100644 --- a/src/main/java/tech/redroma/yelp/exceptions/YelpOperationFailedException.java +++ b/src/main/java/tech/redroma/yelp/exceptions/YelpOperationFailedException.java @@ -17,7 +17,13 @@ package tech.redroma.yelp.exceptions; /** - * + * Thrown when an operation fails and could not be completed. + * + *
+ * + 500 error from Yelp's API
+ * + Network issue
+ * + Unexpected condition
+ * 
* @author SirWellington */ public class YelpOperationFailedException extends YelpExcetion From bc94a5f9e76a2a0b6a509a41134be7928d587063 Mon Sep 17 00:00:00 2001 From: Wellington Moreno Date: Fri, 2 Dec 2016 11:25:26 -0800 Subject: [PATCH 108/168] Adds unit tests: YelpSearchRequestBuilderTest --- .../tech/redroma/yelp/YelpSearchRequest.java | 2 +- .../yelp/YelpSearchRequestBuilderTest.java | 58 +++++++++++++++++++ 2 files changed, 59 insertions(+), 1 deletion(-) create mode 100644 src/test/java/tech/redroma/yelp/YelpSearchRequestBuilderTest.java diff --git a/src/main/java/tech/redroma/yelp/YelpSearchRequest.java b/src/main/java/tech/redroma/yelp/YelpSearchRequest.java index 33fd4ab..e276580 100644 --- a/src/main/java/tech/redroma/yelp/YelpSearchRequest.java +++ b/src/main/java/tech/redroma/yelp/YelpSearchRequest.java @@ -924,7 +924,7 @@ private void checkThatOnlyOneOfOpenNowOrOpenAtIsSet() { if (openAt != null && isOpenNow != null) { - throw new IllegalArgumentException("You can use 'open_now' or 'open_at', but not both."); + throw new YelpBadArgumentException("You can use 'open_now' or 'open_at', but not both."); } } } diff --git a/src/test/java/tech/redroma/yelp/YelpSearchRequestBuilderTest.java b/src/test/java/tech/redroma/yelp/YelpSearchRequestBuilderTest.java new file mode 100644 index 0000000..52a24e0 --- /dev/null +++ b/src/test/java/tech/redroma/yelp/YelpSearchRequestBuilderTest.java @@ -0,0 +1,58 @@ +/* + * Copyright 2016 RedRoma, Inc.. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + + +package tech.redroma.yelp; + + +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import tech.redroma.yelp.exceptions.YelpBadArgumentException; +import tech.sirwellington.alchemy.test.junit.runners.AlchemyTestRunner; + +import static tech.sirwellington.alchemy.test.junit.ThrowableAssertion.assertThrows; + +/** + * + * @author SirWellington + */ +@RunWith(AlchemyTestRunner.class) +public class YelpSearchRequestBuilderTest +{ + private YelpSearchRequest.Builder instance; + + @Before + public void setUp() + { + instance = YelpSearchRequest.Builder.newInstance(); + } + + @Test + public void testBuildWithNoParameters() + { + assertThrows(() -> instance.build()).isInstanceOf(YelpBadArgumentException.class); + } + + @Test + public void testWhenBothOpenAtAndOpenNowAreSet() + { + instance.lookingForOpenNow() + .withBusinessesOpenAt(10); + + assertThrows(() -> instance.build()).isInstanceOf(YelpBadArgumentException.class); + } +} From 387d898ebf60ba4e84449a46c9dcc33ae13bbc3c Mon Sep 17 00:00:00 2001 From: Wellington Moreno Date: Fri, 2 Dec 2016 16:09:29 -0800 Subject: [PATCH 109/168] Adds a NO_OP YelpAPI --- src/main/java/tech/redroma/yelp/NoOpYelp.java | 50 +++++++++++++++++++ src/main/java/tech/redroma/yelp/YelpAPI.java | 3 ++ .../yelp/YelpSearchRequestBuilderTest.java | 8 +++ 3 files changed, 61 insertions(+) create mode 100644 src/main/java/tech/redroma/yelp/NoOpYelp.java diff --git a/src/main/java/tech/redroma/yelp/NoOpYelp.java b/src/main/java/tech/redroma/yelp/NoOpYelp.java new file mode 100644 index 0000000..47e4f68 --- /dev/null +++ b/src/main/java/tech/redroma/yelp/NoOpYelp.java @@ -0,0 +1,50 @@ +/* + * Copyright 2016 RedRoma, Inc.. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + + +package tech.redroma.yelp; + + +import java.util.List; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import sir.wellington.alchemy.collections.lists.Lists; +import tech.redroma.yelp.exceptions.YelpExcetion; +import tech.sirwellington.alchemy.annotations.access.Internal; + +/** + * A No-Op implementation of {@link YelpAPI} that returns no results. + * + * @author SirWellington + */ +@Internal +final class NoOpYelp implements YelpAPI +{ + private final static Logger LOG = LoggerFactory.getLogger(NoOpYelp.class); + + @Override + public YelpBusinessDetails getBusinessDetails(String businessId) throws YelpExcetion + { + return null; + } + + @Override + public List searchForBusinesses(YelpSearchRequest request) throws YelpExcetion + { + return Lists.emptyList(); + } + +} diff --git a/src/main/java/tech/redroma/yelp/YelpAPI.java b/src/main/java/tech/redroma/yelp/YelpAPI.java index 28a5b55..bf494dd 100644 --- a/src/main/java/tech/redroma/yelp/YelpAPI.java +++ b/src/main/java/tech/redroma/yelp/YelpAPI.java @@ -111,6 +111,9 @@ static YelpAPI newInstance(@NonEmpty String cliendId, @NonEmpty String clientSec .build(); } + /** A No-Op Instance that returns no results. Suitable for testing purposes. */ + static YelpAPI NO_OP = new NoOpYelp(); + /** * Use to create a more customized {@link YelpAPI}. */ diff --git a/src/test/java/tech/redroma/yelp/YelpSearchRequestBuilderTest.java b/src/test/java/tech/redroma/yelp/YelpSearchRequestBuilderTest.java index 52a24e0..598eb3b 100644 --- a/src/test/java/tech/redroma/yelp/YelpSearchRequestBuilderTest.java +++ b/src/test/java/tech/redroma/yelp/YelpSearchRequestBuilderTest.java @@ -23,6 +23,7 @@ import org.junit.runner.RunWith; import tech.redroma.yelp.exceptions.YelpBadArgumentException; import tech.sirwellington.alchemy.test.junit.runners.AlchemyTestRunner; +import tech.sirwellington.alchemy.test.junit.runners.GenerateString; import static tech.sirwellington.alchemy.test.junit.ThrowableAssertion.assertThrows; @@ -35,6 +36,13 @@ public class YelpSearchRequestBuilderTest { private YelpSearchRequest.Builder instance; + @GenerateString + private String cliendId; + + @GenerateString + private String cliendSecret; + + @Before public void setUp() { From 3e7d78f178f8072fc01caaf1e95928d8694dd007 Mon Sep 17 00:00:00 2001 From: Wellington Moreno Date: Fri, 2 Dec 2016 16:10:38 -0800 Subject: [PATCH 110/168] Adds unit tests: NoOpYelpIT --- .../java/tech/redroma/yelp/NoOpYelpIT.java | 78 +++++++++++++++++++ 1 file changed, 78 insertions(+) create mode 100644 src/test/java/tech/redroma/yelp/NoOpYelpIT.java diff --git a/src/test/java/tech/redroma/yelp/NoOpYelpIT.java b/src/test/java/tech/redroma/yelp/NoOpYelpIT.java new file mode 100644 index 0000000..5e63e2a --- /dev/null +++ b/src/test/java/tech/redroma/yelp/NoOpYelpIT.java @@ -0,0 +1,78 @@ +/* + * Copyright 2016 RedRoma, Inc.. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package tech.redroma.yelp; + +import java.util.List; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import tech.sirwellington.alchemy.test.junit.runners.AlchemyTestRunner; +import tech.sirwellington.alchemy.test.junit.runners.GenerateString; +import tech.sirwellington.alchemy.test.junit.runners.Repeat; + +import static org.junit.Assert.*; +import static org.hamcrest.Matchers.*; + +/** + * + * @author SirWellington + */ +@Repeat(10) +@RunWith(AlchemyTestRunner.class) +public class NoOpYelpIT +{ + @GenerateString + private String businessId; + + private NoOpYelp instance; + + @Before + public void setUp() throws Exception + { + instance = new NoOpYelp(); + setupData(); + setupMocks(); + } + + + private void setupData() throws Exception + { + + } + + private void setupMocks() throws Exception + { + + } + + @Test + public void testGetBusinessDetails() + { + YelpBusinessDetails result = instance.getBusinessDetails(businessId); + assertThat(result, nullValue()); + } + + @Test + public void testSearchForBusinesses() + { + List results = instance.searchForBusinesses(null); + assertThat(results, notNullValue()); + assertThat(results, empty()); + + } + +} \ No newline at end of file From 664cefc20ab22b4e86fcec3c91206ef7acab0f30 Mon Sep 17 00:00:00 2001 From: Wellington Moreno Date: Fri, 2 Dec 2016 16:10:52 -0800 Subject: [PATCH 111/168] Renames unit test --- .../tech/redroma/yelp/{NoOpYelpIT.java => NoOpYelpTest.java} | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) rename src/test/java/tech/redroma/yelp/{NoOpYelpIT.java => NoOpYelpTest.java} (98%) diff --git a/src/test/java/tech/redroma/yelp/NoOpYelpIT.java b/src/test/java/tech/redroma/yelp/NoOpYelpTest.java similarity index 98% rename from src/test/java/tech/redroma/yelp/NoOpYelpIT.java rename to src/test/java/tech/redroma/yelp/NoOpYelpTest.java index 5e63e2a..9d728e5 100644 --- a/src/test/java/tech/redroma/yelp/NoOpYelpIT.java +++ b/src/test/java/tech/redroma/yelp/NoOpYelpTest.java @@ -24,8 +24,8 @@ import tech.sirwellington.alchemy.test.junit.runners.GenerateString; import tech.sirwellington.alchemy.test.junit.runners.Repeat; -import static org.junit.Assert.*; import static org.hamcrest.Matchers.*; +import static org.junit.Assert.*; /** * @@ -33,7 +33,7 @@ */ @Repeat(10) @RunWith(AlchemyTestRunner.class) -public class NoOpYelpIT +public class NoOpYelpTest { @GenerateString private String businessId; From fd3f01d4c133e80c9a29edbce56d4fb4fc859bf1 Mon Sep 17 00:00:00 2001 From: Wellington Moreno Date: Sat, 3 Dec 2016 18:32:57 -0800 Subject: [PATCH 112/168] Adds unit tests: YelpAPITest --- .../java/tech/redroma/yelp/YelpAPITest.java | 54 +++++++------------ 1 file changed, 20 insertions(+), 34 deletions(-) diff --git a/src/test/java/tech/redroma/yelp/YelpAPITest.java b/src/test/java/tech/redroma/yelp/YelpAPITest.java index 7a8b769..bd8b15c 100644 --- a/src/test/java/tech/redroma/yelp/YelpAPITest.java +++ b/src/test/java/tech/redroma/yelp/YelpAPITest.java @@ -16,20 +16,18 @@ package tech.redroma.yelp; -import java.util.List; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; -import org.mockito.runners.MockitoJUnitRunner; -import org.mockito.Mock; -import tech.redroma.yelp.exceptions.YelpExcetion; import tech.sirwellington.alchemy.test.junit.runners.AlchemyTestRunner; import tech.sirwellington.alchemy.test.junit.runners.DontRepeat; +import tech.sirwellington.alchemy.test.junit.runners.GenerateString; import tech.sirwellington.alchemy.test.junit.runners.Repeat; -import static org.junit.Assert.*; -import static org.hamcrest.Matchers.*; -import static org.mockito.Mockito.*; +import static org.hamcrest.Matchers.notNullValue; +import static org.junit.Assert.assertThat; +import static tech.sirwellington.alchemy.test.junit.ThrowableAssertion.assertThrows; + /** * @@ -39,7 +37,13 @@ @RunWith(AlchemyTestRunner.class) public class YelpAPITest { - + @GenerateString + private String cliendId; + + @GenerateString + private String cliendSecret; + + @Before public void setUp() throws Exception { @@ -59,38 +63,20 @@ private void setupMocks() throws Exception } - @Test - public void testGetBusinessDetails_YelpBusiness() - { - } - - @Test - public void testGetBusinessDetails_String() - { - } - - @Test - public void testSearchForBusinesses() - { - } @Test public void testNewInstance() { + YelpAPI result = YelpAPI.newInstance(cliendId, cliendSecret); + assertThat(result, notNullValue()); } - - public class YelpAPIImpl implements YelpAPI + + @DontRepeat + @Test + public void testNewInstanceWithBadArgs() { - - public YelpBusinessDetails getBusinessDetails(String businessId) throws YelpExcetion - { - return null; - } - - public List searchForBusinesses(YelpSearchRequest request) throws YelpExcetion - { - return null; - } + assertThrows(() -> YelpAPI.newInstance("", cliendSecret)).isInstanceOf(IllegalArgumentException.class); + assertThrows(() -> YelpAPI.newInstance(cliendId, "")).isInstanceOf(IllegalArgumentException.class); } } \ No newline at end of file From 4aad9a25b62791cb217b67ec9fb1d55110c77934 Mon Sep 17 00:00:00 2001 From: Wellington Moreno Date: Sun, 4 Dec 2016 00:28:01 -0800 Subject: [PATCH 113/168] Adds hasCountry() to Address --- src/main/java/tech/redroma/yelp/Address.java | 5 +++++ src/test/java/tech/redroma/yelp/AddressTest.java | 8 ++++++++ 2 files changed, 13 insertions(+) diff --git a/src/main/java/tech/redroma/yelp/Address.java b/src/main/java/tech/redroma/yelp/Address.java index f51c8d1..9064882 100644 --- a/src/main/java/tech/redroma/yelp/Address.java +++ b/src/main/java/tech/redroma/yelp/Address.java @@ -61,6 +61,11 @@ public boolean hasZipCode() return !isNullOrEmpty(zipCode); } + public boolean hasCountry() + { + return !isNullOrEmpty(country); + } + @Override public int hashCode() { diff --git a/src/test/java/tech/redroma/yelp/AddressTest.java b/src/test/java/tech/redroma/yelp/AddressTest.java index f48cab7..f741a59 100644 --- a/src/test/java/tech/redroma/yelp/AddressTest.java +++ b/src/test/java/tech/redroma/yelp/AddressTest.java @@ -112,4 +112,12 @@ public void testHasZipCode() assertFalse(instance.hasZipCode()); } + @Test + public void testHasCountry() + { + assertTrue(instance.hasCountry()); + instance.country = null; + assertFalse(instance.hasCountry()); + } + } From 3ada0e95cd5af54029056d4ed676f7217005d4b3 Mon Sep 17 00:00:00 2001 From: Wellington Moreno Date: Sun, 4 Dec 2016 00:29:48 -0800 Subject: [PATCH 114/168] Adds hasState() and hasCity() to Address --- src/main/java/tech/redroma/yelp/Address.java | 10 ++++++++++ src/test/java/tech/redroma/yelp/AddressTest.java | 16 ++++++++++++++++ 2 files changed, 26 insertions(+) diff --git a/src/main/java/tech/redroma/yelp/Address.java b/src/main/java/tech/redroma/yelp/Address.java index 9064882..e244a49 100644 --- a/src/main/java/tech/redroma/yelp/Address.java +++ b/src/main/java/tech/redroma/yelp/Address.java @@ -61,6 +61,16 @@ public boolean hasZipCode() return !isNullOrEmpty(zipCode); } + public boolean hasCity() + { + return !isNullOrEmpty(city); + } + + public boolean hasState() + { + return !isNullOrEmpty(state); + } + public boolean hasCountry() { return !isNullOrEmpty(country); diff --git a/src/test/java/tech/redroma/yelp/AddressTest.java b/src/test/java/tech/redroma/yelp/AddressTest.java index f741a59..319c7f1 100644 --- a/src/test/java/tech/redroma/yelp/AddressTest.java +++ b/src/test/java/tech/redroma/yelp/AddressTest.java @@ -120,4 +120,20 @@ public void testHasCountry() assertFalse(instance.hasCountry()); } + @Test + public void testHasCity() + { + assertTrue(instance.hasCity()); + instance.city = null; + assertFalse(instance.hasCity()); + } + + @Test + public void testHasState() + { + assertTrue(instance.hasState()); + instance.state = null; + assertFalse(instance.hasState()); + } + } From 70f86cede011c63f8d454048e3301277b26e7f7b Mon Sep 17 00:00:00 2001 From: Wellington Moreno Date: Sun, 4 Dec 2016 00:30:30 -0800 Subject: [PATCH 115/168] Updates YelpSearchRequestBuilder with additional checks + In method withLocation() --- .../tech/redroma/yelp/YelpSearchRequest.java | 17 ++++++++++++++--- 1 file changed, 14 insertions(+), 3 deletions(-) diff --git a/src/main/java/tech/redroma/yelp/YelpSearchRequest.java b/src/main/java/tech/redroma/yelp/YelpSearchRequest.java index e276580..a0819ad 100644 --- a/src/main/java/tech/redroma/yelp/YelpSearchRequest.java +++ b/src/main/java/tech/redroma/yelp/YelpSearchRequest.java @@ -558,9 +558,20 @@ public Builder withLocation(@Required Address address) throws IllegalArgumentExc text += " " + address.address3; } - text += " " + address.city; - text += " " + address.state; - text += " " + address.country; + if (address.hasCity()) + { + text += " " + address.city; + } + + if (address.hasState()) + { + text += " " + address.state; + } + + if (address.hasCountry()) + { + text += " " + address.country; + } if (address.hasZipCode()) { From 1e05c00d2006c54802b343a124e82af8aae16e46 Mon Sep 17 00:00:00 2001 From: Wellington Moreno Date: Sun, 4 Dec 2016 08:53:55 -0800 Subject: [PATCH 116/168] Adds hasAddress1() to Address --- src/main/java/tech/redroma/yelp/Address.java | 5 +++++ src/test/java/tech/redroma/yelp/AddressTest.java | 12 ++++++++++-- 2 files changed, 15 insertions(+), 2 deletions(-) diff --git a/src/main/java/tech/redroma/yelp/Address.java b/src/main/java/tech/redroma/yelp/Address.java index e244a49..8c4039e 100644 --- a/src/main/java/tech/redroma/yelp/Address.java +++ b/src/main/java/tech/redroma/yelp/Address.java @@ -46,6 +46,11 @@ public class Address @SerializedName("zip_code") public String zipCode; + public boolean hasAddress1() + { + return !isNullOrEmpty(address1); + } + public boolean hasAddress2() { return !isNullOrEmpty(address2); diff --git a/src/test/java/tech/redroma/yelp/AddressTest.java b/src/test/java/tech/redroma/yelp/AddressTest.java index 319c7f1..31730f8 100644 --- a/src/test/java/tech/redroma/yelp/AddressTest.java +++ b/src/test/java/tech/redroma/yelp/AddressTest.java @@ -59,7 +59,7 @@ public void setUp() throws Exception public void testInstance() { assertThat(instance, notNullValue()); - + assertThat(instance.address1, not(isEmptyOrNullString())); assertThat(instance.address2, not(isEmptyOrNullString())); assertThat(instance.address3, not(isEmptyOrNullString())); @@ -68,7 +68,7 @@ public void testInstance() assertThat(instance.country, not(isEmptyOrNullString())); assertThat(instance.zipCode, not(isEmptyOrNullString())); } - + @Test public void testEqualsWhenNotEqual() { @@ -88,6 +88,14 @@ public void testHashCode() assertThat(first.hashCode(), not(second.hashCode())); } + @Test + public void testHasAddress1() + { + assertTrue(instance.hasAddress1()); + instance.address1 = null; + assertFalse(instance.hasAddress1()); + } + @Test public void testHasAddress2() { From abee889063156b7fb0076e2895dcdebff89b35f8 Mon Sep 17 00:00:00 2001 From: Wellington Moreno Date: Sun, 4 Dec 2016 12:41:40 -0800 Subject: [PATCH 117/168] Adds documentation --- src/main/java/tech/redroma/yelp/Category.java | 4 +-- .../java/tech/redroma/yelp/Coordinate.java | 12 ++++--- src/main/java/tech/redroma/yelp/YelpAPI.java | 32 ++++++++++++------- 3 files changed, 29 insertions(+), 19 deletions(-) diff --git a/src/main/java/tech/redroma/yelp/Category.java b/src/main/java/tech/redroma/yelp/Category.java index 649e6d5..654f639 100644 --- a/src/main/java/tech/redroma/yelp/Category.java +++ b/src/main/java/tech/redroma/yelp/Category.java @@ -27,8 +27,8 @@ import static tech.sirwellington.alchemy.arguments.assertions.StringAssertions.nonEmptyString; /** - * The category of a business, for example "Grocery", "Restaurant". - * + * The category of a business, for example "Grocery", "Restaurant", etc. + *

* Can be {@linkplain YelpBusiness#categories returned by Yelp} or * {@linkplain YelpAPI#searchForBusinesses(tech.redroma.yelp.YelpSearchRequest) used in a Search request}. * diff --git a/src/main/java/tech/redroma/yelp/Coordinate.java b/src/main/java/tech/redroma/yelp/Coordinate.java index 6fec7ea..d3dc4be 100644 --- a/src/main/java/tech/redroma/yelp/Coordinate.java +++ b/src/main/java/tech/redroma/yelp/Coordinate.java @@ -27,7 +27,8 @@ import static tech.sirwellington.alchemy.arguments.assertions.GeolocationAssertions.validLongitude; /** - * + * Represents a Geo-Coordinate of latitude and longitude; + * * @author SirWellington */ @Pojo @@ -52,11 +53,12 @@ public Coordinate(double latitude, double longitude) } /** - * - * @param latitude A valid latitude - * @param longitude A valid longitude + * Creates a {@code Coordinate} object from the specified latitude and longitude. + * + * @param latitude A valid latitude: {@code (-90...90)}. + * @param longitude A valid longitude: {@code (-180...180)}. * @return - * @throws IllegalArgumentException + * @throws IllegalArgumentException If the coordinate is invalid. */ public static Coordinate of(double latitude, double longitude) throws IllegalArgumentException { diff --git a/src/main/java/tech/redroma/yelp/YelpAPI.java b/src/main/java/tech/redroma/yelp/YelpAPI.java index bf494dd..2fe7131 100644 --- a/src/main/java/tech/redroma/yelp/YelpAPI.java +++ b/src/main/java/tech/redroma/yelp/YelpAPI.java @@ -49,14 +49,19 @@ public interface YelpAPI { /** - * Returns the detail information of a business. Normally, you'll get the business id from the - * {@linkplain #searchForBusinesses(tech.redroma.yelp.YelpSearchRequest) search API}. To get - * review information for a business, refer to ... - * + * Returns detailed information of a business. This includes things like times-of-operation and additional photos. + *

+ * Normally, you'll get the {@link YelpBusiness} object from the + * {@linkplain #searchForBusinesses(tech.redroma.yelp.YelpSearchRequest) search API}. + *

+ * To get review information for a business, refer to ... + * * @param business The business to get more details of. * @return - * @throws YelpExcetion - * @see #getBusinessDetails(java.lang.String) + * @throws YelpExcetion + * + * @see YelpBusinessDetails + * @see #getBusinessDetails(java.lang.String) */ default YelpBusinessDetails getBusinessDetails(@Required YelpBusiness business) throws YelpExcetion { @@ -64,18 +69,21 @@ default YelpBusinessDetails getBusinessDetails(@Required YelpBusiness business) .throwing(YelpBadArgumentException.class) .usingMessage("business cannot be null") .is(notNull()); - + checkThat(business.id) .usingMessage("business is missing it's id") .is(nonEmptyString()); - + return getBusinessDetails(business.id); } - + /** - * Returns the detail information of a business. Normally, you'll get the business id from - * the {@linkplain #searchForBusinesses(tech.redroma.yelp.YelpSearchRequest) search API}. - * TO get review information for a business, refer to ... + * Returns detailed information of a business. This includes things like times-of-operation and additional photos. + *

+ * Normally, you'll get the business id from the + * {@linkplain #searchForBusinesses(tech.redroma.yelp.YelpSearchRequest) search API}. + *

+ * To get review information for a business, refer to ... * * @param businessId * @return From 60efc7d456d426fc5ef1e70cff611352c7c70472 Mon Sep 17 00:00:00 2001 From: Wellington Moreno Date: Mon, 5 Dec 2016 22:45:45 -0800 Subject: [PATCH 118/168] Updates and improves error messages --- src/main/java/tech/redroma/yelp/YelpAPIImpl.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/main/java/tech/redroma/yelp/YelpAPIImpl.java b/src/main/java/tech/redroma/yelp/YelpAPIImpl.java index 8b93156..8083b19 100644 --- a/src/main/java/tech/redroma/yelp/YelpAPIImpl.java +++ b/src/main/java/tech/redroma/yelp/YelpAPIImpl.java @@ -121,7 +121,7 @@ public List searchForBusinesses(YelpSearchRequest request) throws throw new YelpBadArgumentException(ex); } - throw new YelpOperationFailedException(format("Failed to make serach request at %s with [%s]", url, request), ex); + throw new YelpOperationFailedException(format("Failed to make search request at %s with [%s]", url, request), ex); } catch (Exception ex) { @@ -132,6 +132,7 @@ public List searchForBusinesses(YelpSearchRequest request) throws if (response == null) { LOG.warn("Received null response from Yelp at {} for request {}", url, request); + throw new YelpOperationFailedException("Received null response from yelp at:" + url); } List results = Lists.nullToEmpty(response.businesses); From 6ba9e8ffbe46c16ba578c4a0083be68550d0d9aa Mon Sep 17 00:00:00 2001 From: Wellington Moreno Date: Mon, 5 Dec 2016 22:49:20 -0800 Subject: [PATCH 119/168] Fixes incorrectly named exception to 'YelpException' --- src/main/java/tech/redroma/yelp/NoOpYelp.java | 6 +++--- src/main/java/tech/redroma/yelp/YelpAPI.java | 15 ++++++++------- src/main/java/tech/redroma/yelp/YelpAPIImpl.java | 8 +++++--- .../exceptions/YelpAreaTooLargeException.java | 2 +- .../exceptions/YelpAuthenticationException.java | 2 +- .../yelp/exceptions/YelpBadArgumentException.java | 2 +- .../{YelpExcetion.java => YelpException.java} | 10 +++++----- .../exceptions/YelpOperationFailedException.java | 2 +- .../java/tech/redroma/yelp/YelpAPIImplTest.java | 6 +++--- 9 files changed, 28 insertions(+), 25 deletions(-) rename src/main/java/tech/redroma/yelp/exceptions/{YelpExcetion.java => YelpException.java} (79%) diff --git a/src/main/java/tech/redroma/yelp/NoOpYelp.java b/src/main/java/tech/redroma/yelp/NoOpYelp.java index 47e4f68..3b9c098 100644 --- a/src/main/java/tech/redroma/yelp/NoOpYelp.java +++ b/src/main/java/tech/redroma/yelp/NoOpYelp.java @@ -22,7 +22,7 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; import sir.wellington.alchemy.collections.lists.Lists; -import tech.redroma.yelp.exceptions.YelpExcetion; +import tech.redroma.yelp.exceptions.YelpException; import tech.sirwellington.alchemy.annotations.access.Internal; /** @@ -36,13 +36,13 @@ final class NoOpYelp implements YelpAPI private final static Logger LOG = LoggerFactory.getLogger(NoOpYelp.class); @Override - public YelpBusinessDetails getBusinessDetails(String businessId) throws YelpExcetion + public YelpBusinessDetails getBusinessDetails(String businessId) throws YelpException { return null; } @Override - public List searchForBusinesses(YelpSearchRequest request) throws YelpExcetion + public List searchForBusinesses(YelpSearchRequest request) throws YelpException { return Lists.emptyList(); } diff --git a/src/main/java/tech/redroma/yelp/YelpAPI.java b/src/main/java/tech/redroma/yelp/YelpAPI.java index 2fe7131..1d6640a 100644 --- a/src/main/java/tech/redroma/yelp/YelpAPI.java +++ b/src/main/java/tech/redroma/yelp/YelpAPI.java @@ -22,7 +22,7 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; import tech.redroma.yelp.exceptions.YelpBadArgumentException; -import tech.redroma.yelp.exceptions.YelpExcetion; +import tech.redroma.yelp.exceptions.YelpException; import tech.redroma.yelp.oauth.OAuthTokenProvider; import tech.sirwellington.alchemy.annotations.arguments.NonEmpty; import tech.sirwellington.alchemy.annotations.arguments.Required; @@ -36,6 +36,7 @@ import static tech.sirwellington.alchemy.arguments.assertions.NetworkAssertions.validURL; import static tech.sirwellington.alchemy.arguments.assertions.StringAssertions.nonEmptyString; import static tech.sirwellington.alchemy.arguments.assertions.StringAssertions.stringWithLengthGreaterThanOrEqualTo; +import static tech.sirwellington.alchemy.arguments.Arguments.checkThat; /** * The interface used to interact with Yelp's Developer API. @@ -58,12 +59,12 @@ public interface YelpAPI * * @param business The business to get more details of. * @return - * @throws YelpExcetion + * @throws YelpException * * @see YelpBusinessDetails * @see #getBusinessDetails(java.lang.String) */ - default YelpBusinessDetails getBusinessDetails(@Required YelpBusiness business) throws YelpExcetion + default YelpBusinessDetails getBusinessDetails(@Required YelpBusiness business) throws YelpException { checkThat(business) .throwing(YelpBadArgumentException.class) @@ -87,12 +88,12 @@ default YelpBusinessDetails getBusinessDetails(@Required YelpBusiness business) * * @param businessId * @return - * @throws YelpExcetion + * @throws YelpException * @see #getBusinessDetails(java.lang.String) * @see #searchForBusinesses(tech.redroma.yelp.YelpSearchRequest) */ @Required - YelpBusinessDetails getBusinessDetails(@NonEmpty String businessId) throws YelpExcetion; + YelpBusinessDetails getBusinessDetails(@NonEmpty String businessId) throws YelpException; /** * Returns up to 1,000 businesses based on the provided search criteria. It has some basic information about the businesses @@ -102,12 +103,12 @@ default YelpBusinessDetails getBusinessDetails(@Required YelpBusiness business) * * @param request * @return - * @throws YelpExcetion + * @throws YelpException * @see #getBusinessDetails(java.lang.String) * @see #getBusinessDetails(tech.redroma.yelp.YelpBusiness) * @see YelpSearchRequest */ - List searchForBusinesses(@Required YelpSearchRequest request) throws YelpExcetion; + List searchForBusinesses(@Required YelpSearchRequest request) throws YelpException; static YelpAPI newInstance(@NonEmpty String cliendId, @NonEmpty String clientSecret) { diff --git a/src/main/java/tech/redroma/yelp/YelpAPIImpl.java b/src/main/java/tech/redroma/yelp/YelpAPIImpl.java index 8083b19..e172cc6 100644 --- a/src/main/java/tech/redroma/yelp/YelpAPIImpl.java +++ b/src/main/java/tech/redroma/yelp/YelpAPIImpl.java @@ -24,7 +24,7 @@ import sir.wellington.alchemy.collections.lists.Lists; import tech.redroma.yelp.exceptions.YelpAuthenticationException; import tech.redroma.yelp.exceptions.YelpBadArgumentException; -import tech.redroma.yelp.exceptions.YelpExcetion; +import tech.redroma.yelp.exceptions.YelpException; import tech.redroma.yelp.exceptions.YelpOperationFailedException; import tech.redroma.yelp.oauth.OAuthTokenProvider; import tech.sirwellington.alchemy.annotations.access.Internal; @@ -38,6 +38,8 @@ import static tech.sirwellington.alchemy.arguments.assertions.Assertions.notNull; import static tech.sirwellington.alchemy.arguments.assertions.NetworkAssertions.validURL; import static tech.sirwellington.alchemy.arguments.assertions.StringAssertions.nonEmptyString; +import static java.lang.String.format; +import static tech.sirwellington.alchemy.arguments.Arguments.checkThat; /** * This internal class is responsible for implementing the business logic necessary for making Yelp API Queries. It implements all @@ -70,7 +72,7 @@ final class YelpAPIImpl implements YelpAPI } @Override - public YelpBusinessDetails getBusinessDetails(String businessId) throws YelpExcetion + public YelpBusinessDetails getBusinessDetails(String businessId) throws YelpException { checkThat(businessId) .throwing(YelpBadArgumentException.class) @@ -86,7 +88,7 @@ public YelpBusinessDetails getBusinessDetails(String businessId) throws YelpExce } @Override - public List searchForBusinesses(YelpSearchRequest request) throws YelpExcetion + public List searchForBusinesses(YelpSearchRequest request) throws YelpException { String token = tokenProvider.getToken(); diff --git a/src/main/java/tech/redroma/yelp/exceptions/YelpAreaTooLargeException.java b/src/main/java/tech/redroma/yelp/exceptions/YelpAreaTooLargeException.java index 55c396d..81eea22 100644 --- a/src/main/java/tech/redroma/yelp/exceptions/YelpAreaTooLargeException.java +++ b/src/main/java/tech/redroma/yelp/exceptions/YelpAreaTooLargeException.java @@ -21,7 +21,7 @@ * * @author SirWellington */ -public class YelpAreaTooLargeException extends YelpExcetion +public class YelpAreaTooLargeException extends YelpException { public YelpAreaTooLargeException() diff --git a/src/main/java/tech/redroma/yelp/exceptions/YelpAuthenticationException.java b/src/main/java/tech/redroma/yelp/exceptions/YelpAuthenticationException.java index a9cccb2..94c621d 100644 --- a/src/main/java/tech/redroma/yelp/exceptions/YelpAuthenticationException.java +++ b/src/main/java/tech/redroma/yelp/exceptions/YelpAuthenticationException.java @@ -28,7 +28,7 @@ * * @author SirWellington */ -public class YelpAuthenticationException extends YelpExcetion +public class YelpAuthenticationException extends YelpException { public YelpAuthenticationException() diff --git a/src/main/java/tech/redroma/yelp/exceptions/YelpBadArgumentException.java b/src/main/java/tech/redroma/yelp/exceptions/YelpBadArgumentException.java index 6a94c13..90aad32 100644 --- a/src/main/java/tech/redroma/yelp/exceptions/YelpBadArgumentException.java +++ b/src/main/java/tech/redroma/yelp/exceptions/YelpBadArgumentException.java @@ -21,7 +21,7 @@ * * @author SirWellington */ -public class YelpBadArgumentException extends YelpExcetion +public class YelpBadArgumentException extends YelpException { public YelpBadArgumentException() diff --git a/src/main/java/tech/redroma/yelp/exceptions/YelpExcetion.java b/src/main/java/tech/redroma/yelp/exceptions/YelpException.java similarity index 79% rename from src/main/java/tech/redroma/yelp/exceptions/YelpExcetion.java rename to src/main/java/tech/redroma/yelp/exceptions/YelpException.java index bae6efe..726f264 100644 --- a/src/main/java/tech/redroma/yelp/exceptions/YelpExcetion.java +++ b/src/main/java/tech/redroma/yelp/exceptions/YelpException.java @@ -21,24 +21,24 @@ * * @author SirWellington */ -public class YelpExcetion extends RuntimeException +public class YelpException extends RuntimeException { - public YelpExcetion() + public YelpException() { } - public YelpExcetion(String message) + public YelpException(String message) { super(message); } - public YelpExcetion(String message, Throwable cause) + public YelpException(String message, Throwable cause) { super(message, cause); } - public YelpExcetion(Throwable cause) + public YelpException(Throwable cause) { super(cause); } diff --git a/src/main/java/tech/redroma/yelp/exceptions/YelpOperationFailedException.java b/src/main/java/tech/redroma/yelp/exceptions/YelpOperationFailedException.java index 2c4d220..7d2ee08 100644 --- a/src/main/java/tech/redroma/yelp/exceptions/YelpOperationFailedException.java +++ b/src/main/java/tech/redroma/yelp/exceptions/YelpOperationFailedException.java @@ -26,7 +26,7 @@ * * @author SirWellington */ -public class YelpOperationFailedException extends YelpExcetion +public class YelpOperationFailedException extends YelpException { public YelpOperationFailedException() diff --git a/src/test/java/tech/redroma/yelp/YelpAPIImplTest.java b/src/test/java/tech/redroma/yelp/YelpAPIImplTest.java index 95420f1..c7b29bf 100644 --- a/src/test/java/tech/redroma/yelp/YelpAPIImplTest.java +++ b/src/test/java/tech/redroma/yelp/YelpAPIImplTest.java @@ -23,7 +23,7 @@ import org.junit.runner.RunWith; import org.mockito.Mock; import tech.redroma.yelp.exceptions.YelpAuthenticationException; -import tech.redroma.yelp.exceptions.YelpExcetion; +import tech.redroma.yelp.exceptions.YelpException; import tech.redroma.yelp.oauth.OAuthTokenProvider; import tech.sirwellington.alchemy.http.AlchemyHttp; import tech.sirwellington.alchemy.http.HttpResponse; @@ -163,7 +163,7 @@ public void testGetBusinessDetailsWhenFails() throws Exception instance = new YelpAPIImpl(http, tokenProvider, baseURL.toString()); assertThrows(() -> instance.getBusinessDetails(businessID)) - .isInstanceOf(YelpExcetion.class); + .isInstanceOf(YelpException.class); } @DontRepeat @@ -220,7 +220,7 @@ public void testSearchForBusinessesWhenFails() throws Exception instance = new YelpAPIImpl(http, tokenProvider, baseURL.toString()); assertThrows(() -> instance.searchForBusinesses(request)) - .isInstanceOf(YelpExcetion.class); + .isInstanceOf(YelpException.class); } @DontRepeat From 4075934b46e0aaa48a9ab5aceb3de6b39a9401ff Mon Sep 17 00:00:00 2001 From: Wellington Moreno Date: Tue, 6 Dec 2016 13:15:15 -0800 Subject: [PATCH 120/168] Organizes imports --- src/main/java/tech/redroma/yelp/YelpAPI.java | 1 - 1 file changed, 1 deletion(-) diff --git a/src/main/java/tech/redroma/yelp/YelpAPI.java b/src/main/java/tech/redroma/yelp/YelpAPI.java index 1d6640a..e170f42 100644 --- a/src/main/java/tech/redroma/yelp/YelpAPI.java +++ b/src/main/java/tech/redroma/yelp/YelpAPI.java @@ -36,7 +36,6 @@ import static tech.sirwellington.alchemy.arguments.assertions.NetworkAssertions.validURL; import static tech.sirwellington.alchemy.arguments.assertions.StringAssertions.nonEmptyString; import static tech.sirwellington.alchemy.arguments.assertions.StringAssertions.stringWithLengthGreaterThanOrEqualTo; -import static tech.sirwellington.alchemy.arguments.Arguments.checkThat; /** * The interface used to interact with Yelp's Developer API. From b5bae2dd7f1df09454401ddeecd5d61347e98ca2 Mon Sep 17 00:00:00 2001 From: Wellington Moreno Date: Tue, 6 Dec 2016 13:15:29 -0800 Subject: [PATCH 121/168] Organizes imports --- src/main/java/tech/redroma/yelp/YelpAPIImpl.java | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/main/java/tech/redroma/yelp/YelpAPIImpl.java b/src/main/java/tech/redroma/yelp/YelpAPIImpl.java index e172cc6..1b546ee 100644 --- a/src/main/java/tech/redroma/yelp/YelpAPIImpl.java +++ b/src/main/java/tech/redroma/yelp/YelpAPIImpl.java @@ -38,8 +38,6 @@ import static tech.sirwellington.alchemy.arguments.assertions.Assertions.notNull; import static tech.sirwellington.alchemy.arguments.assertions.NetworkAssertions.validURL; import static tech.sirwellington.alchemy.arguments.assertions.StringAssertions.nonEmptyString; -import static java.lang.String.format; -import static tech.sirwellington.alchemy.arguments.Arguments.checkThat; /** * This internal class is responsible for implementing the business logic necessary for making Yelp API Queries. It implements all From 255cd0ce2ce9c921ecdbcb40c148dfdad7a9963b Mon Sep 17 00:00:00 2001 From: Wellington Moreno Date: Tue, 6 Dec 2016 13:43:46 -0800 Subject: [PATCH 122/168] #6 - Adds documentation to the YelpAPI Builder --- src/main/java/tech/redroma/yelp/YelpAPI.java | 53 ++++++++++++++++---- 1 file changed, 43 insertions(+), 10 deletions(-) diff --git a/src/main/java/tech/redroma/yelp/YelpAPI.java b/src/main/java/tech/redroma/yelp/YelpAPI.java index e170f42..1c24384 100644 --- a/src/main/java/tech/redroma/yelp/YelpAPI.java +++ b/src/main/java/tech/redroma/yelp/YelpAPI.java @@ -40,7 +40,7 @@ /** * The interface used to interact with Yelp's Developer API. *

- * To create, see {@link #newInstance(java.lang.String, java.lang.String) } or {@link YelpAPI.Builder}. + * To create an instance, see {@link #newInstance(java.lang.String, java.lang.String) } or {@link YelpAPI.Builder}. * * @author SirWellington * @see Yelp API @@ -48,8 +48,13 @@ @BuilderPattern(role = PRODUCT) public interface YelpAPI { + /** The Default Yelp API endpoint */ + + public static final String DEFAULT_BASE_URL = "https://api.yelp.com/v3"; + /** - * Returns detailed information of a business. This includes things like times-of-operation and additional photos. + * Returns detailed information of a business. This includes things like business hours, additional photos, and price + * information. *

* Normally, you'll get the {@link YelpBusiness} object from the * {@linkplain #searchForBusinesses(tech.redroma.yelp.YelpSearchRequest) search API}. @@ -78,14 +83,15 @@ default YelpBusinessDetails getBusinessDetails(@Required YelpBusiness business) } /** - * Returns detailed information of a business. This includes things like times-of-operation and additional photos. + * Returns detailed information of a business. This includes things like business hours, additional photos, and price + * information. *

* Normally, you'll get the business id from the * {@linkplain #searchForBusinesses(tech.redroma.yelp.YelpSearchRequest) search API}. *

* To get review information for a business, refer to ... - * - * @param businessId + * + * @param businessId The {@linkplain YelpBusiness#id Business ID} to query. * @return * @throws YelpException * @see #getBusinessDetails(java.lang.String) @@ -123,38 +129,65 @@ static YelpAPI newInstance(@NonEmpty String cliendId, @NonEmpty String clientSec static YelpAPI NO_OP = new NoOpYelp(); /** - * Use to create a more customized {@link YelpAPI}. + * Used to create a more customized {@link YelpAPI}. + * + * @see #newInstance() + * @author SirWellington */ @BuilderPattern(role = BUILDER) static final class Builder { - private static final String DEFAULT_BASE_URL = "https://api.yelp.com/v3"; + private static final Logger LOG = LoggerFactory.getLogger(Builder.class); + //Used to connect to authenticate calls with Yelp private OAuthTokenProvider oauthProvider; private String baseURL = DEFAULT_BASE_URL; + //The HTTP Client to use; starts with a default client private AlchemyHttp http = AlchemyHttp.newBuilder() .usingTimeout(60, TimeUnit.SECONDS) .build(); - + + //Determins whether an OAuth token is fetched immediately after the client is built private boolean requestTokenImmediately = false; + /** + * Creates a new instance of a Builder. + * @return + */ public static Builder newInstance() { return new Builder(); } + /** + * Sets the base URL to make calls to. Note that this should only be used for testing purposes. + *

+ * If this method isn't called, it defaults to {@link #DEFAULT_BASE_URL}. + * + * @param baseURL The base URL to use when making API calls. Must be a valid URL and cannot be empty. + * @return + * @throws IllegalArgumentException + */ public Builder withBaseURL(@NonEmpty String baseURL) throws IllegalArgumentException { - checkThat(baseURL) - .is(validURL()); + checkThat(baseURL).is(validURL()); this.baseURL = baseURL; return this; } + /** + * Sets the {@linkplain AlchemyHttp HTTP Client} to use when making requests. + * + * @param http The Alchemy HTTP client to use. + * @return + * @throws IllegalArgumentException If the client is null + * @see AlchemyHttp#newInstance(org.apache.http.client.HttpClient, java.util.concurrent.ExecutorService, java.util.Map) + * @see AlchemyHttp.Builder + */ public Builder withHttpClient(@Required AlchemyHttp http) throws IllegalArgumentException { checkThat(http).is(notNull()); From fb57fcead976899c925eb4e4287bcec28c21ba77 Mon Sep 17 00:00:00 2001 From: Wellington Moreno Date: Tue, 6 Dec 2016 13:52:12 -0800 Subject: [PATCH 123/168] #6 - Adds documentation to the YelpAPI Builder --- src/main/java/tech/redroma/yelp/YelpAPI.java | 49 ++++++++++++++++++-- 1 file changed, 45 insertions(+), 4 deletions(-) diff --git a/src/main/java/tech/redroma/yelp/YelpAPI.java b/src/main/java/tech/redroma/yelp/YelpAPI.java index 1c24384..fc26e91 100644 --- a/src/main/java/tech/redroma/yelp/YelpAPI.java +++ b/src/main/java/tech/redroma/yelp/YelpAPI.java @@ -152,10 +152,14 @@ static final class Builder //Determins whether an OAuth token is fetched immediately after the client is built private boolean requestTokenImmediately = false; - + /** * Creates a new instance of a Builder. - * @return + *

+ * The only thing worth customizing is the + * {@linkplain #withClientCredentials(java.lang.String, java.lang.String) OAuth Authentication} + * + * @return */ public static Builder newInstance() { @@ -196,6 +200,16 @@ public Builder withHttpClient(@Required AlchemyHttp http) throws IllegalArgument return this; } + /** + * If you prefer to obtain an OAuth Token yourself, you can provide it using this method. + *

+ * Note that unlike {@link #withClientCredentials(java.lang.String, java.lang.String) }, this token + * will not be reloaded when it expires. + * + * @param oauthToken + * @return + * @throws IllegalArgumentException + */ public Builder withOAuthToken(@NonEmpty String oauthToken) throws IllegalArgumentException { checkThat(oauthToken) @@ -206,6 +220,19 @@ public Builder withOAuthToken(@NonEmpty String oauthToken) throws IllegalArgumen return this; } + /** + * Uses the provided Client ID and Client Secret to obtain an OAuth token from Yelp. + * Note that these are also known as {@code 'App ID'} and {@code 'App Secret'}. + *

+ * See Yelp Documentation for more + * information. + * + * @param cliendId Client ID, also known as 'App ID'. + * @param clientSecret Client Secret, also known as 'App Secret'. + * @return + * @throws IllegalArgumentException + * @see https://www.yelp.com/developers/documentation/v3/get_started + */ public Builder withClientCredentials(@NonEmpty String cliendId, @NonEmpty String clientSecret) throws IllegalArgumentException { checkThat(cliendId, clientSecret) @@ -217,13 +244,27 @@ public Builder withClientCredentials(@NonEmpty String cliendId, @NonEmpty String return this; } + /** + * When used with {@link #withClientCredentials(java.lang.String, java.lang.String) }, ensures that + * an OAuth token is obtained eagerly, before the {@link YelpAPI} is created. + * @return + */ public Builder withEagerAuthentication() { requestTokenImmediately = true; return this; } - - public YelpAPI build() + + /** + * Builds a usable {@link YelpAPI}. + *

+ * Note that the client credentials must be set using either + * {@link #withClientCredentials(java.lang.String, java.lang.String) } or {@link #withOAuthToken(java.lang.String) }. + * + * @return + * @throws IllegalStateException If the Yelp API is not ready to build. + */ + public YelpAPI build() throws IllegalStateException { ensureReadyToBuild(); From 8bd172769ad24f53e52a17e3621bf5c398778a50 Mon Sep 17 00:00:00 2001 From: Wellington Moreno Date: Tue, 6 Dec 2016 13:57:30 -0800 Subject: [PATCH 124/168] #6 - Adds documentation to YelpBusiness --- src/main/java/tech/redroma/yelp/YelpBusiness.java | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/src/main/java/tech/redroma/yelp/YelpBusiness.java b/src/main/java/tech/redroma/yelp/YelpBusiness.java index c2288c3..641418b 100644 --- a/src/main/java/tech/redroma/yelp/YelpBusiness.java +++ b/src/main/java/tech/redroma/yelp/YelpBusiness.java @@ -36,31 +36,42 @@ @ThreadUnsafe public class YelpBusiness { - + /** Yelp ID of this business */ public String id; - + + /** Name of the business */ public String name; + /** URL for the business page on Yelp */ public String url; + /** An integer rating of the business, ranging from (1...5} */ + //TODO: Change this to a double public int rating; public String phone; + /** Determines whether the business has permanently closed */ @SerializedName("is_closed") public Boolean isClosed; + /** A list of categories associated with this business */ public List categories; + /** The number of reviews for this business */ public int reviewCount; + /** The geo-coordinates of this business */ public Coordinate coordinates; + /** The location of this business, including city, state, zip code. */ public Address location; + /** A URL Photo for this business */ @SerializedName("image_url") public String imageURL; + /** The distance, in meters, from the search location */ @Optional public Double distance; From caec6cf003775dc8aa07bad3277a60c93b1d6ef4 Mon Sep 17 00:00:00 2001 From: Wellington Moreno Date: Tue, 6 Dec 2016 13:59:48 -0800 Subject: [PATCH 125/168] #6 - Fixes documentation to the YelpAPI Builder --- src/main/java/tech/redroma/yelp/YelpAPI.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/tech/redroma/yelp/YelpAPI.java b/src/main/java/tech/redroma/yelp/YelpAPI.java index fc26e91..805c6a3 100644 --- a/src/main/java/tech/redroma/yelp/YelpAPI.java +++ b/src/main/java/tech/redroma/yelp/YelpAPI.java @@ -189,8 +189,8 @@ public Builder withBaseURL(@NonEmpty String baseURL) throws IllegalArgumentExcep * @param http The Alchemy HTTP client to use. * @return * @throws IllegalArgumentException If the client is null + * @see AlchemyHttp#newBuilder() * @see AlchemyHttp#newInstance(org.apache.http.client.HttpClient, java.util.concurrent.ExecutorService, java.util.Map) - * @see AlchemyHttp.Builder */ public Builder withHttpClient(@Required AlchemyHttp http) throws IllegalArgumentException { From c25c75d403da5ffcd78e36ac882ac15dda99734f Mon Sep 17 00:00:00 2001 From: Wellington Moreno Date: Tue, 6 Dec 2016 14:10:48 -0800 Subject: [PATCH 126/168] #6 - Fixes documentation to YelpBusinessDetails --- .../tech/redroma/yelp/YelpBusinessDetails.java | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/src/main/java/tech/redroma/yelp/YelpBusinessDetails.java b/src/main/java/tech/redroma/yelp/YelpBusinessDetails.java index bd7267b..afc522a 100644 --- a/src/main/java/tech/redroma/yelp/YelpBusinessDetails.java +++ b/src/main/java/tech/redroma/yelp/YelpBusinessDetails.java @@ -24,30 +24,43 @@ import tech.sirwellington.alchemy.annotations.objects.Pojo; /** + * Detailed information about a Yelp Business. This can be obtained by calling + * {@link YelpAPI#getBusinessDetails(java.lang.String) } + * + *

+ * See Yelp's Documentaion for more information. * * @author SirWellington + * @see + * https://www.yelp.com/developers/documentation/v3/business */ @Pojo @Mutable @ThreadUnsafe public class YelpBusinessDetails { - + /** The Yelp ID of this business */ public String id; + /** The Name of this business */ public String name; + /** An URL photo for this business */ @SerializedName("image_url") public String imageURL; + /** Whether this business has been claimed by a Business Owner */ @SerializedName("is_claimed") public Boolean isClaimed; + /** Whether the businesss has been permanently closed */ @SerializedName("is_closed") public Boolean isClosed; + /** URL for the business on Yelp */ public String url; + /** The price level of the business. */ public String price; public double rating; From b8f75c3b1a932c44202e7ed7083348c8f17b24f8 Mon Sep 17 00:00:00 2001 From: Wellington Moreno Date: Tue, 6 Dec 2016 14:11:49 -0800 Subject: [PATCH 127/168] Updates YelpBusiness to ensure the Rating variable is a Double --- .../java/tech/redroma/yelp/YelpBusiness.java | 82 ++++++++++++------- 1 file changed, 52 insertions(+), 30 deletions(-) diff --git a/src/main/java/tech/redroma/yelp/YelpBusiness.java b/src/main/java/tech/redroma/yelp/YelpBusiness.java index 641418b..522aa2f 100644 --- a/src/main/java/tech/redroma/yelp/YelpBusiness.java +++ b/src/main/java/tech/redroma/yelp/YelpBusiness.java @@ -36,42 +36,65 @@ @ThreadUnsafe public class YelpBusiness { - /** Yelp ID of this business */ + + /** + * Yelp ID of this business + */ public String id; - - /** Name of the business */ + + /** + * Name of the business + */ public String name; - /** URL for the business page on Yelp */ + /** + * URL for the business page on Yelp + */ public String url; - /** An integer rating of the business, ranging from (1...5} */ + /** + * An integer rating of the business, ranging from (1...5} + */ //TODO: Change this to a double - public int rating; + public Double rating; public String phone; - /** Determines whether the business has permanently closed */ + /** + * Determines whether the business has permanently closed + */ @SerializedName("is_closed") public Boolean isClosed; - /** A list of categories associated with this business */ + /** + * A list of categories associated with this business + */ public List categories; - /** The number of reviews for this business */ + /** + * The number of reviews for this business + */ public int reviewCount; - /** The geo-coordinates of this business */ + /** + * The geo-coordinates of this business + */ public Coordinate coordinates; - /** The location of this business, including city, state, zip code. */ + /** + * The location of this business, including city, state, zip code. + */ public Address location; - /** A URL Photo for this business */ + /** + * A URL Photo for this business + */ @SerializedName("image_url") public String imageURL; - /** The distance, in meters, from the search location */ + /** + * The distance, in meters, from the search location + */ @Optional public Double distance; @@ -79,18 +102,18 @@ public class YelpBusiness public int hashCode() { int hash = 7; - hash = 37 * hash + Objects.hashCode(this.id); - hash = 37 * hash + Objects.hashCode(this.name); - hash = 37 * hash + Objects.hashCode(this.url); - hash = 37 * hash + this.rating; - hash = 37 * hash + Objects.hashCode(this.phone); - hash = 37 * hash + Objects.hashCode(this.isClosed); - hash = 37 * hash + Objects.hashCode(this.categories); - hash = 37 * hash + this.reviewCount; - hash = 37 * hash + Objects.hashCode(this.coordinates); - hash = 37 * hash + Objects.hashCode(this.location); - hash = 37 * hash + Objects.hashCode(this.imageURL); - hash = 37 * hash + Objects.hashCode(this.distance); + hash = 53 * hash + Objects.hashCode(this.id); + hash = 53 * hash + Objects.hashCode(this.name); + hash = 53 * hash + Objects.hashCode(this.url); + hash = 53 * hash + Objects.hashCode(this.rating); + hash = 53 * hash + Objects.hashCode(this.phone); + hash = 53 * hash + Objects.hashCode(this.isClosed); + hash = 53 * hash + Objects.hashCode(this.categories); + hash = 53 * hash + this.reviewCount; + hash = 53 * hash + Objects.hashCode(this.coordinates); + hash = 53 * hash + Objects.hashCode(this.location); + hash = 53 * hash + Objects.hashCode(this.imageURL); + hash = 53 * hash + Objects.hashCode(this.distance); return hash; } @@ -110,10 +133,6 @@ public boolean equals(Object obj) return false; } final YelpBusiness other = (YelpBusiness) obj; - if (this.rating != other.rating) - { - return false; - } if (this.reviewCount != other.reviewCount) { return false; @@ -138,6 +157,10 @@ public boolean equals(Object obj) { return false; } + if (!Objects.equals(this.rating, other.rating)) + { + return false; + } if (!Objects.equals(this.isClosed, other.isClosed)) { return false; @@ -167,5 +190,4 @@ public String toString() return "YelpBusiness{" + "id=" + id + ", name=" + name + ", url=" + url + ", rating=" + rating + ", phone=" + phone + ", isClosed=" + isClosed + ", categories=" + categories + ", reviewCount=" + reviewCount + ", coordinates=" + coordinates + ", location=" + location + ", imageURL=" + imageURL + ", distance=" + distance + '}'; } - } From 3467c472e383e466cf25a7013f111d20298d2119 Mon Sep 17 00:00:00 2001 From: Wellington Moreno Date: Tue, 6 Dec 2016 14:12:29 -0800 Subject: [PATCH 128/168] Increases test iterations for YelpBusinessTest --- src/test/java/tech/redroma/yelp/YelpBusinessTest.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/test/java/tech/redroma/yelp/YelpBusinessTest.java b/src/test/java/tech/redroma/yelp/YelpBusinessTest.java index 08e3c9b..216cb8b 100644 --- a/src/test/java/tech/redroma/yelp/YelpBusinessTest.java +++ b/src/test/java/tech/redroma/yelp/YelpBusinessTest.java @@ -43,7 +43,7 @@ * * @author SirWellington */ -@Repeat(10) +@Repeat(25) @RunWith(AlchemyTestRunner.class) public class YelpBusinessTest { From 52362db63a78cfa6aeb1bb3e556c4d65d1546e68 Mon Sep 17 00:00:00 2001 From: Wellington Moreno Date: Tue, 6 Dec 2016 14:14:03 -0800 Subject: [PATCH 129/168] Updates YelpBusinessDetails to ensure the Rating variable is a Double --- .../redroma/yelp/YelpBusinessDetails.java | 40 +++++++++---------- 1 file changed, 20 insertions(+), 20 deletions(-) diff --git a/src/main/java/tech/redroma/yelp/YelpBusinessDetails.java b/src/main/java/tech/redroma/yelp/YelpBusinessDetails.java index afc522a..68aa1c2 100644 --- a/src/main/java/tech/redroma/yelp/YelpBusinessDetails.java +++ b/src/main/java/tech/redroma/yelp/YelpBusinessDetails.java @@ -63,7 +63,7 @@ public class YelpBusinessDetails /** The price level of the business. */ public String price; - public double rating; + public Double rating; public int reviewCount; @@ -84,21 +84,21 @@ public class YelpBusinessDetails public int hashCode() { int hash = 7; - hash = 37 * hash + Objects.hashCode(this.id); - hash = 37 * hash + Objects.hashCode(this.name); - hash = 37 * hash + Objects.hashCode(this.imageURL); - hash = 37 * hash + Objects.hashCode(this.isClaimed); - hash = 37 * hash + Objects.hashCode(this.isClosed); - hash = 37 * hash + Objects.hashCode(this.url); - hash = 37 * hash + Objects.hashCode(this.price); - hash = 37 * hash + (int) (Double.doubleToLongBits(this.rating) ^ (Double.doubleToLongBits(this.rating) >>> 32)); - hash = 37 * hash + this.reviewCount; - hash = 37 * hash + Objects.hashCode(this.phone); - hash = 37 * hash + Objects.hashCode(this.photosURLS); - hash = 37 * hash + Objects.hashCode(this.hours); - hash = 37 * hash + Objects.hashCode(this.categories); - hash = 37 * hash + Objects.hashCode(this.coordinates); - hash = 37 * hash + Objects.hashCode(this.location); + hash = 89 * hash + Objects.hashCode(this.id); + hash = 89 * hash + Objects.hashCode(this.name); + hash = 89 * hash + Objects.hashCode(this.imageURL); + hash = 89 * hash + Objects.hashCode(this.isClaimed); + hash = 89 * hash + Objects.hashCode(this.isClosed); + hash = 89 * hash + Objects.hashCode(this.url); + hash = 89 * hash + Objects.hashCode(this.price); + hash = 89 * hash + Objects.hashCode(this.rating); + hash = 89 * hash + this.reviewCount; + hash = 89 * hash + Objects.hashCode(this.phone); + hash = 89 * hash + Objects.hashCode(this.photosURLS); + hash = 89 * hash + Objects.hashCode(this.hours); + hash = 89 * hash + Objects.hashCode(this.categories); + hash = 89 * hash + Objects.hashCode(this.coordinates); + hash = 89 * hash + Objects.hashCode(this.location); return hash; } @@ -118,10 +118,6 @@ public boolean equals(Object obj) return false; } final YelpBusinessDetails other = (YelpBusinessDetails) obj; - if (Double.doubleToLongBits(this.rating) != Double.doubleToLongBits(other.rating)) - { - return false; - } if (this.reviewCount != other.reviewCount) { return false; @@ -158,6 +154,10 @@ public boolean equals(Object obj) { return false; } + if (!Objects.equals(this.rating, other.rating)) + { + return false; + } if (!Objects.equals(this.photosURLS, other.photosURLS)) { return false; From ba611bb24428e5cf591264309da88730e4bf8f3e Mon Sep 17 00:00:00 2001 From: Wellington Moreno Date: Tue, 6 Dec 2016 14:14:15 -0800 Subject: [PATCH 130/168] #6 - Fixes documentation to YelpBusinessDetails --- src/main/java/tech/redroma/yelp/YelpBusinessDetails.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/tech/redroma/yelp/YelpBusinessDetails.java b/src/main/java/tech/redroma/yelp/YelpBusinessDetails.java index 68aa1c2..3a46d18 100644 --- a/src/main/java/tech/redroma/yelp/YelpBusinessDetails.java +++ b/src/main/java/tech/redroma/yelp/YelpBusinessDetails.java @@ -53,7 +53,7 @@ public class YelpBusinessDetails @SerializedName("is_claimed") public Boolean isClaimed; - /** Whether the businesss has been permanently closed */ + /** Whether the businesses has been permanently closed */ @SerializedName("is_closed") public Boolean isClosed; From 41427cfbb65eb115f6b2c0eb0d33add89065832d Mon Sep 17 00:00:00 2001 From: Wellington Moreno Date: Tue, 6 Dec 2016 14:15:17 -0800 Subject: [PATCH 131/168] Renames PriceLevel enum to Price --- .../java/tech/redroma/yelp/YelpSearchRequest.java | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/src/main/java/tech/redroma/yelp/YelpSearchRequest.java b/src/main/java/tech/redroma/yelp/YelpSearchRequest.java index a0819ad..840016f 100644 --- a/src/main/java/tech/redroma/yelp/YelpSearchRequest.java +++ b/src/main/java/tech/redroma/yelp/YelpSearchRequest.java @@ -46,6 +46,10 @@ import static tech.sirwellington.alchemy.arguments.assertions.StringAssertions.nonEmptyString; import static tech.sirwellington.alchemy.arguments.assertions.StringAssertions.stringContaining; import static tech.sirwellington.alchemy.arguments.assertions.StringAssertions.stringWithLengthBetween; +import static java.util.stream.Collectors.joining; +import static tech.sirwellington.alchemy.arguments.Arguments.checkThat; +import static tech.sirwellington.alchemy.arguments.assertions.NumberAssertions.greaterThanOrEqualTo; +import static tech.sirwellington.alchemy.arguments.assertions.NumberAssertions.lessThanOrEqualTo; /** * Use to make search requests to the Yelp API. Use {@link #newBuilder() } to create a request object. @@ -401,7 +405,7 @@ private SortType(String text) } } - public enum PricingLevel + public enum Price { $(1), $$(2), @@ -410,7 +414,7 @@ public enum PricingLevel final int number; - private PricingLevel(int number) + private Price(int number) { this.number = number; } @@ -782,11 +786,11 @@ public Builder withSortBy(@Required SortType sortType) throws IllegalArgumentExc * @see #withPrices(java.util.List) */ @Optional - public Builder withPrices(@Required PricingLevel first, PricingLevel...others) throws IllegalArgumentException + public Builder withPrices(@Required Price first, Price...others) throws IllegalArgumentException { checkThat(first).is(notNull()); - List pricingLevels = Lists.createFrom(first, others); + List pricingLevels = Lists.createFrom(first, others); return withPrices(pricingLevels); } @@ -799,7 +803,7 @@ public Builder withPrices(@Required PricingLevel first, PricingLevel...others) t * @see #withPrices(tech.redroma.yelp.YelpSearchRequest.PricingLevel, tech.redroma.yelp.YelpSearchRequest.PricingLevel...) */ @Optional - public Builder withPrices(@NonEmpty List pricingLevels) throws IllegalArgumentException + public Builder withPrices(@NonEmpty List pricingLevels) throws IllegalArgumentException { checkThat(pricingLevels) .is(notNull()) From 1f0b9750c0415e87194b9f9767da3655bb32f084 Mon Sep 17 00:00:00 2001 From: Wellington Moreno Date: Tue, 6 Dec 2016 14:16:21 -0800 Subject: [PATCH 132/168] Refactors Price out into its own class --- src/main/java/tech/redroma/yelp/Price.java | 36 +++++++++++++++++++ .../tech/redroma/yelp/YelpSearchRequest.java | 18 ---------- 2 files changed, 36 insertions(+), 18 deletions(-) create mode 100644 src/main/java/tech/redroma/yelp/Price.java diff --git a/src/main/java/tech/redroma/yelp/Price.java b/src/main/java/tech/redroma/yelp/Price.java new file mode 100644 index 0000000..551d986 --- /dev/null +++ b/src/main/java/tech/redroma/yelp/Price.java @@ -0,0 +1,36 @@ +/* + * Copyright 2016 RedRoma, Inc.. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package tech.redroma.yelp; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * + * @author SirWellington + */ +public enum Price +{ + $(1), $$(2), $$$(3), $$$$(4); + final int number; + + private Price(int number) + { + this.number = number; + } + +} diff --git a/src/main/java/tech/redroma/yelp/YelpSearchRequest.java b/src/main/java/tech/redroma/yelp/YelpSearchRequest.java index 840016f..0e1322c 100644 --- a/src/main/java/tech/redroma/yelp/YelpSearchRequest.java +++ b/src/main/java/tech/redroma/yelp/YelpSearchRequest.java @@ -32,16 +32,12 @@ import tech.sirwellington.alchemy.annotations.objects.Pojo; import static com.google.common.base.Strings.isNullOrEmpty; -import static java.util.stream.Collectors.joining; import static tech.sirwellington.alchemy.annotations.designs.patterns.BuilderPattern.Role.BUILDER; import static tech.sirwellington.alchemy.annotations.designs.patterns.BuilderPattern.Role.PRODUCT; -import static tech.sirwellington.alchemy.arguments.Arguments.checkThat; import static tech.sirwellington.alchemy.arguments.assertions.Assertions.notNull; import static tech.sirwellington.alchemy.arguments.assertions.CollectionAssertions.nonEmptyList; import static tech.sirwellington.alchemy.arguments.assertions.GeolocationAssertions.validLatitude; import static tech.sirwellington.alchemy.arguments.assertions.GeolocationAssertions.validLongitude; -import static tech.sirwellington.alchemy.arguments.assertions.NumberAssertions.greaterThanOrEqualTo; -import static tech.sirwellington.alchemy.arguments.assertions.NumberAssertions.lessThanOrEqualTo; import static tech.sirwellington.alchemy.arguments.assertions.NumberAssertions.positiveInteger; import static tech.sirwellington.alchemy.arguments.assertions.StringAssertions.nonEmptyString; import static tech.sirwellington.alchemy.arguments.assertions.StringAssertions.stringContaining; @@ -405,20 +401,6 @@ private SortType(String text) } } - public enum Price - { - $(1), - $$(2), - $$$(3), - $$$$(4); - - final int number; - - private Price(int number) - { - this.number = number; - } - } public enum Attribute { From cc6a3732cab06b2fc30f0abf633d38a62b11e4ee Mon Sep 17 00:00:00 2001 From: Wellington Moreno Date: Tue, 6 Dec 2016 14:34:11 -0800 Subject: [PATCH 133/168] Adds a fromNumber() to Price --- src/main/java/tech/redroma/yelp/Price.java | 28 +++++++++++++++++++--- 1 file changed, 25 insertions(+), 3 deletions(-) diff --git a/src/main/java/tech/redroma/yelp/Price.java b/src/main/java/tech/redroma/yelp/Price.java index 551d986..6b166f6 100644 --- a/src/main/java/tech/redroma/yelp/Price.java +++ b/src/main/java/tech/redroma/yelp/Price.java @@ -16,9 +16,6 @@ package tech.redroma.yelp; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - /** * * @author SirWellington @@ -33,4 +30,29 @@ private Price(int number) this.number = number; } + /** + * Creates a {@link Price} from the given number, which must be in the range (1...4). + *

+     * 1 = $
+     * 2 = $$
+     * 3 = $$$
+     * 4 = $$$$
+     * 
+ * @param number Must be in the range + * @return + * @throws IllegalArgumentException + */ + public static Price fromNumber(int number) throws IllegalArgumentException + { + + for (Price price : Price.values()) + { + if (price.number == number) + { + return price; + } + } + + throw new IllegalArgumentException("Cannot determine price from number: " + number); + } } From db012e2fda50f68582e60999ae60c8050c7ed138 Mon Sep 17 00:00:00 2001 From: Wellington Moreno Date: Tue, 6 Dec 2016 14:34:33 -0800 Subject: [PATCH 134/168] #6 - Updates documentation for Price --- src/main/java/tech/redroma/yelp/Price.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/tech/redroma/yelp/Price.java b/src/main/java/tech/redroma/yelp/Price.java index 6b166f6..1e2d10f 100644 --- a/src/main/java/tech/redroma/yelp/Price.java +++ b/src/main/java/tech/redroma/yelp/Price.java @@ -40,7 +40,7 @@ private Price(int number) * * @param number Must be in the range * @return - * @throws IllegalArgumentException + * @throws IllegalArgumentException If the number could not be matched to a {@link Price}. */ public static Price fromNumber(int number) throws IllegalArgumentException { From 7bf3ee0a72984d60922b90d8b91ebc27f8c1a212 Mon Sep 17 00:00:00 2001 From: Wellington Moreno Date: Tue, 6 Dec 2016 14:43:24 -0800 Subject: [PATCH 135/168] Adds a fromString() to Price --- src/main/java/tech/redroma/yelp/Price.java | 48 ++++++++++++++++++++-- 1 file changed, 44 insertions(+), 4 deletions(-) diff --git a/src/main/java/tech/redroma/yelp/Price.java b/src/main/java/tech/redroma/yelp/Price.java index 1e2d10f..b84d5df 100644 --- a/src/main/java/tech/redroma/yelp/Price.java +++ b/src/main/java/tech/redroma/yelp/Price.java @@ -16,6 +16,11 @@ package tech.redroma.yelp; +import tech.sirwellington.alchemy.annotations.arguments.NonEmpty; + +import static tech.sirwellington.alchemy.arguments.Arguments.checkThat; +import static tech.sirwellington.alchemy.arguments.assertions.StringAssertions.nonEmptyString; + /** * * @author SirWellington @@ -30,13 +35,18 @@ private Price(int number) this.number = number; } + public String asString() + { + return this.toString(); + } + /** * Creates a {@link Price} from the given number, which must be in the range (1...4). *
-     * 1 = $
-     * 2 = $$
-     * 3 = $$$
-     * 4 = $$$$
+     *  1 = $
+     *  2 = $$
+     *  3 = $$$
+     *  4 = $$$$
      * 
* @param number Must be in the range * @return @@ -55,4 +65,34 @@ public static Price fromNumber(int number) throws IllegalArgumentException throw new IllegalArgumentException("Cannot determine price from number: " + number); } + + /** + * Creates a price from the give string representation. + *

+ * Valid values include: + *

+     * + "$"
+     * + "$$"
+     * + "$$$"
+     * + "$$$$"
+     * 
+ * + * @param price + * @return + * @throws IllegalArgumentException + */ + public static Price fromString(@NonEmpty String price) throws IllegalArgumentException + { + checkThat(price).is(nonEmptyString()); + + for (Price p : Price.values()) + { + if (price.equals(p.asString())) + { + return p; + } + } + + throw new IllegalArgumentException("Cannot determine price from: " + price); + } } From f3979b055650fa601adbd08849c7995af632f41a Mon Sep 17 00:00:00 2001 From: Wellington Moreno Date: Tue, 6 Dec 2016 14:43:40 -0800 Subject: [PATCH 136/168] Adds Price unit tests --- .../java/tech/redroma/yelp/PriceTest.java | 142 ++++++++++++++++++ 1 file changed, 142 insertions(+) create mode 100644 src/test/java/tech/redroma/yelp/PriceTest.java diff --git a/src/test/java/tech/redroma/yelp/PriceTest.java b/src/test/java/tech/redroma/yelp/PriceTest.java new file mode 100644 index 0000000..f16628e --- /dev/null +++ b/src/test/java/tech/redroma/yelp/PriceTest.java @@ -0,0 +1,142 @@ +/* + * Copyright 2016 RedRoma, Inc.. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package tech.redroma.yelp; + +import com.google.common.base.Strings; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import tech.sirwellington.alchemy.test.junit.runners.AlchemyTestRunner; +import tech.sirwellington.alchemy.test.junit.runners.DontRepeat; +import tech.sirwellington.alchemy.test.junit.runners.GenerateEnum; +import tech.sirwellington.alchemy.test.junit.runners.GenerateInteger; +import tech.sirwellington.alchemy.test.junit.runners.Repeat; + +import static org.hamcrest.Matchers.is; +import static org.hamcrest.Matchers.notNullValue; +import static org.junit.Assert.assertThat; +import static tech.sirwellington.alchemy.generator.AlchemyGenerator.one; +import static tech.sirwellington.alchemy.generator.StringGenerators.alphabeticString; +import static tech.sirwellington.alchemy.test.junit.ThrowableAssertion.assertThrows; +import static tech.sirwellington.alchemy.test.junit.runners.GenerateInteger.Type.NEGATIVE; +import static tech.sirwellington.alchemy.test.junit.runners.GenerateInteger.Type.RANGE; + + +/** + * + * @author SirWellington + */ +@Repeat(10) +@RunWith(AlchemyTestRunner.class) +public class PriceTest +{ + @GenerateEnum + private Price price; + + @GenerateInteger(value = RANGE, min = 1, max = 4) + Integer validNumber; + + @GenerateInteger(value = RANGE, min = 5, max = 100) + Integer invalidNumber; + + @GenerateInteger(value = NEGATIVE) + Integer negativeNumber; + + @Before + public void setUp() throws Exception + { + + setupData(); + setupMocks(); + } + + + private void setupData() throws Exception + { + + } + + private void setupMocks() throws Exception + { + + } + + @Test + public void testInstance() + { + assertThat(price, notNullValue()); + } + + @Test + public void testFromNumberWithValid() + { + Price result = Price.fromNumber(validNumber); + assertThat(result, notNullValue()); + } + + @Test + public void testFromNumberWithInvalid() + { + assertThrows(() -> Price.fromNumber(invalidNumber)) + .isInstanceOf(IllegalArgumentException.class); + + assertThrows(() -> Price.fromNumber(negativeNumber)) + .isInstanceOf(IllegalArgumentException.class); + } + + @Test + public void testValues() + { + } + + @Test + public void testValueOf() + { + } + + @Test + public void testAsString() + { + String result = price.asString(); + assertThat(result, is(price.toString())); + } + + @Test + public void testFromString() + { + String string = Strings.repeat("$", validNumber); + + Price result = Price.fromString(string); + assertThat(result, notNullValue()); + } + + @DontRepeat + @Test + public void testFromStringWithInvalidArguments() + { + assertThrows(() -> Price.fromString("")) + .isInstanceOf(IllegalArgumentException.class); + + assertThrows(() -> Price.fromString(null)) + .isInstanceOf(IllegalArgumentException.class); + + String invalid = one(alphabeticString()); + assertThrows(() -> Price.fromString(invalid)) + .isInstanceOf(IllegalArgumentException.class); + } + +} \ No newline at end of file From 0bde3560638c4732478c0d39997573ebcc93c5e2 Mon Sep 17 00:00:00 2001 From: Wellington Moreno Date: Tue, 6 Dec 2016 14:44:10 -0800 Subject: [PATCH 137/168] Formats --- .../tech/redroma/yelp/YelpSearchRequest.java | 34 +++++++++---------- 1 file changed, 17 insertions(+), 17 deletions(-) diff --git a/src/main/java/tech/redroma/yelp/YelpSearchRequest.java b/src/main/java/tech/redroma/yelp/YelpSearchRequest.java index 0e1322c..b2ea693 100644 --- a/src/main/java/tech/redroma/yelp/YelpSearchRequest.java +++ b/src/main/java/tech/redroma/yelp/YelpSearchRequest.java @@ -32,20 +32,20 @@ import tech.sirwellington.alchemy.annotations.objects.Pojo; import static com.google.common.base.Strings.isNullOrEmpty; +import static java.util.stream.Collectors.joining; import static tech.sirwellington.alchemy.annotations.designs.patterns.BuilderPattern.Role.BUILDER; import static tech.sirwellington.alchemy.annotations.designs.patterns.BuilderPattern.Role.PRODUCT; +import static tech.sirwellington.alchemy.arguments.Arguments.checkThat; import static tech.sirwellington.alchemy.arguments.assertions.Assertions.notNull; import static tech.sirwellington.alchemy.arguments.assertions.CollectionAssertions.nonEmptyList; import static tech.sirwellington.alchemy.arguments.assertions.GeolocationAssertions.validLatitude; import static tech.sirwellington.alchemy.arguments.assertions.GeolocationAssertions.validLongitude; +import static tech.sirwellington.alchemy.arguments.assertions.NumberAssertions.greaterThanOrEqualTo; +import static tech.sirwellington.alchemy.arguments.assertions.NumberAssertions.lessThanOrEqualTo; import static tech.sirwellington.alchemy.arguments.assertions.NumberAssertions.positiveInteger; import static tech.sirwellington.alchemy.arguments.assertions.StringAssertions.nonEmptyString; import static tech.sirwellington.alchemy.arguments.assertions.StringAssertions.stringContaining; import static tech.sirwellington.alchemy.arguments.assertions.StringAssertions.stringWithLengthBetween; -import static java.util.stream.Collectors.joining; -import static tech.sirwellington.alchemy.arguments.Arguments.checkThat; -import static tech.sirwellington.alchemy.arguments.assertions.NumberAssertions.greaterThanOrEqualTo; -import static tech.sirwellington.alchemy.arguments.assertions.NumberAssertions.lessThanOrEqualTo; /** * Use to make search requests to the Yelp API. Use {@link #newBuilder() } to create a request object. @@ -887,19 +887,19 @@ public YelpSearchRequest build() throws YelpBadArgumentException checkThatOnlyOneOfOpenNowOrOpenAtIsSet(); return new YelpSearchRequest(searchTerm, - location, - latitude, - longitude, - radiusInMeters, - categories, - locale, - limit, - offset, - sortBy, - prices, - isOpenNow, - openAt, - attributes); + location, + latitude, + longitude, + radiusInMeters, + categories, + locale, + limit, + offset, + sortBy, + prices, + isOpenNow, + openAt, + attributes); } private void checkThatLocationIsSet() From 0612de6f232ec32db89330fcbd5f3ae44d80b978 Mon Sep 17 00:00:00 2001 From: Wellington Moreno Date: Tue, 6 Dec 2016 14:44:19 -0800 Subject: [PATCH 138/168] Formats --- src/main/java/tech/redroma/yelp/YelpBusiness.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/tech/redroma/yelp/YelpBusiness.java b/src/main/java/tech/redroma/yelp/YelpBusiness.java index 522aa2f..b658239 100644 --- a/src/main/java/tech/redroma/yelp/YelpBusiness.java +++ b/src/main/java/tech/redroma/yelp/YelpBusiness.java @@ -97,7 +97,7 @@ public class YelpBusiness */ @Optional public Double distance; - + @Override public int hashCode() { From 9c4eda9a07a0808b6933c38516331c1453c12ff4 Mon Sep 17 00:00:00 2001 From: Wellington Moreno Date: Tue, 6 Dec 2016 14:57:40 -0800 Subject: [PATCH 139/168] Update YelpBusinessDetails to get Price Level as Enum --- .../tech/redroma/yelp/YelpBusinessDetails.java | 13 ++++++++++++- .../redroma/yelp/YelpBusinessDetailsTest.java | 16 ++++++++++++++++ 2 files changed, 28 insertions(+), 1 deletion(-) diff --git a/src/main/java/tech/redroma/yelp/YelpBusinessDetails.java b/src/main/java/tech/redroma/yelp/YelpBusinessDetails.java index 3a46d18..3f1eacc 100644 --- a/src/main/java/tech/redroma/yelp/YelpBusinessDetails.java +++ b/src/main/java/tech/redroma/yelp/YelpBusinessDetails.java @@ -23,6 +23,8 @@ import tech.sirwellington.alchemy.annotations.concurrency.ThreadUnsafe; import tech.sirwellington.alchemy.annotations.objects.Pojo; +import static com.google.common.base.Strings.isNullOrEmpty; + /** * Detailed information about a Yelp Business. This can be obtained by calling * {@link YelpAPI#getBusinessDetails(java.lang.String) } @@ -79,8 +81,17 @@ public class YelpBusinessDetails public Coordinate coordinates; public Address location; + + public Price getPriceLevel() + { + if (isNullOrEmpty(price)) + { + return null; + } + + return Price.fromString(price); + } - @Override public int hashCode() { int hash = 7; diff --git a/src/test/java/tech/redroma/yelp/YelpBusinessDetailsTest.java b/src/test/java/tech/redroma/yelp/YelpBusinessDetailsTest.java index 8494f64..4435fe9 100644 --- a/src/test/java/tech/redroma/yelp/YelpBusinessDetailsTest.java +++ b/src/test/java/tech/redroma/yelp/YelpBusinessDetailsTest.java @@ -21,6 +21,8 @@ import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; +import tech.sirwellington.alchemy.generator.AlchemyGenerator; +import tech.sirwellington.alchemy.generator.StringGenerators; import tech.sirwellington.alchemy.test.junit.runners.AlchemyTestRunner; import tech.sirwellington.alchemy.test.junit.runners.DontRepeat; import tech.sirwellington.alchemy.test.junit.runners.GeneratePojo; @@ -70,12 +72,15 @@ private void setupData() { instance.coordinates.latitude = one(latitudes()); instance.coordinates.longitude = one(longitudes()); + instance.price = one(prices()); first.coordinates.latitude = one(latitudes()); first.coordinates.longitude = one(longitudes()); + first.price = one(prices()); second.coordinates.latitude = one(latitudes()); second.coordinates.longitude = one(longitudes()); + second.price = one(prices()); } @DontRepeat @@ -199,4 +204,15 @@ public void testDeserialization() throws IOException checkBusiness(result); } + @Test + public void testGetPriceLevel() + { + Price price = instance.getPriceLevel(); + assertThat(price, notNullValue()); + } + + private AlchemyGenerator prices() + { + return StringGenerators.stringsFromFixedList("$", "$$", "$$$", "$$$$"); + } } From a939cefbe4b2ea20f1a2350535dce313777203aa Mon Sep 17 00:00:00 2001 From: Wellington Moreno Date: Tue, 6 Dec 2016 14:59:03 -0800 Subject: [PATCH 140/168] # 6 - Adds documentation --- src/main/java/tech/redroma/yelp/YelpResponses.java | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/main/java/tech/redroma/yelp/YelpResponses.java b/src/main/java/tech/redroma/yelp/YelpResponses.java index ba8b7b7..19d2858 100644 --- a/src/main/java/tech/redroma/yelp/YelpResponses.java +++ b/src/main/java/tech/redroma/yelp/YelpResponses.java @@ -25,7 +25,7 @@ import tech.sirwellington.alchemy.annotations.objects.Pojo; /** - * + * Internal class for extracting Yelp's Responses. * @author SirWellington */ @Internal @@ -48,8 +48,10 @@ final class YelpResponses @Internal static class SearchResponse { - + + /** Represents the total number of businesses that matched the search query */ public int total; + /** The Businesses returned from Yelp */ public List businesses; SearchResponse() From 379fa7a547bedbbf16c288357db5aecf1c102031 Mon Sep 17 00:00:00 2001 From: Wellington Moreno Date: Tue, 6 Dec 2016 15:01:33 -0800 Subject: [PATCH 141/168] Adds ReviewsResponse to YelpResponses --- .../java/tech/redroma/yelp/YelpResponses.java | 57 +++++++++++++++++++ 1 file changed, 57 insertions(+) diff --git a/src/main/java/tech/redroma/yelp/YelpResponses.java b/src/main/java/tech/redroma/yelp/YelpResponses.java index 19d2858..5b1bf5f 100644 --- a/src/main/java/tech/redroma/yelp/YelpResponses.java +++ b/src/main/java/tech/redroma/yelp/YelpResponses.java @@ -101,4 +101,61 @@ public String toString() } } + + @Internal + @Pojo + static class ReviewsResponse + { + + /** + * The total number of reviews for the business + */ + public int total; + /** + * The reviews returned from the Yelp API + */ + public List reviews; + + ReviewsResponse() + { + } + + @Override + public int hashCode() + { + int hash = 7; + hash = 29 * hash + this.total; + hash = 29 * hash + Objects.hashCode(this.reviews); + return hash; + } + + @Override + public boolean equals(Object obj) + { + if (this == obj) + { + return true; + } + if (obj == null) + { + return false; + } + if (getClass() != obj.getClass()) + { + return false; + } + final ReviewsResponse other = (ReviewsResponse) obj; + if (this.total != other.total) + { + return false; + } + if (!Objects.equals(this.reviews, other.reviews)) + { + return false; + } + return true; + } + + } + } From da363c0d4da826e5cd5287955430b8d3c32b5225 Mon Sep 17 00:00:00 2001 From: Wellington Moreno Date: Tue, 6 Dec 2016 15:03:11 -0800 Subject: [PATCH 142/168] Adds unit tests: YelpResponsesTest --- .../tech/redroma/yelp/YelpResponsesTest.java | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/src/test/java/tech/redroma/yelp/YelpResponsesTest.java b/src/test/java/tech/redroma/yelp/YelpResponsesTest.java index df07a24..f547dac 100644 --- a/src/test/java/tech/redroma/yelp/YelpResponsesTest.java +++ b/src/test/java/tech/redroma/yelp/YelpResponsesTest.java @@ -41,6 +41,9 @@ public class YelpResponsesTest @GeneratePojo private YelpResponses.SearchResponse searchResponse; + @GeneratePojo + private YelpResponses.ReviewsResponse reviewsResponse; + @Before public void setUp() throws Exception { @@ -74,4 +77,18 @@ public void testSearchResponseHashCode() throws Exception assertThat(hashCode, not(0)); } + @Test + public void testReviewsResponse() throws Exception + { + assertThat(reviewsResponse, notNullValue()); + assertThat(reviewsResponse.total, greaterThan(0)); + assertThat(reviewsResponse.reviews, not(empty())); + } + + @Test + public void testReviewsResponseHashCode() + { + int hashCode = reviewsResponse.hashCode(); + assertThat(hashCode, not(0)); + } } From e9e653f25b738648d842d0eac059e8a113f9c93f Mon Sep 17 00:00:00 2001 From: Wellington Moreno Date: Tue, 6 Dec 2016 15:05:16 -0800 Subject: [PATCH 143/168] #6 - Adds documentation to YelpReview --- .../java/tech/redroma/yelp/YelpReview.java | 28 +++++++++++++++++++ 1 file changed, 28 insertions(+) diff --git a/src/main/java/tech/redroma/yelp/YelpReview.java b/src/main/java/tech/redroma/yelp/YelpReview.java index 83d147e..e29e88c 100644 --- a/src/main/java/tech/redroma/yelp/YelpReview.java +++ b/src/main/java/tech/redroma/yelp/YelpReview.java @@ -36,10 +36,32 @@ @ThreadUnsafe public class YelpReview { + + /** + * The raring of the business associated with this review + * @see YelpBusiness#rating + */ public int rating; + + /** + * The user who wrote the review + * @see User + */ public User user; + + /** + * A Text excerpt of this review. + */ public String text; + + /** + * The time that the review was created, in PST. + */ public Instant timeCreated; + + /** + * A URL of this review. + */ public String url; @Pojo @@ -47,8 +69,14 @@ public class YelpReview @ThreadUnsafe public static class User { + /** + * The name of the user. + */ public String name; + /** + * A URL of the user's profile picture. + */ @SerializedName("image_url") public String imageURL; } From 4a6cc03e70dd7cdacfb9cd3f293a9b142a2c5579 Mon Sep 17 00:00:00 2001 From: Wellington Moreno Date: Tue, 6 Dec 2016 15:08:44 -0800 Subject: [PATCH 144/168] #3 - Adds unit test: CategoryTest --- .../java/tech/redroma/yelp/CategoryTest.java | 99 +++++++++++++++++++ 1 file changed, 99 insertions(+) create mode 100644 src/test/java/tech/redroma/yelp/CategoryTest.java diff --git a/src/test/java/tech/redroma/yelp/CategoryTest.java b/src/test/java/tech/redroma/yelp/CategoryTest.java new file mode 100644 index 0000000..314a6a1 --- /dev/null +++ b/src/test/java/tech/redroma/yelp/CategoryTest.java @@ -0,0 +1,99 @@ +/* + * Copyright 2016 RedRoma, Inc.. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package tech.redroma.yelp; + +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import tech.sirwellington.alchemy.test.junit.runners.AlchemyTestRunner; +import tech.sirwellington.alchemy.test.junit.runners.GeneratePojo; +import tech.sirwellington.alchemy.test.junit.runners.Repeat; + +import static org.hamcrest.Matchers.is; +import static org.hamcrest.Matchers.isEmptyOrNullString; +import static org.hamcrest.Matchers.not; +import static org.hamcrest.Matchers.notNullValue; +import static org.junit.Assert.assertThat; + + +/** + * + * @author SirWellington + */ +@Repeat(10) +@RunWith(AlchemyTestRunner.class) +public class CategoryTest +{ + @GeneratePojo + private Category instance; + + @GeneratePojo + private Category first; + + @GeneratePojo + private Category second; + + @Before + public void setUp() throws Exception + { + + setupData(); + setupMocks(); + } + + + private void setupData() throws Exception + { + + } + + private void setupMocks() throws Exception + { + + } + + @Test + public void testInstance() + { + assertThat(instance, notNullValue()); + assertThat(instance.alias, not(isEmptyOrNullString())); + assertThat(instance.title, not(isEmptyOrNullString())); + } + + @Test + public void testHashCode() + { + int hashCode = instance.hashCode(); + assertThat(hashCode, not(0)); + } + + @Test + public void testEquals() + { + Category copy = new Category(); + copy.alias = instance.alias; + copy.title = instance.title; + assertThat(copy, is(instance)); + } + + @Test + public void testEqualsWhenDifferent() + { + assertThat(first, not(second)); + } + +} \ No newline at end of file From 3993bde9eacc9fd0ff88404c0af88a34c75dcad4 Mon Sep 17 00:00:00 2001 From: Wellington Moreno Date: Tue, 6 Dec 2016 15:43:10 -0800 Subject: [PATCH 145/168] #6 - Fixes documentation in YelpSearchRequest --- src/main/java/tech/redroma/yelp/YelpSearchRequest.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/tech/redroma/yelp/YelpSearchRequest.java b/src/main/java/tech/redroma/yelp/YelpSearchRequest.java index b2ea693..db1d608 100644 --- a/src/main/java/tech/redroma/yelp/YelpSearchRequest.java +++ b/src/main/java/tech/redroma/yelp/YelpSearchRequest.java @@ -782,7 +782,7 @@ public Builder withPrices(@Required Price first, Price...others) throws IllegalA * @param pricingLevels The pricing levels to the filter the search results with (cannot be empty). * @return * @throws IllegalArgumentException - * @see #withPrices(tech.redroma.yelp.YelpSearchRequest.PricingLevel, tech.redroma.yelp.YelpSearchRequest.PricingLevel...) + * @see #withPrices(tech.redroma.yelp.Price, tech.redroma.yelp.Price...) */ @Optional public Builder withPrices(@NonEmpty List pricingLevels) throws IllegalArgumentException From c28867c92880aa3a7a72e22776557bd16a486bf1 Mon Sep 17 00:00:00 2001 From: Wellington Moreno Date: Tue, 6 Dec 2016 15:43:47 -0800 Subject: [PATCH 146/168] #7 - Adds getReviews() to Yelp API interface --- src/main/java/tech/redroma/yelp/NoOpYelp.java | 6 ++++ src/main/java/tech/redroma/yelp/YelpAPI.java | 29 ++++++++++++++++ .../java/tech/redroma/yelp/YelpAPIImpl.java | 34 +++++++++++++------ 3 files changed, 58 insertions(+), 11 deletions(-) diff --git a/src/main/java/tech/redroma/yelp/NoOpYelp.java b/src/main/java/tech/redroma/yelp/NoOpYelp.java index 3b9c098..eb0b24c 100644 --- a/src/main/java/tech/redroma/yelp/NoOpYelp.java +++ b/src/main/java/tech/redroma/yelp/NoOpYelp.java @@ -47,4 +47,10 @@ public List searchForBusinesses(YelpSearchRequest request) throws return Lists.emptyList(); } + @Override + public List getReviewsForBusiness(String businessId) throws YelpException + { + return Lists.emptyList(); + } + } diff --git a/src/main/java/tech/redroma/yelp/YelpAPI.java b/src/main/java/tech/redroma/yelp/YelpAPI.java index 805c6a3..754bb41 100644 --- a/src/main/java/tech/redroma/yelp/YelpAPI.java +++ b/src/main/java/tech/redroma/yelp/YelpAPI.java @@ -115,6 +115,35 @@ default YelpBusinessDetails getBusinessDetails(@Required YelpBusiness business) */ List searchForBusinesses(@Required YelpSearchRequest request) throws YelpException; + /** + * Gets the reviews, if any, associated with a Business. + * + * @param business The business, obtained from a call to {@link #searchForBusinesses(tech.redroma.yelp.YelpSearchRequest) }. + * @return + * @throws YelpException + */ + default List getReviewsForBusiness(@Required YelpBusiness business) throws YelpException + { + checkThat(business) + .throwing(YelpBadArgumentException.class) + .is(notNull()); + + checkThat(business.id) + .throwing(YelpBadArgumentException.class) + .is(nonEmptyString()); + + return getReviewsForBusiness(business.id); + } + + /** + * Gets the reviews, if any, associated with a Business. + * + * @param businessId The ID of the business. Obtained from calls to {@link #searchForBusinesses(tech.redroma.yelp.YelpSearchRequest) }. + * @return + * @throws YelpException + */ + List getReviewsForBusiness(@NonEmpty String businessId) throws YelpException; + static YelpAPI newInstance(@NonEmpty String cliendId, @NonEmpty String clientSecret) { checkThat(cliendId, clientSecret) diff --git a/src/main/java/tech/redroma/yelp/YelpAPIImpl.java b/src/main/java/tech/redroma/yelp/YelpAPIImpl.java index 1b546ee..df10e6e 100644 --- a/src/main/java/tech/redroma/yelp/YelpAPIImpl.java +++ b/src/main/java/tech/redroma/yelp/YelpAPIImpl.java @@ -90,16 +90,7 @@ public List searchForBusinesses(YelpSearchRequest request) throws { String token = tokenProvider.getToken(); - checkThat(token) - .throwing(YelpAuthenticationException.class) - .usingMessage("No token available to make API call") - .is(nonEmptyString()); - - AlchemyRequest.Step3 httpRequest = http.go() - .get() - .usingHeader(HeaderParameters.AUTHORIZATION, HeaderParameters.BEARER + " " + token); - - httpRequest = requestFilledWithParametersFrom(httpRequest, request); + AlchemyRequest.Step3 httpRequest = tryToCreateHTTPRequestToSearch(token, request); String url = baseURL + URLS.BUSINESS_SEARCH; @@ -140,7 +131,28 @@ public List searchForBusinesses(YelpSearchRequest request) throws LOG.info("Received {} results out of {} total for search: {}", results.size(), response.total, request); return results; } - + + @Override + public List getReviewsForBusiness(String businessId) throws YelpException + { + return Lists.emptyList(); + } + + private AlchemyRequest.Step3 tryToCreateHTTPRequestToSearch(String token, YelpSearchRequest request) + { + checkThat(token) + .throwing(YelpAuthenticationException.class) + .usingMessage("No token available to make API call") + .is(nonEmptyString()); + + AlchemyRequest.Step3 httpRequest = http.go() + .get() + .usingHeader(HeaderParameters.AUTHORIZATION, HeaderParameters.BEARER + " " + token); + + httpRequest = requestFilledWithParametersFrom(httpRequest, request); + return httpRequest; + } + private String createDetailUrlFor(String businessId) { return format("%s%s/%s", baseURL, URLS.BUSINESSES, businessId); From e6768581e93fc330258993b5aba2a8d018854db4 Mon Sep 17 00:00:00 2001 From: Wellington Moreno Date: Tue, 6 Dec 2016 15:44:42 -0800 Subject: [PATCH 147/168] #7 - Updates NoOpYelpTest test --- src/test/java/tech/redroma/yelp/NoOpYelpTest.java | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/src/test/java/tech/redroma/yelp/NoOpYelpTest.java b/src/test/java/tech/redroma/yelp/NoOpYelpTest.java index 9d728e5..21f826d 100644 --- a/src/test/java/tech/redroma/yelp/NoOpYelpTest.java +++ b/src/test/java/tech/redroma/yelp/NoOpYelpTest.java @@ -75,4 +75,12 @@ public void testSearchForBusinesses() } + @Test + public void testGetReviewsForBusiness() + { + List results = instance.getReviewsForBusiness(businessId); + assertThat(results, notNullValue()); + assertThat(results, is(empty())); + } + } \ No newline at end of file From c3512a70d73b446e75e99b8d55f4293876f77aaa Mon Sep 17 00:00:00 2001 From: Wellington Moreno Date: Tue, 6 Dec 2016 16:48:27 -0800 Subject: [PATCH 148/168] #7 - Adds the ability to search a business for reviews --- .../java/tech/redroma/yelp/YelpAPIImpl.java | 92 ++++++++++++--- .../tech/redroma/yelp/YelpAPIImplTest.java | 111 ++++++++++++++++-- 2 files changed, 180 insertions(+), 23 deletions(-) diff --git a/src/main/java/tech/redroma/yelp/YelpAPIImpl.java b/src/main/java/tech/redroma/yelp/YelpAPIImpl.java index df10e6e..78a65f6 100644 --- a/src/main/java/tech/redroma/yelp/YelpAPIImpl.java +++ b/src/main/java/tech/redroma/yelp/YelpAPIImpl.java @@ -59,16 +59,16 @@ final class YelpAPIImpl implements YelpAPI { checkThat(http, tokenProvider) .are(notNull()); - + checkThat(baseURL) .is(nonEmptyString()) .is(validURL()); - + this.http = http; this.tokenProvider = tokenProvider; this.baseURL = baseURL; } - + @Override public YelpBusinessDetails getBusinessDetails(String businessId) throws YelpException { @@ -76,21 +76,21 @@ public YelpBusinessDetails getBusinessDetails(String businessId) throws YelpExce .throwing(YelpBadArgumentException.class) .usingMessage("Business ID cannot be empty") .is(nonEmptyString()); - + String url = createDetailUrlFor(businessId); - + YelpBusinessDetails details = tryToGetDetailsAt(url); - + return details; - + } - + @Override public List searchForBusinesses(YelpSearchRequest request) throws YelpException { String token = tokenProvider.getToken(); - - AlchemyRequest.Step3 httpRequest = tryToCreateHTTPRequestToSearch(token, request); + checkToken(token); + AlchemyRequest.Step3 httpRequest = createHTTPRequestToSearch(token, request); String url = baseURL + URLS.BUSINESS_SEARCH; @@ -135,16 +135,32 @@ public List searchForBusinesses(YelpSearchRequest request) throws @Override public List getReviewsForBusiness(String businessId) throws YelpException { + String token = tokenProvider.getToken(); + checkToken(token); + + String url = createUrlToGetReviewsFor(businessId); + + YelpResponses.ReviewsResponse response = tryToGetReviewsAt(url, token); + + if (Objects.nonNull(response)) + { + LOG.debug("Found {} total reviews for business {}", response.total, businessId); + return Lists.nullToEmpty(response.reviews); + } + return Lists.emptyList(); } - - private AlchemyRequest.Step3 tryToCreateHTTPRequestToSearch(String token, YelpSearchRequest request) + + private void checkToken(String token) throws YelpAuthenticationException { checkThat(token) .throwing(YelpAuthenticationException.class) .usingMessage("No token available to make API call") .is(nonEmptyString()); - + } + + private AlchemyRequest.Step3 createHTTPRequestToSearch(String token, YelpSearchRequest request) + { AlchemyRequest.Step3 httpRequest = http.go() .get() .usingHeader(HeaderParameters.AUTHORIZATION, HeaderParameters.BEARER + " " + token); @@ -158,6 +174,11 @@ private String createDetailUrlFor(String businessId) return format("%s%s/%s", baseURL, URLS.BUSINESSES, businessId); } + private String createUrlToGetReviewsFor(String businessId) + { + return format("%s%s/%s%s", baseURL, URLS.BUSINESSES, businessId, URLS.REVIEWS); + } + private YelpBusinessDetails tryToGetDetailsAt(String url) { checkThat(url) @@ -206,7 +227,50 @@ else if (isBadRequest(ex)) throw new YelpOperationFailedException(ex); } } - + + private YelpResponses.ReviewsResponse tryToGetReviewsAt(String url, String token) throws YelpException + { + checkThat(url).is(validURL()); + checkThat(token).is(nonEmptyString()); + + try + { + return http + .go() + .get() + .usingHeader(HeaderParameters.AUTHORIZATION, HeaderParameters.BEARER + " " + token) + .expecting(YelpResponses.ReviewsResponse.class) + .at(url); + } + catch (AlchemyHttpException ex) + { + LOG.error("Failed to mkae Alchemy HTTP Call at [{]]", url, ex); + + if (ex.hasResponse()) + { + int statusCode = ex.getResponse().statusCode(); + + if (statusCode == 400) + { + throw new YelpBadArgumentException("Bad Request", ex); + } + + if (statusCode == 401) + { + throw new YelpAuthenticationException("Invalid token", ex); + } + } + + throw new YelpOperationFailedException("Yelp call failed", ex); + } + catch (Exception ex) + { + LOG.error("Failed to make HTTP Call at [{}]", url, ex); + throw new YelpOperationFailedException("Yelp call failed to URL: " + url, ex); + } + } + + private AlchemyRequest.Step3 requestFilledWithParametersFrom(AlchemyRequest.Step3 httpRequest, YelpSearchRequest request) { diff --git a/src/test/java/tech/redroma/yelp/YelpAPIImplTest.java b/src/test/java/tech/redroma/yelp/YelpAPIImplTest.java index c7b29bf..e1d27ff 100644 --- a/src/test/java/tech/redroma/yelp/YelpAPIImplTest.java +++ b/src/test/java/tech/redroma/yelp/YelpAPIImplTest.java @@ -22,8 +22,11 @@ import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.Mock; +import tech.redroma.yelp.YelpAPIImpl.URLS; import tech.redroma.yelp.exceptions.YelpAuthenticationException; +import tech.redroma.yelp.exceptions.YelpBadArgumentException; import tech.redroma.yelp.exceptions.YelpException; +import tech.redroma.yelp.exceptions.YelpOperationFailedException; import tech.redroma.yelp.oauth.OAuthTokenProvider; import tech.sirwellington.alchemy.http.AlchemyHttp; import tech.sirwellington.alchemy.http.HttpResponse; @@ -75,6 +78,9 @@ public class YelpAPIImplTest @GeneratePojo private YelpResponses.SearchResponse searchResponse; + @GeneratePojo + private YelpResponses.ReviewsResponse reviewsResponse; + private List businesses; private YelpSearchRequest request; @@ -97,6 +103,8 @@ public class YelpAPIImplTest private String expectedSearchURL; + private String expectedReviewsURL; + @Before public void setUp() throws Exception { @@ -121,8 +129,9 @@ private void setupData() throws Exception .withLocale(enumValueOf(Locale.Locales.class).get()) .build(); - expectedGetBusinessDetailsURL = baseURL + YelpAPIImpl.URLS.BUSINESSES + "/" + businessID; - expectedSearchURL = baseURL + YelpAPIImpl.URLS.BUSINESS_SEARCH; + expectedGetBusinessDetailsURL = baseURL + URLS.BUSINESSES + "/" + businessID; + expectedSearchURL = baseURL + URLS.BUSINESS_SEARCH; + expectedReviewsURL = baseURL + URLS.BUSINESSES + "/" + businessID + URLS.REVIEWS; } private void setupMocks() throws Exception @@ -170,9 +179,7 @@ public void testGetBusinessDetailsWhenFails() throws Exception @Test public void testGetBusinessDetailsWhenTokenInvalid() throws Exception { - HttpResponse fakeResponse = mock(HttpResponse.class); - when(fakeResponse.statusCode()).thenReturn(401); - Exception ex = new AlchemyHttpException(fakeResponse); + Exception ex = createAlchemyExceptionWithStatus(401); http = AlchemyHttpMock.begin() .whenGet() @@ -227,10 +234,8 @@ public void testSearchForBusinessesWhenFails() throws Exception @Test public void testSearchForBusinessesWhenTokenInvalid() throws Exception { - HttpResponse fakeResponse = mock(HttpResponse.class); - when(fakeResponse.statusCode()).thenReturn(401); - Exception ex = new AlchemyHttpException(fakeResponse); - + AlchemyHttpException ex = createAlchemyExceptionWithStatus(401); + http = AlchemyHttpMock.begin() .whenGet() .noBody() @@ -243,5 +248,93 @@ public void testSearchForBusinessesWhenTokenInvalid() throws Exception assertThrows(() -> instance.searchForBusinesses(request)) .isInstanceOf(YelpAuthenticationException.class); } + + @Test + public void testGetReviewsForBusiness() throws Exception + { + + http = AlchemyHttpMock.begin() + .whenGet() + .noBody() + .at(expectedReviewsURL) + .thenReturnPOJO(reviewsResponse) + .build(); + + instance = new YelpAPIImpl(http, tokenProvider, baseURL.toString()); + + List results = instance.getReviewsForBusiness(businessID); + + assertThat(results, is(reviewsResponse.reviews)); + + AlchemyHttpMock.verifyAllRequestsMade(http); + } + @Test + public void testGetReviewsForBusinessWhenBadArguments() throws Exception + { + AlchemyHttpException ex = createAlchemyExceptionWithStatus(400); + http = AlchemyHttpMock.begin() + .whenGet() + .noBody() + .at(expectedReviewsURL) + .thenThrow(ex) + .build(); + + instance = new YelpAPIImpl(http, tokenProvider, baseURL.toString()); + + assertThrows(() -> instance.getReviewsForBusiness(businessID)) + .isInstanceOf(YelpBadArgumentException.class); + } + + @Test + public void testGetReviewsForBusinessWhenOperationFails() throws Exception + { + AlchemyHttpException ex = createAlchemyExceptionWithStatus(500); + http = AlchemyHttpMock.begin() + .whenGet() + .noBody() + .at(expectedReviewsURL) + .thenThrow(ex) + .build(); + + instance = new YelpAPIImpl(http, tokenProvider, baseURL.toString()); + + assertThrows(() -> instance.getReviewsForBusiness(businessID)) + .isInstanceOf(YelpOperationFailedException.class); + } + + @Test + public void testGetReviewsForBusinessWhenTokenInvalid() throws Exception + { + AlchemyHttpException ex = createAlchemyExceptionWithStatus(401); + + http = AlchemyHttpMock.begin() + .whenGet() + .noBody() + .at(expectedReviewsURL) + .thenThrow(ex) + .build(); + + instance = new YelpAPIImpl(http, tokenProvider, baseURL.toString()); + + assertThrows(() -> instance.getReviewsForBusiness(businessID)) + .isInstanceOf(YelpAuthenticationException.class); + } + + private AlchemyHttpException createAlchemyExceptionWithStatus(int code) + { + HttpResponse fakeResponse = createFakeHttpResponseWithCode(code); + AlchemyHttpException ex = new AlchemyHttpException(fakeResponse); + return ex; + } + + private HttpResponse createFakeHttpResponseWithCode(int code) + { + HttpResponse fakeResponse = mock(HttpResponse.class); + when(fakeResponse.statusCode()).thenReturn(code); + return fakeResponse; + } + + + } From cb67752f0b6b44d31d02fd46425aaca04823e854 Mon Sep 17 00:00:00 2001 From: Wellington Moreno Date: Tue, 6 Dec 2016 16:50:44 -0800 Subject: [PATCH 149/168] #3 - Adds testGetBusinessReviews() to YelpAPI IT Test --- .../java/tech/redroma/yelp/YelpAPIIT.java | 22 +++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/src/test/java/tech/redroma/yelp/YelpAPIIT.java b/src/test/java/tech/redroma/yelp/YelpAPIIT.java index 841b848..5de1e3d 100644 --- a/src/test/java/tech/redroma/yelp/YelpAPIIT.java +++ b/src/test/java/tech/redroma/yelp/YelpAPIIT.java @@ -100,4 +100,26 @@ public void testGetBusinessDetails() throws Exception } + @Test + public void testGetBusinessReviews() throws Exception + { + if (yelp == null) + { + return; + } + + request = YelpSearchRequest.newBuilder() + .withSearchTerm("Grocery") + .withCoordinate(Coordinate.of(34.018363, -118.492343)) + .withLimit(5) + .build(); + + List results = yelp.searchForBusinesses(request); + YelpBusiness business = Lists.oneOf(results); + + List reviews = yelp.getReviewsForBusiness(business); + + LOG.info("Successfully loaded reviews for business: {}", reviews); + } + } \ No newline at end of file From 6e99613087b5075f95d292c67751895d5bad84b6 Mon Sep 17 00:00:00 2001 From: Wellington Moreno Date: Tue, 6 Dec 2016 16:52:32 -0800 Subject: [PATCH 150/168] Adds toString() to ReviewsResponse --- src/main/java/tech/redroma/yelp/YelpResponses.java | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/main/java/tech/redroma/yelp/YelpResponses.java b/src/main/java/tech/redroma/yelp/YelpResponses.java index 5b1bf5f..5d18df9 100644 --- a/src/main/java/tech/redroma/yelp/YelpResponses.java +++ b/src/main/java/tech/redroma/yelp/YelpResponses.java @@ -156,6 +156,12 @@ public boolean equals(Object obj) return true; } + @Override + public String toString() + { + return "ReviewsResponse{" + "total=" + total + ", reviews=" + reviews + '}'; + } + } } From cd41b83336c5db9d7c05dba8484c0cce1df437fa Mon Sep 17 00:00:00 2001 From: Wellington Moreno Date: Tue, 6 Dec 2016 16:55:28 -0800 Subject: [PATCH 151/168] Adds Pojo methods to User class --- .../java/tech/redroma/yelp/YelpReview.java | 79 ++++++++++++++++++- 1 file changed, 76 insertions(+), 3 deletions(-) diff --git a/src/main/java/tech/redroma/yelp/YelpReview.java b/src/main/java/tech/redroma/yelp/YelpReview.java index e29e88c..29f5817 100644 --- a/src/main/java/tech/redroma/yelp/YelpReview.java +++ b/src/main/java/tech/redroma/yelp/YelpReview.java @@ -20,10 +20,16 @@ import com.google.gson.annotations.SerializedName; import java.time.Instant; +import java.util.Objects; +import tech.sirwellington.alchemy.annotations.arguments.NonEmpty; import tech.sirwellington.alchemy.annotations.concurrency.Mutable; import tech.sirwellington.alchemy.annotations.concurrency.ThreadUnsafe; import tech.sirwellington.alchemy.annotations.objects.Pojo; +import static tech.sirwellington.alchemy.arguments.Arguments.checkThat; +import static tech.sirwellington.alchemy.arguments.assertions.NetworkAssertions.validURL; +import static tech.sirwellington.alchemy.arguments.assertions.StringAssertions.nonEmptyString; + /** * Represents a Yelp Review JSON Object, as documented in the Yelp API. @@ -63,21 +69,88 @@ public class YelpReview * A URL of this review. */ public String url; - + @Pojo @Mutable @ThreadUnsafe - public static class User + public static class User { + /** * The name of the user. */ public String name; - + /** * A URL of the user's profile picture. */ @SerializedName("image_url") public String imageURL; + + /** + * Creates a {@link User} from the specified arguments. + * + * @param name + * @param imageURL + * @return + */ + public static User with(@NonEmpty String name, @NonEmpty String imageURL) + { + checkThat(name).is(nonEmptyString()); + checkThat(imageURL).is(validURL()); + + User user = new User(); + user.name = name; + user.imageURL = imageURL; + + return user; + } + + public User() + { + } + + @Override + public int hashCode() + { + int hash = 7; + hash = 41 * hash + Objects.hashCode(this.name); + hash = 41 * hash + Objects.hashCode(this.imageURL); + return hash; + } + + @Override + public boolean equals(Object obj) + { + if (this == obj) + { + return true; + } + if (obj == null) + { + return false; + } + if (getClass() != obj.getClass()) + { + return false; + } + final User other = (User) obj; + if (!Objects.equals(this.name, other.name)) + { + return false; + } + if (!Objects.equals(this.imageURL, other.imageURL)) + { + return false; + } + return true; + } + + @Override + public String toString() + { + return "User{" + "name=" + name + ", imageURL=" + imageURL + '}'; + } + } } From d4c7aed0409dd3b0d63a646c669d6bb0efc279fa Mon Sep 17 00:00:00 2001 From: Wellington Moreno Date: Tue, 6 Dec 2016 16:56:21 -0800 Subject: [PATCH 152/168] Adds Pojo methods to YelpReview class --- .../java/tech/redroma/yelp/YelpReview.java | 76 ++++++++++++++++--- 1 file changed, 67 insertions(+), 9 deletions(-) diff --git a/src/main/java/tech/redroma/yelp/YelpReview.java b/src/main/java/tech/redroma/yelp/YelpReview.java index 29f5817..d371a75 100644 --- a/src/main/java/tech/redroma/yelp/YelpReview.java +++ b/src/main/java/tech/redroma/yelp/YelpReview.java @@ -19,7 +19,6 @@ import com.google.gson.annotations.SerializedName; -import java.time.Instant; import java.util.Objects; import tech.sirwellington.alchemy.annotations.arguments.NonEmpty; import tech.sirwellington.alchemy.annotations.concurrency.Mutable; @@ -40,36 +39,95 @@ @Pojo @Mutable @ThreadUnsafe -public class YelpReview +public class YelpReview { /** * The raring of the business associated with this review + * * @see YelpBusiness#rating */ - public int rating; - + public Double rating; + /** * The user who wrote the review + * * @see User */ public User user; - + /** * A Text excerpt of this review. */ public String text; - + /** * The time that the review was created, in PST. */ - public Instant timeCreated; - + public String timeCreated; + /** * A URL of this review. */ public String url; - + + @Override + public int hashCode() + { + int hash = 7; + hash = 13 * hash + Objects.hashCode(this.rating); + hash = 13 * hash + Objects.hashCode(this.user); + hash = 13 * hash + Objects.hashCode(this.text); + hash = 13 * hash + Objects.hashCode(this.timeCreated); + hash = 13 * hash + Objects.hashCode(this.url); + return hash; + } + + @Override + public boolean equals(Object obj) + { + if (this == obj) + { + return true; + } + if (obj == null) + { + return false; + } + if (getClass() != obj.getClass()) + { + return false; + } + final YelpReview other = (YelpReview) obj; + if (!Objects.equals(this.text, other.text)) + { + return false; + } + if (!Objects.equals(this.timeCreated, other.timeCreated)) + { + return false; + } + if (!Objects.equals(this.url, other.url)) + { + return false; + } + if (!Objects.equals(this.rating, other.rating)) + { + return false; + } + if (!Objects.equals(this.user, other.user)) + { + return false; + } + return true; + } + + @Override + public String toString() + { + return "YelpReview{" + "rating=" + rating + ", user=" + user + ", text=" + text + ", timeCreated=" + timeCreated + ", url=" + url + '}'; + } + @Pojo @Mutable @ThreadUnsafe From 432aa293af66a618f3c2eacf74b5b894adefea09 Mon Sep 17 00:00:00 2001 From: Wellington Moreno Date: Tue, 6 Dec 2016 17:17:58 -0800 Subject: [PATCH 153/168] Expands the YelpReview class with additional methods --- .../java/tech/redroma/yelp/YelpReview.java | 43 ++++++++++++++ .../tech/redroma/yelp/YelpReviewTest.java | 57 +++++++++++++++++++ 2 files changed, 100 insertions(+) diff --git a/src/main/java/tech/redroma/yelp/YelpReview.java b/src/main/java/tech/redroma/yelp/YelpReview.java index d371a75..8fc32a4 100644 --- a/src/main/java/tech/redroma/yelp/YelpReview.java +++ b/src/main/java/tech/redroma/yelp/YelpReview.java @@ -19,12 +19,17 @@ import com.google.gson.annotations.SerializedName; +import java.time.LocalDateTime; +import java.time.ZoneId; +import java.time.ZoneOffset; +import java.time.ZonedDateTime; import java.util.Objects; import tech.sirwellington.alchemy.annotations.arguments.NonEmpty; import tech.sirwellington.alchemy.annotations.concurrency.Mutable; import tech.sirwellington.alchemy.annotations.concurrency.ThreadUnsafe; import tech.sirwellington.alchemy.annotations.objects.Pojo; +import static com.google.common.base.Strings.isNullOrEmpty; import static tech.sirwellington.alchemy.arguments.Arguments.checkThat; import static tech.sirwellington.alchemy.arguments.assertions.NetworkAssertions.validURL; import static tech.sirwellington.alchemy.arguments.assertions.StringAssertions.nonEmptyString; @@ -70,6 +75,44 @@ public class YelpReview * A URL of this review. */ public String url; + + public boolean hasRating() + { + return Objects.nonNull(rating); + } + + public boolean hasUser() + { + return user != null; + } + + public boolean hasText() + { + return !isNullOrEmpty(text); + } + + public boolean hasTimeCreated() + { + return !isNullOrEmpty(timeCreated); + } + + public boolean hasURL() + { + return !isNullOrEmpty(url); + } + + public ZonedDateTime getDateTimeCreated() + { + if (!hasTimeCreated()) + { + return null; + } + + ZoneId pst = ZoneOffset.of("PST", ZoneOffset.SHORT_IDS); + LocalDateTime localTime = LocalDateTime.parse(timeCreated); + + return ZonedDateTime.of(localTime, pst); + } @Override public int hashCode() diff --git a/src/test/java/tech/redroma/yelp/YelpReviewTest.java b/src/test/java/tech/redroma/yelp/YelpReviewTest.java index da5fbdf..5580d86 100644 --- a/src/test/java/tech/redroma/yelp/YelpReviewTest.java +++ b/src/test/java/tech/redroma/yelp/YelpReviewTest.java @@ -16,6 +16,8 @@ package tech.redroma.yelp; +import java.time.LocalDateTime; +import java.time.ZonedDateTime; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; @@ -24,6 +26,7 @@ import tech.sirwellington.alchemy.test.junit.runners.GeneratePojo; import tech.sirwellington.alchemy.test.junit.runners.Repeat; +import static java.time.ZoneId.of; import static org.hamcrest.Matchers.*; import static org.junit.Assert.*; @@ -48,6 +51,9 @@ public class YelpReviewTest @Before public void setUp() throws Exception { + LocalDateTime time = LocalDateTime.now(of("America/Los_Angeles")); + + instance.timeCreated = time.toString(); } @DontRepeat @@ -84,4 +90,55 @@ public void testHashCode() assertThat(first.hashCode(), is(first.hashCode())); assertThat(first.hashCode(), not(second.hashCode())); } + + @Test + public void testHasRating() + { + assertTrue(instance.hasRating()); + instance.rating = null; + assertFalse(instance.hasRating()); + } + + @Test + public void testHasUser() + { + assertTrue(instance.hasUser()); + instance.user = null; + assertFalse(instance.hasUser()); + } + + @Test + public void testHasText() + { + assertTrue(instance.hasText()); + instance.text = null; + assertFalse(instance.hasText()); + } + + @Test + public void testHasTimeCreated() + { + assertTrue(instance.hasTimeCreated()); + instance.timeCreated = null; + assertFalse(instance.hasTimeCreated()); + } + + @Test + public void testHasURL() + { + assertTrue(instance.hasURL()); + instance.url = null; + assertFalse(instance.hasURL()); + } + + @Test + public void testGetDateTimeCreated() + { + ZonedDateTime time = instance.getDateTimeCreated(); + assertThat(time, notNullValue()); + + LocalDateTime localTime = LocalDateTime.parse(instance.timeCreated); + ZonedDateTime expectedTime = ZonedDateTime.of(localTime, of("America/Los_Angeles")); + assertThat(time, is(expectedTime)); + } } \ No newline at end of file From c7e90e676982d1d0f9d0051f2236b9e4d9cd41f0 Mon Sep 17 00:00:00 2001 From: Wellington Moreno Date: Tue, 6 Dec 2016 17:26:22 -0800 Subject: [PATCH 154/168] #6 - Adds documentation --- src/main/java/tech/redroma/yelp/YelpReview.java | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/main/java/tech/redroma/yelp/YelpReview.java b/src/main/java/tech/redroma/yelp/YelpReview.java index 8fc32a4..fc6a68d 100644 --- a/src/main/java/tech/redroma/yelp/YelpReview.java +++ b/src/main/java/tech/redroma/yelp/YelpReview.java @@ -101,6 +101,11 @@ public boolean hasURL() return !isNullOrEmpty(url); } + /** + * Gets the {@link #timeCreated} as a Java {@link ZonedDateTime}, if available. + * + * @return ZonedDateTime representation of {@link #timeCreated}, or {@code null}. + */ public ZonedDateTime getDateTimeCreated() { if (!hasTimeCreated()) From fe6e6011c4adf205ded4471019ceaf4bca63fa38 Mon Sep 17 00:00:00 2001 From: Wellington Moreno Date: Tue, 6 Dec 2016 22:02:08 -0800 Subject: [PATCH 155/168] #7 - Refactors and improves --- .../java/tech/redroma/yelp/YelpAPIImpl.java | 24 ++++++++----------- 1 file changed, 10 insertions(+), 14 deletions(-) diff --git a/src/main/java/tech/redroma/yelp/YelpAPIImpl.java b/src/main/java/tech/redroma/yelp/YelpAPIImpl.java index 78a65f6..e4fad71 100644 --- a/src/main/java/tech/redroma/yelp/YelpAPIImpl.java +++ b/src/main/java/tech/redroma/yelp/YelpAPIImpl.java @@ -246,22 +246,18 @@ private YelpResponses.ReviewsResponse tryToGetReviewsAt(String url, String token { LOG.error("Failed to mkae Alchemy HTTP Call at [{]]", url, ex); - if (ex.hasResponse()) + if (isBadRequest(ex)) { - int statusCode = ex.getResponse().statusCode(); - - if (statusCode == 400) - { - throw new YelpBadArgumentException("Bad Request", ex); - } - - if (statusCode == 401) - { - throw new YelpAuthenticationException("Invalid token", ex); - } + throw new YelpBadArgumentException("Bad Request", ex); + } + else if (isBadAuth(ex)) + { + throw new YelpAuthenticationException("Invalid token", ex); + } + else + { + throw new YelpOperationFailedException("Yelp call failed", ex); } - - throw new YelpOperationFailedException("Yelp call failed", ex); } catch (Exception ex) { From f0ef4b516fac7b53fc54835506686bb6e5d176ee Mon Sep 17 00:00:00 2001 From: Wellington Moreno Date: Tue, 6 Dec 2016 22:19:07 -0800 Subject: [PATCH 156/168] #6 - Adds documentation to yelp reviews --- src/main/java/tech/redroma/yelp/YelpReview.java | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/src/main/java/tech/redroma/yelp/YelpReview.java b/src/main/java/tech/redroma/yelp/YelpReview.java index fc6a68d..d3eaf60 100644 --- a/src/main/java/tech/redroma/yelp/YelpReview.java +++ b/src/main/java/tech/redroma/yelp/YelpReview.java @@ -76,26 +76,41 @@ public class YelpReview */ public String url; + /** + * @return Whether this review has a rating. + */ public boolean hasRating() { return Objects.nonNull(rating); } + /** + * @return Whether this review has a user. + */ public boolean hasUser() { return user != null; } + /** + * @return Whether this review has a text excerpt. + */ public boolean hasText() { return !isNullOrEmpty(text); } + /** + * @return Whether this review has a creation time. + */ public boolean hasTimeCreated() { return !isNullOrEmpty(timeCreated); } + /** + * @return Whether this review has a URL. + */ public boolean hasURL() { return !isNullOrEmpty(url); From 7a05bb65b39c90a1587cc199c0481ebdcfbcb40e Mon Sep 17 00:00:00 2001 From: Wellington Moreno Date: Tue, 6 Dec 2016 22:19:53 -0800 Subject: [PATCH 157/168] #6 - Updates documentation --- .../java/tech/redroma/yelp/YelpBusinessDetails.java | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/main/java/tech/redroma/yelp/YelpBusinessDetails.java b/src/main/java/tech/redroma/yelp/YelpBusinessDetails.java index 3f1eacc..b6a7ebf 100644 --- a/src/main/java/tech/redroma/yelp/YelpBusinessDetails.java +++ b/src/main/java/tech/redroma/yelp/YelpBusinessDetails.java @@ -41,25 +41,25 @@ @ThreadUnsafe public class YelpBusinessDetails { - /** The Yelp ID of this business */ + /** The Yelp ID of this business. */ public String id; - /** The Name of this business */ + /** The Name of this business. */ public String name; - /** An URL photo for this business */ + /** An URL photo for this business. */ @SerializedName("image_url") public String imageURL; - /** Whether this business has been claimed by a Business Owner */ + /** Whether this business has been claimed by a Business Owner. */ @SerializedName("is_claimed") public Boolean isClaimed; - /** Whether the businesses has been permanently closed */ + /** Whether the businesses has been permanently closed. */ @SerializedName("is_closed") public Boolean isClosed; - /** URL for the business on Yelp */ + /** URL for the business on Yelp. */ public String url; /** The price level of the business. */ From 67bfb4314dd1b69f49fd524f3328969f285101d1 Mon Sep 17 00:00:00 2001 From: Wellington Moreno Date: Tue, 6 Dec 2016 22:27:57 -0800 Subject: [PATCH 158/168] #6 - Updates documentation for YelpBusinessDetails --- .../redroma/yelp/YelpBusinessDetails.java | 49 ++++++++++++++++++- 1 file changed, 48 insertions(+), 1 deletion(-) diff --git a/src/main/java/tech/redroma/yelp/YelpBusinessDetails.java b/src/main/java/tech/redroma/yelp/YelpBusinessDetails.java index b6a7ebf..048d5b5 100644 --- a/src/main/java/tech/redroma/yelp/YelpBusinessDetails.java +++ b/src/main/java/tech/redroma/yelp/YelpBusinessDetails.java @@ -65,23 +65,34 @@ public class YelpBusinessDetails /** The price level of the business. */ public String price; + /** Rating for this business. Ranges from (1...5). */ public Double rating; + /** The number of reviews for this business. */ public int reviewCount; + /** The phone number of this business, if available. */ public String phone; + /** The URLs of up to 3 photos. */ @SerializedName("photos") public List photosURLS; + /** Opening hours of the business. */ public List hours; + /** A list of categories associated with this business. */ public List categories; + /** The Geo-Coordinate (latitude-longitude) of this business. */ public Coordinate coordinates; + /** The physical address of this business. */ public Address location; - + + /** + * @return The {@link #price} as a {@link Price}. + */ public Price getPriceLevel() { if (isNullOrEmpty(price)) @@ -92,6 +103,7 @@ public Price getPriceLevel() return Price.fromString(price); } + @Override public int hashCode() { int hash = 7; @@ -204,20 +216,55 @@ public String toString() public static class Hours { + /** + * The type of opening hours information. Right now, it is always {@code 'REGULAR'}. + */ @SerializedName("hours_type") public String hoursType; + /** + * Whether the business is open at the time of the request. + */ @SerializedName("is_open_now") public Boolean isOpenNow; + /** + * A detailed list of opening hours for the business throughout the week. + */ public List open; public static class OpenTimes { + /** + * From 0 to 6, represents the day of the week from Monday to Sunday (respectively). + * Notice that you may get the same + * day of the week more than once if the business has more than one opening time slots. + */ public int day; + /** + * Start of the opening hours in a day, in 24-hour clock notation. + *
+             * 300 = 3am
+             * 1000 = 10am
+             * 1800 = 6pm
+             * 
+ */ public String start; + /** + * End of the opening hours in a day, in 24-hour clock notation. + *
+             * 300 = 3am
+             * 1000 = 10am
+             * 1800 = 6pm
+             * 
+ */ public String end; + + /** + * Whether the business opens overnight or not. When this is true, the end time will be lower than the start time, as + * it signifies the end time is in the following day. + */ @SerializedName("is_overnight") public Boolean isOvernight; From a41ed16c17490781283fc95a6084695b11b63b8a Mon Sep 17 00:00:00 2001 From: Wellington Moreno Date: Tue, 6 Dec 2016 23:00:05 -0800 Subject: [PATCH 159/168] Adds isOpenNow() to YelpBusinessDetails --- .../redroma/yelp/YelpBusinessDetails.java | 15 +++++++++ .../redroma/yelp/YelpBusinessDetailsTest.java | 31 +++++++++++++++++++ 2 files changed, 46 insertions(+) diff --git a/src/main/java/tech/redroma/yelp/YelpBusinessDetails.java b/src/main/java/tech/redroma/yelp/YelpBusinessDetails.java index 048d5b5..ad0559e 100644 --- a/src/main/java/tech/redroma/yelp/YelpBusinessDetails.java +++ b/src/main/java/tech/redroma/yelp/YelpBusinessDetails.java @@ -19,6 +19,7 @@ import com.google.gson.annotations.SerializedName; import java.util.List; import java.util.Objects; +import sir.wellington.alchemy.collections.lists.Lists; import tech.sirwellington.alchemy.annotations.concurrency.Mutable; import tech.sirwellington.alchemy.annotations.concurrency.ThreadUnsafe; import tech.sirwellington.alchemy.annotations.objects.Pojo; @@ -102,6 +103,20 @@ public Price getPriceLevel() return Price.fromString(price); } + + /** + * @return Whether this business is open now or not. + */ + public boolean isOpenNow() + { + if (Lists.isEmpty(hours)) + { + return false; + } + + return hours.stream() + .anyMatch(h -> h.isOpenNow); + } @Override public int hashCode() diff --git a/src/test/java/tech/redroma/yelp/YelpBusinessDetailsTest.java b/src/test/java/tech/redroma/yelp/YelpBusinessDetailsTest.java index 4435fe9..9f3d610 100644 --- a/src/test/java/tech/redroma/yelp/YelpBusinessDetailsTest.java +++ b/src/test/java/tech/redroma/yelp/YelpBusinessDetailsTest.java @@ -21,6 +21,7 @@ import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; +import sir.wellington.alchemy.collections.lists.Lists; import tech.sirwellington.alchemy.generator.AlchemyGenerator; import tech.sirwellington.alchemy.generator.StringGenerators; import tech.sirwellington.alchemy.test.junit.runners.AlchemyTestRunner; @@ -34,7 +35,9 @@ import static org.hamcrest.Matchers.isEmptyOrNullString; import static org.hamcrest.Matchers.not; import static org.hamcrest.Matchers.notNullValue; +import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertThat; +import static org.junit.Assert.assertTrue; import static tech.redroma.yelp.Resources.GSON; import static tech.sirwellington.alchemy.arguments.Arguments.checkThat; import static tech.sirwellington.alchemy.arguments.assertions.GeolocationAssertions.validLatitude; @@ -215,4 +218,32 @@ private AlchemyGenerator prices() { return StringGenerators.stringsFromFixedList("$", "$$", "$$$", "$$$$"); } + + @DontRepeat + @Test + public void testIsOpenNowWhenOpen() + { + YelpBusinessDetails.Hours hours = new YelpBusinessDetails.Hours(); + hours.isOpenNow = true; + hours.hoursType = "REGULAR"; + hours.open = Lists.emptyList(); + + instance.hours = Lists.createFrom(hours); + + assertTrue(instance.isOpenNow()); + } + + @DontRepeat + @Test + public void testIsOpenNowWhenClosed() + { + YelpBusinessDetails.Hours hours = new YelpBusinessDetails.Hours(); + hours.isOpenNow = false; + hours.hoursType = "REGULAR"; + hours.open = Lists.emptyList(); + + instance.hours = Lists.createFrom(hours); + + assertFalse(instance.isOpenNow()); + } } From a609415aca7aa3f7bb301d721c8a20ead38cf173 Mon Sep 17 00:00:00 2001 From: Wellington Moreno Date: Tue, 6 Dec 2016 23:05:36 -0800 Subject: [PATCH 160/168] #6 - Updates documentation for YelpAPI --- src/main/java/tech/redroma/yelp/YelpAPI.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main/java/tech/redroma/yelp/YelpAPI.java b/src/main/java/tech/redroma/yelp/YelpAPI.java index 754bb41..054a345 100644 --- a/src/main/java/tech/redroma/yelp/YelpAPI.java +++ b/src/main/java/tech/redroma/yelp/YelpAPI.java @@ -59,7 +59,7 @@ public interface YelpAPI * Normally, you'll get the {@link YelpBusiness} object from the * {@linkplain #searchForBusinesses(tech.redroma.yelp.YelpSearchRequest) search API}. *

- * To get review information for a business, refer to ... + * To get review information for a business, refer to {@link #getReviewsForBusiness(java.lang.String) }. * * @param business The business to get more details of. * @return @@ -89,7 +89,7 @@ default YelpBusinessDetails getBusinessDetails(@Required YelpBusiness business) * Normally, you'll get the business id from the * {@linkplain #searchForBusinesses(tech.redroma.yelp.YelpSearchRequest) search API}. *

- * To get review information for a business, refer to ... + * To get review information for a business, refer to {@link #getReviewsForBusiness(java.lang.String) }. * * @param businessId The {@linkplain YelpBusiness#id Business ID} to query. * @return From afc6f02f25d1b334ca589c44684ddb8b56f5ae6d Mon Sep 17 00:00:00 2001 From: Wellington Moreno Date: Tue, 6 Dec 2016 23:17:56 -0800 Subject: [PATCH 161/168] #1 - Adding a public-facing README for documentation --- LICENSE | 2 +- README.md | 120 +++++++++++++++++++++++++++++++++++++++++++++++++++++- 2 files changed, 119 insertions(+), 3 deletions(-) diff --git a/LICENSE b/LICENSE index 8dada3e..20ec7a3 100644 --- a/LICENSE +++ b/LICENSE @@ -186,7 +186,7 @@ same "printed page" as the copyright notice for easier identification within third-party archives. - Copyright {yyyy} {name of copyright owner} + Copyright {2016} {RedRoma, Inc.} Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. diff --git a/README.md b/README.md index 97b2a25..f2103c2 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,123 @@ YelpJavaClient =================== + +[](http://RedRoma.tech) + [![Jenkins](http://jenkins.redroma.tech/job/YelpAPI/badge/icon)](http://jenkins.redroma.tech/job/YelpAPI/) [![Travis](https://travis-ci.org/RedRoma/YelpJavaClient.svg?branch=develop)](https://travis-ci.org/RedRoma/YelpJavaClient) -Provides a convenient Java Client Library for interfacing with Yelp. +--- + +The **YelpJavaClient** provides + +## Download + +### Maven + +```xml + + tech.redroma.yelp + yelp-api + 1.0 + +``` + +## Creating a Client + +Create a client using the App ID and App Secret that you obtained from the [Yelp Developer console](https://www.yelp.com/developers/v3/manage_app). + +```java +String clientId = "..."; +String clientSecret = "..."; +YelpAPI yelp = YelpAPI.newInstance(clientId, clientSecret); +``` + +## Searching Businesses + +#### [Yelp API Documentation](https://www.yelp.com/developers/documentation/v3/business_search) + + +Searching businesses is as easy as making a request object and using the `searchForBusinesses()` method. + +```java +//Create a request object +YelpSearchRequest request = YelpSearchRequest.newBuilder() + .withSearchTerm("Deli") + .withCoordinate(Coordinate.of(34.018363, -118.492343)) + .withLimit(10) + .withSortBy(YelpSearchRequest.SortType.DISTANCE) + .build(); + +//Make the request +List results = yelp.searchForBusinesses(request); + +LOG.info("Found {} results for request {}", results.size(), request); +``` + + +## Business Details + +#### [Yelp API Documentation](https://www.yelp.com/developers/documentation/v3/business) + + +Sometimes you want more detailed information about a business, such as the business hours, additional photos, and price information. + +Simply call the `getBusinessDetails()` method. + +```java +//Using any business +YelpBusiness business = Lists.oneOf(results); + +//Make the request to get business details. +YelpBusinessDetails businessDetails = yelp.getBusinessDetails(business); + +LOG.info("Received detailed info for business named {}: [{}]", business.name, businessDetails); + +if (businessDetails.isOpenNow()) +{ + LOG.info("{} is open now.", businessDetails.name) +} +``` + +## Reviews + +#### [Yelp API Documentation](https://www.yelp.com/developers/documentation/v3/business_reviews) + + +```java +YelpBusiness business = Lists.oneof(business); +List reviews = yelp.getReviewsForBusiness(business); + +LOG.info("Business named") +``` + +## [Javadocs](http://www.javadoc.io/doc/tech.redroma.yelp/yelp-api/) + +## Currently Unsupported + +We do not yet support the following API calls: + ++ [Phone Search](https://www.yelp.com/developers/documentation/v3/business_search_phone) ++ [Transaction Search](https://www.yelp.com/developers/documentation/v3/transactions_search) ++ [Autocomplete](https://www.yelp.com/developers/documentation/v3/autocomplete) + +## Guiding Philosophy + +We used [**Alchemy Design Principles**](https://github.com/SirWellington/alchemy) when designing this library. + +### Swift +We wanted our code to feel like it was barely there. This meant keeping things minimal and light. + +### Intuitive +Yelp already designed a great intuitive API. We didn't want to add a pool of unnecessary soda. + +### Solid +Nearly everything is unit tested, and it is already being used in production by [BlackNectar](http://docs.blacknectarapi.apiary.io/), and others. + +### Invigorating +We wanted you to have fun, and to feel powerful. + +# License + +This Software is licensed under the Apache 2.0 License -> Documentation coming soon... +http://www.apache.org/licenses/LICENSE-2.0 From 2e376c4978e6bff50f434acc8b3789b3c41e506a Mon Sep 17 00:00:00 2001 From: Wellington Moreno Date: Tue, 6 Dec 2016 23:34:58 -0800 Subject: [PATCH 162/168] Updates README --- README.md | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index f2103c2..8e456e7 100644 --- a/README.md +++ b/README.md @@ -104,6 +104,8 @@ We do not yet support the following API calls: We used [**Alchemy Design Principles**](https://github.com/SirWellington/alchemy) when designing this library. +[](https://github.com/SirWellington/alchemy) + ### Swift We wanted our code to feel like it was barely there. This meant keeping things minimal and light. @@ -114,8 +116,10 @@ Yelp already designed a great intuitive API. We didn't want to add a pool of unn Nearly everything is unit tested, and it is already being used in production by [BlackNectar](http://docs.blacknectarapi.apiary.io/), and others. ### Invigorating -We wanted you to have fun, and to feel powerful. - +We wanted you to have fun, and to feel powerful. +We ditched the no-fun java `get() set()` pojo style in favor of open `public` + variables. We trust you. + # License This Software is licensed under the Apache 2.0 License From 92cbcbb86d20384d5073cb2b995412fd03e3af8d Mon Sep 17 00:00:00 2001 From: Wellington Moreno Date: Tue, 6 Dec 2016 23:36:05 -0800 Subject: [PATCH 163/168] Bumps pom version to 1.0 --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index f092e08..b360b18 100644 --- a/pom.xml +++ b/pom.xml @@ -4,7 +4,7 @@ tech.redroma.yelp yelp-api - 1.0-SNAPSHOT + 1.0 YelpAPI jar From c04360efcdbc7587d5f036f1aba7bc18f5340d1c Mon Sep 17 00:00:00 2001 From: Wellington Moreno Date: Wed, 7 Dec 2016 00:42:42 -0800 Subject: [PATCH 164/168] Updates alchemy-arguments --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index b360b18..e71506b 100644 --- a/pom.xml +++ b/pom.xml @@ -128,7 +128,7 @@ tech.sirwellington.alchemy alchemy-arguments - 1.5-SNAPSHOT + 1.5 From 9edd3b735cbeb52774f8ecc176faeb5bda352bf6 Mon Sep 17 00:00:00 2001 From: Wellington Moreno Date: Wed, 7 Dec 2016 00:46:19 -0800 Subject: [PATCH 165/168] Updates alchemy-generator --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index e71506b..1e1cf54 100644 --- a/pom.xml +++ b/pom.xml @@ -140,7 +140,7 @@ tech.sirwellington.alchemy alchemy-generator - 1.6-SNAPSHOT + 1.6 test From 56ea14d00b8f94cd052f87ad5e48684572913496 Mon Sep 17 00:00:00 2001 From: Wellington Moreno Date: Wed, 7 Dec 2016 01:09:19 -0800 Subject: [PATCH 166/168] Updates alchemy-http --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 1e1cf54..58641fb 100644 --- a/pom.xml +++ b/pom.xml @@ -147,7 +147,7 @@ tech.sirwellington.alchemy alchemy-http - 1.3-SNAPSHOT + 1.3 From 6f8d9f82253c440174cdc994e49dcebbae6d4339 Mon Sep 17 00:00:00 2001 From: Wellington Moreno Date: Wed, 7 Dec 2016 01:36:31 -0800 Subject: [PATCH 167/168] Updates alchemy-http-mock --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 58641fb..d280f98 100644 --- a/pom.xml +++ b/pom.xml @@ -153,7 +153,7 @@ tech.sirwellington.alchemy alchemy-http-mock - 1.1-SNAPSHOT + 1.1 test From 3970a8a2b02cedb71627a210d87b723b7a3668f3 Mon Sep 17 00:00:00 2001 From: Wellington Moreno Date: Wed, 7 Dec 2016 01:36:41 -0800 Subject: [PATCH 168/168] Updates alchemy-test --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index d280f98..57edf72 100644 --- a/pom.xml +++ b/pom.xml @@ -160,7 +160,7 @@ tech.sirwellington.alchemy alchemy-test - 1.6-SNAPSHOT + 1.6 test